From f597a718296eef9622ebc1d13f4f8324fa169cef Mon Sep 17 00:00:00 2001 From: Arnaud Lacombe Date: Fri, 1 Jul 2011 17:45:31 -0400 Subject: [PATCH 0001/1531] kconfig/streamline_config.pl: directly access LSMOD from the environment Cc: Steven Rostedt Signed-off-by: Arnaud Lacombe --- scripts/kconfig/Makefile | 12 ++---------- scripts/kconfig/streamline_config.pl | 8 ++++++-- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile index 82d2eb285b70..bd41ab29e680 100644 --- a/scripts/kconfig/Makefile +++ b/scripts/kconfig/Makefile @@ -33,17 +33,9 @@ silentoldconfig: $(obj)/conf $(Q)mkdir -p include/generated $< --$@ $(Kconfig) -# if no path is given, then use src directory to find file -ifdef LSMOD -LSMOD_F := $(LSMOD) -ifeq ($(findstring /,$(LSMOD)),) - LSMOD_F := $(objtree)/$(LSMOD) -endif -endif - localmodconfig: $(obj)/streamline_config.pl $(obj)/conf $(Q)mkdir -p include/generated - $(Q)perl $< $(srctree) $(Kconfig) $(LSMOD_F) > .tmp.config + $(Q)perl $< $(srctree) $(Kconfig) > .tmp.config $(Q)if [ -f .config ]; then \ cmp -s .tmp.config .config || \ (mv -f .config .config.old.1; \ @@ -58,7 +50,7 @@ localmodconfig: $(obj)/streamline_config.pl $(obj)/conf localyesconfig: $(obj)/streamline_config.pl $(obj)/conf $(Q)mkdir -p include/generated - $(Q)perl $< $(srctree) $(Kconfig) $(LSMOD_F) > .tmp.config + $(Q)perl $< $(srctree) $(Kconfig) > .tmp.config $(Q)sed -i s/=m/=y/ .tmp.config $(Q)if [ -f .config ]; then \ cmp -s .tmp.config .config || \ diff --git a/scripts/kconfig/streamline_config.pl b/scripts/kconfig/streamline_config.pl index a4fe923c0131..ae34d2007f57 100644 --- a/scripts/kconfig/streamline_config.pl +++ b/scripts/kconfig/streamline_config.pl @@ -115,7 +115,7 @@ sub find_config { # Get the build source and top level Kconfig file (passed in) my $ksource = $ARGV[0]; my $kconfig = $ARGV[1]; -my $lsmod_file = $ARGV[2]; +my $lsmod_file = $ENV{'LSMOD'}; my @makefiles = `find $ksource -name Makefile 2>/dev/null`; chomp @makefiles; @@ -296,7 +296,11 @@ sub read_kconfig { if (defined($lsmod_file)) { if ( ! -f $lsmod_file) { - die "$lsmod_file not found"; + if ( -f $ENV{'objtree'}."/".$lsmod_file) { + $lsmod_file = $ENV{'objtree'}."/".$lsmod_file; + } else { + die "$lsmod_file not found"; + } } if ( -x $lsmod_file) { # the file is executable, run it From 22d550ae83603aa8d64d72dd1e2309cd7a96ac89 Mon Sep 17 00:00:00 2001 From: Arnaud Lacombe Date: Wed, 20 Jul 2011 00:40:09 -0400 Subject: [PATCH 0002/1531] kconfig/streamline_config.pl: use options to determine operating mode The options introduced are --localmodconfig (default) and --localyesconfig. They match the Makefile target behavior. Cc: Steven Rostedt Signed-off-by: Arnaud Lacombe --- scripts/kconfig/Makefile | 4 ++-- scripts/kconfig/streamline_config.pl | 14 +++++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile index bd41ab29e680..4de89738827c 100644 --- a/scripts/kconfig/Makefile +++ b/scripts/kconfig/Makefile @@ -35,7 +35,7 @@ silentoldconfig: $(obj)/conf localmodconfig: $(obj)/streamline_config.pl $(obj)/conf $(Q)mkdir -p include/generated - $(Q)perl $< $(srctree) $(Kconfig) > .tmp.config + $(Q)perl $< --$@ $(srctree) $(Kconfig) > .tmp.config $(Q)if [ -f .config ]; then \ cmp -s .tmp.config .config || \ (mv -f .config .config.old.1; \ @@ -50,7 +50,7 @@ localmodconfig: $(obj)/streamline_config.pl $(obj)/conf localyesconfig: $(obj)/streamline_config.pl $(obj)/conf $(Q)mkdir -p include/generated - $(Q)perl $< $(srctree) $(Kconfig) > .tmp.config + $(Q)perl $< --$@ $(srctree) $(Kconfig) > .tmp.config $(Q)sed -i s/=m/=y/ .tmp.config $(Q)if [ -f .config ]; then \ cmp -s .tmp.config .config || \ diff --git a/scripts/kconfig/streamline_config.pl b/scripts/kconfig/streamline_config.pl index ae34d2007f57..ec7afce4c88d 100644 --- a/scripts/kconfig/streamline_config.pl +++ b/scripts/kconfig/streamline_config.pl @@ -43,6 +43,7 @@ # make oldconfig # use strict; +use Getopt::Long; my $config = ".config"; @@ -112,6 +113,13 @@ sub find_config { find_config; +# Parse options +my $localmodconfig = 0; +my $localyesconfig = 0; + +GetOptions("localmodconfig" => \$localmodconfig, + "localyesconfig" => \$localyesconfig); + # Get the build source and top level Kconfig file (passed in) my $ksource = $ARGV[0]; my $kconfig = $ARGV[1]; @@ -425,7 +433,11 @@ sub parse_config_dep_select if (/^(CONFIG.*)=(m|y)/) { if (defined($configs{$1})) { - $setconfigs{$1} = $2; + if ($localyesconfig) { + $setconfigs{$1} = 'y'; + } else { + $setconfigs{$1} = $2; + } } elsif ($2 eq "m") { print "# $1 is not set\n"; next; From 50bce3e80773e06316fcb92796ff26b42348d30b Mon Sep 17 00:00:00 2001 From: Arnaud Lacombe Date: Fri, 1 Jul 2011 18:13:03 -0400 Subject: [PATCH 0003/1531] kconfig/streamline_config.pl: merge local{mod,yes}config The two targets `localmodconfig' and `localyesconfig' only differs from the sed(1) ran on the result of `streamline_config.pl' to convert symbols set to `modules' to `yes'. This conversion can be made directly from the perl script, and thus avoid duplicating the command to generate the configuration. Cc: Steven Rostedt Signed-off-by: Arnaud Lacombe --- scripts/kconfig/Makefile | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile index 4de89738827c..ba573fe7c74d 100644 --- a/scripts/kconfig/Makefile +++ b/scripts/kconfig/Makefile @@ -33,7 +33,7 @@ silentoldconfig: $(obj)/conf $(Q)mkdir -p include/generated $< --$@ $(Kconfig) -localmodconfig: $(obj)/streamline_config.pl $(obj)/conf +localyesconfig localmodconfig: $(obj)/streamline_config.pl $(obj)/conf $(Q)mkdir -p include/generated $(Q)perl $< --$@ $(srctree) $(Kconfig) > .tmp.config $(Q)if [ -f .config ]; then \ @@ -48,22 +48,6 @@ localmodconfig: $(obj)/streamline_config.pl $(obj)/conf fi $(Q)rm -f .tmp.config -localyesconfig: $(obj)/streamline_config.pl $(obj)/conf - $(Q)mkdir -p include/generated - $(Q)perl $< --$@ $(srctree) $(Kconfig) > .tmp.config - $(Q)sed -i s/=m/=y/ .tmp.config - $(Q)if [ -f .config ]; then \ - cmp -s .tmp.config .config || \ - (mv -f .config .config.old.1; \ - mv -f .tmp.config .config; \ - $(obj)/conf --silentoldconfig $(Kconfig); \ - mv -f .config.old.1 .config.old) \ - else \ - mv -f .tmp.config .config; \ - $(obj)/conf --silentoldconfig $(Kconfig); \ - fi - $(Q)rm -f .tmp.config - # Create new linux.pot file # Adjust charset to UTF-8 in .po file to accept UTF-8 in Kconfig files # The symlink is used to repair a deficiency in arch/um From 9f0fa7991af382bfa8c9575d2457a0b6ad03ac4c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 15 Jun 2011 06:08:32 +0000 Subject: [PATCH 0004/1531] sh: ecovec: add renesas_usbhs DMAEngine support Signed-off-by: Kuninori Morimoto Signed-off-by: Paul Mundt --- arch/sh/boards/mach-ecovec24/setup.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c index b24d69d509e7..22faf2a2d8fc 100644 --- a/arch/sh/boards/mach-ecovec24/setup.c +++ b/arch/sh/boards/mach-ecovec24/setup.c @@ -248,6 +248,10 @@ static struct renesas_usbhs_platform_info usbhs_info = { .driver_param = { .buswait_bwait = 4, .detection_delay = 5, + .d0_tx_id = SHDMA_SLAVE_USB1D0_TX, + .d0_rx_id = SHDMA_SLAVE_USB1D0_RX, + .d1_tx_id = SHDMA_SLAVE_USB1D1_TX, + .d1_rx_id = SHDMA_SLAVE_USB1D1_RX, }, }; From 13049537007dee73a76f0a30fcbc24d02c6fa9e4 Mon Sep 17 00:00:00 2001 From: Joseph Handzik Date: Mon, 8 Aug 2011 11:40:15 +0200 Subject: [PATCH 0005/1531] cciss: Adds simple mode functionality Signed-off-by: Joseph Handzik Acked-by: Stephen M. Cameron Signed-off-by: Jens Axboe --- Documentation/blockdev/cciss.txt | 10 ++++++ drivers/block/cciss.c | 56 +++++++++++++++++++++++++------- drivers/block/cciss.h | 1 + 3 files changed, 56 insertions(+), 11 deletions(-) diff --git a/Documentation/blockdev/cciss.txt b/Documentation/blockdev/cciss.txt index c00c6a5ab21f..71464e09ec18 100644 --- a/Documentation/blockdev/cciss.txt +++ b/Documentation/blockdev/cciss.txt @@ -78,6 +78,16 @@ The device naming scheme is: /dev/cciss/c1d1p2 Controller 1, disk 1, partition 2 /dev/cciss/c1d1p3 Controller 1, disk 1, partition 3 +CCISS simple mode support +------------------------- + +The "cciss_simple_mode=1" boot parameter may be used to prevent the driver +from putting the controller into "performant" mode. The difference is that +with simple mode, each command completion requires an interrupt, while with +"performant mode" (the default, and ordinarily better performing) it is +possible to have multiple command completions indicated by a single +interrupt. + SCSI tape drive and medium changer support ------------------------------------------ diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 8f4ef656a1af..61f0b5b6a415 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -68,6 +68,10 @@ static int cciss_tape_cmds = 6; module_param(cciss_tape_cmds, int, 0644); MODULE_PARM_DESC(cciss_tape_cmds, "number of commands to allocate for tape devices (default: 6)"); +static int cciss_simple_mode; +module_param(cciss_simple_mode, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(cciss_simple_mode, + "Use 'simple mode' rather than 'performant mode'"); static DEFINE_MUTEX(cciss_mutex); static struct proc_dir_entry *proc_cciss; @@ -176,6 +180,7 @@ static void cciss_geometry_inquiry(ctlr_info_t *h, int logvol, unsigned int block_size, InquiryData_struct *inq_buff, drive_info_struct *drv); static void __devinit cciss_interrupt_mode(ctlr_info_t *); +static int __devinit cciss_enter_simple_mode(struct ctlr_info *h); static void start_io(ctlr_info_t *h); static int sendcmd_withirq(ctlr_info_t *h, __u8 cmd, void *buff, size_t size, __u8 page_code, unsigned char scsi3addr[], @@ -388,7 +393,7 @@ static void cciss_seq_show_header(struct seq_file *seq) h->product_name, (unsigned long)h->board_id, h->firm_ver[0], h->firm_ver[1], h->firm_ver[2], - h->firm_ver[3], (unsigned int)h->intr[PERF_MODE_INT], + h->firm_ver[3], (unsigned int)h->intr[h->intr_mode], h->num_luns, h->Qdepth, h->commands_outstanding, h->maxQsinceinit, h->max_outstanding, h->maxSG); @@ -3984,6 +3989,9 @@ static void __devinit cciss_put_controller_into_performant_mode(ctlr_info_t *h) { __u32 trans_support; + if (cciss_simple_mode) + return; + dev_dbg(&h->pdev->dev, "Trying to put board into Performant mode\n"); /* Attempt to put controller into performant mode if supported */ /* Does board support performant mode? */ @@ -4081,7 +4089,7 @@ static void __devinit cciss_interrupt_mode(ctlr_info_t *h) default_int_mode: #endif /* CONFIG_PCI_MSI */ /* if we get here we're going to use the default interrupt mode */ - h->intr[PERF_MODE_INT] = h->pdev->irq; + h->intr[h->intr_mode] = h->pdev->irq; return; } @@ -4341,6 +4349,9 @@ static int __devinit cciss_pci_init(ctlr_info_t *h) } cciss_enable_scsi_prefetch(h); cciss_p600_dma_prefetch_quirk(h); + err = cciss_enter_simple_mode(h); + if (err) + goto err_out_free_res; cciss_put_controller_into_performant_mode(h); return 0; @@ -4843,20 +4854,20 @@ static int cciss_request_irq(ctlr_info_t *h, irqreturn_t (*intxhandler)(int, void *)) { if (h->msix_vector || h->msi_vector) { - if (!request_irq(h->intr[PERF_MODE_INT], msixhandler, + if (!request_irq(h->intr[h->intr_mode], msixhandler, IRQF_DISABLED, h->devname, h)) return 0; dev_err(&h->pdev->dev, "Unable to get msi irq %d" - " for %s\n", h->intr[PERF_MODE_INT], + " for %s\n", h->intr[h->intr_mode], h->devname); return -1; } - if (!request_irq(h->intr[PERF_MODE_INT], intxhandler, + if (!request_irq(h->intr[h->intr_mode], intxhandler, IRQF_DISABLED, h->devname, h)) return 0; dev_err(&h->pdev->dev, "Unable to get irq %d for %s\n", - h->intr[PERF_MODE_INT], h->devname); + h->intr[h->intr_mode], h->devname); return -1; } @@ -4887,7 +4898,7 @@ static void cciss_undo_allocations_after_kdump_soft_reset(ctlr_info_t *h) { int ctlr = h->ctlr; - free_irq(h->intr[PERF_MODE_INT], h); + free_irq(h->intr[h->intr_mode], h); #ifdef CONFIG_PCI_MSI if (h->msix_vector) pci_disable_msix(h->pdev); @@ -4953,6 +4964,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, h = hba[i]; h->pdev = pdev; h->busy_initializing = 1; + h->intr_mode = cciss_simple_mode ? SIMPLE_MODE_INT : PERF_MODE_INT; INIT_LIST_HEAD(&h->cmpQ); INIT_LIST_HEAD(&h->reqQ); mutex_init(&h->busy_shutting_down); @@ -5009,7 +5021,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, dev_info(&h->pdev->dev, "%s: <0x%x> at PCI %s IRQ %d%s using DAC\n", h->devname, pdev->device, pci_name(pdev), - h->intr[PERF_MODE_INT], dac ? "" : " not"); + h->intr[h->intr_mode], dac ? "" : " not"); if (cciss_allocate_cmd_pool(h)) goto clean4; @@ -5056,7 +5068,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, spin_lock_irqsave(&h->lock, flags); h->access.set_intr_mask(h, CCISS_INTR_OFF); spin_unlock_irqrestore(&h->lock, flags); - free_irq(h->intr[PERF_MODE_INT], h); + free_irq(h->intr[h->intr_mode], h); rc = cciss_request_irq(h, cciss_msix_discard_completions, cciss_intx_discard_completions); if (rc) { @@ -5133,7 +5145,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, cciss_free_cmd_pool(h); cciss_free_scatterlists(h); cciss_free_sg_chain_blocks(h->cmd_sg_list, h->nr_cmds); - free_irq(h->intr[PERF_MODE_INT], h); + free_irq(h->intr[h->intr_mode], h); clean2: unregister_blkdev(h->major, h->devname); clean1: @@ -5172,9 +5184,31 @@ static void cciss_shutdown(struct pci_dev *pdev) if (return_code != IO_OK) dev_warn(&h->pdev->dev, "Error flushing cache\n"); h->access.set_intr_mask(h, CCISS_INTR_OFF); - free_irq(h->intr[PERF_MODE_INT], h); + free_irq(h->intr[h->intr_mode], h); } +static int __devinit cciss_enter_simple_mode(struct ctlr_info *h) +{ + u32 trans_support; + + trans_support = readl(&(h->cfgtable->TransportSupport)); + if (!(trans_support & SIMPLE_MODE)) + return -ENOTSUPP; + + h->max_commands = readl(&(h->cfgtable->CmdsOutMax)); + writel(CFGTBL_Trans_Simple, &(h->cfgtable->HostWrite.TransportRequest)); + writel(CFGTBL_ChangeReq, h->vaddr + SA5_DOORBELL); + cciss_wait_for_mode_change_ack(h); + print_cfg_table(h); + if (!(readl(&(h->cfgtable->TransportActive)) & CFGTBL_Trans_Simple)) { + dev_warn(&h->pdev->dev, "unable to get board into simple mode\n"); + return -ENODEV; + } + h->transMethod = CFGTBL_Trans_Simple; + return 0; +} + + static void __devexit cciss_remove_one(struct pci_dev *pdev) { ctlr_info_t *h; diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h index c049548e68b7..7fda30e4a241 100644 --- a/drivers/block/cciss.h +++ b/drivers/block/cciss.h @@ -92,6 +92,7 @@ struct ctlr_info unsigned int intr[4]; unsigned int msix_vector; unsigned int msi_vector; + int intr_mode; int cciss_max_sectors; BYTE cciss_read; BYTE cciss_write; From f963d270cb7bbb8eeb57901d02b22a493e664fd2 Mon Sep 17 00:00:00 2001 From: Joe Handzik Date: Mon, 8 Aug 2011 11:40:17 +0200 Subject: [PATCH 0006/1531] cciss: add transport mode attribute to sys Signed-off-by: Joseph Handzik Acked-by: Stephen M. Cameron Signed-off-by: Jens Axboe --- .../ABI/testing/sysfs-bus-pci-devices-cciss | 7 +++++++ drivers/block/cciss.c | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-pci-devices-cciss b/Documentation/ABI/testing/sysfs-bus-pci-devices-cciss index f5bb0a3bb8c0..53d99edd1d75 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci-devices-cciss +++ b/Documentation/ABI/testing/sysfs-bus-pci-devices-cciss @@ -71,3 +71,10 @@ Description: Value of 1 indicates the controller can honor the reset_devices a dump device, as kdump requires resetting the device in order to work reliably. +Where: /sys/bus/pci/devices//ccissX/transport_mode +Date: July 2011 +Kernel Version: 3.0 +Contact: iss_storagedev@hp.com +Description: Value of "simple" indicates that the controller has been placed + in "simple mode". Value of "performant" indicates that the + controller has been placed in "performant mode". diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 61f0b5b6a415..6da7edea700a 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -641,6 +641,18 @@ static ssize_t host_store_rescan(struct device *dev, } static DEVICE_ATTR(rescan, S_IWUSR, NULL, host_store_rescan); +static ssize_t host_show_transport_mode(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ctlr_info *h = to_hba(dev); + + return snprintf(buf, 20, "%s\n", + h->transMethod & CFGTBL_Trans_Performant ? + "performant" : "simple"); +} +static DEVICE_ATTR(transport_mode, S_IRUGO, host_show_transport_mode, NULL); + static ssize_t dev_show_unique_id(struct device *dev, struct device_attribute *attr, char *buf) @@ -813,6 +825,7 @@ static DEVICE_ATTR(usage_count, S_IRUGO, cciss_show_usage_count, NULL); static struct attribute *cciss_host_attrs[] = { &dev_attr_rescan.attr, &dev_attr_resettable.attr, + &dev_attr_transport_mode.attr, NULL }; From 90b44f8ffdf6c66d190ee71b330009bf7f11a208 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 25 Jul 2011 19:57:52 +0530 Subject: [PATCH 0007/1531] dmaengine: add helper function for slave_single For clients which require a single slave transfer and dont want to be bothered about the scatterlist api, this helper gives simple API for this transfer and creates single scatterlist for DMA API Idea from Russell King Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 8fbf40e0713c..0d738c95fe4e 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -24,6 +24,7 @@ #include #include #include +#include struct scatterlist; @@ -519,6 +520,16 @@ static inline int dmaengine_slave_config(struct dma_chan *chan, (unsigned long)config); } +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) +{ + struct scatterlist sg; + sg_init_one(&sg, buf, len); + + return chan->device->device_prep_slave_sg(chan, &sg, 1, dir, flags); +} + static inline int dmaengine_terminate_all(struct dma_chan *chan) { return dmaengine_device_control(chan, DMA_TERMINATE_ALL, 0); From 4920e05c79417f9a8d236a044502e10729af91b3 Mon Sep 17 00:00:00 2001 From: Arnaud Lacombe Date: Wed, 3 Aug 2011 21:52:06 -0400 Subject: [PATCH 0008/1531] kconfig/nconf: nuke unreferenced `nohelp_text' After commit 5416857867c9cc94aba641898c567d9707de30f1, nohelp_text' is no longer referenced, nuke it. Signed-off-by: Arnaud Lacombe Signed-off-by: Michal Marek --- scripts/kconfig/nconf.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c index 39ca1f1640ea..b113c50209e3 100644 --- a/scripts/kconfig/nconf.c +++ b/scripts/kconfig/nconf.c @@ -182,8 +182,6 @@ setmod_text[] = N_( "This feature depends on another which\n" "has been configured as a module.\n" "As a result, this feature will be built as a module."), -nohelp_text[] = N_( -"There is no help available for this option.\n"), load_config_text[] = N_( "Enter the name of the configuration file you wish to load.\n" "Accept the name shown to restore the configuration you\n" From 3f198dfee49d2e9c30583c62b0c79286c78c7b44 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 2 Aug 2011 18:49:52 +0100 Subject: [PATCH 0009/1531] kbuild: Fix help text not displayed in choice option. Help text under choice menu is never displayed because it does not have symbol name associated with it, however many kconfigs have help text under choice, assuming that it will be displayed when user selects help. for example in Kconfig if we have: choice prompt "Choice" ---help--- HELP TEXT ... config A bool "A" config B bool "B" endchoice Without this patch "HELP TEXT" is not displayed when user selects help option when "Choice" is highlighted from menuconfig or xconfig or gconfig. This patch changes the logic in menu_get_ext_help to display help for cases which dont have symbol names like choice. Signed-off-by: Srinivas Kandagatla Reviewed-by: Stuart Menefy Reviewed-by: Arnaud Lacombe Cc: stable@kernel.org Signed-off-by: Michal Marek --- scripts/kconfig/menu.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index d66008639a43..beeb92e75f8f 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -597,11 +597,10 @@ void menu_get_ext_help(struct menu *menu, struct gstr *help) struct symbol *sym = menu->sym; if (menu_has_help(menu)) { - if (sym->name) { + if (sym->name) str_printf(help, "%s%s:\n\n", CONFIG_, sym->name); - str_append(help, _(menu_get_help(menu))); - str_append(help, "\n"); - } + str_append(help, _(menu_get_help(menu))); + str_append(help, "\n"); } else { str_append(help, nohelp_text); } From 9704efaa52ab18eb3504c4e0bc421c1d01b7981a Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 29 Jul 2011 16:21:57 +0530 Subject: [PATCH 0010/1531] dmaengine/dmatest: Terminate transfers on all channels in case of error or exit In case, some error occurs while doing memcpy transfers, we must terminate all transfers physically too. This is achieved by calling device_control() routine with TERMINATE_ALL as parameter. This is also required to be done in case module is removed while we are in middle of some transfers. Signed-off-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dmatest.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index 765f5ff22304..accc18441b16 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -477,6 +477,8 @@ static int dmatest_func(void *data) pr_notice("%s: terminating after %u tests, %u failures (status %d)\n", thread_name, total_tests, failed_tests, ret); + /* terminate all transfers on specified channels */ + chan->device->device_control(chan, DMA_TERMINATE_ALL, 0); if (iterations > 0) while (!kthread_should_stop()) { DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wait_dmatest_exit); @@ -499,6 +501,10 @@ static void dmatest_cleanup_channel(struct dmatest_chan *dtc) list_del(&thread->node); kfree(thread); } + + /* terminate all transfers on specified channels */ + dtc->chan->device->device_control(dtc->chan, DMA_TERMINATE_ALL, 0); + kfree(dtc); } From 57e6292da67c30acf92f08604f918192a886838a Mon Sep 17 00:00:00 2001 From: Arnaud Lacombe Date: Wed, 3 Aug 2011 21:52:07 -0400 Subject: [PATCH 0011/1531] kconfig: factor code in menu_get_ext_help() Cc: Srinivas Kandagatla Signed-off-by: Arnaud Lacombe Signed-off-by: Michal Marek --- scripts/kconfig/menu.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index beeb92e75f8f..8c2a97e60faf 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -10,8 +10,7 @@ #include "lkc.h" -static const char nohelp_text[] = N_( - "There is no help available for this option.\n"); +static const char nohelp_text[] = "There is no help available for this option."; struct menu rootmenu; static struct menu **last_entry_ptr; @@ -595,15 +594,14 @@ struct gstr get_relations_str(struct symbol **sym_arr) void menu_get_ext_help(struct menu *menu, struct gstr *help) { struct symbol *sym = menu->sym; + const char *help_text = nohelp_text; if (menu_has_help(menu)) { if (sym->name) str_printf(help, "%s%s:\n\n", CONFIG_, sym->name); - str_append(help, _(menu_get_help(menu))); - str_append(help, "\n"); - } else { - str_append(help, nohelp_text); + help_text = menu_get_help(menu); } + str_printf(help, "%s\n", _(help_text)); if (sym) get_symbol_str(help, sym); } From f4b129f519f1bbd191dca2bf17d1137edf941fd1 Mon Sep 17 00:00:00 2001 From: Michal Marek Date: Wed, 10 Aug 2011 13:10:20 +0200 Subject: [PATCH 0012/1531] kbuild: Do not delete empty files in make distclean Commit 3d64b44 introduced an empty file under arch/arm/mach-zynq/board_dt.c. While this was not intended and the file was removed from the tree by a later commit, we really should only match junk files by known name patterns and not their size. Reported-by: David Howells Signed-off-by: Michal Marek --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b4ca4e111c9a..d714838d0a3a 100644 --- a/Makefile +++ b/Makefile @@ -1198,7 +1198,7 @@ distclean: mrproper @find $(srctree) $(RCS_FIND_IGNORE) \ \( -name '*.orig' -o -name '*.rej' -o -name '*~' \ -o -name '*.bak' -o -name '#*#' -o -name '.*.orig' \ - -o -name '.*.rej' -o -size 0 \ + -o -name '.*.rej' \ -o -name '*%' -o -name '.*.cmd' -o -name 'core' \) \ -type f -print | xargs rm -f From d5d4e02874537d65ed2f431d39bf2f6d2967bd1b Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Thu, 11 Aug 2011 09:27:54 -0700 Subject: [PATCH 0013/1531] [IA64] sn2: add missing put_cpu() sn_hwperf_op_cpu() uses get_cpu() but is missing a corresponding put_cpu(). Reported-by: Thomas Meyer Acked-by: Jack Steiner Signed-off-by: Tony Luck --- arch/ia64/sn/kernel/sn2/sn_hwperf.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/arch/ia64/sn/kernel/sn2/sn_hwperf.c b/arch/ia64/sn/kernel/sn2/sn_hwperf.c index 30862c0358cd..2de41d44266e 100644 --- a/arch/ia64/sn/kernel/sn2/sn_hwperf.c +++ b/arch/ia64/sn/kernel/sn2/sn_hwperf.c @@ -615,11 +615,15 @@ static int sn_hwperf_op_cpu(struct sn_hwperf_op_info *op_info) } } - if (cpu == SN_HWPERF_ARG_ANY_CPU || cpu == get_cpu()) { - /* don't care, or already on correct cpu */ + if (cpu == SN_HWPERF_ARG_ANY_CPU) { + /* don't care which cpu */ sn_hwperf_call_sal(op_info); - } - else { + } else if (cpu == get_cpu()) { + /* already on correct cpu */ + sn_hwperf_call_sal(op_info); + put_cpu(); + } else { + put_cpu(); if (use_ipi) { /* use an interprocessor interrupt to call SAL */ smp_call_function_single(cpu, sn_hwperf_call_sal, From 674bfa485554156aa90ce17288712fcb568a42c3 Mon Sep 17 00:00:00 2001 From: Suzuki Poulose Date: Mon, 18 Jul 2011 03:29:20 +0000 Subject: [PATCH 0014/1531] powerpc/44x: Kexec support for PPC440X chipsets This patch adds kexec support for PPC440 based chipsets. This work is based on the KEXEC patches for FSL BookE. The FSL BookE patch and the code flow could be found at the link below: http://patchwork.ozlabs.org/patch/49359/ Steps: 1) Invalidate all the TLB entries except the one this code is run from 2) Create a tmp mapping for our code in the other address space and jump to it 3) Invalidate the entry we used 4) Create a 1:1 mapping for 0-2GiB in blocks of 256M 5) Jump to the new 1:1 mapping and invalidate the tmp mapping I have tested this patches on Ebony, Sequoia boards and Virtex on QEMU. You need kexec-tools commit e8b7939b1e or newer for ppc440x support, available at: git://git.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools.git Signed-off-by: Suzuki Poulose Cc: Sebastian Andrzej Siewior Signed-off-by: Josh Boyer --- arch/powerpc/Kconfig | 2 +- arch/powerpc/include/asm/kexec.h | 2 +- arch/powerpc/kernel/misc_32.S | 171 +++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 6926b61acfea..0a3d5560c9be 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -345,7 +345,7 @@ config ARCH_ENABLE_MEMORY_HOTREMOVE config KEXEC bool "kexec system call (EXPERIMENTAL)" - depends on (PPC_BOOK3S || FSL_BOOKE) && EXPERIMENTAL + depends on (PPC_BOOK3S || FSL_BOOKE || (44x && !SMP && !47x)) && EXPERIMENTAL help kexec is a system call that implements the ability to shutdown your current kernel, and to start another kernel. It is like a reboot diff --git a/arch/powerpc/include/asm/kexec.h b/arch/powerpc/include/asm/kexec.h index 8a33698c61bd..f921eb121d39 100644 --- a/arch/powerpc/include/asm/kexec.h +++ b/arch/powerpc/include/asm/kexec.h @@ -2,7 +2,7 @@ #define _ASM_POWERPC_KEXEC_H #ifdef __KERNEL__ -#ifdef CONFIG_FSL_BOOKE +#if defined(CONFIG_FSL_BOOKE) || defined(CONFIG_44x) /* * On FSL-BookE we setup a 1:1 mapping which covers the first 2GiB of memory diff --git a/arch/powerpc/kernel/misc_32.S b/arch/powerpc/kernel/misc_32.S index 998a10028608..f7d760ab5ca1 100644 --- a/arch/powerpc/kernel/misc_32.S +++ b/arch/powerpc/kernel/misc_32.S @@ -8,6 +8,8 @@ * kexec bits: * Copyright (C) 2002-2003 Eric Biederman * GameCube/ppc32 port Copyright (C) 2004 Albert Herranz + * PPC44x port. Copyright (C) 2011, IBM Corporation + * Author: Suzuki Poulose * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -735,6 +737,175 @@ relocate_new_kernel: mr r4, r30 mr r5, r31 + li r0, 0 +#elif defined(CONFIG_44x) && !defined(CONFIG_47x) + +/* + * Code for setting up 1:1 mapping for PPC440x for KEXEC + * + * We cannot switch off the MMU on PPC44x. + * So we: + * 1) Invalidate all the mappings except the one we are running from. + * 2) Create a tmp mapping for our code in the other address space(TS) and + * jump to it. Invalidate the entry we started in. + * 3) Create a 1:1 mapping for 0-2GiB in chunks of 256M in original TS. + * 4) Jump to the 1:1 mapping in original TS. + * 5) Invalidate the tmp mapping. + * + * - Based on the kexec support code for FSL BookE + * - Doesn't support 47x yet. + * + */ + /* Save our parameters */ + mr r29, r3 + mr r30, r4 + mr r31, r5 + + /* Load our MSR_IS and TID to MMUCR for TLB search */ + mfspr r3,SPRN_PID + mfmsr r4 + andi. r4,r4,MSR_IS@l + beq wmmucr + oris r3,r3,PPC44x_MMUCR_STS@h +wmmucr: + mtspr SPRN_MMUCR,r3 + sync + + /* + * Invalidate all the TLB entries except the current entry + * where we are running from + */ + bl 0f /* Find our address */ +0: mflr r5 /* Make it accessible */ + tlbsx r23,0,r5 /* Find entry we are in */ + li r4,0 /* Start at TLB entry 0 */ + li r3,0 /* Set PAGEID inval value */ +1: cmpw r23,r4 /* Is this our entry? */ + beq skip /* If so, skip the inval */ + tlbwe r3,r4,PPC44x_TLB_PAGEID /* If not, inval the entry */ +skip: + addi r4,r4,1 /* Increment */ + cmpwi r4,64 /* Are we done? */ + bne 1b /* If not, repeat */ + isync + + /* Create a temp mapping and jump to it */ + andi. r6, r23, 1 /* Find the index to use */ + addi r24, r6, 1 /* r24 will contain 1 or 2 */ + + mfmsr r9 /* get the MSR */ + rlwinm r5, r9, 27, 31, 31 /* Extract the MSR[IS] */ + xori r7, r5, 1 /* Use the other address space */ + + /* Read the current mapping entries */ + tlbre r3, r23, PPC44x_TLB_PAGEID + tlbre r4, r23, PPC44x_TLB_XLAT + tlbre r5, r23, PPC44x_TLB_ATTRIB + + /* Save our current XLAT entry */ + mr r25, r4 + + /* Extract the TLB PageSize */ + li r10, 1 /* r10 will hold PageSize */ + rlwinm r11, r3, 0, 24, 27 /* bits 24-27 */ + + /* XXX: As of now we use 256M, 4K pages */ + cmpwi r11, PPC44x_TLB_256M + bne tlb_4k + rotlwi r10, r10, 28 /* r10 = 256M */ + b write_out +tlb_4k: + cmpwi r11, PPC44x_TLB_4K + bne default + rotlwi r10, r10, 12 /* r10 = 4K */ + b write_out +default: + rotlwi r10, r10, 10 /* r10 = 1K */ + +write_out: + /* + * Write out the tmp 1:1 mapping for this code in other address space + * Fixup EPN = RPN , TS=other address space + */ + insrwi r3, r7, 1, 23 /* Bit 23 is TS for PAGEID field */ + + /* Write out the tmp mapping entries */ + tlbwe r3, r24, PPC44x_TLB_PAGEID + tlbwe r4, r24, PPC44x_TLB_XLAT + tlbwe r5, r24, PPC44x_TLB_ATTRIB + + subi r11, r10, 1 /* PageOffset Mask = PageSize - 1 */ + not r10, r11 /* Mask for PageNum */ + + /* Switch to other address space in MSR */ + insrwi r9, r7, 1, 26 /* Set MSR[IS] = r7 */ + + bl 1f +1: mflr r8 + addi r8, r8, (2f-1b) /* Find the target offset */ + + /* Jump to the tmp mapping */ + mtspr SPRN_SRR0, r8 + mtspr SPRN_SRR1, r9 + rfi + +2: + /* Invalidate the entry we were executing from */ + li r3, 0 + tlbwe r3, r23, PPC44x_TLB_PAGEID + + /* attribute fields. rwx for SUPERVISOR mode */ + li r5, 0 + ori r5, r5, (PPC44x_TLB_SW | PPC44x_TLB_SR | PPC44x_TLB_SX | PPC44x_TLB_G) + + /* Create 1:1 mapping in 256M pages */ + xori r7, r7, 1 /* Revert back to Original TS */ + + li r8, 0 /* PageNumber */ + li r6, 3 /* TLB Index, start at 3 */ + +next_tlb: + rotlwi r3, r8, 28 /* Create EPN (bits 0-3) */ + mr r4, r3 /* RPN = EPN */ + ori r3, r3, (PPC44x_TLB_VALID | PPC44x_TLB_256M) /* SIZE = 256M, Valid */ + insrwi r3, r7, 1, 23 /* Set TS from r7 */ + + tlbwe r3, r6, PPC44x_TLB_PAGEID /* PageID field : EPN, V, SIZE */ + tlbwe r4, r6, PPC44x_TLB_XLAT /* Address translation : RPN */ + tlbwe r5, r6, PPC44x_TLB_ATTRIB /* Attributes */ + + addi r8, r8, 1 /* Increment PN */ + addi r6, r6, 1 /* Increment TLB Index */ + cmpwi r8, 8 /* Are we done ? */ + bne next_tlb + isync + + /* Jump to the new mapping 1:1 */ + li r9,0 + insrwi r9, r7, 1, 26 /* Set MSR[IS] = r7 */ + + bl 1f +1: mflr r8 + and r8, r8, r11 /* Get our offset within page */ + addi r8, r8, (2f-1b) + + and r5, r25, r10 /* Get our target PageNum */ + or r8, r8, r5 /* Target jump address */ + + mtspr SPRN_SRR0, r8 + mtspr SPRN_SRR1, r9 + rfi +2: + /* Invalidate the tmp entry we used */ + li r3, 0 + tlbwe r3, r24, PPC44x_TLB_PAGEID + sync + + /* Restore the parameters */ + mr r3, r29 + mr r4, r30 + mr r5, r31 + li r0, 0 #else li r0, 0 From 2f6bab96ef60dd87e0a17272f9f8a6ee12118d32 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Mon, 18 Jul 2011 03:49:14 +0000 Subject: [PATCH 0015/1531] powerpc/44x: Add NOR flash device to Yosemite dts Signed-off-by: Stefan Roese Signed-off-by: Josh Boyer --- arch/powerpc/boot/dts/yosemite.dts | 36 ++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/arch/powerpc/boot/dts/yosemite.dts b/arch/powerpc/boot/dts/yosemite.dts index 64923245f0e5..30bb4753577a 100644 --- a/arch/powerpc/boot/dts/yosemite.dts +++ b/arch/powerpc/boot/dts/yosemite.dts @@ -138,6 +138,42 @@ clock-frequency = <0>; /* Filled in by zImage */ interrupts = <0x5 0x1>; interrupt-parent = <&UIC1>; + + nor_flash@0,0 { + compatible = "amd,s29gl256n", "cfi-flash"; + bank-width = <2>; + reg = <0x00000000 0x00000000 0x04000000>; + #address-cells = <1>; + #size-cells = <1>; + partition@0 { + label = "kernel"; + reg = <0x00000000 0x001e0000>; + }; + partition@1e0000 { + label = "dtb"; + reg = <0x001e0000 0x00020000>; + }; + partition@200000 { + label = "ramdisk"; + reg = <0x00200000 0x01400000>; + }; + partition@1600000 { + label = "jffs2"; + reg = <0x01600000 0x00400000>; + }; + partition@1a00000 { + label = "user"; + reg = <0x01a00000 0x02540000>; + }; + partition@3f40000 { + label = "env"; + reg = <0x03f40000 0x00040000>; + }; + partition@3f80000 { + label = "u-boot"; + reg = <0x03f80000 0x00080000>; + }; + }; }; UART0: serial@ef600300 { From e7fa1d131472b1134d33aba56a83f8f7689f54fd Mon Sep 17 00:00:00 2001 From: Ayman El-Khashab Date: Wed, 20 Jul 2011 03:02:29 +0000 Subject: [PATCH 0016/1531] powerpc/4xx: enable and fix pcie gen1/gen2 on the 460sx Adds a register to the config space for the 460sx. Changes the vc0 detect to a pll detect. maps configuration space to test the link status. changes the setup to enable gen2 devices to operate at gen2 speeds. fixes mapping that was not correct for the 460sx. added bit definitions for the OMRxMSKL registers. Removed reserved bit that was set incorrectly in the OMR2MSKL register. tested on the 460sx eiger and custom board Signed-off-by: Ayman El-Khashab Signed-off-by: Josh Boyer --- arch/powerpc/sysdev/ppc4xx_pci.c | 89 ++++++++++++++++++++++++++------ arch/powerpc/sysdev/ppc4xx_pci.h | 12 +++++ 2 files changed, 84 insertions(+), 17 deletions(-) diff --git a/arch/powerpc/sysdev/ppc4xx_pci.c b/arch/powerpc/sysdev/ppc4xx_pci.c index a59ba96d2c21..edd51c85835a 100644 --- a/arch/powerpc/sysdev/ppc4xx_pci.c +++ b/arch/powerpc/sysdev/ppc4xx_pci.c @@ -1091,6 +1091,10 @@ static int __init ppc460sx_pciex_core_init(struct device_node *np) mtdcri(SDR0, PESDR1_460SX_HSSSLEW, 0xFFFF0000); mtdcri(SDR0, PESDR2_460SX_HSSSLEW, 0xFFFF0000); + /* Set HSS PRBS enabled */ + mtdcri(SDR0, PESDR0_460SX_HSSCTLSET, 0x00001130); + mtdcri(SDR0, PESDR2_460SX_HSSCTLSET, 0x00001130); + udelay(100); /* De-assert PLLRESET */ @@ -1131,9 +1135,6 @@ static int ppc460sx_pciex_init_port_hw(struct ppc4xx_pciex_port *port) dcri_clrset(SDR0, port->sdr_base + PESDRn_UTLSET2, 0, 0x01000000); - /*Gen-1*/ - mtdcri(SDR0, port->sdr_base + PESDRn_460SX_RCEI, 0x08000000); - dcri_clrset(SDR0, port->sdr_base + PESDRn_RCSSET, (PESDRx_RCSSET_RSTGU | PESDRx_RCSSET_RSTDL), PESDRx_RCSSET_RSTPYN); @@ -1147,14 +1148,42 @@ static int ppc460sx_pciex_init_utl(struct ppc4xx_pciex_port *port) { /* Max 128 Bytes */ out_be32 (port->utl_base + PEUTL_PBBSZ, 0x00000000); + /* Assert VRB and TXE - per datasheet turn off addr validation */ + out_be32(port->utl_base + PEUTL_PCTL, 0x80800000); return 0; } +static void __init ppc460sx_pciex_check_link(struct ppc4xx_pciex_port *port) +{ + void __iomem *mbase; + int attempt = 50; + + port->link = 0; + + mbase = ioremap(port->cfg_space.start + 0x10000000, 0x1000); + if (mbase == NULL) { + printk(KERN_ERR "%s: Can't map internal config space !", + port->node->full_name); + goto done; + } + + while (attempt && (0 == (in_le32(mbase + PECFG_460SX_DLLSTA) + & PECFG_460SX_DLLSTA_LINKUP))) { + attempt--; + mdelay(10); + } + if (attempt) + port->link = 1; +done: + iounmap(mbase); + +} + static struct ppc4xx_pciex_hwops ppc460sx_pcie_hwops __initdata = { .core_init = ppc460sx_pciex_core_init, .port_init_hw = ppc460sx_pciex_init_port_hw, .setup_utl = ppc460sx_pciex_init_utl, - .check_link = ppc4xx_pciex_check_link_sdr, + .check_link = ppc460sx_pciex_check_link, }; #endif /* CONFIG_44x */ @@ -1337,15 +1366,15 @@ static int __init ppc4xx_pciex_port_init(struct ppc4xx_pciex_port *port) if (rc != 0) return rc; - if (ppc4xx_pciex_hwops->check_link) - ppc4xx_pciex_hwops->check_link(port); - /* * Initialize mapping: disable all regions and configure * CFG and REG regions based on resources in the device tree */ ppc4xx_pciex_port_init_mapping(port); + if (ppc4xx_pciex_hwops->check_link) + ppc4xx_pciex_hwops->check_link(port); + /* * Map UTL */ @@ -1359,13 +1388,23 @@ static int __init ppc4xx_pciex_port_init(struct ppc4xx_pciex_port *port) ppc4xx_pciex_hwops->setup_utl(port); /* - * Check for VC0 active and assert RDY. + * Check for VC0 active or PLL Locked and assert RDY. */ if (port->sdr_base) { - if (port->link && - ppc4xx_pciex_wait_on_sdr(port, PESDRn_RCSSTS, - 1 << 16, 1 << 16, 5000)) { - printk(KERN_INFO "PCIE%d: VC0 not active\n", port->index); + if (of_device_is_compatible(port->node, + "ibm,plb-pciex-460sx")){ + if (port->link && ppc4xx_pciex_wait_on_sdr(port, + PESDRn_RCSSTS, + 1 << 12, 1 << 12, 5000)) { + printk(KERN_INFO "PCIE%d: PLL not locked\n", + port->index); + port->link = 0; + } + } else if (port->link && + ppc4xx_pciex_wait_on_sdr(port, PESDRn_RCSSTS, + 1 << 16, 1 << 16, 5000)) { + printk(KERN_INFO "PCIE%d: VC0 not active\n", + port->index); port->link = 0; } @@ -1572,8 +1611,15 @@ static int __init ppc4xx_setup_one_pciex_POM(struct ppc4xx_pciex_port *port, dcr_write(port->dcrs, DCRO_PEGPL_OMR1BAH, lah); dcr_write(port->dcrs, DCRO_PEGPL_OMR1BAL, lal); dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKH, 0x7fffffff); - /* Note that 3 here means enabled | single region */ - dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKL, sa | 3); + /*Enabled and single region */ + if (of_device_is_compatible(port->node, "ibm,plb-pciex-460sx")) + dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKL, + sa | DCRO_PEGPL_460SX_OMR1MSKL_UOT + | DCRO_PEGPL_OMRxMSKL_VAL); + else + dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKL, + sa | DCRO_PEGPL_OMR1MSKL_UOT + | DCRO_PEGPL_OMRxMSKL_VAL); break; case 1: out_le32(mbase + PECFG_POM1LAH, pciah); @@ -1581,8 +1627,8 @@ static int __init ppc4xx_setup_one_pciex_POM(struct ppc4xx_pciex_port *port, dcr_write(port->dcrs, DCRO_PEGPL_OMR2BAH, lah); dcr_write(port->dcrs, DCRO_PEGPL_OMR2BAL, lal); dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKH, 0x7fffffff); - /* Note that 3 here means enabled | single region */ - dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKL, sa | 3); + dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKL, + sa | DCRO_PEGPL_OMRxMSKL_VAL); break; case 2: out_le32(mbase + PECFG_POM2LAH, pciah); @@ -1591,7 +1637,9 @@ static int __init ppc4xx_setup_one_pciex_POM(struct ppc4xx_pciex_port *port, dcr_write(port->dcrs, DCRO_PEGPL_OMR3BAL, lal); dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKH, 0x7fffffff); /* Note that 3 here means enabled | IO space !!! */ - dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKL, sa | 3); + dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKL, + sa | DCRO_PEGPL_OMR3MSKL_IO + | DCRO_PEGPL_OMRxMSKL_VAL); break; } @@ -1692,6 +1740,9 @@ static void __init ppc4xx_configure_pciex_PIMs(struct ppc4xx_pciex_port *port, if (res->flags & IORESOURCE_PREFETCH) sa |= 0x8; + if (of_device_is_compatible(port->node, "ibm,plb-pciex-460sx")) + sa |= PCI_BASE_ADDRESS_MEM_TYPE_64; + out_le32(mbase + PECFG_BAR0HMPA, RES_TO_U32_HIGH(sa)); out_le32(mbase + PECFG_BAR0LMPA, RES_TO_U32_LOW(sa)); @@ -1853,6 +1904,10 @@ static void __init ppc4xx_pciex_port_setup_hose(struct ppc4xx_pciex_port *port) } out_le16(mbase + 0x202, val); + /* Enable Bus master, memory, and io space */ + if (of_device_is_compatible(port->node, "ibm,plb-pciex-460sx")) + out_le16(mbase + 0x204, 0x7); + if (!port->endpoint) { /* Set Class Code to PCI-PCI bridge and Revision Id to 1 */ out_le32(mbase + 0x208, 0x06040001); diff --git a/arch/powerpc/sysdev/ppc4xx_pci.h b/arch/powerpc/sysdev/ppc4xx_pci.h index c39a134e8684..32ce763a375a 100644 --- a/arch/powerpc/sysdev/ppc4xx_pci.h +++ b/arch/powerpc/sysdev/ppc4xx_pci.h @@ -464,6 +464,18 @@ #define PECFG_POM2LAL 0x390 #define PECFG_POM2LAH 0x394 +/* 460sx only */ +#define PECFG_460SX_DLLSTA 0x3f8 + +/* 460sx Bit Mappings */ +#define PECFG_460SX_DLLSTA_LINKUP 0x00000010 +#define DCRO_PEGPL_460SX_OMR1MSKL_UOT 0x00000004 + +/* PEGPL Bit Mappings */ +#define DCRO_PEGPL_OMRxMSKL_VAL 0x00000001 +#define DCRO_PEGPL_OMR1MSKL_UOT 0x00000002 +#define DCRO_PEGPL_OMR3MSKL_IO 0x00000002 + /* SDR Bit Mappings */ #define PESDRx_RCSSET_HLDPLB 0x10000000 #define PESDRx_RCSSET_RSTGU 0x01000000 From e57b708beaf1bc14031cb9749b574c9aa1f6188f Mon Sep 17 00:00:00 2001 From: Mike Williams Date: Thu, 7 Jul 2011 06:09:58 +0000 Subject: [PATCH 0017/1531] powerpc/4xx: edac: Add comma to fix build error Commit 4018294b53d1dae026880e45f174c1cc63b5d435 broke the ppc4xx_edac driver at line 210 where the struct member is missing a comma. Signed-off-by: Mike Williams Signed-off-by: Josh Boyer --- drivers/edac/ppc4xx_edac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/edac/ppc4xx_edac.c b/drivers/edac/ppc4xx_edac.c index 0de7d8770891..38400963e245 100644 --- a/drivers/edac/ppc4xx_edac.c +++ b/drivers/edac/ppc4xx_edac.c @@ -205,7 +205,7 @@ static struct platform_driver ppc4xx_edac_driver = { .remove = ppc4xx_edac_remove, .driver = { .owner = THIS_MODULE, - .name = PPC4XX_EDAC_MODULE_NAME + .name = PPC4XX_EDAC_MODULE_NAME, .of_match_table = ppc4xx_edac_match, }, }; From 9fcd768d0cc88b41ea459e25d2db12d3e25fa9dd Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Thu, 11 Aug 2011 13:53:44 -0400 Subject: [PATCH 0018/1531] powerpc/40x: Remove obsolete HCU4 board The HCU4 board is unmaintained. Remove it. Signed-off-by: Niklaus Giger Signed-off-by: Josh Boyer --- arch/powerpc/boot/dts/hcu4.dts | 168 ------------------------ arch/powerpc/configs/40x/hcu4_defconfig | 80 ----------- arch/powerpc/configs/ppc40x_defconfig | 1 - arch/powerpc/platforms/40x/Kconfig | 8 -- arch/powerpc/platforms/40x/Makefile | 1 - arch/powerpc/platforms/40x/hcu4.c | 61 --------- 6 files changed, 319 deletions(-) delete mode 100644 arch/powerpc/boot/dts/hcu4.dts delete mode 100644 arch/powerpc/configs/40x/hcu4_defconfig delete mode 100644 arch/powerpc/platforms/40x/hcu4.c diff --git a/arch/powerpc/boot/dts/hcu4.dts b/arch/powerpc/boot/dts/hcu4.dts deleted file mode 100644 index 7988598da4c9..000000000000 --- a/arch/powerpc/boot/dts/hcu4.dts +++ /dev/null @@ -1,168 +0,0 @@ -/* -* Device Tree Source for Netstal Maschinen HCU4 -* based on the IBM Walnut -* -* Copyright 2008 -* Niklaus Giger -* -* Copyright 2007 IBM Corp. -* Josh Boyer -* -* This file is licensed under the terms of the GNU General Public -* License version 2. This program is licensed "as is" without -* any warranty of any kind, whether express or implied. -*/ - -/dts-v1/; - -/ { - #address-cells = <0x1>; - #size-cells = <0x1>; - model = "netstal,hcu4"; - compatible = "netstal,hcu4"; - dcr-parent = <0x1>; - - aliases { - ethernet0 = "/plb/opb/ethernet@ef600800"; - serial0 = "/plb/opb/serial@ef600300"; - }; - - cpus { - #address-cells = <0x1>; - #size-cells = <0x0>; - - cpu@0 { - device_type = "cpu"; - model = "PowerPC,405GPr"; - reg = <0x0>; - clock-frequency = <0>; /* Filled in by U-Boot */ - timebase-frequency = <0x0>; /* Filled in by U-Boot */ - i-cache-line-size = <0x20>; - d-cache-line-size = <0x20>; - i-cache-size = <0x4000>; - d-cache-size = <0x4000>; - dcr-controller; - dcr-access-method = "native"; - linux,phandle = <0x1>; - }; - }; - - memory { - device_type = "memory"; - reg = <0x0 0x0>; /* Filled in by U-Boot */ - }; - - UIC0: interrupt-controller { - compatible = "ibm,uic"; - interrupt-controller; - cell-index = <0x0>; - dcr-reg = <0xc0 0x9>; - #address-cells = <0x0>; - #size-cells = <0x0>; - #interrupt-cells = <0x2>; - linux,phandle = <0x2>; - }; - - plb { - compatible = "ibm,plb3"; - #address-cells = <0x1>; - #size-cells = <0x1>; - ranges; - clock-frequency = <0x0>; /* Filled in by U-Boot */ - - SDRAM0: memory-controller { - compatible = "ibm,sdram-405gp"; - dcr-reg = <0x10 0x2>; - }; - - MAL: mcmal { - compatible = "ibm,mcmal-405gp", "ibm,mcmal"; - dcr-reg = <0x180 0x62>; - num-tx-chans = <0x1>; - num-rx-chans = <0x1>; - interrupt-parent = <0x2>; - interrupts = <0xb 0x4 0xc 0x4 0xa 0x4 0xd 0x4 0xe 0x4>; - linux,phandle = <0x3>; - }; - - POB0: opb { - compatible = "ibm,opb-405gp", "ibm,opb"; - #address-cells = <0x1>; - #size-cells = <0x1>; - ranges = <0xef600000 0xef600000 0xa00000>; - dcr-reg = <0xa0 0x5>; - clock-frequency = <0x0>; /* Filled in by U-Boot */ - - UART0: serial@ef600300 { - device_type = "serial"; - compatible = "ns16550"; - reg = <0xef600300 0x8>; - virtual-reg = <0xef600300>; - clock-frequency = <0x0>;/* Filled in by U-Boot */ - current-speed = <0>; /* Filled in by U-Boot */ - interrupt-parent = <0x2>; - interrupts = <0x0 0x4>; - }; - - IIC: i2c@ef600500 { - compatible = "ibm,iic-405gp", "ibm,iic"; - reg = <0xef600500 0x11>; - interrupt-parent = <0x2>; - interrupts = <0x2 0x4>; - }; - - GPIO: gpio@ef600700 { - compatible = "ibm,gpio-405gp"; - reg = <0xef600700 0x20>; - }; - - EMAC: ethernet@ef600800 { - device_type = "network"; - compatible = "ibm,emac-405gp", "ibm,emac"; - interrupt-parent = <0x2>; - interrupts = <0xf 0x4 0x9 0x4>; - local-mac-address = [00 00 00 00 00 00]; - reg = <0xef600800 0x70>; - mal-device = <0x3>; - mal-tx-channel = <0x0>; - mal-rx-channel = <0x0>; - cell-index = <0x0>; - max-frame-size = <0x5dc>; - rx-fifo-size = <0x1000>; - tx-fifo-size = <0x800>; - phy-mode = "rmii"; - phy-map = <0x1>; - }; - }; - - EBC0: ebc { - compatible = "ibm,ebc-405gp", "ibm,ebc"; - dcr-reg = <0x12 0x2>; - #address-cells = <0x2>; - #size-cells = <0x1>; - clock-frequency = <0x0>; /* Filled in by U-Boot */ - - sram@0,0 { - reg = <0x0 0x0 0x80000>; - }; - - flash@0,80000 { - compatible = "jedec-flash"; - bank-width = <0x1>; - reg = <0x0 0x80000 0x80000>; - #address-cells = <0x1>; - #size-cells = <0x1>; - - partition@0 { - label = "OpenBIOS"; - reg = <0x0 0x80000>; - read-only; - }; - }; - }; - }; - - chosen { - linux,stdout-path = "/plb/opb/serial@ef600300"; - }; -}; diff --git a/arch/powerpc/configs/40x/hcu4_defconfig b/arch/powerpc/configs/40x/hcu4_defconfig deleted file mode 100644 index ebeb4accad65..000000000000 --- a/arch/powerpc/configs/40x/hcu4_defconfig +++ /dev/null @@ -1,80 +0,0 @@ -CONFIG_40x=y -CONFIG_EXPERIMENTAL=y -CONFIG_SYSVIPC=y -CONFIG_POSIX_MQUEUE=y -CONFIG_LOG_BUF_SHIFT=14 -CONFIG_BLK_DEV_INITRD=y -# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -CONFIG_EXPERT=y -CONFIG_KALLSYMS_ALL=y -CONFIG_KALLSYMS_EXTRA_PASS=y -CONFIG_MODULES=y -CONFIG_MODULE_UNLOAD=y -# CONFIG_BLK_DEV_BSG is not set -CONFIG_HCU4=y -# CONFIG_WALNUT is not set -CONFIG_SPARSE_IRQ=y -CONFIG_PCI=y -CONFIG_NET=y -CONFIG_PACKET=y -CONFIG_UNIX=y -CONFIG_INET=y -CONFIG_IP_PNP=y -CONFIG_IP_PNP_DHCP=y -CONFIG_IP_PNP_BOOTP=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_UEVENT_HELPER_PATH="/sbin/hotplug" -CONFIG_CONNECTOR=y -CONFIG_MTD=y -CONFIG_MTD_PARTITIONS=y -CONFIG_MTD_CMDLINE_PARTS=y -CONFIG_MTD_OF_PARTS=y -CONFIG_MTD_CHAR=y -CONFIG_MTD_BLOCK=m -CONFIG_MTD_CFI=y -CONFIG_MTD_JEDECPROBE=y -CONFIG_MTD_CFI_AMDSTD=y -CONFIG_MTD_PHYSMAP_OF=y -CONFIG_PROC_DEVICETREE=y -CONFIG_BLK_DEV_RAM=y -CONFIG_BLK_DEV_RAM_SIZE=35000 -CONFIG_NETDEVICES=y -CONFIG_NET_ETHERNET=y -CONFIG_IBM_NEW_EMAC=y -# CONFIG_INPUT is not set -# CONFIG_SERIO is not set -# CONFIG_VT is not set -CONFIG_SERIAL_8250=y -CONFIG_SERIAL_8250_CONSOLE=y -CONFIG_SERIAL_8250_EXTENDED=y -CONFIG_SERIAL_8250_SHARE_IRQ=y -CONFIG_SERIAL_OF_PLATFORM=y -# CONFIG_HW_RANDOM is not set -# CONFIG_HWMON is not set -CONFIG_VIDEO_OUTPUT_CONTROL=m -# CONFIG_USB_SUPPORT is not set -CONFIG_EXT2_FS=y -CONFIG_INOTIFY=y -CONFIG_PROC_KCORE=y -CONFIG_TMPFS=y -CONFIG_CRAMFS=y -CONFIG_NFS_FS=y -CONFIG_NFS_V3=y -CONFIG_ROOT_NFS=y -CONFIG_MAGIC_SYSRQ=y -CONFIG_DEBUG_FS=y -CONFIG_DEBUG_KERNEL=y -CONFIG_DETECT_HUNG_TASK=y -# CONFIG_RCU_CPU_STALL_DETECTOR is not set -CONFIG_SYSCTL_SYSCALL_CHECK=y -CONFIG_CRYPTO=y -CONFIG_CRYPTO_CBC=y -CONFIG_CRYPTO_ECB=y -CONFIG_CRYPTO_PCBC=y -CONFIG_CRYPTO_MD5=y -CONFIG_CRYPTO_DES=y -# CONFIG_CRYPTO_ANSI_CPRNG is not set diff --git a/arch/powerpc/configs/ppc40x_defconfig b/arch/powerpc/configs/ppc40x_defconfig index bfd634b5ada7..e9d920c5a87a 100644 --- a/arch/powerpc/configs/ppc40x_defconfig +++ b/arch/powerpc/configs/ppc40x_defconfig @@ -14,7 +14,6 @@ CONFIG_MODULE_UNLOAD=y CONFIG_PPC4xx_GPIO=y CONFIG_ACADIA=y CONFIG_EP405=y -CONFIG_HCU4=y CONFIG_HOTFOOT=y CONFIG_KILAUEA=y CONFIG_MAKALU=y diff --git a/arch/powerpc/platforms/40x/Kconfig b/arch/powerpc/platforms/40x/Kconfig index d733d7ca939c..ae5e0bfc0234 100644 --- a/arch/powerpc/platforms/40x/Kconfig +++ b/arch/powerpc/platforms/40x/Kconfig @@ -32,14 +32,6 @@ config EP405 help This option enables support for the EP405/EP405PC boards. -config HCU4 - bool "Hcu4" - depends on 40x - default n - select 405GPR - help - This option enables support for the Nestal Maschinen HCU4 board. - config HOTFOOT bool "Hotfoot" depends on 40x diff --git a/arch/powerpc/platforms/40x/Makefile b/arch/powerpc/platforms/40x/Makefile index 56e89004c468..88c22de0c850 100644 --- a/arch/powerpc/platforms/40x/Makefile +++ b/arch/powerpc/platforms/40x/Makefile @@ -1,4 +1,3 @@ -obj-$(CONFIG_HCU4) += hcu4.o obj-$(CONFIG_WALNUT) += walnut.o obj-$(CONFIG_XILINX_VIRTEX_GENERIC_BOARD) += virtex.o obj-$(CONFIG_EP405) += ep405.o diff --git a/arch/powerpc/platforms/40x/hcu4.c b/arch/powerpc/platforms/40x/hcu4.c deleted file mode 100644 index 60b2afecab75..000000000000 --- a/arch/powerpc/platforms/40x/hcu4.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Architecture- / platform-specific boot-time initialization code for - * IBM PowerPC 4xx based boards. Adapted from original - * code by Gary Thomas, Cort Dougan , and Dan Malek - * . - * - * Copyright(c) 1999-2000 Grant Erickson - * - * Rewritten and ported to the merged powerpc tree: - * Copyright 2007 IBM Corporation - * Josh Boyer - * - * 2002 (c) MontaVista, Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ - -#include -#include - -#include -#include -#include -#include -#include -#include - -static __initdata struct of_device_id hcu4_of_bus[] = { - { .compatible = "ibm,plb3", }, - { .compatible = "ibm,opb", }, - { .compatible = "ibm,ebc", }, - {}, -}; - -static int __init hcu4_device_probe(void) -{ - of_platform_bus_probe(NULL, hcu4_of_bus, NULL); - return 0; -} -machine_device_initcall(hcu4, hcu4_device_probe); - -static int __init hcu4_probe(void) -{ - unsigned long root = of_get_flat_dt_root(); - - if (!of_flat_dt_is_compatible(root, "netstal,hcu4")) - return 0; - - return 1; -} - -define_machine(hcu4) { - .name = "HCU4", - .probe = hcu4_probe, - .progress = udbg_progress, - .init_IRQ = uic_init_tree, - .get_irq = uic_get_irq, - .restart = ppc4xx_reset_system, - .calibrate_decr = generic_calibrate_decr, -}; From a16e470caa173d323ef68dcac98c899b95fa4f84 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Tue, 9 Aug 2011 10:08:10 +0530 Subject: [PATCH 0019/1531] dmaengine: remove struct scatterlist for header Commit 90b44f8 introduces dmaengine_prep_slave_single API which adds scatterlist.h in dmaengine.h, so defining struct scatterlist is not required Signed-off-by: Vinod Koul Acked-by: Dan Williams --- include/linux/dmaengine.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 0d738c95fe4e..ace51af4369f 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -26,8 +26,6 @@ #include #include -struct scatterlist; - /** * typedef dma_cookie_t - an opaque DMA cookie * From ef298c21c0d9c06ed89ea2fa724c3a018acfff39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lothar=20Wa=C3=9Fmann?= Date: Mon, 8 Aug 2011 14:47:47 +0200 Subject: [PATCH 0020/1531] mxs-dma: enable CLKGATE before accessing registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After calling mxs_dma_disable_chan() for a channel, that channel becomes unusable because some controller registers can only be written when the clock is enabled via CLKGATE. Signed-off-by: Lothar Waßmann Signed-off-by: Vinod Koul --- drivers/dma/mxs-dma.c | 45 +++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index be641cbd36fc..b4588bdd98bb 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -130,6 +130,23 @@ 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; @@ -148,38 +165,21 @@ 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)); - /* 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 + MXS_CLR_ADDR); - else - writel(1 << chan_id, - mxs_dma->base + HW_APBHX_CTRL0 + MXS_CLR_ADDR); - } - /* write 1 to SEMA to kick off the channel */ writel(1, mxs_dma->base + HW_APBHX_CHn_SEMA(chan_id)); } static void mxs_dma_disable_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; - /* disable 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 + MXS_SET_ADDR); - else - writel(1 << chan_id, - mxs_dma->base + HW_APBHX_CTRL0 + MXS_SET_ADDR); - } + mxs_dma_clkgate(mxs_chan, 0); mxs_chan->status = DMA_SUCCESS; } @@ -338,7 +338,10 @@ 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; From 5a0143a4f00517ea433bf459a80742ccc623a665 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 28 Jul 2011 17:47:10 +0200 Subject: [PATCH 0021/1531] ext3: Remove i_mutex from ext3_sync_file() ext3_sync_file() does not need i_mutex for anything so just drop it. Signed-off-by: Jan Kara --- fs/ext3/fsync.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/fs/ext3/fsync.c b/fs/ext3/fsync.c index d494c554c6e6..1860ed356323 100644 --- a/fs/ext3/fsync.c +++ b/fs/ext3/fsync.c @@ -61,13 +61,6 @@ int ext3_sync_file(struct file *file, loff_t start, loff_t end, int datasync) if (ret) goto out; - /* - * Taking the mutex here just to keep consistent with how fsync was - * called previously, however it looks like we don't need to take - * i_mutex at all. - */ - mutex_lock(&inode->i_mutex); - J_ASSERT(ext3_journal_current_handle() == NULL); /* @@ -85,7 +78,6 @@ int ext3_sync_file(struct file *file, loff_t start, loff_t end, int datasync) * safe in-journal, which is all fsync() needs to ensure. */ if (ext3_should_journal_data(inode)) { - mutex_unlock(&inode->i_mutex); ret = ext3_force_commit(inode->i_sb); goto out; } @@ -108,8 +100,6 @@ int ext3_sync_file(struct file *file, loff_t start, loff_t end, int datasync) */ if (needs_barrier) blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL); - - mutex_unlock(&inode->i_mutex); out: trace_ext3_sync_file_exit(inode, ret); return ret; From 1cde201da4e97f10a5dd2434cff4ceff381603d1 Mon Sep 17 00:00:00 2001 From: Toshiyuki Okajima Date: Tue, 2 Aug 2011 18:16:57 +0900 Subject: [PATCH 0022/1531] ext3: fix message in ext3_remount for rw-remount case If there are some inodes in orphan list while a filesystem is being read-only mounted, we should recommend that peole umount and then mount it when they try to remount with read-write. But the current message and comment recommend that they umount and then remount it which may be slightly misleading. Signed-off-by: Toshiyuki Okajima Signed-off-by: Jan Kara --- fs/ext3/super.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/ext3/super.c b/fs/ext3/super.c index 7beb69ae0015..2043bcc87719 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -2669,13 +2669,13 @@ static int ext3_remount (struct super_block * sb, int * flags, char * data) /* * If we have an unprocessed orphan list hanging * around from a previously readonly bdev mount, - * require a full umount/remount for now. + * require a full umount & mount for now. */ if (es->s_last_orphan) { ext3_msg(sb, KERN_WARNING, "warning: couldn't " "remount RDWR because of unprocessed " "orphan inode list. Please " - "umount/remount instead."); + "umount & mount instead."); err = -EINVAL; goto restore_opts; } From 6e3d6ca0bf91bcce0453fff9b597154ff6bb9731 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 4 Aug 2011 12:29:32 +0200 Subject: [PATCH 0023/1531] fs/ext2/balloc.c: delete useless initialization Delete nontrivial initialization that is immediately overwritten by the result of an allocation function. The semantic match that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ type T; identifier i; expression e; @@ ( T i = \(0\|NULL\|ERR_PTR(...)\); | -T i = e; +T i; ) ... when != i i = \(kzalloc\|kcalloc\|kmalloc\)(...); // Signed-off-by: Julia Lawall Signed-off-by: Jan Kara --- fs/ext2/balloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 8f44cef1b3ef..a8cbe1bc6ad4 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -421,7 +421,7 @@ static inline int rsv_is_empty(struct ext2_reserve_window *rsv) void ext2_init_block_alloc_info(struct inode *inode) { struct ext2_inode_info *ei = EXT2_I(inode); - struct ext2_block_alloc_info *block_i = ei->i_block_alloc_info; + struct ext2_block_alloc_info *block_i; struct super_block *sb = inode->i_sb; block_i = kmalloc(sizeof(*block_i), GFP_NOFS); From 46130222df8567ffde773216044c7611a1e71d51 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 4 Aug 2011 12:29:31 +0200 Subject: [PATCH 0024/1531] fs/ext3/balloc.c: delete useless initialization Delete nontrivial initialization that is immediately overwritten by the result of an allocation function. The semantic match that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ type T; identifier i; expression e; @@ ( T i = \(0\|NULL\|ERR_PTR(...)\); | -T i = e; +T i; ) ... when != i i = \(kzalloc\|kcalloc\|kmalloc\)(...); // Signed-off-by: Julia Lawall Signed-off-by: Jan Kara --- fs/ext3/balloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ext3/balloc.c b/fs/ext3/balloc.c index 6386d76f44a7..caedc00520ed 100644 --- a/fs/ext3/balloc.c +++ b/fs/ext3/balloc.c @@ -427,7 +427,7 @@ static inline int rsv_is_empty(struct ext3_reserve_window *rsv) void ext3_init_block_alloc_info(struct inode *inode) { struct ext3_inode_info *ei = EXT3_I(inode); - struct ext3_block_alloc_info *block_i = ei->i_block_alloc_info; + struct ext3_block_alloc_info *block_i; struct super_block *sb = inode->i_sb; block_i = kmalloc(sizeof(*block_i), GFP_NOFS); From fbc854027c91fa2813ae7f9de43cc0b5c1119f41 Mon Sep 17 00:00:00 2001 From: Lukas Czerner Date: Tue, 16 Aug 2011 18:08:06 +0200 Subject: [PATCH 0025/1531] ext3: remove deprecated oldalloc For a long time now orlov is the default block allocator in the ext3. It performs better than the old one and no one seems to claim otherwise so we can safely drop it and make oldalloc and orlov mount option deprecated. Signed-off-by: Lukas Czerner Signed-off-by: Jan Kara --- Documentation/filesystems/ext3.txt | 8 ------ fs/ext3/ialloc.c | 45 ++---------------------------- fs/ext3/super.c | 8 +++--- include/linux/ext3_fs.h | 2 +- 4 files changed, 8 insertions(+), 55 deletions(-) diff --git a/Documentation/filesystems/ext3.txt b/Documentation/filesystems/ext3.txt index 22f3a0eda1d2..b100adc38adb 100644 --- a/Documentation/filesystems/ext3.txt +++ b/Documentation/filesystems/ext3.txt @@ -73,14 +73,6 @@ nobarrier (*) This also requires an IO stack which can support also be used to enable or disable barriers, for consistency with other ext3 mount options. -orlov (*) This enables the new Orlov block allocator. It is - enabled by default. - -oldalloc This disables the Orlov block allocator and enables - the old block allocator. Orlov should have better - performance - we'd like to get some feedback if it's - the contrary for you. - user_xattr Enables Extended User Attributes. Additionally, you need to have extended attribute support enabled in the kernel configuration (CONFIG_EXT3_FS_XATTR). See the diff --git a/fs/ext3/ialloc.c b/fs/ext3/ialloc.c index bf09cbf938cc..635bd8ce6d59 100644 --- a/fs/ext3/ialloc.c +++ b/fs/ext3/ialloc.c @@ -177,42 +177,6 @@ void ext3_free_inode (handle_t *handle, struct inode * inode) ext3_std_error(sb, fatal); } -/* - * There are two policies for allocating an inode. If the new inode is - * a directory, then a forward search is made for a block group with both - * free space and a low directory-to-inode ratio; if that fails, then of - * the groups with above-average free space, that group with the fewest - * directories already is chosen. - * - * For other inodes, search forward from the parent directory\'s block - * group to find a free inode. - */ -static int find_group_dir(struct super_block *sb, struct inode *parent) -{ - int ngroups = EXT3_SB(sb)->s_groups_count; - unsigned int freei, avefreei; - struct ext3_group_desc *desc, *best_desc = NULL; - int group, best_group = -1; - - freei = percpu_counter_read_positive(&EXT3_SB(sb)->s_freeinodes_counter); - avefreei = freei / ngroups; - - for (group = 0; group < ngroups; group++) { - desc = ext3_get_group_desc (sb, group, NULL); - if (!desc || !desc->bg_free_inodes_count) - continue; - if (le16_to_cpu(desc->bg_free_inodes_count) < avefreei) - continue; - if (!best_desc || - (le16_to_cpu(desc->bg_free_blocks_count) > - le16_to_cpu(best_desc->bg_free_blocks_count))) { - best_group = group; - best_desc = desc; - } - } - return best_group; -} - /* * Orlov's allocator for directories. * @@ -436,12 +400,9 @@ struct inode *ext3_new_inode(handle_t *handle, struct inode * dir, sbi = EXT3_SB(sb); es = sbi->s_es; - if (S_ISDIR(mode)) { - if (test_opt (sb, OLDALLOC)) - group = find_group_dir(sb, dir); - else - group = find_group_orlov(sb, dir); - } else + if (S_ISDIR(mode)) + group = find_group_orlov(sb, dir); + else group = find_group_other(sb, dir); err = -ENOSPC; diff --git a/fs/ext3/super.c b/fs/ext3/super.c index 2043bcc87719..922d289aeeb3 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -652,8 +652,6 @@ static int ext3_show_options(struct seq_file *seq, struct vfsmount *vfs) seq_puts(seq, ",nouid32"); if (test_opt(sb, DEBUG)) seq_puts(seq, ",debug"); - if (test_opt(sb, OLDALLOC)) - seq_puts(seq, ",oldalloc"); #ifdef CONFIG_EXT3_FS_XATTR if (test_opt(sb, XATTR_USER)) seq_puts(seq, ",user_xattr"); @@ -1049,10 +1047,12 @@ static int parse_options (char *options, struct super_block *sb, set_opt (sbi->s_mount_opt, DEBUG); break; case Opt_oldalloc: - set_opt (sbi->s_mount_opt, OLDALLOC); + ext3_msg(sb, KERN_WARNING, + "Ignoring deprecated oldalloc option"); break; case Opt_orlov: - clear_opt (sbi->s_mount_opt, OLDALLOC); + ext3_msg(sb, KERN_WARNING, + "Ignoring deprecated orlov option"); break; #ifdef CONFIG_EXT3_FS_XATTR case Opt_user_xattr: diff --git a/include/linux/ext3_fs.h b/include/linux/ext3_fs.h index 67a803aee619..96a30b95e5c2 100644 --- a/include/linux/ext3_fs.h +++ b/include/linux/ext3_fs.h @@ -381,7 +381,7 @@ struct ext3_inode { * Mount flags */ #define EXT3_MOUNT_CHECK 0x00001 /* Do mount-time checks */ -#define EXT3_MOUNT_OLDALLOC 0x00002 /* Don't use the new Orlov allocator */ +/* EXT3_MOUNT_OLDALLOC was there */ #define EXT3_MOUNT_GRPID 0x00004 /* Create files with directory's group */ #define EXT3_MOUNT_DEBUG 0x00008 /* Some debugging messages */ #define EXT3_MOUNT_ERRORS_CONT 0x00010 /* Continue on errors */ From 2c929420e8b532c14688dcd500253b90a24e7233 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Thu, 18 Aug 2011 19:24:54 +0900 Subject: [PATCH 0026/1531] ARM: EXYNOS4: use dma-pl330 device name for clock This change replaces s3c-pl330.x clock device names with dma-pl330.x, otherwise there won't be a correspondence between clock device name and amba device name, thus clocks can't be enabled. Fixes runtime errors on clk_get() from drivers/dma/pl330.c: dma-pl330 dma-pl330.0: Cannot get operation clock. dma-pl330: probe of dma-pl330.0 failed with error -22 Signed-off-by: Vladimir Zapolskiy Acked-by: Boojin Kim Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos4/clock.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-exynos4/clock.c b/arch/arm/mach-exynos4/clock.c index 851dea018578..c7b622587229 100644 --- a/arch/arm/mach-exynos4/clock.c +++ b/arch/arm/mach-exynos4/clock.c @@ -455,12 +455,12 @@ static struct clk init_clocks_off[] = { .ctrlbit = (1 << 10), }, { .name = "pdma", - .devname = "s3c-pl330.0", + .devname = "dma-pl330.0", .enable = exynos4_clk_ip_fsys_ctrl, .ctrlbit = (1 << 0), }, { .name = "pdma", - .devname = "s3c-pl330.1", + .devname = "dma-pl330.1", .enable = exynos4_clk_ip_fsys_ctrl, .ctrlbit = (1 << 1), }, { From d20cc4c1b660d59a25f8bebef5d55c1872b9aa76 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Thu, 18 Aug 2011 19:24:55 +0900 Subject: [PATCH 0027/1531] ARM: S5PC100: use dma-pl330 device name for clock This change replaces s3c-pl330.x clock device names with dma-pl330.x, otherwise there won't be a correspondence between clock device name and amba device name, thus clocks can't be enabled. Fixes runtime errors on clk_get() from drivers/dma/pl330.c: dma-pl330 dma-pl330.0: Cannot get operation clock. dma-pl330: probe of dma-pl330.0 failed with error -22 Signed-off-by: Vladimir Zapolskiy Acked-by: Boojin Kim Signed-off-by: Kukjin Kim --- arch/arm/mach-s5pc100/clock.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-s5pc100/clock.c b/arch/arm/mach-s5pc100/clock.c index ff5cbb30de5b..1f69f342a0a0 100644 --- a/arch/arm/mach-s5pc100/clock.c +++ b/arch/arm/mach-s5pc100/clock.c @@ -455,13 +455,13 @@ static struct clk init_clocks_off[] = { .ctrlbit = (1 << 2), }, { .name = "pdma", - .devname = "s3c-pl330.1", + .devname = "dma-pl330.1", .parent = &clk_div_d1_bus.clk, .enable = s5pc100_d1_0_ctrl, .ctrlbit = (1 << 1), }, { .name = "pdma", - .devname = "s3c-pl330.0", + .devname = "dma-pl330.0", .parent = &clk_div_d1_bus.clk, .enable = s5pc100_d1_0_ctrl, .ctrlbit = (1 << 0), From 1ce3ea61c61c1e2127496df2c5d66650fd6456e2 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Thu, 18 Aug 2011 19:24:55 +0900 Subject: [PATCH 0028/1531] ARM: S5PV210: use dma-pl330 device name for clock This change replaces s3c-pl330.x clock device names with dma-pl330.x, otherwise there won't be a correspondence between clock device name and amba device name, thus clocks can't be enabled. Fixes runtime errors on clk_get() from drivers/dma/pl330.c: dma-pl330 dma-pl330.0: Cannot get operation clock. dma-pl330: probe of dma-pl330.0 failed with error -22 Signed-off-by: Vladimir Zapolskiy Acked-by: Boojin Kim Signed-off-by: Kukjin Kim --- arch/arm/mach-s5pv210/clock.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-s5pv210/clock.c b/arch/arm/mach-s5pv210/clock.c index 52a8e607bcc2..50d458047c09 100644 --- a/arch/arm/mach-s5pv210/clock.c +++ b/arch/arm/mach-s5pv210/clock.c @@ -290,13 +290,13 @@ static struct clk_ops clk_fout_apll_ops = { static struct clk init_clocks_off[] = { { .name = "pdma", - .devname = "s3c-pl330.0", + .devname = "dma-pl330.0", .parent = &clk_hclk_psys.clk, .enable = s5pv210_clk_ip0_ctrl, .ctrlbit = (1 << 3), }, { .name = "pdma", - .devname = "s3c-pl330.1", + .devname = "dma-pl330.1", .parent = &clk_hclk_psys.clk, .enable = s5pv210_clk_ip0_ctrl, .ctrlbit = (1 << 4), From 2213d0c0bae510df3860a48e107a5ba17ac72e99 Mon Sep 17 00:00:00 2001 From: Boojin Kim Date: Thu, 18 Aug 2011 19:24:55 +0900 Subject: [PATCH 0029/1531] ARM: S5P64X0: Add the devname for DMA clock This patch adds devname for DMA clock. NOTE: This patch should be added after merging new pl330 driver on dmaengine. Signed-off-by: Boojin Kim Signed-off-by: Kukjin Kim --- arch/arm/mach-s5p64x0/clock-s5p6440.c | 1 + arch/arm/mach-s5p64x0/clock-s5p6450.c | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/arm/mach-s5p64x0/clock-s5p6440.c b/arch/arm/mach-s5p64x0/clock-s5p6440.c index 0e9cd3092dd2..fd7b08fd262b 100644 --- a/arch/arm/mach-s5p64x0/clock-s5p6440.c +++ b/arch/arm/mach-s5p64x0/clock-s5p6440.c @@ -147,6 +147,7 @@ static struct clk init_clocks_off[] = { .ctrlbit = (1 << 8), }, { .name = "pdma", + .devname = "dma-pl330", .parent = &clk_hclk_low.clk, .enable = s5p64x0_hclk0_ctrl, .ctrlbit = (1 << 12), diff --git a/arch/arm/mach-s5p64x0/clock-s5p6450.c b/arch/arm/mach-s5p64x0/clock-s5p6450.c index d9dc16cde109..1d989b8da039 100644 --- a/arch/arm/mach-s5p64x0/clock-s5p6450.c +++ b/arch/arm/mach-s5p64x0/clock-s5p6450.c @@ -180,6 +180,7 @@ static struct clk init_clocks_off[] = { .ctrlbit = (1 << 3), }, { .name = "pdma", + .devname = "dma-pl330", .parent = &clk_hclk_low.clk, .enable = s5p64x0_hclk0_ctrl, .ctrlbit = (1 << 12), From 25ac0c2b971235d3e8c7af0b6889a1eb6988b559 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Fri, 19 Aug 2011 14:48:17 +0200 Subject: [PATCH 0030/1531] nbd: use task_pid_nr() to get current pid Signed-off-by: WANG Cong Cc: Paul Clements Signed-off-by: Andrew Morton Signed-off-by: Jens Axboe --- drivers/block/nbd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index f533f3375e24..a928287177fd 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -405,7 +405,7 @@ static int nbd_do_it(struct nbd_device *lo) BUG_ON(lo->magic != LO_MAGIC); - lo->pid = current->pid; + lo->pid = task_pid_nr(current); ret = sysfs_create_file(&disk_to_dev(lo->disk)->kobj, &pid_attr.attr); if (ret) { printk(KERN_ERR "nbd: sysfs_create_file failed!"); From 1695b87f7dd152b866f0dd867c8e599025fc4965 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Fri, 19 Aug 2011 14:48:21 +0200 Subject: [PATCH 0031/1531] nbd: replace sysfs_create_file() with device_create_file() Signed-off-by: WANG Cong Cc: Paul Clements Signed-off-by: Andrew Morton Signed-off-by: Jens Axboe --- drivers/block/nbd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index a928287177fd..da98360b6463 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -406,9 +406,9 @@ static int nbd_do_it(struct nbd_device *lo) BUG_ON(lo->magic != LO_MAGIC); lo->pid = task_pid_nr(current); - ret = sysfs_create_file(&disk_to_dev(lo->disk)->kobj, &pid_attr.attr); + ret = device_create_file(disk_to_dev(lo->disk), &pid_attr); if (ret) { - printk(KERN_ERR "nbd: sysfs_create_file failed!"); + printk(KERN_ERR "nbd: device_create_file failed!"); lo->pid = 0; return ret; } @@ -416,7 +416,7 @@ static int nbd_do_it(struct nbd_device *lo) while ((req = nbd_read_stat(lo)) != NULL) nbd_end_request(req); - sysfs_remove_file(&disk_to_dev(lo->disk)->kobj, &pid_attr.attr); + device_remove_file(disk_to_dev(lo->disk), &pid_attr); lo->pid = 0; return 0; } From 7f1b90f99a2d4253f8eb1221d39da072178adbc5 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Fri, 19 Aug 2011 14:48:22 +0200 Subject: [PATCH 0032/1531] nbd: replace printk KERN_ERR with dev_err() Signed-off-by: WANG Cong Cc: Paul Clements Signed-off-by: Andrew Morton Signed-off-by: Jens Axboe --- drivers/block/nbd.c | 50 ++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index da98360b6463..1b8e09fffd5a 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -158,8 +158,9 @@ static int sock_xmit(struct nbd_device *lo, int send, void *buf, int size, sigset_t blocked, oldset; if (unlikely(!sock)) { - printk(KERN_ERR "%s: Attempted %s on closed socket in sock_xmit\n", - lo->disk->disk_name, (send ? "send" : "recv")); + dev_err(disk_to_dev(lo->disk), + "Attempted %s on closed socket in sock_xmit\n", + (send ? "send" : "recv")); return -EINVAL; } @@ -250,8 +251,8 @@ static int nbd_send_req(struct nbd_device *lo, struct request *req) result = sock_xmit(lo, 1, &request, sizeof(request), (nbd_cmd(req) == NBD_CMD_WRITE) ? MSG_MORE : 0); if (result <= 0) { - printk(KERN_ERR "%s: Send control failed (result %d)\n", - lo->disk->disk_name, result); + dev_err(disk_to_dev(lo->disk), + "Send control failed (result %d)\n", result); goto error_out; } @@ -270,8 +271,9 @@ static int nbd_send_req(struct nbd_device *lo, struct request *req) lo->disk->disk_name, req, bvec->bv_len); result = sock_send_bvec(lo, bvec, flags); if (result <= 0) { - printk(KERN_ERR "%s: Send data failed (result %d)\n", - lo->disk->disk_name, result); + dev_err(disk_to_dev(lo->disk), + "Send data failed (result %d)\n", + result); goto error_out; } } @@ -328,14 +330,13 @@ static struct request *nbd_read_stat(struct nbd_device *lo) reply.magic = 0; result = sock_xmit(lo, 0, &reply, sizeof(reply), MSG_WAITALL); if (result <= 0) { - printk(KERN_ERR "%s: Receive control failed (result %d)\n", - lo->disk->disk_name, result); + dev_err(disk_to_dev(lo->disk), + "Receive control failed (result %d)\n", result); goto harderror; } if (ntohl(reply.magic) != NBD_REPLY_MAGIC) { - printk(KERN_ERR "%s: Wrong magic (0x%lx)\n", - lo->disk->disk_name, + dev_err(disk_to_dev(lo->disk), "Wrong magic (0x%lx)\n", (unsigned long)ntohl(reply.magic)); result = -EPROTO; goto harderror; @@ -347,15 +348,15 @@ static struct request *nbd_read_stat(struct nbd_device *lo) if (result != -ENOENT) goto harderror; - printk(KERN_ERR "%s: Unexpected reply (%p)\n", - lo->disk->disk_name, reply.handle); + dev_err(disk_to_dev(lo->disk), "Unexpected reply (%p)\n", + reply.handle); result = -EBADR; goto harderror; } if (ntohl(reply.error)) { - printk(KERN_ERR "%s: Other side returned error (%d)\n", - lo->disk->disk_name, ntohl(reply.error)); + dev_err(disk_to_dev(lo->disk), "Other side returned error (%d)\n", + ntohl(reply.error)); req->errors++; return req; } @@ -369,8 +370,8 @@ static struct request *nbd_read_stat(struct nbd_device *lo) rq_for_each_segment(bvec, req, iter) { result = sock_recv_bvec(lo, bvec); if (result <= 0) { - printk(KERN_ERR "%s: Receive data failed (result %d)\n", - lo->disk->disk_name, result); + dev_err(disk_to_dev(lo->disk), "Receive data failed (result %d)\n", + result); req->errors++; return req; } @@ -408,7 +409,7 @@ static int nbd_do_it(struct nbd_device *lo) lo->pid = task_pid_nr(current); ret = device_create_file(disk_to_dev(lo->disk), &pid_attr); if (ret) { - printk(KERN_ERR "nbd: device_create_file failed!"); + dev_err(disk_to_dev(lo->disk), "device_create_file failed!\n"); lo->pid = 0; return ret; } @@ -457,8 +458,8 @@ static void nbd_handle_req(struct nbd_device *lo, struct request *req) if (rq_data_dir(req) == WRITE) { nbd_cmd(req) = NBD_CMD_WRITE; if (lo->flags & NBD_READ_ONLY) { - printk(KERN_ERR "%s: Write on read-only\n", - lo->disk->disk_name); + dev_err(disk_to_dev(lo->disk), + "Write on read-only\n"); goto error_out; } } @@ -468,16 +469,15 @@ static void nbd_handle_req(struct nbd_device *lo, struct request *req) mutex_lock(&lo->tx_lock); if (unlikely(!lo->sock)) { mutex_unlock(&lo->tx_lock); - printk(KERN_ERR "%s: Attempted send on closed socket\n", - lo->disk->disk_name); + dev_err(disk_to_dev(lo->disk), + "Attempted send on closed socket\n"); goto error_out; } lo->active_req = req; if (nbd_send_req(lo, req) != 0) { - printk(KERN_ERR "%s: Request send failed\n", - lo->disk->disk_name); + dev_err(disk_to_dev(lo->disk), "Request send failed\n"); req->errors++; nbd_end_request(req); } else { @@ -549,8 +549,8 @@ static void do_nbd_request(struct request_queue *q) BUG_ON(lo->magic != LO_MAGIC); if (unlikely(!lo->sock)) { - printk(KERN_ERR "%s: Attempted send on closed socket\n", - lo->disk->disk_name); + dev_err(disk_to_dev(lo->disk), + "Attempted send on closed socket\n"); req->errors++; nbd_end_request(req); spin_lock_irq(q->queue_lock); From 7742ce4ab49976851ce7f0185dcbe491935371a2 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Fri, 19 Aug 2011 14:48:28 +0200 Subject: [PATCH 0033/1531] nbd: lower the loglevel of an error message This is only an error, no need to use KERN_CRIT log level. Signed-off-by: WANG Cong Cc: Paul Clements Signed-off-by: Andrew Morton Signed-off-by: Jens Axboe --- drivers/block/nbd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 1b8e09fffd5a..2b5fc11be377 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -745,7 +745,7 @@ static int __init nbd_init(void) BUILD_BUG_ON(sizeof(struct nbd_request) != 28); if (max_part < 0) { - printk(KERN_CRIT "nbd: max_part must be >= 0\n"); + printk(KERN_ERR "nbd: max_part must be >= 0\n"); return -EINVAL; } From 5eedf5415cd57f8db8642a5db4cf8e5507390030 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Fri, 19 Aug 2011 14:48:28 +0200 Subject: [PATCH 0034/1531] nbd: replace some printk with dev_warn() and dev_info() Signed-off-by: WANG Cong Cc: Paul Clements Signed-off-by: Andrew Morton Signed-off-by: Jens Axboe --- drivers/block/nbd.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 2b5fc11be377..3353d98f81d6 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -127,8 +127,7 @@ static void sock_shutdown(struct nbd_device *lo, int lock) if (lock) mutex_lock(&lo->tx_lock); if (lo->sock) { - printk(KERN_WARNING "%s: shutting down socket\n", - lo->disk->disk_name); + dev_warn(disk_to_dev(lo->disk), "shutting down socket\n"); kernel_sock_shutdown(lo->sock, SHUT_RDWR); lo->sock = NULL; } @@ -576,7 +575,7 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *lo, case NBD_DISCONNECT: { struct request sreq; - printk(KERN_INFO "%s: NBD_DISCONNECT\n", lo->disk->disk_name); + dev_info(disk_to_dev(lo->disk), "NBD_DISCONNECT\n"); blk_rq_init(NULL, &sreq); sreq.cmd_type = REQ_TYPE_SPECIAL; @@ -674,7 +673,7 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *lo, file = lo->file; lo->file = NULL; nbd_clear_que(lo); - printk(KERN_WARNING "%s: queue cleared\n", lo->disk->disk_name); + dev_warn(disk_to_dev(lo->disk), "queue cleared\n"); if (file) fput(file); lo->bytesize = 0; @@ -694,8 +693,8 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *lo, return 0; case NBD_PRINT_DEBUG: - printk(KERN_INFO "%s: next = %p, prev = %p, head = %p\n", - bdev->bd_disk->disk_name, + dev_info(disk_to_dev(lo->disk), + "next = %p, prev = %p, head = %p\n", lo->queue_head.next, lo->queue_head.prev, &lo->queue_head); return 0; From 548ef6cc26ca1c81f19855d57d3fb0f9a7ce3385 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 19 Aug 2011 14:48:28 +0200 Subject: [PATCH 0035/1531] nbd-replace-some-printk-with-dev_warn-and-dev_info-checkpatch-fixes ERROR: code indent should use tabs where possible #30: FILE: drivers/block/nbd.c:578: +^I dev_info(disk_to_dev(lo->disk), "NBD_DISCONNECT\n");$ total: 1 errors, 0 warnings, 35 lines checked NOTE: whitespace errors detected, you may wish to use scripts/cleanpatch or scripts/cleanfile ./patches/nbd-replace-some-printk-with-dev_warn-and-dev_info.patch has style problems, please review. If any of these errors are false positives, please report them to the maintainer, see CHECKPATCH in MAINTAINERS. Please run checkpatch prior to sending patches Cc: Paul Clements Cc: WANG Cong Signed-off-by: Andrew Morton Signed-off-by: Jens Axboe --- drivers/block/nbd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 3353d98f81d6..c3f0ee16594d 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -575,7 +575,7 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *lo, case NBD_DISCONNECT: { struct request sreq; - dev_info(disk_to_dev(lo->disk), "NBD_DISCONNECT\n"); + dev_info(disk_to_dev(lo->disk), "NBD_DISCONNECT\n"); blk_rq_init(NULL, &sreq); sreq.cmd_type = REQ_TYPE_SPECIAL; From dfaa2ef68e80c378e610e3c8c536f1c239e8d3ef Mon Sep 17 00:00:00 2001 From: Lukas Czerner Date: Fri, 19 Aug 2011 14:50:46 +0200 Subject: [PATCH 0036/1531] loop: add discard support for loop devices This commit adds discard support for loop devices. Discard is usually supported by SSD and thinly provisioned devices as a method for reclaiming unused space. This is no different than trying to reclaim back space which is not used by the file system on the image, but it still occupies space on the host file system. We can do the reclamation on file system which does support hole punching. So when discard request gets to the loop driver we can translate that to punch a hole to the underlying file, hence reclaim the free space. This is very useful for trimming down the size of the image to only what is really used by the file system on that image. Fstrim may be used for that purpose. It has been tested on ext4, xfs and btrfs with the image file systems ext4, ext3, xfs and btrfs. ext4, or ext6 image on ext4 file system has some problems but it seems that ext4 punch hole implementation is somewhat flawed and it is unrelated to this commit. Also this is a very good method of validating file systems punch hole implementation. Note that when encryption is used, discard support is disabled, because using it might leak some information useful for possible attacker. Signed-off-by: Lukas Czerner Reviewed-by: Jeff Moyer Signed-off-by: Jens Axboe --- drivers/block/loop.c | 54 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 76c8da78212b..936cac3c3126 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -75,6 +75,7 @@ #include #include #include +#include #include @@ -484,6 +485,29 @@ static int do_bio_filebacked(struct loop_device *lo, struct bio *bio) } } + /* + * We use punch hole to reclaim the free space used by the + * image a.k.a. discard. However we do support discard if + * encryption is enabled, because it may give an attacker + * useful information. + */ + if (bio->bi_rw & REQ_DISCARD) { + struct file *file = lo->lo_backing_file; + int mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE; + + if ((!file->f_op->fallocate) || + lo->lo_encrypt_key_size) { + ret = -EOPNOTSUPP; + goto out; + } + ret = file->f_op->fallocate(file, mode, pos, + bio->bi_size); + if (unlikely(ret && ret != -EINVAL && + ret != -EOPNOTSUPP)) + ret = -EIO; + goto out; + } + ret = lo_send(lo, bio, pos); if ((bio->bi_rw & REQ_FUA) && !ret) { @@ -814,6 +838,35 @@ static void loop_sysfs_exit(struct loop_device *lo) &loop_attribute_group); } +static void loop_config_discard(struct loop_device *lo) +{ + struct file *file = lo->lo_backing_file; + struct inode *inode = file->f_mapping->host; + struct request_queue *q = lo->lo_queue; + + /* + * We use punch hole to reclaim the free space used by the + * image a.k.a. discard. However we do support discard if + * encryption is enabled, because it may give an attacker + * useful information. + */ + if ((!file->f_op->fallocate) || + lo->lo_encrypt_key_size) { + q->limits.discard_granularity = 0; + q->limits.discard_alignment = 0; + q->limits.max_discard_sectors = 0; + q->limits.discard_zeroes_data = 0; + queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, q); + return; + } + + q->limits.discard_granularity = inode->i_sb->s_blocksize; + q->limits.discard_alignment = inode->i_sb->s_blocksize; + q->limits.max_discard_sectors = UINT_MAX >> 9; + q->limits.discard_zeroes_data = 1; + queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q); +} + static int loop_set_fd(struct loop_device *lo, fmode_t mode, struct block_device *bdev, unsigned int arg) { @@ -1090,6 +1143,7 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) if (figure_loop_size(lo)) return -EFBIG; } + loop_config_discard(lo); memcpy(lo->lo_file_name, info->lo_file_name, LO_NAME_SIZE); memcpy(lo->lo_crypt_name, info->lo_crypt_name, LO_NAME_SIZE); From d8cb04b070c2a55f7201714d231cff4f8f9fbd16 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Wed, 27 Jul 2011 12:21:28 +0000 Subject: [PATCH 0037/1531] dmaengine: at_hdmac: replace spin_lock* with irqsave variants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dmaengine routines can be called from interrupt context and with interrupts disabled. Whereas spin_unlock_bh can't be called from such contexts. So this patch converts all spin_lock* routines to irqsave variants. Also, spin_lock() used in tasklet is converted to irqsave variants, as tasklet can be interrupted, and dma requests from such interruptions may also call spin_lock. Idea from dw_dmac patch by Viresh Kumar. Signed-off-by: Nicolas Ferre Signed-off-by: Uwe Kleine-König Signed-off-by: Vinod Koul --- drivers/dma/at_hdmac.c | 52 ++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 6a483eac7b3f..fd87b9690e1b 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -107,10 +107,11 @@ static struct at_desc *atc_desc_get(struct at_dma_chan *atchan) { struct at_desc *desc, *_desc; struct at_desc *ret = NULL; + unsigned long flags; unsigned int i = 0; LIST_HEAD(tmp_list); - spin_lock_bh(&atchan->lock); + spin_lock_irqsave(&atchan->lock, flags); list_for_each_entry_safe(desc, _desc, &atchan->free_list, desc_node) { i++; if (async_tx_test_ack(&desc->txd)) { @@ -121,7 +122,7 @@ static struct at_desc *atc_desc_get(struct at_dma_chan *atchan) dev_dbg(chan2dev(&atchan->chan_common), "desc %p not ACKed\n", desc); } - spin_unlock_bh(&atchan->lock); + spin_unlock_irqrestore(&atchan->lock, flags); dev_vdbg(chan2dev(&atchan->chan_common), "scanned %u descriptors on freelist\n", i); @@ -129,9 +130,9 @@ static struct at_desc *atc_desc_get(struct at_dma_chan *atchan) if (!ret) { ret = atc_alloc_descriptor(&atchan->chan_common, GFP_ATOMIC); if (ret) { - spin_lock_bh(&atchan->lock); + spin_lock_irqsave(&atchan->lock, flags); atchan->descs_allocated++; - spin_unlock_bh(&atchan->lock); + spin_unlock_irqrestore(&atchan->lock, flags); } else { dev_err(chan2dev(&atchan->chan_common), "not enough descriptors available\n"); @@ -150,8 +151,9 @@ static void atc_desc_put(struct at_dma_chan *atchan, struct at_desc *desc) { if (desc) { struct at_desc *child; + unsigned long flags; - spin_lock_bh(&atchan->lock); + spin_lock_irqsave(&atchan->lock, flags); list_for_each_entry(child, &desc->tx_list, desc_node) dev_vdbg(chan2dev(&atchan->chan_common), "moving child desc %p to freelist\n", @@ -160,7 +162,7 @@ static void atc_desc_put(struct at_dma_chan *atchan, struct at_desc *desc) dev_vdbg(chan2dev(&atchan->chan_common), "moving desc %p to freelist\n", desc); list_add(&desc->desc_node, &atchan->free_list); - spin_unlock_bh(&atchan->lock); + spin_unlock_irqrestore(&atchan->lock, flags); } } @@ -471,8 +473,9 @@ static void atc_handle_cyclic(struct at_dma_chan *atchan) static void atc_tasklet(unsigned long data) { struct at_dma_chan *atchan = (struct at_dma_chan *)data; + unsigned long flags; - spin_lock(&atchan->lock); + spin_lock_irqsave(&atchan->lock, flags); if (test_and_clear_bit(ATC_IS_ERROR, &atchan->status)) atc_handle_error(atchan); else if (test_bit(ATC_IS_CYCLIC, &atchan->status)) @@ -480,7 +483,7 @@ static void atc_tasklet(unsigned long data) else atc_advance_work(atchan); - spin_unlock(&atchan->lock); + spin_unlock_irqrestore(&atchan->lock, flags); } static irqreturn_t at_dma_interrupt(int irq, void *dev_id) @@ -539,8 +542,9 @@ static dma_cookie_t atc_tx_submit(struct dma_async_tx_descriptor *tx) struct at_desc *desc = txd_to_at_desc(tx); struct at_dma_chan *atchan = to_at_dma_chan(tx->chan); dma_cookie_t cookie; + unsigned long flags; - spin_lock_bh(&atchan->lock); + spin_lock_irqsave(&atchan->lock, flags); cookie = atc_assign_cookie(atchan, desc); if (list_empty(&atchan->active_list)) { @@ -554,7 +558,7 @@ static dma_cookie_t atc_tx_submit(struct dma_async_tx_descriptor *tx) list_add_tail(&desc->desc_node, &atchan->queue); } - spin_unlock_bh(&atchan->lock); + spin_unlock_irqrestore(&atchan->lock, flags); return cookie; } @@ -927,28 +931,29 @@ static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, struct at_dma_chan *atchan = to_at_dma_chan(chan); struct at_dma *atdma = to_at_dma(chan->device); int chan_id = atchan->chan_common.chan_id; + unsigned long flags; LIST_HEAD(list); dev_vdbg(chan2dev(chan), "atc_control (%d)\n", cmd); if (cmd == DMA_PAUSE) { - spin_lock_bh(&atchan->lock); + spin_lock_irqsave(&atchan->lock, flags); dma_writel(atdma, CHER, AT_DMA_SUSP(chan_id)); set_bit(ATC_IS_PAUSED, &atchan->status); - spin_unlock_bh(&atchan->lock); + spin_unlock_irqrestore(&atchan->lock, flags); } else if (cmd == DMA_RESUME) { if (!test_bit(ATC_IS_PAUSED, &atchan->status)) return 0; - spin_lock_bh(&atchan->lock); + spin_lock_irqsave(&atchan->lock, flags); dma_writel(atdma, CHDR, AT_DMA_RES(chan_id)); clear_bit(ATC_IS_PAUSED, &atchan->status); - spin_unlock_bh(&atchan->lock); + spin_unlock_irqrestore(&atchan->lock, flags); } else if (cmd == DMA_TERMINATE_ALL) { struct at_desc *desc, *_desc; /* @@ -957,7 +962,7 @@ static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, * channel. We still have to poll the channel enable bit due * to AHB/HSB limitations. */ - spin_lock_bh(&atchan->lock); + spin_lock_irqsave(&atchan->lock, flags); /* disabling channel: must also remove suspend state */ dma_writel(atdma, CHDR, AT_DMA_RES(chan_id) | atchan->mask); @@ -978,7 +983,7 @@ static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, /* if channel dedicated to cyclic operations, free it */ clear_bit(ATC_IS_CYCLIC, &atchan->status); - spin_unlock_bh(&atchan->lock); + spin_unlock_irqrestore(&atchan->lock, flags); } else { return -ENXIO; } @@ -1004,9 +1009,10 @@ atc_tx_status(struct dma_chan *chan, struct at_dma_chan *atchan = to_at_dma_chan(chan); dma_cookie_t last_used; dma_cookie_t last_complete; + unsigned long flags; enum dma_status ret; - spin_lock_bh(&atchan->lock); + spin_lock_irqsave(&atchan->lock, flags); last_complete = atchan->completed_cookie; last_used = chan->cookie; @@ -1021,7 +1027,7 @@ atc_tx_status(struct dma_chan *chan, ret = dma_async_is_complete(cookie, last_complete, last_used); } - spin_unlock_bh(&atchan->lock); + spin_unlock_irqrestore(&atchan->lock, flags); if (ret != DMA_SUCCESS) dma_set_tx_state(txstate, last_complete, last_used, @@ -1046,6 +1052,7 @@ atc_tx_status(struct dma_chan *chan, static void atc_issue_pending(struct dma_chan *chan) { struct at_dma_chan *atchan = to_at_dma_chan(chan); + unsigned long flags; dev_vdbg(chan2dev(chan), "issue_pending\n"); @@ -1053,11 +1060,11 @@ static void atc_issue_pending(struct dma_chan *chan) if (test_bit(ATC_IS_CYCLIC, &atchan->status)) return; - spin_lock_bh(&atchan->lock); + spin_lock_irqsave(&atchan->lock, flags); if (!atc_chan_is_enabled(atchan)) { atc_advance_work(atchan); } - spin_unlock_bh(&atchan->lock); + spin_unlock_irqrestore(&atchan->lock, flags); } /** @@ -1073,6 +1080,7 @@ static int atc_alloc_chan_resources(struct dma_chan *chan) struct at_dma *atdma = to_at_dma(chan->device); struct at_desc *desc; struct at_dma_slave *atslave; + unsigned long flags; int i; u32 cfg; LIST_HEAD(tmp_list); @@ -1116,11 +1124,11 @@ static int atc_alloc_chan_resources(struct dma_chan *chan) list_add_tail(&desc->desc_node, &tmp_list); } - spin_lock_bh(&atchan->lock); + spin_lock_irqsave(&atchan->lock, flags); atchan->descs_allocated = i; list_splice(&tmp_list, &atchan->free_list); atchan->completed_cookie = chan->cookie = 1; - spin_unlock_bh(&atchan->lock); + spin_unlock_irqrestore(&atchan->lock, flags); /* channel parameters */ channel_writel(atchan, CFG, cfg); From c0ba5947370a0900b1823922fc4faf41515bc901 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Wed, 27 Jul 2011 12:21:29 +0000 Subject: [PATCH 0038/1531] dmaengine: at_hdmac: improve power management routines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Save/restore dma controller state across a suspend-resume sequence. The prepare() function will wait for the non-cyclic channels to become idle. It also deals with cyclic operations with the start at next period while resuming. Signed-off-by: Nicolas Ferre Signed-off-by: Uwe Kleine-König Signed-off-by: Vinod Koul --- drivers/dma/at_hdmac.c | 88 ++++++++++++++++++++++++++++++++++++- drivers/dma/at_hdmac_regs.h | 7 +++ 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index fd87b9690e1b..0ead008e3bdf 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -1385,27 +1385,113 @@ static void at_dma_shutdown(struct platform_device *pdev) clk_disable(atdma->clk); } +static int at_dma_prepare(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct at_dma *atdma = platform_get_drvdata(pdev); + struct dma_chan *chan, *_chan; + + list_for_each_entry_safe(chan, _chan, &atdma->dma_common.channels, + device_node) { + struct at_dma_chan *atchan = to_at_dma_chan(chan); + /* wait for transaction completion (except in cyclic case) */ + if (atc_chan_is_enabled(atchan) && + !test_bit(ATC_IS_CYCLIC, &atchan->status)) + return -EAGAIN; + } + return 0; +} + +static void atc_suspend_cyclic(struct at_dma_chan *atchan) +{ + struct dma_chan *chan = &atchan->chan_common; + + /* Channel should be paused by user + * do it anyway even if it is not done already */ + if (!test_bit(ATC_IS_PAUSED, &atchan->status)) { + dev_warn(chan2dev(chan), + "cyclic channel not paused, should be done by channel user\n"); + atc_control(chan, DMA_PAUSE, 0); + } + + /* now preserve additional data for cyclic operations */ + /* next descriptor address in the cyclic list */ + atchan->save_dscr = channel_readl(atchan, DSCR); + + vdbg_dump_regs(atchan); +} + static int at_dma_suspend_noirq(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct at_dma *atdma = platform_get_drvdata(pdev); + struct dma_chan *chan, *_chan; - at_dma_off(platform_get_drvdata(pdev)); + /* preserve data */ + list_for_each_entry_safe(chan, _chan, &atdma->dma_common.channels, + device_node) { + struct at_dma_chan *atchan = to_at_dma_chan(chan); + + if (test_bit(ATC_IS_CYCLIC, &atchan->status)) + atc_suspend_cyclic(atchan); + atchan->save_cfg = channel_readl(atchan, CFG); + } + atdma->save_imr = dma_readl(atdma, EBCIMR); + + /* disable DMA controller */ + at_dma_off(atdma); clk_disable(atdma->clk); return 0; } +static void atc_resume_cyclic(struct at_dma_chan *atchan) +{ + struct at_dma *atdma = to_at_dma(atchan->chan_common.device); + + /* restore channel status for cyclic descriptors list: + * next descriptor in the cyclic list at the time of suspend */ + channel_writel(atchan, SADDR, 0); + channel_writel(atchan, DADDR, 0); + channel_writel(atchan, CTRLA, 0); + channel_writel(atchan, CTRLB, 0); + channel_writel(atchan, DSCR, atchan->save_dscr); + dma_writel(atdma, CHER, atchan->mask); + + /* channel pause status should be removed by channel user + * We cannot take the initiative to do it here */ + + vdbg_dump_regs(atchan); +} + static int at_dma_resume_noirq(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct at_dma *atdma = platform_get_drvdata(pdev); + struct dma_chan *chan, *_chan; + /* bring back DMA controller */ clk_enable(atdma->clk); dma_writel(atdma, EN, AT_DMA_ENABLE); + + /* clear any pending interrupt */ + while (dma_readl(atdma, EBCISR)) + cpu_relax(); + + /* restore saved data */ + dma_writel(atdma, EBCIER, atdma->save_imr); + list_for_each_entry_safe(chan, _chan, &atdma->dma_common.channels, + device_node) { + struct at_dma_chan *atchan = to_at_dma_chan(chan); + + channel_writel(atchan, CFG, atchan->save_cfg); + if (test_bit(ATC_IS_CYCLIC, &atchan->status)) + atc_resume_cyclic(atchan); + } return 0; } static const struct dev_pm_ops at_dma_dev_pm_ops = { + .prepare = at_dma_prepare, .suspend_noirq = at_dma_suspend_noirq, .resume_noirq = at_dma_resume_noirq, }; diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h index 087dbf1dd39c..6f0c4a3eb091 100644 --- a/drivers/dma/at_hdmac_regs.h +++ b/drivers/dma/at_hdmac_regs.h @@ -204,6 +204,9 @@ enum atc_status { * @status: transmit status information from irq/prep* functions * to tasklet (use atomic operations) * @tasklet: bottom half to finish transaction work + * @save_cfg: configuration register that is saved on suspend/resume cycle + * @save_dscr: for cyclic operations, preserve next descriptor address in + * the cyclic list on suspend/resume cycle * @lock: serializes enqueue/dequeue operations to descriptors lists * @completed_cookie: identifier for the most recently completed operation * @active_list: list of descriptors dmaengine is being running on @@ -218,6 +221,8 @@ struct at_dma_chan { u8 mask; unsigned long status; struct tasklet_struct tasklet; + u32 save_cfg; + u32 save_dscr; spinlock_t lock; @@ -248,6 +253,7 @@ static inline struct at_dma_chan *to_at_dma_chan(struct dma_chan *dchan) * @chan_common: common dmaengine dma_device object members * @ch_regs: memory mapped register base * @clk: dma controller clock + * @save_imr: interrupt mask register that is saved on suspend/resume cycle * @all_chan_mask: all channels availlable in a mask * @dma_desc_pool: base of DMA descriptor region (DMA address) * @chan: channels table to store at_dma_chan structures @@ -256,6 +262,7 @@ struct at_dma { struct dma_device dma_common; void __iomem *regs; struct clk *clk; + u32 save_imr; u8 all_chan_mask; From 3c477482bb9f976e5451c50be7d3d60ea6f88646 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Mon, 25 Jul 2011 21:09:23 +0000 Subject: [PATCH 0039/1531] dmaengine: at_hdmac: add wrappers for testing channel state Cyclic property and paused state are encoded as bits in the channel status bitfield. Tests of those bits are wrapped in convenient helper functions. Signed-off-by: Nicolas Ferre Signed-off-by: Vinod Koul --- drivers/dma/at_hdmac.c | 19 +++++++++---------- drivers/dma/at_hdmac_regs.h | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 0ead008e3bdf..d774800b9fa4 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -301,7 +301,7 @@ atc_chain_complete(struct at_dma_chan *atchan, struct at_desc *desc) /* for cyclic transfers, * no need to replay callback function while stopping */ - if (!test_bit(ATC_IS_CYCLIC, &atchan->status)) { + if (!atc_chan_is_cyclic(atchan)) { dma_async_tx_callback callback = txd->callback; void *param = txd->callback_param; @@ -478,7 +478,7 @@ static void atc_tasklet(unsigned long data) spin_lock_irqsave(&atchan->lock, flags); if (test_and_clear_bit(ATC_IS_ERROR, &atchan->status)) atc_handle_error(atchan); - else if (test_bit(ATC_IS_CYCLIC, &atchan->status)) + else if (atc_chan_is_cyclic(atchan)) atc_handle_cyclic(atchan); else atc_advance_work(atchan); @@ -945,7 +945,7 @@ static int atc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, spin_unlock_irqrestore(&atchan->lock, flags); } else if (cmd == DMA_RESUME) { - if (!test_bit(ATC_IS_PAUSED, &atchan->status)) + if (!atc_chan_is_paused(atchan)) return 0; spin_lock_irqsave(&atchan->lock, flags); @@ -1035,7 +1035,7 @@ atc_tx_status(struct dma_chan *chan, else dma_set_tx_state(txstate, last_complete, last_used, 0); - if (test_bit(ATC_IS_PAUSED, &atchan->status)) + if (atc_chan_is_paused(atchan)) ret = DMA_PAUSED; dev_vdbg(chan2dev(chan), "tx_status %d: cookie = %d (d%d, u%d)\n", @@ -1057,7 +1057,7 @@ static void atc_issue_pending(struct dma_chan *chan) dev_vdbg(chan2dev(chan), "issue_pending\n"); /* Not needed for cyclic transfers */ - if (test_bit(ATC_IS_CYCLIC, &atchan->status)) + if (atc_chan_is_cyclic(atchan)) return; spin_lock_irqsave(&atchan->lock, flags); @@ -1395,8 +1395,7 @@ static int at_dma_prepare(struct device *dev) device_node) { struct at_dma_chan *atchan = to_at_dma_chan(chan); /* wait for transaction completion (except in cyclic case) */ - if (atc_chan_is_enabled(atchan) && - !test_bit(ATC_IS_CYCLIC, &atchan->status)) + if (atc_chan_is_enabled(atchan) && !atc_chan_is_cyclic(atchan)) return -EAGAIN; } return 0; @@ -1408,7 +1407,7 @@ static void atc_suspend_cyclic(struct at_dma_chan *atchan) /* Channel should be paused by user * do it anyway even if it is not done already */ - if (!test_bit(ATC_IS_PAUSED, &atchan->status)) { + if (!atc_chan_is_paused(atchan)) { dev_warn(chan2dev(chan), "cyclic channel not paused, should be done by channel user\n"); atc_control(chan, DMA_PAUSE, 0); @@ -1432,7 +1431,7 @@ static int at_dma_suspend_noirq(struct device *dev) device_node) { struct at_dma_chan *atchan = to_at_dma_chan(chan); - if (test_bit(ATC_IS_CYCLIC, &atchan->status)) + if (atc_chan_is_cyclic(atchan)) atc_suspend_cyclic(atchan); atchan->save_cfg = channel_readl(atchan, CFG); } @@ -1484,7 +1483,7 @@ static int at_dma_resume_noirq(struct device *dev) struct at_dma_chan *atchan = to_at_dma_chan(chan); channel_writel(atchan, CFG, atchan->save_cfg); - if (test_bit(ATC_IS_CYCLIC, &atchan->status)) + if (atc_chan_is_cyclic(atchan)) atc_resume_cyclic(atchan); } return 0; diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h index 6f0c4a3eb091..aa4c9aebab7c 100644 --- a/drivers/dma/at_hdmac_regs.h +++ b/drivers/dma/at_hdmac_regs.h @@ -362,6 +362,23 @@ static inline int atc_chan_is_enabled(struct at_dma_chan *atchan) return !!(dma_readl(atdma, CHSR) & atchan->mask); } +/** + * atc_chan_is_paused - test channel pause/resume status + * @atchan: channel we want to test status + */ +static inline int atc_chan_is_paused(struct at_dma_chan *atchan) +{ + return test_bit(ATC_IS_PAUSED, &atchan->status); +} + +/** + * atc_chan_is_cyclic - test if given channel has cyclic property set + * @atchan: channel we want to test status + */ +static inline int atc_chan_is_cyclic(struct at_dma_chan *atchan) +{ + return test_bit(ATC_IS_CYCLIC, &atchan->status); +} /** * set_desc_eol - set end-of-link to descriptor so it will end transfer From d7db80801f8117cf210b9e2cd2c800e326d59fa2 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Fri, 5 Aug 2011 11:43:44 +0000 Subject: [PATCH 0040/1531] dmaengine: at_hdmac: fix way to specify cyclic capability In this driver, we can trigger cyclic transfer on peripherals-DMA interfaces. It is dependent on driver implementation but cannot depend on a platform property: we remove the dma_has_cap(DMA_CYCLIC, ) test which has no meaning. Signed-off-by: Nicolas Ferre Signed-off-by: Vinod Koul --- drivers/dma/at_hdmac.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index d774800b9fa4..3b99dc62874b 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -1301,15 +1301,13 @@ static int __init at_dma_probe(struct platform_device *pdev) if (dma_has_cap(DMA_MEMCPY, atdma->dma_common.cap_mask)) atdma->dma_common.device_prep_dma_memcpy = atc_prep_dma_memcpy; - if (dma_has_cap(DMA_SLAVE, atdma->dma_common.cap_mask)) + if (dma_has_cap(DMA_SLAVE, atdma->dma_common.cap_mask)) { atdma->dma_common.device_prep_slave_sg = atc_prep_slave_sg; - - if (dma_has_cap(DMA_CYCLIC, atdma->dma_common.cap_mask)) + /* controller can do slave DMA: can trigger cyclic transfers */ + dma_cap_set(DMA_CYCLIC, atdma->dma_common.cap_mask); atdma->dma_common.device_prep_dma_cyclic = atc_prep_dma_cyclic; - - if (dma_has_cap(DMA_SLAVE, atdma->dma_common.cap_mask) || - dma_has_cap(DMA_CYCLIC, atdma->dma_common.cap_mask)) atdma->dma_common.device_control = atc_control; + } dma_writel(atdma, EN, AT_DMA_ENABLE); From d27769ec3df1a8de9ca450d2dcd72d1ab259ba32 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 23 Aug 2011 20:01:04 +0200 Subject: [PATCH 0041/1531] block: add GENHD_FL_NO_PART_SCAN There are cases where suppressing partition scan is useful - e.g. for lo devices and pseudo SATA devices which advertise to be a disk but get upset on partition scan (some port multiplier control devices show such behavior). This patch adds GENHD_FL_NO_PART_SCAN which suppresses partition scan regardless of the number of possible partitions. disk_partitionable() is renamed to disk_part_scan_enabled() as suppressing partition scan doesn't imply the device can't be partitioned using BLKPG_ADD/DEL_PARTITION calls from userland. show_partition() now directly tests disk_max_parts() to maintain backward-compatibility. -v2: Updated to make it clear that only partition scan is suppressed not partitioning itself as suggested by Kay Sievers. Signed-off-by: Tejun Heo Cc: Kay Sievers Signed-off-by: Jens Axboe --- block/genhd.c | 4 ++-- block/ioctl.c | 2 +- fs/block_dev.c | 2 +- include/linux/genhd.h | 6 ++++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/block/genhd.c b/block/genhd.c index 5cb51c55f6d8..2429ecbbd97d 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -536,7 +536,7 @@ void register_disk(struct gendisk *disk) disk->slave_dir = kobject_create_and_add("slaves", &ddev->kobj); /* No minors to use for partitions */ - if (!disk_partitionable(disk)) + if (!disk_part_scan_enabled(disk)) goto exit; /* No such device (e.g., media were just removed) */ @@ -841,7 +841,7 @@ static int show_partition(struct seq_file *seqf, void *v) char buf[BDEVNAME_SIZE]; /* Don't show non-partitionable removeable devices or empty devices */ - if (!get_capacity(sgp) || (!disk_partitionable(sgp) && + if (!get_capacity(sgp) || (!disk_max_parts(sgp) && (sgp->flags & GENHD_FL_REMOVABLE))) return 0; if (sgp->flags & GENHD_FL_SUPPRESS_PARTITION_INFO) diff --git a/block/ioctl.c b/block/ioctl.c index 1124cd297263..5c74efc01903 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -101,7 +101,7 @@ static int blkdev_reread_part(struct block_device *bdev) struct gendisk *disk = bdev->bd_disk; int res; - if (!disk_partitionable(disk) || bdev != bdev->bd_contains) + if (!disk_part_scan_enabled(disk) || bdev != bdev->bd_contains) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EACCES; diff --git a/fs/block_dev.c b/fs/block_dev.c index ff77262e887c..0bed0d4588dd 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -971,7 +971,7 @@ static void flush_disk(struct block_device *bdev, bool kill_dirty) if (!bdev->bd_disk) return; - if (disk_partitionable(bdev->bd_disk)) + if (disk_part_scan_enabled(bdev->bd_disk)) bdev->bd_invalidated = 1; } diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 02fa4697a0e5..6d18f3531f18 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -128,6 +128,7 @@ struct hd_struct { #define GENHD_FL_EXT_DEVT 64 /* allow extended devt */ #define GENHD_FL_NATIVE_CAPACITY 128 #define GENHD_FL_BLOCK_EVENTS_ON_EXCL_WRITE 256 +#define GENHD_FL_NO_PART_SCAN 512 enum { DISK_EVENT_MEDIA_CHANGE = 1 << 0, /* media changed */ @@ -234,9 +235,10 @@ static inline int disk_max_parts(struct gendisk *disk) return disk->minors; } -static inline bool disk_partitionable(struct gendisk *disk) +static inline bool disk_part_scan_enabled(struct gendisk *disk) { - return disk_max_parts(disk) > 1; + return disk_max_parts(disk) > 1 && + !(disk->flags & GENHD_FL_NO_PART_SCAN); } static inline dev_t disk_devt(struct gendisk *disk) From e03c8dd14915fabc101aa495828d58598dc5af98 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Tue, 23 Aug 2011 20:12:04 +0200 Subject: [PATCH 0042/1531] loop: always allow userspace partitions and optionally support automatic scanning Automatic partition scanning can be requested individually per loop device during its setup by setting LO_FLAGS_PARTSCAN. By default, no partition tables are scanned. Userspace can now always add and remove partitions from all loop devices, regardless if the in-kernel partition scanner is enabled or not. The needed partition minor numbers are allocated from the extended minors space, the main loop device numbers will continue to match the loop minors, regardless of the number of partitions used. # grep . /sys/class/block/loop1/loop/* /sys/block/loop1/loop/autoclear:0 /sys/block/loop1/loop/backing_file:/home/kay/data/stuff/part.img /sys/block/loop1/loop/offset:0 /sys/block/loop1/loop/partscan:1 /sys/block/loop1/loop/sizelimit:0 # ls -l /dev/loop* brw-rw---- 1 root disk 7, 0 Aug 14 20:22 /dev/loop0 brw-rw---- 1 root disk 7, 1 Aug 14 20:23 /dev/loop1 brw-rw---- 1 root disk 259, 0 Aug 14 20:23 /dev/loop1p1 brw-rw---- 1 root disk 259, 1 Aug 14 20:23 /dev/loop1p2 brw-rw---- 1 root disk 7, 99 Aug 14 20:23 /dev/loop99 brw-rw---- 1 root disk 259, 2 Aug 14 20:23 /dev/loop99p1 brw-rw---- 1 root disk 259, 3 Aug 14 20:23 /dev/loop99p2 crw------T 1 root root 10, 237 Aug 14 20:22 /dev/loop-control Cc: Karel Zak Cc: Davidlohr Bueso Acked-By: Tejun Heo Signed-off-by: Kay Sievers Signed-off-by: Jens Axboe --- drivers/block/loop.c | 49 ++++++++++++++++++++++++++++++++++++++++---- include/linux/loop.h | 1 + 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 936cac3c3126..b336433f8157 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -724,7 +724,7 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev, goto out_putf; fput(old_file); - if (max_part > 0) + if (lo->lo_flags & LO_FLAGS_PARTSCAN) ioctl_by_bdev(bdev, BLKRRPART, 0); return 0; @@ -808,16 +808,25 @@ static ssize_t loop_attr_autoclear_show(struct loop_device *lo, char *buf) return sprintf(buf, "%s\n", autoclear ? "1" : "0"); } +static ssize_t loop_attr_partscan_show(struct loop_device *lo, char *buf) +{ + int partscan = (lo->lo_flags & LO_FLAGS_PARTSCAN); + + return sprintf(buf, "%s\n", partscan ? "1" : "0"); +} + LOOP_ATTR_RO(backing_file); LOOP_ATTR_RO(offset); LOOP_ATTR_RO(sizelimit); LOOP_ATTR_RO(autoclear); +LOOP_ATTR_RO(partscan); static struct attribute *loop_attrs[] = { &loop_attr_backing_file.attr, &loop_attr_offset.attr, &loop_attr_sizelimit.attr, &loop_attr_autoclear.attr, + &loop_attr_partscan.attr, NULL, }; @@ -979,7 +988,9 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode, } lo->lo_state = Lo_bound; wake_up_process(lo->lo_thread); - if (max_part > 0) + if (part_shift) + lo->lo_flags |= LO_FLAGS_PARTSCAN; + if (lo->lo_flags & LO_FLAGS_PARTSCAN) ioctl_by_bdev(bdev, BLKRRPART, 0); return 0; @@ -1070,7 +1081,6 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev) lo->lo_offset = 0; lo->lo_sizelimit = 0; lo->lo_encrypt_key_size = 0; - lo->lo_flags = 0; lo->lo_thread = NULL; memset(lo->lo_encrypt_key, 0, LO_KEY_SIZE); memset(lo->lo_crypt_name, 0, LO_NAME_SIZE); @@ -1088,8 +1098,11 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev) lo->lo_state = Lo_unbound; /* This is safe: open() is still holding a reference. */ module_put(THIS_MODULE); - if (max_part > 0 && bdev) + if (lo->lo_flags & LO_FLAGS_PARTSCAN && bdev) ioctl_by_bdev(bdev, BLKRRPART, 0); + lo->lo_flags = 0; + if (!part_shift) + lo->lo_disk->flags |= GENHD_FL_NO_PART_SCAN; mutex_unlock(&lo->lo_ctl_mutex); /* * Need not hold lo_ctl_mutex to fput backing file. @@ -1159,6 +1172,13 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) (info->lo_flags & LO_FLAGS_AUTOCLEAR)) lo->lo_flags ^= LO_FLAGS_AUTOCLEAR; + if ((info->lo_flags & LO_FLAGS_PARTSCAN) && + !(lo->lo_flags & LO_FLAGS_PARTSCAN)) { + lo->lo_flags |= LO_FLAGS_PARTSCAN; + lo->lo_disk->flags &= ~GENHD_FL_NO_PART_SCAN; + ioctl_by_bdev(lo->lo_device, BLKRRPART, 0); + } + lo->lo_encrypt_key_size = info->lo_encrypt_key_size; lo->lo_init[0] = info->lo_init[0]; lo->lo_init[1] = info->lo_init[1]; @@ -1654,6 +1674,27 @@ static struct loop_device *loop_alloc(int i) if (!disk) goto out_free_queue; + /* + * Disable partition scanning by default. The in-kernel partition + * scanning can be requested individually per-device during its + * setup. Userspace can always add and remove partitions from all + * devices. The needed partition minors are allocated from the + * extended minor space, the main loop device numbers will continue + * to match the loop minors, regardless of the number of partitions + * used. + * + * If max_part is given, partition scanning is globally enabled for + * all loop devices. The minors for the main loop devices will be + * multiples of max_part. + * + * Note: Global-for-all-devices, set-only-at-init, read-only module + * parameteters like 'max_loop' and 'max_part' make things needlessly + * complicated, are too static, inflexible and may surprise + * userspace tools. Parameters like this in general should be avoided. + */ + if (!part_shift) + disk->flags |= GENHD_FL_NO_PART_SCAN; + disk->flags |= GENHD_FL_EXT_DEVT; mutex_init(&lo->lo_ctl_mutex); lo->lo_number = i; lo->lo_thread = NULL; diff --git a/include/linux/loop.h b/include/linux/loop.h index 66c194e2d9b9..4367fc507fe9 100644 --- a/include/linux/loop.h +++ b/include/linux/loop.h @@ -76,6 +76,7 @@ enum { LO_FLAGS_READ_ONLY = 1, LO_FLAGS_USE_AOPS = 2, LO_FLAGS_AUTOCLEAR = 4, + LO_FLAGS_PARTSCAN = 8, }; #include /* for __kernel_old_dev_t */ From fc000154d7a63dee209aab1c13f2c19abdcf6b59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89meric=20Maschino?= Date: Tue, 23 Aug 2011 21:48:23 +0200 Subject: [PATCH 0043/1531] [IA64] agp/hp-agp: Allow binding user memory to the AGP GART MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dmesg reports: [   29.365973] [TTM] AGP Bind memory failed. [   29.366015] radeon 0000:80:00.0: object_init failed for (4096, 0x00000002) [   29.366052] radeon 0000:80:00.0: (-22) create WB bo failed [   29.366087] radeon 0000:80:00.0: Disabling GPU acceleration [   29.366124] [drm] radeon: cp finalized [   29.366168] [drm] radeon: cp finalized [   29.366210] [TTM] Finalizing pool allocator. [   29.366924] [TTM] Zone  kernel: Used memory at exit: 0 kiB. [   29.366961] [TTM] Zone   dma32: Used memory at exit: 0 kiB. [   29.366996] [drm] radeon: ttm finalized [   29.367030] [drm] Forcing AGP to PCI mode This patch allows binding user memory to the AGP GART on zx1-based systems. dmesg thus no more complains about AGP bind memory failure, disabled GPU acceleration or AGP mode forced to PCI. Original work from Francisco Jerez in agp/amd-k7 (https://lkml.org/lkml/2010/10/15/469). Tested-by: Émeric Maschino Signed-off-by: Tony Luck --- drivers/char/agp/hp-agp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/char/agp/hp-agp.c b/drivers/char/agp/hp-agp.c index 056b289a1e89..3695773ce7c3 100644 --- a/drivers/char/agp/hp-agp.c +++ b/drivers/char/agp/hp-agp.c @@ -336,7 +336,8 @@ hp_zx1_insert_memory (struct agp_memory *mem, off_t pg_start, int type) off_t j, io_pg_start; int io_pg_count; - if (type != 0 || mem->type != 0) { + if (type != mem->type || + agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type)) { return -EINVAL; } @@ -380,7 +381,8 @@ hp_zx1_remove_memory (struct agp_memory *mem, off_t pg_start, int type) struct _hp_private *hp = &hp_private; int i, io_pg_start, io_pg_count; - if (type != 0 || mem->type != 0) { + if (type != mem->type || + agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type)) { return -EINVAL; } From 47850a27306997be914dedcbca1dfce09fa4ef0a Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 5 Aug 2011 15:32:26 +0530 Subject: [PATCH 0044/1531] ARM: asm/pl080.h: Protect against multiple inclusion of header file doesn't have protection to deal with multiple inclusion. And so we get compilation errors in cases where this file is included more than once. This patch adds #ifdefs at the top of file to protect it against multiple inclusions. Signed-off-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- arch/arm/include/asm/hardware/pl080.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/include/asm/hardware/pl080.h b/arch/arm/include/asm/hardware/pl080.h index e4a04e4e5627..33c78d7af2e1 100644 --- a/arch/arm/include/asm/hardware/pl080.h +++ b/arch/arm/include/asm/hardware/pl080.h @@ -21,6 +21,9 @@ * OneNAND features. */ +#ifndef ASM_PL080_H +#define ASM_PL080_H + #define PL080_INT_STATUS (0x00) #define PL080_TC_STATUS (0x04) #define PL080_TC_CLEAR (0x08) @@ -138,3 +141,4 @@ struct pl080s_lli { u32 control1; }; +#endif /* ASM_PL080_H */ From 3e27ee8448bcbc8b4f060b107aa622c116f287ab Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 5 Aug 2011 15:32:27 +0530 Subject: [PATCH 0045/1531] dmaengine/amba-pl08x: Resolve formatting issues There were few formatting related issues in code. This patch fixes them. Fixes include: - Remove extra blank lines - align code to 80 cols - combine several lines to one line Signed-off-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 41 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 196a7378d332..4c4a3092e09c 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -125,7 +125,8 @@ struct pl08x_lli { * @phy_chans: array of data for the physical channels * @pool: a pool for the LLI descriptors * @pool_ctr: counter of LLIs in the pool - * @lli_buses: bitmask to or in to LLI pointer selecting AHB port for LLI fetches + * @lli_buses: bitmask to or in to LLI pointer selecting AHB port for LLI + * fetches * @mem_buses: set to indicate memory transfers on AHB2. * @lock: a spinlock for this struct */ @@ -271,7 +272,6 @@ static void pl08x_resume_phy_chan(struct pl08x_phy_chan *ch) writel(val, ch->base + PL080_CH_CONFIG); } - /* * pl08x_terminate_phy_chan() stops the channel, clears the FIFO and * clears any pending interrupt status. This should not be used for @@ -546,7 +546,8 @@ static void pl08x_fill_lli_for_desc(struct pl08x_lli_build_data *bd, llis_va[num_llis].cctl = cctl; llis_va[num_llis].src = bd->srcbus.addr; llis_va[num_llis].dst = bd->dstbus.addr; - llis_va[num_llis].lli = llis_bus + (num_llis + 1) * sizeof(struct pl08x_lli); + llis_va[num_llis].lli = llis_bus + (num_llis + 1) * + sizeof(struct pl08x_lli); llis_va[num_llis].lli |= bd->lli_bus; if (cctl & PL080_CONTROL_SRC_INCR) @@ -583,12 +584,10 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, struct pl08x_lli_build_data bd; int num_llis = 0; u32 cctl; - size_t max_bytes_per_lli; - size_t total_bytes = 0; + size_t max_bytes_per_lli, total_bytes = 0; struct pl08x_lli *llis_va; - txd->llis_va = dma_pool_alloc(pl08x->pool, GFP_NOWAIT, - &txd->llis_bus); + txd->llis_va = dma_pool_alloc(pl08x->pool, GFP_NOWAIT, &txd->llis_bus); if (!txd->llis_va) { dev_err(&pl08x->adev->dev, "%s no memory for llis\n", __func__); return 0; @@ -779,7 +778,6 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, total_bytes += lli_len; } - if (odd_bytes) { /* * Creep past the boundary, maintaining @@ -916,9 +914,7 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan, * need, but for slaves the physical signals may be muxed! * Can the platform allow us to use this channel? */ - if (plchan->slave && - ch->signal < 0 && - pl08x->pd->get_signal) { + if (plchan->slave && ch->signal < 0 && pl08x->pd->get_signal) { ret = pl08x->pd->get_signal(plchan); if (ret < 0) { dev_dbg(&pl08x->adev->dev, @@ -1007,10 +1003,8 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_interrupt( * If slaves are relying on interrupts to signal completion this function * must not be called with interrupts disabled. */ -static enum dma_status -pl08x_dma_tx_status(struct dma_chan *chan, - dma_cookie_t cookie, - struct dma_tx_state *txstate) +static enum dma_status pl08x_dma_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *txstate) { struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); dma_cookie_t last_used; @@ -1588,8 +1582,8 @@ static void pl08x_tasklet(unsigned long data) */ list_for_each_entry(waiting, &pl08x->memcpy.channels, chan.device_node) { - if (waiting->state == PL08X_CHAN_WAITING && - waiting->waiting != NULL) { + if (waiting->state == PL08X_CHAN_WAITING && + waiting->waiting != NULL) { int ret; /* This should REALLY not fail now */ @@ -1684,9 +1678,7 @@ static void pl08x_dma_slave_init(struct pl08x_dma_chan *chan) * Make a local wrapper to hold required data */ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x, - struct dma_device *dmadev, - unsigned int channels, - bool slave) + struct dma_device *dmadev, unsigned int channels, bool slave) { struct pl08x_dma_chan *chan; int i; @@ -1836,9 +1828,9 @@ static const struct file_operations pl08x_debugfs_operations = { static void init_pl08x_debugfs(struct pl08x_driver_data *pl08x) { /* Expose a simple debugfs interface to view all clocks */ - (void) debugfs_create_file(dev_name(&pl08x->adev->dev), S_IFREG | S_IRUGO, - NULL, pl08x, - &pl08x_debugfs_operations); + (void) debugfs_create_file(dev_name(&pl08x->adev->dev), + S_IFREG | S_IRUGO, NULL, pl08x, + &pl08x_debugfs_operations); } #else @@ -1973,8 +1965,7 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) /* Register slave channels */ ret = pl08x_dma_init_virtual_channels(pl08x, &pl08x->slave, - pl08x->pd->num_slave_channels, - true); + pl08x->pd->num_slave_channels, true); if (ret <= 0) { dev_warn(&pl08x->adev->dev, "%s failed to enumerate slave channels - %d\n", From 0c38d70139138713e66c6f98e19a0320014476ff Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 5 Aug 2011 15:32:28 +0530 Subject: [PATCH 0046/1531] dmaengine/amba-pl08x: Rearrange inclusion of header files in ascending order Header files included in driver are not present in alphabetical order. Rearrange them in alphabetical order. Signed-off-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 4c4a3092e09c..8e2056bf1623 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -74,19 +74,18 @@ * Global TODO: * - Break out common code from arch/arm/mach-s3c64xx and share */ -#include -#include -#include -#include -#include -#include -#include -#include #include #include #include +#include +#include +#include +#include +#include +#include +#include #include - +#include #include #define DRIVER_NAME "pl08xdmac" From b201c111c87a4cf36d009abe57c62bd14d17d762 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 5 Aug 2011 15:32:29 +0530 Subject: [PATCH 0047/1531] dmaengine/amba-pl08x: pass (*ptr) to sizeof() instead of (struct xyz) As mentioned in Documentation/CodingStyle, The preferred form for passing a size of a struct is the following: p = kmalloc(sizeof(*p), ...); The alternative form where struct name is spelled out hurts readability and introduces an opportunity for a bug when the pointer variable type is changed but the corresponding sizeof that is passed to a memory allocator is not. This patch replaces (struct xyz) with *ptr at several occurrences in driver. Signed-off-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 8e2056bf1623..01c2f507e950 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -1293,7 +1293,7 @@ static int pl08x_prep_channel_resources(struct pl08x_dma_chan *plchan, static struct pl08x_txd *pl08x_get_txd(struct pl08x_dma_chan *plchan, unsigned long flags) { - struct pl08x_txd *txd = kzalloc(sizeof(struct pl08x_txd), GFP_NOWAIT); + struct pl08x_txd *txd = kzalloc(sizeof(*txd), GFP_NOWAIT); if (txd) { dma_async_tx_descriptor_init(&txd->tx, &plchan->chan); @@ -1690,7 +1690,7 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x, * to cope with that situation. */ for (i = 0; i < channels; i++) { - chan = kzalloc(sizeof(struct pl08x_dma_chan), GFP_KERNEL); + chan = kzalloc(sizeof(*chan), GFP_KERNEL); if (!chan) { dev_err(&pl08x->adev->dev, "%s no memory for channel\n", __func__); @@ -1850,7 +1850,7 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) return ret; /* Create the driver state holder */ - pl08x = kzalloc(sizeof(struct pl08x_driver_data), GFP_KERNEL); + pl08x = kzalloc(sizeof(*pl08x), GFP_KERNEL); if (!pl08x) { ret = -ENOMEM; goto out_no_pl08x; @@ -1929,7 +1929,7 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) } /* Initialize physical channels */ - pl08x->phy_chans = kmalloc((vd->channels * sizeof(struct pl08x_phy_chan)), + pl08x->phy_chans = kmalloc((vd->channels * sizeof(*pl08x->phy_chans)), GFP_KERNEL); if (!pl08x->phy_chans) { dev_err(&adev->dev, "%s failed to allocate " From 5a61233073a35a7ae152af77ed80dfc465c38fc7 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 5 Aug 2011 15:32:30 +0530 Subject: [PATCH 0048/1531] dmaengine/amba-pl08x: Complete doc comment for struct pl08x_txd Doc comment for struct pl08x_txd was incomplete. Complete that. Signed-off-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- include/linux/amba/pl08x.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/linux/amba/pl08x.h b/include/linux/amba/pl08x.h index e6e28f37d8ec..cd8f629da58f 100644 --- a/include/linux/amba/pl08x.h +++ b/include/linux/amba/pl08x.h @@ -105,8 +105,16 @@ struct pl08x_phy_chan { /** * struct pl08x_txd - wrapper for struct dma_async_tx_descriptor + * @tx: async tx descriptor + * @node: node for txd list for channels + * @src_addr: src address of txd + * @dst_addr: dst address of txd + * @len: transfer len in bytes + * @direction: direction of transfer * @llis_bus: DMA memory address (physical) start for the LLIs * @llis_va: virtual memory address start for the LLIs + * @cctl: control reg values for current txd + * @ccfg: config reg values for current txd */ struct pl08x_txd { struct dma_async_tx_descriptor tx; From 0532e6fced3c4f6a4eda7f078d8aa36405647c07 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 5 Aug 2011 15:32:31 +0530 Subject: [PATCH 0049/1531] dmaengine/amba-pl08x: Remove redundant comment and rewrite original Similar comment is present over routine also pl08x_choose_master_bus(). Keeping one of them. Also rewrite that comment to convey message clearly. Signed-off-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 01c2f507e950..6c52959b00af 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -497,9 +497,13 @@ struct pl08x_lli_build_data { }; /* - * Autoselect a master bus to use for the transfer this prefers the - * destination bus if both available if fixed address on one bus the - * other will be chosen + * Autoselect a master bus to use for the transfer. Slave will be the chosen as + * victim in case src & dest are not similarly aligned. i.e. If after aligning + * masters address with width requirements of transfer (by sending few byte by + * byte data), slave is still not aligned, then its width will be reduced to + * BYTE. + * - prefers the destination bus if both available + * - if fixed address on one bus the other will be chosen */ static void pl08x_choose_master_bus(struct pl08x_lli_build_data *bd, struct pl08x_bus_data **mbus, struct pl08x_bus_data **sbus, u32 cctl) @@ -625,11 +629,6 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, /* We need to count this down to zero */ bd.remainder = txd->len; - /* - * Choose bus to align to - * - prefers destination bus if both available - * - if fixed address on one bus chooses other - */ pl08x_choose_master_bus(&bd, &mbus, &sbus, cctl); dev_vdbg(&pl08x->adev->dev, "src=0x%08x%s/%u dst=0x%08x%s/%u len=%zu llimax=%zu\n", From 175a5e617cd820d9e22d9e4f6d3ef736b2f874b1 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 5 Aug 2011 15:32:32 +0530 Subject: [PATCH 0050/1531] dmaengine/amba-pl08x: Changing few prints to dev_dbg from dev_info For 8 memory and 16 slave channels 35 boot print lines are printed. And that is too much. Most of this would be more useful for debugging. So moving few of them to dev_dbg instead of dev_info. Now only 3 prints will be printed. This also rearrange one of the debug message to fit into two lines. Signed-off-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 6c52959b00af..ead88c9a5e93 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -1717,7 +1717,7 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x, kfree(chan); continue; } - dev_info(&pl08x->adev->dev, + dev_dbg(&pl08x->adev->dev, "initialize virtual channel \"%s\"\n", chan->name); @@ -1945,9 +1945,8 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) spin_lock_init(&ch->lock); ch->serving = NULL; ch->signal = -1; - dev_info(&adev->dev, - "physical channel %d is %s\n", i, - pl08x_phy_channel_busy(ch) ? "BUSY" : "FREE"); + dev_dbg(&adev->dev, "physical channel %d is %s\n", + i, pl08x_phy_channel_busy(ch) ? "BUSY" : "FREE"); } /* Register as many memcpy channels as there are physical channels */ From b7b6018bad6fd7ebe5a78bda5f2a71a6ecf5406a Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 5 Aug 2011 15:32:33 +0530 Subject: [PATCH 0051/1531] dmaengine/amba-pl08x: support runtime PM Insert notifiers for the runtime PM API. With this the runtime PM layer kicks in to action where used. Signed-off-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index ead88c9a5e93..5dd97f450925 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -84,6 +84,7 @@ #include #include #include +#include #include #include #include @@ -405,6 +406,7 @@ pl08x_get_phy_channel(struct pl08x_driver_data *pl08x, return NULL; } + pm_runtime_get_sync(&pl08x->adev->dev); return ch; } @@ -418,6 +420,8 @@ static inline void pl08x_put_phy_channel(struct pl08x_driver_data *pl08x, /* Stop the channel and clear its interrupts */ pl08x_terminate_phy_chan(pl08x, ch); + pm_runtime_put(&pl08x->adev->dev); + /* Mark it as free */ ch->serving = NULL; spin_unlock_irqrestore(&ch->lock, flags); @@ -1855,6 +1859,9 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) goto out_no_pl08x; } + pm_runtime_set_active(&adev->dev); + pm_runtime_enable(&adev->dev); + /* Initialize memcpy engine */ dma_cap_set(DMA_MEMCPY, pl08x->memcpy.cap_mask); pl08x->memcpy.dev = &adev->dev; @@ -1992,6 +1999,8 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) dev_info(&pl08x->adev->dev, "DMA: PL%03x rev%u at 0x%08llx irq %d\n", amba_part(adev), amba_rev(adev), (unsigned long long)adev->res.start, adev->irq[0]); + + pm_runtime_put(&adev->dev); return 0; out_no_slave_reg: @@ -2010,6 +2019,9 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id) dma_pool_destroy(pl08x->pool); out_no_lli_pool: out_no_platdata: + pm_runtime_put(&adev->dev); + pm_runtime_disable(&adev->dev); + kfree(pl08x); out_no_pl08x: amba_release_regions(adev); From 48a59ef3579492855d41405f8bf0a2983e061976 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 5 Aug 2011 15:32:34 +0530 Subject: [PATCH 0052/1531] dmaengine/amba-pl08x: Simplify pl08x_ensure_on() Simply writing 1 on bit 0 is sufficient instead of reading and clearing bits. Also as per manual, for bit 3-31 of DMACConfiguration register: "read undefined, write as 0" So, we must not rely on values read from this registers bit 3-31. Signed-off-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 5dd97f450925..d79688d64886 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -1502,13 +1502,7 @@ bool pl08x_filter_id(struct dma_chan *chan, void *chan_id) */ static void pl08x_ensure_on(struct pl08x_driver_data *pl08x) { - u32 val; - - val = readl(pl08x->base + PL080_CONFIG); - val &= ~(PL080_CONFIG_M2_BE | PL080_CONFIG_M1_BE | PL080_CONFIG_ENABLE); - /* We implicitly clear bit 1 and that means little-endian mode */ - val |= PL080_CONFIG_ENABLE; - writel(val, pl08x->base + PL080_CONFIG); + writel(PL080_CONFIG_ENABLE, pl08x->base + PL080_CONFIG); } static void pl08x_unmap_buffers(struct pl08x_txd *txd) From 16ca8105040217acf5b4b506d04bb933fb3a76af Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 5 Aug 2011 15:32:35 +0530 Subject: [PATCH 0053/1531] dmaengine/amba-pl08x: No need to check "ch->signal < 0" We have just executed following in pl08x_get_phy_channel(): ch->signal = -1; We don't have to compare "ch->signal < 0", as this will always be true. Signed-off-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index d79688d64886..3653961b6088 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -916,7 +916,7 @@ static int prep_phy_channel(struct pl08x_dma_chan *plchan, * need, but for slaves the physical signals may be muxed! * Can the platform allow us to use this channel? */ - if (plchan->slave && ch->signal < 0 && pl08x->pd->get_signal) { + if (plchan->slave && pl08x->pd->get_signal) { ret = pl08x->pd->get_signal(plchan); if (ret < 0) { dev_dbg(&pl08x->adev->dev, From 28da28365da3f3bea1d4b7212a8a40e4b9ac3229 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 5 Aug 2011 15:32:36 +0530 Subject: [PATCH 0054/1531] dmaengine/amba-pl08x: Schedule tasklet in case of error interrupt Currently, if error interrupt occurs, nothing is done in interrupt handler (just clearing the interrupts). We must somehow indicate this to the user that DMA is over, due to ERR interrupt or TC interrupt. So, this patch just schedules existing tasklet, with a print showing error interrupt has occurred on which channels. Signed-off-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 44 +++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 3653961b6088..6bba32e5ddb8 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -1619,38 +1619,40 @@ static void pl08x_tasklet(unsigned long data) static irqreturn_t pl08x_irq(int irq, void *dev) { struct pl08x_driver_data *pl08x = dev; - u32 mask = 0; - u32 val; - int i; - - val = readl(pl08x->base + PL080_ERR_STATUS); - if (val) { - /* An error interrupt (on one or more channels) */ - dev_err(&pl08x->adev->dev, - "%s error interrupt, register value 0x%08x\n", - __func__, val); - /* - * Simply clear ALL PL08X error interrupts, - * regardless of channel and cause - * FIXME: should be 0x00000003 on PL081 really. - */ - writel(0x000000FF, pl08x->base + PL080_ERR_CLEAR); + u32 mask = 0, err, tc, i; + + /* check & clear - ERR & TC interrupts */ + err = readl(pl08x->base + PL080_ERR_STATUS); + if (err) { + dev_err(&pl08x->adev->dev, "%s error interrupt, register value 0x%08x\n", + __func__, err); + writel(err, pl08x->base + PL080_ERR_CLEAR); } - val = readl(pl08x->base + PL080_INT_STATUS); + tc = readl(pl08x->base + PL080_INT_STATUS); + if (tc) + writel(tc, pl08x->base + PL080_TC_CLEAR); + + if (!err && !tc) + return IRQ_NONE; + for (i = 0; i < pl08x->vd->channels; i++) { - if ((1 << i) & val) { + if (((1 << i) & err) || ((1 << i) & tc)) { /* Locate physical channel */ struct pl08x_phy_chan *phychan = &pl08x->phy_chans[i]; struct pl08x_dma_chan *plchan = phychan->serving; + if (!plchan) { + dev_err(&pl08x->adev->dev, + "%s Error TC interrupt on unused channel: 0x%08x\n", + __func__, i); + continue; + } + /* Schedule tasklet on this channel */ tasklet_schedule(&plchan->tasklet); - mask |= (1 << i); } } - /* Clear only the terminal interrupts on channels we processed */ - writel(mask, pl08x->base + PL080_TC_CLEAR); return mask ? IRQ_HANDLED : IRQ_NONE; } From 16a2e7d359b9fc64fb8a6717c0642691b1e60bb7 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 5 Aug 2011 15:32:37 +0530 Subject: [PATCH 0055/1531] dmaengine/amba-pl08x: Get rid of pl08x_pre_boundary() Pl080 Manual says: "Bursts do not cross the 1KB address boundary" We can program the controller to cross 1 KB boundary on a burst and controller can take care of this boundary condition by itself. Following is the discussion with ARM Technical Support Guys (David): [Viresh] Manual says: "Bursts do not cross the 1KB address boundary" What does that actually mean? As, Maximum size transferable with a single LLI is 4095 * 4 =16380 ~ 16KB. So, if we don't have src/dest address aligned to burst size, we can't use this big of an LLI. [David] There is a difference between bursts describing the total data transferred by the DMA controller and AHB bursts. Bursts described by the programmable parameters in the PL080 have no direct connection with the bursts that are seen on the AHB bus. The statement that "Bursts do not cross the 1KB address boundary" in the TRM is referring to AHB bursts, where this limitation is a requirement of the AHB spec. You can still issue bursts within the PL080 that are in excess of 1KB. The PL080 will make sure that its bursts are broken down into legal AHB bursts which will be formatted to ensure that no AHB burst crosses a 1KB boundary. Based on above discussion, this patch removes all code related to 1 KB boundary as we are not required to handle this in driver. Signed-off-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 141 +++++-------------------------------- include/linux/amba/pl08x.h | 2 - 2 files changed, 17 insertions(+), 126 deletions(-) diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 6bba32e5ddb8..be9a1c718f9a 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -149,14 +149,6 @@ struct pl08x_driver_data { * PL08X specific defines */ -/* - * Memory boundaries: the manual for PL08x says that the controller - * cannot read past a 1KiB boundary, so these defines are used to - * create transfer LLIs that do not cross such boundaries. - */ -#define PL08X_BOUNDARY_SHIFT (10) /* 1KB 0x400 */ -#define PL08X_BOUNDARY_SIZE (1 << PL08X_BOUNDARY_SHIFT) - /* Size (bytes) of each LLI buffer allocated for one transfer */ # define PL08X_LLI_TSFR_SIZE 0x2000 @@ -567,18 +559,6 @@ static void pl08x_fill_lli_for_desc(struct pl08x_lli_build_data *bd, bd->remainder -= len; } -/* - * Return number of bytes to fill to boundary, or len. - * This calculation works for any value of addr. - */ -static inline size_t pl08x_pre_boundary(u32 addr, size_t len) -{ - size_t boundary_len = PL08X_BOUNDARY_SIZE - - (addr & (PL08X_BOUNDARY_SIZE - 1)); - - return min(boundary_len, len); -} - /* * This fills in the table of LLIs for the transfer descriptor * Note that we assume we never have to change the burst sizes @@ -685,118 +665,30 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, * width left */ while (bd.remainder > (mbus->buswidth - 1)) { - size_t lli_len, target_len, tsize, odd_bytes; + size_t lli_len, tsize; /* * If enough left try to send max possible, * otherwise try to send the remainder */ - target_len = min(bd.remainder, max_bytes_per_lli); - + lli_len = min(bd.remainder, max_bytes_per_lli); /* - * Set bus lengths for incrementing buses to the - * number of bytes which fill to next memory boundary, - * limiting on the target length calculated above. + * Check against minimum bus alignment: Calculate actual + * transfer size in relation to bus width and get a + * maximum remainder of the smallest bus width - 1 */ - if (cctl & PL080_CONTROL_SRC_INCR) - bd.srcbus.fill_bytes = - pl08x_pre_boundary(bd.srcbus.addr, - target_len); - else - bd.srcbus.fill_bytes = target_len; - - if (cctl & PL080_CONTROL_DST_INCR) - bd.dstbus.fill_bytes = - pl08x_pre_boundary(bd.dstbus.addr, - target_len); - else - bd.dstbus.fill_bytes = target_len; - - /* Find the nearest */ - lli_len = min(bd.srcbus.fill_bytes, - bd.dstbus.fill_bytes); - - BUG_ON(lli_len > bd.remainder); - - if (lli_len <= 0) { - dev_err(&pl08x->adev->dev, - "%s lli_len is %zu, <= 0\n", - __func__, lli_len); - return 0; - } - - if (lli_len == target_len) { - /* - * Can send what we wanted. - * Maintain alignment - */ - lli_len = (lli_len/mbus->buswidth) * - mbus->buswidth; - odd_bytes = 0; - } else { - /* - * So now we know how many bytes to transfer - * to get to the nearest boundary. The next - * LLI will past the boundary. However, we - * may be working to a boundary on the slave - * bus. We need to ensure the master stays - * aligned, and that we are working in - * multiples of the bus widths. - */ - odd_bytes = lli_len % mbus->buswidth; - lli_len -= odd_bytes; - - } - - if (lli_len) { - /* - * Check against minimum bus alignment: - * Calculate actual transfer size in relation - * to bus width an get a maximum remainder of - * the smallest bus width - 1 - */ - /* FIXME: use round_down()? */ - tsize = lli_len / min(mbus->buswidth, - sbus->buswidth); - lli_len = tsize * min(mbus->buswidth, - sbus->buswidth); - - if (target_len != lli_len) { - dev_vdbg(&pl08x->adev->dev, - "%s can't send what we want. Desired 0x%08zx, lli of 0x%08zx bytes in txd of 0x%08zx\n", - __func__, target_len, lli_len, txd->len); - } - - cctl = pl08x_cctl_bits(cctl, - bd.srcbus.buswidth, - bd.dstbus.buswidth, - tsize); - - dev_vdbg(&pl08x->adev->dev, - "%s fill lli with single lli chunk of size 0x%08zx (remainder 0x%08zx)\n", - __func__, lli_len, bd.remainder); - pl08x_fill_lli_for_desc(&bd, num_llis++, - lli_len, cctl); - total_bytes += lli_len; - } + tsize = lli_len / min(mbus->buswidth, sbus->buswidth); + lli_len = tsize * min(mbus->buswidth, sbus->buswidth); - if (odd_bytes) { - /* - * Creep past the boundary, maintaining - * master alignment - */ - int j; - for (j = 0; (j < mbus->buswidth) - && (bd.remainder); j++) { - cctl = pl08x_cctl_bits(cctl, 1, 1, 1); - dev_vdbg(&pl08x->adev->dev, - "%s align with boundary, single byte (remain 0x%08zx)\n", - __func__, bd.remainder); - pl08x_fill_lli_for_desc(&bd, - num_llis++, 1, cctl); - total_bytes++; - } - } + dev_vdbg(&pl08x->adev->dev, + "%s fill lli with single lli chunk of " + "size 0x%08zx (remainder 0x%08zx)\n", + __func__, lli_len, bd.remainder); + + cctl = pl08x_cctl_bits(cctl, bd.srcbus.buswidth, + bd.dstbus.buswidth, tsize); + pl08x_fill_lli_for_desc(&bd, num_llis++, lli_len, cctl); + total_bytes += lli_len; } /* @@ -811,6 +703,7 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, total_bytes++; } } + if (total_bytes != txd->len) { dev_err(&pl08x->adev->dev, "%s size of encoded lli:s don't match total txd, transferred 0x%08zx from size 0x%08zx\n", diff --git a/include/linux/amba/pl08x.h b/include/linux/amba/pl08x.h index cd8f629da58f..ecd17f581e71 100644 --- a/include/linux/amba/pl08x.h +++ b/include/linux/amba/pl08x.h @@ -77,13 +77,11 @@ struct pl08x_channel_data { * @addr: current address * @maxwidth: the maximum width of a transfer on this bus * @buswidth: the width of this bus in bytes: 1, 2 or 4 - * @fill_bytes: bytes required to fill to the next bus memory boundary */ struct pl08x_bus_data { dma_addr_t addr; u8 maxwidth; u8 buswidth; - size_t fill_bytes; }; /** From fa6a940bf129c5417b602a4cdfe88b3dbd8e5898 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 5 Aug 2011 15:32:38 +0530 Subject: [PATCH 0056/1531] dmaengine/amba-pl08x: max_bytes_per_lli is TRANSFER_SIZE * src_width (not MIN(width)) max_bytes_per_lli = bd.srcbus.buswidth * PL080_CONTROL_TRANSFER_SIZE_MASK; This is confirmed by ARM support guys. Below is summary of mail exchange with them: [Viresh] What is the total data to be transferred in case source and destination bus widths are different. Suppose, source bus width is 2 bytes and destination is 4 bytes. Now in order to transfer 80 bytes, what should be value of TransferSize field in control reg: 40? or 20?. [David from ARM] The value that is programmed into the TransferSize field should be the number of transfers needed to achieve the required data transfer. So, to transfer 80 bytes, with a Source Width of 2, the TransferSize field = should be programmed with: Total transfer size ------------------- = 40 [Viresh] Will this change if source is 4 bytes and dest is 2? [David] Yes - the calculation then becomes: Total transfer size ------------------- =20 Also, max_bytes_per_lli must be calculated after fixing src and dest widths not before that. So move this code to the correct place. This patch also removes max_bytes_per_lli from earlier print message, as till that point max_bytes_per_lli is unknown. Signed-off-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index be9a1c718f9a..e5930d512b00 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -604,23 +604,17 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, bd.srcbus.buswidth = bd.srcbus.maxwidth; bd.dstbus.buswidth = bd.dstbus.maxwidth; - /* - * Bytes transferred == tsize * MIN(buswidths), not max(buswidths) - */ - max_bytes_per_lli = min(bd.srcbus.buswidth, bd.dstbus.buswidth) * - PL080_CONTROL_TRANSFER_SIZE_MASK; - /* We need to count this down to zero */ bd.remainder = txd->len; pl08x_choose_master_bus(&bd, &mbus, &sbus, cctl); - dev_vdbg(&pl08x->adev->dev, "src=0x%08x%s/%u dst=0x%08x%s/%u len=%zu llimax=%zu\n", + dev_vdbg(&pl08x->adev->dev, "src=0x%08x%s/%u dst=0x%08x%s/%u len=%zu\n", bd.srcbus.addr, cctl & PL080_CONTROL_SRC_INCR ? "+" : "", bd.srcbus.buswidth, bd.dstbus.addr, cctl & PL080_CONTROL_DST_INCR ? "+" : "", bd.dstbus.buswidth, - bd.remainder, max_bytes_per_lli); + bd.remainder); dev_vdbg(&pl08x->adev->dev, "mbus=%s sbus=%s\n", mbus == &bd.srcbus ? "src" : "dst", sbus == &bd.srcbus ? "src" : "dst"); @@ -660,6 +654,10 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, sbus->buswidth = 1; } + /* Bytes transferred = tsize * src width, not MIN(buswidths) */ + max_bytes_per_lli = bd.srcbus.buswidth * + PL080_CONTROL_TRANSFER_SIZE_MASK; + /* * Make largest possible LLIs until less than one bus * width left From 03af500f743f486648fc8afc38593e9844411945 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 5 Aug 2011 15:32:39 +0530 Subject: [PATCH 0057/1531] dmaengine/amba-pl08x: Add prep_single_byte_llis() routine Code for creating single byte llis is present at several places. Create a routine to avoid code redundancy. Also, we don't need one lli per single byte transfer, we can have single lli to do all single byte transfer. Signed-off-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 61 ++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index e5930d512b00..45d8a5d5bccd 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -559,6 +559,14 @@ static void pl08x_fill_lli_for_desc(struct pl08x_lli_build_data *bd, bd->remainder -= len; } +static inline void prep_byte_width_lli(struct pl08x_lli_build_data *bd, + u32 *cctl, u32 len, int num_llis, size_t *total_bytes) +{ + *cctl = pl08x_cctl_bits(*cctl, 1, 1, len); + pl08x_fill_lli_for_desc(bd, num_llis, len, *cctl); + (*total_bytes) += len; +} + /* * This fills in the table of LLIs for the transfer descriptor * Note that we assume we never have to change the burst sizes @@ -570,7 +578,7 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, struct pl08x_bus_data *mbus, *sbus; struct pl08x_lli_build_data bd; int num_llis = 0; - u32 cctl; + u32 cctl, early_bytes = 0; size_t max_bytes_per_lli, total_bytes = 0; struct pl08x_lli *llis_va; @@ -619,29 +627,27 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, mbus == &bd.srcbus ? "src" : "dst", sbus == &bd.srcbus ? "src" : "dst"); - if (txd->len < mbus->buswidth) { - /* Less than a bus width available - send as single bytes */ - while (bd.remainder) { - dev_vdbg(&pl08x->adev->dev, - "%s single byte LLIs for a transfer of " - "less than a bus width (remain 0x%08x)\n", - __func__, bd.remainder); - cctl = pl08x_cctl_bits(cctl, 1, 1, 1); - pl08x_fill_lli_for_desc(&bd, num_llis++, 1, cctl); - total_bytes++; - } - } else { - /* Make one byte LLIs until master bus is aligned */ - while ((mbus->addr) % (mbus->buswidth)) { - dev_vdbg(&pl08x->adev->dev, - "%s adjustment lli for less than bus width " - "(remain 0x%08x)\n", - __func__, bd.remainder); - cctl = pl08x_cctl_bits(cctl, 1, 1, 1); - pl08x_fill_lli_for_desc(&bd, num_llis++, 1, cctl); - total_bytes++; - } + /* + * Send byte by byte for following cases + * - Less than a bus width available + * - until master bus is aligned + */ + if (bd.remainder < mbus->buswidth) + early_bytes = bd.remainder; + else if ((mbus->addr) % (mbus->buswidth)) { + early_bytes = mbus->buswidth - (mbus->addr) % (mbus->buswidth); + if ((bd.remainder - early_bytes) < mbus->buswidth) + early_bytes = bd.remainder; + } + + if (early_bytes) { + dev_vdbg(&pl08x->adev->dev, "%s byte width LLIs " + "(remain 0x%08x)\n", __func__, bd.remainder); + prep_byte_width_lli(&bd, &cctl, early_bytes, num_llis++, + &total_bytes); + } + if (bd.remainder) { /* * Master now aligned * - if slave is not then we must set its width down @@ -692,13 +698,12 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, /* * Send any odd bytes */ - while (bd.remainder) { - cctl = pl08x_cctl_bits(cctl, 1, 1, 1); + if (bd.remainder) { dev_vdbg(&pl08x->adev->dev, - "%s align with boundary, single odd byte (remain %zu)\n", + "%s align with boundary, send odd bytes (remain %zu)\n", __func__, bd.remainder); - pl08x_fill_lli_for_desc(&bd, num_llis++, 1, cctl); - total_bytes++; + prep_byte_width_lli(&bd, &cctl, bd.remainder, + num_llis++, &total_bytes); } } From e0719165801fad04073e7dcd90e4afd02aba3fb7 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 5 Aug 2011 15:32:40 +0530 Subject: [PATCH 0058/1531] dmaengine/amba-pl08x: Align lli_len to max(src.width, dst.width) Currently lli_len is aligned to min of two widths, which looks to be incorrect. Instead it should be aligned to max of both widths. Lets say, total_size = 441 bytes MIN: lets check if min() suits or not: CASE 1: srcwidth = 1, dstwidth = 4 min(src, dst) = 1 i.e. We program transfer size in control reg to 441. Now, till 440 bytes everything is fine, but on the last byte DMAC can't transfer 1 byte to dst, as its width is 4. CASE 2: srcwidth = 4, dstwidth = 1 min(src, dst) = 1 i.e. we program transfer size in control reg to 110 (data transferred = 110 * srcwidth). So, here too 1 byte is left, but on the source side. MAX: Lets check if max() suits or not: CASE 3: srcwidth = 1, dstwidth = 4 max(src, dst) = 4 Aligned size is 440 i.e. We program transfer size in control reg to 440. Now, all 440 bytes will be transferred without any issues. CASE 4: srcwidth = 4, dstwidth = 1 max(src, dst) = 4 Aligned size is 440 i.e. We program transfer size in control reg to 110 (data transferred = 110 * srcwidth). Now, also all 440 bytes will be transferred without any issues. Signed-off-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 45d8a5d5bccd..4bcf6036f35d 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -669,20 +669,22 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, * width left */ while (bd.remainder > (mbus->buswidth - 1)) { - size_t lli_len, tsize; + size_t lli_len, tsize, width; /* * If enough left try to send max possible, * otherwise try to send the remainder */ lli_len = min(bd.remainder, max_bytes_per_lli); + /* - * Check against minimum bus alignment: Calculate actual + * Check against maximum bus alignment: Calculate actual * transfer size in relation to bus width and get a - * maximum remainder of the smallest bus width - 1 + * maximum remainder of the highest bus width - 1 */ - tsize = lli_len / min(mbus->buswidth, sbus->buswidth); - lli_len = tsize * min(mbus->buswidth, sbus->buswidth); + width = max(mbus->buswidth, sbus->buswidth); + lli_len = (lli_len / width) * width; + tsize = lli_len / bd.srcbus.buswidth; dev_vdbg(&pl08x->adev->dev, "%s fill lli with single lli chunk of " From 036f05fd6dcdb6a6b9e55703cb663112fa4c4e42 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 5 Aug 2011 15:32:41 +0530 Subject: [PATCH 0059/1531] dmaengine/amba-pl08x: Choose peripheral bus as master bus When we have DMA transfers between peripheral and memory, then we shouldn't reduce width of peripheral at all, as that may be a strict requirement. But we can always reduce width of memory access, with some compromise in performance. Thus, we must select peripheral as master and not memory. Also this rearranges code to make it shorter. Signed-off-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 4bcf6036f35d..f70aa574c58f 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -499,34 +499,24 @@ struct pl08x_lli_build_data { * byte data), slave is still not aligned, then its width will be reduced to * BYTE. * - prefers the destination bus if both available - * - if fixed address on one bus the other will be chosen + * - prefers bus with fixed address (i.e. peripheral) */ static void pl08x_choose_master_bus(struct pl08x_lli_build_data *bd, struct pl08x_bus_data **mbus, struct pl08x_bus_data **sbus, u32 cctl) { if (!(cctl & PL080_CONTROL_DST_INCR)) { - *mbus = &bd->srcbus; - *sbus = &bd->dstbus; - } else if (!(cctl & PL080_CONTROL_SRC_INCR)) { *mbus = &bd->dstbus; *sbus = &bd->srcbus; + } else if (!(cctl & PL080_CONTROL_SRC_INCR)) { + *mbus = &bd->srcbus; + *sbus = &bd->dstbus; } else { - if (bd->dstbus.buswidth == 4) { - *mbus = &bd->dstbus; - *sbus = &bd->srcbus; - } else if (bd->srcbus.buswidth == 4) { - *mbus = &bd->srcbus; - *sbus = &bd->dstbus; - } else if (bd->dstbus.buswidth == 2) { + if (bd->dstbus.buswidth >= bd->srcbus.buswidth) { *mbus = &bd->dstbus; *sbus = &bd->srcbus; - } else if (bd->srcbus.buswidth == 2) { + } else { *mbus = &bd->srcbus; *sbus = &bd->dstbus; - } else { - /* bd->srcbus.buswidth == 1 */ - *mbus = &bd->dstbus; - *sbus = &bd->srcbus; } } } From 0a2356572b1910cc977f4ccf3c9ee1ecab08327a Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 5 Aug 2011 15:32:42 +0530 Subject: [PATCH 0060/1531] dmaengine/amba-pl08x: Pass flow controller information with slave channel data At least, on SPEAr platforms there is one peripheral, JPEG, which can be flow controller for DMA transfer. Currently DMA controller driver didn't support peripheral flow controller configurations. This patch adds device_fc field in struct pl08x_channel_data, which will be used only for slave transfers and is not used in case of mem2mem transfers. Signed-off-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 61 +++++++++++++++++++++++++++++++++----- include/linux/amba/pl08x.h | 4 +++ 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index f70aa574c58f..a59c3c47286c 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -66,11 +66,6 @@ * after the final transfer signalled by LBREQ or LSREQ. The DMAC * will then move to the next LLI entry. * - * Only the former works sanely with scatter lists, so we only implement - * the DMAC flow control method. However, peripherals which use the LBREQ - * and LSREQ signals (eg, MMCI) are unable to use this mode, which through - * these hardware restrictions prevents them from using scatter DMA. - * * Global TODO: * - Break out common code from arch/arm/mach-s3c64xx and share */ @@ -617,6 +612,49 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, mbus == &bd.srcbus ? "src" : "dst", sbus == &bd.srcbus ? "src" : "dst"); + /* + * Zero length is only allowed if all these requirements are met: + * - flow controller is peripheral. + * - src.addr is aligned to src.width + * - dst.addr is aligned to dst.width + * + * sg_len == 1 should be true, as there can be two cases here: + * - Memory addresses are contiguous and are not scattered. Here, Only + * one sg will be passed by user driver, with memory address and zero + * length. We pass this to controller and after the transfer it will + * receive the last burst request from peripheral and so transfer + * finishes. + * + * - Memory addresses are scattered and are not contiguous. Here, + * Obviously as DMA controller doesn't know when a lli's transfer gets + * over, it can't load next lli. So in this case, there has to be an + * assumption that only one lli is supported. Thus, we can't have + * scattered addresses. + */ + if (!bd.remainder) { + u32 fc = (txd->ccfg & PL080_CONFIG_FLOW_CONTROL_MASK) >> + PL080_CONFIG_FLOW_CONTROL_SHIFT; + if (!((fc >= PL080_FLOW_SRC2DST_DST) && + (fc <= PL080_FLOW_SRC2DST_SRC))) { + dev_err(&pl08x->adev->dev, "%s sg len can't be zero", + __func__); + return 0; + } + + if ((bd.srcbus.addr % bd.srcbus.buswidth) || + (bd.srcbus.addr % bd.srcbus.buswidth)) { + dev_err(&pl08x->adev->dev, + "%s src & dst address must be aligned to src" + " & dst width if peripheral is flow controller", + __func__); + return 0; + } + + cctl = pl08x_cctl_bits(cctl, bd.srcbus.buswidth, + bd.dstbus.buswidth, 0); + pl08x_fill_lli_for_desc(&bd, num_llis++, 0, cctl); + } + /* * Send byte by byte for following cases * - Less than a bus width available @@ -1250,7 +1288,7 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); struct pl08x_driver_data *pl08x = plchan->host; struct pl08x_txd *txd; - int ret; + int ret, tmp; /* * Current implementation ASSUMES only one sg @@ -1284,12 +1322,10 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( txd->len = sgl->length; if (direction == DMA_TO_DEVICE) { - txd->ccfg |= PL080_FLOW_MEM2PER << PL080_CONFIG_FLOW_CONTROL_SHIFT; txd->cctl = plchan->dst_cctl; txd->src_addr = sgl->dma_address; txd->dst_addr = plchan->dst_addr; } else if (direction == DMA_FROM_DEVICE) { - txd->ccfg |= PL080_FLOW_PER2MEM << PL080_CONFIG_FLOW_CONTROL_SHIFT; txd->cctl = plchan->src_cctl; txd->src_addr = plchan->src_addr; txd->dst_addr = sgl->dma_address; @@ -1299,6 +1335,15 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( return NULL; } + if (plchan->cd->device_fc) + tmp = (direction == DMA_TO_DEVICE) ? PL080_FLOW_MEM2PER_PER : + PL080_FLOW_PER2MEM_PER; + else + tmp = (direction == DMA_TO_DEVICE) ? PL080_FLOW_MEM2PER : + PL080_FLOW_PER2MEM; + + txd->ccfg |= tmp << PL080_CONFIG_FLOW_CONTROL_SHIFT; + ret = pl08x_prep_channel_resources(plchan, txd); if (ret) return NULL; diff --git a/include/linux/amba/pl08x.h b/include/linux/amba/pl08x.h index ecd17f581e71..a22662c93981 100644 --- a/include/linux/amba/pl08x.h +++ b/include/linux/amba/pl08x.h @@ -47,6 +47,9 @@ enum { * @muxval: a number usually used to poke into some mux regiser to * mux in the signal to this channel * @cctl_opt: default options for the channel control register + * @device_fc: Flow Controller Settings for ccfg register. Only valid for slave + * channels. Fill with 'true' if peripheral should be flow controller. Direction + * will be selected at Runtime. * @addr: source/target address in physical memory for this DMA channel, * can be the address of a FIFO register for burst requests for example. * This can be left undefined if the PrimeCell API is used for configuring @@ -65,6 +68,7 @@ struct pl08x_channel_data { int max_signal; u32 muxval; u32 cctl; + bool device_fc; dma_addr_t addr; bool circular_buffer; bool single; From 57001a606f845ce2eda21a0f23e6aab20ee0cb04 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 5 Aug 2011 15:32:45 +0530 Subject: [PATCH 0061/1531] dmaengine/amba-pl08x: Call pl08x_free_txd() instead of calling kfree() directly pl08x_prep_channel_resources() is calling kfree() directly for txd(). To maintain consistency in code call pl08x_free_txd() instead. Signed-off-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index a59c3c47286c..849eab85514b 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -1174,7 +1174,9 @@ static int pl08x_prep_channel_resources(struct pl08x_dma_chan *plchan, num_llis = pl08x_fill_llis_for_desc(pl08x, txd); if (!num_llis) { - kfree(txd); + spin_lock_irqsave(&plchan->lock, flags); + pl08x_free_txd(pl08x, txd); + spin_unlock_irqrestore(&plchan->lock, flags); return -EINVAL; } From 1b60e77d8485841cf6278963b151a486fbae1445 Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Fri, 26 Aug 2011 14:02:43 -0700 Subject: [PATCH 0062/1531] [IA64[ add CONFIG_NET_VENDOR_INTEL=y to default config files where needed Jeff Kirsher's reorganization of network drivers means that we need to choose CONFIG_NET_VENDOR_INTEL before options like CONFIG_E100 and CONFIG_E1000 become visible. Fix the minimized ia64 config files so that we continue to have network drivers in the kernels that they build. Signed-off-by: Tony Luck --- arch/ia64/configs/generic_defconfig | 1 + arch/ia64/configs/gensparse_defconfig | 1 + arch/ia64/configs/tiger_defconfig | 1 + arch/ia64/configs/xen_domu_defconfig | 1 + arch/ia64/configs/zx1_defconfig | 1 + 5 files changed, 5 insertions(+) diff --git a/arch/ia64/configs/generic_defconfig b/arch/ia64/configs/generic_defconfig index 1d7bca0a396d..e9077c533205 100644 --- a/arch/ia64/configs/generic_defconfig +++ b/arch/ia64/configs/generic_defconfig @@ -87,6 +87,7 @@ CONFIG_NET_ETHERNET=y CONFIG_NET_TULIP=y CONFIG_TULIP=m CONFIG_NET_PCI=y +CONFIG_NET_VENDOR_INTEL=y CONFIG_E100=m CONFIG_E1000=y CONFIG_IGB=y diff --git a/arch/ia64/configs/gensparse_defconfig b/arch/ia64/configs/gensparse_defconfig index b11fa880e4b6..91c41ecfa6d9 100644 --- a/arch/ia64/configs/gensparse_defconfig +++ b/arch/ia64/configs/gensparse_defconfig @@ -75,6 +75,7 @@ CONFIG_NET_ETHERNET=y CONFIG_NET_TULIP=y CONFIG_TULIP=m CONFIG_NET_PCI=y +CONFIG_NET_VENDOR_INTEL=y CONFIG_E100=m CONFIG_E1000=y CONFIG_TIGON3=y diff --git a/arch/ia64/configs/tiger_defconfig b/arch/ia64/configs/tiger_defconfig index 137a453dd508..a5a9e02e60a0 100644 --- a/arch/ia64/configs/tiger_defconfig +++ b/arch/ia64/configs/tiger_defconfig @@ -77,6 +77,7 @@ CONFIG_NET_ETHERNET=y CONFIG_NET_TULIP=y CONFIG_TULIP=m CONFIG_NET_PCI=y +CONFIG_NET_VENDOR_INTEL=y CONFIG_E100=m CONFIG_E1000=y CONFIG_TIGON3=y diff --git a/arch/ia64/configs/xen_domu_defconfig b/arch/ia64/configs/xen_domu_defconfig index 2bf76e418bc5..37b9b422caad 100644 --- a/arch/ia64/configs/xen_domu_defconfig +++ b/arch/ia64/configs/xen_domu_defconfig @@ -80,6 +80,7 @@ CONFIG_NET_ETHERNET=y CONFIG_NET_TULIP=y CONFIG_TULIP=m CONFIG_NET_PCI=y +CONFIG_NET_VENDOR_INTEL=y CONFIG_E100=m CONFIG_E1000=y CONFIG_TIGON3=y diff --git a/arch/ia64/configs/zx1_defconfig b/arch/ia64/configs/zx1_defconfig index 1d42827f9fa4..fc7aba07c2b4 100644 --- a/arch/ia64/configs/zx1_defconfig +++ b/arch/ia64/configs/zx1_defconfig @@ -57,6 +57,7 @@ CONFIG_TULIP_MMIO=y CONFIG_TULIP_NAPI=y CONFIG_TULIP_NAPI_HW_MITIGATION=y CONFIG_NET_PCI=y +CONFIG_NET_VENDOR_INTEL=y CONFIG_E100=y CONFIG_E1000=y CONFIG_TIGON3=y From 09dafe9e4e266c7868454a532c9ca762aa5c40a5 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Thu, 18 Aug 2011 04:32:08 +0000 Subject: [PATCH 0063/1531] ARM: mach-shmobile: sh73a0 PFC pull-up support for SDHI0+2 Extend the existing sh73a0 PFC code with pull-ups for SDHI0 and SDHI2. Without this patch only SDHI1 has pull-up support on sh73a0. Needed by boards that make use of the internal pull-up resistor support built in the SoC. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- arch/arm/mach-shmobile/include/mach/sh73a0.h | 13 ++++ arch/arm/mach-shmobile/pfc-sh73a0.c | 77 ++++++++++++++++---- 2 files changed, 75 insertions(+), 15 deletions(-) diff --git a/arch/arm/mach-shmobile/include/mach/sh73a0.h b/arch/arm/mach-shmobile/include/mach/sh73a0.h index 216c3d695ef1..b385e976797a 100644 --- a/arch/arm/mach-shmobile/include/mach/sh73a0.h +++ b/arch/arm/mach-shmobile/include/mach/sh73a0.h @@ -451,11 +451,23 @@ enum { GPIO_FN_KEYIN5_PU, GPIO_FN_KEYIN6_PU, GPIO_FN_KEYIN7_PU, + GPIO_FN_SDHICD0_PU, + GPIO_FN_SDHID0_0_PU, + GPIO_FN_SDHID0_1_PU, + GPIO_FN_SDHID0_2_PU, + GPIO_FN_SDHID0_3_PU, + GPIO_FN_SDHICMD0_PU, + GPIO_FN_SDHIWP0_PU, GPIO_FN_SDHID1_0_PU, GPIO_FN_SDHID1_1_PU, GPIO_FN_SDHID1_2_PU, GPIO_FN_SDHID1_3_PU, GPIO_FN_SDHICMD1_PU, + GPIO_FN_SDHID2_0_PU, + GPIO_FN_SDHID2_1_PU, + GPIO_FN_SDHID2_2_PU, + GPIO_FN_SDHID2_3_PU, + GPIO_FN_SDHICMD2_PU, GPIO_FN_MMCCMD0_PU, GPIO_FN_MMCCMD1_PU, GPIO_FN_FSIACK_PU, @@ -463,6 +475,7 @@ enum { GPIO_FN_FSIAIBT_PU, GPIO_FN_FSIAISLD_PU, }; + /* DMA slave IDs */ enum { SHDMA_SLAVE_INVALID, diff --git a/arch/arm/mach-shmobile/pfc-sh73a0.c b/arch/arm/mach-shmobile/pfc-sh73a0.c index 3eed44eb98b4..a26d9059036b 100644 --- a/arch/arm/mach-shmobile/pfc-sh73a0.c +++ b/arch/arm/mach-shmobile/pfc-sh73a0.c @@ -488,13 +488,26 @@ enum { KEYIN5_PU_MARK, KEYIN6_PU_MARK, KEYIN7_PU_MARK, + SDHICD0_PU_MARK, + SDHID0_0_PU_MARK, + SDHID0_1_PU_MARK, + SDHID0_2_PU_MARK, + SDHID0_3_PU_MARK, + SDHICMD0_PU_MARK, + SDHIWP0_PU_MARK, SDHID1_0_PU_MARK, SDHID1_1_PU_MARK, SDHID1_2_PU_MARK, SDHID1_3_PU_MARK, SDHICMD1_PU_MARK, + SDHID2_0_PU_MARK, + SDHID2_1_PU_MARK, + SDHID2_2_PU_MARK, + SDHID2_3_PU_MARK, + SDHICMD2_PU_MARK, MMCCMD0_PU_MARK, MMCCMD1_PU_MARK, + FSIBISLD_PU_MARK, FSIACK_PU_MARK, FSIAILR_PU_MARK, FSIAIBT_PU_MARK, @@ -1387,19 +1400,28 @@ static pinmux_enum_t pinmux_data[] = { PINMUX_DATA(TS_SCK4_MARK, PORT268_FN3), PINMUX_DATA(SDHICMD2_MARK, PORT269_FN1), PINMUX_DATA(MMCCLK0_MARK, PORT270_FN1, MSEL4CR_MSEL15_0), - PINMUX_DATA(MMCD0_0_MARK, PORT271_FN1, MSEL4CR_MSEL15_0), - PINMUX_DATA(MMCD0_1_MARK, PORT272_FN1, MSEL4CR_MSEL15_0), - PINMUX_DATA(MMCD0_2_MARK, PORT273_FN1, MSEL4CR_MSEL15_0), - PINMUX_DATA(MMCD0_3_MARK, PORT274_FN1, MSEL4CR_MSEL15_0), - PINMUX_DATA(MMCD0_4_MARK, PORT275_FN1, MSEL4CR_MSEL15_0), \ + PINMUX_DATA(MMCD0_0_MARK, PORT271_FN1, PORT271_IN_PU, + MSEL4CR_MSEL15_0), + PINMUX_DATA(MMCD0_1_MARK, PORT272_FN1, PORT272_IN_PU, + MSEL4CR_MSEL15_0), + PINMUX_DATA(MMCD0_2_MARK, PORT273_FN1, PORT273_IN_PU, + MSEL4CR_MSEL15_0), + PINMUX_DATA(MMCD0_3_MARK, PORT274_FN1, PORT274_IN_PU, + MSEL4CR_MSEL15_0), + PINMUX_DATA(MMCD0_4_MARK, PORT275_FN1, PORT275_IN_PU, + MSEL4CR_MSEL15_0), \ PINMUX_DATA(TS_SPSYNC5_MARK, PORT275_FN3), - PINMUX_DATA(MMCD0_5_MARK, PORT276_FN1, MSEL4CR_MSEL15_0), \ + PINMUX_DATA(MMCD0_5_MARK, PORT276_FN1, PORT276_IN_PU, + MSEL4CR_MSEL15_0), \ PINMUX_DATA(TS_SDAT5_MARK, PORT276_FN3), - PINMUX_DATA(MMCD0_6_MARK, PORT277_FN1, MSEL4CR_MSEL15_0), \ + PINMUX_DATA(MMCD0_6_MARK, PORT277_FN1, PORT277_IN_PU, + MSEL4CR_MSEL15_0), \ PINMUX_DATA(TS_SDEN5_MARK, PORT277_FN3), - PINMUX_DATA(MMCD0_7_MARK, PORT278_FN1, MSEL4CR_MSEL15_0), \ + PINMUX_DATA(MMCD0_7_MARK, PORT278_FN1, PORT278_IN_PU, + MSEL4CR_MSEL15_0), \ PINMUX_DATA(TS_SCK5_MARK, PORT278_FN3), - PINMUX_DATA(MMCCMD0_MARK, PORT279_FN1, MSEL4CR_MSEL15_0), + PINMUX_DATA(MMCCMD0_MARK, PORT279_FN1, PORT279_IN_PU, + MSEL4CR_MSEL15_0), PINMUX_DATA(RESETOUTS__MARK, PORT281_FN1), \ PINMUX_DATA(EXTAL2OUT_MARK, PORT281_FN2), PINMUX_DATA(MCP_WAIT__MCP_FRB_MARK, PORT288_FN1), @@ -1516,16 +1538,29 @@ static pinmux_enum_t pinmux_data[] = { PINMUX_DATA(KEYIN6_PU_MARK, PORT72_FN2, PORT72_IN_PU), PINMUX_DATA(KEYIN7_PU_MARK, PORT73_FN2, PORT73_IN_PU), - PINMUX_DATA(SDHID1_0_PU_MARK, PORT259_IN_PU, PORT259_FN1), - PINMUX_DATA(SDHID1_1_PU_MARK, PORT260_IN_PU, PORT260_FN1), - PINMUX_DATA(SDHID1_2_PU_MARK, PORT261_IN_PU, PORT261_FN1), - PINMUX_DATA(SDHID1_3_PU_MARK, PORT262_IN_PU, PORT262_FN1), - PINMUX_DATA(SDHICMD1_PU_MARK, PORT263_IN_PU, PORT263_FN1), + PINMUX_DATA(SDHICD0_PU_MARK, PORT251_FN1, PORT251_IN_PU), + PINMUX_DATA(SDHID0_0_PU_MARK, PORT252_FN1, PORT252_IN_PU), + PINMUX_DATA(SDHID0_1_PU_MARK, PORT253_FN1, PORT253_IN_PU), + PINMUX_DATA(SDHID0_2_PU_MARK, PORT254_FN1, PORT254_IN_PU), + PINMUX_DATA(SDHID0_3_PU_MARK, PORT255_FN1, PORT255_IN_PU), + PINMUX_DATA(SDHICMD0_PU_MARK, PORT256_FN1, PORT256_IN_PU), + PINMUX_DATA(SDHIWP0_PU_MARK, PORT257_FN1, PORT256_IN_PU), + PINMUX_DATA(SDHID1_0_PU_MARK, PORT259_FN1, PORT259_IN_PU), + PINMUX_DATA(SDHID1_1_PU_MARK, PORT260_FN1, PORT260_IN_PU), + PINMUX_DATA(SDHID1_2_PU_MARK, PORT261_FN1, PORT261_IN_PU), + PINMUX_DATA(SDHID1_3_PU_MARK, PORT262_FN1, PORT262_IN_PU), + PINMUX_DATA(SDHICMD1_PU_MARK, PORT263_FN1, PORT263_IN_PU), + PINMUX_DATA(SDHID2_0_PU_MARK, PORT265_FN1, PORT265_IN_PU), + PINMUX_DATA(SDHID2_1_PU_MARK, PORT266_FN1, PORT266_IN_PU), + PINMUX_DATA(SDHID2_2_PU_MARK, PORT267_FN1, PORT267_IN_PU), + PINMUX_DATA(SDHID2_3_PU_MARK, PORT268_FN1, PORT268_IN_PU), + PINMUX_DATA(SDHICMD2_PU_MARK, PORT269_FN1, PORT269_IN_PU), PINMUX_DATA(MMCCMD0_PU_MARK, PORT279_FN1, PORT279_IN_PU, MSEL4CR_MSEL15_0), - PINMUX_DATA(MMCCMD1_PU_MARK, PORT297_FN2, PORT279_IN_PU, + PINMUX_DATA(MMCCMD1_PU_MARK, PORT297_FN2, PORT297_IN_PU, MSEL4CR_MSEL15_1), + PINMUX_DATA(FSIBISLD_PU_MARK, PORT39_FN1, PORT39_IN_PU), PINMUX_DATA(FSIACK_PU_MARK, PORT49_FN1, PORT49_IN_PU), PINMUX_DATA(FSIAILR_PU_MARK, PORT50_FN5, PORT50_IN_PU), PINMUX_DATA(FSIAIBT_PU_MARK, PORT51_FN5, PORT51_IN_PU), @@ -2181,11 +2216,23 @@ static struct pinmux_gpio pinmux_gpios[] = { GPIO_FN(KEYIN5_PU), GPIO_FN(KEYIN6_PU), GPIO_FN(KEYIN7_PU), + GPIO_FN(SDHICD0_PU), + GPIO_FN(SDHID0_0_PU), + GPIO_FN(SDHID0_1_PU), + GPIO_FN(SDHID0_2_PU), + GPIO_FN(SDHID0_3_PU), + GPIO_FN(SDHICMD0_PU), + GPIO_FN(SDHIWP0_PU), GPIO_FN(SDHID1_0_PU), GPIO_FN(SDHID1_1_PU), GPIO_FN(SDHID1_2_PU), GPIO_FN(SDHID1_3_PU), GPIO_FN(SDHICMD1_PU), + GPIO_FN(SDHID2_0_PU), + GPIO_FN(SDHID2_1_PU), + GPIO_FN(SDHID2_2_PU), + GPIO_FN(SDHID2_3_PU), + GPIO_FN(SDHICMD2_PU), GPIO_FN(MMCCMD0_PU), GPIO_FN(MMCCMD1_PU), GPIO_FN(FSIACK_PU), From 28626632d83696ab3c8f2b9d5d8a658a1787551f Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Thu, 18 Aug 2011 05:44:07 +0000 Subject: [PATCH 0064/1531] ARM: mach-shmobile: Kota2 SCIFA2 and SMSC911X support Kota2 base board support including the on-chip SCIFA2 serial console and the on-board SMSC911X ethernet port. The s73a0 SMP bits are also updated to include Kota2. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- arch/arm/mach-shmobile/Kconfig | 7 ++ arch/arm/mach-shmobile/Makefile | 1 + arch/arm/mach-shmobile/board-kota2.c | 168 +++++++++++++++++++++++++++ arch/arm/mach-shmobile/platsmp.c | 10 +- 4 files changed, 182 insertions(+), 4 deletions(-) create mode 100644 arch/arm/mach-shmobile/board-kota2.c diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig index 0c8f6cf3e948..c1b45342e1fb 100644 --- a/arch/arm/mach-shmobile/Kconfig +++ b/arch/arm/mach-shmobile/Kconfig @@ -69,6 +69,11 @@ config MACH_MACKEREL depends on ARCH_SH7372 select ARCH_REQUIRE_GPIOLIB +config MACH_KOTA2 + bool "KOTA2 board" + select ARCH_REQUIRE_GPIOLIB + depends on ARCH_SH73A0 + comment "SH-Mobile System Configuration" menu "Memory configuration" @@ -78,6 +83,7 @@ config MEMORY_START default "0x50000000" if MACH_G3EVM default "0x40000000" if MACH_G4EVM || MACH_AP4EVB || MACH_AG5EVM || \ MACH_MACKEREL + default "0x41000000" if MACH_KOTA2 default "0x00000000" ---help--- Tweak this only when porting to a new machine which does not @@ -89,6 +95,7 @@ config MEMORY_SIZE default "0x08000000" if MACH_G3EVM default "0x08000000" if MACH_G4EVM default "0x20000000" if MACH_AG5EVM + default "0x1e000000" if MACH_KOTA2 default "0x10000000" if MACH_AP4EVB || MACH_MACKEREL default "0x04000000" help diff --git a/arch/arm/mach-shmobile/Makefile b/arch/arm/mach-shmobile/Makefile index 612b27000c3e..2aec2f732515 100644 --- a/arch/arm/mach-shmobile/Makefile +++ b/arch/arm/mach-shmobile/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_MACH_G4EVM) += board-g4evm.o obj-$(CONFIG_MACH_AP4EVB) += board-ap4evb.o obj-$(CONFIG_MACH_AG5EVM) += board-ag5evm.o obj-$(CONFIG_MACH_MACKEREL) += board-mackerel.o +obj-$(CONFIG_MACH_KOTA2) += board-kota2.o # Framework support obj-$(CONFIG_SMP) += $(smp-y) diff --git a/arch/arm/mach-shmobile/board-kota2.c b/arch/arm/mach-shmobile/board-kota2.c new file mode 100644 index 000000000000..4ab05e855077 --- /dev/null +++ b/arch/arm/mach-shmobile/board-kota2.c @@ -0,0 +1,168 @@ +/* + * kota2 board support + * + * Copyright (C) 2011 Renesas Solutions Corp. + * Copyright (C) 2011 Magnus Damm + * Copyright (C) 2010 Takashi Yoshii + * Copyright (C) 2009 Yoshihiro Shimoda + * + * 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; version 2 of the License. + * + * 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 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 + +static struct resource smsc9220_resources[] = { + [0] = { + .start = 0x14000000, /* CS5A */ + .end = 0x140000ff, /* A1->A7 */ + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = gic_spi(33), /* PINTA2 @ PORT144 */ + .flags = IORESOURCE_IRQ, + }, +}; + +static struct smsc911x_platform_config smsc9220_platdata = { + .flags = SMSC911X_USE_32BIT, /* 32-bit SW on 16-bit HW bus */ + .phy_interface = PHY_INTERFACE_MODE_MII, + .irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_LOW, + .irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL, +}; + +static struct platform_device eth_device = { + .name = "smsc911x", + .id = 0, + .dev = { + .platform_data = &smsc9220_platdata, + }, + .resource = smsc9220_resources, + .num_resources = ARRAY_SIZE(smsc9220_resources), +}; + +static struct platform_device *kota2_devices[] __initdata = { + ð_device, +}; + +static struct map_desc kota2_io_desc[] __initdata = { + /* create a 1:1 entity map for 0xe6xxxxxx + * used by CPGA, INTC and PFC. + */ + { + .virtual = 0xe6000000, + .pfn = __phys_to_pfn(0xe6000000), + .length = 256 << 20, + .type = MT_DEVICE_NONSHARED + }, +}; + +static void __init kota2_map_io(void) +{ + iotable_init(kota2_io_desc, ARRAY_SIZE(kota2_io_desc)); + + /* setup early devices and console here as well */ + sh73a0_add_early_devices(); + shmobile_setup_console(); +} + +#define PINTER0A 0xe69000a0 +#define PINTCR0A 0xe69000b0 + +void __init kota2_init_irq(void) +{ + sh73a0_init_irq(); + + /* setup PINT: enable PINTA2 as active low */ + __raw_writel(1 << 29, PINTER0A); + __raw_writew(2 << 10, PINTCR0A); +} + +static void __init kota2_init(void) +{ + sh73a0_pinmux_init(); + + /* SCIFA2 (UART2) */ + gpio_request(GPIO_FN_SCIFA2_TXD1, NULL); + gpio_request(GPIO_FN_SCIFA2_RXD1, NULL); + gpio_request(GPIO_FN_SCIFA2_RTS1_, NULL); + gpio_request(GPIO_FN_SCIFA2_CTS1_, NULL); + + /* SMSC911X */ + gpio_request(GPIO_FN_D0_NAF0, NULL); + gpio_request(GPIO_FN_D1_NAF1, NULL); + gpio_request(GPIO_FN_D2_NAF2, NULL); + gpio_request(GPIO_FN_D3_NAF3, NULL); + gpio_request(GPIO_FN_D4_NAF4, NULL); + gpio_request(GPIO_FN_D5_NAF5, NULL); + gpio_request(GPIO_FN_D6_NAF6, NULL); + gpio_request(GPIO_FN_D7_NAF7, NULL); + gpio_request(GPIO_FN_D8_NAF8, NULL); + gpio_request(GPIO_FN_D9_NAF9, NULL); + gpio_request(GPIO_FN_D10_NAF10, NULL); + gpio_request(GPIO_FN_D11_NAF11, NULL); + gpio_request(GPIO_FN_D12_NAF12, NULL); + gpio_request(GPIO_FN_D13_NAF13, NULL); + gpio_request(GPIO_FN_D14_NAF14, NULL); + gpio_request(GPIO_FN_D15_NAF15, NULL); + gpio_request(GPIO_FN_CS5A_, NULL); + gpio_request(GPIO_FN_WE0__FWE, NULL); + gpio_request(GPIO_PORT144, NULL); /* PINTA2 */ + gpio_direction_input(GPIO_PORT144); + gpio_request(GPIO_PORT145, NULL); /* RESET */ + gpio_direction_output(GPIO_PORT145, 1); + +#ifdef CONFIG_CACHE_L2X0 + /* Early BRESP enable, Shared attribute override enable, 64K*8way */ + l2x0_init(__io(0xf0100000), 0x40460000, 0x82000fff); +#endif + sh73a0_add_standard_devices(); + platform_add_devices(kota2_devices, ARRAY_SIZE(kota2_devices)); +} + +static void __init kota2_timer_init(void) +{ + sh73a0_clock_init(); + shmobile_timer.init(); + return; +} + +struct sys_timer kota2_timer = { + .init = kota2_timer_init, +}; + +MACHINE_START(KOTA2, "kota2") + .map_io = kota2_map_io, + .init_irq = kota2_init_irq, + .handle_irq = shmobile_handle_irq_gic, + .init_machine = kota2_init, + .timer = &kota2_timer, +MACHINE_END diff --git a/arch/arm/mach-shmobile/platsmp.c b/arch/arm/mach-shmobile/platsmp.c index 66f980625a33..ccd81dac42f8 100644 --- a/arch/arm/mach-shmobile/platsmp.c +++ b/arch/arm/mach-shmobile/platsmp.c @@ -21,9 +21,11 @@ #include #include +#define is_sh73a0() (machine_is_ag5evm() || machine_is_kota2()) + static unsigned int __init shmobile_smp_get_core_count(void) { - if (machine_is_ag5evm()) + if (is_sh73a0()) return sh73a0_get_core_count(); return 1; @@ -31,7 +33,7 @@ static unsigned int __init shmobile_smp_get_core_count(void) static void __init shmobile_smp_prepare_cpus(void) { - if (machine_is_ag5evm()) + if (is_sh73a0()) sh73a0_smp_prepare_cpus(); } @@ -39,13 +41,13 @@ void __cpuinit platform_secondary_init(unsigned int cpu) { trace_hardirqs_off(); - if (machine_is_ag5evm()) + if (is_sh73a0()) sh73a0_secondary_init(cpu); } int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) { - if (machine_is_ag5evm()) + if (is_sh73a0()) return sh73a0_boot_secondary(cpu); return -ENOSYS; From ef4f994ae11c0a587e2554b1a6b8b0e1dfa07c4e Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Thu, 18 Aug 2011 05:44:16 +0000 Subject: [PATCH 0065/1531] ARM: mach-shmobile: Kota2 KEYSC support This patch adds KEYSC support to the Kota2 board. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- arch/arm/mach-shmobile/board-kota2.c | 65 ++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/arch/arm/mach-shmobile/board-kota2.c b/arch/arm/mach-shmobile/board-kota2.c index 4ab05e855077..8a6b85a52cb7 100644 --- a/arch/arm/mach-shmobile/board-kota2.c +++ b/arch/arm/mach-shmobile/board-kota2.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include #include @@ -69,8 +71,52 @@ static struct platform_device eth_device = { .num_resources = ARRAY_SIZE(smsc9220_resources), }; +static struct sh_keysc_info keysc_platdata = { + .mode = SH_KEYSC_MODE_6, + .scan_timing = 3, + .delay = 100, + .keycodes = { + KEY_NUMERIC_STAR, KEY_NUMERIC_0, KEY_NUMERIC_POUND, + 0, 0, 0, 0, 0, + KEY_NUMERIC_7, KEY_NUMERIC_8, KEY_NUMERIC_9, + 0, KEY_DOWN, 0, 0, 0, + KEY_NUMERIC_4, KEY_NUMERIC_5, KEY_NUMERIC_6, + KEY_LEFT, KEY_ENTER, KEY_RIGHT, 0, 0, + KEY_NUMERIC_1, KEY_NUMERIC_2, KEY_NUMERIC_3, + 0, KEY_UP, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + }, +}; + +static struct resource keysc_resources[] = { + [0] = { + .name = "KEYSC", + .start = 0xe61b0000, + .end = 0xe61b0098 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = gic_spi(71), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device keysc_device = { + .name = "sh_keysc", + .id = 0, + .num_resources = ARRAY_SIZE(keysc_resources), + .resource = keysc_resources, + .dev = { + .platform_data = &keysc_platdata, + }, +}; + static struct platform_device *kota2_devices[] __initdata = { ð_device, + &keysc_device, }; static struct map_desc kota2_io_desc[] __initdata = { @@ -140,6 +186,25 @@ static void __init kota2_init(void) gpio_request(GPIO_PORT145, NULL); /* RESET */ gpio_direction_output(GPIO_PORT145, 1); + /* KEYSC */ + gpio_request(GPIO_FN_KEYIN0_PU, NULL); + gpio_request(GPIO_FN_KEYIN1_PU, NULL); + gpio_request(GPIO_FN_KEYIN2_PU, NULL); + gpio_request(GPIO_FN_KEYIN3_PU, NULL); + gpio_request(GPIO_FN_KEYIN4_PU, NULL); + gpio_request(GPIO_FN_KEYIN5_PU, NULL); + gpio_request(GPIO_FN_KEYIN6_PU, NULL); + gpio_request(GPIO_FN_KEYIN7_PU, NULL); + gpio_request(GPIO_FN_KEYOUT0, NULL); + gpio_request(GPIO_FN_KEYOUT1, NULL); + gpio_request(GPIO_FN_KEYOUT2, NULL); + gpio_request(GPIO_FN_KEYOUT3, NULL); + gpio_request(GPIO_FN_KEYOUT4, NULL); + gpio_request(GPIO_FN_KEYOUT5, NULL); + gpio_request(GPIO_FN_PORT59_KEYOUT6, NULL); + gpio_request(GPIO_FN_PORT58_KEYOUT7, NULL); + gpio_request(GPIO_FN_KEYOUT8, NULL); + #ifdef CONFIG_CACHE_L2X0 /* Early BRESP enable, Shared attribute override enable, 64K*8way */ l2x0_init(__io(0xf0100000), 0x40460000, 0x82000fff); From 6b7c0ea21226972e08ebb71e134fbebdf7459d1a Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Thu, 18 Aug 2011 05:44:25 +0000 Subject: [PATCH 0066/1531] ARM: mach-shmobile: Kota2 GPIO Keys support This patch ties in GPIO Keys support on the Kota2 board. For now the keys are used in polling mode, but after extending the sh73a0 PFC with IRQ support we should be able to switch to the IRQ driven driver. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- arch/arm/mach-shmobile/board-kota2.c | 32 ++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/arch/arm/mach-shmobile/board-kota2.c b/arch/arm/mach-shmobile/board-kota2.c index 8a6b85a52cb7..849423cddca9 100644 --- a/arch/arm/mach-shmobile/board-kota2.c +++ b/arch/arm/mach-shmobile/board-kota2.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -114,9 +115,40 @@ static struct platform_device keysc_device = { }, }; +#define GPIO_KEY(c, g, d) { .code = c, .gpio = g, .desc = d, .active_low = 1 } + +static struct gpio_keys_button gpio_buttons[] = { + GPIO_KEY(KEY_VOLUMEUP, GPIO_PORT56, "+"), /* S2: VOL+ [IRQ9] */ + GPIO_KEY(KEY_VOLUMEDOWN, GPIO_PORT54, "-"), /* S3: VOL- [IRQ10] */ + GPIO_KEY(KEY_MENU, GPIO_PORT27, "Menu"), /* S4: MENU [IRQ30] */ + GPIO_KEY(KEY_HOMEPAGE, GPIO_PORT26, "Home"), /* S5: HOME [IRQ31] */ + GPIO_KEY(KEY_BACK, GPIO_PORT11, "Back"), /* S6: BACK [IRQ0] */ + GPIO_KEY(KEY_PHONE, GPIO_PORT238, "Tel"), /* S7: TEL [IRQ11] */ + GPIO_KEY(KEY_POWER, GPIO_PORT239, "C1"), /* S8: CAM [IRQ13] */ + GPIO_KEY(KEY_MAIL, GPIO_PORT224, "Mail"), /* S9: MAIL [IRQ3] */ + /* Omitted button "C3?": GPIO_PORT223 - S10: CUST [IRQ8] */ + GPIO_KEY(KEY_CAMERA, GPIO_PORT164, "C2"), /* S11: CAM_HALF [IRQ25] */ + /* Omitted button "?": GPIO_PORT152 - S12: CAM_FULL [No IRQ] */ +}; + +static struct gpio_keys_platform_data gpio_key_info = { + .buttons = gpio_buttons, + .nbuttons = ARRAY_SIZE(gpio_buttons), + .poll_interval = 250, /* polled for now */ +}; + +static struct platform_device gpio_keys_device = { + .name = "gpio-keys-polled", /* polled for now */ + .id = -1, + .dev = { + .platform_data = &gpio_key_info, + }, +}; + static struct platform_device *kota2_devices[] __initdata = { ð_device, &keysc_device, + &gpio_keys_device, }; static struct map_desc kota2_io_desc[] __initdata = { From ae6e70831805c1d3bb4cdb87a29877b9846d3c15 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Thu, 18 Aug 2011 05:44:33 +0000 Subject: [PATCH 0067/1531] ARM: mach-shmobile: Kota2 GPIO LEDs support This patch ties in GPIO LEDs support on the Kota2 board. For now all LEDs are driven by the GPIO LED driver, but in the not so distant future the LEDs hooked up to TPU pin functions will be moved over to the recently posted LED TPU driver. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- arch/arm/mach-shmobile/board-kota2.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/arch/arm/mach-shmobile/board-kota2.c b/arch/arm/mach-shmobile/board-kota2.c index 849423cddca9..d46bd9f7cea5 100644 --- a/arch/arm/mach-shmobile/board-kota2.c +++ b/arch/arm/mach-shmobile/board-kota2.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -145,10 +146,36 @@ static struct platform_device gpio_keys_device = { }, }; +#define GPIO_LED(n, g) { .name = n, .gpio = g } + +static struct gpio_led gpio_leds[] = { + GPIO_LED("V2513", GPIO_PORT153), /* PORT153 [TPU1T02] -> V2513 */ + GPIO_LED("V2514", GPIO_PORT199), /* PORT199 [TPU4TO1] -> V2514 */ + GPIO_LED("V2515", GPIO_PORT197), /* PORT197 [TPU2TO1] -> V2515 */ + GPIO_LED("KEYLED", GPIO_PORT163), /* PORT163 [TPU3TO0] -> KEYLED */ + GPIO_LED("G", GPIO_PORT20), /* PORT20 [GPO0] -> LED7 -> "G" */ + GPIO_LED("H", GPIO_PORT21), /* PORT21 [GPO1] -> LED8 -> "H" */ + GPIO_LED("J", GPIO_PORT22), /* PORT22 [GPO2] -> LED9 -> "J" */ +}; + +static struct gpio_led_platform_data gpio_leds_info = { + .leds = gpio_leds, + .num_leds = ARRAY_SIZE(gpio_leds), +}; + +static struct platform_device gpio_leds_device = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &gpio_leds_info, + }, +}; + static struct platform_device *kota2_devices[] __initdata = { ð_device, &keysc_device, &gpio_keys_device, + &gpio_leds_device, }; static struct map_desc kota2_io_desc[] __initdata = { From 4e9279452502c043469cf34cd813db83ae87c7d3 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Thu, 18 Aug 2011 05:44:42 +0000 Subject: [PATCH 0068/1531] ARM: mach-shmobile: Kota2 MMCIF support Add support for sh73a0 MMCIF hardware block on the Kota2 board. A non-removable eMMC chip is used with 8 data bits on the MMC bus. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- arch/arm/mach-shmobile/board-kota2.c | 49 ++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/arch/arm/mach-shmobile/board-kota2.c b/arch/arm/mach-shmobile/board-kota2.c index d46bd9f7cea5..e1380df01315 100644 --- a/arch/arm/mach-shmobile/board-kota2.c +++ b/arch/arm/mach-shmobile/board-kota2.c @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #include #include @@ -171,11 +173,44 @@ static struct platform_device gpio_leds_device = { }, }; +static struct resource mmcif_resources[] = { + [0] = { + .name = "MMCIF", + .start = 0xe6bd0000, + .end = 0xe6bd00ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = gic_spi(140), + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = gic_spi(141), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct sh_mmcif_plat_data mmcif_info = { + .ocr = MMC_VDD_165_195, + .caps = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE, +}; + +static struct platform_device mmcif_device = { + .name = "sh_mmcif", + .id = 0, + .dev = { + .platform_data = &mmcif_info, + }, + .num_resources = ARRAY_SIZE(mmcif_resources), + .resource = mmcif_resources, +}; + static struct platform_device *kota2_devices[] __initdata = { ð_device, &keysc_device, &gpio_keys_device, &gpio_leds_device, + &mmcif_device, }; static struct map_desc kota2_io_desc[] __initdata = { @@ -264,6 +299,20 @@ static void __init kota2_init(void) gpio_request(GPIO_FN_PORT58_KEYOUT7, NULL); gpio_request(GPIO_FN_KEYOUT8, NULL); + /* MMCIF */ + gpio_request(GPIO_FN_MMCCLK0, NULL); + gpio_request(GPIO_FN_MMCD0_0, NULL); + gpio_request(GPIO_FN_MMCD0_1, NULL); + gpio_request(GPIO_FN_MMCD0_2, NULL); + gpio_request(GPIO_FN_MMCD0_3, NULL); + gpio_request(GPIO_FN_MMCD0_4, NULL); + gpio_request(GPIO_FN_MMCD0_5, NULL); + gpio_request(GPIO_FN_MMCD0_6, NULL); + gpio_request(GPIO_FN_MMCD0_7, NULL); + gpio_request(GPIO_FN_MMCCMD0, NULL); + gpio_request(GPIO_PORT208, NULL); /* Reset */ + gpio_direction_output(GPIO_PORT208, 1); + #ifdef CONFIG_CACHE_L2X0 /* Early BRESP enable, Shared attribute override enable, 64K*8way */ l2x0_init(__io(0xf0100000), 0x40460000, 0x82000fff); From 9e9a892417317c3c7fc98f9a87f51c0e7b013b36 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Thu, 18 Aug 2011 05:44:51 +0000 Subject: [PATCH 0069/1531] ARM: mach-shmobile: Kota2 SCIFA4 and SCIFB support Add SCIFA4 and SCIFB support to the Kota2 board. Only pins are configured since the SCIF platform devices are already present in the sh73a0 code. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- arch/arm/mach-shmobile/board-kota2.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/arch/arm/mach-shmobile/board-kota2.c b/arch/arm/mach-shmobile/board-kota2.c index e1380df01315..0dbee59bc6d9 100644 --- a/arch/arm/mach-shmobile/board-kota2.c +++ b/arch/arm/mach-shmobile/board-kota2.c @@ -256,6 +256,12 @@ static void __init kota2_init(void) gpio_request(GPIO_FN_SCIFA2_RTS1_, NULL); gpio_request(GPIO_FN_SCIFA2_CTS1_, NULL); + /* SCIFA4 (UART1) */ + gpio_request(GPIO_FN_SCIFA4_TXD, NULL); + gpio_request(GPIO_FN_SCIFA4_RXD, NULL); + gpio_request(GPIO_FN_SCIFA4_RTS_, NULL); + gpio_request(GPIO_FN_SCIFA4_CTS_, NULL); + /* SMSC911X */ gpio_request(GPIO_FN_D0_NAF0, NULL); gpio_request(GPIO_FN_D1_NAF1, NULL); @@ -313,6 +319,13 @@ static void __init kota2_init(void) gpio_request(GPIO_PORT208, NULL); /* Reset */ gpio_direction_output(GPIO_PORT208, 1); + /* SCIFB (BT) */ + gpio_request(GPIO_FN_PORT159_SCIFB_SCK, NULL); + gpio_request(GPIO_FN_PORT160_SCIFB_TXD, NULL); + gpio_request(GPIO_FN_PORT161_SCIFB_CTS_, NULL); + gpio_request(GPIO_FN_PORT162_SCIFB_RXD, NULL); + gpio_request(GPIO_FN_PORT163_SCIFB_RTS_, NULL); + #ifdef CONFIG_CACHE_L2X0 /* Early BRESP enable, Shared attribute override enable, 64K*8way */ l2x0_init(__io(0xf0100000), 0x40460000, 0x82000fff); From 8722c996d61948a57ada840350b0383f6879bcaa Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Thu, 18 Aug 2011 05:45:00 +0000 Subject: [PATCH 0070/1531] ARM: mach-shmobile: Kota2 SDHI0 and SDHI1 support Add SDHI0 and SDHI1 support to the Kota2 board. SDHI0 is hooked up to a microSD card slot and SDHI1 to a wireless module that also connects to SCIFB. This depends on the recently merged code for TMIO_MMC_HAS_IDLE_WAIT together with PFC support for pull-ups on SDHI0 and SDHI1. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- arch/arm/mach-shmobile/board-kota2.c | 93 ++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/arch/arm/mach-shmobile/board-kota2.c b/arch/arm/mach-shmobile/board-kota2.c index 0dbee59bc6d9..adc73122bf20 100644 --- a/arch/arm/mach-shmobile/board-kota2.c +++ b/arch/arm/mach-shmobile/board-kota2.c @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include #include #include @@ -205,12 +207,86 @@ static struct platform_device mmcif_device = { .resource = mmcif_resources, }; +static struct sh_mobile_sdhi_info sdhi0_info = { + .tmio_caps = MMC_CAP_SD_HIGHSPEED, + .tmio_flags = TMIO_MMC_WRPROTECT_DISABLE | TMIO_MMC_HAS_IDLE_WAIT, +}; + +static struct resource sdhi0_resources[] = { + [0] = { + .name = "SDHI0", + .start = 0xee100000, + .end = 0xee1000ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = gic_spi(83), + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = gic_spi(84), + .flags = IORESOURCE_IRQ, + }, + [3] = { + .start = gic_spi(85), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device sdhi0_device = { + .name = "sh_mobile_sdhi", + .id = 0, + .num_resources = ARRAY_SIZE(sdhi0_resources), + .resource = sdhi0_resources, + .dev = { + .platform_data = &sdhi0_info, + }, +}; + +static struct sh_mobile_sdhi_info sdhi1_info = { + .tmio_caps = MMC_CAP_NONREMOVABLE | MMC_CAP_SDIO_IRQ, + .tmio_flags = TMIO_MMC_WRPROTECT_DISABLE | TMIO_MMC_HAS_IDLE_WAIT, +}; + +static struct resource sdhi1_resources[] = { + [0] = { + .name = "SDHI1", + .start = 0xee120000, + .end = 0xee1200ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = gic_spi(87), + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = gic_spi(88), + .flags = IORESOURCE_IRQ, + }, + [3] = { + .start = gic_spi(89), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device sdhi1_device = { + .name = "sh_mobile_sdhi", + .id = 1, + .num_resources = ARRAY_SIZE(sdhi1_resources), + .resource = sdhi1_resources, + .dev = { + .platform_data = &sdhi1_info, + }, +}; + static struct platform_device *kota2_devices[] __initdata = { ð_device, &keysc_device, &gpio_keys_device, &gpio_leds_device, &mmcif_device, + &sdhi0_device, + &sdhi1_device, }; static struct map_desc kota2_io_desc[] __initdata = { @@ -319,6 +395,15 @@ static void __init kota2_init(void) gpio_request(GPIO_PORT208, NULL); /* Reset */ gpio_direction_output(GPIO_PORT208, 1); + /* SDHI0 (microSD) */ + gpio_request(GPIO_FN_SDHICD0_PU, NULL); + gpio_request(GPIO_FN_SDHICMD0_PU, NULL); + gpio_request(GPIO_FN_SDHICLK0, NULL); + gpio_request(GPIO_FN_SDHID0_3_PU, NULL); + gpio_request(GPIO_FN_SDHID0_2_PU, NULL); + gpio_request(GPIO_FN_SDHID0_1_PU, NULL); + gpio_request(GPIO_FN_SDHID0_0_PU, NULL); + /* SCIFB (BT) */ gpio_request(GPIO_FN_PORT159_SCIFB_SCK, NULL); gpio_request(GPIO_FN_PORT160_SCIFB_TXD, NULL); @@ -326,6 +411,14 @@ static void __init kota2_init(void) gpio_request(GPIO_FN_PORT162_SCIFB_RXD, NULL); gpio_request(GPIO_FN_PORT163_SCIFB_RTS_, NULL); + /* SDHI1 (BCM4330) */ + gpio_request(GPIO_FN_SDHICLK1, NULL); + gpio_request(GPIO_FN_SDHICMD1_PU, NULL); + gpio_request(GPIO_FN_SDHID1_3_PU, NULL); + gpio_request(GPIO_FN_SDHID1_2_PU, NULL); + gpio_request(GPIO_FN_SDHID1_1_PU, NULL); + gpio_request(GPIO_FN_SDHID1_0_PU, NULL); + #ifdef CONFIG_CACHE_L2X0 /* Early BRESP enable, Shared attribute override enable, 64K*8way */ l2x0_init(__io(0xf0100000), 0x40460000, 0x82000fff); From 981ed70d8e4faf3689dbf3c48868a31d5b004d7a Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 18 Aug 2011 16:50:51 +0200 Subject: [PATCH 0071/1531] dmatest: make dmatest threads freezable Making dmatest threads freezable allows its use for system PM testing. Signed-off-by: Guennadi Liakhovetski Acked-by: Dan Williams Signed-off-by: Vinod Koul --- drivers/dma/dmatest.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index accc18441b16..eb1d8641cf5c 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -251,6 +252,7 @@ static int dmatest_func(void *data) int i; thread_name = current->comm; + set_freezable_with_signal(); ret = -ENOMEM; @@ -305,7 +307,8 @@ static int dmatest_func(void *data) dma_addr_t dma_srcs[src_cnt]; dma_addr_t dma_dsts[dst_cnt]; struct completion cmp; - unsigned long tmo = msecs_to_jiffies(timeout); + unsigned long start, tmo, end = 0 /* compiler... */; + bool reload = true; u8 align = 0; total_tests++; @@ -404,7 +407,17 @@ static int dmatest_func(void *data) } dma_async_issue_pending(chan); - tmo = wait_for_completion_timeout(&cmp, tmo); + do { + start = jiffies; + if (reload) + end = start + msecs_to_jiffies(timeout); + else if (end <= start) + end = start + 1; + tmo = wait_for_completion_interruptible_timeout(&cmp, + end - start); + reload = try_to_freeze(); + } while (tmo == -ERESTARTSYS); + status = dma_async_is_tx_complete(chan, cookie, NULL, NULL); if (tmo == 0) { From 73eab978ad6934499b83ecc920d470fe99c5e54d Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 25 Aug 2011 11:03:35 +0200 Subject: [PATCH 0072/1531] dmaengine i.MX SDMA: lock channel 0 channel0 of the sdma engine is the configuration channel. It is a shared resource and thus must be protected by a mutex. Signed-off-by: Sascha Hauer Signed-off-by: Vinod Koul --- drivers/dma/imx-sdma.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 7bd7e98548cd..f50c87c303dd 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -318,6 +318,7 @@ struct sdma_engine { dma_addr_t context_phys; struct dma_device dma_device; struct clk *clk; + struct mutex channel_0_lock; struct sdma_script_start_addrs *script_addrs; }; @@ -415,11 +416,15 @@ static int sdma_load_script(struct sdma_engine *sdma, void *buf, int size, dma_addr_t buf_phys; int ret; + mutex_lock(&sdma->channel_0_lock); + buf_virt = dma_alloc_coherent(NULL, size, &buf_phys, GFP_KERNEL); - if (!buf_virt) - return -ENOMEM; + if (!buf_virt) { + ret = -ENOMEM; + goto err_out; + } bd0->mode.command = C0_SETPM; bd0->mode.status = BD_DONE | BD_INTR | BD_WRAP | BD_EXTD; @@ -433,6 +438,9 @@ static int sdma_load_script(struct sdma_engine *sdma, void *buf, int size, dma_free_coherent(NULL, size, buf_virt, buf_phys); +err_out: + mutex_unlock(&sdma->channel_0_lock); + return ret; } @@ -656,6 +664,8 @@ static int sdma_load_context(struct sdma_channel *sdmac) dev_dbg(sdma->dev, "event_mask0 = 0x%08x\n", sdmac->event_mask0); dev_dbg(sdma->dev, "event_mask1 = 0x%08x\n", sdmac->event_mask1); + mutex_lock(&sdma->channel_0_lock); + memset(context, 0, sizeof(*context)); context->channel_state.pc = load_address; @@ -676,6 +686,8 @@ static int sdma_load_context(struct sdma_channel *sdmac) ret = sdma_run_channel(&sdma->channel[0]); + mutex_unlock(&sdma->channel_0_lock); + return ret; } @@ -1274,6 +1286,8 @@ static int __init sdma_probe(struct platform_device *pdev) if (!sdma) return -ENOMEM; + mutex_init(&sdma->channel_0_lock); + sdma->dev = &pdev->dev; iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); From 36e2f21ab481b3d6bd31b99e1de669fbbac4bd0e Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 25 Aug 2011 11:03:36 +0200 Subject: [PATCH 0073/1531] dmaengine i.MX SDMA: set firmware scripts addresses to negative value initially If we do not have a firmare script for a given transfer, the setup of this channel must fail. For this the script addresses have to be < 0 initially, not 0. Signed-off-by: Sascha Hauer Signed-off-by: Vinod Koul --- drivers/dma/imx-sdma.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index f50c87c303dd..8abf8c190aad 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -1281,6 +1281,7 @@ static int __init sdma_probe(struct platform_device *pdev) struct sdma_platform_data *pdata = pdev->dev.platform_data; int i; struct sdma_engine *sdma; + s32 *saddr_arr; sdma = kzalloc(sizeof(*sdma), GFP_KERNEL); if (!sdma) @@ -1324,6 +1325,11 @@ static int __init sdma_probe(struct platform_device *pdev) goto err_alloc; } + /* initially no scripts available */ + saddr_arr = (s32 *)sdma->script_addrs; + for (i = 0; i < SDMA_SCRIPT_ADDRS_ARRAY_SIZE_V1; i++) + saddr_arr[i] = -EINVAL; + if (of_id) pdev->id_entry = of_id->data; sdma->devtype = pdev->id_entry->driver_data; From 7b4b88e067d37cbbafd856121767f7e154294eb2 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 25 Aug 2011 11:03:37 +0200 Subject: [PATCH 0074/1531] dmaengine i.MX SDMA: use request_firmware_nowait The firmware blob may not be available when the driver probes. Instead of blocking the whole kernel use request_firmware_nowait() and continue without firmware. The ROM scripts can already be used then if available. For the devicetree case the ROM scripts are not available, still the probe function should not block. The driver will be unusable in this case, but we have no way of detecting this properly. The configuration of the dma channels will fail, so nothing bad should happen. Signed-off-by: Sascha Hauer Signed-off-by: Vinod Koul --- drivers/dma/imx-sdma.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 8abf8c190aad..b5cc27dc9a51 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -1143,18 +1143,17 @@ static void sdma_add_scripts(struct sdma_engine *sdma, saddr_arr[i] = addr_arr[i]; } -static int __init sdma_get_firmware(struct sdma_engine *sdma, - const char *fw_name) +static void sdma_load_firmware(const struct firmware *fw, void *context) { - const struct firmware *fw; + struct sdma_engine *sdma = context; const struct sdma_firmware_header *header; - int ret; const struct sdma_script_start_addrs *addr; unsigned short *ram_code; - ret = request_firmware(&fw, fw_name, sdma->dev); - if (ret) - return ret; + if (!fw) { + dev_err(sdma->dev, "firmware not found\n"); + return; + } if (fw->size < sizeof(*header)) goto err_firmware; @@ -1184,6 +1183,16 @@ static int __init sdma_get_firmware(struct sdma_engine *sdma, err_firmware: release_firmware(fw); +} + +static int __init sdma_get_firmware(struct sdma_engine *sdma, + const char *fw_name) +{ + int ret; + + ret = request_firmware_nowait(THIS_MODULE, + FW_ACTION_HOTPLUG, fw_name, sdma->dev, + GFP_KERNEL, sdma, sdma_load_firmware); return ret; } From 953742c8fe8ac45be453fee959d7be40cd89f920 Mon Sep 17 00:00:00 2001 From: Arnaud Lacombe Date: Tue, 16 Aug 2011 01:20:20 -0400 Subject: [PATCH 0075/1531] kconfig: fix __enabled_ macros definition for invisible and un-selected symbols __enabled_ are only generated on visible or selected entries, do not reflect the purpose of its introduction. Fix this by always generating these entries for named symbol. Reported-by: Rabin Vincent Signed-off-by: Arnaud Lacombe --- scripts/kconfig/confdata.c | 49 ++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c index 59b667cae5f3..5a58965d8800 100644 --- a/scripts/kconfig/confdata.c +++ b/scripts/kconfig/confdata.c @@ -503,17 +503,6 @@ header_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg) fprintf(fp, "#define %s%s%s 1\n", CONFIG_, sym->name, suffix); } - /* - * Generate the __enabled_CONFIG_* and - * __enabled_CONFIG_*_MODULE macros for use by the - * IS_{ENABLED,BUILTIN,MODULE} macros. The _MODULE variant is - * generated even for booleans so that the IS_ENABLED() macro - * works. - */ - fprintf(fp, "#define __enabled_" CONFIG_ "%s %d\n", - sym->name, (*value == 'y')); - fprintf(fp, "#define __enabled_" CONFIG_ "%s_MODULE %d\n", - sym->name, (*value == 'm')); break; } case S_HEX: { @@ -564,6 +553,35 @@ static struct conf_printer header_printer_cb = .print_comment = header_print_comment, }; +/* + * Generate the __enabled_CONFIG_* and __enabled_CONFIG_*_MODULE macros for + * use by the IS_{ENABLED,BUILTIN,MODULE} macros. The _MODULE variant is + * generated even for booleans so that the IS_ENABLED() macro works. + */ +static void +header_print__enabled_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg) +{ + + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: { + fprintf(fp, "#define __enabled_" CONFIG_ "%s %d\n", + sym->name, (*value == 'y')); + fprintf(fp, "#define __enabled_" CONFIG_ "%s_MODULE %d\n", + sym->name, (*value == 'm')); + break; + } + default: + break; + } +} + +static struct conf_printer header__enabled_printer_cb = +{ + .print_symbol = header_print__enabled_symbol, + .print_comment = header_print_comment, +}; + /* * Tristate printer * @@ -945,11 +963,16 @@ int conf_write_autoconf(void) conf_write_heading(out_h, &header_printer_cb, NULL); for_all_symbols(i, sym) { + if (!sym->name) + continue; + sym_calc_value(sym); - if (!(sym->flags & SYMBOL_WRITE) || !sym->name) + + conf_write_symbol(out_h, sym, &header__enabled_printer_cb, NULL); + + if (!(sym->flags & SYMBOL_WRITE)) continue; - /* write symbol to auto.conf, tristate and header files */ conf_write_symbol(out, sym, &kconfig_printer_cb, (void *)1); conf_write_symbol(tristate, sym, &tristate_printer_cb, (void *)1); From 564899f9f0a2df85fa367c8749a1fef323cb3215 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Sun, 21 Aug 2011 22:04:09 -0300 Subject: [PATCH 0076/1531] kconfig: handle SIGINT in menuconfig I recently got bitten in the ass when pressing Ctrl-C and lost all my current configuration changes. This patch captures SIGINT and allows the user to save any changes. Some code refactoring was made in order to handle the exit behavior. Signed-off-by: Davidlohr Bueso Signed-off-by: Arnaud Lacombe --- scripts/kconfig/mconf.c | 86 +++++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 34 deletions(-) diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c index 820d2b6800fb..19e200d91120 100644 --- a/scripts/kconfig/mconf.c +++ b/scripts/kconfig/mconf.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -272,6 +273,7 @@ static struct menu *current_menu; static int child_count; static int single_menu_mode; static int show_all_options; +static int saved_x, saved_y; static void conf(struct menu *menu); static void conf_choice(struct menu *menu); @@ -792,9 +794,54 @@ static void conf_save(void) } } +static int handle_exit(void) +{ + int res; + + dialog_clear(); + if (conf_get_changed()) + res = dialog_yesno(NULL, + _("Do you wish to save your new configuration ?\n" + " to continue."), + 6, 60); + else + res = -1; + + end_dialog(saved_x, saved_y); + + switch (res) { + case 0: + if (conf_write(filename)) { + fprintf(stderr, _("\n\n" + "Error while writing of the configuration.\n" + "Your configuration changes were NOT saved." + "\n\n")); + return 1; + } + /* fall through */ + case -1: + printf(_("\n\n" + "*** End of the configuration.\n" + "*** Execute 'make' to start the build or try 'make help'." + "\n\n")); + res = 0; + break; + default: + fprintf(stderr, _("\n\n" + "Your configuration changes were NOT saved." + "\n\n")); + } + + return res; +} + +static void sig_handler(int signo) +{ + exit(handle_exit()); +} + int main(int ac, char **av) { - int saved_x, saved_y; char *mode; int res; @@ -802,6 +849,8 @@ int main(int ac, char **av) bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); + signal(SIGINT, sig_handler); + conf_parse(av[1]); conf_read(NULL); @@ -823,40 +872,9 @@ int main(int ac, char **av) set_config_filename(conf_get_configname()); do { conf(&rootmenu); - dialog_clear(); - if (conf_get_changed()) - res = dialog_yesno(NULL, - _("Do you wish to save your " - "new configuration?\n" - " to continue."), - 6, 60); - else - res = -1; + res = handle_exit(); } while (res == KEY_ESC); - end_dialog(saved_x, saved_y); - - switch (res) { - case 0: - if (conf_write(filename)) { - fprintf(stderr, _("\n\n" - "Error while writing of the configuration.\n" - "Your configuration changes were NOT saved." - "\n\n")); - return 1; - } - /* fall through */ - case -1: - printf(_("\n\n" - "*** End of the configuration.\n" - "*** Execute 'make' to start the build or try 'make help'." - "\n\n")); - break; - default: - fprintf(stderr, _("\n\n" - "Your configuration changes were NOT saved." - "\n\n")); - } - return 0; + return res; } From 702a945028fd7cbf7a5e55546b3c47a5c984a1ba Mon Sep 17 00:00:00 2001 From: Lucas De Marchi Date: Sat, 20 Aug 2011 02:28:53 -0300 Subject: [PATCH 0077/1531] kconfig: fix set but not used variables Some variables were being set but never used, which was triggering warnings in GCC >= 4.6. Signed-off-by: Lucas De Marchi Signed-off-by: Arnaud Lacombe --- scripts/kconfig/lxdialog/textbox.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/kconfig/lxdialog/textbox.c b/scripts/kconfig/lxdialog/textbox.c index c704712d0227..154c2dd245b7 100644 --- a/scripts/kconfig/lxdialog/textbox.c +++ b/scripts/kconfig/lxdialog/textbox.c @@ -320,7 +320,6 @@ static void print_page(WINDOW * win, int height, int width) */ static void print_line(WINDOW * win, int row, int width) { - int y, x; char *line; line = get_line(); @@ -329,10 +328,10 @@ static void print_line(WINDOW * win, int row, int width) waddch(win, ' '); waddnstr(win, line, MIN(strlen(line), width - 2)); - getyx(win, y, x); /* Clear 'residue' of previous line */ #if OLD_NCURSES { + int x = getcurx(win); int i; for (i = 0; i < width - x; i++) waddch(win, ' '); From f32948ddd1179ac0b105ceacc235cfc3f98ebea3 Mon Sep 17 00:00:00 2001 From: Li Haifeng Date: Tue, 30 Aug 2011 17:32:50 +0200 Subject: [PATCH 0078/1531] ext2: fix the outdated comment in ext2_nfs_get_inode() Signed-off-by: Li Haifeng Signed-off-by: Jan Kara --- fs/ext2/super.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 1dd62ed35b85..bd8ac164a3bf 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -327,10 +327,10 @@ static struct inode *ext2_nfs_get_inode(struct super_block *sb, if (ino > le32_to_cpu(EXT2_SB(sb)->s_es->s_inodes_count)) return ERR_PTR(-ESTALE); - /* iget isn't really right if the inode is currently unallocated!! - * ext2_read_inode currently does appropriate checks, but - * it might be "neater" to call ext2_get_inode first and check - * if the inode is valid..... + /* + * ext2_iget isn't quite right if the inode is currently unallocated! + * However ext2_iget currently does appropriate checks to handle stale + * inodes so everything is OK. */ inode = ext2_iget(sb, ino); if (IS_ERR(inode)) From 09d481270d445d98342d8ab872f05491b6d23f8b Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Tue, 16 Aug 2011 10:46:05 +0200 Subject: [PATCH 0079/1531] scripts: add extract-vmlinux This script can be used to extract vmlinux from a compressed kernel image (bzImage, etc..). It's inspired from (a subset of) extract-ikconfig. It's something a lot of people have been looking for (mainly people with xen < 4 that doesn't support bzImages at all). Signed-off-by: Corentin Chary Signed-off-by: Michal Marek --- scripts/extract-vmlinux | 62 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100755 scripts/extract-vmlinux diff --git a/scripts/extract-vmlinux b/scripts/extract-vmlinux new file mode 100755 index 000000000000..5061abcc2540 --- /dev/null +++ b/scripts/extract-vmlinux @@ -0,0 +1,62 @@ +#!/bin/sh +# ---------------------------------------------------------------------- +# extract-vmlinux - Extract uncompressed vmlinux from a kernel image +# +# Inspired from extract-ikconfig +# (c) 2009,2010 Dick Streefland +# +# (c) 2011 Corentin Chary +# +# Licensed under the GNU General Public License, version 2 (GPLv2). +# ---------------------------------------------------------------------- + +check_vmlinux() +{ + # Use readelf to check if it's a valid ELF + # TODO: find a better to way to check that it's really vmlinux + # and not just an elf + readelf -h $1 > /dev/null 2>&1 || return 1 + + cat $1 + exit 0 +} + +try_decompress() +{ + # The obscure use of the "tr" filter is to work around older versions of + # "grep" that report the byte offset of the line instead of the pattern. + + # Try to find the header ($1) and decompress from here + for pos in `tr "$1\n$2" "\n$2=" < "$img" | grep -abo "^$2"` + do + pos=${pos%%:*} + tail -c+$pos "$img" | $3 > $tmp 2> /dev/null + check_vmlinux $tmp + done +} + +# Check invocation: +me=${0##*/} +img=$1 +if [ $# -ne 1 -o ! -s "$img" ] +then + echo "Usage: $me " >&2 + exit 2 +fi + +# Prepare temp files: +tmp=$(mktemp /tmp/vmlinux-XXX) +trap "rm -f $tmp" 0 + +# Initial attempt for uncompressed images or objects: +check_vmlinux $img + +# That didn't work, so retry after decompression. +try_decompress '\037\213\010' xy gunzip +try_decompress '\3757zXZ\000' abcde unxz +try_decompress 'BZh' xy bunzip2 +try_decompress '\135\0\0\0' xxx unlzma +try_decompress '\211\114\132' xy 'lzop -d' + +# Bail out: +echo "$me: Cannot find vmlinux." >&2 From 9c65426ad2cce12a2d72cdb42aa08f7ce946065d Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Tue, 30 Aug 2011 11:47:58 +1000 Subject: [PATCH 0080/1531] tags, powerpc: Update tags.sh to support _GLOBAL symbols On PowerPC we use _GLOBAL throughout the assembly to define symbols, but currently these symbols are missing from the tags generated with ARCH=powerpc make tags. This patch modifies the tags.sh script to recognise _GLOBAL(.*) so that these symbols will be in the tags. This is almost (but not quite) PowerPC specific and this change should not affect anyone else: $ git grep -E '^_GLOBAL\(([^)]*)\).*' |sed 's/^\([^/]*\/[^/]*\)\/.*$/\1/'|uniq -c 627 arch/powerpc 2 arch/um Signed-off-by: Ian Munsie Signed-off-by: Michal Marek --- scripts/tags.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/tags.sh b/scripts/tags.sh index 75c5d24f1993..38f6617a2cb1 100755 --- a/scripts/tags.sh +++ b/scripts/tags.sh @@ -129,7 +129,7 @@ exuberant() -I EXPORT_SYMBOL,EXPORT_SYMBOL_GPL \ -I DEFINE_TRACE,EXPORT_TRACEPOINT_SYMBOL,EXPORT_TRACEPOINT_SYMBOL_GPL \ --extra=+f --c-kinds=+px \ - --regex-asm='/^ENTRY\(([^)]*)\).*/\1/' \ + --regex-asm='/^(ENTRY|_GLOBAL)\(([^)]*)\).*/\2/' \ --regex-c='/^SYSCALL_DEFINE[[:digit:]]?\(([^,)]*).*/sys_\1/' \ --regex-c++='/^TRACE_EVENT\(([^,)]*).*/trace_\1/' \ --regex-c++='/^DEFINE_EVENT\([^,)]*, *([^,)]*).*/trace_\1/' @@ -151,7 +151,7 @@ exuberant() emacs() { all_sources | xargs $1 -a \ - --regex='/^ENTRY(\([^)]*\)).*/\1/' \ + --regex='/^(ENTRY|_GLOBAL)(\([^)]*\)).*/\2/' \ --regex='/^SYSCALL_DEFINE[0-9]?(\([^,)]*\).*/sys_\1/' \ --regex='/^TRACE_EVENT(\([^,)]*\).*/trace_\1/' \ --regex='/^DEFINE_EVENT([^,)]*, *\([^,)]*\).*/trace_\1/' From 58238c81443a83922e07b409e54ec93ce909b6ac Mon Sep 17 00:00:00 2001 From: Peter Foley Date: Sun, 31 Jul 2011 14:45:40 -0400 Subject: [PATCH 0081/1531] kbuild: prevent make from deleting _shipped files commit 7373f4f (kbuild: add implicit rules for parser generation) created a implicit rule chain (%.c: %.c_shipped: %.y). Make considers the _shipped files to be intermediate files which causes them to be deleted if they didn't exist before make was run. Mark the _shipped files PRECIOUS to prevent make from deleting them. Signed-off-by: Peter Foley Acked-by: Arnaud Lacombe Signed-off-by: Michal Marek --- scripts/Makefile.lib | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index aeea84a24836..5d986d9adf1b 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -167,6 +167,7 @@ ifdef REGENERATE_PARSERS quiet_cmd_gperf = GPERF $@ cmd_gperf = gperf -t --output-file $@ -a -C -E -g -k 1,3,$$ -p -t $< +.PRECIOUS: $(src)/%.hash.c_shipped $(src)/%.hash.c_shipped: $(src)/%.gperf $(call cmd,gperf) @@ -177,6 +178,7 @@ LEX_PREFIX = $(if $(LEX_PREFIX_${baseprereq}),$(LEX_PREFIX_${baseprereq}),yy) quiet_cmd_flex = LEX $@ cmd_flex = flex -o$@ -L -P $(LEX_PREFIX) $< +.PRECIOUS: $(src)/%.lex.c_shipped $(src)/%.lex.c_shipped: $(src)/%.l $(call cmd,flex) @@ -187,12 +189,14 @@ YACC_PREFIX = $(if $(YACC_PREFIX_${baseprereq}),$(YACC_PREFIX_${baseprereq}),yy) quiet_cmd_bison = YACC $@ cmd_bison = bison -o$@ -t -l -p $(YACC_PREFIX) $< +.PRECIOUS: $(src)/%.tab.c_shipped $(src)/%.tab.c_shipped: $(src)/%.y $(call cmd,bison) quiet_cmd_bison_h = YACC $@ cmd_bison_h = bison -o/dev/null --defines=$@ -t -l -p $(YACC_PREFIX) $< +.PRECIOUS: $(src)/%.tab.h_shipped $(src)/%.tab.h_shipped: $(src)/%.y $(call cmd,bison_h) From 49c57d254e691a45ade207c754ba09ca7129ed93 Mon Sep 17 00:00:00 2001 From: Arnaud Lacombe Date: Mon, 15 Aug 2011 01:07:13 -0400 Subject: [PATCH 0082/1531] scripts/Makefile.build: do not reference EXTRA_CFLAGS as CFLAGS replacement Usage of these flags has been deprecated for nearly 4 years by: commit f77bf01425b11947eeb3b5b54685212c302741b8 Author: Sam Ravnborg Date: Mon Oct 15 22:25:06 2007 +0200 kbuild: introduce ccflags-y, asflags-y and ldflags-y Moreover, these flags (at least EXTRA_CFLAGS) have been documented for command line use. By default, gmake(1) do not override command line setting, so this is likely to result in build failure or unexpected behavior. Do not advertise for its usage. Cc: Sam Ravnborg Cc: linux-kbuild@vger.kernel.org Signed-off-by: Arnaud Lacombe Signed-off-by: Michal Marek --- scripts/Makefile.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/Makefile.build b/scripts/Makefile.build index a0fd5029cfe7..d2b366c16b64 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -46,7 +46,7 @@ include $(kbuild-file) # If the save-* variables changed error out ifeq ($(KBUILD_NOPEDANTIC),) ifneq ("$(save-cflags)","$(CFLAGS)") - $(error CFLAGS was changed in "$(kbuild-file)". Fix it to use EXTRA_CFLAGS) + $(error CFLAGS was changed in "$(kbuild-file)". Fix it to use ccflags-y) endif endif From c68e58783f20d3eb32b99e1962b26462f2e3195a Mon Sep 17 00:00:00 2001 From: Arnaud Lacombe Date: Mon, 15 Aug 2011 01:07:14 -0400 Subject: [PATCH 0083/1531] script/checkpatch.pl: warn about deprecated use of EXTRA_{A,C,CPP,LD}FLAGS Usage of these flags has been deprecated for nearly 4 years by: commit f77bf01425b11947eeb3b5b54685212c302741b8 Author: Sam Ravnborg Date: Mon Oct 15 22:25:06 2007 +0200 kbuild: introduce ccflags-y, asflags-y and ldflags-y Moreover, these flags (at least EXTRA_CFLAGS) have been documented for command line use. By default, gmake(1) do not override command line setting, so this is likely to result in build failure or unexpected behavior. Warn about their introduction in Makefile or Kbuild files. Cc: Sam Ravnborg Cc: Andy Whitcroft Signed-off-by: Arnaud Lacombe Signed-off-by: Michal Marek --- scripts/checkpatch.pl | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 9d761c95eca2..955183ada37a 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1661,6 +1661,20 @@ sub process { #print "is_end<$is_end> length<$length>\n"; } + if (($realfile =~ /Makefile.*/ || $realfile =~ /Kbuild.*/) && + ($line =~ /\+(EXTRA_[A-Z]+FLAGS).*/)) { + my $flag = $1; + my $replacement = { + 'EXTRA_AFLAGS' => 'asflags-y', + 'EXTRA_CFLAGS' => 'ccflags-y', + 'EXTRA_CPPFLAGS' => 'cppflags-y', + 'EXTRA_LDFLAGS' => 'ldflags-y', + }; + + WARN("DEPRECATED_VARIABLE", + "Use of $flag is deprecated, please use \`$replacement->{$flag} instead.\n" . $herecurr) if ($replacement->{$flag}); + } + # check we are in a valid source file if not then ignore this hunk next if ($realfile !~ /\.(h|c|s|S|pl|sh)$/); From 1cd9f0976aa4606db8d6e3dc3edd0aca8019372a Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Wed, 31 Aug 2011 11:54:51 -0400 Subject: [PATCH 0084/1531] ext2,ext3,ext4: don't inherit APPEND_FL or IMMUTABLE_FL for new inodes This doesn't make much sense, and it exposes a bug in the kernel where attempts to create a new file in an append-only directory using O_CREAT will fail (but still leave a zero-length file). This was discovered when xfstests #79 was generalized so it could run on all file systems. Signed-off-by: "Theodore Ts'o" Cc:stable@kernel.org --- fs/ext4/ext4.h | 3 +-- include/linux/ext2_fs.h | 4 ++-- include/linux/ext3_fs.h | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index b7d7bd0f066e..5c38120c389c 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -358,8 +358,7 @@ struct flex_groups { /* Flags that should be inherited by new inodes from their parent. */ #define EXT4_FL_INHERITED (EXT4_SECRM_FL | EXT4_UNRM_FL | EXT4_COMPR_FL |\ - EXT4_SYNC_FL | EXT4_IMMUTABLE_FL | EXT4_APPEND_FL |\ - EXT4_NODUMP_FL | EXT4_NOATIME_FL |\ + EXT4_SYNC_FL | EXT4_NODUMP_FL | EXT4_NOATIME_FL |\ EXT4_NOCOMPR_FL | EXT4_JOURNAL_DATA_FL |\ EXT4_NOTAIL_FL | EXT4_DIRSYNC_FL) diff --git a/include/linux/ext2_fs.h b/include/linux/ext2_fs.h index 53792bf36c71..ce1b719e8bd4 100644 --- a/include/linux/ext2_fs.h +++ b/include/linux/ext2_fs.h @@ -197,8 +197,8 @@ struct ext2_group_desc /* Flags that should be inherited by new inodes from their parent. */ #define EXT2_FL_INHERITED (EXT2_SECRM_FL | EXT2_UNRM_FL | EXT2_COMPR_FL |\ - EXT2_SYNC_FL | EXT2_IMMUTABLE_FL | EXT2_APPEND_FL |\ - EXT2_NODUMP_FL | EXT2_NOATIME_FL | EXT2_COMPRBLK_FL|\ + EXT2_SYNC_FL | EXT2_NODUMP_FL |\ + EXT2_NOATIME_FL | EXT2_COMPRBLK_FL |\ EXT2_NOCOMP_FL | EXT2_JOURNAL_DATA_FL |\ EXT2_NOTAIL_FL | EXT2_DIRSYNC_FL) diff --git a/include/linux/ext3_fs.h b/include/linux/ext3_fs.h index 67a803aee619..0244611eb2b8 100644 --- a/include/linux/ext3_fs.h +++ b/include/linux/ext3_fs.h @@ -180,8 +180,8 @@ struct ext3_group_desc /* Flags that should be inherited by new inodes from their parent. */ #define EXT3_FL_INHERITED (EXT3_SECRM_FL | EXT3_UNRM_FL | EXT3_COMPR_FL |\ - EXT3_SYNC_FL | EXT3_IMMUTABLE_FL | EXT3_APPEND_FL |\ - EXT3_NODUMP_FL | EXT3_NOATIME_FL | EXT3_COMPRBLK_FL|\ + EXT3_SYNC_FL | EXT3_NODUMP_FL |\ + EXT3_NOATIME_FL | EXT3_COMPRBLK_FL |\ EXT3_NOCOMPR_FL | EXT3_JOURNAL_DATA_FL |\ EXT3_NOTAIL_FL | EXT3_DIRSYNC_FL) From 84ebd795613488992b273220c2937d575d27d2a9 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Wed, 31 Aug 2011 11:56:51 -0400 Subject: [PATCH 0085/1531] ext4: fake direct I/O mode for data=journal Currently attempts to open a file with O_DIRECT in data=journal mode causes the open to fail with -EINVAL. This makes it very hard to test data=journal mode. So we will let the open succeed, but then always fall back to O_DSYNC buffered writes. Signed-off-by: "Theodore Ts'o" --- fs/ext4/inode.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 18d2558b7624..b84f127c085d 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -2854,6 +2854,12 @@ static ssize_t ext4_direct_IO(int rw, struct kiocb *iocb, struct inode *inode = file->f_mapping->host; ssize_t ret; + /* + * If we are doing data journalling we don't support O_DIRECT + */ + if (ext4_should_journal_data(inode)) + return 0; + trace_ext4_direct_IO_enter(inode, offset, iov_length(iov, nr_segs), rw); if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) ret = ext4_ext_direct_IO(rw, iocb, iov, offset, nr_segs); @@ -2923,6 +2929,7 @@ static const struct address_space_operations ext4_journalled_aops = { .bmap = ext4_bmap, .invalidatepage = ext4_invalidatepage, .releasepage = ext4_releasepage, + .direct_IO = ext4_direct_IO, .is_partially_uptodate = block_is_partially_uptodate, .error_remove_page = generic_error_remove_page, }; From bcaa992975041e40449be8c010c26192b8c8b409 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Wed, 31 Aug 2011 11:58:51 -0400 Subject: [PATCH 0086/1531] ext4: ext4_rename should dirty dir_bh with the correct directory When ext4_rename performs a directory rename (move), dir_bh is a buffer that is modified to update the '..' link in the directory being moved (old_inode). However, ext4_handle_dirty_metadata is called with the old parent directory inode (old_dir) and dir_bh, which is incorrect because dir_bh does not belong to the parent inode. Fix this error. Signed-off-by: Darrick J. Wong Signed-off-by: "Theodore Ts'o" Cc: stable@kernel.org --- fs/ext4/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index f8068c7bae9f..09f930b7a785 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -2529,7 +2529,7 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry, PARENT_INO(dir_bh->b_data, new_dir->i_sb->s_blocksize) = cpu_to_le32(new_dir->i_ino); BUFFER_TRACE(dir_bh, "call ext4_handle_dirty_metadata"); - retval = ext4_handle_dirty_metadata(handle, old_dir, dir_bh); + retval = ext4_handle_dirty_metadata(handle, old_inode, dir_bh); if (retval) { ext4_std_error(old_dir->i_sb, retval); goto end_rename; From f9287c1f2d329f4d78a3bbc9cf0db0ebae6f146a Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Wed, 31 Aug 2011 12:00:51 -0400 Subject: [PATCH 0087/1531] ext4: ext4_mkdir should dirty dir_block with newly created directory inode ext4_mkdir calls ext4_handle_dirty_metadata with dir_block and the inode "dir". Unfortunately, dir_block belongs to the newly created directory (which is "inode"), not the parent directory (which is "dir"). Fix the incorrect association. Signed-off-by: Darrick J. Wong Signed-off-by: "Theodore Ts'o" Cc: stable@kernel.org --- fs/ext4/namei.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 09f930b7a785..f0abe4323136 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -1862,7 +1862,7 @@ static int ext4_mkdir(struct inode *dir, struct dentry *dentry, int mode) ext4_set_de_type(dir->i_sb, de, S_IFDIR); inode->i_nlink = 2; BUFFER_TRACE(dir_block, "call ext4_handle_dirty_metadata"); - err = ext4_handle_dirty_metadata(handle, dir, dir_block); + err = ext4_handle_dirty_metadata(handle, inode, dir_block); if (err) goto out_clear_inode; err = ext4_mark_inode_dirty(handle, inode); From 5930ea643805feb50a2f8383ae12eb6f10935e49 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Wed, 31 Aug 2011 12:02:51 -0400 Subject: [PATCH 0088/1531] ext4: call ext4_handle_dirty_metadata with correct inode in ext4_dx_add_entry ext4_dx_add_entry manipulates bh2 and frames[0].bh, which are two buffer_heads that point to directory blocks assigned to the directory inode. However, the function calls ext4_handle_dirty_metadata with the inode of the file that's being added to the directory, not the directory inode itself. Therefore, correct the code to dirty the directory buffers with the directory inode, not the file inode. Signed-off-by: Darrick J. Wong Signed-off-by: "Theodore Ts'o" Cc: stable@kernel.org --- fs/ext4/namei.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index f0abe4323136..a067835bbac1 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -1585,7 +1585,7 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry, dxtrace(dx_show_index("node", frames[1].entries)); dxtrace(dx_show_index("node", ((struct dx_node *) bh2->b_data)->entries)); - err = ext4_handle_dirty_metadata(handle, inode, bh2); + err = ext4_handle_dirty_metadata(handle, dir, bh2); if (err) goto journal_error; brelse (bh2); @@ -1611,7 +1611,7 @@ static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry, if (err) goto journal_error; } - err = ext4_handle_dirty_metadata(handle, inode, frames[0].bh); + err = ext4_handle_dirty_metadata(handle, dir, frames[0].bh); if (err) { ext4_std_error(inode->i_sb, err); goto cleanup; From ce395088832bfd56bd28824b31a6a3685f3fd339 Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Fri, 17 Jun 2011 02:51:47 +0000 Subject: [PATCH 0089/1531] cpc925_edac: Support single-processor configurations If second CPU is not enabled, CPC925 EDAC driver will spill out warnings about errors on second Processor Interface. Support masking that out, by detecting at runtime which CPUs are present in device tree. Signed-off-by: Dmitry Eremin-Solenikov Cc: Harry Ciao Cc: Doug Thompson Signed-off-by: Dmitry Eremin-Solenikov Signed-off-by: Benjamin Herrenschmidt --- drivers/edac/cpc925_edac.c | 67 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/drivers/edac/cpc925_edac.c b/drivers/edac/cpc925_edac.c index a687a0d16962..a774c0ddaf5b 100644 --- a/drivers/edac/cpc925_edac.c +++ b/drivers/edac/cpc925_edac.c @@ -90,6 +90,7 @@ enum apimask_bits { ECC_MASK_ENABLE = (APIMASK_ECC_UE_H | APIMASK_ECC_CE_H | APIMASK_ECC_UE_L | APIMASK_ECC_CE_L), }; +#define APIMASK_ADI(n) CPC925_BIT(((n)+1)) /************************************************************ * Processor Interface Exception Register (APIEXCP) @@ -581,16 +582,73 @@ static void cpc925_mc_check(struct mem_ctl_info *mci) } /******************** CPU err device********************************/ +static u32 cpc925_cpu_mask_disabled(void) +{ + struct device_node *cpus; + struct device_node *cpunode = NULL; + static u32 mask = 0; + + /* use cached value if available */ + if (mask != 0) + return mask; + + mask = APIMASK_ADI0 | APIMASK_ADI1; + + cpus = of_find_node_by_path("/cpus"); + if (cpus == NULL) { + cpc925_printk(KERN_DEBUG, "No /cpus node !\n"); + return 0; + } + + while ((cpunode = of_get_next_child(cpus, cpunode)) != NULL) { + const u32 *reg = of_get_property(cpunode, "reg", NULL); + + if (strcmp(cpunode->type, "cpu")) { + cpc925_printk(KERN_ERR, "Not a cpu node in /cpus: %s\n", cpunode->name); + continue; + } + + if (reg == NULL || *reg > 2) { + cpc925_printk(KERN_ERR, "Bad reg value at %s\n", cpunode->full_name); + continue; + } + + mask &= ~APIMASK_ADI(*reg); + } + + if (mask != (APIMASK_ADI0 | APIMASK_ADI1)) { + /* We assume that each CPU sits on it's own PI and that + * for present CPUs the reg property equals to the PI + * interface id */ + cpc925_printk(KERN_WARNING, + "Assuming PI id is equal to CPU MPIC id!\n"); + } + + of_node_put(cpunode); + of_node_put(cpus); + + return mask; +} + /* Enable CPU Errors detection */ static void cpc925_cpu_init(struct cpc925_dev_info *dev_info) { u32 apimask; + u32 cpumask; apimask = __raw_readl(dev_info->vbase + REG_APIMASK_OFFSET); - if ((apimask & CPU_MASK_ENABLE) == 0) { - apimask |= CPU_MASK_ENABLE; - __raw_writel(apimask, dev_info->vbase + REG_APIMASK_OFFSET); + + cpumask = cpc925_cpu_mask_disabled(); + if (apimask & cpumask) { + cpc925_printk(KERN_WARNING, "CPU(s) not present, " + "but enabled in APIMASK, disabling\n"); + apimask &= ~cpumask; } + + if ((apimask & CPU_MASK_ENABLE) == 0) + apimask |= CPU_MASK_ENABLE; + + __raw_writel(apimask, dev_info->vbase + REG_APIMASK_OFFSET); } /* Disable CPU Errors detection */ @@ -622,6 +680,9 @@ static void cpc925_cpu_check(struct edac_device_ctl_info *edac_dev) if ((apiexcp & CPU_EXCP_DETECTED) == 0) return; + if ((apiexcp & ~cpc925_cpu_mask_disabled()) == 0) + return; + apimask = __raw_readl(dev_info->vbase + REG_APIMASK_OFFSET); cpc925_printk(KERN_INFO, "Processor Interface Fault\n" "Processor Interface register dump:\n"); From 6a5c7be5e484bda5b2639fedf7dbe3f25c15c962 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Fri, 24 Jun 2011 09:05:22 +0000 Subject: [PATCH 0090/1531] powerpc: Override dma_get_required_mask by platform hook and ops The hook dma_get_required_mask is supposed to return the mask required by the platform to operate efficently. The generic version of dma_get_required_mask in driver/base/platform.c returns a mask based only on max_pfn. However, this is likely too big for iommu systems and could be too small for platforms that require a dma offset or have a secondary window at a high offset. Override the default, provide a hook in ppc_md used by pseries lpar and cell, and provide the default answer based on memblock_end_of_DRAM(), with hooks for get_dma_offset, and provide an implementation for iommu that looks at the defined table size. Coverting from the end address to the required bit mask is based on the generic implementation. The need for this was discovered when the qla2xxx driver switched to 64 bit dma then reverted to 32 bit when dma_get_required_mask said 32 bits was sufficient. Signed-off-by: Milton Miller Signed-off-by: Nishanth Aravamudan Cc: linuxppc-dev@lists.ozlabs.org Cc: linux-kernel@vger.kernel.org Cc: benh@kernel.crashing.org Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/dma-mapping.h | 3 ++ arch/powerpc/include/asm/machdep.h | 3 +- arch/powerpc/kernel/dma-iommu.c | 13 +++++++++ arch/powerpc/kernel/dma.c | 39 ++++++++++++++++++++++++++ arch/powerpc/platforms/cell/iommu.c | 12 ++++++++ arch/powerpc/platforms/pseries/iommu.c | 27 ++++++++++++++++++ 6 files changed, 96 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/dma-mapping.h b/arch/powerpc/include/asm/dma-mapping.h index dd70fac57ec8..8135e66a4bb9 100644 --- a/arch/powerpc/include/asm/dma-mapping.h +++ b/arch/powerpc/include/asm/dma-mapping.h @@ -20,6 +20,8 @@ #define DMA_ERROR_CODE (~(dma_addr_t)0x0) +#define ARCH_HAS_DMA_GET_REQUIRED_MASK + /* Some dma direct funcs must be visible for use in other dma_ops */ extern void *dma_direct_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag); @@ -69,6 +71,7 @@ static inline unsigned long device_to_mask(struct device *dev) */ #ifdef CONFIG_PPC64 extern struct dma_map_ops dma_iommu_ops; +extern u64 dma_iommu_get_required_mask(struct device *dev); #endif extern struct dma_map_ops dma_direct_ops; diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h index 47cacddb14cf..58fc21623014 100644 --- a/arch/powerpc/include/asm/machdep.h +++ b/arch/powerpc/include/asm/machdep.h @@ -85,8 +85,9 @@ struct machdep_calls { void (*pci_dma_dev_setup)(struct pci_dev *dev); void (*pci_dma_bus_setup)(struct pci_bus *bus); - /* Platform set_dma_mask override */ + /* Platform set_dma_mask and dma_get_required_mask overrides */ int (*dma_set_mask)(struct device *dev, u64 dma_mask); + u64 (*dma_get_required_mask)(struct device *dev); int (*probe)(void); void (*setup_arch)(void); /* Optional, may be NULL */ diff --git a/arch/powerpc/kernel/dma-iommu.c b/arch/powerpc/kernel/dma-iommu.c index e7554154a6de..1f2a711a261e 100644 --- a/arch/powerpc/kernel/dma-iommu.c +++ b/arch/powerpc/kernel/dma-iommu.c @@ -90,6 +90,19 @@ static int dma_iommu_dma_supported(struct device *dev, u64 mask) return 1; } +u64 dma_iommu_get_required_mask(struct device *dev) +{ + struct iommu_table *tbl = get_iommu_table_base(dev); + u64 mask; + if (!tbl) + return 0; + + mask = 1ULL < (fls_long(tbl->it_offset + tbl->it_size) - 1); + mask += mask - 1; + + return mask; +} + struct dma_map_ops dma_iommu_ops = { .alloc_coherent = dma_iommu_alloc_coherent, .free_coherent = dma_iommu_free_coherent, diff --git a/arch/powerpc/kernel/dma.c b/arch/powerpc/kernel/dma.c index 4f0959fbfbee..503093efa202 100644 --- a/arch/powerpc/kernel/dma.c +++ b/arch/powerpc/kernel/dma.c @@ -170,6 +170,45 @@ int dma_set_mask(struct device *dev, u64 dma_mask) } EXPORT_SYMBOL(dma_set_mask); +u64 dma_get_required_mask(struct device *dev) +{ + struct dma_map_ops *dma_ops = get_dma_ops(dev); + u64 mask, end = 0; + + if (ppc_md.dma_get_required_mask) + return ppc_md.dma_get_required_mask(dev); + + if (unlikely(dma_ops == NULL)) + return 0; + +#ifdef CONFIG_PPC64 + else if (dma_ops == &dma_iommu_ops) + return dma_iommu_get_required_mask(dev); +#endif +#ifdef CONFIG_SWIOTLB + else if (dma_ops == &swiotlb_dma_ops) { + u64 max_direct_dma_addr = dev->archdata.max_direct_dma_addr; + + end = memblock_end_of_DRAM(); + if (max_direct_dma_addr && end > max_direct_dma_addr) + end = max_direct_dma_addr; + end += get_dma_offset(dev); + } +#endif + else if (dma_ops == &dma_direct_ops) + end = memblock_end_of_DRAM() + get_dma_offset(dev); + else { + WARN_ONCE(1, "%s: unknown ops %p\n", __func__, dma_ops); + end = memblock_end_of_DRAM(); + } + + mask = 1ULL << (fls64(end) - 1); + mask += mask - 1; + + return mask; +} +EXPORT_SYMBOL_GPL(dma_get_required_mask); + static int __init dma_init(void) { dma_debug_init(PREALLOC_DMA_DEBUG_ENTRIES); diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c index 26a067122a54..5ef55f3b0987 100644 --- a/arch/powerpc/platforms/cell/iommu.c +++ b/arch/powerpc/platforms/cell/iommu.c @@ -1159,6 +1159,17 @@ static int __init setup_iommu_fixed(char *str) } __setup("iommu_fixed=", setup_iommu_fixed); +static u64 cell_dma_get_required_mask(struct device *dev) +{ + if (!dev->dma_mask) + return 0; + + if (iommu_fixed_disabled && get_dma_ops(dev) == &dma_iommu_ops) + return dma_iommu_get_required_mask(dev); + + return DMA_BIT_MASK(64); +} + static int __init cell_iommu_init(void) { struct device_node *np; @@ -1175,6 +1186,7 @@ static int __init cell_iommu_init(void) /* Setup various ppc_md. callbacks */ ppc_md.pci_dma_dev_setup = cell_pci_dma_dev_setup; + ppc_md.dma_get_required_mask = cell_dma_get_required_mask; ppc_md.tce_build = tce_build_cell; ppc_md.tce_free = tce_free_cell; diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index 01faab9456ca..fe5ededf0d60 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -1077,12 +1077,38 @@ static int dma_set_mask_pSeriesLP(struct device *dev, u64 dma_mask) return 0; } +static u64 dma_get_required_mask_pSeriesLP(struct device *dev) +{ + if (!dev->dma_mask) + return 0; + + if (!disable_ddw && dev_is_pci(dev)) { + struct pci_dev *pdev = to_pci_dev(dev); + struct device_node *dn; + + dn = pci_device_to_OF_node(pdev); + + /* search upwards for ibm,dma-window */ + for (; dn && PCI_DN(dn) && !PCI_DN(dn)->iommu_table; + dn = dn->parent) + if (of_get_property(dn, "ibm,dma-window", NULL)) + break; + /* if there is a ibm,ddw-applicable property require 64 bits */ + if (dn && PCI_DN(dn) && + of_get_property(dn, "ibm,ddw-applicable", NULL)) + return DMA_BIT_MASK(64); + } + + return dma_iommu_get_required_mask(dev); +} + #else /* CONFIG_PCI */ #define pci_dma_bus_setup_pSeries NULL #define pci_dma_dev_setup_pSeries NULL #define pci_dma_bus_setup_pSeriesLP NULL #define pci_dma_dev_setup_pSeriesLP NULL #define dma_set_mask_pSeriesLP NULL +#define dma_get_required_mask_pSeriesLP NULL #endif /* !CONFIG_PCI */ static int iommu_mem_notifier(struct notifier_block *nb, unsigned long action, @@ -1186,6 +1212,7 @@ void iommu_init_early_pSeries(void) ppc_md.pci_dma_bus_setup = pci_dma_bus_setup_pSeriesLP; ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_pSeriesLP; ppc_md.dma_set_mask = dma_set_mask_pSeriesLP; + ppc_md.dma_get_required_mask = dma_get_required_mask_pSeriesLP; } else { ppc_md.tce_build = tce_build_pSeries; ppc_md.tce_free = tce_free_pSeries; From 954886987085072cd7dd06622ddf850e46ec2902 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 1 Sep 2011 05:46:00 +0100 Subject: [PATCH 0091/1531] [IA64] gpio: GENERIC_GPIO default must be n Since GPIOLIB is optional on ia64, GENERIC_GPIO must not be selected by default. If GPIOLIB is enabled, it will select GENERIC_GPIO. Signed-off-by: Ben Hutchings Signed-off-by: Tony Luck --- arch/ia64/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig index 124854714958..aaaee506499f 100644 --- a/arch/ia64/Kconfig +++ b/arch/ia64/Kconfig @@ -92,7 +92,7 @@ config HAVE_SETUP_PER_CPU_AREA def_bool y config GENERIC_GPIO - def_bool y + bool config DMI bool From 4e96b2dbbf1d7e81f22047a50f862555a6cb87cb Mon Sep 17 00:00:00 2001 From: Allison Henderson Date: Sat, 3 Sep 2011 11:51:09 -0400 Subject: [PATCH 0092/1531] ext4: Add new ext4_discard_partial_page_buffers routines This patch adds two new routines: ext4_discard_partial_page_buffers and ext4_discard_partial_page_buffers_no_lock. The ext4_discard_partial_page_buffers routine is a wrapper function to ext4_discard_partial_page_buffers_no_lock. The wrapper function locks the page and passes it to ext4_discard_partial_page_buffers_no_lock. Calling functions that already have the page locked can call ext4_discard_partial_page_buffers_no_lock directly. The ext4_discard_partial_page_buffers_no_lock function zeros a specified range in a page, and unmaps the corresponding buffer heads. Only block aligned regions of the page will have their buffer heads unmapped. Unblock aligned regions will be mapped if needed so that they can be updated with the partial zero out. This function is meant to be used to update a page and its buffer heads to be zeroed and unmapped when the corresponding blocks have been released or will be released. This routine is used in the following scenarios: * A hole is punched and the non page aligned regions of the head and tail of the hole need to be discarded * The file is truncated and the partial page beyond EOF needs to be discarded * The end of a hole is in the same page as EOF. After the page is flushed, the partial page beyond EOF needs to be discarded. * A write operation begins or ends inside a hole and the partial page appearing before or after the write needs to be discarded * A write operation extends EOF and the partial page beyond EOF needs to be discarded This function takes a flag EXT4_DISCARD_PARTIAL_PG_ZERO_UNMAPPED which is used when a write operation begins or ends in a hole. When the EXT4_DISCARD_PARTIAL_PG_ZERO_UNMAPPED flag is used, only buffer heads that are already unmapped will have the corresponding regions of the page zeroed. Signed-off-by: Allison Henderson Signed-off-by: "Theodore Ts'o" --- fs/ext4/ext4.h | 11 +++ fs/ext4/inode.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 235 insertions(+) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 5c38120c389c..ccfa81f33bb0 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -528,6 +528,11 @@ struct ext4_new_group_data { #define EXT4_FREE_BLOCKS_VALIDATED 0x0004 #define EXT4_FREE_BLOCKS_NO_QUOT_UPDATE 0x0008 +/* + * Flags used by ext4_discard_partial_page_buffers + */ +#define EXT4_DISCARD_PARTIAL_PG_ZERO_UNMAPPED 0x0001 + /* * ioctl commands */ @@ -1838,6 +1843,12 @@ extern int ext4_block_truncate_page(handle_t *handle, struct address_space *mapping, loff_t from); extern int ext4_block_zero_page_range(handle_t *handle, struct address_space *mapping, loff_t from, loff_t length); +extern int ext4_discard_partial_page_buffers(handle_t *handle, + struct address_space *mapping, loff_t from, + loff_t length, int flags); +extern int ext4_discard_partial_page_buffers_no_lock(handle_t *handle, + struct inode *inode, struct page *page, loff_t from, + loff_t length, int flags); extern int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf); extern qsize_t *ext4_get_reserved_space(struct inode *inode); extern void ext4_da_update_reserve_space(struct inode *inode, diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index b84f127c085d..d1b1ef71e5b7 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -2966,6 +2966,230 @@ void ext4_set_aops(struct inode *inode) inode->i_mapping->a_ops = &ext4_journalled_aops; } + +/* + * ext4_discard_partial_page_buffers() + * Wrapper function for ext4_discard_partial_page_buffers_no_lock. + * This function finds and locks the page containing the offset + * "from" and passes it to ext4_discard_partial_page_buffers_no_lock. + * Calling functions that already have the page locked should call + * ext4_discard_partial_page_buffers_no_lock directly. + */ +int ext4_discard_partial_page_buffers(handle_t *handle, + struct address_space *mapping, loff_t from, + loff_t length, int flags) +{ + struct inode *inode = mapping->host; + struct page *page; + int err = 0; + + page = find_or_create_page(mapping, from >> PAGE_CACHE_SHIFT, + mapping_gfp_mask(mapping) & ~__GFP_FS); + if (!page) + return -EINVAL; + + err = ext4_discard_partial_page_buffers_no_lock(handle, inode, page, + from, length, flags); + + unlock_page(page); + page_cache_release(page); + return err; +} + +/* + * ext4_discard_partial_page_buffers_no_lock() + * Zeros a page range of length 'length' starting from offset 'from'. + * Buffer heads that correspond to the block aligned regions of the + * zeroed range will be unmapped. Unblock aligned regions + * will have the corresponding buffer head mapped if needed so that + * that region of the page can be updated with the partial zero out. + * + * This function assumes that the page has already been locked. The + * The range to be discarded must be contained with in the given page. + * If the specified range exceeds the end of the page it will be shortened + * to the end of the page that corresponds to 'from'. This function is + * appropriate for updating a page and it buffer heads to be unmapped and + * zeroed for blocks that have been either released, or are going to be + * released. + * + * handle: The journal handle + * inode: The files inode + * page: A locked page that contains the offset "from" + * from: The starting byte offset (from the begining of the file) + * to begin discarding + * len: The length of bytes to discard + * flags: Optional flags that may be used: + * + * EXT4_DISCARD_PARTIAL_PG_ZERO_UNMAPPED + * Only zero the regions of the page whose buffer heads + * have already been unmapped. This flag is appropriate + * for updateing the contents of a page whose blocks may + * have already been released, and we only want to zero + * out the regions that correspond to those released blocks. + * + * Returns zero on sucess or negative on failure. + */ +int ext4_discard_partial_page_buffers_no_lock(handle_t *handle, + struct inode *inode, struct page *page, loff_t from, + loff_t length, int flags) +{ + ext4_fsblk_t index = from >> PAGE_CACHE_SHIFT; + unsigned int offset = from & (PAGE_CACHE_SIZE-1); + unsigned int blocksize, max, pos; + unsigned int end_of_block, range_to_discard; + ext4_lblk_t iblock; + struct buffer_head *bh; + int err = 0; + + blocksize = inode->i_sb->s_blocksize; + max = PAGE_CACHE_SIZE - offset; + + if (index != page->index) + return -EINVAL; + + /* + * correct length if it does not fall between + * 'from' and the end of the page + */ + if (length > max || length < 0) + length = max; + + iblock = index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits); + + if (!page_has_buffers(page)) { + /* + * If the range to be discarded covers a partial block + * we need to get the page buffers. This is because + * partial blocks cannot be released and the page needs + * to be updated with the contents of the block before + * we write the zeros on top of it. + */ + if (!(from & (blocksize - 1)) || + !((from + length) & (blocksize - 1))) { + create_empty_buffers(page, blocksize, 0); + } else { + /* + * If there are no partial blocks, + * there is nothing to update, + * so we can return now + */ + return 0; + } + } + + /* Find the buffer that contains "offset" */ + bh = page_buffers(page); + pos = blocksize; + while (offset >= pos) { + bh = bh->b_this_page; + iblock++; + pos += blocksize; + } + + pos = offset; + while (pos < offset + length) { + err = 0; + + /* The length of space left to zero and unmap */ + range_to_discard = offset + length - pos; + + /* The length of space until the end of the block */ + end_of_block = blocksize - (pos & (blocksize-1)); + + /* + * Do not unmap or zero past end of block + * for this buffer head + */ + if (range_to_discard > end_of_block) + range_to_discard = end_of_block; + + + /* + * Skip this buffer head if we are only zeroing unampped + * regions of the page + */ + if (flags & EXT4_DISCARD_PARTIAL_PG_ZERO_UNMAPPED && + buffer_mapped(bh)) + goto next; + + /* If the range is block aligned, unmap */ + if (range_to_discard == blocksize) { + clear_buffer_dirty(bh); + bh->b_bdev = NULL; + clear_buffer_mapped(bh); + clear_buffer_req(bh); + clear_buffer_new(bh); + clear_buffer_delay(bh); + clear_buffer_unwritten(bh); + clear_buffer_uptodate(bh); + zero_user(page, pos, range_to_discard); + BUFFER_TRACE(bh, "Buffer discarded"); + goto next; + } + + /* + * If this block is not completely contained in the range + * to be discarded, then it is not going to be released. Because + * we need to keep this block, we need to make sure this part + * of the page is uptodate before we modify it by writeing + * partial zeros on it. + */ + if (!buffer_mapped(bh)) { + /* + * Buffer head must be mapped before we can read + * from the block + */ + BUFFER_TRACE(bh, "unmapped"); + ext4_get_block(inode, iblock, bh, 0); + /* unmapped? It's a hole - nothing to do */ + if (!buffer_mapped(bh)) { + BUFFER_TRACE(bh, "still unmapped"); + goto next; + } + } + + /* Ok, it's mapped. Make sure it's up-to-date */ + if (PageUptodate(page)) + set_buffer_uptodate(bh); + + if (!buffer_uptodate(bh)) { + err = -EIO; + ll_rw_block(READ, 1, &bh); + wait_on_buffer(bh); + /* Uhhuh. Read error. Complain and punt.*/ + if (!buffer_uptodate(bh)) + goto next; + } + + if (ext4_should_journal_data(inode)) { + BUFFER_TRACE(bh, "get write access"); + err = ext4_journal_get_write_access(handle, bh); + if (err) + goto next; + } + + zero_user(page, pos, range_to_discard); + + err = 0; + if (ext4_should_journal_data(inode)) { + err = ext4_handle_dirty_metadata(handle, inode, bh); + } else { + if (ext4_should_order_data(inode) && + EXT4_I(inode)->jinode) + err = ext4_jbd2_file_inode(handle, inode); + mark_buffer_dirty(bh); + } + + BUFFER_TRACE(bh, "Partial buffer zeroed"); +next: + bh = bh->b_this_page; + iblock++; + pos += range_to_discard; + } + + return err; +} + /* * ext4_block_truncate_page() zeroes out a mapping from file offset `from' * up to the end of the block which corresponds to `from'. From ba06208a1315ab2d2217e09c79582b886c9f629e Mon Sep 17 00:00:00 2001 From: Allison Henderson Date: Sat, 3 Sep 2011 11:55:59 -0400 Subject: [PATCH 0093/1531] ext4: fix xfstests 75, 112, 127 punch hole failure This patch addresses a bug found by xfstests 75, 112, 127 when blocksize = 1k This bug happens because the punch hole code only zeros out non block aligned regions of the page. This means that if the blocks are smaller than a page, then the block aligned regions of the page inside the hole are left un-zeroed, and their buffer heads are still mapped. This bug is corrected by using ext4_discard_partial_page_buffers to properly zero the partial page at the head and tail of the hole, and unmap the corresponding buffer heads This patch also addresses a bug reported by Lukas while working on a new patch to add discard support for loop devices using punch hole. The bug happened because of the first and last block number needed to be cast to a larger data type before calculating the byte offset, but since now we only need the byte offsets of the pages, we no longer even need to be calculating the byte offsets of the blocks. The code to do the block offset calculations is removed in this patch. Signed-off-by: Allison Henderson --- fs/ext4/extents.c | 61 ++++++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 57cf568a98ab..18f7e04a4fa3 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -4162,17 +4162,14 @@ int ext4_ext_punch_hole(struct file *file, loff_t offset, loff_t length) struct address_space *mapping = inode->i_mapping; struct ext4_map_blocks map; handle_t *handle; - loff_t first_block_offset, last_block_offset, block_len; - loff_t first_page, last_page, first_page_offset, last_page_offset; + loff_t first_page, last_page, page_len; + loff_t first_page_offset, last_page_offset; int ret, credits, blocks_released, err = 0; first_block = (offset + sb->s_blocksize - 1) >> EXT4_BLOCK_SIZE_BITS(sb); last_block = (offset + length) >> EXT4_BLOCK_SIZE_BITS(sb); - first_block_offset = first_block << EXT4_BLOCK_SIZE_BITS(sb); - last_block_offset = last_block << EXT4_BLOCK_SIZE_BITS(sb); - first_page = (offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; last_page = (offset + length) >> PAGE_CACHE_SHIFT; @@ -4211,24 +4208,44 @@ int ext4_ext_punch_hole(struct file *file, loff_t offset, loff_t length) goto out; /* - * Now we need to zero out the un block aligned data. - * If the file is smaller than a block, just - * zero out the middle + * Now we need to zero out the non-page-aligned data in the + * pages at the start and tail of the hole, and unmap the buffer + * heads for the block aligned regions of the page that were + * completely zeroed. */ - if (first_block > last_block) - ext4_block_zero_page_range(handle, mapping, offset, length); - else { - /* zero out the head of the hole before the first block */ - block_len = first_block_offset - offset; - if (block_len > 0) - ext4_block_zero_page_range(handle, mapping, - offset, block_len); - - /* zero out the tail of the hole after the last block */ - block_len = offset + length - last_block_offset; - if (block_len > 0) { - ext4_block_zero_page_range(handle, mapping, - last_block_offset, block_len); + if (first_page > last_page) { + /* + * If the file space being truncated is contained within a page + * just zero out and unmap the middle of that page + */ + err = ext4_discard_partial_page_buffers(handle, + mapping, offset, length, 0); + + if (err) + goto out; + } else { + /* + * zero out and unmap the partial page that contains + * the start of the hole + */ + page_len = first_page_offset - offset; + if (page_len > 0) { + err = ext4_discard_partial_page_buffers(handle, mapping, + offset, page_len, 0); + if (err) + goto out; + } + + /* + * zero out and unmap the partial page that contains + * the end of the hole + */ + page_len = offset + length - last_page_offset; + if (page_len > 0) { + err = ext4_discard_partial_page_buffers(handle, mapping, + last_page_offset, page_len, 0); + if (err) + goto out; } } From 2be4751b21ae1cacb002da48cfc5bf6743fee8c1 Mon Sep 17 00:00:00 2001 From: Allison Henderson Date: Sat, 3 Sep 2011 11:56:52 -0400 Subject: [PATCH 0094/1531] ext4: fix 2nd xfstests 127 punch hole failure This patch fixes a second punch hole bug found by xfstests 127. This bug happens because punch hole needs to flush the pages of the hole to avoid race conditions. But if the end of the hole is in the same page as i_size, the buffer heads beyond i_size need to be unmapped and the page needs to be zeroed after it is flushed. To correct this, the new ext4_discard_partial_page_buffers routine is used to zero and unmap the partial page beyond i_size if the end of the hole appears in the same page as i_size. The code has also been optimized to set the end of the hole to the page after i_size if the specified hole exceeds i_size, and the code that flushes the pages has been simplified. Signed-off-by: Allison Henderson --- fs/ext4/extents.c | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 18f7e04a4fa3..9124cd24e093 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -4166,6 +4166,20 @@ int ext4_ext_punch_hole(struct file *file, loff_t offset, loff_t length) loff_t first_page_offset, last_page_offset; int ret, credits, blocks_released, err = 0; + /* No need to punch hole beyond i_size */ + if (offset >= inode->i_size) + return 0; + + /* + * If the hole extends beyond i_size, set the hole + * to end after the page that contains i_size + */ + if (offset + length > inode->i_size) { + length = inode->i_size + + PAGE_CACHE_SIZE - (inode->i_size & (PAGE_CACHE_SIZE - 1)) - + offset; + } + first_block = (offset + sb->s_blocksize - 1) >> EXT4_BLOCK_SIZE_BITS(sb); last_block = (offset + length) >> EXT4_BLOCK_SIZE_BITS(sb); @@ -4182,11 +4196,10 @@ int ext4_ext_punch_hole(struct file *file, loff_t offset, loff_t length) */ if (mapping->nrpages && mapping_tagged(mapping, PAGECACHE_TAG_DIRTY)) { err = filemap_write_and_wait_range(mapping, - first_page_offset == 0 ? 0 : first_page_offset-1, - last_page_offset); + offset, offset + length - 1); - if (err) - return err; + if (err) + return err; } /* Now release the pages */ @@ -4249,6 +4262,26 @@ int ext4_ext_punch_hole(struct file *file, loff_t offset, loff_t length) } } + + /* + * If i_size is contained in the last page, we need to + * unmap and zero the partial page after i_size + */ + if (inode->i_size >> PAGE_CACHE_SHIFT == last_page && + inode->i_size % PAGE_CACHE_SIZE != 0) { + + page_len = PAGE_CACHE_SIZE - + (inode->i_size & (PAGE_CACHE_SIZE - 1)); + + if (page_len > 0) { + err = ext4_discard_partial_page_buffers(handle, + mapping, inode->i_size, page_len, 0); + + if (err) + goto out; + } + } + /* If there are no blocks to remove, return now */ if (first_block >= last_block) goto out; From 56889787cfa77dfd96f0b3a3e6a4f26c2e4a5134 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Sat, 3 Sep 2011 18:22:38 -0400 Subject: [PATCH 0095/1531] ext4: improve handling of conflicting mount options If the user explicitly specifies conflicting mount options for delalloc or dioread_nolock and data=journal, fail the mount, instead of printing a warning and continuing (since many user's won't look at dmesg and notice the warning). Also, print a single warning that data=journal implies that delayed allocation is not on by default (since it's not supported), and furthermore that O_DIRECT is not supported. Improve the text in Documentation/filesystems/ext4.txt so this is clear there as well. Similarly, if the dioread_nolock mount option is specified when the file system block size != PAGE_SIZE, fail the mount instead of printing a warning message and ignoring the mount option. Signed-off-by: "Theodore Ts'o" --- Documentation/filesystems/ext4.txt | 8 +++-- fs/ext4/ext4.h | 3 ++ fs/ext4/super.c | 50 +++++++++++++++++------------- 3 files changed, 37 insertions(+), 24 deletions(-) diff --git a/Documentation/filesystems/ext4.txt b/Documentation/filesystems/ext4.txt index 232a575a0c48..168242b5c045 100644 --- a/Documentation/filesystems/ext4.txt +++ b/Documentation/filesystems/ext4.txt @@ -160,7 +160,9 @@ noload if the filesystem was not unmounted cleanly, lead to any number of problems. data=journal All data are committed into the journal prior to being - written into the main file system. + written into the main file system. Enabling + this mode will disable delayed allocation and + O_DIRECT support. data=ordered (*) All data are forced directly out to the main file system prior to its metadata being committed to the @@ -419,8 +421,8 @@ written to the journal first, and then to its final location. In the event of a crash, the journal can be replayed, bringing both data and metadata into a consistent state. This mode is the slowest except when data needs to be read from and written to disk at the same time where it -outperforms all others modes. Currently ext4 does not have delayed -allocation support if this data journalling mode is selected. +outperforms all others modes. Enabling this mode will disable delayed +allocation and O_DIRECT support. /proc entries ============= diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index ccfa81f33bb0..48ae98819d35 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -922,6 +922,9 @@ struct ext4_inode_info { #define EXT4_MOUNT_DISCARD 0x40000000 /* Issue DISCARD requests */ #define EXT4_MOUNT_INIT_INODE_TABLE 0x80000000 /* Initialize uninitialized itables */ +#define EXT4_MOUNT2_EXPLICIT_DELALLOC 0x00000001 /* User explicitly + specified delalloc */ + #define clear_opt(sb, opt) EXT4_SB(sb)->s_mount_opt &= \ ~EXT4_MOUNT_##opt #define set_opt(sb, opt) EXT4_SB(sb)->s_mount_opt |= \ diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 44d0c8db2239..ee2f74a7084d 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1801,6 +1801,7 @@ static int parse_options(char *options, struct super_block *sb, break; case Opt_nodelalloc: clear_opt(sb, DELALLOC); + clear_opt2(sb, EXPLICIT_DELALLOC); break; case Opt_mblk_io_submit: set_opt(sb, MBLK_IO_SUBMIT); @@ -1817,6 +1818,7 @@ static int parse_options(char *options, struct super_block *sb, break; case Opt_delalloc: set_opt(sb, DELALLOC); + set_opt2(sb, EXPLICIT_DELALLOC); break; case Opt_block_validity: set_opt(sb, BLOCK_VALIDITY); @@ -3224,6 +3226,33 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) &journal_ioprio, NULL, 0)) goto failed_mount; + if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA) { + printk_once(KERN_WARNING "EXT4-fs: Warning: mounting " + "with data=journal disables delayed " + "allocation and O_DIRECT support!\n"); + if (test_opt2(sb, EXPLICIT_DELALLOC)) { + ext4_msg(sb, KERN_ERR, "can't mount with " + "both data=journal and delalloc"); + goto failed_mount; + } + if (test_opt(sb, DIOREAD_NOLOCK)) { + ext4_msg(sb, KERN_ERR, "can't mount with " + "both data=journal and delalloc"); + goto failed_mount; + } + if (test_opt(sb, DELALLOC)) + clear_opt(sb, DELALLOC); + } + + blocksize = BLOCK_SIZE << le32_to_cpu(es->s_log_block_size); + if (test_opt(sb, DIOREAD_NOLOCK)) { + if (blocksize < PAGE_SIZE) { + ext4_msg(sb, KERN_ERR, "can't mount with " + "dioread_nolock if block size != PAGE_SIZE"); + goto failed_mount; + } + } + sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | (test_opt(sb, POSIX_ACL) ? MS_POSIXACL : 0); @@ -3265,8 +3294,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) if (!ext4_feature_set_ok(sb, (sb->s_flags & MS_RDONLY))) goto failed_mount; - blocksize = BLOCK_SIZE << le32_to_cpu(es->s_log_block_size); - if (blocksize < EXT4_MIN_BLOCK_SIZE || blocksize > EXT4_MAX_BLOCK_SIZE) { ext4_msg(sb, KERN_ERR, @@ -3679,25 +3706,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) "available"); } - if (test_opt(sb, DELALLOC) && - (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA)) { - ext4_msg(sb, KERN_WARNING, "Ignoring delalloc option - " - "requested data journaling mode"); - clear_opt(sb, DELALLOC); - } - if (test_opt(sb, DIOREAD_NOLOCK)) { - if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA) { - ext4_msg(sb, KERN_WARNING, "Ignoring dioread_nolock " - "option - requested data journaling mode"); - clear_opt(sb, DIOREAD_NOLOCK); - } - if (sb->s_blocksize < PAGE_SIZE) { - ext4_msg(sb, KERN_WARNING, "Ignoring dioread_nolock " - "option - block size is too small"); - clear_opt(sb, DIOREAD_NOLOCK); - } - } - err = ext4_setup_system_zone(sb); if (err) { ext4_msg(sb, KERN_ERR, "failed to initialize system " From 9ea7a0df63630ad8197716cd313ea66e28906fc0 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Sun, 4 Sep 2011 10:18:14 -0400 Subject: [PATCH 0096/1531] jbd2: add debugging information to jbd2_journal_dirty_metadata() Add debugging information in case jbd2_journal_dirty_metadata() is called with a buffer_head which didn't have jbd2_journal_get_write_access() called on it, or if the journal_head has the wrong transaction in it. In addition, return an error code. This won't change anything for ocfs2, which will BUG_ON() the non-zero exit code. For ext4, the caller of this function is ext4_handle_dirty_metadata(), and on seeing a non-zero return code, will call __ext4_journal_stop(), which will print the function and line number of the (buggy) calling function and abort the journal. This will allow us to recover instead of bug halting, which is better from a robustness and reliability point of view. Signed-off-by: "Theodore Ts'o" --- fs/ext4/ext4_jbd2.c | 8 +++--- fs/ext4/extents.c | 10 +++++--- fs/jbd2/transaction.c | 58 ++++++++++++++++++++++++++++++++++++++----- 3 files changed, 64 insertions(+), 12 deletions(-) diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c index f5240aa15601..aca179017582 100644 --- a/fs/ext4/ext4_jbd2.c +++ b/fs/ext4/ext4_jbd2.c @@ -109,9 +109,11 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line, if (ext4_handle_valid(handle)) { err = jbd2_journal_dirty_metadata(handle, bh); - if (err) - ext4_journal_abort_handle(where, line, __func__, - bh, handle, err); + if (err) { + /* Errors can only happen if there is a bug */ + handle->h_err = err; + __ext4_journal_stop(where, line, handle); + } } else { if (inode) mark_buffer_dirty_inode(bh, inode); diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 9124cd24e093..2c5216a8d03b 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -96,13 +96,17 @@ static int ext4_ext_get_access(handle_t *handle, struct inode *inode, * - ENOMEM * - EIO */ -static int ext4_ext_dirty(handle_t *handle, struct inode *inode, - struct ext4_ext_path *path) +#define ext4_ext_dirty(handle, inode, path) \ + __ext4_ext_dirty(__func__, __LINE__, (handle), (inode), (path)) +static int __ext4_ext_dirty(const char *where, unsigned int line, + handle_t *handle, struct inode *inode, + struct ext4_ext_path *path) { int err; if (path->p_bh) { /* path points to block */ - err = ext4_handle_dirty_metadata(handle, inode, path->p_bh); + err = __ext4_handle_dirty_metadata(where, line, handle, + inode, path->p_bh); } else { /* path points to leaf/index in inode body */ err = ext4_mark_inode_dirty(handle, inode); diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index 2d7109414cdd..cb56fe9aaabb 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -1049,6 +1049,10 @@ void jbd2_buffer_abort_trigger(struct journal_head *jh, * mark dirty metadata which needs to be journaled as part of the current * transaction. * + * The buffer must have previously had jbd2_journal_get_write_access() + * called so that it has a valid journal_head attached to the buffer + * head. + * * The buffer is placed on the transaction's metadata list and is marked * as belonging to the transaction. * @@ -1065,11 +1069,16 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh) transaction_t *transaction = handle->h_transaction; journal_t *journal = transaction->t_journal; struct journal_head *jh = bh2jh(bh); + int ret = 0; jbd_debug(5, "journal_head %p\n", jh); JBUFFER_TRACE(jh, "entry"); if (is_handle_aborted(handle)) goto out; + if (!buffer_jbd(bh)) { + ret = -EUCLEAN; + goto out; + } jbd_lock_bh_state(bh); @@ -1093,8 +1102,20 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh) */ if (jh->b_transaction == transaction && jh->b_jlist == BJ_Metadata) { JBUFFER_TRACE(jh, "fastpath"); - J_ASSERT_JH(jh, jh->b_transaction == - journal->j_running_transaction); + if (unlikely(jh->b_transaction != + journal->j_running_transaction)) { + printk(KERN_EMERG "JBD: %s: " + "jh->b_transaction (%llu, %p, %u) != " + "journal->j_running_transaction (%p, %u)", + journal->j_devname, + (unsigned long long) bh->b_blocknr, + jh->b_transaction, + jh->b_transaction ? jh->b_transaction->t_tid : 0, + journal->j_running_transaction, + journal->j_running_transaction ? + journal->j_running_transaction->t_tid : 0); + ret = -EINVAL; + } goto out_unlock_bh; } @@ -1108,9 +1129,32 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh) */ if (jh->b_transaction != transaction) { JBUFFER_TRACE(jh, "already on other transaction"); - J_ASSERT_JH(jh, jh->b_transaction == - journal->j_committing_transaction); - J_ASSERT_JH(jh, jh->b_next_transaction == transaction); + if (unlikely(jh->b_transaction != + journal->j_committing_transaction)) { + printk(KERN_EMERG "JBD: %s: " + "jh->b_transaction (%llu, %p, %u) != " + "journal->j_committing_transaction (%p, %u)", + journal->j_devname, + (unsigned long long) bh->b_blocknr, + jh->b_transaction, + jh->b_transaction ? jh->b_transaction->t_tid : 0, + journal->j_committing_transaction, + journal->j_committing_transaction ? + journal->j_committing_transaction->t_tid : 0); + ret = -EINVAL; + } + if (unlikely(jh->b_next_transaction != transaction)) { + printk(KERN_EMERG "JBD: %s: " + "jh->b_next_transaction (%llu, %p, %u) != " + "transaction (%p, %u)", + journal->j_devname, + (unsigned long long) bh->b_blocknr, + jh->b_next_transaction, + jh->b_next_transaction ? + jh->b_next_transaction->t_tid : 0, + transaction, transaction->t_tid); + ret = -EINVAL; + } /* And this case is illegal: we can't reuse another * transaction's data buffer, ever. */ goto out_unlock_bh; @@ -1127,7 +1171,9 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh) jbd_unlock_bh_state(bh); out: JBUFFER_TRACE(jh, "exit"); - return 0; + if (ret) + __WARN(); /* All errors are bugs, so dump the stack */ + return ret; } /* From d2159fb7b8bac12684aabdf41d84b56da9f5c062 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sun, 4 Sep 2011 10:20:14 -0400 Subject: [PATCH 0097/1531] jbd2: use gfp_t instead of int This silences some Sparse warnings: fs/jbd2/transaction.c:135:69: warning: incorrect type in argument 2 (different base types) fs/jbd2/transaction.c:135:69: expected restricted gfp_t [usertype] flags fs/jbd2/transaction.c:135:69: got int [signed] gfp_mask Signed-off-by: Dan Carpenter Signed-off-by: "Theodore Ts'o" --- fs/jbd2/transaction.c | 6 +++--- include/linux/jbd2.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index cb56fe9aaabb..b01fd6104089 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -115,7 +115,7 @@ static inline void update_t_max_wait(transaction_t *transaction, */ static int start_this_handle(journal_t *journal, handle_t *handle, - int gfp_mask) + gfp_t gfp_mask) { transaction_t *transaction, *new_transaction = NULL; tid_t tid; @@ -320,7 +320,7 @@ static handle_t *new_handle(int nblocks) * Return a pointer to a newly allocated handle, or an ERR_PTR() value * on failure. */ -handle_t *jbd2__journal_start(journal_t *journal, int nblocks, int gfp_mask) +handle_t *jbd2__journal_start(journal_t *journal, int nblocks, gfp_t gfp_mask) { handle_t *handle = journal_current_handle(); int err; @@ -443,7 +443,7 @@ int jbd2_journal_extend(handle_t *handle, int nblocks) * transaction capabable of guaranteeing the requested number of * credits. */ -int jbd2__journal_restart(handle_t *handle, int nblocks, int gfp_mask) +int jbd2__journal_restart(handle_t *handle, int nblocks, gfp_t gfp_mask) { transaction_t *transaction = handle->h_transaction; journal_t *journal = transaction->t_journal; diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index 38f307b8c334..3dd101e49d36 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -1106,9 +1106,9 @@ static inline handle_t *journal_current_handle(void) */ extern handle_t *jbd2_journal_start(journal_t *, int nblocks); -extern handle_t *jbd2__journal_start(journal_t *, int nblocks, int gfp_mask); +extern handle_t *jbd2__journal_start(journal_t *, int nblocks, gfp_t gfp_mask); extern int jbd2_journal_restart(handle_t *, int nblocks); -extern int jbd2__journal_restart(handle_t *, int nblocks, int gfp_mask); +extern int jbd2__journal_restart(handle_t *, int nblocks, gfp_t gfp_mask); extern int jbd2_journal_extend (handle_t *, int nblocks); extern int jbd2_journal_get_write_access(handle_t *, struct buffer_head *); extern int jbd2_journal_get_create_access (handle_t *, struct buffer_head *); From 41309b7a22805f1650c600723d729af453d52719 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Fri, 2 Sep 2011 03:47:12 +0000 Subject: [PATCH 0098/1531] sh: kexec: Register crashk_res Register crashk_res so that it can be used by kexec-tools via /proc/iomem. The crash kernel resource needs to be requested the same as the other kernel resources due to the fact that it's handled during the common path for adding new memory ranges, so it's added in to __add_active_range() with the others. This ensures that the crash kernel is properly reserved regardless of which memory range it's placed in. Signed-off-by: Simon Horman Signed-off-by: Paul Mundt --- arch/sh/kernel/setup.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/arch/sh/kernel/setup.c b/arch/sh/kernel/setup.c index 58bff45d1156..1a0e946679a4 100644 --- a/arch/sh/kernel/setup.c +++ b/arch/sh/kernel/setup.c @@ -211,13 +211,16 @@ void __init __add_active_range(unsigned int nid, unsigned long start_pfn, } /* - * We don't know which RAM region contains kernel data, - * so we try it repeatedly and let the resource manager - * test it. + * We don't know which RAM region contains kernel data or + * the reserved crashkernel region, so try it repeatedly + * and let the resource manager test it. */ request_resource(res, &code_resource); request_resource(res, &data_resource); request_resource(res, &bss_resource); +#ifdef CONFIG_KEXEC + request_resource(res, &crashk_res); +#endif /* * Also make sure that there is a PMB mapping that covers this From 6988d647231d284733f6c8bfea473a17dccdd39f Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Fri, 26 Aug 2011 05:05:41 +0000 Subject: [PATCH 0099/1531] sh: Fix implicit declaration of function numa_node_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CC arch/sh/kernel/topology.o arch/sh/kernel/topology.c: In function ‘topology_init’: arch/sh/kernel/topology.c:77: error: implicit declaration of function ‘numa_node_id’ Signed-off-by: Nobuhiro Iwamatsu Signed-off-by: Paul Mundt --- arch/sh/kernel/topology.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/sh/kernel/topology.c b/arch/sh/kernel/topology.c index 38e862852dd0..ecc2d3d0f54a 100644 --- a/arch/sh/kernel/topology.c +++ b/arch/sh/kernel/topology.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include From 89de9f65429a97ab627310e2e85426dcbe423f39 Mon Sep 17 00:00:00 2001 From: Per Forlin Date: Mon, 29 Aug 2011 13:33:32 +0200 Subject: [PATCH 0100/1531] dmaengine/ste_dma40: add missing kernel doc for pending_queue Signed-off-by: Per Forlin Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/ste_dma40.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index cd3a7c726bf8..486b6c0b44e3 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -174,6 +174,7 @@ struct d40_base; * @tasklet: Tasklet that gets scheduled from interrupt context to complete a * transfer and call client callback. * @client: Cliented owned descriptor list. + * @pending_queue: Submitted jobs, to be issued by issue_pending() * @active: Active descriptor. * @queue: Queued jobs. * @dma_cfg: The client configuration of this dma channel. From 270e779036ff144d6c6904ce9480f0d70ff93e86 Mon Sep 17 00:00:00 2001 From: Per Forlin Date: Mon, 29 Aug 2011 13:33:33 +0200 Subject: [PATCH 0101/1531] dmaengine/ste_dma40: remove duplicate call to d40_pool_lli_free(). d40_desc_free() already calls d40_pool_lli_free(). Signed-off-by: Per Forlin Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/ste_dma40.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 486b6c0b44e3..37388d10497a 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -478,7 +478,6 @@ static struct d40_desc *d40_desc_get(struct d40_chan *d40c) list_for_each_entry_safe(d, _d, &d40c->client, node) if (async_tx_test_ack(&d->txd)) { - d40_pool_lli_free(d40c, d); d40_desc_remove(d); desc = d; memset(desc, 0, sizeof(*desc)); @@ -1209,7 +1208,6 @@ static void dma_tasklet(unsigned long data) if (!d40d->cyclic) { if (async_tx_test_ack(&d40d->txd)) { - d40_pool_lli_free(d40c, d40d); d40_desc_remove(d40d); d40_desc_free(d40c, d40d); } else { @@ -1606,7 +1604,6 @@ static int d40_free_dma(struct d40_chan *d40c) /* Release client owned descriptors */ if (!list_empty(&d40c->client)) list_for_each_entry_safe(d, _d, &d40c->client, node) { - d40_pool_lli_free(d40c, d); d40_desc_remove(d); d40_desc_free(d40c, d); } From 70a207ad4db2f0c60308b3f32086263c438c67a3 Mon Sep 17 00:00:00 2001 From: Per Forlin Date: Mon, 29 Aug 2011 13:33:34 +0200 Subject: [PATCH 0102/1531] dmaengine/ste_dma40: fix Oops due to double free of client descriptor The client list may exist in two lists at the same time. This makes free fail since the same desc is freed multiple times. Remove desc from client list when adding it to the pending queue. Move free of client owned descriptors from free_dma() to terminate_all(). Unable to handle kernel paging request at virtual address 00100104 pgd = dea8c000 [00100104] *pgd=1ea62831, *pte=00000000, *ppte=00000000 Internal error: Oops: 817 [#1] PREEMPT SMP Modules linked in: CPU: 0 Not tainted (3.1.0-rc3+ #58) PC is at d40_free_chan_resources+0x64/0x330 Signed-off-by: Per Forlin Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/ste_dma40.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 37388d10497a..92ec0a26401a 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -644,8 +644,11 @@ static struct d40_desc *d40_first_active_get(struct d40_chan *d40c) return d; } +/* remove desc from current queue and add it to the pending_queue */ static void d40_desc_queue(struct d40_chan *d40c, struct d40_desc *desc) { + d40_desc_remove(desc); + desc->is_in_client_list = false; list_add_tail(&desc->node, &d40c->pending_queue); } @@ -803,6 +806,7 @@ static int d40_channel_execute_command(struct d40_chan *d40c, static void d40_term_all(struct d40_chan *d40c) { struct d40_desc *d40d; + struct d40_desc *_d; /* Release active descriptors */ while ((d40d = d40_first_active_get(d40c))) { @@ -822,6 +826,14 @@ static void d40_term_all(struct d40_chan *d40c) d40_desc_free(d40c, d40d); } + /* Release client owned descriptors */ + if (!list_empty(&d40c->client)) + list_for_each_entry_safe(d40d, _d, &d40c->client, node) { + d40_desc_remove(d40d); + d40_desc_free(d40c, d40d); + } + + d40c->pending_tx = 0; d40c->busy = false; } @@ -1594,20 +1606,10 @@ static int d40_free_dma(struct d40_chan *d40c) u32 event; struct d40_phy_res *phy = d40c->phy_chan; bool is_src; - struct d40_desc *d; - struct d40_desc *_d; - /* Terminate all queued and active transfers */ d40_term_all(d40c); - /* Release client owned descriptors */ - if (!list_empty(&d40c->client)) - list_for_each_entry_safe(d, _d, &d40c->client, node) { - d40_desc_remove(d); - d40_desc_free(d40c, d); - } - if (phy == NULL) { chan_err(d40c, "phy == null\n"); return -EINVAL; From 503473ac2a3952e6af254b0769fe788a67d797e5 Mon Sep 17 00:00:00 2001 From: Per Forlin Date: Mon, 29 Aug 2011 13:33:35 +0200 Subject: [PATCH 0103/1531] dmaengine/ste_dma40: fix memory leak due to prepared descriptors Prepared descriptors that are not submitted will not be freed. Add prepared descriptor to a list to be able to release them upon dmaengine_terminate_all(). Signed-off-by: Per Forlin Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/ste_dma40.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 92ec0a26401a..467e4dcb20a0 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -177,6 +177,7 @@ struct d40_base; * @pending_queue: Submitted jobs, to be issued by issue_pending() * @active: Active descriptor. * @queue: Queued jobs. + * @prepare_queue: Prepared jobs. * @dma_cfg: The client configuration of this dma channel. * @configured: whether the dma_cfg configuration is valid * @base: Pointer to the device instance struct. @@ -204,6 +205,7 @@ struct d40_chan { struct list_head pending_queue; struct list_head active; struct list_head queue; + struct list_head prepare_queue; struct stedma40_chan_cfg dma_cfg; bool configured; struct d40_base *base; @@ -833,6 +835,13 @@ static void d40_term_all(struct d40_chan *d40c) d40_desc_free(d40c, d40d); } + /* Release descriptors in prepare queue */ + if (!list_empty(&d40c->prepare_queue)) + list_for_each_entry_safe(d40d, _d, + &d40c->prepare_queue, node) { + d40_desc_remove(d40d); + d40_desc_free(d40c, d40d); + } d40c->pending_tx = 0; d40c->busy = false; @@ -1911,6 +1920,12 @@ d40_prep_sg(struct dma_chan *dchan, struct scatterlist *sg_src, goto err; } + /* + * add descriptor to the prepare queue in order to be able + * to free them later in terminate_all + */ + list_add_tail(&desc->node, &chan->prepare_queue); + spin_unlock_irqrestore(&chan->lock, flags); return &desc->txd; @@ -2400,6 +2415,7 @@ static void __init d40_chan_init(struct d40_base *base, struct dma_device *dma, INIT_LIST_HEAD(&d40c->queue); INIT_LIST_HEAD(&d40c->pending_queue); INIT_LIST_HEAD(&d40c->client); + INIT_LIST_HEAD(&d40c->prepare_queue); tasklet_init(&d40c->tasklet, dma_tasklet, (unsigned long) d40c); From 7703eac96abd119dcfbb04f287a5127462d18269 Mon Sep 17 00:00:00 2001 From: Russell King - ARM Linux Date: Wed, 31 Aug 2011 09:34:35 +0100 Subject: [PATCH 0104/1531] dmaengine: amba-pl08x: make filter check that the channel is owned by pl08x Before converting the dma channel to our private data structure, first check that the channel is indeed one which our driver registered. We do this by ensuring that the underlying device is bound to our driver. This avoids potential oopses if we try to reference 'plchan->name' against a foreign drivers dma channel. Signed-off-by: Russell King Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 3c2cad5b1165..cd8df7f5b5c8 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -87,6 +87,8 @@ #define DRIVER_NAME "pl08xdmac" +static struct amba_driver pl08x_amba_driver; + /** * struct vendor_data - vendor-specific config parameters for PL08x derivatives * @channels: the number of channels available in this variant @@ -1420,9 +1422,15 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, bool pl08x_filter_id(struct dma_chan *chan, void *chan_id) { - struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); + struct pl08x_dma_chan *plchan; char *name = chan_id; + /* Reject channels for devices not bound to this driver */ + if (chan->device->dev->driver != &pl08x_amba_driver.drv) + return false; + + plchan = to_pl08x_chan(chan); + /* Check that the channel is not taken! */ if (!strcmp(plchan->name, name)) return true; From decbd919f4bb9cb698486880c026c4104b13d3c3 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Tue, 6 Sep 2011 02:37:06 -0400 Subject: [PATCH 0105/1531] ext4: only call ext4_jbd2_file_inode when an inode has been extended In delayed allocation mode, it's important to only call ext4_jbd2_file_inode when the file has been extended. This is necessary to avoid a race which first got introduced in commit 678aaf481, but which was made much more common with the introduction of the "punch hole" functionality. (Especially when dioread_nolock was enabled; when I could reliably reproduce this problem with xfstests #74.) The race is this: If while trying to writeback a delayed allocation inode, there is a need to map delalloc blocks, and we run out of space in the journal, *and* at the same time the inode is already on the committing transaction's t_inode_list (because for example while doing the punch hole operation, ext4_jbd2_file_inode() is called), then the commit operation will wait for the inode to finish all of its pending writebacks by calling filemap_fdatawait(), but since that inode has one or more pages with the PageWriteback flag set, the commit operation will wait forever, and the so the writeback of the inode can never take place, and the kjournald thread and the writeback thread end up waiting for each other --- forever. It's important at this point to recall why an inode is placed on the t_inode_list; it is to provide the data=ordered guarantees that we don't end up exposing stale data. In the case where we are truncating or punching a hole in the inode, there is no possibility that stale data could be exposed in the first place, so we don't need to put the inode on the t_inode_list! The right long-term fix is to get rid of data=ordered mode altogether, and only update the extent tree or indirect blocks after the data has been written. Until then, this change will also avoid some unnecessary waiting in the commit operation. Signed-off-by: "Theodore Ts'o" Cc: Allison Henderson Cc: Jan Kara --- fs/ext4/inode.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index d1b1ef71e5b7..f86b149fb8b1 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1471,13 +1471,13 @@ static void mpage_da_map_and_submit(struct mpage_da_data *mpd) for (i = 0; i < map.m_len; i++) unmap_underlying_metadata(bdev, map.m_pblk + i); - } - if (ext4_should_order_data(mpd->inode)) { - err = ext4_jbd2_file_inode(handle, mpd->inode); - if (err) - /* This only happens if the journal is aborted */ - return; + if (ext4_should_order_data(mpd->inode)) { + err = ext4_jbd2_file_inode(handle, mpd->inode); + if (err) + /* Only if the journal is aborted */ + return; + } } /* @@ -3173,12 +3173,8 @@ int ext4_discard_partial_page_buffers_no_lock(handle_t *handle, err = 0; if (ext4_should_journal_data(inode)) { err = ext4_handle_dirty_metadata(handle, inode, bh); - } else { - if (ext4_should_order_data(inode) && - EXT4_I(inode)->jinode) - err = ext4_jbd2_file_inode(handle, inode); + } else mark_buffer_dirty(bh); - } BUFFER_TRACE(bh, "Partial buffer zeroed"); next: @@ -3301,11 +3297,8 @@ int ext4_block_zero_page_range(handle_t *handle, err = 0; if (ext4_should_journal_data(inode)) { err = ext4_handle_dirty_metadata(handle, inode, bh); - } else { - if (ext4_should_order_data(inode) && EXT4_I(inode)->jinode) - err = ext4_jbd2_file_inode(handle, inode); + } else mark_buffer_dirty(bh); - } unlock: unlock_page(page); From 189e868fa8fdca702eb9db9d8afc46b5cb9144c9 Mon Sep 17 00:00:00 2001 From: Allison Henderson Date: Tue, 6 Sep 2011 21:49:44 -0400 Subject: [PATCH 0106/1531] ext4: fix fsx truncate failure While running extended fsx tests to verify the first two patches, a similar bug was also found in the truncate operation. This bug happens because the truncate routine only zeros the unblock aligned portion of the last page. This means that the block aligned portions of the page appearing after i_size are left unzeroed, and the buffer heads still mapped. This bug is corrected by using ext4_discard_partial_page_buffers in the truncate routine to zero the partial page and unmap the buffer headers. Signed-off-by: Allison Henderson Signed-off-by: "Theodore Ts'o" --- fs/ext4/extents.c | 13 +++++++++++-- fs/ext4/indirect.c | 13 +++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 2c5216a8d03b..ba7bd5a176ce 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -3653,6 +3653,7 @@ void ext4_ext_truncate(struct inode *inode) struct super_block *sb = inode->i_sb; ext4_lblk_t last_block; handle_t *handle; + loff_t page_len; int err = 0; /* @@ -3669,8 +3670,16 @@ void ext4_ext_truncate(struct inode *inode) if (IS_ERR(handle)) return; - if (inode->i_size & (sb->s_blocksize - 1)) - ext4_block_truncate_page(handle, mapping, inode->i_size); + if (inode->i_size % PAGE_CACHE_SIZE != 0) { + page_len = PAGE_CACHE_SIZE - + (inode->i_size & (PAGE_CACHE_SIZE - 1)); + + err = ext4_discard_partial_page_buffers(handle, + mapping, inode->i_size, page_len, 0); + + if (err) + goto out_stop; + } if (ext4_orphan_add(handle, inode)) goto out_stop; diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c index 0962642119c0..ea704dff8669 100644 --- a/fs/ext4/indirect.c +++ b/fs/ext4/indirect.c @@ -1343,7 +1343,9 @@ void ext4_ind_truncate(struct inode *inode) __le32 nr = 0; int n = 0; ext4_lblk_t last_block, max_block; + loff_t page_len; unsigned blocksize = inode->i_sb->s_blocksize; + int err; handle = start_transaction(inode); if (IS_ERR(handle)) @@ -1354,9 +1356,16 @@ void ext4_ind_truncate(struct inode *inode) max_block = (EXT4_SB(inode->i_sb)->s_bitmap_maxbytes + blocksize-1) >> EXT4_BLOCK_SIZE_BITS(inode->i_sb); - if (inode->i_size & (blocksize - 1)) - if (ext4_block_truncate_page(handle, mapping, inode->i_size)) + if (inode->i_size % PAGE_CACHE_SIZE != 0) { + page_len = PAGE_CACHE_SIZE - + (inode->i_size & (PAGE_CACHE_SIZE - 1)); + + err = ext4_discard_partial_page_buffers(handle, + mapping, inode->i_size, page_len, 0); + + if (err) goto out_stop; + } if (last_block != max_block) { n = ext4_block_to_path(inode, last_block, offsets, NULL); From 02fac1297eb3f471a27368271aadd285548297b0 Mon Sep 17 00:00:00 2001 From: Allison Henderson Date: Tue, 6 Sep 2011 21:53:01 -0400 Subject: [PATCH 0107/1531] ext4: fix partial page writes While running extended fsx tests to verify the preceeding patches, a similar bug was also found in the write operation When ever a write operation begins or ends in a hole, or extends EOF, the partial page contained in the hole or beyond EOF needs to be zeroed out. To correct this the new ext4_discard_partial_page_buffers_no_lock routine is used to zero out the partial page, but only for buffer heads that are already unmapped. Signed-off-by: Allison Henderson Signed-off-by: "Theodore Ts'o" --- fs/ext4/inode.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index f86b149fb8b1..6ecc93979e48 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -2255,6 +2255,7 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping, pgoff_t index; struct inode *inode = mapping->host; handle_t *handle; + loff_t page_len; index = pos >> PAGE_CACHE_SHIFT; @@ -2301,6 +2302,13 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping, */ if (pos + len > inode->i_size) ext4_truncate_failed_write(inode); + } else { + page_len = pos & (PAGE_CACHE_SIZE - 1); + if (page_len > 0) { + ret = ext4_discard_partial_page_buffers_no_lock(handle, + inode, page, pos - page_len, page_len, + EXT4_DISCARD_PARTIAL_PG_ZERO_UNMAPPED); + } } if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries)) @@ -2343,6 +2351,7 @@ static int ext4_da_write_end(struct file *file, loff_t new_i_size; unsigned long start, end; int write_mode = (int)(unsigned long)fsdata; + loff_t page_len; if (write_mode == FALL_BACK_TO_NONDELALLOC) { if (ext4_should_order_data(inode)) { @@ -2391,6 +2400,16 @@ static int ext4_da_write_end(struct file *file, } ret2 = generic_write_end(file, mapping, pos, len, copied, page, fsdata); + + page_len = PAGE_CACHE_SIZE - + ((pos + copied - 1) & (PAGE_CACHE_SIZE - 1)); + + if (page_len > 0) { + ret = ext4_discard_partial_page_buffers_no_lock(handle, + inode, page, pos + copied - 1, page_len, + EXT4_DISCARD_PARTIAL_PG_ZERO_UNMAPPED); + } + copied = ret2; if (ret2 < 0) ret = ret2; From 6a5be57f0f00bf5f39b983ec98d58058bc696543 Mon Sep 17 00:00:00 2001 From: Peter Foley Date: Mon, 1 Aug 2011 09:51:24 -0400 Subject: [PATCH 0108/1531] fixdep: fix extraneous dependencies The introduction of include/linux/kconfig.h created 3 extraneous dependencies: include/config/.h include/config/h.h include/config/foo.h Fix this by excluding kconfig.h from fixdep calculations. Signed-off-by: Peter Foley Signed-off-by: Michal Marek --- scripts/basic/fixdep.c | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/basic/fixdep.c b/scripts/basic/fixdep.c index 291228e25984..cb1f50cf12e3 100644 --- a/scripts/basic/fixdep.c +++ b/scripts/basic/fixdep.c @@ -345,6 +345,7 @@ static void parse_dep_file(void *map, size_t len) memcpy(s, m, p-m); s[p-m] = 0; if (strrcmp(s, "include/generated/autoconf.h") && strrcmp(s, "arch/um/include/uml-config.h") && + strrcmp(s, "include/linux/kconfig.h") && strrcmp(s, ".ver")) { /* * Do not list the source file as dependency, so that From cd96ea3a4f2c9c226216c8d8e57fd8f86801515d Mon Sep 17 00:00:00 2001 From: Amerigo Wang Date: Wed, 17 Aug 2011 15:10:52 +0800 Subject: [PATCH 0109/1531] Makefile: remove a duplicated line There are two duplicated lines for 'make RECORDMCOUNT_WARN=1 [targets]' in 'make help', remove the second one. Signed-off-by: WANG Cong Signed-off-by: Michal Marek --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index d714838d0a3a..ab38311014e6 100644 --- a/Makefile +++ b/Makefile @@ -1296,7 +1296,6 @@ help: @echo ' 2: warnings which occur quite often but may still be relevant' @echo ' 3: more obscure warnings, can most likely be ignored' @echo ' Multiple levels can be combined with W=12 or W=123' - @echo ' make RECORDMCOUNT_WARN=1 [targets] Warn about ignored mcount sections' @echo '' @echo 'Execute "make" or "make all" to build all targets marked with [*] ' @echo 'For further info see the ./README file' From 4e24dbfc26f8b8285c9ed2f92ffddf4eb8ab960a Mon Sep 17 00:00:00 2001 From: Cheng Renquan Date: Thu, 1 Sep 2011 10:52:18 -0700 Subject: [PATCH 0110/1531] scripts/kconfig/nconf: fix typo: unknow => unknown Signed-off-by: Cheng Renquan Acked-by: Arnaud Lacombe --- scripts/kconfig/nconf.gui.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/kconfig/nconf.gui.c b/scripts/kconfig/nconf.gui.c index f8137b3a5382..d3af04e95288 100644 --- a/scripts/kconfig/nconf.gui.c +++ b/scripts/kconfig/nconf.gui.c @@ -461,7 +461,7 @@ int dialog_inputbox(WINDOW *main_window, result[cursor_position] = res; cursor_position++; } else { - mvprintw(0, 0, "unknow key: %d\n", res); + mvprintw(0, 0, "unknown key: %d\n", res); } break; } From cd58a90fa6ff2ec86bcc9e399acfd6dcc97268b3 Mon Sep 17 00:00:00 2001 From: Cheng Renquan Date: Thu, 1 Sep 2011 10:52:19 -0700 Subject: [PATCH 0111/1531] scripts/kconfig/nconf: fix memmove's length arg In case KEY_BACKSPACE / KEY_DC to delete a char, it memmove only (len-cursor_position+1) bytes; the default case is to insert a char, it should also memmove exactly (len-cursor_position+1) bytes; the original use of (len+1) is wrong and may access following memory that doesn't belong to result, may cause SegFault in theory; case KEY_BACKSPACE: if (cursor_position > 0) { memmove(&result[cursor_position-1], &result[cursor_position], len-cursor_position+1); cursor_position--; } break; case KEY_DC: if (cursor_position >= 0 && cursor_position < len) { memmove(&result[cursor_position], &result[cursor_position+1], len-cursor_position+1); } break; default: if ((isgraph(res) || isspace(res)) && len-2 < result_len) { /* insert the char at the proper position */ memmove(&result[cursor_position+1], &result[cursor_position], len-cursor_position+1); result[cursor_position] = res; cursor_position++; } Signed-off-by: Cheng Renquan Acked-by: Nir Tzachar --- scripts/kconfig/nconf.gui.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/kconfig/nconf.gui.c b/scripts/kconfig/nconf.gui.c index d3af04e95288..3ce2a7c0bda6 100644 --- a/scripts/kconfig/nconf.gui.c +++ b/scripts/kconfig/nconf.gui.c @@ -457,7 +457,7 @@ int dialog_inputbox(WINDOW *main_window, /* insert the char at the proper position */ memmove(&result[cursor_position+1], &result[cursor_position], - len+1); + len-cursor_position+1); result[cursor_position] = res; cursor_position++; } else { From 5ea9f64ffc073bf2882f6aa83b0dad3609b1e67a Mon Sep 17 00:00:00 2001 From: Cheng Renquan Date: Thu, 1 Sep 2011 10:52:20 -0700 Subject: [PATCH 0112/1531] scripts/kconfig/nconf: dynamically alloc dialog_input_result To support unlimited length string config items; No check for realloc return value keeps code simple, and to be consistent with other existing unchecked malloc in kconfig. Signed-off-by: Cheng Renquan Signed-off-by: Arnaud Lacombe --- scripts/kconfig/nconf.c | 21 ++++++++++----------- scripts/kconfig/nconf.gui.c | 20 +++++++++++++++----- scripts/kconfig/nconf.h | 2 +- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c index b113c50209e3..73070cb0b6de 100644 --- a/scripts/kconfig/nconf.c +++ b/scripts/kconfig/nconf.c @@ -278,6 +278,9 @@ static int global_exit; /* the currently selected button */ const char *current_instructions = menu_instructions; +static char *dialog_input_result; +static int dialog_input_result_len; + static void conf(struct menu *menu); static void conf_choice(struct menu *menu); static void conf_string(struct menu *menu); @@ -693,7 +696,6 @@ static void search_conf(void) { struct symbol **sym_arr; struct gstr res; - char dialog_input_result[100]; char *dialog_input; int dres; again: @@ -701,7 +703,7 @@ static void search_conf(void) _("Search Configuration Parameter"), _("Enter " CONFIG_ " (sub)string to search for " "(with or without \"" CONFIG_ "\")"), - "", dialog_input_result, 99); + "", &dialog_input_result, &dialog_input_result_len); switch (dres) { case 0: break; @@ -1346,7 +1348,6 @@ static void conf_choice(struct menu *menu) static void conf_string(struct menu *menu) { const char *prompt = menu_get_prompt(menu); - char dialog_input_result[256]; while (1) { int res; @@ -1369,8 +1370,8 @@ static void conf_string(struct menu *menu) prompt ? _(prompt) : _("Main Menu"), heading, sym_get_string_value(menu->sym), - dialog_input_result, - sizeof(dialog_input_result)); + &dialog_input_result, + &dialog_input_result_len); switch (res) { case 0: if (sym_set_string_value(menu->sym, @@ -1390,14 +1391,13 @@ static void conf_string(struct menu *menu) static void conf_load(void) { - char dialog_input_result[256]; while (1) { int res; res = dialog_inputbox(main_window, NULL, load_config_text, filename, - dialog_input_result, - sizeof(dialog_input_result)); + &dialog_input_result, + &dialog_input_result_len); switch (res) { case 0: if (!dialog_input_result[0]) @@ -1422,14 +1422,13 @@ static void conf_load(void) static void conf_save(void) { - char dialog_input_result[256]; while (1) { int res; res = dialog_inputbox(main_window, NULL, save_config_text, filename, - dialog_input_result, - sizeof(dialog_input_result)); + &dialog_input_result, + &dialog_input_result_len); switch (res) { case 0: if (!dialog_input_result[0]) diff --git a/scripts/kconfig/nconf.gui.c b/scripts/kconfig/nconf.gui.c index 3ce2a7c0bda6..d64bc1c24c20 100644 --- a/scripts/kconfig/nconf.gui.c +++ b/scripts/kconfig/nconf.gui.c @@ -356,7 +356,7 @@ int btn_dialog(WINDOW *main_window, const char *msg, int btn_num, ...) int dialog_inputbox(WINDOW *main_window, const char *title, const char *prompt, - const char *init, char *result, int result_len) + const char *init, char **resultp, int *result_len) { int prompt_lines = 0; int prompt_width = 0; @@ -367,7 +367,12 @@ int dialog_inputbox(WINDOW *main_window, int i, x, y; int res = -1; int cursor_position = strlen(init); + char *result = *resultp; + if (strlen(init)+1 > *result_len) { + *result_len = strlen(init)+1; + *resultp = result = realloc(result, *result_len); + } /* find the widest line of msg: */ prompt_lines = get_line_no(prompt); @@ -384,7 +389,7 @@ int dialog_inputbox(WINDOW *main_window, y = (LINES-(prompt_lines+4))/2; x = (COLS-(prompt_width+4))/2; - strncpy(result, init, result_len); + strncpy(result, init, *result_len); /* create the windows */ win = newwin(prompt_lines+6, prompt_width+7, y, x); @@ -443,7 +448,7 @@ int dialog_inputbox(WINDOW *main_window, case KEY_UP: case KEY_RIGHT: if (cursor_position < len && - cursor_position < min(result_len, prompt_width)) + cursor_position < min(*result_len, prompt_width)) cursor_position++; break; case KEY_DOWN: @@ -452,8 +457,13 @@ int dialog_inputbox(WINDOW *main_window, cursor_position--; break; default: - if ((isgraph(res) || isspace(res)) && - len-2 < result_len) { + if ((isgraph(res) || isspace(res))) { + /* one for new char, one for '\0' */ + if (len+2 > *result_len) { + *result_len = len+2; + *resultp = result = realloc(result, + *result_len); + } /* insert the char at the proper position */ memmove(&result[cursor_position+1], &result[cursor_position], diff --git a/scripts/kconfig/nconf.h b/scripts/kconfig/nconf.h index 58fbda8fc0dc..0d5261705ef5 100644 --- a/scripts/kconfig/nconf.h +++ b/scripts/kconfig/nconf.h @@ -89,7 +89,7 @@ void fill_window(WINDOW *win, const char *text); int btn_dialog(WINDOW *main_window, const char *msg, int btn_num, ...); int dialog_inputbox(WINDOW *main_window, const char *title, const char *prompt, - const char *init, char *result, int result_len); + const char *init, char **resultp, int *result_len); void refresh_all_windows(WINDOW *main_window); void show_scroll_win(WINDOW *main_window, const char *title, From e631a57a19e103c3bb59495b236634ec62e2a850 Mon Sep 17 00:00:00 2001 From: Cheng Renquan Date: Thu, 1 Sep 2011 10:52:21 -0700 Subject: [PATCH 0113/1531] scripts/kconfig/nconf: fix editing long strings The original dialog_inputbox doesn't work with longer than prompt_width strings, here fixed it in this way: 1) add variable cursor_form_win to record cursor of form_win, keep its value always between [0, prompt_width-1]; reuse the original cursor_position as cursor of the string result, use (cursor_position-cursor_form_win) as begin offset to show part of the string in form_win; Signed-off-by: Cheng Renquan Cc: Arnaud Lacombe Cc: Nir Tzachar --- scripts/kconfig/nconf.gui.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/scripts/kconfig/nconf.gui.c b/scripts/kconfig/nconf.gui.c index d64bc1c24c20..4b9d8b66087d 100644 --- a/scripts/kconfig/nconf.gui.c +++ b/scripts/kconfig/nconf.gui.c @@ -367,6 +367,7 @@ int dialog_inputbox(WINDOW *main_window, int i, x, y; int res = -1; int cursor_position = strlen(init); + int cursor_form_win; char *result = *resultp; if (strlen(init)+1 > *result_len) { @@ -410,7 +411,9 @@ int dialog_inputbox(WINDOW *main_window, fill_window(prompt_win, prompt); mvwprintw(form_win, 0, 0, "%*s", prompt_width, " "); - mvwprintw(form_win, 0, 0, "%s", result); + cursor_form_win = min(cursor_position, prompt_width-1); + mvwprintw(form_win, 0, 0, "%s", + result + cursor_position-cursor_form_win); /* create panels */ panel = new_panel(win); @@ -436,6 +439,8 @@ int dialog_inputbox(WINDOW *main_window, &result[cursor_position], len-cursor_position+1); cursor_position--; + cursor_form_win--; + len--; } break; case KEY_DC: @@ -443,18 +448,22 @@ int dialog_inputbox(WINDOW *main_window, memmove(&result[cursor_position], &result[cursor_position+1], len-cursor_position+1); + len--; } break; case KEY_UP: case KEY_RIGHT: - if (cursor_position < len && - cursor_position < min(*result_len, prompt_width)) + if (cursor_position < len) { cursor_position++; + cursor_form_win++; + } break; case KEY_DOWN: case KEY_LEFT: - if (cursor_position > 0) + if (cursor_position > 0) { cursor_position--; + cursor_form_win--; + } break; default: if ((isgraph(res) || isspace(res))) { @@ -470,16 +479,24 @@ int dialog_inputbox(WINDOW *main_window, len-cursor_position+1); result[cursor_position] = res; cursor_position++; + cursor_form_win++; + len++; } else { mvprintw(0, 0, "unknown key: %d\n", res); } break; } + if (cursor_form_win < 0) + cursor_form_win = 0; + else if (cursor_form_win > prompt_width-1) + cursor_form_win = prompt_width-1; + wmove(form_win, 0, 0); wclrtoeol(form_win); mvwprintw(form_win, 0, 0, "%*s", prompt_width, " "); - mvwprintw(form_win, 0, 0, "%s", result); - wmove(form_win, 0, cursor_position); + mvwprintw(form_win, 0, 0, "%s", + result + cursor_position-cursor_form_win); + wmove(form_win, 0, cursor_form_win); touchwin(win); refresh_all_windows(main_window); From 93072c3ecafcf188390750cc755185f3150736b9 Mon Sep 17 00:00:00 2001 From: Cheng Renquan Date: Thu, 1 Sep 2011 10:52:22 -0700 Subject: [PATCH 0114/1531] scripts/kconfig/nconf: add KEY_HOME / KEY_END for dialog_inputbox to make it easier to locate begin/end when editing long strings; Signed-off-by: Cheng Renquan Acked By: Nir Tzachar --- scripts/kconfig/nconf.gui.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/kconfig/nconf.gui.c b/scripts/kconfig/nconf.gui.c index 4b9d8b66087d..3b18dd839668 100644 --- a/scripts/kconfig/nconf.gui.c +++ b/scripts/kconfig/nconf.gui.c @@ -465,6 +465,14 @@ int dialog_inputbox(WINDOW *main_window, cursor_form_win--; } break; + case KEY_HOME: + cursor_position = 0; + cursor_form_win = 0; + break; + case KEY_END: + cursor_position = len; + cursor_form_win = min(cursor_position, prompt_width-1); + break; default: if ((isgraph(res) || isspace(res))) { /* one for new char, one for '\0' */ From 7c2e70879fc0949b4220ee61b7c4553f6976a94d Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 9 Sep 2011 18:28:51 -0400 Subject: [PATCH 0115/1531] ext4: add ext4-specific kludge to avoid an oops after the disk disappears The del_gendisk() function uninitializes the disk-specific data structures, including the bdi structure, without telling anyone else. Once this happens, any attempt to call mark_buffer_dirty() (for example, by ext4_commit_super), will cause a kernel OOPS. Fix this for now until we can fix things in an architecturally correct way. Signed-off-by: "Theodore Ts'o" --- fs/ext4/super.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index ee2f74a7084d..5dcd0dacc591 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -414,6 +414,22 @@ static void save_error_info(struct super_block *sb, const char *func, ext4_commit_super(sb, 1); } +/* + * The del_gendisk() function uninitializes the disk-specific data + * structures, including the bdi structure, without telling anyone + * else. Once this happens, any attempt to call mark_buffer_dirty() + * (for example, by ext4_commit_super), will cause a kernel OOPS. + * This is a kludge to prevent these oops until we can put in a proper + * hook in del_gendisk() to inform the VFS and file system layers. + */ +static int block_device_ejected(struct super_block *sb) +{ + struct inode *bd_inode = sb->s_bdev->bd_inode; + struct backing_dev_info *bdi = bd_inode->i_mapping->backing_dev_info; + + return bdi->dev == NULL; +} + /* Deal with the reporting of failure conditions on a filesystem such as * inconsistencies detected or read IO failures. @@ -4072,7 +4088,7 @@ static int ext4_commit_super(struct super_block *sb, int sync) struct buffer_head *sbh = EXT4_SB(sb)->s_sbh; int error = 0; - if (!sbh) + if (!sbh || block_device_ejected(sb)) return error; if (buffer_write_io_error(sbh)) { /* From 281b59959707dfae03ce038cdf231bf4904e170c Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 9 Sep 2011 18:34:51 -0400 Subject: [PATCH 0116/1531] ext4: read-only support for bigalloc file systems This adds supports for bigalloc file systems. It teaches the mount code just enough about bigalloc superblock fields that it will mount the file system without freaking out that the number of blocks per group is too big. Signed-off-by: "Theodore Ts'o" --- fs/ext4/ext4.h | 17 ++++++++++++--- fs/ext4/super.c | 58 ++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 64 insertions(+), 11 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 48ae98819d35..f7257aa6bf81 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -239,8 +239,11 @@ struct ext4_io_submit { # define EXT4_BLOCK_SIZE(s) (EXT4_MIN_BLOCK_SIZE << (s)->s_log_block_size) #endif #define EXT4_ADDR_PER_BLOCK(s) (EXT4_BLOCK_SIZE(s) / sizeof(__u32)) +#define EXT4_CLUSTER_SIZE(s) (EXT4_BLOCK_SIZE(s) << \ + EXT4_SB(s)->s_cluster_bits) #ifdef __KERNEL__ # define EXT4_BLOCK_SIZE_BITS(s) ((s)->s_blocksize_bits) +# define EXT4_CLUSTER_BITS(s) (EXT4_SB(s)->s_cluster_bits) #else # define EXT4_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10) #endif @@ -306,6 +309,7 @@ struct flex_groups { #define EXT4_DESC_SIZE(s) (EXT4_SB(s)->s_desc_size) #ifdef __KERNEL__ # define EXT4_BLOCKS_PER_GROUP(s) (EXT4_SB(s)->s_blocks_per_group) +# define EXT4_CLUSTERS_PER_GROUP(s) (EXT4_SB(s)->s_clusters_per_group) # define EXT4_DESC_PER_BLOCK(s) (EXT4_SB(s)->s_desc_per_block) # define EXT4_INODES_PER_GROUP(s) (EXT4_SB(s)->s_inodes_per_group) # define EXT4_DESC_PER_BLOCK_BITS(s) (EXT4_SB(s)->s_desc_per_block_bits) @@ -975,9 +979,9 @@ struct ext4_super_block { /*10*/ __le32 s_free_inodes_count; /* Free inodes count */ __le32 s_first_data_block; /* First Data Block */ __le32 s_log_block_size; /* Block size */ - __le32 s_obso_log_frag_size; /* Obsoleted fragment size */ + __le32 s_log_cluster_size; /* Allocation cluster size */ /*20*/ __le32 s_blocks_per_group; /* # Blocks per group */ - __le32 s_obso_frags_per_group; /* Obsoleted fragments per group */ + __le32 s_clusters_per_group; /* # Clusters per group */ __le32 s_inodes_per_group; /* # Inodes per group */ __le32 s_mtime; /* Mount time */ /*30*/ __le32 s_wtime; /* Write time */ @@ -1073,7 +1077,10 @@ struct ext4_super_block { __u8 s_last_error_func[32]; /* function where the error happened */ #define EXT4_S_ERR_END offsetof(struct ext4_super_block, s_mount_opts) __u8 s_mount_opts[64]; - __le32 s_reserved[112]; /* Padding to the end of the block */ + __le32 s_usr_quota_inum; /* inode for tracking user quota */ + __le32 s_grp_quota_inum; /* inode for tracking group quota */ + __le32 s_overhead_clusters; /* overhead blocks/clusters in fs */ + __le32 s_reserved[109]; /* Padding to the end of the block */ }; #define EXT4_S_ERR_LEN (EXT4_S_ERR_END - EXT4_S_ERR_START) @@ -1093,6 +1100,7 @@ struct ext4_sb_info { unsigned long s_desc_size; /* Size of a group descriptor in bytes */ unsigned long s_inodes_per_block;/* Number of inodes per block */ unsigned long s_blocks_per_group;/* Number of blocks in a group */ + unsigned long s_clusters_per_group; /* Number of clusters in a group */ unsigned long s_inodes_per_group;/* Number of inodes in a group */ unsigned long s_itb_per_group; /* Number of inode table blocks per group */ unsigned long s_gdb_count; /* Number of group descriptor blocks */ @@ -1101,6 +1109,8 @@ struct ext4_sb_info { ext4_group_t s_blockfile_groups;/* Groups acceptable for non-extent files */ unsigned long s_overhead_last; /* Last calculated overhead */ unsigned long s_blocks_last; /* Last seen block count */ + unsigned int s_cluster_ratio; /* Number of blocks per cluster */ + unsigned int s_cluster_bits; /* log2 of s_cluster_ratio */ loff_t s_bitmap_maxbytes; /* max bytes for bitmap files */ struct buffer_head * s_sbh; /* Buffer containing the super block */ struct ext4_super_block *s_es; /* Pointer to the super block in the buffer */ @@ -1367,6 +1377,7 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei) #define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020 #define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040 #define EXT4_FEATURE_RO_COMPAT_QUOTA 0x0100 +#define EXT4_FEATURE_RO_COMPAT_BIGALLOC 0x0200 #define EXT4_FEATURE_INCOMPAT_COMPRESSION 0x0001 #define EXT4_FEATURE_INCOMPAT_FILETYPE 0x0002 diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 5dcd0dacc591..823e7d9deee2 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1953,7 +1953,7 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es, res = MS_RDONLY; } if (read_only) - return res; + goto done; if (!(sbi->s_mount_state & EXT4_VALID_FS)) ext4_msg(sb, KERN_WARNING, "warning: mounting unchecked fs, " "running e2fsck is recommended"); @@ -1984,6 +1984,7 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es, EXT4_SET_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER); ext4_commit_super(sb, 1); +done: if (test_opt(sb, DEBUG)) printk(KERN_INFO "[EXT4 FS bs=%lu, gc=%u, " "bpg=%lu, ipg=%lu, mo=%04x, mo2=%04x]\n", @@ -3105,10 +3106,10 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) char *cp; const char *descr; int ret = -ENOMEM; - int blocksize; + int blocksize, clustersize; unsigned int db_count; unsigned int i; - int needs_recovery, has_huge_files; + int needs_recovery, has_huge_files, has_bigalloc; __u64 blocks_count; int err; unsigned int journal_ioprio = DEFAULT_JOURNAL_IOPRIO; @@ -3412,12 +3413,53 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) sb->s_dirt = 1; } - if (sbi->s_blocks_per_group > blocksize * 8) { - ext4_msg(sb, KERN_ERR, - "#blocks per group too big: %lu", - sbi->s_blocks_per_group); - goto failed_mount; + /* Handle clustersize */ + clustersize = BLOCK_SIZE << le32_to_cpu(es->s_log_cluster_size); + has_bigalloc = EXT4_HAS_RO_COMPAT_FEATURE(sb, + EXT4_FEATURE_RO_COMPAT_BIGALLOC); + if (has_bigalloc) { + if (clustersize < blocksize) { + ext4_msg(sb, KERN_ERR, + "cluster size (%d) smaller than " + "block size (%d)", clustersize, blocksize); + goto failed_mount; + } + sbi->s_cluster_bits = le32_to_cpu(es->s_log_cluster_size) - + le32_to_cpu(es->s_log_block_size); + sbi->s_clusters_per_group = + le32_to_cpu(es->s_clusters_per_group); + if (sbi->s_clusters_per_group > blocksize * 8) { + ext4_msg(sb, KERN_ERR, + "#clusters per group too big: %lu", + sbi->s_clusters_per_group); + goto failed_mount; + } + if (sbi->s_blocks_per_group != + (sbi->s_clusters_per_group * (clustersize / blocksize))) { + ext4_msg(sb, KERN_ERR, "blocks per group (%lu) and " + "clusters per group (%lu) inconsistent", + sbi->s_blocks_per_group, + sbi->s_clusters_per_group); + goto failed_mount; + } + } else { + if (clustersize != blocksize) { + ext4_warning(sb, "fragment/cluster size (%d) != " + "block size (%d)", clustersize, + blocksize); + clustersize = blocksize; + } + if (sbi->s_blocks_per_group > blocksize * 8) { + ext4_msg(sb, KERN_ERR, + "#blocks per group too big: %lu", + sbi->s_blocks_per_group); + goto failed_mount; + } + sbi->s_clusters_per_group = sbi->s_blocks_per_group; + sbi->s_cluster_bits = 0; } + sbi->s_cluster_ratio = clustersize / blocksize; + if (sbi->s_inodes_per_group > blocksize * 8) { ext4_msg(sb, KERN_ERR, "#inodes per group too big: %lu", From bab08ab9646288f1b0b72a7aaeecdff94bd62c18 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 9 Sep 2011 18:36:51 -0400 Subject: [PATCH 0117/1531] ext4: enforce bigalloc restrictions (e.g., no online resizing, etc.) At least initially if the bigalloc feature is enabled, we will not support non-extent mapped inodes, online resizing, online defrag, or the FITRIM ioctl. This simplifies the initial implementation. Signed-off-by: "Theodore Ts'o" --- fs/ext4/indirect.c | 7 +++++++ fs/ext4/inode.c | 5 +++++ fs/ext4/ioctl.c | 33 +++++++++++++++++++++++++++++---- fs/ext4/super.c | 7 +++++++ 4 files changed, 48 insertions(+), 4 deletions(-) diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c index ea704dff8669..3cfc73fbca8e 100644 --- a/fs/ext4/indirect.c +++ b/fs/ext4/indirect.c @@ -699,6 +699,13 @@ int ext4_ind_map_blocks(handle_t *handle, struct inode *inode, /* * Okay, we need to do block allocation. */ + if (EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb, + EXT4_FEATURE_RO_COMPAT_BIGALLOC)) { + EXT4_ERROR_INODE(inode, "Can't allocate blocks for " + "non-extent mapped inodes with bigalloc"); + return -ENOSPC; + } + goal = ext4_find_goal(inode, map->m_lblk, partial); /* the number of blocks need to allocate for [d,t]indirect blocks */ diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 6ecc93979e48..904a9a623dab 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -3358,6 +3358,11 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length) return -ENOTSUPP; } + if (EXT4_SB(inode->i_sb)->s_cluster_ratio > 1) { + /* TODO: Add support for bigalloc file systems */ + return -ENOTSUPP; + } + return ext4_ext_punch_hole(file, offset, length); } diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index f18bfe37aff8..2046d699b4a7 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -21,6 +21,7 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = filp->f_dentry->d_inode; + struct super_block *sb = inode->i_sb; struct ext4_inode_info *ei = EXT4_I(inode); unsigned int flags; @@ -183,7 +184,6 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) * Returns 1 if it slept, else zero. */ { - struct super_block *sb = inode->i_sb; DECLARE_WAITQUEUE(wait, current); int ret = 0; @@ -199,7 +199,6 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) #endif case EXT4_IOC_GROUP_EXTEND: { ext4_fsblk_t n_blocks_count; - struct super_block *sb = inode->i_sb; int err, err2=0; err = ext4_resize_begin(sb); @@ -209,6 +208,13 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (get_user(n_blocks_count, (__u32 __user *)arg)) return -EFAULT; + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, + EXT4_FEATURE_RO_COMPAT_BIGALLOC)) { + ext4_msg(sb, KERN_ERR, + "Online resizing not supported with bigalloc"); + return -EOPNOTSUPP; + } + err = mnt_want_write(filp->f_path.mnt); if (err) return err; @@ -250,6 +256,13 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) goto mext_out; } + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, + EXT4_FEATURE_RO_COMPAT_BIGALLOC)) { + ext4_msg(sb, KERN_ERR, + "Online defrag not supported with bigalloc"); + return -EOPNOTSUPP; + } + err = mnt_want_write(filp->f_path.mnt); if (err) goto mext_out; @@ -270,7 +283,6 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) case EXT4_IOC_GROUP_ADD: { struct ext4_new_group_data input; - struct super_block *sb = inode->i_sb; int err, err2=0; err = ext4_resize_begin(sb); @@ -281,6 +293,13 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) sizeof(input))) return -EFAULT; + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, + EXT4_FEATURE_RO_COMPAT_BIGALLOC)) { + ext4_msg(sb, KERN_ERR, + "Online resizing not supported with bigalloc"); + return -EOPNOTSUPP; + } + err = mnt_want_write(filp->f_path.mnt); if (err) return err; @@ -337,7 +356,6 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) case FITRIM: { - struct super_block *sb = inode->i_sb; struct request_queue *q = bdev_get_queue(sb->s_bdev); struct fstrim_range range; int ret = 0; @@ -348,6 +366,13 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (!blk_queue_discard(q)) return -EOPNOTSUPP; + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, + EXT4_FEATURE_RO_COMPAT_BIGALLOC)) { + ext4_msg(sb, KERN_ERR, + "FITRIM not supported with bigalloc"); + return -EOPNOTSUPP; + } + if (copy_from_user(&range, (struct fstrim_range *)arg, sizeof(range))) return -EFAULT; diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 823e7d9deee2..25a4bfe3f39f 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -2701,6 +2701,13 @@ static int ext4_feature_set_ok(struct super_block *sb, int readonly) return 0; } } + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_BIGALLOC) && + !EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_EXTENTS)) { + ext4_msg(sb, KERN_ERR, + "Can't support bigalloc feature without " + "extents feature\n"); + return 0; + } return 1; } From 7137d7a48e2213eb1f6d6529da14c2ed3706b795 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 9 Sep 2011 18:38:51 -0400 Subject: [PATCH 0118/1531] ext4: convert instances of EXT4_BLOCKS_PER_GROUP to EXT4_CLUSTERS_PER_GROUP Change the places in fs/ext4/mballoc.c where EXT4_BLOCKS_PER_GROUP are used to indicate the number of bits in a block bitmap (which is really a cluster allocation bitmap in bigalloc file systems). There are still some places in the ext4 codebase where usage of EXT4_BLOCKS_PER_GROUP needs to be audited/fixed, in code paths that aren't used given the initial restricted assumptions for bigalloc. These will need to be fixed before we can relax those restrictions. Signed-off-by: "Theodore Ts'o" --- fs/ext4/mballoc.c | 48 +++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 17a5a57c415a..81e28657a3c2 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -653,7 +653,7 @@ static void ext4_mb_mark_free_simple(struct super_block *sb, ext4_grpblk_t chunk; unsigned short border; - BUG_ON(len > EXT4_BLOCKS_PER_GROUP(sb)); + BUG_ON(len > EXT4_CLUSTERS_PER_GROUP(sb)); border = 2 << sb->s_blocksize_bits; @@ -705,7 +705,7 @@ void ext4_mb_generate_buddy(struct super_block *sb, void *buddy, void *bitmap, ext4_group_t group) { struct ext4_group_info *grp = ext4_get_group_info(sb, group); - ext4_grpblk_t max = EXT4_BLOCKS_PER_GROUP(sb); + ext4_grpblk_t max = EXT4_CLUSTERS_PER_GROUP(sb); ext4_grpblk_t i = 0; ext4_grpblk_t first; ext4_grpblk_t len; @@ -1624,8 +1624,8 @@ static void ext4_mb_measure_extent(struct ext4_allocation_context *ac, struct ext4_free_extent *gex = &ac->ac_g_ex; BUG_ON(ex->fe_len <= 0); - BUG_ON(ex->fe_len > EXT4_BLOCKS_PER_GROUP(ac->ac_sb)); - BUG_ON(ex->fe_start >= EXT4_BLOCKS_PER_GROUP(ac->ac_sb)); + BUG_ON(ex->fe_len > EXT4_CLUSTERS_PER_GROUP(ac->ac_sb)); + BUG_ON(ex->fe_start >= EXT4_CLUSTERS_PER_GROUP(ac->ac_sb)); BUG_ON(ac->ac_status != AC_STATUS_CONTINUE); ac->ac_found++; @@ -1823,8 +1823,8 @@ void ext4_mb_complex_scan_group(struct ext4_allocation_context *ac, while (free && ac->ac_status == AC_STATUS_CONTINUE) { i = mb_find_next_zero_bit(bitmap, - EXT4_BLOCKS_PER_GROUP(sb), i); - if (i >= EXT4_BLOCKS_PER_GROUP(sb)) { + EXT4_CLUSTERS_PER_GROUP(sb), i); + if (i >= EXT4_CLUSTERS_PER_GROUP(sb)) { /* * IF we have corrupt bitmap, we won't find any * free blocks even though group info says we @@ -1887,7 +1887,7 @@ void ext4_mb_scan_aligned(struct ext4_allocation_context *ac, do_div(a, sbi->s_stripe); i = (a * sbi->s_stripe) - first_group_block; - while (i < EXT4_BLOCKS_PER_GROUP(sb)) { + while (i < EXT4_CLUSTERS_PER_GROUP(sb)) { if (!mb_test_bit(i, bitmap)) { max = mb_find_extent(e4b, 0, i, sbi->s_stripe, &ex); if (max >= sbi->s_stripe) { @@ -3036,7 +3036,7 @@ ext4_mb_normalize_request(struct ext4_allocation_context *ac, } BUG_ON(start + size <= ac->ac_o_ex.fe_logical && start > ac->ac_o_ex.fe_logical); - BUG_ON(size <= 0 || size > EXT4_BLOCKS_PER_GROUP(ac->ac_sb)); + BUG_ON(size <= 0 || size > EXT4_CLUSTERS_PER_GROUP(ac->ac_sb)); /* now prepare goal request */ @@ -3690,7 +3690,7 @@ ext4_mb_discard_group_preallocations(struct super_block *sb, } if (needed == 0) - needed = EXT4_BLOCKS_PER_GROUP(sb) + 1; + needed = EXT4_CLUSTERS_PER_GROUP(sb) + 1; INIT_LIST_HEAD(&list); repeat: @@ -4007,8 +4007,8 @@ ext4_mb_initialize_context(struct ext4_allocation_context *ac, len = ar->len; /* just a dirty hack to filter too big requests */ - if (len >= EXT4_BLOCKS_PER_GROUP(sb) - 10) - len = EXT4_BLOCKS_PER_GROUP(sb) - 10; + if (len >= EXT4_CLUSTERS_PER_GROUP(sb) - 10) + len = EXT4_CLUSTERS_PER_GROUP(sb) - 10; /* start searching from the goal */ goal = ar->goal; @@ -4552,8 +4552,8 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode, * Check to see if we are freeing blocks across a group * boundary. */ - if (bit + count > EXT4_BLOCKS_PER_GROUP(sb)) { - overflow = bit + count - EXT4_BLOCKS_PER_GROUP(sb); + if (bit + count > EXT4_CLUSTERS_PER_GROUP(sb)) { + overflow = bit + count - EXT4_CLUSTERS_PER_GROUP(sb); count -= overflow; } bitmap_bh = ext4_read_block_bitmap(sb, block_group); @@ -4948,7 +4948,7 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range) struct ext4_group_info *grp; ext4_group_t first_group, last_group; ext4_group_t group, ngroups = ext4_get_groups_count(sb); - ext4_grpblk_t cnt = 0, first_block, last_block; + ext4_grpblk_t cnt = 0, first_cluster, last_cluster; uint64_t start, len, minlen, trimmed = 0; ext4_fsblk_t first_data_blk = le32_to_cpu(EXT4_SB(sb)->s_es->s_first_data_block); @@ -4958,7 +4958,7 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range) len = range->len >> sb->s_blocksize_bits; minlen = range->minlen >> sb->s_blocksize_bits; - if (unlikely(minlen > EXT4_BLOCKS_PER_GROUP(sb))) + if (unlikely(minlen > EXT4_CLUSTERS_PER_GROUP(sb))) return -EINVAL; if (start + len <= first_data_blk) goto out; @@ -4969,11 +4969,11 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range) /* Determine first and last group to examine based on start and len */ ext4_get_group_no_and_offset(sb, (ext4_fsblk_t) start, - &first_group, &first_block); + &first_group, &first_cluster); ext4_get_group_no_and_offset(sb, (ext4_fsblk_t) (start + len), - &last_group, &last_block); + &last_group, &last_cluster); last_group = (last_group > ngroups - 1) ? ngroups - 1 : last_group; - last_block = EXT4_BLOCKS_PER_GROUP(sb); + last_cluster = EXT4_CLUSTERS_PER_GROUP(sb); if (first_group > last_group) return -EINVAL; @@ -4993,20 +4993,20 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range) * change it for the last group in which case start + * len < EXT4_BLOCKS_PER_GROUP(sb). */ - if (first_block + len < EXT4_BLOCKS_PER_GROUP(sb)) - last_block = first_block + len; - len -= last_block - first_block; + if (first_cluster + len < EXT4_CLUSTERS_PER_GROUP(sb)) + last_cluster = first_cluster + len; + len -= last_cluster - first_cluster; if (grp->bb_free >= minlen) { - cnt = ext4_trim_all_free(sb, group, first_block, - last_block, minlen); + cnt = ext4_trim_all_free(sb, group, first_cluster, + last_cluster, minlen); if (cnt < 0) { ret = cnt; break; } } trimmed += cnt; - first_block = 0; + first_cluster = 0; } range->len = trimmed * sb->s_blocksize; From 49f7f9af4bb4d7972f3a35a74877937fec9f622d Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 9 Sep 2011 18:40:51 -0400 Subject: [PATCH 0119/1531] ext4: factor out block group accounting into functions This makes it easier to understand how ext4_init_block_bitmap() works, and it will assist when we split out ext4_free_blocks_after_init() in the next commit. Signed-off-by: "Theodore Ts'o" --- fs/ext4/balloc.c | 80 +++++++++++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index f8224adf496e..8573e2bfb78a 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -23,6 +23,9 @@ #include +static unsigned int num_base_meta_blocks(struct super_block *sb, + ext4_group_t block_group); + /* * balloc.c contains the blocks allocation and deallocation routines */ @@ -83,14 +86,30 @@ static int ext4_group_used_meta_blocks(struct super_block *sb, return used_blocks; } +static unsigned int num_blocks_in_group(struct super_block *sb, + ext4_group_t block_group) +{ + if (block_group == ext4_get_groups_count(sb) - 1) { + /* + * Even though mke2fs always initializes the first and + * last group, just in case some other tool was used, + * we need to make sure we calculate the right free + * blocks. + */ + return ext4_blocks_count(EXT4_SB(sb)->s_es) - + ext4_group_first_block_no(sb, block_group); + } else + return EXT4_BLOCKS_PER_GROUP(sb); +} + /* Initializes an uninitialized block bitmap if given, and returns the * number of blocks free in the group. */ unsigned ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh, ext4_group_t block_group, struct ext4_group_desc *gdp) { - int bit, bit_max; + unsigned int bit, bit_max = num_base_meta_blocks(sb, block_group); ext4_group_t ngroups = ext4_get_groups_count(sb); - unsigned free_blocks, group_blocks; + unsigned group_blocks = num_blocks_in_group(sb, block_group); struct ext4_sb_info *sbi = EXT4_SB(sb); if (bh) { @@ -110,35 +129,6 @@ unsigned ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh, memset(bh->b_data, 0, sb->s_blocksize); } - /* Check for superblock and gdt backups in this group */ - bit_max = ext4_bg_has_super(sb, block_group); - - if (!EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_META_BG) || - block_group < le32_to_cpu(sbi->s_es->s_first_meta_bg) * - sbi->s_desc_per_block) { - if (bit_max) { - bit_max += ext4_bg_num_gdb(sb, block_group); - bit_max += - le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks); - } - } else { /* For META_BG_BLOCK_GROUPS */ - bit_max += ext4_bg_num_gdb(sb, block_group); - } - - if (block_group == ngroups - 1) { - /* - * Even though mke2fs always initialize first and last group - * if some other tool enabled the EXT4_BG_BLOCK_UNINIT we need - * to make sure we calculate the right free blocks - */ - group_blocks = ext4_blocks_count(sbi->s_es) - - ext4_group_first_block_no(sb, ngroups - 1); - } else { - group_blocks = EXT4_BLOCKS_PER_GROUP(sb); - } - - free_blocks = group_blocks - bit_max; - if (bh) { ext4_fsblk_t start, tmp; int flex_bg = 0; @@ -176,7 +166,8 @@ unsigned ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh, ext4_mark_bitmap_end(group_blocks, sb->s_blocksize * 8, bh->b_data); } - return free_blocks - ext4_group_used_meta_blocks(sb, block_group, gdp); + return group_blocks - bit_max - + ext4_group_used_meta_blocks(sb, block_group, gdp); } @@ -620,6 +611,31 @@ unsigned long ext4_bg_num_gdb(struct super_block *sb, ext4_group_t group) } +/* + * This function returns the number of file system metadata blocks at + * the beginning of a block group, including the reserved gdt blocks. + */ +static unsigned int num_base_meta_blocks(struct super_block *sb, + ext4_group_t block_group) +{ + struct ext4_sb_info *sbi = EXT4_SB(sb); + int num; + + /* Check for superblock and gdt backups in this group */ + num = ext4_bg_has_super(sb, block_group); + + if (!EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_META_BG) || + block_group < le32_to_cpu(sbi->s_es->s_first_meta_bg) * + sbi->s_desc_per_block) { + if (num) { + num += ext4_bg_num_gdb(sb, block_group); + num += le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks); + } + } else { /* For META_BG_BLOCK_GROUPS */ + num += ext4_bg_num_gdb(sb, block_group); + } + return num; +} /** * ext4_inode_to_goal_block - return a hint for block allocation * @inode: inode for block allocation From fd034a84e1ea5c8c8d159cd2089c32e792c269b0 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 9 Sep 2011 18:42:51 -0400 Subject: [PATCH 0120/1531] ext4: split out ext4_free_blocks_after_init() The function ext4_free_blocks_after_init() used to be a #define of ext4_init_block_bitmap(). This actually made it difficult to understand how the function worked, and made it hard make changes to support clusters. So as an initial cleanup, I've separated out the functionality of initializing block bitmap from calculating the number of free blocks in the new block group. Signed-off-by: "Theodore Ts'o" --- fs/ext4/balloc.c | 105 +++++++++++++++++++++++------------------------ fs/ext4/ext4.h | 13 +++--- fs/ext4/ialloc.c | 20 ++++----- 3 files changed, 66 insertions(+), 72 deletions(-) diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index 8573e2bfb78a..735d9fcc72e6 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -102,74 +102,73 @@ static unsigned int num_blocks_in_group(struct super_block *sb, return EXT4_BLOCKS_PER_GROUP(sb); } -/* Initializes an uninitialized block bitmap if given, and returns the - * number of blocks free in the group. */ -unsigned ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh, - ext4_group_t block_group, struct ext4_group_desc *gdp) +/* Initializes an uninitialized block bitmap */ +void ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh, + ext4_group_t block_group, + struct ext4_group_desc *gdp) { unsigned int bit, bit_max = num_base_meta_blocks(sb, block_group); - ext4_group_t ngroups = ext4_get_groups_count(sb); - unsigned group_blocks = num_blocks_in_group(sb, block_group); struct ext4_sb_info *sbi = EXT4_SB(sb); - - if (bh) { - J_ASSERT_BH(bh, buffer_locked(bh)); - - /* If checksum is bad mark all blocks used to prevent allocation - * essentially implementing a per-group read-only flag. */ - if (!ext4_group_desc_csum_verify(sbi, block_group, gdp)) { - ext4_error(sb, "Checksum bad for group %u", - block_group); - ext4_free_blks_set(sb, gdp, 0); - ext4_free_inodes_set(sb, gdp, 0); - ext4_itable_unused_set(sb, gdp, 0); - memset(bh->b_data, 0xff, sb->s_blocksize); - return 0; - } - memset(bh->b_data, 0, sb->s_blocksize); + ext4_fsblk_t start, tmp; + int flex_bg = 0; + + J_ASSERT_BH(bh, buffer_locked(bh)); + + /* If checksum is bad mark all blocks used to prevent allocation + * essentially implementing a per-group read-only flag. */ + if (!ext4_group_desc_csum_verify(sbi, block_group, gdp)) { + ext4_error(sb, "Checksum bad for group %u", block_group); + ext4_free_blks_set(sb, gdp, 0); + ext4_free_inodes_set(sb, gdp, 0); + ext4_itable_unused_set(sb, gdp, 0); + memset(bh->b_data, 0xff, sb->s_blocksize); + return; } + memset(bh->b_data, 0, sb->s_blocksize); - if (bh) { - ext4_fsblk_t start, tmp; - int flex_bg = 0; + for (bit = 0; bit < bit_max; bit++) + ext4_set_bit(bit, bh->b_data); - for (bit = 0; bit < bit_max; bit++) - ext4_set_bit(bit, bh->b_data); + start = ext4_group_first_block_no(sb, block_group); - start = ext4_group_first_block_no(sb, block_group); + if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_FLEX_BG)) + flex_bg = 1; - if (EXT4_HAS_INCOMPAT_FEATURE(sb, - EXT4_FEATURE_INCOMPAT_FLEX_BG)) - flex_bg = 1; + /* Set bits for block and inode bitmaps, and inode table */ + tmp = ext4_block_bitmap(sb, gdp); + if (!flex_bg || ext4_block_in_group(sb, tmp, block_group)) + ext4_set_bit(tmp - start, bh->b_data); - /* Set bits for block and inode bitmaps, and inode table */ - tmp = ext4_block_bitmap(sb, gdp); - if (!flex_bg || ext4_block_in_group(sb, tmp, block_group)) - ext4_set_bit(tmp - start, bh->b_data); + tmp = ext4_inode_bitmap(sb, gdp); + if (!flex_bg || ext4_block_in_group(sb, tmp, block_group)) + ext4_set_bit(tmp - start, bh->b_data); - tmp = ext4_inode_bitmap(sb, gdp); + tmp = ext4_inode_table(sb, gdp); + for (; tmp < ext4_inode_table(sb, gdp) + + sbi->s_itb_per_group; tmp++) { if (!flex_bg || ext4_block_in_group(sb, tmp, block_group)) ext4_set_bit(tmp - start, bh->b_data); - - tmp = ext4_inode_table(sb, gdp); - for (; tmp < ext4_inode_table(sb, gdp) + - sbi->s_itb_per_group; tmp++) { - if (!flex_bg || - ext4_block_in_group(sb, tmp, block_group)) - ext4_set_bit(tmp - start, bh->b_data); - } - /* - * Also if the number of blocks within the group is - * less than the blocksize * 8 ( which is the size - * of bitmap ), set rest of the block bitmap to 1 - */ - ext4_mark_bitmap_end(group_blocks, sb->s_blocksize * 8, - bh->b_data); } - return group_blocks - bit_max - - ext4_group_used_meta_blocks(sb, block_group, gdp); + /* + * Also if the number of blocks within the group is less than + * the blocksize * 8 ( which is the size of bitmap ), set rest + * of the block bitmap to 1 + */ + ext4_mark_bitmap_end(num_blocks_in_group(sb, block_group), + sb->s_blocksize * 8, bh->b_data); } +/* Return the number of free blocks in a block group. It is used when + * the block bitmap is uninitialized, so we can't just count the bits + * in the bitmap. */ +unsigned ext4_free_blocks_after_init(struct super_block *sb, + ext4_group_t block_group, + struct ext4_group_desc *gdp) +{ + return num_blocks_in_group(sb, block_group) - + num_base_meta_blocks(sb, block_group) - + ext4_group_used_meta_blocks(sb, block_group, gdp); +} /* * The free blocks are managed by bitmaps. A file system contains several diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index f7257aa6bf81..b0b7b67e439d 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1763,12 +1763,13 @@ extern struct ext4_group_desc * ext4_get_group_desc(struct super_block * sb, extern int ext4_should_retry_alloc(struct super_block *sb, int *retries); struct buffer_head *ext4_read_block_bitmap(struct super_block *sb, ext4_group_t block_group); -extern unsigned ext4_init_block_bitmap(struct super_block *sb, - struct buffer_head *bh, - ext4_group_t group, - struct ext4_group_desc *desc); -#define ext4_free_blocks_after_init(sb, group, desc) \ - ext4_init_block_bitmap(sb, NULL, group, desc) +extern void ext4_init_block_bitmap(struct super_block *sb, + struct buffer_head *bh, + ext4_group_t group, + struct ext4_group_desc *desc); +extern unsigned ext4_free_blocks_after_init(struct super_block *sb, + ext4_group_t block_group, + struct ext4_group_desc *gdp); ext4_fsblk_t ext4_inode_to_goal_block(struct inode *); /* dir.c */ diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index 9c63f273b550..b7a8130d0af4 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -816,7 +816,6 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, int mode, int ret2, err = 0; struct inode *ret; ext4_group_t i; - int free = 0; static int once = 1; ext4_group_t flex_group; @@ -950,26 +949,21 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, int mode, goto fail; } - free = 0; - ext4_lock_group(sb, group); + BUFFER_TRACE(block_bitmap_bh, "dirty block bitmap"); + err = ext4_handle_dirty_metadata(handle, NULL, block_bitmap_bh); + brelse(block_bitmap_bh); + /* recheck and clear flag under lock if we still need to */ + ext4_lock_group(sb, group); if (gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) { - free = ext4_free_blocks_after_init(sb, group, gdp); gdp->bg_flags &= cpu_to_le16(~EXT4_BG_BLOCK_UNINIT); - ext4_free_blks_set(sb, gdp, free); + ext4_free_blks_set(sb, gdp, + ext4_free_blocks_after_init(sb, group, gdp)); gdp->bg_checksum = ext4_group_desc_csum(sbi, group, gdp); } ext4_unlock_group(sb, group); - /* Don't need to dirty bitmap block if we didn't change it */ - if (free) { - BUFFER_TRACE(block_bitmap_bh, "dirty block bitmap"); - err = ext4_handle_dirty_metadata(handle, - NULL, block_bitmap_bh); - } - - brelse(block_bitmap_bh); if (err) goto fail; } From d5b8f31007a93777cfb0603b665858fb7aebebfc Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 9 Sep 2011 18:44:51 -0400 Subject: [PATCH 0121/1531] ext4: bigalloc changes to block bitmap initialization functions Add bigalloc support to ext4_init_block_bitmap() and ext4_free_blocks_after_init(). Signed-off-by: "Theodore Ts'o" --- fs/ext4/balloc.c | 131 ++++++++++++++++++++++++++++++++--------------- fs/ext4/ext4.h | 13 +++++ 2 files changed, 103 insertions(+), 41 deletions(-) diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index 735d9fcc72e6..1c6d777b35a2 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -23,9 +23,6 @@ #include -static unsigned int num_base_meta_blocks(struct super_block *sb, - ext4_group_t block_group); - /* * balloc.c contains the blocks allocation and deallocation routines */ @@ -58,37 +55,87 @@ static int ext4_block_in_group(struct super_block *sb, ext4_fsblk_t block, return 0; } -static int ext4_group_used_meta_blocks(struct super_block *sb, - ext4_group_t block_group, - struct ext4_group_desc *gdp) +/* Return the number of clusters used for file system metadata; this + * represents the overhead needed by the file system. + */ +unsigned ext4_num_overhead_clusters(struct super_block *sb, + ext4_group_t block_group, + struct ext4_group_desc *gdp) { - ext4_fsblk_t tmp; + unsigned num_clusters; + int block_cluster = -1, inode_cluster = -1, itbl_cluster = -1, i, c; + ext4_fsblk_t start = ext4_group_first_block_no(sb, block_group); + ext4_fsblk_t itbl_blk; struct ext4_sb_info *sbi = EXT4_SB(sb); - /* block bitmap, inode bitmap, and inode table blocks */ - int used_blocks = sbi->s_itb_per_group + 2; - if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_FLEX_BG)) { - if (!ext4_block_in_group(sb, ext4_block_bitmap(sb, gdp), - block_group)) - used_blocks--; - - if (!ext4_block_in_group(sb, ext4_inode_bitmap(sb, gdp), - block_group)) - used_blocks--; - - tmp = ext4_inode_table(sb, gdp); - for (; tmp < ext4_inode_table(sb, gdp) + - sbi->s_itb_per_group; tmp++) { - if (!ext4_block_in_group(sb, tmp, block_group)) - used_blocks -= 1; + /* This is the number of clusters used by the superblock, + * block group descriptors, and reserved block group + * descriptor blocks */ + num_clusters = ext4_num_base_meta_clusters(sb, block_group); + + /* + * For the allocation bitmaps and inode table, we first need + * to check to see if the block is in the block group. If it + * is, then check to see if the cluster is already accounted + * for in the clusters used for the base metadata cluster, or + * if we can increment the base metadata cluster to include + * that block. Otherwise, we will have to track the cluster + * used for the allocation bitmap or inode table explicitly. + * Normally all of these blocks are contiguous, so the special + * case handling shouldn't be necessary except for *very* + * unusual file system layouts. + */ + if (ext4_block_in_group(sb, ext4_block_bitmap(sb, gdp), block_group)) { + block_cluster = EXT4_B2C(sbi, (start - + ext4_block_bitmap(sb, gdp))); + if (block_cluster < num_clusters) + block_cluster = -1; + else if (block_cluster == num_clusters) { + num_clusters++; + block_cluster = -1; + } + } + + if (ext4_block_in_group(sb, ext4_inode_bitmap(sb, gdp), block_group)) { + inode_cluster = EXT4_B2C(sbi, + start - ext4_inode_bitmap(sb, gdp)); + if (inode_cluster < num_clusters) + inode_cluster = -1; + else if (inode_cluster == num_clusters) { + num_clusters++; + inode_cluster = -1; + } + } + + itbl_blk = ext4_inode_table(sb, gdp); + for (i = 0; i < sbi->s_itb_per_group; i++) { + if (ext4_block_in_group(sb, itbl_blk + i, block_group)) { + c = EXT4_B2C(sbi, start - itbl_blk + i); + if ((c < num_clusters) || (c == inode_cluster) || + (c == block_cluster) || (c == itbl_cluster)) + continue; + if (c == num_clusters) { + num_clusters++; + continue; + } + num_clusters++; + itbl_cluster = c; } } - return used_blocks; + + if (block_cluster != -1) + num_clusters++; + if (inode_cluster != -1) + num_clusters++; + + return num_clusters; } -static unsigned int num_blocks_in_group(struct super_block *sb, - ext4_group_t block_group) +static unsigned int num_clusters_in_group(struct super_block *sb, + ext4_group_t block_group) { + unsigned int blocks; + if (block_group == ext4_get_groups_count(sb) - 1) { /* * Even though mke2fs always initializes the first and @@ -96,10 +143,11 @@ static unsigned int num_blocks_in_group(struct super_block *sb, * we need to make sure we calculate the right free * blocks. */ - return ext4_blocks_count(EXT4_SB(sb)->s_es) - + blocks = ext4_blocks_count(EXT4_SB(sb)->s_es) - ext4_group_first_block_no(sb, block_group); } else - return EXT4_BLOCKS_PER_GROUP(sb); + blocks = EXT4_BLOCKS_PER_GROUP(sb); + return EXT4_NUM_B2C(EXT4_SB(sb), blocks); } /* Initializes an uninitialized block bitmap */ @@ -107,7 +155,7 @@ void ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh, ext4_group_t block_group, struct ext4_group_desc *gdp) { - unsigned int bit, bit_max = num_base_meta_blocks(sb, block_group); + unsigned int bit, bit_max; struct ext4_sb_info *sbi = EXT4_SB(sb); ext4_fsblk_t start, tmp; int flex_bg = 0; @@ -126,6 +174,7 @@ void ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh, } memset(bh->b_data, 0, sb->s_blocksize); + bit_max = ext4_num_base_meta_clusters(sb, block_group); for (bit = 0; bit < bit_max; bit++) ext4_set_bit(bit, bh->b_data); @@ -137,24 +186,25 @@ void ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh, /* Set bits for block and inode bitmaps, and inode table */ tmp = ext4_block_bitmap(sb, gdp); if (!flex_bg || ext4_block_in_group(sb, tmp, block_group)) - ext4_set_bit(tmp - start, bh->b_data); + ext4_set_bit(EXT4_B2C(sbi, tmp - start), bh->b_data); tmp = ext4_inode_bitmap(sb, gdp); if (!flex_bg || ext4_block_in_group(sb, tmp, block_group)) - ext4_set_bit(tmp - start, bh->b_data); + ext4_set_bit(EXT4_B2C(sbi, tmp - start), bh->b_data); tmp = ext4_inode_table(sb, gdp); for (; tmp < ext4_inode_table(sb, gdp) + sbi->s_itb_per_group; tmp++) { if (!flex_bg || ext4_block_in_group(sb, tmp, block_group)) - ext4_set_bit(tmp - start, bh->b_data); + ext4_set_bit(EXT4_B2C(sbi, tmp - start), bh->b_data); } + /* * Also if the number of blocks within the group is less than * the blocksize * 8 ( which is the size of bitmap ), set rest * of the block bitmap to 1 */ - ext4_mark_bitmap_end(num_blocks_in_group(sb, block_group), + ext4_mark_bitmap_end(num_clusters_in_group(sb, block_group), sb->s_blocksize * 8, bh->b_data); } @@ -165,9 +215,8 @@ unsigned ext4_free_blocks_after_init(struct super_block *sb, ext4_group_t block_group, struct ext4_group_desc *gdp) { - return num_blocks_in_group(sb, block_group) - - num_base_meta_blocks(sb, block_group) - - ext4_group_used_meta_blocks(sb, block_group, gdp); + return num_clusters_in_group(sb, block_group) - + ext4_num_overhead_clusters(sb, block_group, gdp); } /* @@ -611,14 +660,14 @@ unsigned long ext4_bg_num_gdb(struct super_block *sb, ext4_group_t group) } /* - * This function returns the number of file system metadata blocks at + * This function returns the number of file system metadata clusters at * the beginning of a block group, including the reserved gdt blocks. */ -static unsigned int num_base_meta_blocks(struct super_block *sb, - ext4_group_t block_group) +unsigned ext4_num_base_meta_clusters(struct super_block *sb, + ext4_group_t block_group) { struct ext4_sb_info *sbi = EXT4_SB(sb); - int num; + unsigned num; /* Check for superblock and gdt backups in this group */ num = ext4_bg_has_super(sb, block_group); @@ -633,7 +682,7 @@ static unsigned int num_base_meta_blocks(struct super_block *sb, } else { /* For META_BG_BLOCK_GROUPS */ num += ext4_bg_num_gdb(sb, block_group); } - return num; + return EXT4_NUM_B2C(sbi, num); } /** * ext4_inode_to_goal_block - return a hint for block allocation diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index b0b7b67e439d..803cfa42e1e8 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -261,6 +261,14 @@ struct ext4_io_submit { #endif #define EXT4_BLOCK_ALIGN(size, blkbits) ALIGN((size), (1 << (blkbits))) +/* Translate a block number to a cluster number */ +#define EXT4_B2C(sbi, blk) ((blk) >> (sbi)->s_cluster_bits) +/* Translate a cluster number to a block number */ +#define EXT4_C2B(sbi, cluster) ((cluster) << (sbi)->s_cluster_bits) +/* Translate # of blks to # of clusters */ +#define EXT4_NUM_B2C(sbi, blks) (((blks) + (sbi)->s_cluster_ratio - 1) >> \ + (sbi)->s_cluster_bits) + /* * Structure of a blocks group descriptor */ @@ -1770,6 +1778,11 @@ extern void ext4_init_block_bitmap(struct super_block *sb, extern unsigned ext4_free_blocks_after_init(struct super_block *sb, ext4_group_t block_group, struct ext4_group_desc *gdp); +extern unsigned ext4_num_base_meta_clusters(struct super_block *sb, + ext4_group_t block_group); +extern unsigned ext4_num_overhead_clusters(struct super_block *sb, + ext4_group_t block_group, + struct ext4_group_desc *gdp); ext4_fsblk_t ext4_inode_to_goal_block(struct inode *); /* dir.c */ From 3212a80a58062056bb922811071062be58d8fee1 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 9 Sep 2011 18:46:51 -0400 Subject: [PATCH 0122/1531] ext4: convert block group-relative offsets to use clusters Certain parts of the ext4 code base, primarily in mballoc.c, use a block group number and offset from the beginning of the block group. This offset is invariably used to index into the allocation bitmap, so change the offset to be denominated in units of clusters. Signed-off-by: "Theodore Ts'o" --- fs/ext4/balloc.c | 6 ++++-- fs/ext4/mballoc.h | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index 1c6d777b35a2..89abf1f7b253 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -28,7 +28,8 @@ */ /* - * Calculate the block group number and offset, given a block number + * Calculate the block group number and offset into the block/cluster + * allocation bitmap, given a block number */ void ext4_get_group_no_and_offset(struct super_block *sb, ext4_fsblk_t blocknr, ext4_group_t *blockgrpp, ext4_grpblk_t *offsetp) @@ -37,7 +38,8 @@ void ext4_get_group_no_and_offset(struct super_block *sb, ext4_fsblk_t blocknr, ext4_grpblk_t offset; blocknr = blocknr - le32_to_cpu(es->s_first_data_block); - offset = do_div(blocknr, EXT4_BLOCKS_PER_GROUP(sb)); + offset = do_div(blocknr, EXT4_BLOCKS_PER_GROUP(sb)) >> + EXT4_SB(sb)->s_cluster_bits; if (offsetp) *offsetp = offset; if (blockgrpp) diff --git a/fs/ext4/mballoc.h b/fs/ext4/mballoc.h index 9d4a636b546c..3cdb8aa9f6b7 100644 --- a/fs/ext4/mballoc.h +++ b/fs/ext4/mballoc.h @@ -216,6 +216,7 @@ struct ext4_buddy { static inline ext4_fsblk_t ext4_grp_offs_to_block(struct super_block *sb, struct ext4_free_extent *fex) { - return ext4_group_first_block_no(sb, fex->fe_group) + fex->fe_start; + return ext4_group_first_block_no(sb, fex->fe_group) + + (fex->fe_start << EXT4_SB(sb)->s_cluster_bits); } #endif From 53accfa9f819c80056db6f03f9c5cfa4bcba1ed8 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 9 Sep 2011 18:48:51 -0400 Subject: [PATCH 0123/1531] ext4: teach mballoc preallocation code about bigalloc clusters In most of mballoc.c, we do everything in units of clusters, since the block allocation bitmaps and buddy bitmaps are all denominated in clusters. The one place where we do deal with absolute block numbers is in the code that handles the preallocation regions, since in the case of inode-based preallocation regions, the start of the preallocation region can't be relative to the beginning of the group. So this adds a bit of complexity, where pa_pstart and pa_lstart are block numbers, while pa_free, pa_len, and fe_len are denominated in units of clusters. Signed-off-by: "Theodore Ts'o" --- fs/ext4/mballoc.c | 95 +++++++++++++++++++++++++++-------------------- fs/ext4/mballoc.h | 4 +- 2 files changed, 56 insertions(+), 43 deletions(-) diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 81e28657a3c2..8765f2512f13 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -70,8 +70,8 @@ * * pa_lstart -> the logical start block for this prealloc space * pa_pstart -> the physical start block for this prealloc space - * pa_len -> length for this prealloc space - * pa_free -> free space available in this prealloc space + * pa_len -> length for this prealloc space (in clusters) + * pa_free -> free space available in this prealloc space (in clusters) * * The inode preallocation space is used looking at the _logical_ start * block. If only the logical file block falls within the range of prealloc @@ -459,7 +459,7 @@ static void mb_free_blocks_double(struct inode *inode, struct ext4_buddy *e4b, ext4_fsblk_t blocknr; blocknr = ext4_group_first_block_no(sb, e4b->bd_group); - blocknr += first + i; + blocknr += EXT4_C2B(EXT4_SB(sb), first + i); ext4_grp_locked_error(sb, e4b->bd_group, inode ? inode->i_ino : 0, blocknr, @@ -734,7 +734,7 @@ void ext4_mb_generate_buddy(struct super_block *sb, if (free != grp->bb_free) { ext4_grp_locked_error(sb, group, 0, 0, - "%u blocks in bitmap, %u in gd", + "%u clusters in bitmap, %u in gd", free, grp->bb_free); /* * If we intent to continue, we consider group descritor @@ -1339,7 +1339,7 @@ static void mb_free_blocks(struct inode *inode, struct ext4_buddy *e4b, ext4_fsblk_t blocknr; blocknr = ext4_group_first_block_no(sb, e4b->bd_group); - blocknr += block; + blocknr += EXT4_C2B(EXT4_SB(sb), block); ext4_grp_locked_error(sb, e4b->bd_group, inode ? inode->i_ino : 0, blocknr, @@ -1831,7 +1831,7 @@ void ext4_mb_complex_scan_group(struct ext4_allocation_context *ac, * we have free blocks */ ext4_grp_locked_error(sb, e4b->bd_group, 0, 0, - "%d free blocks as per " + "%d free clusters as per " "group info. But bitmap says 0", free); break; @@ -1841,7 +1841,7 @@ void ext4_mb_complex_scan_group(struct ext4_allocation_context *ac, BUG_ON(ex.fe_len <= 0); if (free < ex.fe_len) { ext4_grp_locked_error(sb, e4b->bd_group, 0, 0, - "%d free blocks as per " + "%d free clusters as per " "group info. But got %d blocks", free, ex.fe_len); /* @@ -2752,7 +2752,7 @@ void ext4_exit_mballoc(void) */ static noinline_for_stack int ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac, - handle_t *handle, unsigned int reserv_blks) + handle_t *handle, unsigned int reserv_clstrs) { struct buffer_head *bitmap_bh = NULL; struct ext4_group_desc *gdp; @@ -2791,7 +2791,7 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac, block = ext4_grp_offs_to_block(sb, &ac->ac_b_ex); - len = ac->ac_b_ex.fe_len; + len = EXT4_C2B(sbi, ac->ac_b_ex.fe_len); if (!ext4_data_block_valid(sbi, block, len)) { ext4_error(sb, "Allocating blocks %llu-%llu which overlap " "fs metadata\n", block, block+len); @@ -2838,7 +2838,7 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac, */ if (!(ac->ac_flags & EXT4_MB_DELALLOC_RESERVED)) /* release all the reserved blocks if non delalloc */ - percpu_counter_sub(&sbi->s_dirtyblocks_counter, reserv_blks); + percpu_counter_sub(&sbi->s_dirtyblocks_counter, reserv_clstrs); if (sbi->s_log_groups_per_flex) { ext4_group_t flex_group = ext4_flex_group(sbi, @@ -2886,6 +2886,7 @@ static noinline_for_stack void ext4_mb_normalize_request(struct ext4_allocation_context *ac, struct ext4_allocation_request *ar) { + struct ext4_sb_info *sbi = EXT4_SB(ac->ac_sb); int bsbits, max; ext4_lblk_t end; loff_t size, orig_size, start_off; @@ -2916,7 +2917,7 @@ ext4_mb_normalize_request(struct ext4_allocation_context *ac, /* first, let's learn actual file size * given current request is allocated */ - size = ac->ac_o_ex.fe_logical + ac->ac_o_ex.fe_len; + size = ac->ac_o_ex.fe_logical + EXT4_C2B(sbi, ac->ac_o_ex.fe_len); size = size << bsbits; if (size < i_size_read(ac->ac_inode)) size = i_size_read(ac->ac_inode); @@ -2988,7 +2989,8 @@ ext4_mb_normalize_request(struct ext4_allocation_context *ac, continue; } - pa_end = pa->pa_lstart + pa->pa_len; + pa_end = pa->pa_lstart + EXT4_C2B(EXT4_SB(ac->ac_sb), + pa->pa_len); /* PA must not overlap original request */ BUG_ON(!(ac->ac_o_ex.fe_logical >= pa_end || @@ -3018,9 +3020,11 @@ ext4_mb_normalize_request(struct ext4_allocation_context *ac, rcu_read_lock(); list_for_each_entry_rcu(pa, &ei->i_prealloc_list, pa_inode_list) { ext4_lblk_t pa_end; + spin_lock(&pa->pa_lock); if (pa->pa_deleted == 0) { - pa_end = pa->pa_lstart + pa->pa_len; + pa_end = pa->pa_lstart + EXT4_C2B(EXT4_SB(ac->ac_sb), + pa->pa_len); BUG_ON(!(start >= pa_end || end <= pa->pa_lstart)); } spin_unlock(&pa->pa_lock); @@ -3043,7 +3047,7 @@ ext4_mb_normalize_request(struct ext4_allocation_context *ac, /* XXX: is it better to align blocks WRT to logical * placement or satisfy big request as is */ ac->ac_g_ex.fe_logical = start; - ac->ac_g_ex.fe_len = size; + ac->ac_g_ex.fe_len = EXT4_NUM_B2C(sbi, size); /* define goal start in order to merge */ if (ar->pright && (ar->lright == (start + size))) { @@ -3112,14 +3116,16 @@ static void ext4_discard_allocated_blocks(struct ext4_allocation_context *ac) static void ext4_mb_use_inode_pa(struct ext4_allocation_context *ac, struct ext4_prealloc_space *pa) { + struct ext4_sb_info *sbi = EXT4_SB(ac->ac_sb); ext4_fsblk_t start; ext4_fsblk_t end; int len; /* found preallocated blocks, use them */ start = pa->pa_pstart + (ac->ac_o_ex.fe_logical - pa->pa_lstart); - end = min(pa->pa_pstart + pa->pa_len, start + ac->ac_o_ex.fe_len); - len = end - start; + end = min(pa->pa_pstart + EXT4_C2B(sbi, pa->pa_len), + start + EXT4_C2B(sbi, ac->ac_o_ex.fe_len)); + len = EXT4_NUM_B2C(sbi, end - start); ext4_get_group_no_and_offset(ac->ac_sb, start, &ac->ac_b_ex.fe_group, &ac->ac_b_ex.fe_start); ac->ac_b_ex.fe_len = len; @@ -3127,7 +3133,7 @@ static void ext4_mb_use_inode_pa(struct ext4_allocation_context *ac, ac->ac_pa = pa; BUG_ON(start < pa->pa_pstart); - BUG_ON(start + len > pa->pa_pstart + pa->pa_len); + BUG_ON(end > pa->pa_pstart + EXT4_C2B(sbi, pa->pa_len)); BUG_ON(pa->pa_free < len); pa->pa_free -= len; @@ -3193,6 +3199,7 @@ ext4_mb_check_group_pa(ext4_fsblk_t goal_block, static noinline_for_stack int ext4_mb_use_preallocated(struct ext4_allocation_context *ac) { + struct ext4_sb_info *sbi = EXT4_SB(ac->ac_sb); int order, i; struct ext4_inode_info *ei = EXT4_I(ac->ac_inode); struct ext4_locality_group *lg; @@ -3210,12 +3217,14 @@ ext4_mb_use_preallocated(struct ext4_allocation_context *ac) /* all fields in this condition don't change, * so we can skip locking for them */ if (ac->ac_o_ex.fe_logical < pa->pa_lstart || - ac->ac_o_ex.fe_logical >= pa->pa_lstart + pa->pa_len) + ac->ac_o_ex.fe_logical >= (pa->pa_lstart + + EXT4_C2B(sbi, pa->pa_len))) continue; /* non-extent files can't have physical blocks past 2^32 */ if (!(ext4_test_inode_flag(ac->ac_inode, EXT4_INODE_EXTENTS)) && - pa->pa_pstart + pa->pa_len > EXT4_MAX_BLOCK_FILE_PHYS) + (pa->pa_pstart + EXT4_C2B(sbi, pa->pa_len) > + EXT4_MAX_BLOCK_FILE_PHYS)) continue; /* found preallocated blocks, use them */ @@ -3412,6 +3421,7 @@ static noinline_for_stack int ext4_mb_new_inode_pa(struct ext4_allocation_context *ac) { struct super_block *sb = ac->ac_sb; + struct ext4_sb_info *sbi = EXT4_SB(sb); struct ext4_prealloc_space *pa; struct ext4_group_info *grp; struct ext4_inode_info *ei; @@ -3443,16 +3453,18 @@ ext4_mb_new_inode_pa(struct ext4_allocation_context *ac) winl = ac->ac_o_ex.fe_logical - ac->ac_g_ex.fe_logical; /* also, we should cover whole original request */ - wins = ac->ac_b_ex.fe_len - ac->ac_o_ex.fe_len; + wins = EXT4_C2B(sbi, ac->ac_b_ex.fe_len - ac->ac_o_ex.fe_len); /* the smallest one defines real window */ win = min(winl, wins); - offs = ac->ac_o_ex.fe_logical % ac->ac_b_ex.fe_len; + offs = ac->ac_o_ex.fe_logical % + EXT4_C2B(sbi, ac->ac_b_ex.fe_len); if (offs && offs < win) win = offs; - ac->ac_b_ex.fe_logical = ac->ac_o_ex.fe_logical - win; + ac->ac_b_ex.fe_logical = ac->ac_o_ex.fe_logical - + EXT4_B2C(sbi, win); BUG_ON(ac->ac_o_ex.fe_logical < ac->ac_b_ex.fe_logical); BUG_ON(ac->ac_o_ex.fe_len > ac->ac_b_ex.fe_len); } @@ -3477,7 +3489,7 @@ ext4_mb_new_inode_pa(struct ext4_allocation_context *ac) trace_ext4_mb_new_inode_pa(ac, pa); ext4_mb_use_inode_pa(ac, pa); - atomic_add(pa->pa_free, &EXT4_SB(sb)->s_mb_preallocated); + atomic_add(pa->pa_free, &sbi->s_mb_preallocated); ei = EXT4_I(ac->ac_inode); grp = ext4_get_group_info(sb, ac->ac_b_ex.fe_group); @@ -3592,7 +3604,7 @@ ext4_mb_release_inode_pa(struct ext4_buddy *e4b, struct buffer_head *bitmap_bh, BUG_ON(pa->pa_deleted == 0); ext4_get_group_no_and_offset(sb, pa->pa_pstart, &group, &bit); - grp_blk_start = pa->pa_pstart - bit; + grp_blk_start = pa->pa_pstart - EXT4_C2B(sbi, bit); BUG_ON(group != e4b->bd_group && pa->pa_len != 0); end = bit + pa->pa_len; @@ -3607,7 +3619,8 @@ ext4_mb_release_inode_pa(struct ext4_buddy *e4b, struct buffer_head *bitmap_bh, free += next - bit; trace_ext4_mballoc_discard(sb, NULL, group, bit, next - bit); - trace_ext4_mb_release_inode_pa(pa, grp_blk_start + bit, + trace_ext4_mb_release_inode_pa(pa, (grp_blk_start + + EXT4_C2B(sbi, bit)), next - bit); mb_free_blocks(pa->pa_inode, e4b, bit, next - bit); bit = next + 1; @@ -3958,7 +3971,7 @@ static void ext4_mb_group_or_file(struct ext4_allocation_context *ac) if (unlikely(ac->ac_flags & EXT4_MB_HINT_GOAL_ONLY)) return; - size = ac->ac_o_ex.fe_logical + ac->ac_o_ex.fe_len; + size = ac->ac_o_ex.fe_logical + EXT4_C2B(sbi, ac->ac_o_ex.fe_len); isize = (i_size_read(ac->ac_inode) + ac->ac_sb->s_blocksize - 1) >> bsbits; @@ -4019,18 +4032,15 @@ ext4_mb_initialize_context(struct ext4_allocation_context *ac, /* set up allocation goals */ memset(ac, 0, sizeof(struct ext4_allocation_context)); - ac->ac_b_ex.fe_logical = ar->logical; + ac->ac_b_ex.fe_logical = ar->logical & ~(sbi->s_cluster_ratio - 1); ac->ac_status = AC_STATUS_CONTINUE; ac->ac_sb = sb; ac->ac_inode = ar->inode; - ac->ac_o_ex.fe_logical = ar->logical; + ac->ac_o_ex.fe_logical = ac->ac_b_ex.fe_logical; ac->ac_o_ex.fe_group = group; ac->ac_o_ex.fe_start = block; ac->ac_o_ex.fe_len = len; - ac->ac_g_ex.fe_logical = ar->logical; - ac->ac_g_ex.fe_group = group; - ac->ac_g_ex.fe_start = block; - ac->ac_g_ex.fe_len = len; + ac->ac_g_ex = ac->ac_o_ex; ac->ac_flags = ar->flags; /* we have to define context: we'll we work with a file or @@ -4182,13 +4192,14 @@ static void ext4_mb_add_n_trim(struct ext4_allocation_context *ac) */ static int ext4_mb_release_context(struct ext4_allocation_context *ac) { + struct ext4_sb_info *sbi = EXT4_SB(ac->ac_sb); struct ext4_prealloc_space *pa = ac->ac_pa; if (pa) { if (pa->pa_type == MB_GROUP_PA) { /* see comment in ext4_mb_use_group_pa() */ spin_lock(&pa->pa_lock); - pa->pa_pstart += ac->ac_b_ex.fe_len; - pa->pa_lstart += ac->ac_b_ex.fe_len; + pa->pa_pstart += EXT4_C2B(sbi, ac->ac_b_ex.fe_len); + pa->pa_lstart += EXT4_C2B(sbi, ac->ac_b_ex.fe_len); pa->pa_free -= ac->ac_b_ex.fe_len; pa->pa_len -= ac->ac_b_ex.fe_len; spin_unlock(&pa->pa_lock); @@ -4249,7 +4260,7 @@ ext4_fsblk_t ext4_mb_new_blocks(handle_t *handle, struct super_block *sb; ext4_fsblk_t block = 0; unsigned int inquota = 0; - unsigned int reserv_blks = 0; + unsigned int reserv_clstrs = 0; sb = ar->inode->i_sb; sbi = EXT4_SB(sb); @@ -4279,12 +4290,14 @@ ext4_fsblk_t ext4_mb_new_blocks(handle_t *handle, *errp = -ENOSPC; return 0; } - reserv_blks = ar->len; + reserv_clstrs = ar->len; if (ar->flags & EXT4_MB_USE_ROOT_BLOCKS) { - dquot_alloc_block_nofail(ar->inode, ar->len); + dquot_alloc_block_nofail(ar->inode, + EXT4_C2B(sbi, ar->len)); } else { while (ar->len && - dquot_alloc_block(ar->inode, ar->len)) { + dquot_alloc_block(ar->inode, + EXT4_C2B(sbi, ar->len))) { ar->flags |= EXT4_MB_HINT_NOPREALLOC; ar->len--; @@ -4328,7 +4341,7 @@ ext4_fsblk_t ext4_mb_new_blocks(handle_t *handle, ext4_mb_new_preallocation(ac); } if (likely(ac->ac_status == AC_STATUS_FOUND)) { - *errp = ext4_mb_mark_diskspace_used(ac, handle, reserv_blks); + *errp = ext4_mb_mark_diskspace_used(ac, handle, reserv_clstrs); if (*errp == -EAGAIN) { /* * drop the reference that we took @@ -4364,13 +4377,13 @@ ext4_fsblk_t ext4_mb_new_blocks(handle_t *handle, if (ac) kmem_cache_free(ext4_ac_cachep, ac); if (inquota && ar->len < inquota) - dquot_free_block(ar->inode, inquota - ar->len); + dquot_free_block(ar->inode, EXT4_C2B(sbi, inquota - ar->len)); if (!ar->len) { if (!ext4_test_inode_state(ar->inode, EXT4_STATE_DELALLOC_RESERVED)) /* release all the reserved blocks if non delalloc */ percpu_counter_sub(&sbi->s_dirtyblocks_counter, - reserv_blks); + reserv_clstrs); } trace_ext4_allocate_blocks(ar, (unsigned long long)block); diff --git a/fs/ext4/mballoc.h b/fs/ext4/mballoc.h index 3cdb8aa9f6b7..1641f4b57439 100644 --- a/fs/ext4/mballoc.h +++ b/fs/ext4/mballoc.h @@ -139,9 +139,9 @@ enum { struct ext4_free_extent { ext4_lblk_t fe_logical; - ext4_grpblk_t fe_start; + ext4_grpblk_t fe_start; /* In cluster units */ ext4_group_t fe_group; - ext4_grpblk_t fe_len; + ext4_grpblk_t fe_len; /* In cluster units */ }; /* From 84130193e0e6568dfdfb823f0e1e19aec80aff6e Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 9 Sep 2011 18:50:51 -0400 Subject: [PATCH 0124/1531] ext4: teach ext4_free_blocks() about bigalloc and clusters The ext4_free_blocks() function now has two new flags that indicate whether a partial cluster at the beginning or the end of the block extents should be freed or not. That will be up the caller (i.e., truncate), who can figure out whether partial clusters at the beginning or the end of a block range can be freed. We also have to update the ext4_mb_free_metadata() and release_blocks_on_commit() machinery to be cluster-based, since it is used by ext4_free_blocks(). Signed-off-by: "Theodore Ts'o" --- fs/ext4/ext4.h | 2 ++ fs/ext4/mballoc.c | 86 ++++++++++++++++++++++++++++++++++------------- fs/ext4/mballoc.h | 2 +- 3 files changed, 65 insertions(+), 25 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 803cfa42e1e8..030bfc1cb59d 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -539,6 +539,8 @@ struct ext4_new_group_data { #define EXT4_FREE_BLOCKS_FORGET 0x0002 #define EXT4_FREE_BLOCKS_VALIDATED 0x0004 #define EXT4_FREE_BLOCKS_NO_QUOT_UPDATE 0x0008 +#define EXT4_FREE_BLOCKS_NOFREE_FIRST_CLUSTER 0x0010 +#define EXT4_FREE_BLOCKS_NOFREE_LAST_CLUSTER 0x0020 /* * Flags used by ext4_discard_partial_page_buffers diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 8765f2512f13..57ce6960e940 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -2602,11 +2602,13 @@ int ext4_mb_release(struct super_block *sb) } static inline int ext4_issue_discard(struct super_block *sb, - ext4_group_t block_group, ext4_grpblk_t block, int count) + ext4_group_t block_group, ext4_grpblk_t cluster, int count) { ext4_fsblk_t discard_block; - discard_block = block + ext4_group_first_block_no(sb, block_group); + discard_block = (EXT4_C2B(EXT4_SB(sb), cluster) + + ext4_group_first_block_no(sb, block_group)); + count = EXT4_C2B(EXT4_SB(sb), count); trace_ext4_discard_blocks(sb, (unsigned long long) discard_block, count); return sb_issue_discard(sb, discard_block, count, GFP_NOFS, 0); @@ -2633,7 +2635,7 @@ static void release_blocks_on_commit(journal_t *journal, transaction_t *txn) if (test_opt(sb, DISCARD)) ext4_issue_discard(sb, entry->group, - entry->start_blk, entry->count); + entry->start_cluster, entry->count); err = ext4_mb_load_buddy(sb, entry->group, &e4b); /* we expect to find existing buddy because it's pinned */ @@ -2646,7 +2648,7 @@ static void release_blocks_on_commit(journal_t *journal, transaction_t *txn) ext4_lock_group(sb, entry->group); /* Take it out of per group rb tree */ rb_erase(&entry->node, &(db->bb_free_root)); - mb_free_blocks(NULL, &e4b, entry->start_blk, entry->count); + mb_free_blocks(NULL, &e4b, entry->start_cluster, entry->count); /* * Clear the trimmed flag for the group so that the next @@ -3300,7 +3302,7 @@ static void ext4_mb_generate_from_freelist(struct super_block *sb, void *bitmap, while (n) { entry = rb_entry(n, struct ext4_free_data, node); - ext4_set_bits(bitmap, entry->start_blk, entry->count); + ext4_set_bits(bitmap, entry->start_cluster, entry->count); n = rb_next(n); } return; @@ -4401,7 +4403,7 @@ static int can_merge(struct ext4_free_data *entry1, { if ((entry1->t_tid == entry2->t_tid) && (entry1->group == entry2->group) && - ((entry1->start_blk + entry1->count) == entry2->start_blk)) + ((entry1->start_cluster + entry1->count) == entry2->start_cluster)) return 1; return 0; } @@ -4411,7 +4413,7 @@ ext4_mb_free_metadata(handle_t *handle, struct ext4_buddy *e4b, struct ext4_free_data *new_entry) { ext4_group_t group = e4b->bd_group; - ext4_grpblk_t block; + ext4_grpblk_t cluster; struct ext4_free_data *entry; struct ext4_group_info *db = e4b->bd_info; struct super_block *sb = e4b->bd_sb; @@ -4424,7 +4426,7 @@ ext4_mb_free_metadata(handle_t *handle, struct ext4_buddy *e4b, BUG_ON(e4b->bd_buddy_page == NULL); new_node = &new_entry->node; - block = new_entry->start_blk; + cluster = new_entry->start_cluster; if (!*n) { /* first free block exent. We need to @@ -4438,13 +4440,14 @@ ext4_mb_free_metadata(handle_t *handle, struct ext4_buddy *e4b, while (*n) { parent = *n; entry = rb_entry(parent, struct ext4_free_data, node); - if (block < entry->start_blk) + if (cluster < entry->start_cluster) n = &(*n)->rb_left; - else if (block >= (entry->start_blk + entry->count)) + else if (cluster >= (entry->start_cluster + entry->count)) n = &(*n)->rb_right; else { ext4_grp_locked_error(sb, group, 0, - ext4_group_first_block_no(sb, group) + block, + ext4_group_first_block_no(sb, group) + + EXT4_C2B(sbi, cluster), "Block already on to-be-freed list"); return 0; } @@ -4458,7 +4461,7 @@ ext4_mb_free_metadata(handle_t *handle, struct ext4_buddy *e4b, if (node) { entry = rb_entry(node, struct ext4_free_data, node); if (can_merge(entry, new_entry)) { - new_entry->start_blk = entry->start_blk; + new_entry->start_cluster = entry->start_cluster; new_entry->count += entry->count; rb_erase(node, &(db->bb_free_root)); spin_lock(&sbi->s_md_lock); @@ -4509,6 +4512,7 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode, ext4_group_t block_group; struct ext4_sb_info *sbi; struct ext4_buddy e4b; + unsigned int count_clusters; int err = 0; int ret; @@ -4557,6 +4561,38 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode, if (!ext4_should_writeback_data(inode)) flags |= EXT4_FREE_BLOCKS_METADATA; + /* + * If the extent to be freed does not begin on a cluster + * boundary, we need to deal with partial clusters at the + * beginning and end of the extent. Normally we will free + * blocks at the beginning or the end unless we are explicitly + * requested to avoid doing so. + */ + overflow = block & (sbi->s_cluster_ratio - 1); + if (overflow) { + if (flags & EXT4_FREE_BLOCKS_NOFREE_FIRST_CLUSTER) { + overflow = sbi->s_cluster_ratio - overflow; + block += overflow; + if (count > overflow) + count -= overflow; + else + return; + } else { + block -= overflow; + count += overflow; + } + } + overflow = count & (sbi->s_cluster_ratio - 1); + if (overflow) { + if (flags & EXT4_FREE_BLOCKS_NOFREE_LAST_CLUSTER) { + if (count > overflow) + count -= overflow; + else + return; + } else + count += sbi->s_cluster_ratio - overflow; + } + do_more: overflow = 0; ext4_get_group_no_and_offset(sb, block, &block_group, &bit); @@ -4565,10 +4601,12 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode, * Check to see if we are freeing blocks across a group * boundary. */ - if (bit + count > EXT4_CLUSTERS_PER_GROUP(sb)) { - overflow = bit + count - EXT4_CLUSTERS_PER_GROUP(sb); + if (EXT4_C2B(sbi, bit) + count > EXT4_BLOCKS_PER_GROUP(sb)) { + overflow = EXT4_C2B(sbi, bit) + count - + EXT4_BLOCKS_PER_GROUP(sb); count -= overflow; } + count_clusters = EXT4_B2C(sbi, count); bitmap_bh = ext4_read_block_bitmap(sb, block_group); if (!bitmap_bh) { err = -EIO; @@ -4583,9 +4621,9 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode, if (in_range(ext4_block_bitmap(sb, gdp), block, count) || in_range(ext4_inode_bitmap(sb, gdp), block, count) || in_range(block, ext4_inode_table(sb, gdp), - EXT4_SB(sb)->s_itb_per_group) || + EXT4_SB(sb)->s_itb_per_group) || in_range(block + count - 1, ext4_inode_table(sb, gdp), - EXT4_SB(sb)->s_itb_per_group)) { + EXT4_SB(sb)->s_itb_per_group)) { ext4_error(sb, "Freeing blocks in system zone - " "Block = %llu, count = %lu", block, count); @@ -4610,11 +4648,11 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode, #ifdef AGGRESSIVE_CHECK { int i; - for (i = 0; i < count; i++) + for (i = 0; i < count_clusters; i++) BUG_ON(!mb_test_bit(bit + i, bitmap_bh->b_data)); } #endif - trace_ext4_mballoc_free(sb, inode, block_group, bit, count); + trace_ext4_mballoc_free(sb, inode, block_group, bit, count_clusters); err = ext4_mb_load_buddy(sb, block_group, &e4b); if (err) @@ -4631,13 +4669,13 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode, err = -ENOMEM; goto error_return; } - new_entry->start_blk = bit; + new_entry->start_cluster = bit; new_entry->group = block_group; - new_entry->count = count; + new_entry->count = count_clusters; new_entry->t_tid = handle->h_transaction->t_tid; ext4_lock_group(sb, block_group); - mb_clear_bits(bitmap_bh->b_data, bit, count); + mb_clear_bits(bitmap_bh->b_data, bit, count_clusters); ext4_mb_free_metadata(handle, &e4b, new_entry); } else { /* need to update group_info->bb_free and bitmap @@ -4645,11 +4683,11 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode, * them with group lock_held */ ext4_lock_group(sb, block_group); - mb_clear_bits(bitmap_bh->b_data, bit, count); - mb_free_blocks(inode, &e4b, bit, count); + mb_clear_bits(bitmap_bh->b_data, bit, count_clusters); + mb_free_blocks(inode, &e4b, bit, count_clusters); } - ret = ext4_free_blks_count(sb, gdp) + count; + ret = ext4_free_blks_count(sb, gdp) + count_clusters; ext4_free_blks_set(sb, gdp, ret); gdp->bg_checksum = ext4_group_desc_csum(sbi, block_group, gdp); ext4_unlock_group(sb, block_group); diff --git a/fs/ext4/mballoc.h b/fs/ext4/mballoc.h index 1641f4b57439..dc99930d4cb5 100644 --- a/fs/ext4/mballoc.h +++ b/fs/ext4/mballoc.h @@ -106,7 +106,7 @@ struct ext4_free_data { ext4_group_t group; /* free block extent */ - ext4_grpblk_t start_blk; + ext4_grpblk_t start_cluster; ext4_grpblk_t count; /* transaction which freed this extent */ From 4d33b1ef10995d7ba6191d67456202c697a92a32 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 9 Sep 2011 18:52:51 -0400 Subject: [PATCH 0125/1531] ext4: teach ext4_ext_map_blocks() about the bigalloc feature If we need to allocate a new block in ext4_ext_map_blocks(), the function needs to see if the cluster has already been allocated. Signed-off-by: "Theodore Ts'o" --- fs/ext4/extents.c | 181 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 162 insertions(+), 19 deletions(-) diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index ba7bd5a176ce..bd42ab29efec 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -1270,7 +1270,8 @@ static int ext4_ext_search_left(struct inode *inode, */ static int ext4_ext_search_right(struct inode *inode, struct ext4_ext_path *path, - ext4_lblk_t *logical, ext4_fsblk_t *phys) + ext4_lblk_t *logical, ext4_fsblk_t *phys, + struct ext4_extent **ret_ex) { struct buffer_head *bh = NULL; struct ext4_extent_header *eh; @@ -1312,9 +1313,7 @@ static int ext4_ext_search_right(struct inode *inode, return -EIO; } } - *logical = le32_to_cpu(ex->ee_block); - *phys = ext4_ext_pblock(ex); - return 0; + goto found_extent; } if (unlikely(*logical < (le32_to_cpu(ex->ee_block) + ee_len))) { @@ -1327,9 +1326,7 @@ static int ext4_ext_search_right(struct inode *inode, if (ex != EXT_LAST_EXTENT(path[depth].p_hdr)) { /* next allocated block in this leaf */ ex++; - *logical = le32_to_cpu(ex->ee_block); - *phys = ext4_ext_pblock(ex); - return 0; + goto found_extent; } /* go up and search for index to the right */ @@ -1372,9 +1369,12 @@ static int ext4_ext_search_right(struct inode *inode, return -EIO; } ex = EXT_FIRST_EXTENT(eh); +found_extent: *logical = le32_to_cpu(ex->ee_block); *phys = ext4_ext_pblock(ex); - put_bh(bh); + *ret_ex = ex; + if (bh) + put_bh(bh); return 0; } @@ -1627,7 +1627,8 @@ static int ext4_ext_try_to_merge(struct inode *inode, * such that there will be no overlap, and then returns 1. * If there is no overlap found, it returns 0. */ -static unsigned int ext4_ext_check_overlap(struct inode *inode, +static unsigned int ext4_ext_check_overlap(struct ext4_sb_info *sbi, + struct inode *inode, struct ext4_extent *newext, struct ext4_ext_path *path) { @@ -1641,6 +1642,7 @@ static unsigned int ext4_ext_check_overlap(struct inode *inode, if (!path[depth].p_ext) goto out; b2 = le32_to_cpu(path[depth].p_ext->ee_block); + b2 &= ~(sbi->s_cluster_ratio - 1); /* * get the next allocated block if the extent in the path @@ -1650,6 +1652,7 @@ static unsigned int ext4_ext_check_overlap(struct inode *inode, b2 = ext4_ext_next_allocated_block(path); if (b2 == EXT_MAX_BLOCKS) goto out; + b2 &= ~(sbi->s_cluster_ratio - 1); } /* check for wrap through zero on extent logical start block*/ @@ -3293,6 +3296,106 @@ ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode, return err ? err : allocated; } +/* + * get_implied_cluster_alloc - check to see if the requested + * allocation (in the map structure) overlaps with a cluster already + * allocated in an extent. + * @sbi The ext4-specific superblock structure + * @map The requested lblk->pblk mapping + * @ex The extent structure which might contain an implied + * cluster allocation + * + * This function is called by ext4_ext_map_blocks() after we failed to + * find blocks that were already in the inode's extent tree. Hence, + * we know that the beginning of the requested region cannot overlap + * the extent from the inode's extent tree. There are three cases we + * want to catch. The first is this case: + * + * |--- cluster # N--| + * |--- extent ---| |---- requested region ---| + * |==========| + * + * The second case that we need to test for is this one: + * + * |--------- cluster # N ----------------| + * |--- requested region --| |------- extent ----| + * |=======================| + * + * The third case is when the requested region lies between two extents + * within the same cluster: + * |------------- cluster # N-------------| + * |----- ex -----| |---- ex_right ----| + * |------ requested region ------| + * |================| + * + * In each of the above cases, we need to set the map->m_pblk and + * map->m_len so it corresponds to the return the extent labelled as + * "|====|" from cluster #N, since it is already in use for data in + * cluster EXT4_B2C(sbi, map->m_lblk). We will then return 1 to + * signal to ext4_ext_map_blocks() that map->m_pblk should be treated + * as a new "allocated" block region. Otherwise, we will return 0 and + * ext4_ext_map_blocks() will then allocate one or more new clusters + * by calling ext4_mb_new_blocks(). + */ +static int get_implied_cluster_alloc(struct ext4_sb_info *sbi, + struct ext4_map_blocks *map, + struct ext4_extent *ex, + struct ext4_ext_path *path) +{ + ext4_lblk_t c_offset = map->m_lblk & (sbi->s_cluster_ratio-1); + ext4_lblk_t ex_cluster_start, ex_cluster_end; + ext4_lblk_t rr_cluster_start, rr_cluster_end; + ext4_lblk_t ee_block = le32_to_cpu(ex->ee_block); + ext4_fsblk_t ee_start = ext4_ext_pblock(ex); + unsigned short ee_len = ext4_ext_get_actual_len(ex); + + /* The extent passed in that we are trying to match */ + ex_cluster_start = EXT4_B2C(sbi, ee_block); + ex_cluster_end = EXT4_B2C(sbi, ee_block + ee_len - 1); + + /* The requested region passed into ext4_map_blocks() */ + rr_cluster_start = EXT4_B2C(sbi, map->m_lblk); + rr_cluster_end = EXT4_B2C(sbi, map->m_lblk + map->m_len - 1); + + if ((rr_cluster_start == ex_cluster_end) || + (rr_cluster_start == ex_cluster_start)) { + if (rr_cluster_start == ex_cluster_end) + ee_start += ee_len - 1; + map->m_pblk = (ee_start & ~(sbi->s_cluster_ratio - 1)) + + c_offset; + map->m_len = min(map->m_len, + (unsigned) sbi->s_cluster_ratio - c_offset); + /* + * Check for and handle this case: + * + * |--------- cluster # N-------------| + * |------- extent ----| + * |--- requested region ---| + * |===========| + */ + + if (map->m_lblk < ee_block) + map->m_len = min(map->m_len, ee_block - map->m_lblk); + + /* + * Check for the case where there is already another allocated + * block to the right of 'ex' but before the end of the cluster. + * + * |------------- cluster # N-------------| + * |----- ex -----| |---- ex_right ----| + * |------ requested region ------| + * |================| + */ + if (map->m_lblk > ee_block) { + ext4_lblk_t next = ext4_ext_next_allocated_block(path); + map->m_len = min(map->m_len, next - map->m_lblk); + } + return 1; + } + return 0; +} + + /* * Block allocation/map/preallocation routine for extents based files * @@ -3315,14 +3418,16 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, struct ext4_map_blocks *map, int flags) { struct ext4_ext_path *path = NULL; - struct ext4_extent newex, *ex; + struct ext4_extent newex, *ex, *ex2; + struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); ext4_fsblk_t newblock = 0; - int err = 0, depth, ret; - unsigned int allocated = 0; + int free_on_err = 0, err = 0, depth, ret; + unsigned int allocated = 0, offset = 0; unsigned int punched_out = 0; unsigned int result = 0; struct ext4_allocation_request ar; ext4_io_end_t *io = EXT4_I(inode)->cur_aio_dio; + ext4_lblk_t cluster_offset; struct ext4_map_blocks punch_map; ext_debug("blocks %u/%u requested for inode %lu\n", @@ -3508,9 +3613,23 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, ext4_ext_put_gap_in_cache(inode, path, map->m_lblk); goto out2; } + /* * Okay, we need to do block allocation. */ + newex.ee_block = cpu_to_le32(map->m_lblk); + cluster_offset = map->m_lblk & (sbi->s_cluster_ratio-1); + + /* + * If we are doing bigalloc, check to see if the extent returned + * by ext4_ext_find_extent() implies a cluster we can use. + */ + if (cluster_offset && ex && + get_implied_cluster_alloc(sbi, map, ex, path)) { + ar.len = allocated = map->m_len; + newblock = map->m_pblk; + goto got_allocated_blocks; + } /* find neighbour allocated blocks */ ar.lleft = map->m_lblk; @@ -3518,10 +3637,20 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, if (err) goto out2; ar.lright = map->m_lblk; - err = ext4_ext_search_right(inode, path, &ar.lright, &ar.pright); + ex2 = NULL; + err = ext4_ext_search_right(inode, path, &ar.lright, &ar.pright, &ex2); if (err) goto out2; + /* Check if the extent after searching to the right implies a + * cluster we can use. */ + if ((sbi->s_cluster_ratio > 1) && ex2 && + get_implied_cluster_alloc(sbi, map, ex2, path)) { + ar.len = allocated = map->m_len; + newblock = map->m_pblk; + goto got_allocated_blocks; + } + /* * See if request is beyond maximum number of blocks we can have in * a single extent. For an initialized extent this limit is @@ -3536,9 +3665,8 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, map->m_len = EXT_UNINIT_MAX_LEN; /* Check if we can really insert (m_lblk)::(m_lblk + m_len) extent */ - newex.ee_block = cpu_to_le32(map->m_lblk); newex.ee_len = cpu_to_le16(map->m_len); - err = ext4_ext_check_overlap(inode, &newex, path); + err = ext4_ext_check_overlap(sbi, inode, &newex, path); if (err) allocated = ext4_ext_get_actual_len(&newex); else @@ -3548,7 +3676,18 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, ar.inode = inode; ar.goal = ext4_ext_find_goal(inode, path, map->m_lblk); ar.logical = map->m_lblk; - ar.len = allocated; + /* + * We calculate the offset from the beginning of the cluster + * for the logical block number, since when we allocate a + * physical cluster, the physical block should start at the + * same offset from the beginning of the cluster. This is + * needed so that future calls to get_implied_cluster_alloc() + * work correctly. + */ + offset = map->m_lblk & (sbi->s_cluster_ratio - 1); + ar.len = EXT4_NUM_B2C(sbi, offset+allocated); + ar.goal -= offset; + ar.logical -= offset; if (S_ISREG(inode->i_mode)) ar.flags = EXT4_MB_HINT_DATA; else @@ -3561,9 +3700,14 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, goto out2; ext_debug("allocate new block: goal %llu, found %llu/%u\n", ar.goal, newblock, allocated); + free_on_err = 1; + ar.len = EXT4_C2B(sbi, ar.len) - offset; + if (ar.len > allocated) + ar.len = allocated; +got_allocated_blocks: /* try to insert new extent into found leaf and return */ - ext4_ext_store_pblock(&newex, newblock); + ext4_ext_store_pblock(&newex, newblock + offset); newex.ee_len = cpu_to_le16(ar.len); /* Mark uninitialized */ if (flags & EXT4_GET_BLOCKS_UNINIT_EXT){ @@ -3591,7 +3735,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, if (!err) err = ext4_ext_insert_extent(handle, inode, path, &newex, flags); - if (err) { + if (err && free_on_err) { int fb_flags = flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE ? EXT4_FREE_BLOCKS_NO_QUOT_UPDATE : 0; /* free data blocks we just allocated */ @@ -4115,7 +4259,6 @@ static int ext4_ext_fiemap_cb(struct inode *inode, ext4_lblk_t next, return EXT_BREAK; return EXT_CONTINUE; } - /* fiemap flags we can handle specified here */ #define EXT4_FIEMAP_FLAGS (FIEMAP_FLAG_SYNC|FIEMAP_FLAG_XATTR) From 0aa060000e83ca3d09ddc446a7174fb0820d99bc Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 9 Sep 2011 18:54:51 -0400 Subject: [PATCH 0126/1531] ext4: teach ext4_ext_truncate() about the bigalloc feature When we are truncating (as opposed unlinking) a file, we need to worry about partial truncates of a file, especially in the light of sparse files. The changes here make sure that arbitrary truncates of sparse files works correctly. Yeah, it's messy. Note that these functions will need to be revisted when the punch ioctl is integrated --- in fact this commit will probably have merge conflicts with the punch changes which Allison Henders and the IBM LTC have been working on. I will need to fix this up when either patch hits mainline. Signed-off-by: "Theodore Ts'o" --- fs/ext4/extents.c | 88 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 76 insertions(+), 12 deletions(-) diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index bd42ab29efec..cd4479c08031 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -2202,14 +2202,39 @@ int ext4_ext_index_trans_blocks(struct inode *inode, int nrblocks, int chunk) } static int ext4_remove_blocks(handle_t *handle, struct inode *inode, - struct ext4_extent *ex, - ext4_lblk_t from, ext4_lblk_t to) + struct ext4_extent *ex, + ext4_fsblk_t *partial_cluster, + ext4_lblk_t from, ext4_lblk_t to) { + struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); unsigned short ee_len = ext4_ext_get_actual_len(ex); + ext4_fsblk_t pblk; int flags = EXT4_FREE_BLOCKS_FORGET; if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) flags |= EXT4_FREE_BLOCKS_METADATA; + /* + * For bigalloc file systems, we never free a partial cluster + * at the beginning of the extent. Instead, we make a note + * that we tried freeing the cluster, and check to see if we + * need to free it on a subsequent call to ext4_remove_blocks, + * or at the end of the ext4_truncate() operation. + */ + flags |= EXT4_FREE_BLOCKS_NOFREE_FIRST_CLUSTER; + + /* + * If we have a partial cluster, and it's different from the + * cluster of the last block, we need to explicitly free the + * partial cluster here. + */ + pblk = ext4_ext_pblock(ex) + ee_len - 1; + if (*partial_cluster && (EXT4_B2C(sbi, pblk) != *partial_cluster)) { + ext4_free_blocks(handle, inode, NULL, + EXT4_C2B(sbi, *partial_cluster), + sbi->s_cluster_ratio, flags); + *partial_cluster = 0; + } + #ifdef EXTENTS_STATS { struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); @@ -2229,12 +2254,24 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode, && to == le32_to_cpu(ex->ee_block) + ee_len - 1) { /* tail removal */ ext4_lblk_t num; - ext4_fsblk_t start; num = le32_to_cpu(ex->ee_block) + ee_len - from; - start = ext4_ext_pblock(ex) + ee_len - num; - ext_debug("free last %u blocks starting %llu\n", num, start); - ext4_free_blocks(handle, inode, NULL, start, num, flags); + pblk = ext4_ext_pblock(ex) + ee_len - num; + ext_debug("free last %u blocks starting %llu\n", num, pblk); + ext4_free_blocks(handle, inode, NULL, pblk, num, flags); + /* + * If the block range to be freed didn't start at the + * beginning of a cluster, and we removed the entire + * extent, save the partial cluster here, since we + * might need to delete if we determine that the + * truncate operation has removed all of the blocks in + * the cluster. + */ + if (pblk & (sbi->s_cluster_ratio - 1) && + (ee_len == num)) + *partial_cluster = EXT4_B2C(sbi, pblk); + else + *partial_cluster = 0; } else if (from == le32_to_cpu(ex->ee_block) && to <= le32_to_cpu(ex->ee_block) + ee_len - 1) { /* head removal */ @@ -2269,9 +2306,10 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode, */ static int ext4_ext_rm_leaf(handle_t *handle, struct inode *inode, - struct ext4_ext_path *path, ext4_lblk_t start, - ext4_lblk_t end) + struct ext4_ext_path *path, ext4_fsblk_t *partial_cluster, + ext4_lblk_t start, ext4_lblk_t end) { + struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); int err = 0, correct_index = 0; int depth = ext_depth(inode), credits; struct ext4_extent_header *eh; @@ -2423,7 +2461,8 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode, if (err) goto out; - err = ext4_remove_blocks(handle, inode, ex, a, b); + err = ext4_remove_blocks(handle, inode, ex, partial_cluster, + a, b); if (err) goto out; @@ -2471,7 +2510,8 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode, sizeof(struct ext4_extent)); } le16_add_cpu(&eh->eh_entries, -1); - } + } else + *partial_cluster = 0; ext_debug("new extent: %u:%u:%llu\n", block, num, ext4_ext_pblock(ex)); @@ -2483,6 +2523,25 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode, if (correct_index && eh->eh_entries) err = ext4_ext_correct_indexes(handle, inode, path); + /* + * If there is still a entry in the leaf node, check to see if + * it references the partial cluster. This is the only place + * where it could; if it doesn't, we can free the cluster. + */ + if (*partial_cluster && ex >= EXT_FIRST_EXTENT(eh) && + (EXT4_B2C(sbi, ext4_ext_pblock(ex) + ex_ee_len - 1) != + *partial_cluster)) { + int flags = EXT4_FREE_BLOCKS_FORGET; + + if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) + flags |= EXT4_FREE_BLOCKS_METADATA; + + ext4_free_blocks(handle, inode, NULL, + EXT4_C2B(sbi, *partial_cluster), + sbi->s_cluster_ratio, flags); + *partial_cluster = 0; + } + /* if this leaf is free, then we should * remove it from index block above */ if (err == 0 && eh->eh_entries == 0 && path[depth].p_bh != NULL) @@ -2518,6 +2577,7 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start) struct super_block *sb = inode->i_sb; int depth = ext_depth(inode); struct ext4_ext_path *path; + ext4_fsblk_t partial_cluster = 0; handle_t *handle; int i, err; @@ -2553,7 +2613,8 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start) if (i == depth) { /* this is leaf block */ err = ext4_ext_rm_leaf(handle, inode, path, - start, EXT_MAX_BLOCKS - 1); + &partial_cluster, start, + EXT_MAX_BLOCKS - 1); /* root level has p_bh == NULL, brelse() eats this */ brelse(path[i].p_bh); path[i].p_bh = NULL; @@ -3495,6 +3556,8 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, ee_len = ext4_ext_get_actual_len(ex); /* if found extent covers block, simply return it */ if (in_range(map->m_lblk, ee_block, ee_len)) { + ext4_fsblk_t partial_cluster = 0; + newblock = map->m_lblk - ee_block + ee_start; /* number of remaining blocks in the extent */ allocated = ee_len - (map->m_lblk - ee_block); @@ -3578,7 +3641,8 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, ext4_ext_invalidate_cache(inode); err = ext4_ext_rm_leaf(handle, inode, path, - map->m_lblk, map->m_lblk + punched_out); + &partial_cluster, map->m_lblk, + map->m_lblk + punched_out); if (!err && path->p_hdr->eh_entries == 0) { /* From 5704265188ffe4290ed73b3cb685206c3ed8209d Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 9 Sep 2011 18:56:51 -0400 Subject: [PATCH 0127/1531] ext4: convert s_{dirty,free}blocks_counter to s_{dirty,free}clusters_counter Convert the percpu counters s_dirtyblocks_counter and s_freeblocks_counter in struct ext4_super_info to be s_dirtyclusters_counter and s_freeclusters_counter. Signed-off-by: "Theodore Ts'o" --- fs/ext4/balloc.c | 10 +++++----- fs/ext4/ext4.h | 5 +++-- fs/ext4/ialloc.c | 3 ++- fs/ext4/inode.c | 18 ++++++++++-------- fs/ext4/mballoc.c | 12 +++++++----- fs/ext4/resize.c | 4 ++-- fs/ext4/super.c | 29 +++++++++++++++-------------- 7 files changed, 44 insertions(+), 37 deletions(-) diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index 89abf1f7b253..9080a857cda9 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -414,16 +414,16 @@ static int ext4_has_free_blocks(struct ext4_sb_info *sbi, s64 nblocks, unsigned int flags) { s64 free_blocks, dirty_blocks, root_blocks; - struct percpu_counter *fbc = &sbi->s_freeblocks_counter; - struct percpu_counter *dbc = &sbi->s_dirtyblocks_counter; + struct percpu_counter *fcc = &sbi->s_freeclusters_counter; + struct percpu_counter *dbc = &sbi->s_dirtyclusters_counter; - free_blocks = percpu_counter_read_positive(fbc); + free_blocks = percpu_counter_read_positive(fcc); dirty_blocks = percpu_counter_read_positive(dbc); root_blocks = ext4_r_blocks_count(sbi->s_es); if (free_blocks - (nblocks + root_blocks + dirty_blocks) < EXT4_FREEBLOCKS_WATERMARK) { - free_blocks = percpu_counter_sum_positive(fbc); + free_blocks = EXT4_C2B(sbi, percpu_counter_sum_positive(fcc)); dirty_blocks = percpu_counter_sum_positive(dbc); } /* Check whether we have space after @@ -449,7 +449,7 @@ int ext4_claim_free_blocks(struct ext4_sb_info *sbi, s64 nblocks, unsigned int flags) { if (ext4_has_free_blocks(sbi, nblocks, flags)) { - percpu_counter_add(&sbi->s_dirtyblocks_counter, nblocks); + percpu_counter_add(&sbi->s_dirtyclusters_counter, nblocks); return 0; } else return -ENOSPC; diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 030bfc1cb59d..c7588366471c 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -855,6 +855,7 @@ struct ext4_inode_info { ext4_group_t i_last_alloc_group; /* allocation reservation info for delalloc */ + /* In case of bigalloc, these refer to clusters rather than blocks */ unsigned int i_reserved_data_blocks; unsigned int i_reserved_meta_blocks; unsigned int i_allocated_meta_blocks; @@ -1144,10 +1145,10 @@ struct ext4_sb_info { u32 s_hash_seed[4]; int s_def_hash_version; int s_hash_unsigned; /* 3 if hash should be signed, 0 if not */ - struct percpu_counter s_freeblocks_counter; + struct percpu_counter s_freeclusters_counter; struct percpu_counter s_freeinodes_counter; struct percpu_counter s_dirs_counter; - struct percpu_counter s_dirtyblocks_counter; + struct percpu_counter s_dirtyclusters_counter; struct blockgroup_lock *s_blockgroup_lock; struct proc_dir_entry *s_proc; struct kobject s_kobj; diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index b7a8130d0af4..58115bad163f 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -490,7 +490,8 @@ static int find_group_orlov(struct super_block *sb, struct inode *parent, freei = percpu_counter_read_positive(&sbi->s_freeinodes_counter); avefreei = freei / ngroups; - freeb = percpu_counter_read_positive(&sbi->s_freeblocks_counter); + freeb = EXT4_C2B(sbi, + percpu_counter_read_positive(&sbi->s_freeclusters_counter)); avefreeb = freeb; do_div(avefreeb, ngroups); ndirs = percpu_counter_read_positive(&sbi->s_dirs_counter); diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 904a9a623dab..40f51aae42fe 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -281,7 +281,7 @@ void ext4_da_update_reserve_space(struct inode *inode, /* Update per-inode reservations */ ei->i_reserved_data_blocks -= used; ei->i_reserved_meta_blocks -= ei->i_allocated_meta_blocks; - percpu_counter_sub(&sbi->s_dirtyblocks_counter, + percpu_counter_sub(&sbi->s_dirtyclusters_counter, used + ei->i_allocated_meta_blocks); ei->i_allocated_meta_blocks = 0; @@ -291,7 +291,7 @@ void ext4_da_update_reserve_space(struct inode *inode, * only when we have written all of the delayed * allocation blocks. */ - percpu_counter_sub(&sbi->s_dirtyblocks_counter, + percpu_counter_sub(&sbi->s_dirtyclusters_counter, ei->i_reserved_meta_blocks); ei->i_reserved_meta_blocks = 0; ei->i_da_metadata_calc_len = 0; @@ -1119,14 +1119,14 @@ static void ext4_da_release_space(struct inode *inode, int to_free) * only when we have written all of the delayed * allocation blocks. */ - percpu_counter_sub(&sbi->s_dirtyblocks_counter, + percpu_counter_sub(&sbi->s_dirtyclusters_counter, ei->i_reserved_meta_blocks); ei->i_reserved_meta_blocks = 0; ei->i_da_metadata_calc_len = 0; } /* update fs dirty data blocks counter */ - percpu_counter_sub(&sbi->s_dirtyblocks_counter, to_free); + percpu_counter_sub(&sbi->s_dirtyclusters_counter, to_free); spin_unlock(&EXT4_I(inode)->i_block_reservation_lock); @@ -1349,9 +1349,10 @@ static void ext4_print_free_blocks(struct inode *inode) ext4_count_free_blocks(inode->i_sb)); printk(KERN_CRIT "Free/Dirty block details\n"); printk(KERN_CRIT "free_blocks=%lld\n", - (long long) percpu_counter_sum(&sbi->s_freeblocks_counter)); + (long long) EXT4_C2B(EXT4_SB(inode->i_sb), + percpu_counter_sum(&sbi->s_freeclusters_counter))); printk(KERN_CRIT "dirty_blocks=%lld\n", - (long long) percpu_counter_sum(&sbi->s_dirtyblocks_counter)); + (long long) percpu_counter_sum(&sbi->s_dirtyclusters_counter)); printk(KERN_CRIT "Block reservation details\n"); printk(KERN_CRIT "i_reserved_data_blocks=%u\n", EXT4_I(inode)->i_reserved_data_blocks); @@ -2226,8 +2227,9 @@ static int ext4_nonda_switch(struct super_block *sb) * Delalloc need an accurate free block accounting. So switch * to non delalloc when we are near to error range. */ - free_blocks = percpu_counter_read_positive(&sbi->s_freeblocks_counter); - dirty_blocks = percpu_counter_read_positive(&sbi->s_dirtyblocks_counter); + free_blocks = EXT4_C2B(sbi, + percpu_counter_read_positive(&sbi->s_freeclusters_counter)); + dirty_blocks = percpu_counter_read_positive(&sbi->s_dirtyclusters_counter); if (2 * free_blocks < 3 * dirty_blocks || free_blocks < (dirty_blocks + EXT4_FREEBLOCKS_WATERMARK)) { /* diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 57ce6960e940..4a38b65bd564 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -2834,13 +2834,14 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac, gdp->bg_checksum = ext4_group_desc_csum(sbi, ac->ac_b_ex.fe_group, gdp); ext4_unlock_group(sb, ac->ac_b_ex.fe_group); - percpu_counter_sub(&sbi->s_freeblocks_counter, ac->ac_b_ex.fe_len); + percpu_counter_sub(&sbi->s_freeclusters_counter, ac->ac_b_ex.fe_len); /* * Now reduce the dirty block count also. Should not go negative */ if (!(ac->ac_flags & EXT4_MB_DELALLOC_RESERVED)) /* release all the reserved blocks if non delalloc */ - percpu_counter_sub(&sbi->s_dirtyblocks_counter, reserv_clstrs); + percpu_counter_sub(&sbi->s_dirtyclusters_counter, + reserv_clstrs); if (sbi->s_log_groups_per_flex) { ext4_group_t flex_group = ext4_flex_group(sbi, @@ -4384,7 +4385,7 @@ ext4_fsblk_t ext4_mb_new_blocks(handle_t *handle, if (!ext4_test_inode_state(ar->inode, EXT4_STATE_DELALLOC_RESERVED)) /* release all the reserved blocks if non delalloc */ - percpu_counter_sub(&sbi->s_dirtyblocks_counter, + percpu_counter_sub(&sbi->s_dirtyclusters_counter, reserv_clstrs); } @@ -4691,7 +4692,7 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode, ext4_free_blks_set(sb, gdp, ret); gdp->bg_checksum = ext4_group_desc_csum(sbi, block_group, gdp); ext4_unlock_group(sb, block_group); - percpu_counter_add(&sbi->s_freeblocks_counter, count); + percpu_counter_add(&sbi->s_freeclusters_counter, count_clusters); if (sbi->s_log_groups_per_flex) { ext4_group_t flex_group = ext4_flex_group(sbi, block_group); @@ -4833,7 +4834,8 @@ int ext4_group_add_blocks(handle_t *handle, struct super_block *sb, ext4_free_blks_set(sb, desc, blk_free_count); desc->bg_checksum = ext4_group_desc_csum(sbi, block_group, desc); ext4_unlock_group(sb, block_group); - percpu_counter_add(&sbi->s_freeblocks_counter, blocks_freed); + percpu_counter_add(&sbi->s_freeclusters_counter, + EXT4_B2C(sbi, blocks_freed)); if (sbi->s_log_groups_per_flex) { ext4_group_t flex_group = ext4_flex_group(sbi, block_group); diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c index 707d3f16f7ce..a324a537f2dc 100644 --- a/fs/ext4/resize.c +++ b/fs/ext4/resize.c @@ -937,8 +937,8 @@ int ext4_group_add(struct super_block *sb, struct ext4_new_group_data *input) input->reserved_blocks); /* Update the free space counts */ - percpu_counter_add(&sbi->s_freeblocks_counter, - input->free_blocks_count); + percpu_counter_add(&sbi->s_freeclusters_counter, + EXT4_B2C(sbi, input->free_blocks_count)); percpu_counter_add(&sbi->s_freeinodes_counter, EXT4_INODES_PER_GROUP(sb)); diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 25a4bfe3f39f..f81e7e791655 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -837,10 +837,10 @@ static void ext4_put_super(struct super_block *sb) brelse(sbi->s_group_desc[i]); ext4_kvfree(sbi->s_group_desc); ext4_kvfree(sbi->s_flex_groups); - percpu_counter_destroy(&sbi->s_freeblocks_counter); + percpu_counter_destroy(&sbi->s_freeclusters_counter); percpu_counter_destroy(&sbi->s_freeinodes_counter); percpu_counter_destroy(&sbi->s_dirs_counter); - percpu_counter_destroy(&sbi->s_dirtyblocks_counter); + percpu_counter_destroy(&sbi->s_dirtyclusters_counter); brelse(sbi->s_sbh); #ifdef CONFIG_QUOTA for (i = 0; i < MAXQUOTAS; i++) @@ -2473,7 +2473,7 @@ static ssize_t delayed_allocation_blocks_show(struct ext4_attr *a, char *buf) { return snprintf(buf, PAGE_SIZE, "%llu\n", - (s64) percpu_counter_sum(&sbi->s_dirtyblocks_counter)); + (s64) percpu_counter_sum(&sbi->s_dirtyclusters_counter)); } static ssize_t session_write_kbytes_show(struct ext4_attr *a, @@ -3575,7 +3575,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) sbi->s_err_report.function = print_daily_error_info; sbi->s_err_report.data = (unsigned long) sb; - err = percpu_counter_init(&sbi->s_freeblocks_counter, + err = percpu_counter_init(&sbi->s_freeclusters_counter, ext4_count_free_blocks(sb)); if (!err) { err = percpu_counter_init(&sbi->s_freeinodes_counter, @@ -3586,7 +3586,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) ext4_count_dirs(sb)); } if (!err) { - err = percpu_counter_init(&sbi->s_dirtyblocks_counter, 0); + err = percpu_counter_init(&sbi->s_dirtyclusters_counter, 0); } if (err) { ext4_msg(sb, KERN_ERR, "insufficient memory"); @@ -3701,13 +3701,13 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) * The journal may have updated the bg summary counts, so we * need to update the global counters. */ - percpu_counter_set(&sbi->s_freeblocks_counter, + percpu_counter_set(&sbi->s_freeclusters_counter, ext4_count_free_blocks(sb)); percpu_counter_set(&sbi->s_freeinodes_counter, ext4_count_free_inodes(sb)); percpu_counter_set(&sbi->s_dirs_counter, ext4_count_dirs(sb)); - percpu_counter_set(&sbi->s_dirtyblocks_counter, 0); + percpu_counter_set(&sbi->s_dirtyclusters_counter, 0); no_journal: /* @@ -3847,10 +3847,10 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) del_timer(&sbi->s_err_report); if (sbi->s_flex_groups) ext4_kvfree(sbi->s_flex_groups); - percpu_counter_destroy(&sbi->s_freeblocks_counter); + percpu_counter_destroy(&sbi->s_freeclusters_counter); percpu_counter_destroy(&sbi->s_freeinodes_counter); percpu_counter_destroy(&sbi->s_dirs_counter); - percpu_counter_destroy(&sbi->s_dirtyblocks_counter); + percpu_counter_destroy(&sbi->s_dirtyclusters_counter); if (sbi->s_mmp_tsk) kthread_stop(sbi->s_mmp_tsk); failed_mount2: @@ -4173,8 +4173,9 @@ static int ext4_commit_super(struct super_block *sb, int sync) else es->s_kbytes_written = cpu_to_le64(EXT4_SB(sb)->s_kbytes_written); - ext4_free_blocks_count_set(es, percpu_counter_sum_positive( - &EXT4_SB(sb)->s_freeblocks_counter)); + ext4_free_blocks_count_set(es, + EXT4_C2B(EXT4_SB(sb), percpu_counter_sum_positive( + &EXT4_SB(sb)->s_freeclusters_counter))); es->s_free_inodes_count = cpu_to_le32(percpu_counter_sum_positive( &EXT4_SB(sb)->s_freeinodes_counter)); @@ -4629,10 +4630,10 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_type = EXT4_SUPER_MAGIC; buf->f_bsize = sb->s_blocksize; buf->f_blocks = ext4_blocks_count(es) - sbi->s_overhead_last; - bfree = percpu_counter_sum_positive(&sbi->s_freeblocks_counter) - - percpu_counter_sum_positive(&sbi->s_dirtyblocks_counter); + bfree = percpu_counter_sum_positive(&sbi->s_freeclusters_counter) - + percpu_counter_sum_positive(&sbi->s_dirtyclusters_counter); /* prevent underflow in case that few free space is available */ - buf->f_bfree = max_t(s64, bfree, 0); + buf->f_bfree = EXT4_C2B(sbi, max_t(s64, bfree, 0)); buf->f_bavail = buf->f_bfree - ext4_r_blocks_count(es); if (buf->f_bfree < ext4_r_blocks_count(es)) buf->f_bavail = 0; From 24aaa8ef4e2b5764ada1fc69787e2fbd4f6276e5 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 9 Sep 2011 18:58:51 -0400 Subject: [PATCH 0128/1531] ext4: convert the free_blocks field in s_flex_groups to be free_clusters Convert the free_blocks to be free_clusters to make the final revised bigalloc changes easier to read/understand. Signed-off-by: "Theodore Ts'o" --- fs/ext4/ext4.h | 2 +- fs/ext4/ialloc.c | 40 +++++++++++++++++++++------------------- fs/ext4/mballoc.c | 9 +++++---- fs/ext4/resize.c | 4 ++-- fs/ext4/super.c | 2 +- 5 files changed, 30 insertions(+), 27 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index c7588366471c..d2584224c89a 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -300,7 +300,7 @@ struct ext4_group_desc struct flex_groups { atomic_t free_inodes; - atomic_t free_blocks; + atomic_t free_clusters; atomic_t used_dirs; }; diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index 58115bad163f..0be5862313f0 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -346,7 +346,7 @@ static int find_group_flex(struct super_block *sb, struct inode *parent, int flex_size = ext4_flex_bg_size(sbi); ext4_group_t best_flex = parent_fbg_group; int blocks_per_flex = sbi->s_blocks_per_group * flex_size; - int flexbg_free_blocks; + int flexbg_free_clusters; int flex_freeb_ratio; ext4_group_t n_fbg_groups; ext4_group_t i; @@ -355,8 +355,9 @@ static int find_group_flex(struct super_block *sb, struct inode *parent, sbi->s_log_groups_per_flex; find_close_to_parent: - flexbg_free_blocks = atomic_read(&flex_group[best_flex].free_blocks); - flex_freeb_ratio = flexbg_free_blocks * 100 / blocks_per_flex; + flexbg_free_clusters = atomic_read(&flex_group[best_flex].free_clusters); + flex_freeb_ratio = EXT4_C2B(sbi, flexbg_free_clusters) * 100 / + blocks_per_flex; if (atomic_read(&flex_group[best_flex].free_inodes) && flex_freeb_ratio > free_block_ratio) goto found_flexbg; @@ -370,8 +371,9 @@ static int find_group_flex(struct super_block *sb, struct inode *parent, if (i == parent_fbg_group || i == parent_fbg_group - 1) continue; - flexbg_free_blocks = atomic_read(&flex_group[i].free_blocks); - flex_freeb_ratio = flexbg_free_blocks * 100 / blocks_per_flex; + flexbg_free_clusters = atomic_read(&flex_group[i].free_clusters); + flex_freeb_ratio = EXT4_C2B(sbi, flexbg_free_clusters) * 100 / + blocks_per_flex; if (flex_freeb_ratio > free_block_ratio && (atomic_read(&flex_group[i].free_inodes))) { @@ -380,14 +382,14 @@ static int find_group_flex(struct super_block *sb, struct inode *parent, } if ((atomic_read(&flex_group[best_flex].free_inodes) == 0) || - ((atomic_read(&flex_group[i].free_blocks) > - atomic_read(&flex_group[best_flex].free_blocks)) && + ((atomic_read(&flex_group[i].free_clusters) > + atomic_read(&flex_group[best_flex].free_clusters)) && atomic_read(&flex_group[i].free_inodes))) best_flex = i; } if (!atomic_read(&flex_group[best_flex].free_inodes) || - !atomic_read(&flex_group[best_flex].free_blocks)) + !atomic_read(&flex_group[best_flex].free_clusters)) return -1; found_flexbg: @@ -407,7 +409,7 @@ static int find_group_flex(struct super_block *sb, struct inode *parent, struct orlov_stats { __u32 free_inodes; - __u32 free_blocks; + __u32 free_clusters; __u32 used_dirs; }; @@ -424,7 +426,7 @@ static void get_orlov_stats(struct super_block *sb, ext4_group_t g, if (flex_size > 1) { stats->free_inodes = atomic_read(&flex_group[g].free_inodes); - stats->free_blocks = atomic_read(&flex_group[g].free_blocks); + stats->free_clusters = atomic_read(&flex_group[g].free_clusters); stats->used_dirs = atomic_read(&flex_group[g].used_dirs); return; } @@ -432,11 +434,11 @@ static void get_orlov_stats(struct super_block *sb, ext4_group_t g, desc = ext4_get_group_desc(sb, g, NULL); if (desc) { stats->free_inodes = ext4_free_inodes_count(sb, desc); - stats->free_blocks = ext4_free_blks_count(sb, desc); + stats->free_clusters = ext4_free_blks_count(sb, desc); stats->used_dirs = ext4_used_dirs_count(sb, desc); } else { stats->free_inodes = 0; - stats->free_blocks = 0; + stats->free_clusters = 0; stats->used_dirs = 0; } } @@ -471,10 +473,10 @@ static int find_group_orlov(struct super_block *sb, struct inode *parent, ext4_group_t real_ngroups = ext4_get_groups_count(sb); int inodes_per_group = EXT4_INODES_PER_GROUP(sb); unsigned int freei, avefreei; - ext4_fsblk_t freeb, avefreeb; + ext4_fsblk_t freeb, avefreec; unsigned int ndirs; int max_dirs, min_inodes; - ext4_grpblk_t min_blocks; + ext4_grpblk_t min_clusters; ext4_group_t i, grp, g, ngroups; struct ext4_group_desc *desc; struct orlov_stats stats; @@ -492,8 +494,8 @@ static int find_group_orlov(struct super_block *sb, struct inode *parent, avefreei = freei / ngroups; freeb = EXT4_C2B(sbi, percpu_counter_read_positive(&sbi->s_freeclusters_counter)); - avefreeb = freeb; - do_div(avefreeb, ngroups); + avefreec = freeb; + do_div(avefreec, ngroups); ndirs = percpu_counter_read_positive(&sbi->s_dirs_counter); if (S_ISDIR(mode) && @@ -519,7 +521,7 @@ static int find_group_orlov(struct super_block *sb, struct inode *parent, continue; if (stats.free_inodes < avefreei) continue; - if (stats.free_blocks < avefreeb) + if (stats.free_clusters < avefreec) continue; grp = g; ret = 0; @@ -557,7 +559,7 @@ static int find_group_orlov(struct super_block *sb, struct inode *parent, min_inodes = avefreei - inodes_per_group*flex_size / 4; if (min_inodes < 1) min_inodes = 1; - min_blocks = avefreeb - EXT4_BLOCKS_PER_GROUP(sb)*flex_size / 4; + min_clusters = avefreec - EXT4_CLUSTERS_PER_GROUP(sb)*flex_size / 4; /* * Start looking in the flex group where we last allocated an @@ -576,7 +578,7 @@ static int find_group_orlov(struct super_block *sb, struct inode *parent, continue; if (stats.free_inodes < min_inodes) continue; - if (stats.free_blocks < min_blocks) + if (stats.free_clusters < min_clusters) continue; goto found_flex_bg; } diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 4a38b65bd564..f8e37cf2c2dd 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -2847,7 +2847,7 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac, ext4_group_t flex_group = ext4_flex_group(sbi, ac->ac_b_ex.fe_group); atomic_sub(ac->ac_b_ex.fe_len, - &sbi->s_flex_groups[flex_group].free_blocks); + &sbi->s_flex_groups[flex_group].free_clusters); } err = ext4_handle_dirty_metadata(handle, NULL, bitmap_bh); @@ -4696,7 +4696,8 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode, if (sbi->s_log_groups_per_flex) { ext4_group_t flex_group = ext4_flex_group(sbi, block_group); - atomic_add(count, &sbi->s_flex_groups[flex_group].free_blocks); + atomic_add(count_clusters, + &sbi->s_flex_groups[flex_group].free_clusters); } ext4_mb_unload_buddy(&e4b); @@ -4839,8 +4840,8 @@ int ext4_group_add_blocks(handle_t *handle, struct super_block *sb, if (sbi->s_log_groups_per_flex) { ext4_group_t flex_group = ext4_flex_group(sbi, block_group); - atomic_add(blocks_freed, - &sbi->s_flex_groups[flex_group].free_blocks); + atomic_add(EXT4_B2C(sbi, blocks_freed), + &sbi->s_flex_groups[flex_group].free_clusters); } ext4_mb_unload_buddy(&e4b); diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c index a324a537f2dc..95a09ddca3b9 100644 --- a/fs/ext4/resize.c +++ b/fs/ext4/resize.c @@ -946,8 +946,8 @@ int ext4_group_add(struct super_block *sb, struct ext4_new_group_data *input) sbi->s_log_groups_per_flex) { ext4_group_t flex_group; flex_group = ext4_flex_group(sbi, input->group); - atomic_add(input->free_blocks_count, - &sbi->s_flex_groups[flex_group].free_blocks); + atomic_add(EXT4_B2C(sbi, input->free_blocks_count), + &sbi->s_flex_groups[flex_group].free_clusters); atomic_add(EXT4_INODES_PER_GROUP(sb), &sbi->s_flex_groups[flex_group].free_inodes); } diff --git a/fs/ext4/super.c b/fs/ext4/super.c index f81e7e791655..d7e0e045b11b 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -2035,7 +2035,7 @@ static int ext4_fill_flex_info(struct super_block *sb) atomic_add(ext4_free_inodes_count(sb, gdp), &sbi->s_flex_groups[flex_group].free_inodes); atomic_add(ext4_free_blks_count(sb, gdp), - &sbi->s_flex_groups[flex_group].free_blocks); + &sbi->s_flex_groups[flex_group].free_clusters); atomic_add(ext4_used_dirs_count(sb, gdp), &sbi->s_flex_groups[flex_group].used_dirs); } From f975d6bcc7a698a10cc755115e27d3612dcfe322 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 9 Sep 2011 19:00:51 -0400 Subject: [PATCH 0129/1531] ext4: teach ext4_statfs() to deal with clusters if bigalloc is enabled Signed-off-by: "Theodore Ts'o" --- fs/ext4/super.c | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index d7e0e045b11b..6810957e0ac7 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -4580,16 +4580,34 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) return err; } +/* + * Note: calculating the overhead so we can be compatible with + * historical BSD practice is quite difficult in the face of + * clusters/bigalloc. This is because multiple metadata blocks from + * different block group can end up in the same allocation cluster. + * Calculating the exact overhead in the face of clustered allocation + * requires either O(all block bitmaps) in memory or O(number of block + * groups**2) in time. We will still calculate the superblock for + * older file systems --- and if we come across with a bigalloc file + * system with zero in s_overhead_clusters the estimate will be close to + * correct especially for very large cluster sizes --- but for newer + * file systems, it's better to calculate this figure once at mkfs + * time, and store it in the superblock. If the superblock value is + * present (even for non-bigalloc file systems), we will use it. + */ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf) { struct super_block *sb = dentry->d_sb; struct ext4_sb_info *sbi = EXT4_SB(sb); struct ext4_super_block *es = sbi->s_es; + struct ext4_group_desc *gdp; u64 fsid; s64 bfree; if (test_opt(sb, MINIX_DF)) { sbi->s_overhead_last = 0; + } else if (es->s_overhead_clusters) { + sbi->s_overhead_last = le32_to_cpu(es->s_overhead_clusters); } else if (sbi->s_blocks_last != ext4_blocks_count(es)) { ext4_group_t i, ngroups = ext4_get_groups_count(sb); ext4_fsblk_t overhead = 0; @@ -4604,24 +4622,16 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf) * All of the blocks before first_data_block are * overhead */ - overhead = le32_to_cpu(es->s_first_data_block); + overhead = EXT4_B2C(sbi, le32_to_cpu(es->s_first_data_block)); /* - * Add the overhead attributed to the superblock and - * block group descriptors. If the sparse superblocks - * feature is turned on, then not all groups have this. + * Add the overhead found in each block group */ for (i = 0; i < ngroups; i++) { - overhead += ext4_bg_has_super(sb, i) + - ext4_bg_num_gdb(sb, i); + gdp = ext4_get_group_desc(sb, i, NULL); + overhead += ext4_num_overhead_clusters(sb, i, gdp); cond_resched(); } - - /* - * Every block group has an inode bitmap, a block - * bitmap, and an inode table. - */ - overhead += ngroups * (2 + sbi->s_itb_per_group); sbi->s_overhead_last = overhead; smp_wmb(); sbi->s_blocks_last = ext4_blocks_count(es); @@ -4629,7 +4639,8 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_type = EXT4_SUPER_MAGIC; buf->f_bsize = sb->s_blocksize; - buf->f_blocks = ext4_blocks_count(es) - sbi->s_overhead_last; + buf->f_blocks = (ext4_blocks_count(es) - + EXT4_C2B(sbi, sbi->s_overhead_last)); bfree = percpu_counter_sum_positive(&sbi->s_freeclusters_counter) - percpu_counter_sum_positive(&sbi->s_dirtyclusters_counter); /* prevent underflow in case that few free space is available */ From 27baebb849d46d901e756e6502b0a65a62e43771 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 9 Sep 2011 19:02:51 -0400 Subject: [PATCH 0130/1531] ext4: tune mballoc's default group prealloc size for bigalloc file systems The default group preallocation size had been previously set to 512 blocks/clusters, regardless of the block/cluster size. This is probably too big for large cluster sizes. So adjust the default so that it is 2 megabytes or 32 clusters, whichever is larger. Signed-off-by: "Theodore Ts'o" --- fs/ext4/mballoc.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index f8e37cf2c2dd..63dd56703342 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -126,7 +126,8 @@ * list. In case of inode preallocation we follow a list of heuristics * based on file size. This can be found in ext4_mb_normalize_request. If * we are doing a group prealloc we try to normalize the request to - * sbi->s_mb_group_prealloc. Default value of s_mb_group_prealloc is + * sbi->s_mb_group_prealloc. The default value of s_mb_group_prealloc is + * dependent on the cluster size; for non-bigalloc file systems, it is * 512 blocks. This can be tuned via * /sys/fs/ext4//mb_group_prealloc. The value is represented in * terms of number of blocks. If we have mounted the file system with -O @@ -2473,7 +2474,20 @@ int ext4_mb_init(struct super_block *sb, int needs_recovery) sbi->s_mb_stats = MB_DEFAULT_STATS; sbi->s_mb_stream_request = MB_DEFAULT_STREAM_THRESHOLD; sbi->s_mb_order2_reqs = MB_DEFAULT_ORDER2_REQS; - sbi->s_mb_group_prealloc = MB_DEFAULT_GROUP_PREALLOC; + /* + * The default group preallocation is 512, which for 4k block + * sizes translates to 2 megabytes. However for bigalloc file + * systems, this is probably too big (i.e, if the cluster size + * is 1 megabyte, then group preallocation size becomes half a + * gigabyte!). As a default, we will keep a two megabyte + * group pralloc size for cluster sizes up to 64k, and after + * that, we will force a minimum group preallocation size of + * 32 clusters. This translates to 8 megs when the cluster + * size is 256k, and 32 megs when the cluster size is 1 meg, + * which seems reasonable as a default. + */ + sbi->s_mb_group_prealloc = max(MB_DEFAULT_GROUP_PREALLOC >> + sbi->s_cluster_bits, 32); /* * If there is a s_stripe > 1, then we set the s_mb_group_prealloc * to the lowest multiple of s_stripe which is bigger than From 7b415bf60f6afb0499fd3dc0ee33444f54e28567 Mon Sep 17 00:00:00 2001 From: Aditya Kali Date: Fri, 9 Sep 2011 19:04:51 -0400 Subject: [PATCH 0131/1531] ext4: Fix bigalloc quota accounting and i_blocks value With bigalloc changes, the i_blocks value was not correctly set (it was still set to number of blocks being used, but in case of bigalloc, we want i_blocks to represent the number of clusters being used). Since the quota subsystem sets the i_blocks value, this patch fixes the quota accounting and makes sure that the i_blocks value is set correctly. Signed-off-by: Aditya Kali Signed-off-by: "Theodore Ts'o" --- fs/ext4/balloc.c | 5 +- fs/ext4/ext4.h | 16 ++- fs/ext4/ext4_extents.h | 2 + fs/ext4/extents.c | 306 ++++++++++++++++++++++++++++++++++++++++- fs/ext4/inode.c | 54 ++++++-- fs/ext4/mballoc.c | 5 +- fs/ext4/super.c | 3 +- 7 files changed, 366 insertions(+), 25 deletions(-) diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index 9080a857cda9..bf42b3219e3c 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -485,7 +485,7 @@ int ext4_should_retry_alloc(struct super_block *sb, int *retries) * @handle: handle to this transaction * @inode: file inode * @goal: given target block(filesystem wide) - * @count: pointer to total number of blocks needed + * @count: pointer to total number of clusters needed * @errp: error code * * Return 1st allocated block number on success, *count stores total account @@ -517,7 +517,8 @@ ext4_fsblk_t ext4_new_meta_blocks(handle_t *handle, struct inode *inode, spin_lock(&EXT4_I(inode)->i_block_reservation_lock); EXT4_I(inode)->i_allocated_meta_blocks += ar.len; spin_unlock(&EXT4_I(inode)->i_block_reservation_lock); - dquot_alloc_block_nofail(inode, ar.len); + dquot_alloc_block_nofail(inode, + EXT4_C2B(EXT4_SB(inode->i_sb), ar.len)); } return ret; } diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index d2584224c89a..a6307f7c9807 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -144,9 +144,17 @@ struct ext4_allocation_request { #define EXT4_MAP_UNWRITTEN (1 << BH_Unwritten) #define EXT4_MAP_BOUNDARY (1 << BH_Boundary) #define EXT4_MAP_UNINIT (1 << BH_Uninit) +/* Sometimes (in the bigalloc case, from ext4_da_get_block_prep) the caller of + * ext4_map_blocks wants to know whether or not the underlying cluster has + * already been accounted for. EXT4_MAP_FROM_CLUSTER conveys to the caller that + * the requested mapping was from previously mapped (or delayed allocated) + * cluster. We use BH_AllocFromCluster only for this flag. BH_AllocFromCluster + * should never appear on buffer_head's state flags. + */ +#define EXT4_MAP_FROM_CLUSTER (1 << BH_AllocFromCluster) #define EXT4_MAP_FLAGS (EXT4_MAP_NEW | EXT4_MAP_MAPPED |\ EXT4_MAP_UNWRITTEN | EXT4_MAP_BOUNDARY |\ - EXT4_MAP_UNINIT) + EXT4_MAP_UNINIT | EXT4_MAP_FROM_CLUSTER) struct ext4_map_blocks { ext4_fsblk_t m_pblk; @@ -1884,6 +1892,7 @@ extern int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf); extern qsize_t *ext4_get_reserved_space(struct inode *inode); extern void ext4_da_update_reserve_space(struct inode *inode, int used, int quota_claim); +extern int ext4_da_reserve_space(struct inode *inode, ext4_lblk_t lblock); /* indirect.c */ extern int ext4_ind_map_blocks(handle_t *handle, struct inode *inode, @@ -2284,6 +2293,11 @@ extern int ext4_multi_mount_protect(struct super_block *, ext4_fsblk_t); enum ext4_state_bits { BH_Uninit /* blocks are allocated but uninitialized on disk */ = BH_JBDPrivateStart, + BH_AllocFromCluster, /* allocated blocks were part of already + * allocated cluster. Note that this flag will + * never, ever appear in a buffer_head's state + * flag. See EXT4_MAP_FROM_CLUSTER to see where + * this is used. */ }; BUFFER_FNS(Uninit, uninit) diff --git a/fs/ext4/ext4_extents.h b/fs/ext4/ext4_extents.h index 095c36f3b612..a52db3a69a30 100644 --- a/fs/ext4/ext4_extents.h +++ b/fs/ext4/ext4_extents.h @@ -290,5 +290,7 @@ extern struct ext4_ext_path *ext4_ext_find_extent(struct inode *, ext4_lblk_t, struct ext4_ext_path *); extern void ext4_ext_drop_refs(struct ext4_ext_path *); extern int ext4_ext_check_inode(struct inode *inode); +extern int ext4_find_delalloc_cluster(struct inode *inode, ext4_lblk_t lblk, + int search_hint_reverse); #endif /* _EXT4_EXTENTS */ diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index cd4479c08031..c4e005864534 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -2686,6 +2686,21 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start) } } + /* If we still have something in the partial cluster and we have removed + * even the first extent, then we should free the blocks in the partial + * cluster as well. */ + if (partial_cluster && path->p_hdr->eh_entries == 0) { + int flags = EXT4_FREE_BLOCKS_FORGET; + + if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) + flags |= EXT4_FREE_BLOCKS_METADATA; + + ext4_free_blocks(handle, inode, NULL, + EXT4_C2B(EXT4_SB(sb), partial_cluster), + EXT4_SB(sb)->s_cluster_ratio, flags); + partial_cluster = 0; + } + /* TODO: flexible tree reduction should be here */ if (path->p_hdr->eh_entries == 0) { /* @@ -3233,6 +3248,195 @@ static int check_eofblocks_fl(handle_t *handle, struct inode *inode, return ext4_mark_inode_dirty(handle, inode); } +/** + * ext4_find_delalloc_range: find delayed allocated block in the given range. + * + * Goes through the buffer heads in the range [lblk_start, lblk_end] and returns + * whether there are any buffers marked for delayed allocation. It returns '1' + * on the first delalloc'ed buffer head found. If no buffer head in the given + * range is marked for delalloc, it returns 0. + * lblk_start should always be <= lblk_end. + * search_hint_reverse is to indicate that searching in reverse from lblk_end to + * lblk_start might be more efficient (i.e., we will likely hit the delalloc'ed + * block sooner). This is useful when blocks are truncated sequentially from + * lblk_start towards lblk_end. + */ +static int ext4_find_delalloc_range(struct inode *inode, + ext4_lblk_t lblk_start, + ext4_lblk_t lblk_end, + int search_hint_reverse) +{ + struct address_space *mapping = inode->i_mapping; + struct buffer_head *head, *bh = NULL; + struct page *page; + ext4_lblk_t i, pg_lblk; + pgoff_t index; + + /* reverse search wont work if fs block size is less than page size */ + if (inode->i_blkbits < PAGE_CACHE_SHIFT) + search_hint_reverse = 0; + + if (search_hint_reverse) + i = lblk_end; + else + i = lblk_start; + + index = i >> (PAGE_CACHE_SHIFT - inode->i_blkbits); + + while ((i >= lblk_start) && (i <= lblk_end)) { + page = find_get_page(mapping, index); + if (!page || !PageDirty(page)) + goto nextpage; + + if (PageWriteback(page)) { + /* + * This might be a race with allocation and writeout. In + * this case we just assume that the rest of the range + * will eventually be written and there wont be any + * delalloc blocks left. + * TODO: the above assumption is troublesome, but might + * work better in practice. other option could be note + * somewhere that the cluster is getting written out and + * detect that here. + */ + page_cache_release(page); + return 0; + } + + if (!page_has_buffers(page)) + goto nextpage; + + head = page_buffers(page); + if (!head) + goto nextpage; + + bh = head; + pg_lblk = index << (PAGE_CACHE_SHIFT - + inode->i_blkbits); + do { + if (unlikely(pg_lblk < lblk_start)) { + /* + * This is possible when fs block size is less + * than page size and our cluster starts/ends in + * middle of the page. So we need to skip the + * initial few blocks till we reach the 'lblk' + */ + pg_lblk++; + continue; + } + + if (buffer_delay(bh)) { + page_cache_release(page); + return 1; + } + if (search_hint_reverse) + i--; + else + i++; + } while ((i >= lblk_start) && (i <= lblk_end) && + ((bh = bh->b_this_page) != head)); +nextpage: + if (page) + page_cache_release(page); + /* + * Move to next page. 'i' will be the first lblk in the next + * page. + */ + if (search_hint_reverse) + index--; + else + index++; + i = index << (PAGE_CACHE_SHIFT - inode->i_blkbits); + } + + return 0; +} + +int ext4_find_delalloc_cluster(struct inode *inode, ext4_lblk_t lblk, + int search_hint_reverse) +{ + struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); + ext4_lblk_t lblk_start, lblk_end; + lblk_start = lblk & (~(sbi->s_cluster_ratio - 1)); + lblk_end = lblk_start + sbi->s_cluster_ratio - 1; + + return ext4_find_delalloc_range(inode, lblk_start, lblk_end, + search_hint_reverse); +} + +/** + * Determines how many complete clusters (out of those specified by the 'map') + * are under delalloc and were reserved quota for. + * This function is called when we are writing out the blocks that were + * originally written with their allocation delayed, but then the space was + * allocated using fallocate() before the delayed allocation could be resolved. + * The cases to look for are: + * ('=' indicated delayed allocated blocks + * '-' indicates non-delayed allocated blocks) + * (a) partial clusters towards beginning and/or end outside of allocated range + * are not delalloc'ed. + * Ex: + * |----c---=|====c====|====c====|===-c----| + * |++++++ allocated ++++++| + * ==> 4 complete clusters in above example + * + * (b) partial cluster (outside of allocated range) towards either end is + * marked for delayed allocation. In this case, we will exclude that + * cluster. + * Ex: + * |----====c========|========c========| + * |++++++ allocated ++++++| + * ==> 1 complete clusters in above example + * + * Ex: + * |================c================| + * |++++++ allocated ++++++| + * ==> 0 complete clusters in above example + * + * The ext4_da_update_reserve_space will be called only if we + * determine here that there were some "entire" clusters that span + * this 'allocated' range. + * In the non-bigalloc case, this function will just end up returning num_blks + * without ever calling ext4_find_delalloc_range. + */ +static unsigned int +get_reserved_cluster_alloc(struct inode *inode, ext4_lblk_t lblk_start, + unsigned int num_blks) +{ + struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); + ext4_lblk_t alloc_cluster_start, alloc_cluster_end; + ext4_lblk_t lblk_from, lblk_to, c_offset; + unsigned int allocated_clusters = 0; + + alloc_cluster_start = EXT4_B2C(sbi, lblk_start); + alloc_cluster_end = EXT4_B2C(sbi, lblk_start + num_blks - 1); + + /* max possible clusters for this allocation */ + allocated_clusters = alloc_cluster_end - alloc_cluster_start + 1; + + /* Check towards left side */ + c_offset = lblk_start & (sbi->s_cluster_ratio - 1); + if (c_offset) { + lblk_from = lblk_start & (~(sbi->s_cluster_ratio - 1)); + lblk_to = lblk_from + c_offset - 1; + + if (ext4_find_delalloc_range(inode, lblk_from, lblk_to, 0)) + allocated_clusters--; + } + + /* Now check towards right. */ + c_offset = (lblk_start + num_blks) & (sbi->s_cluster_ratio - 1); + if (allocated_clusters && c_offset) { + lblk_from = lblk_start + num_blks; + lblk_to = lblk_from + (sbi->s_cluster_ratio - c_offset) - 1; + + if (ext4_find_delalloc_range(inode, lblk_from, lblk_to, 0)) + allocated_clusters--; + } + + return allocated_clusters; +} + static int ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode, struct ext4_map_blocks *map, @@ -3338,8 +3542,15 @@ ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode, * But fallocate would have already updated quota and block * count for this offset. So cancel these reservation */ - if (flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) - ext4_da_update_reserve_space(inode, allocated, 0); + if (flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) { + unsigned int reserved_clusters; + reserved_clusters = get_reserved_cluster_alloc(inode, + map->m_lblk, map->m_len); + if (reserved_clusters) + ext4_da_update_reserve_space(inode, + reserved_clusters, + 0); + } map_out: map->m_flags |= EXT4_MAP_MAPPED; @@ -3484,6 +3695,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, ext4_fsblk_t newblock = 0; int free_on_err = 0, err = 0, depth, ret; unsigned int allocated = 0, offset = 0; + unsigned int allocated_clusters = 0, reserved_clusters = 0; unsigned int punched_out = 0; unsigned int result = 0; struct ext4_allocation_request ar; @@ -3499,6 +3711,10 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, if (!(flags & EXT4_GET_BLOCKS_PUNCH_OUT_EXT) && ext4_ext_in_cache(inode, map->m_lblk, &newex)) { if (!newex.ee_start_lo && !newex.ee_start_hi) { + if ((sbi->s_cluster_ratio > 1) && + ext4_find_delalloc_cluster(inode, map->m_lblk, 0)) + map->m_flags |= EXT4_MAP_FROM_CLUSTER; + if ((flags & EXT4_GET_BLOCKS_CREATE) == 0) { /* * block isn't allocated yet and @@ -3509,6 +3725,8 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, /* we should allocate requested block */ } else { /* block is already allocated */ + if (sbi->s_cluster_ratio > 1) + map->m_flags |= EXT4_MAP_FROM_CLUSTER; newblock = map->m_lblk - le32_to_cpu(newex.ee_block) + ext4_ext_pblock(&newex); @@ -3665,6 +3883,10 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, } } + if ((sbi->s_cluster_ratio > 1) && + ext4_find_delalloc_cluster(inode, map->m_lblk, 0)) + map->m_flags |= EXT4_MAP_FROM_CLUSTER; + /* * requested block isn't allocated yet; * we couldn't try to create block if create flag is zero @@ -3681,6 +3903,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, /* * Okay, we need to do block allocation. */ + map->m_flags &= ~EXT4_MAP_FROM_CLUSTER; newex.ee_block = cpu_to_le32(map->m_lblk); cluster_offset = map->m_lblk & (sbi->s_cluster_ratio-1); @@ -3692,6 +3915,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, get_implied_cluster_alloc(sbi, map, ex, path)) { ar.len = allocated = map->m_len; newblock = map->m_pblk; + map->m_flags |= EXT4_MAP_FROM_CLUSTER; goto got_allocated_blocks; } @@ -3712,6 +3936,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, get_implied_cluster_alloc(sbi, map, ex2, path)) { ar.len = allocated = map->m_len; newblock = map->m_pblk; + map->m_flags |= EXT4_MAP_FROM_CLUSTER; goto got_allocated_blocks; } @@ -3765,6 +3990,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, ext_debug("allocate new block: goal %llu, found %llu/%u\n", ar.goal, newblock, allocated); free_on_err = 1; + allocated_clusters = ar.len; ar.len = EXT4_C2B(sbi, ar.len) - offset; if (ar.len > allocated) ar.len = allocated; @@ -3822,8 +4048,80 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, * Update reserved blocks/metadata blocks after successful * block allocation which had been deferred till now. */ - if (flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) - ext4_da_update_reserve_space(inode, allocated, 1); + if (flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) { + /* + * Check how many clusters we had reserved this allocted range. + */ + reserved_clusters = get_reserved_cluster_alloc(inode, + map->m_lblk, allocated); + if (map->m_flags & EXT4_MAP_FROM_CLUSTER) { + if (reserved_clusters) { + /* + * We have clusters reserved for this range. + * But since we are not doing actual allocation + * and are simply using blocks from previously + * allocated cluster, we should release the + * reservation and not claim quota. + */ + ext4_da_update_reserve_space(inode, + reserved_clusters, 0); + } + } else { + BUG_ON(allocated_clusters < reserved_clusters); + /* We will claim quota for all newly allocated blocks.*/ + ext4_da_update_reserve_space(inode, allocated_clusters, + 1); + if (reserved_clusters < allocated_clusters) { + int reservation = allocated_clusters - + reserved_clusters; + /* + * It seems we claimed few clusters outside of + * the range of this allocation. We should give + * it back to the reservation pool. This can + * happen in the following case: + * + * * Suppose s_cluster_ratio is 4 (i.e., each + * cluster has 4 blocks. Thus, the clusters + * are [0-3],[4-7],[8-11]... + * * First comes delayed allocation write for + * logical blocks 10 & 11. Since there were no + * previous delayed allocated blocks in the + * range [8-11], we would reserve 1 cluster + * for this write. + * * Next comes write for logical blocks 3 to 8. + * In this case, we will reserve 2 clusters + * (for [0-3] and [4-7]; and not for [8-11] as + * that range has a delayed allocated blocks. + * Thus total reserved clusters now becomes 3. + * * Now, during the delayed allocation writeout + * time, we will first write blocks [3-8] and + * allocate 3 clusters for writing these + * blocks. Also, we would claim all these + * three clusters above. + * * Now when we come here to writeout the + * blocks [10-11], we would expect to claim + * the reservation of 1 cluster we had made + * (and we would claim it since there are no + * more delayed allocated blocks in the range + * [8-11]. But our reserved cluster count had + * already gone to 0. + * + * Thus, at the step 4 above when we determine + * that there are still some unwritten delayed + * allocated blocks outside of our current + * block range, we should increment the + * reserved clusters count so that when the + * remaining blocks finally gets written, we + * could claim them. + */ + while (reservation) { + ext4_da_reserve_space(inode, + map->m_lblk); + reservation--; + } + } + } + } /* * Cache the extent and update transaction to commit on fdatasync only diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 40f51aae42fe..d1c17e47c1c6 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -300,14 +300,14 @@ void ext4_da_update_reserve_space(struct inode *inode, /* Update quota subsystem for data blocks */ if (quota_claim) - dquot_claim_block(inode, used); + dquot_claim_block(inode, EXT4_C2B(sbi, used)); else { /* * We did fallocate with an offset that is already delayed * allocated. So on delayed allocated writeback we should * not re-claim the quota for fallocated blocks. */ - dquot_release_reservation_block(inode, used); + dquot_release_reservation_block(inode, EXT4_C2B(sbi, used)); } /* @@ -1037,14 +1037,14 @@ static int ext4_journalled_write_end(struct file *file, } /* - * Reserve a single block located at lblock + * Reserve a single cluster located at lblock */ -static int ext4_da_reserve_space(struct inode *inode, ext4_lblk_t lblock) +int ext4_da_reserve_space(struct inode *inode, ext4_lblk_t lblock) { int retries = 0; struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); struct ext4_inode_info *ei = EXT4_I(inode); - unsigned long md_needed; + unsigned int md_needed; int ret; /* @@ -1054,7 +1054,8 @@ static int ext4_da_reserve_space(struct inode *inode, ext4_lblk_t lblock) */ repeat: spin_lock(&ei->i_block_reservation_lock); - md_needed = ext4_calc_metadata_amount(inode, lblock); + md_needed = EXT4_NUM_B2C(sbi, + ext4_calc_metadata_amount(inode, lblock)); trace_ext4_da_reserve_space(inode, md_needed); spin_unlock(&ei->i_block_reservation_lock); @@ -1063,7 +1064,7 @@ static int ext4_da_reserve_space(struct inode *inode, ext4_lblk_t lblock) * us from metadata over-estimation, though we may go over by * a small amount in the end. Here we just reserve for data. */ - ret = dquot_reserve_block(inode, 1); + ret = dquot_reserve_block(inode, EXT4_C2B(sbi, 1)); if (ret) return ret; /* @@ -1071,7 +1072,7 @@ static int ext4_da_reserve_space(struct inode *inode, ext4_lblk_t lblock) * we cannot afford to run out of free blocks. */ if (ext4_claim_free_blocks(sbi, md_needed + 1, 0)) { - dquot_release_reservation_block(inode, 1); + dquot_release_reservation_block(inode, EXT4_C2B(sbi, 1)); if (ext4_should_retry_alloc(inode->i_sb, &retries)) { yield(); goto repeat; @@ -1118,6 +1119,8 @@ static void ext4_da_release_space(struct inode *inode, int to_free) * We can release all of the reserved metadata blocks * only when we have written all of the delayed * allocation blocks. + * Note that in case of bigalloc, i_reserved_meta_blocks, + * i_reserved_data_blocks, etc. refer to number of clusters. */ percpu_counter_sub(&sbi->s_dirtyclusters_counter, ei->i_reserved_meta_blocks); @@ -1130,7 +1133,7 @@ static void ext4_da_release_space(struct inode *inode, int to_free) spin_unlock(&EXT4_I(inode)->i_block_reservation_lock); - dquot_release_reservation_block(inode, to_free); + dquot_release_reservation_block(inode, EXT4_C2B(sbi, to_free)); } static void ext4_da_page_release_reservation(struct page *page, @@ -1139,6 +1142,9 @@ static void ext4_da_page_release_reservation(struct page *page, int to_release = 0; struct buffer_head *head, *bh; unsigned int curr_off = 0; + struct inode *inode = page->mapping->host; + struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); + int num_clusters; head = page_buffers(page); bh = head; @@ -1151,7 +1157,20 @@ static void ext4_da_page_release_reservation(struct page *page, } curr_off = next_off; } while ((bh = bh->b_this_page) != head); - ext4_da_release_space(page->mapping->host, to_release); + + /* If we have released all the blocks belonging to a cluster, then we + * need to release the reserved space for that cluster. */ + num_clusters = EXT4_NUM_B2C(sbi, to_release); + while (num_clusters > 0) { + ext4_fsblk_t lblk; + lblk = (page->index << (PAGE_CACHE_SHIFT - inode->i_blkbits)) + + ((num_clusters - 1) << sbi->s_cluster_bits); + if (sbi->s_cluster_ratio == 1 || + !ext4_find_delalloc_cluster(inode, lblk, 1)) + ext4_da_release_space(inode, 1); + + num_clusters--; + } } /* @@ -1352,7 +1371,8 @@ static void ext4_print_free_blocks(struct inode *inode) (long long) EXT4_C2B(EXT4_SB(inode->i_sb), percpu_counter_sum(&sbi->s_freeclusters_counter))); printk(KERN_CRIT "dirty_blocks=%lld\n", - (long long) percpu_counter_sum(&sbi->s_dirtyclusters_counter)); + (long long) EXT4_C2B(EXT4_SB(inode->i_sb), + percpu_counter_sum(&sbi->s_dirtyclusters_counter))); printk(KERN_CRIT "Block reservation details\n"); printk(KERN_CRIT "i_reserved_data_blocks=%u\n", EXT4_I(inode)->i_reserved_data_blocks); @@ -1626,10 +1646,14 @@ static int ext4_da_get_block_prep(struct inode *inode, sector_t iblock, /* * XXX: __block_write_begin() unmaps passed block, is it OK? */ - ret = ext4_da_reserve_space(inode, iblock); - if (ret) - /* not enough space to reserve */ - return ret; + /* If the block was allocated from previously allocated cluster, + * then we dont need to reserve it again. */ + if (!(map.m_flags & EXT4_MAP_FROM_CLUSTER)) { + ret = ext4_da_reserve_space(inode, iblock); + if (ret) + /* not enough space to reserve */ + return ret; + } map_bh(bh, inode->i_sb, invalid_block); set_buffer_new(bh); diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 63dd56703342..5e1215d38331 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -4718,6 +4718,9 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode, freed += count; + if (!(flags & EXT4_FREE_BLOCKS_NO_QUOT_UPDATE)) + dquot_free_block(inode, EXT4_C2B(sbi, count_clusters)); + /* We dirtied the bitmap block */ BUFFER_TRACE(bitmap_bh, "dirtied bitmap block"); err = ext4_handle_dirty_metadata(handle, NULL, bitmap_bh); @@ -4736,8 +4739,6 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode, } ext4_mark_super_dirty(sb); error_return: - if (freed && !(flags & EXT4_FREE_BLOCKS_NO_QUOT_UPDATE)) - dquot_free_block(inode, freed); brelse(bitmap_bh); ext4_std_error(sb, err); return; diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 6810957e0ac7..66b8cfa15636 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -2473,7 +2473,8 @@ static ssize_t delayed_allocation_blocks_show(struct ext4_attr *a, char *buf) { return snprintf(buf, PAGE_SIZE, "%llu\n", - (s64) percpu_counter_sum(&sbi->s_dirtyclusters_counter)); + (s64) EXT4_C2B(sbi, + percpu_counter_sum(&sbi->s_dirtyclusters_counter))); } static ssize_t session_write_kbytes_show(struct ext4_attr *a, From 6f16b60690ba04cf476480a6f19b204e4b95b4a6 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 9 Sep 2011 19:06:51 -0400 Subject: [PATCH 0132/1531] ext4: enable mounting bigalloc as read/write Now that we have implemented all of the changes needed for bigalloc, we can finally enable it! Signed-off-by: "Theodore Ts'o" --- fs/ext4/ext4.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index a6307f7c9807..a5a7e369f719 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1439,7 +1439,8 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei) EXT4_FEATURE_RO_COMPAT_DIR_NLINK | \ EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE | \ EXT4_FEATURE_RO_COMPAT_BTREE_DIR |\ - EXT4_FEATURE_RO_COMPAT_HUGE_FILE) + EXT4_FEATURE_RO_COMPAT_HUGE_FILE |\ + EXT4_FEATURE_RO_COMPAT_BIGALLOC) /* * Default values for user and/or group using reserved blocks From 021b65bb1e4e4b625c80bbb82651e5e155721ef3 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 9 Sep 2011 19:08:51 -0400 Subject: [PATCH 0133/1531] ext4: Rename ext4_free_blks_{count,set}() to refer to clusters The field bg_free_blocks_count_{lo,high} in the block group descriptor has been repurposed to hold the number of free clusters for bigalloc functions. So rename the functions so it makes it easier to read and audit the block allocation and block freeing code. Note: at this point in bigalloc development we doesn't support online resize, so this also makes it really obvious all of the places we need to fix up to add support for online resize. Signed-off-by: "Theodore Ts'o" --- fs/ext4/balloc.c | 8 ++++---- fs/ext4/ext4.h | 9 +++++---- fs/ext4/ialloc.c | 14 +++++++------- fs/ext4/mballoc.c | 22 +++++++++++----------- fs/ext4/resize.c | 2 +- fs/ext4/super.c | 10 +++++----- 6 files changed, 33 insertions(+), 32 deletions(-) diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index bf42b3219e3c..84dc3d62b40c 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -168,7 +168,7 @@ void ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh, * essentially implementing a per-group read-only flag. */ if (!ext4_group_desc_csum_verify(sbi, block_group, gdp)) { ext4_error(sb, "Checksum bad for group %u", block_group); - ext4_free_blks_set(sb, gdp, 0); + ext4_free_group_clusters_set(sb, gdp, 0); ext4_free_inodes_set(sb, gdp, 0); ext4_itable_unused_set(sb, gdp, 0); memset(bh->b_data, 0xff, sb->s_blocksize); @@ -550,7 +550,7 @@ ext4_fsblk_t ext4_count_free_blocks(struct super_block *sb) gdp = ext4_get_group_desc(sb, i, NULL); if (!gdp) continue; - desc_count += ext4_free_blks_count(sb, gdp); + desc_count += ext4_free_group_clusters(sb, gdp); brelse(bitmap_bh); bitmap_bh = ext4_read_block_bitmap(sb, i); if (bitmap_bh == NULL) @@ -558,7 +558,7 @@ ext4_fsblk_t ext4_count_free_blocks(struct super_block *sb) x = ext4_count_free(bitmap_bh, sb->s_blocksize); printk(KERN_DEBUG "group %u: stored = %d, counted = %u\n", - i, ext4_free_blks_count(sb, gdp), x); + i, ext4_free_group_clusters(sb, gdp), x); bitmap_count += x; } brelse(bitmap_bh); @@ -572,7 +572,7 @@ ext4_fsblk_t ext4_count_free_blocks(struct super_block *sb) gdp = ext4_get_group_desc(sb, i, NULL); if (!gdp) continue; - desc_count += ext4_free_blks_count(sb, gdp); + desc_count += ext4_free_group_clusters(sb, gdp); } return desc_count; diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index a5a7e369f719..db279c362017 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1978,8 +1978,8 @@ extern ext4_fsblk_t ext4_inode_bitmap(struct super_block *sb, struct ext4_group_desc *bg); extern ext4_fsblk_t ext4_inode_table(struct super_block *sb, struct ext4_group_desc *bg); -extern __u32 ext4_free_blks_count(struct super_block *sb, - struct ext4_group_desc *bg); +extern __u32 ext4_free_group_clusters(struct super_block *sb, + struct ext4_group_desc *bg); extern __u32 ext4_free_inodes_count(struct super_block *sb, struct ext4_group_desc *bg); extern __u32 ext4_used_dirs_count(struct super_block *sb, @@ -1992,8 +1992,9 @@ extern void ext4_inode_bitmap_set(struct super_block *sb, struct ext4_group_desc *bg, ext4_fsblk_t blk); extern void ext4_inode_table_set(struct super_block *sb, struct ext4_group_desc *bg, ext4_fsblk_t blk); -extern void ext4_free_blks_set(struct super_block *sb, - struct ext4_group_desc *bg, __u32 count); +extern void ext4_free_group_clusters_set(struct super_block *sb, + struct ext4_group_desc *bg, + __u32 count); extern void ext4_free_inodes_set(struct super_block *sb, struct ext4_group_desc *bg, __u32 count); extern void ext4_used_dirs_set(struct super_block *sb, diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index 0be5862313f0..71a9c8f3dece 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -78,7 +78,7 @@ static unsigned ext4_init_inode_bitmap(struct super_block *sb, * allocation, essentially implementing a per-group read-only flag. */ if (!ext4_group_desc_csum_verify(sbi, block_group, gdp)) { ext4_error(sb, "Checksum bad for group %u", block_group); - ext4_free_blks_set(sb, gdp, 0); + ext4_free_group_clusters_set(sb, gdp, 0); ext4_free_inodes_set(sb, gdp, 0); ext4_itable_unused_set(sb, gdp, 0); memset(bh->b_data, 0xff, sb->s_blocksize); @@ -322,8 +322,8 @@ static int find_group_dir(struct super_block *sb, struct inode *parent, if (ext4_free_inodes_count(sb, desc) < avefreei) continue; if (!best_desc || - (ext4_free_blks_count(sb, desc) > - ext4_free_blks_count(sb, best_desc))) { + (ext4_free_group_clusters(sb, desc) > + ext4_free_group_clusters(sb, best_desc))) { *best_group = group; best_desc = desc; ret = 0; @@ -434,7 +434,7 @@ static void get_orlov_stats(struct super_block *sb, ext4_group_t g, desc = ext4_get_group_desc(sb, g, NULL); if (desc) { stats->free_inodes = ext4_free_inodes_count(sb, desc); - stats->free_clusters = ext4_free_blks_count(sb, desc); + stats->free_clusters = ext4_free_group_clusters(sb, desc); stats->used_dirs = ext4_used_dirs_count(sb, desc); } else { stats->free_inodes = 0; @@ -662,7 +662,7 @@ static int find_group_other(struct super_block *sb, struct inode *parent, *group = parent_group; desc = ext4_get_group_desc(sb, *group, NULL); if (desc && ext4_free_inodes_count(sb, desc) && - ext4_free_blks_count(sb, desc)) + ext4_free_group_clusters(sb, desc)) return 0; /* @@ -686,7 +686,7 @@ static int find_group_other(struct super_block *sb, struct inode *parent, *group -= ngroups; desc = ext4_get_group_desc(sb, *group, NULL); if (desc && ext4_free_inodes_count(sb, desc) && - ext4_free_blks_count(sb, desc)) + ext4_free_group_clusters(sb, desc)) return 0; } @@ -960,7 +960,7 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, int mode, ext4_lock_group(sb, group); if (gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) { gdp->bg_flags &= cpu_to_le16(~EXT4_BG_BLOCK_UNINIT); - ext4_free_blks_set(sb, gdp, + ext4_free_group_clusters_set(sb, gdp, ext4_free_blocks_after_init(sb, group, gdp)); gdp->bg_checksum = ext4_group_desc_csum(sbi, group, gdp); diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 5e1215d38331..76db2f12107f 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -2256,7 +2256,7 @@ int ext4_mb_add_groupinfo(struct super_block *sb, ext4_group_t group, ext4_free_blocks_after_init(sb, group, desc); } else { meta_group_info[i]->bb_free = - ext4_free_blks_count(sb, desc); + ext4_free_group_clusters(sb, desc); } INIT_LIST_HEAD(&meta_group_info[i]->bb_prealloc_list); @@ -2799,7 +2799,7 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac, goto out_err; ext4_debug("using block group %u(%d)\n", ac->ac_b_ex.fe_group, - ext4_free_blks_count(sb, gdp)); + ext4_free_group_clusters(sb, gdp)); err = ext4_journal_get_write_access(handle, gdp_bh); if (err) @@ -2839,12 +2839,12 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac, ac->ac_b_ex.fe_len); if (gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) { gdp->bg_flags &= cpu_to_le16(~EXT4_BG_BLOCK_UNINIT); - ext4_free_blks_set(sb, gdp, - ext4_free_blocks_after_init(sb, - ac->ac_b_ex.fe_group, gdp)); + ext4_free_group_clusters_set(sb, gdp, + ext4_free_blocks_after_init(sb, + ac->ac_b_ex.fe_group, gdp)); } - len = ext4_free_blks_count(sb, gdp) - ac->ac_b_ex.fe_len; - ext4_free_blks_set(sb, gdp, len); + len = ext4_free_group_clusters(sb, gdp) - ac->ac_b_ex.fe_len; + ext4_free_group_clusters_set(sb, gdp, len); gdp->bg_checksum = ext4_group_desc_csum(sbi, ac->ac_b_ex.fe_group, gdp); ext4_unlock_group(sb, ac->ac_b_ex.fe_group); @@ -4702,8 +4702,8 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode, mb_free_blocks(inode, &e4b, bit, count_clusters); } - ret = ext4_free_blks_count(sb, gdp) + count_clusters; - ext4_free_blks_set(sb, gdp, ret); + ret = ext4_free_group_clusters(sb, gdp) + count_clusters; + ext4_free_group_clusters_set(sb, gdp, ret); gdp->bg_checksum = ext4_group_desc_csum(sbi, block_group, gdp); ext4_unlock_group(sb, block_group); percpu_counter_add(&sbi->s_freeclusters_counter, count_clusters); @@ -4846,8 +4846,8 @@ int ext4_group_add_blocks(handle_t *handle, struct super_block *sb, ext4_lock_group(sb, block_group); mb_clear_bits(bitmap_bh->b_data, bit, count); mb_free_blocks(NULL, &e4b, bit, count); - blk_free_count = blocks_freed + ext4_free_blks_count(sb, desc); - ext4_free_blks_set(sb, desc, blk_free_count); + blk_free_count = blocks_freed + ext4_free_group_clusters(sb, desc); + ext4_free_group_clusters_set(sb, desc, blk_free_count); desc->bg_checksum = ext4_group_desc_csum(sbi, block_group, desc); ext4_unlock_group(sb, block_group); percpu_counter_add(&sbi->s_freeclusters_counter, diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c index 95a09ddca3b9..996780ab4f4e 100644 --- a/fs/ext4/resize.c +++ b/fs/ext4/resize.c @@ -875,7 +875,7 @@ int ext4_group_add(struct super_block *sb, struct ext4_new_group_data *input) ext4_block_bitmap_set(sb, gdp, input->block_bitmap); /* LV FIXME */ ext4_inode_bitmap_set(sb, gdp, input->inode_bitmap); /* LV FIXME */ ext4_inode_table_set(sb, gdp, input->inode_table); /* LV FIXME */ - ext4_free_blks_set(sb, gdp, input->free_blocks_count); + ext4_free_group_clusters_set(sb, gdp, input->free_blocks_count); ext4_free_inodes_set(sb, gdp, EXT4_INODES_PER_GROUP(sb)); gdp->bg_flags = cpu_to_le16(EXT4_BG_INODE_ZEROED); gdp->bg_checksum = ext4_group_desc_csum(sbi, input->group, gdp); diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 66b8cfa15636..30cf0e1bd89c 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -163,8 +163,8 @@ ext4_fsblk_t ext4_inode_table(struct super_block *sb, (ext4_fsblk_t)le32_to_cpu(bg->bg_inode_table_hi) << 32 : 0); } -__u32 ext4_free_blks_count(struct super_block *sb, - struct ext4_group_desc *bg) +__u32 ext4_free_group_clusters(struct super_block *sb, + struct ext4_group_desc *bg) { return le16_to_cpu(bg->bg_free_blocks_count_lo) | (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT ? @@ -219,8 +219,8 @@ void ext4_inode_table_set(struct super_block *sb, bg->bg_inode_table_hi = cpu_to_le32(blk >> 32); } -void ext4_free_blks_set(struct super_block *sb, - struct ext4_group_desc *bg, __u32 count) +void ext4_free_group_clusters_set(struct super_block *sb, + struct ext4_group_desc *bg, __u32 count) { bg->bg_free_blocks_count_lo = cpu_to_le16((__u16)count); if (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT) @@ -2034,7 +2034,7 @@ static int ext4_fill_flex_info(struct super_block *sb) flex_group = ext4_flex_group(sbi, i); atomic_add(ext4_free_inodes_count(sb, gdp), &sbi->s_flex_groups[flex_group].free_inodes); - atomic_add(ext4_free_blks_count(sb, gdp), + atomic_add(ext4_free_group_clusters(sb, gdp), &sbi->s_flex_groups[flex_group].free_clusters); atomic_add(ext4_used_dirs_count(sb, gdp), &sbi->s_flex_groups[flex_group].used_dirs); From 5dee54372c1ea15ab482b959634cda8c01b042bd Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 9 Sep 2011 19:10:51 -0400 Subject: [PATCH 0134/1531] ext4: rename ext4_count_free_blocks() to ext4_count_free_clusters() This function really counts the free clusters reported in the block group descriptors, so rename it to reduce confusion. Signed-off-by: "Theodore Ts'o" --- fs/ext4/balloc.c | 11 ++++++----- fs/ext4/ext4.h | 2 +- fs/ext4/inode.c | 6 +++--- fs/ext4/super.c | 7 ++++--- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index 84dc3d62b40c..4f303809e076 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -524,12 +524,12 @@ ext4_fsblk_t ext4_new_meta_blocks(handle_t *handle, struct inode *inode, } /** - * ext4_count_free_blocks() -- count filesystem free blocks + * ext4_count_free_clusters() -- count filesystem free clusters * @sb: superblock * - * Adds up the number of free blocks from each block group. + * Adds up the number of free clusters from each block group. */ -ext4_fsblk_t ext4_count_free_blocks(struct super_block *sb) +ext4_fsblk_t ext4_count_free_clusters(struct super_block *sb) { ext4_fsblk_t desc_count; struct ext4_group_desc *gdp; @@ -562,8 +562,9 @@ ext4_fsblk_t ext4_count_free_blocks(struct super_block *sb) bitmap_count += x; } brelse(bitmap_bh); - printk(KERN_DEBUG "ext4_count_free_blocks: stored = %llu" - ", computed = %llu, %llu\n", ext4_free_blocks_count(es), + printk(KERN_DEBUG "ext4_count_free_clusters: stored = %llu" + ", computed = %llu, %llu\n", + EXT4_B2C(sbi, ext4_free_blocks_count(es)), desc_count, bitmap_count); return bitmap_count; #else diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index db279c362017..f6963cd7f709 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1775,7 +1775,7 @@ extern ext4_fsblk_t ext4_new_meta_blocks(handle_t *handle, struct inode *inode, int *errp); extern int ext4_claim_free_blocks(struct ext4_sb_info *sbi, s64 nblocks, unsigned int flags); -extern ext4_fsblk_t ext4_count_free_blocks(struct super_block *); +extern ext4_fsblk_t ext4_count_free_clusters(struct super_block *); extern void ext4_check_blocks_bitmap(struct super_block *); extern struct ext4_group_desc * ext4_get_group_desc(struct super_block * sb, ext4_group_t block_group, diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index d1c17e47c1c6..f9198d7875cc 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1365,7 +1365,8 @@ static void ext4_print_free_blocks(struct inode *inode) { struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); printk(KERN_CRIT "Total free blocks count %lld\n", - ext4_count_free_blocks(inode->i_sb)); + EXT4_C2B(EXT4_SB(inode->i_sb), + ext4_count_free_clusters(inode->i_sb))); printk(KERN_CRIT "Free/Dirty block details\n"); printk(KERN_CRIT "free_blocks=%lld\n", (long long) EXT4_C2B(EXT4_SB(inode->i_sb), @@ -1451,8 +1452,7 @@ static void mpage_da_map_and_submit(struct mpage_da_data *mpd) if (err == -EAGAIN) goto submit_io; - if (err == -ENOSPC && - ext4_count_free_blocks(sb)) { + if (err == -ENOSPC && ext4_count_free_clusters(sb)) { mpd->retval = err; goto submit_io; } diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 30cf0e1bd89c..07f3de341452 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -2153,7 +2153,8 @@ static int ext4_check_descriptors(struct super_block *sb, if (NULL != first_not_zeroed) *first_not_zeroed = grp; - ext4_free_blocks_count_set(sbi->s_es, ext4_count_free_blocks(sb)); + ext4_free_blocks_count_set(sbi->s_es, + EXT4_C2B(sbi, ext4_count_free_clusters(sb))); sbi->s_es->s_free_inodes_count =cpu_to_le32(ext4_count_free_inodes(sb)); return 1; } @@ -3577,7 +3578,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) sbi->s_err_report.data = (unsigned long) sb; err = percpu_counter_init(&sbi->s_freeclusters_counter, - ext4_count_free_blocks(sb)); + ext4_count_free_clusters(sb)); if (!err) { err = percpu_counter_init(&sbi->s_freeinodes_counter, ext4_count_free_inodes(sb)); @@ -3703,7 +3704,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) * need to update the global counters. */ percpu_counter_set(&sbi->s_freeclusters_counter, - ext4_count_free_blocks(sb)); + ext4_count_free_clusters(sb)); percpu_counter_set(&sbi->s_freeinodes_counter, ext4_count_free_inodes(sb)); percpu_counter_set(&sbi->s_dirs_counter, From cff1dfd767d1ee3c773fd8b57fe310957e5f8abb Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 9 Sep 2011 19:12:51 -0400 Subject: [PATCH 0135/1531] ext4: rename ext4_free_blocks_after_init() to ext4_free_clusters_after_init() This function really returns the number of clusters after initializing an uninitalized block bitmap has been initialized. Signed-off-by: "Theodore Ts'o" --- fs/ext4/balloc.c | 6 +++--- fs/ext4/ext4.h | 6 +++--- fs/ext4/ialloc.c | 2 +- fs/ext4/mballoc.c | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index 4f303809e076..3d07c35c7089 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -213,9 +213,9 @@ void ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh, /* Return the number of free blocks in a block group. It is used when * the block bitmap is uninitialized, so we can't just count the bits * in the bitmap. */ -unsigned ext4_free_blocks_after_init(struct super_block *sb, - ext4_group_t block_group, - struct ext4_group_desc *gdp) +unsigned ext4_free_clusters_after_init(struct super_block *sb, + ext4_group_t block_group, + struct ext4_group_desc *gdp) { return num_clusters_in_group(sb, block_group) - ext4_num_overhead_clusters(sb, block_group, gdp); diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index f6963cd7f709..1473c06e4c94 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1787,9 +1787,9 @@ extern void ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh, ext4_group_t group, struct ext4_group_desc *desc); -extern unsigned ext4_free_blocks_after_init(struct super_block *sb, - ext4_group_t block_group, - struct ext4_group_desc *gdp); +extern unsigned ext4_free_clusters_after_init(struct super_block *sb, + ext4_group_t block_group, + struct ext4_group_desc *gdp); extern unsigned ext4_num_base_meta_clusters(struct super_block *sb, ext4_group_t block_group); extern unsigned ext4_num_overhead_clusters(struct super_block *sb, diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index 71a9c8f3dece..d50a7d5e4726 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -961,7 +961,7 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode *dir, int mode, if (gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) { gdp->bg_flags &= cpu_to_le16(~EXT4_BG_BLOCK_UNINIT); ext4_free_group_clusters_set(sb, gdp, - ext4_free_blocks_after_init(sb, group, gdp)); + ext4_free_clusters_after_init(sb, group, gdp)); gdp->bg_checksum = ext4_group_desc_csum(sbi, group, gdp); } diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 76db2f12107f..90a3ed7c565c 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -2253,7 +2253,7 @@ int ext4_mb_add_groupinfo(struct super_block *sb, ext4_group_t group, */ if (desc->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) { meta_group_info[i]->bb_free = - ext4_free_blocks_after_init(sb, group, desc); + ext4_free_clusters_after_init(sb, group, desc); } else { meta_group_info[i]->bb_free = ext4_free_group_clusters(sb, desc); @@ -2840,7 +2840,7 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac, if (gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) { gdp->bg_flags &= cpu_to_le16(~EXT4_BG_BLOCK_UNINIT); ext4_free_group_clusters_set(sb, gdp, - ext4_free_blocks_after_init(sb, + ext4_free_clusters_after_init(sb, ac->ac_b_ex.fe_group, gdp)); } len = ext4_free_group_clusters(sb, gdp) - ac->ac_b_ex.fe_len; From e7d5f3156e6827970f75ab27ad7eb0155826eb0b Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 9 Sep 2011 19:14:51 -0400 Subject: [PATCH 0136/1531] ext4: rename ext4_claim_free_blocks() to ext4_claim_free_clusters() This function really claims a number of free clusters, not blocks, so rename it so it's clearer what's going on. Signed-off-by: "Theodore Ts'o" --- fs/ext4/balloc.c | 8 ++++---- fs/ext4/ext4.h | 4 ++-- fs/ext4/inode.c | 2 +- fs/ext4/mballoc.c | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index 3d07c35c7089..af0c5357e35a 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -445,11 +445,11 @@ static int ext4_has_free_blocks(struct ext4_sb_info *sbi, return 0; } -int ext4_claim_free_blocks(struct ext4_sb_info *sbi, - s64 nblocks, unsigned int flags) +int ext4_claim_free_clusters(struct ext4_sb_info *sbi, + s64 nclusters, unsigned int flags) { - if (ext4_has_free_blocks(sbi, nblocks, flags)) { - percpu_counter_add(&sbi->s_dirtyclusters_counter, nblocks); + if (ext4_has_free_blocks(sbi, nclusters, flags)) { + percpu_counter_add(&sbi->s_dirtyclusters_counter, nclusters); return 0; } else return -ENOSPC; diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 1473c06e4c94..f97638634e34 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1773,8 +1773,8 @@ extern ext4_fsblk_t ext4_new_meta_blocks(handle_t *handle, struct inode *inode, unsigned int flags, unsigned long *count, int *errp); -extern int ext4_claim_free_blocks(struct ext4_sb_info *sbi, - s64 nblocks, unsigned int flags); +extern int ext4_claim_free_clusters(struct ext4_sb_info *sbi, + s64 nclusters, unsigned int flags); extern ext4_fsblk_t ext4_count_free_clusters(struct super_block *); extern void ext4_check_blocks_bitmap(struct super_block *); extern struct ext4_group_desc * ext4_get_group_desc(struct super_block * sb, diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index f9198d7875cc..557c34ffa05b 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1071,7 +1071,7 @@ int ext4_da_reserve_space(struct inode *inode, ext4_lblk_t lblock) * We do still charge estimated metadata to the sb though; * we cannot afford to run out of free blocks. */ - if (ext4_claim_free_blocks(sbi, md_needed + 1, 0)) { + if (ext4_claim_free_clusters(sbi, md_needed + 1, 0)) { dquot_release_reservation_block(inode, EXT4_C2B(sbi, 1)); if (ext4_should_retry_alloc(inode->i_sb, &retries)) { yield(); diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 90a3ed7c565c..1c83161090f8 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -4297,7 +4297,7 @@ ext4_fsblk_t ext4_mb_new_blocks(handle_t *handle, * and verify allocation doesn't exceed the quota limits. */ while (ar->len && - ext4_claim_free_blocks(sbi, ar->len, ar->flags)) { + ext4_claim_free_clusters(sbi, ar->len, ar->flags)) { /* let others to free the space */ yield(); From df55c99dc8ee4c3c886a5edc8a4aa6b131c95afc Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 9 Sep 2011 19:16:51 -0400 Subject: [PATCH 0137/1531] ext4: rename ext4_has_free_blocks() to ext4_has_free_clusters() Rename the function so it is more clear what is going on. Also rename the various variables so it's clearer what's happening. Also fix a missing blocks to cluster conversion when reading the number of reserved blocks for root. Signed-off-by: "Theodore Ts'o" --- fs/ext4/balloc.c | 43 ++++++++++++++++++++++--------------------- fs/ext4/ext4.h | 8 ++++---- fs/ext4/inode.c | 2 +- 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index af0c5357e35a..f6dba4505f1c 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -403,42 +403,43 @@ ext4_read_block_bitmap(struct super_block *sb, ext4_group_t block_group) } /** - * ext4_has_free_blocks() + * ext4_has_free_clusters() * @sbi: in-core super block structure. - * @nblocks: number of needed blocks + * @nclusters: number of needed blocks + * @flags: flags from ext4_mb_new_blocks() * - * Check if filesystem has nblocks free & available for allocation. + * Check if filesystem has nclusters free & available for allocation. * On success return 1, return 0 on failure. */ -static int ext4_has_free_blocks(struct ext4_sb_info *sbi, - s64 nblocks, unsigned int flags) +static int ext4_has_free_clusters(struct ext4_sb_info *sbi, + s64 nclusters, unsigned int flags) { - s64 free_blocks, dirty_blocks, root_blocks; + s64 free_clusters, dirty_clusters, root_clusters; struct percpu_counter *fcc = &sbi->s_freeclusters_counter; - struct percpu_counter *dbc = &sbi->s_dirtyclusters_counter; + struct percpu_counter *dcc = &sbi->s_dirtyclusters_counter; - free_blocks = percpu_counter_read_positive(fcc); - dirty_blocks = percpu_counter_read_positive(dbc); - root_blocks = ext4_r_blocks_count(sbi->s_es); + free_clusters = percpu_counter_read_positive(fcc); + dirty_clusters = percpu_counter_read_positive(dcc); + root_clusters = EXT4_B2C(sbi, ext4_r_blocks_count(sbi->s_es)); - if (free_blocks - (nblocks + root_blocks + dirty_blocks) < - EXT4_FREEBLOCKS_WATERMARK) { - free_blocks = EXT4_C2B(sbi, percpu_counter_sum_positive(fcc)); - dirty_blocks = percpu_counter_sum_positive(dbc); + if (free_clusters - (nclusters + root_clusters + dirty_clusters) < + EXT4_FREECLUSTERS_WATERMARK) { + free_clusters = EXT4_C2B(sbi, percpu_counter_sum_positive(fcc)); + dirty_clusters = percpu_counter_sum_positive(dcc); } - /* Check whether we have space after - * accounting for current dirty blocks & root reserved blocks. + /* Check whether we have space after accounting for current + * dirty clusters & root reserved clusters. */ - if (free_blocks >= ((root_blocks + nblocks) + dirty_blocks)) + if (free_clusters >= ((root_clusters + nclusters) + dirty_clusters)) return 1; - /* Hm, nope. Are (enough) root reserved blocks available? */ + /* Hm, nope. Are (enough) root reserved clusters available? */ if (sbi->s_resuid == current_fsuid() || ((sbi->s_resgid != 0) && in_group_p(sbi->s_resgid)) || capable(CAP_SYS_RESOURCE) || (flags & EXT4_MB_USE_ROOT_BLOCKS)) { - if (free_blocks >= (nblocks + dirty_blocks)) + if (free_clusters >= (nclusters + dirty_clusters)) return 1; } @@ -448,7 +449,7 @@ static int ext4_has_free_blocks(struct ext4_sb_info *sbi, int ext4_claim_free_clusters(struct ext4_sb_info *sbi, s64 nclusters, unsigned int flags) { - if (ext4_has_free_blocks(sbi, nclusters, flags)) { + if (ext4_has_free_clusters(sbi, nclusters, flags)) { percpu_counter_add(&sbi->s_dirtyclusters_counter, nclusters); return 0; } else @@ -469,7 +470,7 @@ int ext4_claim_free_clusters(struct ext4_sb_info *sbi, */ int ext4_should_retry_alloc(struct super_block *sb, int *retries) { - if (!ext4_has_free_blocks(EXT4_SB(sb), 1, 0) || + if (!ext4_has_free_clusters(EXT4_SB(sb), 1, 0) || (*retries)++ > 3 || !EXT4_SB(sb)->s_journal) return 0; diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index f97638634e34..21ea65d8bd46 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -2103,13 +2103,13 @@ do { \ } while (0) #ifdef CONFIG_SMP -/* Each CPU can accumulate percpu_counter_batch blocks in their local - * counters. So we need to make sure we have free blocks more +/* Each CPU can accumulate percpu_counter_batch clusters in their local + * counters. So we need to make sure we have free clusters more * than percpu_counter_batch * nr_cpu_ids. Also add a window of 4 times. */ -#define EXT4_FREEBLOCKS_WATERMARK (4 * (percpu_counter_batch * nr_cpu_ids)) +#define EXT4_FREECLUSTERS_WATERMARK (4 * (percpu_counter_batch * nr_cpu_ids)) #else -#define EXT4_FREEBLOCKS_WATERMARK 0 +#define EXT4_FREECLUSTERS_WATERMARK 0 #endif static inline void ext4_update_i_disksize(struct inode *inode, loff_t newsize) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 557c34ffa05b..88dc63a01756 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -2255,7 +2255,7 @@ static int ext4_nonda_switch(struct super_block *sb) percpu_counter_read_positive(&sbi->s_freeclusters_counter)); dirty_blocks = percpu_counter_read_positive(&sbi->s_dirtyclusters_counter); if (2 * free_blocks < 3 * dirty_blocks || - free_blocks < (dirty_blocks + EXT4_FREEBLOCKS_WATERMARK)) { + free_blocks < (dirty_blocks + EXT4_FREECLUSTERS_WATERMARK)) { /* * free block count is less than 150% of dirty blocks * or free blocks is less than watermark From d8990240d8c911064447f8aa5a440f9345a6d692 Mon Sep 17 00:00:00 2001 From: Aditya Kali Date: Fri, 9 Sep 2011 19:18:51 -0400 Subject: [PATCH 0138/1531] ext4: add some tracepoints in ext4/extents.c This patch adds some tracepoints in ext4/extents.c and updates a tracepoint in ext4/inode.c. Tested: Built and ran the kernel and verified that these tracepoints work. Also ran xfstests. Signed-off-by: Aditya Kali Signed-off-by: "Theodore Ts'o" --- fs/ext4/ext4.h | 2 + fs/ext4/extents.c | 44 +++- fs/ext4/inode.c | 3 +- fs/ext4/migrate.c | 1 - fs/ext4/move_extent.c | 1 - include/trace/events/ext4.h | 398 +++++++++++++++++++++++++++++++++++- 6 files changed, 433 insertions(+), 16 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 21ea65d8bd46..751277a4890c 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -2339,4 +2339,6 @@ extern void ext4_resize_end(struct super_block *sb); #endif /* __KERNEL__ */ +#include "ext4_extents.h" + #endif /* _EXT4_H */ diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index c4e005864534..9b119308daea 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -42,7 +42,6 @@ #include #include #include "ext4_jbd2.h" -#include "ext4_extents.h" #include @@ -1969,6 +1968,7 @@ ext4_ext_put_in_cache(struct inode *inode, ext4_lblk_t block, struct ext4_ext_cache *cex; BUG_ON(len == 0); spin_lock(&EXT4_I(inode)->i_block_reservation_lock); + trace_ext4_ext_put_in_cache(inode, block, len, start); cex = &EXT4_I(inode)->i_cached_extent; cex->ec_block = block; cex->ec_len = len; @@ -2070,6 +2070,7 @@ static int ext4_ext_check_cache(struct inode *inode, ext4_lblk_t block, sbi->extent_cache_misses++; else sbi->extent_cache_hits++; + trace_ext4_ext_in_cache(inode, block, ret); spin_unlock(&EXT4_I(inode)->i_block_reservation_lock); return ret; } @@ -2137,6 +2138,8 @@ static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode, if (err) return err; ext_debug("index is empty, remove it, free block %llu\n", leaf); + trace_ext4_ext_rm_idx(inode, leaf); + ext4_free_blocks(handle, inode, NULL, leaf, 1, EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET); return err; @@ -2222,6 +2225,7 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode, */ flags |= EXT4_FREE_BLOCKS_NOFREE_FIRST_CLUSTER; + trace_ext4_remove_blocks(inode, ex, from, to, *partial_cluster); /* * If we have a partial cluster, and it's different from the * cluster of the last block, we need to explicitly free the @@ -2336,6 +2340,8 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode, ex_ee_block = le32_to_cpu(ex->ee_block); ex_ee_len = ext4_ext_get_actual_len(ex); + trace_ext4_ext_rm_leaf(inode, start, ex, *partial_cluster); + while (ex >= EXT_FIRST_EXTENT(eh) && ex_ee_block + ex_ee_len > start) { @@ -2591,6 +2597,8 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start) again: ext4_ext_invalidate_cache(inode); + trace_ext4_ext_remove_space(inode, start, depth); + /* * We start scanning from right side, freeing all the blocks * after i_size and walking into the tree depth-wise. @@ -2686,6 +2694,9 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start) } } + trace_ext4_ext_remove_space_done(inode, start, depth, partial_cluster, + path->p_hdr->eh_entries); + /* If we still have something in the partial cluster and we have removed * even the first extent, then we should free the blocks in the partial * cluster as well. */ @@ -3300,6 +3311,10 @@ static int ext4_find_delalloc_range(struct inode *inode, * detect that here. */ page_cache_release(page); + trace_ext4_find_delalloc_range(inode, + lblk_start, lblk_end, + search_hint_reverse, + 0, i); return 0; } @@ -3327,6 +3342,10 @@ static int ext4_find_delalloc_range(struct inode *inode, if (buffer_delay(bh)) { page_cache_release(page); + trace_ext4_find_delalloc_range(inode, + lblk_start, lblk_end, + search_hint_reverse, + 1, i); return 1; } if (search_hint_reverse) @@ -3349,6 +3368,8 @@ static int ext4_find_delalloc_range(struct inode *inode, i = index << (PAGE_CACHE_SHIFT - inode->i_blkbits); } + trace_ext4_find_delalloc_range(inode, lblk_start, lblk_end, + search_hint_reverse, 0, 0); return 0; } @@ -3414,6 +3435,8 @@ get_reserved_cluster_alloc(struct inode *inode, ext4_lblk_t lblk_start, /* max possible clusters for this allocation */ allocated_clusters = alloc_cluster_end - alloc_cluster_start + 1; + trace_ext4_get_reserved_cluster_alloc(inode, lblk_start, num_blks); + /* Check towards left side */ c_offset = lblk_start & (sbi->s_cluster_ratio - 1); if (c_offset) { @@ -3453,6 +3476,9 @@ ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode, flags, allocated); ext4_ext_show_leaf(inode, path); + trace_ext4_ext_handle_uninitialized_extents(inode, map, allocated, + newblock); + /* get_block() before submit the IO, split the extent */ if ((flags & EXT4_GET_BLOCKS_PRE_IO)) { ret = ext4_split_unwritten_extents(handle, inode, map, @@ -3572,7 +3598,7 @@ ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode, * get_implied_cluster_alloc - check to see if the requested * allocation (in the map structure) overlaps with a cluster already * allocated in an extent. - * @sbi The ext4-specific superblock structure + * @sb The filesystem superblock structure * @map The requested lblk->pblk mapping * @ex The extent structure which might contain an implied * cluster allocation @@ -3609,11 +3635,12 @@ ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode, * ext4_ext_map_blocks() will then allocate one or more new clusters * by calling ext4_mb_new_blocks(). */ -static int get_implied_cluster_alloc(struct ext4_sb_info *sbi, +static int get_implied_cluster_alloc(struct super_block *sb, struct ext4_map_blocks *map, struct ext4_extent *ex, struct ext4_ext_path *path) { + struct ext4_sb_info *sbi = EXT4_SB(sb); ext4_lblk_t c_offset = map->m_lblk & (sbi->s_cluster_ratio-1); ext4_lblk_t ex_cluster_start, ex_cluster_end; ext4_lblk_t rr_cluster_start, rr_cluster_end; @@ -3662,8 +3689,12 @@ static int get_implied_cluster_alloc(struct ext4_sb_info *sbi, ext4_lblk_t next = ext4_ext_next_allocated_block(path); map->m_len = min(map->m_len, next - map->m_lblk); } + + trace_ext4_get_implied_cluster_alloc_exit(sb, map, 1); return 1; } + + trace_ext4_get_implied_cluster_alloc_exit(sb, map, 0); return 0; } @@ -3772,6 +3803,9 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, * we split out initialized portions during a write. */ ee_len = ext4_ext_get_actual_len(ex); + + trace_ext4_ext_show_extent(inode, ee_block, ee_start, ee_len); + /* if found extent covers block, simply return it */ if (in_range(map->m_lblk, ee_block, ee_len)) { ext4_fsblk_t partial_cluster = 0; @@ -3912,7 +3946,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, * by ext4_ext_find_extent() implies a cluster we can use. */ if (cluster_offset && ex && - get_implied_cluster_alloc(sbi, map, ex, path)) { + get_implied_cluster_alloc(inode->i_sb, map, ex, path)) { ar.len = allocated = map->m_len; newblock = map->m_pblk; map->m_flags |= EXT4_MAP_FROM_CLUSTER; @@ -3933,7 +3967,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, /* Check if the extent after searching to the right implies a * cluster we can use. */ if ((sbi->s_cluster_ratio > 1) && ex2 && - get_implied_cluster_alloc(sbi, map, ex2, path)) { + get_implied_cluster_alloc(inode->i_sb, map, ex2, path)) { ar.len = allocated = map->m_len; newblock = map->m_pblk; map->m_flags |= EXT4_MAP_FROM_CLUSTER; diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 88dc63a01756..2dcd4fed96ec 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -42,7 +42,6 @@ #include "ext4_jbd2.h" #include "xattr.h" #include "acl.h" -#include "ext4_extents.h" #include "truncate.h" #include @@ -268,7 +267,7 @@ void ext4_da_update_reserve_space(struct inode *inode, struct ext4_inode_info *ei = EXT4_I(inode); spin_lock(&ei->i_block_reservation_lock); - trace_ext4_da_update_reserve_space(inode, used); + trace_ext4_da_update_reserve_space(inode, used, quota_claim); if (unlikely(used > ei->i_reserved_data_blocks)) { ext4_msg(inode->i_sb, KERN_NOTICE, "%s: ino %lu, used %d " "with only %d reserved data blocks\n", diff --git a/fs/ext4/migrate.c b/fs/ext4/migrate.c index b57b98fb44d1..6f07a06f2437 100644 --- a/fs/ext4/migrate.c +++ b/fs/ext4/migrate.c @@ -15,7 +15,6 @@ #include #include #include "ext4_jbd2.h" -#include "ext4_extents.h" /* * The contiguous blocks details which can be diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c index f57455a1b1b2..c5826c623e7a 100644 --- a/fs/ext4/move_extent.c +++ b/fs/ext4/move_extent.c @@ -17,7 +17,6 @@ #include #include #include "ext4_jbd2.h" -#include "ext4_extents.h" #include "ext4.h" /** diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h index b50a54736242..c9a341e385a3 100644 --- a/include/trace/events/ext4.h +++ b/include/trace/events/ext4.h @@ -12,6 +12,8 @@ struct ext4_allocation_request; struct ext4_prealloc_space; struct ext4_inode_info; struct mpage_da_data; +struct ext4_map_blocks; +struct ext4_extent; #define EXT4_I(inode) (container_of(inode, struct ext4_inode_info, vfs_inode)) @@ -1032,9 +1034,9 @@ TRACE_EVENT(ext4_forget, ); TRACE_EVENT(ext4_da_update_reserve_space, - TP_PROTO(struct inode *inode, int used_blocks), + TP_PROTO(struct inode *inode, int used_blocks, int quota_claim), - TP_ARGS(inode, used_blocks), + TP_ARGS(inode, used_blocks, quota_claim), TP_STRUCT__entry( __field( dev_t, dev ) @@ -1045,6 +1047,7 @@ TRACE_EVENT(ext4_da_update_reserve_space, __field( int, reserved_data_blocks ) __field( int, reserved_meta_blocks ) __field( int, allocated_meta_blocks ) + __field( int, quota_claim ) ), TP_fast_assign( @@ -1053,19 +1056,24 @@ TRACE_EVENT(ext4_da_update_reserve_space, __entry->mode = inode->i_mode; __entry->i_blocks = inode->i_blocks; __entry->used_blocks = used_blocks; - __entry->reserved_data_blocks = EXT4_I(inode)->i_reserved_data_blocks; - __entry->reserved_meta_blocks = EXT4_I(inode)->i_reserved_meta_blocks; - __entry->allocated_meta_blocks = EXT4_I(inode)->i_allocated_meta_blocks; + __entry->reserved_data_blocks = + EXT4_I(inode)->i_reserved_data_blocks; + __entry->reserved_meta_blocks = + EXT4_I(inode)->i_reserved_meta_blocks; + __entry->allocated_meta_blocks = + EXT4_I(inode)->i_allocated_meta_blocks; + __entry->quota_claim = quota_claim; ), TP_printk("dev %d,%d ino %lu mode 0%o i_blocks %llu used_blocks %d " "reserved_data_blocks %d reserved_meta_blocks %d " - "allocated_meta_blocks %d", + "allocated_meta_blocks %d quota_claim %d", MAJOR(__entry->dev), MINOR(__entry->dev), (unsigned long) __entry->ino, __entry->mode, __entry->i_blocks, __entry->used_blocks, __entry->reserved_data_blocks, - __entry->reserved_meta_blocks, __entry->allocated_meta_blocks) + __entry->reserved_meta_blocks, __entry->allocated_meta_blocks, + __entry->quota_claim) ); TRACE_EVENT(ext4_da_reserve_space, @@ -1589,6 +1597,382 @@ DEFINE_EVENT(ext4__trim, ext4_trim_all_free, TP_ARGS(sb, group, start, len) ); +TRACE_EVENT(ext4_ext_handle_uninitialized_extents, + TP_PROTO(struct inode *inode, struct ext4_map_blocks *map, + unsigned int allocated, ext4_fsblk_t newblock), + + TP_ARGS(inode, map, allocated, newblock), + + TP_STRUCT__entry( + __field( ino_t, ino ) + __field( dev_t, dev ) + __field( ext4_lblk_t, lblk ) + __field( ext4_fsblk_t, pblk ) + __field( unsigned int, len ) + __field( int, flags ) + __field( unsigned int, allocated ) + __field( ext4_fsblk_t, newblk ) + ), + + TP_fast_assign( + __entry->ino = inode->i_ino; + __entry->dev = inode->i_sb->s_dev; + __entry->lblk = map->m_lblk; + __entry->pblk = map->m_pblk; + __entry->len = map->m_len; + __entry->flags = map->m_flags; + __entry->allocated = allocated; + __entry->newblk = newblock; + ), + + TP_printk("dev %d,%d ino %lu m_lblk %u m_pblk %llu m_len %u flags %d" + "allocated %d newblock %llu", + MAJOR(__entry->dev), MINOR(__entry->dev), + (unsigned long) __entry->ino, + (unsigned) __entry->lblk, (unsigned long long) __entry->pblk, + __entry->len, __entry->flags, + (unsigned int) __entry->allocated, + (unsigned long long) __entry->newblk) +); + +TRACE_EVENT(ext4_get_implied_cluster_alloc_exit, + TP_PROTO(struct super_block *sb, struct ext4_map_blocks *map, int ret), + + TP_ARGS(sb, map, ret), + + TP_STRUCT__entry( + __field( dev_t, dev ) + __field( ext4_lblk_t, lblk ) + __field( ext4_fsblk_t, pblk ) + __field( unsigned int, len ) + __field( unsigned int, flags ) + __field( int, ret ) + ), + + TP_fast_assign( + __entry->dev = sb->s_dev; + __entry->lblk = map->m_lblk; + __entry->pblk = map->m_pblk; + __entry->len = map->m_len; + __entry->flags = map->m_flags; + __entry->ret = ret; + ), + + TP_printk("dev %d,%d m_lblk %u m_pblk %llu m_len %u m_flags %u ret %d", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->lblk, (unsigned long long) __entry->pblk, + __entry->len, __entry->flags, __entry->ret) +); + +TRACE_EVENT(ext4_ext_put_in_cache, + TP_PROTO(struct inode *inode, ext4_lblk_t lblk, unsigned int len, + ext4_fsblk_t start), + + TP_ARGS(inode, lblk, len, start), + + TP_STRUCT__entry( + __field( ino_t, ino ) + __field( dev_t, dev ) + __field( ext4_lblk_t, lblk ) + __field( unsigned int, len ) + __field( ext4_fsblk_t, start ) + ), + + TP_fast_assign( + __entry->ino = inode->i_ino; + __entry->dev = inode->i_sb->s_dev; + __entry->lblk = lblk; + __entry->len = len; + __entry->start = start; + ), + + TP_printk("dev %d,%d ino %lu lblk %u len %u start %llu", + MAJOR(__entry->dev), MINOR(__entry->dev), + (unsigned long) __entry->ino, + (unsigned) __entry->lblk, + __entry->len, + (unsigned long long) __entry->start) +); + +TRACE_EVENT(ext4_ext_in_cache, + TP_PROTO(struct inode *inode, ext4_lblk_t lblk, int ret), + + TP_ARGS(inode, lblk, ret), + + TP_STRUCT__entry( + __field( ino_t, ino ) + __field( dev_t, dev ) + __field( ext4_lblk_t, lblk ) + __field( int, ret ) + ), + + TP_fast_assign( + __entry->ino = inode->i_ino; + __entry->dev = inode->i_sb->s_dev; + __entry->lblk = lblk; + __entry->ret = ret; + ), + + TP_printk("dev %d,%d ino %lu lblk %u ret %d", + MAJOR(__entry->dev), MINOR(__entry->dev), + (unsigned long) __entry->ino, + (unsigned) __entry->lblk, + __entry->ret) + +); + +TRACE_EVENT(ext4_find_delalloc_range, + TP_PROTO(struct inode *inode, ext4_lblk_t from, ext4_lblk_t to, + int reverse, int found, ext4_lblk_t found_blk), + + TP_ARGS(inode, from, to, reverse, found, found_blk), + + TP_STRUCT__entry( + __field( ino_t, ino ) + __field( dev_t, dev ) + __field( ext4_lblk_t, from ) + __field( ext4_lblk_t, to ) + __field( int, reverse ) + __field( int, found ) + __field( ext4_lblk_t, found_blk ) + ), + + TP_fast_assign( + __entry->ino = inode->i_ino; + __entry->dev = inode->i_sb->s_dev; + __entry->from = from; + __entry->to = to; + __entry->reverse = reverse; + __entry->found = found; + __entry->found_blk = found_blk; + ), + + TP_printk("dev %d,%d ino %lu from %u to %u reverse %d found %d " + "(blk = %u)", + MAJOR(__entry->dev), MINOR(__entry->dev), + (unsigned long) __entry->ino, + (unsigned) __entry->from, (unsigned) __entry->to, + __entry->reverse, __entry->found, + (unsigned) __entry->found_blk) +); + +TRACE_EVENT(ext4_get_reserved_cluster_alloc, + TP_PROTO(struct inode *inode, ext4_lblk_t lblk, unsigned int len), + + TP_ARGS(inode, lblk, len), + + TP_STRUCT__entry( + __field( ino_t, ino ) + __field( dev_t, dev ) + __field( ext4_lblk_t, lblk ) + __field( unsigned int, len ) + ), + + TP_fast_assign( + __entry->ino = inode->i_ino; + __entry->dev = inode->i_sb->s_dev; + __entry->lblk = lblk; + __entry->len = len; + ), + + TP_printk("dev %d,%d ino %lu lblk %u len %u", + MAJOR(__entry->dev), MINOR(__entry->dev), + (unsigned long) __entry->ino, + (unsigned) __entry->lblk, + __entry->len) +); + +TRACE_EVENT(ext4_ext_show_extent, + TP_PROTO(struct inode *inode, ext4_lblk_t lblk, ext4_fsblk_t pblk, + unsigned short len), + + TP_ARGS(inode, lblk, pblk, len), + + TP_STRUCT__entry( + __field( ino_t, ino ) + __field( dev_t, dev ) + __field( ext4_lblk_t, lblk ) + __field( ext4_fsblk_t, pblk ) + __field( unsigned short, len ) + ), + + TP_fast_assign( + __entry->ino = inode->i_ino; + __entry->dev = inode->i_sb->s_dev; + __entry->lblk = lblk; + __entry->pblk = pblk; + __entry->len = len; + ), + + TP_printk("dev %d,%d ino %lu lblk %u pblk %llu len %u", + MAJOR(__entry->dev), MINOR(__entry->dev), + (unsigned long) __entry->ino, + (unsigned) __entry->lblk, + (unsigned long long) __entry->pblk, + (unsigned short) __entry->len) +); + +TRACE_EVENT(ext4_remove_blocks, + TP_PROTO(struct inode *inode, struct ext4_extent *ex, + ext4_lblk_t from, ext4_fsblk_t to, + ext4_fsblk_t partial_cluster), + + TP_ARGS(inode, ex, from, to, partial_cluster), + + TP_STRUCT__entry( + __field( ino_t, ino ) + __field( dev_t, dev ) + __field( ext4_lblk_t, ee_lblk ) + __field( ext4_fsblk_t, ee_pblk ) + __field( unsigned short, ee_len ) + __field( ext4_lblk_t, from ) + __field( ext4_lblk_t, to ) + __field( ext4_fsblk_t, partial ) + ), + + TP_fast_assign( + __entry->ino = inode->i_ino; + __entry->dev = inode->i_sb->s_dev; + __entry->ee_lblk = cpu_to_le32(ex->ee_block); + __entry->ee_pblk = ext4_ext_pblock(ex); + __entry->ee_len = ext4_ext_get_actual_len(ex); + __entry->from = from; + __entry->to = to; + __entry->partial = partial_cluster; + ), + + TP_printk("dev %d,%d ino %lu extent [%u(%llu), %u]" + "from %u to %u partial_cluster %u", + MAJOR(__entry->dev), MINOR(__entry->dev), + (unsigned long) __entry->ino, + (unsigned) __entry->ee_lblk, + (unsigned long long) __entry->ee_pblk, + (unsigned short) __entry->ee_len, + (unsigned) __entry->from, + (unsigned) __entry->to, + (unsigned) __entry->partial) +); + +TRACE_EVENT(ext4_ext_rm_leaf, + TP_PROTO(struct inode *inode, ext4_lblk_t start, + struct ext4_extent *ex, ext4_fsblk_t partial_cluster), + + TP_ARGS(inode, start, ex, partial_cluster), + + TP_STRUCT__entry( + __field( ino_t, ino ) + __field( dev_t, dev ) + __field( ext4_lblk_t, start ) + __field( ext4_lblk_t, ee_lblk ) + __field( ext4_fsblk_t, ee_pblk ) + __field( short, ee_len ) + __field( ext4_fsblk_t, partial ) + ), + + TP_fast_assign( + __entry->ino = inode->i_ino; + __entry->dev = inode->i_sb->s_dev; + __entry->start = start; + __entry->ee_lblk = le32_to_cpu(ex->ee_block); + __entry->ee_pblk = ext4_ext_pblock(ex); + __entry->ee_len = ext4_ext_get_actual_len(ex); + __entry->partial = partial_cluster; + ), + + TP_printk("dev %d,%d ino %lu start_lblk %u last_extent [%u(%llu), %u]" + "partial_cluster %u", + MAJOR(__entry->dev), MINOR(__entry->dev), + (unsigned long) __entry->ino, + (unsigned) __entry->start, + (unsigned) __entry->ee_lblk, + (unsigned long long) __entry->ee_pblk, + (unsigned short) __entry->ee_len, + (unsigned) __entry->partial) +); + +TRACE_EVENT(ext4_ext_rm_idx, + TP_PROTO(struct inode *inode, ext4_fsblk_t pblk), + + TP_ARGS(inode, pblk), + + TP_STRUCT__entry( + __field( ino_t, ino ) + __field( dev_t, dev ) + __field( ext4_fsblk_t, pblk ) + ), + + TP_fast_assign( + __entry->ino = inode->i_ino; + __entry->dev = inode->i_sb->s_dev; + __entry->pblk = pblk; + ), + + TP_printk("dev %d,%d ino %lu index_pblk %llu", + MAJOR(__entry->dev), MINOR(__entry->dev), + (unsigned long) __entry->ino, + (unsigned long long) __entry->pblk) +); + +TRACE_EVENT(ext4_ext_remove_space, + TP_PROTO(struct inode *inode, ext4_lblk_t start, int depth), + + TP_ARGS(inode, start, depth), + + TP_STRUCT__entry( + __field( ino_t, ino ) + __field( dev_t, dev ) + __field( ext4_lblk_t, start ) + __field( int, depth ) + ), + + TP_fast_assign( + __entry->ino = inode->i_ino; + __entry->dev = inode->i_sb->s_dev; + __entry->start = start; + __entry->depth = depth; + ), + + TP_printk("dev %d,%d ino %lu since %u depth %d", + MAJOR(__entry->dev), MINOR(__entry->dev), + (unsigned long) __entry->ino, + (unsigned) __entry->start, + __entry->depth) +); + +TRACE_EVENT(ext4_ext_remove_space_done, + TP_PROTO(struct inode *inode, ext4_lblk_t start, int depth, + ext4_lblk_t partial, unsigned short eh_entries), + + TP_ARGS(inode, start, depth, partial, eh_entries), + + TP_STRUCT__entry( + __field( ino_t, ino ) + __field( dev_t, dev ) + __field( ext4_lblk_t, start ) + __field( int, depth ) + __field( ext4_lblk_t, partial ) + __field( unsigned short, eh_entries ) + ), + + TP_fast_assign( + __entry->ino = inode->i_ino; + __entry->dev = inode->i_sb->s_dev; + __entry->start = start; + __entry->depth = depth; + __entry->partial = partial; + __entry->eh_entries = eh_entries; + ), + + TP_printk("dev %d,%d ino %lu since %u depth %d partial %u " + "remaining_entries %u", + MAJOR(__entry->dev), MINOR(__entry->dev), + (unsigned long) __entry->ino, + (unsigned) __entry->start, + __entry->depth, + (unsigned) __entry->partial, + (unsigned short) __entry->eh_entries) +); + #endif /* _TRACE_EXT4_H */ /* This part must be outside protection */ From 5356f2615cd558c57a1f7d7528d1ad4de3640d96 Mon Sep 17 00:00:00 2001 From: Aditya Kali Date: Fri, 9 Sep 2011 19:20:51 -0400 Subject: [PATCH 0139/1531] ext4: attempt to fix race in bigalloc code path Currently, there exists a race between delayed allocated writes and the writeback when bigalloc feature is in use. The race was because we wanted to determine what blocks in a cluster are under delayed allocation and we were using buffer_delayed(bh) check for it. But, the writeback codepath clears this bit without any synchronization which resulted in a race and an ext4 warning similar to: EXT4-fs (ram1): ext4_da_update_reserve_space: ino 13, used 1 with only 0 reserved data blocks The race existed in two places. (1) between ext4_find_delalloc_range() and ext4_map_blocks() when called from writeback code path. (2) between ext4_find_delalloc_range() and ext4_da_get_block_prep() (where buffer_delayed(bh) is set. To fix (1), this patch introduces a new buffer_head state bit - BH_Da_Mapped. This bit is set under the protection of EXT4_I(inode)->i_data_sem when we have actually mapped the delayed allocated blocks during the writeout time. We can now reliably check for this bit inside ext4_find_delalloc_range() to determine whether the reservation for the blocks have already been claimed or not. To fix (2), it was necessary to set buffer_delay(bh) under the protection of i_data_sem. So, I extracted the very beginning of ext4_map_blocks into a new function - ext4_da_map_blocks() - and performed the required setting of bh_delay bit and the quota reservation under the protection of i_data_sem. These two fixes makes the checking of buffer_delay(bh) and buffer_da_mapped(bh) consistent, thus removing the race. Tested: I was able to reproduce the problem by running 'dd' and 'fsync' in parallel. Also, xfstests sometimes used to reproduce this race. After the fix both my test and xfstests were successful and no race (warning message) was observed. Google-Bug-Id: 4997027 Signed-off-by: Aditya Kali Signed-off-by: "Theodore Ts'o" --- fs/ext4/ext4.h | 5 +- fs/ext4/extents.c | 38 ++++-------- fs/ext4/inode.c | 146 +++++++++++++++++++++++++++++++++++++--------- 3 files changed, 134 insertions(+), 55 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 751277a4890c..1bbd2caebe7f 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1893,7 +1893,6 @@ extern int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf); extern qsize_t *ext4_get_reserved_space(struct inode *inode); extern void ext4_da_update_reserve_space(struct inode *inode, int used, int quota_claim); -extern int ext4_da_reserve_space(struct inode *inode, ext4_lblk_t lblock); /* indirect.c */ extern int ext4_ind_map_blocks(handle_t *handle, struct inode *inode, @@ -2300,10 +2299,14 @@ enum ext4_state_bits { * never, ever appear in a buffer_head's state * flag. See EXT4_MAP_FROM_CLUSTER to see where * this is used. */ + BH_Da_Mapped, /* Delayed allocated block that now has a mapping. This + * flag is set when ext4_map_blocks is called on a + * delayed allocated block to get its real mapping. */ }; BUFFER_FNS(Uninit, uninit) TAS_BUFFER_FNS(Uninit, uninit) +BUFFER_FNS(Da_Mapped, da_mapped) /* * Add new method to test wether block and inode bitmaps are properly diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 9b119308daea..ad39627c1fbc 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -3296,28 +3296,9 @@ static int ext4_find_delalloc_range(struct inode *inode, while ((i >= lblk_start) && (i <= lblk_end)) { page = find_get_page(mapping, index); - if (!page || !PageDirty(page)) + if (!page) goto nextpage; - if (PageWriteback(page)) { - /* - * This might be a race with allocation and writeout. In - * this case we just assume that the rest of the range - * will eventually be written and there wont be any - * delalloc blocks left. - * TODO: the above assumption is troublesome, but might - * work better in practice. other option could be note - * somewhere that the cluster is getting written out and - * detect that here. - */ - page_cache_release(page); - trace_ext4_find_delalloc_range(inode, - lblk_start, lblk_end, - search_hint_reverse, - 0, i); - return 0; - } - if (!page_has_buffers(page)) goto nextpage; @@ -3340,7 +3321,11 @@ static int ext4_find_delalloc_range(struct inode *inode, continue; } - if (buffer_delay(bh)) { + /* Check if the buffer is delayed allocated and that it + * is not yet mapped. (when da-buffers are mapped during + * their writeout, their da_mapped bit is set.) + */ + if (buffer_delay(bh) && !buffer_da_mapped(bh)) { page_cache_release(page); trace_ext4_find_delalloc_range(inode, lblk_start, lblk_end, @@ -4106,6 +4091,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, ext4_da_update_reserve_space(inode, allocated_clusters, 1); if (reserved_clusters < allocated_clusters) { + struct ext4_inode_info *ei = EXT4_I(inode); int reservation = allocated_clusters - reserved_clusters; /* @@ -4148,11 +4134,11 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, * remaining blocks finally gets written, we * could claim them. */ - while (reservation) { - ext4_da_reserve_space(inode, - map->m_lblk); - reservation--; - } + dquot_reserve_block(inode, + EXT4_C2B(sbi, reservation)); + spin_lock(&ei->i_block_reservation_lock); + ei->i_reserved_data_blocks += reservation; + spin_unlock(&ei->i_block_reservation_lock); } } } diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 2dcd4fed96ec..1380cd29c312 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -397,6 +397,49 @@ static pgoff_t ext4_num_dirty_pages(struct inode *inode, pgoff_t idx, return num; } +/* + * Sets the BH_Da_Mapped bit on the buffer heads corresponding to the given map. + */ +static void set_buffers_da_mapped(struct inode *inode, + struct ext4_map_blocks *map) +{ + struct address_space *mapping = inode->i_mapping; + struct pagevec pvec; + int i, nr_pages; + pgoff_t index, end; + + index = map->m_lblk >> (PAGE_CACHE_SHIFT - inode->i_blkbits); + end = (map->m_lblk + map->m_len - 1) >> + (PAGE_CACHE_SHIFT - inode->i_blkbits); + + pagevec_init(&pvec, 0); + while (index <= end) { + nr_pages = pagevec_lookup(&pvec, mapping, index, + min(end - index + 1, + (pgoff_t)PAGEVEC_SIZE)); + if (nr_pages == 0) + break; + for (i = 0; i < nr_pages; i++) { + struct page *page = pvec.pages[i]; + struct buffer_head *bh, *head; + + if (unlikely(page->mapping != mapping) || + !PageDirty(page)) + break; + + if (page_has_buffers(page)) { + bh = head = page_buffers(page); + do { + set_buffer_da_mapped(bh); + bh = bh->b_this_page; + } while (bh != head); + } + index++; + } + pagevec_release(&pvec); + } +} + /* * The ext4_map_blocks() function tries to look up the requested blocks, * and returns if the blocks are already mapped. @@ -516,9 +559,17 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode, (flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE)) ext4_da_update_reserve_space(inode, retval, 1); } - if (flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) + if (flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) { ext4_clear_inode_state(inode, EXT4_STATE_DELALLOC_RESERVED); + /* If we have successfully mapped the delayed allocated blocks, + * set the BH_Da_Mapped bit on them. Its important to do this + * under the protection of i_data_sem. + */ + if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) + set_buffers_da_mapped(inode, map); + } + up_write((&EXT4_I(inode)->i_data_sem)); if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) { int ret = check_block_validity(inode, map); @@ -1038,7 +1089,7 @@ static int ext4_journalled_write_end(struct file *file, /* * Reserve a single cluster located at lblock */ -int ext4_da_reserve_space(struct inode *inode, ext4_lblk_t lblock) +static int ext4_da_reserve_space(struct inode *inode, ext4_lblk_t lblock) { int retries = 0; struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); @@ -1153,6 +1204,7 @@ static void ext4_da_page_release_reservation(struct page *page, if ((offset <= curr_off) && (buffer_delay(bh))) { to_release++; clear_buffer_delay(bh); + clear_buffer_da_mapped(bh); } curr_off = next_off; } while ((bh = bh->b_this_page) != head); @@ -1271,6 +1323,8 @@ static int mpage_da_submit_io(struct mpage_da_data *mpd, clear_buffer_delay(bh); bh->b_blocknr = pblock; } + if (buffer_da_mapped(bh)) + clear_buffer_da_mapped(bh); if (buffer_unwritten(bh) || buffer_mapped(bh)) BUG_ON(bh->b_blocknr != pblock); @@ -1603,6 +1657,66 @@ static int ext4_bh_delay_or_unwritten(handle_t *handle, struct buffer_head *bh) return (buffer_delay(bh) || buffer_unwritten(bh)) && buffer_dirty(bh); } +/* + * This function is grabs code from the very beginning of + * ext4_map_blocks, but assumes that the caller is from delayed write + * time. This function looks up the requested blocks and sets the + * buffer delay bit under the protection of i_data_sem. + */ +static int ext4_da_map_blocks(struct inode *inode, sector_t iblock, + struct ext4_map_blocks *map, + struct buffer_head *bh) +{ + int retval; + sector_t invalid_block = ~((sector_t) 0xffff); + + if (invalid_block < ext4_blocks_count(EXT4_SB(inode->i_sb)->s_es)) + invalid_block = ~0; + + map->m_flags = 0; + ext_debug("ext4_da_map_blocks(): inode %lu, max_blocks %u," + "logical block %lu\n", inode->i_ino, map->m_len, + (unsigned long) map->m_lblk); + /* + * Try to see if we can get the block without requesting a new + * file system block. + */ + down_read((&EXT4_I(inode)->i_data_sem)); + if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) + retval = ext4_ext_map_blocks(NULL, inode, map, 0); + else + retval = ext4_ind_map_blocks(NULL, inode, map, 0); + + if (retval == 0) { + /* + * XXX: __block_prepare_write() unmaps passed block, + * is it OK? + */ + /* If the block was allocated from previously allocated cluster, + * then we dont need to reserve it again. */ + if (!(map->m_flags & EXT4_MAP_FROM_CLUSTER)) { + retval = ext4_da_reserve_space(inode, iblock); + if (retval) + /* not enough space to reserve */ + goto out_unlock; + } + + /* Clear EXT4_MAP_FROM_CLUSTER flag since its purpose is served + * and it should not appear on the bh->b_state. + */ + map->m_flags &= ~EXT4_MAP_FROM_CLUSTER; + + map_bh(bh, inode->i_sb, invalid_block); + set_buffer_new(bh); + set_buffer_delay(bh); + } + +out_unlock: + up_read((&EXT4_I(inode)->i_data_sem)); + + return retval; +} + /* * This is a special get_blocks_t callback which is used by * ext4_da_write_begin(). It will either return mapped block or @@ -1620,10 +1734,6 @@ static int ext4_da_get_block_prep(struct inode *inode, sector_t iblock, { struct ext4_map_blocks map; int ret = 0; - sector_t invalid_block = ~((sector_t) 0xffff); - - if (invalid_block < ext4_blocks_count(EXT4_SB(inode->i_sb)->s_es)) - invalid_block = ~0; BUG_ON(create == 0); BUG_ON(bh->b_size != inode->i_sb->s_blocksize); @@ -1636,29 +1746,9 @@ static int ext4_da_get_block_prep(struct inode *inode, sector_t iblock, * preallocated blocks are unmapped but should treated * the same as allocated blocks. */ - ret = ext4_map_blocks(NULL, inode, &map, 0); - if (ret < 0) + ret = ext4_da_map_blocks(inode, iblock, &map, bh); + if (ret <= 0) return ret; - if (ret == 0) { - if (buffer_delay(bh)) - return 0; /* Not sure this could or should happen */ - /* - * XXX: __block_write_begin() unmaps passed block, is it OK? - */ - /* If the block was allocated from previously allocated cluster, - * then we dont need to reserve it again. */ - if (!(map.m_flags & EXT4_MAP_FROM_CLUSTER)) { - ret = ext4_da_reserve_space(inode, iblock); - if (ret) - /* not enough space to reserve */ - return ret; - } - - map_bh(bh, inode->i_sb, invalid_block); - set_buffer_new(bh); - set_buffer_delay(bh); - return 0; - } map_bh(bh, inode->i_sb, map.m_pblk); bh->b_state = (bh->b_state & ~EXT4_MAP_FLAGS) | map.m_flags; From 484fc254b88257a2d8b3759aa062e8e8b35e0988 Mon Sep 17 00:00:00 2001 From: Wang Sheng-Hui Date: Thu, 8 Sep 2011 12:32:14 +0200 Subject: [PATCH 0140/1531] elevator: use ELV_NAME_MAX instead of magic number 16 for chosen_elevator We have ELV_NAME_MAX defined to 16, and hence we should use it instead of the magic nubmer 16 for elevator's name string. Signed-off-by: Wang Sheng-Hui Signed-off-by: Jens Axboe --- block/elevator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/elevator.c b/block/elevator.c index a3b64bc71d88..cb332cb7ac6b 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -182,7 +182,7 @@ static void elevator_attach(struct request_queue *q, struct elevator_queue *eq, eq->elevator_data = data; } -static char chosen_elevator[16]; +static char chosen_elevator[ELV_NAME_MAX]; static int __init elevator_setup(char *str) { From 166e1f901b01872e8b70733a3f2e2c6980389cf8 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 12 Sep 2011 12:08:27 +0200 Subject: [PATCH 0141/1531] block: export __make_request Avoid the hacks need for request based device mappers currently by simply exporting the symbol instead of trying to get it through the back door. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- block/blk-core.c | 5 ++--- drivers/md/dm.c | 13 +------------ include/linux/blkdev.h | 2 ++ 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/block/blk-core.c b/block/blk-core.c index b627558c461f..56ef387e7d27 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -38,8 +38,6 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(block_bio_remap); EXPORT_TRACEPOINT_SYMBOL_GPL(block_rq_remap); EXPORT_TRACEPOINT_SYMBOL_GPL(block_bio_complete); -static int __make_request(struct request_queue *q, struct bio *bio); - /* * For the allocated request tables */ @@ -1213,7 +1211,7 @@ void init_request_from_bio(struct request *req, struct bio *bio) blk_rq_bio_prep(req->q, req, bio); } -static int __make_request(struct request_queue *q, struct bio *bio) +int __make_request(struct request_queue *q, struct bio *bio) { const bool sync = !!(bio->bi_rw & REQ_SYNC); struct blk_plug *plug; @@ -1317,6 +1315,7 @@ static int __make_request(struct request_queue *q, struct bio *bio) out: return 0; } +EXPORT_SYMBOL_GPL(__make_request); /* for device mapper only */ /* * If bio->bi_dev is a partition, remap the location diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 52b39f335bb3..d8d7b8d9dd28 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -180,9 +180,6 @@ struct mapped_device { /* forced geometry settings */ struct hd_geometry geometry; - /* For saving the address of __make_request for request based dm */ - make_request_fn *saved_make_request_fn; - /* sysfs handle */ struct kobject kobj; @@ -1420,13 +1417,6 @@ static int _dm_request(struct request_queue *q, struct bio *bio) return 0; } -static int dm_make_request(struct request_queue *q, struct bio *bio) -{ - struct mapped_device *md = q->queuedata; - - return md->saved_make_request_fn(q, bio); /* call __make_request() */ -} - static int dm_request_based(struct mapped_device *md) { return blk_queue_stackable(md->queue); @@ -1437,7 +1427,7 @@ static int dm_request(struct request_queue *q, struct bio *bio) struct mapped_device *md = q->queuedata; if (dm_request_based(md)) - return dm_make_request(q, bio); + return __make_request(q, bio); return _dm_request(q, bio); } @@ -2172,7 +2162,6 @@ static int dm_init_request_based_queue(struct mapped_device *md) return 0; md->queue = q; - md->saved_make_request_fn = md->queue->make_request_fn; dm_init_md_queue(md); blk_queue_softirq_done(md->queue, dm_softirq_done); blk_queue_prep_rq(md->queue, dm_prep_fn); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 0e67c45b3bc9..e9c3d9b07630 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -675,6 +675,8 @@ extern int scsi_cmd_ioctl(struct request_queue *, struct gendisk *, fmode_t, extern int sg_scsi_ioctl(struct request_queue *, struct gendisk *, fmode_t, struct scsi_ioctl_command __user *); +extern int __make_request(struct request_queue *q, struct bio *bio); + /* * A queue has just exitted congestion. Note this in the global counter of * congested queues, and wake up anyone who was waiting for requests to be From c20e8de27fef9f59869c81c288ad6cf28200e00c Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 12 Sep 2011 12:03:37 +0200 Subject: [PATCH 0142/1531] block: rename __make_request() to blk_queue_bio() Now that it's exported, lets put it in a more sane namespace. Signed-off-by: Jens Axboe --- block/blk-core.c | 6 +++--- drivers/md/dm.c | 2 +- include/linux/blkdev.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/block/blk-core.c b/block/blk-core.c index 56ef387e7d27..ab673f0b8c30 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -540,7 +540,7 @@ blk_init_allocated_queue_node(struct request_queue *q, request_fn_proc *rfn, /* * This also sets hw/phys segments, boundary and size */ - blk_queue_make_request(q, __make_request); + blk_queue_make_request(q, blk_queue_bio); q->sg_reserved_size = INT_MAX; @@ -1211,7 +1211,7 @@ void init_request_from_bio(struct request *req, struct bio *bio) blk_rq_bio_prep(req->q, req, bio); } -int __make_request(struct request_queue *q, struct bio *bio) +int blk_queue_bio(struct request_queue *q, struct bio *bio) { const bool sync = !!(bio->bi_rw & REQ_SYNC); struct blk_plug *plug; @@ -1315,7 +1315,7 @@ int __make_request(struct request_queue *q, struct bio *bio) out: return 0; } -EXPORT_SYMBOL_GPL(__make_request); /* for device mapper only */ +EXPORT_SYMBOL_GPL(blk_queue_bio); /* for device mapper only */ /* * If bio->bi_dev is a partition, remap the location diff --git a/drivers/md/dm.c b/drivers/md/dm.c index d8d7b8d9dd28..78b20868bcbc 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -1427,7 +1427,7 @@ static int dm_request(struct request_queue *q, struct bio *bio) struct mapped_device *md = q->queuedata; if (dm_request_based(md)) - return __make_request(q, bio); + return blk_queue_bio(q, bio); return _dm_request(q, bio); } diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index e9c3d9b07630..085f95414c7f 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -675,7 +675,7 @@ extern int scsi_cmd_ioctl(struct request_queue *, struct gendisk *, fmode_t, extern int sg_scsi_ioctl(struct request_queue *, struct gendisk *, fmode_t, struct scsi_ioctl_command __user *); -extern int __make_request(struct request_queue *q, struct bio *bio); +extern int blk_queue_bio(struct request_queue *q, struct bio *bio); /* * A queue has just exitted congestion. Note this in the global counter of From 5a7bbad27a410350e64a2d7f5ec18fc73836c14f Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 12 Sep 2011 12:12:01 +0200 Subject: [PATCH 0143/1531] block: remove support for bio remapping from ->make_request There is very little benefit in allowing to let a ->make_request instance update the bios device and sector and loop around it in __generic_make_request when we can archive the same through calling generic_make_request from the driver and letting the loop in generic_make_request handle it. Note that various drivers got the return value from ->make_request and returned non-zero values for errors. Signed-off-by: Christoph Hellwig Acked-by: NeilBrown Signed-off-by: Jens Axboe --- arch/m68k/emu/nfblock.c | 3 +- arch/powerpc/sysdev/axonram.c | 8 +- block/blk-core.c | 153 +++++++++++++------------------- drivers/block/aoe/aoeblk.c | 14 ++- drivers/block/brd.c | 4 +- drivers/block/drbd/drbd_int.h | 2 +- drivers/block/drbd/drbd_req.c | 8 +- drivers/block/loop.c | 5 +- drivers/block/pktcdvd.c | 11 ++- drivers/block/ps3vram.c | 6 +- drivers/block/umem.c | 4 +- drivers/md/dm.c | 14 +-- drivers/md/faulty.c | 14 +-- drivers/md/linear.c | 17 ++-- drivers/md/md.c | 12 +-- drivers/md/md.h | 2 +- drivers/md/multipath.c | 8 +- drivers/md/raid0.c | 22 ++--- drivers/md/raid1.c | 8 +- drivers/md/raid10.c | 19 ++-- drivers/md/raid5.c | 8 +- drivers/s390/block/dcssblk.c | 7 +- drivers/s390/block/xpram.c | 5 +- drivers/staging/zram/zram_drv.c | 8 +- include/linux/blkdev.h | 4 +- 25 files changed, 151 insertions(+), 215 deletions(-) diff --git a/arch/m68k/emu/nfblock.c b/arch/m68k/emu/nfblock.c index 48e50f8c1c7e..e3011338ab40 100644 --- a/arch/m68k/emu/nfblock.c +++ b/arch/m68k/emu/nfblock.c @@ -59,7 +59,7 @@ struct nfhd_device { struct gendisk *disk; }; -static int nfhd_make_request(struct request_queue *queue, struct bio *bio) +static void nfhd_make_request(struct request_queue *queue, struct bio *bio) { struct nfhd_device *dev = queue->queuedata; struct bio_vec *bvec; @@ -76,7 +76,6 @@ static int nfhd_make_request(struct request_queue *queue, struct bio *bio) sec += len; } bio_endio(bio, 0); - return 0; } static int nfhd_getgeo(struct block_device *bdev, struct hd_geometry *geo) diff --git a/arch/powerpc/sysdev/axonram.c b/arch/powerpc/sysdev/axonram.c index 265f0f09395a..ba4271919062 100644 --- a/arch/powerpc/sysdev/axonram.c +++ b/arch/powerpc/sysdev/axonram.c @@ -104,7 +104,7 @@ axon_ram_irq_handler(int irq, void *dev) * axon_ram_make_request - make_request() method for block device * @queue, @bio: see blk_queue_make_request() */ -static int +static void axon_ram_make_request(struct request_queue *queue, struct bio *bio) { struct axon_ram_bank *bank = bio->bi_bdev->bd_disk->private_data; @@ -113,7 +113,6 @@ axon_ram_make_request(struct request_queue *queue, struct bio *bio) struct bio_vec *vec; unsigned int transfered; unsigned short idx; - int rc = 0; phys_mem = bank->io_addr + (bio->bi_sector << AXON_RAM_SECTOR_SHIFT); phys_end = bank->io_addr + bank->size; @@ -121,8 +120,7 @@ axon_ram_make_request(struct request_queue *queue, struct bio *bio) bio_for_each_segment(vec, bio, idx) { if (unlikely(phys_mem + vec->bv_len > phys_end)) { bio_io_error(bio); - rc = -ERANGE; - break; + return; } user_mem = page_address(vec->bv_page) + vec->bv_offset; @@ -135,8 +133,6 @@ axon_ram_make_request(struct request_queue *queue, struct bio *bio) transfered += vec->bv_len; } bio_endio(bio, 0); - - return rc; } /** diff --git a/block/blk-core.c b/block/blk-core.c index ab673f0b8c30..f58e019be67b 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -1211,7 +1211,7 @@ void init_request_from_bio(struct request *req, struct bio *bio) blk_rq_bio_prep(req->q, req, bio); } -int blk_queue_bio(struct request_queue *q, struct bio *bio) +void blk_queue_bio(struct request_queue *q, struct bio *bio) { const bool sync = !!(bio->bi_rw & REQ_SYNC); struct blk_plug *plug; @@ -1236,7 +1236,7 @@ int blk_queue_bio(struct request_queue *q, struct bio *bio) * any locks. */ if (attempt_plug_merge(current, q, bio)) - goto out; + return; spin_lock_irq(q->queue_lock); @@ -1312,8 +1312,6 @@ int blk_queue_bio(struct request_queue *q, struct bio *bio) out_unlock: spin_unlock_irq(q->queue_lock); } -out: - return 0; } EXPORT_SYMBOL_GPL(blk_queue_bio); /* for device mapper only */ @@ -1441,112 +1439,85 @@ static inline int bio_check_eod(struct bio *bio, unsigned int nr_sectors) static inline void __generic_make_request(struct bio *bio) { struct request_queue *q; - sector_t old_sector; - int ret, nr_sectors = bio_sectors(bio); - dev_t old_dev; + int nr_sectors = bio_sectors(bio); int err = -EIO; + char b[BDEVNAME_SIZE]; + struct hd_struct *part; might_sleep(); if (bio_check_eod(bio, nr_sectors)) goto end_io; - /* - * Resolve the mapping until finished. (drivers are - * still free to implement/resolve their own stacking - * by explicitly returning 0) - * - * NOTE: we don't repeat the blk_size check for each new device. - * Stacking drivers are expected to know what they are doing. - */ - old_sector = -1; - old_dev = 0; - do { - char b[BDEVNAME_SIZE]; - struct hd_struct *part; - - q = bdev_get_queue(bio->bi_bdev); - if (unlikely(!q)) { - printk(KERN_ERR - "generic_make_request: Trying to access " - "nonexistent block-device %s (%Lu)\n", - bdevname(bio->bi_bdev, b), - (long long) bio->bi_sector); - goto end_io; - } - - if (unlikely(!(bio->bi_rw & REQ_DISCARD) && - nr_sectors > queue_max_hw_sectors(q))) { - printk(KERN_ERR "bio too big device %s (%u > %u)\n", - bdevname(bio->bi_bdev, b), - bio_sectors(bio), - queue_max_hw_sectors(q)); - goto end_io; - } - - if (unlikely(test_bit(QUEUE_FLAG_DEAD, &q->queue_flags))) - goto end_io; - - part = bio->bi_bdev->bd_part; - if (should_fail_request(part, bio->bi_size) || - should_fail_request(&part_to_disk(part)->part0, - bio->bi_size)) - goto end_io; + q = bdev_get_queue(bio->bi_bdev); + if (unlikely(!q)) { + printk(KERN_ERR + "generic_make_request: Trying to access " + "nonexistent block-device %s (%Lu)\n", + bdevname(bio->bi_bdev, b), + (long long) bio->bi_sector); + goto end_io; + } - /* - * If this device has partitions, remap block n - * of partition p to block n+start(p) of the disk. - */ - blk_partition_remap(bio); + if (unlikely(!(bio->bi_rw & REQ_DISCARD) && + nr_sectors > queue_max_hw_sectors(q))) { + printk(KERN_ERR "bio too big device %s (%u > %u)\n", + bdevname(bio->bi_bdev, b), + bio_sectors(bio), + queue_max_hw_sectors(q)); + goto end_io; + } - if (bio_integrity_enabled(bio) && bio_integrity_prep(bio)) - goto end_io; + if (unlikely(test_bit(QUEUE_FLAG_DEAD, &q->queue_flags))) + goto end_io; - if (old_sector != -1) - trace_block_bio_remap(q, bio, old_dev, old_sector); + part = bio->bi_bdev->bd_part; + if (should_fail_request(part, bio->bi_size) || + should_fail_request(&part_to_disk(part)->part0, + bio->bi_size)) + goto end_io; - old_sector = bio->bi_sector; - old_dev = bio->bi_bdev->bd_dev; + /* + * If this device has partitions, remap block n + * of partition p to block n+start(p) of the disk. + */ + blk_partition_remap(bio); - if (bio_check_eod(bio, nr_sectors)) - goto end_io; + if (bio_integrity_enabled(bio) && bio_integrity_prep(bio)) + goto end_io; - /* - * Filter flush bio's early so that make_request based - * drivers without flush support don't have to worry - * about them. - */ - if ((bio->bi_rw & (REQ_FLUSH | REQ_FUA)) && !q->flush_flags) { - bio->bi_rw &= ~(REQ_FLUSH | REQ_FUA); - if (!nr_sectors) { - err = 0; - goto end_io; - } - } + if (bio_check_eod(bio, nr_sectors)) + goto end_io; - if ((bio->bi_rw & REQ_DISCARD) && - (!blk_queue_discard(q) || - ((bio->bi_rw & REQ_SECURE) && - !blk_queue_secdiscard(q)))) { - err = -EOPNOTSUPP; + /* + * Filter flush bio's early so that make_request based + * drivers without flush support don't have to worry + * about them. + */ + if ((bio->bi_rw & (REQ_FLUSH | REQ_FUA)) && !q->flush_flags) { + bio->bi_rw &= ~(REQ_FLUSH | REQ_FUA); + if (!nr_sectors) { + err = 0; goto end_io; } + } - if (blk_throtl_bio(q, &bio)) - goto end_io; - - /* - * If bio = NULL, bio has been throttled and will be submitted - * later. - */ - if (!bio) - break; - - trace_block_bio_queue(q, bio); + if ((bio->bi_rw & REQ_DISCARD) && + (!blk_queue_discard(q) || + ((bio->bi_rw & REQ_SECURE) && + !blk_queue_secdiscard(q)))) { + err = -EOPNOTSUPP; + goto end_io; + } - ret = q->make_request_fn(q, bio); - } while (ret); + if (blk_throtl_bio(q, &bio)) + goto end_io; + /* if bio = NULL, bio has been throttled and will be submitted later. */ + if (!bio) + return; + trace_block_bio_queue(q, bio); + q->make_request_fn(q, bio); return; end_io: diff --git a/drivers/block/aoe/aoeblk.c b/drivers/block/aoe/aoeblk.c index 528f6318ded1..167ba0af47f5 100644 --- a/drivers/block/aoe/aoeblk.c +++ b/drivers/block/aoe/aoeblk.c @@ -159,7 +159,7 @@ aoeblk_release(struct gendisk *disk, fmode_t mode) return 0; } -static int +static void aoeblk_make_request(struct request_queue *q, struct bio *bio) { struct sk_buff_head queue; @@ -172,25 +172,25 @@ aoeblk_make_request(struct request_queue *q, struct bio *bio) if (bio == NULL) { printk(KERN_ERR "aoe: bio is NULL\n"); BUG(); - return 0; + return; } d = bio->bi_bdev->bd_disk->private_data; if (d == NULL) { printk(KERN_ERR "aoe: bd_disk->private_data is NULL\n"); BUG(); bio_endio(bio, -ENXIO); - return 0; + return; } else if (bio->bi_io_vec == NULL) { printk(KERN_ERR "aoe: bi_io_vec is NULL\n"); BUG(); bio_endio(bio, -ENXIO); - return 0; + return; } buf = mempool_alloc(d->bufpool, GFP_NOIO); if (buf == NULL) { printk(KERN_INFO "aoe: buf allocation failure\n"); bio_endio(bio, -ENOMEM); - return 0; + return; } memset(buf, 0, sizeof(*buf)); INIT_LIST_HEAD(&buf->bufs); @@ -211,7 +211,7 @@ aoeblk_make_request(struct request_queue *q, struct bio *bio) spin_unlock_irqrestore(&d->lock, flags); mempool_free(buf, d->bufpool); bio_endio(bio, -ENXIO); - return 0; + return; } list_add_tail(&buf->bufs, &d->bufq); @@ -222,8 +222,6 @@ aoeblk_make_request(struct request_queue *q, struct bio *bio) spin_unlock_irqrestore(&d->lock, flags); aoenet_xmit(&queue); - - return 0; } static int diff --git a/drivers/block/brd.c b/drivers/block/brd.c index dba1c32e1ddf..d22119d49e53 100644 --- a/drivers/block/brd.c +++ b/drivers/block/brd.c @@ -323,7 +323,7 @@ static int brd_do_bvec(struct brd_device *brd, struct page *page, return err; } -static int brd_make_request(struct request_queue *q, struct bio *bio) +static void brd_make_request(struct request_queue *q, struct bio *bio) { struct block_device *bdev = bio->bi_bdev; struct brd_device *brd = bdev->bd_disk->private_data; @@ -359,8 +359,6 @@ static int brd_make_request(struct request_queue *q, struct bio *bio) out: bio_endio(bio, err); - - return 0; } #ifdef CONFIG_BLK_DEV_XIP diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h index ef2ceed3be4b..36eee3969a98 100644 --- a/drivers/block/drbd/drbd_int.h +++ b/drivers/block/drbd/drbd_int.h @@ -1507,7 +1507,7 @@ extern void drbd_free_mdev(struct drbd_conf *mdev); extern int proc_details; /* drbd_req */ -extern int drbd_make_request(struct request_queue *q, struct bio *bio); +extern void drbd_make_request(struct request_queue *q, struct bio *bio); extern int drbd_read_remote(struct drbd_conf *mdev, struct drbd_request *req); extern int drbd_merge_bvec(struct request_queue *q, struct bvec_merge_data *bvm, struct bio_vec *bvec); extern int is_valid_ar_handle(struct drbd_request *, sector_t); diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c index 3424d675b769..4a0f314086e5 100644 --- a/drivers/block/drbd/drbd_req.c +++ b/drivers/block/drbd/drbd_req.c @@ -1073,7 +1073,7 @@ static int drbd_fail_request_early(struct drbd_conf *mdev, int is_write) return 0; } -int drbd_make_request(struct request_queue *q, struct bio *bio) +void drbd_make_request(struct request_queue *q, struct bio *bio) { unsigned int s_enr, e_enr; struct drbd_conf *mdev = (struct drbd_conf *) q->queuedata; @@ -1081,7 +1081,7 @@ int drbd_make_request(struct request_queue *q, struct bio *bio) if (drbd_fail_request_early(mdev, bio_data_dir(bio) & WRITE)) { bio_endio(bio, -EPERM); - return 0; + return; } start_time = jiffies; @@ -1100,7 +1100,8 @@ int drbd_make_request(struct request_queue *q, struct bio *bio) if (likely(s_enr == e_enr)) { inc_ap_bio(mdev, 1); - return drbd_make_request_common(mdev, bio, start_time); + drbd_make_request_common(mdev, bio, start_time); + return; } /* can this bio be split generically? @@ -1148,7 +1149,6 @@ int drbd_make_request(struct request_queue *q, struct bio *bio) bio_pair_release(bp); } - return 0; } /* This is called by bio_add_page(). With this function we reduce diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 76c8da78212b..8360239d553c 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -514,7 +514,7 @@ static struct bio *loop_get_bio(struct loop_device *lo) return bio_list_pop(&lo->lo_bio_list); } -static int loop_make_request(struct request_queue *q, struct bio *old_bio) +static void loop_make_request(struct request_queue *q, struct bio *old_bio) { struct loop_device *lo = q->queuedata; int rw = bio_rw(old_bio); @@ -532,12 +532,11 @@ static int loop_make_request(struct request_queue *q, struct bio *old_bio) loop_add_bio(lo, old_bio); wake_up(&lo->lo_event); spin_unlock_irq(&lo->lo_lock); - return 0; + return; out: spin_unlock_irq(&lo->lo_lock); bio_io_error(old_bio); - return 0; } struct switch_request { diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index e133f094ab08..a63b0a2b7805 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -2444,7 +2444,7 @@ static void pkt_end_io_read_cloned(struct bio *bio, int err) pkt_bio_finished(pd); } -static int pkt_make_request(struct request_queue *q, struct bio *bio) +static void pkt_make_request(struct request_queue *q, struct bio *bio) { struct pktcdvd_device *pd; char b[BDEVNAME_SIZE]; @@ -2473,7 +2473,7 @@ static int pkt_make_request(struct request_queue *q, struct bio *bio) cloned_bio->bi_end_io = pkt_end_io_read_cloned; pd->stats.secs_r += bio->bi_size >> 9; pkt_queue_bio(pd, cloned_bio); - return 0; + return; } if (!test_bit(PACKET_WRITABLE, &pd->flags)) { @@ -2509,7 +2509,7 @@ static int pkt_make_request(struct request_queue *q, struct bio *bio) pkt_make_request(q, &bp->bio1); pkt_make_request(q, &bp->bio2); bio_pair_release(bp); - return 0; + return; } } @@ -2533,7 +2533,7 @@ static int pkt_make_request(struct request_queue *q, struct bio *bio) } spin_unlock(&pkt->lock); spin_unlock(&pd->cdrw.active_list_lock); - return 0; + return; } else { blocked_bio = 1; } @@ -2584,10 +2584,9 @@ static int pkt_make_request(struct request_queue *q, struct bio *bio) */ wake_up(&pd->wqueue); } - return 0; + return; end_io: bio_io_error(bio); - return 0; } diff --git a/drivers/block/ps3vram.c b/drivers/block/ps3vram.c index b3bdb8af89cf..7fad7af87eb2 100644 --- a/drivers/block/ps3vram.c +++ b/drivers/block/ps3vram.c @@ -596,7 +596,7 @@ static struct bio *ps3vram_do_bio(struct ps3_system_bus_device *dev, return next; } -static int ps3vram_make_request(struct request_queue *q, struct bio *bio) +static void ps3vram_make_request(struct request_queue *q, struct bio *bio) { struct ps3_system_bus_device *dev = q->queuedata; struct ps3vram_priv *priv = ps3_system_bus_get_drvdata(dev); @@ -610,13 +610,11 @@ static int ps3vram_make_request(struct request_queue *q, struct bio *bio) spin_unlock_irq(&priv->lock); if (busy) - return 0; + return; do { bio = ps3vram_do_bio(dev, bio); } while (bio); - - return 0; } static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) diff --git a/drivers/block/umem.c b/drivers/block/umem.c index 031ca720d926..aa2712060bfb 100644 --- a/drivers/block/umem.c +++ b/drivers/block/umem.c @@ -513,7 +513,7 @@ static void process_page(unsigned long data) } } -static int mm_make_request(struct request_queue *q, struct bio *bio) +static void mm_make_request(struct request_queue *q, struct bio *bio) { struct cardinfo *card = q->queuedata; pr_debug("mm_make_request %llu %u\n", @@ -525,7 +525,7 @@ static int mm_make_request(struct request_queue *q, struct bio *bio) card->biotail = &bio->bi_next; spin_unlock_irq(&card->lock); - return 0; + return; } static irqreturn_t mm_interrupt(int irq, void *__card) diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 78b20868bcbc..7b986e77b75e 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -1388,7 +1388,7 @@ static int dm_merge_bvec(struct request_queue *q, * The request function that just remaps the bio built up by * dm_merge_bvec. */ -static int _dm_request(struct request_queue *q, struct bio *bio) +static void _dm_request(struct request_queue *q, struct bio *bio) { int rw = bio_data_dir(bio); struct mapped_device *md = q->queuedata; @@ -1409,12 +1409,12 @@ static int _dm_request(struct request_queue *q, struct bio *bio) queue_io(md, bio); else bio_io_error(bio); - return 0; + return; } __split_and_process_bio(md, bio); up_read(&md->io_lock); - return 0; + return; } static int dm_request_based(struct mapped_device *md) @@ -1422,14 +1422,14 @@ static int dm_request_based(struct mapped_device *md) return blk_queue_stackable(md->queue); } -static int dm_request(struct request_queue *q, struct bio *bio) +static void dm_request(struct request_queue *q, struct bio *bio) { struct mapped_device *md = q->queuedata; if (dm_request_based(md)) - return blk_queue_bio(q, bio); - - return _dm_request(q, bio); + blk_queue_bio(q, bio); + else + _dm_request(q, bio); } void dm_dispatch_request(struct request *rq) diff --git a/drivers/md/faulty.c b/drivers/md/faulty.c index 23078dabb6df..5ef304d4341c 100644 --- a/drivers/md/faulty.c +++ b/drivers/md/faulty.c @@ -169,7 +169,7 @@ static void add_sector(conf_t *conf, sector_t start, int mode) conf->nfaults = n+1; } -static int make_request(mddev_t *mddev, struct bio *bio) +static void make_request(mddev_t *mddev, struct bio *bio) { conf_t *conf = mddev->private; int failit = 0; @@ -181,7 +181,7 @@ static int make_request(mddev_t *mddev, struct bio *bio) * just fail immediately */ bio_endio(bio, -EIO); - return 0; + return; } if (check_sector(conf, bio->bi_sector, bio->bi_sector+(bio->bi_size>>9), @@ -211,15 +211,15 @@ static int make_request(mddev_t *mddev, struct bio *bio) } if (failit) { struct bio *b = bio_clone_mddev(bio, GFP_NOIO, mddev); + b->bi_bdev = conf->rdev->bdev; b->bi_private = bio; b->bi_end_io = faulty_fail; - generic_make_request(b); - return 0; - } else { + bio = b; + } else bio->bi_bdev = conf->rdev->bdev; - return 1; - } + + generic_make_request(bio); } static void status(struct seq_file *seq, mddev_t *mddev) diff --git a/drivers/md/linear.c b/drivers/md/linear.c index 6cd2c313e800..c6ee491d98e7 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -264,14 +264,14 @@ static int linear_stop (mddev_t *mddev) return 0; } -static int linear_make_request (mddev_t *mddev, struct bio *bio) +static void linear_make_request (mddev_t *mddev, struct bio *bio) { dev_info_t *tmp_dev; sector_t start_sector; if (unlikely(bio->bi_rw & REQ_FLUSH)) { md_flush_request(mddev, bio); - return 0; + return; } rcu_read_lock(); @@ -293,7 +293,7 @@ static int linear_make_request (mddev_t *mddev, struct bio *bio) (unsigned long long)start_sector); rcu_read_unlock(); bio_io_error(bio); - return 0; + return; } if (unlikely(bio->bi_sector + (bio->bi_size >> 9) > tmp_dev->end_sector)) { @@ -307,20 +307,17 @@ static int linear_make_request (mddev_t *mddev, struct bio *bio) bp = bio_split(bio, end_sector - bio->bi_sector); - if (linear_make_request(mddev, &bp->bio1)) - generic_make_request(&bp->bio1); - if (linear_make_request(mddev, &bp->bio2)) - generic_make_request(&bp->bio2); + linear_make_request(mddev, &bp->bio1); + linear_make_request(mddev, &bp->bio2); bio_pair_release(bp); - return 0; + return; } bio->bi_bdev = tmp_dev->rdev->bdev; bio->bi_sector = bio->bi_sector - start_sector + tmp_dev->rdev->data_offset; rcu_read_unlock(); - - return 1; + generic_make_request(bio); } static void linear_status (struct seq_file *seq, mddev_t *mddev) diff --git a/drivers/md/md.c b/drivers/md/md.c index 8e221a20f5d9..5c2178562c96 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -330,18 +330,17 @@ static DEFINE_SPINLOCK(all_mddevs_lock); * call has finished, the bio has been linked into some internal structure * and so is visible to ->quiesce(), so we don't need the refcount any more. */ -static int md_make_request(struct request_queue *q, struct bio *bio) +static void md_make_request(struct request_queue *q, struct bio *bio) { const int rw = bio_data_dir(bio); mddev_t *mddev = q->queuedata; - int rv; int cpu; unsigned int sectors; if (mddev == NULL || mddev->pers == NULL || !mddev->ready) { bio_io_error(bio); - return 0; + return; } smp_rmb(); /* Ensure implications of 'active' are visible */ rcu_read_lock(); @@ -366,7 +365,7 @@ static int md_make_request(struct request_queue *q, struct bio *bio) * go away inside make_request */ sectors = bio_sectors(bio); - rv = mddev->pers->make_request(mddev, bio); + mddev->pers->make_request(mddev, bio); cpu = part_stat_lock(); part_stat_inc(cpu, &mddev->gendisk->part0, ios[rw]); @@ -375,8 +374,6 @@ static int md_make_request(struct request_queue *q, struct bio *bio) if (atomic_dec_and_test(&mddev->active_io) && mddev->suspended) wake_up(&mddev->sb_wait); - - return rv; } /* mddev_suspend makes sure no new requests are submitted @@ -475,8 +472,7 @@ static void md_submit_flush_data(struct work_struct *ws) bio_endio(bio, 0); else { bio->bi_rw &= ~REQ_FLUSH; - if (mddev->pers->make_request(mddev, bio)) - generic_make_request(bio); + mddev->pers->make_request(mddev, bio); } mddev->flush_bio = NULL; diff --git a/drivers/md/md.h b/drivers/md/md.h index 1e586bb4452e..bd47847cf7ca 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -424,7 +424,7 @@ struct mdk_personality int level; struct list_head list; struct module *owner; - int (*make_request)(mddev_t *mddev, struct bio *bio); + void (*make_request)(mddev_t *mddev, struct bio *bio); int (*run)(mddev_t *mddev); int (*stop)(mddev_t *mddev); void (*status)(struct seq_file *seq, mddev_t *mddev); diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c index 3535c23af288..407cb5691425 100644 --- a/drivers/md/multipath.c +++ b/drivers/md/multipath.c @@ -106,7 +106,7 @@ static void multipath_end_request(struct bio *bio, int error) rdev_dec_pending(rdev, conf->mddev); } -static int multipath_make_request(mddev_t *mddev, struct bio * bio) +static void multipath_make_request(mddev_t *mddev, struct bio * bio) { multipath_conf_t *conf = mddev->private; struct multipath_bh * mp_bh; @@ -114,7 +114,7 @@ static int multipath_make_request(mddev_t *mddev, struct bio * bio) if (unlikely(bio->bi_rw & REQ_FLUSH)) { md_flush_request(mddev, bio); - return 0; + return; } mp_bh = mempool_alloc(conf->pool, GFP_NOIO); @@ -126,7 +126,7 @@ static int multipath_make_request(mddev_t *mddev, struct bio * bio) if (mp_bh->path < 0) { bio_endio(bio, -EIO); mempool_free(mp_bh, conf->pool); - return 0; + return; } multipath = conf->multipaths + mp_bh->path; @@ -137,7 +137,7 @@ static int multipath_make_request(mddev_t *mddev, struct bio * bio) mp_bh->bio.bi_end_io = multipath_end_request; mp_bh->bio.bi_private = mp_bh; generic_make_request(&mp_bh->bio); - return 0; + return; } static void multipath_status (struct seq_file *seq, mddev_t *mddev) diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index e86bf3682e1e..4066615d61af 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -466,7 +466,7 @@ static inline int is_io_in_chunk_boundary(mddev_t *mddev, } } -static int raid0_make_request(mddev_t *mddev, struct bio *bio) +static void raid0_make_request(mddev_t *mddev, struct bio *bio) { unsigned int chunk_sects; sector_t sector_offset; @@ -475,7 +475,7 @@ static int raid0_make_request(mddev_t *mddev, struct bio *bio) if (unlikely(bio->bi_rw & REQ_FLUSH)) { md_flush_request(mddev, bio); - return 0; + return; } chunk_sects = mddev->chunk_sectors; @@ -495,13 +495,10 @@ static int raid0_make_request(mddev_t *mddev, struct bio *bio) else bp = bio_split(bio, chunk_sects - sector_div(sector, chunk_sects)); - if (raid0_make_request(mddev, &bp->bio1)) - generic_make_request(&bp->bio1); - if (raid0_make_request(mddev, &bp->bio2)) - generic_make_request(&bp->bio2); - + raid0_make_request(mddev, &bp->bio1); + raid0_make_request(mddev, &bp->bio2); bio_pair_release(bp); - return 0; + return; } sector_offset = bio->bi_sector; @@ -511,10 +508,9 @@ static int raid0_make_request(mddev_t *mddev, struct bio *bio) bio->bi_bdev = tmp_dev->bdev; bio->bi_sector = sector_offset + zone->dev_start + tmp_dev->data_offset; - /* - * Let the main block layer submit the IO and resolve recursion: - */ - return 1; + + generic_make_request(bio); + return; bad_map: printk("md/raid0:%s: make_request bug: can't convert block across chunks" @@ -523,7 +519,7 @@ static int raid0_make_request(mddev_t *mddev, struct bio *bio) (unsigned long long)bio->bi_sector, bio->bi_size >> 10); bio_io_error(bio); - return 0; + return; } static void raid0_status(struct seq_file *seq, mddev_t *mddev) diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 32323f0afd89..97f2a5f977b1 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -785,7 +785,7 @@ static void alloc_behind_pages(struct bio *bio, r1bio_t *r1_bio) PRINTK("%dB behind alloc failed, doing sync I/O\n", bio->bi_size); } -static int make_request(mddev_t *mddev, struct bio * bio) +static void make_request(mddev_t *mddev, struct bio * bio) { conf_t *conf = mddev->private; mirror_info_t *mirror; @@ -870,7 +870,7 @@ static int make_request(mddev_t *mddev, struct bio * bio) if (rdisk < 0) { /* couldn't find anywhere to read from */ raid_end_bio_io(r1_bio); - return 0; + return; } mirror = conf->mirrors + rdisk; @@ -928,7 +928,7 @@ static int make_request(mddev_t *mddev, struct bio * bio) goto read_again; } else generic_make_request(read_bio); - return 0; + return; } /* @@ -1119,8 +1119,6 @@ static int make_request(mddev_t *mddev, struct bio * bio) if (do_sync || !bitmap || !plugged) md_wakeup_thread(mddev->thread); - - return 0; } static void status(struct seq_file *seq, mddev_t *mddev) diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 8b29cd4f01c8..04b625e1cb60 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -825,7 +825,7 @@ static void unfreeze_array(conf_t *conf) spin_unlock_irq(&conf->resync_lock); } -static int make_request(mddev_t *mddev, struct bio * bio) +static void make_request(mddev_t *mddev, struct bio * bio) { conf_t *conf = mddev->private; mirror_info_t *mirror; @@ -844,7 +844,7 @@ static int make_request(mddev_t *mddev, struct bio * bio) if (unlikely(bio->bi_rw & REQ_FLUSH)) { md_flush_request(mddev, bio); - return 0; + return; } /* If this request crosses a chunk boundary, we need to @@ -876,10 +876,8 @@ static int make_request(mddev_t *mddev, struct bio * bio) conf->nr_waiting++; spin_unlock_irq(&conf->resync_lock); - if (make_request(mddev, &bp->bio1)) - generic_make_request(&bp->bio1); - if (make_request(mddev, &bp->bio2)) - generic_make_request(&bp->bio2); + make_request(mddev, &bp->bio1); + make_request(mddev, &bp->bio2); spin_lock_irq(&conf->resync_lock); conf->nr_waiting--; @@ -887,14 +885,14 @@ static int make_request(mddev_t *mddev, struct bio * bio) spin_unlock_irq(&conf->resync_lock); bio_pair_release(bp); - return 0; + return; bad_map: printk("md/raid10:%s: make_request bug: can't convert block across chunks" " or bigger than %dk %llu %d\n", mdname(mddev), chunk_sects/2, (unsigned long long)bio->bi_sector, bio->bi_size >> 10); bio_io_error(bio); - return 0; + return; } md_write_start(mddev, bio); @@ -937,7 +935,7 @@ static int make_request(mddev_t *mddev, struct bio * bio) slot = r10_bio->read_slot; if (disk < 0) { raid_end_bio_io(r10_bio); - return 0; + return; } mirror = conf->mirrors + disk; @@ -985,7 +983,7 @@ static int make_request(mddev_t *mddev, struct bio * bio) goto read_again; } else generic_make_request(read_bio); - return 0; + return; } /* @@ -1157,7 +1155,6 @@ static int make_request(mddev_t *mddev, struct bio * bio) if (do_sync || !mddev->bitmap || !plugged) md_wakeup_thread(mddev->thread); - return 0; } static void status(struct seq_file *seq, mddev_t *mddev) diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index dbae459fb02d..96b7f6a1b6f2 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -3695,7 +3695,7 @@ static struct stripe_head *__get_priority_stripe(raid5_conf_t *conf) return sh; } -static int make_request(mddev_t *mddev, struct bio * bi) +static void make_request(mddev_t *mddev, struct bio * bi) { raid5_conf_t *conf = mddev->private; int dd_idx; @@ -3708,7 +3708,7 @@ static int make_request(mddev_t *mddev, struct bio * bi) if (unlikely(bi->bi_rw & REQ_FLUSH)) { md_flush_request(mddev, bi); - return 0; + return; } md_write_start(mddev, bi); @@ -3716,7 +3716,7 @@ static int make_request(mddev_t *mddev, struct bio * bi) if (rw == READ && mddev->reshape_position == MaxSector && chunk_aligned_read(mddev,bi)) - return 0; + return; logical_sector = bi->bi_sector & ~((sector_t)STRIPE_SECTORS-1); last_sector = bi->bi_sector + (bi->bi_size>>9); @@ -3851,8 +3851,6 @@ static int make_request(mddev_t *mddev, struct bio * bi) bio_endio(bi, 0); } - - return 0; } static sector_t raid5_size(mddev_t *mddev, sector_t sectors, int raid_disks); diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index 9b43ae94beba..a5a55da2a1ac 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -27,7 +27,7 @@ static int dcssblk_open(struct block_device *bdev, fmode_t mode); static int dcssblk_release(struct gendisk *disk, fmode_t mode); -static int dcssblk_make_request(struct request_queue *q, struct bio *bio); +static void dcssblk_make_request(struct request_queue *q, struct bio *bio); static int dcssblk_direct_access(struct block_device *bdev, sector_t secnum, void **kaddr, unsigned long *pfn); @@ -814,7 +814,7 @@ dcssblk_release(struct gendisk *disk, fmode_t mode) return rc; } -static int +static void dcssblk_make_request(struct request_queue *q, struct bio *bio) { struct dcssblk_dev_info *dev_info; @@ -871,10 +871,9 @@ dcssblk_make_request(struct request_queue *q, struct bio *bio) bytes_done += bvec->bv_len; } bio_endio(bio, 0); - return 0; + return; fail: bio_io_error(bio); - return 0; } static int diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c index 1f6a4d894e73..98f3e4ade924 100644 --- a/drivers/s390/block/xpram.c +++ b/drivers/s390/block/xpram.c @@ -181,7 +181,7 @@ static unsigned long xpram_highest_page_index(void) /* * Block device make request function. */ -static int xpram_make_request(struct request_queue *q, struct bio *bio) +static void xpram_make_request(struct request_queue *q, struct bio *bio) { xpram_device_t *xdev = bio->bi_bdev->bd_disk->private_data; struct bio_vec *bvec; @@ -221,10 +221,9 @@ static int xpram_make_request(struct request_queue *q, struct bio *bio) } set_bit(BIO_UPTODATE, &bio->bi_flags); bio_endio(bio, 0); - return 0; + return; fail: bio_io_error(bio); - return 0; } static int xpram_getgeo(struct block_device *bdev, struct hd_geometry *geo) diff --git a/drivers/staging/zram/zram_drv.c b/drivers/staging/zram/zram_drv.c index d70ec1ad10de..02589cab6710 100644 --- a/drivers/staging/zram/zram_drv.c +++ b/drivers/staging/zram/zram_drv.c @@ -556,24 +556,22 @@ static inline int valid_io_request(struct zram *zram, struct bio *bio) /* * Handler function for all zram I/O requests. */ -static int zram_make_request(struct request_queue *queue, struct bio *bio) +static void zram_make_request(struct request_queue *queue, struct bio *bio) { struct zram *zram = queue->queuedata; if (!valid_io_request(zram, bio)) { zram_stat64_inc(zram, &zram->stats.invalid_io); bio_io_error(bio); - return 0; + return; } if (unlikely(!zram->init_done) && zram_init_device(zram)) { bio_io_error(bio); - return 0; + return; } __zram_make_request(zram, bio, bio_data_dir(bio)); - - return 0; } void zram_reset_device(struct zram *zram) diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 085f95414c7f..c712efdafc3f 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -193,7 +193,7 @@ struct request_pm_state #include typedef void (request_fn_proc) (struct request_queue *q); -typedef int (make_request_fn) (struct request_queue *q, struct bio *bio); +typedef void (make_request_fn) (struct request_queue *q, struct bio *bio); typedef int (prep_rq_fn) (struct request_queue *, struct request *); typedef void (unprep_rq_fn) (struct request_queue *, struct request *); @@ -675,7 +675,7 @@ extern int scsi_cmd_ioctl(struct request_queue *, struct gendisk *, fmode_t, extern int sg_scsi_ioctl(struct request_queue *, struct gendisk *, fmode_t, struct scsi_ioctl_command __user *); -extern int blk_queue_bio(struct request_queue *q, struct bio *bio); +extern void blk_queue_bio(struct request_queue *q, struct bio *bio); /* * A queue has just exitted congestion. Note this in the global counter of From a2f5203fec3c06d68a6bb45ad41f2adebf9ac5e0 Mon Sep 17 00:00:00 2001 From: Boojin Kim Date: Fri, 2 Sep 2011 09:44:29 +0900 Subject: [PATCH 0144/1531] DMA: PL330: Add support runtime PM for PL330 DMAC Signed-off-by: Boojin Kim Acked-by: Jassi Brar Acked-by: Linus Walleij Acked-by: Vinod Koul Cc: Dan Williams Signed-off-by: Kukjin Kim Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 75 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 2 deletions(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 00eee59e8b33..0b99af18f9a1 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -17,6 +17,7 @@ #include #include #include +#include #define NR_DEFAULT_DESC 16 @@ -83,6 +84,8 @@ struct dma_pl330_dmac { /* Peripheral channels connected to this DMAC */ struct dma_pl330_chan *peripherals; /* keep at end */ + + struct clk *clk; }; struct dma_pl330_desc { @@ -696,6 +699,30 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) goto probe_err1; } + pdmac->clk = clk_get(&adev->dev, "dma"); + if (IS_ERR(pdmac->clk)) { + dev_err(&adev->dev, "Cannot get operation clock.\n"); + ret = -EINVAL; + goto probe_err1; + } + + 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 + /* enable dma clk */ + clk_enable(pdmac->clk); +#endif + irq = adev->irq[0]; ret = request_irq(irq, pl330_irq_handler, 0, dev_name(&adev->dev), pi); @@ -771,8 +798,6 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) goto probe_err4; } - amba_set_drvdata(adev, pdmac); - dev_info(&adev->dev, "Loaded driver for PL330 DMAC-%d\n", adev->periphid); dev_info(&adev->dev, @@ -833,6 +858,13 @@ 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 + clk_disable(pdmac->clk); +#endif + kfree(pdmac); return 0; @@ -846,10 +878,49 @@ static struct amba_id pl330_ids[] = { { 0, 0 }, }; +#ifdef CONFIG_PM_RUNTIME +static int pl330_runtime_suspend(struct device *dev) +{ + struct dma_pl330_dmac *pdmac = dev_get_drvdata(dev); + + if (!pdmac) { + dev_err(dev, "failed to get dmac\n"); + return -ENODEV; + } + + clk_disable(pdmac->clk); + + return 0; +} + +static int pl330_runtime_resume(struct device *dev) +{ + struct dma_pl330_dmac *pdmac = dev_get_drvdata(dev); + + if (!pdmac) { + dev_err(dev, "failed to get dmac\n"); + return -ENODEV; + } + + clk_enable(pdmac->clk); + + return 0; +} +#else +#define pl330_runtime_suspend NULL +#define pl330_runtime_resume NULL +#endif /* CONFIG_PM_RUNTIME */ + +static const struct dev_pm_ops pl330_pm_ops = { + .runtime_suspend = pl330_runtime_suspend, + .runtime_resume = pl330_runtime_resume, +}; + static struct amba_driver pl330_driver = { .drv = { .owner = THIS_MODULE, .name = "dma-pl330", + .pm = &pl330_pm_ops, }, .id_table = pl330_ids, .probe = pl330_probe, From 1b9bb715e7c4c189c4215a11a09e2ccb16598d86 Mon Sep 17 00:00:00 2001 From: Boojin Kim Date: Fri, 2 Sep 2011 09:44:30 +0900 Subject: [PATCH 0145/1531] DMA: PL330: Update PL330 DMA API driver This patch updates following 3 items. 1. Removes unneccessary code. 2. Add AMBA, PL330 configuration 3. Change the meaning of 'peri_id' variable from PL330 event number to specific dma id by user. Signed-off-by: Boojin Kim Acked-by: Linus Walleij Acked-by: Vinod Koul Cc: Dan Williams Signed-off-by: Kukjin Kim Signed-off-by: Vinod Koul --- drivers/dma/Kconfig | 3 ++- drivers/dma/pl330.c | 14 +++++++++----- include/linux/amba/pl330.h | 6 +----- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 2e3b3d38c465..ab8f469f5cf8 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -193,7 +193,8 @@ config ARCH_HAS_ASYNC_TX_FIND_CHANNEL config PL330_DMA tristate "DMA API Driver for PL330" select DMA_ENGINE - depends on PL330 + depends on ARM_AMBA + select PL330 help Select if your platform has one or more PL330 DMACs. You need to provide platform specific settings via diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 0b99af18f9a1..d5829c734fad 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -18,6 +18,7 @@ #include #include #include +#include #define NR_DEFAULT_DESC 16 @@ -69,6 +70,10 @@ struct dma_pl330_chan { * NULL if the channel is available to be acquired. */ void *pl330_chid; + + /* For D-to-M and M-to-D channels */ + int burst_sz; /* the peripheral fifo width */ + dma_addr_t fifo_addr; }; struct dma_pl330_dmac { @@ -456,7 +461,7 @@ static struct dma_pl330_desc *pl330_get_desc(struct dma_pl330_chan *pch) if (peri) { desc->req.rqtype = peri->rqtype; - desc->req.peri = peri->peri_id; + desc->req.peri = pch->chan.chan_id; } else { desc->req.rqtype = MEMTOMEM; desc->req.peri = 0; @@ -582,7 +587,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, struct dma_pl330_peri *peri = chan->private; struct scatterlist *sg; unsigned long flags; - int i, burst_size; + int i; dma_addr_t addr; if (unlikely(!pch || !sgl || !sg_len || !peri)) @@ -598,8 +603,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, return NULL; } - addr = peri->fifo_addr; - burst_size = peri->burst_sz; + addr = pch->fifo_addr; first = NULL; @@ -647,7 +651,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, sg_dma_address(sg), addr, sg_dma_len(sg)); } - desc->rqcfg.brst_size = burst_size; + desc->rqcfg.brst_size = pch->burst_sz; desc->rqcfg.brst_len = 1; } diff --git a/include/linux/amba/pl330.h b/include/linux/amba/pl330.h index cbee7de7dd36..d12f077a6daf 100644 --- a/include/linux/amba/pl330.h +++ b/include/linux/amba/pl330.h @@ -19,12 +19,8 @@ struct dma_pl330_peri { * Peri_Req i/f of the DMAC that is * peripheral could be reached from. */ - u8 peri_id; /* {0, 31} */ + u8 peri_id; /* specific dma id */ enum pl330_reqtype rqtype; - - /* For M->D and D->M Channels */ - int burst_sz; /* in power of 2 */ - dma_addr_t fifo_addr; }; struct dma_pl330_platdata { From 1d0c1d606d787e833ee3bd9e1cda640e75c4681a Mon Sep 17 00:00:00 2001 From: Boojin Kim Date: Fri, 2 Sep 2011 09:44:31 +0900 Subject: [PATCH 0146/1531] DMA: PL330: Support DMA_SLAVE_CONFIG command Signed-off-by: Boojin Kim Acked-by: Linus Walleij Acked-by: Vinod Koul Cc: Dan Williams Signed-off-by: Kukjin Kim Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 49 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index d5829c734fad..e7f9d1d3d81a 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -73,6 +73,7 @@ struct dma_pl330_chan { /* For D-to-M and M-to-D channels */ int burst_sz; /* the peripheral fifo width */ + int burst_len; /* the number of burst */ dma_addr_t fifo_addr; }; @@ -263,23 +264,47 @@ static int pl330_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned struct dma_pl330_chan *pch = to_pchan(chan); struct dma_pl330_desc *desc; unsigned long flags; + struct dma_pl330_dmac *pdmac = pch->dmac; + struct dma_slave_config *slave_config; - /* Only supports DMA_TERMINATE_ALL */ - if (cmd != DMA_TERMINATE_ALL) - return -ENXIO; + switch (cmd) { + case DMA_TERMINATE_ALL: + spin_lock_irqsave(&pch->lock, flags); - spin_lock_irqsave(&pch->lock, flags); + /* FLUSH the PL330 Channel thread */ + pl330_chan_ctrl(pch->pl330_chid, PL330_OP_FLUSH); - /* FLUSH the PL330 Channel thread */ - pl330_chan_ctrl(pch->pl330_chid, PL330_OP_FLUSH); - - /* Mark all desc done */ - list_for_each_entry(desc, &pch->work_list, node) - desc->status = DONE; + /* Mark all desc done */ + list_for_each_entry(desc, &pch->work_list, node) + desc->status = DONE; - spin_unlock_irqrestore(&pch->lock, flags); + spin_unlock_irqrestore(&pch->lock, flags); - pl330_tasklet((unsigned long) pch); + pl330_tasklet((unsigned long) pch); + break; + case DMA_SLAVE_CONFIG: + slave_config = (struct dma_slave_config *)arg; + + if (slave_config->direction == DMA_TO_DEVICE) { + 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) { + if (slave_config->src_addr) + pch->fifo_addr = slave_config->src_addr; + if (slave_config->src_addr_width) + pch->burst_sz = __ffs(slave_config->src_addr_width); + if (slave_config->src_maxburst) + pch->burst_len = slave_config->src_maxburst; + } + break; + default: + dev_err(pch->dmac->pif.dev, "Not supported command.\n"); + return -ENXIO; + } return 0; } From ae43b886f174297366d4e09a008ad8e6592d95df Mon Sep 17 00:00:00 2001 From: Boojin Kim Date: Fri, 2 Sep 2011 09:44:32 +0900 Subject: [PATCH 0147/1531] DMA: PL330: Remove the start operation for handling DMA_TERMINATE_ALL command Original code carries out the start operation after flush operation. But start operation is not required for DMA_TERMINATE_ALL command. So, this patch removes the unnecessary start operation and only carries out the flush operation for handling DMA_TERMINATE_ALL command. Signed-off-by: Boojin Kim Acked-by: Linus Walleij Acked-by: Vinod Koul Cc: Dan Williams Signed-off-by: Kukjin Kim [Fixed typos in changelog] Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index e7f9d1d3d81a..59943ec1e74a 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -262,10 +262,11 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan) static int pl330_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg) { struct dma_pl330_chan *pch = to_pchan(chan); - struct dma_pl330_desc *desc; + struct dma_pl330_desc *desc, *_dt; unsigned long flags; struct dma_pl330_dmac *pdmac = pch->dmac; struct dma_slave_config *slave_config; + LIST_HEAD(list); switch (cmd) { case DMA_TERMINATE_ALL: @@ -275,12 +276,14 @@ static int pl330_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned pl330_chan_ctrl(pch->pl330_chid, PL330_OP_FLUSH); /* Mark all desc done */ - list_for_each_entry(desc, &pch->work_list, node) + list_for_each_entry_safe(desc, _dt, &pch->work_list , node) { desc->status = DONE; + pch->completed = desc->txd.cookie; + list_move_tail(&desc->node, &list); + } + list_splice_tail_init(&list, &pdmac->desc_pool); spin_unlock_irqrestore(&pch->lock, flags); - - pl330_tasklet((unsigned long) pch); break; case DMA_SLAVE_CONFIG: slave_config = (struct dma_slave_config *)arg; From 42bc9cf45939c26a5c5eb946d4fd35f1a7b0f9f8 Mon Sep 17 00:00:00 2001 From: Boojin Kim Date: Fri, 2 Sep 2011 09:44:33 +0900 Subject: [PATCH 0148/1531] DMA: PL330: Add DMA_CYCLIC capability This patch adds DMA_CYCLIC capability that is used for audio driver. DMA driver activated with it reuses the dma requests that were submitted through tx_submit(). Signed-off-by: Boojin Kim Acked-by: Linus Walleij Acked-by: Vinod Koul Cc: Dan Williams Signed-off-by: Kukjin Kim Signed-off-by: Vinod Koul --- drivers/dma/pl330.c | 84 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 59943ec1e74a..621134fdba4c 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -75,6 +75,9 @@ struct dma_pl330_chan { int burst_sz; /* the peripheral fifo width */ int burst_len; /* the number of burst */ dma_addr_t fifo_addr; + + /* for cyclic capability */ + bool cyclic; }; struct dma_pl330_dmac { @@ -161,6 +164,31 @@ static inline void free_desc_list(struct list_head *list) spin_unlock_irqrestore(&pdmac->pool_lock, flags); } +static inline void handle_cyclic_desc_list(struct list_head *list) +{ + struct dma_pl330_desc *desc; + struct dma_pl330_chan *pch; + unsigned long flags; + + if (list_empty(list)) + return; + + list_for_each_entry(desc, list, node) { + dma_async_tx_callback callback; + + /* Change status to reload it */ + desc->status = PREP; + pch = desc->pchan; + callback = desc->txd.callback; + if (callback) + callback(desc->txd.callback_param); + } + + spin_lock_irqsave(&pch->lock, flags); + list_splice_tail_init(list, &pch->work_list); + spin_unlock_irqrestore(&pch->lock, flags); +} + static inline void fill_queue(struct dma_pl330_chan *pch) { struct dma_pl330_desc *desc; @@ -214,7 +242,10 @@ static void pl330_tasklet(unsigned long data) spin_unlock_irqrestore(&pch->lock, flags); - free_desc_list(&list); + if (pch->cyclic) + handle_cyclic_desc_list(&list); + else + free_desc_list(&list); } static void dma_pl330_rqcb(void *token, enum pl330_op_err err) @@ -245,6 +276,7 @@ static int pl330_alloc_chan_resources(struct dma_chan *chan) spin_lock_irqsave(&pch->lock, flags); pch->completed = chan->cookie = 1; + pch->cyclic = false; pch->pl330_chid = pl330_request_channel(&pdmac->pif); if (!pch->pl330_chid) { @@ -324,6 +356,9 @@ static void pl330_free_chan_resources(struct dma_chan *chan) pl330_release_channel(pch->pl330_chid); pch->pl330_chid = NULL; + if (pch->cyclic) + list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool); + spin_unlock_irqrestore(&pch->lock, flags); } @@ -560,6 +595,51 @@ static inline int get_burst_len(struct dma_pl330_desc *desc, size_t len) return burst_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) +{ + struct dma_pl330_desc *desc; + struct dma_pl330_chan *pch = to_pchan(chan); + dma_addr_t dst; + dma_addr_t src; + + desc = pl330_get_desc(pch); + if (!desc) { + dev_err(pch->dmac->pif.dev, "%s:%d Unable to fetch desc\n", + __func__, __LINE__); + return NULL; + } + + switch (direction) { + case DMA_TO_DEVICE: + desc->rqcfg.src_inc = 1; + desc->rqcfg.dst_inc = 0; + src = dma_addr; + dst = pch->fifo_addr; + break; + case DMA_FROM_DEVICE: + desc->rqcfg.src_inc = 0; + desc->rqcfg.dst_inc = 1; + src = pch->fifo_addr; + dst = dma_addr; + break; + default: + dev_err(pch->dmac->pif.dev, "%s:%d Invalid dma direction\n", + __func__, __LINE__); + return NULL; + } + + desc->rqcfg.brst_size = pch->burst_sz; + desc->rqcfg.brst_len = 1; + + pch->cyclic = true; + + fill_px(&desc->px, dst, src, period_len); + + return &desc->txd; +} + static struct dma_async_tx_descriptor * pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst, dma_addr_t src, size_t len, unsigned long flags) @@ -791,6 +871,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) case MEMTODEV: case DEVTOMEM: dma_cap_set(DMA_SLAVE, pd->cap_mask); + dma_cap_set(DMA_CYCLIC, pd->cap_mask); break; default: dev_err(&adev->dev, "DEVTODEV Not Supported\n"); @@ -819,6 +900,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) pd->device_alloc_chan_resources = pl330_alloc_chan_resources; pd->device_free_chan_resources = pl330_free_chan_resources; pd->device_prep_dma_memcpy = pl330_prep_dma_memcpy; + pd->device_prep_dma_cyclic = pl330_prep_dma_cyclic; pd->device_tx_status = pl330_tx_status; pd->device_prep_slave_sg = pl330_prep_slave_sg; pd->device_control = pl330_control; From aa0de00e4b9b3adba5db5ef5ee01e61b1b2e4329 Mon Sep 17 00:00:00 2001 From: Boojin Kim Date: Fri, 2 Sep 2011 09:44:34 +0900 Subject: [PATCH 0149/1531] ARM: SAMSUNG: Update to use PL330-DMA driver This patch adds to support PL330-DMA driver on DMADEVICE for S5P SoCs. Signed-off-by: Boojin Kim Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Kukjin Kim Signed-off-by: Vinod Koul --- arch/arm/mach-exynos4/include/mach/dma.h | 4 ++-- arch/arm/mach-s5p64x0/include/mach/dma.h | 2 +- arch/arm/mach-s5pc100/include/mach/dma.h | 2 +- arch/arm/mach-s5pv210/include/mach/dma.h | 2 +- arch/arm/plat-samsung/Kconfig | 9 +++++++++ .../include/plat/{s3c-dma-pl330.h => dma-pl330.h} | 8 ++++---- arch/arm/plat-samsung/include/plat/s3c-pl330-pdata.h | 2 +- 7 files changed, 19 insertions(+), 10 deletions(-) rename arch/arm/plat-samsung/include/plat/{s3c-dma-pl330.h => dma-pl330.h} (93%) diff --git a/arch/arm/mach-exynos4/include/mach/dma.h b/arch/arm/mach-exynos4/include/mach/dma.h index 81209eb1409b..201842a3769e 100644 --- a/arch/arm/mach-exynos4/include/mach/dma.h +++ b/arch/arm/mach-exynos4/include/mach/dma.h @@ -20,7 +20,7 @@ #ifndef __MACH_DMA_H #define __MACH_DMA_H -/* This platform uses the common S3C DMA API driver for PL330 */ -#include +/* This platform uses the common DMA API driver for PL330 */ +#include #endif /* __MACH_DMA_H */ diff --git a/arch/arm/mach-s5p64x0/include/mach/dma.h b/arch/arm/mach-s5p64x0/include/mach/dma.h index 81209eb1409b..1beae2cca6a3 100644 --- a/arch/arm/mach-s5p64x0/include/mach/dma.h +++ b/arch/arm/mach-s5p64x0/include/mach/dma.h @@ -21,6 +21,6 @@ #define __MACH_DMA_H /* This platform uses the common S3C DMA API driver for PL330 */ -#include +#include #endif /* __MACH_DMA_H */ diff --git a/arch/arm/mach-s5pc100/include/mach/dma.h b/arch/arm/mach-s5pc100/include/mach/dma.h index 81209eb1409b..1beae2cca6a3 100644 --- a/arch/arm/mach-s5pc100/include/mach/dma.h +++ b/arch/arm/mach-s5pc100/include/mach/dma.h @@ -21,6 +21,6 @@ #define __MACH_DMA_H /* This platform uses the common S3C DMA API driver for PL330 */ -#include +#include #endif /* __MACH_DMA_H */ diff --git a/arch/arm/mach-s5pv210/include/mach/dma.h b/arch/arm/mach-s5pv210/include/mach/dma.h index 81209eb1409b..1beae2cca6a3 100644 --- a/arch/arm/mach-s5pv210/include/mach/dma.h +++ b/arch/arm/mach-s5pv210/include/mach/dma.h @@ -21,6 +21,6 @@ #define __MACH_DMA_H /* This platform uses the common S3C DMA API driver for PL330 */ -#include +#include #endif /* __MACH_DMA_H */ diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig index b3e10659e4b8..e6f01ab86208 100644 --- a/arch/arm/plat-samsung/Kconfig +++ b/arch/arm/plat-samsung/Kconfig @@ -306,6 +306,15 @@ config S3C_PL330_DMA help S3C DMA API Driver for PL330 DMAC. +config SAMSUNG_DMADEV + bool + select DMADEVICES + select PL330_DMA if (CPU_EXYNOS4210 || CPU_S5PV210 || CPU_S5PC100 || \ + CPU_S5P6450 || CPU_S5P6440) + select ARM_AMBA + help + Use DMA device engine for PL330 DMAC. + comment "Power management" config SAMSUNG_PM_DEBUG diff --git a/arch/arm/plat-samsung/include/plat/s3c-dma-pl330.h b/arch/arm/plat-samsung/include/plat/dma-pl330.h similarity index 93% rename from arch/arm/plat-samsung/include/plat/s3c-dma-pl330.h rename to arch/arm/plat-samsung/include/plat/dma-pl330.h index 810744213120..0a5dade0e85c 100644 --- a/arch/arm/plat-samsung/include/plat/s3c-dma-pl330.h +++ b/arch/arm/plat-samsung/include/plat/dma-pl330.h @@ -8,8 +8,8 @@ * (at your option) any later version. */ -#ifndef __S3C_DMA_PL330_H_ -#define __S3C_DMA_PL330_H_ +#ifndef __DMA_PL330_H_ +#define __DMA_PL330_H_ __FILE__ #define S3C2410_DMAF_AUTOSTART (1 << 0) #define S3C2410_DMAF_CIRCULAR (1 << 1) @@ -20,7 +20,7 @@ * For the sake of consistency across client drivers, * We keep the channel names unchanged and only add * missing peripherals are added. - * Order is not important since S3C PL330 API driver + * Order is not important since DMA PL330 API driver * use these just as IDs. */ enum dma_ch { @@ -95,4 +95,4 @@ static inline bool s3c_dma_has_circular(void) #include -#endif /* __S3C_DMA_PL330_H_ */ +#endif /* __DMA_PL330_H_ */ diff --git a/arch/arm/plat-samsung/include/plat/s3c-pl330-pdata.h b/arch/arm/plat-samsung/include/plat/s3c-pl330-pdata.h index bf5e2a9d408d..64fdf66ff9cf 100644 --- a/arch/arm/plat-samsung/include/plat/s3c-pl330-pdata.h +++ b/arch/arm/plat-samsung/include/plat/s3c-pl330-pdata.h @@ -12,7 +12,7 @@ #ifndef __S3C_PL330_PDATA_H #define __S3C_PL330_PDATA_H -#include +#include /* * Every PL330 DMAC has max 32 peripheral interfaces, From c4e1662550a3bd23df7cff4611eff67ba2afe078 Mon Sep 17 00:00:00 2001 From: Boojin Kim Date: Fri, 2 Sep 2011 09:44:35 +0900 Subject: [PATCH 0150/1531] ARM: SAMSUNG: Add common DMA operations This patch adds common DMA operations which are used for Samsung DMA drivers. Currently there are two types of DMA driver for Samsung SoCs. The one is S3C-DMA for S3C SoCs and the other is PL330-DMA for S5P SoCs. This patch provides funcion pointers for common DMA operations to DMA client driver like SPI and Audio. It makes DMA client drivers support multi-platform. In addition, this common DMA operations implement the shared actions that are needed for DMA client driver. For example shared actions are filter() function for dma_request_channel() and parameter passing for device_prep_slave_sg(). Signed-off-by: Boojin Kim Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Kukjin Kim Signed-off-by: Vinod Koul --- arch/arm/mach-s3c2410/include/mach/dma.h | 8 +- arch/arm/mach-s3c64xx/include/mach/dma.h | 4 + arch/arm/plat-samsung/Makefile | 6 +- arch/arm/plat-samsung/dma-ops.c | 131 +++++++++++++++++ arch/arm/plat-samsung/include/plat/dma-ops.h | 63 +++++++++ .../arm/plat-samsung/include/plat/dma-pl330.h | 4 + arch/arm/plat-samsung/include/plat/dma.h | 1 + arch/arm/plat-samsung/s3c-dma-ops.c | 133 ++++++++++++++++++ 8 files changed, 347 insertions(+), 3 deletions(-) create mode 100644 arch/arm/plat-samsung/dma-ops.c create mode 100644 arch/arm/plat-samsung/include/plat/dma-ops.h create mode 100644 arch/arm/plat-samsung/s3c-dma-ops.c diff --git a/arch/arm/mach-s3c2410/include/mach/dma.h b/arch/arm/mach-s3c2410/include/mach/dma.h index b2b2a5bb275e..e61a91f88c52 100644 --- a/arch/arm/mach-s3c2410/include/mach/dma.h +++ b/arch/arm/mach-s3c2410/include/mach/dma.h @@ -13,7 +13,6 @@ #ifndef __ASM_ARCH_DMA_H #define __ASM_ARCH_DMA_H __FILE__ -#include #include #define MAX_DMA_TRANSFER_SIZE 0x100000 /* Data Unit is half word */ @@ -51,6 +50,13 @@ enum dma_ch { DMACH_MAX, /* the end entry */ }; +static inline bool samsung_dma_is_dmadev(void) +{ + return false; +} + +#include + #define DMACH_LOW_LEVEL (1<<28) /* use this to specifiy hardware ch no */ /* we have 4 dma channels */ diff --git a/arch/arm/mach-s3c64xx/include/mach/dma.h b/arch/arm/mach-s3c64xx/include/mach/dma.h index 0a5d9268a23e..49c3a53135d4 100644 --- a/arch/arm/mach-s3c64xx/include/mach/dma.h +++ b/arch/arm/mach-s3c64xx/include/mach/dma.h @@ -63,6 +63,10 @@ static __inline__ bool s3c_dma_has_circular(void) return true; } +static inline bool samsung_dma_is_dmadev(void) +{ + return false; +} #define S3C2410_DMAF_CIRCULAR (1 << 0) #include diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile index 853764ba8cc5..e2ee0b8a3763 100644 --- a/arch/arm/plat-samsung/Makefile +++ b/arch/arm/plat-samsung/Makefile @@ -63,9 +63,11 @@ obj-$(CONFIG_SAMSUNG_DEV_BACKLIGHT) += dev-backlight.o # DMA support -obj-$(CONFIG_S3C_DMA) += dma.o +obj-$(CONFIG_S3C_DMA) += dma.o s3c-dma-ops.o -obj-$(CONFIG_S3C_PL330_DMA) += s3c-pl330.o +obj-$(CONFIG_SAMSUNG_DMADEV) += dma-ops.o + +obj-$(CONFIG_S3C_PL330_DMA) += s3c-pl330.o s3c-dma-ops.o # PM support diff --git a/arch/arm/plat-samsung/dma-ops.c b/arch/arm/plat-samsung/dma-ops.c new file mode 100644 index 000000000000..6e3d9abc9e2e --- /dev/null +++ b/arch/arm/plat-samsung/dma-ops.c @@ -0,0 +1,131 @@ +/* linux/arch/arm/plat-samsung/dma-ops.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Samsung DMA Operations + * + * 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 + +static inline bool pl330_filter(struct dma_chan *chan, void *param) +{ + struct dma_pl330_peri *peri = chan->private; + return peri->peri_id == (unsigned)param; +} + +static unsigned samsung_dmadev_request(enum dma_ch dma_ch, + struct samsung_dma_info *info) +{ + struct dma_chan *chan; + dma_cap_mask_t mask; + struct dma_slave_config slave_config; + + dma_cap_zero(mask); + dma_cap_set(info->cap, mask); + + chan = dma_request_channel(mask, pl330_filter, (void *)dma_ch); + + if (info->direction == DMA_FROM_DEVICE) { + 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) { + memset(&slave_config, 0, sizeof(struct dma_slave_config)); + slave_config.direction = info->direction; + slave_config.dst_addr = info->fifo; + slave_config.dst_addr_width = info->width; + slave_config.dst_maxburst = 1; + dmaengine_slave_config(chan, &slave_config); + } + + return (unsigned)chan; +} + +static int samsung_dmadev_release(unsigned ch, + struct s3c2410_dma_client *client) +{ + dma_release_channel((struct dma_chan *)ch); + + return 0; +} + +static int samsung_dmadev_prepare(unsigned ch, + struct samsung_dma_prep_info *info) +{ + struct scatterlist sg; + struct dma_chan *chan = (struct dma_chan *)ch; + struct dma_async_tx_descriptor *desc; + + switch (info->cap) { + case DMA_SLAVE: + sg_init_table(&sg, 1); + sg_dma_len(&sg) = info->len; + sg_set_page(&sg, pfn_to_page(PFN_DOWN(info->buf)), + info->len, offset_in_page(info->buf)); + sg_dma_address(&sg) = info->buf; + + desc = chan->device->device_prep_slave_sg(chan, + &sg, 1, info->direction, DMA_PREP_INTERRUPT); + break; + case DMA_CYCLIC: + desc = chan->device->device_prep_dma_cyclic(chan, + info->buf, info->len, info->period, info->direction); + break; + default: + dev_err(&chan->dev->device, "unsupported format\n"); + return -EFAULT; + } + + if (!desc) { + dev_err(&chan->dev->device, "cannot prepare cyclic dma\n"); + return -EFAULT; + } + + desc->callback = info->fp; + desc->callback_param = info->fp_param; + + dmaengine_submit((struct dma_async_tx_descriptor *)desc); + + return 0; +} + +static inline int samsung_dmadev_trigger(unsigned ch) +{ + dma_async_issue_pending((struct dma_chan *)ch); + + return 0; +} + +static inline int samsung_dmadev_flush(unsigned ch) +{ + return dmaengine_terminate_all((struct dma_chan *)ch); +} + +struct samsung_dma_ops dmadev_ops = { + .request = samsung_dmadev_request, + .release = samsung_dmadev_release, + .prepare = samsung_dmadev_prepare, + .trigger = samsung_dmadev_trigger, + .started = NULL, + .flush = samsung_dmadev_flush, + .stop = samsung_dmadev_flush, +}; + +void *samsung_dmadev_get_ops(void) +{ + return &dmadev_ops; +} +EXPORT_SYMBOL(samsung_dmadev_get_ops); diff --git a/arch/arm/plat-samsung/include/plat/dma-ops.h b/arch/arm/plat-samsung/include/plat/dma-ops.h new file mode 100644 index 000000000000..4c1a363526cf --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/dma-ops.h @@ -0,0 +1,63 @@ +/* arch/arm/plat-samsung/include/plat/dma-ops.h + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Samsung DMA support + * + * 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. + */ + +#ifndef __SAMSUNG_DMA_OPS_H_ +#define __SAMSUNG_DMA_OPS_H_ __FILE__ + +#include + +struct samsung_dma_prep_info { + enum dma_transaction_type cap; + enum dma_data_direction direction; + dma_addr_t buf; + unsigned long period; + unsigned long len; + void (*fp)(void *data); + void *fp_param; +}; + +struct samsung_dma_info { + enum dma_transaction_type cap; + enum dma_data_direction direction; + enum dma_slave_buswidth width; + dma_addr_t fifo; + struct s3c2410_dma_client *client; +}; + +struct samsung_dma_ops { + unsigned (*request)(enum dma_ch ch, struct samsung_dma_info *info); + int (*release)(unsigned ch, struct s3c2410_dma_client *client); + int (*prepare)(unsigned ch, struct samsung_dma_prep_info *info); + int (*trigger)(unsigned ch); + int (*started)(unsigned ch); + int (*flush)(unsigned ch); + int (*stop)(unsigned ch); +}; + +extern void *samsung_dmadev_get_ops(void); +extern void *s3c_dma_get_ops(void); + +static inline void *__samsung_dma_get_ops(void) +{ + if (samsung_dma_is_dmadev()) + return samsung_dmadev_get_ops(); + else + return s3c_dma_get_ops(); +} + +/* + * samsung_dma_get_ops + * get the set of samsung dma operations + */ +#define samsung_dma_get_ops() __samsung_dma_get_ops() + +#endif /* __SAMSUNG_DMA_OPS_H_ */ diff --git a/arch/arm/plat-samsung/include/plat/dma-pl330.h b/arch/arm/plat-samsung/include/plat/dma-pl330.h index 0a5dade0e85c..2916920034f0 100644 --- a/arch/arm/plat-samsung/include/plat/dma-pl330.h +++ b/arch/arm/plat-samsung/include/plat/dma-pl330.h @@ -93,6 +93,10 @@ static inline bool s3c_dma_has_circular(void) return true; } +static inline bool samsung_dma_is_dmadev(void) +{ + return true; +} #include #endif /* __DMA_PL330_H_ */ diff --git a/arch/arm/plat-samsung/include/plat/dma.h b/arch/arm/plat-samsung/include/plat/dma.h index 8c273b7a6f56..361076060744 100644 --- a/arch/arm/plat-samsung/include/plat/dma.h +++ b/arch/arm/plat-samsung/include/plat/dma.h @@ -126,3 +126,4 @@ 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 diff --git a/arch/arm/plat-samsung/s3c-dma-ops.c b/arch/arm/plat-samsung/s3c-dma-ops.c new file mode 100644 index 000000000000..33ab3241268d --- /dev/null +++ b/arch/arm/plat-samsung/s3c-dma-ops.c @@ -0,0 +1,133 @@ +/* linux/arch/arm/plat-samsung/s3c-dma-ops.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Samsung S3C-DMA Operations + * + * 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 + +struct cb_data { + void (*fp) (void *); + void *fp_param; + unsigned ch; + struct list_head node; +}; + +static LIST_HEAD(dma_list); + +static void s3c_dma_cb(struct s3c2410_dma_chan *channel, void *param, + int size, enum s3c2410_dma_buffresult res) +{ + struct cb_data *data = param; + + data->fp(data->fp_param); +} + +static unsigned s3c_dma_request(enum dma_ch dma_ch, + struct samsung_dma_info *info) +{ + struct cb_data *data; + + if (s3c2410_dma_request(dma_ch, info->client, NULL) < 0) { + s3c2410_dma_free(dma_ch, info->client); + return 0; + } + + data = kzalloc(sizeof(struct cb_data), GFP_KERNEL); + data->ch = dma_ch; + list_add_tail(&data->node, &dma_list); + + if (info->direction == DMA_FROM_DEVICE) + s3c2410_dma_devconfig(dma_ch, S3C2410_DMASRC_HW, info->fifo); + else + s3c2410_dma_devconfig(dma_ch, S3C2410_DMASRC_MEM, info->fifo); + + if (info->cap == DMA_CYCLIC) + s3c2410_dma_setflags(dma_ch, S3C2410_DMAF_CIRCULAR); + + s3c2410_dma_config(dma_ch, info->width); + + return (unsigned)dma_ch; +} + +static int s3c_dma_release(unsigned ch, struct s3c2410_dma_client *client) +{ + struct cb_data *data; + + list_for_each_entry(data, &dma_list, node) + if (data->ch == ch) + break; + list_del(&data->node); + + s3c2410_dma_free(ch, client); + kfree(data); + + return 0; +} + +static int s3c_dma_prepare(unsigned ch, struct samsung_dma_prep_info *info) +{ + struct cb_data *data; + int len = (info->cap == DMA_CYCLIC) ? info->period : info->len; + + list_for_each_entry(data, &dma_list, node) + if (data->ch == ch) + break; + + if (!data->fp) { + s3c2410_dma_set_buffdone_fn(ch, s3c_dma_cb); + data->fp = info->fp; + data->fp_param = info->fp_param; + } + + s3c2410_dma_enqueue(ch, (void *)data, info->buf, len); + + return 0; +} + +static inline int s3c_dma_trigger(unsigned ch) +{ + return s3c2410_dma_ctrl(ch, S3C2410_DMAOP_START); +} + +static inline int s3c_dma_started(unsigned ch) +{ + return s3c2410_dma_ctrl(ch, S3C2410_DMAOP_STARTED); +} + +static inline int s3c_dma_flush(unsigned ch) +{ + return s3c2410_dma_ctrl(ch, S3C2410_DMAOP_FLUSH); +} + +static inline int s3c_dma_stop(unsigned ch) +{ + return s3c2410_dma_ctrl(ch, S3C2410_DMAOP_STOP); +} + +static struct samsung_dma_ops s3c_dma_ops = { + .request = s3c_dma_request, + .release = s3c_dma_release, + .prepare = s3c_dma_prepare, + .trigger = s3c_dma_trigger, + .started = s3c_dma_started, + .flush = s3c_dma_flush, + .stop = s3c_dma_stop, +}; + +void *s3c_dma_get_ops(void) +{ + return &s3c_dma_ops; +} +EXPORT_SYMBOL(s3c_dma_get_ops); From bf856fbb5e104ab9afd366baa0d744dd8cfa074d Mon Sep 17 00:00:00 2001 From: Boojin Kim Date: Fri, 2 Sep 2011 09:44:36 +0900 Subject: [PATCH 0151/1531] ARM: EXYNOS4: Use generic DMA PL330 driver This patch makes Samsung EXYNOS4 to use DMA PL330 driver on DMADEVICE. The EXYNOS4 uses DMA generic APIs instead of SAMSUNG specific S3C-PL330 APIs. Signed-off-by: Boojin Kim Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Kukjin Kim Signed-off-by: Vinod Koul --- arch/arm/mach-exynos4/Kconfig | 2 +- arch/arm/mach-exynos4/clock.c | 11 +- arch/arm/mach-exynos4/dma.c | 299 +++++++++++++++++++++------------- 3 files changed, 198 insertions(+), 114 deletions(-) diff --git a/arch/arm/mach-exynos4/Kconfig b/arch/arm/mach-exynos4/Kconfig index 0c77ab99fa16..9f00cf3450df 100644 --- a/arch/arm/mach-exynos4/Kconfig +++ b/arch/arm/mach-exynos4/Kconfig @@ -11,7 +11,7 @@ if ARCH_EXYNOS4 config CPU_EXYNOS4210 bool - select S3C_PL330_DMA + select SAMSUNG_DMADEV help Enable EXYNOS4210 CPU support diff --git a/arch/arm/mach-exynos4/clock.c b/arch/arm/mach-exynos4/clock.c index 851dea018578..fee2dd8791d0 100644 --- a/arch/arm/mach-exynos4/clock.c +++ b/arch/arm/mach-exynos4/clock.c @@ -43,6 +43,11 @@ static struct clk clk_sclk_usbphy1 = { .name = "sclk_usbphy1", }; +static struct clk dummy_apb_pclk = { + .name = "apb_pclk", + .id = -1, +}; + static int exynos4_clksrc_mask_top_ctrl(struct clk *clk, int enable) { return s5p_gatectrl(S5P_CLKSRC_MASK_TOP, clk, enable); @@ -454,12 +459,12 @@ static struct clk init_clocks_off[] = { .enable = exynos4_clk_ip_fsys_ctrl, .ctrlbit = (1 << 10), }, { - .name = "pdma", + .name = "dma", .devname = "s3c-pl330.0", .enable = exynos4_clk_ip_fsys_ctrl, .ctrlbit = (1 << 0), }, { - .name = "pdma", + .name = "dma", .devname = "s3c-pl330.1", .enable = exynos4_clk_ip_fsys_ctrl, .ctrlbit = (1 << 1), @@ -1210,5 +1215,7 @@ void __init exynos4_register_clocks(void) s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off)); s3c_disable_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off)); + s3c24xx_register_clock(&dummy_apb_pclk); + s3c_pwmclk_init(); } diff --git a/arch/arm/mach-exynos4/dma.c b/arch/arm/mach-exynos4/dma.c index 564bb530f332..d57d66255021 100644 --- a/arch/arm/mach-exynos4/dma.c +++ b/arch/arm/mach-exynos4/dma.c @@ -21,151 +21,228 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include #include +#include +#include +#include #include #include #include #include - -#include +#include static u64 dma_dmamask = DMA_BIT_MASK(32); -static struct resource exynos4_pdma0_resource[] = { - [0] = { - .start = EXYNOS4_PA_PDMA0, - .end = EXYNOS4_PA_PDMA0 + SZ_4K, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_PDMA0, - .end = IRQ_PDMA0, - .flags = IORESOURCE_IRQ, +struct dma_pl330_peri pdma0_peri[28] = { + { + .peri_id = (u8)DMACH_PCM0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_PCM0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_PCM2_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_PCM2_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_MSM_REQ0, + }, { + .peri_id = (u8)DMACH_MSM_REQ2, + }, { + .peri_id = (u8)DMACH_SPI0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_SPI0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_SPI2_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_SPI2_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_I2S0S_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_I2S0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_I2S0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_UART0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_UART2_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART2_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_UART4_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART4_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_SLIMBUS0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_SLIMBUS0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_SLIMBUS2_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_SLIMBUS2_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_SLIMBUS4_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_SLIMBUS4_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_AC97_MICIN, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_AC97_PCMIN, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_AC97_PCMOUT, + .rqtype = MEMTODEV, }, }; -static struct s3c_pl330_platdata exynos4_pdma0_pdata = { - .peri = { - [0] = DMACH_PCM0_RX, - [1] = DMACH_PCM0_TX, - [2] = DMACH_PCM2_RX, - [3] = DMACH_PCM2_TX, - [4] = DMACH_MSM_REQ0, - [5] = DMACH_MSM_REQ2, - [6] = DMACH_SPI0_RX, - [7] = DMACH_SPI0_TX, - [8] = DMACH_SPI2_RX, - [9] = DMACH_SPI2_TX, - [10] = DMACH_I2S0S_TX, - [11] = DMACH_I2S0_RX, - [12] = DMACH_I2S0_TX, - [13] = DMACH_I2S2_RX, - [14] = DMACH_I2S2_TX, - [15] = DMACH_UART0_RX, - [16] = DMACH_UART0_TX, - [17] = DMACH_UART2_RX, - [18] = DMACH_UART2_TX, - [19] = DMACH_UART4_RX, - [20] = DMACH_UART4_TX, - [21] = DMACH_SLIMBUS0_RX, - [22] = DMACH_SLIMBUS0_TX, - [23] = DMACH_SLIMBUS2_RX, - [24] = DMACH_SLIMBUS2_TX, - [25] = DMACH_SLIMBUS4_RX, - [26] = DMACH_SLIMBUS4_TX, - [27] = DMACH_AC97_MICIN, - [28] = DMACH_AC97_PCMIN, - [29] = DMACH_AC97_PCMOUT, - [30] = DMACH_MAX, - [31] = DMACH_MAX, - }, +struct dma_pl330_platdata exynos4_pdma0_pdata = { + .nr_valid_peri = ARRAY_SIZE(pdma0_peri), + .peri = pdma0_peri, }; -static struct platform_device exynos4_device_pdma0 = { - .name = "s3c-pl330", - .id = 0, - .num_resources = ARRAY_SIZE(exynos4_pdma0_resource), - .resource = exynos4_pdma0_resource, - .dev = { +struct amba_device exynos4_device_pdma0 = { + .dev = { + .init_name = "dma-pl330.0", .dma_mask = &dma_dmamask, .coherent_dma_mask = DMA_BIT_MASK(32), .platform_data = &exynos4_pdma0_pdata, }, + .res = { + .start = EXYNOS4_PA_PDMA0, + .end = EXYNOS4_PA_PDMA0 + SZ_4K, + .flags = IORESOURCE_MEM, + }, + .irq = {IRQ_PDMA0, NO_IRQ}, + .periphid = 0x00041330, }; -static struct resource exynos4_pdma1_resource[] = { - [0] = { - .start = EXYNOS4_PA_PDMA1, - .end = EXYNOS4_PA_PDMA1 + SZ_4K, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_PDMA1, - .end = IRQ_PDMA1, - .flags = IORESOURCE_IRQ, +struct dma_pl330_peri pdma1_peri[25] = { + { + .peri_id = (u8)DMACH_PCM0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_PCM0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_PCM1_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_PCM1_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_MSM_REQ1, + }, { + .peri_id = (u8)DMACH_MSM_REQ3, + }, { + .peri_id = (u8)DMACH_SPI1_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_SPI1_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_I2S0S_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_I2S0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_I2S0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_I2S1_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_I2S1_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_UART0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_UART1_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART1_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_UART3_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART3_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_SLIMBUS1_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_SLIMBUS1_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_SLIMBUS3_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_SLIMBUS3_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_SLIMBUS5_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_SLIMBUS5_TX, + .rqtype = MEMTODEV, }, }; -static struct s3c_pl330_platdata exynos4_pdma1_pdata = { - .peri = { - [0] = DMACH_PCM0_RX, - [1] = DMACH_PCM0_TX, - [2] = DMACH_PCM1_RX, - [3] = DMACH_PCM1_TX, - [4] = DMACH_MSM_REQ1, - [5] = DMACH_MSM_REQ3, - [6] = DMACH_SPI1_RX, - [7] = DMACH_SPI1_TX, - [8] = DMACH_I2S0S_TX, - [9] = DMACH_I2S0_RX, - [10] = DMACH_I2S0_TX, - [11] = DMACH_I2S1_RX, - [12] = DMACH_I2S1_TX, - [13] = DMACH_UART0_RX, - [14] = DMACH_UART0_TX, - [15] = DMACH_UART1_RX, - [16] = DMACH_UART1_TX, - [17] = DMACH_UART3_RX, - [18] = DMACH_UART3_TX, - [19] = DMACH_SLIMBUS1_RX, - [20] = DMACH_SLIMBUS1_TX, - [21] = DMACH_SLIMBUS3_RX, - [22] = DMACH_SLIMBUS3_TX, - [23] = DMACH_SLIMBUS5_RX, - [24] = DMACH_SLIMBUS5_TX, - [25] = DMACH_SLIMBUS0AUX_RX, - [26] = DMACH_SLIMBUS0AUX_TX, - [27] = DMACH_SPDIF, - [28] = DMACH_MAX, - [29] = DMACH_MAX, - [30] = DMACH_MAX, - [31] = DMACH_MAX, - }, +struct dma_pl330_platdata exynos4_pdma1_pdata = { + .nr_valid_peri = ARRAY_SIZE(pdma1_peri), + .peri = pdma1_peri, }; -static struct platform_device exynos4_device_pdma1 = { - .name = "s3c-pl330", - .id = 1, - .num_resources = ARRAY_SIZE(exynos4_pdma1_resource), - .resource = exynos4_pdma1_resource, - .dev = { +struct amba_device exynos4_device_pdma1 = { + .dev = { + .init_name = "dma-pl330.1", .dma_mask = &dma_dmamask, .coherent_dma_mask = DMA_BIT_MASK(32), .platform_data = &exynos4_pdma1_pdata, }, -}; - -static struct platform_device *exynos4_dmacs[] __initdata = { - &exynos4_device_pdma0, - &exynos4_device_pdma1, + .res = { + .start = EXYNOS4_PA_PDMA1, + .end = EXYNOS4_PA_PDMA1 + SZ_4K, + .flags = IORESOURCE_MEM, + }, + .irq = {IRQ_PDMA1, NO_IRQ}, + .periphid = 0x00041330, }; static int __init exynos4_dma_init(void) { - platform_add_devices(exynos4_dmacs, ARRAY_SIZE(exynos4_dmacs)); + amba_device_register(&exynos4_device_pdma0, &iomem_resource); return 0; } From dafc954304ad3a471c82e7aad15b26f6be264560 Mon Sep 17 00:00:00 2001 From: Boojin Kim Date: Fri, 2 Sep 2011 09:44:37 +0900 Subject: [PATCH 0152/1531] ARM: S5PV210: Use generic DMA PL330 driver This patch makes Samsung S5PV210 to use DMA PL330 driver on DMADEVICE. The S5PV210 uses DMA generic APIs instead of SAMSUNG specific S3C-PL330 APIs. Signed-off-by: Boojin Kim Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Kukjin Kim Signed-off-by: Vinod Koul --- arch/arm/mach-s5pv210/Kconfig | 2 +- arch/arm/mach-s5pv210/clock.c | 10 +- arch/arm/mach-s5pv210/dma.c | 316 +++++++++++++++-------- arch/arm/mach-s5pv210/include/mach/dma.h | 2 +- 4 files changed, 214 insertions(+), 116 deletions(-) diff --git a/arch/arm/mach-s5pv210/Kconfig b/arch/arm/mach-s5pv210/Kconfig index 69dd87cd8e22..d960090e4656 100644 --- a/arch/arm/mach-s5pv210/Kconfig +++ b/arch/arm/mach-s5pv210/Kconfig @@ -11,7 +11,7 @@ if ARCH_S5PV210 config CPU_S5PV210 bool - select S3C_PL330_DMA + select SAMSUNG_DMADEV select S5P_EXT_INT select S5P_HRT select S5PV210_PM if PM diff --git a/arch/arm/mach-s5pv210/clock.c b/arch/arm/mach-s5pv210/clock.c index 52a8e607bcc2..d35726a6faea 100644 --- a/arch/arm/mach-s5pv210/clock.c +++ b/arch/arm/mach-s5pv210/clock.c @@ -203,6 +203,11 @@ static struct clk clk_pcmcdclk2 = { .name = "pcmcdclk", }; +static struct clk dummy_apb_pclk = { + .name = "apb_pclk", + .id = -1, +}; + static struct clk *clkset_vpllsrc_list[] = { [0] = &clk_fin_vpll, [1] = &clk_sclk_hdmi27m, @@ -289,13 +294,13 @@ static struct clk_ops clk_fout_apll_ops = { static struct clk init_clocks_off[] = { { - .name = "pdma", + .name = "dma", .devname = "s3c-pl330.0", .parent = &clk_hclk_psys.clk, .enable = s5pv210_clk_ip0_ctrl, .ctrlbit = (1 << 3), }, { - .name = "pdma", + .name = "dma", .devname = "s3c-pl330.1", .parent = &clk_hclk_psys.clk, .enable = s5pv210_clk_ip0_ctrl, @@ -1161,5 +1166,6 @@ void __init s5pv210_register_clocks(void) s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off)); s3c_disable_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off)); + s3c24xx_register_clock(&dummy_apb_pclk); s3c_pwmclk_init(); } diff --git a/arch/arm/mach-s5pv210/dma.c b/arch/arm/mach-s5pv210/dma.c index 497d3439a142..f79d0b06cbf9 100644 --- a/arch/arm/mach-s5pv210/dma.c +++ b/arch/arm/mach-s5pv210/dma.c @@ -1,4 +1,8 @@ -/* +/* linux/arch/arm/mach-s5pv210/dma.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * * Copyright (C) 2010 Samsung Electronics Co. Ltd. * Jaswinder Singh * @@ -17,151 +21,239 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include #include +#include +#include +#include #include #include #include #include - -#include +#include static u64 dma_dmamask = DMA_BIT_MASK(32); -static struct resource s5pv210_pdma0_resource[] = { - [0] = { - .start = S5PV210_PA_PDMA0, - .end = S5PV210_PA_PDMA0 + SZ_4K, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_PDMA0, - .end = IRQ_PDMA0, - .flags = IORESOURCE_IRQ, +struct dma_pl330_peri pdma0_peri[28] = { + { + .peri_id = (u8)DMACH_UART0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_UART1_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART1_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_UART2_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART2_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_UART3_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART3_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = DMACH_MAX, + }, { + .peri_id = (u8)DMACH_I2S0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_I2S0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_I2S0S_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_I2S1_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_I2S1_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_MAX, + }, { + .peri_id = (u8)DMACH_MAX, + }, { + .peri_id = (u8)DMACH_SPI0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_SPI0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_SPI1_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_SPI1_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_MAX, + }, { + .peri_id = (u8)DMACH_MAX, + }, { + .peri_id = (u8)DMACH_AC97_MICIN, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_AC97_PCMIN, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_AC97_PCMOUT, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_MAX, + }, { + .peri_id = (u8)DMACH_PWM, + }, { + .peri_id = (u8)DMACH_SPDIF, + .rqtype = MEMTODEV, }, }; -static struct s3c_pl330_platdata s5pv210_pdma0_pdata = { - .peri = { - [0] = DMACH_UART0_RX, - [1] = DMACH_UART0_TX, - [2] = DMACH_UART1_RX, - [3] = DMACH_UART1_TX, - [4] = DMACH_UART2_RX, - [5] = DMACH_UART2_TX, - [6] = DMACH_UART3_RX, - [7] = DMACH_UART3_TX, - [8] = DMACH_MAX, - [9] = DMACH_I2S0_RX, - [10] = DMACH_I2S0_TX, - [11] = DMACH_I2S0S_TX, - [12] = DMACH_I2S1_RX, - [13] = DMACH_I2S1_TX, - [14] = DMACH_MAX, - [15] = DMACH_MAX, - [16] = DMACH_SPI0_RX, - [17] = DMACH_SPI0_TX, - [18] = DMACH_SPI1_RX, - [19] = DMACH_SPI1_TX, - [20] = DMACH_MAX, - [21] = DMACH_MAX, - [22] = DMACH_AC97_MICIN, - [23] = DMACH_AC97_PCMIN, - [24] = DMACH_AC97_PCMOUT, - [25] = DMACH_MAX, - [26] = DMACH_PWM, - [27] = DMACH_SPDIF, - [28] = DMACH_MAX, - [29] = DMACH_MAX, - [30] = DMACH_MAX, - [31] = DMACH_MAX, - }, +struct dma_pl330_platdata s5pv210_pdma0_pdata = { + .nr_valid_peri = ARRAY_SIZE(pdma0_peri), + .peri = pdma0_peri, }; -static struct platform_device s5pv210_device_pdma0 = { - .name = "s3c-pl330", - .id = 0, - .num_resources = ARRAY_SIZE(s5pv210_pdma0_resource), - .resource = s5pv210_pdma0_resource, - .dev = { +struct amba_device s5pv210_device_pdma0 = { + .dev = { + .init_name = "dma-pl330.0", .dma_mask = &dma_dmamask, .coherent_dma_mask = DMA_BIT_MASK(32), .platform_data = &s5pv210_pdma0_pdata, }, -}; - -static struct resource s5pv210_pdma1_resource[] = { - [0] = { - .start = S5PV210_PA_PDMA1, - .end = S5PV210_PA_PDMA1 + SZ_4K, + .res = { + .start = S5PV210_PA_PDMA0, + .end = S5PV210_PA_PDMA0 + SZ_4K, .flags = IORESOURCE_MEM, }, - [1] = { - .start = IRQ_PDMA1, - .end = IRQ_PDMA1, - .flags = IORESOURCE_IRQ, - }, + .irq = {IRQ_PDMA0, NO_IRQ}, + .periphid = 0x00041330, }; -static struct s3c_pl330_platdata s5pv210_pdma1_pdata = { - .peri = { - [0] = DMACH_UART0_RX, - [1] = DMACH_UART0_TX, - [2] = DMACH_UART1_RX, - [3] = DMACH_UART1_TX, - [4] = DMACH_UART2_RX, - [5] = DMACH_UART2_TX, - [6] = DMACH_UART3_RX, - [7] = DMACH_UART3_TX, - [8] = DMACH_MAX, - [9] = DMACH_I2S0_RX, - [10] = DMACH_I2S0_TX, - [11] = DMACH_I2S0S_TX, - [12] = DMACH_I2S1_RX, - [13] = DMACH_I2S1_TX, - [14] = DMACH_I2S2_RX, - [15] = DMACH_I2S2_TX, - [16] = DMACH_SPI0_RX, - [17] = DMACH_SPI0_TX, - [18] = DMACH_SPI1_RX, - [19] = DMACH_SPI1_TX, - [20] = DMACH_MAX, - [21] = DMACH_MAX, - [22] = DMACH_PCM0_RX, - [23] = DMACH_PCM0_TX, - [24] = DMACH_PCM1_RX, - [25] = DMACH_PCM1_TX, - [26] = DMACH_MSM_REQ0, - [27] = DMACH_MSM_REQ1, - [28] = DMACH_MSM_REQ2, - [29] = DMACH_MSM_REQ3, - [30] = DMACH_PCM2_RX, - [31] = DMACH_PCM2_TX, +struct dma_pl330_peri pdma1_peri[32] = { + { + .peri_id = (u8)DMACH_UART0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_UART1_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART1_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_UART2_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART2_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_UART3_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART3_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = DMACH_MAX, + }, { + .peri_id = (u8)DMACH_I2S0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_I2S0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_I2S0S_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_I2S1_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_I2S1_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_I2S2_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_I2S2_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_SPI0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_SPI0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_SPI1_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_SPI1_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_MAX, + }, { + .peri_id = (u8)DMACH_MAX, + }, { + .peri_id = (u8)DMACH_PCM0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_PCM0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_PCM1_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_PCM1_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_MSM_REQ0, + }, { + .peri_id = (u8)DMACH_MSM_REQ1, + }, { + .peri_id = (u8)DMACH_MSM_REQ2, + }, { + .peri_id = (u8)DMACH_MSM_REQ3, + }, { + .peri_id = (u8)DMACH_PCM2_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_PCM2_TX, + .rqtype = MEMTODEV, }, }; -static struct platform_device s5pv210_device_pdma1 = { - .name = "s3c-pl330", - .id = 1, - .num_resources = ARRAY_SIZE(s5pv210_pdma1_resource), - .resource = s5pv210_pdma1_resource, - .dev = { +struct dma_pl330_platdata s5pv210_pdma1_pdata = { + .nr_valid_peri = ARRAY_SIZE(pdma1_peri), + .peri = pdma1_peri, +}; + +struct amba_device s5pv210_device_pdma1 = { + .dev = { + .init_name = "dma-pl330.1", .dma_mask = &dma_dmamask, .coherent_dma_mask = DMA_BIT_MASK(32), .platform_data = &s5pv210_pdma1_pdata, }, -}; - -static struct platform_device *s5pv210_dmacs[] __initdata = { - &s5pv210_device_pdma0, - &s5pv210_device_pdma1, + .res = { + .start = S5PV210_PA_PDMA1, + .end = S5PV210_PA_PDMA1 + SZ_4K, + .flags = IORESOURCE_MEM, + }, + .irq = {IRQ_PDMA1, NO_IRQ}, + .periphid = 0x00041330, }; static int __init s5pv210_dma_init(void) { - platform_add_devices(s5pv210_dmacs, ARRAY_SIZE(s5pv210_dmacs)); + amba_device_register(&s5pv210_device_pdma0, &iomem_resource); return 0; } diff --git a/arch/arm/mach-s5pv210/include/mach/dma.h b/arch/arm/mach-s5pv210/include/mach/dma.h index 1beae2cca6a3..201842a3769e 100644 --- a/arch/arm/mach-s5pv210/include/mach/dma.h +++ b/arch/arm/mach-s5pv210/include/mach/dma.h @@ -20,7 +20,7 @@ #ifndef __MACH_DMA_H #define __MACH_DMA_H -/* This platform uses the common S3C DMA API driver for PL330 */ +/* This platform uses the common DMA API driver for PL330 */ #include #endif /* __MACH_DMA_H */ From a422bd0f6d50bcb9c529d890bde70295915bb3e9 Mon Sep 17 00:00:00 2001 From: Boojin Kim Date: Fri, 2 Sep 2011 09:44:38 +0900 Subject: [PATCH 0153/1531] ARM: S5PC100: Use generic DMA PL330 driver This patch makes Samsung S5PC100 to use DMA PL330 driver on DMADEVICE. The S5PC100 uses DMA generic APIs instead of SAMSUNG specific S3C-PL330 APIs. Signed-off-by: Boojin Kim Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Kukjin Kim Signed-off-by: Vinod Koul --- arch/arm/mach-s5pc100/Kconfig | 2 +- arch/arm/mach-s5pc100/clock.c | 11 +- arch/arm/mach-s5pc100/dma.c | 323 +++++++++++++++-------- arch/arm/mach-s5pc100/include/mach/dma.h | 2 +- 4 files changed, 222 insertions(+), 116 deletions(-) diff --git a/arch/arm/mach-s5pc100/Kconfig b/arch/arm/mach-s5pc100/Kconfig index e8a33c4b054c..e538a4c67e9c 100644 --- a/arch/arm/mach-s5pc100/Kconfig +++ b/arch/arm/mach-s5pc100/Kconfig @@ -10,7 +10,7 @@ if ARCH_S5PC100 config CPU_S5PC100 bool select S5P_EXT_INT - select S3C_PL330_DMA + select SAMSUNG_DMADEV help Enable S5PC100 CPU support diff --git a/arch/arm/mach-s5pc100/clock.c b/arch/arm/mach-s5pc100/clock.c index ff5cbb30de5b..6527c05c5fa1 100644 --- a/arch/arm/mach-s5pc100/clock.c +++ b/arch/arm/mach-s5pc100/clock.c @@ -33,6 +33,11 @@ static struct clk s5p_clk_otgphy = { .name = "otg_phy", }; +static struct clk dummy_apb_pclk = { + .name = "apb_pclk", + .id = -1, +}; + static struct clk *clk_src_mout_href_list[] = { [0] = &s5p_clk_27m, [1] = &clk_fin_hpll, @@ -454,13 +459,13 @@ static struct clk init_clocks_off[] = { .enable = s5pc100_d1_0_ctrl, .ctrlbit = (1 << 2), }, { - .name = "pdma", + .name = "dma", .devname = "s3c-pl330.1", .parent = &clk_div_d1_bus.clk, .enable = s5pc100_d1_0_ctrl, .ctrlbit = (1 << 1), }, { - .name = "pdma", + .name = "dma", .devname = "s3c-pl330.0", .parent = &clk_div_d1_bus.clk, .enable = s5pc100_d1_0_ctrl, @@ -1276,5 +1281,7 @@ void __init s5pc100_register_clocks(void) s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off)); s3c_disable_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off)); + s3c24xx_register_clock(&dummy_apb_pclk); + s3c_pwmclk_init(); } diff --git a/arch/arm/mach-s5pc100/dma.c b/arch/arm/mach-s5pc100/dma.c index bf4cd0fb97c6..ef803e92d35d 100644 --- a/arch/arm/mach-s5pc100/dma.c +++ b/arch/arm/mach-s5pc100/dma.c @@ -1,4 +1,8 @@ -/* +/* linux/arch/arm/mach-s5pc100/dma.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * * Copyright (C) 2010 Samsung Electronics Co. Ltd. * Jaswinder Singh * @@ -17,150 +21,245 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include #include +#include +#include +#include #include +#include #include #include - -#include +#include static u64 dma_dmamask = DMA_BIT_MASK(32); -static struct resource s5pc100_pdma0_resource[] = { - [0] = { - .start = S5PC100_PA_PDMA0, - .end = S5PC100_PA_PDMA0 + SZ_4K, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_PDMA0, - .end = IRQ_PDMA0, - .flags = IORESOURCE_IRQ, +struct dma_pl330_peri pdma0_peri[30] = { + { + .peri_id = (u8)DMACH_UART0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_UART1_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART1_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_UART2_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART2_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_UART3_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART3_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = DMACH_IRDA, + }, { + .peri_id = (u8)DMACH_I2S0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_I2S0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_I2S0S_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_I2S1_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_I2S1_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_I2S2_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_I2S2_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_SPI0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_SPI0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_SPI1_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_SPI1_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_SPI2_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_SPI2_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_AC97_MICIN, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_AC97_PCMIN, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_AC97_PCMOUT, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_EXTERNAL, + }, { + .peri_id = (u8)DMACH_PWM, + }, { + .peri_id = (u8)DMACH_SPDIF, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_HSI_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_HSI_TX, + .rqtype = MEMTODEV, }, }; -static struct s3c_pl330_platdata s5pc100_pdma0_pdata = { - .peri = { - [0] = DMACH_UART0_RX, - [1] = DMACH_UART0_TX, - [2] = DMACH_UART1_RX, - [3] = DMACH_UART1_TX, - [4] = DMACH_UART2_RX, - [5] = DMACH_UART2_TX, - [6] = DMACH_UART3_RX, - [7] = DMACH_UART3_TX, - [8] = DMACH_IRDA, - [9] = DMACH_I2S0_RX, - [10] = DMACH_I2S0_TX, - [11] = DMACH_I2S0S_TX, - [12] = DMACH_I2S1_RX, - [13] = DMACH_I2S1_TX, - [14] = DMACH_I2S2_RX, - [15] = DMACH_I2S2_TX, - [16] = DMACH_SPI0_RX, - [17] = DMACH_SPI0_TX, - [18] = DMACH_SPI1_RX, - [19] = DMACH_SPI1_TX, - [20] = DMACH_SPI2_RX, - [21] = DMACH_SPI2_TX, - [22] = DMACH_AC97_MICIN, - [23] = DMACH_AC97_PCMIN, - [24] = DMACH_AC97_PCMOUT, - [25] = DMACH_EXTERNAL, - [26] = DMACH_PWM, - [27] = DMACH_SPDIF, - [28] = DMACH_HSI_RX, - [29] = DMACH_HSI_TX, - [30] = DMACH_MAX, - [31] = DMACH_MAX, - }, +struct dma_pl330_platdata s5pc100_pdma0_pdata = { + .nr_valid_peri = ARRAY_SIZE(pdma0_peri), + .peri = pdma0_peri, }; -static struct platform_device s5pc100_device_pdma0 = { - .name = "s3c-pl330", - .id = 0, - .num_resources = ARRAY_SIZE(s5pc100_pdma0_resource), - .resource = s5pc100_pdma0_resource, - .dev = { +struct amba_device s5pc100_device_pdma0 = { + .dev = { + .init_name = "dma-pl330.0", .dma_mask = &dma_dmamask, .coherent_dma_mask = DMA_BIT_MASK(32), .platform_data = &s5pc100_pdma0_pdata, }, -}; - -static struct resource s5pc100_pdma1_resource[] = { - [0] = { - .start = S5PC100_PA_PDMA1, - .end = S5PC100_PA_PDMA1 + SZ_4K, + .res = { + .start = S5PC100_PA_PDMA0, + .end = S5PC100_PA_PDMA0 + SZ_4K, .flags = IORESOURCE_MEM, }, - [1] = { - .start = IRQ_PDMA1, - .end = IRQ_PDMA1, - .flags = IORESOURCE_IRQ, - }, + .irq = {IRQ_PDMA0, NO_IRQ}, + .periphid = 0x00041330, }; -static struct s3c_pl330_platdata s5pc100_pdma1_pdata = { - .peri = { - [0] = DMACH_UART0_RX, - [1] = DMACH_UART0_TX, - [2] = DMACH_UART1_RX, - [3] = DMACH_UART1_TX, - [4] = DMACH_UART2_RX, - [5] = DMACH_UART2_TX, - [6] = DMACH_UART3_RX, - [7] = DMACH_UART3_TX, - [8] = DMACH_IRDA, - [9] = DMACH_I2S0_RX, - [10] = DMACH_I2S0_TX, - [11] = DMACH_I2S0S_TX, - [12] = DMACH_I2S1_RX, - [13] = DMACH_I2S1_TX, - [14] = DMACH_I2S2_RX, - [15] = DMACH_I2S2_TX, - [16] = DMACH_SPI0_RX, - [17] = DMACH_SPI0_TX, - [18] = DMACH_SPI1_RX, - [19] = DMACH_SPI1_TX, - [20] = DMACH_SPI2_RX, - [21] = DMACH_SPI2_TX, - [22] = DMACH_PCM0_RX, - [23] = DMACH_PCM0_TX, - [24] = DMACH_PCM1_RX, - [25] = DMACH_PCM1_TX, - [26] = DMACH_MSM_REQ0, - [27] = DMACH_MSM_REQ1, - [28] = DMACH_MSM_REQ2, - [29] = DMACH_MSM_REQ3, - [30] = DMACH_MAX, - [31] = DMACH_MAX, +struct dma_pl330_peri pdma1_peri[30] = { + { + .peri_id = (u8)DMACH_UART0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_UART1_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART1_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_UART2_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART2_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_UART3_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART3_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = DMACH_IRDA, + }, { + .peri_id = (u8)DMACH_I2S0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_I2S0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_I2S0S_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_I2S1_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_I2S1_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_I2S2_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_I2S2_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_SPI0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_SPI0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_SPI1_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_SPI1_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_SPI2_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_SPI2_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_PCM0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_PCM1_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_PCM1_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_PCM1_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_MSM_REQ0, + }, { + .peri_id = (u8)DMACH_MSM_REQ1, + }, { + .peri_id = (u8)DMACH_MSM_REQ2, + }, { + .peri_id = (u8)DMACH_MSM_REQ3, }, }; -static struct platform_device s5pc100_device_pdma1 = { - .name = "s3c-pl330", - .id = 1, - .num_resources = ARRAY_SIZE(s5pc100_pdma1_resource), - .resource = s5pc100_pdma1_resource, - .dev = { +struct dma_pl330_platdata s5pc100_pdma1_pdata = { + .nr_valid_peri = ARRAY_SIZE(pdma1_peri), + .peri = pdma1_peri, +}; + +struct amba_device s5pc100_device_pdma1 = { + .dev = { + .init_name = "dma-pl330.1", .dma_mask = &dma_dmamask, .coherent_dma_mask = DMA_BIT_MASK(32), .platform_data = &s5pc100_pdma1_pdata, }, -}; - -static struct platform_device *s5pc100_dmacs[] __initdata = { - &s5pc100_device_pdma0, - &s5pc100_device_pdma1, + .res = { + .start = S5PC100_PA_PDMA1, + .end = S5PC100_PA_PDMA1 + SZ_4K, + .flags = IORESOURCE_MEM, + }, + .irq = {IRQ_PDMA1, NO_IRQ}, + .periphid = 0x00041330, }; static int __init s5pc100_dma_init(void) { - platform_add_devices(s5pc100_dmacs, ARRAY_SIZE(s5pc100_dmacs)); + amba_device_register(&s5pc100_device_pdma0, &iomem_resource); return 0; } diff --git a/arch/arm/mach-s5pc100/include/mach/dma.h b/arch/arm/mach-s5pc100/include/mach/dma.h index 1beae2cca6a3..201842a3769e 100644 --- a/arch/arm/mach-s5pc100/include/mach/dma.h +++ b/arch/arm/mach-s5pc100/include/mach/dma.h @@ -20,7 +20,7 @@ #ifndef __MACH_DMA_H #define __MACH_DMA_H -/* This platform uses the common S3C DMA API driver for PL330 */ +/* This platform uses the common DMA API driver for PL330 */ #include #endif /* __MACH_DMA_H */ From 3091e61173211de3fbd9bcb99ddc33333377fcb7 Mon Sep 17 00:00:00 2001 From: Boojin Kim Date: Fri, 2 Sep 2011 09:44:39 +0900 Subject: [PATCH 0154/1531] ARM: S5P64X0: Use generic DMA PL330 driver This patch makes Samsung S5P64X0 to use DMA PL330 driver on DMADEVICE. The S5P64X0 uses DMA generic APIs instead of SAMSUNG specific S3C-PL330 APIs. Signed-off-by: Boojin Kim Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Kukjin Kim Signed-off-by: Vinod Koul --- arch/arm/mach-s5p64x0/Kconfig | 4 +- arch/arm/mach-s5p64x0/clock-s5p6440.c | 9 +- arch/arm/mach-s5p64x0/clock-s5p6450.c | 9 +- arch/arm/mach-s5p64x0/dma.c | 273 +++++++++++++++-------- arch/arm/mach-s5p64x0/include/mach/dma.h | 2 +- 5 files changed, 201 insertions(+), 96 deletions(-) diff --git a/arch/arm/mach-s5p64x0/Kconfig b/arch/arm/mach-s5p64x0/Kconfig index 65c7518dad7f..9527ed24dbff 100644 --- a/arch/arm/mach-s5p64x0/Kconfig +++ b/arch/arm/mach-s5p64x0/Kconfig @@ -9,14 +9,14 @@ if ARCH_S5P64X0 config CPU_S5P6440 bool - select S3C_PL330_DMA + select SAMSUNG_DMADEV select S5P_HRT help Enable S5P6440 CPU support config CPU_S5P6450 bool - select S3C_PL330_DMA + select SAMSUNG_DMADEV select S5P_HRT help Enable S5P6450 CPU support diff --git a/arch/arm/mach-s5p64x0/clock-s5p6440.c b/arch/arm/mach-s5p64x0/clock-s5p6440.c index 0e9cd3092dd2..c1f548f69a0d 100644 --- a/arch/arm/mach-s5p64x0/clock-s5p6440.c +++ b/arch/arm/mach-s5p64x0/clock-s5p6440.c @@ -146,7 +146,7 @@ static struct clk init_clocks_off[] = { .enable = s5p64x0_hclk0_ctrl, .ctrlbit = (1 << 8), }, { - .name = "pdma", + .name = "dma", .parent = &clk_hclk_low.clk, .enable = s5p64x0_hclk0_ctrl, .ctrlbit = (1 << 12), @@ -499,6 +499,11 @@ static struct clksrc_clk *sysclks[] = { &clk_pclk_low, }; +static struct clk dummy_apb_pclk = { + .name = "apb_pclk", + .id = -1, +}; + void __init_or_cpufreq s5p6440_setup_clocks(void) { struct clk *xtal_clk; @@ -581,5 +586,7 @@ void __init s5p6440_register_clocks(void) s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off)); s3c_disable_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off)); + s3c24xx_register_clock(&dummy_apb_pclk); + s3c_pwmclk_init(); } diff --git a/arch/arm/mach-s5p64x0/clock-s5p6450.c b/arch/arm/mach-s5p64x0/clock-s5p6450.c index d9dc16cde109..3d9b60975570 100644 --- a/arch/arm/mach-s5p64x0/clock-s5p6450.c +++ b/arch/arm/mach-s5p64x0/clock-s5p6450.c @@ -179,7 +179,7 @@ static struct clk init_clocks_off[] = { .enable = s5p64x0_hclk0_ctrl, .ctrlbit = (1 << 3), }, { - .name = "pdma", + .name = "dma", .parent = &clk_hclk_low.clk, .enable = s5p64x0_hclk0_ctrl, .ctrlbit = (1 << 12), @@ -553,6 +553,11 @@ static struct clksrc_clk *sysclks[] = { &clk_sclk_audio0, }; +static struct clk dummy_apb_pclk = { + .name = "apb_pclk", + .id = -1, +}; + void __init_or_cpufreq s5p6450_setup_clocks(void) { struct clk *xtal_clk; @@ -632,5 +637,7 @@ void __init s5p6450_register_clocks(void) s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off)); s3c_disable_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off)); + s3c24xx_register_clock(&dummy_apb_pclk); + s3c_pwmclk_init(); } diff --git a/arch/arm/mach-s5p64x0/dma.c b/arch/arm/mach-s5p64x0/dma.c index d7ad944b3475..aebf3fcb1ebe 100644 --- a/arch/arm/mach-s5p64x0/dma.c +++ b/arch/arm/mach-s5p64x0/dma.c @@ -21,128 +21,219 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include #include +#include +#include + +#include #include #include #include +#include #include -#include +#include static u64 dma_dmamask = DMA_BIT_MASK(32); -static struct resource s5p64x0_pdma_resource[] = { - [0] = { - .start = S5P64X0_PA_PDMA, - .end = S5P64X0_PA_PDMA + SZ_4K, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_DMA0, - .end = IRQ_DMA0, - .flags = IORESOURCE_IRQ, +struct dma_pl330_peri s5p6440_pdma_peri[22] = { + { + .peri_id = (u8)DMACH_UART0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_UART1_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART1_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_UART2_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART2_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_UART3_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART3_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = DMACH_MAX, + }, { + .peri_id = DMACH_MAX, + }, { + .peri_id = (u8)DMACH_PCM0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_PCM0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_I2S0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_I2S0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_SPI0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_SPI0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_MAX, + }, { + .peri_id = (u8)DMACH_MAX, + }, { + .peri_id = (u8)DMACH_MAX, + }, { + .peri_id = (u8)DMACH_MAX, + }, { + .peri_id = (u8)DMACH_SPI1_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_SPI1_RX, + .rqtype = DEVTOMEM, }, }; -static struct s3c_pl330_platdata s5p6440_pdma_pdata = { - .peri = { - [0] = DMACH_UART0_RX, - [1] = DMACH_UART0_TX, - [2] = DMACH_UART1_RX, - [3] = DMACH_UART1_TX, - [4] = DMACH_UART2_RX, - [5] = DMACH_UART2_TX, - [6] = DMACH_UART3_RX, - [7] = DMACH_UART3_TX, - [8] = DMACH_MAX, - [9] = DMACH_MAX, - [10] = DMACH_PCM0_TX, - [11] = DMACH_PCM0_RX, - [12] = DMACH_I2S0_TX, - [13] = DMACH_I2S0_RX, - [14] = DMACH_SPI0_TX, - [15] = DMACH_SPI0_RX, - [16] = DMACH_MAX, - [17] = DMACH_MAX, - [18] = DMACH_MAX, - [19] = DMACH_MAX, - [20] = DMACH_SPI1_TX, - [21] = DMACH_SPI1_RX, - [22] = DMACH_MAX, - [23] = DMACH_MAX, - [24] = DMACH_MAX, - [25] = DMACH_MAX, - [26] = DMACH_MAX, - [27] = DMACH_MAX, - [28] = DMACH_MAX, - [29] = DMACH_PWM, - [30] = DMACH_MAX, - [31] = DMACH_MAX, - }, +struct dma_pl330_platdata s5p6440_pdma_pdata = { + .nr_valid_peri = ARRAY_SIZE(s5p6440_pdma_peri), + .peri = s5p6440_pdma_peri, }; -static struct s3c_pl330_platdata s5p6450_pdma_pdata = { - .peri = { - [0] = DMACH_UART0_RX, - [1] = DMACH_UART0_TX, - [2] = DMACH_UART1_RX, - [3] = DMACH_UART1_TX, - [4] = DMACH_UART2_RX, - [5] = DMACH_UART2_TX, - [6] = DMACH_UART3_RX, - [7] = DMACH_UART3_TX, - [8] = DMACH_UART4_RX, - [9] = DMACH_UART4_TX, - [10] = DMACH_PCM0_TX, - [11] = DMACH_PCM0_RX, - [12] = DMACH_I2S0_TX, - [13] = DMACH_I2S0_RX, - [14] = DMACH_SPI0_TX, - [15] = DMACH_SPI0_RX, - [16] = DMACH_PCM1_TX, - [17] = DMACH_PCM1_RX, - [18] = DMACH_PCM2_TX, - [19] = DMACH_PCM2_RX, - [20] = DMACH_SPI1_TX, - [21] = DMACH_SPI1_RX, - [22] = DMACH_USI_TX, - [23] = DMACH_USI_RX, - [24] = DMACH_MAX, - [25] = DMACH_I2S1_TX, - [26] = DMACH_I2S1_RX, - [27] = DMACH_I2S2_TX, - [28] = DMACH_I2S2_RX, - [29] = DMACH_PWM, - [30] = DMACH_UART5_RX, - [31] = DMACH_UART5_TX, +struct dma_pl330_peri s5p6450_pdma_peri[32] = { + { + .peri_id = (u8)DMACH_UART0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_UART1_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART1_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_UART2_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART2_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_UART3_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART3_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_UART4_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART4_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_PCM0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_PCM0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_I2S0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_I2S0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_SPI0_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_SPI0_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_PCM1_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_PCM1_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_PCM2_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_PCM2_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_SPI1_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_SPI1_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_USI_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_USI_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_MAX, + }, { + .peri_id = (u8)DMACH_I2S1_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_I2S1_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_I2S2_TX, + .rqtype = MEMTODEV, + }, { + .peri_id = (u8)DMACH_I2S2_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_PWM, + }, { + .peri_id = (u8)DMACH_UART5_RX, + .rqtype = DEVTOMEM, + }, { + .peri_id = (u8)DMACH_UART5_TX, + .rqtype = MEMTODEV, }, }; -static struct platform_device s5p64x0_device_pdma = { - .name = "s3c-pl330", - .id = -1, - .num_resources = ARRAY_SIZE(s5p64x0_pdma_resource), - .resource = s5p64x0_pdma_resource, - .dev = { +struct dma_pl330_platdata s5p6450_pdma_pdata = { + .nr_valid_peri = ARRAY_SIZE(s5p6450_pdma_peri), + .peri = s5p6450_pdma_peri, +}; + +struct amba_device s5p64x0_device_pdma = { + .dev = { + .init_name = "dma-pl330", .dma_mask = &dma_dmamask, .coherent_dma_mask = DMA_BIT_MASK(32), }, + .res = { + .start = S5P64X0_PA_PDMA, + .end = S5P64X0_PA_PDMA + SZ_4K, + .flags = IORESOURCE_MEM, + }, + .irq = {IRQ_DMA0, NO_IRQ}, + .periphid = 0x00041330, }; static int __init s5p64x0_dma_init(void) { - unsigned int id; - - id = __raw_readl(S5P64X0_SYS_ID) & 0xFF000; + unsigned int id = __raw_readl(S5P64X0_SYS_ID) & 0xFF000; if (id == 0x50000) s5p64x0_device_pdma.dev.platform_data = &s5p6450_pdma_pdata; else s5p64x0_device_pdma.dev.platform_data = &s5p6440_pdma_pdata; - platform_device_register(&s5p64x0_device_pdma); + amba_device_register(&s5p64x0_device_pdma, &iomem_resource); return 0; } diff --git a/arch/arm/mach-s5p64x0/include/mach/dma.h b/arch/arm/mach-s5p64x0/include/mach/dma.h index 1beae2cca6a3..5a622af461d7 100644 --- a/arch/arm/mach-s5p64x0/include/mach/dma.h +++ b/arch/arm/mach-s5p64x0/include/mach/dma.h @@ -20,7 +20,7 @@ #ifndef __MACH_DMA_H #define __MACH_DMA_H -/* This platform uses the common S3C DMA API driver for PL330 */ +/* This platform uses the common common DMA API driver for PL330 */ #include #endif /* __MACH_DMA_H */ From 978ce50dd5cfd93380ded89d61de9d8109ebd814 Mon Sep 17 00:00:00 2001 From: Boojin Kim Date: Fri, 2 Sep 2011 09:44:40 +0900 Subject: [PATCH 0155/1531] ARM: SAMSUNG: Remove S3C-PL330-DMA driver Since DMA generic APIs can be used for Samsung DMA now so that the s3c-pl330 which includes Samsung specific DMA APIs can be removed. Signed-off-by: Boojin Kim Cc: Jassi Brar Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Kukjin Kim Signed-off-by: Vinod Koul --- arch/arm/plat-samsung/Kconfig | 6 - arch/arm/plat-samsung/Makefile | 2 - .../arm/plat-samsung/include/plat/dma-pl330.h | 10 +- .../include/plat/s3c-pl330-pdata.h | 32 - arch/arm/plat-samsung/s3c-pl330.c | 1244 ----------------- 5 files changed, 6 insertions(+), 1288 deletions(-) delete mode 100644 arch/arm/plat-samsung/include/plat/s3c-pl330-pdata.h delete mode 100644 arch/arm/plat-samsung/s3c-pl330.c diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig index e6f01ab86208..0a3eec616d61 100644 --- a/arch/arm/plat-samsung/Kconfig +++ b/arch/arm/plat-samsung/Kconfig @@ -300,12 +300,6 @@ config S3C_DMA help Internal configuration for S3C DMA core -config S3C_PL330_DMA - bool - select PL330 - help - S3C DMA API Driver for PL330 DMAC. - config SAMSUNG_DMADEV bool select DMADEVICES diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile index e2ee0b8a3763..90758494cad8 100644 --- a/arch/arm/plat-samsung/Makefile +++ b/arch/arm/plat-samsung/Makefile @@ -67,8 +67,6 @@ obj-$(CONFIG_S3C_DMA) += dma.o s3c-dma-ops.o obj-$(CONFIG_SAMSUNG_DMADEV) += dma-ops.o -obj-$(CONFIG_S3C_PL330_DMA) += s3c-pl330.o s3c-dma-ops.o - # PM support obj-$(CONFIG_PM) += pm.o diff --git a/arch/arm/plat-samsung/include/plat/dma-pl330.h b/arch/arm/plat-samsung/include/plat/dma-pl330.h index 2916920034f0..9a1dadb0218e 100644 --- a/arch/arm/plat-samsung/include/plat/dma-pl330.h +++ b/arch/arm/plat-samsung/include/plat/dma-pl330.h @@ -11,9 +11,6 @@ #ifndef __DMA_PL330_H_ #define __DMA_PL330_H_ __FILE__ -#define S3C2410_DMAF_AUTOSTART (1 << 0) -#define S3C2410_DMAF_CIRCULAR (1 << 1) - /* * PL330 can assign any channel to communicate with * any of the peripherals attched to the DMAC. @@ -88,6 +85,10 @@ enum dma_ch { DMACH_MAX, }; +struct s3c2410_dma_client { + char *name; +}; + static inline bool s3c_dma_has_circular(void) { return true; @@ -97,6 +98,7 @@ static inline bool samsung_dma_is_dmadev(void) { return true; } -#include + +#include #endif /* __DMA_PL330_H_ */ diff --git a/arch/arm/plat-samsung/include/plat/s3c-pl330-pdata.h b/arch/arm/plat-samsung/include/plat/s3c-pl330-pdata.h deleted file mode 100644 index 64fdf66ff9cf..000000000000 --- a/arch/arm/plat-samsung/include/plat/s3c-pl330-pdata.h +++ /dev/null @@ -1,32 +0,0 @@ -/* linux/arch/arm/plat-samsung/include/plat/s3c-pl330-pdata.h - * - * Copyright (C) 2010 Samsung Electronics Co. Ltd. - * Jaswinder Singh - * - * 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 __S3C_PL330_PDATA_H -#define __S3C_PL330_PDATA_H - -#include - -/* - * Every PL330 DMAC has max 32 peripheral interfaces, - * of which some may be not be really used in your - * DMAC's configuration. - * Populate this array of 32 peri i/fs with relevant - * channel IDs for used peri i/f and DMACH_MAX for - * those unused. - * - * The platforms just need to provide this info - * to the S3C DMA API driver for PL330. - */ -struct s3c_pl330_platdata { - enum dma_ch peri[32]; -}; - -#endif /* __S3C_PL330_PDATA_H */ diff --git a/arch/arm/plat-samsung/s3c-pl330.c b/arch/arm/plat-samsung/s3c-pl330.c deleted file mode 100644 index f85638c6f5ae..000000000000 --- a/arch/arm/plat-samsung/s3c-pl330.c +++ /dev/null @@ -1,1244 +0,0 @@ -/* linux/arch/arm/plat-samsung/s3c-pl330.c - * - * Copyright (C) 2010 Samsung Electronics Co. Ltd. - * Jaswinder Singh - * - * 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 - -/** - * struct s3c_pl330_dmac - Logical representation of a PL330 DMAC. - * @busy_chan: Number of channels currently busy. - * @peri: List of IDs of peripherals this DMAC can work with. - * @node: To attach to the global list of DMACs. - * @pi: PL330 configuration info for the DMAC. - * @kmcache: Pool to quickly allocate xfers for all channels in the dmac. - * @clk: Pointer of DMAC operation clock. - */ -struct s3c_pl330_dmac { - unsigned busy_chan; - enum dma_ch *peri; - struct list_head node; - struct pl330_info *pi; - struct kmem_cache *kmcache; - struct clk *clk; -}; - -/** - * struct s3c_pl330_xfer - A request submitted by S3C DMA clients. - * @token: Xfer ID provided by the client. - * @node: To attach to the list of xfers on a channel. - * @px: Xfer for PL330 core. - * @chan: Owner channel of this xfer. - */ -struct s3c_pl330_xfer { - void *token; - struct list_head node; - struct pl330_xfer px; - struct s3c_pl330_chan *chan; -}; - -/** - * struct s3c_pl330_chan - Logical channel to communicate with - * a Physical peripheral. - * @pl330_chan_id: Token of a hardware channel thread of PL330 DMAC. - * NULL if the channel is available to be acquired. - * @id: ID of the peripheral that this channel can communicate with. - * @options: Options specified by the client. - * @sdaddr: Address provided via s3c2410_dma_devconfig. - * @node: To attach to the global list of channels. - * @lrq: Pointer to the last submitted pl330_req to PL330 core. - * @xfer_list: To manage list of xfers enqueued. - * @req: Two requests to communicate with the PL330 engine. - * @callback_fn: Callback function to the client. - * @rqcfg: Channel configuration for the xfers. - * @xfer_head: Pointer to the xfer to be next executed. - * @dmac: Pointer to the DMAC that manages this channel, NULL if the - * channel is available to be acquired. - * @client: Client of this channel. NULL if the - * channel is available to be acquired. - */ -struct s3c_pl330_chan { - void *pl330_chan_id; - enum dma_ch id; - unsigned int options; - unsigned long sdaddr; - struct list_head node; - struct pl330_req *lrq; - struct list_head xfer_list; - struct pl330_req req[2]; - s3c2410_dma_cbfn_t callback_fn; - struct pl330_reqcfg rqcfg; - struct s3c_pl330_xfer *xfer_head; - struct s3c_pl330_dmac *dmac; - struct s3c2410_dma_client *client; -}; - -/* All DMACs in the platform */ -static LIST_HEAD(dmac_list); - -/* All channels to peripherals in the platform */ -static LIST_HEAD(chan_list); - -/* - * Since we add resources(DMACs and Channels) to the global pool, - * we need to guard access to the resources using a global lock - */ -static DEFINE_SPINLOCK(res_lock); - -/* Returns the channel with ID 'id' in the chan_list */ -static struct s3c_pl330_chan *id_to_chan(const enum dma_ch id) -{ - struct s3c_pl330_chan *ch; - - list_for_each_entry(ch, &chan_list, node) - if (ch->id == id) - return ch; - - return NULL; -} - -/* Allocate a new channel with ID 'id' and add to chan_list */ -static void chan_add(const enum dma_ch id) -{ - struct s3c_pl330_chan *ch = id_to_chan(id); - - /* Return if the channel already exists */ - if (ch) - return; - - ch = kmalloc(sizeof(*ch), GFP_KERNEL); - /* Return silently to work with other channels */ - if (!ch) - return; - - ch->id = id; - ch->dmac = NULL; - - list_add_tail(&ch->node, &chan_list); -} - -/* If the channel is not yet acquired by any client */ -static bool chan_free(struct s3c_pl330_chan *ch) -{ - if (!ch) - return false; - - /* Channel points to some DMAC only when it's acquired */ - return ch->dmac ? false : true; -} - -/* - * Returns 0 is peripheral i/f is invalid or not present on the dmac. - * Index + 1, otherwise. - */ -static unsigned iface_of_dmac(struct s3c_pl330_dmac *dmac, enum dma_ch ch_id) -{ - enum dma_ch *id = dmac->peri; - int i; - - /* Discount invalid markers */ - if (ch_id == DMACH_MAX) - return 0; - - for (i = 0; i < PL330_MAX_PERI; i++) - if (id[i] == ch_id) - return i + 1; - - return 0; -} - -/* If all channel threads of the DMAC are busy */ -static inline bool dmac_busy(struct s3c_pl330_dmac *dmac) -{ - struct pl330_info *pi = dmac->pi; - - return (dmac->busy_chan < pi->pcfg.num_chan) ? false : true; -} - -/* - * Returns the number of free channels that - * can be handled by this dmac only. - */ -static unsigned ch_onlyby_dmac(struct s3c_pl330_dmac *dmac) -{ - enum dma_ch *id = dmac->peri; - struct s3c_pl330_dmac *d; - struct s3c_pl330_chan *ch; - unsigned found, count = 0; - enum dma_ch p; - int i; - - for (i = 0; i < PL330_MAX_PERI; i++) { - p = id[i]; - ch = id_to_chan(p); - - if (p == DMACH_MAX || !chan_free(ch)) - continue; - - found = 0; - list_for_each_entry(d, &dmac_list, node) { - if (d != dmac && iface_of_dmac(d, ch->id)) { - found = 1; - break; - } - } - if (!found) - count++; - } - - return count; -} - -/* - * Measure of suitability of 'dmac' handling 'ch' - * - * 0 indicates 'dmac' can not handle 'ch' either - * because it is not supported by the hardware or - * because all dmac channels are currently busy. - * - * >0 vlaue indicates 'dmac' has the capability. - * The bigger the value the more suitable the dmac. - */ -#define MAX_SUIT UINT_MAX -#define MIN_SUIT 0 - -static unsigned suitablility(struct s3c_pl330_dmac *dmac, - struct s3c_pl330_chan *ch) -{ - struct pl330_info *pi = dmac->pi; - enum dma_ch *id = dmac->peri; - struct s3c_pl330_dmac *d; - unsigned s; - int i; - - s = MIN_SUIT; - /* If all the DMAC channel threads are busy */ - if (dmac_busy(dmac)) - return s; - - for (i = 0; i < PL330_MAX_PERI; i++) - if (id[i] == ch->id) - break; - - /* If the 'dmac' can't talk to 'ch' */ - if (i == PL330_MAX_PERI) - return s; - - s = MAX_SUIT; - list_for_each_entry(d, &dmac_list, node) { - /* - * If some other dmac can talk to this - * peri and has some channel free. - */ - if (d != dmac && iface_of_dmac(d, ch->id) && !dmac_busy(d)) { - s = 0; - break; - } - } - if (s) - return s; - - s = 100; - - /* Good if free chans are more, bad otherwise */ - s += (pi->pcfg.num_chan - dmac->busy_chan) - ch_onlyby_dmac(dmac); - - return s; -} - -/* More than one DMAC may have capability to transfer data with the - * peripheral. This function assigns most suitable DMAC to manage the - * channel and hence communicate with the peripheral. - */ -static struct s3c_pl330_dmac *map_chan_to_dmac(struct s3c_pl330_chan *ch) -{ - struct s3c_pl330_dmac *d, *dmac = NULL; - unsigned sn, sl = MIN_SUIT; - - list_for_each_entry(d, &dmac_list, node) { - sn = suitablility(d, ch); - - if (sn == MAX_SUIT) - return d; - - if (sn > sl) - dmac = d; - } - - return dmac; -} - -/* Acquire the channel for peripheral 'id' */ -static struct s3c_pl330_chan *chan_acquire(const enum dma_ch id) -{ - struct s3c_pl330_chan *ch = id_to_chan(id); - struct s3c_pl330_dmac *dmac; - - /* If the channel doesn't exist or is already acquired */ - if (!ch || !chan_free(ch)) { - ch = NULL; - goto acq_exit; - } - - dmac = map_chan_to_dmac(ch); - /* If couldn't map */ - if (!dmac) { - ch = NULL; - goto acq_exit; - } - - dmac->busy_chan++; - ch->dmac = dmac; - -acq_exit: - return ch; -} - -/* Delete xfer from the queue */ -static inline void del_from_queue(struct s3c_pl330_xfer *xfer) -{ - struct s3c_pl330_xfer *t; - struct s3c_pl330_chan *ch; - int found; - - if (!xfer) - return; - - ch = xfer->chan; - - /* Make sure xfer is in the queue */ - found = 0; - list_for_each_entry(t, &ch->xfer_list, node) - if (t == xfer) { - found = 1; - break; - } - - if (!found) - return; - - /* If xfer is last entry in the queue */ - if (xfer->node.next == &ch->xfer_list) - t = list_entry(ch->xfer_list.next, - struct s3c_pl330_xfer, node); - else - t = list_entry(xfer->node.next, - struct s3c_pl330_xfer, node); - - /* If there was only one node left */ - if (t == xfer) - ch->xfer_head = NULL; - else if (ch->xfer_head == xfer) - ch->xfer_head = t; - - list_del(&xfer->node); -} - -/* Provides pointer to the next xfer in the queue. - * If CIRCULAR option is set, the list is left intact, - * otherwise the xfer is removed from the list. - * Forced delete 'pluck' can be set to override the CIRCULAR option. - */ -static struct s3c_pl330_xfer *get_from_queue(struct s3c_pl330_chan *ch, - int pluck) -{ - struct s3c_pl330_xfer *xfer = ch->xfer_head; - - if (!xfer) - return NULL; - - /* If xfer is last entry in the queue */ - if (xfer->node.next == &ch->xfer_list) - ch->xfer_head = list_entry(ch->xfer_list.next, - struct s3c_pl330_xfer, node); - else - ch->xfer_head = list_entry(xfer->node.next, - struct s3c_pl330_xfer, node); - - if (pluck || !(ch->options & S3C2410_DMAF_CIRCULAR)) - del_from_queue(xfer); - - return xfer; -} - -static inline void add_to_queue(struct s3c_pl330_chan *ch, - struct s3c_pl330_xfer *xfer, int front) -{ - struct pl330_xfer *xt; - - /* If queue empty */ - if (ch->xfer_head == NULL) - ch->xfer_head = xfer; - - xt = &ch->xfer_head->px; - /* If the head already submitted (CIRCULAR head) */ - if (ch->options & S3C2410_DMAF_CIRCULAR && - (xt == ch->req[0].x || xt == ch->req[1].x)) - ch->xfer_head = xfer; - - /* If this is a resubmission, it should go at the head */ - if (front) { - ch->xfer_head = xfer; - list_add(&xfer->node, &ch->xfer_list); - } else { - list_add_tail(&xfer->node, &ch->xfer_list); - } -} - -static inline void _finish_off(struct s3c_pl330_xfer *xfer, - enum s3c2410_dma_buffresult res, int ffree) -{ - struct s3c_pl330_chan *ch; - - if (!xfer) - return; - - ch = xfer->chan; - - /* Do callback */ - if (ch->callback_fn) - ch->callback_fn(NULL, xfer->token, xfer->px.bytes, res); - - /* Force Free or if buffer is not needed anymore */ - if (ffree || !(ch->options & S3C2410_DMAF_CIRCULAR)) - kmem_cache_free(ch->dmac->kmcache, xfer); -} - -static inline int s3c_pl330_submit(struct s3c_pl330_chan *ch, - struct pl330_req *r) -{ - struct s3c_pl330_xfer *xfer; - int ret = 0; - - /* If already submitted */ - if (r->x) - return 0; - - xfer = get_from_queue(ch, 0); - if (xfer) { - r->x = &xfer->px; - - /* Use max bandwidth for M<->M xfers */ - if (r->rqtype == MEMTOMEM) { - struct pl330_info *pi = xfer->chan->dmac->pi; - int burst = 1 << ch->rqcfg.brst_size; - u32 bytes = r->x->bytes; - int bl; - - bl = pi->pcfg.data_bus_width / 8; - bl *= pi->pcfg.data_buf_dep; - bl /= burst; - - /* src/dst_burst_len can't be more than 16 */ - if (bl > 16) - bl = 16; - - while (bl > 1) { - if (!(bytes % (bl * burst))) - break; - bl--; - } - - ch->rqcfg.brst_len = bl; - } else { - ch->rqcfg.brst_len = 1; - } - - ret = pl330_submit_req(ch->pl330_chan_id, r); - - /* If submission was successful */ - if (!ret) { - ch->lrq = r; /* latest submitted req */ - return 0; - } - - r->x = NULL; - - /* If both of the PL330 ping-pong buffers filled */ - if (ret == -EAGAIN) { - dev_err(ch->dmac->pi->dev, "%s:%d!\n", - __func__, __LINE__); - /* Queue back again */ - add_to_queue(ch, xfer, 1); - ret = 0; - } else { - dev_err(ch->dmac->pi->dev, "%s:%d!\n", - __func__, __LINE__); - _finish_off(xfer, S3C2410_RES_ERR, 0); - } - } - - return ret; -} - -static void s3c_pl330_rq(struct s3c_pl330_chan *ch, - struct pl330_req *r, enum pl330_op_err err) -{ - unsigned long flags; - struct s3c_pl330_xfer *xfer; - struct pl330_xfer *xl = r->x; - enum s3c2410_dma_buffresult res; - - spin_lock_irqsave(&res_lock, flags); - - r->x = NULL; - - s3c_pl330_submit(ch, r); - - spin_unlock_irqrestore(&res_lock, flags); - - /* Map result to S3C DMA API */ - if (err == PL330_ERR_NONE) - res = S3C2410_RES_OK; - else if (err == PL330_ERR_ABORT) - res = S3C2410_RES_ABORT; - else - res = S3C2410_RES_ERR; - - /* If last request had some xfer */ - if (xl) { - xfer = container_of(xl, struct s3c_pl330_xfer, px); - _finish_off(xfer, res, 0); - } else { - dev_info(ch->dmac->pi->dev, "%s:%d No Xfer?!\n", - __func__, __LINE__); - } -} - -static void s3c_pl330_rq0(void *token, enum pl330_op_err err) -{ - struct pl330_req *r = token; - struct s3c_pl330_chan *ch = container_of(r, - struct s3c_pl330_chan, req[0]); - s3c_pl330_rq(ch, r, err); -} - -static void s3c_pl330_rq1(void *token, enum pl330_op_err err) -{ - struct pl330_req *r = token; - struct s3c_pl330_chan *ch = container_of(r, - struct s3c_pl330_chan, req[1]); - s3c_pl330_rq(ch, r, err); -} - -/* Release an acquired channel */ -static void chan_release(struct s3c_pl330_chan *ch) -{ - struct s3c_pl330_dmac *dmac; - - if (chan_free(ch)) - return; - - dmac = ch->dmac; - ch->dmac = NULL; - dmac->busy_chan--; -} - -int s3c2410_dma_ctrl(enum dma_ch id, enum s3c2410_chan_op op) -{ - struct s3c_pl330_xfer *xfer; - enum pl330_chan_op pl330op; - struct s3c_pl330_chan *ch; - unsigned long flags; - int idx, ret; - - spin_lock_irqsave(&res_lock, flags); - - ch = id_to_chan(id); - - if (!ch || chan_free(ch)) { - ret = -EINVAL; - goto ctrl_exit; - } - - switch (op) { - case S3C2410_DMAOP_START: - /* Make sure both reqs are enqueued */ - idx = (ch->lrq == &ch->req[0]) ? 1 : 0; - s3c_pl330_submit(ch, &ch->req[idx]); - s3c_pl330_submit(ch, &ch->req[1 - idx]); - pl330op = PL330_OP_START; - break; - - case S3C2410_DMAOP_STOP: - pl330op = PL330_OP_ABORT; - break; - - case S3C2410_DMAOP_FLUSH: - pl330op = PL330_OP_FLUSH; - break; - - case S3C2410_DMAOP_PAUSE: - case S3C2410_DMAOP_RESUME: - case S3C2410_DMAOP_TIMEOUT: - case S3C2410_DMAOP_STARTED: - spin_unlock_irqrestore(&res_lock, flags); - return 0; - - default: - spin_unlock_irqrestore(&res_lock, flags); - return -EINVAL; - } - - ret = pl330_chan_ctrl(ch->pl330_chan_id, pl330op); - - if (pl330op == PL330_OP_START) { - spin_unlock_irqrestore(&res_lock, flags); - return ret; - } - - idx = (ch->lrq == &ch->req[0]) ? 1 : 0; - - /* Abort the current xfer */ - if (ch->req[idx].x) { - xfer = container_of(ch->req[idx].x, - struct s3c_pl330_xfer, px); - - /* Drop xfer during FLUSH */ - if (pl330op == PL330_OP_FLUSH) - del_from_queue(xfer); - - ch->req[idx].x = NULL; - - spin_unlock_irqrestore(&res_lock, flags); - _finish_off(xfer, S3C2410_RES_ABORT, - pl330op == PL330_OP_FLUSH ? 1 : 0); - spin_lock_irqsave(&res_lock, flags); - } - - /* Flush the whole queue */ - if (pl330op == PL330_OP_FLUSH) { - - if (ch->req[1 - idx].x) { - xfer = container_of(ch->req[1 - idx].x, - struct s3c_pl330_xfer, px); - - del_from_queue(xfer); - - ch->req[1 - idx].x = NULL; - - spin_unlock_irqrestore(&res_lock, flags); - _finish_off(xfer, S3C2410_RES_ABORT, 1); - spin_lock_irqsave(&res_lock, flags); - } - - /* Finish off the remaining in the queue */ - xfer = ch->xfer_head; - while (xfer) { - - del_from_queue(xfer); - - spin_unlock_irqrestore(&res_lock, flags); - _finish_off(xfer, S3C2410_RES_ABORT, 1); - spin_lock_irqsave(&res_lock, flags); - - xfer = ch->xfer_head; - } - } - -ctrl_exit: - spin_unlock_irqrestore(&res_lock, flags); - - return ret; -} -EXPORT_SYMBOL(s3c2410_dma_ctrl); - -int s3c2410_dma_enqueue(enum dma_ch id, void *token, - dma_addr_t addr, int size) -{ - struct s3c_pl330_chan *ch; - struct s3c_pl330_xfer *xfer; - unsigned long flags; - int idx, ret = 0; - - spin_lock_irqsave(&res_lock, flags); - - ch = id_to_chan(id); - - /* Error if invalid or free channel */ - if (!ch || chan_free(ch)) { - ret = -EINVAL; - goto enq_exit; - } - - /* Error if size is unaligned */ - if (ch->rqcfg.brst_size && size % (1 << ch->rqcfg.brst_size)) { - ret = -EINVAL; - goto enq_exit; - } - - xfer = kmem_cache_alloc(ch->dmac->kmcache, GFP_ATOMIC); - if (!xfer) { - ret = -ENOMEM; - goto enq_exit; - } - - xfer->token = token; - xfer->chan = ch; - xfer->px.bytes = size; - xfer->px.next = NULL; /* Single request */ - - /* For S3C DMA API, direction is always fixed for all xfers */ - if (ch->req[0].rqtype == MEMTODEV) { - xfer->px.src_addr = addr; - xfer->px.dst_addr = ch->sdaddr; - } else { - xfer->px.src_addr = ch->sdaddr; - xfer->px.dst_addr = addr; - } - - add_to_queue(ch, xfer, 0); - - /* Try submitting on either request */ - idx = (ch->lrq == &ch->req[0]) ? 1 : 0; - - if (!ch->req[idx].x) - s3c_pl330_submit(ch, &ch->req[idx]); - else - s3c_pl330_submit(ch, &ch->req[1 - idx]); - - spin_unlock_irqrestore(&res_lock, flags); - - if (ch->options & S3C2410_DMAF_AUTOSTART) - s3c2410_dma_ctrl(id, S3C2410_DMAOP_START); - - return 0; - -enq_exit: - spin_unlock_irqrestore(&res_lock, flags); - - return ret; -} -EXPORT_SYMBOL(s3c2410_dma_enqueue); - -int s3c2410_dma_request(enum dma_ch id, - struct s3c2410_dma_client *client, - void *dev) -{ - struct s3c_pl330_dmac *dmac; - struct s3c_pl330_chan *ch; - unsigned long flags; - int ret = 0; - - spin_lock_irqsave(&res_lock, flags); - - ch = chan_acquire(id); - if (!ch) { - ret = -EBUSY; - goto req_exit; - } - - dmac = ch->dmac; - - ch->pl330_chan_id = pl330_request_channel(dmac->pi); - if (!ch->pl330_chan_id) { - chan_release(ch); - ret = -EBUSY; - goto req_exit; - } - - ch->client = client; - ch->options = 0; /* Clear any option */ - ch->callback_fn = NULL; /* Clear any callback */ - ch->lrq = NULL; - - ch->rqcfg.brst_size = 2; /* Default word size */ - ch->rqcfg.swap = SWAP_NO; - ch->rqcfg.scctl = SCCTRL0; /* Noncacheable and nonbufferable */ - ch->rqcfg.dcctl = DCCTRL0; /* Noncacheable and nonbufferable */ - ch->rqcfg.privileged = 0; - ch->rqcfg.insnaccess = 0; - - /* Set invalid direction */ - ch->req[0].rqtype = DEVTODEV; - ch->req[1].rqtype = ch->req[0].rqtype; - - ch->req[0].cfg = &ch->rqcfg; - ch->req[1].cfg = ch->req[0].cfg; - - ch->req[0].peri = iface_of_dmac(dmac, id) - 1; /* Original index */ - ch->req[1].peri = ch->req[0].peri; - - ch->req[0].token = &ch->req[0]; - ch->req[0].xfer_cb = s3c_pl330_rq0; - ch->req[1].token = &ch->req[1]; - ch->req[1].xfer_cb = s3c_pl330_rq1; - - ch->req[0].x = NULL; - ch->req[1].x = NULL; - - /* Reset xfer list */ - INIT_LIST_HEAD(&ch->xfer_list); - ch->xfer_head = NULL; - -req_exit: - spin_unlock_irqrestore(&res_lock, flags); - - return ret; -} -EXPORT_SYMBOL(s3c2410_dma_request); - -int s3c2410_dma_free(enum dma_ch id, struct s3c2410_dma_client *client) -{ - struct s3c_pl330_chan *ch; - struct s3c_pl330_xfer *xfer; - unsigned long flags; - int ret = 0; - unsigned idx; - - spin_lock_irqsave(&res_lock, flags); - - ch = id_to_chan(id); - - if (!ch || chan_free(ch)) - goto free_exit; - - /* Refuse if someone else wanted to free the channel */ - if (ch->client != client) { - ret = -EBUSY; - goto free_exit; - } - - /* Stop any active xfer, Flushe the queue and do callbacks */ - pl330_chan_ctrl(ch->pl330_chan_id, PL330_OP_FLUSH); - - /* Abort the submitted requests */ - idx = (ch->lrq == &ch->req[0]) ? 1 : 0; - - if (ch->req[idx].x) { - xfer = container_of(ch->req[idx].x, - struct s3c_pl330_xfer, px); - - ch->req[idx].x = NULL; - del_from_queue(xfer); - - spin_unlock_irqrestore(&res_lock, flags); - _finish_off(xfer, S3C2410_RES_ABORT, 1); - spin_lock_irqsave(&res_lock, flags); - } - - if (ch->req[1 - idx].x) { - xfer = container_of(ch->req[1 - idx].x, - struct s3c_pl330_xfer, px); - - ch->req[1 - idx].x = NULL; - del_from_queue(xfer); - - spin_unlock_irqrestore(&res_lock, flags); - _finish_off(xfer, S3C2410_RES_ABORT, 1); - spin_lock_irqsave(&res_lock, flags); - } - - /* Pluck and Abort the queued requests in order */ - do { - xfer = get_from_queue(ch, 1); - - spin_unlock_irqrestore(&res_lock, flags); - _finish_off(xfer, S3C2410_RES_ABORT, 1); - spin_lock_irqsave(&res_lock, flags); - } while (xfer); - - ch->client = NULL; - - pl330_release_channel(ch->pl330_chan_id); - - ch->pl330_chan_id = NULL; - - chan_release(ch); - -free_exit: - spin_unlock_irqrestore(&res_lock, flags); - - return ret; -} -EXPORT_SYMBOL(s3c2410_dma_free); - -int s3c2410_dma_config(enum dma_ch id, int xferunit) -{ - struct s3c_pl330_chan *ch; - struct pl330_info *pi; - unsigned long flags; - int i, dbwidth, ret = 0; - - spin_lock_irqsave(&res_lock, flags); - - ch = id_to_chan(id); - - if (!ch || chan_free(ch)) { - ret = -EINVAL; - goto cfg_exit; - } - - pi = ch->dmac->pi; - dbwidth = pi->pcfg.data_bus_width / 8; - - /* Max size of xfer can be pcfg.data_bus_width */ - if (xferunit > dbwidth) { - ret = -EINVAL; - goto cfg_exit; - } - - i = 0; - while (xferunit != (1 << i)) - i++; - - /* If valid value */ - if (xferunit == (1 << i)) - ch->rqcfg.brst_size = i; - else - ret = -EINVAL; - -cfg_exit: - spin_unlock_irqrestore(&res_lock, flags); - - return ret; -} -EXPORT_SYMBOL(s3c2410_dma_config); - -/* Options that are supported by this driver */ -#define S3C_PL330_FLAGS (S3C2410_DMAF_CIRCULAR | S3C2410_DMAF_AUTOSTART) - -int s3c2410_dma_setflags(enum dma_ch id, unsigned int options) -{ - struct s3c_pl330_chan *ch; - unsigned long flags; - int ret = 0; - - spin_lock_irqsave(&res_lock, flags); - - ch = id_to_chan(id); - - if (!ch || chan_free(ch) || options & ~(S3C_PL330_FLAGS)) - ret = -EINVAL; - else - ch->options = options; - - spin_unlock_irqrestore(&res_lock, flags); - - return 0; -} -EXPORT_SYMBOL(s3c2410_dma_setflags); - -int s3c2410_dma_set_buffdone_fn(enum dma_ch id, s3c2410_dma_cbfn_t rtn) -{ - struct s3c_pl330_chan *ch; - unsigned long flags; - int ret = 0; - - spin_lock_irqsave(&res_lock, flags); - - ch = id_to_chan(id); - - if (!ch || chan_free(ch)) - ret = -EINVAL; - else - ch->callback_fn = rtn; - - spin_unlock_irqrestore(&res_lock, flags); - - return ret; -} -EXPORT_SYMBOL(s3c2410_dma_set_buffdone_fn); - -int s3c2410_dma_devconfig(enum dma_ch id, enum s3c2410_dmasrc source, - unsigned long address) -{ - struct s3c_pl330_chan *ch; - unsigned long flags; - int ret = 0; - - spin_lock_irqsave(&res_lock, flags); - - ch = id_to_chan(id); - - if (!ch || chan_free(ch)) { - ret = -EINVAL; - goto devcfg_exit; - } - - switch (source) { - case S3C2410_DMASRC_HW: /* P->M */ - ch->req[0].rqtype = DEVTOMEM; - ch->req[1].rqtype = DEVTOMEM; - ch->rqcfg.src_inc = 0; - ch->rqcfg.dst_inc = 1; - break; - case S3C2410_DMASRC_MEM: /* M->P */ - ch->req[0].rqtype = MEMTODEV; - ch->req[1].rqtype = MEMTODEV; - ch->rqcfg.src_inc = 1; - ch->rqcfg.dst_inc = 0; - break; - default: - ret = -EINVAL; - goto devcfg_exit; - } - - ch->sdaddr = address; - -devcfg_exit: - spin_unlock_irqrestore(&res_lock, flags); - - return ret; -} -EXPORT_SYMBOL(s3c2410_dma_devconfig); - -int s3c2410_dma_getposition(enum dma_ch id, dma_addr_t *src, dma_addr_t *dst) -{ - struct s3c_pl330_chan *ch = id_to_chan(id); - struct pl330_chanstatus status; - int ret; - - if (!ch || chan_free(ch)) - return -EINVAL; - - ret = pl330_chan_status(ch->pl330_chan_id, &status); - if (ret < 0) - return ret; - - *src = status.src_addr; - *dst = status.dst_addr; - - return 0; -} -EXPORT_SYMBOL(s3c2410_dma_getposition); - -static irqreturn_t pl330_irq_handler(int irq, void *data) -{ - if (pl330_update(data)) - return IRQ_HANDLED; - else - return IRQ_NONE; -} - -static int pl330_probe(struct platform_device *pdev) -{ - struct s3c_pl330_dmac *s3c_pl330_dmac; - struct s3c_pl330_platdata *pl330pd; - struct pl330_info *pl330_info; - struct resource *res; - int i, ret, irq; - - pl330pd = pdev->dev.platform_data; - - /* Can't do without the list of _32_ peripherals */ - if (!pl330pd || !pl330pd->peri) { - dev_err(&pdev->dev, "platform data missing!\n"); - return -ENODEV; - } - - pl330_info = kzalloc(sizeof(*pl330_info), GFP_KERNEL); - if (!pl330_info) - return -ENOMEM; - - pl330_info->pl330_data = NULL; - pl330_info->dev = &pdev->dev; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - ret = -ENODEV; - goto probe_err1; - } - - request_mem_region(res->start, resource_size(res), pdev->name); - - pl330_info->base = ioremap(res->start, resource_size(res)); - if (!pl330_info->base) { - ret = -ENXIO; - goto probe_err2; - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - ret = irq; - goto probe_err3; - } - - ret = request_irq(irq, pl330_irq_handler, 0, - dev_name(&pdev->dev), pl330_info); - if (ret) - goto probe_err4; - - /* Allocate a new DMAC */ - s3c_pl330_dmac = kmalloc(sizeof(*s3c_pl330_dmac), GFP_KERNEL); - if (!s3c_pl330_dmac) { - ret = -ENOMEM; - goto probe_err5; - } - - /* Get operation clock and enable it */ - s3c_pl330_dmac->clk = clk_get(&pdev->dev, "pdma"); - if (IS_ERR(s3c_pl330_dmac->clk)) { - dev_err(&pdev->dev, "Cannot get operation clock.\n"); - ret = -EINVAL; - goto probe_err6; - } - clk_enable(s3c_pl330_dmac->clk); - - ret = pl330_add(pl330_info); - if (ret) - goto probe_err7; - - /* Hook the info */ - s3c_pl330_dmac->pi = pl330_info; - - /* No busy channels */ - s3c_pl330_dmac->busy_chan = 0; - - s3c_pl330_dmac->kmcache = kmem_cache_create(dev_name(&pdev->dev), - sizeof(struct s3c_pl330_xfer), 0, 0, NULL); - - if (!s3c_pl330_dmac->kmcache) { - ret = -ENOMEM; - goto probe_err8; - } - - /* Get the list of peripherals */ - s3c_pl330_dmac->peri = pl330pd->peri; - - /* Attach to the list of DMACs */ - list_add_tail(&s3c_pl330_dmac->node, &dmac_list); - - /* Create a channel for each peripheral in the DMAC - * that is, if it doesn't already exist - */ - for (i = 0; i < PL330_MAX_PERI; i++) - if (s3c_pl330_dmac->peri[i] != DMACH_MAX) - chan_add(s3c_pl330_dmac->peri[i]); - - printk(KERN_INFO - "Loaded driver for PL330 DMAC-%d %s\n", pdev->id, pdev->name); - printk(KERN_INFO - "\tDBUFF-%ux%ubytes Num_Chans-%u Num_Peri-%u Num_Events-%u\n", - pl330_info->pcfg.data_buf_dep, - pl330_info->pcfg.data_bus_width / 8, pl330_info->pcfg.num_chan, - pl330_info->pcfg.num_peri, pl330_info->pcfg.num_events); - - return 0; - -probe_err8: - pl330_del(pl330_info); -probe_err7: - clk_disable(s3c_pl330_dmac->clk); - clk_put(s3c_pl330_dmac->clk); -probe_err6: - kfree(s3c_pl330_dmac); -probe_err5: - free_irq(irq, pl330_info); -probe_err4: -probe_err3: - iounmap(pl330_info->base); -probe_err2: - release_mem_region(res->start, resource_size(res)); -probe_err1: - kfree(pl330_info); - - return ret; -} - -static int pl330_remove(struct platform_device *pdev) -{ - struct s3c_pl330_dmac *dmac, *d; - struct s3c_pl330_chan *ch; - unsigned long flags; - int del, found; - - if (!pdev->dev.platform_data) - return -EINVAL; - - spin_lock_irqsave(&res_lock, flags); - - found = 0; - list_for_each_entry(d, &dmac_list, node) - if (d->pi->dev == &pdev->dev) { - found = 1; - break; - } - - if (!found) { - spin_unlock_irqrestore(&res_lock, flags); - return 0; - } - - dmac = d; - - /* Remove all Channels that are managed only by this DMAC */ - list_for_each_entry(ch, &chan_list, node) { - - /* Only channels that are handled by this DMAC */ - if (iface_of_dmac(dmac, ch->id)) - del = 1; - else - continue; - - /* Don't remove if some other DMAC has it too */ - list_for_each_entry(d, &dmac_list, node) - if (d != dmac && iface_of_dmac(d, ch->id)) { - del = 0; - break; - } - - if (del) { - spin_unlock_irqrestore(&res_lock, flags); - s3c2410_dma_free(ch->id, ch->client); - spin_lock_irqsave(&res_lock, flags); - list_del(&ch->node); - kfree(ch); - } - } - - /* Disable operation clock */ - clk_disable(dmac->clk); - clk_put(dmac->clk); - - /* Remove the DMAC */ - list_del(&dmac->node); - kfree(dmac); - - spin_unlock_irqrestore(&res_lock, flags); - - return 0; -} - -static struct platform_driver pl330_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "s3c-pl330", - }, - .probe = pl330_probe, - .remove = pl330_remove, -}; - -static int __init pl330_init(void) -{ - return platform_driver_register(&pl330_driver); -} -module_init(pl330_init); - -static void __exit pl330_exit(void) -{ - platform_driver_unregister(&pl330_driver); - return; -} -module_exit(pl330_exit); - -MODULE_AUTHOR("Jaswinder Singh "); -MODULE_DESCRIPTION("Driver for PL330 DMA Controller"); -MODULE_LICENSE("GPL"); From 39d3e8074e44c1953928a0b91bc328f552c5fc79 Mon Sep 17 00:00:00 2001 From: Boojin Kim Date: Fri, 2 Sep 2011 09:44:41 +0900 Subject: [PATCH 0156/1531] spi/s3c64xx: Add support DMA engine API This patch adds to support DMA generic API to transfer raw SPI data. Basiclly the spi driver uses DMA generic API if architecture supports it. Otherwise, uses Samsung specific S3C-PL330 APIs. Signed-off-by: Boojin Kim Acked-by: Linus Walleij Acked-by: Vinod Koul Acked-by: Grant Likely Signed-off-by: Kukjin Kim Signed-off-by: Vinod Koul --- drivers/spi/spi-s3c64xx.c | 141 +++++++++++++++++++------------------- 1 file changed, 69 insertions(+), 72 deletions(-) diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 595dacc7645f..24f49032ec35 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -171,6 +171,9 @@ struct s3c64xx_spi_driver_data { unsigned state; unsigned cur_mode, cur_bpw; unsigned cur_speed; + unsigned rx_ch; + unsigned tx_ch; + struct samsung_dma_ops *ops; }; static struct s3c2410_dma_client s3c64xx_spi_dma_client = { @@ -226,6 +229,38 @@ static void flush_fifo(struct s3c64xx_spi_driver_data *sdd) writel(val, regs + S3C64XX_SPI_CH_CFG); } +static void s3c64xx_spi_dma_rxcb(void *data) +{ + struct s3c64xx_spi_driver_data *sdd + = (struct s3c64xx_spi_driver_data *)data; + unsigned long flags; + + spin_lock_irqsave(&sdd->lock, flags); + + sdd->state &= ~RXBUSY; + /* If the other done */ + if (!(sdd->state & TXBUSY)) + complete(&sdd->xfer_completion); + + spin_unlock_irqrestore(&sdd->lock, flags); +} + +static void s3c64xx_spi_dma_txcb(void *data) +{ + struct s3c64xx_spi_driver_data *sdd + = (struct s3c64xx_spi_driver_data *)data; + unsigned long flags; + + spin_lock_irqsave(&sdd->lock, flags); + + sdd->state &= ~TXBUSY; + /* If the other done */ + if (!(sdd->state & RXBUSY)) + complete(&sdd->xfer_completion); + + spin_unlock_irqrestore(&sdd->lock, flags); +} + static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, struct spi_device *spi, struct spi_transfer *xfer, int dma_mode) @@ -233,6 +268,7 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, struct s3c64xx_spi_info *sci = sdd->cntrlr_info; void __iomem *regs = sdd->regs; u32 modecfg, chcfg; + struct samsung_dma_prep_info info; modecfg = readl(regs + S3C64XX_SPI_MODE_CFG); modecfg &= ~(S3C64XX_SPI_MODE_TXDMA_ON | S3C64XX_SPI_MODE_RXDMA_ON); @@ -258,10 +294,14 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, chcfg |= S3C64XX_SPI_CH_TXCH_ON; if (dma_mode) { modecfg |= S3C64XX_SPI_MODE_TXDMA_ON; - s3c2410_dma_config(sdd->tx_dmach, sdd->cur_bpw / 8); - s3c2410_dma_enqueue(sdd->tx_dmach, (void *)sdd, - xfer->tx_dma, xfer->len); - s3c2410_dma_ctrl(sdd->tx_dmach, S3C2410_DMAOP_START); + info.cap = DMA_SLAVE; + info.direction = DMA_TO_DEVICE; + info.buf = xfer->tx_dma; + info.len = xfer->len; + info.fp = s3c64xx_spi_dma_txcb; + info.fp_param = sdd; + sdd->ops->prepare(sdd->tx_ch, &info); + sdd->ops->trigger(sdd->tx_ch); } else { switch (sdd->cur_bpw) { case 32: @@ -293,10 +333,14 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff) | S3C64XX_SPI_PACKET_CNT_EN, regs + S3C64XX_SPI_PACKET_CNT); - s3c2410_dma_config(sdd->rx_dmach, sdd->cur_bpw / 8); - s3c2410_dma_enqueue(sdd->rx_dmach, (void *)sdd, - xfer->rx_dma, xfer->len); - s3c2410_dma_ctrl(sdd->rx_dmach, S3C2410_DMAOP_START); + info.cap = DMA_SLAVE; + info.direction = DMA_FROM_DEVICE; + info.buf = xfer->rx_dma; + info.len = xfer->len; + info.fp = s3c64xx_spi_dma_rxcb; + info.fp_param = sdd; + sdd->ops->prepare(sdd->rx_ch, &info); + sdd->ops->trigger(sdd->rx_ch); } } @@ -482,46 +526,6 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) } } -static void s3c64xx_spi_dma_rxcb(struct s3c2410_dma_chan *chan, void *buf_id, - int size, enum s3c2410_dma_buffresult res) -{ - struct s3c64xx_spi_driver_data *sdd = buf_id; - unsigned long flags; - - spin_lock_irqsave(&sdd->lock, flags); - - if (res == S3C2410_RES_OK) - sdd->state &= ~RXBUSY; - else - dev_err(&sdd->pdev->dev, "DmaAbrtRx-%d\n", size); - - /* If the other done */ - if (!(sdd->state & TXBUSY)) - complete(&sdd->xfer_completion); - - spin_unlock_irqrestore(&sdd->lock, flags); -} - -static void s3c64xx_spi_dma_txcb(struct s3c2410_dma_chan *chan, void *buf_id, - int size, enum s3c2410_dma_buffresult res) -{ - struct s3c64xx_spi_driver_data *sdd = buf_id; - unsigned long flags; - - spin_lock_irqsave(&sdd->lock, flags); - - if (res == S3C2410_RES_OK) - sdd->state &= ~TXBUSY; - else - dev_err(&sdd->pdev->dev, "DmaAbrtTx-%d \n", size); - - /* If the other done */ - if (!(sdd->state & RXBUSY)) - complete(&sdd->xfer_completion); - - spin_unlock_irqrestore(&sdd->lock, flags); -} - #define XFER_DMAADDR_INVALID DMA_BIT_MASK(32) static int s3c64xx_spi_map_mssg(struct s3c64xx_spi_driver_data *sdd, @@ -696,12 +700,10 @@ static void handle_msg(struct s3c64xx_spi_driver_data *sdd, if (use_dma) { if (xfer->tx_buf != NULL && (sdd->state & TXBUSY)) - s3c2410_dma_ctrl(sdd->tx_dmach, - S3C2410_DMAOP_FLUSH); + sdd->ops->stop(sdd->tx_ch); if (xfer->rx_buf != NULL && (sdd->state & RXBUSY)) - s3c2410_dma_ctrl(sdd->rx_dmach, - S3C2410_DMAOP_FLUSH); + sdd->ops->stop(sdd->rx_ch); } goto out; @@ -741,24 +743,19 @@ static void handle_msg(struct s3c64xx_spi_driver_data *sdd, static int acquire_dma(struct s3c64xx_spi_driver_data *sdd) { - if (s3c2410_dma_request(sdd->rx_dmach, - &s3c64xx_spi_dma_client, NULL) < 0) { - dev_err(&sdd->pdev->dev, "cannot get RxDMA\n"); - return 0; - } - s3c2410_dma_set_buffdone_fn(sdd->rx_dmach, s3c64xx_spi_dma_rxcb); - s3c2410_dma_devconfig(sdd->rx_dmach, S3C2410_DMASRC_HW, - sdd->sfr_start + S3C64XX_SPI_RX_DATA); - - if (s3c2410_dma_request(sdd->tx_dmach, - &s3c64xx_spi_dma_client, NULL) < 0) { - dev_err(&sdd->pdev->dev, "cannot get TxDMA\n"); - s3c2410_dma_free(sdd->rx_dmach, &s3c64xx_spi_dma_client); - return 0; - } - s3c2410_dma_set_buffdone_fn(sdd->tx_dmach, s3c64xx_spi_dma_txcb); - s3c2410_dma_devconfig(sdd->tx_dmach, S3C2410_DMASRC_MEM, - sdd->sfr_start + S3C64XX_SPI_TX_DATA); + + struct samsung_dma_info info; + sdd->ops = samsung_dma_get_ops(); + + info.cap = DMA_SLAVE; + info.client = &s3c64xx_spi_dma_client; + info.direction = DMA_FROM_DEVICE; + info.fifo = sdd->sfr_start + S3C64XX_SPI_RX_DATA; + info.width = sdd->cur_bpw / 8; + sdd->rx_ch = sdd->ops->request(sdd->rx_dmach, &info); + info.direction = DMA_TO_DEVICE; + info.fifo = sdd->sfr_start + S3C64XX_SPI_TX_DATA; + sdd->tx_ch = sdd->ops->request(sdd->tx_dmach, &info); return 1; } @@ -799,8 +796,8 @@ static void s3c64xx_spi_work(struct work_struct *work) spin_unlock_irqrestore(&sdd->lock, flags); /* Free DMA channels */ - s3c2410_dma_free(sdd->tx_dmach, &s3c64xx_spi_dma_client); - s3c2410_dma_free(sdd->rx_dmach, &s3c64xx_spi_dma_client); + sdd->ops->release(sdd->rx_ch, &s3c64xx_spi_dma_client); + sdd->ops->release(sdd->tx_ch, &s3c64xx_spi_dma_client); } static int s3c64xx_spi_transfer(struct spi_device *spi, From 82ab8cd7ec32194757ac73a66633be73ba88ea69 Mon Sep 17 00:00:00 2001 From: Boojin Kim Date: Fri, 2 Sep 2011 09:44:42 +0900 Subject: [PATCH 0157/1531] spi/s3c64xx: Merge dma control code This patch modifies to merge the dma control code. Original s3c64xx spi driver has each dma control code for rx and tx channel. This patch merges these dma control codes into one. With this patch, a dma setup function and callback function handle for both rx and tx channel. Signed-off-by: Boojin Kim Acked-by: Linus Walleij Acked-by: Vinod Koul Cc: Grant Likely Signed-off-by: Kukjin Kim Signed-off-by: Vinod Koul --- drivers/spi/spi-s3c64xx.c | 140 +++++++++++++++++++++----------------- 1 file changed, 76 insertions(+), 64 deletions(-) diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 24f49032ec35..019a7163572f 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -131,6 +131,12 @@ #define RXBUSY (1<<2) #define TXBUSY (1<<3) +struct s3c64xx_spi_dma_data { + unsigned ch; + enum dma_data_direction direction; + enum dma_ch dmach; +}; + /** * struct s3c64xx_spi_driver_data - Runtime info holder for SPI driver. * @clk: Pointer to the spi clock. @@ -164,15 +170,13 @@ struct s3c64xx_spi_driver_data { struct work_struct work; struct list_head queue; spinlock_t lock; - enum dma_ch rx_dmach; - enum dma_ch tx_dmach; unsigned long sfr_start; struct completion xfer_completion; unsigned state; unsigned cur_mode, cur_bpw; unsigned cur_speed; - unsigned rx_ch; - unsigned tx_ch; + struct s3c64xx_spi_dma_data rx_dma; + struct s3c64xx_spi_dma_data tx_dma; struct samsung_dma_ops *ops; }; @@ -229,36 +233,76 @@ static void flush_fifo(struct s3c64xx_spi_driver_data *sdd) writel(val, regs + S3C64XX_SPI_CH_CFG); } -static void s3c64xx_spi_dma_rxcb(void *data) +static void s3c64xx_spi_dmacb(void *data) { - struct s3c64xx_spi_driver_data *sdd - = (struct s3c64xx_spi_driver_data *)data; + struct s3c64xx_spi_driver_data *sdd; + struct s3c64xx_spi_dma_data *dma = data; unsigned long flags; + if (dma->direction == DMA_FROM_DEVICE) + sdd = container_of(data, + struct s3c64xx_spi_driver_data, rx_dma); + else + sdd = container_of(data, + struct s3c64xx_spi_driver_data, tx_dma); + spin_lock_irqsave(&sdd->lock, flags); - sdd->state &= ~RXBUSY; - /* If the other done */ - if (!(sdd->state & TXBUSY)) - complete(&sdd->xfer_completion); + if (dma->direction == DMA_FROM_DEVICE) { + sdd->state &= ~RXBUSY; + if (!(sdd->state & TXBUSY)) + complete(&sdd->xfer_completion); + } else { + sdd->state &= ~TXBUSY; + if (!(sdd->state & RXBUSY)) + complete(&sdd->xfer_completion); + } spin_unlock_irqrestore(&sdd->lock, flags); } -static void s3c64xx_spi_dma_txcb(void *data) +static void prepare_dma(struct s3c64xx_spi_dma_data *dma, + unsigned len, dma_addr_t buf) { - struct s3c64xx_spi_driver_data *sdd - = (struct s3c64xx_spi_driver_data *)data; - unsigned long flags; + struct s3c64xx_spi_driver_data *sdd; + struct samsung_dma_prep_info info; - spin_lock_irqsave(&sdd->lock, flags); + if (dma->direction == DMA_FROM_DEVICE) + sdd = container_of((void *)dma, + struct s3c64xx_spi_driver_data, rx_dma); + else + sdd = container_of((void *)dma, + struct s3c64xx_spi_driver_data, tx_dma); - sdd->state &= ~TXBUSY; - /* If the other done */ - if (!(sdd->state & RXBUSY)) - complete(&sdd->xfer_completion); + info.cap = DMA_SLAVE; + info.len = len; + info.fp = s3c64xx_spi_dmacb; + info.fp_param = dma; + info.direction = dma->direction; + info.buf = buf; + + sdd->ops->prepare(dma->ch, &info); + sdd->ops->trigger(dma->ch); +} - spin_unlock_irqrestore(&sdd->lock, flags); +static int acquire_dma(struct s3c64xx_spi_driver_data *sdd) +{ + struct samsung_dma_info info; + + sdd->ops = samsung_dma_get_ops(); + + info.cap = DMA_SLAVE; + info.client = &s3c64xx_spi_dma_client; + info.width = sdd->cur_bpw / 8; + + info.direction = sdd->rx_dma.direction; + info.fifo = sdd->sfr_start + S3C64XX_SPI_RX_DATA; + sdd->rx_dma.ch = sdd->ops->request(sdd->rx_dma.dmach, &info); + info.direction = sdd->tx_dma.direction; + info.fifo = sdd->sfr_start + S3C64XX_SPI_TX_DATA; + sdd->tx_dma.ch = sdd->ops->request(sdd->tx_dma.dmach, &info); + + return 1; } static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, @@ -268,7 +312,6 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, struct s3c64xx_spi_info *sci = sdd->cntrlr_info; void __iomem *regs = sdd->regs; u32 modecfg, chcfg; - struct samsung_dma_prep_info info; modecfg = readl(regs + S3C64XX_SPI_MODE_CFG); modecfg &= ~(S3C64XX_SPI_MODE_TXDMA_ON | S3C64XX_SPI_MODE_RXDMA_ON); @@ -294,14 +337,7 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, chcfg |= S3C64XX_SPI_CH_TXCH_ON; if (dma_mode) { modecfg |= S3C64XX_SPI_MODE_TXDMA_ON; - info.cap = DMA_SLAVE; - info.direction = DMA_TO_DEVICE; - info.buf = xfer->tx_dma; - info.len = xfer->len; - info.fp = s3c64xx_spi_dma_txcb; - info.fp_param = sdd; - sdd->ops->prepare(sdd->tx_ch, &info); - sdd->ops->trigger(sdd->tx_ch); + prepare_dma(&sdd->tx_dma, xfer->len, xfer->tx_dma); } else { switch (sdd->cur_bpw) { case 32: @@ -333,14 +369,7 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff) | S3C64XX_SPI_PACKET_CNT_EN, regs + S3C64XX_SPI_PACKET_CNT); - info.cap = DMA_SLAVE; - info.direction = DMA_FROM_DEVICE; - info.buf = xfer->rx_dma; - info.len = xfer->len; - info.fp = s3c64xx_spi_dma_rxcb; - info.fp_param = sdd; - sdd->ops->prepare(sdd->rx_ch, &info); - sdd->ops->trigger(sdd->rx_ch); + prepare_dma(&sdd->rx_dma, xfer->len, xfer->rx_dma); } } @@ -700,10 +729,10 @@ static void handle_msg(struct s3c64xx_spi_driver_data *sdd, if (use_dma) { if (xfer->tx_buf != NULL && (sdd->state & TXBUSY)) - sdd->ops->stop(sdd->tx_ch); + sdd->ops->stop(sdd->tx_dma.ch); if (xfer->rx_buf != NULL && (sdd->state & RXBUSY)) - sdd->ops->stop(sdd->rx_ch); + sdd->ops->stop(sdd->rx_dma.ch); } goto out; @@ -741,25 +770,6 @@ static void handle_msg(struct s3c64xx_spi_driver_data *sdd, msg->complete(msg->context); } -static int acquire_dma(struct s3c64xx_spi_driver_data *sdd) -{ - - struct samsung_dma_info info; - sdd->ops = samsung_dma_get_ops(); - - info.cap = DMA_SLAVE; - info.client = &s3c64xx_spi_dma_client; - info.direction = DMA_FROM_DEVICE; - info.fifo = sdd->sfr_start + S3C64XX_SPI_RX_DATA; - info.width = sdd->cur_bpw / 8; - sdd->rx_ch = sdd->ops->request(sdd->rx_dmach, &info); - info.direction = DMA_TO_DEVICE; - info.fifo = sdd->sfr_start + S3C64XX_SPI_TX_DATA; - sdd->tx_ch = sdd->ops->request(sdd->tx_dmach, &info); - - return 1; -} - static void s3c64xx_spi_work(struct work_struct *work) { struct s3c64xx_spi_driver_data *sdd = container_of(work, @@ -796,8 +806,8 @@ static void s3c64xx_spi_work(struct work_struct *work) spin_unlock_irqrestore(&sdd->lock, flags); /* Free DMA channels */ - sdd->ops->release(sdd->rx_ch, &s3c64xx_spi_dma_client); - sdd->ops->release(sdd->tx_ch, &s3c64xx_spi_dma_client); + sdd->ops->release(sdd->rx_dma.ch, &s3c64xx_spi_dma_client); + sdd->ops->release(sdd->tx_dma.ch, &s3c64xx_spi_dma_client); } static int s3c64xx_spi_transfer(struct spi_device *spi, @@ -1014,8 +1024,10 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) sdd->cntrlr_info = sci; sdd->pdev = pdev; sdd->sfr_start = mem_res->start; - sdd->tx_dmach = dmatx_res->start; - sdd->rx_dmach = dmarx_res->start; + sdd->tx_dma.dmach = dmatx_res->start; + sdd->tx_dma.direction = DMA_TO_DEVICE; + sdd->rx_dma.dmach = dmarx_res->start; + sdd->rx_dma.direction = DMA_FROM_DEVICE; sdd->cur_bpw = 8; @@ -1103,7 +1115,7 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) pdev->id, master->num_chipselect); dev_dbg(&pdev->dev, "\tIOmem=[0x%x-0x%x]\tDMA=[Rx-%d, Tx-%d]\n", mem_res->end, mem_res->start, - sdd->rx_dmach, sdd->tx_dmach); + sdd->rx_dma.dmach, sdd->tx_dma.dmach); return 0; From 344b4c48887a443f7478fc7047d1397b20821ed3 Mon Sep 17 00:00:00 2001 From: Boojin Kim Date: Fri, 2 Sep 2011 09:44:43 +0900 Subject: [PATCH 0158/1531] ASoC: Samsung: Update DMA interface This patch adds to support the DMA PL330 driver that uses DMA generic API. Samsung sound driver uses DMA generic API if architecture supports it. Otherwise, use samsung specific S3C-PL330 API driver to transfer PCM data. Signed-off-by: Boojin Kim Acked-by: Linus Walleij Acked-by: Vinod Koul Cc: Jassi Brar Cc: Liam Girdwood Acked-by: Mark Brown [kgene.kim@samsung.com: removed useless variable] Signed-off-by: Kukjin Kim Signed-off-by: Vinod Koul --- arch/arm/mach-s3c2410/include/mach/dma.h | 10 +- arch/arm/mach-s3c64xx/include/mach/dma.h | 2 +- .../arm/plat-samsung/include/plat/dma-pl330.h | 2 +- sound/soc/samsung/ac97.c | 10 +- sound/soc/samsung/dma.c | 146 +++++++----------- sound/soc/samsung/dma.h | 4 +- 6 files changed, 78 insertions(+), 96 deletions(-) diff --git a/arch/arm/mach-s3c2410/include/mach/dma.h b/arch/arm/mach-s3c2410/include/mach/dma.h index e61a91f88c52..4e485bab66de 100644 --- a/arch/arm/mach-s3c2410/include/mach/dma.h +++ b/arch/arm/mach-s3c2410/include/mach/dma.h @@ -50,6 +50,11 @@ enum dma_ch { DMACH_MAX, /* the end entry */ }; +static inline bool samsung_dma_has_circular(void) +{ + return false; +} + static inline bool samsung_dma_is_dmadev(void) { return false; @@ -202,9 +207,4 @@ struct s3c2410_dma_chan { typedef unsigned long dma_device_t; -static inline bool s3c_dma_has_circular(void) -{ - return false; -} - #endif /* __ASM_ARCH_DMA_H */ diff --git a/arch/arm/mach-s3c64xx/include/mach/dma.h b/arch/arm/mach-s3c64xx/include/mach/dma.h index 49c3a53135d4..74fdf25c9042 100644 --- a/arch/arm/mach-s3c64xx/include/mach/dma.h +++ b/arch/arm/mach-s3c64xx/include/mach/dma.h @@ -58,7 +58,7 @@ enum dma_ch { DMACH_MAX /* the end */ }; -static __inline__ bool s3c_dma_has_circular(void) +static inline bool samsung_dma_has_circular(void) { return true; } diff --git a/arch/arm/plat-samsung/include/plat/dma-pl330.h b/arch/arm/plat-samsung/include/plat/dma-pl330.h index 9a1dadb0218e..2e55e5958674 100644 --- a/arch/arm/plat-samsung/include/plat/dma-pl330.h +++ b/arch/arm/plat-samsung/include/plat/dma-pl330.h @@ -89,7 +89,7 @@ struct s3c2410_dma_client { char *name; }; -static inline bool s3c_dma_has_circular(void) +static inline bool samsung_dma_has_circular(void) { return true; } diff --git a/sound/soc/samsung/ac97.c b/sound/soc/samsung/ac97.c index f97110e72e85..b4f9b0003685 100644 --- a/sound/soc/samsung/ac97.c +++ b/sound/soc/samsung/ac97.c @@ -271,7 +271,10 @@ static int s3c_ac97_trigger(struct snd_pcm_substream *substream, int cmd, writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL); - s3c2410_dma_ctrl(dma_data->channel, S3C2410_DMAOP_STARTED); + if (!dma_data->ops) + dma_data->ops = samsung_dma_get_ops(); + + dma_data->ops->started(dma_data->channel); return 0; } @@ -317,7 +320,10 @@ static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream, writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL); - s3c2410_dma_ctrl(dma_data->channel, S3C2410_DMAOP_STARTED); + if (!dma_data->ops) + dma_data->ops = samsung_dma_get_ops(); + + dma_data->ops->started(dma_data->channel); return 0; } diff --git a/sound/soc/samsung/dma.c b/sound/soc/samsung/dma.c index 9465588b02f2..851346f7d68d 100644 --- a/sound/soc/samsung/dma.c +++ b/sound/soc/samsung/dma.c @@ -54,7 +54,6 @@ struct runtime_data { spinlock_t lock; int state; unsigned int dma_loaded; - unsigned int dma_limit; unsigned int dma_period; dma_addr_t dma_start; dma_addr_t dma_pos; @@ -62,77 +61,79 @@ struct runtime_data { struct s3c_dma_params *params; }; +static void audio_buffdone(void *data); + /* dma_enqueue * * place a dma buffer onto the queue for the dma system * to handle. -*/ + */ static void dma_enqueue(struct snd_pcm_substream *substream) { struct runtime_data *prtd = substream->runtime->private_data; dma_addr_t pos = prtd->dma_pos; unsigned int limit; - int ret; + struct samsung_dma_prep_info dma_info; pr_debug("Entered %s\n", __func__); - if (s3c_dma_has_circular()) - limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period; - else - limit = prtd->dma_limit; + limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period; pr_debug("%s: loaded %d, limit %d\n", __func__, prtd->dma_loaded, limit); - while (prtd->dma_loaded < limit) { - unsigned long len = prtd->dma_period; + 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_info.fp = audio_buffdone; + dma_info.fp_param = substream; + dma_info.period = prtd->dma_period; + dma_info.len = prtd->dma_period*limit; + while (prtd->dma_loaded < limit) { pr_debug("dma_loaded: %d\n", prtd->dma_loaded); - if ((pos + len) > prtd->dma_end) { - len = prtd->dma_end - pos; - pr_debug("%s: corrected dma len %ld\n", __func__, len); + if ((pos + dma_info.period) > prtd->dma_end) { + dma_info.period = prtd->dma_end - pos; + pr_debug("%s: corrected dma len %ld\n", + __func__, dma_info.period); } - ret = s3c2410_dma_enqueue(prtd->params->channel, - substream, pos, len); + dma_info.buf = pos; + prtd->params->ops->prepare(prtd->params->ch, &dma_info); - if (ret == 0) { - prtd->dma_loaded++; - pos += prtd->dma_period; - if (pos >= prtd->dma_end) - pos = prtd->dma_start; - } else - break; + prtd->dma_loaded++; + pos += prtd->dma_period; + if (pos >= prtd->dma_end) + pos = prtd->dma_start; } prtd->dma_pos = pos; } -static void audio_buffdone(struct s3c2410_dma_chan *channel, - void *dev_id, int size, - enum s3c2410_dma_buffresult result) +static void audio_buffdone(void *data) { - struct snd_pcm_substream *substream = dev_id; - struct runtime_data *prtd; + struct snd_pcm_substream *substream = data; + struct runtime_data *prtd = substream->runtime->private_data; pr_debug("Entered %s\n", __func__); - if (result == S3C2410_RES_ABORT || result == S3C2410_RES_ERR) - return; - - prtd = substream->runtime->private_data; + if (prtd->state & ST_RUNNING) { + prtd->dma_pos += prtd->dma_period; + if (prtd->dma_pos >= prtd->dma_end) + prtd->dma_pos = prtd->dma_start; - if (substream) - snd_pcm_period_elapsed(substream); + if (substream) + snd_pcm_period_elapsed(substream); - spin_lock(&prtd->lock); - if (prtd->state & ST_RUNNING && !s3c_dma_has_circular()) { - prtd->dma_loaded--; - dma_enqueue(substream); + spin_lock(&prtd->lock); + if (!samsung_dma_has_circular()) { + prtd->dma_loaded--; + dma_enqueue(substream); + } + spin_unlock(&prtd->lock); } - - spin_unlock(&prtd->lock); } static int dma_hw_params(struct snd_pcm_substream *substream, @@ -144,8 +145,7 @@ static int dma_hw_params(struct snd_pcm_substream *substream, unsigned long totbytes = params_buffer_bytes(params); struct s3c_dma_params *dma = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - int ret = 0; - + struct samsung_dma_info dma_info; pr_debug("Entered %s\n", __func__); @@ -163,30 +163,26 @@ static int dma_hw_params(struct snd_pcm_substream *substream, pr_debug("params %p, client %p, channel %d\n", prtd->params, prtd->params->client, prtd->params->channel); - ret = s3c2410_dma_request(prtd->params->channel, - prtd->params->client, NULL); - - if (ret < 0) { - printk(KERN_ERR "failed to get dma channel\n"); - return ret; - } - - /* use the circular buffering if we have it available. */ - if (s3c_dma_has_circular()) - s3c2410_dma_setflags(prtd->params->channel, - S3C2410_DMAF_CIRCULAR); + prtd->params->ops = samsung_dma_get_ops(); + + dma_info.cap = (samsung_dma_has_circular() ? + DMA_CYCLIC : DMA_SLAVE); + dma_info.client = prtd->params->client; + dma_info.direction = + (substream->stream == SNDRV_PCM_STREAM_PLAYBACK + ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + dma_info.width = prtd->params->dma_size; + dma_info.fifo = prtd->params->dma_addr; + prtd->params->ch = prtd->params->ops->request( + prtd->params->channel, &dma_info); } - s3c2410_dma_set_buffdone_fn(prtd->params->channel, - audio_buffdone); - snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); runtime->dma_bytes = totbytes; spin_lock_irq(&prtd->lock); prtd->dma_loaded = 0; - prtd->dma_limit = runtime->hw.periods_min; prtd->dma_period = params_period_bytes(params); prtd->dma_start = runtime->dma_addr; prtd->dma_pos = prtd->dma_start; @@ -206,7 +202,8 @@ static int dma_hw_free(struct snd_pcm_substream *substream) snd_pcm_set_runtime_buffer(substream, NULL); if (prtd->params) { - s3c2410_dma_free(prtd->params->channel, prtd->params->client); + prtd->params->ops->release(prtd->params->ch, + prtd->params->client); prtd->params = NULL; } @@ -225,23 +222,9 @@ static int dma_prepare(struct snd_pcm_substream *substream) if (!prtd->params) return 0; - /* channel needs configuring for mem=>device, increment memory addr, - * sync to pclk, half-word transfers to the IIS-FIFO. */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - s3c2410_dma_devconfig(prtd->params->channel, - S3C2410_DMASRC_MEM, - prtd->params->dma_addr); - } else { - s3c2410_dma_devconfig(prtd->params->channel, - S3C2410_DMASRC_HW, - prtd->params->dma_addr); - } - - s3c2410_dma_config(prtd->params->channel, - prtd->params->dma_size); - /* flush the DMA channel */ - s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_FLUSH); + prtd->params->ops->flush(prtd->params->ch); + prtd->dma_loaded = 0; prtd->dma_pos = prtd->dma_start; @@ -265,14 +248,14 @@ static int dma_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: prtd->state |= ST_RUNNING; - s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_START); + prtd->params->ops->trigger(prtd->params->ch); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: prtd->state &= ~ST_RUNNING; - s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_STOP); + prtd->params->ops->stop(prtd->params->ch); break; default: @@ -291,21 +274,12 @@ dma_pointer(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct runtime_data *prtd = runtime->private_data; unsigned long res; - dma_addr_t src, dst; pr_debug("Entered %s\n", __func__); - spin_lock(&prtd->lock); - s3c2410_dma_getposition(prtd->params->channel, &src, &dst); - - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) - res = dst - prtd->dma_start; - else - res = src - prtd->dma_start; - - spin_unlock(&prtd->lock); + res = prtd->dma_pos - prtd->dma_start; - pr_debug("Pointer %x %x\n", src, dst); + pr_debug("Pointer offset: %lu\n", res); /* we seem to be getting the odd error from the pcm library due * to out-of-bounds pointers. this is maybe due to the dma engine diff --git a/sound/soc/samsung/dma.h b/sound/soc/samsung/dma.h index c50659269a40..7d1ead77ef21 100644 --- a/sound/soc/samsung/dma.h +++ b/sound/soc/samsung/dma.h @@ -6,7 +6,7 @@ * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * - * ALSA PCM interface for the Samsung S3C24xx CPU + * ALSA PCM interface for the Samsung SoC */ #ifndef _S3C_AUDIO_H @@ -17,6 +17,8 @@ struct s3c_dma_params { int channel; /* Channel ID */ dma_addr_t dma_addr; int dma_size; /* Size of the DMA transfer */ + unsigned ch; + struct samsung_dma_ops *ops; }; #endif From 51ddf31da16b1ab9da861eafedad6d263faf4388 Mon Sep 17 00:00:00 2001 From: Boojin Kim Date: Fri, 2 Sep 2011 09:44:44 +0900 Subject: [PATCH 0159/1531] ARM: SAMSUNG: Remove Samsung specific enum type for dma direction This patch removes the samsung specific enum type 's3c2410_dmasrc' and uses 'dma_data_direction' instead. Signed-off-by: Boojin Kim Acked-by: Linus Walleij Acked-by: Vinod Koul Signed-off-by: Kukjin Kim Signed-off-by: Vinod Koul --- arch/arm/mach-s3c2410/include/mach/dma.h | 2 +- arch/arm/mach-s3c2412/dma.c | 4 ++-- arch/arm/mach-s3c64xx/dma.c | 10 +++++----- arch/arm/mach-s3c64xx/include/mach/dma.h | 2 +- arch/arm/plat-s3c24xx/dma.c | 10 +++++----- arch/arm/plat-samsung/include/plat/dma-s3c24xx.h | 2 +- arch/arm/plat-samsung/include/plat/dma.h | 9 +++------ arch/arm/plat-samsung/s3c-dma-ops.c | 5 +---- drivers/mmc/host/s3cmci.c | 6 +++--- 9 files changed, 22 insertions(+), 28 deletions(-) diff --git a/arch/arm/mach-s3c2410/include/mach/dma.h b/arch/arm/mach-s3c2410/include/mach/dma.h index 4e485bab66de..ae8e482b6427 100644 --- a/arch/arm/mach-s3c2410/include/mach/dma.h +++ b/arch/arm/mach-s3c2410/include/mach/dma.h @@ -174,7 +174,7 @@ struct s3c2410_dma_chan { struct s3c2410_dma_client *client; /* channel configuration */ - enum s3c2410_dmasrc source; + enum dma_data_direction source; enum dma_ch req_ch; unsigned long dev_addr; unsigned long load_timeout; diff --git a/arch/arm/mach-s3c2412/dma.c b/arch/arm/mach-s3c2412/dma.c index 7abecfca0b7e..b4fc3ba367d6 100644 --- a/arch/arm/mach-s3c2412/dma.c +++ b/arch/arm/mach-s3c2412/dma.c @@ -148,11 +148,11 @@ static struct s3c24xx_dma_map __initdata s3c2412_dma_mappings[] = { static void s3c2412_dma_direction(struct s3c2410_dma_chan *chan, struct s3c24xx_dma_map *map, - enum s3c2410_dmasrc dir) + enum dma_data_direction dir) { unsigned long chsel; - if (dir == S3C2410_DMASRC_HW) + if (dir == DMA_FROM_DEVICE) chsel = map->channels_rx[0]; else chsel = map->channels[0]; diff --git a/arch/arm/mach-s3c64xx/dma.c b/arch/arm/mach-s3c64xx/dma.c index 204bfafe4bfc..67c97fab62fd 100644 --- a/arch/arm/mach-s3c64xx/dma.c +++ b/arch/arm/mach-s3c64xx/dma.c @@ -147,14 +147,14 @@ static void s3c64xx_dma_fill_lli(struct s3c2410_dma_chan *chan, u32 control0, control1; switch (chan->source) { - case S3C2410_DMASRC_HW: + case DMA_FROM_DEVICE: src = chan->dev_addr; dst = data; control0 = PL080_CONTROL_SRC_AHB2; control0 |= PL080_CONTROL_DST_INCR; break; - case S3C2410_DMASRC_MEM: + case DMA_TO_DEVICE: src = data; dst = chan->dev_addr; control0 = PL080_CONTROL_DST_AHB2; @@ -416,7 +416,7 @@ EXPORT_SYMBOL(s3c2410_dma_enqueue); int s3c2410_dma_devconfig(enum dma_ch channel, - enum s3c2410_dmasrc source, + enum dma_data_direction source, unsigned long devaddr) { struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel); @@ -437,11 +437,11 @@ int s3c2410_dma_devconfig(enum dma_ch channel, pr_debug("%s: peripheral %d\n", __func__, peripheral); switch (source) { - case S3C2410_DMASRC_HW: + case DMA_FROM_DEVICE: config = 2 << PL080_CONFIG_FLOW_CONTROL_SHIFT; config |= peripheral << PL080_CONFIG_SRC_SEL_SHIFT; break; - case S3C2410_DMASRC_MEM: + case DMA_TO_DEVICE: config = 1 << PL080_CONFIG_FLOW_CONTROL_SHIFT; config |= peripheral << PL080_CONFIG_DST_SEL_SHIFT; break; diff --git a/arch/arm/mach-s3c64xx/include/mach/dma.h b/arch/arm/mach-s3c64xx/include/mach/dma.h index 74fdf25c9042..fe1a98cf0e4c 100644 --- a/arch/arm/mach-s3c64xx/include/mach/dma.h +++ b/arch/arm/mach-s3c64xx/include/mach/dma.h @@ -99,7 +99,7 @@ struct s3c2410_dma_chan { unsigned char peripheral; unsigned int flags; - enum s3c2410_dmasrc source; + enum dma_data_direction source; dma_addr_t dev_addr; diff --git a/arch/arm/plat-s3c24xx/dma.c b/arch/arm/plat-s3c24xx/dma.c index 539bd0e3defd..53754bcf15a7 100644 --- a/arch/arm/plat-s3c24xx/dma.c +++ b/arch/arm/plat-s3c24xx/dma.c @@ -1094,14 +1094,14 @@ EXPORT_SYMBOL(s3c2410_dma_config); * * configure the dma source/destination hardware type and address * - * source: S3C2410_DMASRC_HW: source is hardware - * S3C2410_DMASRC_MEM: source is memory + * source: DMA_FROM_DEVICE: source is hardware + * DMA_TO_DEVICE: source is memory * * devaddr: physical address of the source */ int s3c2410_dma_devconfig(enum dma_ch channel, - enum s3c2410_dmasrc source, + enum dma_data_direction source, unsigned long devaddr) { struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel); @@ -1131,7 +1131,7 @@ int s3c2410_dma_devconfig(enum dma_ch channel, hwcfg |= S3C2410_DISRCC_INC; switch (source) { - case S3C2410_DMASRC_HW: + case DMA_FROM_DEVICE: /* source is hardware */ pr_debug("%s: hw source, devaddr=%08lx, hwcfg=%d\n", __func__, devaddr, hwcfg); @@ -1142,7 +1142,7 @@ int s3c2410_dma_devconfig(enum dma_ch channel, chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DIDST); break; - case S3C2410_DMASRC_MEM: + case DMA_TO_DEVICE: /* source is memory */ pr_debug("%s: mem source, devaddr=%08lx, hwcfg=%d\n", __func__, devaddr, hwcfg); diff --git a/arch/arm/plat-samsung/include/plat/dma-s3c24xx.h b/arch/arm/plat-samsung/include/plat/dma-s3c24xx.h index 336d5ac02035..19828296a562 100644 --- a/arch/arm/plat-samsung/include/plat/dma-s3c24xx.h +++ b/arch/arm/plat-samsung/include/plat/dma-s3c24xx.h @@ -47,7 +47,7 @@ struct s3c24xx_dma_selection { void (*direction)(struct s3c2410_dma_chan *chan, struct s3c24xx_dma_map *map, - enum s3c2410_dmasrc dir); + enum dma_data_direction dir); }; extern int s3c24xx_dma_init_map(struct s3c24xx_dma_selection *sel); diff --git a/arch/arm/plat-samsung/include/plat/dma.h b/arch/arm/plat-samsung/include/plat/dma.h index 361076060744..b9061128abde 100644 --- a/arch/arm/plat-samsung/include/plat/dma.h +++ b/arch/arm/plat-samsung/include/plat/dma.h @@ -10,17 +10,14 @@ * published by the Free Software Foundation. */ +#include + enum s3c2410_dma_buffresult { S3C2410_RES_OK, S3C2410_RES_ERR, S3C2410_RES_ABORT }; -enum s3c2410_dmasrc { - S3C2410_DMASRC_HW, /* source is memory */ - S3C2410_DMASRC_MEM /* source is hardware */ -}; - /* enum s3c2410_chan_op * * operation codes passed to the DMA code by the user, and also used @@ -112,7 +109,7 @@ extern int s3c2410_dma_config(enum dma_ch channel, int xferunit); */ extern int s3c2410_dma_devconfig(enum dma_ch channel, - enum s3c2410_dmasrc source, unsigned long devaddr); + enum dma_data_direction source, unsigned long devaddr); /* s3c2410_dma_getposition * diff --git a/arch/arm/plat-samsung/s3c-dma-ops.c b/arch/arm/plat-samsung/s3c-dma-ops.c index 33ab3241268d..582333c70585 100644 --- a/arch/arm/plat-samsung/s3c-dma-ops.c +++ b/arch/arm/plat-samsung/s3c-dma-ops.c @@ -48,10 +48,7 @@ static unsigned s3c_dma_request(enum dma_ch dma_ch, data->ch = dma_ch; list_add_tail(&data->node, &dma_list); - if (info->direction == DMA_FROM_DEVICE) - s3c2410_dma_devconfig(dma_ch, S3C2410_DMASRC_HW, info->fifo); - else - s3c2410_dma_devconfig(dma_ch, S3C2410_DMASRC_MEM, info->fifo); + s3c2410_dma_devconfig(dma_ch, info->direction, info->fifo); if (info->cap == DMA_CYCLIC) s3c2410_dma_setflags(dma_ch, S3C2410_DMAF_CIRCULAR); diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index a04f87d7ee3d..03cfdab99c8f 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -913,9 +913,9 @@ static void finalize_request(struct s3cmci_host *host) } static void s3cmci_dma_setup(struct s3cmci_host *host, - enum s3c2410_dmasrc source) + enum dma_data_direction source) { - static enum s3c2410_dmasrc last_source = -1; + static enum dma_data_direction last_source = -1; static int setup_ok; if (last_source == source) @@ -1087,7 +1087,7 @@ static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data) BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR); - s3cmci_dma_setup(host, rw ? S3C2410_DMASRC_MEM : S3C2410_DMASRC_HW); + s3cmci_dma_setup(host, rw ? DMA_TO_DEVICE : DMA_FROM_DEVICE); s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH); dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, From 27a84d54c02591e815d291ae0ee4bfb9cfd21065 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 15 Sep 2011 14:01:40 +0200 Subject: [PATCH 0160/1531] block: refactor generic_make_request Move all the checks performed on a bio into a new helper, and call it as soon as bio is submitted even if it is a re-submission from ->make_request. We explicitly mark the new helper as beeing non-inlined as the stack usage for printing the block device name in the failure case is quite high and this a patch where we have to be extremely conservative about stack usage. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- block/blk-core.c | 95 +++++++++++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 46 deletions(-) diff --git a/block/blk-core.c b/block/blk-core.c index f58e019be67b..684d7eb33d43 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -1412,31 +1412,8 @@ static inline int bio_check_eod(struct bio *bio, unsigned int nr_sectors) return 0; } -/** - * generic_make_request - hand a buffer to its device driver for I/O - * @bio: The bio describing the location in memory and on the device. - * - * generic_make_request() is used to make I/O requests of block - * devices. It is passed a &struct bio, which describes the I/O that needs - * to be done. - * - * generic_make_request() does not return any status. The - * success/failure status of the request, along with notification of - * completion, is delivered asynchronously through the bio->bi_end_io - * function described (one day) else where. - * - * The caller of generic_make_request must make sure that bi_io_vec - * are set to describe the memory buffer, and that bi_dev and bi_sector are - * set to describe the device address, and the - * bi_end_io and optionally bi_private are set to describe how - * completion notification should be signaled. - * - * generic_make_request and the drivers it calls may use bi_next if this - * bio happens to be merged with someone else, and may change bi_dev and - * bi_sector for remaps as it sees fit. So the values of these fields - * should NOT be depended on after the call to generic_make_request. - */ -static inline void __generic_make_request(struct bio *bio) +static noinline_for_stack bool +generic_make_request_checks(struct bio *bio) { struct request_queue *q; int nr_sectors = bio_sectors(bio); @@ -1515,35 +1492,62 @@ static inline void __generic_make_request(struct bio *bio) /* if bio = NULL, bio has been throttled and will be submitted later. */ if (!bio) - return; + return false; + trace_block_bio_queue(q, bio); - q->make_request_fn(q, bio); - return; + return true; end_io: bio_endio(bio, err); + return false; } -/* - * We only want one ->make_request_fn to be active at a time, - * else stack usage with stacked devices could be a problem. - * So use current->bio_list to keep a list of requests - * submited by a make_request_fn function. - * current->bio_list is also used as a flag to say if - * generic_make_request is currently active in this task or not. - * If it is NULL, then no make_request is active. If it is non-NULL, - * then a make_request is active, and new requests should be added - * at the tail +/** + * generic_make_request - hand a buffer to its device driver for I/O + * @bio: The bio describing the location in memory and on the device. + * + * generic_make_request() is used to make I/O requests of block + * devices. It is passed a &struct bio, which describes the I/O that needs + * to be done. + * + * generic_make_request() does not return any status. The + * success/failure status of the request, along with notification of + * completion, is delivered asynchronously through the bio->bi_end_io + * function described (one day) else where. + * + * The caller of generic_make_request must make sure that bi_io_vec + * are set to describe the memory buffer, and that bi_dev and bi_sector are + * set to describe the device address, and the + * bi_end_io and optionally bi_private are set to describe how + * completion notification should be signaled. + * + * generic_make_request and the drivers it calls may use bi_next if this + * bio happens to be merged with someone else, and may resubmit the bio to + * a lower device by calling into generic_make_request recursively, which + * means the bio should NOT be touched after the call to ->make_request_fn. */ void generic_make_request(struct bio *bio) { struct bio_list bio_list_on_stack; + if (!generic_make_request_checks(bio)) + return; + + /* + * We only want one ->make_request_fn to be active at a time, else + * stack usage with stacked devices could be a problem. So use + * current->bio_list to keep a list of requests submited by a + * make_request_fn function. current->bio_list is also used as a + * flag to say if generic_make_request is currently active in this + * task or not. If it is NULL, then no make_request is active. If + * it is non-NULL, then a make_request is active, and new requests + * should be added at the tail + */ if (current->bio_list) { - /* make_request is active */ bio_list_add(current->bio_list, bio); return; } + /* following loop may be a bit non-obvious, and so deserves some * explanation. * Before entering the loop, bio->bi_next is NULL (as all callers @@ -1551,22 +1555,21 @@ void generic_make_request(struct bio *bio) * We pretend that we have just taken it off a longer list, so * we assign bio_list to a pointer to the bio_list_on_stack, * thus initialising the bio_list of new bios to be - * added. __generic_make_request may indeed add some more bios + * added. ->make_request() may indeed add some more bios * through a recursive call to generic_make_request. If it * did, we find a non-NULL value in bio_list and re-enter the loop * from the top. In this case we really did just take the bio * of the top of the list (no pretending) and so remove it from - * bio_list, and call into __generic_make_request again. - * - * The loop was structured like this to make only one call to - * __generic_make_request (which is important as it is large and - * inlined) and to keep the structure simple. + * bio_list, and call into ->make_request() again. */ BUG_ON(bio->bi_next); bio_list_init(&bio_list_on_stack); current->bio_list = &bio_list_on_stack; do { - __generic_make_request(bio); + struct request_queue *q = bdev_get_queue(bio->bi_bdev); + + q->make_request_fn(q, bio); + bio = bio_list_pop(current->bio_list); } while (bio); current->bio_list = NULL; /* deactivate */ From 986afc98ce24901865828dab9ba876fd9be6beb2 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 12 Aug 2011 18:08:17 +0900 Subject: [PATCH 0161/1531] ARM: S3C64XX: Hook up GPIO initiated DVS on Cragganmore Allow us to ramp VDDARM quickly by using a GPIO to signal a voltage change instead of doing a register write. Signed-off-by: Mark Brown Signed-off-by: Kukjin Kim --- arch/arm/mach-s3c64xx/mach-crag6410.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/arm/mach-s3c64xx/mach-crag6410.c b/arch/arm/mach-s3c64xx/mach-crag6410.c index af0c2fe1ea37..5affb26a1985 100644 --- a/arch/arm/mach-s3c64xx/mach-crag6410.c +++ b/arch/arm/mach-s3c64xx/mach-crag6410.c @@ -353,6 +353,12 @@ static struct pca953x_platform_data crag6410_pca_data = { .irq_base = 0, }; +/* VDDARM is controlled by DVS1 connected to GPK(0) */ +static struct wm831x_buckv_pdata vddarm_pdata = { + .dvs_control_src = 1, + .dvs_gpio = S3C64XX_GPK(0), +}; + static struct regulator_consumer_supply vddarm_consumers[] __initdata = { REGULATOR_SUPPLY("vddarm", NULL), }; @@ -368,6 +374,7 @@ static struct regulator_init_data vddarm __initdata = { .num_consumer_supplies = ARRAY_SIZE(vddarm_consumers), .consumer_supplies = vddarm_consumers, .supply_regulator = "WALLVDD", + .driver_data = &vddarm_pdata, }; static struct regulator_init_data vddint __initdata = { @@ -503,6 +510,8 @@ static struct wm831x_pdata crag_pmic_pdata __initdata = { .backup = &banff_backup_pdata, .gpio_defaults = { + /* GPIO5: DVS1_REQ - CMOS, DBVDD, active high */ + [4] = WM831X_GPN_DIR | WM831X_GPN_POL | WM831X_GPN_ENA | 0x8, /* GPIO11: Touchscreen data - CMOS, DBVDD, active high*/ [10] = WM831X_GPN_POL | WM831X_GPN_ENA | 0x6, /* GPIO12: Touchscreen pen down - CMOS, DBVDD, active high*/ From fad23ddac36920600e2457cf540c4d6383290cd2 Mon Sep 17 00:00:00 2001 From: Alim Akhtar Date: Mon, 19 Sep 2011 10:27:19 +0900 Subject: [PATCH 0162/1531] ARM: EXYNOS4: register the second instance of pl330 DMAC Platform data is provided for two instance of pl330 DMAC, but only one DMAC is register with amba_device. This patch register the second instance. Signed-off-by: Alim Akhtar Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos4/dma.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-exynos4/dma.c b/arch/arm/mach-exynos4/dma.c index d57d66255021..9667c61e64fb 100644 --- a/arch/arm/mach-exynos4/dma.c +++ b/arch/arm/mach-exynos4/dma.c @@ -243,6 +243,7 @@ struct amba_device exynos4_device_pdma1 = { static int __init exynos4_dma_init(void) { amba_device_register(&exynos4_device_pdma0, &iomem_resource); + amba_device_register(&exynos4_device_pdma1, &iomem_resource); return 0; } From f86147cc8faca81de5edfa1fab07b119c1867322 Mon Sep 17 00:00:00 2001 From: Alim Akhtar Date: Mon, 19 Sep 2011 10:27:23 +0900 Subject: [PATCH 0163/1531] ARM: S5PV210: register the second instance of pl330 DMAC Platform data is provided for two instance of pl330 DMAC, but only one DMAC is register with amba_device. This patch register the second instance. Signed-off-by: Alim Akhtar Signed-off-by: Kukjin Kim --- arch/arm/mach-s5pv210/dma.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-s5pv210/dma.c b/arch/arm/mach-s5pv210/dma.c index f79d0b06cbf9..86b749c18b77 100644 --- a/arch/arm/mach-s5pv210/dma.c +++ b/arch/arm/mach-s5pv210/dma.c @@ -254,6 +254,7 @@ struct amba_device s5pv210_device_pdma1 = { static int __init s5pv210_dma_init(void) { amba_device_register(&s5pv210_device_pdma0, &iomem_resource); + amba_device_register(&s5pv210_device_pdma1, &iomem_resource); return 0; } From d5663e7a404ae97888bb8e7a7376de3fb0878ddd Mon Sep 17 00:00:00 2001 From: Alim Akhtar Date: Mon, 19 Sep 2011 10:27:26 +0900 Subject: [PATCH 0164/1531] ARM: S5PC100: register the second instance of pl330 DMAC Platform data is provided for two instance of pl330 DMAC, but only one DMAC is register with amba_device. This patch register the second instance. Signed-off-by: Alim Akhtar Signed-off-by: Kukjin Kim --- arch/arm/mach-s5pc100/dma.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-s5pc100/dma.c b/arch/arm/mach-s5pc100/dma.c index ef803e92d35d..065a087f5a8b 100644 --- a/arch/arm/mach-s5pc100/dma.c +++ b/arch/arm/mach-s5pc100/dma.c @@ -260,6 +260,7 @@ struct amba_device s5pc100_device_pdma1 = { static int __init s5pc100_dma_init(void) { amba_device_register(&s5pc100_device_pdma0, &iomem_resource); + amba_device_register(&s5pc100_device_pdma1, &iomem_resource); return 0; } From 937bb6e4c676fecbfbc1939b942241c3f27bf5d8 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 24 Jun 2011 13:56:15 +0200 Subject: [PATCH 0165/1531] serial: sh-sci: don't filter on DMA device, use only channel ID On some sh-mobile systems there are more than one DMA controllers, that can be used for serial ports. Specifying a DMA device in sh-sci platform data unnecessarily restricts the driver to only use one DMA controller. Signed-off-by: Guennadi Liakhovetski [Fixed the trivial conflict in include/linux/serial_sci.h] Signed-off-by: Vinod Koul --- drivers/tty/serial/sh-sci.c | 25 ++++++++----------------- include/linux/serial_sci.h | 2 -- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index a9414facda47..dbd32a1286d3 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1439,12 +1439,8 @@ static bool filter(struct dma_chan *chan, void *slave) dev_dbg(chan->device->dev, "%s: slave ID %d\n", __func__, param->slave_id); - if (param->dma_dev == chan->device->dev) { - chan->private = param; - return true; - } else { - return false; - } + chan->private = param; + return true; } static void rx_timer_fn(unsigned long arg) @@ -1470,10 +1466,10 @@ static void sci_request_dma(struct uart_port *port) dma_cap_mask_t mask; int nent; - dev_dbg(port->dev, "%s: port %d DMA %p\n", __func__, - port->line, s->cfg->dma_dev); + dev_dbg(port->dev, "%s: port %d\n", __func__, + port->line); - if (!s->cfg->dma_dev) + if (s->cfg->dma_slave_tx <= 0 || s->cfg->dma_slave_rx <= 0) return; dma_cap_zero(mask); @@ -1483,7 +1479,6 @@ static void sci_request_dma(struct uart_port *port) /* Slave ID, e.g., SHDMA_SLAVE_SCIF0_TX */ param->slave_id = s->cfg->dma_slave_tx; - param->dma_dev = s->cfg->dma_dev; s->cookie_tx = -EINVAL; chan = dma_request_channel(mask, filter, param); @@ -1512,7 +1507,6 @@ static void sci_request_dma(struct uart_port *port) /* Slave ID, e.g., SHDMA_SLAVE_SCIF0_RX */ param->slave_id = s->cfg->dma_slave_rx; - param->dma_dev = s->cfg->dma_dev; chan = dma_request_channel(mask, filter, param); dev_dbg(port->dev, "%s: RX: got channel %p\n", __func__, chan); @@ -1557,9 +1551,6 @@ static void sci_free_dma(struct uart_port *port) { struct sci_port *s = to_sci_port(port); - if (!s->cfg->dma_dev) - return; - if (s->chan_tx) sci_tx_dma_release(s, false); if (s->chan_rx) @@ -1967,9 +1958,9 @@ static int __devinit sci_init_single(struct platform_device *dev, port->serial_in = sci_serial_in; port->serial_out = sci_serial_out; - if (p->dma_dev) - dev_dbg(port->dev, "DMA device %p, tx %d, rx %d\n", - p->dma_dev, p->dma_slave_tx, p->dma_slave_rx); + if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) + dev_dbg(port->dev, "DMA tx %d, rx %d\n", + p->dma_slave_tx, p->dma_slave_rx); return 0; } diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h index 8bffe9ae2ca0..0efa1f10bc2b 100644 --- a/include/linux/serial_sci.h +++ b/include/linux/serial_sci.h @@ -131,8 +131,6 @@ struct plat_sci_port { struct plat_sci_port_ops *ops; - struct device *dma_dev; - unsigned int dma_slave_tx; unsigned int dma_slave_rx; }; From d0f0b43f78b88b42e795e4c28f3591cb57590692 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 19 Aug 2011 22:40:07 +0900 Subject: [PATCH 0166/1531] ARM: S3C64XX: Use module identification for Cragganmore system builds The Cragganmore system is modular with I2C based identification chips on the system allowing identification of the system build. Provide a stub I2C driver which parses the module IDs and uses them to select the appropriate audio subsystem components to register. To avoid confusion due to having the mini-driver in the system the driver is placed in a separate file. Signed-off-by: Mark Brown Signed-off-by: Kukjin Kim --- arch/arm/mach-s3c64xx/Kconfig | 1 + arch/arm/mach-s3c64xx/Makefile | 2 +- arch/arm/mach-s3c64xx/include/mach/crag6410.h | 23 +++ arch/arm/mach-s3c64xx/mach-crag6410-module.c | 173 ++++++++++++++++++ arch/arm/mach-s3c64xx/mach-crag6410.c | 85 +-------- 5 files changed, 203 insertions(+), 81 deletions(-) create mode 100644 arch/arm/mach-s3c64xx/include/mach/crag6410.h create mode 100644 arch/arm/mach-s3c64xx/mach-crag6410-module.c diff --git a/arch/arm/mach-s3c64xx/Kconfig b/arch/arm/mach-s3c64xx/Kconfig index f057b6ae4f90..5552e048c2be 100644 --- a/arch/arm/mach-s3c64xx/Kconfig +++ b/arch/arm/mach-s3c64xx/Kconfig @@ -288,5 +288,6 @@ config MACH_WLF_CRAGG_6410 select S3C_DEV_RTC select S3C64XX_DEV_SPI select S3C24XX_GPIO_EXTRA128 + select I2C help Machine support for the Wolfson Cragganmore S3C6410 variant. diff --git a/arch/arm/mach-s3c64xx/Makefile b/arch/arm/mach-s3c64xx/Makefile index 61b4034a0c22..5fdea6aa50d4 100644 --- a/arch/arm/mach-s3c64xx/Makefile +++ b/arch/arm/mach-s3c64xx/Makefile @@ -55,7 +55,7 @@ obj-$(CONFIG_MACH_HMT) += mach-hmt.o obj-$(CONFIG_MACH_SMARTQ) += mach-smartq.o obj-$(CONFIG_MACH_SMARTQ5) += mach-smartq5.o obj-$(CONFIG_MACH_SMARTQ7) += mach-smartq7.o -obj-$(CONFIG_MACH_WLF_CRAGG_6410) += mach-crag6410.o +obj-$(CONFIG_MACH_WLF_CRAGG_6410) += mach-crag6410.o mach-crag6410-module.o # device support diff --git a/arch/arm/mach-s3c64xx/include/mach/crag6410.h b/arch/arm/mach-s3c64xx/include/mach/crag6410.h new file mode 100644 index 000000000000..be9074e17dfd --- /dev/null +++ b/arch/arm/mach-s3c64xx/include/mach/crag6410.h @@ -0,0 +1,23 @@ +/* Cragganmore 6410 shared definitions + * + * Copyright 2011 Wolfson Microelectronics plc + * Mark Brown + * + * 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. + */ + +#ifndef MACH_CRAG6410_H +#define MACH_CRAG6410_H + +#include + +#define BANFF_PMIC_IRQ_BASE IRQ_BOARD_START +#define GLENFARCLAS_PMIC_IRQ_BASE (IRQ_BOARD_START + 64) + +#define PCA935X_GPIO_BASE GPIO_BOARD_START +#define CODEC_GPIO_BASE (GPIO_BOARD_START + 8) +#define GLENFARCLAS_PMIC_GPIO_BASE (GPIO_BOARD_START + 16) + +#endif diff --git a/arch/arm/mach-s3c64xx/mach-crag6410-module.c b/arch/arm/mach-s3c64xx/mach-crag6410-module.c new file mode 100644 index 000000000000..0708fa01db9c --- /dev/null +++ b/arch/arm/mach-s3c64xx/mach-crag6410-module.c @@ -0,0 +1,173 @@ +/* Speyside modules for Cragganmore - board data probing + * + * Copyright 2011 Wolfson Microelectronics plc + * Mark Brown + * + * 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 + +static struct wm8996_retune_mobile_config wm8996_retune[] = { + { + .name = "Sub LPF", + .rate = 48000, + .regs = { + 0x6318, 0x6300, 0x1000, 0x0000, 0x0004, 0x2000, 0xF000, + 0x0000, 0x0004, 0x2000, 0xF000, 0x0000, 0x0004, 0x2000, + 0xF000, 0x0000, 0x0004, 0x1000, 0x0800, 0x4000 + }, + }, + { + .name = "Sub HPF", + .rate = 48000, + .regs = { + 0x000A, 0x6300, 0x1000, 0x0000, 0x0004, 0x2000, 0xF000, + 0x0000, 0x0004, 0x2000, 0xF000, 0x0000, 0x0004, 0x2000, + 0xF000, 0x0000, 0x0004, 0x1000, 0x0800, 0x4000 + }, + }, +}; + +static struct wm8996_pdata wm8996_pdata __initdata = { + .ldo_ena = S3C64XX_GPN(7), + .gpio_base = CODEC_GPIO_BASE, + .micdet_def = 1, + .inl_mode = WM8996_DIFFERRENTIAL_1, + .inr_mode = WM8996_DIFFERRENTIAL_1, + + .irq_flags = IRQF_TRIGGER_RISING, + + .gpio_default = { + 0x8001, /* GPIO1 == ADCLRCLK1 */ + 0x8001, /* GPIO2 == ADCLRCLK2, input due to CPU */ + 0x0141, /* GPIO3 == HP_SEL */ + 0x0002, /* GPIO4 == IRQ */ + 0x020e, /* GPIO5 == CLKOUT */ + }, + + .retune_mobile_cfgs = wm8996_retune, + .num_retune_mobile_cfgs = ARRAY_SIZE(wm8996_retune), +}; + +static struct wm8962_pdata wm8962_pdata __initdata = { + .gpio_init = { + 0, + WM8962_GPIO_FN_OPCLK, + WM8962_GPIO_FN_DMICCLK, + 0, + 0x8000 | WM8962_GPIO_FN_DMICDAT, + WM8962_GPIO_FN_IRQ, /* Open drain mode */ + }, + .irq_active_low = true, +}; + +static struct wm9081_pdata wm9081_pdata __initdata = { + .irq_high = false, + .irq_cmos = false, +}; + +static const struct i2c_board_info wm1254_devs[] = { + { I2C_BOARD_INFO("wm8996", 0x1a), + .platform_data = &wm8996_pdata, + .irq = GLENFARCLAS_PMIC_IRQ_BASE + WM831X_IRQ_GPIO_2, + }, + { I2C_BOARD_INFO("wm9081", 0x6c), + .platform_data = &wm9081_pdata, }, +}; + +static const struct i2c_board_info wm1259_devs[] = { + { I2C_BOARD_INFO("wm8962", 0x1a), + .platform_data = &wm8962_pdata, + .irq = GLENFARCLAS_PMIC_IRQ_BASE + WM831X_IRQ_GPIO_2, + }, +}; + + +static __devinitdata const struct { + u8 id; + const char *name; + const struct i2c_board_info *i2c_devs; + int num_i2c_devs; +} gf_mods[] = { + { .id = 0x01, .name = "1250-EV1 Springbank" }, + { .id = 0x02, .name = "1251-EV1 Jura" }, + { .id = 0x03, .name = "1252-EV1 Glenlivet" }, + { .id = 0x11, .name = "6249-EV2 Glenfarclas", }, + { .id = 0x21, .name = "1275-EV1 Mortlach" }, + { .id = 0x25, .name = "1274-EV1 Glencadam" }, + { .id = 0x31, .name = "1253-EV1 Tomatin", }, + { .id = 0x39, .name = "1254-EV1 Dallas Dhu", + .i2c_devs = wm1254_devs, .num_i2c_devs = ARRAY_SIZE(wm1254_devs) }, + { .id = 0x3a, .name = "1259-EV1 Tobermory", + .i2c_devs = wm1259_devs, .num_i2c_devs = ARRAY_SIZE(wm1259_devs) }, + { .id = 0x3b, .name = "1255-EV1 Kilchoman" }, + { .id = 0x3c, .name = "1273-EV1 Longmorn" }, +}; + +static __devinit int wlf_gf_module_probe(struct i2c_client *i2c, + const struct i2c_device_id *i2c_id) +{ + int ret, i, j, id, rev; + + ret = i2c_smbus_read_byte_data(i2c, 0); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read ID: %d\n", ret); + return ret; + } + + id = (ret & 0xfe) >> 2; + rev = ret & 0x3; + for (i = 0; i < ARRAY_SIZE(gf_mods); i++) + if (id == gf_mods[i].id) + break; + + if (i < ARRAY_SIZE(gf_mods)) { + dev_info(&i2c->dev, "%s revision %d\n", + gf_mods[i].name, rev + 1); + for (j = 0; j < gf_mods[i].num_i2c_devs; j++) { + if (!i2c_new_device(i2c->adapter, + &(gf_mods[i].i2c_devs[j]))) + dev_err(&i2c->dev, + "Failed to register dev: %d\n", ret); + } + } else { + dev_warn(&i2c->dev, "Unknown module ID %d revision %d\n", + id, rev); + } + + return 0; +} + +static const struct i2c_device_id wlf_gf_module_id[] = { + { "wlf-gf-module", 0 }, + { } +}; + +static struct i2c_driver wlf_gf_module_driver = { + .driver = { + .name = "wlf-gf-module", + .owner = THIS_MODULE, + }, + .probe = wlf_gf_module_probe, + .id_table = wlf_gf_module_id, +}; + +static int __init wlf_gf_module_register(void) +{ + return i2c_add_driver(&wlf_gf_module_driver); +} +module_init(wlf_gf_module_register); diff --git a/arch/arm/mach-s3c64xx/mach-crag6410.c b/arch/arm/mach-s3c64xx/mach-crag6410.c index 5affb26a1985..1ef53b28f683 100644 --- a/arch/arm/mach-s3c64xx/mach-crag6410.c +++ b/arch/arm/mach-s3c64xx/mach-crag6410.c @@ -47,6 +47,7 @@ #include #include #include +#include #include @@ -65,17 +66,6 @@ #include #include -#include -#include -#include - -#define BANFF_PMIC_IRQ_BASE IRQ_BOARD_START -#define GLENFARCLAS_PMIC_IRQ_BASE (IRQ_BOARD_START + 64) - -#define PCA935X_GPIO_BASE GPIO_BOARD_START -#define CODEC_GPIO_BASE (GPIO_BOARD_START + 8) -#define GLENFARCLAS_PMIC_GPIO_BASE (GPIO_BOARD_START + 16) - /* serial port setup */ #define UCON (S3C2410_UCON_DEFAULT | S3C2410_UCON_UCLK) @@ -623,81 +613,16 @@ static struct wm831x_pdata glenfarclas_pmic_pdata __initdata = { .disable_touch = true, }; -static struct wm8996_retune_mobile_config wm8996_retune[] = { - { - .name = "Sub LPF", - .rate = 48000, - .regs = { - 0x6318, 0x6300, 0x1000, 0x0000, 0x0004, 0x2000, 0xF000, - 0x0000, 0x0004, 0x2000, 0xF000, 0x0000, 0x0004, 0x2000, - 0xF000, 0x0000, 0x0004, 0x1000, 0x0800, 0x4000 - }, - }, - { - .name = "Sub HPF", - .rate = 48000, - .regs = { - 0x000A, 0x6300, 0x1000, 0x0000, 0x0004, 0x2000, 0xF000, - 0x0000, 0x0004, 0x2000, 0xF000, 0x0000, 0x0004, 0x2000, - 0xF000, 0x0000, 0x0004, 0x1000, 0x0800, 0x4000 - }, - }, -}; - -static struct wm8996_pdata wm8996_pdata __initdata = { - .ldo_ena = S3C64XX_GPN(7), - .gpio_base = CODEC_GPIO_BASE, - .micdet_def = 1, - .inl_mode = WM8996_DIFFERRENTIAL_1, - .inr_mode = WM8996_DIFFERRENTIAL_1, - - .irq_flags = IRQF_TRIGGER_RISING, - - .gpio_default = { - 0x8001, /* GPIO1 == ADCLRCLK1 */ - 0x8001, /* GPIO2 == ADCLRCLK2, input due to CPU */ - 0x0141, /* GPIO3 == HP_SEL */ - 0x0002, /* GPIO4 == IRQ */ - 0x020e, /* GPIO5 == CLKOUT */ - }, - - .retune_mobile_cfgs = wm8996_retune, - .num_retune_mobile_cfgs = ARRAY_SIZE(wm8996_retune), -}; - -static struct wm8962_pdata wm8962_pdata __initdata = { - .gpio_init = { - 0, - WM8962_GPIO_FN_OPCLK, - WM8962_GPIO_FN_DMICCLK, - 0, - 0x8000 | WM8962_GPIO_FN_DMICDAT, - WM8962_GPIO_FN_IRQ, /* Open drain mode */ - }, - .irq_active_low = true, -}; - -static struct wm9081_pdata wm9081_pdata __initdata = { - .irq_high = false, - .irq_cmos = false, -}; - static struct i2c_board_info i2c_devs1[] __initdata = { { I2C_BOARD_INFO("wm8311", 0x34), .irq = S3C_EINT(0), .platform_data = &glenfarclas_pmic_pdata }, + { I2C_BOARD_INFO("wlf-gf-module", 0x24) }, + { I2C_BOARD_INFO("wlf-gf-module", 0x25) }, + { I2C_BOARD_INFO("wlf-gf-module", 0x26) }, + { I2C_BOARD_INFO("wm1250-ev1", 0x27) }, - { I2C_BOARD_INFO("wm8996", 0x1a), - .platform_data = &wm8996_pdata, - .irq = GLENFARCLAS_PMIC_IRQ_BASE + WM831X_IRQ_GPIO_2, - }, - { I2C_BOARD_INFO("wm9081", 0x6c), - .platform_data = &wm9081_pdata, }, - { I2C_BOARD_INFO("wm8962", 0x1a), - .platform_data = &wm8962_pdata, - .irq = GLENFARCLAS_PMIC_IRQ_BASE + WM831X_IRQ_GPIO_2, - }, }; static void __init crag6410_map_io(void) From 4ed12b50708875e23ecea191d538e6666b2196eb Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 31 Aug 2011 08:03:11 +0900 Subject: [PATCH 0167/1531] ARM: S3C64XX: Hook up some additional supplies on Cragganmore For further modules. Signed-off-by: Mark Brown Signed-off-by: Kukjin Kim --- arch/arm/mach-s3c64xx/mach-crag6410.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/mach-s3c64xx/mach-crag6410.c b/arch/arm/mach-s3c64xx/mach-crag6410.c index 1ef53b28f683..9c287a3e28e5 100644 --- a/arch/arm/mach-s3c64xx/mach-crag6410.c +++ b/arch/arm/mach-s3c64xx/mach-crag6410.c @@ -285,6 +285,8 @@ static struct platform_device speyside_wm8962_device = { static struct regulator_consumer_supply wallvdd_consumers[] = { REGULATOR_SUPPLY("SPKVDD1", "1-001a"), REGULATOR_SUPPLY("SPKVDD2", "1-001a"), + REGULATOR_SUPPLY("SPKVDDL", "1-001a"), + REGULATOR_SUPPLY("SPKVDDR", "1-001a"), }; static struct regulator_init_data wallvdd_data = { @@ -561,6 +563,9 @@ static struct regulator_init_data pvdd_1v2 __initdata = { static struct regulator_consumer_supply pvdd_1v8_consumers[] __initdata = { REGULATOR_SUPPLY("PLLVDD", "1-001a"), REGULATOR_SUPPLY("DBVDD", "1-001a"), + REGULATOR_SUPPLY("DBVDD1", "1-001a"), + REGULATOR_SUPPLY("DBVDD2", "1-001a"), + REGULATOR_SUPPLY("DBVDD3", "1-001a"), REGULATOR_SUPPLY("CPVDD", "1-001a"), REGULATOR_SUPPLY("AVDD2", "1-001a"), REGULATOR_SUPPLY("DCVDD", "1-001a"), From 3aa7779c748bf340dd122e2d4205f312af977544 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 5 Sep 2011 14:48:39 +0900 Subject: [PATCH 0168/1531] ARM: S3C64XX: Add devices for Kilchoman audio module on Cragganmore The Kilchoman audio module carries a WM5100 audio CODEC and WM9081 speaker amplifier. Signed-off-by: Mark Brown Signed-off-by: Kukjin Kim --- arch/arm/mach-s3c64xx/mach-crag6410-module.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-s3c64xx/mach-crag6410-module.c b/arch/arm/mach-s3c64xx/mach-crag6410-module.c index 0708fa01db9c..66668565ee75 100644 --- a/arch/arm/mach-s3c64xx/mach-crag6410-module.c +++ b/arch/arm/mach-s3c64xx/mach-crag6410-module.c @@ -89,6 +89,14 @@ static const struct i2c_board_info wm1254_devs[] = { .platform_data = &wm9081_pdata, }, }; +static const struct i2c_board_info wm1255_devs[] = { + { I2C_BOARD_INFO("wm5100", 0x1a), + .irq = GLENFARCLAS_PMIC_IRQ_BASE + WM831X_IRQ_GPIO_2, + }, + { I2C_BOARD_INFO("wm9081", 0x6c), + .platform_data = &wm9081_pdata, }, +}; + static const struct i2c_board_info wm1259_devs[] = { { I2C_BOARD_INFO("wm8962", 0x1a), .platform_data = &wm8962_pdata, @@ -114,7 +122,8 @@ static __devinitdata const struct { .i2c_devs = wm1254_devs, .num_i2c_devs = ARRAY_SIZE(wm1254_devs) }, { .id = 0x3a, .name = "1259-EV1 Tobermory", .i2c_devs = wm1259_devs, .num_i2c_devs = ARRAY_SIZE(wm1259_devs) }, - { .id = 0x3b, .name = "1255-EV1 Kilchoman" }, + { .id = 0x3b, .name = "1255-EV1 Kilchoman", + .i2c_devs = wm1255_devs, .num_i2c_devs = ARRAY_SIZE(wm1255_devs) }, { .id = 0x3c, .name = "1273-EV1 Longmorn" }, }; From 8c051ab47bf3e0d35636d4772f9c0504bebe2fec Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 5 Sep 2011 14:50:02 +0900 Subject: [PATCH 0169/1531] ARM: S3C64XX: Register Lowland audio device on Cragganmore The Lowland system is a combination of the Cragganmore CPU module and the Tomatin and Kilchomin audio modules on Glenfarclas. Signed-off-by: Mark Brown Signed-off-by: Kukjin Kim --- arch/arm/mach-s3c64xx/mach-crag6410.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm/mach-s3c64xx/mach-crag6410.c b/arch/arm/mach-s3c64xx/mach-crag6410.c index 9c287a3e28e5..a0422a719996 100644 --- a/arch/arm/mach-s3c64xx/mach-crag6410.c +++ b/arch/arm/mach-s3c64xx/mach-crag6410.c @@ -277,6 +277,11 @@ static struct platform_device speyside_device = { .id = -1, }; +static struct platform_device lowland_device = { + .name = "lowland", + .id = -1, +}; + static struct platform_device speyside_wm8962_device = { .name = "speyside-wm8962", .id = -1, @@ -337,6 +342,7 @@ static struct platform_device *crag6410_devices[] __initdata = { &crag6410_backlight_device, &speyside_device, &speyside_wm8962_device, + &lowland_device, &wallvdd_device, }; From 6d8d492ef73433ea8debfba623dc2ee4d627aa7e Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Tue, 23 Aug 2011 11:33:08 +0900 Subject: [PATCH 0170/1531] ARM: S3C64XX: Use S3C64XX_SDMA_SEL register name instead of numeric address This patch adds a definition of S3C64XX_SDMA_SEL register and modifies the s3c64xx DMA driver to use it instead of a magic register address. Signed-off-by: Tomasz Figa Signed-off-by: Kukjin Kim --- arch/arm/mach-s3c64xx/dma.c | 2 +- arch/arm/mach-s3c64xx/include/mach/regs-sys.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-s3c64xx/dma.c b/arch/arm/mach-s3c64xx/dma.c index 204bfafe4bfc..0849f2d3b105 100644 --- a/arch/arm/mach-s3c64xx/dma.c +++ b/arch/arm/mach-s3c64xx/dma.c @@ -740,7 +740,7 @@ static int __init s3c64xx_dma_init(void) } /* Set all DMA configuration to be DMA, not SDMA */ - writel(0xffffff, S3C_SYSREG(0x110)); + writel(0xffffff, S3C64XX_SDMA_SEL); /* Register standard DMA controllers */ s3c64xx_dma_init1(0, DMACH_UART0, IRQ_DMA0, 0x75000000); diff --git a/arch/arm/mach-s3c64xx/include/mach/regs-sys.h b/arch/arm/mach-s3c64xx/include/mach/regs-sys.h index 69b78d9f83b8..774e0de31400 100644 --- a/arch/arm/mach-s3c64xx/include/mach/regs-sys.h +++ b/arch/arm/mach-s3c64xx/include/mach/regs-sys.h @@ -21,6 +21,8 @@ #define S3C64XX_AHB_CON1 S3C_SYSREG(0x104) #define S3C64XX_AHB_CON2 S3C_SYSREG(0x108) +#define S3C64XX_SDMA_SEL S3C_SYSREG(0x110) + #define S3C64XX_OTHERS S3C_SYSREG(0x900) #define S3C64XX_OTHERS_USBMASK (1 << 16) From 348276f4a37ab98559c8c63cbe2a122b7ff65421 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Tue, 23 Aug 2011 11:33:08 +0900 Subject: [PATCH 0171/1531] ARM: S3C64XX: Save/restore S3C64XX_SDMA_SEL on suspend/resume This patch makes sure that S3C64XX_SDMA_SEL register is preserved during sleep mode, as it is critical for DMA operation and the DMA driver alone does not provide any power management facilities. Signed-off-by: Tomasz Figa Signed-off-by: Kukjin Kim --- arch/arm/mach-s3c64xx/pm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/mach-s3c64xx/pm.c b/arch/arm/mach-s3c64xx/pm.c index 055e2858b0dd..72e3ab2ac088 100644 --- a/arch/arm/mach-s3c64xx/pm.c +++ b/arch/arm/mach-s3c64xx/pm.c @@ -85,6 +85,8 @@ static struct sleep_save misc_save[] = { SAVE_ITEM(S3C64XX_MEM0CONSLP0), SAVE_ITEM(S3C64XX_MEM0CONSLP1), SAVE_ITEM(S3C64XX_MEM1CONSLP), + + SAVE_ITEM(S3C64XX_SDMA_SEL), }; void s3c_pm_configure_extint(void) From d9018df00785d7ff52aa7fa8acfacd8a036fc832 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Tue, 23 Aug 2011 11:33:08 +0900 Subject: [PATCH 0172/1531] ARM: S3C64XX: Save/restore S3C64XX_MODEM_MIFPCON on suspend/resume This patch makes sure that the LCD bypass state is preserved during sleep mode. It achieves this by saving the S3C64XX_MODEM_MIFPCON register, which is not handled by any other code currently. Signed-off-by: Tomasz Figa Signed-off-by: Kukjin Kim --- arch/arm/mach-s3c64xx/pm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/mach-s3c64xx/pm.c b/arch/arm/mach-s3c64xx/pm.c index 72e3ab2ac088..b375cd5c47cb 100644 --- a/arch/arm/mach-s3c64xx/pm.c +++ b/arch/arm/mach-s3c64xx/pm.c @@ -29,6 +29,7 @@ #include #include #include +#include #ifdef CONFIG_S3C_PM_DEBUG_LED_SMDK void s3c_pm_debug_smdkled(u32 set, u32 clear) @@ -87,6 +88,7 @@ static struct sleep_save misc_save[] = { SAVE_ITEM(S3C64XX_MEM1CONSLP), SAVE_ITEM(S3C64XX_SDMA_SEL), + SAVE_ITEM(S3C64XX_MODEM_MIFPCON), }; void s3c_pm_configure_extint(void) From fb5d375d352ee5830b33ccdff06eb4f5f1d603f5 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Fri, 19 Aug 2011 11:54:31 +0200 Subject: [PATCH 0173/1531] ARM: S3C64XX: Add support for synchronous clock operation Some boards based on S3C6410 use synchronous clocking, which means that HCLKx2 and other system clocks are generated from APLL instead of MPLL. This patch adds support for such boards, by calculating hclk2 depending on the status of S3C_OTHERS_SYNCMUXSEL bit in S3C64XX_OTHERS regist Signed-off-by: Tomasz Figa Signed-off-by: Kukjin Kim --- arch/arm/mach-s3c64xx/clock.c | 8 +++++++- arch/arm/mach-s3c64xx/include/mach/regs-sys.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-s3c64xx/clock.c b/arch/arm/mach-s3c64xx/clock.c index 8cf39e33579e..872e68361eb4 100644 --- a/arch/arm/mach-s3c64xx/clock.c +++ b/arch/arm/mach-s3c64xx/clock.c @@ -744,7 +744,13 @@ void __init_or_cpufreq s3c6400_setup_clocks(void) printk(KERN_INFO "S3C64XX: PLL settings, A=%ld, M=%ld, E=%ld\n", apll, mpll, epll); - hclk2 = mpll / GET_DIV(clkdiv0, S3C6400_CLKDIV0_HCLK2); + if(__raw_readl(S3C64XX_OTHERS) & S3C64XX_OTHERS_SYNCMUXSEL) + /* Synchronous mode */ + hclk2 = apll / GET_DIV(clkdiv0, S3C6400_CLKDIV0_HCLK2); + else + /* Asynchronous mode */ + hclk2 = mpll / GET_DIV(clkdiv0, S3C6400_CLKDIV0_HCLK2); + hclk = hclk2 / GET_DIV(clkdiv0, S3C6400_CLKDIV0_HCLK); pclk = hclk2 / GET_DIV(clkdiv0, S3C6400_CLKDIV0_PCLK); diff --git a/arch/arm/mach-s3c64xx/include/mach/regs-sys.h b/arch/arm/mach-s3c64xx/include/mach/regs-sys.h index 774e0de31400..b91e02093289 100644 --- a/arch/arm/mach-s3c64xx/include/mach/regs-sys.h +++ b/arch/arm/mach-s3c64xx/include/mach/regs-sys.h @@ -26,5 +26,6 @@ #define S3C64XX_OTHERS S3C_SYSREG(0x900) #define S3C64XX_OTHERS_USBMASK (1 << 16) +#define S3C64XX_OTHERS_SYNCMUXSEL (1 << 6) #endif /* _PLAT_REGS_SYS_H */ From b7f69d9d4283cfbbf7458962cf9bdba6463b831d Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 5 Aug 2011 15:32:43 +0530 Subject: [PATCH 0174/1531] dmaengine/amba-pl08x: Add support for sg len greater than one for slave transfers Untill now, sg_len greater than one is not supported. This patch adds support to do that. Note: Still, if peripheral is flow controller, sg_len can't be greater that one. Signed-off-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 378 +++++++++++++++++++++---------------- include/linux/amba/pl08x.h | 22 ++- 2 files changed, 231 insertions(+), 169 deletions(-) diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index cd8df7f5b5c8..2c390d1b9cad 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -352,7 +352,9 @@ static u32 pl08x_getbytes_chan(struct pl08x_dma_chan *plchan) if (!list_empty(&plchan->pend_list)) { struct pl08x_txd *txdi; list_for_each_entry(txdi, &plchan->pend_list, node) { - bytes += txdi->len; + struct pl08x_sg *dsg; + list_for_each_entry(dsg, &txd->dsg_list, node) + bytes += dsg->len; } } @@ -567,8 +569,9 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, struct pl08x_lli_build_data bd; int num_llis = 0; u32 cctl, early_bytes = 0; - size_t max_bytes_per_lli, total_bytes = 0; + size_t max_bytes_per_lli, total_bytes; struct pl08x_lli *llis_va; + struct pl08x_sg *dsg; txd->llis_va = dma_pool_alloc(pl08x->pool, GFP_NOWAIT, &txd->llis_bus); if (!txd->llis_va) { @@ -578,13 +581,9 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, pl08x->pool_ctr++; - /* Get the default CCTL */ - cctl = txd->cctl; - bd.txd = txd; - bd.srcbus.addr = txd->src_addr; - bd.dstbus.addr = txd->dst_addr; bd.lli_bus = (pl08x->lli_buses & PL08X_AHB2) ? PL080_LLI_LM_AHB2 : 0; + cctl = txd->cctl; /* Find maximum width of the source bus */ bd.srcbus.maxwidth = @@ -596,162 +595,179 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, pl08x_get_bytes_for_cctl((cctl & PL080_CONTROL_DWIDTH_MASK) >> PL080_CONTROL_DWIDTH_SHIFT); - /* Set up the bus widths to the maximum */ - bd.srcbus.buswidth = bd.srcbus.maxwidth; - bd.dstbus.buswidth = bd.dstbus.maxwidth; + list_for_each_entry(dsg, &txd->dsg_list, node) { + total_bytes = 0; + cctl = txd->cctl; - /* We need to count this down to zero */ - bd.remainder = txd->len; + bd.srcbus.addr = dsg->src_addr; + bd.dstbus.addr = dsg->dst_addr; + bd.remainder = dsg->len; + bd.srcbus.buswidth = bd.srcbus.maxwidth; + bd.dstbus.buswidth = bd.dstbus.maxwidth; - pl08x_choose_master_bus(&bd, &mbus, &sbus, cctl); + pl08x_choose_master_bus(&bd, &mbus, &sbus, cctl); - dev_vdbg(&pl08x->adev->dev, "src=0x%08x%s/%u dst=0x%08x%s/%u len=%zu\n", - bd.srcbus.addr, cctl & PL080_CONTROL_SRC_INCR ? "+" : "", - bd.srcbus.buswidth, - bd.dstbus.addr, cctl & PL080_CONTROL_DST_INCR ? "+" : "", - bd.dstbus.buswidth, - bd.remainder); - dev_vdbg(&pl08x->adev->dev, "mbus=%s sbus=%s\n", - mbus == &bd.srcbus ? "src" : "dst", - sbus == &bd.srcbus ? "src" : "dst"); + dev_vdbg(&pl08x->adev->dev, "src=0x%08x%s/%u dst=0x%08x%s/%u len=%zu\n", + bd.srcbus.addr, cctl & PL080_CONTROL_SRC_INCR ? "+" : "", + bd.srcbus.buswidth, + bd.dstbus.addr, cctl & PL080_CONTROL_DST_INCR ? "+" : "", + bd.dstbus.buswidth, + bd.remainder); + dev_vdbg(&pl08x->adev->dev, "mbus=%s sbus=%s\n", + mbus == &bd.srcbus ? "src" : "dst", + sbus == &bd.srcbus ? "src" : "dst"); - /* - * Zero length is only allowed if all these requirements are met: - * - flow controller is peripheral. - * - src.addr is aligned to src.width - * - dst.addr is aligned to dst.width - * - * sg_len == 1 should be true, as there can be two cases here: - * - Memory addresses are contiguous and are not scattered. Here, Only - * one sg will be passed by user driver, with memory address and zero - * length. We pass this to controller and after the transfer it will - * receive the last burst request from peripheral and so transfer - * finishes. - * - * - Memory addresses are scattered and are not contiguous. Here, - * Obviously as DMA controller doesn't know when a lli's transfer gets - * over, it can't load next lli. So in this case, there has to be an - * assumption that only one lli is supported. Thus, we can't have - * scattered addresses. - */ - if (!bd.remainder) { - u32 fc = (txd->ccfg & PL080_CONFIG_FLOW_CONTROL_MASK) >> - PL080_CONFIG_FLOW_CONTROL_SHIFT; - if (!((fc >= PL080_FLOW_SRC2DST_DST) && + /* + * Zero length is only allowed if all these requirements are + * met: + * - flow controller is peripheral. + * - src.addr is aligned to src.width + * - dst.addr is aligned to dst.width + * + * sg_len == 1 should be true, as there can be two cases here: + * + * - Memory addresses are contiguous and are not scattered. + * Here, Only one sg will be passed by user driver, with + * memory address and zero length. We pass this to controller + * and after the transfer it will receive the last burst + * request from peripheral and so transfer finishes. + * + * - Memory addresses are scattered and are not contiguous. + * Here, Obviously as DMA controller doesn't know when a lli's + * transfer gets over, it can't load next lli. So in this + * case, there has to be an assumption that only one lli is + * supported. Thus, we can't have scattered addresses. + */ + if (!bd.remainder) { + u32 fc = (txd->ccfg & PL080_CONFIG_FLOW_CONTROL_MASK) >> + PL080_CONFIG_FLOW_CONTROL_SHIFT; + if (!((fc >= PL080_FLOW_SRC2DST_DST) && (fc <= PL080_FLOW_SRC2DST_SRC))) { - dev_err(&pl08x->adev->dev, "%s sg len can't be zero", - __func__); - return 0; - } - - if ((bd.srcbus.addr % bd.srcbus.buswidth) || - (bd.srcbus.addr % bd.srcbus.buswidth)) { - dev_err(&pl08x->adev->dev, - "%s src & dst address must be aligned to src" - " & dst width if peripheral is flow controller", - __func__); - return 0; - } - - cctl = pl08x_cctl_bits(cctl, bd.srcbus.buswidth, - bd.dstbus.buswidth, 0); - pl08x_fill_lli_for_desc(&bd, num_llis++, 0, cctl); - } + dev_err(&pl08x->adev->dev, "%s sg len can't be zero", + __func__); + return 0; + } - /* - * Send byte by byte for following cases - * - Less than a bus width available - * - until master bus is aligned - */ - if (bd.remainder < mbus->buswidth) - early_bytes = bd.remainder; - else if ((mbus->addr) % (mbus->buswidth)) { - early_bytes = mbus->buswidth - (mbus->addr) % (mbus->buswidth); - if ((bd.remainder - early_bytes) < mbus->buswidth) - early_bytes = bd.remainder; - } + if ((bd.srcbus.addr % bd.srcbus.buswidth) || + (bd.srcbus.addr % bd.srcbus.buswidth)) { + dev_err(&pl08x->adev->dev, + "%s src & dst address must be aligned to src" + " & dst width if peripheral is flow controller", + __func__); + return 0; + } - if (early_bytes) { - dev_vdbg(&pl08x->adev->dev, "%s byte width LLIs " - "(remain 0x%08x)\n", __func__, bd.remainder); - prep_byte_width_lli(&bd, &cctl, early_bytes, num_llis++, - &total_bytes); - } + cctl = pl08x_cctl_bits(cctl, bd.srcbus.buswidth, + bd.dstbus.buswidth, 0); + pl08x_fill_lli_for_desc(&bd, num_llis++, 0, cctl); + break; + } - if (bd.remainder) { /* - * Master now aligned - * - if slave is not then we must set its width down + * Send byte by byte for following cases + * - Less than a bus width available + * - until master bus is aligned */ - if (sbus->addr % sbus->buswidth) { - dev_dbg(&pl08x->adev->dev, - "%s set down bus width to one byte\n", - __func__); + if (bd.remainder < mbus->buswidth) + early_bytes = bd.remainder; + else if ((mbus->addr) % (mbus->buswidth)) { + early_bytes = mbus->buswidth - (mbus->addr) % + (mbus->buswidth); + if ((bd.remainder - early_bytes) < mbus->buswidth) + early_bytes = bd.remainder; + } - sbus->buswidth = 1; + if (early_bytes) { + dev_vdbg(&pl08x->adev->dev, + "%s byte width LLIs (remain 0x%08x)\n", + __func__, bd.remainder); + prep_byte_width_lli(&bd, &cctl, early_bytes, num_llis++, + &total_bytes); } - /* Bytes transferred = tsize * src width, not MIN(buswidths) */ - max_bytes_per_lli = bd.srcbus.buswidth * - PL080_CONTROL_TRANSFER_SIZE_MASK; + if (bd.remainder) { + /* + * Master now aligned + * - if slave is not then we must set its width down + */ + if (sbus->addr % sbus->buswidth) { + dev_dbg(&pl08x->adev->dev, + "%s set down bus width to one byte\n", + __func__); - /* - * Make largest possible LLIs until less than one bus - * width left - */ - while (bd.remainder > (mbus->buswidth - 1)) { - size_t lli_len, tsize, width; + sbus->buswidth = 1; + } /* - * If enough left try to send max possible, - * otherwise try to send the remainder + * Bytes transferred = tsize * src width, not + * MIN(buswidths) */ - lli_len = min(bd.remainder, max_bytes_per_lli); + max_bytes_per_lli = bd.srcbus.buswidth * + PL080_CONTROL_TRANSFER_SIZE_MASK; + dev_vdbg(&pl08x->adev->dev, + "%s max bytes per lli = %zu\n", + __func__, max_bytes_per_lli); /* - * Check against maximum bus alignment: Calculate actual - * transfer size in relation to bus width and get a - * maximum remainder of the highest bus width - 1 + * Make largest possible LLIs until less than one bus + * width left */ - width = max(mbus->buswidth, sbus->buswidth); - lli_len = (lli_len / width) * width; - tsize = lli_len / bd.srcbus.buswidth; + while (bd.remainder > (mbus->buswidth - 1)) { + size_t lli_len, tsize, width; - dev_vdbg(&pl08x->adev->dev, - "%s fill lli with single lli chunk of " - "size 0x%08zx (remainder 0x%08zx)\n", - __func__, lli_len, bd.remainder); + /* + * If enough left try to send max possible, + * otherwise try to send the remainder + */ + lli_len = min(bd.remainder, max_bytes_per_lli); - cctl = pl08x_cctl_bits(cctl, bd.srcbus.buswidth, + /* + * Check against maximum bus alignment: + * Calculate actual transfer size in relation to + * bus width an get a maximum remainder of the + * highest bus width - 1 + */ + width = max(mbus->buswidth, sbus->buswidth); + lli_len = (lli_len / width) * width; + tsize = lli_len / bd.srcbus.buswidth; + + dev_vdbg(&pl08x->adev->dev, + "%s fill lli with single lli chunk of " + "size 0x%08zx (remainder 0x%08zx)\n", + __func__, lli_len, bd.remainder); + + cctl = pl08x_cctl_bits(cctl, bd.srcbus.buswidth, bd.dstbus.buswidth, tsize); - pl08x_fill_lli_for_desc(&bd, num_llis++, lli_len, cctl); - total_bytes += lli_len; - } + pl08x_fill_lli_for_desc(&bd, num_llis++, + lli_len, cctl); + total_bytes += lli_len; + } - /* - * Send any odd bytes - */ - if (bd.remainder) { - dev_vdbg(&pl08x->adev->dev, - "%s align with boundary, send odd bytes (remain %zu)\n", - __func__, bd.remainder); - prep_byte_width_lli(&bd, &cctl, bd.remainder, - num_llis++, &total_bytes); + /* + * Send any odd bytes + */ + if (bd.remainder) { + dev_vdbg(&pl08x->adev->dev, + "%s align with boundary, send odd bytes (remain %zu)\n", + __func__, bd.remainder); + prep_byte_width_lli(&bd, &cctl, bd.remainder, + num_llis++, &total_bytes); + } } - } - if (total_bytes != txd->len) { - dev_err(&pl08x->adev->dev, - "%s size of encoded lli:s don't match total txd, transferred 0x%08zx from size 0x%08zx\n", - __func__, total_bytes, txd->len); - return 0; - } + if (total_bytes != dsg->len) { + dev_err(&pl08x->adev->dev, + "%s size of encoded lli:s don't match total txd, transferred 0x%08zx from size 0x%08zx\n", + __func__, total_bytes, dsg->len); + return 0; + } - if (num_llis >= MAX_NUM_TSFR_LLIS) { - dev_err(&pl08x->adev->dev, - "%s need to increase MAX_NUM_TSFR_LLIS from 0x%08x\n", - __func__, (u32) MAX_NUM_TSFR_LLIS); - return 0; + if (num_llis >= MAX_NUM_TSFR_LLIS) { + dev_err(&pl08x->adev->dev, + "%s need to increase MAX_NUM_TSFR_LLIS from 0x%08x\n", + __func__, (u32) MAX_NUM_TSFR_LLIS); + return 0; + } } llis_va = txd->llis_va; @@ -784,11 +800,18 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x, static void pl08x_free_txd(struct pl08x_driver_data *pl08x, struct pl08x_txd *txd) { + struct pl08x_sg *dsg, *_dsg; + /* Free the LLI */ dma_pool_free(pl08x->pool, txd->llis_va, txd->llis_bus); pl08x->pool_ctr--; + list_for_each_entry_safe(dsg, _dsg, &txd->dsg_list, node) { + list_del(&dsg->node); + kfree(dsg); + } + kfree(txd); } @@ -1234,6 +1257,7 @@ static struct pl08x_txd *pl08x_get_txd(struct pl08x_dma_chan *plchan, txd->tx.flags = flags; txd->tx.tx_submit = pl08x_tx_submit; INIT_LIST_HEAD(&txd->node); + INIT_LIST_HEAD(&txd->dsg_list); /* Always enable error and terminal interrupts */ txd->ccfg = PL080_CONFIG_ERR_IRQ_MASK | @@ -1252,6 +1276,7 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy( struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); struct pl08x_driver_data *pl08x = plchan->host; struct pl08x_txd *txd; + struct pl08x_sg *dsg; int ret; txd = pl08x_get_txd(plchan, flags); @@ -1261,10 +1286,19 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy( return NULL; } + dsg = kzalloc(sizeof(struct pl08x_sg), GFP_NOWAIT); + if (!dsg) { + pl08x_free_txd(pl08x, txd); + dev_err(&pl08x->adev->dev, "%s no memory for pl080 sg\n", + __func__); + return NULL; + } + list_add_tail(&dsg->node, &txd->dsg_list); + txd->direction = DMA_NONE; - txd->src_addr = src; - txd->dst_addr = dest; - txd->len = len; + dsg->src_addr = src; + dsg->dst_addr = dest; + dsg->len = len; /* Set platform data for m2m */ txd->ccfg |= PL080_FLOW_MEM2MEM << PL080_CONFIG_FLOW_CONTROL_SHIFT; @@ -1293,19 +1327,13 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( struct pl08x_dma_chan *plchan = to_pl08x_chan(chan); struct pl08x_driver_data *pl08x = plchan->host; struct pl08x_txd *txd; + struct pl08x_sg *dsg; + struct scatterlist *sg; + dma_addr_t slave_addr; int ret, tmp; - /* - * Current implementation ASSUMES only one sg - */ - if (sg_len != 1) { - dev_err(&pl08x->adev->dev, "%s prepared too long sglist\n", - __func__); - BUG(); - } - dev_dbg(&pl08x->adev->dev, "%s prepare transaction of %d bytes from %s\n", - __func__, sgl->length, plchan->name); + __func__, sgl->length, plchan->name); txd = pl08x_get_txd(plchan, flags); if (!txd) { @@ -1324,17 +1352,15 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( * channel target address dynamically at runtime. */ txd->direction = direction; - txd->len = sgl->length; if (direction == DMA_TO_DEVICE) { txd->cctl = plchan->dst_cctl; - txd->src_addr = sgl->dma_address; - txd->dst_addr = plchan->dst_addr; + slave_addr = plchan->dst_addr; } else if (direction == DMA_FROM_DEVICE) { txd->cctl = plchan->src_cctl; - txd->src_addr = plchan->src_addr; - txd->dst_addr = sgl->dma_address; + slave_addr = plchan->src_addr; } else { + pl08x_free_txd(pl08x, txd); dev_err(&pl08x->adev->dev, "%s direction unsupported\n", __func__); return NULL; @@ -1349,6 +1375,26 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg( txd->ccfg |= tmp << PL080_CONFIG_FLOW_CONTROL_SHIFT; + for_each_sg(sgl, sg, sg_len, tmp) { + dsg = kzalloc(sizeof(struct pl08x_sg), GFP_NOWAIT); + if (!dsg) { + pl08x_free_txd(pl08x, txd); + dev_err(&pl08x->adev->dev, "%s no mem for pl080 sg\n", + __func__); + return NULL; + } + list_add_tail(&dsg->node, &txd->dsg_list); + + dsg->len = sg_dma_len(sg); + if (direction == DMA_TO_DEVICE) { + dsg->src_addr = sg_phys(sg); + dsg->dst_addr = slave_addr; + } else { + dsg->src_addr = slave_addr; + dsg->dst_addr = sg_phys(sg); + } + } + ret = pl08x_prep_channel_resources(plchan, txd); if (ret) return NULL; @@ -1452,22 +1498,28 @@ static void pl08x_ensure_on(struct pl08x_driver_data *pl08x) static void pl08x_unmap_buffers(struct pl08x_txd *txd) { struct device *dev = txd->tx.chan->device->dev; + struct pl08x_sg *dsg; if (!(txd->tx.flags & DMA_COMPL_SKIP_SRC_UNMAP)) { if (txd->tx.flags & DMA_COMPL_SRC_UNMAP_SINGLE) - dma_unmap_single(dev, txd->src_addr, txd->len, - DMA_TO_DEVICE); - else - dma_unmap_page(dev, txd->src_addr, txd->len, - DMA_TO_DEVICE); + list_for_each_entry(dsg, &txd->dsg_list, node) + dma_unmap_single(dev, dsg->src_addr, dsg->len, + DMA_TO_DEVICE); + else { + list_for_each_entry(dsg, &txd->dsg_list, node) + dma_unmap_page(dev, dsg->src_addr, dsg->len, + DMA_TO_DEVICE); + } } if (!(txd->tx.flags & DMA_COMPL_SKIP_DEST_UNMAP)) { if (txd->tx.flags & DMA_COMPL_DEST_UNMAP_SINGLE) - dma_unmap_single(dev, txd->dst_addr, txd->len, - DMA_FROM_DEVICE); + list_for_each_entry(dsg, &txd->dsg_list, node) + dma_unmap_single(dev, dsg->dst_addr, dsg->len, + DMA_FROM_DEVICE); else - dma_unmap_page(dev, txd->dst_addr, txd->len, - DMA_FROM_DEVICE); + list_for_each_entry(dsg, &txd->dsg_list, node) + dma_unmap_page(dev, dsg->dst_addr, dsg->len, + DMA_FROM_DEVICE); } } diff --git a/include/linux/amba/pl08x.h b/include/linux/amba/pl08x.h index a22662c93981..9eabffbc4e50 100644 --- a/include/linux/amba/pl08x.h +++ b/include/linux/amba/pl08x.h @@ -105,13 +105,25 @@ struct pl08x_phy_chan { struct pl08x_dma_chan *serving; }; +/** + * struct pl08x_sg - structure containing data per sg + * @src_addr: src address of sg + * @dst_addr: dst address of sg + * @len: transfer len in bytes + * @node: node for txd's dsg_list + */ +struct pl08x_sg { + dma_addr_t src_addr; + dma_addr_t dst_addr; + size_t len; + struct list_head node; +}; + /** * struct pl08x_txd - wrapper for struct dma_async_tx_descriptor * @tx: async tx descriptor * @node: node for txd list for channels - * @src_addr: src address of txd - * @dst_addr: dst address of txd - * @len: transfer len in bytes + * @dsg_list: list of children sg's * @direction: direction of transfer * @llis_bus: DMA memory address (physical) start for the LLIs * @llis_va: virtual memory address start for the LLIs @@ -121,10 +133,8 @@ struct pl08x_phy_chan { struct pl08x_txd { struct dma_async_tx_descriptor tx; struct list_head node; + struct list_head dsg_list; enum dma_data_direction direction; - dma_addr_t src_addr; - dma_addr_t dst_addr; - size_t len; dma_addr_t llis_bus; struct pl08x_lli *llis_va; /* Default cctl value for LLIs */ From c12056466d76cdff884402d15f077dd0586e5215 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 5 Aug 2011 15:32:44 +0530 Subject: [PATCH 0175/1531] dmaengine/amba-pl08x: Check txd->llis_va before freeing dma_pool In pl08x_free_txd(), check if pool is allocated successfully before freeing it. Signed-off-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/amba-pl08x.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 2c390d1b9cad..b7cbd1ab1db1 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -803,7 +803,8 @@ static void pl08x_free_txd(struct pl08x_driver_data *pl08x, struct pl08x_sg *dsg, *_dsg; /* Free the LLI */ - dma_pool_free(pl08x->pool, txd->llis_va, txd->llis_bus); + if (txd->llis_va) + dma_pool_free(pl08x->pool, txd->llis_va, txd->llis_bus); pl08x->pool_ctr--; From 3a8f7558e475b68254d8bc3a2211f3f89bf67a71 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Fri, 24 Jun 2011 09:05:23 +0000 Subject: [PATCH 0176/1531] dma-mapping: Add get_required_mask if arch overrides default If an architecture sets ARCH_HAS_DMA_GET_REQUIRED_MASK and has settable dma_map_ops, the required mask may change by the ops implementation. For example, a system that always has an mmu inline may only require 32 bits while a swiotlb would desire bits to cover all of memory. Therefore add the field if the architecture does not use the generic definition of dma_get_required_mask. The first use will by by powerpc. Note that this does add some dependency on the order in which files are visible here. Signed-off-by: Milton Miller Signed-off-by: Nishanth Aravamudan Signed-off-by: Benjamin Herrenschmidt Acked-by: FUJITA Tomonori --- include/linux/dma-mapping.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index 347fdc32177a..aa32fecd1d34 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -41,6 +41,9 @@ struct dma_map_ops { int (*mapping_error)(struct device *dev, dma_addr_t dma_addr); int (*dma_supported)(struct device *dev, u64 mask); int (*set_dma_mask)(struct device *dev, u64 mask); +#ifdef ARCH_HAS_DMA_GET_REQUIRED_MASK + u64 (*get_required_mask)(struct device *dev); +#endif int is_phys; }; From d24f9c6999eacd3a7bc2b289e49fcb2bf2fafef2 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Fri, 24 Jun 2011 09:05:24 +0000 Subject: [PATCH 0177/1531] powerpc: Use the newly added get_required_mask dma_map_ops hook Now that the generic code has dma_map_ops set, instead of having a messy ifdef & if block in the base dma_get_required_mask hook push the computation into the dma ops. If the ops fails to set the get_required_mask hook default to the width of dma_addr_t. This also corrects ibmbus ibmebus_dma_supported to require a 64 bit mask. I doubt anything is checking or setting the dma mask on that bus. Signed-off-by: Milton Miller Signed-off-by: Nishanth Aravamudan Cc: linuxppc-dev@lists.ozlabs.org Cc: linux-kernel@vger.kernel.org Cc: benh@kernel.crashing.org Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/device.h | 2 ++ arch/powerpc/include/asm/dma-mapping.h | 3 -- arch/powerpc/kernel/dma-iommu.c | 3 +- arch/powerpc/kernel/dma-swiotlb.c | 16 ++++++++++ arch/powerpc/kernel/dma.c | 41 ++++++++++--------------- arch/powerpc/kernel/ibmebus.c | 8 ++++- arch/powerpc/kernel/vio.c | 7 ++++- arch/powerpc/platforms/cell/iommu.c | 13 ++++++-- arch/powerpc/platforms/ps3/system-bus.c | 7 +++++ arch/powerpc/platforms/pseries/iommu.c | 2 +- 10 files changed, 68 insertions(+), 34 deletions(-) diff --git a/arch/powerpc/include/asm/device.h b/arch/powerpc/include/asm/device.h index 16d25c0974be..d57c08acedfc 100644 --- a/arch/powerpc/include/asm/device.h +++ b/arch/powerpc/include/asm/device.h @@ -37,4 +37,6 @@ struct pdev_archdata { u64 dma_mask; }; +#define ARCH_HAS_DMA_GET_REQUIRED_MASK + #endif /* _ASM_POWERPC_DEVICE_H */ diff --git a/arch/powerpc/include/asm/dma-mapping.h b/arch/powerpc/include/asm/dma-mapping.h index 8135e66a4bb9..dd70fac57ec8 100644 --- a/arch/powerpc/include/asm/dma-mapping.h +++ b/arch/powerpc/include/asm/dma-mapping.h @@ -20,8 +20,6 @@ #define DMA_ERROR_CODE (~(dma_addr_t)0x0) -#define ARCH_HAS_DMA_GET_REQUIRED_MASK - /* Some dma direct funcs must be visible for use in other dma_ops */ extern void *dma_direct_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag); @@ -71,7 +69,6 @@ static inline unsigned long device_to_mask(struct device *dev) */ #ifdef CONFIG_PPC64 extern struct dma_map_ops dma_iommu_ops; -extern u64 dma_iommu_get_required_mask(struct device *dev); #endif extern struct dma_map_ops dma_direct_ops; diff --git a/arch/powerpc/kernel/dma-iommu.c b/arch/powerpc/kernel/dma-iommu.c index 1f2a711a261e..c1ad9db934f6 100644 --- a/arch/powerpc/kernel/dma-iommu.c +++ b/arch/powerpc/kernel/dma-iommu.c @@ -90,7 +90,7 @@ static int dma_iommu_dma_supported(struct device *dev, u64 mask) return 1; } -u64 dma_iommu_get_required_mask(struct device *dev) +static u64 dma_iommu_get_required_mask(struct device *dev) { struct iommu_table *tbl = get_iommu_table_base(dev); u64 mask; @@ -111,5 +111,6 @@ struct dma_map_ops dma_iommu_ops = { .dma_supported = dma_iommu_dma_supported, .map_page = dma_iommu_map_page, .unmap_page = dma_iommu_unmap_page, + .get_required_mask = dma_iommu_get_required_mask, }; EXPORT_SYMBOL(dma_iommu_ops); diff --git a/arch/powerpc/kernel/dma-swiotlb.c b/arch/powerpc/kernel/dma-swiotlb.c index 4295e0b94b2d..1ebc9189aada 100644 --- a/arch/powerpc/kernel/dma-swiotlb.c +++ b/arch/powerpc/kernel/dma-swiotlb.c @@ -24,6 +24,21 @@ unsigned int ppc_swiotlb_enable; +static u64 swiotlb_powerpc_get_required(struct device *dev) +{ + u64 end, mask, max_direct_dma_addr = dev->archdata.max_direct_dma_addr; + + end = memblock_end_of_DRAM(); + if (max_direct_dma_addr && end > max_direct_dma_addr) + end = max_direct_dma_addr; + end += get_dma_offset(dev); + + mask = 1ULL << (fls64(end) - 1); + mask += mask - 1; + + return mask; +} + /* * At the moment, all platforms that use this code only require * swiotlb to be used if we're operating on HIGHMEM. Since @@ -44,6 +59,7 @@ struct dma_map_ops swiotlb_dma_ops = { .sync_sg_for_cpu = swiotlb_sync_sg_for_cpu, .sync_sg_for_device = swiotlb_sync_sg_for_device, .mapping_error = swiotlb_dma_mapping_error, + .get_required_mask = swiotlb_powerpc_get_required, }; void pci_dma_dev_setup_swiotlb(struct pci_dev *pdev) diff --git a/arch/powerpc/kernel/dma.c b/arch/powerpc/kernel/dma.c index 503093efa202..10b136afbf50 100644 --- a/arch/powerpc/kernel/dma.c +++ b/arch/powerpc/kernel/dma.c @@ -96,6 +96,18 @@ static int dma_direct_dma_supported(struct device *dev, u64 mask) #endif } +static u64 dma_direct_get_required_mask(struct device *dev) +{ + u64 end, mask; + + end = memblock_end_of_DRAM() + get_dma_offset(dev); + + mask = 1ULL << (fls64(end) - 1); + mask += mask - 1; + + return mask; +} + static inline dma_addr_t dma_direct_map_page(struct device *dev, struct page *page, unsigned long offset, @@ -144,6 +156,7 @@ struct dma_map_ops dma_direct_ops = { .dma_supported = dma_direct_dma_supported, .map_page = dma_direct_map_page, .unmap_page = dma_direct_unmap_page, + .get_required_mask = dma_direct_get_required_mask, #ifdef CONFIG_NOT_COHERENT_CACHE .sync_single_for_cpu = dma_direct_sync_single, .sync_single_for_device = dma_direct_sync_single, @@ -173,7 +186,6 @@ EXPORT_SYMBOL(dma_set_mask); u64 dma_get_required_mask(struct device *dev) { struct dma_map_ops *dma_ops = get_dma_ops(dev); - u64 mask, end = 0; if (ppc_md.dma_get_required_mask) return ppc_md.dma_get_required_mask(dev); @@ -181,31 +193,10 @@ u64 dma_get_required_mask(struct device *dev) if (unlikely(dma_ops == NULL)) return 0; -#ifdef CONFIG_PPC64 - else if (dma_ops == &dma_iommu_ops) - return dma_iommu_get_required_mask(dev); -#endif -#ifdef CONFIG_SWIOTLB - else if (dma_ops == &swiotlb_dma_ops) { - u64 max_direct_dma_addr = dev->archdata.max_direct_dma_addr; - - end = memblock_end_of_DRAM(); - if (max_direct_dma_addr && end > max_direct_dma_addr) - end = max_direct_dma_addr; - end += get_dma_offset(dev); - } -#endif - else if (dma_ops == &dma_direct_ops) - end = memblock_end_of_DRAM() + get_dma_offset(dev); - else { - WARN_ONCE(1, "%s: unknown ops %p\n", __func__, dma_ops); - end = memblock_end_of_DRAM(); - } + if (dma_ops->get_required_mask) + return dma_ops->get_required_mask(dev); - mask = 1ULL << (fls64(end) - 1); - mask += mask - 1; - - return mask; + return DMA_BIT_MASK(8 * sizeof(dma_addr_t)); } EXPORT_SYMBOL_GPL(dma_get_required_mask); diff --git a/arch/powerpc/kernel/ibmebus.c b/arch/powerpc/kernel/ibmebus.c index 28581f1ad2c0..90ef2a44613b 100644 --- a/arch/powerpc/kernel/ibmebus.c +++ b/arch/powerpc/kernel/ibmebus.c @@ -125,7 +125,12 @@ static void ibmebus_unmap_sg(struct device *dev, static int ibmebus_dma_supported(struct device *dev, u64 mask) { - return 1; + return mask == DMA_BIT_MASK(64); +} + +static u64 ibmebus_dma_get_required_mask(struct device *dev) +{ + return DMA_BIT_MASK(64); } static struct dma_map_ops ibmebus_dma_ops = { @@ -134,6 +139,7 @@ static struct dma_map_ops ibmebus_dma_ops = { .map_sg = ibmebus_map_sg, .unmap_sg = ibmebus_unmap_sg, .dma_supported = ibmebus_dma_supported, + .get_required_mask = ibmebus_dma_get_required_mask, .map_page = ibmebus_map_page, .unmap_page = ibmebus_unmap_page, }; diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c index 1b695fdc362b..c0493259d133 100644 --- a/arch/powerpc/kernel/vio.c +++ b/arch/powerpc/kernel/vio.c @@ -605,6 +605,11 @@ static int vio_dma_iommu_dma_supported(struct device *dev, u64 mask) return dma_iommu_ops.dma_supported(dev, mask); } +static u64 vio_dma_get_required_mask(struct device *dev) +{ + return dma_iommu_ops.get_required_mask(dev); +} + struct dma_map_ops vio_dma_mapping_ops = { .alloc_coherent = vio_dma_iommu_alloc_coherent, .free_coherent = vio_dma_iommu_free_coherent, @@ -613,7 +618,7 @@ struct dma_map_ops vio_dma_mapping_ops = { .map_page = vio_dma_iommu_map_page, .unmap_page = vio_dma_iommu_unmap_page, .dma_supported = vio_dma_iommu_dma_supported, - + .get_required_mask = vio_dma_get_required_mask, }; /** diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c index 5ef55f3b0987..fc46fcac3921 100644 --- a/arch/powerpc/platforms/cell/iommu.c +++ b/arch/powerpc/platforms/cell/iommu.c @@ -1161,11 +1161,20 @@ __setup("iommu_fixed=", setup_iommu_fixed); static u64 cell_dma_get_required_mask(struct device *dev) { + struct dma_map_ops *dma_ops; + if (!dev->dma_mask) return 0; - if (iommu_fixed_disabled && get_dma_ops(dev) == &dma_iommu_ops) - return dma_iommu_get_required_mask(dev); + if (!iommu_fixed_disabled && + cell_iommu_get_fixed_address(dev) != OF_BAD_ADDR) + return DMA_BIT_MASK(64); + + dma_ops = get_dma_ops(dev); + if (dma_ops->get_required_mask) + return dma_ops->get_required_mask(dev); + + WARN_ONCE(1, "no get_required_mask in %p ops", dma_ops); return DMA_BIT_MASK(64); } diff --git a/arch/powerpc/platforms/ps3/system-bus.c b/arch/powerpc/platforms/ps3/system-bus.c index 23083c397528..688141c76e03 100644 --- a/arch/powerpc/platforms/ps3/system-bus.c +++ b/arch/powerpc/platforms/ps3/system-bus.c @@ -695,12 +695,18 @@ static int ps3_dma_supported(struct device *_dev, u64 mask) return mask >= DMA_BIT_MASK(32); } +static u64 ps3_dma_get_required_mask(struct device *_dev) +{ + return DMA_BIT_MASK(32); +} + static struct dma_map_ops ps3_sb_dma_ops = { .alloc_coherent = ps3_alloc_coherent, .free_coherent = ps3_free_coherent, .map_sg = ps3_sb_map_sg, .unmap_sg = ps3_sb_unmap_sg, .dma_supported = ps3_dma_supported, + .get_required_mask = ps3_dma_get_required_mask, .map_page = ps3_sb_map_page, .unmap_page = ps3_unmap_page, }; @@ -711,6 +717,7 @@ static struct dma_map_ops ps3_ioc0_dma_ops = { .map_sg = ps3_ioc0_map_sg, .unmap_sg = ps3_ioc0_unmap_sg, .dma_supported = ps3_dma_supported, + .get_required_mask = ps3_dma_get_required_mask, .map_page = ps3_ioc0_map_page, .unmap_page = ps3_unmap_page, }; diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index fe5ededf0d60..9f121a37eb51 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -1099,7 +1099,7 @@ static u64 dma_get_required_mask_pSeriesLP(struct device *dev) return DMA_BIT_MASK(64); } - return dma_iommu_get_required_mask(dev); + return dma_iommu_ops.get_required_mask(dev); } #else /* CONFIG_PCI */ From 2eccacd0974dca73e2151d3fd4c2dacf1a5c7cc2 Mon Sep 17 00:00:00 2001 From: Milton Miller Date: Fri, 24 Jun 2011 09:05:25 +0000 Subject: [PATCH 0178/1531] powerpc: Tidy up dma_map_ops after adding new hook The new get_required_mask hook name is longer than many of but not all of the prior ops. Tidy the struct initializers to align the equal signs using the local whitespace. Signed-off-by: Milton Miller Signed-off-by: Nishanth Aravamudan Cc: linuxppc-dev@lists.ozlabs.org Cc: linux-kernel@vger.kernel.org Cc: benh@kernel.crashing.org Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/dma-iommu.c | 14 +++++++------- arch/powerpc/kernel/dma.c | 16 ++++++++-------- arch/powerpc/kernel/ibmebus.c | 14 +++++++------- arch/powerpc/kernel/vio.c | 14 +++++++------- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/arch/powerpc/kernel/dma-iommu.c b/arch/powerpc/kernel/dma-iommu.c index c1ad9db934f6..6f04b9c383a7 100644 --- a/arch/powerpc/kernel/dma-iommu.c +++ b/arch/powerpc/kernel/dma-iommu.c @@ -104,13 +104,13 @@ static u64 dma_iommu_get_required_mask(struct device *dev) } struct dma_map_ops dma_iommu_ops = { - .alloc_coherent = dma_iommu_alloc_coherent, - .free_coherent = dma_iommu_free_coherent, - .map_sg = dma_iommu_map_sg, - .unmap_sg = dma_iommu_unmap_sg, - .dma_supported = dma_iommu_dma_supported, - .map_page = dma_iommu_map_page, - .unmap_page = dma_iommu_unmap_page, + .alloc_coherent = dma_iommu_alloc_coherent, + .free_coherent = dma_iommu_free_coherent, + .map_sg = dma_iommu_map_sg, + .unmap_sg = dma_iommu_unmap_sg, + .dma_supported = dma_iommu_dma_supported, + .map_page = dma_iommu_map_page, + .unmap_page = dma_iommu_unmap_page, .get_required_mask = dma_iommu_get_required_mask, }; EXPORT_SYMBOL(dma_iommu_ops); diff --git a/arch/powerpc/kernel/dma.c b/arch/powerpc/kernel/dma.c index 10b136afbf50..8593f53c4f6c 100644 --- a/arch/powerpc/kernel/dma.c +++ b/arch/powerpc/kernel/dma.c @@ -149,14 +149,14 @@ static inline void dma_direct_sync_single(struct device *dev, #endif struct dma_map_ops dma_direct_ops = { - .alloc_coherent = dma_direct_alloc_coherent, - .free_coherent = dma_direct_free_coherent, - .map_sg = dma_direct_map_sg, - .unmap_sg = dma_direct_unmap_sg, - .dma_supported = dma_direct_dma_supported, - .map_page = dma_direct_map_page, - .unmap_page = dma_direct_unmap_page, - .get_required_mask = dma_direct_get_required_mask, + .alloc_coherent = dma_direct_alloc_coherent, + .free_coherent = dma_direct_free_coherent, + .map_sg = dma_direct_map_sg, + .unmap_sg = dma_direct_unmap_sg, + .dma_supported = dma_direct_dma_supported, + .map_page = dma_direct_map_page, + .unmap_page = dma_direct_unmap_page, + .get_required_mask = dma_direct_get_required_mask, #ifdef CONFIG_NOT_COHERENT_CACHE .sync_single_for_cpu = dma_direct_sync_single, .sync_single_for_device = dma_direct_sync_single, diff --git a/arch/powerpc/kernel/ibmebus.c b/arch/powerpc/kernel/ibmebus.c index 90ef2a44613b..73110fb6bb6c 100644 --- a/arch/powerpc/kernel/ibmebus.c +++ b/arch/powerpc/kernel/ibmebus.c @@ -134,14 +134,14 @@ static u64 ibmebus_dma_get_required_mask(struct device *dev) } static struct dma_map_ops ibmebus_dma_ops = { - .alloc_coherent = ibmebus_alloc_coherent, - .free_coherent = ibmebus_free_coherent, - .map_sg = ibmebus_map_sg, - .unmap_sg = ibmebus_unmap_sg, - .dma_supported = ibmebus_dma_supported, + .alloc_coherent = ibmebus_alloc_coherent, + .free_coherent = ibmebus_free_coherent, + .map_sg = ibmebus_map_sg, + .unmap_sg = ibmebus_unmap_sg, + .dma_supported = ibmebus_dma_supported, .get_required_mask = ibmebus_dma_get_required_mask, - .map_page = ibmebus_map_page, - .unmap_page = ibmebus_unmap_page, + .map_page = ibmebus_map_page, + .unmap_page = ibmebus_unmap_page, }; static int ibmebus_match_path(struct device *dev, void *data) diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c index c0493259d133..34d291d83ec8 100644 --- a/arch/powerpc/kernel/vio.c +++ b/arch/powerpc/kernel/vio.c @@ -611,13 +611,13 @@ static u64 vio_dma_get_required_mask(struct device *dev) } struct dma_map_ops vio_dma_mapping_ops = { - .alloc_coherent = vio_dma_iommu_alloc_coherent, - .free_coherent = vio_dma_iommu_free_coherent, - .map_sg = vio_dma_iommu_map_sg, - .unmap_sg = vio_dma_iommu_unmap_sg, - .map_page = vio_dma_iommu_map_page, - .unmap_page = vio_dma_iommu_unmap_page, - .dma_supported = vio_dma_iommu_dma_supported, + .alloc_coherent = vio_dma_iommu_alloc_coherent, + .free_coherent = vio_dma_iommu_free_coherent, + .map_sg = vio_dma_iommu_map_sg, + .unmap_sg = vio_dma_iommu_unmap_sg, + .map_page = vio_dma_iommu_map_page, + .unmap_page = vio_dma_iommu_unmap_page, + .dma_supported = vio_dma_iommu_dma_supported, .get_required_mask = vio_dma_get_required_mask, }; From 7df5659eefad9b6d457ccdee016bd78bd064cfc0 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 27 Jun 2011 11:45:16 +0000 Subject: [PATCH 0179/1531] serial/8250: Move UPIO_TSI to powerpc This iotype is only used by the legacy_serial code in powerpc, so the code should live there, rather than be compiled in for every 8250 driver. Signed-off-by: Arnd Bergmann Cc: Benjamin Herrenschmidt Cc: linuxppc-dev@lists.ozlabs.org Cc: Greg Kroah-Hartman Cc: linux-serial@vger.kernel.org Acked-by: David Daney Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/legacy_serial.c | 25 +++++++++++++++++++++++++ drivers/tty/serial/8250.c | 23 ----------------------- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/arch/powerpc/kernel/legacy_serial.c b/arch/powerpc/kernel/legacy_serial.c index 2b97b80d6d7d..c7b5afeecaf2 100644 --- a/arch/powerpc/kernel/legacy_serial.c +++ b/arch/powerpc/kernel/legacy_serial.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,24 @@ static struct __initdata of_device_id legacy_serial_parents[] = { static unsigned int legacy_serial_count; static int legacy_serial_console = -1; +static unsigned int tsi_serial_in(struct uart_port *p, int offset) +{ + unsigned int tmp; + offset = offset << p->regshift; + if (offset == UART_IIR) { + tmp = readl(p->membase + (UART_IIR & ~3)); + return (tmp >> 16) & 0xff; /* UART_IIR % 4 == 2 */ + } else + return readb(p->membase + offset); +} + +static void tsi_serial_out(struct uart_port *p, int offset, int value) +{ + offset = offset << p->regshift; + if (!((offset == UART_IER) && (value & UART_IER_UUE))) + writeb(value, p->membase + offset); +} + static int __init add_legacy_port(struct device_node *np, int want_index, int iotype, phys_addr_t base, phys_addr_t taddr, unsigned long irq, @@ -102,6 +121,7 @@ static int __init add_legacy_port(struct device_node *np, int want_index, legacy_serial_ports[index].iobase = base; else legacy_serial_ports[index].mapbase = base; + legacy_serial_ports[index].iotype = iotype; legacy_serial_ports[index].uartclk = clock; legacy_serial_ports[index].irq = irq; @@ -112,6 +132,11 @@ static int __init add_legacy_port(struct device_node *np, int want_index, legacy_serial_infos[index].speed = spd ? be32_to_cpup(spd) : 0; legacy_serial_infos[index].irq_check_parent = irq_check_parent; + if (iotype == UPIO_TSI) { + legacy_serial_ports[index].serial_in = tsi_serial_in; + legacy_serial_ports[index].serial_out = tsi_serial_out; + } + printk(KERN_DEBUG "Found legacy serial port %d for %s\n", index, np->full_name); printk(KERN_DEBUG " %s=%llx, taddr=%llx, irq=%lx, clk=%d, speed=%d\n", diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index 7f50999eebc2..610b8e63710d 100644 --- a/drivers/tty/serial/8250.c +++ b/drivers/tty/serial/8250.c @@ -443,24 +443,6 @@ static void au_serial_out(struct uart_port *p, int offset, int value) __raw_writel(value, p->membase + offset); } -static unsigned int tsi_serial_in(struct uart_port *p, int offset) -{ - unsigned int tmp; - offset = map_8250_in_reg(p, offset) << p->regshift; - if (offset == UART_IIR) { - tmp = readl(p->membase + (UART_IIR & ~3)); - return (tmp >> 16) & 0xff; /* UART_IIR % 4 == 2 */ - } else - return readb(p->membase + offset); -} - -static void tsi_serial_out(struct uart_port *p, int offset, int value) -{ - offset = map_8250_out_reg(p, offset) << p->regshift; - if (!((offset == UART_IER) && (value & UART_IER_UUE))) - writeb(value, p->membase + offset); -} - /* Save the LCR value so it can be re-written when a Busy Detect IRQ occurs. */ static inline void dwapb_save_out_value(struct uart_port *p, int offset, int value) @@ -535,11 +517,6 @@ static void set_io_from_upio(struct uart_port *p) p->serial_out = au_serial_out; break; - case UPIO_TSI: - p->serial_in = tsi_serial_in; - p->serial_out = tsi_serial_out; - break; - case UPIO_DWAPB: p->serial_in = mem_serial_in; p->serial_out = dwapb_serial_out; From 41151e77a4d96ea138cede6d84c955aa4769ce74 Mon Sep 17 00:00:00 2001 From: Becky Bruce Date: Tue, 28 Jun 2011 09:54:48 +0000 Subject: [PATCH 0180/1531] powerpc: Hugetlb for BookE Enable hugepages on Freescale BookE processors. This allows the kernel to use huge TLB entries to map pages, which can greatly reduce the number of TLB misses and the amount of TLB thrashing experienced by applications with large memory footprints. Care should be taken when using this on FSL processors, as the number of large TLB entries supported by the core is low (16-64) on current processors. The supported set of hugepage sizes include 4m, 16m, 64m, 256m, and 1g. Page sizes larger than the max zone size are called "gigantic" pages and must be allocated on the command line (and cannot be deallocated). This is currently only fully implemented for Freescale 32-bit BookE processors, but there is some infrastructure in the code for 64-bit BooKE. Signed-off-by: Becky Bruce Signed-off-by: David Gibson Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/Kconfig | 3 +- arch/powerpc/include/asm/hugetlb.h | 63 +++- arch/powerpc/include/asm/mmu-book3e.h | 7 + arch/powerpc/include/asm/mmu-hash64.h | 3 +- arch/powerpc/include/asm/mmu.h | 18 +- arch/powerpc/include/asm/page.h | 31 +- arch/powerpc/include/asm/page_64.h | 11 - arch/powerpc/include/asm/pte-book3e.h | 3 + arch/powerpc/kernel/head_fsl_booke.S | 133 +++++++-- arch/powerpc/mm/Makefile | 1 + arch/powerpc/mm/hash_utils_64.c | 3 - arch/powerpc/mm/hugetlbpage-book3e.c | 121 ++++++++ arch/powerpc/mm/hugetlbpage.c | 379 ++++++++++++++++++++++--- arch/powerpc/mm/init_32.c | 9 + arch/powerpc/mm/mem.c | 5 + arch/powerpc/mm/mmu_context_nohash.c | 5 + arch/powerpc/mm/pgtable.c | 3 +- arch/powerpc/mm/tlb_low_64e.S | 24 +- arch/powerpc/mm/tlb_nohash.c | 46 ++- arch/powerpc/platforms/Kconfig.cputype | 4 +- 20 files changed, 766 insertions(+), 106 deletions(-) create mode 100644 arch/powerpc/mm/hugetlbpage-book3e.c diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 0a3d5560c9be..62711421cd64 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -429,8 +429,7 @@ config ARCH_POPULATES_NODE_MAP def_bool y config SYS_SUPPORTS_HUGETLBFS - def_bool y - depends on PPC_BOOK3S_64 + bool source "mm/Kconfig" diff --git a/arch/powerpc/include/asm/hugetlb.h b/arch/powerpc/include/asm/hugetlb.h index 5856a66ab404..86004930a78e 100644 --- a/arch/powerpc/include/asm/hugetlb.h +++ b/arch/powerpc/include/asm/hugetlb.h @@ -1,15 +1,60 @@ #ifndef _ASM_POWERPC_HUGETLB_H #define _ASM_POWERPC_HUGETLB_H +#ifdef CONFIG_HUGETLB_PAGE #include +extern struct kmem_cache *hugepte_cache; +extern void __init reserve_hugetlb_gpages(void); + +static inline pte_t *hugepd_page(hugepd_t hpd) +{ + BUG_ON(!hugepd_ok(hpd)); + return (pte_t *)((hpd.pd & ~HUGEPD_SHIFT_MASK) | PD_HUGE); +} + +static inline unsigned int hugepd_shift(hugepd_t hpd) +{ + return hpd.pd & HUGEPD_SHIFT_MASK; +} + +static inline pte_t *hugepte_offset(hugepd_t *hpdp, unsigned long addr, + unsigned pdshift) +{ + /* + * On 32-bit, we have multiple higher-level table entries that point to + * the same hugepte. Just use the first one since they're all + * identical. So for that case, idx=0. + */ + unsigned long idx = 0; + + pte_t *dir = hugepd_page(*hpdp); +#ifdef CONFIG_PPC64 + idx = (addr & ((1UL << pdshift) - 1)) >> hugepd_shift(*hpdp); +#endif + + return dir + idx; +} + pte_t *huge_pte_offset_and_shift(struct mm_struct *mm, unsigned long addr, unsigned *shift); void flush_dcache_icache_hugepage(struct page *page); +#if defined(CONFIG_PPC_MM_SLICES) || defined(CONFIG_PPC_SUBPAGE_PROT) int is_hugepage_only_range(struct mm_struct *mm, unsigned long addr, unsigned long len); +#else +static inline int is_hugepage_only_range(struct mm_struct *mm, + unsigned long addr, + unsigned long len) +{ + return 0; +} +#endif + +void book3e_hugetlb_preload(struct mm_struct *mm, unsigned long ea, pte_t pte); +void flush_hugetlb_page(struct vm_area_struct *vma, unsigned long vmaddr); void hugetlb_free_pgd_range(struct mmu_gather *tlb, unsigned long addr, unsigned long end, unsigned long floor, @@ -50,8 +95,11 @@ static inline void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, static inline pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep) { - unsigned long old = pte_update(mm, addr, ptep, ~0UL, 1); - return __pte(old); +#ifdef CONFIG_PPC64 + return __pte(pte_update(mm, addr, ptep, ~0UL, 1)); +#else + return __pte(pte_update(ptep, ~0UL, 0)); +#endif } static inline void huge_ptep_clear_flush(struct vm_area_struct *vma, @@ -93,4 +141,15 @@ static inline void arch_release_hugepage(struct page *page) { } +#else /* ! CONFIG_HUGETLB_PAGE */ +static inline void reserve_hugetlb_gpages(void) +{ + pr_err("Cannot reserve gpages without hugetlb enabled\n"); +} +static inline void flush_hugetlb_page(struct vm_area_struct *vma, + unsigned long vmaddr) +{ +} +#endif + #endif /* _ASM_POWERPC_HUGETLB_H */ diff --git a/arch/powerpc/include/asm/mmu-book3e.h b/arch/powerpc/include/asm/mmu-book3e.h index 3ea0f9a259d8..0260ea5ec3c2 100644 --- a/arch/powerpc/include/asm/mmu-book3e.h +++ b/arch/powerpc/include/asm/mmu-book3e.h @@ -66,6 +66,7 @@ #define MAS2_M 0x00000004 #define MAS2_G 0x00000002 #define MAS2_E 0x00000001 +#define MAS2_WIMGE_MASK 0x0000001f #define MAS2_EPN_MASK(size) (~0 << (size + 10)) #define MAS2_VAL(addr, size, flags) ((addr) & MAS2_EPN_MASK(size) | (flags)) @@ -80,6 +81,7 @@ #define MAS3_SW 0x00000004 #define MAS3_UR 0x00000002 #define MAS3_SR 0x00000001 +#define MAS3_BAP_MASK 0x0000003f #define MAS3_SPSIZE 0x0000003e #define MAS3_SPSIZE_SHIFT 1 @@ -212,6 +214,11 @@ typedef struct { unsigned int id; unsigned int active; unsigned long vdso_base; +#ifdef CONFIG_PPC_MM_SLICES + u64 low_slices_psize; /* SLB page size encodings */ + u64 high_slices_psize; /* 4 bits per slice for now */ + u16 user_psize; /* page size index */ +#endif } mm_context_t; /* Page size definitions, common between 32 and 64-bit diff --git a/arch/powerpc/include/asm/mmu-hash64.h b/arch/powerpc/include/asm/mmu-hash64.h index b445e0af4c2b..db645ec842bd 100644 --- a/arch/powerpc/include/asm/mmu-hash64.h +++ b/arch/powerpc/include/asm/mmu-hash64.h @@ -262,8 +262,7 @@ extern void hash_failure_debug(unsigned long ea, unsigned long access, extern int htab_bolt_mapping(unsigned long vstart, unsigned long vend, unsigned long pstart, unsigned long prot, int psize, int ssize); -extern void add_gpage(unsigned long addr, unsigned long page_size, - unsigned long number_of_pages); +extern void add_gpage(u64 addr, u64 page_size, unsigned long number_of_pages); extern void demote_segment_4k(struct mm_struct *mm, unsigned long addr); extern void hpte_init_native(void); diff --git a/arch/powerpc/include/asm/mmu.h b/arch/powerpc/include/asm/mmu.h index 698b30638681..f0145522cfba 100644 --- a/arch/powerpc/include/asm/mmu.h +++ b/arch/powerpc/include/asm/mmu.h @@ -175,14 +175,16 @@ extern u64 ppc64_rma_size; #define MMU_PAGE_64K_AP 3 /* "Admixed pages" (hash64 only) */ #define MMU_PAGE_256K 4 #define MMU_PAGE_1M 5 -#define MMU_PAGE_8M 6 -#define MMU_PAGE_16M 7 -#define MMU_PAGE_256M 8 -#define MMU_PAGE_1G 9 -#define MMU_PAGE_16G 10 -#define MMU_PAGE_64G 11 -#define MMU_PAGE_COUNT 12 - +#define MMU_PAGE_4M 6 +#define MMU_PAGE_8M 7 +#define MMU_PAGE_16M 8 +#define MMU_PAGE_64M 9 +#define MMU_PAGE_256M 10 +#define MMU_PAGE_1G 11 +#define MMU_PAGE_16G 12 +#define MMU_PAGE_64G 13 + +#define MMU_PAGE_COUNT 14 #if defined(CONFIG_PPC_STD_MMU_64) /* 64-bit classic hash table MMU */ diff --git a/arch/powerpc/include/asm/page.h b/arch/powerpc/include/asm/page.h index 2cd664ef0a5e..dd9c4fd038e0 100644 --- a/arch/powerpc/include/asm/page.h +++ b/arch/powerpc/include/asm/page.h @@ -36,6 +36,18 @@ #define PAGE_SIZE (ASM_CONST(1) << PAGE_SHIFT) +#ifndef __ASSEMBLY__ +#ifdef CONFIG_HUGETLB_PAGE +extern unsigned int HPAGE_SHIFT; +#else +#define HPAGE_SHIFT PAGE_SHIFT +#endif +#define HPAGE_SIZE ((1UL) << HPAGE_SHIFT) +#define HPAGE_MASK (~(HPAGE_SIZE - 1)) +#define HUGETLB_PAGE_ORDER (HPAGE_SHIFT - PAGE_SHIFT) +#define HUGE_MAX_HSTATE (MMU_PAGE_COUNT-1) +#endif + /* We do define AT_SYSINFO_EHDR but don't use the gate mechanism */ #define __HAVE_ARCH_GATE_AREA 1 @@ -158,6 +170,24 @@ extern phys_addr_t kernstart_addr; #define is_kernel_addr(x) ((x) >= PAGE_OFFSET) #endif +/* + * Use the top bit of the higher-level page table entries to indicate whether + * the entries we point to contain hugepages. This works because we know that + * the page tables live in kernel space. If we ever decide to support having + * page tables at arbitrary addresses, this breaks and will have to change. + */ +#ifdef CONFIG_PPC64 +#define PD_HUGE 0x8000000000000000 +#else +#define PD_HUGE 0x80000000 +#endif + +/* + * Some number of bits at the level of the page table that points to + * a hugepte are used to encode the size. This masks those bits. + */ +#define HUGEPD_SHIFT_MASK 0x3f + #ifndef __ASSEMBLY__ #undef STRICT_MM_TYPECHECKS @@ -243,7 +273,6 @@ typedef unsigned long pgprot_t; #endif typedef struct { signed long pd; } hugepd_t; -#define HUGEPD_SHIFT_MASK 0x3f #ifdef CONFIG_HUGETLB_PAGE static inline int hugepd_ok(hugepd_t hpd) diff --git a/arch/powerpc/include/asm/page_64.h b/arch/powerpc/include/asm/page_64.h index 9356262fd3cc..fb40ede6bc0d 100644 --- a/arch/powerpc/include/asm/page_64.h +++ b/arch/powerpc/include/asm/page_64.h @@ -64,17 +64,6 @@ extern void copy_page(void *to, void *from); /* Log 2 of page table size */ extern u64 ppc64_pft_size; -/* Large pages size */ -#ifdef CONFIG_HUGETLB_PAGE -extern unsigned int HPAGE_SHIFT; -#else -#define HPAGE_SHIFT PAGE_SHIFT -#endif -#define HPAGE_SIZE ((1UL) << HPAGE_SHIFT) -#define HPAGE_MASK (~(HPAGE_SIZE - 1)) -#define HUGETLB_PAGE_ORDER (HPAGE_SHIFT - PAGE_SHIFT) -#define HUGE_MAX_HSTATE (MMU_PAGE_COUNT-1) - #endif /* __ASSEMBLY__ */ #ifdef CONFIG_PPC_MM_SLICES diff --git a/arch/powerpc/include/asm/pte-book3e.h b/arch/powerpc/include/asm/pte-book3e.h index 082d515930a2..0156702ba24e 100644 --- a/arch/powerpc/include/asm/pte-book3e.h +++ b/arch/powerpc/include/asm/pte-book3e.h @@ -72,6 +72,9 @@ #define PTE_RPN_SHIFT (24) #endif +#define PTE_WIMGE_SHIFT (19) +#define PTE_BAP_SHIFT (2) + /* On 32-bit, we never clear the top part of the PTE */ #ifdef CONFIG_PPC32 #define _PTE_NONE_MASK 0xffffffff00000000ULL diff --git a/arch/powerpc/kernel/head_fsl_booke.S b/arch/powerpc/kernel/head_fsl_booke.S index 50845924b7d9..4ea9bfbf67e9 100644 --- a/arch/powerpc/kernel/head_fsl_booke.S +++ b/arch/powerpc/kernel/head_fsl_booke.S @@ -236,8 +236,24 @@ _ENTRY(__early_start) * if we find the pte (fall through): * r11 is low pte word * r12 is pointer to the pte + * r10 is the pshift from the PGD, if we're a hugepage */ #ifdef CONFIG_PTE_64BIT +#ifdef CONFIG_HUGETLB_PAGE +#define FIND_PTE \ + rlwinm r12, r10, 13, 19, 29; /* Compute pgdir/pmd offset */ \ + lwzx r11, r12, r11; /* Get pgd/pmd entry */ \ + rlwinm. r12, r11, 0, 0, 20; /* Extract pt base address */ \ + blt 1000f; /* Normal non-huge page */ \ + beq 2f; /* Bail if no table */ \ + oris r11, r11, PD_HUGE@h; /* Put back address bit */ \ + andi. r10, r11, HUGEPD_SHIFT_MASK@l; /* extract size field */ \ + xor r12, r10, r11; /* drop size bits from pointer */ \ + b 1001f; \ +1000: rlwimi r12, r10, 23, 20, 28; /* Compute pte address */ \ + li r10, 0; /* clear r10 */ \ +1001: lwz r11, 4(r12); /* Get pte entry */ +#else #define FIND_PTE \ rlwinm r12, r10, 13, 19, 29; /* Compute pgdir/pmd offset */ \ lwzx r11, r12, r11; /* Get pgd/pmd entry */ \ @@ -245,7 +261,8 @@ _ENTRY(__early_start) beq 2f; /* Bail if no table */ \ rlwimi r12, r10, 23, 20, 28; /* Compute pte address */ \ lwz r11, 4(r12); /* Get pte entry */ -#else +#endif /* HUGEPAGE */ +#else /* !PTE_64BIT */ #define FIND_PTE \ rlwimi r11, r10, 12, 20, 29; /* Create L1 (pgdir/pmd) address */ \ lwz r11, 0(r11); /* Get L1 entry */ \ @@ -402,8 +419,8 @@ interrupt_base: #ifdef CONFIG_PTE_64BIT #ifdef CONFIG_SMP - subf r10,r11,r12 /* create false data dep */ - lwzx r13,r11,r10 /* Get upper pte bits */ + subf r13,r11,r12 /* create false data dep */ + lwzx r13,r11,r13 /* Get upper pte bits */ #else lwz r13,0(r12) /* Get upper pte bits */ #endif @@ -483,8 +500,8 @@ interrupt_base: #ifdef CONFIG_PTE_64BIT #ifdef CONFIG_SMP - subf r10,r11,r12 /* create false data dep */ - lwzx r13,r11,r10 /* Get upper pte bits */ + subf r13,r11,r12 /* create false data dep */ + lwzx r13,r11,r13 /* Get upper pte bits */ #else lwz r13,0(r12) /* Get upper pte bits */ #endif @@ -548,7 +565,7 @@ interrupt_base: /* * Both the instruction and data TLB miss get to this * point to load the TLB. - * r10 - available to use + * r10 - tsize encoding (if HUGETLB_PAGE) or available to use * r11 - TLB (info from Linux PTE) * r12 - available to use * r13 - upper bits of PTE (if PTE_64BIT) or available to use @@ -558,21 +575,73 @@ interrupt_base: * Upon exit, we reload everything and RFI. */ finish_tlb_load: +#ifdef CONFIG_HUGETLB_PAGE + cmpwi 6, r10, 0 /* check for huge page */ + beq 6, finish_tlb_load_cont /* !huge */ + + /* Alas, we need more scratch registers for hugepages */ + mfspr r12, SPRN_SPRG_THREAD + stw r14, THREAD_NORMSAVE(4)(r12) + stw r15, THREAD_NORMSAVE(5)(r12) + stw r16, THREAD_NORMSAVE(6)(r12) + stw r17, THREAD_NORMSAVE(7)(r12) + + /* Get the next_tlbcam_idx percpu var */ +#ifdef CONFIG_SMP + lwz r12, THREAD_INFO-THREAD(r12) + lwz r15, TI_CPU(r12) + lis r14, __per_cpu_offset@h + ori r14, r14, __per_cpu_offset@l + rlwinm r15, r15, 2, 0, 29 + lwzx r16, r14, r15 +#else + li r16, 0 +#endif + lis r17, next_tlbcam_idx@h + ori r17, r17, next_tlbcam_idx@l + add r17, r17, r16 /* r17 = *next_tlbcam_idx */ + lwz r15, 0(r17) /* r15 = next_tlbcam_idx */ + + lis r14, MAS0_TLBSEL(1)@h /* select TLB1 (TLBCAM) */ + rlwimi r14, r15, 16, 4, 15 /* next_tlbcam_idx entry */ + mtspr SPRN_MAS0, r14 + + /* Extract TLB1CFG(NENTRY) */ + mfspr r16, SPRN_TLB1CFG + andi. r16, r16, 0xfff + + /* Update next_tlbcam_idx, wrapping when necessary */ + addi r15, r15, 1 + cmpw r15, r16 + blt 100f + lis r14, tlbcam_index@h + ori r14, r14, tlbcam_index@l + lwz r15, 0(r14) +100: stw r15, 0(r17) + + /* + * Calc MAS1_TSIZE from r10 (which has pshift encoded) + * tlb_enc = (pshift - 10). + */ + subi r15, r10, 10 + mfspr r16, SPRN_MAS1 + rlwimi r16, r15, 7, 20, 24 + mtspr SPRN_MAS1, r16 + + /* copy the pshift for use later */ + mr r14, r10 + + /* fall through */ + +#endif /* CONFIG_HUGETLB_PAGE */ + /* * We set execute, because we don't have the granularity to * properly set this at the page level (Linux problem). * Many of these bits are software only. Bits we don't set * here we (properly should) assume have the appropriate value. */ - - mfspr r12, SPRN_MAS2 -#ifdef CONFIG_PTE_64BIT - rlwimi r12, r11, 32-19, 27, 31 /* extract WIMGE from pte */ -#else - rlwimi r12, r11, 26, 27, 31 /* extract WIMGE from pte */ -#endif - mtspr SPRN_MAS2, r12 - +finish_tlb_load_cont: #ifdef CONFIG_PTE_64BIT rlwinm r12, r11, 32-2, 26, 31 /* Move in perm bits */ andi. r10, r11, _PAGE_DIRTY @@ -581,22 +650,40 @@ finish_tlb_load: andc r12, r12, r10 1: rlwimi r12, r13, 20, 0, 11 /* grab RPN[32:43] */ rlwimi r12, r11, 20, 12, 19 /* grab RPN[44:51] */ - mtspr SPRN_MAS3, r12 +2: mtspr SPRN_MAS3, r12 BEGIN_MMU_FTR_SECTION srwi r10, r13, 12 /* grab RPN[12:31] */ mtspr SPRN_MAS7, r10 END_MMU_FTR_SECTION_IFSET(MMU_FTR_BIG_PHYS) #else li r10, (_PAGE_EXEC | _PAGE_PRESENT) + mr r13, r11 rlwimi r10, r11, 31, 29, 29 /* extract _PAGE_DIRTY into SW */ and r12, r11, r10 andi. r10, r11, _PAGE_USER /* Test for _PAGE_USER */ slwi r10, r12, 1 or r10, r10, r12 iseleq r12, r12, r10 - rlwimi r11, r12, 0, 20, 31 /* Extract RPN from PTE and merge with perms */ - mtspr SPRN_MAS3, r11 + rlwimi r13, r12, 0, 20, 31 /* Get RPN from PTE, merge w/ perms */ + mtspr SPRN_MAS3, r13 #endif + + mfspr r12, SPRN_MAS2 +#ifdef CONFIG_PTE_64BIT + rlwimi r12, r11, 32-19, 27, 31 /* extract WIMGE from pte */ +#else + rlwimi r12, r11, 26, 27, 31 /* extract WIMGE from pte */ +#endif +#ifdef CONFIG_HUGETLB_PAGE + beq 6, 3f /* don't mask if page isn't huge */ + li r13, 1 + slw r13, r13, r14 + subi r13, r13, 1 + rlwinm r13, r13, 0, 0, 19 /* bottom bits used for WIMGE/etc */ + andc r12, r12, r13 /* mask off ea bits within the page */ +#endif +3: mtspr SPRN_MAS2, r12 + #ifdef CONFIG_E200 /* Round robin TLB1 entries assignment */ mfspr r12, SPRN_MAS0 @@ -622,11 +709,19 @@ END_MMU_FTR_SECTION_IFSET(MMU_FTR_BIG_PHYS) mtspr SPRN_MAS0,r12 #endif /* CONFIG_E200 */ +tlb_write_entry: tlbwe /* Done...restore registers and get out of here. */ mfspr r10, SPRN_SPRG_THREAD - lwz r11, THREAD_NORMSAVE(3)(r10) +#ifdef CONFIG_HUGETLB_PAGE + beq 6, 8f /* skip restore for 4k page faults */ + lwz r14, THREAD_NORMSAVE(4)(r10) + lwz r15, THREAD_NORMSAVE(5)(r10) + lwz r16, THREAD_NORMSAVE(6)(r10) + lwz r17, THREAD_NORMSAVE(7)(r10) +#endif +8: lwz r11, THREAD_NORMSAVE(3)(r10) mtcr r11 lwz r13, THREAD_NORMSAVE(2)(r10) lwz r12, THREAD_NORMSAVE(1)(r10) diff --git a/arch/powerpc/mm/Makefile b/arch/powerpc/mm/Makefile index bdca46e08382..991ee813d2a8 100644 --- a/arch/powerpc/mm/Makefile +++ b/arch/powerpc/mm/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_PPC_MM_SLICES) += slice.o ifeq ($(CONFIG_HUGETLB_PAGE),y) obj-y += hugetlbpage.o obj-$(CONFIG_PPC_STD_MMU_64) += hugetlbpage-hash64.o +obj-$(CONFIG_PPC_BOOK3E_MMU) += hugetlbpage-book3e.o endif obj-$(CONFIG_PPC_SUBPAGE_PROT) += subpage-prot.o obj-$(CONFIG_NOT_COHERENT_CACHE) += dma-noncoherent.o diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c index 26b2872b3d00..1f8b2a05e3d0 100644 --- a/arch/powerpc/mm/hash_utils_64.c +++ b/arch/powerpc/mm/hash_utils_64.c @@ -105,9 +105,6 @@ int mmu_kernel_ssize = MMU_SEGSIZE_256M; int mmu_highuser_ssize = MMU_SEGSIZE_256M; u16 mmu_slb_size = 64; EXPORT_SYMBOL_GPL(mmu_slb_size); -#ifdef CONFIG_HUGETLB_PAGE -unsigned int HPAGE_SHIFT; -#endif #ifdef CONFIG_PPC_64K_PAGES int mmu_ci_restrictions; #endif diff --git a/arch/powerpc/mm/hugetlbpage-book3e.c b/arch/powerpc/mm/hugetlbpage-book3e.c new file mode 100644 index 000000000000..1295b7c1cdac --- /dev/null +++ b/arch/powerpc/mm/hugetlbpage-book3e.c @@ -0,0 +1,121 @@ +/* + * PPC Huge TLB Page Support for Book3E MMU + * + * Copyright (C) 2009 David Gibson, IBM Corporation. + * Copyright (C) 2011 Becky Bruce, Freescale Semiconductor + * + */ +#include +#include + +static inline int mmu_get_tsize(int psize) +{ + return mmu_psize_defs[psize].enc; +} + +static inline int book3e_tlb_exists(unsigned long ea, unsigned long pid) +{ + int found = 0; + + mtspr(SPRN_MAS6, pid << 16); + if (mmu_has_feature(MMU_FTR_USE_TLBRSRV)) { + asm volatile( + "li %0,0\n" + "tlbsx. 0,%1\n" + "bne 1f\n" + "li %0,1\n" + "1:\n" + : "=&r"(found) : "r"(ea)); + } else { + asm volatile( + "tlbsx 0,%1\n" + "mfspr %0,0x271\n" + "srwi %0,%0,31\n" + : "=&r"(found) : "r"(ea)); + } + + return found; +} + +void book3e_hugetlb_preload(struct mm_struct *mm, unsigned long ea, pte_t pte) +{ + unsigned long mas1, mas2; + u64 mas7_3; + unsigned long psize, tsize, shift; + unsigned long flags; + +#ifdef CONFIG_PPC_FSL_BOOK3E + int index, lz, ncams; + struct vm_area_struct *vma; +#endif + + if (unlikely(is_kernel_addr(ea))) + return; + +#ifdef CONFIG_MM_SLICES + psize = mmu_get_tsize(get_slice_psize(mm, ea)); + tsize = mmu_get_psize(psize); + shift = mmu_psize_defs[psize].shift; +#else + vma = find_vma(mm, ea); + psize = vma_mmu_pagesize(vma); /* returns actual size in bytes */ + asm (PPC_CNTLZL "%0,%1" : "=r" (lz) : "r" (psize)); + shift = 31 - lz; + tsize = 21 - lz; +#endif + + /* + * We can't be interrupted while we're setting up the MAS + * regusters or after we've confirmed that no tlb exists. + */ + local_irq_save(flags); + + if (unlikely(book3e_tlb_exists(ea, mm->context.id))) { + local_irq_restore(flags); + return; + } + +#ifdef CONFIG_PPC_FSL_BOOK3E + ncams = mfspr(SPRN_TLB1CFG) & TLBnCFG_N_ENTRY; + + /* We have to use the CAM(TLB1) on FSL parts for hugepages */ + index = __get_cpu_var(next_tlbcam_idx); + mtspr(SPRN_MAS0, MAS0_ESEL(index) | MAS0_TLBSEL(1)); + + /* Just round-robin the entries and wrap when we hit the end */ + if (unlikely(index == ncams - 1)) + __get_cpu_var(next_tlbcam_idx) = tlbcam_index; + else + __get_cpu_var(next_tlbcam_idx)++; +#endif + mas1 = MAS1_VALID | MAS1_TID(mm->context.id) | MAS1_TSIZE(tsize); + mas2 = ea & ~((1UL << shift) - 1); + mas2 |= (pte_val(pte) >> PTE_WIMGE_SHIFT) & MAS2_WIMGE_MASK; + mas7_3 = (u64)pte_pfn(pte) << PAGE_SHIFT; + mas7_3 |= (pte_val(pte) >> PTE_BAP_SHIFT) & MAS3_BAP_MASK; + if (!pte_dirty(pte)) + mas7_3 &= ~(MAS3_SW|MAS3_UW); + + mtspr(SPRN_MAS1, mas1); + mtspr(SPRN_MAS2, mas2); + + if (mmu_has_feature(MMU_FTR_USE_PAIRED_MAS)) { + mtspr(SPRN_MAS7_MAS3, mas7_3); + } else { + mtspr(SPRN_MAS7, upper_32_bits(mas7_3)); + mtspr(SPRN_MAS3, lower_32_bits(mas7_3)); + } + + asm volatile ("tlbwe"); + + local_irq_restore(flags); +} + +void flush_hugetlb_page(struct vm_area_struct *vma, unsigned long vmaddr) +{ + struct hstate *hstate = hstate_file(vma->vm_file); + unsigned long tsize = huge_page_shift(hstate) - 10; + + __flush_tlb_page(vma ? vma->vm_mm : NULL, vmaddr, tsize, 0); + +} diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c index 0b9a5c1901b9..3a5f59dcbb33 100644 --- a/arch/powerpc/mm/hugetlbpage.c +++ b/arch/powerpc/mm/hugetlbpage.c @@ -1,7 +1,8 @@ /* - * PPC64 (POWER4) Huge TLB Page Support for Kernel. + * PPC Huge TLB Page Support for Kernel. * * Copyright (C) 2003 David Gibson, IBM Corporation. + * Copyright (C) 2011 Becky Bruce, Freescale Semiconductor * * Based on the IA-32 version: * Copyright (C) 2002, Rohit Seth @@ -11,24 +12,39 @@ #include #include #include +#include +#include +#include #include #include #include +#include #define PAGE_SHIFT_64K 16 #define PAGE_SHIFT_16M 24 #define PAGE_SHIFT_16G 34 -#define MAX_NUMBER_GPAGES 1024 +unsigned int HPAGE_SHIFT; -/* Tracks the 16G pages after the device tree is scanned and before the - * huge_boot_pages list is ready. */ -static unsigned long gpage_freearray[MAX_NUMBER_GPAGES]; +/* + * Tracks gpages after the device tree is scanned and before the + * huge_boot_pages list is ready. On 64-bit implementations, this is + * just used to track 16G pages and so is a single array. 32-bit + * implementations may have more than one gpage size due to limitations + * of the memory allocators, so we need multiple arrays + */ +#ifdef CONFIG_PPC64 +#define MAX_NUMBER_GPAGES 1024 +static u64 gpage_freearray[MAX_NUMBER_GPAGES]; static unsigned nr_gpages; - -/* Flag to mark huge PD pointers. This means pmd_bad() and pud_bad() - * will choke on pointers to hugepte tables, which is handy for - * catching screwups early. */ +#else +#define MAX_NUMBER_GPAGES 128 +struct psize_gpages { + u64 gpage_list[MAX_NUMBER_GPAGES]; + unsigned int nr_gpages; +}; +static struct psize_gpages gpage_freearray[MMU_PAGE_COUNT]; +#endif static inline int shift_to_mmu_psize(unsigned int shift) { @@ -49,25 +65,6 @@ static inline unsigned int mmu_psize_to_shift(unsigned int mmu_psize) #define hugepd_none(hpd) ((hpd).pd == 0) -static inline pte_t *hugepd_page(hugepd_t hpd) -{ - BUG_ON(!hugepd_ok(hpd)); - return (pte_t *)((hpd.pd & ~HUGEPD_SHIFT_MASK) | 0xc000000000000000); -} - -static inline unsigned int hugepd_shift(hugepd_t hpd) -{ - return hpd.pd & HUGEPD_SHIFT_MASK; -} - -static inline pte_t *hugepte_offset(hugepd_t *hpdp, unsigned long addr, unsigned pdshift) -{ - unsigned long idx = (addr & ((1UL << pdshift) - 1)) >> hugepd_shift(*hpdp); - pte_t *dir = hugepd_page(*hpdp); - - return dir + idx; -} - pte_t *find_linux_pte_or_hugepte(pgd_t *pgdir, unsigned long ea, unsigned *shift) { pgd_t *pg; @@ -93,7 +90,7 @@ pte_t *find_linux_pte_or_hugepte(pgd_t *pgdir, unsigned long ea, unsigned *shift if (is_hugepd(pm)) hpdp = (hugepd_t *)pm; else if (!pmd_none(*pm)) { - return pte_offset_map(pm, ea); + return pte_offset_kernel(pm, ea); } } } @@ -114,8 +111,18 @@ pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr) static int __hugepte_alloc(struct mm_struct *mm, hugepd_t *hpdp, unsigned long address, unsigned pdshift, unsigned pshift) { - pte_t *new = kmem_cache_zalloc(PGT_CACHE(pdshift - pshift), - GFP_KERNEL|__GFP_REPEAT); + struct kmem_cache *cachep; + pte_t *new; + +#ifdef CONFIG_PPC64 + cachep = PGT_CACHE(pdshift - pshift); +#else + int i; + int num_hugepd = 1 << (pshift - pdshift); + cachep = hugepte_cache; +#endif + + new = kmem_cache_zalloc(cachep, GFP_KERNEL|__GFP_REPEAT); BUG_ON(pshift > HUGEPD_SHIFT_MASK); BUG_ON((unsigned long)new & HUGEPD_SHIFT_MASK); @@ -124,10 +131,31 @@ static int __hugepte_alloc(struct mm_struct *mm, hugepd_t *hpdp, return -ENOMEM; spin_lock(&mm->page_table_lock); +#ifdef CONFIG_PPC64 if (!hugepd_none(*hpdp)) - kmem_cache_free(PGT_CACHE(pdshift - pshift), new); + kmem_cache_free(cachep, new); else - hpdp->pd = ((unsigned long)new & ~0x8000000000000000) | pshift; + hpdp->pd = ((unsigned long)new & ~PD_HUGE) | pshift; +#else + /* + * We have multiple higher-level entries that point to the same + * actual pte location. Fill in each as we go and backtrack on error. + * We need all of these so the DTLB pgtable walk code can find the + * right higher-level entry without knowing if it's a hugepage or not. + */ + for (i = 0; i < num_hugepd; i++, hpdp++) { + if (unlikely(!hugepd_none(*hpdp))) + break; + else + hpdp->pd = ((unsigned long)new & ~PD_HUGE) | pshift; + } + /* If we bailed from the for loop early, an error occurred, clean up */ + if (i < num_hugepd) { + for (i = i - 1 ; i >= 0; i--, hpdp--) + hpdp->pd = 0; + kmem_cache_free(cachep, new); + } +#endif spin_unlock(&mm->page_table_lock); return 0; } @@ -169,11 +197,132 @@ pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr, unsigned long sz return hugepte_offset(hpdp, addr, pdshift); } +#ifdef CONFIG_PPC32 /* Build list of addresses of gigantic pages. This function is used in early * boot before the buddy or bootmem allocator is setup. */ -void add_gpage(unsigned long addr, unsigned long page_size, - unsigned long number_of_pages) +void add_gpage(u64 addr, u64 page_size, unsigned long number_of_pages) +{ + unsigned int idx = shift_to_mmu_psize(__ffs(page_size)); + int i; + + if (addr == 0) + return; + + gpage_freearray[idx].nr_gpages = number_of_pages; + + for (i = 0; i < number_of_pages; i++) { + gpage_freearray[idx].gpage_list[i] = addr; + addr += page_size; + } +} + +/* + * Moves the gigantic page addresses from the temporary list to the + * huge_boot_pages list. + */ +int alloc_bootmem_huge_page(struct hstate *hstate) +{ + struct huge_bootmem_page *m; + int idx = shift_to_mmu_psize(hstate->order + PAGE_SHIFT); + int nr_gpages = gpage_freearray[idx].nr_gpages; + + if (nr_gpages == 0) + return 0; + +#ifdef CONFIG_HIGHMEM + /* + * If gpages can be in highmem we can't use the trick of storing the + * data structure in the page; allocate space for this + */ + m = alloc_bootmem(sizeof(struct huge_bootmem_page)); + m->phys = gpage_freearray[idx].gpage_list[--nr_gpages]; +#else + m = phys_to_virt(gpage_freearray[idx].gpage_list[--nr_gpages]); +#endif + + list_add(&m->list, &huge_boot_pages); + gpage_freearray[idx].nr_gpages = nr_gpages; + gpage_freearray[idx].gpage_list[nr_gpages] = 0; + m->hstate = hstate; + + return 1; +} +/* + * Scan the command line hugepagesz= options for gigantic pages; store those in + * a list that we use to allocate the memory once all options are parsed. + */ + +unsigned long gpage_npages[MMU_PAGE_COUNT]; + +static int __init do_gpage_early_setup(char *param, char *val) +{ + static phys_addr_t size; + unsigned long npages; + + /* + * The hugepagesz and hugepages cmdline options are interleaved. We + * use the size variable to keep track of whether or not this was done + * properly and skip over instances where it is incorrect. Other + * command-line parsing code will issue warnings, so we don't need to. + * + */ + if ((strcmp(param, "default_hugepagesz") == 0) || + (strcmp(param, "hugepagesz") == 0)) { + size = memparse(val, NULL); + } else if (strcmp(param, "hugepages") == 0) { + if (size != 0) { + if (sscanf(val, "%lu", &npages) <= 0) + npages = 0; + gpage_npages[shift_to_mmu_psize(__ffs(size))] = npages; + size = 0; + } + } + return 0; +} + + +/* + * This function allocates physical space for pages that are larger than the + * buddy allocator can handle. We want to allocate these in highmem because + * the amount of lowmem is limited. This means that this function MUST be + * called before lowmem_end_addr is set up in MMU_init() in order for the lmb + * allocate to grab highmem. + */ +void __init reserve_hugetlb_gpages(void) +{ + static __initdata char cmdline[COMMAND_LINE_SIZE]; + phys_addr_t size, base; + int i; + + strlcpy(cmdline, boot_command_line, COMMAND_LINE_SIZE); + parse_args("hugetlb gpages", cmdline, NULL, 0, &do_gpage_early_setup); + + /* + * Walk gpage list in reverse, allocating larger page sizes first. + * Skip over unsupported sizes, or sizes that have 0 gpages allocated. + * When we reach the point in the list where pages are no longer + * considered gpages, we're done. + */ + for (i = MMU_PAGE_COUNT-1; i >= 0; i--) { + if (mmu_psize_defs[i].shift == 0 || gpage_npages[i] == 0) + continue; + else if (mmu_psize_to_shift(i) < (MAX_ORDER + PAGE_SHIFT)) + break; + + size = (phys_addr_t)(1ULL << mmu_psize_to_shift(i)); + base = memblock_alloc_base(size * gpage_npages[i], size, + MEMBLOCK_ALLOC_ANYWHERE); + add_gpage(base, size, gpage_npages[i]); + } +} + +#else /* PPC64 */ + +/* Build list of addresses of gigantic pages. This function is used in early + * boot before the buddy or bootmem allocator is setup. + */ +void add_gpage(u64 addr, u64 page_size, unsigned long number_of_pages) { if (!addr) return; @@ -199,19 +348,79 @@ int alloc_bootmem_huge_page(struct hstate *hstate) m->hstate = hstate; return 1; } +#endif int huge_pmd_unshare(struct mm_struct *mm, unsigned long *addr, pte_t *ptep) { return 0; } +#ifdef CONFIG_PPC32 +#define HUGEPD_FREELIST_SIZE \ + ((PAGE_SIZE - sizeof(struct hugepd_freelist)) / sizeof(pte_t)) + +struct hugepd_freelist { + struct rcu_head rcu; + unsigned int index; + void *ptes[0]; +}; + +static DEFINE_PER_CPU(struct hugepd_freelist *, hugepd_freelist_cur); + +static void hugepd_free_rcu_callback(struct rcu_head *head) +{ + struct hugepd_freelist *batch = + container_of(head, struct hugepd_freelist, rcu); + unsigned int i; + + for (i = 0; i < batch->index; i++) + kmem_cache_free(hugepte_cache, batch->ptes[i]); + + free_page((unsigned long)batch); +} + +static void hugepd_free(struct mmu_gather *tlb, void *hugepte) +{ + struct hugepd_freelist **batchp; + + batchp = &__get_cpu_var(hugepd_freelist_cur); + + if (atomic_read(&tlb->mm->mm_users) < 2 || + cpumask_equal(mm_cpumask(tlb->mm), + cpumask_of(smp_processor_id()))) { + kmem_cache_free(hugepte_cache, hugepte); + return; + } + + if (*batchp == NULL) { + *batchp = (struct hugepd_freelist *)__get_free_page(GFP_ATOMIC); + (*batchp)->index = 0; + } + + (*batchp)->ptes[(*batchp)->index++] = hugepte; + if ((*batchp)->index == HUGEPD_FREELIST_SIZE) { + call_rcu_sched(&(*batchp)->rcu, hugepd_free_rcu_callback); + *batchp = NULL; + } +} +#endif + static void free_hugepd_range(struct mmu_gather *tlb, hugepd_t *hpdp, int pdshift, unsigned long start, unsigned long end, unsigned long floor, unsigned long ceiling) { pte_t *hugepte = hugepd_page(*hpdp); - unsigned shift = hugepd_shift(*hpdp); + int i; + unsigned long pdmask = ~((1UL << pdshift) - 1); + unsigned int num_hugepd = 1; + +#ifdef CONFIG_PPC64 + unsigned int shift = hugepd_shift(*hpdp); +#else + /* Note: On 32-bit the hpdp may be the first of several */ + num_hugepd = (1 << (hugepd_shift(*hpdp) - pdshift)); +#endif start &= pdmask; if (start < floor) @@ -224,9 +433,15 @@ static void free_hugepd_range(struct mmu_gather *tlb, hugepd_t *hpdp, int pdshif if (end - 1 > ceiling - 1) return; - hpdp->pd = 0; + for (i = 0; i < num_hugepd; i++, hpdp++) + hpdp->pd = 0; + tlb->need_flush = 1; +#ifdef CONFIG_PPC64 pgtable_free_tlb(tlb, hugepte, pdshift - shift); +#else + hugepd_free(tlb, hugepte); +#endif } static void hugetlb_free_pmd_range(struct mmu_gather *tlb, pud_t *pud, @@ -331,18 +546,27 @@ void hugetlb_free_pgd_range(struct mmu_gather *tlb, * too. */ - pgd = pgd_offset(tlb->mm, addr); do { next = pgd_addr_end(addr, end); + pgd = pgd_offset(tlb->mm, addr); if (!is_hugepd(pgd)) { if (pgd_none_or_clear_bad(pgd)) continue; hugetlb_free_pud_range(tlb, pgd, addr, next, floor, ceiling); } else { +#ifdef CONFIG_PPC32 + /* + * Increment next by the size of the huge mapping since + * on 32-bit there may be more than one entry at the pgd + * level for a single hugepage, but all of them point to + * the same kmem cache that holds the hugepte. + */ + next = addr + (1 << hugepd_shift(*(hugepd_t *)pgd)); +#endif free_hugepd_range(tlb, (hugepd_t *)pgd, PGDIR_SHIFT, addr, next, floor, ceiling); } - } while (pgd++, addr = next, addr != end); + } while (addr = next, addr != end); } struct page * @@ -466,17 +690,35 @@ unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags) { +#ifdef CONFIG_MM_SLICES struct hstate *hstate = hstate_file(file); int mmu_psize = shift_to_mmu_psize(huge_page_shift(hstate)); return slice_get_unmapped_area(addr, len, flags, mmu_psize, 1, 0); +#else + return get_unmapped_area(file, addr, len, pgoff, flags); +#endif } unsigned long vma_mmu_pagesize(struct vm_area_struct *vma) { +#ifdef CONFIG_MM_SLICES unsigned int psize = get_slice_psize(vma->vm_mm, vma->vm_start); return 1UL << mmu_psize_to_shift(psize); +#else + if (!is_vm_hugetlb_page(vma)) + return PAGE_SIZE; + + return huge_page_size(hstate_vma(vma)); +#endif +} + +static inline bool is_power_of_4(unsigned long x) +{ + if (is_power_of_2(x)) + return (__ilog2(x) % 2) ? false : true; + return false; } static int __init add_huge_page_size(unsigned long long size) @@ -486,9 +728,14 @@ static int __init add_huge_page_size(unsigned long long size) /* Check that it is a page size supported by the hardware and * that it fits within pagetable and slice limits. */ +#ifdef CONFIG_PPC_FSL_BOOK3E + if ((size < PAGE_SIZE) || !is_power_of_4(size)) + return -EINVAL; +#else if (!is_power_of_2(size) || (shift > SLICE_HIGH_SHIFT) || (shift <= PAGE_SHIFT)) return -EINVAL; +#endif if ((mmu_psize = shift_to_mmu_psize(shift)) < 0) return -EINVAL; @@ -525,6 +772,46 @@ static int __init hugepage_setup_sz(char *str) } __setup("hugepagesz=", hugepage_setup_sz); +#ifdef CONFIG_FSL_BOOKE +struct kmem_cache *hugepte_cache; +static int __init hugetlbpage_init(void) +{ + int psize; + + for (psize = 0; psize < MMU_PAGE_COUNT; ++psize) { + unsigned shift; + + if (!mmu_psize_defs[psize].shift) + continue; + + shift = mmu_psize_to_shift(psize); + + /* Don't treat normal page sizes as huge... */ + if (shift != PAGE_SHIFT) + if (add_huge_page_size(1ULL << shift) < 0) + continue; + } + + /* + * Create a kmem cache for hugeptes. The bottom bits in the pte have + * size information encoded in them, so align them to allow this + */ + hugepte_cache = kmem_cache_create("hugepte-cache", sizeof(pte_t), + HUGEPD_SHIFT_MASK + 1, 0, NULL); + if (hugepte_cache == NULL) + panic("%s: Unable to create kmem cache for hugeptes\n", + __func__); + + /* Default hpage size = 4M */ + if (mmu_psize_defs[MMU_PAGE_4M].shift) + HPAGE_SHIFT = mmu_psize_defs[MMU_PAGE_4M].shift; + else + panic("%s: Unable to set default huge page size\n", __func__); + + + return 0; +} +#else static int __init hugetlbpage_init(void) { int psize; @@ -567,15 +854,23 @@ static int __init hugetlbpage_init(void) return 0; } - +#endif module_init(hugetlbpage_init); void flush_dcache_icache_hugepage(struct page *page) { int i; + void *start; BUG_ON(!PageCompound(page)); - for (i = 0; i < (1UL << compound_order(page)); i++) - __flush_dcache_icache(page_address(page+i)); + for (i = 0; i < (1UL << compound_order(page)); i++) { + if (!PageHighMem(page)) { + __flush_dcache_icache(page_address(page+i)); + } else { + start = kmap_atomic(page+i, KM_PPC_SYNC_ICACHE); + __flush_dcache_icache(start); + kunmap_atomic(start, KM_PPC_SYNC_ICACHE); + } + } } diff --git a/arch/powerpc/mm/init_32.c b/arch/powerpc/mm/init_32.c index c77fef56dad6..161cefde5c15 100644 --- a/arch/powerpc/mm/init_32.c +++ b/arch/powerpc/mm/init_32.c @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include #include @@ -44,6 +46,7 @@ #include #include #include +#include #include "mmu_decl.h" @@ -123,6 +126,12 @@ void __init MMU_init(void) /* parse args from command line */ MMU_setup(); + /* + * Reserve gigantic pages for hugetlb. This MUST occur before + * lowmem_end_addr is initialized below. + */ + reserve_hugetlb_gpages(); + if (memblock.memory.cnt > 1) { #ifndef CONFIG_WII memblock.memory.cnt = 1; diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index c781bbcf7338..ad9cf49dfb89 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -548,4 +548,9 @@ void update_mmu_cache(struct vm_area_struct *vma, unsigned long address, return; hash_preload(vma->vm_mm, address, access, trap); #endif /* CONFIG_PPC_STD_MMU */ +#if (defined(CONFIG_PPC_BOOK3E_64) || defined(CONFIG_PPC_FSL_BOOK3E)) \ + && defined(CONFIG_HUGETLB_PAGE) + if (is_vm_hugetlb_page(vma)) + book3e_hugetlb_preload(vma->vm_mm, address, *ptep); +#endif } diff --git a/arch/powerpc/mm/mmu_context_nohash.c b/arch/powerpc/mm/mmu_context_nohash.c index 336807de550e..5b63bd3da4a9 100644 --- a/arch/powerpc/mm/mmu_context_nohash.c +++ b/arch/powerpc/mm/mmu_context_nohash.c @@ -292,6 +292,11 @@ int init_new_context(struct task_struct *t, struct mm_struct *mm) mm->context.id = MMU_NO_CONTEXT; mm->context.active = 0; +#ifdef CONFIG_PPC_MM_SLICES + if (slice_mm_new_context(mm)) + slice_set_user_psize(mm, mmu_virtual_psize); +#endif + return 0; } diff --git a/arch/powerpc/mm/pgtable.c b/arch/powerpc/mm/pgtable.c index af40c8768a78..214130a4edc6 100644 --- a/arch/powerpc/mm/pgtable.c +++ b/arch/powerpc/mm/pgtable.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -212,7 +213,7 @@ int ptep_set_access_flags(struct vm_area_struct *vma, unsigned long address, entry = set_access_flags_filter(entry, vma, dirty); changed = !pte_same(*(ptep), entry); if (changed) { - if (!(vma->vm_flags & VM_HUGETLB)) + if (!is_vm_hugetlb_page(vma)) assert_pte_locked(vma->vm_mm, address); __ptep_set_access_flags(ptep, entry); flush_tlb_page_nohash(vma, address); diff --git a/arch/powerpc/mm/tlb_low_64e.S b/arch/powerpc/mm/tlb_low_64e.S index 4ebb34bc01d6..dc4a5f385e41 100644 --- a/arch/powerpc/mm/tlb_low_64e.S +++ b/arch/powerpc/mm/tlb_low_64e.S @@ -553,24 +553,24 @@ END_MMU_FTR_SECTION_IFSET(MMU_FTR_USE_TLBRSRV) rldicl r11,r16,64-VPTE_PGD_SHIFT,64-PGD_INDEX_SIZE-3 clrrdi r10,r11,3 ldx r15,r10,r15 - cmpldi cr0,r15,0 - beq virt_page_table_tlb_miss_fault + cmpdi cr0,r15,0 + bge virt_page_table_tlb_miss_fault #ifndef CONFIG_PPC_64K_PAGES /* Get to PUD entry */ rldicl r11,r16,64-VPTE_PUD_SHIFT,64-PUD_INDEX_SIZE-3 clrrdi r10,r11,3 ldx r15,r10,r15 - cmpldi cr0,r15,0 - beq virt_page_table_tlb_miss_fault + cmpdi cr0,r15,0 + bge virt_page_table_tlb_miss_fault #endif /* CONFIG_PPC_64K_PAGES */ /* Get to PMD entry */ rldicl r11,r16,64-VPTE_PMD_SHIFT,64-PMD_INDEX_SIZE-3 clrrdi r10,r11,3 ldx r15,r10,r15 - cmpldi cr0,r15,0 - beq virt_page_table_tlb_miss_fault + cmpdi cr0,r15,0 + bge virt_page_table_tlb_miss_fault /* Ok, we're all right, we can now create a kernel translation for * a 4K or 64K page from r16 -> r15. @@ -802,24 +802,24 @@ htw_tlb_miss: rldicl r11,r16,64-(PGDIR_SHIFT-3),64-PGD_INDEX_SIZE-3 clrrdi r10,r11,3 ldx r15,r10,r15 - cmpldi cr0,r15,0 - beq htw_tlb_miss_fault + cmpdi cr0,r15,0 + bge htw_tlb_miss_fault #ifndef CONFIG_PPC_64K_PAGES /* Get to PUD entry */ rldicl r11,r16,64-(PUD_SHIFT-3),64-PUD_INDEX_SIZE-3 clrrdi r10,r11,3 ldx r15,r10,r15 - cmpldi cr0,r15,0 - beq htw_tlb_miss_fault + cmpdi cr0,r15,0 + bge htw_tlb_miss_fault #endif /* CONFIG_PPC_64K_PAGES */ /* Get to PMD entry */ rldicl r11,r16,64-(PMD_SHIFT-3),64-PMD_INDEX_SIZE-3 clrrdi r10,r11,3 ldx r15,r10,r15 - cmpldi cr0,r15,0 - beq htw_tlb_miss_fault + cmpdi cr0,r15,0 + bge htw_tlb_miss_fault /* Ok, we're all right, we can now create an indirect entry for * a 1M or 256M page. diff --git a/arch/powerpc/mm/tlb_nohash.c b/arch/powerpc/mm/tlb_nohash.c index d32ec643c231..afc95c7304ae 100644 --- a/arch/powerpc/mm/tlb_nohash.c +++ b/arch/powerpc/mm/tlb_nohash.c @@ -36,14 +36,49 @@ #include #include #include +#include #include #include #include +#include #include "mmu_decl.h" -#ifdef CONFIG_PPC_BOOK3E +/* + * This struct lists the sw-supported page sizes. The hardawre MMU may support + * other sizes not listed here. The .ind field is only used on MMUs that have + * indirect page table entries. + */ +#ifdef CONFIG_PPC_BOOK3E_MMU +#ifdef CONFIG_FSL_BOOKE +struct mmu_psize_def mmu_psize_defs[MMU_PAGE_COUNT] = { + [MMU_PAGE_4K] = { + .shift = 12, + .enc = BOOK3E_PAGESZ_4K, + }, + [MMU_PAGE_4M] = { + .shift = 22, + .enc = BOOK3E_PAGESZ_4M, + }, + [MMU_PAGE_16M] = { + .shift = 24, + .enc = BOOK3E_PAGESZ_16M, + }, + [MMU_PAGE_64M] = { + .shift = 26, + .enc = BOOK3E_PAGESZ_64M, + }, + [MMU_PAGE_256M] = { + .shift = 28, + .enc = BOOK3E_PAGESZ_256M, + }, + [MMU_PAGE_1G] = { + .shift = 30, + .enc = BOOK3E_PAGESZ_1GB, + }, +}; +#else struct mmu_psize_def mmu_psize_defs[MMU_PAGE_COUNT] = { [MMU_PAGE_4K] = { .shift = 12, @@ -77,6 +112,8 @@ struct mmu_psize_def mmu_psize_defs[MMU_PAGE_COUNT] = { .enc = BOOK3E_PAGESZ_1GB, }, }; +#endif /* CONFIG_FSL_BOOKE */ + static inline int mmu_get_tsize(int psize) { return mmu_psize_defs[psize].enc; @@ -87,7 +124,7 @@ static inline int mmu_get_tsize(int psize) /* This isn't used on !Book3E for now */ return 0; } -#endif +#endif /* CONFIG_PPC_BOOK3E_MMU */ /* The variables below are currently only used on 64-bit Book3E * though this will probably be made common with other nohash @@ -266,6 +303,11 @@ void __flush_tlb_page(struct mm_struct *mm, unsigned long vmaddr, void flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr) { +#ifdef CONFIG_HUGETLB_PAGE + if (is_vm_hugetlb_page(vma)) + flush_hugetlb_page(vma, vmaddr); +#endif + __flush_tlb_page(vma ? vma->vm_mm : NULL, vmaddr, mmu_get_tsize(mmu_virtual_psize), 0); } diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype index e06e39589a09..a85990c886e9 100644 --- a/arch/powerpc/platforms/Kconfig.cputype +++ b/arch/powerpc/platforms/Kconfig.cputype @@ -69,6 +69,7 @@ config PPC_BOOK3S_64 bool "Server processors" select PPC_FPU select PPC_HAVE_PMU_SUPPORT + select SYS_SUPPORTS_HUGETLBFS config PPC_BOOK3E_64 bool "Embedded processors" @@ -173,6 +174,7 @@ config BOOKE config FSL_BOOKE bool depends on (E200 || E500) && PPC32 + select SYS_SUPPORTS_HUGETLBFS if PHYS_64BIT default y # this is for common code between PPC32 & PPC64 FSL BOOKE @@ -296,7 +298,7 @@ config PPC_BOOK3E_MMU config PPC_MM_SLICES bool - default y if HUGETLB_PAGE || (PPC_STD_MMU_64 && PPC_64K_PAGES) + default y if (PPC64 && HUGETLB_PAGE) || (PPC_STD_MMU_64 && PPC_64K_PAGES) default n config VIRT_CPU_ACCOUNTING From 14b9247019432fc25e606b78262eb16a4a33b8ed Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Fri, 8 Jul 2011 11:12:42 +0000 Subject: [PATCH 0181/1531] powerpc/mpic: Add support for discontiguous cores There is one place in the MPIC driver that assumes that the cores are numbered from 0 to n-1. However, this is not true if the CPUs are not numbered sequentially. This can happen on a eight-core SOC where cores two and three are removed in the device tree. So instead of blindly looping, we iterate over the discovered CPUs and use the SMP ID as the index. This means that we no longer ask the MPIC how many CPUs there are, so we also delete mpic->num_cpus. We also catch if the number of CPUs in the SOC exceeds the number that the MPIC supports. This should never happen, of course, but it's good to be sure. Signed-off-by: Timur Tabi Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/mpic.h | 2 -- arch/powerpc/sysdev/mpic.c | 24 +++++++++++++++--------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/arch/powerpc/include/asm/mpic.h b/arch/powerpc/include/asm/mpic.h index df18989e78d4..e6fae49e0b74 100644 --- a/arch/powerpc/include/asm/mpic.h +++ b/arch/powerpc/include/asm/mpic.h @@ -273,8 +273,6 @@ struct mpic unsigned int irq_count; /* Number of sources */ unsigned int num_sources; - /* Number of CPUs */ - unsigned int num_cpus; /* default senses array */ unsigned char *senses; unsigned int senses_count; diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index d5d3ff3d757e..9678081dc4e2 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -1285,13 +1285,11 @@ struct mpic * __init mpic_alloc(struct device_node *node, mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) | MPIC_GREG_GCONF_MCK); - /* Read feature register, calculate num CPUs and, for non-ISU - * MPICs, num sources as well. On ISU MPICs, sources are counted - * as ISUs are added + /* + * Read feature register. For non-ISU MPICs, num sources as well. On + * ISU MPICs, sources are counted as ISUs are added */ greg_feature = mpic_read(mpic->gregs, MPIC_INFO(GREG_FEATURE_0)); - mpic->num_cpus = ((greg_feature & MPIC_GREG_FEATURE_LAST_CPU_MASK) - >> MPIC_GREG_FEATURE_LAST_CPU_SHIFT) + 1; if (isu_size == 0) { if (flags & MPIC_BROKEN_FRR_NIRQS) mpic->num_sources = mpic->irq_count; @@ -1301,10 +1299,18 @@ struct mpic * __init mpic_alloc(struct device_node *node, >> MPIC_GREG_FEATURE_LAST_SRC_SHIFT) + 1; } + /* + * The MPIC driver will crash if there are more cores than we + * can initialize, so we may as well catch that problem here. + */ + BUG_ON(num_possible_cpus() > MPIC_MAX_CPUS); + /* Map the per-CPU registers */ - for (i = 0; i < mpic->num_cpus; i++) { - mpic_map(mpic, node, paddr, &mpic->cpuregs[i], - MPIC_INFO(CPU_BASE) + i * MPIC_INFO(CPU_STRIDE), + for_each_possible_cpu(i) { + unsigned int cpu = get_hard_smp_processor_id(i); + + mpic_map(mpic, node, paddr, &mpic->cpuregs[cpu], + MPIC_INFO(CPU_BASE) + cpu * MPIC_INFO(CPU_STRIDE), 0x1000); } @@ -1343,7 +1349,7 @@ struct mpic * __init mpic_alloc(struct device_node *node, } printk(KERN_INFO "mpic: Setting up MPIC \"%s\" version %s at %llx," " max %d CPUs\n", - name, vers, (unsigned long long)paddr, mpic->num_cpus); + name, vers, (unsigned long long)paddr, num_possible_cpus()); printk(KERN_INFO "mpic: ISU size: %d, shift: %d, mask: %x\n", mpic->isu_size, mpic->isu_shift, mpic->isu_mask); From 7392769365f32c82340f184f93408b12dc3da4dc Mon Sep 17 00:00:00 2001 From: Matthew McClintock Date: Tue, 19 Jul 2011 06:22:44 +0000 Subject: [PATCH 0182/1531] powerpc: Fix build dependencies for epapr.c which needs libfdt.h Currently, the build can (very rarely) fail to build because libfdt.h has not been created or is in the process of being copied. Signed-off-by: Matthew McClintock Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/boot/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index c26200b40a47..ac6705e75711 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -58,7 +58,7 @@ $(addprefix $(obj)/,$(zlib) cuboot-c2k.o gunzip_util.o main.o prpmc2800.o): \ libfdt := fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c libfdtheader := fdt.h libfdt.h libfdt_internal.h -$(addprefix $(obj)/,$(libfdt) libfdt-wrapper.o simpleboot.o): \ +$(addprefix $(obj)/,$(libfdt) libfdt-wrapper.o simpleboot.o epapr.o): \ $(addprefix $(obj)/,$(libfdtheader)) src-wlib := string.S crt0.S crtsavres.S stdio.c main.c \ From 6c493685f1b209dd4ae41eb52c818cf12da20def Mon Sep 17 00:00:00 2001 From: Jim Keniston Date: Mon, 25 Jul 2011 07:54:50 +0000 Subject: [PATCH 0183/1531] powerpc/nvram: Add compression to fit more oops output into NVRAM Capture more than twice as much text from the printk buffer, and compress it to fit it in the lnx,oops-log NVRAM partition. You can view the compressed text using the new (as of July 20) --unzip option of the nvram command in the powerpc-utils package. [BenH: Added select of ZLIB_DEFLATE] Signed-off-by: Jim Keniston Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/rtas.h | 6 +- arch/powerpc/platforms/pseries/Kconfig | 1 + arch/powerpc/platforms/pseries/nvram.c | 171 ++++++++++++++++++++++++- 3 files changed, 169 insertions(+), 9 deletions(-) diff --git a/arch/powerpc/include/asm/rtas.h b/arch/powerpc/include/asm/rtas.h index 58625d1e7802..41f69ae79d4e 100644 --- a/arch/powerpc/include/asm/rtas.h +++ b/arch/powerpc/include/asm/rtas.h @@ -249,10 +249,12 @@ extern void pSeries_log_error(char *buf, unsigned int err_type, int fatal); #define ERR_FLAG_ALREADY_LOGGED 0x0 #define ERR_FLAG_BOOT 0x1 /* log was pulled from NVRAM on boot */ #define ERR_TYPE_RTAS_LOG 0x2 /* from rtas event-scan */ -#define ERR_TYPE_KERNEL_PANIC 0x4 /* from panic() */ +#define ERR_TYPE_KERNEL_PANIC 0x4 /* from die()/panic() */ +#define ERR_TYPE_KERNEL_PANIC_GZ 0x8 /* ditto, compressed */ /* All the types and not flags */ -#define ERR_TYPE_MASK (ERR_TYPE_RTAS_LOG | ERR_TYPE_KERNEL_PANIC) +#define ERR_TYPE_MASK \ + (ERR_TYPE_RTAS_LOG | ERR_TYPE_KERNEL_PANIC | ERR_TYPE_KERNEL_PANIC_GZ) #define RTAS_DEBUG KERN_DEBUG "RTAS: " diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig index 05cf4769b88c..c81f6bb9c10f 100644 --- a/arch/powerpc/platforms/pseries/Kconfig +++ b/arch/powerpc/platforms/pseries/Kconfig @@ -15,6 +15,7 @@ config PPC_PSERIES select PPC_UDBG_16550 select PPC_NATIVE select PPC_PCI_CHOICE if EXPERT + select ZLIB_DEFLATE default y config PPC_SPLPAR diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c index 00cc3a094885..a76b22844d18 100644 --- a/arch/powerpc/platforms/pseries/nvram.c +++ b/arch/powerpc/platforms/pseries/nvram.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include #include @@ -78,8 +80,41 @@ static struct kmsg_dumper nvram_kmsg_dumper = { #define NVRAM_RTAS_READ_TIMEOUT 5 /* seconds */ static unsigned long last_unread_rtas_event; /* timestamp */ -/* We preallocate oops_buf during init to avoid kmalloc during oops/panic. */ -static char *oops_buf; +/* + * For capturing and compressing an oops or panic report... + + * big_oops_buf[] holds the uncompressed text we're capturing. + * + * oops_buf[] holds the compressed text, preceded by a prefix. + * The prefix is just a u16 holding the length of the compressed* text. + * (*Or uncompressed, if compression fails.) oops_buf[] gets written + * to NVRAM. + * + * oops_len points to the prefix. oops_data points to the compressed text. + * + * +- oops_buf + * | +- oops_data + * v v + * +------------+-----------------------------------------------+ + * | length | text | + * | (2 bytes) | (oops_data_sz bytes) | + * +------------+-----------------------------------------------+ + * ^ + * +- oops_len + * + * We preallocate these buffers during init to avoid kmalloc during oops/panic. + */ +static size_t big_oops_buf_sz; +static char *big_oops_buf, *oops_buf; +static u16 *oops_len; +static char *oops_data; +static size_t oops_data_sz; + +/* Compression parameters */ +#define COMPR_LEVEL 6 +#define WINDOW_BITS 12 +#define MEM_LEVEL 4 +static struct z_stream_s stream; static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index) { @@ -387,11 +422,44 @@ static void __init nvram_init_oops_partition(int rtas_partition_exists) sizeof(rtas_log_partition)); } oops_buf = kmalloc(oops_log_partition.size, GFP_KERNEL); + if (!oops_buf) { + pr_err("nvram: No memory for %s partition\n", + oops_log_partition.name); + return; + } + oops_len = (u16*) oops_buf; + oops_data = oops_buf + sizeof(u16); + oops_data_sz = oops_log_partition.size - sizeof(u16); + + /* + * Figure compression (preceded by elimination of each line's + * severity prefix) will reduce the oops/panic report to at most + * 45% of its original size. + */ + big_oops_buf_sz = (oops_data_sz * 100) / 45; + big_oops_buf = kmalloc(big_oops_buf_sz, GFP_KERNEL); + if (big_oops_buf) { + stream.workspace = kmalloc(zlib_deflate_workspacesize( + WINDOW_BITS, MEM_LEVEL), GFP_KERNEL); + if (!stream.workspace) { + pr_err("nvram: No memory for compression workspace; " + "skipping compression of %s partition data\n", + oops_log_partition.name); + kfree(big_oops_buf); + big_oops_buf = NULL; + } + } else { + pr_err("No memory for uncompressed %s data; " + "skipping compression\n", oops_log_partition.name); + stream.workspace = NULL; + } + rc = kmsg_dump_register(&nvram_kmsg_dumper); if (rc != 0) { pr_err("nvram: kmsg_dump_register() failed; returned %d\n", rc); kfree(oops_buf); - return; + kfree(big_oops_buf); + kfree(stream.workspace); } } @@ -473,7 +541,83 @@ static int clobbering_unread_rtas_event(void) NVRAM_RTAS_READ_TIMEOUT); } -/* our kmsg_dump callback */ +/* Squeeze out each line's severity prefix. */ +static size_t elide_severities(char *buf, size_t len) +{ + char *in, *out, *buf_end = buf + len; + /* Assume a at the very beginning marks the start of a line. */ + int newline = 1; + + in = out = buf; + while (in < buf_end) { + if (newline && in+3 <= buf_end && + *in == '<' && isdigit(in[1]) && in[2] == '>') { + in += 3; + newline = 0; + } else { + newline = (*in == '\n'); + *out++ = *in++; + } + } + return out - buf; +} + +/* Derived from logfs_compress() */ +static int nvram_compress(const void *in, void *out, size_t inlen, + size_t outlen) +{ + int err, ret; + + ret = -EIO; + err = zlib_deflateInit2(&stream, COMPR_LEVEL, Z_DEFLATED, WINDOW_BITS, + MEM_LEVEL, Z_DEFAULT_STRATEGY); + if (err != Z_OK) + goto error; + + stream.next_in = in; + stream.avail_in = inlen; + stream.total_in = 0; + stream.next_out = out; + stream.avail_out = outlen; + stream.total_out = 0; + + err = zlib_deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) + goto error; + + err = zlib_deflateEnd(&stream); + if (err != Z_OK) + goto error; + + if (stream.total_out >= stream.total_in) + goto error; + + ret = stream.total_out; +error: + return ret; +} + +/* Compress the text from big_oops_buf into oops_buf. */ +static int zip_oops(size_t text_len) +{ + int zipped_len = nvram_compress(big_oops_buf, oops_data, text_len, + oops_data_sz); + if (zipped_len < 0) { + pr_err("nvram: compression failed; returned %d\n", zipped_len); + pr_err("nvram: logging uncompressed oops/panic report\n"); + return -1; + } + *oops_len = (u16) zipped_len; + return 0; +} + +/* + * This is our kmsg_dump callback, called after an oops or panic report + * has been written to the printk buffer. We want to capture as much + * of the printk buffer as possible. First, capture as much as we can + * that we think will compress sufficiently to fit in the lnx,oops-log + * partition. If that's too much, go back and capture uncompressed text. + */ static void oops_to_nvram(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason, const char *old_msgs, unsigned long old_len, @@ -482,6 +626,8 @@ static void oops_to_nvram(struct kmsg_dumper *dumper, static unsigned int oops_count = 0; static bool panicking = false; size_t text_len; + unsigned int err_type = ERR_TYPE_KERNEL_PANIC_GZ; + int rc = -1; switch (reason) { case KMSG_DUMP_RESTART: @@ -509,8 +655,19 @@ static void oops_to_nvram(struct kmsg_dumper *dumper, if (clobbering_unread_rtas_event()) return; - text_len = capture_last_msgs(old_msgs, old_len, new_msgs, new_len, - oops_buf, oops_log_partition.size); + if (big_oops_buf) { + text_len = capture_last_msgs(old_msgs, old_len, + new_msgs, new_len, big_oops_buf, big_oops_buf_sz); + text_len = elide_severities(big_oops_buf, text_len); + rc = zip_oops(text_len); + } + if (rc != 0) { + text_len = capture_last_msgs(old_msgs, old_len, + new_msgs, new_len, oops_data, oops_data_sz); + err_type = ERR_TYPE_KERNEL_PANIC; + *oops_len = (u16) text_len; + } + (void) nvram_write_os_partition(&oops_log_partition, oops_buf, - (int) text_len, ERR_TYPE_KERNEL_PANIC, ++oops_count); + (int) (sizeof(*oops_len) + *oops_len), err_type, ++oops_count); } From 6dece0eb69b2a28e18d104bc5d707f1cb673f5e0 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Mon, 25 Jul 2011 11:29:33 +0000 Subject: [PATCH 0184/1531] powerpc/32: Pass device tree address as u64 to machine_init u64 is used rather than phys_addr_t to keep things simple, as this is called from assembly code. Update callers to pass a 64-bit address in r3/r4. Other unused register assignments that were once parameters to machine_init are dropped. For FSL BookE, look up the physical address of the device tree from the effective address passed in r3 by the loader. This is required for situations where memory does not start at zero (due to AMP or IOMMU-less virtualization), and thus the IMA doesn't start at zero, and thus the device tree effective address does not equal the physical address. Signed-off-by: Scott Wood Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/head_32.S | 7 ++--- arch/powerpc/kernel/head_40x.S | 15 ++-------- arch/powerpc/kernel/head_44x.S | 16 ++--------- arch/powerpc/kernel/head_8xx.S | 13 ++------- arch/powerpc/kernel/head_fsl_booke.S | 42 +++++++++++++++++----------- arch/powerpc/kernel/setup_32.c | 2 +- 6 files changed, 39 insertions(+), 56 deletions(-) diff --git a/arch/powerpc/kernel/head_32.S b/arch/powerpc/kernel/head_32.S index ba250d505e07..0654dba2c1f1 100644 --- a/arch/powerpc/kernel/head_32.S +++ b/arch/powerpc/kernel/head_32.S @@ -139,8 +139,7 @@ __start: trap #endif /* CONFIG_PPC_PMAC */ -1: mr r31,r3 /* save parameters */ - mr r30,r4 +1: mr r31,r3 /* save device tree ptr */ li r24,0 /* cpu # */ /* @@ -964,8 +963,8 @@ start_here: * Do early platform-specific initialization, * and set up the MMU. */ - mr r3,r31 - mr r4,r30 + li r3,0 + mr r4,r31 bl machine_init bl __save_cpu_setup bl MMU_init diff --git a/arch/powerpc/kernel/head_40x.S b/arch/powerpc/kernel/head_40x.S index a91626d87fc9..872a6af83bad 100644 --- a/arch/powerpc/kernel/head_40x.S +++ b/arch/powerpc/kernel/head_40x.S @@ -58,13 +58,7 @@ _ENTRY(_stext); _ENTRY(_start); - /* Save parameters we are passed. - */ - mr r31,r3 - mr r30,r4 - mr r29,r5 - mr r28,r6 - mr r27,r7 + mr r31,r3 /* save device tree ptr */ /* We have to turn on the MMU right away so we get cache modes * set correctly. @@ -849,11 +843,8 @@ start_here: /* * Decide what sort of machine this is and initialize the MMU. */ - mr r3,r31 - mr r4,r30 - mr r5,r29 - mr r6,r28 - mr r7,r27 + li r3,0 + mr r4,r31 bl machine_init bl MMU_init diff --git a/arch/powerpc/kernel/head_44x.S b/arch/powerpc/kernel/head_44x.S index f8e971ba94f5..b725dab0f88a 100644 --- a/arch/powerpc/kernel/head_44x.S +++ b/arch/powerpc/kernel/head_44x.S @@ -61,14 +61,7 @@ _ENTRY(_start); * of abatron_pteptrs */ nop -/* - * Save parameters we are passed - */ - mr r31,r3 - mr r30,r4 - mr r29,r5 - mr r28,r6 - mr r27,r7 + mr r31,r3 /* save device tree ptr */ li r24,0 /* CPU number */ bl init_cpu_state @@ -120,11 +113,8 @@ _ENTRY(_start); /* * Decide what sort of machine this is and initialize the MMU. */ - mr r3,r31 - mr r4,r30 - mr r5,r29 - mr r6,r28 - mr r7,r27 + li r3,0 + mr r4,r31 bl machine_init bl MMU_init diff --git a/arch/powerpc/kernel/head_8xx.S b/arch/powerpc/kernel/head_8xx.S index 1cbf64e6b416..b68cb173ba2c 100644 --- a/arch/powerpc/kernel/head_8xx.S +++ b/arch/powerpc/kernel/head_8xx.S @@ -76,11 +76,7 @@ _ENTRY(_start); */ .globl __start __start: - mr r31,r3 /* save parameters */ - mr r30,r4 - mr r29,r5 - mr r28,r6 - mr r27,r7 + mr r31,r3 /* save device tree ptr */ /* We have to turn on the MMU right away so we get cache modes * set correctly. @@ -723,11 +719,8 @@ start_here: /* * Decide what sort of machine this is and initialize the MMU. */ - mr r3,r31 - mr r4,r30 - mr r5,r29 - mr r6,r28 - mr r7,r27 + li r3,0 + mr r4,r31 bl machine_init bl MMU_init diff --git a/arch/powerpc/kernel/head_fsl_booke.S b/arch/powerpc/kernel/head_fsl_booke.S index 4ea9bfbf67e9..e1c699f3b7a7 100644 --- a/arch/powerpc/kernel/head_fsl_booke.S +++ b/arch/powerpc/kernel/head_fsl_booke.S @@ -63,17 +63,30 @@ _ENTRY(_start); * of abatron_pteptrs */ nop -/* - * Save parameters we are passed - */ - mr r31,r3 - mr r30,r4 - mr r29,r5 - mr r28,r6 - mr r27,r7 - li r25,0 /* phys kernel start (low) */ - li r24,0 /* CPU number */ - li r23,0 /* phys kernel start (high) */ + + /* Translate device tree address to physical, save in r30/r31 */ + mfmsr r16 + mfspr r17,SPRN_PID + rlwinm r17,r17,16,0x3fff0000 /* turn PID into MAS6[SPID] */ + rlwimi r17,r16,28,0x00000001 /* turn MSR[DS] into MAS6[SAS] */ + mtspr SPRN_MAS6,r17 + + tlbsx 0,r3 /* must succeed */ + + mfspr r16,SPRN_MAS1 + mfspr r20,SPRN_MAS3 + rlwinm r17,r16,25,0x1f /* r17 = log2(page size) */ + li r18,1024 + slw r18,r18,r17 /* r18 = page size */ + addi r18,r18,-1 + and r19,r3,r18 /* r19 = page offset */ + andc r31,r20,r18 /* r3 = page base */ + or r31,r31,r19 /* r3 = devtree phys addr */ + mfspr r30,SPRN_MAS7 + + li r25,0 /* phys kernel start (low) */ + li r24,0 /* CPU number */ + li r23,0 /* phys kernel start (high) */ /* We try to not make any assumptions about how the boot loader * setup or used the TLBs. We invalidate all mappings from the @@ -198,11 +211,8 @@ _ENTRY(__early_start) /* * Decide what sort of machine this is and initialize the MMU. */ - mr r3,r31 - mr r4,r30 - mr r5,r29 - mr r6,r28 - mr r7,r27 + mr r3,r30 + mr r4,r31 bl machine_init bl MMU_init diff --git a/arch/powerpc/kernel/setup_32.c b/arch/powerpc/kernel/setup_32.c index 209135af0a40..c1ce86357ecb 100644 --- a/arch/powerpc/kernel/setup_32.c +++ b/arch/powerpc/kernel/setup_32.c @@ -117,7 +117,7 @@ notrace unsigned long __init early_init(unsigned long dt_ptr) * This is called very early on the boot process, after a minimal * MMU environment has been set up but before MMU_init is called. */ -notrace void __init machine_init(unsigned long dt_ptr) +notrace void __init machine_init(u64 dt_ptr) { lockdep_init(); From 7a19081fc2658157a1b84e6f3288450c33d98569 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 8 Aug 2011 01:18:00 +0000 Subject: [PATCH 0185/1531] pseries/iommu: Add missing kfree At this point, window has not been stored anywhere, so it has to be freed before leaving the function. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @exists@ local idexpression x; statement S,S1; expression E; identifier fl; expression *ptr != NULL; @@ x = \(kmalloc\|kzalloc\|kcalloc\)(...); ... if (x == NULL) S <... when != x when != if (...) { <+...kfree(x)...+> } when any when != true x == NULL x->fl ...> ( if (x == NULL) S1 | if (...) { ... when != x when forall ( return \(0\|<+...x...+>\|ptr\); | * return ...; ) } ) // Signed-off-by: Julia Lawall Acked-by: Nishanth Aravamudan Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/iommu.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index 9f121a37eb51..5905a3b9f7e6 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -939,14 +939,14 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn) if (ret) { dev_info(&dev->dev, "failed to map direct window for %s: %d\n", dn->full_name, ret); - goto out_clear_window; + goto out_free_window; } ret = prom_add_property(pdn, win64); if (ret) { dev_err(&dev->dev, "unable to add dma window property for %s: %d", pdn->full_name, ret); - goto out_clear_window; + goto out_free_window; } window->device = pdn; @@ -958,6 +958,9 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn) dma_addr = of_read_number(&create.addr_hi, 2); goto out_unlock; +out_free_window: + kfree(window); + out_clear_window: remove_ddw(pdn); From 2fa3d9e5c699ff49e96199eeb9bea4508e7f5dac Mon Sep 17 00:00:00 2001 From: Jimi Xenidis Date: Mon, 8 Aug 2011 11:33:16 +0000 Subject: [PATCH 0186/1531] powerpc/wsp: Fix Wire Speed Processor platform configs Some config selections were applied to the platform (reference board) when they actuall apply to the chip. Signed-off-by: Jimi Xenidis Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/wsp/Kconfig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/platforms/wsp/Kconfig b/arch/powerpc/platforms/wsp/Kconfig index c3c48eb62cc1..d051581e9ebf 100644 --- a/arch/powerpc/platforms/wsp/Kconfig +++ b/arch/powerpc/platforms/wsp/Kconfig @@ -1,5 +1,9 @@ config PPC_WSP bool + select PPC_A2 + select PPC_SCOM + select PPC_XICS + select PPC_ICP_NATIVE default n menu "WSP platform selection" @@ -7,13 +11,9 @@ menu "WSP platform selection" config PPC_PSR2 bool "PSR-2 platform" - select PPC_A2 select GENERIC_TBSYNC - select PPC_SCOM select EPAPR_BOOT select PPC_WSP - select PPC_XICS - select PPC_ICP_NATIVE default y endmenu From f352c7251255effe6c2326190f1378adbd142aa3 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 8 Aug 2011 12:30:54 +0000 Subject: [PATCH 0187/1531] powerpc/wsp: Add PCIe Root support to PowerEN/WSP Based on a patch by Benjamin Herrenschmidt Modernized and slightly modified to not record erros into the nvram log since we do not have that device driver just yet. Jimi Xenidis Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/wsp/Kconfig | 3 + arch/powerpc/platforms/wsp/Makefile | 1 + arch/powerpc/platforms/wsp/psr2.c | 4 + arch/powerpc/platforms/wsp/wsp.h | 3 + arch/powerpc/platforms/wsp/wsp_pci.c | 1133 ++++++++++++++++++++++++++ arch/powerpc/platforms/wsp/wsp_pci.h | 268 ++++++ 6 files changed, 1412 insertions(+) create mode 100644 arch/powerpc/platforms/wsp/wsp_pci.c create mode 100644 arch/powerpc/platforms/wsp/wsp_pci.h diff --git a/arch/powerpc/platforms/wsp/Kconfig b/arch/powerpc/platforms/wsp/Kconfig index d051581e9ebf..f4fb837873fb 100644 --- a/arch/powerpc/platforms/wsp/Kconfig +++ b/arch/powerpc/platforms/wsp/Kconfig @@ -4,6 +4,9 @@ config PPC_WSP select PPC_SCOM select PPC_XICS select PPC_ICP_NATIVE + select PCI + select PPC_IO_WORKAROUNDS if PCI + select PPC_INDIRECT_PIO if PCI default n menu "WSP platform selection" diff --git a/arch/powerpc/platforms/wsp/Makefile b/arch/powerpc/platforms/wsp/Makefile index 095be73d6cd4..84519f8d8945 100644 --- a/arch/powerpc/platforms/wsp/Makefile +++ b/arch/powerpc/platforms/wsp/Makefile @@ -4,3 +4,4 @@ obj-y += setup.o ics.o obj-$(CONFIG_PPC_PSR2) += psr2.o opb_pic.o obj-$(CONFIG_PPC_WSP) += scom_wsp.o obj-$(CONFIG_SMP) += smp.o scom_smp.o +obj-$(CONFIG_PCI) += wsp_pci.o diff --git a/arch/powerpc/platforms/wsp/psr2.c b/arch/powerpc/platforms/wsp/psr2.c index 40f28916ff6c..166f2e4b4bee 100644 --- a/arch/powerpc/platforms/wsp/psr2.c +++ b/arch/powerpc/platforms/wsp/psr2.c @@ -63,6 +63,10 @@ static void __init psr2_setup_arch(void) #ifdef CONFIG_SMP a2_setup_smp(); #endif +#ifdef CONFIG_PCI + wsp_setup_pci(); +#endif + } static int __init psr2_probe(void) diff --git a/arch/powerpc/platforms/wsp/wsp.h b/arch/powerpc/platforms/wsp/wsp.h index 7c3e087fd2f2..33479818f62a 100644 --- a/arch/powerpc/platforms/wsp/wsp.h +++ b/arch/powerpc/platforms/wsp/wsp.h @@ -3,6 +3,9 @@ #include +/* Devtree compatible strings for major devices */ +#define PCIE_COMPATIBLE "ibm,wsp-pciex" + extern void wsp_setup_pci(void); extern void scom_init_wsp(void); diff --git a/arch/powerpc/platforms/wsp/wsp_pci.c b/arch/powerpc/platforms/wsp/wsp_pci.c new file mode 100644 index 000000000000..e0262cd0e2d3 --- /dev/null +++ b/arch/powerpc/platforms/wsp/wsp_pci.c @@ -0,0 +1,1133 @@ +/* + * Copyright 2010 Ben Herrenschmidt, IBM Corporation + * + * 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. + */ + +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wsp.h" +#include "wsp_pci.h" +#include "msi.h" + + +/* Max number of TVTs for one table. Only 32-bit tables can use + * multiple TVTs and so the max currently supported is thus 8 + * since only 2G of DMA space is supported + */ +#define MAX_TABLE_TVT_COUNT 8 + +struct wsp_dma_table { + struct list_head link; + struct iommu_table table; + struct wsp_phb *phb; + struct page *tces[MAX_TABLE_TVT_COUNT]; +}; + +/* We support DMA regions from 0...2G in 32bit space (no support for + * 64-bit DMA just yet). Each device gets a separate TCE table (TVT + * entry) with validation enabled (though not supported by SimiCS + * just yet). + * + * To simplify things, we divide this 2G space into N regions based + * on the constant below which could be turned into a tunable eventually + * + * We then assign dynamically those regions to devices as they show up. + * + * We use a bitmap as an allocator for these. + * + * Tables are allocated/created dynamically as devices are discovered, + * multiple TVT entries are used if needed + * + * When 64-bit DMA support is added we should simply use a separate set + * of larger regions (the HW supports 64 TVT entries). We can + * additionally create a bypass region in 64-bit space for performances + * though that would have a cost in term of security. + * + * If you set NUM_DMA32_REGIONS to 1, then a single table is shared + * for all devices and bus/dev/fn validation is disabled + * + * Note that a DMA32 region cannot be smaller than 256M so the max + * supported here for now is 8. We don't yet support sharing regions + * between multiple devices so the max number of devices supported + * is MAX_TABLE_TVT_COUNT. + */ +#define NUM_DMA32_REGIONS 1 + +struct wsp_phb { + struct pci_controller *hose; + + /* Lock controlling access to the list of dma tables. + * It does -not- protect against dma_* operations on + * those tables, those should be stopped before an entry + * is removed from the list. + * + * The lock is also used for error handling operations + */ + spinlock_t lock; + struct list_head dma_tables; + unsigned long dma32_map; + unsigned long dma32_base; + unsigned int dma32_num_regions; + unsigned long dma32_region_size; + + /* Debugfs stuff */ + struct dentry *ddir; + + struct list_head all; +}; +static LIST_HEAD(wsp_phbs); + +//#define cfg_debug(fmt...) pr_debug(fmt) +#define cfg_debug(fmt...) + + +static int wsp_pcie_read_config(struct pci_bus *bus, unsigned int devfn, + int offset, int len, u32 *val) +{ + struct pci_controller *hose; + int suboff; + u64 addr; + + hose = pci_bus_to_host(bus); + if (hose == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + if (offset >= 0x1000) + return PCIBIOS_BAD_REGISTER_NUMBER; + addr = PCIE_REG_CA_ENABLE | + ((u64)bus->number) << PCIE_REG_CA_BUS_SHIFT | + ((u64)devfn) << PCIE_REG_CA_FUNC_SHIFT | + ((u64)offset & ~3) << PCIE_REG_CA_REG_SHIFT; + suboff = offset & 3; + + /* + * Note: the caller has already checked that offset is + * suitably aligned and that len is 1, 2 or 4. + */ + + switch (len) { + case 1: + addr |= (0x8ul >> suboff) << PCIE_REG_CA_BE_SHIFT; + out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr); + *val = (in_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA) + >> (suboff << 3)) & 0xff; + cfg_debug("read 1 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%02x\n", + bus->number, devfn >> 3, devfn & 7, + offset, suboff, addr, *val); + break; + case 2: + addr |= (0xcul >> suboff) << PCIE_REG_CA_BE_SHIFT; + out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr); + *val = (in_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA) + >> (suboff << 3)) & 0xffff; + cfg_debug("read 2 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%04x\n", + bus->number, devfn >> 3, devfn & 7, + offset, suboff, addr, *val); + break; + default: + addr |= 0xful << PCIE_REG_CA_BE_SHIFT; + out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr); + *val = in_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA); + cfg_debug("read 4 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%08x\n", + bus->number, devfn >> 3, devfn & 7, + offset, suboff, addr, *val); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static int wsp_pcie_write_config(struct pci_bus *bus, unsigned int devfn, + int offset, int len, u32 val) +{ + struct pci_controller *hose; + int suboff; + u64 addr; + + hose = pci_bus_to_host(bus); + if (hose == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + if (offset >= 0x1000) + return PCIBIOS_BAD_REGISTER_NUMBER; + addr = PCIE_REG_CA_ENABLE | + ((u64)bus->number) << PCIE_REG_CA_BUS_SHIFT | + ((u64)devfn) << PCIE_REG_CA_FUNC_SHIFT | + ((u64)offset & ~3) << PCIE_REG_CA_REG_SHIFT; + suboff = offset & 3; + + /* + * Note: the caller has already checked that offset is + * suitably aligned and that len is 1, 2 or 4. + */ + switch (len) { + case 1: + addr |= (0x8ul >> suboff) << PCIE_REG_CA_BE_SHIFT; + val <<= suboff << 3; + out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr); + out_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA, val); + cfg_debug("write 1 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%02x\n", + bus->number, devfn >> 3, devfn & 7, + offset, suboff, addr, val); + break; + case 2: + addr |= (0xcul >> suboff) << PCIE_REG_CA_BE_SHIFT; + val <<= suboff << 3; + out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr); + out_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA, val); + cfg_debug("write 2 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%04x\n", + bus->number, devfn >> 3, devfn & 7, + offset, suboff, addr, val); + break; + default: + addr |= 0xful << PCIE_REG_CA_BE_SHIFT; + out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr); + out_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA, val); + cfg_debug("write 4 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%08x\n", + bus->number, devfn >> 3, devfn & 7, + offset, suboff, addr, val); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops wsp_pcie_pci_ops = +{ + .read = wsp_pcie_read_config, + .write = wsp_pcie_write_config, +}; + +#define TCE_SHIFT 12 +#define TCE_PAGE_SIZE (1 << TCE_SHIFT) +#define TCE_PCI_WRITE 0x2 /* write from PCI allowed */ +#define TCE_PCI_READ 0x1 /* read from PCI allowed */ +#define TCE_RPN_MASK 0x3fffffffffful /* 42-bit RPN (4K pages) */ +#define TCE_RPN_SHIFT 12 + +//#define dma_debug(fmt...) pr_debug(fmt) +#define dma_debug(fmt...) + +static int tce_build_wsp(struct iommu_table *tbl, long index, long npages, + unsigned long uaddr, enum dma_data_direction direction, + struct dma_attrs *attrs) +{ + struct wsp_dma_table *ptbl = container_of(tbl, + struct wsp_dma_table, + table); + u64 proto_tce; + u64 *tcep; + u64 rpn; + + proto_tce = TCE_PCI_READ; +#ifdef CONFIG_WSP_DD1_WORKAROUND_DD1_TCE_BUGS + proto_tce |= TCE_PCI_WRITE; +#else + if (direction != DMA_TO_DEVICE) + proto_tce |= TCE_PCI_WRITE; +#endif + + /* XXX Make this faster by factoring out the page address for + * within a TCE table + */ + while (npages--) { + /* We don't use it->base as the table can be scattered */ + tcep = (u64 *)page_address(ptbl->tces[index >> 16]); + tcep += (index & 0xffff); + + /* can't move this out since we might cross LMB boundary */ + rpn = __pa(uaddr) >> TCE_SHIFT; + *tcep = proto_tce | (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT; + + dma_debug("[DMA] TCE %p set to 0x%016llx (dma addr: 0x%lx)\n", + tcep, *tcep, (tbl->it_offset + index) << IOMMU_PAGE_SHIFT); + + uaddr += TCE_PAGE_SIZE; + index++; + } + return 0; +} + +static void tce_free_wsp(struct iommu_table *tbl, long index, long npages) +{ + struct wsp_dma_table *ptbl = container_of(tbl, + struct wsp_dma_table, + table); +#ifndef CONFIG_WSP_DD1_WORKAROUND_DD1_TCE_BUGS + struct pci_controller *hose = ptbl->phb->hose; +#endif + u64 *tcep; + + /* XXX Make this faster by factoring out the page address for + * within a TCE table. Also use line-kill option to kill multiple + * TCEs at once + */ + while (npages--) { + /* We don't use it->base as the table can be scattered */ + tcep = (u64 *)page_address(ptbl->tces[index >> 16]); + tcep += (index & 0xffff); + dma_debug("[DMA] TCE %p cleared\n", tcep); + *tcep = 0; +#ifndef CONFIG_WSP_DD1_WORKAROUND_DD1_TCE_BUGS + /* Don't write there since it would pollute other MMIO accesses */ + out_be64(hose->cfg_data + PCIE_REG_TCE_KILL, + PCIE_REG_TCEKILL_SINGLE | PCIE_REG_TCEKILL_PS_4K | + (__pa(tcep) & PCIE_REG_TCEKILL_ADDR_MASK)); +#endif + index++; + } +} + +static struct wsp_dma_table *wsp_pci_create_dma32_table(struct wsp_phb *phb, + unsigned int region, + struct pci_dev *validate) +{ + struct pci_controller *hose = phb->hose; + unsigned long size = phb->dma32_region_size; + unsigned long addr = phb->dma32_region_size * region + phb->dma32_base; + struct wsp_dma_table *tbl; + int tvts_per_table, i, tvt, nid; + unsigned long flags; + + nid = of_node_to_nid(phb->hose->dn); + + /* Calculate how many TVTs are needed */ + tvts_per_table = size / 0x10000000; + if (tvts_per_table == 0) + tvts_per_table = 1; + + /* Calculate the base TVT index. We know all tables have the same + * size so we just do a simple multiply here + */ + tvt = region * tvts_per_table; + + pr_debug(" Region : %d\n", region); + pr_debug(" DMA range : 0x%08lx..0x%08lx\n", addr, addr + size - 1); + pr_debug(" Number of TVTs : %d\n", tvts_per_table); + pr_debug(" Base TVT : %d\n", tvt); + pr_debug(" Node : %d\n", nid); + + tbl = kzalloc_node(sizeof(struct wsp_dma_table), GFP_KERNEL, nid); + if (!tbl) + return ERR_PTR(-ENOMEM); + tbl->phb = phb; + + /* Create as many TVTs as needed, each represents 256M at most */ + for (i = 0; i < tvts_per_table; i++) { + u64 tvt_data1, tvt_data0; + + /* Allocate table. We use a 4K TCE size for now always so + * one table is always 8 * (258M / 4K) == 512K + */ + tbl->tces[i] = alloc_pages_node(nid, GFP_KERNEL, get_order(0x80000)); + if (tbl->tces[i] == NULL) + goto fail; + memset(page_address(tbl->tces[i]), 0, 0x80000); + + pr_debug(" TCE table %d at : %p\n", i, page_address(tbl->tces[i])); + + /* Table size. We currently set it to be the whole 256M region */ + tvt_data0 = 2ull << IODA_TVT0_TCE_TABLE_SIZE_SHIFT; + /* IO page size set to 4K */ + tvt_data1 = 1ull << IODA_TVT1_IO_PAGE_SIZE_SHIFT; + /* Shift in the address */ + tvt_data0 |= __pa(page_address(tbl->tces[i])) << IODA_TVT0_TTA_SHIFT; + + /* Validation stuff. We only validate fully bus/dev/fn for now + * one day maybe we can group devices but that isn't the case + * at the moment + */ + if (validate) { + tvt_data0 |= IODA_TVT0_BUSNUM_VALID_MASK; + tvt_data0 |= validate->bus->number; + tvt_data1 |= IODA_TVT1_DEVNUM_VALID; + tvt_data1 |= ((u64)PCI_SLOT(validate->devfn)) + << IODA_TVT1_DEVNUM_VALUE_SHIFT; + tvt_data1 |= IODA_TVT1_FUNCNUM_VALID; + tvt_data1 |= ((u64)PCI_FUNC(validate->devfn)) + << IODA_TVT1_FUNCNUM_VALUE_SHIFT; + } + + /* XX PE number is always 0 for now */ + + /* Program the values using the PHB lock */ + spin_lock_irqsave(&phb->lock, flags); + out_be64(hose->cfg_data + PCIE_REG_IODA_ADDR, + (tvt + i) | PCIE_REG_IODA_AD_TBL_TVT); + out_be64(hose->cfg_data + PCIE_REG_IODA_DATA1, tvt_data1); + out_be64(hose->cfg_data + PCIE_REG_IODA_DATA0, tvt_data0); + spin_unlock_irqrestore(&phb->lock, flags); + } + + /* Init bits and pieces */ + tbl->table.it_blocksize = 16; + tbl->table.it_offset = addr >> IOMMU_PAGE_SHIFT; + tbl->table.it_size = size >> IOMMU_PAGE_SHIFT; + + /* + * It's already blank but we clear it anyway. + * Consider an aditiona interface that makes cleaing optional + */ + iommu_init_table(&tbl->table, nid); + + list_add(&tbl->link, &phb->dma_tables); + return tbl; + + fail: + pr_debug(" Failed to allocate a 256M TCE table !\n"); + for (i = 0; i < tvts_per_table; i++) + if (tbl->tces[i]) + __free_pages(tbl->tces[i], get_order(0x80000)); + kfree(tbl); + return ERR_PTR(-ENOMEM); +} + +static void __devinit wsp_pci_dma_dev_setup(struct pci_dev *pdev) +{ + struct dev_archdata *archdata = &pdev->dev.archdata; + struct pci_controller *hose = pci_bus_to_host(pdev->bus); + struct wsp_phb *phb = hose->private_data; + struct wsp_dma_table *table = NULL; + unsigned long flags; + int i; + + /* Don't assign an iommu table to a bridge */ + if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE) + return; + + pr_debug("%s: Setting up DMA...\n", pci_name(pdev)); + + spin_lock_irqsave(&phb->lock, flags); + + /* If only one region, check if it already exist */ + if (phb->dma32_num_regions == 1) { + spin_unlock_irqrestore(&phb->lock, flags); + if (list_empty(&phb->dma_tables)) + table = wsp_pci_create_dma32_table(phb, 0, NULL); + else + table = list_first_entry(&phb->dma_tables, + struct wsp_dma_table, + link); + } else { + /* else find a free region */ + for (i = 0; i < phb->dma32_num_regions && !table; i++) { + if (__test_and_set_bit(i, &phb->dma32_map)) + continue; + spin_unlock_irqrestore(&phb->lock, flags); + table = wsp_pci_create_dma32_table(phb, i, pdev); + } + } + + /* Check if we got an error */ + if (IS_ERR(table)) { + pr_err("%s: Failed to create DMA table, err %ld !\n", + pci_name(pdev), PTR_ERR(table)); + return; + } + + /* Or a valid table */ + if (table) { + pr_info("%s: Setup iommu: 32-bit DMA region 0x%08lx..0x%08lx\n", + pci_name(pdev), + table->table.it_offset << IOMMU_PAGE_SHIFT, + (table->table.it_offset << IOMMU_PAGE_SHIFT) + + phb->dma32_region_size - 1); + archdata->dma_data.iommu_table_base = &table->table; + return; + } + + /* Or no room */ + spin_unlock_irqrestore(&phb->lock, flags); + pr_err("%s: Out of DMA space !\n", pci_name(pdev)); +} + +static void __init wsp_pcie_configure_hw(struct pci_controller *hose) +{ + u64 val; + int i; + +#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 */ + 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 */ + out_be64(hose->cfg_data + 0xe50, + in_be64(hose->cfg_data + 0xe50) | (3ull << 62)); + printk("PCI-E DEBUG CONTROL 5 = 0x%llx\n", in_be64(hose->cfg_data + 0xe50)); +#endif + + /* Configure M32A and IO. IO is hard wired to be 1M for now */ + out_be64(hose->cfg_data + PCIE_REG_IO_BASE_ADDR, hose->io_base_phys); + out_be64(hose->cfg_data + PCIE_REG_IO_BASE_MASK, + (~(hose->io_resource.end - hose->io_resource.start)) & + 0x3fffffff000ul); + out_be64(hose->cfg_data + PCIE_REG_IO_START_ADDR, 0 | 1); + + out_be64(hose->cfg_data + PCIE_REG_M32A_BASE_ADDR, + hose->mem_resources[0].start); + printk("Want to write to M32A_BASE_MASK : 0x%llx\n", + (~(hose->mem_resources[0].end - + hose->mem_resources[0].start)) & 0x3ffffff0000ul); + out_be64(hose->cfg_data + PCIE_REG_M32A_BASE_MASK, + (~(hose->mem_resources[0].end - + hose->mem_resources[0].start)) & 0x3ffffff0000ul); + out_be64(hose->cfg_data + PCIE_REG_M32A_START_ADDR, + (hose->mem_resources[0].start - hose->pci_mem_offset) | 1); + + /* Clear all TVT entries + * + * XX Might get TVT count from device-tree + */ + for (i = 0; i < IODA_TVT_COUNT; i++) { + out_be64(hose->cfg_data + PCIE_REG_IODA_ADDR, + PCIE_REG_IODA_AD_TBL_TVT | i); + out_be64(hose->cfg_data + PCIE_REG_IODA_DATA1, 0); + out_be64(hose->cfg_data + PCIE_REG_IODA_DATA0, 0); + } + + /* Kill the TCE cache */ + out_be64(hose->cfg_data + PCIE_REG_PHB_CONFIG, + in_be64(hose->cfg_data + PCIE_REG_PHB_CONFIG) | + PCIE_REG_PHBC_64B_TCE_EN); + + /* Enable 32 & 64-bit MSIs, IO space and M32A */ + val = PCIE_REG_PHBC_32BIT_MSI_EN | + PCIE_REG_PHBC_IO_EN | + PCIE_REG_PHBC_64BIT_MSI_EN | + PCIE_REG_PHBC_M32A_EN; + if (iommu_is_off) + val |= PCIE_REG_PHBC_DMA_XLATE_BYPASS; + pr_debug("Will write config: 0x%llx\n", val); + out_be64(hose->cfg_data + PCIE_REG_PHB_CONFIG, val); + + /* Enable error reporting */ + out_be64(hose->cfg_data + 0xe00, + in_be64(hose->cfg_data + 0xe00) | 0x0008000000000000ull); + + /* Mask an error that's generated when doing config space probe + * + * XXX Maybe we should only mask it around config space cycles... that or + * ignore it when we know we had a config space cycle recently ? + */ + out_be64(hose->cfg_data + PCIE_REG_DMA_ERR_STATUS_MASK, 0x8000000000000000ull); + out_be64(hose->cfg_data + PCIE_REG_DMA_ERR1_STATUS_MASK, 0x8000000000000000ull); + + /* Enable UTL errors, for now, all of them got to UTL irq 1 + * + * We similarily mask one UTL error caused apparently during normal + * probing. We also mask the link up error + */ + out_be64(hose->cfg_data + PCIE_UTL_SYS_BUS_AGENT_ERR_SEV, 0); + out_be64(hose->cfg_data + PCIE_UTL_RC_ERR_SEVERITY, 0); + out_be64(hose->cfg_data + PCIE_UTL_PCIE_PORT_ERROR_SEV, 0); + out_be64(hose->cfg_data + PCIE_UTL_SYS_BUS_AGENT_IRQ_EN, 0xffffffff00000000ull); + out_be64(hose->cfg_data + PCIE_UTL_PCIE_PORT_IRQ_EN, 0xff5fffff00000000ull); + out_be64(hose->cfg_data + PCIE_UTL_EP_ERR_IRQ_EN, 0xffffffff00000000ull); + + DUMP_REG(PCIE_REG_IO_BASE_ADDR); + DUMP_REG(PCIE_REG_IO_BASE_MASK); + DUMP_REG(PCIE_REG_IO_START_ADDR); + DUMP_REG(PCIE_REG_M32A_BASE_ADDR); + DUMP_REG(PCIE_REG_M32A_BASE_MASK); + DUMP_REG(PCIE_REG_M32A_START_ADDR); + DUMP_REG(PCIE_REG_M32B_BASE_ADDR); + DUMP_REG(PCIE_REG_M32B_BASE_MASK); + DUMP_REG(PCIE_REG_M32B_START_ADDR); + DUMP_REG(PCIE_REG_M64_BASE_ADDR); + DUMP_REG(PCIE_REG_M64_BASE_MASK); + DUMP_REG(PCIE_REG_M64_START_ADDR); + DUMP_REG(PCIE_REG_PHB_CONFIG); +} + +static void wsp_pci_wait_io_idle(struct wsp_phb *phb, unsigned long port) +{ + u64 val; + int i; + + for (i = 0; i < 10000; i++) { + val = in_be64(phb->hose->cfg_data + 0xe08); + if ((val & 0x1900000000000000ull) == 0x0100000000000000ull) + return; + udelay(1); + } + pr_warning("PCI IO timeout on domain %d port 0x%lx\n", + phb->hose->global_number, port); +} + +#define DEF_PCI_AC_RET_pio(name, ret, at, al, aa) \ +static ret wsp_pci_##name at \ +{ \ + struct iowa_bus *bus; \ + struct wsp_phb *phb; \ + unsigned long flags; \ + ret rval; \ + bus = iowa_pio_find_bus(aa); \ + WARN_ON(!bus); \ + phb = bus->private; \ + spin_lock_irqsave(&phb->lock, flags); \ + wsp_pci_wait_io_idle(phb, aa); \ + rval = __do_##name al; \ + spin_unlock_irqrestore(&phb->lock, flags); \ + return rval; \ +} + +#define DEF_PCI_AC_NORET_pio(name, at, al, aa) \ +static void wsp_pci_##name at \ +{ \ + struct iowa_bus *bus; \ + struct wsp_phb *phb; \ + unsigned long flags; \ + bus = iowa_pio_find_bus(aa); \ + WARN_ON(!bus); \ + phb = bus->private; \ + spin_lock_irqsave(&phb->lock, flags); \ + wsp_pci_wait_io_idle(phb, aa); \ + __do_##name al; \ + spin_unlock_irqrestore(&phb->lock, flags); \ +} + +#define DEF_PCI_AC_RET_mem(name, ret, at, al, aa) +#define DEF_PCI_AC_NORET_mem(name, at, al, aa) + +#define DEF_PCI_AC_RET(name, ret, at, al, space, aa) \ + DEF_PCI_AC_RET_##space(name, ret, at, al, aa) + +#define DEF_PCI_AC_NORET(name, at, al, space, aa) \ + DEF_PCI_AC_NORET_##space(name, at, al, aa) \ + + +#include + +#undef DEF_PCI_AC_RET +#undef DEF_PCI_AC_NORET + +static struct ppc_pci_io wsp_pci_iops = { + .inb = wsp_pci_inb, + .inw = wsp_pci_inw, + .inl = wsp_pci_inl, + .outb = wsp_pci_outb, + .outw = wsp_pci_outw, + .outl = wsp_pci_outl, + .insb = wsp_pci_insb, + .insw = wsp_pci_insw, + .insl = wsp_pci_insl, + .outsb = wsp_pci_outsb, + .outsw = wsp_pci_outsw, + .outsl = wsp_pci_outsl, +}; + +static int __init wsp_setup_one_phb(struct device_node *np) +{ + struct pci_controller *hose; + struct wsp_phb *phb; + + pr_info("PCI: Setting up PCIe host bridge 0x%s\n", np->full_name); + + phb = zalloc_maybe_bootmem(sizeof(struct wsp_phb), GFP_KERNEL); + if (!phb) + return -ENOMEM; + hose = pcibios_alloc_controller(np); + if (!hose) { + /* Can't really free the phb */ + return -ENOMEM; + } + hose->private_data = phb; + phb->hose = hose; + + INIT_LIST_HEAD(&phb->dma_tables); + spin_lock_init(&phb->lock); + + /* XXX Use bus-range property ? */ + hose->first_busno = 0; + hose->last_busno = 0xff; + + /* We use cfg_data as the address for the whole bridge MMIO space + */ + hose->cfg_data = of_iomap(hose->dn, 0); + + pr_debug("PCIe registers mapped at 0x%p\n", hose->cfg_data); + + /* Get the ranges of the device-tree */ + pci_process_bridge_OF_ranges(hose, np, 0); + + /* XXX Force re-assigning of everything for now */ + pci_add_flags(PCI_REASSIGN_ALL_BUS | PCI_REASSIGN_ALL_RSRC | + PCI_ENABLE_PROC_DOMAINS); + pci_probe_only = 0; + + /* Calculate how the TCE space is divided */ + phb->dma32_base = 0; + phb->dma32_num_regions = NUM_DMA32_REGIONS; + if (phb->dma32_num_regions > MAX_TABLE_TVT_COUNT) { + pr_warning("IOMMU: Clamped to %d DMA32 regions\n", + MAX_TABLE_TVT_COUNT); + phb->dma32_num_regions = MAX_TABLE_TVT_COUNT; + } + phb->dma32_region_size = 0x80000000 / phb->dma32_num_regions; + + BUG_ON(!is_power_of_2(phb->dma32_region_size)); + + /* Setup config ops */ + hose->ops = &wsp_pcie_pci_ops; + + /* Configure the HW */ + wsp_pcie_configure_hw(hose); + + /* Instanciate IO workarounds */ + iowa_register_bus(hose, &wsp_pci_iops, NULL, phb); +#ifdef CONFIG_PCI_MSI + wsp_setup_phb_msi(hose); +#endif + + /* Add to global list */ + list_add(&phb->all, &wsp_phbs); + + return 0; +} + +void __init wsp_setup_pci(void) +{ + struct device_node *np; + int rc; + + /* Find host bridges */ + for_each_compatible_node(np, "pciex", PCIE_COMPATIBLE) { + rc = wsp_setup_one_phb(np); + if (rc) + pr_err("Failed to setup PCIe bridge %s, rc=%d\n", + np->full_name, rc); + } + + /* Establish device-tree linkage */ + pci_devs_phb_init(); + + /* Set DMA ops to use TCEs */ + if (iommu_is_off) { + pr_info("PCI-E: Disabled TCEs, using direct DMA\n"); + set_pci_dma_ops(&dma_direct_ops); + } else { + ppc_md.pci_dma_dev_setup = wsp_pci_dma_dev_setup; + ppc_md.tce_build = tce_build_wsp; + ppc_md.tce_free = tce_free_wsp; + set_pci_dma_ops(&dma_iommu_ops); + } +} + +#define err_debug(fmt...) pr_debug(fmt) +//#define err_debug(fmt...) + +static int __init wsp_pci_get_err_irq_no_dt(struct device_node *np) +{ + const u32 *prop; + int hw_irq; + + /* Ok, no interrupts property, let's try to find our child P2P */ + np = of_get_next_child(np, NULL); + if (np == NULL) + return 0; + + /* Grab it's interrupt map */ + prop = of_get_property(np, "interrupt-map", NULL); + if (prop == NULL) + return 0; + + /* Grab one of the interrupts in there, keep the low 4 bits */ + hw_irq = prop[5] & 0xf; + + /* 0..4 for PHB 0 and 5..9 for PHB 1 */ + if (hw_irq < 5) + hw_irq = 4; + else + hw_irq = 9; + hw_irq |= prop[5] & ~0xf; + + err_debug("PCI: Using 0x%x as error IRQ for %s\n", + hw_irq, np->parent->full_name); + return irq_create_mapping(NULL, hw_irq); +} + +static const struct { + u32 offset; + const char *name; +} wsp_pci_regs[] = { +#define DREG(x) { PCIE_REG_##x, #x } +#define DUTL(x) { PCIE_UTL_##x, "UTL_" #x } + /* Architected registers except CONFIG_ and IODA + * to avoid side effects + */ + DREG(DMA_CHAN_STATUS), + DREG(CPU_LOADSTORE_STATUS), + DREG(LOCK0), + DREG(LOCK1), + DREG(PHB_CONFIG), + DREG(IO_BASE_ADDR), + DREG(IO_BASE_MASK), + DREG(IO_START_ADDR), + DREG(M32A_BASE_ADDR), + DREG(M32A_BASE_MASK), + DREG(M32A_START_ADDR), + DREG(M32B_BASE_ADDR), + DREG(M32B_BASE_MASK), + DREG(M32B_START_ADDR), + DREG(M64_BASE_ADDR), + DREG(M64_BASE_MASK), + DREG(M64_START_ADDR), + DREG(TCE_KILL), + DREG(LOCK2), + DREG(PHB_GEN_CAP), + DREG(PHB_TCE_CAP), + DREG(PHB_IRQ_CAP), + DREG(PHB_EEH_CAP), + DREG(PAPR_ERR_INJ_CONTROL), + DREG(PAPR_ERR_INJ_ADDR), + DREG(PAPR_ERR_INJ_MASK), + + /* UTL core regs */ + DUTL(SYS_BUS_CONTROL), + DUTL(STATUS), + DUTL(SYS_BUS_AGENT_STATUS), + DUTL(SYS_BUS_AGENT_ERR_SEV), + DUTL(SYS_BUS_AGENT_IRQ_EN), + DUTL(SYS_BUS_BURST_SZ_CONF), + DUTL(REVISION_ID), + DUTL(OUT_POST_HDR_BUF_ALLOC), + DUTL(OUT_POST_DAT_BUF_ALLOC), + DUTL(IN_POST_HDR_BUF_ALLOC), + DUTL(IN_POST_DAT_BUF_ALLOC), + DUTL(OUT_NP_BUF_ALLOC), + DUTL(IN_NP_BUF_ALLOC), + DUTL(PCIE_TAGS_ALLOC), + DUTL(GBIF_READ_TAGS_ALLOC), + + DUTL(PCIE_PORT_CONTROL), + DUTL(PCIE_PORT_STATUS), + DUTL(PCIE_PORT_ERROR_SEV), + DUTL(PCIE_PORT_IRQ_EN), + DUTL(RC_STATUS), + DUTL(RC_ERR_SEVERITY), + DUTL(RC_IRQ_EN), + DUTL(EP_STATUS), + DUTL(EP_ERR_SEVERITY), + DUTL(EP_ERR_IRQ_EN), + DUTL(PCI_PM_CTRL1), + DUTL(PCI_PM_CTRL2), + + /* PCIe stack regs */ + DREG(SYSTEM_CONFIG1), + DREG(SYSTEM_CONFIG2), + DREG(EP_SYSTEM_CONFIG), + DREG(EP_FLR), + DREG(EP_BAR_CONFIG), + DREG(LINK_CONFIG), + DREG(PM_CONFIG), + DREG(DLP_CONTROL), + DREG(DLP_STATUS), + DREG(ERR_REPORT_CONTROL), + DREG(SLOT_CONTROL1), + DREG(SLOT_CONTROL2), + DREG(UTL_CONFIG), + DREG(BUFFERS_CONFIG), + DREG(ERROR_INJECT), + DREG(SRIOV_CONFIG), + DREG(PF0_SRIOV_STATUS), + DREG(PF1_SRIOV_STATUS), + DREG(PORT_NUMBER), + DREG(POR_SYSTEM_CONFIG), + + /* Internal logic regs */ + DREG(PHB_VERSION), + DREG(RESET), + DREG(PHB_CONTROL), + DREG(PHB_TIMEOUT_CONTROL1), + DREG(PHB_QUIESCE_DMA), + DREG(PHB_DMA_READ_TAG_ACTV), + DREG(PHB_TCE_READ_TAG_ACTV), + + /* FIR registers */ + DREG(LEM_FIR_ACCUM), + DREG(LEM_FIR_AND_MASK), + DREG(LEM_FIR_OR_MASK), + DREG(LEM_ACTION0), + DREG(LEM_ACTION1), + DREG(LEM_ERROR_MASK), + DREG(LEM_ERROR_AND_MASK), + DREG(LEM_ERROR_OR_MASK), + + /* Error traps registers */ + DREG(PHB_ERR_STATUS), + DREG(PHB_ERR_STATUS), + DREG(PHB_ERR1_STATUS), + DREG(PHB_ERR_INJECT), + DREG(PHB_ERR_LEM_ENABLE), + DREG(PHB_ERR_IRQ_ENABLE), + DREG(PHB_ERR_FREEZE_ENABLE), + DREG(PHB_ERR_SIDE_ENABLE), + DREG(PHB_ERR_LOG_0), + DREG(PHB_ERR_LOG_1), + DREG(PHB_ERR_STATUS_MASK), + DREG(PHB_ERR1_STATUS_MASK), + DREG(MMIO_ERR_STATUS), + DREG(MMIO_ERR1_STATUS), + DREG(MMIO_ERR_INJECT), + DREG(MMIO_ERR_LEM_ENABLE), + DREG(MMIO_ERR_IRQ_ENABLE), + DREG(MMIO_ERR_FREEZE_ENABLE), + DREG(MMIO_ERR_SIDE_ENABLE), + DREG(MMIO_ERR_LOG_0), + DREG(MMIO_ERR_LOG_1), + DREG(MMIO_ERR_STATUS_MASK), + DREG(MMIO_ERR1_STATUS_MASK), + DREG(DMA_ERR_STATUS), + DREG(DMA_ERR1_STATUS), + DREG(DMA_ERR_INJECT), + DREG(DMA_ERR_LEM_ENABLE), + DREG(DMA_ERR_IRQ_ENABLE), + DREG(DMA_ERR_FREEZE_ENABLE), + DREG(DMA_ERR_SIDE_ENABLE), + DREG(DMA_ERR_LOG_0), + DREG(DMA_ERR_LOG_1), + DREG(DMA_ERR_STATUS_MASK), + DREG(DMA_ERR1_STATUS_MASK), + + /* Debug and Trace registers */ + DREG(PHB_DEBUG_CONTROL0), + DREG(PHB_DEBUG_STATUS0), + DREG(PHB_DEBUG_CONTROL1), + DREG(PHB_DEBUG_STATUS1), + DREG(PHB_DEBUG_CONTROL2), + DREG(PHB_DEBUG_STATUS2), + DREG(PHB_DEBUG_CONTROL3), + DREG(PHB_DEBUG_STATUS3), + DREG(PHB_DEBUG_CONTROL4), + DREG(PHB_DEBUG_STATUS4), + DREG(PHB_DEBUG_CONTROL5), + DREG(PHB_DEBUG_STATUS5), + + /* Don't seem to exist ... + DREG(PHB_DEBUG_CONTROL6), + DREG(PHB_DEBUG_STATUS6), + */ +}; + +static int wsp_pci_regs_show(struct seq_file *m, void *private) +{ + struct wsp_phb *phb = m->private; + struct pci_controller *hose = phb->hose; + int i; + + for (i = 0; i < ARRAY_SIZE(wsp_pci_regs); i++) { + /* Skip write-only regs */ + if (wsp_pci_regs[i].offset == 0xc08 || + wsp_pci_regs[i].offset == 0xc10 || + wsp_pci_regs[i].offset == 0xc38 || + wsp_pci_regs[i].offset == 0xc40) + continue; + seq_printf(m, "0x%03x: 0x%016llx %s\n", + wsp_pci_regs[i].offset, + in_be64(hose->cfg_data + wsp_pci_regs[i].offset), + wsp_pci_regs[i].name); + } + return 0; +} + +static int wsp_pci_regs_open(struct inode *inode, struct file *file) +{ + return single_open(file, wsp_pci_regs_show, inode->i_private); +} + +static const struct file_operations wsp_pci_regs_fops = { + .open = wsp_pci_regs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int wsp_pci_reg_set(void *data, u64 val) +{ + out_be64((void __iomem *)data, val); + return 0; +} + +static int wsp_pci_reg_get(void *data, u64 *val) +{ + *val = in_be64((void __iomem *)data); + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(wsp_pci_reg_fops, wsp_pci_reg_get, wsp_pci_reg_set, "0x%llx\n"); + +static irqreturn_t wsp_pci_err_irq(int irq, void *dev_id) +{ + struct wsp_phb *phb = dev_id; + struct pci_controller *hose = phb->hose; + irqreturn_t handled = IRQ_NONE; + struct wsp_pcie_err_log_data ed; + + pr_err("PCI: Error interrupt on %s (PHB %d)\n", + hose->dn->full_name, hose->global_number); + again: + memset(&ed, 0, sizeof(ed)); + + /* Read and clear UTL errors */ + ed.utl_sys_err = in_be64(hose->cfg_data + PCIE_UTL_SYS_BUS_AGENT_STATUS); + if (ed.utl_sys_err) + out_be64(hose->cfg_data + PCIE_UTL_SYS_BUS_AGENT_STATUS, ed.utl_sys_err); + ed.utl_port_err = in_be64(hose->cfg_data + PCIE_UTL_PCIE_PORT_STATUS); + if (ed.utl_port_err) + out_be64(hose->cfg_data + PCIE_UTL_PCIE_PORT_STATUS, ed.utl_port_err); + ed.utl_rc_err = in_be64(hose->cfg_data + PCIE_UTL_RC_STATUS); + if (ed.utl_rc_err) + out_be64(hose->cfg_data + PCIE_UTL_RC_STATUS, ed.utl_rc_err); + + /* Read and clear main trap errors */ + ed.phb_err = in_be64(hose->cfg_data + PCIE_REG_PHB_ERR_STATUS); + if (ed.phb_err) { + ed.phb_err1 = in_be64(hose->cfg_data + PCIE_REG_PHB_ERR1_STATUS); + ed.phb_log0 = in_be64(hose->cfg_data + PCIE_REG_PHB_ERR_LOG_0); + ed.phb_log1 = in_be64(hose->cfg_data + PCIE_REG_PHB_ERR_LOG_1); + out_be64(hose->cfg_data + PCIE_REG_PHB_ERR1_STATUS, 0); + out_be64(hose->cfg_data + PCIE_REG_PHB_ERR_STATUS, 0); + } + ed.mmio_err = in_be64(hose->cfg_data + PCIE_REG_MMIO_ERR_STATUS); + if (ed.mmio_err) { + ed.mmio_err1 = in_be64(hose->cfg_data + PCIE_REG_MMIO_ERR1_STATUS); + ed.mmio_log0 = in_be64(hose->cfg_data + PCIE_REG_MMIO_ERR_LOG_0); + ed.mmio_log1 = in_be64(hose->cfg_data + PCIE_REG_MMIO_ERR_LOG_1); + out_be64(hose->cfg_data + PCIE_REG_MMIO_ERR1_STATUS, 0); + out_be64(hose->cfg_data + PCIE_REG_MMIO_ERR_STATUS, 0); + } + ed.dma_err = in_be64(hose->cfg_data + PCIE_REG_DMA_ERR_STATUS); + if (ed.dma_err) { + ed.dma_err1 = in_be64(hose->cfg_data + PCIE_REG_DMA_ERR1_STATUS); + ed.dma_log0 = in_be64(hose->cfg_data + PCIE_REG_DMA_ERR_LOG_0); + ed.dma_log1 = in_be64(hose->cfg_data + PCIE_REG_DMA_ERR_LOG_1); + out_be64(hose->cfg_data + PCIE_REG_DMA_ERR1_STATUS, 0); + out_be64(hose->cfg_data + PCIE_REG_DMA_ERR_STATUS, 0); + } + + /* Now print things out */ + if (ed.phb_err) { + pr_err(" PHB Error Status : 0x%016llx\n", ed.phb_err); + pr_err(" PHB First Error Status: 0x%016llx\n", ed.phb_err1); + pr_err(" PHB Error Log 0 : 0x%016llx\n", ed.phb_log0); + pr_err(" PHB Error Log 1 : 0x%016llx\n", ed.phb_log1); + } + if (ed.mmio_err) { + pr_err(" MMIO Error Status : 0x%016llx\n", ed.mmio_err); + pr_err(" MMIO First Error Status: 0x%016llx\n", ed.mmio_err1); + pr_err(" MMIO Error Log 0 : 0x%016llx\n", ed.mmio_log0); + pr_err(" MMIO Error Log 1 : 0x%016llx\n", ed.mmio_log1); + } + if (ed.dma_err) { + pr_err(" DMA Error Status : 0x%016llx\n", ed.dma_err); + pr_err(" DMA First Error Status: 0x%016llx\n", ed.dma_err1); + pr_err(" DMA Error Log 0 : 0x%016llx\n", ed.dma_log0); + pr_err(" DMA Error Log 1 : 0x%016llx\n", ed.dma_log1); + } + if (ed.utl_sys_err) + pr_err(" UTL Sys Error Status : 0x%016llx\n", ed.utl_sys_err); + if (ed.utl_port_err) + pr_err(" UTL Port Error Status : 0x%016llx\n", ed.utl_port_err); + if (ed.utl_rc_err) + pr_err(" UTL RC Error Status : 0x%016llx\n", ed.utl_rc_err); + + /* Interrupts are caused by the error traps. If we had any error there + * we loop again in case the UTL buffered some new stuff between + * going there and going to the traps + */ + if (ed.dma_err || ed.mmio_err || ed.phb_err) { + handled = IRQ_HANDLED; + goto again; + } + return handled; +} + +static void __init wsp_setup_pci_err_reporting(struct wsp_phb *phb) +{ + struct pci_controller *hose = phb->hose; + int err_irq, i, rc; + char fname[16]; + + /* Create a debugfs file for that PHB */ + sprintf(fname, "phb%d", phb->hose->global_number); + phb->ddir = debugfs_create_dir(fname, powerpc_debugfs_root); + + /* Some useful debug output */ + if (phb->ddir) { + struct dentry *d = debugfs_create_dir("regs", phb->ddir); + char tmp[64]; + + for (i = 0; i < ARRAY_SIZE(wsp_pci_regs); i++) { + sprintf(tmp, "%03x_%s", wsp_pci_regs[i].offset, + wsp_pci_regs[i].name); + debugfs_create_file(tmp, 0600, d, + hose->cfg_data + wsp_pci_regs[i].offset, + &wsp_pci_reg_fops); + } + debugfs_create_file("all_regs", 0600, phb->ddir, phb, &wsp_pci_regs_fops); + } + + /* Find the IRQ number for that PHB */ + err_irq = irq_of_parse_and_map(hose->dn, 0); + if (err_irq == 0) + /* XXX Error IRQ lacking from device-tree */ + err_irq = wsp_pci_get_err_irq_no_dt(hose->dn); + if (err_irq == 0) { + pr_err("PCI: Failed to fetch error interrupt for %s\n", + hose->dn->full_name); + return; + } + /* Request it */ + rc = request_irq(err_irq, wsp_pci_err_irq, 0, "wsp_pci error", phb); + if (rc) { + pr_err("PCI: Failed to request interrupt for %s\n", + hose->dn->full_name); + } + /* Enable interrupts for all errors for now */ + out_be64(hose->cfg_data + PCIE_REG_PHB_ERR_IRQ_ENABLE, 0xffffffffffffffffull); + out_be64(hose->cfg_data + PCIE_REG_MMIO_ERR_IRQ_ENABLE, 0xffffffffffffffffull); + out_be64(hose->cfg_data + PCIE_REG_DMA_ERR_IRQ_ENABLE, 0xffffffffffffffffull); +} + +/* + * This is called later to hookup with the error interrupt + */ +static int __init wsp_setup_pci_late(void) +{ + struct wsp_phb *phb; + + list_for_each_entry(phb, &wsp_phbs, all) + wsp_setup_pci_err_reporting(phb); + + return 0; +} +arch_initcall(wsp_setup_pci_late); diff --git a/arch/powerpc/platforms/wsp/wsp_pci.h b/arch/powerpc/platforms/wsp/wsp_pci.h new file mode 100644 index 000000000000..52e9bd95250d --- /dev/null +++ b/arch/powerpc/platforms/wsp/wsp_pci.h @@ -0,0 +1,268 @@ +/* + * Copyright 2010 Ben Herrenschmidt, IBM Corporation + * + * 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 __WSP_PCI_H +#define __WSP_PCI_H + +/* Architected registers */ +#define PCIE_REG_DMA_CHAN_STATUS 0x110 +#define PCIE_REG_CPU_LOADSTORE_STATUS 0x120 + +#define PCIE_REG_CONFIG_DATA 0x130 +#define PCIE_REG_LOCK0 0x138 +#define PCIE_REG_CONFIG_ADDRESS 0x140 +#define PCIE_REG_CA_ENABLE 0x8000000000000000ull +#define PCIE_REG_CA_BUS_MASK 0x0ff0000000000000ull +#define PCIE_REG_CA_BUS_SHIFT (20+32) +#define PCIE_REG_CA_DEV_MASK 0x000f800000000000ull +#define PCIE_REG_CA_DEV_SHIFT (15+32) +#define PCIE_REG_CA_FUNC_MASK 0x0000700000000000ull +#define PCIE_REG_CA_FUNC_SHIFT (12+32) +#define PCIE_REG_CA_REG_MASK 0x00000fff00000000ull +#define PCIE_REG_CA_REG_SHIFT ( 0+32) +#define PCIE_REG_CA_BE_MASK 0x00000000f0000000ull +#define PCIE_REG_CA_BE_SHIFT ( 28) +#define PCIE_REG_LOCK1 0x148 + +#define PCIE_REG_PHB_CONFIG 0x160 +#define PCIE_REG_PHBC_64B_TCE_EN 0x2000000000000000ull +#define PCIE_REG_PHBC_MMIO_DMA_FREEZE_EN 0x1000000000000000ull +#define PCIE_REG_PHBC_32BIT_MSI_EN 0x0080000000000000ull +#define PCIE_REG_PHBC_M64_EN 0x0040000000000000ull +#define PCIE_REG_PHBC_IO_EN 0x0008000000000000ull +#define PCIE_REG_PHBC_64BIT_MSI_EN 0x0002000000000000ull +#define PCIE_REG_PHBC_M32A_EN 0x0000800000000000ull +#define PCIE_REG_PHBC_M32B_EN 0x0000400000000000ull +#define PCIE_REG_PHBC_MSI_PE_VALIDATE 0x0000200000000000ull +#define PCIE_REG_PHBC_DMA_XLATE_BYPASS 0x0000100000000000ull + +#define PCIE_REG_IO_BASE_ADDR 0x170 +#define PCIE_REG_IO_BASE_MASK 0x178 +#define PCIE_REG_IO_START_ADDR 0x180 + +#define PCIE_REG_M32A_BASE_ADDR 0x190 +#define PCIE_REG_M32A_BASE_MASK 0x198 +#define PCIE_REG_M32A_START_ADDR 0x1a0 + +#define PCIE_REG_M32B_BASE_ADDR 0x1b0 +#define PCIE_REG_M32B_BASE_MASK 0x1b8 +#define PCIE_REG_M32B_START_ADDR 0x1c0 + +#define PCIE_REG_M64_BASE_ADDR 0x1e0 +#define PCIE_REG_M64_BASE_MASK 0x1e8 +#define PCIE_REG_M64_START_ADDR 0x1f0 + +#define PCIE_REG_TCE_KILL 0x210 +#define PCIE_REG_TCEKILL_SINGLE 0x8000000000000000ull +#define PCIE_REG_TCEKILL_ADDR_MASK 0x000003fffffffff8ull +#define PCIE_REG_TCEKILL_PS_4K 0 +#define PCIE_REG_TCEKILL_PS_64K 1 +#define PCIE_REG_TCEKILL_PS_16M 2 +#define PCIE_REG_TCEKILL_PS_16G 3 + +#define PCIE_REG_IODA_ADDR 0x220 +#define PCIE_REG_IODA_AD_AUTOINC 0x8000000000000000ull +#define PCIE_REG_IODA_AD_TBL_MVT 0x0005000000000000ull +#define PCIE_REG_IODA_AD_TBL_PELT 0x0006000000000000ull +#define PCIE_REG_IODA_AD_TBL_PESTA 0x0007000000000000ull +#define PCIE_REG_IODA_AD_TBL_PESTB 0x0008000000000000ull +#define PCIE_REG_IODA_AD_TBL_TVT 0x0009000000000000ull +#define PCIE_REG_IODA_AD_TBL_TCE 0x000a000000000000ull +#define PCIE_REG_IODA_DATA0 0x228 +#define PCIE_REG_IODA_DATA1 0x230 + +#define PCIE_REG_LOCK2 0x240 + +#define PCIE_REG_PHB_GEN_CAP 0x250 +#define PCIE_REG_PHB_TCE_CAP 0x258 +#define PCIE_REG_PHB_IRQ_CAP 0x260 +#define PCIE_REG_PHB_EEH_CAP 0x268 + +#define PCIE_REG_PAPR_ERR_INJ_CONTROL 0x2b0 +#define PCIE_REG_PAPR_ERR_INJ_ADDR 0x2b8 +#define PCIE_REG_PAPR_ERR_INJ_MASK 0x2c0 + + +#define PCIE_REG_SYS_CFG1 0x600 +#define PCIE_REG_SYS_CFG1_CLASS_CODE 0x0000000000ffffffull + +#define IODA_TVT0_TTA_MASK 0x000fffffffff0000ull +#define IODA_TVT0_TTA_SHIFT 4 +#define IODA_TVT0_BUSNUM_VALID_MASK 0x000000000000e000ull +#define IODA_TVT0_TCE_TABLE_SIZE_MASK 0x0000000000001f00ull +#define IODA_TVT0_TCE_TABLE_SIZE_SHIFT 8 +#define IODA_TVT0_BUSNUM_VALUE_MASK 0x00000000000000ffull +#define IODA_TVT0_BUSNUM_VALID_SHIFT 0 +#define IODA_TVT1_DEVNUM_VALID 0x2000000000000000ull +#define IODA_TVT1_DEVNUM_VALUE_MASK 0x1f00000000000000ull +#define IODA_TVT1_DEVNUM_VALUE_SHIFT 56 +#define IODA_TVT1_FUNCNUM_VALID 0x0008000000000000ull +#define IODA_TVT1_FUNCNUM_VALUE_MASK 0x0007000000000000ull +#define IODA_TVT1_FUNCNUM_VALUE_SHIFT 48 +#define IODA_TVT1_IO_PAGE_SIZE_MASK 0x00001f0000000000ull +#define IODA_TVT1_IO_PAGE_SIZE_SHIFT 40 +#define IODA_TVT1_PE_NUMBER_MASK 0x000000000000003full +#define IODA_TVT1_PE_NUMBER_SHIFT 0 + +#define IODA_TVT_COUNT 64 + +/* UTL Core registers */ +#define PCIE_UTL_SYS_BUS_CONTROL 0x400 +#define PCIE_UTL_STATUS 0x408 +#define PCIE_UTL_SYS_BUS_AGENT_STATUS 0x410 +#define PCIE_UTL_SYS_BUS_AGENT_ERR_SEV 0x418 +#define PCIE_UTL_SYS_BUS_AGENT_IRQ_EN 0x420 +#define PCIE_UTL_SYS_BUS_BURST_SZ_CONF 0x440 +#define PCIE_UTL_REVISION_ID 0x448 + +#define PCIE_UTL_OUT_POST_HDR_BUF_ALLOC 0x4c0 +#define PCIE_UTL_OUT_POST_DAT_BUF_ALLOC 0x4d0 +#define PCIE_UTL_IN_POST_HDR_BUF_ALLOC 0x4e0 +#define PCIE_UTL_IN_POST_DAT_BUF_ALLOC 0x4f0 +#define PCIE_UTL_OUT_NP_BUF_ALLOC 0x500 +#define PCIE_UTL_IN_NP_BUF_ALLOC 0x510 +#define PCIE_UTL_PCIE_TAGS_ALLOC 0x520 +#define PCIE_UTL_GBIF_READ_TAGS_ALLOC 0x530 + +#define PCIE_UTL_PCIE_PORT_CONTROL 0x540 +#define PCIE_UTL_PCIE_PORT_STATUS 0x548 +#define PCIE_UTL_PCIE_PORT_ERROR_SEV 0x550 +#define PCIE_UTL_PCIE_PORT_IRQ_EN 0x558 +#define PCIE_UTL_RC_STATUS 0x560 +#define PCIE_UTL_RC_ERR_SEVERITY 0x568 +#define PCIE_UTL_RC_IRQ_EN 0x570 +#define PCIE_UTL_EP_STATUS 0x578 +#define PCIE_UTL_EP_ERR_SEVERITY 0x580 +#define PCIE_UTL_EP_ERR_IRQ_EN 0x588 + +#define PCIE_UTL_PCI_PM_CTRL1 0x590 +#define PCIE_UTL_PCI_PM_CTRL2 0x598 + +/* PCIe stack registers */ +#define PCIE_REG_SYSTEM_CONFIG1 0x600 +#define PCIE_REG_SYSTEM_CONFIG2 0x608 +#define PCIE_REG_EP_SYSTEM_CONFIG 0x618 +#define PCIE_REG_EP_FLR 0x620 +#define PCIE_REG_EP_BAR_CONFIG 0x628 +#define PCIE_REG_LINK_CONFIG 0x630 +#define PCIE_REG_PM_CONFIG 0x640 +#define PCIE_REG_DLP_CONTROL 0x650 +#define PCIE_REG_DLP_STATUS 0x658 +#define PCIE_REG_ERR_REPORT_CONTROL 0x660 +#define PCIE_REG_SLOT_CONTROL1 0x670 +#define PCIE_REG_SLOT_CONTROL2 0x678 +#define PCIE_REG_UTL_CONFIG 0x680 +#define PCIE_REG_BUFFERS_CONFIG 0x690 +#define PCIE_REG_ERROR_INJECT 0x698 +#define PCIE_REG_SRIOV_CONFIG 0x6a0 +#define PCIE_REG_PF0_SRIOV_STATUS 0x6a8 +#define PCIE_REG_PF1_SRIOV_STATUS 0x6b0 +#define PCIE_REG_PORT_NUMBER 0x700 +#define PCIE_REG_POR_SYSTEM_CONFIG 0x708 + +/* PHB internal logic registers */ +#define PCIE_REG_PHB_VERSION 0x800 +#define PCIE_REG_RESET 0x808 +#define PCIE_REG_PHB_CONTROL 0x810 +#define PCIE_REG_PHB_TIMEOUT_CONTROL1 0x878 +#define PCIE_REG_PHB_QUIESCE_DMA 0x888 +#define PCIE_REG_PHB_DMA_READ_TAG_ACTV 0x900 +#define PCIE_REG_PHB_TCE_READ_TAG_ACTV 0x908 + +/* FIR registers */ +#define PCIE_REG_LEM_FIR_ACCUM 0xc00 +#define PCIE_REG_LEM_FIR_AND_MASK 0xc08 +#define PCIE_REG_LEM_FIR_OR_MASK 0xc10 +#define PCIE_REG_LEM_ACTION0 0xc18 +#define PCIE_REG_LEM_ACTION1 0xc20 +#define PCIE_REG_LEM_ERROR_MASK 0xc30 +#define PCIE_REG_LEM_ERROR_AND_MASK 0xc38 +#define PCIE_REG_LEM_ERROR_OR_MASK 0xc40 + +/* PHB Error registers */ +#define PCIE_REG_PHB_ERR_STATUS 0xc80 +#define PCIE_REG_PHB_ERR1_STATUS 0xc88 +#define PCIE_REG_PHB_ERR_INJECT 0xc90 +#define PCIE_REG_PHB_ERR_LEM_ENABLE 0xc98 +#define PCIE_REG_PHB_ERR_IRQ_ENABLE 0xca0 +#define PCIE_REG_PHB_ERR_FREEZE_ENABLE 0xca8 +#define PCIE_REG_PHB_ERR_SIDE_ENABLE 0xcb8 +#define PCIE_REG_PHB_ERR_LOG_0 0xcc0 +#define PCIE_REG_PHB_ERR_LOG_1 0xcc8 +#define PCIE_REG_PHB_ERR_STATUS_MASK 0xcd0 +#define PCIE_REG_PHB_ERR1_STATUS_MASK 0xcd8 + +#define PCIE_REG_MMIO_ERR_STATUS 0xd00 +#define PCIE_REG_MMIO_ERR1_STATUS 0xd08 +#define PCIE_REG_MMIO_ERR_INJECT 0xd10 +#define PCIE_REG_MMIO_ERR_LEM_ENABLE 0xd18 +#define PCIE_REG_MMIO_ERR_IRQ_ENABLE 0xd20 +#define PCIE_REG_MMIO_ERR_FREEZE_ENABLE 0xd28 +#define PCIE_REG_MMIO_ERR_SIDE_ENABLE 0xd38 +#define PCIE_REG_MMIO_ERR_LOG_0 0xd40 +#define PCIE_REG_MMIO_ERR_LOG_1 0xd48 +#define PCIE_REG_MMIO_ERR_STATUS_MASK 0xd50 +#define PCIE_REG_MMIO_ERR1_STATUS_MASK 0xd58 + +#define PCIE_REG_DMA_ERR_STATUS 0xd80 +#define PCIE_REG_DMA_ERR1_STATUS 0xd88 +#define PCIE_REG_DMA_ERR_INJECT 0xd90 +#define PCIE_REG_DMA_ERR_LEM_ENABLE 0xd98 +#define PCIE_REG_DMA_ERR_IRQ_ENABLE 0xda0 +#define PCIE_REG_DMA_ERR_FREEZE_ENABLE 0xda8 +#define PCIE_REG_DMA_ERR_SIDE_ENABLE 0xdb8 +#define PCIE_REG_DMA_ERR_LOG_0 0xdc0 +#define PCIE_REG_DMA_ERR_LOG_1 0xdc8 +#define PCIE_REG_DMA_ERR_STATUS_MASK 0xdd0 +#define PCIE_REG_DMA_ERR1_STATUS_MASK 0xdd8 + +/* Shortcuts for access to the above using the PHB definitions + * with an offset + */ +#define PCIE_REG_ERR_PHB_OFFSET 0x0 +#define PCIE_REG_ERR_MMIO_OFFSET 0x80 +#define PCIE_REG_ERR_DMA_OFFSET 0x100 + +/* Debug and Trace registers */ +#define PCIE_REG_PHB_DEBUG_CONTROL0 0xe00 +#define PCIE_REG_PHB_DEBUG_STATUS0 0xe08 +#define PCIE_REG_PHB_DEBUG_CONTROL1 0xe10 +#define PCIE_REG_PHB_DEBUG_STATUS1 0xe18 +#define PCIE_REG_PHB_DEBUG_CONTROL2 0xe20 +#define PCIE_REG_PHB_DEBUG_STATUS2 0xe28 +#define PCIE_REG_PHB_DEBUG_CONTROL3 0xe30 +#define PCIE_REG_PHB_DEBUG_STATUS3 0xe38 +#define PCIE_REG_PHB_DEBUG_CONTROL4 0xe40 +#define PCIE_REG_PHB_DEBUG_STATUS4 0xe48 +#define PCIE_REG_PHB_DEBUG_CONTROL5 0xe50 +#define PCIE_REG_PHB_DEBUG_STATUS5 0xe58 +#define PCIE_REG_PHB_DEBUG_CONTROL6 0xe60 +#define PCIE_REG_PHB_DEBUG_STATUS6 0xe68 + +/* Definition for PCIe errors */ +struct wsp_pcie_err_log_data { + __u64 phb_err; + __u64 phb_err1; + __u64 phb_log0; + __u64 phb_log1; + __u64 mmio_err; + __u64 mmio_err1; + __u64 mmio_log0; + __u64 mmio_log1; + __u64 dma_err; + __u64 dma_err1; + __u64 dma_log0; + __u64 dma_log1; + __u64 utl_sys_err; + __u64 utl_port_err; + __u64 utl_rc_err; + __u64 unused; +}; + +#endif /* __WSP_PCI_H */ From f9a71e0fd1b44148d7af6ce2fecfb2cf7a4df636 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Mon, 8 Aug 2011 12:30:55 +0000 Subject: [PATCH 0188/1531] powerpc/wsp: Add MSI support for PCI on PowerEN Based on a patch by Michael Ellerman Patch was simply forward ported upstream. Jimi Xenidis Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/wsp/Makefile | 1 + arch/powerpc/platforms/wsp/ics.c | 48 +++++++++++++ arch/powerpc/platforms/wsp/ics.h | 5 ++ arch/powerpc/platforms/wsp/msi.c | 102 ++++++++++++++++++++++++++++ arch/powerpc/platforms/wsp/msi.h | 19 ++++++ 5 files changed, 175 insertions(+) create mode 100644 arch/powerpc/platforms/wsp/msi.c create mode 100644 arch/powerpc/platforms/wsp/msi.h diff --git a/arch/powerpc/platforms/wsp/Makefile b/arch/powerpc/platforms/wsp/Makefile index 84519f8d8945..a1486b436f02 100644 --- a/arch/powerpc/platforms/wsp/Makefile +++ b/arch/powerpc/platforms/wsp/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_PPC_PSR2) += psr2.o opb_pic.o obj-$(CONFIG_PPC_WSP) += scom_wsp.o obj-$(CONFIG_SMP) += smp.o scom_smp.o obj-$(CONFIG_PCI) += wsp_pci.o +obj-$(CONFIG_PCI_MSI) += msi.o \ No newline at end of file diff --git a/arch/powerpc/platforms/wsp/ics.c b/arch/powerpc/platforms/wsp/ics.c index e53bd9e7b125..576874392543 100644 --- a/arch/powerpc/platforms/wsp/ics.c +++ b/arch/powerpc/platforms/wsp/ics.c @@ -710,3 +710,51 @@ void __init wsp_init_irq(void) /* We need to patch our irq chip's EOI to point to the right ICP */ wsp_irq_chip.irq_eoi = icp_ops->eoi; } + +#ifdef CONFIG_PCI_MSI +static void wsp_ics_msi_unmask_irq(struct irq_data *d) +{ + wsp_chip_unmask_irq(d); + unmask_msi_irq(d); +} + +static unsigned int wsp_ics_msi_startup(struct irq_data *d) +{ + wsp_ics_msi_unmask_irq(d); + return 0; +} + +static void wsp_ics_msi_mask_irq(struct irq_data *d) +{ + mask_msi_irq(d); + wsp_chip_mask_irq(d); +} + +/* + * we do it this way because we reassinge default EOI handling in + * irq_init() above + */ +static void wsp_ics_eoi(struct irq_data *data) +{ + wsp_irq_chip.irq_eoi(data); +} + +static struct irq_chip wsp_ics_msi = { + .name = "WSP ICS MSI", + .irq_startup = wsp_ics_msi_startup, + .irq_mask = wsp_ics_msi_mask_irq, + .irq_unmask = wsp_ics_msi_unmask_irq, + .irq_eoi = wsp_ics_eoi, + .irq_set_affinity = wsp_chip_set_affinity +}; + +void wsp_ics_set_msi_chip(unsigned int irq) +{ + irq_set_chip(irq, &wsp_ics_msi); +} + +void wsp_ics_set_std_chip(unsigned int irq) +{ + irq_set_chip(irq, &wsp_irq_chip); +} +#endif /* CONFIG_PCI_MSI */ diff --git a/arch/powerpc/platforms/wsp/ics.h b/arch/powerpc/platforms/wsp/ics.h index e34d53102640..07b644e0cf97 100644 --- a/arch/powerpc/platforms/wsp/ics.h +++ b/arch/powerpc/platforms/wsp/ics.h @@ -17,4 +17,9 @@ extern void wsp_init_irq(void); extern int wsp_ics_alloc_irq(struct device_node *dn, int num); extern void wsp_ics_free_irq(struct device_node *dn, unsigned int irq); +#ifdef CONFIG_PCI_MSI +extern void wsp_ics_set_msi_chip(unsigned int irq); +extern void wsp_ics_set_std_chip(unsigned int irq); +#endif /* CONFIG_PCI_MSI */ + #endif /* __ICS_H */ diff --git a/arch/powerpc/platforms/wsp/msi.c b/arch/powerpc/platforms/wsp/msi.c new file mode 100644 index 000000000000..380882f27add --- /dev/null +++ b/arch/powerpc/platforms/wsp/msi.c @@ -0,0 +1,102 @@ +/* + * Copyright 2011 Michael Ellerman, IBM Corp. + * + * 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 "msi.h" +#include "ics.h" +#include "wsp_pci.h" + +/* Magic addresses for 32 & 64-bit MSIs with hardcoded MVE 0 */ +#define MSI_ADDR_32 0xFFFF0000ul +#define MSI_ADDR_64 0x1000000000000000ul + +int wsp_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) +{ + struct pci_controller *phb; + struct msi_desc *entry; + struct msi_msg msg; + unsigned int virq; + int hwirq; + + phb = pci_bus_to_host(dev->bus); + if (!phb) + return -ENOENT; + + entry = list_first_entry(&dev->msi_list, struct msi_desc, list); + if (entry->msi_attrib.is_64) { + msg.address_lo = 0; + msg.address_hi = MSI_ADDR_64 >> 32; + } else { + msg.address_lo = MSI_ADDR_32; + msg.address_hi = 0; + } + + list_for_each_entry(entry, &dev->msi_list, list) { + hwirq = wsp_ics_alloc_irq(phb->dn, 1); + if (hwirq < 0) { + dev_warn(&dev->dev, "wsp_msi: hwirq alloc failed!\n"); + return hwirq; + } + + virq = irq_create_mapping(NULL, hwirq); + if (virq == NO_IRQ) { + dev_warn(&dev->dev, "wsp_msi: virq alloc failed!\n"); + return -1; + } + + dev_dbg(&dev->dev, "wsp_msi: allocated irq %#x/%#x\n", + hwirq, virq); + + wsp_ics_set_msi_chip(virq); + irq_set_msi_desc(virq, entry); + msg.data = hwirq & XIVE_ADDR_MASK; + write_msi_msg(virq, &msg); + } + + return 0; +} + +void wsp_teardown_msi_irqs(struct pci_dev *dev) +{ + struct pci_controller *phb; + struct msi_desc *entry; + int hwirq; + + phb = pci_bus_to_host(dev->bus); + + dev_dbg(&dev->dev, "wsp_msi: tearing down msi irqs\n"); + + list_for_each_entry(entry, &dev->msi_list, list) { + if (entry->irq == NO_IRQ) + continue; + + irq_set_msi_desc(entry->irq, NULL); + wsp_ics_set_std_chip(entry->irq); + + hwirq = virq_to_hw(entry->irq); + /* In this order to avoid racing with irq_create_mapping() */ + irq_dispose_mapping(entry->irq); + wsp_ics_free_irq(phb->dn, hwirq); + } +} + +void wsp_setup_phb_msi(struct pci_controller *phb) +{ + /* Create a single MVE at offset 0 that matches everything */ + out_be64(phb->cfg_data + PCIE_REG_IODA_ADDR, PCIE_REG_IODA_AD_TBL_MVT); + out_be64(phb->cfg_data + PCIE_REG_IODA_DATA0, 1ull << 63); + + ppc_md.setup_msi_irqs = wsp_setup_msi_irqs; + ppc_md.teardown_msi_irqs = wsp_teardown_msi_irqs; +} diff --git a/arch/powerpc/platforms/wsp/msi.h b/arch/powerpc/platforms/wsp/msi.h new file mode 100644 index 000000000000..0ab27b71b24d --- /dev/null +++ b/arch/powerpc/platforms/wsp/msi.h @@ -0,0 +1,19 @@ +/* + * Copyright 2011 Michael Ellerman, IBM Corp. + * + * 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 __WSP_MSI_H +#define __WSP_MSI_H + +#ifdef CONFIG_PCI_MSI +extern void wsp_setup_phb_msi(struct pci_controller *phb); +#else +static inline void wsp_setup_phb_msi(struct pci_controller *phb) { } +#endif + +#endif /* __WSP_MSI_H */ From 9c57a32b2f1615e0ad77e9d3b68fd720f43430da Mon Sep 17 00:00:00 2001 From: Tony Breeds Date: Wed, 10 Aug 2011 20:16:54 +0000 Subject: [PATCH 0189/1531] powerpc/4xx/pci: Add __init annotations for *init_port_hw() functions. The various port_init_hw methods of ppc4xx_pciex_hwops should have been marked __init and when I added ppc4xx_pciex_port_reset_sdr(), which is __init. This added many section mismatch warnings like: WARNING: arch/powerpc/sysdev/built-in.o(.text+0x5c68): Section mismatch in reference from the function ppc440spe_pciex_init_port_hw() to the function .init.text:ppc4xx_pciex_port_reset_sdr() The function ppc440spe_pciex_init_port_hw() references the function __init ppc4xx_pciex_port_reset_sdr(). This is often because ppc440spe_pciex_init_port_hw lacks a __init annotation or the annotation of ppc4xx_pciex_port_reset_sdr is wrong. Trivial patch to silence those warnings. Reported-By: Stephen Rothwell Signed-off-by: Tony Breeds Yours Tony Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/sysdev/ppc4xx_pci.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/sysdev/ppc4xx_pci.c b/arch/powerpc/sysdev/ppc4xx_pci.c index 2ec046d0a394..862f11b3821e 100644 --- a/arch/powerpc/sysdev/ppc4xx_pci.c +++ b/arch/powerpc/sysdev/ppc4xx_pci.c @@ -834,7 +834,7 @@ static int __init ppc440spe_pciex_core_init(struct device_node *np) return 3; } -static int ppc440spe_pciex_init_port_hw(struct ppc4xx_pciex_port *port) +static int __init ppc440spe_pciex_init_port_hw(struct ppc4xx_pciex_port *port) { u32 val = 1 << 24; @@ -872,12 +872,12 @@ static int ppc440spe_pciex_init_port_hw(struct ppc4xx_pciex_port *port) return ppc4xx_pciex_port_reset_sdr(port); } -static int ppc440speA_pciex_init_port_hw(struct ppc4xx_pciex_port *port) +static int __init ppc440speA_pciex_init_port_hw(struct ppc4xx_pciex_port *port) { return ppc440spe_pciex_init_port_hw(port); } -static int ppc440speB_pciex_init_port_hw(struct ppc4xx_pciex_port *port) +static int __init ppc440speB_pciex_init_port_hw(struct ppc4xx_pciex_port *port) { int rc = ppc440spe_pciex_init_port_hw(port); @@ -936,7 +936,7 @@ static int __init ppc460ex_pciex_core_init(struct device_node *np) return 2; } -static int ppc460ex_pciex_init_port_hw(struct ppc4xx_pciex_port *port) +static int __init ppc460ex_pciex_init_port_hw(struct ppc4xx_pciex_port *port) { u32 val; u32 utlset1; @@ -1126,7 +1126,7 @@ static int __init ppc460sx_pciex_core_init(struct device_node *np) return 2; } -static int ppc460sx_pciex_init_port_hw(struct ppc4xx_pciex_port *port) +static int __init ppc460sx_pciex_init_port_hw(struct ppc4xx_pciex_port *port) { if (port->endpoint) @@ -1218,7 +1218,7 @@ static void ppc405ex_pcie_phy_reset(struct ppc4xx_pciex_port *port) mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, 0x00101000); } -static int ppc405ex_pciex_init_port_hw(struct ppc4xx_pciex_port *port) +static int __init ppc405ex_pciex_init_port_hw(struct ppc4xx_pciex_port *port) { u32 val; From 0330581ab3b9002d55ee66f377ccbbb742175c01 Mon Sep 17 00:00:00 2001 From: Tang Yuantian Date: Tue, 16 Aug 2011 19:51:33 +0000 Subject: [PATCH 0190/1531] powerpc/mm: Fix the call trace when resumed from hibernation In SMP mode, the kernel would produce call trace when resumed from hibernation. The reason is when the function destroy_context is called to drop the resuming mm context, the mm->context.active is 1 which is wrong and should be zero. We pass the current->active_mm as previous mm context to function switch_mmu_context to decrease the context.active by 1. In UP mode, there is no effect. Signed-off-by: Tang Yuantian Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/swsusp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/swsusp.c b/arch/powerpc/kernel/swsusp.c index aa17b76dd427..641f9adc6205 100644 --- a/arch/powerpc/kernel/swsusp.c +++ b/arch/powerpc/kernel/swsusp.c @@ -33,6 +33,6 @@ void save_processor_state(void) void restore_processor_state(void) { #ifdef CONFIG_PPC32 - switch_mmu_context(NULL, current->active_mm); + switch_mmu_context(current->active_mm, current->active_mm); #endif } From 9c740025c51a26ab00192cfc464064d4ccbfe3fc Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Sun, 14 Aug 2011 14:30:30 +0000 Subject: [PATCH 0191/1531] powerpc/pseries: Avoid spurious error during hotplug CPU add During hotplug CPU add we get the following error: Unexpected Error (0) returned from configure-connector ibm,configure-connector returns 0 for configuration complete, so catch this and avoid the error. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt Cc: --- arch/powerpc/platforms/pseries/dlpar.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/powerpc/platforms/pseries/dlpar.c b/arch/powerpc/platforms/pseries/dlpar.c index e9be25bc571b..0f1b706506ed 100644 --- a/arch/powerpc/platforms/pseries/dlpar.c +++ b/arch/powerpc/platforms/pseries/dlpar.c @@ -112,6 +112,7 @@ void dlpar_free_cc_nodes(struct device_node *dn) dlpar_free_one_cc_node(dn); } +#define COMPLETE 0 #define NEXT_SIBLING 1 #define NEXT_CHILD 2 #define NEXT_PROPERTY 3 @@ -158,6 +159,9 @@ struct device_node *dlpar_configure_connector(u32 drc_index) spin_unlock(&rtas_data_buf_lock); switch (rc) { + case COMPLETE: + break; + case NEXT_SIBLING: dn = dlpar_parse_cc_node(ccwa); if (!dn) From cf01a404d96f9bf6f514074579f0a2d355bfa38e Mon Sep 17 00:00:00 2001 From: Arnaud Lacombe Date: Thu, 25 Aug 2011 06:07:13 +0000 Subject: [PATCH 0192/1531] powerpc/xics: Add __init to marker icp_native_init() This should fix the following warning: LD arch/powerpc/sysdev/xics/built-in.o WARNING: arch/powerpc/sysdev/xics/built-in.o(.text+0x1310): Section mismatch in reference from the function .icp_native_init() to the function .init.text:.icp_native_init_one_node() The function .icp_native_init() references the function __init .icp_native_init_one_node(). This is often because .icp_native_init lacks a __init annotation or the annotation of .icp_native_init_one_node is wrong. icp_native_init() is only referenced in `arch/powerpc/sysdev/xics/xics-common.c' by xics_init() which is itself marked with __init. = not built-tested = Reported-by: Timur Tabi Signed-off-by: Arnaud Lacombe Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/sysdev/xics/icp-native.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/sysdev/xics/icp-native.c b/arch/powerpc/sysdev/xics/icp-native.c index 50e32afe392e..4c79b6fbee1c 100644 --- a/arch/powerpc/sysdev/xics/icp-native.c +++ b/arch/powerpc/sysdev/xics/icp-native.c @@ -276,7 +276,7 @@ static const struct icp_ops icp_native_ops = { #endif }; -int icp_native_init(void) +int __init icp_native_init(void) { struct device_node *np; u32 indx = 0; From 8feaa43494cee5e938fd5a57b9e9bf1c827e6ccd Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Fri, 26 Aug 2011 10:36:31 +0000 Subject: [PATCH 0193/1531] powerpc/eeh: Fix /proc/ppc64/eeh creation Since commit 188917e183cf9ad0374b571006d0fc6d48a7f447, /proc/ppc64 is a symlink to /proc/powerpc/. That means that creating /proc/ppc64/eeh will end up with a unaccessible file, that is not listed under /proc/powerpc/ and, then, not listed under /proc/ppc64/. Creating /proc/powerpc/eeh fixes that problem and maintain the compatibility intended with the ppc64 symlink. Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: Benjamin Herrenschmidt Cc: [3.x] --- arch/powerpc/platforms/pseries/eeh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index ada6e07532ec..d42f37d8a440 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -1338,7 +1338,7 @@ static const struct file_operations proc_eeh_operations = { static int __init eeh_init_proc(void) { if (machine_is(pseries)) - proc_create("ppc64/eeh", 0, NULL, &proc_eeh_operations); + proc_create("powerpc/eeh", 0, NULL, &proc_eeh_operations); return 0; } __initcall(eeh_init_proc); From c26afe9e8591f306d79aab8071f1d34e4f60b700 Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Wed, 31 Aug 2011 06:32:26 +0000 Subject: [PATCH 0194/1531] powerpc/ps3: Add gelic udbg driver Add a new udbg driver for the PS3 gelic Ehthernet device. This driver shares only a few stucture and constant definitions with the gelic Ethernet device driver, so is implemented as a stand-alone driver with no dependencies on the gelic Ethernet device driver. Signed-off-by: Hector Martin Signed-off-by: Andre Heider Signed-off-by: Geoff Levand Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/Kconfig.debug | 8 + arch/powerpc/include/asm/udbg.h | 1 + arch/powerpc/kernel/udbg.c | 2 + arch/powerpc/platforms/ps3/Kconfig | 12 ++ arch/powerpc/platforms/ps3/Makefile | 1 + arch/powerpc/platforms/ps3/gelic_udbg.c | 273 ++++++++++++++++++++++++ drivers/net/ps3_gelic_net.c | 3 + drivers/net/ps3_gelic_net.h | 6 + 8 files changed, 306 insertions(+) create mode 100644 arch/powerpc/platforms/ps3/gelic_udbg.c diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug index 067cb8480747..ab2335f160a0 100644 --- a/arch/powerpc/Kconfig.debug +++ b/arch/powerpc/Kconfig.debug @@ -258,6 +258,14 @@ config PPC_EARLY_DEBUG_WSP depends on PPC_WSP select PPC_UDBG_16550 +config PPC_EARLY_DEBUG_PS3GELIC + bool "Early debugging through the PS3 Ethernet port" + depends on PPC_PS3 + select PS3GELIC_UDBG + help + Select this to enable early debugging for the PlayStation3 via + UDP broadcasts sent out through the Ethernet port. + endchoice config PPC_EARLY_DEBUG_HVSI_VTERMNO diff --git a/arch/powerpc/include/asm/udbg.h b/arch/powerpc/include/asm/udbg.h index 93e05d1b34b2..7cf796fa03f2 100644 --- a/arch/powerpc/include/asm/udbg.h +++ b/arch/powerpc/include/asm/udbg.h @@ -54,6 +54,7 @@ extern void __init udbg_init_40x_realmode(void); extern void __init udbg_init_cpm(void); extern void __init udbg_init_usbgecko(void); extern void __init udbg_init_wsp(void); +extern void __init udbg_init_ps3gelic(void); #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_UDBG_H */ diff --git a/arch/powerpc/kernel/udbg.c b/arch/powerpc/kernel/udbg.c index faa82c1f3f68..5b3e98e03158 100644 --- a/arch/powerpc/kernel/udbg.c +++ b/arch/powerpc/kernel/udbg.c @@ -67,6 +67,8 @@ void __init udbg_early_init(void) udbg_init_usbgecko(); #elif defined(CONFIG_PPC_EARLY_DEBUG_WSP) udbg_init_wsp(); +#elif defined(CONFIG_PPC_EARLY_DEBUG_PS3GELIC) + udbg_init_ps3gelic(); #endif #ifdef CONFIG_PPC_EARLY_DEBUG diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig index dfe316b161a9..476d9d9b2405 100644 --- a/arch/powerpc/platforms/ps3/Kconfig +++ b/arch/powerpc/platforms/ps3/Kconfig @@ -148,4 +148,16 @@ config PS3_LPM profiling support of the Cell processor with programs like oprofile and perfmon2, then say Y or M, otherwise say N. +config PS3GELIC_UDBG + bool "PS3 udbg output via UDP broadcasts on Ethernet" + depends on PPC_PS3 + help + Enables udbg early debugging output by sending broadcast UDP + via the Ethernet port (UDP port number 18194). + + This driver uses a trivial implementation and is independent + from the main network driver. + + If in doubt, say N here. + endmenu diff --git a/arch/powerpc/platforms/ps3/Makefile b/arch/powerpc/platforms/ps3/Makefile index ac1bdf844eca..02b9e636dab7 100644 --- a/arch/powerpc/platforms/ps3/Makefile +++ b/arch/powerpc/platforms/ps3/Makefile @@ -2,6 +2,7 @@ obj-y += setup.o mm.o time.o hvcall.o htab.o repository.o obj-y += interrupt.o exports.o os-area.o obj-y += system-bus.o +obj-$(CONFIG_PS3GELIC_UDBG) += gelic_udbg.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_SPU_BASE) += spu.o obj-y += device-init.o diff --git a/arch/powerpc/platforms/ps3/gelic_udbg.c b/arch/powerpc/platforms/ps3/gelic_udbg.c new file mode 100644 index 000000000000..20b46a19a48f --- /dev/null +++ b/arch/powerpc/platforms/ps3/gelic_udbg.c @@ -0,0 +1,273 @@ +/* + * udbg debug output routine via GELIC UDP broadcasts + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2006, 2007 Sony Corporation + * Copyright (C) 2010 Hector Martin + * Copyright (C) 2011 Andre Heider + * + * 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 + +#define GELIC_BUS_ID 1 +#define GELIC_DEVICE_ID 0 +#define GELIC_DEBUG_PORT 18194 +#define GELIC_MAX_MESSAGE_SIZE 1000 + +#define GELIC_LV1_GET_MAC_ADDRESS 1 +#define GELIC_LV1_GET_VLAN_ID 4 +#define GELIC_LV1_VLAN_TX_ETHERNET_0 2 + +#define GELIC_DESCR_DMA_STAT_MASK 0xf0000000 +#define GELIC_DESCR_DMA_CARDOWNED 0xa0000000 + +#define GELIC_DESCR_TX_DMA_IKE 0x00080000 +#define GELIC_DESCR_TX_DMA_NO_CHKSUM 0x00000000 +#define GELIC_DESCR_TX_DMA_FRAME_TAIL 0x00040000 + +#define GELIC_DESCR_DMA_CMD_NO_CHKSUM (GELIC_DESCR_DMA_CARDOWNED | \ + GELIC_DESCR_TX_DMA_IKE | \ + GELIC_DESCR_TX_DMA_NO_CHKSUM) + +static u64 bus_addr; + +struct gelic_descr { + /* as defined by the hardware */ + __be32 buf_addr; + __be32 buf_size; + __be32 next_descr_addr; + __be32 dmac_cmd_status; + __be32 result_size; + __be32 valid_size; /* all zeroes for tx */ + __be32 data_status; + __be32 data_error; /* all zeroes for tx */ +} __attribute__((aligned(32))); + +struct debug_block { + struct gelic_descr descr; + u8 pkt[1520]; +} __packed; + +struct ethhdr { + u8 dest[6]; + u8 src[6]; + u16 type; +} __packed; + +struct vlantag { + u16 vlan; + u16 subtype; +} __packed; + +struct iphdr { + u8 ver_len; + u8 dscp_ecn; + u16 total_length; + u16 ident; + u16 frag_off_flags; + u8 ttl; + u8 proto; + u16 checksum; + u32 src; + u32 dest; +} __packed; + +struct udphdr { + u16 src; + u16 dest; + u16 len; + u16 checksum; +} __packed; + +static __iomem struct ethhdr *h_eth; +static __iomem struct vlantag *h_vlan; +static __iomem struct iphdr *h_ip; +static __iomem struct udphdr *h_udp; + +static __iomem char *pmsg; +static __iomem char *pmsgc; + +static __iomem struct debug_block dbg __attribute__((aligned(32))); + +static int header_size; + +static void map_dma_mem(int bus_id, int dev_id, void *start, size_t len, + u64 *real_bus_addr) +{ + s64 result; + u64 real_addr = ((u64)start) & 0x0fffffffffffffffUL; + u64 real_end = real_addr + len; + u64 map_start = real_addr & ~0xfff; + u64 map_end = (real_end + 0xfff) & ~0xfff; + u64 bus_addr = 0; + + u64 flags = 0xf800000000000000UL; + + result = lv1_allocate_device_dma_region(bus_id, dev_id, + map_end - map_start, 12, 0, + &bus_addr); + if (result) + lv1_panic(0); + + result = lv1_map_device_dma_region(bus_id, dev_id, map_start, + bus_addr, map_end - map_start, + flags); + if (result) + lv1_panic(0); + + *real_bus_addr = bus_addr + real_addr - map_start; +} + +static int unmap_dma_mem(int bus_id, int dev_id, u64 bus_addr, size_t len) +{ + s64 result; + u64 real_bus_addr; + + real_bus_addr = bus_addr & ~0xfff; + len += bus_addr - real_bus_addr; + len = (len + 0xfff) & ~0xfff; + + result = lv1_unmap_device_dma_region(bus_id, dev_id, real_bus_addr, + len); + if (result) + return result; + + return lv1_free_device_dma_region(bus_id, dev_id, real_bus_addr); +} + +static void gelic_debug_init(void) +{ + s64 result; + u64 v2; + u64 mac; + u64 vlan_id; + + result = lv1_open_device(GELIC_BUS_ID, GELIC_DEVICE_ID, 0); + if (result) + lv1_panic(0); + + map_dma_mem(GELIC_BUS_ID, GELIC_DEVICE_ID, &dbg, sizeof(dbg), + &bus_addr); + + memset(&dbg, 0, sizeof(dbg)); + + dbg.descr.buf_addr = bus_addr + offsetof(struct debug_block, pkt); + + wmb(); + + result = lv1_net_control(GELIC_BUS_ID, GELIC_DEVICE_ID, + GELIC_LV1_GET_MAC_ADDRESS, 0, 0, 0, + &mac, &v2); + if (result) + lv1_panic(0); + + mac <<= 16; + + h_eth = (struct ethhdr *)dbg.pkt; + + memset(&h_eth->dest, 0xff, 6); + memcpy(&h_eth->src, &mac, 6); + + header_size = sizeof(struct ethhdr); + + result = lv1_net_control(GELIC_BUS_ID, GELIC_DEVICE_ID, + GELIC_LV1_GET_VLAN_ID, + GELIC_LV1_VLAN_TX_ETHERNET_0, 0, 0, + &vlan_id, &v2); + if (!result) { + h_eth->type = 0x8100; + + header_size += sizeof(struct vlantag); + h_vlan = (struct vlantag *)(h_eth + 1); + h_vlan->vlan = vlan_id; + h_vlan->subtype = 0x0800; + h_ip = (struct iphdr *)(h_vlan + 1); + } else { + h_eth->type = 0x0800; + h_ip = (struct iphdr *)(h_eth + 1); + } + + header_size += sizeof(struct iphdr); + h_ip->ver_len = 0x45; + h_ip->ttl = 10; + h_ip->proto = 0x11; + h_ip->src = 0x00000000; + h_ip->dest = 0xffffffff; + + header_size += sizeof(struct udphdr); + h_udp = (struct udphdr *)(h_ip + 1); + h_udp->src = GELIC_DEBUG_PORT; + h_udp->dest = GELIC_DEBUG_PORT; + + pmsgc = pmsg = (char *)(h_udp + 1); +} + +static void gelic_debug_shutdown(void) +{ + if (bus_addr) + unmap_dma_mem(GELIC_BUS_ID, GELIC_DEVICE_ID, + bus_addr, sizeof(dbg)); + lv1_close_device(GELIC_BUS_ID, GELIC_DEVICE_ID); +} + +static void gelic_sendbuf(int msgsize) +{ + u16 *p; + u32 sum; + int i; + + dbg.descr.buf_size = header_size + msgsize; + h_ip->total_length = msgsize + sizeof(struct udphdr) + + sizeof(struct iphdr); + h_udp->len = msgsize + sizeof(struct udphdr); + + h_ip->checksum = 0; + sum = 0; + p = (u16 *)h_ip; + for (i = 0; i < 5; i++) + sum += *p++; + h_ip->checksum = ~(sum + (sum >> 16)); + + dbg.descr.dmac_cmd_status = GELIC_DESCR_DMA_CMD_NO_CHKSUM | + GELIC_DESCR_TX_DMA_FRAME_TAIL; + dbg.descr.result_size = 0; + dbg.descr.data_status = 0; + + wmb(); + + lv1_net_start_tx_dma(GELIC_BUS_ID, GELIC_DEVICE_ID, bus_addr, 0); + + while ((dbg.descr.dmac_cmd_status & GELIC_DESCR_DMA_STAT_MASK) == + GELIC_DESCR_DMA_CARDOWNED) + cpu_relax(); +} + +static void ps3gelic_udbg_putc(char ch) +{ + *pmsgc++ = ch; + if (ch == '\n' || (pmsgc-pmsg) >= GELIC_MAX_MESSAGE_SIZE) { + gelic_sendbuf(pmsgc-pmsg); + pmsgc = pmsg; + } +} + +void __init udbg_init_ps3gelic(void) +{ + gelic_debug_init(); + udbg_putc = ps3gelic_udbg_putc; +} + +void udbg_shutdown_ps3gelic(void) +{ + udbg_putc = NULL; + gelic_debug_shutdown(); +} +EXPORT_SYMBOL(udbg_shutdown_ps3gelic); diff --git a/drivers/net/ps3_gelic_net.c b/drivers/net/ps3_gelic_net.c index d82a82d9870c..e743c9418ac9 100644 --- a/drivers/net/ps3_gelic_net.c +++ b/drivers/net/ps3_gelic_net.c @@ -1674,6 +1674,9 @@ static int __devinit ps3_gelic_driver_probe(struct ps3_system_bus_device *dev) int result; pr_debug("%s: called\n", __func__); + + udbg_shutdown_ps3gelic(); + result = ps3_open_hv_device(dev); if (result) { diff --git a/drivers/net/ps3_gelic_net.h b/drivers/net/ps3_gelic_net.h index d3fadfbc3bcc..a93df6ac1909 100644 --- a/drivers/net/ps3_gelic_net.h +++ b/drivers/net/ps3_gelic_net.h @@ -359,6 +359,12 @@ static inline void *port_priv(struct gelic_port *port) return port->priv; } +#ifdef CONFIG_PPC_EARLY_DEBUG_PS3GELIC +extern void udbg_shutdown_ps3gelic(void); +#else +static inline void udbg_shutdown_ps3gelic(void) {} +#endif + extern int gelic_card_set_irq_mask(struct gelic_card *card, u64 mask); /* shared netdev ops */ extern void gelic_card_up(struct gelic_card *card); From a200d8e44649de2cbb39de95f42ad4ef5dc8dc22 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Sun, 24 Jul 2011 16:33:12 +0000 Subject: [PATCH 0195/1531] powerpc/numa: Enable SD_WAKE_AFFINE in node definition When chasing a performance issue on ppc64, I noticed tasks communicating via a pipe would often end up on different nodes. It turns out SD_WAKE_AFFINE is not set in our node defition. Commit 9fcd18c9e63e (sched: re-tune balancing) enabled SD_WAKE_AFFINE in the node definition for x86 and we need a similar change for ppc64. I used lmbench lat_ctx and perf bench pipe to verify this fix. Each benchmark was run 10 times and the average taken. lmbench lat_ctx: before: 66565 ops/sec after: 204700 ops/sec 3.1x faster perf bench pipe: before: 5.6570 usecs after: 1.3470 usecs 4.2x faster Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/topology.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/topology.h b/arch/powerpc/include/asm/topology.h index 7ef0d90defc8..6a7e7251cc02 100644 --- a/arch/powerpc/include/asm/topology.h +++ b/arch/powerpc/include/asm/topology.h @@ -73,7 +73,7 @@ static inline int pcibus_to_node(struct pci_bus *bus) | 1*SD_BALANCE_EXEC \ | 1*SD_BALANCE_FORK \ | 0*SD_BALANCE_WAKE \ - | 0*SD_WAKE_AFFINE \ + | 1*SD_WAKE_AFFINE \ | 0*SD_PREFER_LOCAL \ | 0*SD_SHARE_CPUPOWER \ | 0*SD_POWERSAVINGS_BALANCE \ From 590e4d857153c5d4cf86052cdfd42cf9b0779841 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Sun, 24 Jul 2011 16:33:13 +0000 Subject: [PATCH 0196/1531] sched: Allow SD_NODES_PER_DOMAIN to be overridden We want to override the default value of SD_NODES_PER_DOMAIN on ppc64, so move it into linux/topology.h. Signed-off-by: Anton Blanchard Acked-by: Peter Zijlstra Signed-off-by: Benjamin Herrenschmidt --- include/linux/topology.h | 4 ++++ kernel/sched.c | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/linux/topology.h b/include/linux/topology.h index fc839bfa7935..e26db031303b 100644 --- a/include/linux/topology.h +++ b/include/linux/topology.h @@ -201,6 +201,10 @@ int arch_update_cpu_topology(void); .balance_interval = 64, \ } +#ifndef SD_NODES_PER_DOMAIN +#define SD_NODES_PER_DOMAIN 16 +#endif + #ifdef CONFIG_SCHED_BOOK #ifndef SD_BOOK_INIT #error Please define an appropriate SD_BOOK_INIT in include/asm/topology.h!!! diff --git a/kernel/sched.c b/kernel/sched.c index ec5f472bc5b9..d258b2d9a66d 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -6947,8 +6947,6 @@ static int __init isolated_cpu_setup(char *str) __setup("isolcpus=", isolated_cpu_setup); -#define SD_NODES_PER_DOMAIN 16 - #ifdef CONFIG_NUMA /** From d4761ad2ef18ec2c9a0037d6649c0afc4a7b907d Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Sun, 24 Jul 2011 16:33:14 +0000 Subject: [PATCH 0197/1531] powerpc/numa: Increase SD_NODES_PER_DOMAIN to 32. The largest POWER7 boxes have 32 nodes. SD_NODES_PER_DOMAIN groups nodes into chunks of 16 and adds a global balancing domain (SD_ALLNODES) above it. If we bump SD_NODES_PER_DOMAIN to 32, then we avoid this extra level of balancing on our largest boxes. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/topology.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/powerpc/include/asm/topology.h b/arch/powerpc/include/asm/topology.h index 6a7e7251cc02..d1c1d312f808 100644 --- a/arch/powerpc/include/asm/topology.h +++ b/arch/powerpc/include/asm/topology.h @@ -28,6 +28,12 @@ struct device_node; */ #define RECLAIM_DISTANCE 10 +/* + * Avoid creating an extra level of balancing (SD_ALLNODES) on the largest + * POWER7 boxes which have a maximum of 32 nodes. + */ +#define SD_NODES_PER_DOMAIN 32 + #include static inline int cpu_to_node(int cpu) From 7bebcf0925f09224393a8992af706fa39aa10395 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Sun, 24 Jul 2011 16:33:15 +0000 Subject: [PATCH 0198/1531] powerpc/numa: Disable NEWIDLE balancing at node level On big POWER7 boxes we see large amounts of CPU time in system processes like workqueue and watchdog kernel threads. We currently rebalance the entire machine each time a task goes idle and this is very expensive on large machines. Disable newidle balancing at the node level and rely on the scheduler tick to rebalance across nodes. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/topology.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/topology.h b/arch/powerpc/include/asm/topology.h index d1c1d312f808..13efa4d95e76 100644 --- a/arch/powerpc/include/asm/topology.h +++ b/arch/powerpc/include/asm/topology.h @@ -75,7 +75,7 @@ static inline int pcibus_to_node(struct pci_bus *bus) .forkexec_idx = 0, \ \ .flags = 1*SD_LOAD_BALANCE \ - | 1*SD_BALANCE_NEWIDLE \ + | 0*SD_BALANCE_NEWIDLE \ | 1*SD_BALANCE_EXEC \ | 1*SD_BALANCE_FORK \ | 0*SD_BALANCE_WAKE \ From e377bc5d49fdbcb5f0e559b644d806a15454d407 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Sun, 24 Jul 2011 16:33:16 +0000 Subject: [PATCH 0199/1531] powerpc/numa: Remove duplicate RECLAIM_DISTANCE definition We have two identical definitions of RECLAIM_DISTANCE, looks like the patch got applied twice. Remove one. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/topology.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/arch/powerpc/include/asm/topology.h b/arch/powerpc/include/asm/topology.h index 13efa4d95e76..1e104af08483 100644 --- a/arch/powerpc/include/asm/topology.h +++ b/arch/powerpc/include/asm/topology.h @@ -18,16 +18,6 @@ struct device_node; */ #define RECLAIM_DISTANCE 10 -/* - * Before going off node we want the VM to try and reclaim from the local - * node. It does this if the remote distance is larger than RECLAIM_DISTANCE. - * With the default REMOTE_DISTANCE of 20 and the default RECLAIM_DISTANCE of - * 20, we never reclaim and go off node straight away. - * - * To fix this we choose a smaller value of RECLAIM_DISTANCE. - */ -#define RECLAIM_DISTANCE 10 - /* * Avoid creating an extra level of balancing (SD_ALLNODES) on the largest * POWER7 boxes which have a maximum of 32 nodes. From 6083184269fd723affca4f6340e491950267622a Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Wed, 10 Aug 2011 20:44:21 +0000 Subject: [PATCH 0200/1531] powerpc/numa: Remove double of_node_put in hot_add_node_scn_to_nid During memory hotplug testing, I got the following warning: ERROR: Bad of_node_put() on /memory@0 of_node_release kref_put of_node_put of_find_node_by_type hot_add_node_scn_to_nid hot_add_scn_to_nid memory_add_physaddr_to_nid ... of_find_node_by_type() loop does the of_node_put for us so we only need the handle the case where we terminate the loop early. As suggested by Stephen Rothwell we can do the of_node_put unconditionally outside of the loop since of_node_put handles a NULL argument fine. Signed-off-by: Anton Blanchard Cc: stable@kernel.org Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/mm/numa.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index 2164006fe170..2c1ae7a5fb53 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -1214,11 +1214,12 @@ int hot_add_node_scn_to_nid(unsigned long scn_addr) break; } - of_node_put(memory); if (nid >= 0) break; } + of_node_put(memory); + return nid; } From 94db7c5e14f44b943febe54e089d077cd983d284 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Wed, 10 Aug 2011 20:44:22 +0000 Subject: [PATCH 0201/1531] powerpc: Use for_each_node_by_type instead of open coding it Use for_each_node_by_type instead of open coding it. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/machine_kexec_64.c | 3 +-- arch/powerpc/kernel/setup_64.c | 2 +- arch/powerpc/mm/numa.c | 10 +++++----- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/arch/powerpc/kernel/machine_kexec_64.c b/arch/powerpc/kernel/machine_kexec_64.c index 583af70c4b14..26ccbf77dd41 100644 --- a/arch/powerpc/kernel/machine_kexec_64.c +++ b/arch/powerpc/kernel/machine_kexec_64.c @@ -74,8 +74,7 @@ int default_machine_kexec_prepare(struct kimage *image) } /* We also should not overwrite the tce tables */ - for (node = of_find_node_by_type(NULL, "pci"); node != NULL; - node = of_find_node_by_type(node, "pci")) { + for_each_node_by_type(node, "pci") { basep = of_get_property(node, "linux,tce-base", NULL); sizep = of_get_property(node, "linux,tce-size", NULL); if (basep == NULL || sizep == NULL) diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index aebef1320ed7..eade1fd8ee2e 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c @@ -278,7 +278,7 @@ static void __init initialize_cache_info(void) DBG(" -> initialize_cache_info()\n"); - for (np = NULL; (np = of_find_node_by_type(np, "cpu"));) { + for_each_node_by_type(np, "cpu") { num_cpus += 1; /* We're assuming *all* of the CPUs have the same diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index 2c1ae7a5fb53..00cc090cd1dc 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -710,7 +710,7 @@ static void __init parse_drconf_memory(struct device_node *memory) static int __init parse_numa_properties(void) { struct device_node *cpu = NULL; - struct device_node *memory = NULL; + struct device_node *memory; int default_nid = 0; unsigned long i; @@ -750,8 +750,8 @@ static int __init parse_numa_properties(void) } get_n_mem_cells(&n_mem_addr_cells, &n_mem_size_cells); - memory = NULL; - while ((memory = of_find_node_by_type(memory, "memory")) != NULL) { + + for_each_node_by_type(memory, "memory") { unsigned long start; unsigned long size; int nid; @@ -1187,10 +1187,10 @@ static int hot_add_drconf_scn_to_nid(struct device_node *memory, */ int hot_add_node_scn_to_nid(unsigned long scn_addr) { - struct device_node *memory = NULL; + struct device_node *memory; int nid = -1; - while ((memory = of_find_node_by_type(memory, "memory")) != NULL) { + for_each_node_by_type(memory, "memory") { unsigned long start, size; int ranges; const unsigned int *memcell_buf; From dfbe93a222e74b6f96ad84eff2b04a0f864fac65 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Wed, 10 Aug 2011 20:44:23 +0000 Subject: [PATCH 0202/1531] powerpc: Coding style cleanups While converting code to use for_each_node_by_type I noticed a number of coding style issues. Signed-off-by: Anton Blanchard Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/setup_64.c | 20 +++++++++++++------- arch/powerpc/mm/numa.c | 7 ++++--- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index eade1fd8ee2e..d4168c93098f 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c @@ -281,11 +281,11 @@ static void __init initialize_cache_info(void) for_each_node_by_type(np, "cpu") { num_cpus += 1; - /* We're assuming *all* of the CPUs have the same + /* + * We're assuming *all* of the CPUs have the same * d-cache and i-cache sizes... -Peter */ - - if ( num_cpus == 1 ) { + if (num_cpus == 1) { const u32 *sizep, *lsizep; u32 size, lsize; @@ -294,10 +294,13 @@ static void __init initialize_cache_info(void) sizep = of_get_property(np, "d-cache-size", NULL); if (sizep != NULL) size = *sizep; - lsizep = of_get_property(np, "d-cache-block-size", NULL); + lsizep = of_get_property(np, "d-cache-block-size", + NULL); /* fallback if block size missing */ if (lsizep == NULL) - lsizep = of_get_property(np, "d-cache-line-size", NULL); + lsizep = of_get_property(np, + "d-cache-line-size", + NULL); if (lsizep != NULL) lsize = *lsizep; if (sizep == 0 || lsizep == 0) @@ -314,9 +317,12 @@ static void __init initialize_cache_info(void) sizep = of_get_property(np, "i-cache-size", NULL); if (sizep != NULL) size = *sizep; - lsizep = of_get_property(np, "i-cache-block-size", NULL); + lsizep = of_get_property(np, "i-cache-block-size", + NULL); if (lsizep == NULL) - lsizep = of_get_property(np, "i-cache-line-size", NULL); + lsizep = of_get_property(np, + "i-cache-line-size", + NULL); if (lsizep != NULL) lsize = *lsizep; if (sizep == 0 || lsizep == 0) diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index 00cc090cd1dc..0bfb90c50571 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -709,7 +709,6 @@ static void __init parse_drconf_memory(struct device_node *memory) static int __init parse_numa_properties(void) { - struct device_node *cpu = NULL; struct device_node *memory; int default_nid = 0; unsigned long i; @@ -732,6 +731,7 @@ static int __init parse_numa_properties(void) * each node to be onlined must have NODE_DATA etc backing it. */ for_each_present_cpu(i) { + struct device_node *cpu; int nid; cpu = of_get_cpu_node(i, NULL); @@ -800,8 +800,9 @@ static int __init parse_numa_properties(void) } /* - * Now do the same thing for each MEMBLOCK listed in the ibm,dynamic-memory - * property in the ibm,dynamic-reconfiguration-memory node. + * Now do the same thing for each MEMBLOCK listed in the + * ibm,dynamic-memory property in the + * ibm,dynamic-reconfiguration-memory node. */ memory = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory"); if (memory) From a11940978bd598e65996b4f807cf4904793f7025 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Wed, 10 Aug 2011 20:44:24 +0000 Subject: [PATCH 0203/1531] powerpc: Fix oops when echoing bad values to /sys/devices/system/memory/probe If we echo an address the hypervisor doesn't like to /sys/devices/system/memory/probe we oops the box: # echo 0x10000000000 > /sys/devices/system/memory/probe kernel BUG at arch/powerpc/mm/hash_utils_64.c:541! The backtrace is: create_section_mapping arch_add_memory add_memory memory_probe_store sysdev_class_store sysfs_write_file vfs_write SyS_write In create_section_mapping we BUG if htab_bolt_mapping returned an error. A better approach is to return an error which will propagate back to userspace. Rerunning the test with this patch applied: # echo 0x10000000000 > /sys/devices/system/memory/probe -bash: echo: write error: Invalid argument Signed-off-by: Anton Blanchard Cc: stable@kernel.org Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/sparsemem.h | 2 +- arch/powerpc/mm/hash_utils_64.c | 6 +++--- arch/powerpc/mm/mem.c | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/include/asm/sparsemem.h b/arch/powerpc/include/asm/sparsemem.h index 54a47ea2c3aa..0c5fa3145615 100644 --- a/arch/powerpc/include/asm/sparsemem.h +++ b/arch/powerpc/include/asm/sparsemem.h @@ -16,7 +16,7 @@ #endif /* CONFIG_SPARSEMEM */ #ifdef CONFIG_MEMORY_HOTPLUG -extern void create_section_mapping(unsigned long start, unsigned long end); +extern int create_section_mapping(unsigned long start, unsigned long end); extern int remove_section_mapping(unsigned long start, unsigned long end); #ifdef CONFIG_NUMA extern int hot_add_scn_to_nid(unsigned long scn_addr); diff --git a/arch/powerpc/mm/hash_utils_64.c b/arch/powerpc/mm/hash_utils_64.c index 1f8b2a05e3d0..1628201c8cea 100644 --- a/arch/powerpc/mm/hash_utils_64.c +++ b/arch/powerpc/mm/hash_utils_64.c @@ -531,11 +531,11 @@ static unsigned long __init htab_get_table_size(void) } #ifdef CONFIG_MEMORY_HOTPLUG -void create_section_mapping(unsigned long start, unsigned long end) +int create_section_mapping(unsigned long start, unsigned long end) { - BUG_ON(htab_bolt_mapping(start, end, __pa(start), + return htab_bolt_mapping(start, end, __pa(start), pgprot_val(PAGE_KERNEL), mmu_linear_psize, - mmu_kernel_ssize)); + mmu_kernel_ssize); } int remove_section_mapping(unsigned long start, unsigned long end) diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index ad9cf49dfb89..5db316cad47b 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -123,7 +123,8 @@ int arch_add_memory(int nid, u64 start, u64 size) pgdata = NODE_DATA(nid); start = (unsigned long)__va(start); - create_section_mapping(start, start + size); + if (create_section_mapping(start, start + size)) + return -EINVAL; /* this should work for most non-highmem platforms */ zone = pgdata->node_zones; From 8bdafa39a47265bc029838b35cc6585f69224afa Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Wed, 14 Sep 2011 09:43:15 +0000 Subject: [PATCH 0204/1531] powerpc: Fix deadlock in icswx code The icswx code introduced an A-B B-A deadlock: CPU0 CPU1 ---- ---- lock(&anon_vma->mutex); lock(&mm->mmap_sem); lock(&anon_vma->mutex); lock(&mm->mmap_sem); Instead of using the mmap_sem to keep mm_users constant, take the page table spinlock. Signed-off-by: Anton Blanchard Cc: Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/mm/mmu_context_hash64.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/mm/mmu_context_hash64.c b/arch/powerpc/mm/mmu_context_hash64.c index 3bafc3deca6d..4ff587e38250 100644 --- a/arch/powerpc/mm/mmu_context_hash64.c +++ b/arch/powerpc/mm/mmu_context_hash64.c @@ -136,8 +136,8 @@ int use_cop(unsigned long acop, struct mm_struct *mm) if (!mm || !acop) return -EINVAL; - /* We need to make sure mm_users doesn't change */ - down_read(&mm->mmap_sem); + /* The page_table_lock ensures mm_users won't change under us */ + spin_lock(&mm->page_table_lock); spin_lock(mm->context.cop_lockp); if (mm->context.cop_pid == COP_PID_NONE) { @@ -164,7 +164,7 @@ int use_cop(unsigned long acop, struct mm_struct *mm) out: spin_unlock(mm->context.cop_lockp); - up_read(&mm->mmap_sem); + spin_unlock(&mm->page_table_lock); return ret; } @@ -185,8 +185,8 @@ void drop_cop(unsigned long acop, struct mm_struct *mm) if (WARN_ON_ONCE(!mm)) return; - /* We need to make sure mm_users doesn't change */ - down_read(&mm->mmap_sem); + /* The page_table_lock ensures mm_users won't change under us */ + spin_lock(&mm->page_table_lock); spin_lock(mm->context.cop_lockp); mm->context.acop &= ~acop; @@ -213,7 +213,7 @@ void drop_cop(unsigned long acop, struct mm_struct *mm) } spin_unlock(mm->context.cop_lockp); - up_read(&mm->mmap_sem); + spin_unlock(&mm->page_table_lock); } EXPORT_SYMBOL_GPL(drop_cop); From ac07a4a57f7408922a0b3d4dcb87104fe8a3d8ca Mon Sep 17 00:00:00 2001 From: Brian King Date: Tue, 13 Sep 2011 11:22:51 +0000 Subject: [PATCH 0205/1531] hvcs: Ensure page aligned partner info buffer The Power platform requires the partner info buffer to be page aligned otherwise it will fail the partner info hcall with H_PARAMETER. Switch from using kmalloc to allocate this buffer to __get_free_page to ensure page alignment. Signed-off-by: Brian King Signed-off-by: Benjamin Herrenschmidt --- drivers/tty/hvc/hvcs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c index 4c8b66546930..39291665eca3 100644 --- a/drivers/tty/hvc/hvcs.c +++ b/drivers/tty/hvc/hvcs.c @@ -1532,7 +1532,7 @@ static int __devinit hvcs_initialize(void) goto register_fail; } - hvcs_pi_buff = kmalloc(PAGE_SIZE, GFP_KERNEL); + hvcs_pi_buff = (unsigned long *) __get_free_page(GFP_KERNEL); if (!hvcs_pi_buff) { rc = -ENOMEM; goto buff_alloc_fail; @@ -1548,7 +1548,7 @@ static int __devinit hvcs_initialize(void) return 0; kthread_fail: - kfree(hvcs_pi_buff); + free_page((unsigned long)hvcs_pi_buff); buff_alloc_fail: tty_unregister_driver(hvcs_tty_driver); register_fail: @@ -1597,7 +1597,7 @@ static void __exit hvcs_module_exit(void) kthread_stop(hvcs_task); spin_lock(&hvcs_pi_lock); - kfree(hvcs_pi_buff); + free_page((unsigned long)hvcs_pi_buff); hvcs_pi_buff = NULL; spin_unlock(&hvcs_pi_lock); From b8bb922c680b7e21af2268bc8eec93b17f5b9ab8 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:44:48 +0000 Subject: [PATCH 0206/1531] powerpc/udbg: Fix Kconfig entry for avoiding 44x early debug with KVM It was preventing the global early debug selection whenever KVM was enabled instead of only preventing the 440 specific one. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/Kconfig.debug | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug index ab2335f160a0..06eb62b0fd90 100644 --- a/arch/powerpc/Kconfig.debug +++ b/arch/powerpc/Kconfig.debug @@ -141,9 +141,6 @@ config BOOTX_TEXT config PPC_EARLY_DEBUG bool "Early debugging (dangerous)" - # PPC_EARLY_DEBUG on 440 leaves AS=1 mappings above the TLB high water - # mark, which doesn't work with current 440 KVM. - depends on !KVM help Say Y to enable some early debugging facilities that may be available for your processor/board combination. Those facilities are hacks @@ -222,7 +219,9 @@ config PPC_EARLY_DEBUG_BEAT config PPC_EARLY_DEBUG_44x bool "Early serial debugging for IBM/AMCC 44x CPUs" - depends on 44x + # PPC_EARLY_DEBUG on 440 leaves AS=1 mappings above the TLB high water + # mark, which doesn't work with current 440 KVM. + depends on 44x && !KVM help Select this to enable early debugging for IBM 44x chips via the inbuilt serial port. If you enable this, ensure you set From fb82b83970a32263698e54a8779d2ce88cd3b060 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:44:49 +0000 Subject: [PATCH 0207/1531] powerpc/smp: More generic support for "soft hotplug" This adds more generic support for doing CPU hotplug with a simple idle loop and no actual reset of the processors. The generic smp_generic_kick_cpu() does the hotplug bringup trick if the PACA shows that the CPU has already been started at boot and we provide an accessor for the CPU state. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/smp.h | 1 + arch/powerpc/kernel/smp.c | 30 +++++++++++++++++++++++++----- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/include/asm/smp.h b/arch/powerpc/include/asm/smp.h index 15a70b7f638b..adba970ce918 100644 --- a/arch/powerpc/include/asm/smp.h +++ b/arch/powerpc/include/asm/smp.h @@ -65,6 +65,7 @@ int generic_cpu_disable(void); void generic_cpu_die(unsigned int cpu); void generic_mach_cpu_die(void); void generic_set_cpu_dead(unsigned int cpu); +int generic_check_cpu_restart(unsigned int cpu); #endif #ifdef CONFIG_PPC64 diff --git a/arch/powerpc/kernel/smp.c b/arch/powerpc/kernel/smp.c index 7bf2187dfd99..af7e7722ecae 100644 --- a/arch/powerpc/kernel/smp.c +++ b/arch/powerpc/kernel/smp.c @@ -70,6 +70,10 @@ static DEFINE_PER_CPU(struct task_struct *, idle_thread_array); #define get_idle_for_cpu(x) (per_cpu(idle_thread_array, x)) #define set_idle_for_cpu(x, p) (per_cpu(idle_thread_array, x) = (p)) + +/* State of each CPU during hotplug phases */ +static DEFINE_PER_CPU(int, cpu_state) = { 0 }; + #else static struct task_struct *idle_thread_array[NR_CPUS] __cpuinitdata ; #define get_idle_for_cpu(x) (idle_thread_array[(x)]) @@ -104,12 +108,25 @@ int __devinit smp_generic_kick_cpu(int nr) * cpu_start field to become non-zero After we set cpu_start, * the processor will continue on to secondary_start */ - paca[nr].cpu_start = 1; - smp_mb(); + if (!paca[nr].cpu_start) { + paca[nr].cpu_start = 1; + smp_mb(); + return 0; + } + +#ifdef CONFIG_HOTPLUG_CPU + /* + * Ok it's not there, so it might be soft-unplugged, let's + * try to bring it back + */ + per_cpu(cpu_state, nr) = CPU_UP_PREPARE; + smp_wmb(); + smp_send_reschedule(nr); +#endif /* CONFIG_HOTPLUG_CPU */ return 0; } -#endif +#endif /* CONFIG_PPC64 */ static irqreturn_t call_function_action(int irq, void *data) { @@ -357,8 +374,6 @@ void __devinit smp_prepare_boot_cpu(void) } #ifdef CONFIG_HOTPLUG_CPU -/* State of each CPU during hotplug phases */ -static DEFINE_PER_CPU(int, cpu_state) = { 0 }; int generic_cpu_disable(void) { @@ -406,6 +421,11 @@ void generic_set_cpu_dead(unsigned int cpu) { per_cpu(cpu_state, cpu) = CPU_DEAD; } + +int generic_check_cpu_restart(unsigned int cpu) +{ + return per_cpu(cpu_state, cpu) == CPU_UP_PREPARE; +} #endif struct create_idle { From 781fb7a3e4cdca28236ae23e2c77070ed3ae531f Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:44:50 +0000 Subject: [PATCH 0208/1531] powerpc/pci: Call pcie_bus_configure_settings() This new function is used to properly setup the PCI Express Max Payload Size (and in some circumstances Max Read Request Size). Some systems will not operate properly if these aren't set correctly and the firmware doesn't always do it. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/pci-common.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 32656f105250..1bd47f36b25f 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -1730,6 +1730,17 @@ void __devinit pcibios_scan_phb(struct pci_controller *hose) if (mode == PCI_PROBE_NORMAL) hose->last_busno = bus->subordinate = pci_scan_child_bus(bus); + + /* Configure PCI Express settings */ + if (bus) { + struct pci_bus *child; + list_for_each_entry(child, &bus->children, node) { + struct pci_dev *self = child->self; + if (!self) + continue; + pcie_bus_configure_settings(child, self->pcie_mpss); + } + } } static void fixup_hide_host_resource_fsl(struct pci_dev *dev) From e550592e689cf8d682937f356497f989f3d88292 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:44:51 +0000 Subject: [PATCH 0209/1531] powerpc/powernv: Don't clobber r9 in relative_toc() With OPAL, r8 and r9 will be used to pass the OPAL base and entry for debugging purposes (those informations are also in the device-tree). We don't want to clobber those registers that early. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/head_64.S | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S index 3564c49c683e..e708abe576d3 100644 --- a/arch/powerpc/kernel/head_64.S +++ b/arch/powerpc/kernel/head_64.S @@ -674,9 +674,9 @@ _GLOBAL(enable_64b_mode) _GLOBAL(relative_toc) mflr r0 bcl 20,31,$+4 -0: mflr r9 - ld r2,(p_toc - 0b)(r9) - add r2,r2,r9 +0: mflr r11 + ld r2,(p_toc - 0b)(r11) + add r2,r2,r11 mtlr r0 blr From 55190f88789ab62a42c3ee050090406b0bcefff8 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:44:52 +0000 Subject: [PATCH 0210/1531] powerpc: Add skeleton PowerNV platform This adds a skeletton for the new Power "Non Virtualized" platform which will be used by machines supporting running without an hypervisor, for example in order to run KVM. These machines will be using a new firmware called OPAL for which the support will be provided by later patches. The PowerNV platform is intended to be also usable under the BML environment used internally for early CPU bringup which is why the code also supports using RTAS instead of OPAL in various places. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/boot/Makefile | 1 + arch/powerpc/platforms/Kconfig | 1 + arch/powerpc/platforms/Makefile | 1 + arch/powerpc/platforms/powernv/Kconfig | 11 ++ arch/powerpc/platforms/powernv/Makefile | 2 + arch/powerpc/platforms/powernv/powernv.h | 10 ++ arch/powerpc/platforms/powernv/setup.c | 153 +++++++++++++++++++++++ arch/powerpc/platforms/powernv/smp.c | 83 ++++++++++++ 8 files changed, 262 insertions(+) create mode 100644 arch/powerpc/platforms/powernv/Kconfig create mode 100644 arch/powerpc/platforms/powernv/Makefile create mode 100644 arch/powerpc/platforms/powernv/powernv.h create mode 100644 arch/powerpc/platforms/powernv/setup.c create mode 100644 arch/powerpc/platforms/powernv/smp.c diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index ac6705e75711..72ee8c1fba48 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -171,6 +171,7 @@ quiet_cmd_wrap = WRAP $@ $(if $3, -s $3)$(if $4, -d $4)$(if $5, -i $5) vmlinux image-$(CONFIG_PPC_PSERIES) += zImage.pseries +image-$(CONFIG_PPC_POWERNV) += zImage.pseries image-$(CONFIG_PPC_MAPLE) += zImage.maple image-$(CONFIG_PPC_IBM_CELL_BLADE) += zImage.pseries image-$(CONFIG_PPC_PS3) += dtbImage.ps3 diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index b9ba86191aed..6de27d275728 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -1,5 +1,6 @@ menu "Platform support" +source "arch/powerpc/platforms/powernv/Kconfig" source "arch/powerpc/platforms/pseries/Kconfig" source "arch/powerpc/platforms/iseries/Kconfig" source "arch/powerpc/platforms/chrp/Kconfig" diff --git a/arch/powerpc/platforms/Makefile b/arch/powerpc/platforms/Makefile index 73e2116cfeed..2635a22bade2 100644 --- a/arch/powerpc/platforms/Makefile +++ b/arch/powerpc/platforms/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_PPC_82xx) += 82xx/ obj-$(CONFIG_PPC_83xx) += 83xx/ obj-$(CONFIG_FSL_SOC_BOOKE) += 85xx/ obj-$(CONFIG_PPC_86xx) += 86xx/ +obj-$(CONFIG_PPC_POWERNV) += powernv/ obj-$(CONFIG_PPC_PSERIES) += pseries/ obj-$(CONFIG_PPC_ISERIES) += iseries/ obj-$(CONFIG_PPC_MAPLE) += maple/ diff --git a/arch/powerpc/platforms/powernv/Kconfig b/arch/powerpc/platforms/powernv/Kconfig new file mode 100644 index 000000000000..24700e8df19c --- /dev/null +++ b/arch/powerpc/platforms/powernv/Kconfig @@ -0,0 +1,11 @@ +config PPC_POWERNV + depends on PPC64 && PPC_BOOK3S + bool "IBM PowerNV (Non-Virtualized) platform support" + select PPC_RTAS + select PPC_NATIVE + select PPC_XICS + select PPC_ICP_NATIVE + select PPC_ICS_RTAS + select PPC_P7_NAP + select PPC_PCI_CHOICE if EMBEDDED + default y diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile new file mode 100644 index 000000000000..1c4325056b85 --- /dev/null +++ b/arch/powerpc/platforms/powernv/Makefile @@ -0,0 +1,2 @@ +obj-y += setup.o +obj-$(CONFIG_SMP) += smp.o diff --git a/arch/powerpc/platforms/powernv/powernv.h b/arch/powerpc/platforms/powernv/powernv.h new file mode 100644 index 000000000000..35b716008cd3 --- /dev/null +++ b/arch/powerpc/platforms/powernv/powernv.h @@ -0,0 +1,10 @@ +#ifndef _POWERNV_H +#define _POWERNV_H + +#ifdef CONFIG_SMP +extern void pnv_smp_init(void); +#else +static inline void pnv_smp_init(void) { } +#endif + +#endif /* _POWERNV_H */ diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c new file mode 100644 index 000000000000..569f9cc4eb04 --- /dev/null +++ b/arch/powerpc/platforms/powernv/setup.c @@ -0,0 +1,153 @@ +/* + * PowerNV setup code. + * + * Copyright 2011 IBM Corp. + * + * 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. + */ + +#undef DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "powernv.h" + +static void __init pnv_setup_arch(void) +{ + /* Force console to hvc for now until we have sorted out the + * real console situation for the platform. This will make + * hvc_udbg work at least. + */ + add_preferred_console("hvc", 0, NULL); + + /* Initialize SMP */ + pnv_smp_init(); + + /* XXX PCI */ + + /* XXX NVRAM */ + + /* Enable NAP mode */ + powersave_nap = 1; + + /* XXX PMCS */ +} + +static void __init pnv_init_early(void) +{ + /* XXX IOMMU */ +} + +static void __init pnv_init_IRQ(void) +{ + xics_init(); + + WARN_ON(!ppc_md.get_irq); +} + +static void pnv_show_cpuinfo(struct seq_file *m) +{ + struct device_node *root; + const char *model = ""; + + root = of_find_node_by_path("/"); + if (root) + model = of_get_property(root, "model", NULL); + seq_printf(m, "machine\t\t: PowerNV %s\n", model); + of_node_put(root); +} + +static void pnv_restart(char *cmd) +{ + for (;;); +} + +static void pnv_power_off(void) +{ + for (;;); +} + +static void pnv_halt(void) +{ + for (;;); +} + +static unsigned long __init pnv_get_boot_time(void) +{ + return 0; +} + +static void pnv_get_rtc_time(struct rtc_time *rtc_tm) +{ +} + +static int pnv_set_rtc_time(struct rtc_time *tm) +{ + return 0; +} + +static void pnv_progress(char *s, unsigned short hex) +{ +} + +#ifdef CONFIG_KEXEC +static void pnv_kexec_cpu_down(int crash_shutdown, int secondary) +{ + xics_kexec_teardown_cpu(secondary); +} +#endif /* CONFIG_KEXEC */ + +static int __init pnv_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + if (!of_flat_dt_is_compatible(root, "ibm,powernv")) + return 0; + + hpte_init_native(); + + pr_debug("PowerNV detected !\n"); + + return 1; +} + +define_machine(powernv) { + .name = "PowerNV", + .probe = pnv_probe, + .setup_arch = pnv_setup_arch, + .init_early = pnv_init_early, + .init_IRQ = pnv_init_IRQ, + .show_cpuinfo = pnv_show_cpuinfo, + .restart = pnv_restart, + .power_off = pnv_power_off, + .halt = pnv_halt, + .get_boot_time = pnv_get_boot_time, + .get_rtc_time = pnv_get_rtc_time, + .set_rtc_time = pnv_set_rtc_time, + .progress = pnv_progress, + .power_save = power7_idle, + .calibrate_decr = generic_calibrate_decr, +#ifdef CONFIG_KEXEC + .kexec_cpu_down = pnv_kexec_cpu_down, +#endif +}; diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c new file mode 100644 index 000000000000..36c715102c58 --- /dev/null +++ b/arch/powerpc/platforms/powernv/smp.c @@ -0,0 +1,83 @@ +/* + * SMP support for PowerNV machines. + * + * Copyright 2011 IBM Corp. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "powernv.h" + +static void __devinit pnv_smp_setup_cpu(int cpu) +{ + if (cpu != boot_cpuid) + xics_setup_cpu(); +} + +static int pnv_smp_cpu_bootable(unsigned int nr) +{ + /* Special case - we inhibit secondary thread startup + * during boot if the user requests it. + */ + if (system_state < SYSTEM_RUNNING && cpu_has_feature(CPU_FTR_SMT)) { + if (!smt_enabled_at_boot && cpu_thread_in_core(nr) != 0) + return 0; + if (smt_enabled_at_boot + && cpu_thread_in_core(nr) >= smt_enabled_at_boot) + return 0; + } + + return 1; +} + +static struct smp_ops_t pnv_smp_ops = { + .message_pass = smp_muxed_ipi_message_pass, + .cause_ipi = NULL, /* Filled at runtime by xics_smp_probe() */ + .probe = xics_smp_probe, + .kick_cpu = smp_generic_kick_cpu, + .setup_cpu = pnv_smp_setup_cpu, + .cpu_bootable = pnv_smp_cpu_bootable, +}; + +/* This is called very early during platform setup_arch */ +void __init pnv_smp_init(void) +{ + smp_ops = &pnv_smp_ops; + + /* XXX We don't yet have a proper entry point from HAL, for + * now we rely on kexec-style entry from BML + */ + +#ifdef CONFIG_PPC_RTAS + /* Non-lpar has additional take/give timebase */ + if (rtas_token("freeze-time-base") != RTAS_UNKNOWN_SERVICE) { + smp_ops->give_timebase = rtas_give_timebase; + smp_ops->take_timebase = rtas_take_timebase; + } +#endif /* CONFIG_PPC_RTAS */ +} From 78b782cb788cadbda151ecb61753c109602a250c Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 18:50:15 +0000 Subject: [PATCH 0211/1531] of: Change logic to overwrite cmd_line with CONFIG_CMDLINE We used to overwrite with CONFIG_CMDLINE if we found a chosen node but failed to get bootargs out of it or they were empty, unless CONFIG_CMDLINE_FORCE is set. Instead change that to overwrite if "data" is non empty after the bootargs check. It allows arch code to have other mechanisms to retrieve the command line prior to parsing the device-tree. Note: CONFIG_CMDLINE_FORCE case should ideally be handled elsewhere as it won't work as it-is if the device-tree has no /chosen node Signed-off-by: Benjamin Herrenschmidt CC: devicetree-discuss@lists-ozlabs.org CC: Grant Likely Acked-by: Grant Likely --- drivers/of/fdt.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 65200af29c52..4af69a3564cc 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -681,9 +681,14 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, if (p != NULL && l > 0) strlcpy(data, p, min((int)l, COMMAND_LINE_SIZE)); + /* + * CONFIG_CMDLINE is meant to be a default in case nothing else + * managed to set the command line, unless CONFIG_CMDLINE_FORCE + * is set in which case we override whatever was found earlier. + */ #ifdef CONFIG_CMDLINE #ifndef CONFIG_CMDLINE_FORCE - if (p == NULL || l == 0 || (l == 1 && (*p) == 0)) + if (!((char *)data)[0]) #endif strlcpy(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE); #endif /* CONFIG_CMDLINE */ From 344eb010b2e399069bac474a9fd0ba04908a2601 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:44:54 +0000 Subject: [PATCH 0212/1531] powerpc/powernv: Add CPU hotplug support Unplugged CPU go into NAP mode in a loop until woken up Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/Kconfig | 2 +- arch/powerpc/platforms/powernv/smp.c | 78 +++++++++++++++++++++++++++- 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 62711421cd64..8523bd1b8d7e 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -323,7 +323,7 @@ config SWIOTLB config HOTPLUG_CPU bool "Support for enabling/disabling CPUs" - depends on SMP && HOTPLUG && EXPERIMENTAL && (PPC_PSERIES || PPC_PMAC) + depends on SMP && HOTPLUG && EXPERIMENTAL && (PPC_PSERIES || PPC_PMAC || PPC_POWERNV) ---help--- Say Y here to be able to disable and re-enable individual CPUs at runtime on SMP machines. diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c index 36c715102c58..4f4ec3797eb6 100644 --- a/arch/powerpc/platforms/powernv/smp.c +++ b/arch/powerpc/platforms/powernv/smp.c @@ -33,7 +33,14 @@ #include "powernv.h" -static void __devinit pnv_smp_setup_cpu(int cpu) +#ifdef DEBUG +#include +#define DBG(fmt...) udbg_printf(fmt) +#else +#define DBG(fmt...) +#endif + +static void __cpuinit pnv_smp_setup_cpu(int cpu) { if (cpu != boot_cpuid) xics_setup_cpu(); @@ -55,6 +62,67 @@ static int pnv_smp_cpu_bootable(unsigned int nr) return 1; } +#ifdef CONFIG_HOTPLUG_CPU + +static int pnv_smp_cpu_disable(void) +{ + int cpu = smp_processor_id(); + + /* This is identical to pSeries... might consolidate by + * moving migrate_irqs_away to a ppc_md with default to + * the generic fixup_irqs. --BenH. + */ + set_cpu_online(cpu, false); + vdso_data->processorCount--; + if (cpu == boot_cpuid) + boot_cpuid = cpumask_any(cpu_online_mask); + xics_migrate_irqs_away(); + return 0; +} + +static void pnv_smp_cpu_kill_self(void) +{ + unsigned int cpu; + + /* If powersave_nap is enabled, use NAP mode, else just + * spin aimlessly + */ + if (!powersave_nap) { + generic_mach_cpu_die(); + return; + } + + /* Standard hot unplug procedure */ + local_irq_disable(); + idle_task_exit(); + current->active_mm = NULL; /* for sanity */ + cpu = smp_processor_id(); + DBG("CPU%d offline\n", cpu); + generic_set_cpu_dead(cpu); + smp_wmb(); + + /* We don't want to take decrementer interrupts while we are offline, + * so clear LPCR:PECE1. We keep PECE2 enabled. + */ + mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) & ~(u64)LPCR_PECE1); + while (!generic_check_cpu_restart(cpu)) { + power7_idle(); + if (!generic_check_cpu_restart(cpu)) { + DBG("CPU%d Unexpected exit while offline !\n", cpu); + /* We may be getting an IPI, so we re-enable + * interrupts to process it, it will be ignored + * since we aren't online (hopefully) + */ + local_irq_enable(); + local_irq_disable(); + } + } + mtspr(SPRN_LPCR, mfspr(SPRN_LPCR) | LPCR_PECE1); + DBG("CPU%d coming online...\n", cpu); +} + +#endif /* CONFIG_HOTPLUG_CPU */ + static struct smp_ops_t pnv_smp_ops = { .message_pass = smp_muxed_ipi_message_pass, .cause_ipi = NULL, /* Filled at runtime by xics_smp_probe() */ @@ -62,6 +130,10 @@ static struct smp_ops_t pnv_smp_ops = { .kick_cpu = smp_generic_kick_cpu, .setup_cpu = pnv_smp_setup_cpu, .cpu_bootable = pnv_smp_cpu_bootable, +#ifdef CONFIG_HOTPLUG_CPU + .cpu_disable = pnv_smp_cpu_disable, + .cpu_die = generic_cpu_die, +#endif /* CONFIG_HOTPLUG_CPU */ }; /* This is called very early during platform setup_arch */ @@ -80,4 +152,8 @@ void __init pnv_smp_init(void) smp_ops->take_timebase = rtas_take_timebase; } #endif /* CONFIG_PPC_RTAS */ + +#ifdef CONFIG_HOTPLUG_CPU + ppc_md.cpu_die = pnv_smp_cpu_kill_self; +#endif } From 27f4488872d9ef2a4b9aa2be58fb0789d6c0ba84 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 18:27:58 +0000 Subject: [PATCH 0213/1531] powerpc/powernv: Add OPAL takeover from PowerVM On machines supporting the OPAL firmware version 1, the system is initially booted under pHyp. We then use a special hypercall to verify if OPAL is available and if it is, we then trigger a "takeover" which disables pHyp and loads the OPAL runtime firmware, giving control to the kernel in hypervisor mode. This patch add the necessary code to detect that the OPAL takeover capability is present when running under PowerVM (aka pHyp) and perform said takeover to get hypervisor control of the processor. To perform the takeover, we must first use RTAS (within Open Firmware runtime environment) to start all processors & threads, in order to give control to OPAL on all of them. We then call the takeover hypercall on everybody, OPAL will re-enter the kernel main entry point passing it a flat device-tree. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/opal.h | 50 ++++ arch/powerpc/kernel/head_64.S | 4 + arch/powerpc/kernel/prom_init.c | 239 ++++++++++++++++-- arch/powerpc/kernel/prom_init_check.sh | 3 +- arch/powerpc/platforms/powernv/Makefile | 2 +- .../powerpc/platforms/powernv/opal-takeover.S | 140 ++++++++++ 6 files changed, 419 insertions(+), 19 deletions(-) create mode 100644 arch/powerpc/include/asm/opal.h create mode 100644 arch/powerpc/platforms/powernv/opal-takeover.S diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h new file mode 100644 index 000000000000..ecdb283f8b7c --- /dev/null +++ b/arch/powerpc/include/asm/opal.h @@ -0,0 +1,50 @@ +/* + * PowerNV OPAL definitions. + * + * Copyright 2011 IBM Corp. + * + * 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 __OPAL_H +#define __OPAL_H + +/****** Takeover interface ********/ + +/* PAPR H-Call used to querty the HAL existence and/or instanciate + * it from within pHyp (tech preview only). + * + * This is exclusively used in prom_init.c + */ + +#ifndef __ASSEMBLY__ + +struct opal_takeover_args { + u64 k_image; /* r4 */ + u64 k_size; /* r5 */ + u64 k_entry; /* r6 */ + u64 k_entry2; /* r7 */ + u64 hal_addr; /* r8 */ + u64 rd_image; /* r9 */ + u64 rd_size; /* r10 */ + u64 rd_loc; /* r11 */ +}; + +extern long opal_query_takeover(u64 *hal_size, u64 *hal_align); + +extern long opal_do_takeover(struct opal_takeover_args *args); + +extern int opal_enter_rtas(struct rtas_args *args, + unsigned long data, + unsigned long entry); + + +#endif /* __ASSEMBLY__ */ + +/****** OPAL APIs ******/ + + +#endif /* __OPAL_H */ diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S index e708abe576d3..dea8191253d2 100644 --- a/arch/powerpc/kernel/head_64.S +++ b/arch/powerpc/kernel/head_64.S @@ -51,6 +51,10 @@ * For pSeries or server processors: * 1. The MMU is off & open firmware is running in real mode. * 2. The kernel is entered at __start + * -or- For OPAL entry: + * 1. The MMU is off, processor in HV mode, primary CPU enters at 0 + * with device-tree in gpr3 + * 2. Secondary processors enter at 0x60 with PIR in gpr3 * * For iSeries: * 1. The MMU is on (as it always is for iSeries) diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index a909f4e9343b..9369287aa8c2 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -43,6 +43,7 @@ #include #include #include +#include #include @@ -185,6 +186,7 @@ static unsigned long __initdata prom_tce_alloc_end; #define PLATFORM_LPAR 0x0001 #define PLATFORM_POWERMAC 0x0400 #define PLATFORM_GENERIC 0x0500 +#define PLATFORM_OPAL 0x0600 static int __initdata of_platform; @@ -644,7 +646,7 @@ static void __init early_cmdline_parse(void) } } -#ifdef CONFIG_PPC_PSERIES +#if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) /* * There are two methods for telling firmware what our capabilities are. * Newer machines have an "ibm,client-architecture-support" method on the @@ -1274,6 +1276,195 @@ static void __init prom_init_mem(void) prom_printf(" ram_top : %x\n", RELOC(ram_top)); } +static void __init prom_close_stdin(void) +{ + struct prom_t *_prom = &RELOC(prom); + ihandle val; + + if (prom_getprop(_prom->chosen, "stdin", &val, sizeof(val)) > 0) + call_prom("close", 1, 0, val); +} + +#ifdef CONFIG_PPC_POWERNV + +static u64 __initdata prom_opal_size; +static u64 __initdata prom_opal_align; +static int __initdata prom_rtas_start_cpu; +static u64 __initdata prom_rtas_data; +static u64 __initdata prom_rtas_entry; + +/* XXX Don't change this structure without updating opal-takeover.S */ +static struct opal_secondary_data { + s64 ack; /* 0 */ + u64 go; /* 8 */ + struct opal_takeover_args args; /* 16 */ +} opal_secondary_data; + +extern char opal_secondary_entry; + +static void prom_query_opal(void) +{ + long rc; + + prom_printf("Querying for OPAL presence... "); + rc = opal_query_takeover(&RELOC(prom_opal_size), + &RELOC(prom_opal_align)); + prom_debug("(rc = %ld) ", rc); + if (rc != 0) { + prom_printf("not there.\n"); + return; + } + RELOC(of_platform) = PLATFORM_OPAL; + prom_printf(" there !\n"); + prom_debug(" opal_size = 0x%lx\n", RELOC(prom_opal_size)); + prom_debug(" opal_align = 0x%lx\n", RELOC(prom_opal_align)); + if (RELOC(prom_opal_align) < 0x10000) + RELOC(prom_opal_align) = 0x10000; +} + +static int prom_rtas_call(int token, int nargs, int nret, int *outputs, ...) +{ + struct rtas_args rtas_args; + va_list list; + int i; + + rtas_args.token = token; + rtas_args.nargs = nargs; + rtas_args.nret = nret; + rtas_args.rets = (rtas_arg_t *)&(rtas_args.args[nargs]); + va_start(list, outputs); + for (i = 0; i < nargs; ++i) + rtas_args.args[i] = va_arg(list, rtas_arg_t); + va_end(list); + + for (i = 0; i < nret; ++i) + rtas_args.rets[i] = 0; + + opal_enter_rtas(&rtas_args, RELOC(prom_rtas_data), + RELOC(prom_rtas_entry)); + + if (nret > 1 && outputs != NULL) + for (i = 0; i < nret-1; ++i) + outputs[i] = rtas_args.rets[i+1]; + return (nret > 0)? rtas_args.rets[0]: 0; +} + +static void __init prom_opal_hold_cpus(void) +{ + int i, cnt, cpu, rc; + long j; + phandle node; + char type[64]; + u32 servers[8]; + struct prom_t *_prom = &RELOC(prom); + void *entry = (unsigned long *)&RELOC(opal_secondary_entry); + struct opal_secondary_data *data = &RELOC(opal_secondary_data); + + prom_debug("prom_opal_hold_cpus: start...\n"); + prom_debug(" - entry = 0x%x\n", entry); + prom_debug(" - data = 0x%x\n", data); + + data->ack = -1; + data->go = 0; + + /* look for cpus */ + for (node = 0; prom_next_node(&node); ) { + type[0] = 0; + prom_getprop(node, "device_type", type, sizeof(type)); + if (strcmp(type, RELOC("cpu")) != 0) + continue; + + /* Skip non-configured cpus. */ + if (prom_getprop(node, "status", type, sizeof(type)) > 0) + if (strcmp(type, RELOC("okay")) != 0) + continue; + + cnt = prom_getprop(node, "ibm,ppc-interrupt-server#s", servers, + sizeof(servers)); + if (cnt == PROM_ERROR) + break; + cnt >>= 2; + for (i = 0; i < cnt; i++) { + cpu = servers[i]; + prom_debug("CPU %d ... ", cpu); + if (cpu == _prom->cpu) { + prom_debug("booted !\n"); + continue; + } + prom_debug("starting ... "); + + /* Init the acknowledge var which will be reset by + * the secondary cpu when it awakens from its OF + * spinloop. + */ + data->ack = -1; + rc = prom_rtas_call(RELOC(prom_rtas_start_cpu), 3, 1, + NULL, cpu, entry, data); + prom_debug("rtas rc=%d ...", rc); + + for (j = 0; j < 100000000 && data->ack == -1; j++) { + HMT_low(); + mb(); + } + HMT_medium(); + if (data->ack != -1) + prom_debug("done, PIR=0x%x\n", data->ack); + else + prom_debug("timeout !\n"); + } + } + prom_debug("prom_opal_hold_cpus: end...\n"); +} + +static void prom_opal_takeover(void) +{ + struct opal_secondary_data *data = &RELOC(opal_secondary_data); + struct opal_takeover_args *args = &data->args; + u64 align = RELOC(prom_opal_align); + u64 top_addr, opal_addr; + + args->k_image = (u64)RELOC(_stext); + args->k_size = _end - _stext; + args->k_entry = 0; + args->k_entry2 = 0x60; + + top_addr = _ALIGN_UP(args->k_size, align); + + if (RELOC(prom_initrd_start) != 0) { + args->rd_image = RELOC(prom_initrd_start); + args->rd_size = RELOC(prom_initrd_end) - args->rd_image; + args->rd_loc = top_addr; + top_addr = _ALIGN_UP(args->rd_loc + args->rd_size, align); + } + + /* Pickup an address for the HAL. We want to go really high + * up to avoid problem with future kexecs. On the other hand + * we don't want to be all over the TCEs on P5IOC2 machines + * which are going to be up there too. We assume the machine + * has plenty of memory, and we ask for the HAL for now to + * be just below the 1G point, or above the initrd + */ + opal_addr = _ALIGN_DOWN(0x40000000 - RELOC(prom_opal_size), align); + if (opal_addr < top_addr) + opal_addr = top_addr; + args->hal_addr = opal_addr; + + prom_debug(" k_image = 0x%lx\n", args->k_image); + prom_debug(" k_size = 0x%lx\n", args->k_size); + prom_debug(" k_entry = 0x%lx\n", args->k_entry); + prom_debug(" k_entry2 = 0x%lx\n", args->k_entry2); + prom_debug(" hal_addr = 0x%lx\n", args->hal_addr); + prom_debug(" rd_image = 0x%lx\n", args->rd_image); + prom_debug(" rd_size = 0x%lx\n", args->rd_size); + prom_debug(" rd_loc = 0x%lx\n", args->rd_loc); + prom_printf("Performing OPAL takeover,this can take a few minutes..\n"); + prom_close_stdin(); + mb(); + data->go = 1; + for (;;) + opal_do_takeover(args); +} +#endif /* CONFIG_PPC_POWERNV */ /* * Allocate room for and instantiate RTAS @@ -1326,6 +1517,12 @@ static void __init prom_instantiate_rtas(void) prom_setprop(rtas_node, "/rtas", "linux,rtas-entry", &entry, sizeof(entry)); +#ifdef CONFIG_PPC_POWERNV + /* PowerVN takeover hack */ + RELOC(prom_rtas_data) = base; + RELOC(prom_rtas_entry) = entry; + prom_getprop(rtas_node, "start-cpu", &RELOC(prom_rtas_start_cpu), 4); +#endif prom_debug("rtas base = 0x%x\n", base); prom_debug("rtas entry = 0x%x\n", entry); prom_debug("rtas size = 0x%x\n", (long)size); @@ -1543,7 +1740,7 @@ static void __init prom_hold_cpus(void) *acknowledge = (unsigned long)-1; if (reg != _prom->cpu) { - /* Primary Thread of non-boot cpu */ + /* Primary Thread of non-boot cpu or any thread */ prom_printf("starting cpu hw idx %lu... ", reg); call_prom("start-cpu", 3, 0, node, secondary_hold, reg); @@ -1652,15 +1849,6 @@ static void __init prom_init_stdout(void) prom_setprop(val, path, "linux,boot-display", NULL, 0); } -static void __init prom_close_stdin(void) -{ - struct prom_t *_prom = &RELOC(prom); - ihandle val; - - if (prom_getprop(_prom->chosen, "stdin", &val, sizeof(val)) > 0) - call_prom("close", 1, 0, val); -} - static int __init prom_find_machine_type(void) { struct prom_t *_prom = &RELOC(prom); @@ -2504,6 +2692,7 @@ static void __init prom_check_initrd(unsigned long r3, unsigned long r4) #endif /* CONFIG_BLK_DEV_INITRD */ } + /* * We enter here early on, when the Open Firmware prom is still * handling exceptions and the MMU hash table for us. @@ -2565,7 +2754,7 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, */ prom_check_initrd(r3, r4); -#ifdef CONFIG_PPC_PSERIES +#if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) /* * On pSeries, inform the firmware about our capabilities */ @@ -2611,14 +2800,30 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, #endif /* - * On non-powermacs, try to instantiate RTAS and puts all CPUs - * in spin-loops. PowerMacs don't have a working RTAS and use - * a different way to spin CPUs + * On non-powermacs, try to instantiate RTAS. PowerMacs don't + * have a usable RTAS implementation. */ - if (RELOC(of_platform) != PLATFORM_POWERMAC) { + if (RELOC(of_platform) != PLATFORM_POWERMAC) prom_instantiate_rtas(); - prom_hold_cpus(); + +#ifdef CONFIG_PPC_POWERNV + /* Detect HAL and try instanciating it & doing takeover */ + if (RELOC(of_platform) == PLATFORM_PSERIES_LPAR) { + prom_query_opal(); + if (RELOC(of_platform) == PLATFORM_OPAL) { + prom_opal_hold_cpus(); + prom_opal_takeover(); + } } +#endif + + /* + * On non-powermacs, put all CPUs in spin-loops. + * + * PowerMacs use a different mechanism to spin CPUs + */ + if (RELOC(of_platform) != PLATFORM_POWERMAC) + prom_hold_cpus(); /* * Fill in some infos for use by the kernel later on diff --git a/arch/powerpc/kernel/prom_init_check.sh b/arch/powerpc/kernel/prom_init_check.sh index 9f82f4937892..20af6aada517 100644 --- a/arch/powerpc/kernel/prom_init_check.sh +++ b/arch/powerpc/kernel/prom_init_check.sh @@ -20,7 +20,8 @@ WHITELIST="add_reloc_offset __bss_start __bss_stop copy_and_flush _end enter_prom memcpy memset reloc_offset __secondary_hold __secondary_hold_acknowledge __secondary_hold_spinloop __start strcmp strcpy strlcpy strlen strncmp strstr logo_linux_clut224 -reloc_got2 kernstart_addr memstart_addr linux_banner" +reloc_got2 kernstart_addr memstart_addr linux_banner _stext +opal_query_takeover opal_do_takeover opal_enter_rtas opal_secondary_entry" NM="$1" OBJ="$2" diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile index 1c4325056b85..497133027ae5 100644 --- a/arch/powerpc/platforms/powernv/Makefile +++ b/arch/powerpc/platforms/powernv/Makefile @@ -1,2 +1,2 @@ -obj-y += setup.o +obj-y += setup.o opal-takeover.o obj-$(CONFIG_SMP) += smp.o diff --git a/arch/powerpc/platforms/powernv/opal-takeover.S b/arch/powerpc/platforms/powernv/opal-takeover.S new file mode 100644 index 000000000000..77b48b2b9309 --- /dev/null +++ b/arch/powerpc/platforms/powernv/opal-takeover.S @@ -0,0 +1,140 @@ +/* + * PowerNV OPAL takeover assembly code, for use by prom_init.c + * + * Copyright 2011 IBM Corp. + * + * 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 + +#define STK_PARAM(i) (48 + ((i)-3)*8) + +#define H_HAL_TAKEOVER 0x5124 +#define H_HAL_TAKEOVER_QUERY_MAGIC -1 + + .text +_GLOBAL(opal_query_takeover) + mfcr r0 + stw r0,8(r1) + std r3,STK_PARAM(r3)(r1) + std r4,STK_PARAM(r4)(r1) + li r3,H_HAL_TAKEOVER + li r4,H_HAL_TAKEOVER_QUERY_MAGIC + HVSC + ld r10,STK_PARAM(r3)(r1) + std r4,0(r10) + ld r10,STK_PARAM(r4)(r1) + std r5,0(r10) + lwz r0,8(r1) + mtcrf 0xff,r0 + blr + +_GLOBAL(opal_do_takeover) + mfcr r0 + stw r0,8(r1) + mflr r0 + std r0,16(r1) + bl __opal_do_takeover + ld r0,16(r1) + mtlr r0 + lwz r0,8(r1) + mtcrf 0xff,r0 + blr + +__opal_do_takeover: + ld r4,0(r3) + ld r5,0x8(r3) + ld r6,0x10(r3) + ld r7,0x18(r3) + ld r8,0x20(r3) + ld r9,0x28(r3) + ld r10,0x30(r3) + ld r11,0x38(r3) + li r3,H_HAL_TAKEOVER + HVSC + blr + + .globl opal_secondary_entry +opal_secondary_entry: + mr r31,r3 + mfmsr r11 + li r12,(MSR_SF | MSR_ISF)@highest + sldi r12,r12,48 + or r11,r11,r12 + mtmsrd r11 + isync + mfspr r4,SPRN_PIR + std r4,0(r3) +1: HMT_LOW + ld r4,8(r3) + cmpli cr0,r4,0 + beq 1b + HMT_MEDIUM +1: addi r3,r31,16 + bl __opal_do_takeover + b 1b + +_GLOBAL(opal_enter_rtas) + mflr r0 + std r0,16(r1) + stdu r1,-PROM_FRAME_SIZE(r1) /* Save SP and create stack space */ + + /* Because PROM is running in 32b mode, it clobbers the high order half + * of all registers that it saves. We therefore save those registers + * PROM might touch to the stack. (r0, r3-r13 are caller saved) + */ + SAVE_GPR(2, r1) + SAVE_GPR(13, r1) + SAVE_8GPRS(14, r1) + SAVE_10GPRS(22, r1) + mfcr r10 + mfmsr r11 + std r10,_CCR(r1) + std r11,_MSR(r1) + + /* Get the PROM entrypoint */ + mtlr r5 + + /* Switch MSR to 32 bits mode + */ + li r12,1 + rldicr r12,r12,MSR_SF_LG,(63-MSR_SF_LG) + andc r11,r11,r12 + li r12,1 + rldicr r12,r12,MSR_ISF_LG,(63-MSR_ISF_LG) + andc r11,r11,r12 + mtmsrd r11 + isync + + /* Enter RTAS here... */ + blrl + + /* Just make sure that r1 top 32 bits didn't get + * corrupt by OF + */ + rldicl r1,r1,0,32 + + /* Restore the MSR (back to 64 bits) */ + ld r0,_MSR(r1) + MTMSRD(r0) + isync + + /* Restore other registers */ + REST_GPR(2, r1) + REST_GPR(13, r1) + REST_8GPRS(14, r1) + REST_10GPRS(22, r1) + ld r4,_CCR(r1) + mtcr r4 + + addi r1,r1,PROM_FRAME_SIZE + ld r0,16(r1) + mtlr r0 + blr From 817c21ad9a1f00926f080265493923ada3458c63 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:44:56 +0000 Subject: [PATCH 0214/1531] powerpc/powernv: Get kernel command line accross OPAL takeover We stash it in boot_command_line which isn't in BSS and so won't be overwritten. We then use that as a default cmd_line before we walk the device-tree. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/prom.c | 7 +++++++ arch/powerpc/kernel/prom_init.c | 4 ++++ arch/powerpc/kernel/prom_init_check.sh | 3 ++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index 174e1e96175e..7b90c564e932 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -712,6 +712,13 @@ void __init early_init_devtree(void *params) of_scan_flat_dt(early_init_dt_scan_phyp_dump, NULL); #endif + /* Pre-initialize the cmd_line with the content of boot_commmand_line, + * which will be empty except when the content of the variable has + * been overriden by a bootloading mechanism. This happens typically + * with HAL takeover + */ + strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE); + /* Retrieve various informations from the /chosen node of the * device-tree, including the platform type, initrd location and * size, TCE reserve, and more ... diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index 9369287aa8c2..e3f390427216 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -1449,6 +1449,10 @@ static void prom_opal_takeover(void) opal_addr = top_addr; args->hal_addr = opal_addr; + /* Copy the command line to the kernel image */ + strlcpy(RELOC(boot_command_line), RELOC(prom_cmd_line), + COMMAND_LINE_SIZE); + prom_debug(" k_image = 0x%lx\n", args->k_image); prom_debug(" k_size = 0x%lx\n", args->k_size); prom_debug(" k_entry = 0x%lx\n", args->k_entry); diff --git a/arch/powerpc/kernel/prom_init_check.sh b/arch/powerpc/kernel/prom_init_check.sh index 20af6aada517..70f4286eaa7a 100644 --- a/arch/powerpc/kernel/prom_init_check.sh +++ b/arch/powerpc/kernel/prom_init_check.sh @@ -21,7 +21,8 @@ _end enter_prom memcpy memset reloc_offset __secondary_hold __secondary_hold_acknowledge __secondary_hold_spinloop __start strcmp strcpy strlcpy strlen strncmp strstr logo_linux_clut224 reloc_got2 kernstart_addr memstart_addr linux_banner _stext -opal_query_takeover opal_do_takeover opal_enter_rtas opal_secondary_entry" +opal_query_takeover opal_do_takeover opal_enter_rtas opal_secondary_entry +boot_command_line" NM="$1" OBJ="$2" From 14a43e69ed257a1fadadf9fea2c05adb1686419f Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:44:57 +0000 Subject: [PATCH 0215/1531] powerpc/powernv: Basic support for OPAL Add definition of OPAL interfaces along with the wrappers to call into OPAL runtime and the early device-tree parsing hook to locate the OPAL runtime firmware. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/firmware.h | 10 + arch/powerpc/include/asm/opal.h | 382 +++++++++++++++++- arch/powerpc/kernel/prom.c | 7 + arch/powerpc/platforms/powernv/Kconfig | 9 +- arch/powerpc/platforms/powernv/Makefile | 2 +- .../powerpc/platforms/powernv/opal-wrappers.S | 101 +++++ arch/powerpc/platforms/powernv/opal.c | 154 +++++++ arch/powerpc/platforms/powernv/setup.c | 6 + arch/powerpc/platforms/powernv/smp.c | 25 +- 9 files changed, 691 insertions(+), 5 deletions(-) create mode 100644 arch/powerpc/platforms/powernv/opal-wrappers.S create mode 100644 arch/powerpc/platforms/powernv/opal.c diff --git a/arch/powerpc/include/asm/firmware.h b/arch/powerpc/include/asm/firmware.h index 3a6c586c4e40..14db29b18d0e 100644 --- a/arch/powerpc/include/asm/firmware.h +++ b/arch/powerpc/include/asm/firmware.h @@ -48,6 +48,8 @@ #define FW_FEATURE_CMO ASM_CONST(0x0000000002000000) #define FW_FEATURE_VPHN ASM_CONST(0x0000000004000000) #define FW_FEATURE_XCMO ASM_CONST(0x0000000008000000) +#define FW_FEATURE_OPAL ASM_CONST(0x0000000010000000) +#define FW_FEATURE_OPALv2 ASM_CONST(0x0000000020000000) #ifndef __ASSEMBLY__ @@ -65,6 +67,8 @@ enum { FW_FEATURE_PSERIES_ALWAYS = 0, FW_FEATURE_ISERIES_POSSIBLE = FW_FEATURE_ISERIES | FW_FEATURE_LPAR, FW_FEATURE_ISERIES_ALWAYS = FW_FEATURE_ISERIES | FW_FEATURE_LPAR, + FW_FEATURE_POWERNV_POSSIBLE = FW_FEATURE_OPAL | FW_FEATURE_OPALv2, + FW_FEATURE_POWERNV_ALWAYS = 0, FW_FEATURE_PS3_POSSIBLE = FW_FEATURE_LPAR | FW_FEATURE_PS3_LV1, FW_FEATURE_PS3_ALWAYS = FW_FEATURE_LPAR | FW_FEATURE_PS3_LV1, FW_FEATURE_CELLEB_POSSIBLE = FW_FEATURE_LPAR | FW_FEATURE_BEAT, @@ -78,6 +82,9 @@ enum { #ifdef CONFIG_PPC_ISERIES FW_FEATURE_ISERIES_POSSIBLE | #endif +#ifdef CONFIG_PPC_POWERNV + FW_FEATURE_POWERNV_POSSIBLE | +#endif #ifdef CONFIG_PPC_PS3 FW_FEATURE_PS3_POSSIBLE | #endif @@ -95,6 +102,9 @@ enum { #ifdef CONFIG_PPC_ISERIES FW_FEATURE_ISERIES_ALWAYS & #endif +#ifdef CONFIG_PPC_POWERNV + FW_FEATURE_POWERNV_ALWAYS & +#endif #ifdef CONFIG_PPC_PS3 FW_FEATURE_PS3_ALWAYS & #endif diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h index ecdb283f8b7c..c7a3202d10a0 100644 --- a/arch/powerpc/include/asm/opal.h +++ b/arch/powerpc/include/asm/opal.h @@ -37,14 +37,394 @@ extern long opal_query_takeover(u64 *hal_size, u64 *hal_align); extern long opal_do_takeover(struct opal_takeover_args *args); +struct rtas_args; extern int opal_enter_rtas(struct rtas_args *args, unsigned long data, unsigned long entry); - #endif /* __ASSEMBLY__ */ /****** OPAL APIs ******/ +/* Return codes */ +#define OPAL_SUCCESS 0 +#define OPAL_PARAMETER -1 +#define OPAL_BUSY -2 +#define OPAL_PARTIAL -3 +#define OPAL_CONSTRAINED -4 +#define OPAL_CLOSED -5 +#define OPAL_HARDWARE -6 +#define OPAL_UNSUPPORTED -7 +#define OPAL_PERMISSION -8 +#define OPAL_NO_MEM -9 +#define OPAL_RESOURCE -10 +#define OPAL_INTERNAL_ERROR -11 +#define OPAL_BUSY_EVENT -12 +#define OPAL_HARDWARE_FROZEN -13 + +/* API Tokens (in r0) */ +#define OPAL_CONSOLE_WRITE 1 +#define OPAL_CONSOLE_READ 2 +#define OPAL_RTC_READ 3 +#define OPAL_RTC_WRITE 4 +#define OPAL_CEC_POWER_DOWN 5 +#define OPAL_CEC_REBOOT 6 +#define OPAL_READ_NVRAM 7 +#define OPAL_WRITE_NVRAM 8 +#define OPAL_HANDLE_INTERRUPT 9 +#define OPAL_POLL_EVENTS 10 +#define OPAL_PCI_SET_HUB_TCE_MEMORY 11 +#define OPAL_PCI_SET_PHB_TCE_MEMORY 12 +#define OPAL_PCI_CONFIG_READ_BYTE 13 +#define OPAL_PCI_CONFIG_READ_HALF_WORD 14 +#define OPAL_PCI_CONFIG_READ_WORD 15 +#define OPAL_PCI_CONFIG_WRITE_BYTE 16 +#define OPAL_PCI_CONFIG_WRITE_HALF_WORD 17 +#define OPAL_PCI_CONFIG_WRITE_WORD 18 +#define OPAL_SET_XIVE 19 +#define OPAL_GET_XIVE 20 +#define OPAL_GET_COMPLETION_TOKEN_STATUS 21 /* obsolete */ +#define OPAL_REGISTER_OPAL_EXCEPTION_HANDLER 22 +#define OPAL_PCI_EEH_FREEZE_STATUS 23 +#define OPAL_PCI_SHPC 24 +#define OPAL_CONSOLE_WRITE_BUFFER_SPACE 25 +#define OPAL_PCI_EEH_FREEZE_CLEAR 26 +#define OPAL_PCI_PHB_MMIO_ENABLE 27 +#define OPAL_PCI_SET_PHB_MEM_WINDOW 28 +#define OPAL_PCI_MAP_PE_MMIO_WINDOW 29 +#define OPAL_PCI_SET_PHB_TABLE_MEMORY 30 +#define OPAL_PCI_SET_PE 31 +#define OPAL_PCI_SET_PELTV 32 +#define OPAL_PCI_SET_MVE 33 +#define OPAL_PCI_SET_MVE_ENABLE 34 +#define OPAL_PCI_GET_XIVE_REISSUE 35 +#define OPAL_PCI_SET_XIVE_REISSUE 36 +#define OPAL_PCI_SET_XIVE_PE 37 +#define OPAL_GET_XIVE_SOURCE 38 +#define OPAL_GET_MSI_32 39 +#define OPAL_GET_MSI_64 40 +#define OPAL_START_CPU 41 +#define OPAL_QUERY_CPU_STATUS 42 +#define OPAL_WRITE_OPPANEL 43 +#define OPAL_PCI_MAP_PE_DMA_WINDOW 44 +#define OPAL_PCI_MAP_PE_DMA_WINDOW_REAL 45 +#define OPAL_PCI_RESET 49 + +#ifndef __ASSEMBLY__ + +/* Other enums */ +enum OpalVendorApiTokens { + OPAL_START_VENDOR_API_RANGE = 1000, OPAL_END_VENDOR_API_RANGE = 1999 +}; +enum OpalFreezeState { + OPAL_EEH_STOPPED_NOT_FROZEN = 0, + OPAL_EEH_STOPPED_MMIO_FREEZE = 1, + OPAL_EEH_STOPPED_DMA_FREEZE = 2, + OPAL_EEH_STOPPED_MMIO_DMA_FREEZE = 3, + OPAL_EEH_STOPPED_RESET = 4, + OPAL_EEH_STOPPED_TEMP_UNAVAIL = 5, + OPAL_EEH_STOPPED_PERM_UNAVAIL = 6 +}; +enum OpalEehFreezeActionToken { + OPAL_EEH_ACTION_CLEAR_FREEZE_MMIO = 1, + OPAL_EEH_ACTION_CLEAR_FREEZE_DMA = 2, + OPAL_EEH_ACTION_CLEAR_FREEZE_ALL = 3 +}; +enum OpalPciStatusToken { + OPAL_EEH_PHB_NO_ERROR = 0, + OPAL_EEH_PHB_FATAL = 1, + OPAL_EEH_PHB_RECOVERABLE = 2, + OPAL_EEH_PHB_BUS_ERROR = 3, + OPAL_EEH_PCI_NO_DEVSEL = 4, + OPAL_EEH_PCI_TA = 5, + OPAL_EEH_PCIEX_UR = 6, + OPAL_EEH_PCIEX_CA = 7, + OPAL_EEH_PCI_MMIO_ERROR = 8, + OPAL_EEH_PCI_DMA_ERROR = 9 +}; +enum OpalShpcAction { + OPAL_SHPC_GET_LINK_STATE = 0, + OPAL_SHPC_GET_SLOT_STATE = 1 +}; +enum OpalShpcLinkState { + OPAL_SHPC_LINK_DOWN = 0, + OPAL_SHPC_LINK_UP = 1 +}; +enum OpalMmioWindowType { + OPAL_M32_WINDOW_TYPE = 1, + OPAL_M64_WINDOW_TYPE = 2, + OPAL_IO_WINDOW_TYPE = 3 +}; +enum OpalShpcSlotState { + OPAL_SHPC_DEV_NOT_PRESENT = 0, + OPAL_SHPC_DEV_PRESENT = 1 +}; +enum OpalExceptionHandler { + OPAL_MACHINE_CHECK_HANDLER = 1, + OPAL_HYPERVISOR_MAINTENANCE_HANDLER = 2, + OPAL_SOFTPATCH_HANDLER = 3 +}; +enum OpalPendingState { + OPAL_EVENT_OPAL_INTERNAL = 0x1, + OPAL_EVENT_NVRAM = 0x2, + OPAL_EVENT_RTC = 0x4, + OPAL_EVENT_CONSOLE_OUTPUT = 0x8, + OPAL_EVENT_CONSOLE_INPUT = 0x10 +}; + +/* Machine check related definitions */ +enum OpalMCE_Version { + OpalMCE_V1 = 1, +}; + +enum OpalMCE_Severity { + OpalMCE_SEV_NO_ERROR = 0, + OpalMCE_SEV_WARNING = 1, + OpalMCE_SEV_ERROR_SYNC = 2, + OpalMCE_SEV_FATAL = 3, +}; + +enum OpalMCE_Disposition { + OpalMCE_DISPOSITION_RECOVERED = 0, + OpalMCE_DISPOSITION_NOT_RECOVERED = 1, +}; + +enum OpalMCE_Initiator { + OpalMCE_INITIATOR_UNKNOWN = 0, + OpalMCE_INITIATOR_CPU = 1, +}; + +enum OpalMCE_ErrorType { + OpalMCE_ERROR_TYPE_UNKNOWN = 0, + OpalMCE_ERROR_TYPE_UE = 1, + OpalMCE_ERROR_TYPE_SLB = 2, + OpalMCE_ERROR_TYPE_ERAT = 3, + OpalMCE_ERROR_TYPE_TLB = 4, +}; + +enum OpalMCE_UeErrorType { + OpalMCE_UE_ERROR_INDETERMINATE = 0, + OpalMCE_UE_ERROR_IFETCH = 1, + OpalMCE_UE_ERROR_PAGE_TABLE_WALK_IFETCH = 2, + OpalMCE_UE_ERROR_LOAD_STORE = 3, + OpalMCE_UE_ERROR_PAGE_TABLE_WALK_LOAD_STORE = 4, +}; + +enum OpalMCE_SlbErrorType { + OpalMCE_SLB_ERROR_INDETERMINATE = 0, + OpalMCE_SLB_ERROR_PARITY = 1, + OpalMCE_SLB_ERROR_MULTIHIT = 2, +}; + +enum OpalMCE_EratErrorType { + OpalMCE_ERAT_ERROR_INDETERMINATE = 0, + OpalMCE_ERAT_ERROR_PARITY = 1, + OpalMCE_ERAT_ERROR_MULTIHIT = 2, +}; + +enum OpalMCE_TlbErrorType { + OpalMCE_TLB_ERROR_INDETERMINATE = 0, + OpalMCE_TLB_ERROR_PARITY = 1, + OpalMCE_TLB_ERROR_MULTIHIT = 2, +}; + +enum OpalThreadStatus { + OPAL_THREAD_INACTIVE = 0x0, + OPAL_THREAD_STARTED = 0x1 +}; + +enum OpalPciBusCompare { + OpalPciBusAny = 0, /* Any bus number match */ + OpalPciBus3Bits = 2, /* Match top 3 bits of bus number */ + OpalPciBus4Bits = 3, /* Match top 4 bits of bus number */ + OpalPciBus5Bits = 4, /* Match top 5 bits of bus number */ + OpalPciBus6Bits = 5, /* Match top 6 bits of bus number */ + OpalPciBus7Bits = 6, /* Match top 7 bits of bus number */ + OpalPciBusAll = 7, /* Match bus number exactly */ +}; + +enum OpalDeviceCompare { + OPAL_IGNORE_RID_DEVICE_NUMBER = 0, + OPAL_COMPARE_RID_DEVICE_NUMBER = 1 +}; + +enum OpalFuncCompare { + OPAL_IGNORE_RID_FUNCTION_NUMBER = 0, + OPAL_COMPARE_RID_FUNCTION_NUMBER = 1 +}; + +enum OpalPeAction { + OPAL_UNMAP_PE = 0, + OPAL_MAP_PE = 1 +}; + +enum OpalPciResetAndReinitScope { + OPAL_PHB_COMPLETE = 1, OPAL_PCI_LINK = 2, OPAL_PHB_ERROR = 3, + OPAL_PCI_HOT_RESET = 4, OPAL_PCI_FUNDAMENTAL_RESET = 5, + OPAL_PCI_IODA_RESET = 6, +}; + +enum OpalPciResetState { OPAL_DEASSERT_RESET = 0, OPAL_ASSERT_RESET = 1 }; + +struct opal_machine_check_event { + enum OpalMCE_Version version:8; /* 0x00 */ + uint8_t in_use; /* 0x01 */ + enum OpalMCE_Severity severity:8; /* 0x02 */ + enum OpalMCE_Initiator initiator:8; /* 0x03 */ + enum OpalMCE_ErrorType error_type:8; /* 0x04 */ + enum OpalMCE_Disposition disposition:8; /* 0x05 */ + uint8_t reserved_1[2]; /* 0x06 */ + uint64_t gpr3; /* 0x08 */ + uint64_t srr0; /* 0x10 */ + uint64_t srr1; /* 0x18 */ + union { /* 0x20 */ + struct { + enum OpalMCE_UeErrorType ue_error_type:8; + uint8_t effective_address_provided; + uint8_t physical_address_provided; + uint8_t reserved_1[5]; + uint64_t effective_address; + uint64_t physical_address; + uint8_t reserved_2[8]; + } ue_error; + + struct { + enum OpalMCE_SlbErrorType slb_error_type:8; + uint8_t effective_address_provided; + uint8_t reserved_1[6]; + uint64_t effective_address; + uint8_t reserved_2[16]; + } slb_error; + + struct { + enum OpalMCE_EratErrorType erat_error_type:8; + uint8_t effective_address_provided; + uint8_t reserved_1[6]; + uint64_t effective_address; + uint8_t reserved_2[16]; + } erat_error; + + struct { + enum OpalMCE_TlbErrorType tlb_error_type:8; + uint8_t effective_address_provided; + uint8_t reserved_1[6]; + uint64_t effective_address; + uint8_t reserved_2[16]; + } tlb_error; + } u; +}; + +typedef struct oppanel_line { + /* XXX */ +} oppanel_line_t; + +/* API functions */ +int64_t opal_console_write(int64_t term_number, int64_t *length, + const uint8_t *buffer); +int64_t opal_console_read(int64_t term_number, int64_t *length, + uint8_t *buffer); +int64_t opal_console_write_buffer_space(int64_t term_number, + int64_t *length); +int64_t opal_rtc_read(uint32_t *year_month_day, + uint64_t *hour_minute_second_millisecond); +int64_t opal_rtc_write(uint32_t year_month_day, + uint64_t hour_minute_second_millisecond); +int64_t opal_cec_power_down(uint64_t request); +int64_t opal_cec_reboot(void); +int64_t opal_read_nvram(uint64_t buffer, uint64_t size, uint64_t offset); +int64_t opal_write_nvram(uint64_t buffer, uint64_t size, uint64_t offset); +int64_t opal_handle_interrupt(uint64_t isn, uint64_t *outstanding_event_mask); +int64_t opal_poll_events(uint64_t *outstanding_event_mask); +int64_t opal_pci_set_hub_tce_memory(uint64_t hub_id, uint64_t tce_mem_addr, + uint64_t tce_mem_size); +int64_t opal_pci_set_phb_tce_memory(uint64_t phb_id, uint64_t tce_mem_addr, + uint64_t tce_mem_size); +int64_t opal_pci_config_read_byte(uint64_t phb_id, uint64_t bus_dev_func, + uint64_t offset, uint8_t *data); +int64_t opal_pci_config_read_half_word(uint64_t phb_id, uint64_t bus_dev_func, + uint64_t offset, uint16_t *data); +int64_t opal_pci_config_read_word(uint64_t phb_id, uint64_t bus_dev_func, + uint64_t offset, uint32_t *data); +int64_t opal_pci_config_write_byte(uint64_t phb_id, uint64_t bus_dev_func, + uint64_t offset, uint8_t data); +int64_t opal_pci_config_write_half_word(uint64_t phb_id, uint64_t bus_dev_func, + uint64_t offset, uint16_t data); +int64_t opal_pci_config_write_word(uint64_t phb_id, uint64_t bus_dev_func, + uint64_t offset, uint32_t data); +int64_t opal_set_xive(uint32_t isn, uint16_t server, uint8_t priority); +int64_t opal_get_xive(uint32_t isn, uint16_t *server, uint8_t *priority); +int64_t opal_register_exception_handler(uint64_t opal_exception, + uint64_t handler_address, + uint64_t glue_cache_line); +int64_t opal_pci_eeh_freeze_status(uint64_t phb_id, uint64_t pe_number, + uint8_t *freeze_state, + uint16_t *pci_error_type, + uint64_t *phb_status); +int64_t opal_pci_eeh_freeze_clear(uint64_t phb_id, uint64_t pe_number, + uint64_t eeh_action_token); +int64_t opal_pci_shpc(uint64_t phb_id, uint64_t shpc_action, uint8_t *state); + + + +int64_t opal_pci_phb_mmio_enable(uint64_t phb_id, uint16_t window_type, + uint16_t window_num, uint16_t enable); +int64_t opal_pci_set_phb_mem_window(uint64_t phb_id, uint16_t window_type, + uint16_t window_num, + uint64_t starting_real_address, + uint64_t starting_pci_address, + uint16_t segment_size); +int64_t opal_pci_map_pe_mmio_window(uint64_t phb_id, uint16_t pe_number, + uint16_t window_type, uint16_t window_num, + uint16_t segment_num); +int64_t opal_pci_set_phb_table_memory(uint64_t phb_id, uint64_t rtt_addr, + uint64_t ivt_addr, uint64_t ivt_len, + uint64_t reject_array_addr, + uint64_t peltv_addr); +int64_t opal_pci_set_pe(uint64_t phb_id, uint64_t pe_number, uint64_t bus_dev_func, + uint8_t bus_compare, uint8_t dev_compare, uint8_t func_compare, + uint8_t pe_action); +int64_t opal_pci_set_peltv(uint64_t phb_id, uint32_t parent_pe, uint32_t child_pe, + uint8_t state); +int64_t opal_pci_set_mve(uint64_t phb_id, uint32_t mve_number, uint32_t pe_number); +int64_t opal_pci_set_mve_enable(uint64_t phb_id, uint32_t mve_number, + uint32_t state); +int64_t opal_pci_get_xive_reissue(uint64_t phb_id, uint32_t xive_number, + uint8_t *p_bit, uint8_t *q_bit); +int64_t opal_pci_set_xive_reissue(uint64_t phb_id, uint32_t xive_number, + uint8_t p_bit, uint8_t q_bit); +int64_t opal_pci_set_xive_pe(uint64_t phb_id, uint32_t pe_number, + uint32_t xive_num); +int64_t opal_get_xive_source(uint64_t phb_id, uint32_t xive_num, + int32_t *interrupt_source_number); +int64_t opal_get_msi_32(uint64_t phb_id, uint32_t mve_number, uint32_t xive_num, + uint8_t msi_range, uint32_t *msi_address, + uint32_t *message_data); +int64_t opal_get_msi_64(uint64_t phb_id, uint32_t mve_number, + uint32_t xive_num, uint8_t msi_range, + uint64_t *msi_address, uint32_t *message_data); +int64_t opal_start_cpu(uint64_t thread_number, uint64_t start_address); +int64_t opal_query_cpu_status(uint64_t thread_number, uint8_t *thread_status); +int64_t opal_write_oppanel(oppanel_line_t *lines, uint64_t num_lines); +int64_t opal_pci_map_pe_dma_window(uint64_t phb_id, uint16_t pe_number, uint16_t window_id, + uint16_t tce_levels, uint64_t tce_table_addr, + uint64_t tce_table_size, uint64_t tce_page_size); +int64_t opal_pci_map_pe_dma_window_real(uint64_t phb_id, uint16_t pe_number, + uint16_t dma_window_number, uint64_t pci_start_addr, + uint64_t pci_mem_size); +int64_t opal_pci_reset(uint64_t phb_id, uint8_t reset_scope, uint8_t assert_state); + +/* Internal functions */ +extern int early_init_dt_scan_opal(unsigned long node, const char *uname, int depth, void *data); + +extern int opal_get_chars(uint32_t vtermno, char *buf, int count); +extern int opal_put_chars(uint32_t vtermno, const char *buf, int total_len); + +extern void hvc_opal_init_early(void); + +/* Internal functions */ +extern int early_init_dt_scan_opal(unsigned long node, const char *uname, + int depth, void *data); + +#endif /* __ASSEMBLY__ */ #endif /* __OPAL_H */ diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index 7b90c564e932..831a201e03d2 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -54,6 +54,8 @@ #include #include #include +#include + #include #ifdef DEBUG @@ -707,6 +709,11 @@ void __init early_init_devtree(void *params) of_scan_flat_dt(early_init_dt_scan_rtas, NULL); #endif +#ifdef CONFIG_PPC_POWERNV + /* Some machines might need OPAL info for debugging, grab it now. */ + of_scan_flat_dt(early_init_dt_scan_opal, NULL); +#endif + #ifdef CONFIG_PHYP_DUMP /* scan tree to see if dump occurred during last boot */ of_scan_flat_dt(early_init_dt_scan_phyp_dump, NULL); diff --git a/arch/powerpc/platforms/powernv/Kconfig b/arch/powerpc/platforms/powernv/Kconfig index 24700e8df19c..74fea5c21839 100644 --- a/arch/powerpc/platforms/powernv/Kconfig +++ b/arch/powerpc/platforms/powernv/Kconfig @@ -1,11 +1,16 @@ config PPC_POWERNV depends on PPC64 && PPC_BOOK3S bool "IBM PowerNV (Non-Virtualized) platform support" - select PPC_RTAS select PPC_NATIVE select PPC_XICS select PPC_ICP_NATIVE - select PPC_ICS_RTAS select PPC_P7_NAP select PPC_PCI_CHOICE if EMBEDDED default y + +config PPC_POWERNV_RTAS + depends on PPC_POWERNV + bool "Support for RTAS based PowerNV platforms such as BML" + default y + select PPC_ICS_RTAS + select PPC_RTAS diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile index 497133027ae5..8f69c0db612c 100644 --- a/arch/powerpc/platforms/powernv/Makefile +++ b/arch/powerpc/platforms/powernv/Makefile @@ -1,2 +1,2 @@ -obj-y += setup.o opal-takeover.o +obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o obj-$(CONFIG_SMP) += smp.o diff --git a/arch/powerpc/platforms/powernv/opal-wrappers.S b/arch/powerpc/platforms/powernv/opal-wrappers.S new file mode 100644 index 000000000000..4a3f46d8533e --- /dev/null +++ b/arch/powerpc/platforms/powernv/opal-wrappers.S @@ -0,0 +1,101 @@ +/* + * PowerNV OPAL API wrappers + * + * Copyright 2011 IBM Corp. + * + * 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 + +/* TODO: + * + * - Trace irqs in/off (needs saving/restoring all args, argh...) + * - Get r11 feed up by Dave so I can have better register usage + */ +#define OPAL_CALL(name, token) \ + _GLOBAL(name); \ + mflr r0; \ + mfcr r12; \ + std r0,16(r1); \ + std r12,8(r1); \ + std r1,PACAR1(r13); \ + li r0,0; \ + mfmsr r12; \ + ori r0,r0,MSR_EE; \ + std r12,PACASAVEDMSR(r13); \ + andc r12,r12,r0; \ + mtmsrd r12,1; \ + LOAD_REG_ADDR(r0,.opal_return); \ + mtlr r0; \ + li r0,MSR_DR|MSR_IR; \ + andc r12,r12,r0; \ + li r0,token; \ + mtspr SPRN_HSRR1,r12; \ + LOAD_REG_ADDR(r11,opal); \ + ld r12,8(r11); \ + ld r2,0(r11); \ + mtspr SPRN_HSRR0,r12; \ + hrfid + +_STATIC(opal_return) + ld r2,PACATOC(r13); + ld r4,8(r1); + ld r5,16(r1); + ld r6,PACASAVEDMSR(r13); + mtspr SPRN_SRR0,r5; + mtspr SPRN_SRR1,r6; + mtcr r4; + rfid + +OPAL_CALL(opal_console_write, OPAL_CONSOLE_WRITE); +OPAL_CALL(opal_console_read, OPAL_CONSOLE_READ); +OPAL_CALL(opal_console_write_buffer_space, OPAL_CONSOLE_WRITE_BUFFER_SPACE); +OPAL_CALL(opal_rtc_read, OPAL_RTC_READ); +OPAL_CALL(opal_rtc_write, OPAL_RTC_WRITE); +OPAL_CALL(opal_cec_power_down, OPAL_CEC_POWER_DOWN); +OPAL_CALL(opal_cec_reboot, OPAL_CEC_REBOOT); +OPAL_CALL(opal_read_nvram, OPAL_READ_NVRAM); +OPAL_CALL(opal_write_nvram, OPAL_WRITE_NVRAM); +OPAL_CALL(opal_handle_interrupt, OPAL_HANDLE_INTERRUPT); +OPAL_CALL(opal_poll_events, OPAL_POLL_EVENTS); +OPAL_CALL(opal_pci_set_hub_tce_memory, OPAL_PCI_SET_HUB_TCE_MEMORY); +OPAL_CALL(opal_pci_set_phb_tce_memory, OPAL_PCI_SET_PHB_TCE_MEMORY); +OPAL_CALL(opal_pci_config_read_byte, OPAL_PCI_CONFIG_READ_BYTE); +OPAL_CALL(opal_pci_config_read_half_word, OPAL_PCI_CONFIG_READ_HALF_WORD); +OPAL_CALL(opal_pci_config_read_word, OPAL_PCI_CONFIG_READ_WORD); +OPAL_CALL(opal_pci_config_write_byte, OPAL_PCI_CONFIG_WRITE_BYTE); +OPAL_CALL(opal_pci_config_write_half_word, OPAL_PCI_CONFIG_WRITE_HALF_WORD); +OPAL_CALL(opal_pci_config_write_word, OPAL_PCI_CONFIG_WRITE_WORD); +OPAL_CALL(opal_set_xive, OPAL_SET_XIVE); +OPAL_CALL(opal_get_xive, OPAL_GET_XIVE); +OPAL_CALL(opal_register_exception_handler, OPAL_REGISTER_OPAL_EXCEPTION_HANDLER); +OPAL_CALL(opal_pci_eeh_freeze_status, OPAL_PCI_EEH_FREEZE_STATUS); +OPAL_CALL(opal_pci_eeh_freeze_clear, OPAL_PCI_EEH_FREEZE_CLEAR); +OPAL_CALL(opal_pci_shpc, OPAL_PCI_SHPC); +OPAL_CALL(opal_pci_phb_mmio_enable, OPAL_PCI_PHB_MMIO_ENABLE); +OPAL_CALL(opal_pci_set_phb_mem_window, OPAL_PCI_SET_PHB_MEM_WINDOW); +OPAL_CALL(opal_pci_map_pe_mmio_window, OPAL_PCI_MAP_PE_MMIO_WINDOW); +OPAL_CALL(opal_pci_set_phb_table_memory, OPAL_PCI_SET_PHB_TABLE_MEMORY); +OPAL_CALL(opal_pci_set_pe, OPAL_PCI_SET_PE); +OPAL_CALL(opal_pci_set_peltv, OPAL_PCI_SET_PELTV); +OPAL_CALL(opal_pci_set_mve, OPAL_PCI_SET_MVE); +OPAL_CALL(opal_pci_set_mve_enable, OPAL_PCI_SET_MVE_ENABLE); +OPAL_CALL(opal_pci_get_xive_reissue, OPAL_PCI_GET_XIVE_REISSUE); +OPAL_CALL(opal_pci_set_xive_reissue, OPAL_PCI_SET_XIVE_REISSUE); +OPAL_CALL(opal_pci_set_xive_pe, OPAL_PCI_SET_XIVE_PE); +OPAL_CALL(opal_get_xive_source, OPAL_GET_XIVE_SOURCE); +OPAL_CALL(opal_get_msi_32, OPAL_GET_MSI_32); +OPAL_CALL(opal_get_msi_64, OPAL_GET_MSI_64); +OPAL_CALL(opal_start_cpu, OPAL_START_CPU); +OPAL_CALL(opal_query_cpu_status, OPAL_QUERY_CPU_STATUS); +OPAL_CALL(opal_write_oppanel, OPAL_WRITE_OPPANEL); +OPAL_CALL(opal_pci_map_pe_dma_window, OPAL_PCI_MAP_PE_DMA_WINDOW); +OPAL_CALL(opal_pci_map_pe_dma_window_real, OPAL_PCI_MAP_PE_DMA_WINDOW_REAL); +OPAL_CALL(opal_pci_reset, OPAL_PCI_RESET); diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c new file mode 100644 index 000000000000..8d5510784cc2 --- /dev/null +++ b/arch/powerpc/platforms/powernv/opal.c @@ -0,0 +1,154 @@ +/* + * PowerNV OPAL high level interfaces + * + * Copyright 2011 IBM Corp. + * + * 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. + */ + +#undef DEBUG + +#include +#include +#include +#include +#include + +#include "powernv.h" + +struct opal { + u64 base; + u64 entry; +} opal; + +static struct device_node *opal_node; +static DEFINE_SPINLOCK(opal_write_lock); + +int __init early_init_dt_scan_opal(unsigned long node, + const char *uname, int depth, void *data) +{ + const void *basep, *entryp; + unsigned long basesz, entrysz; + + if (depth != 1 || strcmp(uname, "ibm,opal") != 0) + return 0; + + basep = of_get_flat_dt_prop(node, "opal-base-address", &basesz); + entryp = of_get_flat_dt_prop(node, "opal-entry-address", &entrysz); + + if (!basep || !entryp) + return 1; + + opal.base = of_read_number(basep, basesz/4); + opal.entry = of_read_number(entryp, entrysz/4); + + pr_debug("OPAL Base = 0x%llx (basep=%p basesz=%ld)\n", + opal.base, basep, basesz); + pr_debug("OPAL Entry = 0x%llx (entryp=%p basesz=%ld)\n", + opal.entry, entryp, entrysz); + + powerpc_firmware_features |= FW_FEATURE_OPAL; + if (of_flat_dt_is_compatible(node, "ibm,opal-v2")) { + powerpc_firmware_features |= FW_FEATURE_OPALv2; + printk("OPAL V2 detected !\n"); + } else { + printk("OPAL V1 detected !\n"); + } + + return 1; +} + +int opal_get_chars(uint32_t vtermno, char *buf, int count) +{ + s64 len, rc; + u64 evt; + + if (!opal.entry) + return 0; + opal_poll_events(&evt); + if ((evt & OPAL_EVENT_CONSOLE_INPUT) == 0) + return 0; + len = count; + rc = opal_console_read(vtermno, &len, buf); + if (rc == OPAL_SUCCESS) + return len; + return 0; +} + +int opal_put_chars(uint32_t vtermno, const char *data, int total_len) +{ + int written = 0; + s64 len, rc = OPAL_BUSY; + unsigned long flags; + u64 evt; + + if (!opal.entry) + return 0; + + /* We want put_chars to be atomic to avoid mangling of hvsi + * packets. To do that, we first test for room and return + * -EAGAIN if there isn't enough + */ + spin_lock_irqsave(&opal_write_lock, flags); + rc = opal_console_write_buffer_space(vtermno, &len); + if (rc || len < total_len) { + spin_unlock_irqrestore(&opal_write_lock, flags); + /* Closed -> drop characters */ + if (rc) + return total_len; + opal_poll_events(&evt); + return -EAGAIN; + } + + /* We still try to handle partial completions, though they + * should no longer happen. + */ + while(total_len > 0 && (rc == OPAL_BUSY || + rc == OPAL_BUSY_EVENT || rc == OPAL_SUCCESS)) { + len = total_len; + rc = opal_console_write(vtermno, &len, data); + if (rc == OPAL_SUCCESS) { + total_len -= len; + data += len; + written += len; + } + /* This is a bit nasty but we need that for the console to + * flush when there aren't any interrupts. We will clean + * things a bit later to limit that to synchronous path + * such as the kernel console and xmon/udbg + */ + do + opal_poll_events(&evt); + while(rc == OPAL_SUCCESS && (evt & OPAL_EVENT_CONSOLE_OUTPUT)); + } + spin_unlock_irqrestore(&opal_write_lock, flags); + return written; +} + +static int __init opal_init(void) +{ + struct device_node *np, *consoles; + + opal_node = of_find_node_by_path("/ibm,opal"); + if (!opal_node) { + pr_warn("opal: Node not found\n"); + return -ENODEV; + } + if (firmware_has_feature(FW_FEATURE_OPALv2)) + consoles = of_find_node_by_path("/ibm,opal/consoles"); + else + consoles = of_node_get(opal_node); + + /* Register serial ports */ + for_each_child_of_node(consoles, np) { + if (strcmp(np->name, "serial")) + continue; + of_platform_device_create(np, NULL, NULL); + } + of_node_put(consoles); + return 0; +} +subsys_initcall(opal_init); diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c index 569f9cc4eb04..b6e5ff85cc6f 100644 --- a/arch/powerpc/platforms/powernv/setup.c +++ b/arch/powerpc/platforms/powernv/setup.c @@ -74,6 +74,12 @@ static void pnv_show_cpuinfo(struct seq_file *m) if (root) model = of_get_property(root, "model", NULL); seq_printf(m, "machine\t\t: PowerNV %s\n", model); + if (firmware_has_feature(FW_FEATURE_OPALv2)) + seq_printf(m, "firmware\t: OPAL v2\n"); + else if (firmware_has_feature(FW_FEATURE_OPAL)) + seq_printf(m, "firmware\t: OPAL v1\n"); + else + seq_printf(m, "firmware\t: BML\n"); of_node_put(root); } diff --git a/arch/powerpc/platforms/powernv/smp.c b/arch/powerpc/platforms/powernv/smp.c index 4f4ec3797eb6..e87736685243 100644 --- a/arch/powerpc/platforms/powernv/smp.c +++ b/arch/powerpc/platforms/powernv/smp.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "powernv.h" @@ -62,6 +63,28 @@ static int pnv_smp_cpu_bootable(unsigned int nr) return 1; } +int __devinit pnv_smp_kick_cpu(int nr) +{ + unsigned int pcpu = get_hard_smp_processor_id(nr); + unsigned long start_here = __pa(*((unsigned long *) + generic_secondary_smp_init)); + long rc; + + BUG_ON(nr < 0 || nr >= NR_CPUS); + + /* On OPAL v2 the CPU are still spinning inside OPAL itself, + * get them back now + */ + if (firmware_has_feature(FW_FEATURE_OPALv2)) { + pr_devel("OPAL: Starting CPU %d (HW 0x%x)...\n", nr, pcpu); + rc = opal_start_cpu(pcpu, start_here); + if (rc != OPAL_SUCCESS) + pr_warn("OPAL Error %ld starting CPU %d\n", + rc, nr); + } + return smp_generic_kick_cpu(nr); +} + #ifdef CONFIG_HOTPLUG_CPU static int pnv_smp_cpu_disable(void) @@ -127,7 +150,7 @@ static struct smp_ops_t pnv_smp_ops = { .message_pass = smp_muxed_ipi_message_pass, .cause_ipi = NULL, /* Filled at runtime by xics_smp_probe() */ .probe = xics_smp_probe, - .kick_cpu = smp_generic_kick_cpu, + .kick_cpu = pnv_smp_kick_cpu, .setup_cpu = pnv_smp_setup_cpu, .cpu_bootable = pnv_smp_cpu_bootable, #ifdef CONFIG_HOTPLUG_CPU From 6e35d5dac0c83ebb616ff3b9c2d6155c9a9ccb86 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 18:28:01 +0000 Subject: [PATCH 0216/1531] powerpc/powernv: Add support for instanciating OPAL v2 from Open Firmware OPAL v2 is instantiated in a way similar to RTAS using Open Firmware client interface calls, and the resulting address and entry point are put in the device-tree Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/prom_init.c | 136 +++++++++++++++++++++++++++----- 1 file changed, 117 insertions(+), 19 deletions(-) diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index e3f390427216..e96f5d0d2c78 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -140,7 +140,9 @@ struct mem_map_entry { typedef u32 cell_t; -extern void __start(unsigned long r3, unsigned long r4, unsigned long r5); +extern void __start(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7, unsigned long r8, + unsigned long r9); #ifdef CONFIG_PPC64 extern int enter_prom(struct prom_args *args, unsigned long entry); @@ -1293,6 +1295,11 @@ static int __initdata prom_rtas_start_cpu; static u64 __initdata prom_rtas_data; static u64 __initdata prom_rtas_entry; +#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL +static u64 __initdata prom_opal_base; +static u64 __initdata prom_opal_entry; +#endif + /* XXX Don't change this structure without updating opal-takeover.S */ static struct opal_secondary_data { s64 ack; /* 0 */ @@ -1468,6 +1475,76 @@ static void prom_opal_takeover(void) for (;;) opal_do_takeover(args); } + +/* + * Allocate room for and instantiate OPAL + */ +static void __init prom_instantiate_opal(void) +{ + phandle opal_node; + ihandle opal_inst; + u64 base, entry; + u64 size = 0, align = 0x10000; + u32 rets[2]; + + prom_debug("prom_instantiate_opal: start...\n"); + + opal_node = call_prom("finddevice", 1, 1, ADDR("/ibm,opal")); + prom_debug("opal_node: %x\n", opal_node); + if (!PHANDLE_VALID(opal_node)) + return; + + prom_getprop(opal_node, "opal-runtime-size", &size, sizeof(size)); + if (size == 0) + return; + prom_getprop(opal_node, "opal-runtime-alignment", &align, + sizeof(align)); + + base = alloc_down(size, align, 0); + if (base == 0) { + prom_printf("OPAL allocation failed !\n"); + return; + } + + opal_inst = call_prom("open", 1, 1, ADDR("/ibm,opal")); + if (!IHANDLE_VALID(opal_inst)) { + prom_printf("opening opal package failed (%x)\n", opal_inst); + return; + } + + prom_printf("instantiating opal at 0x%x...", base); + + if (call_prom_ret("call-method", 4, 3, rets, + ADDR("load-opal-runtime"), + opal_inst, + base >> 32, base & 0xffffffff) != 0 + || (rets[0] == 0 && rets[1] == 0)) { + prom_printf(" failed\n"); + return; + } + entry = (((u64)rets[0]) << 32) | rets[1]; + + prom_printf(" done\n"); + + reserve_mem(base, size); + + prom_debug("opal base = 0x%x\n", base); + prom_debug("opal align = 0x%x\n", align); + prom_debug("opal entry = 0x%x\n", entry); + prom_debug("opal size = 0x%x\n", (long)size); + + prom_setprop(opal_node, "/ibm,opal", "opal-base-address", + &base, sizeof(base)); + prom_setprop(opal_node, "/ibm,opal", "opal-entry-address", + &entry, sizeof(entry)); + +#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL + RELOC(prom_opal_base) = base; + RELOC(prom_opal_entry) = entry; +#endif + prom_debug("prom_instantiate_opal: end...\n"); +} + #endif /* CONFIG_PPC_POWERNV */ /* @@ -1863,7 +1940,7 @@ static int __init prom_find_machine_type(void) int x; #endif - /* Look for a PowerMac */ + /* Look for a PowerMac or a Cell */ len = prom_getprop(_prom->root, "compatible", compat, sizeof(compat)-1); if (len > 0) { @@ -1889,7 +1966,11 @@ static int __init prom_find_machine_type(void) } } #ifdef CONFIG_PPC64 - /* If not a mac, try to figure out if it's an IBM pSeries or any other + /* Try to detect OPAL */ + if (PHANDLE_VALID(call_prom("finddevice", 1, 1, ADDR("/ibm,opal")))) + return PLATFORM_OPAL; + + /* Try to figure out if it's an IBM pSeries or any other * PAPR compliant platform. We assume it is if : * - /device_type is "chrp" (please, do NOT use that for future * non-IBM designs ! @@ -2116,7 +2197,7 @@ static void __init scan_dt_build_struct(phandle node, unsigned long *mem_start, unsigned long soff; unsigned char *valp; static char pname[MAX_PROPERTY_NAME]; - int l, room; + int l, room, has_phandle = 0; dt_push_token(OF_DT_BEGIN_NODE, mem_start, mem_end); @@ -2200,19 +2281,26 @@ static void __init scan_dt_build_struct(phandle node, unsigned long *mem_start, valp = make_room(mem_start, mem_end, l, 4); call_prom("getprop", 4, 1, node, RELOC(pname), valp, l); *mem_start = _ALIGN(*mem_start, 4); + + if (!strcmp(RELOC(pname), RELOC("phandle"))) + has_phandle = 1; } - /* Add a "linux,phandle" property. */ - soff = dt_find_string(RELOC("linux,phandle")); - if (soff == 0) - prom_printf("WARNING: Can't find string index for" - " node %s\n", path); - else { - dt_push_token(OF_DT_PROP, mem_start, mem_end); - dt_push_token(4, mem_start, mem_end); - dt_push_token(soff, mem_start, mem_end); - valp = make_room(mem_start, mem_end, 4, 4); - *(u32 *)valp = node; + /* Add a "linux,phandle" property if no "phandle" property already + * existed (can happen with OPAL) + */ + if (!has_phandle) { + soff = dt_find_string(RELOC("linux,phandle")); + if (soff == 0) + prom_printf("WARNING: Can't find string index for" + " node %s\n", path); + else { + dt_push_token(OF_DT_PROP, mem_start, mem_end); + dt_push_token(4, mem_start, mem_end); + dt_push_token(soff, mem_start, mem_end); + valp = make_room(mem_start, mem_end, 4, 4); + *(u32 *)valp = node; + } } /* do all our children */ @@ -2746,6 +2834,7 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, * between pSeries SMP and pSeries LPAR */ RELOC(of_platform) = prom_find_machine_type(); + prom_printf("Detected machine type: %x\n", RELOC(of_platform)); #ifndef CONFIG_RELOCATABLE /* Bail if this is a kdump kernel. */ @@ -2807,7 +2896,8 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, * On non-powermacs, try to instantiate RTAS. PowerMacs don't * have a usable RTAS implementation. */ - if (RELOC(of_platform) != PLATFORM_POWERMAC) + if (RELOC(of_platform) != PLATFORM_POWERMAC && + RELOC(of_platform) != PLATFORM_OPAL) prom_instantiate_rtas(); #ifdef CONFIG_PPC_POWERNV @@ -2818,7 +2908,8 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, prom_opal_hold_cpus(); prom_opal_takeover(); } - } + } else if (RELOC(of_platform) == PLATFORM_OPAL) + prom_instantiate_opal(); #endif /* @@ -2826,7 +2917,8 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, * * PowerMacs use a different mechanism to spin CPUs */ - if (RELOC(of_platform) != PLATFORM_POWERMAC) + if (RELOC(of_platform) != PLATFORM_POWERMAC && + RELOC(of_platform) != PLATFORM_OPAL) prom_hold_cpus(); /* @@ -2894,7 +2986,13 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, reloc_got2(-offset); #endif - __start(hdr, kbase, 0); +#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL + /* OPAL early debug gets the OPAL base & entry in r8 and r9 */ + __start(hdr, kbase, 0, 0, 0, + RELOC(prom_opal_base), RELOC(prom_opal_entry)); +#else + __start(hdr, kbase, 0, 0, 0, 0, 0); +#endif return 0; } From daea1175a9f0f70eab5b33e2827d57ba8c686816 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:44:59 +0000 Subject: [PATCH 0217/1531] powerpc/powernv: Support for OPAL console This adds a udbg and an hvc console backend for supporting a console using the OPAL console interfaces. On OPAL v1 we have hvc0 mapped to whatever console the system was configured for (network or hvsi serial port) via the service processor. On OPAL v2 we have hvcN mapped to the Nth console provided by OPAL which generally corresponds to: hvc0 : network console (raw protocol) hvc1 : serial port S1 (hvsi) hvc2 : serial port S2 (hvsi) Note: At this point, early debug console only works with OPAL v1 and shouldn't be enabled in a normal kernel. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/Kconfig.debug | 31 ++ arch/powerpc/include/asm/opal.h | 5 + arch/powerpc/include/asm/udbg.h | 2 + arch/powerpc/kernel/head_64.S | 14 +- arch/powerpc/kernel/udbg.c | 4 + arch/powerpc/platforms/powernv/opal.c | 31 +- arch/powerpc/platforms/powernv/setup.c | 14 +- drivers/tty/hvc/Kconfig | 9 + drivers/tty/hvc/Makefile | 1 + drivers/tty/hvc/hvc_opal.c | 424 +++++++++++++++++++++++++ drivers/tty/hvc/hvsi_lib.c | 4 +- 11 files changed, 517 insertions(+), 22 deletions(-) create mode 100644 drivers/tty/hvc/hvc_opal.c diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug index 06eb62b0fd90..1b8a9c905cf7 100644 --- a/arch/powerpc/Kconfig.debug +++ b/arch/powerpc/Kconfig.debug @@ -265,8 +265,27 @@ config PPC_EARLY_DEBUG_PS3GELIC Select this to enable early debugging for the PlayStation3 via UDP broadcasts sent out through the Ethernet port. +config PPC_EARLY_DEBUG_OPAL_RAW + bool "OPAL raw console" + depends on HVC_OPAL + help + Select this to enable early debugging for the PowerNV platform + using a "raw" console + +config PPC_EARLY_DEBUG_OPAL_HVSI + bool "OPAL hvsi console" + depends on HVC_OPAL + help + Select this to enable early debugging for the PowerNV platform + using an "hvsi" console + endchoice +config PPC_EARLY_DEBUG_OPAL + def_bool y + depends on PPC_EARLY_DEBUG_OPAL_RAW || PPC_EARLY_DEBUG_OPAL_HVSI + + config PPC_EARLY_DEBUG_HVSI_VTERMNO hex "vterm number to use with early debug HVSI" depends on PPC_EARLY_DEBUG_LPAR_HVSI @@ -275,6 +294,18 @@ config PPC_EARLY_DEBUG_HVSI_VTERMNO You probably want 0x30000000 for your first serial port and 0x30000001 for your second one +config PPC_EARLY_DEBUG_OPAL_VTERMNO + hex "vterm number to use with OPAL early debug" + depends on PPC_EARLY_DEBUG_OPAL + default "0" + help + This correspond to which /dev/hvcN you want to use for early + debug. + + On OPAL v1 (takeover) this should always be 0 + On OPAL v2, this will be 0 for network console and 1 or 2 for + the machine built-in serial ports. + config PPC_EARLY_DEBUG_44x_PHYSLOW hex "Low 32 bits of early debug UART physical address" depends on PPC_EARLY_DEBUG_44x diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h index c7a3202d10a0..749de00a02d5 100644 --- a/arch/powerpc/include/asm/opal.h +++ b/arch/powerpc/include/asm/opal.h @@ -425,6 +425,11 @@ extern void hvc_opal_init_early(void); extern int early_init_dt_scan_opal(unsigned long node, const char *uname, int depth, void *data); +extern int opal_get_chars(uint32_t vtermno, char *buf, int count); +extern int opal_put_chars(uint32_t vtermno, const char *buf, int total_len); + +extern void hvc_opal_init_early(void); + #endif /* __ASSEMBLY__ */ #endif /* __OPAL_H */ diff --git a/arch/powerpc/include/asm/udbg.h b/arch/powerpc/include/asm/udbg.h index 7cf796fa03f2..6587ec7bc6ec 100644 --- a/arch/powerpc/include/asm/udbg.h +++ b/arch/powerpc/include/asm/udbg.h @@ -55,6 +55,8 @@ extern void __init udbg_init_cpm(void); extern void __init udbg_init_usbgecko(void); extern void __init udbg_init_wsp(void); extern void __init udbg_init_ps3gelic(void); +extern void __init udbg_init_debug_opal_raw(void); +extern void __init udbg_init_debug_opal_hvsi(void); #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_UDBG_H */ diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S index dea8191253d2..06c7251c1bf7 100644 --- a/arch/powerpc/kernel/head_64.S +++ b/arch/powerpc/kernel/head_64.S @@ -53,7 +53,8 @@ * 2. The kernel is entered at __start * -or- For OPAL entry: * 1. The MMU is off, processor in HV mode, primary CPU enters at 0 - * with device-tree in gpr3 + * with device-tree in gpr3. We also get OPAL base in r8 and + * entry in r9 for debugging purposes * 2. Secondary processors enter at 0x60 with PIR in gpr3 * * For iSeries: @@ -335,6 +336,11 @@ _GLOBAL(__start_initialization_multiplatform) /* Save parameters */ mr r31,r3 mr r30,r4 +#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL + /* Save OPAL entry */ + mr r28,r8 + mr r29,r9 +#endif #ifdef CONFIG_PPC_BOOK3E bl .start_initialization_book3e @@ -711,6 +717,12 @@ _INIT_STATIC(start_here_multiplatform) bdnz 3b 4: +#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL + /* Setup OPAL entry */ + std r28,0(r11); + std r29,8(r11); +#endif + #ifndef CONFIG_PPC_BOOK3E mfmsr r6 ori r6,r6,MSR_RI diff --git a/arch/powerpc/kernel/udbg.c b/arch/powerpc/kernel/udbg.c index 5b3e98e03158..35f948203ec5 100644 --- a/arch/powerpc/kernel/udbg.c +++ b/arch/powerpc/kernel/udbg.c @@ -69,6 +69,10 @@ void __init udbg_early_init(void) udbg_init_wsp(); #elif defined(CONFIG_PPC_EARLY_DEBUG_PS3GELIC) udbg_init_ps3gelic(); +#elif defined(CONFIG_PPC_EARLY_DEBUG_OPAL_RAW) + udbg_init_debug_opal_raw(); +#elif defined(CONFIG_PPC_EARLY_DEBUG_OPAL_HVSI) + udbg_init_debug_opal_hvsi(); #endif #ifdef CONFIG_PPC_EARLY_DEBUG diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c index 8d5510784cc2..7887733b9b31 100644 --- a/arch/powerpc/platforms/powernv/opal.c +++ b/arch/powerpc/platforms/powernv/opal.c @@ -67,7 +67,7 @@ int opal_get_chars(uint32_t vtermno, char *buf, int count) u64 evt; if (!opal.entry) - return 0; + return -ENODEV; opal_poll_events(&evt); if ((evt & OPAL_EVENT_CONSOLE_INPUT) == 0) return 0; @@ -81,31 +81,38 @@ int opal_get_chars(uint32_t vtermno, char *buf, int count) int opal_put_chars(uint32_t vtermno, const char *data, int total_len) { int written = 0; - s64 len, rc = OPAL_BUSY; + s64 len, rc; unsigned long flags; u64 evt; if (!opal.entry) - return 0; + return -ENODEV; /* We want put_chars to be atomic to avoid mangling of hvsi * packets. To do that, we first test for room and return - * -EAGAIN if there isn't enough + * -EAGAIN if there isn't enough. + * + * Unfortunately, opal_console_write_buffer_space() doesn't + * appear to work on opal v1, so we just assume there is + * enough room and be done with it */ spin_lock_irqsave(&opal_write_lock, flags); - rc = opal_console_write_buffer_space(vtermno, &len); - if (rc || len < total_len) { - spin_unlock_irqrestore(&opal_write_lock, flags); - /* Closed -> drop characters */ - if (rc) - return total_len; - opal_poll_events(&evt); - return -EAGAIN; + if (firmware_has_feature(FW_FEATURE_OPALv2)) { + rc = opal_console_write_buffer_space(vtermno, &len); + if (rc || len < total_len) { + spin_unlock_irqrestore(&opal_write_lock, flags); + /* Closed -> drop characters */ + if (rc) + return total_len; + opal_poll_events(&evt); + return -EAGAIN; + } } /* We still try to handle partial completions, though they * should no longer happen. */ + rc = OPAL_BUSY; while(total_len > 0 && (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT || rc == OPAL_SUCCESS)) { len = total_len; diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c index b6e5ff85cc6f..07ba1ecd1807 100644 --- a/arch/powerpc/platforms/powernv/setup.c +++ b/arch/powerpc/platforms/powernv/setup.c @@ -29,17 +29,12 @@ #include #include #include +#include #include "powernv.h" static void __init pnv_setup_arch(void) { - /* Force console to hvc for now until we have sorted out the - * real console situation for the platform. This will make - * hvc_udbg work at least. - */ - add_preferred_console("hvc", 0, NULL); - /* Initialize SMP */ pnv_smp_init(); @@ -55,7 +50,12 @@ static void __init pnv_setup_arch(void) static void __init pnv_init_early(void) { - /* XXX IOMMU */ +#ifdef CONFIG_HVC_OPAL + if (firmware_has_feature(FW_FEATURE_OPAL)) + hvc_opal_init_early(); + else +#endif + add_preferred_console("hvc", 0, NULL); } static void __init pnv_init_IRQ(void) diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig index e371753ba921..4222035acfb7 100644 --- a/drivers/tty/hvc/Kconfig +++ b/drivers/tty/hvc/Kconfig @@ -34,6 +34,15 @@ config HVC_ISERIES help iSeries machines support a hypervisor virtual console. +config HVC_OPAL + bool "OPAL Console support" + depends on PPC_POWERNV + select HVC_DRIVER + select HVC_IRQ + default y + help + PowerNV machines running under OPAL need that driver to get a console + config HVC_RTAS bool "IBM RTAS Console support" depends on PPC_RTAS diff --git a/drivers/tty/hvc/Makefile b/drivers/tty/hvc/Makefile index e29205316376..89abf40bc73d 100644 --- a/drivers/tty/hvc/Makefile +++ b/drivers/tty/hvc/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o hvsi_lib.o +obj-$(CONFIG_HVC_OPAL) += hvc_opal.o hvsi_lib.o obj-$(CONFIG_HVC_OLD_HVSI) += hvsi.o obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c new file mode 100644 index 000000000000..7b38512d6c41 --- /dev/null +++ b/drivers/tty/hvc/hvc_opal.c @@ -0,0 +1,424 @@ +/* + * opal driver interface to hvc_console.c + * + * Copyright 2011 Benjamin Herrenschmidt , IBM Corp. + * + * 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 + * + */ + +#undef DEBUG + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "hvc_console.h" + +static const char hvc_opal_name[] = "hvc_opal"; + +static struct of_device_id hvc_opal_match[] __devinitdata = { + { .name = "serial", .compatible = "ibm,opal-console-raw" }, + { .name = "serial", .compatible = "ibm,opal-console-hvsi" }, + { }, +}; + +typedef enum hv_protocol { + HV_PROTOCOL_RAW, + HV_PROTOCOL_HVSI +} hv_protocol_t; + +struct hvc_opal_priv { + hv_protocol_t proto; /* Raw data or HVSI packets */ + struct hvsi_priv hvsi; /* HVSI specific data */ +}; +static struct hvc_opal_priv *hvc_opal_privs[MAX_NR_HVC_CONSOLES]; + +/* For early boot console */ +static struct hvc_opal_priv hvc_opal_boot_priv; +static u32 hvc_opal_boot_termno; + +static const struct hv_ops hvc_opal_raw_ops = { + .get_chars = opal_get_chars, + .put_chars = opal_put_chars, + .notifier_add = notifier_add_irq, + .notifier_del = notifier_del_irq, + .notifier_hangup = notifier_hangup_irq, +}; + +static int hvc_opal_hvsi_get_chars(uint32_t vtermno, char *buf, int count) +{ + struct hvc_opal_priv *pv = hvc_opal_privs[vtermno]; + + if (WARN_ON(!pv)) + return -ENODEV; + + return hvsilib_get_chars(&pv->hvsi, buf, count); +} + +static int hvc_opal_hvsi_put_chars(uint32_t vtermno, const char *buf, int count) +{ + struct hvc_opal_priv *pv = hvc_opal_privs[vtermno]; + + if (WARN_ON(!pv)) + return -ENODEV; + + return hvsilib_put_chars(&pv->hvsi, buf, count); +} + +static int hvc_opal_hvsi_open(struct hvc_struct *hp, int data) +{ + struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno]; + int rc; + + pr_devel("HVSI@%x: do open !\n", hp->vtermno); + + rc = notifier_add_irq(hp, data); + if (rc) + return rc; + + return hvsilib_open(&pv->hvsi, hp); +} + +static void hvc_opal_hvsi_close(struct hvc_struct *hp, int data) +{ + struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno]; + + pr_devel("HVSI@%x: do close !\n", hp->vtermno); + + hvsilib_close(&pv->hvsi, hp); + + notifier_del_irq(hp, data); +} + +void hvc_opal_hvsi_hangup(struct hvc_struct *hp, int data) +{ + struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno]; + + pr_devel("HVSI@%x: do hangup !\n", hp->vtermno); + + hvsilib_close(&pv->hvsi, hp); + + notifier_hangup_irq(hp, data); +} + +static int hvc_opal_hvsi_tiocmget(struct hvc_struct *hp) +{ + struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno]; + + if (!pv) + return -EINVAL; + return pv->hvsi.mctrl; +} + +static int hvc_opal_hvsi_tiocmset(struct hvc_struct *hp, unsigned int set, + unsigned int clear) +{ + struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno]; + + pr_devel("HVSI@%x: Set modem control, set=%x,clr=%x\n", + hp->vtermno, set, clear); + + if (set & TIOCM_DTR) + hvsilib_write_mctrl(&pv->hvsi, 1); + else if (clear & TIOCM_DTR) + hvsilib_write_mctrl(&pv->hvsi, 0); + + return 0; +} + +static const struct hv_ops hvc_opal_hvsi_ops = { + .get_chars = hvc_opal_hvsi_get_chars, + .put_chars = hvc_opal_hvsi_put_chars, + .notifier_add = hvc_opal_hvsi_open, + .notifier_del = hvc_opal_hvsi_close, + .notifier_hangup = hvc_opal_hvsi_hangup, + .tiocmget = hvc_opal_hvsi_tiocmget, + .tiocmset = hvc_opal_hvsi_tiocmset, +}; + +static int __devinit hvc_opal_probe(struct platform_device *dev) +{ + const struct hv_ops *ops; + struct hvc_struct *hp; + struct hvc_opal_priv *pv; + hv_protocol_t proto; + unsigned int termno, boot = 0; + const __be32 *reg; + + if (of_device_is_compatible(dev->dev.of_node, "ibm,opal-console-raw")) { + proto = HV_PROTOCOL_RAW; + ops = &hvc_opal_raw_ops; + } else if (of_device_is_compatible(dev->dev.of_node, + "ibm,opal-console-hvsi")) { + proto = HV_PROTOCOL_HVSI; + ops = &hvc_opal_hvsi_ops; + } else { + pr_err("hvc_opal: Unkown protocol for %s\n", + dev->dev.of_node->full_name); + return -ENXIO; + } + + reg = of_get_property(dev->dev.of_node, "reg", NULL); + termno = reg ? be32_to_cpup(reg) : 0; + + /* Is it our boot one ? */ + if (hvc_opal_privs[termno] == &hvc_opal_boot_priv) { + pv = hvc_opal_privs[termno]; + boot = 1; + } else if (hvc_opal_privs[termno] == NULL) { + pv = kzalloc(sizeof(struct hvc_opal_priv), GFP_KERNEL); + if (!pv) + return -ENOMEM; + pv->proto = proto; + hvc_opal_privs[termno] = pv; + if (proto == HV_PROTOCOL_HVSI) + hvsilib_init(&pv->hvsi, opal_get_chars, opal_put_chars, + termno, 0); + + /* Instanciate now to establish a mapping index==vtermno */ + hvc_instantiate(termno, termno, ops); + } else { + pr_err("hvc_opal: Device %s has duplicate terminal number #%d\n", + dev->dev.of_node->full_name, termno); + return -ENXIO; + } + + pr_info("hvc%d: %s protocol on %s%s\n", termno, + proto == HV_PROTOCOL_RAW ? "raw" : "hvsi", + dev->dev.of_node->full_name, + boot ? " (boot console)" : ""); + + /* We don't do IRQ yet */ + hp = hvc_alloc(termno, 0, ops, MAX_VIO_PUT_CHARS); + if (IS_ERR(hp)) + return PTR_ERR(hp); + dev_set_drvdata(&dev->dev, hp); + + return 0; +} + +static int __devexit hvc_opal_remove(struct platform_device *dev) +{ + struct hvc_struct *hp = dev_get_drvdata(&dev->dev); + int rc, termno; + + termno = hp->vtermno; + rc = hvc_remove(hp); + if (rc == 0) { + if (hvc_opal_privs[termno] != &hvc_opal_boot_priv) + kfree(hvc_opal_privs[termno]); + hvc_opal_privs[termno] = NULL; + } + return rc; +} + +static struct platform_driver hvc_opal_driver = { + .probe = hvc_opal_probe, + .remove = __devexit_p(hvc_opal_remove), + .driver = { + .name = hvc_opal_name, + .owner = THIS_MODULE, + .of_match_table = hvc_opal_match, + } +}; + +static int __init hvc_opal_init(void) +{ + if (!firmware_has_feature(FW_FEATURE_OPAL)) + return -ENODEV; + + /* Register as a vio device to receive callbacks */ + return platform_driver_register(&hvc_opal_driver); +} +module_init(hvc_opal_init); + +static void __exit hvc_opal_exit(void) +{ + platform_driver_unregister(&hvc_opal_driver); +} +module_exit(hvc_opal_exit); + +static void udbg_opal_putc(char c) +{ + unsigned int termno = hvc_opal_boot_termno; + int count = -1; + + if (c == '\n') + udbg_opal_putc('\r'); + + do { + switch(hvc_opal_boot_priv.proto) { + case HV_PROTOCOL_RAW: + count = opal_put_chars(termno, &c, 1); + break; + case HV_PROTOCOL_HVSI: + count = hvc_opal_hvsi_put_chars(termno, &c, 1); + break; + } + } while(count == 0 || count == -EAGAIN); +} + +static int udbg_opal_getc_poll(void) +{ + unsigned int termno = hvc_opal_boot_termno; + int rc = 0; + char c; + + switch(hvc_opal_boot_priv.proto) { + case HV_PROTOCOL_RAW: + rc = opal_get_chars(termno, &c, 1); + break; + case HV_PROTOCOL_HVSI: + rc = hvc_opal_hvsi_get_chars(termno, &c, 1); + break; + } + if (!rc) + return -1; + return c; +} + +static int udbg_opal_getc(void) +{ + int ch; + for (;;) { + ch = udbg_opal_getc_poll(); + if (ch == -1) { + /* This shouldn't be needed...but... */ + volatile unsigned long delay; + for (delay=0; delay < 2000000; delay++) + ; + } else { + return ch; + } + } +} + +static void udbg_init_opal_common(void) +{ + udbg_putc = udbg_opal_putc; + udbg_getc = udbg_opal_getc; + udbg_getc_poll = udbg_opal_getc_poll; + tb_ticks_per_usec = 0x200; /* Make udelay not suck */ +} + +void __init hvc_opal_init_early(void) +{ + struct device_node *stdout_node = NULL; + const u32 *termno; + const char *name = NULL; + const struct hv_ops *ops; + u32 index; + + /* find the boot console from /chosen/stdout */ + if (of_chosen) + name = of_get_property(of_chosen, "linux,stdout-path", NULL); + if (name) { + stdout_node = of_find_node_by_path(name); + if (!stdout_node) { + pr_err("hvc_opal: Failed to locate default console!\n"); + return; + } + } else { + struct device_node *opal, *np; + + /* Current OPAL takeover doesn't provide the stdout + * path, so we hard wire it + */ + opal = of_find_node_by_path("/ibm,opal/consoles"); + if (opal) + pr_devel("hvc_opal: Found consoles in new location\n"); + if (!opal) { + opal = of_find_node_by_path("/ibm,opal"); + if (opal) + pr_devel("hvc_opal: " + "Found consoles in old location\n"); + } + if (!opal) + return; + for_each_child_of_node(opal, np) { + if (!strcmp(np->name, "serial")) { + stdout_node = np; + break; + } + } + of_node_put(opal); + } + if (!stdout_node) + return; + termno = of_get_property(stdout_node, "reg", NULL); + index = termno ? *termno : 0; + if (index >= MAX_NR_HVC_CONSOLES) + return; + hvc_opal_privs[index] = &hvc_opal_boot_priv; + + /* Check the protocol */ + if (of_device_is_compatible(stdout_node, "ibm,opal-console-raw")) { + hvc_opal_boot_priv.proto = HV_PROTOCOL_RAW; + ops = &hvc_opal_raw_ops; + pr_devel("hvc_opal: Found RAW console\n"); + } + else if (of_device_is_compatible(stdout_node,"ibm,opal-console-hvsi")) { + hvc_opal_boot_priv.proto = HV_PROTOCOL_HVSI; + ops = &hvc_opal_hvsi_ops; + hvsilib_init(&hvc_opal_boot_priv.hvsi, opal_get_chars, + opal_put_chars, index, 1); + /* HVSI, perform the handshake now */ + hvsilib_establish(&hvc_opal_boot_priv.hvsi); + pr_devel("hvc_opal: Found HVSI console\n"); + } else + goto out; + hvc_opal_boot_termno = index; + udbg_init_opal_common(); + add_preferred_console("hvc", index, NULL); + hvc_instantiate(index, index, ops); +out: + of_node_put(stdout_node); +} + +#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL_RAW +void __init udbg_init_debug_opal(void) +{ + u32 index = CONFIG_PPC_EARLY_DEBUG_OPAL_VTERMNO; + hvc_opal_privs[index] = &hvc_opal_boot_priv; + hvc_opal_boot_priv.proto = HV_PROTOCOL_RAW; + hvc_opal_boot_termno = index; + udbg_init_opal_common(); +} +#endif /* CONFIG_PPC_EARLY_DEBUG_OPAL_RAW */ + +#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL_HVSI +void __init udbg_init_debug_opal_hvsi(void) +{ + u32 index = CONFIG_PPC_EARLY_DEBUG_OPAL_VTERMNO; + hvc_opal_privs[index] = &hvc_opal_boot_priv; + hvc_opal_boot_termno = index; + udbg_init_opal_common(); + hvsilib_init(&hvc_opal_boot_priv.hvsi, opal_get_chars, opal_put_chars, + index, 1); + hvsilib_establish(&hvc_opal_boot_priv.hvsi); +} +#endif /* CONFIG_PPC_EARLY_DEBUG_OPAL_HVSI */ diff --git a/drivers/tty/hvc/hvsi_lib.c b/drivers/tty/hvc/hvsi_lib.c index bd9b09827b24..6f4dd83d8695 100644 --- a/drivers/tty/hvc/hvsi_lib.c +++ b/drivers/tty/hvc/hvsi_lib.c @@ -183,7 +183,7 @@ int hvsilib_get_chars(struct hvsi_priv *pv, char *buf, int count) unsigned int tries, read = 0; if (WARN_ON(!pv)) - return 0; + return -ENXIO; /* If we aren't open, don't do anything in order to avoid races * with connection establishment. The hvc core will call this @@ -234,7 +234,7 @@ int hvsilib_put_chars(struct hvsi_priv *pv, const char *buf, int count) int rc, adjcount = min(count, HVSI_MAX_OUTGOING_DATA); if (WARN_ON(!pv)) - return 0; + return -ENODEV; dp.hdr.type = VS_DATA_PACKET_HEADER; dp.hdr.len = adjcount + sizeof(struct hvsi_header); From ec27329ffb3b4f619be9f0065c473fcb36ea52ce Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 18:28:03 +0000 Subject: [PATCH 0218/1531] powerpc/powernv: Hookup reboot and poweroff functions This calls the respective HAL functions, and spin on hal_poll_event() to ensure the HAL has a chance to communicate with the FSP to trigger the reboot or shutdown operation Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/powernv/setup.c | 32 +++++++++++++++++++++----- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c index 07ba1ecd1807..0fac0a6c951e 100644 --- a/arch/powerpc/platforms/powernv/setup.c +++ b/arch/powerpc/platforms/powernv/setup.c @@ -83,19 +83,39 @@ static void pnv_show_cpuinfo(struct seq_file *m) of_node_put(root); } -static void pnv_restart(char *cmd) +static void __noreturn pnv_restart(char *cmd) { - for (;;); + long rc = OPAL_BUSY; + + while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { + rc = opal_cec_reboot(); + if (rc == OPAL_BUSY_EVENT) + opal_poll_events(NULL); + else + mdelay(10); + } + for (;;) + opal_poll_events(NULL); } -static void pnv_power_off(void) +static void __noreturn pnv_power_off(void) { - for (;;); + long rc = OPAL_BUSY; + + while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { + rc = opal_cec_power_down(0); + if (rc == OPAL_BUSY_EVENT) + opal_poll_events(NULL); + else + mdelay(10); + } + for (;;) + opal_poll_events(NULL); } -static void pnv_halt(void) +static void __noreturn pnv_halt(void) { - for (;;); + pnv_power_off(); } static unsigned long __init pnv_get_boot_time(void) From 628daa8d5abfd904a7329a660c5c374212230123 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:45:01 +0000 Subject: [PATCH 0219/1531] powerpc/powernv: Add RTC and NVRAM support plus RTAS fallbacks Implements OPAL RTC and NVRAM support and wire all that up to the powernv platform. We use RTAS for RTC as a fallback if available. Using RTAS for nvram is not supported yet, pending some rework/cleanup and generalization of the pSeries & CHRP code. We also use RTAS fallbacks for power off and reboot Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/opal.h | 6 ++ arch/powerpc/platforms/powernv/Makefile | 2 + arch/powerpc/platforms/powernv/opal-nvram.c | 88 +++++++++++++++++++ arch/powerpc/platforms/powernv/opal-rtc.c | 97 +++++++++++++++++++++ arch/powerpc/platforms/powernv/setup.c | 57 +++++++----- 5 files changed, 229 insertions(+), 21 deletions(-) create mode 100644 arch/powerpc/platforms/powernv/opal-nvram.c create mode 100644 arch/powerpc/platforms/powernv/opal-rtc.c diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h index 749de00a02d5..77ebe50020a2 100644 --- a/arch/powerpc/include/asm/opal.h +++ b/arch/powerpc/include/asm/opal.h @@ -430,6 +430,12 @@ extern int opal_put_chars(uint32_t vtermno, const char *buf, int total_len); extern void hvc_opal_init_early(void); +struct rtc_time; +extern int opal_set_rtc_time(struct rtc_time *tm); +extern void opal_get_rtc_time(struct rtc_time *tm); +extern unsigned long opal_get_boot_time(void); +extern void opal_nvram_init(void); + #endif /* __ASSEMBLY__ */ #endif /* __OPAL_H */ diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile index 8f69c0db612c..618ad836f28b 100644 --- a/arch/powerpc/platforms/powernv/Makefile +++ b/arch/powerpc/platforms/powernv/Makefile @@ -1,2 +1,4 @@ obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o +obj-y += opal-rtc.o opal-nvram.o + obj-$(CONFIG_SMP) += smp.o diff --git a/arch/powerpc/platforms/powernv/opal-nvram.c b/arch/powerpc/platforms/powernv/opal-nvram.c new file mode 100644 index 000000000000..3f83e1ae26ac --- /dev/null +++ b/arch/powerpc/platforms/powernv/opal-nvram.c @@ -0,0 +1,88 @@ +/* + * PowerNV nvram code. + * + * Copyright 2011 IBM Corp. + * + * 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. + */ + +#define DEBUG + +#include +#include +#include + +#include +#include + +static unsigned int nvram_size; + +static ssize_t opal_nvram_size(void) +{ + return nvram_size; +} + +static ssize_t opal_nvram_read(char *buf, size_t count, loff_t *index) +{ + s64 rc; + int off; + + if (*index >= nvram_size) + return 0; + off = *index; + if ((off + count) > nvram_size) + count = nvram_size - off; + rc = opal_read_nvram(__pa(buf), count, off); + if (rc != OPAL_SUCCESS) + return -EIO; + *index += count; + return count; +} + +static ssize_t opal_nvram_write(char *buf, size_t count, loff_t *index) +{ + s64 rc = OPAL_BUSY; + int off; + + if (*index >= nvram_size) + return 0; + off = *index; + if ((off + count) > nvram_size) + count = nvram_size - off; + + while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { + rc = opal_write_nvram(__pa(buf), count, off); + if (rc == OPAL_BUSY_EVENT) + opal_poll_events(NULL); + } + *index += count; + return count; +} + +void __init opal_nvram_init(void) +{ + struct device_node *np; + const u32 *nbytes_p; + + np = of_find_compatible_node(NULL, NULL, "ibm,opal-nvram"); + if (np == NULL) + return; + + nbytes_p = of_get_property(np, "#bytes", NULL); + if (!nbytes_p) { + of_node_put(np); + return; + } + nvram_size = *nbytes_p; + + printk(KERN_INFO "OPAL nvram setup, %u bytes\n", nvram_size); + of_node_put(np); + + ppc_md.nvram_read = opal_nvram_read; + ppc_md.nvram_write = opal_nvram_write; + ppc_md.nvram_size = opal_nvram_size; +} + diff --git a/arch/powerpc/platforms/powernv/opal-rtc.c b/arch/powerpc/platforms/powernv/opal-rtc.c new file mode 100644 index 000000000000..2aa7641aac9b --- /dev/null +++ b/arch/powerpc/platforms/powernv/opal-rtc.c @@ -0,0 +1,97 @@ +/* + * PowerNV Real Time Clock. + * + * Copyright 2011 IBM Corp. + * + * 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 + +static void opal_to_tm(u32 y_m_d, u64 h_m_s_ms, struct rtc_time *tm) +{ + tm->tm_year = ((bcd2bin(y_m_d >> 24) * 100) + + bcd2bin((y_m_d >> 16) & 0xff)) - 1900; + tm->tm_mon = bcd2bin((y_m_d >> 8) & 0xff) - 1; + tm->tm_mday = bcd2bin(y_m_d & 0xff); + tm->tm_hour = bcd2bin((h_m_s_ms >> 56) & 0xff); + tm->tm_min = bcd2bin((h_m_s_ms >> 48) & 0xff); + tm->tm_sec = bcd2bin((h_m_s_ms >> 40) & 0xff); + + GregorianDay(tm); +} + +unsigned long __init opal_get_boot_time(void) +{ + struct rtc_time tm; + u32 y_m_d; + u64 h_m_s_ms; + long rc = OPAL_BUSY; + + while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { + rc = opal_rtc_read(&y_m_d, &h_m_s_ms); + if (rc == OPAL_BUSY_EVENT) + opal_poll_events(NULL); + else + mdelay(10); + } + if (rc != OPAL_SUCCESS) + return 0; + opal_to_tm(y_m_d, h_m_s_ms, &tm); + return mktime(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); +} + +void opal_get_rtc_time(struct rtc_time *tm) +{ + long rc = OPAL_BUSY; + u32 y_m_d; + u64 h_m_s_ms; + + while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { + rc = opal_rtc_read(&y_m_d, &h_m_s_ms); + if (rc == OPAL_BUSY_EVENT) + opal_poll_events(NULL); + else + mdelay(10); + } + if (rc != OPAL_SUCCESS) + return; + opal_to_tm(y_m_d, h_m_s_ms, tm); +} + +int opal_set_rtc_time(struct rtc_time *tm) +{ + long rc = OPAL_BUSY; + u32 y_m_d = 0; + u64 h_m_s_ms = 0; + + y_m_d |= ((u32)bin2bcd((tm->tm_year + 1900) / 100)) << 24; + y_m_d |= ((u32)bin2bcd((tm->tm_year + 1900) % 100)) << 16; + y_m_d |= ((u32)bin2bcd((tm->tm_mon + 1))) << 8; + y_m_d |= ((u32)bin2bcd(tm->tm_mday)); + + h_m_s_ms |= ((u64)bin2bcd(tm->tm_hour)) << 56; + h_m_s_ms |= ((u64)bin2bcd(tm->tm_min)) << 48; + h_m_s_ms |= ((u64)bin2bcd(tm->tm_sec)) << 40; + + while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { + rc = opal_rtc_write(y_m_d, h_m_s_ms); + if (rc == OPAL_BUSY_EVENT) + opal_poll_events(NULL); + else + mdelay(10); + } + return rc == OPAL_SUCCESS ? 0 : -EIO; +} diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c index 0fac0a6c951e..4a2b2e279593 100644 --- a/arch/powerpc/platforms/powernv/setup.c +++ b/arch/powerpc/platforms/powernv/setup.c @@ -29,7 +29,9 @@ #include #include #include +#include #include +#include #include "powernv.h" @@ -40,7 +42,9 @@ static void __init pnv_setup_arch(void) /* XXX PCI */ - /* XXX NVRAM */ + /* Setup RTC and NVRAM callbacks */ + if (firmware_has_feature(FW_FEATURE_OPAL)) + opal_nvram_init(); /* Enable NAP mode */ powersave_nap = 1; @@ -118,30 +122,40 @@ static void __noreturn pnv_halt(void) pnv_power_off(); } -static unsigned long __init pnv_get_boot_time(void) -{ - return 0; -} - -static void pnv_get_rtc_time(struct rtc_time *rtc_tm) +static void pnv_progress(char *s, unsigned short hex) { } -static int pnv_set_rtc_time(struct rtc_time *tm) +#ifdef CONFIG_KEXEC +static void pnv_kexec_cpu_down(int crash_shutdown, int secondary) { - return 0; + xics_kexec_teardown_cpu(secondary); } +#endif /* CONFIG_KEXEC */ -static void pnv_progress(char *s, unsigned short hex) +static void __init pnv_setup_machdep_opal(void) { + ppc_md.get_boot_time = opal_get_boot_time; + ppc_md.get_rtc_time = opal_get_rtc_time; + ppc_md.set_rtc_time = opal_set_rtc_time; + ppc_md.restart = pnv_restart; + ppc_md.power_off = pnv_power_off; + ppc_md.halt = pnv_halt; } -#ifdef CONFIG_KEXEC -static void pnv_kexec_cpu_down(int crash_shutdown, int secondary) +#ifdef CONFIG_PPC_POWERNV_RTAS +static void __init pnv_setup_machdep_rtas(void) { - xics_kexec_teardown_cpu(secondary); + if (rtas_token("get-time-of-day") != RTAS_UNKNOWN_SERVICE) { + ppc_md.get_boot_time = rtas_get_boot_time; + ppc_md.get_rtc_time = rtas_get_rtc_time; + ppc_md.set_rtc_time = rtas_set_rtc_time; + } + ppc_md.restart = rtas_restart; + ppc_md.power_off = rtas_power_off; + ppc_md.halt = rtas_halt; } -#endif /* CONFIG_KEXEC */ +#endif /* CONFIG_PPC_POWERNV_RTAS */ static int __init pnv_probe(void) { @@ -152,6 +166,13 @@ static int __init pnv_probe(void) hpte_init_native(); + if (firmware_has_feature(FW_FEATURE_OPAL)) + pnv_setup_machdep_opal(); +#ifdef CONFIG_PPC_POWERNV_RTAS + else if (rtas.base) + pnv_setup_machdep_rtas(); +#endif /* CONFIG_PPC_POWERNV_RTAS */ + pr_debug("PowerNV detected !\n"); return 1; @@ -160,16 +181,10 @@ static int __init pnv_probe(void) define_machine(powernv) { .name = "PowerNV", .probe = pnv_probe, - .setup_arch = pnv_setup_arch, .init_early = pnv_init_early, + .setup_arch = pnv_setup_arch, .init_IRQ = pnv_init_IRQ, .show_cpuinfo = pnv_show_cpuinfo, - .restart = pnv_restart, - .power_off = pnv_power_off, - .halt = pnv_halt, - .get_boot_time = pnv_get_boot_time, - .get_rtc_time = pnv_get_rtc_time, - .set_rtc_time = pnv_set_rtc_time, .progress = pnv_progress, .power_save = power7_idle, .calibrate_decr = generic_calibrate_decr, From 5c7c1e9444d8bfb721a27a35bba3eeb5236c75d8 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:45:02 +0000 Subject: [PATCH 0220/1531] powerpc/powernv: Add OPAL ICS backend OPAL handles HW access to the various ICS or equivalent chips for us (with the exception of p5ioc2 based HEA which uses a different backend) similarily to what RTAS does on pSeries. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/xics.h | 19 ++ arch/powerpc/sysdev/xics/Makefile | 1 + arch/powerpc/sysdev/xics/ics-opal.c | 244 +++++++++++++++++++++++++ arch/powerpc/sysdev/xics/xics-common.c | 8 +- 4 files changed, 266 insertions(+), 6 deletions(-) create mode 100644 arch/powerpc/sysdev/xics/ics-opal.c diff --git a/arch/powerpc/include/asm/xics.h b/arch/powerpc/include/asm/xics.h index b183a4062011..bd6c401c0ee5 100644 --- a/arch/powerpc/include/asm/xics.h +++ b/arch/powerpc/include/asm/xics.h @@ -27,10 +27,18 @@ #define MAX_NUM_PRIORITIES 3 /* Native ICP */ +#ifdef CONFIG_PPC_ICP_NATIVE extern int icp_native_init(void); +#else +static inline int icp_native_init(void) { return -ENODEV; } +#endif /* PAPR ICP */ +#ifdef CONFIG_PPC_ICP_HV extern int icp_hv_init(void); +#else +static inline int icp_hv_init(void) { return -ENODEV; } +#endif /* ICP ops */ struct icp_ops { @@ -51,7 +59,18 @@ extern const struct icp_ops *icp_ops; extern int ics_native_init(void); /* RTAS ICS */ +#ifdef CONFIG_PPC_ICS_RTAS extern int ics_rtas_init(void); +#else +static inline int ics_rtas_init(void) { return -ENODEV; } +#endif + +/* HAL ICS */ +#ifdef CONFIG_PPC_POWERNV +extern int ics_opal_init(void); +#else +static inline int ics_opal_init(void) { return -ENODEV; } +#endif /* ICS instance, hooked up to chip_data of an irq */ struct ics { diff --git a/arch/powerpc/sysdev/xics/Makefile b/arch/powerpc/sysdev/xics/Makefile index b75a6059337f..c606aa8ba60a 100644 --- a/arch/powerpc/sysdev/xics/Makefile +++ b/arch/powerpc/sysdev/xics/Makefile @@ -4,3 +4,4 @@ obj-y += xics-common.o obj-$(CONFIG_PPC_ICP_NATIVE) += icp-native.o obj-$(CONFIG_PPC_ICP_HV) += icp-hv.o obj-$(CONFIG_PPC_ICS_RTAS) += ics-rtas.o +obj-$(CONFIG_PPC_POWERNV) += ics-opal.o diff --git a/arch/powerpc/sysdev/xics/ics-opal.c b/arch/powerpc/sysdev/xics/ics-opal.c new file mode 100644 index 000000000000..f7e8609df0d5 --- /dev/null +++ b/arch/powerpc/sysdev/xics/ics-opal.c @@ -0,0 +1,244 @@ +/* + * ICS backend for OPAL managed interrupts. + * + * Copyright 2011 IBM Corp. + * + * 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. + */ + +#undef DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static int ics_opal_mangle_server(int server) +{ + /* No link for now */ + return server << 2; +} + +static int ics_opal_unmangle_server(int server) +{ + /* No link for now */ + return server >> 2; +} + +static void ics_opal_unmask_irq(struct irq_data *d) +{ + unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d); + int64_t rc; + int server; + + pr_devel("ics-hal: unmask virq %d [hw 0x%x]\n", d->irq, hw_irq); + + if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS) + return; + + server = xics_get_irq_server(d->irq, d->affinity, 0); + server = ics_opal_mangle_server(server); + + rc = opal_set_xive(hw_irq, server, DEFAULT_PRIORITY); + if (rc != OPAL_SUCCESS) + pr_err("%s: opal_set_xive(irq=%d [hw 0x%x] server=%x)" + " error %lld\n", + __func__, d->irq, hw_irq, server, rc); +} + +static unsigned int ics_opal_startup(struct irq_data *d) +{ +#ifdef CONFIG_PCI_MSI + /* + * The generic MSI code returns with the interrupt disabled on the + * card, using the MSI mask bits. Firmware doesn't appear to unmask + * at that level, so we do it here by hand. + */ + if (d->msi_desc) + unmask_msi_irq(d); +#endif + + /* unmask it */ + ics_opal_unmask_irq(d); + return 0; +} + +static void ics_opal_mask_real_irq(unsigned int hw_irq) +{ + int server = ics_opal_mangle_server(xics_default_server); + int64_t rc; + + if (hw_irq == XICS_IPI) + return; + + /* Have to set XIVE to 0xff to be able to remove a slot */ + rc = opal_set_xive(hw_irq, server, 0xff); + if (rc != OPAL_SUCCESS) + pr_err("%s: opal_set_xive(0xff) irq=%u returned %lld\n", + __func__, hw_irq, rc); +} + +static void ics_opal_mask_irq(struct irq_data *d) +{ + unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d); + + pr_devel("ics-hal: mask virq %d [hw 0x%x]\n", d->irq, hw_irq); + + if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS) + return; + ics_opal_mask_real_irq(hw_irq); +} + +static int ics_opal_set_affinity(struct irq_data *d, + const struct cpumask *cpumask, + bool force) +{ + unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d); + int16_t server; + int8_t priority; + int64_t rc; + int wanted_server; + + if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS) + return -1; + + rc = opal_get_xive(hw_irq, &server, &priority); + if (rc != OPAL_SUCCESS) { + pr_err("%s: opal_set_xive(irq=%d [hw 0x%x] server=%x)" + " error %lld\n", + __func__, d->irq, hw_irq, server, rc); + return -1; + } + + wanted_server = xics_get_irq_server(d->irq, cpumask, 1); + if (wanted_server < 0) { + char cpulist[128]; + cpumask_scnprintf(cpulist, sizeof(cpulist), cpumask); + pr_warning("%s: No online cpus in the mask %s for irq %d\n", + __func__, cpulist, d->irq); + return -1; + } + server = ics_opal_mangle_server(wanted_server); + + pr_devel("ics-hal: set-affinity irq %d [hw 0x%x] server: 0x%x/0x%x\n", + d->irq, hw_irq, wanted_server, server); + + rc = opal_set_xive(hw_irq, server, priority); + if (rc != OPAL_SUCCESS) { + pr_err("%s: opal_set_xive(irq=%d [hw 0x%x] server=%x)" + " error %lld\n", + __func__, d->irq, hw_irq, server, rc); + return -1; + } + return 0; +} + +static struct irq_chip ics_opal_irq_chip = { + .name = "OPAL ICS", + .irq_startup = ics_opal_startup, + .irq_mask = ics_opal_mask_irq, + .irq_unmask = ics_opal_unmask_irq, + .irq_eoi = NULL, /* Patched at init time */ + .irq_set_affinity = ics_opal_set_affinity +}; + +static int ics_opal_map(struct ics *ics, unsigned int virq); +static void ics_opal_mask_unknown(struct ics *ics, unsigned long vec); +static long ics_opal_get_server(struct ics *ics, unsigned long vec); + +static int ics_opal_host_match(struct ics *ics, struct device_node *node) +{ + return 1; +} + +/* Only one global & state struct ics */ +static struct ics ics_hal = { + .map = ics_opal_map, + .mask_unknown = ics_opal_mask_unknown, + .get_server = ics_opal_get_server, + .host_match = ics_opal_host_match, +}; + +static int ics_opal_map(struct ics *ics, unsigned int virq) +{ + unsigned int hw_irq = (unsigned int)virq_to_hw(virq); + int64_t rc; + int16_t server; + int8_t priority; + + if (WARN_ON(hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)) + return -EINVAL; + + /* Check if HAL knows about this interrupt */ + rc = opal_get_xive(hw_irq, &server, &priority); + if (rc != OPAL_SUCCESS) + return -ENXIO; + + irq_set_chip_and_handler(virq, &ics_opal_irq_chip, handle_fasteoi_irq); + irq_set_chip_data(virq, &ics_hal); + + return 0; +} + +static void ics_opal_mask_unknown(struct ics *ics, unsigned long vec) +{ + int64_t rc; + int16_t server; + int8_t priority; + + /* Check if HAL knows about this interrupt */ + rc = opal_get_xive(vec, &server, &priority); + if (rc != OPAL_SUCCESS) + return; + + ics_opal_mask_real_irq(vec); +} + +static long ics_opal_get_server(struct ics *ics, unsigned long vec) +{ + int64_t rc; + int16_t server; + int8_t priority; + + /* Check if HAL knows about this interrupt */ + rc = opal_get_xive(vec, &server, &priority); + if (rc != OPAL_SUCCESS) + return -1; + return ics_opal_unmangle_server(server); +} + +int __init ics_opal_init(void) +{ + if (!firmware_has_feature(FW_FEATURE_OPAL)) + return -ENODEV; + + /* We need to patch our irq chip's EOI to point to the + * right ICP + */ + ics_opal_irq_chip.irq_eoi = icp_ops->eoi; + + /* Register ourselves */ + xics_register_ics(&ics_hal); + + pr_info("ICS OPAL backend registered\n"); + + return 0; +} diff --git a/arch/powerpc/sysdev/xics/xics-common.c b/arch/powerpc/sysdev/xics/xics-common.c index 445c5a01b766..3d93a8ded0f8 100644 --- a/arch/powerpc/sysdev/xics/xics-common.c +++ b/arch/powerpc/sysdev/xics/xics-common.c @@ -409,14 +409,10 @@ void __init xics_init(void) int rc = -1; /* Fist locate ICP */ -#ifdef CONFIG_PPC_ICP_HV if (firmware_has_feature(FW_FEATURE_LPAR)) rc = icp_hv_init(); -#endif -#ifdef CONFIG_PPC_ICP_NATIVE if (rc < 0) rc = icp_native_init(); -#endif if (rc < 0) { pr_warning("XICS: Cannot find a Presentation Controller !\n"); return; @@ -429,9 +425,9 @@ void __init xics_init(void) xics_ipi_chip.irq_eoi = icp_ops->eoi; /* Now locate ICS */ -#ifdef CONFIG_PPC_ICS_RTAS rc = ics_rtas_init(); -#endif + if (rc < 0) + rc = ics_opal_init(); if (rc < 0) pr_warning("XICS: Cannot find a Source Controller !\n"); From a125e0928c736bc50cdd9a13151d4f4ee7821266 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:45:03 +0000 Subject: [PATCH 0221/1531] powerpc/powernv: Register and handle OPAL interrupts We do the minimum which is to "pass" interrupts to HAL, which makes the console smoother and will allow us to implement interrupt based completion and console. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/powernv/opal.c | 31 +++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c index 7887733b9b31..5a598caefcba 100644 --- a/arch/powerpc/platforms/powernv/opal.c +++ b/arch/powerpc/platforms/powernv/opal.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -135,9 +136,22 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len) return written; } +static irqreturn_t opal_interrupt(int irq, void *data) +{ + uint64_t events; + + opal_handle_interrupt(virq_to_hw(irq), &events); + + /* XXX TODO: Do something with the events */ + + return IRQ_HANDLED; +} + static int __init opal_init(void) { struct device_node *np, *consoles; + const u32 *irqs; + int rc, i, irqlen; opal_node = of_find_node_by_path("/ibm,opal"); if (!opal_node) { @@ -156,6 +170,23 @@ static int __init opal_init(void) of_platform_device_create(np, NULL, NULL); } of_node_put(consoles); + + /* Find all OPAL interrupts and request them */ + irqs = of_get_property(opal_node, "opal-interrupts", &irqlen); + pr_debug("opal: Found %d interrupts reserved for OPAL\n", + irqs ? (irqlen / 4) : 0); + for (i = 0; irqs && i < (irqlen / 4); i++, irqs++) { + unsigned int hwirq = be32_to_cpup(irqs); + unsigned int irq = irq_create_mapping(NULL, hwirq); + if (irq == NO_IRQ) { + pr_warning("opal: Failed to map irq 0x%x\n", hwirq); + continue; + } + rc = request_irq(irq, opal_interrupt, 0, "opal", NULL); + if (rc) + pr_warning("opal: Error %d requesting irq %d" + " (0x%x)\n", rc, irq, hwirq); + } return 0; } subsys_initcall(opal_init); From ed79ba9e15f84cef05aba5cbfe6e93f9b43c31f4 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:45:04 +0000 Subject: [PATCH 0222/1531] powerpc/powernv: Machine check and other system interrupts OPAL can handle various interrupt for us such as Machine Checks (it performs all sorts of recovery tasks and passes back control to us with informations about the error), Hardware Management Interrupts and Softpatch interrupts. This wires up the mechanisms and prints out specific informations returned by HAL when a machine check occurs. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/opal.h | 2 + arch/powerpc/include/asm/paca.h | 8 ++ arch/powerpc/kernel/asm-offsets.c | 10 ++ arch/powerpc/kernel/exceptions-64s.S | 27 ++++- arch/powerpc/platforms/powernv/opal.c | 130 +++++++++++++++++++++++++ arch/powerpc/platforms/powernv/setup.c | 1 + 6 files changed, 174 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h index 77ebe50020a2..2893e8f5406d 100644 --- a/arch/powerpc/include/asm/opal.h +++ b/arch/powerpc/include/asm/opal.h @@ -436,6 +436,8 @@ extern void opal_get_rtc_time(struct rtc_time *tm); extern unsigned long opal_get_boot_time(void); extern void opal_nvram_init(void); +extern int opal_machine_check(struct pt_regs *regs); + #endif /* __ASSEMBLY__ */ #endif /* __OPAL_H */ diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca.h index 516bfb3f47d9..17722c73ba2e 100644 --- a/arch/powerpc/include/asm/paca.h +++ b/arch/powerpc/include/asm/paca.h @@ -43,6 +43,7 @@ extern unsigned int debug_smp_processor_id(void); /* from linux/smp.h */ #define get_slb_shadow() (get_paca()->slb_shadow_ptr) struct task_struct; +struct opal_machine_check_event; /* * Defines the layout of the paca. @@ -135,6 +136,13 @@ struct paca_struct { u8 io_sync; /* writel() needs spin_unlock sync */ u8 irq_work_pending; /* IRQ_WORK interrupt while soft-disable */ +#ifdef CONFIG_PPC_POWERNV + /* Pointer to OPAL machine check event structure set by the + * early exception handler for use by high level C handler + */ + struct opal_machine_check_event *opal_mc_evt; +#endif + /* Stuff for accurate time accounting */ u64 user_time; /* accumulated usermode TB ticks */ u64 system_time; /* accumulated system TB ticks */ diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 5f078bc2063e..536ffa897c6c 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -48,6 +48,9 @@ #ifdef CONFIG_PPC_ISERIES #include #endif +#ifdef CONFIG_PPC_POWERNV +#include +#endif #if defined(CONFIG_KVM) || defined(CONFIG_KVM_GUEST) #include #endif @@ -609,5 +612,12 @@ int main(void) arch.timing_last_enter.tv32.tbl)); #endif +#ifdef CONFIG_PPC_POWERNV + DEFINE(OPAL_MC_GPR3, offsetof(struct opal_machine_check_event, gpr3)); + DEFINE(OPAL_MC_SRR0, offsetof(struct opal_machine_check_event, srr0)); + DEFINE(OPAL_MC_SRR1, offsetof(struct opal_machine_check_event, srr1)); + DEFINE(PACA_OPAL_MC_EVT, offsetof(struct paca_struct, opal_mc_evt)); +#endif + return 0; } diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index 41b02c792aa3..d51458fa8dee 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -1143,7 +1143,7 @@ _GLOBAL(do_stab_bolted) rfid b . /* prevent speculative execution */ -#ifdef CONFIG_PPC_PSERIES +#if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) /* * Data area reserved for FWNMI option. * This address (0x7000) is fixed by the RPA. @@ -1151,7 +1151,7 @@ _GLOBAL(do_stab_bolted) .= 0x7000 .globl fwnmi_data_area fwnmi_data_area: -#endif /* CONFIG_PPC_PSERIES */ +#endif /* defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) */ /* iSeries does not use the FWNMI stuff, so it is safe to put * this here, even if we later allow kernels that will boot on @@ -1176,9 +1176,12 @@ xLparMap: #endif /* CONFIG_PPC_ISERIES */ -#ifdef CONFIG_PPC_PSERIES +#if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) + /* pseries and powernv need to keep the whole page from + * 0x7000 to 0x8000 free for use by the firmware + */ . = 0x8000 -#endif /* CONFIG_PPC_PSERIES */ +#endif /* defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) */ /* * Space for CPU0's segment table. @@ -1193,3 +1196,19 @@ xLparMap: .globl initial_stab initial_stab: .space 4096 +#ifdef CONFIG_PPC_POWERNV +_GLOBAL(opal_mc_secondary_handler) + HMT_MEDIUM + SET_SCRATCH0(r13) + GET_PACA(r13) + clrldi r3,r3,2 + tovirt(r3,r3) + std r3,PACA_OPAL_MC_EVT(r13) + ld r13,OPAL_MC_SRR0(r3) + mtspr SPRN_SRR0,r13 + ld r13,OPAL_MC_SRR1(r3) + mtspr SPRN_SRR1,r13 + ld r3,OPAL_MC_GPR3(r3) + GET_SCRATCH0(r13) + b machine_check_pSeries +#endif /* CONFIG_PPC_POWERNV */ diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c index 5a598caefcba..aaa0dba49471 100644 --- a/arch/powerpc/platforms/powernv/opal.c +++ b/arch/powerpc/platforms/powernv/opal.c @@ -27,12 +27,14 @@ struct opal { static struct device_node *opal_node; static DEFINE_SPINLOCK(opal_write_lock); +extern u64 opal_mc_secondary_handler[]; int __init early_init_dt_scan_opal(unsigned long node, const char *uname, int depth, void *data) { const void *basep, *entryp; unsigned long basesz, entrysz; + u64 glue; if (depth != 1 || strcmp(uname, "ibm,opal") != 0) return 0; @@ -59,6 +61,19 @@ int __init early_init_dt_scan_opal(unsigned long node, printk("OPAL V1 detected !\n"); } + /* Hookup some exception handlers. We use the fwnmi area at 0x7000 + * to provide the glue space to OPAL + */ + glue = 0x7000; + opal_register_exception_handler(OPAL_MACHINE_CHECK_HANDLER, + __pa(opal_mc_secondary_handler[0]), + glue); + glue += 128; + opal_register_exception_handler(OPAL_HYPERVISOR_MAINTENANCE_HANDLER, + 0, glue); + glue += 128; + opal_register_exception_handler(OPAL_SOFTPATCH_HANDLER, 0, glue); + return 1; } @@ -136,6 +151,121 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len) return written; } +int opal_machine_check(struct pt_regs *regs) +{ + struct opal_machine_check_event *opal_evt = get_paca()->opal_mc_evt; + struct opal_machine_check_event evt; + const char *level, *sevstr, *subtype; + static const char *opal_mc_ue_types[] = { + "Indeterminate", + "Instruction fetch", + "Page table walk ifetch", + "Load/Store", + "Page table walk Load/Store", + }; + static const char *opal_mc_slb_types[] = { + "Indeterminate", + "Parity", + "Multihit", + }; + static const char *opal_mc_erat_types[] = { + "Indeterminate", + "Parity", + "Multihit", + }; + static const char *opal_mc_tlb_types[] = { + "Indeterminate", + "Parity", + "Multihit", + }; + + /* Copy the event structure and release the original */ + evt = *opal_evt; + opal_evt->in_use = 0; + + /* Print things out */ + if (evt.version != OpalMCE_V1) { + pr_err("Machine Check Exception, Unknown event version %d !\n", + evt.version); + return 0; + } + switch(evt.severity) { + case OpalMCE_SEV_NO_ERROR: + level = KERN_INFO; + sevstr = "Harmless"; + break; + case OpalMCE_SEV_WARNING: + level = KERN_WARNING; + sevstr = ""; + break; + case OpalMCE_SEV_ERROR_SYNC: + level = KERN_ERR; + sevstr = "Severe"; + break; + case OpalMCE_SEV_FATAL: + default: + level = KERN_ERR; + sevstr = "Fatal"; + break; + } + + printk("%s%s Machine check interrupt [%s]\n", level, sevstr, + evt.disposition == OpalMCE_DISPOSITION_RECOVERED ? + "Recovered" : "[Not recovered"); + printk("%s Initiator: %s\n", level, + evt.initiator == OpalMCE_INITIATOR_CPU ? "CPU" : "Unknown"); + switch(evt.error_type) { + case OpalMCE_ERROR_TYPE_UE: + subtype = evt.u.ue_error.ue_error_type < + ARRAY_SIZE(opal_mc_ue_types) ? + opal_mc_ue_types[evt.u.ue_error.ue_error_type] + : "Unknown"; + printk("%s Error type: UE [%s]\n", level, subtype); + if (evt.u.ue_error.effective_address_provided) + printk("%s Effective address: %016llx\n", + level, evt.u.ue_error.effective_address); + if (evt.u.ue_error.physical_address_provided) + printk("%s Physial address: %016llx\n", + level, evt.u.ue_error.physical_address); + break; + case OpalMCE_ERROR_TYPE_SLB: + subtype = evt.u.slb_error.slb_error_type < + ARRAY_SIZE(opal_mc_slb_types) ? + opal_mc_slb_types[evt.u.slb_error.slb_error_type] + : "Unknown"; + printk("%s Error type: SLB [%s]\n", level, subtype); + if (evt.u.slb_error.effective_address_provided) + printk("%s Effective address: %016llx\n", + level, evt.u.slb_error.effective_address); + break; + case OpalMCE_ERROR_TYPE_ERAT: + subtype = evt.u.erat_error.erat_error_type < + ARRAY_SIZE(opal_mc_erat_types) ? + opal_mc_erat_types[evt.u.erat_error.erat_error_type] + : "Unknown"; + printk("%s Error type: ERAT [%s]\n", level, subtype); + if (evt.u.erat_error.effective_address_provided) + printk("%s Effective address: %016llx\n", + level, evt.u.erat_error.effective_address); + break; + case OpalMCE_ERROR_TYPE_TLB: + subtype = evt.u.tlb_error.tlb_error_type < + ARRAY_SIZE(opal_mc_tlb_types) ? + opal_mc_tlb_types[evt.u.tlb_error.tlb_error_type] + : "Unknown"; + printk("%s Error type: TLB [%s]\n", level, subtype); + if (evt.u.tlb_error.effective_address_provided) + printk("%s Effective address: %016llx\n", + level, evt.u.tlb_error.effective_address); + break; + default: + case OpalMCE_ERROR_TYPE_UNKNOWN: + printk("%s Error type: Unknown\n", level); + break; + } + return evt.severity == OpalMCE_SEV_FATAL ? 0 : 1; +} + static irqreturn_t opal_interrupt(int irq, void *data) { uint64_t events; diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c index 4a2b2e279593..f0242f3fd3e6 100644 --- a/arch/powerpc/platforms/powernv/setup.c +++ b/arch/powerpc/platforms/powernv/setup.c @@ -141,6 +141,7 @@ static void __init pnv_setup_machdep_opal(void) ppc_md.restart = pnv_restart; ppc_md.power_off = pnv_power_off; ppc_md.halt = pnv_halt; + ppc_md.machine_check_exception = opal_machine_check; } #ifdef CONFIG_PPC_POWERNV_RTAS From 61305a96fad622ae0f0e78cb06f67ad721d378f9 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:45:05 +0000 Subject: [PATCH 0223/1531] powerpc/powernv: Add support for p5ioc2 PCI-X and PCIe This adds support for PCI-X and PCIe on the p5ioc2 IO hub using OPAL. This includes allocating & setting up TCE tables and config space access routines. This also supports fallbacks via RTAS when OPAL is absent, using legacy TCE format pre-allocated via the device-tree (BML style) Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/powernv/Makefile | 1 + arch/powerpc/platforms/powernv/pci-p5ioc2.c | 185 +++++++++++++ arch/powerpc/platforms/powernv/pci.c | 286 ++++++++++++++++++++ arch/powerpc/platforms/powernv/pci.h | 38 +++ arch/powerpc/platforms/powernv/powernv.h | 6 + arch/powerpc/platforms/powernv/setup.c | 3 +- 6 files changed, 518 insertions(+), 1 deletion(-) create mode 100644 arch/powerpc/platforms/powernv/pci-p5ioc2.c create mode 100644 arch/powerpc/platforms/powernv/pci.c create mode 100644 arch/powerpc/platforms/powernv/pci.h diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile index 618ad836f28b..31853008b418 100644 --- a/arch/powerpc/platforms/powernv/Makefile +++ b/arch/powerpc/platforms/powernv/Makefile @@ -2,3 +2,4 @@ obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o obj-y += opal-rtc.o opal-nvram.o obj-$(CONFIG_SMP) += smp.o +obj-$(CONFIG_PCI) += pci.o pci-p5ioc2.o diff --git a/arch/powerpc/platforms/powernv/pci-p5ioc2.c b/arch/powerpc/platforms/powernv/pci-p5ioc2.c new file mode 100644 index 000000000000..afabc2bcae45 --- /dev/null +++ b/arch/powerpc/platforms/powernv/pci-p5ioc2.c @@ -0,0 +1,185 @@ +/* + * Support PCI/PCIe on PowerNV platforms + * + * Currently supports only P5IOC2 + * + * Copyright 2011 Benjamin Herrenschmidt, IBM Corp. + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "powernv.h" +#include "pci.h" + +/* For now, use a fixed amount of TCE memory for each p5ioc2 + * hub, 16M will do + */ +#define P5IOC2_TCE_MEMORY 0x01000000 + +static void __devinit pnv_pci_p5ioc2_dma_dev_setup(struct pnv_phb *phb, + struct pci_dev *pdev) +{ + if (phb->p5ioc2.iommu_table.it_map == NULL) + iommu_init_table(&phb->p5ioc2.iommu_table, phb->hose->node); + + set_iommu_table_base(&pdev->dev, &phb->p5ioc2.iommu_table); +} + +static void __init pnv_pci_init_p5ioc2_phb(struct device_node *np, + void *tce_mem, u64 tce_size) +{ + struct pnv_phb *phb; + const u64 *prop64; + u64 phb_id; + int64_t rc; + static int primary = 1; + + pr_info(" Initializing p5ioc2 PHB %s\n", np->full_name); + + prop64 = of_get_property(np, "ibm,opal-phbid", NULL); + if (!prop64) { + pr_err(" Missing \"ibm,opal-phbid\" property !\n"); + return; + } + phb_id = be64_to_cpup(prop64); + pr_devel(" PHB-ID : 0x%016llx\n", phb_id); + pr_devel(" TCE AT : 0x%016lx\n", __pa(tce_mem)); + pr_devel(" TCE SZ : 0x%016llx\n", tce_size); + + rc = opal_pci_set_phb_tce_memory(phb_id, __pa(tce_mem), tce_size); + if (rc != OPAL_SUCCESS) { + pr_err(" Failed to set TCE memory, OPAL error %lld\n", rc); + return; + } + + phb = alloc_bootmem(sizeof(struct pnv_phb)); + if (phb) { + memset(phb, 0, sizeof(struct pnv_phb)); + phb->hose = pcibios_alloc_controller(np); + } + if (!phb || !phb->hose) { + pr_err(" Failed to allocate PCI controller\n"); + return; + } + + spin_lock_init(&phb->lock); + phb->hose->first_busno = 0; + phb->hose->last_busno = 0xff; + phb->hose->private_data = phb; + phb->opal_id = phb_id; + phb->type = PNV_PHB_P5IOC2; + + phb->regs = of_iomap(np, 0); + + if (phb->regs == NULL) + pr_err(" Failed to map registers !\n"); + else { + pr_devel(" P_BUID = 0x%08x\n", in_be32(phb->regs + 0x100)); + pr_devel(" P_IOSZ = 0x%08x\n", in_be32(phb->regs + 0x1b0)); + pr_devel(" P_IO_ST = 0x%08x\n", in_be32(phb->regs + 0x1e0)); + pr_devel(" P_MEM1_H = 0x%08x\n", in_be32(phb->regs + 0x1a0)); + pr_devel(" P_MEM1_L = 0x%08x\n", in_be32(phb->regs + 0x190)); + pr_devel(" P_MSZ1_L = 0x%08x\n", in_be32(phb->regs + 0x1c0)); + pr_devel(" P_MEM_ST = 0x%08x\n", in_be32(phb->regs + 0x1d0)); + pr_devel(" P_MEM2_H = 0x%08x\n", in_be32(phb->regs + 0x2c0)); + pr_devel(" P_MEM2_L = 0x%08x\n", in_be32(phb->regs + 0x2b0)); + pr_devel(" P_MSZ2_H = 0x%08x\n", in_be32(phb->regs + 0x2d0)); + pr_devel(" P_MSZ2_L = 0x%08x\n", in_be32(phb->regs + 0x2e0)); + } + + /* Interpret the "ranges" property */ + /* This also maps the I/O region and sets isa_io/mem_base */ + pci_process_bridge_OF_ranges(phb->hose, np, primary); + primary = 0; + + phb->hose->ops = &pnv_pci_ops; + + /* Setup TCEs */ + phb->dma_dev_setup = pnv_pci_p5ioc2_dma_dev_setup; + pnv_pci_setup_iommu_table(&phb->p5ioc2.iommu_table, + tce_mem, tce_size, 0); +} + +void __init pnv_pci_init_p5ioc2_hub(struct device_node *np) +{ + struct device_node *phbn; + const u64 *prop64; + u64 hub_id; + void *tce_mem; + uint64_t tce_per_phb; + int64_t rc; + int phb_count = 0; + + pr_info("Probing p5ioc2 IO-Hub %s\n", np->full_name); + + prop64 = of_get_property(np, "ibm,opal-hubid", NULL); + if (!prop64) { + pr_err(" Missing \"ibm,opal-hubid\" property !\n"); + return; + } + hub_id = be64_to_cpup(prop64); + pr_info(" HUB-ID : 0x%016llx\n", hub_id); + + /* Currently allocate 16M of TCE memory for every Hub + * + * XXX TODO: Make it chip local if possible + */ + tce_mem = __alloc_bootmem(P5IOC2_TCE_MEMORY, P5IOC2_TCE_MEMORY, + __pa(MAX_DMA_ADDRESS)); + if (!tce_mem) { + pr_err(" Failed to allocate TCE Memory !\n"); + return; + } + pr_debug(" TCE : 0x%016lx..0x%016lx\n", + __pa(tce_mem), __pa(tce_mem) + P5IOC2_TCE_MEMORY - 1); + rc = opal_pci_set_hub_tce_memory(hub_id, __pa(tce_mem), + P5IOC2_TCE_MEMORY); + if (rc != OPAL_SUCCESS) { + pr_err(" Failed to allocate TCE memory, OPAL error %lld\n", rc); + return; + } + + /* Count child PHBs */ + for_each_child_of_node(np, phbn) { + if (of_device_is_compatible(phbn, "ibm,p5ioc2-pcix") || + of_device_is_compatible(phbn, "ibm,p5ioc2-pciex")) + phb_count++; + } + + /* Calculate how much TCE space we can give per PHB */ + tce_per_phb = __rounddown_pow_of_two(P5IOC2_TCE_MEMORY / phb_count); + pr_info(" Allocating %lld MB of TCE memory per PHB\n", + tce_per_phb >> 20); + + /* Initialize PHBs */ + for_each_child_of_node(np, phbn) { + if (of_device_is_compatible(phbn, "ibm,p5ioc2-pcix") || + of_device_is_compatible(phbn, "ibm,p5ioc2-pciex")) { + pnv_pci_init_p5ioc2_phb(phbn, tce_mem, tce_per_phb); + tce_mem += tce_per_phb; + } + } +} diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c new file mode 100644 index 000000000000..746ce5e51208 --- /dev/null +++ b/arch/powerpc/platforms/powernv/pci.c @@ -0,0 +1,286 @@ +/* + * Support PCI/PCIe on PowerNV platforms + * + * Currently supports only P5IOC2 + * + * Copyright 2011 Benjamin Herrenschmidt, IBM Corp. + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "powernv.h" +#include "pci.h" + + +#define cfg_dbg(fmt...) do { } while(0) +//#define cfg_dbg(fmt...) printk(fmt) + + +static void pnv_pci_config_check_eeh(struct pnv_phb *phb, struct pci_bus *bus, + u32 bdfn) +{ + s64 rc; + u8 fstate; + u16 pcierr; + u32 pe_no; + + /* Get PE# if we support IODA */ + pe_no = phb->bdfn_to_pe ? phb->bdfn_to_pe(phb, bus, bdfn & 0xff) : 0; + + /* Read freeze status */ + rc = opal_pci_eeh_freeze_status(phb->opal_id, pe_no, &fstate, &pcierr, + NULL); + if (rc) { + pr_warning("PCI %d: Failed to read EEH status for PE#%d," + " err %lld\n", phb->hose->global_number, pe_no, rc); + return; + } + cfg_dbg(" -> EEH check, bdfn=%04x PE%d fstate=%x\n", + bdfn, pe_no, fstate); + if (fstate != 0) { + rc = opal_pci_eeh_freeze_clear(phb->opal_id, pe_no, + OPAL_EEH_ACTION_CLEAR_FREEZE_ALL); + if (rc) { + pr_warning("PCI %d: Failed to clear EEH freeze state" + " for PE#%d, err %lld\n", + phb->hose->global_number, pe_no, rc); + } + } +} + +static int pnv_pci_read_config(struct pci_bus *bus, + unsigned int devfn, + int where, int size, u32 *val) +{ + struct pci_controller *hose = pci_bus_to_host(bus); + struct pnv_phb *phb = hose->private_data; + u32 bdfn = (((uint64_t)bus->number) << 8) | devfn; + s64 rc; + + if (hose == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + + switch (size) { + case 1: { + u8 v8; + rc = opal_pci_config_read_byte(phb->opal_id, bdfn, where, &v8); + *val = (rc == OPAL_SUCCESS) ? v8 : 0xff; + break; + } + case 2: { + u16 v16; + rc = opal_pci_config_read_half_word(phb->opal_id, bdfn, where, + &v16); + *val = (rc == OPAL_SUCCESS) ? v16 : 0xffff; + break; + } + case 4: { + u32 v32; + rc = opal_pci_config_read_word(phb->opal_id, bdfn, where, &v32); + *val = (rc == OPAL_SUCCESS) ? v32 : 0xffffffff; + break; + } + default: + return PCIBIOS_FUNC_NOT_SUPPORTED; + } + cfg_dbg("pnv_pci_read_config bus: %x devfn: %x +%x/%x -> %08x\n", + bus->number, devfn, where, size, *val); + + /* Check if the PHB got frozen due to an error (no response) */ + pnv_pci_config_check_eeh(phb, bus, bdfn); + + return PCIBIOS_SUCCESSFUL; +} + +static int pnv_pci_write_config(struct pci_bus *bus, + unsigned int devfn, + int where, int size, u32 val) +{ + struct pci_controller *hose = pci_bus_to_host(bus); + struct pnv_phb *phb = hose->private_data; + u32 bdfn = (((uint64_t)bus->number) << 8) | devfn; + + if (hose == NULL) + return PCIBIOS_DEVICE_NOT_FOUND; + + cfg_dbg("pnv_pci_write_config bus: %x devfn: %x +%x/%x -> %08x\n", + bus->number, devfn, where, size, val); + switch (size) { + case 1: + opal_pci_config_write_byte(phb->opal_id, bdfn, where, val); + break; + case 2: + opal_pci_config_write_half_word(phb->opal_id, bdfn, where, val); + break; + case 4: + opal_pci_config_write_word(phb->opal_id, bdfn, where, val); + break; + default: + return PCIBIOS_FUNC_NOT_SUPPORTED; + } + /* Check if the PHB got frozen due to an error (no response) */ + pnv_pci_config_check_eeh(phb, bus, bdfn); + + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops pnv_pci_ops = { + .read = pnv_pci_read_config, + .write = pnv_pci_write_config, +}; + +static int pnv_tce_build(struct iommu_table *tbl, long index, long npages, + unsigned long uaddr, enum dma_data_direction direction, + struct dma_attrs *attrs) +{ + u64 proto_tce; + u64 *tcep; + u64 rpn; + + proto_tce = TCE_PCI_READ; // Read allowed + + if (direction != DMA_TO_DEVICE) + proto_tce |= TCE_PCI_WRITE; + + tcep = ((u64 *)tbl->it_base) + index; + + while (npages--) { + /* can't move this out since we might cross LMB boundary */ + rpn = (virt_to_abs(uaddr)) >> TCE_SHIFT; + *tcep = proto_tce | (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT; + + uaddr += TCE_PAGE_SIZE; + tcep++; + } + return 0; +} + +static void pnv_tce_free(struct iommu_table *tbl, long index, long npages) +{ + u64 *tcep = ((u64 *)tbl->it_base) + index; + + while (npages--) + *(tcep++) = 0; +} + +void pnv_pci_setup_iommu_table(struct iommu_table *tbl, + void *tce_mem, u64 tce_size, + u64 dma_offset) +{ + tbl->it_blocksize = 16; + tbl->it_base = (unsigned long)tce_mem; + tbl->it_offset = dma_offset >> IOMMU_PAGE_SHIFT; + tbl->it_index = 0; + tbl->it_size = tce_size >> 3; + tbl->it_busno = 0; + tbl->it_type = TCE_PCI; +} + +static struct iommu_table * __devinit +pnv_pci_setup_bml_iommu(struct pci_controller *hose) +{ + struct iommu_table *tbl; + const __be64 *basep; + const __be32 *sizep; + + basep = of_get_property(hose->dn, "linux,tce-base", NULL); + sizep = of_get_property(hose->dn, "linux,tce-size", NULL); + if (basep == NULL || sizep == NULL) { + pr_err("PCI: %s has missing tce entries !\n", hose->dn->full_name); + return NULL; + } + tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL, hose->node); + if (WARN_ON(!tbl)) + return NULL; + pnv_pci_setup_iommu_table(tbl, __va(be64_to_cpup(basep)), + be32_to_cpup(sizep), 0); + iommu_init_table(tbl, hose->node); + return tbl; +} + +static void __devinit pnv_pci_dma_fallback_setup(struct pci_controller *hose, + struct pci_dev *pdev) +{ + struct device_node *np = pci_bus_to_OF_node(hose->bus); + struct pci_dn *pdn; + + if (np == NULL) + return; + pdn = PCI_DN(np); + if (!pdn->iommu_table) + pdn->iommu_table = pnv_pci_setup_bml_iommu(hose); + if (!pdn->iommu_table) + return; + set_iommu_table_base(&pdev->dev, pdn->iommu_table); +} + +static void __devinit pnv_pci_dma_dev_setup(struct pci_dev *pdev) +{ + struct pci_controller *hose = pci_bus_to_host(pdev->bus); + struct pnv_phb *phb = hose->private_data; + + /* If we have no phb structure, try to setup a fallback based on + * the device-tree (RTAS PCI for example) + */ + if (phb && phb->dma_dev_setup) + phb->dma_dev_setup(phb, pdev); + else + pnv_pci_dma_fallback_setup(hose, pdev); +} + +void __init pnv_pci_init(void) +{ + struct device_node *np; + + pci_set_flags(PCI_CAN_SKIP_ISA_ALIGN); + + /* We do not want to just probe */ + pci_probe_only = 0; + + /* OPAL absent, try POPAL first then RTAS detection of PHBs */ + if (!firmware_has_feature(FW_FEATURE_OPAL)) { +#ifdef CONFIG_PPC_POWERNV_RTAS + init_pci_config_tokens(); + find_and_init_phbs(); +#endif /* CONFIG_PPC_POWERNV_RTAS */ + } else { + /* OPAL is here, do our normal stuff */ + + /* Look for p5ioc2 IO-Hubs */ + for_each_compatible_node(np, NULL, "ibm,p5ioc2") + pnv_pci_init_p5ioc2_hub(np); + } + + /* Setup the linkage between OF nodes and PHBs */ + pci_devs_phb_init(); + + /* Configure IOMMU DMA hooks */ + ppc_md.pci_dma_dev_setup = pnv_pci_dma_dev_setup; + ppc_md.tce_build = pnv_tce_build; + ppc_md.tce_free = pnv_tce_free; + set_pci_dma_ops(&dma_iommu_ops); + +} diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h new file mode 100644 index 000000000000..6730a10d7369 --- /dev/null +++ b/arch/powerpc/platforms/powernv/pci.h @@ -0,0 +1,38 @@ +#ifndef __POWERNV_PCI_H +#define __POWERNV_PCI_H + +struct pci_dn; + +enum pnv_phb_type { + PNV_PHB_P5IOC2, + PNV_PHB_IODA1, + PNV_PHB_IODA2, +}; + +struct pnv_phb { + struct pci_controller *hose; + enum pnv_phb_type type; + u64 opal_id; + void __iomem *regs; + spinlock_t lock; + + void (*dma_dev_setup)(struct pnv_phb *phb, struct pci_dev *pdev); + void (*fixup_phb)(struct pci_controller *hose); + u32 (*bdfn_to_pe)(struct pnv_phb *phb, struct pci_bus *bus, u32 devfn); + + union { + struct { + struct iommu_table iommu_table; + } p5ioc2; + }; +}; + +extern struct pci_ops pnv_pci_ops; + +extern void pnv_pci_setup_iommu_table(struct iommu_table *tbl, + void *tce_mem, u64 tce_size, + u64 dma_offset); +extern void pnv_pci_init_p5ioc2_hub(struct device_node *np); + + +#endif /* __POWERNV_PCI_H */ diff --git a/arch/powerpc/platforms/powernv/powernv.h b/arch/powerpc/platforms/powernv/powernv.h index 35b716008cd3..8a9df7f9667e 100644 --- a/arch/powerpc/platforms/powernv/powernv.h +++ b/arch/powerpc/platforms/powernv/powernv.h @@ -7,4 +7,10 @@ extern void pnv_smp_init(void); static inline void pnv_smp_init(void) { } #endif +#ifdef CONFIG_PCI +extern void pnv_pci_init(void); +#else +static inline void pnv_pci_init(void) { } +#endif + #endif /* _POWERNV_H */ diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c index f0242f3fd3e6..467bd4ac6824 100644 --- a/arch/powerpc/platforms/powernv/setup.c +++ b/arch/powerpc/platforms/powernv/setup.c @@ -40,7 +40,8 @@ static void __init pnv_setup_arch(void) /* Initialize SMP */ pnv_smp_init(); - /* XXX PCI */ + /* Setup PCI */ + pnv_pci_init(); /* Setup RTC and NVRAM callbacks */ if (firmware_has_feature(FW_FEATURE_OPAL)) From c1a2562ac5edcb3965760f4a37368122d85657af Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:45:06 +0000 Subject: [PATCH 0224/1531] powerpc/powernv: Implement MSI support for p5ioc2 PCIe This implements support for MSIs on p5ioc2 PHBs. We only support MSIs on the PCIe PHBs, not the PCI-X ones as the later hasn't been properly verified in HW. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/powernv/pci-p5ioc2.c | 49 +++++++++ arch/powerpc/platforms/powernv/pci.c | 109 ++++++++++++++++++++ arch/powerpc/platforms/powernv/pci.h | 10 ++ 3 files changed, 168 insertions(+) diff --git a/arch/powerpc/platforms/powernv/pci-p5ioc2.c b/arch/powerpc/platforms/powernv/pci-p5ioc2.c index afabc2bcae45..4c80f7c77d56 100644 --- a/arch/powerpc/platforms/powernv/pci-p5ioc2.c +++ b/arch/powerpc/platforms/powernv/pci-p5ioc2.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -39,6 +40,51 @@ */ #define P5IOC2_TCE_MEMORY 0x01000000 +#ifdef CONFIG_PCI_MSI +static int pnv_pci_p5ioc2_msi_setup(struct pnv_phb *phb, struct pci_dev *dev, + unsigned int hwirq, unsigned int is_64, + struct msi_msg *msg) +{ + if (WARN_ON(!is_64)) + return -ENXIO; + msg->data = hwirq - phb->msi_base; + msg->address_hi = 0x10000000; + msg->address_lo = 0; + + return 0; +} + +static void pnv_pci_init_p5ioc2_msis(struct pnv_phb *phb) +{ + unsigned int bmap_size; + const __be32 *prop = of_get_property(phb->hose->dn, + "ibm,opal-msi-ranges", NULL); + if (!prop) + return; + + /* Don't do MSI's on p5ioc2 PCI-X are they are not properly + * verified in HW + */ + if (of_device_is_compatible(phb->hose->dn, "ibm,p5ioc2-pcix")) + return; + phb->msi_base = be32_to_cpup(prop); + phb->msi_count = be32_to_cpup(prop + 1); + bmap_size = BITS_TO_LONGS(phb->msi_count) * sizeof(unsigned long); + phb->msi_map = zalloc_maybe_bootmem(bmap_size, GFP_KERNEL); + if (!phb->msi_map) { + pr_err("PCI %d: Failed to allocate MSI bitmap !\n", + phb->hose->global_number); + return; + } + phb->msi_setup = pnv_pci_p5ioc2_msi_setup; + phb->msi32_support = 0; + pr_info(" Allocated bitmap for %d MSIs (base IRQ 0x%x)\n", + phb->msi_count, phb->msi_base); +} +#else +static void pnv_pci_init_p5ioc2_msis(struct pnv_phb *phb) { } +#endif /* CONFIG_PCI_MSI */ + static void __devinit pnv_pci_p5ioc2_dma_dev_setup(struct pnv_phb *phb, struct pci_dev *pdev) { @@ -117,6 +163,9 @@ static void __init pnv_pci_init_p5ioc2_phb(struct device_node *np, phb->hose->ops = &pnv_pci_ops; + /* Setup MSI support */ + pnv_pci_init_p5ioc2_msis(phb); + /* Setup TCEs */ phb->dma_dev_setup = pnv_pci_p5ioc2_dma_dev_setup; pnv_pci_setup_iommu_table(&phb->p5ioc2.iommu_table, diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c index 746ce5e51208..5c175519cf9e 100644 --- a/arch/powerpc/platforms/powernv/pci.c +++ b/arch/powerpc/platforms/powernv/pci.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -38,6 +39,108 @@ #define cfg_dbg(fmt...) do { } while(0) //#define cfg_dbg(fmt...) printk(fmt) +#ifdef CONFIG_PCI_MSI +static int pnv_msi_check_device(struct pci_dev* pdev, int nvec, int type) +{ + struct pci_controller *hose = pci_bus_to_host(pdev->bus); + struct pnv_phb *phb = hose->private_data; + + return (phb && phb->msi_map) ? 0 : -ENODEV; +} + +static unsigned int pnv_get_one_msi(struct pnv_phb *phb) +{ + unsigned int id; + + 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; + } + __set_bit(id, phb->msi_map); + spin_unlock(&phb->lock); + return id + phb->msi_base; +} + +static void pnv_put_msi(struct pnv_phb *phb, unsigned int hwirq) +{ + 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); + __clear_bit(id, phb->msi_map); + spin_unlock(&phb->lock); +} + +static int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) +{ + struct pci_controller *hose = pci_bus_to_host(pdev->bus); + struct pnv_phb *phb = hose->private_data; + struct msi_desc *entry; + struct msi_msg msg; + unsigned int hwirq, virq; + int rc; + + if (WARN_ON(!phb)) + return -ENODEV; + + list_for_each_entry(entry, &pdev->msi_list, list) { + if (!entry->msi_attrib.is_64 && !phb->msi32_support) { + pr_warn("%s: Supports only 64-bit MSIs\n", + pci_name(pdev)); + return -ENXIO; + } + hwirq = pnv_get_one_msi(phb); + if (!hwirq) { + pr_warn("%s: Failed to find a free MSI\n", + pci_name(pdev)); + return -ENOSPC; + } + virq = irq_create_mapping(NULL, hwirq); + if (virq == NO_IRQ) { + pr_warn("%s: Failed to map MSI to linux irq\n", + pci_name(pdev)); + pnv_put_msi(phb, hwirq); + return -ENOMEM; + } + rc = phb->msi_setup(phb, pdev, hwirq, entry->msi_attrib.is_64, + &msg); + if (rc) { + pr_warn("%s: Failed to setup MSI\n", pci_name(pdev)); + irq_dispose_mapping(virq); + pnv_put_msi(phb, hwirq); + return rc; + } + irq_set_msi_desc(virq, entry); + write_msi_msg(virq, &msg); + } + return 0; +} + +static void pnv_teardown_msi_irqs(struct pci_dev *pdev) +{ + struct pci_controller *hose = pci_bus_to_host(pdev->bus); + struct pnv_phb *phb = hose->private_data; + struct msi_desc *entry; + + if (WARN_ON(!phb)) + return; + + list_for_each_entry(entry, &pdev->msi_list, list) { + if (entry->irq == NO_IRQ) + continue; + irq_set_msi_desc(entry->irq, NULL); + pnv_put_msi(phb, virq_to_hw(entry->irq)); + irq_dispose_mapping(entry->irq); + } +} +#endif /* CONFIG_PCI_MSI */ static void pnv_pci_config_check_eeh(struct pnv_phb *phb, struct pci_bus *bus, u32 bdfn) @@ -283,4 +386,10 @@ void __init pnv_pci_init(void) ppc_md.tce_free = pnv_tce_free; set_pci_dma_ops(&dma_iommu_ops); + /* Configure MSIs */ +#ifdef CONFIG_PCI_MSI + ppc_md.msi_check_device = pnv_msi_check_device; + ppc_md.setup_msi_irqs = pnv_setup_msi_irqs; + ppc_md.teardown_msi_irqs = pnv_teardown_msi_irqs; +#endif } diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h index 6730a10d7369..d4dbc4950936 100644 --- a/arch/powerpc/platforms/powernv/pci.h +++ b/arch/powerpc/platforms/powernv/pci.h @@ -16,6 +16,16 @@ struct pnv_phb { void __iomem *regs; spinlock_t lock; +#ifdef CONFIG_PCI_MSI + unsigned long *msi_map; + unsigned int msi_base; + unsigned int msi_count; + unsigned int msi_next; + unsigned int msi32_support; +#endif + int (*msi_setup)(struct pnv_phb *phb, struct pci_dev *dev, + unsigned int hwirq, unsigned int is_64, + struct msi_msg *msg); void (*dma_dev_setup)(struct pnv_phb *phb, struct pci_dev *pdev); void (*fixup_phb)(struct pci_controller *hose); u32 (*bdfn_to_pe)(struct pnv_phb *phb, struct pci_bus *bus, u32 devfn); From 82ba129baeb1ff72e75d93e70534ba50312153f3 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 19 Sep 2011 17:45:07 +0000 Subject: [PATCH 0225/1531] powerpc/powernv: Handle PCI-X/PCIe reset delay The firmware doesn't wait after lifting the PCI reset. However it does timestamp it in the device tree. We use that to ensure we wait long enough (3s is our current arbitrary setting) from that timestamp to actually probing the bus. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/powernv/pci.c | 32 ++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c index 5c175519cf9e..85bb66d7f933 100644 --- a/arch/powerpc/platforms/powernv/pci.c +++ b/arch/powerpc/platforms/powernv/pci.c @@ -35,6 +35,8 @@ #include "powernv.h" #include "pci.h" +/* Delay in usec */ +#define PCI_RESET_DELAY_US 3000000 #define cfg_dbg(fmt...) do { } while(0) //#define cfg_dbg(fmt...) printk(fmt) @@ -354,6 +356,35 @@ static void __devinit pnv_pci_dma_dev_setup(struct pci_dev *pdev) pnv_pci_dma_fallback_setup(hose, pdev); } +static int pnv_pci_probe_mode(struct pci_bus *bus) +{ + struct pci_controller *hose = pci_bus_to_host(bus); + const __be64 *tstamp; + u64 now, target; + + + /* We hijack this as a way to ensure we have waited long + * enough since the reset was lifted on the PCI bus + */ + if (bus != hose->bus) + return PCI_PROBE_NORMAL; + tstamp = of_get_property(hose->dn, "reset-clear-timestamp", NULL); + if (!tstamp || !*tstamp) + return PCI_PROBE_NORMAL; + + now = mftb() / tb_ticks_per_usec; + target = (be64_to_cpup(tstamp) / tb_ticks_per_usec) + + PCI_RESET_DELAY_US; + + pr_devel("pci %04d: Reset target: 0x%llx now: 0x%llx\n", + hose->global_number, target, now); + + if (now < target) + msleep((target - now + 999) / 1000); + + return PCI_PROBE_NORMAL; +} + void __init pnv_pci_init(void) { struct device_node *np; @@ -384,6 +415,7 @@ void __init pnv_pci_init(void) ppc_md.pci_dma_dev_setup = pnv_pci_dma_dev_setup; ppc_md.tce_build = pnv_tce_build; ppc_md.tce_free = pnv_tce_free; + ppc_md.pci_probe_mode = pnv_pci_probe_mode; set_pci_dma_ops(&dma_iommu_ops); /* Configure MSIs */ From a120db06c3f435c37d028b6e5a1968dad06b7df0 Mon Sep 17 00:00:00 2001 From: Anshuman Khandual Date: Thu, 8 Sep 2011 21:12:06 +0000 Subject: [PATCH 0226/1531] perf events, powerpc: Add POWER7 stalled-cycles-frontend/backend events perf events, powerpc: Add POWER7 stalled-cycles-frontend/backend events Extent the POWER7 PMU driver with definitions for generic front-end and back-end stall events. As explained in Ingo's original comment(8f62242246351b5a4bc0c1f00c0c7003edea128a ), the exact definitions of the stall events are very much processor specific as different things mean different in their respective instruction pipeline. These two Power7 raw events are the closest approximation to the concept detailed in Ingo's comment. [PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = 0x100f8, /* GCT_NOSLOT_CYC */ It means cycles when the Global Completion Table has no slots from this thread [PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = 0x4000a, /* CMPLU_STALL */ It means no groups completed and GCT not empty for this thread Signed-off-by: Anshuman Khandual Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/power7-pmu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/powerpc/kernel/power7-pmu.c b/arch/powerpc/kernel/power7-pmu.c index de83d6060dda..1251e4d7e262 100644 --- a/arch/powerpc/kernel/power7-pmu.c +++ b/arch/powerpc/kernel/power7-pmu.c @@ -297,6 +297,8 @@ static void power7_disable_pmc(unsigned int pmc, unsigned long mmcr[]) static int power7_generic_events[] = { [PERF_COUNT_HW_CPU_CYCLES] = 0x1e, + [PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = 0x100f8, /* GCT_NOSLOT_CYC */ + [PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = 0x4000a, /* CMPLU_STALL */ [PERF_COUNT_HW_INSTRUCTIONS] = 2, [PERF_COUNT_HW_CACHE_REFERENCES] = 0xc880, /* LD_REF_L1_LSU*/ [PERF_COUNT_HW_CACHE_MISSES] = 0x400f0, /* LD_MISS_L1 */ From 463894705e4089d0ff69e7d877312d496ac70e5b Mon Sep 17 00:00:00 2001 From: Barry Song Date: Thu, 15 Sep 2011 03:06:30 -0700 Subject: [PATCH 0227/1531] dmaengine: delete redundant chan_id and chancnt initialization in dma drivers dma_async_device_register will re-init chan_id and chancnt, so whatever chan_id and chancnt are set in drivers, they will be re-written by dma_async_device_register. Cc: Nicolas Ferre Cc: Viresh Kumar Cc: Vinod Koul Cc: Piotr Ziecik Cc: Yong Wang Cc: Jaswinder Singh Cc: Pelagicore AB Signed-off-by: Barry Song Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/at_hdmac.c | 5 ++--- drivers/dma/dw_dmac.c | 5 ++--- drivers/dma/intel_mid_dma.c | 2 -- drivers/dma/mpc512x_dma.c | 1 - drivers/dma/pch_dma.c | 2 -- drivers/dma/pl330.c | 2 -- drivers/dma/timb_dma.c | 3 +-- 7 files changed, 5 insertions(+), 15 deletions(-) diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 3b99dc62874b..fcfa0a8b5c59 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -1268,12 +1268,11 @@ 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++, atdma->dma_common.chancnt++) { + for (i = 0; i < pdata->nr_channels; i++) { struct at_dma_chan *atchan = &atdma->chan[i]; atchan->chan_common.device = &atdma->dma_common; atchan->chan_common.cookie = atchan->completed_cookie = 1; - atchan->chan_common.chan_id = i; list_add_tail(&atchan->chan_common.device_node, &atdma->dma_common.channels); @@ -1314,7 +1313,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 " : "", - atdma->dma_common.chancnt); + pdata->nr_channels); dma_async_device_register(&atdma->dma_common); diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 4d180ca9a1d8..9bfd6d360718 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1407,12 +1407,11 @@ static int __init dw_probe(struct platform_device *pdev) dw->all_chan_mask = (1 << pdata->nr_channels) - 1; INIT_LIST_HEAD(&dw->dma.channels); - for (i = 0; i < pdata->nr_channels; i++, dw->dma.chancnt++) { + for (i = 0; i < pdata->nr_channels; i++) { struct dw_dma_chan *dwc = &dw->chan[i]; dwc->chan.device = &dw->dma; dwc->chan.cookie = dwc->completed = 1; - dwc->chan.chan_id = i; if (pdata->chan_allocation_order == CHAN_ALLOCATION_ASCENDING) list_add_tail(&dwc->chan.device_node, &dw->dma.channels); @@ -1468,7 +1467,7 @@ static int __init dw_probe(struct platform_device *pdev) dma_writel(dw, CFG, DW_CFG_DMA_EN); printk(KERN_INFO "%s: DesignWare DMA Controller, %d channels\n", - dev_name(&pdev->dev), dw->dma.chancnt); + dev_name(&pdev->dev), pdata->nr_channels); dma_async_device_register(&dw->dma); diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c index 8a3fdd87db97..cf74a664c5e0 100644 --- a/drivers/dma/intel_mid_dma.c +++ b/drivers/dma/intel_mid_dma.c @@ -1114,7 +1114,6 @@ static int mid_setup_dma(struct pci_dev *pdev) midch->chan.device = &dma->common; midch->chan.cookie = 1; - midch->chan.chan_id = i; midch->ch_id = dma->chan_base + i; pr_debug("MDMA:Init CH %d, ID %d\n", i, midch->ch_id); @@ -1150,7 +1149,6 @@ static int mid_setup_dma(struct pci_dev *pdev) dma_cap_set(DMA_SLAVE, dma->common.cap_mask); dma_cap_set(DMA_PRIVATE, dma->common.cap_mask); dma->common.dev = &pdev->dev; - dma->common.chancnt = dma->max_chan; dma->common.device_alloc_chan_resources = intel_mid_dma_alloc_chan_resources; diff --git a/drivers/dma/mpc512x_dma.c b/drivers/dma/mpc512x_dma.c index b9bae94f2015..8ba4edc6185e 100644 --- a/drivers/dma/mpc512x_dma.c +++ b/drivers/dma/mpc512x_dma.c @@ -741,7 +741,6 @@ static int __devinit mpc_dma_probe(struct platform_device *op) mchan = &mdma->channels[i]; mchan->chan.device = dma; - mchan->chan.chan_id = i; mchan->chan.cookie = 1; mchan->completed_cookie = mchan->chan.cookie; diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c index 1ac8d4b580b7..5b65362024fd 100644 --- a/drivers/dma/pch_dma.c +++ b/drivers/dma/pch_dma.c @@ -926,7 +926,6 @@ static int __devinit pch_dma_probe(struct pci_dev *pdev, } pd->dma.dev = &pdev->dev; - pd->dma.chancnt = nr_channels; INIT_LIST_HEAD(&pd->dma.channels); @@ -935,7 +934,6 @@ static int __devinit pch_dma_probe(struct pci_dev *pdev, pd_chan->chan.device = &pd->dma; pd_chan->chan.cookie = 1; - pd_chan->chan.chan_id = i; pd_chan->membase = ®s->desc[i]; diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 00eee59e8b33..7f86e7df7da1 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -747,11 +747,9 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) spin_lock_init(&pch->lock); pch->pl330_chid = NULL; pch->chan.device = pd; - pch->chan.chan_id = i; pch->dmac = pdmac; /* Add the channel to the DMAC list */ - pd->chancnt++; list_add_tail(&pch->chan.device_node, &pd->channels); } diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c index f69f90a61873..6dbdf451128e 100644 --- a/drivers/dma/timb_dma.c +++ b/drivers/dma/timb_dma.c @@ -753,7 +753,7 @@ static int __devinit td_probe(struct platform_device *pdev) INIT_LIST_HEAD(&td->dma.channels); - for (i = 0; i < pdata->nr_channels; i++, td->dma.chancnt++) { + for (i = 0; i < pdata->nr_channels; i++) { struct timb_dma_chan *td_chan = &td->channels[i]; struct timb_dma_platform_data_channel *pchan = pdata->channels + i; @@ -767,7 +767,6 @@ static int __devinit td_probe(struct platform_device *pdev) td_chan->chan.device = &td->dma; td_chan->chan.cookie = 1; - td_chan->chan.chan_id = i; spin_lock_init(&td_chan->lock); INIT_LIST_HEAD(&td_chan->active_list); INIT_LIST_HEAD(&td_chan->queue); From 536137bc9ff1738a7bee9b31047a7cd56860180e Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Fri, 26 Aug 2011 11:03:03 +0900 Subject: [PATCH 0228/1531] gpio/s3c24xx: move gpio driver into drivers/gpio/ Cc: Ben Dooks Acked-by: Grant Likely Signed-off-by: Kukjin Kim --- arch/arm/mach-s3c2410/Kconfig | 6 -- arch/arm/mach-s3c2410/Makefile | 1 - arch/arm/mach-s3c2412/Kconfig | 1 - arch/arm/mach-s3c2412/Makefile | 1 - arch/arm/mach-s3c2440/Kconfig | 2 - arch/arm/plat-s3c24xx/Makefile | 2 - arch/arm/plat-s3c24xx/gpio.c | 96 ------------------- drivers/gpio/Kconfig | 4 + drivers/gpio/Makefile | 1 + .../gpiolib.c => drivers/gpio/gpio-s3c24xx.c | 70 ++++++++++++-- 10 files changed, 67 insertions(+), 117 deletions(-) delete mode 100644 arch/arm/plat-s3c24xx/gpio.c rename arch/arm/plat-s3c24xx/gpiolib.c => drivers/gpio/gpio-s3c24xx.c (78%) diff --git a/arch/arm/mach-s3c2410/Kconfig b/arch/arm/mach-s3c2410/Kconfig index 7245a55795dc..3700cf32af0f 100644 --- a/arch/arm/mach-s3c2410/Kconfig +++ b/arch/arm/mach-s3c2410/Kconfig @@ -8,7 +8,6 @@ config CPU_S3C2410 select CPU_ARM920T select S3C_GPIO_PULL_UP select S3C2410_CLOCK - select S3C2410_GPIO select CPU_LLSERIAL_S3C2410 select S3C2410_PM if PM select S3C2410_CPUFREQ if CPU_FREQ_S3C24XX @@ -28,11 +27,6 @@ config S3C2410_PM help Power Management code common to S3C2410 and better -config S3C2410_GPIO - bool - help - GPIO code for S3C2410 and similar processors - config SIMTEC_NOR bool help diff --git a/arch/arm/mach-s3c2410/Makefile b/arch/arm/mach-s3c2410/Makefile index 81695353d8f4..782fd81144e9 100644 --- a/arch/arm/mach-s3c2410/Makefile +++ b/arch/arm/mach-s3c2410/Makefile @@ -13,7 +13,6 @@ obj-$(CONFIG_CPU_S3C2410) += s3c2410.o obj-$(CONFIG_CPU_S3C2410_DMA) += dma.o obj-$(CONFIG_CPU_S3C2410_DMA) += dma.o obj-$(CONFIG_S3C2410_PM) += pm.o sleep.o -obj-$(CONFIG_S3C2410_GPIO) += gpio.o obj-$(CONFIG_S3C2410_CPUFREQ) += cpu-freq.o obj-$(CONFIG_S3C2410_PLLTABLE) += pll.o diff --git a/arch/arm/mach-s3c2412/Kconfig b/arch/arm/mach-s3c2412/Kconfig index c2cf4e569989..b8b9029e9f2d 100644 --- a/arch/arm/mach-s3c2412/Kconfig +++ b/arch/arm/mach-s3c2412/Kconfig @@ -9,7 +9,6 @@ config CPU_S3C2412 select CPU_LLSERIAL_S3C2440 select S3C2412_PM if PM select S3C2412_DMA if S3C2410_DMA - select S3C2410_GPIO help Support for the S3C2412 and S3C2413 SoCs from the S3C24XX line diff --git a/arch/arm/mach-s3c2412/Makefile b/arch/arm/mach-s3c2412/Makefile index 6c48a91ea39e..7e4d95fa8a97 100644 --- a/arch/arm/mach-s3c2412/Makefile +++ b/arch/arm/mach-s3c2412/Makefile @@ -12,7 +12,6 @@ obj- := obj-$(CONFIG_CPU_S3C2412) += s3c2412.o obj-$(CONFIG_CPU_S3C2412) += irq.o obj-$(CONFIG_CPU_S3C2412) += clock.o -obj-$(CONFIG_CPU_S3C2412) += gpio.o obj-$(CONFIG_S3C2412_DMA) += dma.o obj-$(CONFIG_S3C2412_PM) += pm.o obj-$(CONFIG_S3C2412_PM_SLEEP) += sleep.o diff --git a/arch/arm/mach-s3c2440/Kconfig b/arch/arm/mach-s3c2440/Kconfig index 50825a3f91cc..c461fb8e15c0 100644 --- a/arch/arm/mach-s3c2440/Kconfig +++ b/arch/arm/mach-s3c2440/Kconfig @@ -8,7 +8,6 @@ config CPU_S3C2440 select S3C_GPIO_PULL_UP select S3C2410_CLOCK select S3C2410_PM if PM - select S3C2410_GPIO select S3C2440_DMA if S3C2410_DMA select CPU_S3C244X select CPU_LLSERIAL_S3C2440 @@ -20,7 +19,6 @@ config CPU_S3C2442 select CPU_ARM920T select S3C_GPIO_PULL_DOWN select S3C2410_CLOCK - select S3C2410_GPIO select S3C2410_PM if PM select CPU_S3C244X select CPU_LLSERIAL_S3C2440 diff --git a/arch/arm/plat-s3c24xx/Makefile b/arch/arm/plat-s3c24xx/Makefile index 0291bd6e236e..e4f46495ed30 100644 --- a/arch/arm/plat-s3c24xx/Makefile +++ b/arch/arm/plat-s3c24xx/Makefile @@ -15,8 +15,6 @@ obj- := obj-y += cpu.o obj-y += irq.o obj-y += devs.o -obj-y += gpio.o -obj-y += gpiolib.o obj-y += clock.o obj-$(CONFIG_S3C24XX_DCLK) += clock-dclk.o diff --git a/arch/arm/plat-s3c24xx/gpio.c b/arch/arm/plat-s3c24xx/gpio.c deleted file mode 100644 index 2f3d7c089dfa..000000000000 --- a/arch/arm/plat-s3c24xx/gpio.c +++ /dev/null @@ -1,96 +0,0 @@ -/* linux/arch/arm/plat-s3c24xx/gpio.c - * - * Copyright (c) 2004-2010 Simtec Electronics - * Ben Dooks - * - * S3C24XX GPIO support - * - * 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 - -/* gpiolib wrappers until these are totally eliminated */ - -void s3c2410_gpio_pullup(unsigned int pin, unsigned int to) -{ - int ret; - - WARN_ON(to); /* should be none of these left */ - - if (!to) { - /* if pull is enabled, try first with up, and if that - * fails, try using down */ - - ret = s3c_gpio_setpull(pin, S3C_GPIO_PULL_UP); - if (ret) - s3c_gpio_setpull(pin, S3C_GPIO_PULL_DOWN); - } else { - s3c_gpio_setpull(pin, S3C_GPIO_PULL_NONE); - } -} -EXPORT_SYMBOL(s3c2410_gpio_pullup); - -void s3c2410_gpio_setpin(unsigned int pin, unsigned int to) -{ - /* do this via gpiolib until all users removed */ - - gpio_request(pin, "temporary"); - gpio_set_value(pin, to); - gpio_free(pin); -} - -EXPORT_SYMBOL(s3c2410_gpio_setpin); - -unsigned int s3c2410_gpio_getpin(unsigned int pin) -{ - struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin); - unsigned long offs = pin - chip->chip.base; - - return __raw_readl(chip->base + 0x04) & (1<< offs); -} - -EXPORT_SYMBOL(s3c2410_gpio_getpin); - -unsigned int s3c2410_modify_misccr(unsigned int clear, unsigned int change) -{ - unsigned long flags; - unsigned long misccr; - - local_irq_save(flags); - misccr = __raw_readl(S3C24XX_MISCCR); - misccr &= ~clear; - misccr ^= change; - __raw_writel(misccr, S3C24XX_MISCCR); - local_irq_restore(flags); - - return misccr; -} - -EXPORT_SYMBOL(s3c2410_modify_misccr); diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d539efd96d4b..5654e1b082af 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -135,6 +135,10 @@ config GPIO_PLAT_SAMSUNG def_bool y depends on SAMSUNG_GPIOLIB_4BIT +config GPIO_S3C24XX + def_bool y + depends on PLAT_S3C24XX + config GPIO_S5PC100 def_bool y depends on CPU_S5PC100 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 9588948c96f0..c7f1c00986c9 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o obj-$(CONFIG_GPIO_PLAT_SAMSUNG) += gpio-plat-samsung.o +obj-$(CONFIG_GPIO_S3C24XX) += gpio-s3c24xx.o obj-$(CONFIG_GPIO_S5PC100) += gpio-s5pc100.o obj-$(CONFIG_GPIO_S5PV210) += gpio-s5pv210.o diff --git a/arch/arm/plat-s3c24xx/gpiolib.c b/drivers/gpio/gpio-s3c24xx.c similarity index 78% rename from arch/arm/plat-s3c24xx/gpiolib.c rename to drivers/gpio/gpio-s3c24xx.c index 243b6411050d..ff6103130fe8 100644 --- a/arch/arm/plat-s3c24xx/gpiolib.c +++ b/drivers/gpio/gpio-s3c24xx.c @@ -1,5 +1,4 @@ -/* linux/arch/arm/plat-s3c24xx/gpiolib.c - * +/* * Copyright (c) 2008-2010 Simtec Electronics * http://armlinux.simtec.co.uk/ * Ben Dooks @@ -19,16 +18,17 @@ #include #include #include +#include + +#include +#include +#include #include #include #include -#include -#include #include -#include - static int s3c24xx_gpiolib_banka_input(struct gpio_chip *chip, unsigned offset) { return -EINVAL; @@ -210,7 +210,6 @@ struct s3c_gpio_chip s3c24xx_gpios[] = { }, }; - static __init int s3c24xx_gpiolib_init(void) { struct s3c_gpio_chip *chip = s3c24xx_gpios; @@ -225,5 +224,60 @@ static __init int s3c24xx_gpiolib_init(void) return 0; } - core_initcall(s3c24xx_gpiolib_init); + +/* gpiolib wrappers until these are totally eliminated */ + +void s3c2410_gpio_pullup(unsigned int pin, unsigned int to) +{ + int ret; + + WARN_ON(to); /* should be none of these left */ + + if (!to) { + /* if pull is enabled, try first with up, and if that + * fails, try using down */ + + ret = s3c_gpio_setpull(pin, S3C_GPIO_PULL_UP); + if (ret) + s3c_gpio_setpull(pin, S3C_GPIO_PULL_DOWN); + } else { + s3c_gpio_setpull(pin, S3C_GPIO_PULL_NONE); + } +} +EXPORT_SYMBOL(s3c2410_gpio_pullup); + +void s3c2410_gpio_setpin(unsigned int pin, unsigned int to) +{ + /* do this via gpiolib until all users removed */ + + gpio_request(pin, "temporary"); + gpio_set_value(pin, to); + gpio_free(pin); +} +EXPORT_SYMBOL(s3c2410_gpio_setpin); + +unsigned int s3c2410_gpio_getpin(unsigned int pin) +{ + struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin); + unsigned long offs = pin - chip->chip.base; + + return __raw_readl(chip->base + 0x04) & (1<< offs); +} +EXPORT_SYMBOL(s3c2410_gpio_getpin); + +unsigned int s3c2410_modify_misccr(unsigned int clear, unsigned int change) +{ + unsigned long flags; + unsigned long misccr; + + local_irq_save(flags); + misccr = __raw_readl(S3C24XX_MISCCR); + misccr &= ~clear; + misccr ^= change; + __raw_writel(misccr, S3C24XX_MISCCR); + local_irq_restore(flags); + + return misccr; +} +EXPORT_SYMBOL(s3c2410_modify_misccr); From ec080059c1ce7bfdc8453bf4f3328ac0315a66a1 Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Fri, 26 Aug 2011 11:06:25 +0900 Subject: [PATCH 0229/1531] gpio/s3c64xx: move gpio driver into drivers/gpio/ Cc: Ben Dooks Acked-by: Grant Likely Signed-off-by: Kukjin Kim --- arch/arm/mach-s3c64xx/Makefile | 1 - drivers/gpio/Kconfig | 4 ++++ drivers/gpio/Makefile | 1 + .../arm/mach-s3c64xx/gpiolib.c => drivers/gpio/gpio-s3c64xx.c | 3 +-- 4 files changed, 6 insertions(+), 3 deletions(-) rename arch/arm/mach-s3c64xx/gpiolib.c => drivers/gpio/gpio-s3c64xx.c (99%) diff --git a/arch/arm/mach-s3c64xx/Makefile b/arch/arm/mach-s3c64xx/Makefile index 61b4034a0c22..f0102d7b531b 100644 --- a/arch/arm/mach-s3c64xx/Makefile +++ b/arch/arm/mach-s3c64xx/Makefile @@ -13,7 +13,6 @@ obj- := # Core files obj-y += cpu.o obj-y += clock.o -obj-y += gpiolib.o # Core support for S3C6400 system diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 5654e1b082af..6368730a2e58 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -139,6 +139,10 @@ config GPIO_S3C24XX def_bool y depends on PLAT_S3C24XX +config GPIO_S3C64XX + def_bool y + depends on ARCH_S3C64XX + config GPIO_S5PC100 def_bool y depends on CPU_S5PC100 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index c7f1c00986c9..8c1fb23285cb 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o obj-$(CONFIG_GPIO_PLAT_SAMSUNG) += gpio-plat-samsung.o obj-$(CONFIG_GPIO_S3C24XX) += gpio-s3c24xx.o +obj-$(CONFIG_GPIO_S3C64XX) += gpio-s3c64xx.o obj-$(CONFIG_GPIO_S5PC100) += gpio-s5pc100.o obj-$(CONFIG_GPIO_S5PV210) += gpio-s5pv210.o diff --git a/arch/arm/mach-s3c64xx/gpiolib.c b/drivers/gpio/gpio-s3c64xx.c similarity index 99% rename from arch/arm/mach-s3c64xx/gpiolib.c rename to drivers/gpio/gpio-s3c64xx.c index 92b09085caaa..b4f1c8204f03 100644 --- a/arch/arm/mach-s3c64xx/gpiolib.c +++ b/drivers/gpio/gpio-s3c64xx.c @@ -1,5 +1,4 @@ -/* arch/arm/plat-s3c64xx/gpiolib.c - * +/* * Copyright 2008 Openmoko, Inc. * Copyright 2008 Simtec Electronics * Ben Dooks From c4b3fd38dfb677d7a3997527c9cbdc21b81424a3 Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Fri, 26 Aug 2011 11:07:26 +0900 Subject: [PATCH 0230/1531] gpio/s5p64x0: move gpio driver into drivers/gpio/ Acked-by: Grant Likely Signed-off-by: Kukjin Kim --- arch/arm/mach-s5p64x0/Makefile | 2 +- drivers/gpio/Kconfig | 4 ++++ drivers/gpio/Makefile | 1 + .../arm/mach-s5p64x0/gpiolib.c => drivers/gpio/gpio-s5p64x0.c | 3 +-- 4 files changed, 7 insertions(+), 3 deletions(-) rename arch/arm/mach-s5p64x0/gpiolib.c => drivers/gpio/gpio-s5p64x0.c (99%) diff --git a/arch/arm/mach-s5p64x0/Makefile b/arch/arm/mach-s5p64x0/Makefile index 5f6afdf067ed..5b9495538839 100644 --- a/arch/arm/mach-s5p64x0/Makefile +++ b/arch/arm/mach-s5p64x0/Makefile @@ -12,7 +12,7 @@ obj- := # Core support for S5P64X0 system -obj-$(CONFIG_ARCH_S5P64X0) += cpu.o init.o clock.o dma.o gpiolib.o +obj-$(CONFIG_ARCH_S5P64X0) += cpu.o init.o clock.o dma.o obj-$(CONFIG_ARCH_S5P64X0) += setup-i2c0.o irq-eint.o obj-$(CONFIG_CPU_S5P6440) += clock-s5p6440.o obj-$(CONFIG_CPU_S5P6450) += clock-s5p6450.o diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 6368730a2e58..5eb29bc18a56 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -143,6 +143,10 @@ config GPIO_S3C64XX def_bool y depends on ARCH_S3C64XX +config GPIO_S5P64X0 + def_bool y + depends on ARCH_S5P64X0 + config GPIO_S5PC100 def_bool y depends on CPU_S5PC100 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 8c1fb23285cb..10f3bbf0eceb 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o obj-$(CONFIG_GPIO_PLAT_SAMSUNG) += gpio-plat-samsung.o obj-$(CONFIG_GPIO_S3C24XX) += gpio-s3c24xx.o obj-$(CONFIG_GPIO_S3C64XX) += gpio-s3c64xx.o +obj-$(CONFIG_GPIO_S5P64X0) += gpio-s5p64x0.o obj-$(CONFIG_GPIO_S5PC100) += gpio-s5pc100.o obj-$(CONFIG_GPIO_S5PV210) += gpio-s5pv210.o diff --git a/arch/arm/mach-s5p64x0/gpiolib.c b/drivers/gpio/gpio-s5p64x0.c similarity index 99% rename from arch/arm/mach-s5p64x0/gpiolib.c rename to drivers/gpio/gpio-s5p64x0.c index e7fb3b004e77..96e816f5cc95 100644 --- a/arch/arm/mach-s5p64x0/gpiolib.c +++ b/drivers/gpio/gpio-s5p64x0.c @@ -1,5 +1,4 @@ -/* linux/arch/arm/mach-s5p64x0/gpiolib.c - * +/* * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. * http://www.samsung.com * From f8de8f4ce2a83ccf7571ee13d41d02a9040797f9 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 30 Aug 2011 15:08:24 +0800 Subject: [PATCH 0231/1531] dmaengine i.MX DMA/SDMA: add missing include of linux/module.h Add missing include of linux/module.h to fix build error. Signed-off-by: Axel Lin Acked-by: Wolfram Sang Signed-off-by: Vinod Koul --- drivers/dma/imx-dma.c | 1 + drivers/dma/imx-sdma.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index d99f71c356b5..d746899f36e1 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -14,6 +14,7 @@ * http://www.gnu.org/copyleft/gpl.html */ #include +#include #include #include #include diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index b5cc27dc9a51..eab1fe71259e 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -18,6 +18,7 @@ */ #include +#include #include #include #include From 1b39d5f2cc5c28085bbf48db80bf704ab4dedda9 Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Tue, 30 Aug 2011 20:39:08 +0900 Subject: [PATCH 0232/1531] gpio/samsung: gpio-samsung.c to support Samsung GPIOs This patch adds support for Samsung GPIOs with one gpio driver and removes old GPIO drivers which are drivers/gpio-s3c24xx.c, gpio-s3c64xx.c, gpio-s5p64x0.c, gpio-s5pc100.c, gpio-s5pv210.c, gpio-exynos4.c, gpio-plat-samsung.c, plat-samsung/gpio-config.c and gpio.c to support each Samsung SoCs before. Because the gpio-samsung.c can replace old Samsung GPIO drivers. Basically, the gpio-samsung.c has been made by their merging and removing duplicated definitions. Note: gpio-samsung.c includes some SoC dependent codes and it will be replaced next time. Cc: Ben Dooks Acked-by: Grant Likely [kgene.kim@samsung.com: squash the removing and adding patches] [kgene.kim@samsung.com: fixes bug during to register of gpio_chips] Signed-off-by: Kukjin Kim --- arch/arm/plat-samsung/Makefile | 2 - arch/arm/plat-samsung/gpio-config.c | 431 ----- arch/arm/plat-samsung/gpio.c | 167 -- drivers/gpio/Kconfig | 28 - drivers/gpio/Makefile | 10 +- drivers/gpio/gpio-exynos4.c | 385 ---- drivers/gpio/gpio-plat-samsung.c | 205 -- drivers/gpio/gpio-s3c24xx.c | 283 --- drivers/gpio/gpio-s3c64xx.c | 289 --- drivers/gpio/gpio-s5p64x0.c | 510 ----- drivers/gpio/gpio-s5pc100.c | 354 ---- drivers/gpio/gpio-s5pv210.c | 287 --- drivers/gpio/gpio-samsung.c | 2686 +++++++++++++++++++++++++++ 13 files changed, 2687 insertions(+), 2950 deletions(-) delete mode 100644 arch/arm/plat-samsung/gpio-config.c delete mode 100644 arch/arm/plat-samsung/gpio.c delete mode 100644 drivers/gpio/gpio-exynos4.c delete mode 100644 drivers/gpio/gpio-plat-samsung.c delete mode 100644 drivers/gpio/gpio-s3c24xx.c delete mode 100644 drivers/gpio/gpio-s3c64xx.c delete mode 100644 drivers/gpio/gpio-s5p64x0.c delete mode 100644 drivers/gpio/gpio-s5pc100.c delete mode 100644 drivers/gpio/gpio-s5pv210.c create mode 100644 drivers/gpio/gpio-samsung.c diff --git a/arch/arm/plat-samsung/Makefile b/arch/arm/plat-samsung/Makefile index 853764ba8cc5..6d772d7c012b 100644 --- a/arch/arm/plat-samsung/Makefile +++ b/arch/arm/plat-samsung/Makefile @@ -15,8 +15,6 @@ obj-y += init.o obj-$(CONFIG_ARCH_USES_GETTIMEOFFSET) += time.o obj-y += clock.o obj-y += pwm-clock.o -obj-y += gpio.o -obj-y += gpio-config.o obj-y += dev-asocdma.o obj-$(CONFIG_SAMSUNG_CLKSRC) += clock-clksrc.o diff --git a/arch/arm/plat-samsung/gpio-config.c b/arch/arm/plat-samsung/gpio-config.c deleted file mode 100644 index 1c0b0401594b..000000000000 --- a/arch/arm/plat-samsung/gpio-config.c +++ /dev/null @@ -1,431 +0,0 @@ -/* linux/arch/arm/plat-s3c/gpio-config.c - * - * Copyright 2008 Openmoko, Inc. - * Copyright 2008-2010 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * S3C series GPIO configuration core - * - * 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 - -int s3c_gpio_cfgpin(unsigned int pin, unsigned int config) -{ - struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin); - unsigned long flags; - int offset; - int ret; - - if (!chip) - return -EINVAL; - - offset = pin - chip->chip.base; - - s3c_gpio_lock(chip, flags); - ret = s3c_gpio_do_setcfg(chip, offset, config); - s3c_gpio_unlock(chip, flags); - - return ret; -} -EXPORT_SYMBOL(s3c_gpio_cfgpin); - -int s3c_gpio_cfgpin_range(unsigned int start, unsigned int nr, - unsigned int cfg) -{ - int ret; - - for (; nr > 0; nr--, start++) { - ret = s3c_gpio_cfgpin(start, cfg); - if (ret != 0) - return ret; - } - - return 0; -} -EXPORT_SYMBOL_GPL(s3c_gpio_cfgpin_range); - -int s3c_gpio_cfgall_range(unsigned int start, unsigned int nr, - unsigned int cfg, s3c_gpio_pull_t pull) -{ - int ret; - - for (; nr > 0; nr--, start++) { - s3c_gpio_setpull(start, pull); - ret = s3c_gpio_cfgpin(start, cfg); - if (ret != 0) - return ret; - } - - return 0; -} -EXPORT_SYMBOL_GPL(s3c_gpio_cfgall_range); - -unsigned s3c_gpio_getcfg(unsigned int pin) -{ - struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin); - unsigned long flags; - unsigned ret = 0; - int offset; - - if (chip) { - offset = pin - chip->chip.base; - - s3c_gpio_lock(chip, flags); - ret = s3c_gpio_do_getcfg(chip, offset); - s3c_gpio_unlock(chip, flags); - } - - return ret; -} -EXPORT_SYMBOL(s3c_gpio_getcfg); - - -int s3c_gpio_setpull(unsigned int pin, s3c_gpio_pull_t pull) -{ - struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin); - unsigned long flags; - int offset, ret; - - if (!chip) - return -EINVAL; - - offset = pin - chip->chip.base; - - s3c_gpio_lock(chip, flags); - ret = s3c_gpio_do_setpull(chip, offset, pull); - s3c_gpio_unlock(chip, flags); - - return ret; -} -EXPORT_SYMBOL(s3c_gpio_setpull); - -s3c_gpio_pull_t s3c_gpio_getpull(unsigned int pin) -{ - struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin); - unsigned long flags; - int offset; - u32 pup = 0; - - if (chip) { - offset = pin - chip->chip.base; - - s3c_gpio_lock(chip, flags); - pup = s3c_gpio_do_getpull(chip, offset); - s3c_gpio_unlock(chip, flags); - } - - return (__force s3c_gpio_pull_t)pup; -} -EXPORT_SYMBOL(s3c_gpio_getpull); - -#ifdef CONFIG_S3C_GPIO_CFG_S3C24XX -int s3c_gpio_setcfg_s3c24xx_a(struct s3c_gpio_chip *chip, - unsigned int off, unsigned int cfg) -{ - void __iomem *reg = chip->base; - unsigned int shift = off; - u32 con; - - if (s3c_gpio_is_cfg_special(cfg)) { - cfg &= 0xf; - - /* Map output to 0, and SFN2 to 1 */ - cfg -= 1; - if (cfg > 1) - return -EINVAL; - - cfg <<= shift; - } - - con = __raw_readl(reg); - con &= ~(0x1 << shift); - con |= cfg; - __raw_writel(con, reg); - - return 0; -} - -unsigned s3c_gpio_getcfg_s3c24xx_a(struct s3c_gpio_chip *chip, - unsigned int off) -{ - u32 con; - - con = __raw_readl(chip->base); - con >>= off; - con &= 1; - con++; - - return S3C_GPIO_SFN(con); -} - -int s3c_gpio_setcfg_s3c24xx(struct s3c_gpio_chip *chip, - unsigned int off, unsigned int cfg) -{ - void __iomem *reg = chip->base; - unsigned int shift = off * 2; - u32 con; - - if (s3c_gpio_is_cfg_special(cfg)) { - cfg &= 0xf; - if (cfg > 3) - return -EINVAL; - - cfg <<= shift; - } - - con = __raw_readl(reg); - con &= ~(0x3 << shift); - con |= cfg; - __raw_writel(con, reg); - - return 0; -} - -unsigned int s3c_gpio_getcfg_s3c24xx(struct s3c_gpio_chip *chip, - unsigned int off) -{ - u32 con; - - con = __raw_readl(chip->base); - con >>= off * 2; - con &= 3; - - /* this conversion works for IN and OUT as well as special mode */ - return S3C_GPIO_SPECIAL(con); -} -#endif - -#ifdef CONFIG_S3C_GPIO_CFG_S3C64XX -int s3c_gpio_setcfg_s3c64xx_4bit(struct s3c_gpio_chip *chip, - unsigned int off, unsigned int cfg) -{ - void __iomem *reg = chip->base; - unsigned int shift = (off & 7) * 4; - u32 con; - - if (off < 8 && chip->chip.ngpio > 8) - reg -= 4; - - if (s3c_gpio_is_cfg_special(cfg)) { - cfg &= 0xf; - cfg <<= shift; - } - - con = __raw_readl(reg); - con &= ~(0xf << shift); - con |= cfg; - __raw_writel(con, reg); - - return 0; -} - -unsigned s3c_gpio_getcfg_s3c64xx_4bit(struct s3c_gpio_chip *chip, - unsigned int off) -{ - void __iomem *reg = chip->base; - unsigned int shift = (off & 7) * 4; - u32 con; - - if (off < 8 && chip->chip.ngpio > 8) - reg -= 4; - - con = __raw_readl(reg); - con >>= shift; - con &= 0xf; - - /* this conversion works for IN and OUT as well as special mode */ - return S3C_GPIO_SPECIAL(con); -} - -#endif /* CONFIG_S3C_GPIO_CFG_S3C64XX */ - -#ifdef CONFIG_S3C_GPIO_PULL_UPDOWN -int s3c_gpio_setpull_updown(struct s3c_gpio_chip *chip, - unsigned int off, s3c_gpio_pull_t pull) -{ - void __iomem *reg = chip->base + 0x08; - int shift = off * 2; - u32 pup; - - pup = __raw_readl(reg); - pup &= ~(3 << shift); - pup |= pull << shift; - __raw_writel(pup, reg); - - return 0; -} - -s3c_gpio_pull_t s3c_gpio_getpull_updown(struct s3c_gpio_chip *chip, - unsigned int off) -{ - void __iomem *reg = chip->base + 0x08; - int shift = off * 2; - u32 pup = __raw_readl(reg); - - pup >>= shift; - pup &= 0x3; - return (__force s3c_gpio_pull_t)pup; -} - -#ifdef CONFIG_S3C_GPIO_PULL_S3C2443 -int s3c_gpio_setpull_s3c2443(struct s3c_gpio_chip *chip, - unsigned int off, s3c_gpio_pull_t pull) -{ - switch (pull) { - case S3C_GPIO_PULL_NONE: - pull = 0x01; - break; - case S3C_GPIO_PULL_UP: - pull = 0x00; - break; - case S3C_GPIO_PULL_DOWN: - pull = 0x02; - break; - } - return s3c_gpio_setpull_updown(chip, off, pull); -} - -s3c_gpio_pull_t s3c_gpio_getpull_s3c2443(struct s3c_gpio_chip *chip, - unsigned int off) -{ - s3c_gpio_pull_t pull; - - pull = s3c_gpio_getpull_updown(chip, off); - - switch (pull) { - case 0x00: - pull = S3C_GPIO_PULL_UP; - break; - case 0x01: - case 0x03: - pull = S3C_GPIO_PULL_NONE; - break; - case 0x02: - pull = S3C_GPIO_PULL_DOWN; - break; - } - - return pull; -} -#endif -#endif - -#if defined(CONFIG_S3C_GPIO_PULL_UP) || defined(CONFIG_S3C_GPIO_PULL_DOWN) -static int s3c_gpio_setpull_1(struct s3c_gpio_chip *chip, - unsigned int off, s3c_gpio_pull_t pull, - s3c_gpio_pull_t updown) -{ - void __iomem *reg = chip->base + 0x08; - u32 pup = __raw_readl(reg); - - if (pull == updown) - pup &= ~(1 << off); - else if (pull == S3C_GPIO_PULL_NONE) - pup |= (1 << off); - else - return -EINVAL; - - __raw_writel(pup, reg); - return 0; -} - -static s3c_gpio_pull_t s3c_gpio_getpull_1(struct s3c_gpio_chip *chip, - unsigned int off, s3c_gpio_pull_t updown) -{ - void __iomem *reg = chip->base + 0x08; - u32 pup = __raw_readl(reg); - - pup &= (1 << off); - return pup ? S3C_GPIO_PULL_NONE : updown; -} -#endif /* CONFIG_S3C_GPIO_PULL_UP || CONFIG_S3C_GPIO_PULL_DOWN */ - -#ifdef CONFIG_S3C_GPIO_PULL_UP -s3c_gpio_pull_t s3c_gpio_getpull_1up(struct s3c_gpio_chip *chip, - unsigned int off) -{ - return s3c_gpio_getpull_1(chip, off, S3C_GPIO_PULL_UP); -} - -int s3c_gpio_setpull_1up(struct s3c_gpio_chip *chip, - unsigned int off, s3c_gpio_pull_t pull) -{ - return s3c_gpio_setpull_1(chip, off, pull, S3C_GPIO_PULL_UP); -} -#endif /* CONFIG_S3C_GPIO_PULL_UP */ - -#ifdef CONFIG_S3C_GPIO_PULL_DOWN -s3c_gpio_pull_t s3c_gpio_getpull_1down(struct s3c_gpio_chip *chip, - unsigned int off) -{ - return s3c_gpio_getpull_1(chip, off, S3C_GPIO_PULL_DOWN); -} - -int s3c_gpio_setpull_1down(struct s3c_gpio_chip *chip, - unsigned int off, s3c_gpio_pull_t pull) -{ - return s3c_gpio_setpull_1(chip, off, pull, S3C_GPIO_PULL_DOWN); -} -#endif /* CONFIG_S3C_GPIO_PULL_DOWN */ - -#ifdef CONFIG_S5P_GPIO_DRVSTR -s5p_gpio_drvstr_t s5p_gpio_get_drvstr(unsigned int pin) -{ - struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin); - unsigned int off; - void __iomem *reg; - int shift; - u32 drvstr; - - if (!chip) - return -EINVAL; - - off = pin - chip->chip.base; - shift = off * 2; - reg = chip->base + 0x0C; - - drvstr = __raw_readl(reg); - drvstr = drvstr >> shift; - drvstr &= 0x3; - - return (__force s5p_gpio_drvstr_t)drvstr; -} -EXPORT_SYMBOL(s5p_gpio_get_drvstr); - -int s5p_gpio_set_drvstr(unsigned int pin, s5p_gpio_drvstr_t drvstr) -{ - struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin); - unsigned int off; - void __iomem *reg; - int shift; - u32 tmp; - - if (!chip) - return -EINVAL; - - off = pin - chip->chip.base; - shift = off * 2; - reg = chip->base + 0x0C; - - tmp = __raw_readl(reg); - tmp &= ~(0x3 << shift); - tmp |= drvstr << shift; - - __raw_writel(tmp, reg); - - return 0; -} -EXPORT_SYMBOL(s5p_gpio_set_drvstr); -#endif /* CONFIG_S5P_GPIO_DRVSTR */ diff --git a/arch/arm/plat-samsung/gpio.c b/arch/arm/plat-samsung/gpio.c deleted file mode 100644 index 7743c4b8b2fb..000000000000 --- a/arch/arm/plat-samsung/gpio.c +++ /dev/null @@ -1,167 +0,0 @@ -/* linux/arch/arm/plat-s3c/gpio.c - * - * Copyright 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * S3C series GPIO core - * - * 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 - -#ifdef CONFIG_S3C_GPIO_TRACK -struct s3c_gpio_chip *s3c_gpios[S3C_GPIO_END]; - -static __init void s3c_gpiolib_track(struct s3c_gpio_chip *chip) -{ - unsigned int gpn; - int i; - - gpn = chip->chip.base; - for (i = 0; i < chip->chip.ngpio; i++, gpn++) { - BUG_ON(gpn >= ARRAY_SIZE(s3c_gpios)); - s3c_gpios[gpn] = chip; - } -} -#endif /* CONFIG_S3C_GPIO_TRACK */ - -/* Default routines for controlling GPIO, based on the original S3C24XX - * GPIO functions which deal with the case where each gpio bank of the - * chip is as following: - * - * base + 0x00: Control register, 2 bits per gpio - * gpio n: 2 bits starting at (2*n) - * 00 = input, 01 = output, others mean special-function - * base + 0x04: Data register, 1 bit per gpio - * bit n: data bit n -*/ - -static int s3c_gpiolib_input(struct gpio_chip *chip, unsigned offset) -{ - struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); - void __iomem *base = ourchip->base; - unsigned long flags; - unsigned long con; - - s3c_gpio_lock(ourchip, flags); - - con = __raw_readl(base + 0x00); - con &= ~(3 << (offset * 2)); - - __raw_writel(con, base + 0x00); - - s3c_gpio_unlock(ourchip, flags); - return 0; -} - -static int s3c_gpiolib_output(struct gpio_chip *chip, - unsigned offset, int value) -{ - struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); - void __iomem *base = ourchip->base; - unsigned long flags; - unsigned long dat; - unsigned long con; - - s3c_gpio_lock(ourchip, flags); - - dat = __raw_readl(base + 0x04); - dat &= ~(1 << offset); - if (value) - dat |= 1 << offset; - __raw_writel(dat, base + 0x04); - - con = __raw_readl(base + 0x00); - con &= ~(3 << (offset * 2)); - con |= 1 << (offset * 2); - - __raw_writel(con, base + 0x00); - __raw_writel(dat, base + 0x04); - - s3c_gpio_unlock(ourchip, flags); - return 0; -} - -static void s3c_gpiolib_set(struct gpio_chip *chip, - unsigned offset, int value) -{ - struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); - void __iomem *base = ourchip->base; - unsigned long flags; - unsigned long dat; - - s3c_gpio_lock(ourchip, flags); - - dat = __raw_readl(base + 0x04); - dat &= ~(1 << offset); - if (value) - dat |= 1 << offset; - __raw_writel(dat, base + 0x04); - - s3c_gpio_unlock(ourchip, flags); -} - -static int s3c_gpiolib_get(struct gpio_chip *chip, unsigned offset) -{ - struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); - unsigned long val; - - val = __raw_readl(ourchip->base + 0x04); - val >>= offset; - val &= 1; - - return val; -} - -__init void s3c_gpiolib_add(struct s3c_gpio_chip *chip) -{ - struct gpio_chip *gc = &chip->chip; - int ret; - - BUG_ON(!chip->base); - BUG_ON(!gc->label); - BUG_ON(!gc->ngpio); - - spin_lock_init(&chip->lock); - - if (!gc->direction_input) - gc->direction_input = s3c_gpiolib_input; - if (!gc->direction_output) - gc->direction_output = s3c_gpiolib_output; - if (!gc->set) - gc->set = s3c_gpiolib_set; - if (!gc->get) - gc->get = s3c_gpiolib_get; - -#ifdef CONFIG_PM - if (chip->pm != NULL) { - if (!chip->pm->save || !chip->pm->resume) - printk(KERN_ERR "gpio: %s has missing PM functions\n", - gc->label); - } else - printk(KERN_ERR "gpio: %s has no PM function\n", gc->label); -#endif - - /* gpiochip_add() prints own failure message on error. */ - ret = gpiochip_add(gc); - if (ret >= 0) - s3c_gpiolib_track(chip); -} - -int samsung_gpiolib_to_irq(struct gpio_chip *chip, unsigned int offset) -{ - struct s3c_gpio_chip *s3c_chip = container_of(chip, - struct s3c_gpio_chip, chip); - - return s3c_chip->irq_base + offset; -} diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 5eb29bc18a56..ca44d2cceb02 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -95,10 +95,6 @@ config GPIO_EP93XX depends on ARCH_EP93XX select GPIO_GENERIC -config GPIO_EXYNOS4 - def_bool y - depends on CPU_EXYNOS4210 - config GPIO_MPC5200 def_bool y depends on PPC_MPC52xx @@ -131,30 +127,6 @@ config GPIO_MXS select GPIO_GENERIC select GENERIC_IRQ_CHIP -config GPIO_PLAT_SAMSUNG - def_bool y - depends on SAMSUNG_GPIOLIB_4BIT - -config GPIO_S3C24XX - def_bool y - depends on PLAT_S3C24XX - -config GPIO_S3C64XX - def_bool y - depends on ARCH_S3C64XX - -config GPIO_S5P64X0 - def_bool y - depends on ARCH_S5P64X0 - -config GPIO_S5PC100 - def_bool y - depends on CPU_S5PC100 - -config GPIO_S5PV210 - def_bool y - depends on CPU_S5PV210 - config GPIO_PL061 bool "PrimeCell PL061 GPIO support" depends on ARM_AMBA diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 10f3bbf0eceb..62db458c850d 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -15,7 +15,6 @@ obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.o obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o -obj-$(CONFIG_GPIO_EXYNOS4) += gpio-exynos4.o obj-$(CONFIG_GPIO_IT8761E) += gpio-it8761e.o obj-$(CONFIG_GPIO_JANZ_TTL) += gpio-janz-ttl.o obj-$(CONFIG_GPIO_LANGWELL) += gpio-langwell.o @@ -38,14 +37,7 @@ obj-$(CONFIG_GPIO_PCF857X) += gpio-pcf857x.o obj-$(CONFIG_GPIO_PCH) += gpio-pch.o obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o - -obj-$(CONFIG_GPIO_PLAT_SAMSUNG) += gpio-plat-samsung.o -obj-$(CONFIG_GPIO_S3C24XX) += gpio-s3c24xx.o -obj-$(CONFIG_GPIO_S3C64XX) += gpio-s3c64xx.o -obj-$(CONFIG_GPIO_S5P64X0) += gpio-s5p64x0.o -obj-$(CONFIG_GPIO_S5PC100) += gpio-s5pc100.o -obj-$(CONFIG_GPIO_S5PV210) += gpio-s5pv210.o - +obj-$(CONFIG_PLAT_SAMSUNG) += gpio-samsung.o obj-$(CONFIG_GPIO_SCH) += gpio-sch.o obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o obj-$(CONFIG_GPIO_SX150X) += gpio-sx150x.o diff --git a/drivers/gpio/gpio-exynos4.c b/drivers/gpio/gpio-exynos4.c deleted file mode 100644 index d24b337cf1ac..000000000000 --- a/drivers/gpio/gpio-exynos4.c +++ /dev/null @@ -1,385 +0,0 @@ -/* - * EXYNOS4 - GPIOlib support - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * 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 - -int s3c_gpio_setpull_exynos4(struct s3c_gpio_chip *chip, - unsigned int off, s3c_gpio_pull_t pull) -{ - if (pull == S3C_GPIO_PULL_UP) - pull = 3; - - return s3c_gpio_setpull_updown(chip, off, pull); -} - -s3c_gpio_pull_t s3c_gpio_getpull_exynos4(struct s3c_gpio_chip *chip, - unsigned int off) -{ - s3c_gpio_pull_t pull; - - pull = s3c_gpio_getpull_updown(chip, off); - if (pull == 3) - pull = S3C_GPIO_PULL_UP; - - return pull; -} - -static struct s3c_gpio_cfg gpio_cfg = { - .set_config = s3c_gpio_setcfg_s3c64xx_4bit, - .set_pull = s3c_gpio_setpull_exynos4, - .get_pull = s3c_gpio_getpull_exynos4, -}; - -static struct s3c_gpio_cfg gpio_cfg_noint = { - .set_config = s3c_gpio_setcfg_s3c64xx_4bit, - .set_pull = s3c_gpio_setpull_exynos4, - .get_pull = s3c_gpio_getpull_exynos4, -}; - -/* - * Following are the gpio banks in v310. - * - * The 'config' member when left to NULL, is initialized to the default - * structure gpio_cfg in the init function below. - * - * The 'base' member is also initialized in the init function below. - * Note: The initialization of 'base' member of s3c_gpio_chip structure - * uses the above macro and depends on the banks being listed in order here. - */ -static struct s3c_gpio_chip exynos4_gpio_part1_4bit[] = { - { - .chip = { - .base = EXYNOS4_GPA0(0), - .ngpio = EXYNOS4_GPIO_A0_NR, - .label = "GPA0", - }, - }, { - .chip = { - .base = EXYNOS4_GPA1(0), - .ngpio = EXYNOS4_GPIO_A1_NR, - .label = "GPA1", - }, - }, { - .chip = { - .base = EXYNOS4_GPB(0), - .ngpio = EXYNOS4_GPIO_B_NR, - .label = "GPB", - }, - }, { - .chip = { - .base = EXYNOS4_GPC0(0), - .ngpio = EXYNOS4_GPIO_C0_NR, - .label = "GPC0", - }, - }, { - .chip = { - .base = EXYNOS4_GPC1(0), - .ngpio = EXYNOS4_GPIO_C1_NR, - .label = "GPC1", - }, - }, { - .chip = { - .base = EXYNOS4_GPD0(0), - .ngpio = EXYNOS4_GPIO_D0_NR, - .label = "GPD0", - }, - }, { - .chip = { - .base = EXYNOS4_GPD1(0), - .ngpio = EXYNOS4_GPIO_D1_NR, - .label = "GPD1", - }, - }, { - .chip = { - .base = EXYNOS4_GPE0(0), - .ngpio = EXYNOS4_GPIO_E0_NR, - .label = "GPE0", - }, - }, { - .chip = { - .base = EXYNOS4_GPE1(0), - .ngpio = EXYNOS4_GPIO_E1_NR, - .label = "GPE1", - }, - }, { - .chip = { - .base = EXYNOS4_GPE2(0), - .ngpio = EXYNOS4_GPIO_E2_NR, - .label = "GPE2", - }, - }, { - .chip = { - .base = EXYNOS4_GPE3(0), - .ngpio = EXYNOS4_GPIO_E3_NR, - .label = "GPE3", - }, - }, { - .chip = { - .base = EXYNOS4_GPE4(0), - .ngpio = EXYNOS4_GPIO_E4_NR, - .label = "GPE4", - }, - }, { - .chip = { - .base = EXYNOS4_GPF0(0), - .ngpio = EXYNOS4_GPIO_F0_NR, - .label = "GPF0", - }, - }, { - .chip = { - .base = EXYNOS4_GPF1(0), - .ngpio = EXYNOS4_GPIO_F1_NR, - .label = "GPF1", - }, - }, { - .chip = { - .base = EXYNOS4_GPF2(0), - .ngpio = EXYNOS4_GPIO_F2_NR, - .label = "GPF2", - }, - }, { - .chip = { - .base = EXYNOS4_GPF3(0), - .ngpio = EXYNOS4_GPIO_F3_NR, - .label = "GPF3", - }, - }, -}; - -static struct s3c_gpio_chip exynos4_gpio_part2_4bit[] = { - { - .chip = { - .base = EXYNOS4_GPJ0(0), - .ngpio = EXYNOS4_GPIO_J0_NR, - .label = "GPJ0", - }, - }, { - .chip = { - .base = EXYNOS4_GPJ1(0), - .ngpio = EXYNOS4_GPIO_J1_NR, - .label = "GPJ1", - }, - }, { - .chip = { - .base = EXYNOS4_GPK0(0), - .ngpio = EXYNOS4_GPIO_K0_NR, - .label = "GPK0", - }, - }, { - .chip = { - .base = EXYNOS4_GPK1(0), - .ngpio = EXYNOS4_GPIO_K1_NR, - .label = "GPK1", - }, - }, { - .chip = { - .base = EXYNOS4_GPK2(0), - .ngpio = EXYNOS4_GPIO_K2_NR, - .label = "GPK2", - }, - }, { - .chip = { - .base = EXYNOS4_GPK3(0), - .ngpio = EXYNOS4_GPIO_K3_NR, - .label = "GPK3", - }, - }, { - .chip = { - .base = EXYNOS4_GPL0(0), - .ngpio = EXYNOS4_GPIO_L0_NR, - .label = "GPL0", - }, - }, { - .chip = { - .base = EXYNOS4_GPL1(0), - .ngpio = EXYNOS4_GPIO_L1_NR, - .label = "GPL1", - }, - }, { - .chip = { - .base = EXYNOS4_GPL2(0), - .ngpio = EXYNOS4_GPIO_L2_NR, - .label = "GPL2", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = EXYNOS4_GPY0(0), - .ngpio = EXYNOS4_GPIO_Y0_NR, - .label = "GPY0", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = EXYNOS4_GPY1(0), - .ngpio = EXYNOS4_GPIO_Y1_NR, - .label = "GPY1", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = EXYNOS4_GPY2(0), - .ngpio = EXYNOS4_GPIO_Y2_NR, - .label = "GPY2", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = EXYNOS4_GPY3(0), - .ngpio = EXYNOS4_GPIO_Y3_NR, - .label = "GPY3", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = EXYNOS4_GPY4(0), - .ngpio = EXYNOS4_GPIO_Y4_NR, - .label = "GPY4", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = EXYNOS4_GPY5(0), - .ngpio = EXYNOS4_GPIO_Y5_NR, - .label = "GPY5", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = EXYNOS4_GPY6(0), - .ngpio = EXYNOS4_GPIO_Y6_NR, - .label = "GPY6", - }, - }, { - .base = (S5P_VA_GPIO2 + 0xC00), - .config = &gpio_cfg_noint, - .irq_base = IRQ_EINT(0), - .chip = { - .base = EXYNOS4_GPX0(0), - .ngpio = EXYNOS4_GPIO_X0_NR, - .label = "GPX0", - .to_irq = samsung_gpiolib_to_irq, - }, - }, { - .base = (S5P_VA_GPIO2 + 0xC20), - .config = &gpio_cfg_noint, - .irq_base = IRQ_EINT(8), - .chip = { - .base = EXYNOS4_GPX1(0), - .ngpio = EXYNOS4_GPIO_X1_NR, - .label = "GPX1", - .to_irq = samsung_gpiolib_to_irq, - }, - }, { - .base = (S5P_VA_GPIO2 + 0xC40), - .config = &gpio_cfg_noint, - .irq_base = IRQ_EINT(16), - .chip = { - .base = EXYNOS4_GPX2(0), - .ngpio = EXYNOS4_GPIO_X2_NR, - .label = "GPX2", - .to_irq = samsung_gpiolib_to_irq, - }, - }, { - .base = (S5P_VA_GPIO2 + 0xC60), - .config = &gpio_cfg_noint, - .irq_base = IRQ_EINT(24), - .chip = { - .base = EXYNOS4_GPX3(0), - .ngpio = EXYNOS4_GPIO_X3_NR, - .label = "GPX3", - .to_irq = samsung_gpiolib_to_irq, - }, - }, -}; - -static struct s3c_gpio_chip exynos4_gpio_part3_4bit[] = { - { - .chip = { - .base = EXYNOS4_GPZ(0), - .ngpio = EXYNOS4_GPIO_Z_NR, - .label = "GPZ", - }, - }, -}; - -static __init int exynos4_gpiolib_init(void) -{ - struct s3c_gpio_chip *chip; - int i; - int group = 0; - int nr_chips; - - /* GPIO part 1 */ - - chip = exynos4_gpio_part1_4bit; - nr_chips = ARRAY_SIZE(exynos4_gpio_part1_4bit); - - for (i = 0; i < nr_chips; i++, chip++) { - if (chip->config == NULL) { - chip->config = &gpio_cfg; - /* Assign the GPIO interrupt group */ - chip->group = group++; - } - if (chip->base == NULL) - chip->base = S5P_VA_GPIO1 + (i) * 0x20; - } - - samsung_gpiolib_add_4bit_chips(exynos4_gpio_part1_4bit, nr_chips); - - /* GPIO part 2 */ - - chip = exynos4_gpio_part2_4bit; - nr_chips = ARRAY_SIZE(exynos4_gpio_part2_4bit); - - for (i = 0; i < nr_chips; i++, chip++) { - if (chip->config == NULL) { - chip->config = &gpio_cfg; - /* Assign the GPIO interrupt group */ - chip->group = group++; - } - if (chip->base == NULL) - chip->base = S5P_VA_GPIO2 + (i) * 0x20; - } - - samsung_gpiolib_add_4bit_chips(exynos4_gpio_part2_4bit, nr_chips); - - /* GPIO part 3 */ - - chip = exynos4_gpio_part3_4bit; - nr_chips = ARRAY_SIZE(exynos4_gpio_part3_4bit); - - for (i = 0; i < nr_chips; i++, chip++) { - if (chip->config == NULL) { - chip->config = &gpio_cfg; - /* Assign the GPIO interrupt group */ - chip->group = group++; - } - if (chip->base == NULL) - chip->base = S5P_VA_GPIO3 + (i) * 0x20; - } - - samsung_gpiolib_add_4bit_chips(exynos4_gpio_part3_4bit, nr_chips); - s5p_register_gpioint_bank(IRQ_GPIO_XA, 0, IRQ_GPIO1_NR_GROUPS); - s5p_register_gpioint_bank(IRQ_GPIO_XB, IRQ_GPIO1_NR_GROUPS, IRQ_GPIO2_NR_GROUPS); - - return 0; -} -core_initcall(exynos4_gpiolib_init); diff --git a/drivers/gpio/gpio-plat-samsung.c b/drivers/gpio/gpio-plat-samsung.c deleted file mode 100644 index ef67f1952a72..000000000000 --- a/drivers/gpio/gpio-plat-samsung.c +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * Copyright (c) 2009 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * SAMSUNG - GPIOlib support - * - * 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 - -#ifndef DEBUG_GPIO -#define gpio_dbg(x...) do { } while (0) -#else -#define gpio_dbg(x...) printk(KERN_DEBUG x) -#endif - -/* The samsung_gpiolib_4bit routines are to control the gpio banks where - * the gpio configuration register (GPxCON) has 4 bits per GPIO, as the - * following example: - * - * base + 0x00: Control register, 4 bits per gpio - * gpio n: 4 bits starting at (4*n) - * 0000 = input, 0001 = output, others mean special-function - * base + 0x04: Data register, 1 bit per gpio - * bit n: data bit n - * - * Note, since the data register is one bit per gpio and is at base + 0x4 - * we can use s3c_gpiolib_get and s3c_gpiolib_set to change the state of - * the output. -*/ - -static int samsung_gpiolib_4bit_input(struct gpio_chip *chip, - unsigned int offset) -{ - struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); - void __iomem *base = ourchip->base; - unsigned long con; - - con = __raw_readl(base + GPIOCON_OFF); - con &= ~(0xf << con_4bit_shift(offset)); - __raw_writel(con, base + GPIOCON_OFF); - - gpio_dbg("%s: %p: CON now %08lx\n", __func__, base, con); - - return 0; -} - -static int samsung_gpiolib_4bit_output(struct gpio_chip *chip, - unsigned int offset, int value) -{ - struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); - void __iomem *base = ourchip->base; - unsigned long con; - unsigned long dat; - - con = __raw_readl(base + GPIOCON_OFF); - con &= ~(0xf << con_4bit_shift(offset)); - con |= 0x1 << con_4bit_shift(offset); - - dat = __raw_readl(base + GPIODAT_OFF); - - if (value) - dat |= 1 << offset; - else - dat &= ~(1 << offset); - - __raw_writel(dat, base + GPIODAT_OFF); - __raw_writel(con, base + GPIOCON_OFF); - __raw_writel(dat, base + GPIODAT_OFF); - - gpio_dbg("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat); - - return 0; -} - -/* The next set of routines are for the case where the GPIO configuration - * registers are 4 bits per GPIO but there is more than one register (the - * bank has more than 8 GPIOs. - * - * This case is the similar to the 4 bit case, but the registers are as - * follows: - * - * base + 0x00: Control register, 4 bits per gpio (lower 8 GPIOs) - * gpio n: 4 bits starting at (4*n) - * 0000 = input, 0001 = output, others mean special-function - * base + 0x04: Control register, 4 bits per gpio (up to 8 additions GPIOs) - * gpio n: 4 bits starting at (4*n) - * 0000 = input, 0001 = output, others mean special-function - * base + 0x08: Data register, 1 bit per gpio - * bit n: data bit n - * - * To allow us to use the s3c_gpiolib_get and s3c_gpiolib_set routines we - * store the 'base + 0x4' address so that these routines see the data - * register at ourchip->base + 0x04. - */ - -static int samsung_gpiolib_4bit2_input(struct gpio_chip *chip, - unsigned int offset) -{ - struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); - void __iomem *base = ourchip->base; - void __iomem *regcon = base; - unsigned long con; - - if (offset > 7) - offset -= 8; - else - regcon -= 4; - - con = __raw_readl(regcon); - con &= ~(0xf << con_4bit_shift(offset)); - __raw_writel(con, regcon); - - gpio_dbg("%s: %p: CON %08lx\n", __func__, base, con); - - return 0; -} - -static int samsung_gpiolib_4bit2_output(struct gpio_chip *chip, - unsigned int offset, int value) -{ - struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); - void __iomem *base = ourchip->base; - void __iomem *regcon = base; - unsigned long con; - unsigned long dat; - unsigned con_offset = offset; - - if (con_offset > 7) - con_offset -= 8; - else - regcon -= 4; - - con = __raw_readl(regcon); - con &= ~(0xf << con_4bit_shift(con_offset)); - con |= 0x1 << con_4bit_shift(con_offset); - - dat = __raw_readl(base + GPIODAT_OFF); - - if (value) - dat |= 1 << offset; - else - dat &= ~(1 << offset); - - __raw_writel(dat, base + GPIODAT_OFF); - __raw_writel(con, regcon); - __raw_writel(dat, base + GPIODAT_OFF); - - gpio_dbg("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat); - - return 0; -} - -void __init samsung_gpiolib_add_4bit(struct s3c_gpio_chip *chip) -{ - chip->chip.direction_input = samsung_gpiolib_4bit_input; - chip->chip.direction_output = samsung_gpiolib_4bit_output; - chip->pm = __gpio_pm(&s3c_gpio_pm_4bit); -} - -void __init samsung_gpiolib_add_4bit2(struct s3c_gpio_chip *chip) -{ - chip->chip.direction_input = samsung_gpiolib_4bit2_input; - chip->chip.direction_output = samsung_gpiolib_4bit2_output; - chip->pm = __gpio_pm(&s3c_gpio_pm_4bit); -} - -void __init samsung_gpiolib_add_4bit_chips(struct s3c_gpio_chip *chip, - int nr_chips) -{ - for (; nr_chips > 0; nr_chips--, chip++) { - samsung_gpiolib_add_4bit(chip); - s3c_gpiolib_add(chip); - } -} - -void __init samsung_gpiolib_add_4bit2_chips(struct s3c_gpio_chip *chip, - int nr_chips) -{ - for (; nr_chips > 0; nr_chips--, chip++) { - samsung_gpiolib_add_4bit2(chip); - s3c_gpiolib_add(chip); - } -} - -void __init samsung_gpiolib_add_2bit_chips(struct s3c_gpio_chip *chip, - int nr_chips) -{ - for (; nr_chips > 0; nr_chips--, chip++) - s3c_gpiolib_add(chip); -} diff --git a/drivers/gpio/gpio-s3c24xx.c b/drivers/gpio/gpio-s3c24xx.c deleted file mode 100644 index ff6103130fe8..000000000000 --- a/drivers/gpio/gpio-s3c24xx.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright (c) 2008-2010 Simtec Electronics - * http://armlinux.simtec.co.uk/ - * Ben Dooks - * - * S3C24XX GPIOlib support - * - * 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 -#include -#include - -static int s3c24xx_gpiolib_banka_input(struct gpio_chip *chip, unsigned offset) -{ - return -EINVAL; -} - -static int s3c24xx_gpiolib_banka_output(struct gpio_chip *chip, - unsigned offset, int value) -{ - struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); - void __iomem *base = ourchip->base; - unsigned long flags; - unsigned long dat; - unsigned long con; - - local_irq_save(flags); - - con = __raw_readl(base + 0x00); - dat = __raw_readl(base + 0x04); - - dat &= ~(1 << offset); - if (value) - dat |= 1 << offset; - - __raw_writel(dat, base + 0x04); - - con &= ~(1 << offset); - - __raw_writel(con, base + 0x00); - __raw_writel(dat, base + 0x04); - - local_irq_restore(flags); - return 0; -} - -static int s3c24xx_gpiolib_bankf_toirq(struct gpio_chip *chip, unsigned offset) -{ - if (offset < 4) - return IRQ_EINT0 + offset; - - if (offset < 8) - return IRQ_EINT4 + offset - 4; - - return -EINVAL; -} - -static struct s3c_gpio_cfg s3c24xx_gpiocfg_banka = { - .set_config = s3c_gpio_setcfg_s3c24xx_a, - .get_config = s3c_gpio_getcfg_s3c24xx_a, -}; - -struct s3c_gpio_cfg s3c24xx_gpiocfg_default = { - .set_config = s3c_gpio_setcfg_s3c24xx, - .get_config = s3c_gpio_getcfg_s3c24xx, -}; - -struct s3c_gpio_chip s3c24xx_gpios[] = { - [0] = { - .base = S3C2410_GPACON, - .pm = __gpio_pm(&s3c_gpio_pm_1bit), - .config = &s3c24xx_gpiocfg_banka, - .chip = { - .base = S3C2410_GPA(0), - .owner = THIS_MODULE, - .label = "GPIOA", - .ngpio = 24, - .direction_input = s3c24xx_gpiolib_banka_input, - .direction_output = s3c24xx_gpiolib_banka_output, - }, - }, - [1] = { - .base = S3C2410_GPBCON, - .pm = __gpio_pm(&s3c_gpio_pm_2bit), - .chip = { - .base = S3C2410_GPB(0), - .owner = THIS_MODULE, - .label = "GPIOB", - .ngpio = 16, - }, - }, - [2] = { - .base = S3C2410_GPCCON, - .pm = __gpio_pm(&s3c_gpio_pm_2bit), - .chip = { - .base = S3C2410_GPC(0), - .owner = THIS_MODULE, - .label = "GPIOC", - .ngpio = 16, - }, - }, - [3] = { - .base = S3C2410_GPDCON, - .pm = __gpio_pm(&s3c_gpio_pm_2bit), - .chip = { - .base = S3C2410_GPD(0), - .owner = THIS_MODULE, - .label = "GPIOD", - .ngpio = 16, - }, - }, - [4] = { - .base = S3C2410_GPECON, - .pm = __gpio_pm(&s3c_gpio_pm_2bit), - .chip = { - .base = S3C2410_GPE(0), - .label = "GPIOE", - .owner = THIS_MODULE, - .ngpio = 16, - }, - }, - [5] = { - .base = S3C2410_GPFCON, - .pm = __gpio_pm(&s3c_gpio_pm_2bit), - .chip = { - .base = S3C2410_GPF(0), - .owner = THIS_MODULE, - .label = "GPIOF", - .ngpio = 8, - .to_irq = s3c24xx_gpiolib_bankf_toirq, - }, - }, - [6] = { - .base = S3C2410_GPGCON, - .pm = __gpio_pm(&s3c_gpio_pm_2bit), - .irq_base = IRQ_EINT8, - .chip = { - .base = S3C2410_GPG(0), - .owner = THIS_MODULE, - .label = "GPIOG", - .ngpio = 16, - .to_irq = samsung_gpiolib_to_irq, - }, - }, { - .base = S3C2410_GPHCON, - .pm = __gpio_pm(&s3c_gpio_pm_2bit), - .chip = { - .base = S3C2410_GPH(0), - .owner = THIS_MODULE, - .label = "GPIOH", - .ngpio = 11, - }, - }, - /* GPIOS for the S3C2443 and later devices. */ - { - .base = S3C2440_GPJCON, - .pm = __gpio_pm(&s3c_gpio_pm_2bit), - .chip = { - .base = S3C2410_GPJ(0), - .owner = THIS_MODULE, - .label = "GPIOJ", - .ngpio = 16, - }, - }, { - .base = S3C2443_GPKCON, - .pm = __gpio_pm(&s3c_gpio_pm_2bit), - .chip = { - .base = S3C2410_GPK(0), - .owner = THIS_MODULE, - .label = "GPIOK", - .ngpio = 16, - }, - }, { - .base = S3C2443_GPLCON, - .pm = __gpio_pm(&s3c_gpio_pm_2bit), - .chip = { - .base = S3C2410_GPL(0), - .owner = THIS_MODULE, - .label = "GPIOL", - .ngpio = 15, - }, - }, { - .base = S3C2443_GPMCON, - .pm = __gpio_pm(&s3c_gpio_pm_2bit), - .chip = { - .base = S3C2410_GPM(0), - .owner = THIS_MODULE, - .label = "GPIOM", - .ngpio = 2, - }, - }, -}; - -static __init int s3c24xx_gpiolib_init(void) -{ - struct s3c_gpio_chip *chip = s3c24xx_gpios; - int gpn; - - for (gpn = 0; gpn < ARRAY_SIZE(s3c24xx_gpios); gpn++, chip++) { - if (!chip->config) - chip->config = &s3c24xx_gpiocfg_default; - - s3c_gpiolib_add(chip); - } - - return 0; -} -core_initcall(s3c24xx_gpiolib_init); - -/* gpiolib wrappers until these are totally eliminated */ - -void s3c2410_gpio_pullup(unsigned int pin, unsigned int to) -{ - int ret; - - WARN_ON(to); /* should be none of these left */ - - if (!to) { - /* if pull is enabled, try first with up, and if that - * fails, try using down */ - - ret = s3c_gpio_setpull(pin, S3C_GPIO_PULL_UP); - if (ret) - s3c_gpio_setpull(pin, S3C_GPIO_PULL_DOWN); - } else { - s3c_gpio_setpull(pin, S3C_GPIO_PULL_NONE); - } -} -EXPORT_SYMBOL(s3c2410_gpio_pullup); - -void s3c2410_gpio_setpin(unsigned int pin, unsigned int to) -{ - /* do this via gpiolib until all users removed */ - - gpio_request(pin, "temporary"); - gpio_set_value(pin, to); - gpio_free(pin); -} -EXPORT_SYMBOL(s3c2410_gpio_setpin); - -unsigned int s3c2410_gpio_getpin(unsigned int pin) -{ - struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin); - unsigned long offs = pin - chip->chip.base; - - return __raw_readl(chip->base + 0x04) & (1<< offs); -} -EXPORT_SYMBOL(s3c2410_gpio_getpin); - -unsigned int s3c2410_modify_misccr(unsigned int clear, unsigned int change) -{ - unsigned long flags; - unsigned long misccr; - - local_irq_save(flags); - misccr = __raw_readl(S3C24XX_MISCCR); - misccr &= ~clear; - misccr ^= change; - __raw_writel(misccr, S3C24XX_MISCCR); - local_irq_restore(flags); - - return misccr; -} -EXPORT_SYMBOL(s3c2410_modify_misccr); diff --git a/drivers/gpio/gpio-s3c64xx.c b/drivers/gpio/gpio-s3c64xx.c deleted file mode 100644 index b4f1c8204f03..000000000000 --- a/drivers/gpio/gpio-s3c64xx.c +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Copyright 2008 Openmoko, Inc. - * Copyright 2008 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * S3C64XX - GPIOlib support - * - * 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 - -/* GPIO bank summary: - * - * Bank GPIOs Style SlpCon ExtInt Group - * A 8 4Bit Yes 1 - * B 7 4Bit Yes 1 - * C 8 4Bit Yes 2 - * D 5 4Bit Yes 3 - * E 5 4Bit Yes None - * F 16 2Bit Yes 4 [1] - * G 7 4Bit Yes 5 - * H 10 4Bit[2] Yes 6 - * I 16 2Bit Yes None - * J 12 2Bit Yes None - * K 16 4Bit[2] No None - * L 15 4Bit[2] No None - * M 6 4Bit No IRQ_EINT - * N 16 2Bit No IRQ_EINT - * O 16 2Bit Yes 7 - * P 15 2Bit Yes 8 - * Q 9 2Bit Yes 9 - * - * [1] BANKF pins 14,15 do not form part of the external interrupt sources - * [2] BANK has two control registers, GPxCON0 and GPxCON1 - */ - -static struct s3c_gpio_cfg gpio_4bit_cfg_noint = { - .set_config = s3c_gpio_setcfg_s3c64xx_4bit, - .get_config = s3c_gpio_getcfg_s3c64xx_4bit, - .set_pull = s3c_gpio_setpull_updown, - .get_pull = s3c_gpio_getpull_updown, -}; - -static struct s3c_gpio_cfg gpio_4bit_cfg_eint0111 = { - .cfg_eint = 7, - .set_config = s3c_gpio_setcfg_s3c64xx_4bit, - .get_config = s3c_gpio_getcfg_s3c64xx_4bit, - .set_pull = s3c_gpio_setpull_updown, - .get_pull = s3c_gpio_getpull_updown, -}; - -static struct s3c_gpio_cfg gpio_4bit_cfg_eint0011 = { - .cfg_eint = 3, - .get_config = s3c_gpio_getcfg_s3c64xx_4bit, - .set_config = s3c_gpio_setcfg_s3c64xx_4bit, - .set_pull = s3c_gpio_setpull_updown, - .get_pull = s3c_gpio_getpull_updown, -}; - -static int s3c64xx_gpio2int_gpm(struct gpio_chip *chip, unsigned pin) -{ - return pin < 5 ? IRQ_EINT(23) + pin : -ENXIO; -} - -static struct s3c_gpio_chip gpio_4bit[] = { - { - .base = S3C64XX_GPA_BASE, - .config = &gpio_4bit_cfg_eint0111, - .chip = { - .base = S3C64XX_GPA(0), - .ngpio = S3C64XX_GPIO_A_NR, - .label = "GPA", - }, - }, { - .base = S3C64XX_GPB_BASE, - .config = &gpio_4bit_cfg_eint0111, - .chip = { - .base = S3C64XX_GPB(0), - .ngpio = S3C64XX_GPIO_B_NR, - .label = "GPB", - }, - }, { - .base = S3C64XX_GPC_BASE, - .config = &gpio_4bit_cfg_eint0111, - .chip = { - .base = S3C64XX_GPC(0), - .ngpio = S3C64XX_GPIO_C_NR, - .label = "GPC", - }, - }, { - .base = S3C64XX_GPD_BASE, - .config = &gpio_4bit_cfg_eint0111, - .chip = { - .base = S3C64XX_GPD(0), - .ngpio = S3C64XX_GPIO_D_NR, - .label = "GPD", - }, - }, { - .base = S3C64XX_GPE_BASE, - .config = &gpio_4bit_cfg_noint, - .chip = { - .base = S3C64XX_GPE(0), - .ngpio = S3C64XX_GPIO_E_NR, - .label = "GPE", - }, - }, { - .base = S3C64XX_GPG_BASE, - .config = &gpio_4bit_cfg_eint0111, - .chip = { - .base = S3C64XX_GPG(0), - .ngpio = S3C64XX_GPIO_G_NR, - .label = "GPG", - }, - }, { - .base = S3C64XX_GPM_BASE, - .config = &gpio_4bit_cfg_eint0011, - .chip = { - .base = S3C64XX_GPM(0), - .ngpio = S3C64XX_GPIO_M_NR, - .label = "GPM", - .to_irq = s3c64xx_gpio2int_gpm, - }, - }, -}; - -static int s3c64xx_gpio2int_gpl(struct gpio_chip *chip, unsigned pin) -{ - return pin >= 8 ? IRQ_EINT(16) + pin - 8 : -ENXIO; -} - -static struct s3c_gpio_chip gpio_4bit2[] = { - { - .base = S3C64XX_GPH_BASE + 0x4, - .config = &gpio_4bit_cfg_eint0111, - .chip = { - .base = S3C64XX_GPH(0), - .ngpio = S3C64XX_GPIO_H_NR, - .label = "GPH", - }, - }, { - .base = S3C64XX_GPK_BASE + 0x4, - .config = &gpio_4bit_cfg_noint, - .chip = { - .base = S3C64XX_GPK(0), - .ngpio = S3C64XX_GPIO_K_NR, - .label = "GPK", - }, - }, { - .base = S3C64XX_GPL_BASE + 0x4, - .config = &gpio_4bit_cfg_eint0011, - .chip = { - .base = S3C64XX_GPL(0), - .ngpio = S3C64XX_GPIO_L_NR, - .label = "GPL", - .to_irq = s3c64xx_gpio2int_gpl, - }, - }, -}; - -static struct s3c_gpio_cfg gpio_2bit_cfg_noint = { - .set_config = s3c_gpio_setcfg_s3c24xx, - .get_config = s3c_gpio_getcfg_s3c24xx, - .set_pull = s3c_gpio_setpull_updown, - .get_pull = s3c_gpio_getpull_updown, -}; - -static struct s3c_gpio_cfg gpio_2bit_cfg_eint10 = { - .cfg_eint = 2, - .set_config = s3c_gpio_setcfg_s3c24xx, - .get_config = s3c_gpio_getcfg_s3c24xx, - .set_pull = s3c_gpio_setpull_updown, - .get_pull = s3c_gpio_getpull_updown, -}; - -static struct s3c_gpio_cfg gpio_2bit_cfg_eint11 = { - .cfg_eint = 3, - .set_config = s3c_gpio_setcfg_s3c24xx, - .get_config = s3c_gpio_getcfg_s3c24xx, - .set_pull = s3c_gpio_setpull_updown, - .get_pull = s3c_gpio_getpull_updown, -}; - -static struct s3c_gpio_chip gpio_2bit[] = { - { - .base = S3C64XX_GPF_BASE, - .config = &gpio_2bit_cfg_eint11, - .chip = { - .base = S3C64XX_GPF(0), - .ngpio = S3C64XX_GPIO_F_NR, - .label = "GPF", - }, - }, { - .base = S3C64XX_GPI_BASE, - .config = &gpio_2bit_cfg_noint, - .chip = { - .base = S3C64XX_GPI(0), - .ngpio = S3C64XX_GPIO_I_NR, - .label = "GPI", - }, - }, { - .base = S3C64XX_GPJ_BASE, - .config = &gpio_2bit_cfg_noint, - .chip = { - .base = S3C64XX_GPJ(0), - .ngpio = S3C64XX_GPIO_J_NR, - .label = "GPJ", - }, - }, { - .base = S3C64XX_GPN_BASE, - .irq_base = IRQ_EINT(0), - .config = &gpio_2bit_cfg_eint10, - .chip = { - .base = S3C64XX_GPN(0), - .ngpio = S3C64XX_GPIO_N_NR, - .label = "GPN", - .to_irq = samsung_gpiolib_to_irq, - }, - }, { - .base = S3C64XX_GPO_BASE, - .config = &gpio_2bit_cfg_eint11, - .chip = { - .base = S3C64XX_GPO(0), - .ngpio = S3C64XX_GPIO_O_NR, - .label = "GPO", - }, - }, { - .base = S3C64XX_GPP_BASE, - .config = &gpio_2bit_cfg_eint11, - .chip = { - .base = S3C64XX_GPP(0), - .ngpio = S3C64XX_GPIO_P_NR, - .label = "GPP", - }, - }, { - .base = S3C64XX_GPQ_BASE, - .config = &gpio_2bit_cfg_eint11, - .chip = { - .base = S3C64XX_GPQ(0), - .ngpio = S3C64XX_GPIO_Q_NR, - .label = "GPQ", - }, - }, -}; - -static __init void s3c64xx_gpiolib_add_2bit(struct s3c_gpio_chip *chip) -{ - chip->pm = __gpio_pm(&s3c_gpio_pm_2bit); -} - -static __init void s3c64xx_gpiolib_add(struct s3c_gpio_chip *chips, - int nr_chips, - void (*fn)(struct s3c_gpio_chip *)) -{ - for (; nr_chips > 0; nr_chips--, chips++) { - if (fn) - (fn)(chips); - s3c_gpiolib_add(chips); - } -} - -static __init int s3c64xx_gpiolib_init(void) -{ - s3c64xx_gpiolib_add(gpio_4bit, ARRAY_SIZE(gpio_4bit), - samsung_gpiolib_add_4bit); - - s3c64xx_gpiolib_add(gpio_4bit2, ARRAY_SIZE(gpio_4bit2), - samsung_gpiolib_add_4bit2); - - s3c64xx_gpiolib_add(gpio_2bit, ARRAY_SIZE(gpio_2bit), - s3c64xx_gpiolib_add_2bit); - - return 0; -} - -core_initcall(s3c64xx_gpiolib_init); diff --git a/drivers/gpio/gpio-s5p64x0.c b/drivers/gpio/gpio-s5p64x0.c deleted file mode 100644 index 96e816f5cc95..000000000000 --- a/drivers/gpio/gpio-s5p64x0.c +++ /dev/null @@ -1,510 +0,0 @@ -/* - * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * S5P64X0 - GPIOlib support - * - * 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 - -/* - * S5P6440 GPIO bank summary: - * - * Bank GPIOs Style SlpCon ExtInt Group - * A 6 4Bit Yes 1 - * B 7 4Bit Yes 1 - * C 8 4Bit Yes 2 - * F 2 2Bit Yes 4 [1] - * G 7 4Bit Yes 5 - * H 10 4Bit[2] Yes 6 - * I 16 2Bit Yes None - * J 12 2Bit Yes None - * N 16 2Bit No IRQ_EINT - * P 8 2Bit Yes 8 - * R 15 4Bit[2] Yes 8 - * - * S5P6450 GPIO bank summary: - * - * Bank GPIOs Style SlpCon ExtInt Group - * A 6 4Bit Yes 1 - * B 7 4Bit Yes 1 - * C 8 4Bit Yes 2 - * D 8 4Bit Yes None - * F 2 2Bit Yes None - * G 14 4Bit[2] Yes 5 - * H 10 4Bit[2] Yes 6 - * I 16 2Bit Yes None - * J 12 2Bit Yes None - * K 5 4Bit Yes None - * N 16 2Bit No IRQ_EINT - * P 11 2Bit Yes 8 - * Q 14 2Bit Yes None - * R 15 4Bit[2] Yes None - * S 8 2Bit Yes None - * - * [1] BANKF pins 14,15 do not form part of the external interrupt sources - * [2] BANK has two control registers, GPxCON0 and GPxCON1 - */ - -static int s5p64x0_gpiolib_rbank_4bit2_input(struct gpio_chip *chip, - unsigned int offset) -{ - struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); - void __iomem *base = ourchip->base; - void __iomem *regcon = base; - unsigned long con; - unsigned long flags; - - switch (offset) { - case 6: - offset += 1; - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - regcon -= 4; - break; - default: - offset -= 7; - break; - } - - s3c_gpio_lock(ourchip, flags); - - con = __raw_readl(regcon); - con &= ~(0xf << con_4bit_shift(offset)); - __raw_writel(con, regcon); - - s3c_gpio_unlock(ourchip, flags); - - return 0; -} - -static int s5p64x0_gpiolib_rbank_4bit2_output(struct gpio_chip *chip, - unsigned int offset, int value) -{ - struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip); - void __iomem *base = ourchip->base; - void __iomem *regcon = base; - unsigned long con; - unsigned long dat; - unsigned long flags; - unsigned con_offset = offset; - - switch (con_offset) { - case 6: - con_offset += 1; - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - regcon -= 4; - break; - default: - con_offset -= 7; - break; - } - - s3c_gpio_lock(ourchip, flags); - - con = __raw_readl(regcon); - con &= ~(0xf << con_4bit_shift(con_offset)); - con |= 0x1 << con_4bit_shift(con_offset); - - dat = __raw_readl(base + GPIODAT_OFF); - if (value) - dat |= 1 << offset; - else - dat &= ~(1 << offset); - - __raw_writel(con, regcon); - __raw_writel(dat, base + GPIODAT_OFF); - - s3c_gpio_unlock(ourchip, flags); - - return 0; -} - -int s5p64x0_gpio_setcfg_4bit_rbank(struct s3c_gpio_chip *chip, - unsigned int off, unsigned int cfg) -{ - void __iomem *reg = chip->base; - unsigned int shift; - u32 con; - - switch (off) { - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - shift = (off & 7) * 4; - reg -= 4; - break; - case 6: - shift = ((off + 1) & 7) * 4; - reg -= 4; - default: - shift = ((off + 1) & 7) * 4; - break; - } - - if (s3c_gpio_is_cfg_special(cfg)) { - cfg &= 0xf; - cfg <<= shift; - } - - con = __raw_readl(reg); - con &= ~(0xf << shift); - con |= cfg; - __raw_writel(con, reg); - - return 0; -} - -static struct s3c_gpio_cfg s5p64x0_gpio_cfgs[] = { - { - .cfg_eint = 0, - }, { - .cfg_eint = 7, - }, { - .cfg_eint = 3, - .set_config = s5p64x0_gpio_setcfg_4bit_rbank, - }, { - .cfg_eint = 0, - .set_config = s3c_gpio_setcfg_s3c24xx, - .get_config = s3c_gpio_getcfg_s3c24xx, - }, { - .cfg_eint = 2, - .set_config = s3c_gpio_setcfg_s3c24xx, - .get_config = s3c_gpio_getcfg_s3c24xx, - }, { - .cfg_eint = 3, - .set_config = s3c_gpio_setcfg_s3c24xx, - .get_config = s3c_gpio_getcfg_s3c24xx, - }, -}; - -static struct s3c_gpio_chip s5p6440_gpio_4bit[] = { - { - .base = S5P64X0_GPA_BASE, - .config = &s5p64x0_gpio_cfgs[1], - .chip = { - .base = S5P6440_GPA(0), - .ngpio = S5P6440_GPIO_A_NR, - .label = "GPA", - }, - }, { - .base = S5P64X0_GPB_BASE, - .config = &s5p64x0_gpio_cfgs[1], - .chip = { - .base = S5P6440_GPB(0), - .ngpio = S5P6440_GPIO_B_NR, - .label = "GPB", - }, - }, { - .base = S5P64X0_GPC_BASE, - .config = &s5p64x0_gpio_cfgs[1], - .chip = { - .base = S5P6440_GPC(0), - .ngpio = S5P6440_GPIO_C_NR, - .label = "GPC", - }, - }, { - .base = S5P64X0_GPG_BASE, - .config = &s5p64x0_gpio_cfgs[1], - .chip = { - .base = S5P6440_GPG(0), - .ngpio = S5P6440_GPIO_G_NR, - .label = "GPG", - }, - }, -}; - -static struct s3c_gpio_chip s5p6440_gpio_4bit2[] = { - { - .base = S5P64X0_GPH_BASE + 0x4, - .config = &s5p64x0_gpio_cfgs[1], - .chip = { - .base = S5P6440_GPH(0), - .ngpio = S5P6440_GPIO_H_NR, - .label = "GPH", - }, - }, -}; - -static struct s3c_gpio_chip s5p6440_gpio_rbank_4bit2[] = { - { - .base = S5P64X0_GPR_BASE + 0x4, - .config = &s5p64x0_gpio_cfgs[2], - .chip = { - .base = S5P6440_GPR(0), - .ngpio = S5P6440_GPIO_R_NR, - .label = "GPR", - }, - }, -}; - -static struct s3c_gpio_chip s5p6440_gpio_2bit[] = { - { - .base = S5P64X0_GPF_BASE, - .config = &s5p64x0_gpio_cfgs[5], - .chip = { - .base = S5P6440_GPF(0), - .ngpio = S5P6440_GPIO_F_NR, - .label = "GPF", - }, - }, { - .base = S5P64X0_GPI_BASE, - .config = &s5p64x0_gpio_cfgs[3], - .chip = { - .base = S5P6440_GPI(0), - .ngpio = S5P6440_GPIO_I_NR, - .label = "GPI", - }, - }, { - .base = S5P64X0_GPJ_BASE, - .config = &s5p64x0_gpio_cfgs[3], - .chip = { - .base = S5P6440_GPJ(0), - .ngpio = S5P6440_GPIO_J_NR, - .label = "GPJ", - }, - }, { - .base = S5P64X0_GPN_BASE, - .config = &s5p64x0_gpio_cfgs[4], - .chip = { - .base = S5P6440_GPN(0), - .ngpio = S5P6440_GPIO_N_NR, - .label = "GPN", - }, - }, { - .base = S5P64X0_GPP_BASE, - .config = &s5p64x0_gpio_cfgs[5], - .chip = { - .base = S5P6440_GPP(0), - .ngpio = S5P6440_GPIO_P_NR, - .label = "GPP", - }, - }, -}; - -static struct s3c_gpio_chip s5p6450_gpio_4bit[] = { - { - .base = S5P64X0_GPA_BASE, - .config = &s5p64x0_gpio_cfgs[1], - .chip = { - .base = S5P6450_GPA(0), - .ngpio = S5P6450_GPIO_A_NR, - .label = "GPA", - }, - }, { - .base = S5P64X0_GPB_BASE, - .config = &s5p64x0_gpio_cfgs[1], - .chip = { - .base = S5P6450_GPB(0), - .ngpio = S5P6450_GPIO_B_NR, - .label = "GPB", - }, - }, { - .base = S5P64X0_GPC_BASE, - .config = &s5p64x0_gpio_cfgs[1], - .chip = { - .base = S5P6450_GPC(0), - .ngpio = S5P6450_GPIO_C_NR, - .label = "GPC", - }, - }, { - .base = S5P6450_GPD_BASE, - .config = &s5p64x0_gpio_cfgs[1], - .chip = { - .base = S5P6450_GPD(0), - .ngpio = S5P6450_GPIO_D_NR, - .label = "GPD", - }, - }, { - .base = S5P6450_GPK_BASE, - .config = &s5p64x0_gpio_cfgs[1], - .chip = { - .base = S5P6450_GPK(0), - .ngpio = S5P6450_GPIO_K_NR, - .label = "GPK", - }, - }, -}; - -static struct s3c_gpio_chip s5p6450_gpio_4bit2[] = { - { - .base = S5P64X0_GPG_BASE + 0x4, - .config = &s5p64x0_gpio_cfgs[1], - .chip = { - .base = S5P6450_GPG(0), - .ngpio = S5P6450_GPIO_G_NR, - .label = "GPG", - }, - }, { - .base = S5P64X0_GPH_BASE + 0x4, - .config = &s5p64x0_gpio_cfgs[1], - .chip = { - .base = S5P6450_GPH(0), - .ngpio = S5P6450_GPIO_H_NR, - .label = "GPH", - }, - }, -}; - -static struct s3c_gpio_chip s5p6450_gpio_rbank_4bit2[] = { - { - .base = S5P64X0_GPR_BASE + 0x4, - .config = &s5p64x0_gpio_cfgs[2], - .chip = { - .base = S5P6450_GPR(0), - .ngpio = S5P6450_GPIO_R_NR, - .label = "GPR", - }, - }, -}; - -static struct s3c_gpio_chip s5p6450_gpio_2bit[] = { - { - .base = S5P64X0_GPF_BASE, - .config = &s5p64x0_gpio_cfgs[5], - .chip = { - .base = S5P6450_GPF(0), - .ngpio = S5P6450_GPIO_F_NR, - .label = "GPF", - }, - }, { - .base = S5P64X0_GPI_BASE, - .config = &s5p64x0_gpio_cfgs[3], - .chip = { - .base = S5P6450_GPI(0), - .ngpio = S5P6450_GPIO_I_NR, - .label = "GPI", - }, - }, { - .base = S5P64X0_GPJ_BASE, - .config = &s5p64x0_gpio_cfgs[3], - .chip = { - .base = S5P6450_GPJ(0), - .ngpio = S5P6450_GPIO_J_NR, - .label = "GPJ", - }, - }, { - .base = S5P64X0_GPN_BASE, - .config = &s5p64x0_gpio_cfgs[4], - .chip = { - .base = S5P6450_GPN(0), - .ngpio = S5P6450_GPIO_N_NR, - .label = "GPN", - }, - }, { - .base = S5P64X0_GPP_BASE, - .config = &s5p64x0_gpio_cfgs[5], - .chip = { - .base = S5P6450_GPP(0), - .ngpio = S5P6450_GPIO_P_NR, - .label = "GPP", - }, - }, { - .base = S5P6450_GPQ_BASE, - .config = &s5p64x0_gpio_cfgs[4], - .chip = { - .base = S5P6450_GPQ(0), - .ngpio = S5P6450_GPIO_Q_NR, - .label = "GPQ", - }, - }, { - .base = S5P6450_GPS_BASE, - .config = &s5p64x0_gpio_cfgs[5], - .chip = { - .base = S5P6450_GPS(0), - .ngpio = S5P6450_GPIO_S_NR, - .label = "GPS", - }, - }, -}; - -void __init s5p64x0_gpiolib_set_cfg(struct s3c_gpio_cfg *chipcfg, int nr_chips) -{ - for (; nr_chips > 0; nr_chips--, chipcfg++) { - if (!chipcfg->set_config) - chipcfg->set_config = s3c_gpio_setcfg_s3c64xx_4bit; - if (!chipcfg->get_config) - chipcfg->get_config = s3c_gpio_getcfg_s3c64xx_4bit; - if (!chipcfg->set_pull) - chipcfg->set_pull = s3c_gpio_setpull_updown; - if (!chipcfg->get_pull) - chipcfg->get_pull = s3c_gpio_getpull_updown; - } -} - -static void __init s5p64x0_gpio_add_rbank_4bit2(struct s3c_gpio_chip *chip, - int nr_chips) -{ - for (; nr_chips > 0; nr_chips--, chip++) { - chip->chip.direction_input = s5p64x0_gpiolib_rbank_4bit2_input; - chip->chip.direction_output = - s5p64x0_gpiolib_rbank_4bit2_output; - s3c_gpiolib_add(chip); - } -} - -static int __init s5p64x0_gpiolib_init(void) -{ - unsigned int chipid; - - chipid = __raw_readl(S5P64X0_SYS_ID); - - s5p64x0_gpiolib_set_cfg(s5p64x0_gpio_cfgs, - ARRAY_SIZE(s5p64x0_gpio_cfgs)); - - if ((chipid & 0xff000) == 0x50000) { - samsung_gpiolib_add_2bit_chips(s5p6450_gpio_2bit, - ARRAY_SIZE(s5p6450_gpio_2bit)); - - samsung_gpiolib_add_4bit_chips(s5p6450_gpio_4bit, - ARRAY_SIZE(s5p6450_gpio_4bit)); - - samsung_gpiolib_add_4bit2_chips(s5p6450_gpio_4bit2, - ARRAY_SIZE(s5p6450_gpio_4bit2)); - - s5p64x0_gpio_add_rbank_4bit2(s5p6450_gpio_rbank_4bit2, - ARRAY_SIZE(s5p6450_gpio_rbank_4bit2)); - } else { - samsung_gpiolib_add_2bit_chips(s5p6440_gpio_2bit, - ARRAY_SIZE(s5p6440_gpio_2bit)); - - samsung_gpiolib_add_4bit_chips(s5p6440_gpio_4bit, - ARRAY_SIZE(s5p6440_gpio_4bit)); - - samsung_gpiolib_add_4bit2_chips(s5p6440_gpio_4bit2, - ARRAY_SIZE(s5p6440_gpio_4bit2)); - - s5p64x0_gpio_add_rbank_4bit2(s5p6440_gpio_rbank_4bit2, - ARRAY_SIZE(s5p6440_gpio_rbank_4bit2)); - } - - return 0; -} -core_initcall(s5p64x0_gpiolib_init); diff --git a/drivers/gpio/gpio-s5pc100.c b/drivers/gpio/gpio-s5pc100.c deleted file mode 100644 index 7f87b0c76e0b..000000000000 --- a/drivers/gpio/gpio-s5pc100.c +++ /dev/null @@ -1,354 +0,0 @@ -/* - * S5PC100 - GPIOlib support - * - * Copyright (c) 2010 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Copyright 2009 Samsung Electronics Co - * Kyungmin Park - * - * 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 - -/* S5PC100 GPIO bank summary: - * - * Bank GPIOs Style INT Type - * A0 8 4Bit GPIO_INT0 - * A1 5 4Bit GPIO_INT1 - * B 8 4Bit GPIO_INT2 - * C 5 4Bit GPIO_INT3 - * D 7 4Bit GPIO_INT4 - * E0 8 4Bit GPIO_INT5 - * E1 6 4Bit GPIO_INT6 - * F0 8 4Bit GPIO_INT7 - * F1 8 4Bit GPIO_INT8 - * F2 8 4Bit GPIO_INT9 - * F3 4 4Bit GPIO_INT10 - * G0 8 4Bit GPIO_INT11 - * G1 3 4Bit GPIO_INT12 - * G2 7 4Bit GPIO_INT13 - * G3 7 4Bit GPIO_INT14 - * H0 8 4Bit WKUP_INT - * H1 8 4Bit WKUP_INT - * H2 8 4Bit WKUP_INT - * H3 8 4Bit WKUP_INT - * I 8 4Bit GPIO_INT15 - * J0 8 4Bit GPIO_INT16 - * J1 5 4Bit GPIO_INT17 - * J2 8 4Bit GPIO_INT18 - * J3 8 4Bit GPIO_INT19 - * J4 4 4Bit GPIO_INT20 - * K0 8 4Bit None - * K1 6 4Bit None - * K2 8 4Bit None - * K3 8 4Bit None - * L0 8 4Bit None - * L1 8 4Bit None - * L2 8 4Bit None - * L3 8 4Bit None - */ - -static struct s3c_gpio_cfg gpio_cfg = { - .set_config = s3c_gpio_setcfg_s3c64xx_4bit, - .set_pull = s3c_gpio_setpull_updown, - .get_pull = s3c_gpio_getpull_updown, -}; - -static struct s3c_gpio_cfg gpio_cfg_eint = { - .cfg_eint = 0xf, - .set_config = s3c_gpio_setcfg_s3c64xx_4bit, - .set_pull = s3c_gpio_setpull_updown, - .get_pull = s3c_gpio_getpull_updown, -}; - -static struct s3c_gpio_cfg gpio_cfg_noint = { - .set_config = s3c_gpio_setcfg_s3c64xx_4bit, - .set_pull = s3c_gpio_setpull_updown, - .get_pull = s3c_gpio_getpull_updown, -}; - -/* - * GPIO bank's base address given the index of the bank in the - * list of all gpio banks. - */ -#define S5PC100_BANK_BASE(bank_nr) (S5P_VA_GPIO + ((bank_nr) * 0x20)) - -/* - * Following are the gpio banks in S5PC100. - * - * The 'config' member when left to NULL, is initialized to the default - * structure gpio_cfg in the init function below. - * - * The 'base' member is also initialized in the init function below. - * Note: The initialization of 'base' member of s3c_gpio_chip structure - * uses the above macro and depends on the banks being listed in order here. - */ -static struct s3c_gpio_chip s5pc100_gpio_chips[] = { - { - .chip = { - .base = S5PC100_GPA0(0), - .ngpio = S5PC100_GPIO_A0_NR, - .label = "GPA0", - }, - }, { - .chip = { - .base = S5PC100_GPA1(0), - .ngpio = S5PC100_GPIO_A1_NR, - .label = "GPA1", - }, - }, { - .chip = { - .base = S5PC100_GPB(0), - .ngpio = S5PC100_GPIO_B_NR, - .label = "GPB", - }, - }, { - .chip = { - .base = S5PC100_GPC(0), - .ngpio = S5PC100_GPIO_C_NR, - .label = "GPC", - }, - }, { - .chip = { - .base = S5PC100_GPD(0), - .ngpio = S5PC100_GPIO_D_NR, - .label = "GPD", - }, - }, { - .chip = { - .base = S5PC100_GPE0(0), - .ngpio = S5PC100_GPIO_E0_NR, - .label = "GPE0", - }, - }, { - .chip = { - .base = S5PC100_GPE1(0), - .ngpio = S5PC100_GPIO_E1_NR, - .label = "GPE1", - }, - }, { - .chip = { - .base = S5PC100_GPF0(0), - .ngpio = S5PC100_GPIO_F0_NR, - .label = "GPF0", - }, - }, { - .chip = { - .base = S5PC100_GPF1(0), - .ngpio = S5PC100_GPIO_F1_NR, - .label = "GPF1", - }, - }, { - .chip = { - .base = S5PC100_GPF2(0), - .ngpio = S5PC100_GPIO_F2_NR, - .label = "GPF2", - }, - }, { - .chip = { - .base = S5PC100_GPF3(0), - .ngpio = S5PC100_GPIO_F3_NR, - .label = "GPF3", - }, - }, { - .chip = { - .base = S5PC100_GPG0(0), - .ngpio = S5PC100_GPIO_G0_NR, - .label = "GPG0", - }, - }, { - .chip = { - .base = S5PC100_GPG1(0), - .ngpio = S5PC100_GPIO_G1_NR, - .label = "GPG1", - }, - }, { - .chip = { - .base = S5PC100_GPG2(0), - .ngpio = S5PC100_GPIO_G2_NR, - .label = "GPG2", - }, - }, { - .chip = { - .base = S5PC100_GPG3(0), - .ngpio = S5PC100_GPIO_G3_NR, - .label = "GPG3", - }, - }, { - .chip = { - .base = S5PC100_GPI(0), - .ngpio = S5PC100_GPIO_I_NR, - .label = "GPI", - }, - }, { - .chip = { - .base = S5PC100_GPJ0(0), - .ngpio = S5PC100_GPIO_J0_NR, - .label = "GPJ0", - }, - }, { - .chip = { - .base = S5PC100_GPJ1(0), - .ngpio = S5PC100_GPIO_J1_NR, - .label = "GPJ1", - }, - }, { - .chip = { - .base = S5PC100_GPJ2(0), - .ngpio = S5PC100_GPIO_J2_NR, - .label = "GPJ2", - }, - }, { - .chip = { - .base = S5PC100_GPJ3(0), - .ngpio = S5PC100_GPIO_J3_NR, - .label = "GPJ3", - }, - }, { - .chip = { - .base = S5PC100_GPJ4(0), - .ngpio = S5PC100_GPIO_J4_NR, - .label = "GPJ4", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PC100_GPK0(0), - .ngpio = S5PC100_GPIO_K0_NR, - .label = "GPK0", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PC100_GPK1(0), - .ngpio = S5PC100_GPIO_K1_NR, - .label = "GPK1", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PC100_GPK2(0), - .ngpio = S5PC100_GPIO_K2_NR, - .label = "GPK2", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PC100_GPK3(0), - .ngpio = S5PC100_GPIO_K3_NR, - .label = "GPK3", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PC100_GPL0(0), - .ngpio = S5PC100_GPIO_L0_NR, - .label = "GPL0", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PC100_GPL1(0), - .ngpio = S5PC100_GPIO_L1_NR, - .label = "GPL1", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PC100_GPL2(0), - .ngpio = S5PC100_GPIO_L2_NR, - .label = "GPL2", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PC100_GPL3(0), - .ngpio = S5PC100_GPIO_L3_NR, - .label = "GPL3", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PC100_GPL4(0), - .ngpio = S5PC100_GPIO_L4_NR, - .label = "GPL4", - }, - }, { - .base = (S5P_VA_GPIO + 0xC00), - .config = &gpio_cfg_eint, - .irq_base = IRQ_EINT(0), - .chip = { - .base = S5PC100_GPH0(0), - .ngpio = S5PC100_GPIO_H0_NR, - .label = "GPH0", - .to_irq = samsung_gpiolib_to_irq, - }, - }, { - .base = (S5P_VA_GPIO + 0xC20), - .config = &gpio_cfg_eint, - .irq_base = IRQ_EINT(8), - .chip = { - .base = S5PC100_GPH1(0), - .ngpio = S5PC100_GPIO_H1_NR, - .label = "GPH1", - .to_irq = samsung_gpiolib_to_irq, - }, - }, { - .base = (S5P_VA_GPIO + 0xC40), - .config = &gpio_cfg_eint, - .irq_base = IRQ_EINT(16), - .chip = { - .base = S5PC100_GPH2(0), - .ngpio = S5PC100_GPIO_H2_NR, - .label = "GPH2", - .to_irq = samsung_gpiolib_to_irq, - }, - }, { - .base = (S5P_VA_GPIO + 0xC60), - .config = &gpio_cfg_eint, - .irq_base = IRQ_EINT(24), - .chip = { - .base = S5PC100_GPH3(0), - .ngpio = S5PC100_GPIO_H3_NR, - .label = "GPH3", - .to_irq = samsung_gpiolib_to_irq, - }, - }, -}; - -static __init int s5pc100_gpiolib_init(void) -{ - struct s3c_gpio_chip *chip = s5pc100_gpio_chips; - int nr_chips = ARRAY_SIZE(s5pc100_gpio_chips); - int gpioint_group = 0; - int i; - - for (i = 0; i < nr_chips; i++, chip++) { - if (chip->config == NULL) { - chip->config = &gpio_cfg; - chip->group = gpioint_group++; - } - if (chip->base == NULL) - chip->base = S5PC100_BANK_BASE(i); - } - - samsung_gpiolib_add_4bit_chips(s5pc100_gpio_chips, nr_chips); - s5p_register_gpioint_bank(IRQ_GPIOINT, 0, S5P_GPIOINT_GROUP_MAXNR); - - return 0; -} -core_initcall(s5pc100_gpiolib_init); diff --git a/drivers/gpio/gpio-s5pv210.c b/drivers/gpio/gpio-s5pv210.c deleted file mode 100644 index eb12f1602de9..000000000000 --- a/drivers/gpio/gpio-s5pv210.c +++ /dev/null @@ -1,287 +0,0 @@ -/* - * S5PV210 - GPIOlib support - * - * Copyright (c) 2010 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * 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 - -static struct s3c_gpio_cfg gpio_cfg = { - .set_config = s3c_gpio_setcfg_s3c64xx_4bit, - .set_pull = s3c_gpio_setpull_updown, - .get_pull = s3c_gpio_getpull_updown, -}; - -static struct s3c_gpio_cfg gpio_cfg_noint = { - .set_config = s3c_gpio_setcfg_s3c64xx_4bit, - .set_pull = s3c_gpio_setpull_updown, - .get_pull = s3c_gpio_getpull_updown, -}; - -/* GPIO bank's base address given the index of the bank in the - * list of all gpio banks. - */ -#define S5PV210_BANK_BASE(bank_nr) (S5P_VA_GPIO + ((bank_nr) * 0x20)) - -/* - * Following are the gpio banks in v210. - * - * The 'config' member when left to NULL, is initialized to the default - * structure gpio_cfg in the init function below. - * - * The 'base' member is also initialized in the init function below. - * Note: The initialization of 'base' member of s3c_gpio_chip structure - * uses the above macro and depends on the banks being listed in order here. - */ -static struct s3c_gpio_chip s5pv210_gpio_4bit[] = { - { - .chip = { - .base = S5PV210_GPA0(0), - .ngpio = S5PV210_GPIO_A0_NR, - .label = "GPA0", - }, - }, { - .chip = { - .base = S5PV210_GPA1(0), - .ngpio = S5PV210_GPIO_A1_NR, - .label = "GPA1", - }, - }, { - .chip = { - .base = S5PV210_GPB(0), - .ngpio = S5PV210_GPIO_B_NR, - .label = "GPB", - }, - }, { - .chip = { - .base = S5PV210_GPC0(0), - .ngpio = S5PV210_GPIO_C0_NR, - .label = "GPC0", - }, - }, { - .chip = { - .base = S5PV210_GPC1(0), - .ngpio = S5PV210_GPIO_C1_NR, - .label = "GPC1", - }, - }, { - .chip = { - .base = S5PV210_GPD0(0), - .ngpio = S5PV210_GPIO_D0_NR, - .label = "GPD0", - }, - }, { - .chip = { - .base = S5PV210_GPD1(0), - .ngpio = S5PV210_GPIO_D1_NR, - .label = "GPD1", - }, - }, { - .chip = { - .base = S5PV210_GPE0(0), - .ngpio = S5PV210_GPIO_E0_NR, - .label = "GPE0", - }, - }, { - .chip = { - .base = S5PV210_GPE1(0), - .ngpio = S5PV210_GPIO_E1_NR, - .label = "GPE1", - }, - }, { - .chip = { - .base = S5PV210_GPF0(0), - .ngpio = S5PV210_GPIO_F0_NR, - .label = "GPF0", - }, - }, { - .chip = { - .base = S5PV210_GPF1(0), - .ngpio = S5PV210_GPIO_F1_NR, - .label = "GPF1", - }, - }, { - .chip = { - .base = S5PV210_GPF2(0), - .ngpio = S5PV210_GPIO_F2_NR, - .label = "GPF2", - }, - }, { - .chip = { - .base = S5PV210_GPF3(0), - .ngpio = S5PV210_GPIO_F3_NR, - .label = "GPF3", - }, - }, { - .chip = { - .base = S5PV210_GPG0(0), - .ngpio = S5PV210_GPIO_G0_NR, - .label = "GPG0", - }, - }, { - .chip = { - .base = S5PV210_GPG1(0), - .ngpio = S5PV210_GPIO_G1_NR, - .label = "GPG1", - }, - }, { - .chip = { - .base = S5PV210_GPG2(0), - .ngpio = S5PV210_GPIO_G2_NR, - .label = "GPG2", - }, - }, { - .chip = { - .base = S5PV210_GPG3(0), - .ngpio = S5PV210_GPIO_G3_NR, - .label = "GPG3", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PV210_GPI(0), - .ngpio = S5PV210_GPIO_I_NR, - .label = "GPI", - }, - }, { - .chip = { - .base = S5PV210_GPJ0(0), - .ngpio = S5PV210_GPIO_J0_NR, - .label = "GPJ0", - }, - }, { - .chip = { - .base = S5PV210_GPJ1(0), - .ngpio = S5PV210_GPIO_J1_NR, - .label = "GPJ1", - }, - }, { - .chip = { - .base = S5PV210_GPJ2(0), - .ngpio = S5PV210_GPIO_J2_NR, - .label = "GPJ2", - }, - }, { - .chip = { - .base = S5PV210_GPJ3(0), - .ngpio = S5PV210_GPIO_J3_NR, - .label = "GPJ3", - }, - }, { - .chip = { - .base = S5PV210_GPJ4(0), - .ngpio = S5PV210_GPIO_J4_NR, - .label = "GPJ4", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PV210_MP01(0), - .ngpio = S5PV210_GPIO_MP01_NR, - .label = "MP01", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PV210_MP02(0), - .ngpio = S5PV210_GPIO_MP02_NR, - .label = "MP02", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PV210_MP03(0), - .ngpio = S5PV210_GPIO_MP03_NR, - .label = "MP03", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PV210_MP04(0), - .ngpio = S5PV210_GPIO_MP04_NR, - .label = "MP04", - }, - }, { - .config = &gpio_cfg_noint, - .chip = { - .base = S5PV210_MP05(0), - .ngpio = S5PV210_GPIO_MP05_NR, - .label = "MP05", - }, - }, { - .base = (S5P_VA_GPIO + 0xC00), - .config = &gpio_cfg_noint, - .irq_base = IRQ_EINT(0), - .chip = { - .base = S5PV210_GPH0(0), - .ngpio = S5PV210_GPIO_H0_NR, - .label = "GPH0", - .to_irq = samsung_gpiolib_to_irq, - }, - }, { - .base = (S5P_VA_GPIO + 0xC20), - .config = &gpio_cfg_noint, - .irq_base = IRQ_EINT(8), - .chip = { - .base = S5PV210_GPH1(0), - .ngpio = S5PV210_GPIO_H1_NR, - .label = "GPH1", - .to_irq = samsung_gpiolib_to_irq, - }, - }, { - .base = (S5P_VA_GPIO + 0xC40), - .config = &gpio_cfg_noint, - .irq_base = IRQ_EINT(16), - .chip = { - .base = S5PV210_GPH2(0), - .ngpio = S5PV210_GPIO_H2_NR, - .label = "GPH2", - .to_irq = samsung_gpiolib_to_irq, - }, - }, { - .base = (S5P_VA_GPIO + 0xC60), - .config = &gpio_cfg_noint, - .irq_base = IRQ_EINT(24), - .chip = { - .base = S5PV210_GPH3(0), - .ngpio = S5PV210_GPIO_H3_NR, - .label = "GPH3", - .to_irq = samsung_gpiolib_to_irq, - }, - }, -}; - -static __init int s5pv210_gpiolib_init(void) -{ - struct s3c_gpio_chip *chip = s5pv210_gpio_4bit; - int nr_chips = ARRAY_SIZE(s5pv210_gpio_4bit); - int gpioint_group = 0; - int i = 0; - - for (i = 0; i < nr_chips; i++, chip++) { - if (chip->config == NULL) { - chip->config = &gpio_cfg; - chip->group = gpioint_group++; - } - if (chip->base == NULL) - chip->base = S5PV210_BANK_BASE(i); - } - - samsung_gpiolib_add_4bit_chips(s5pv210_gpio_4bit, nr_chips); - s5p_register_gpioint_bank(IRQ_GPIOINT, 0, S5P_GPIOINT_GROUP_MAXNR); - - return 0; -} -core_initcall(s5pv210_gpiolib_init); diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c new file mode 100644 index 000000000000..36f3675e7de8 --- /dev/null +++ b/drivers/gpio/gpio-samsung.c @@ -0,0 +1,2686 @@ +/* + * Copyright (c) 2009-2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Copyright 2008 Openmoko, Inc. + * Copyright 2008 Simtec Electronics + * Ben Dooks + * http://armlinux.simtec.co.uk/ + * + * SAMSUNG - GPIOlib support + * + * 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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifndef DEBUG_GPIO +#define gpio_dbg(x...) do { } while (0) +#else +#define gpio_dbg(x...) printk(KERN_DEBUG x) +#endif + +int samsung_gpio_setpull_updown(struct samsung_gpio_chip *chip, + unsigned int off, samsung_gpio_pull_t pull) +{ + void __iomem *reg = chip->base + 0x08; + int shift = off * 2; + u32 pup; + + pup = __raw_readl(reg); + pup &= ~(3 << shift); + pup |= pull << shift; + __raw_writel(pup, reg); + + return 0; +} + +samsung_gpio_pull_t samsung_gpio_getpull_updown(struct samsung_gpio_chip *chip, + unsigned int off) +{ + void __iomem *reg = chip->base + 0x08; + int shift = off * 2; + u32 pup = __raw_readl(reg); + + pup >>= shift; + pup &= 0x3; + + return (__force samsung_gpio_pull_t)pup; +} + +int s3c2443_gpio_setpull(struct samsung_gpio_chip *chip, + unsigned int off, samsung_gpio_pull_t pull) +{ + switch (pull) { + case S3C_GPIO_PULL_NONE: + pull = 0x01; + break; + case S3C_GPIO_PULL_UP: + pull = 0x00; + break; + case S3C_GPIO_PULL_DOWN: + pull = 0x02; + break; + } + return samsung_gpio_setpull_updown(chip, off, pull); +} + +samsung_gpio_pull_t s3c2443_gpio_getpull(struct samsung_gpio_chip *chip, + unsigned int off) +{ + samsung_gpio_pull_t pull; + + pull = samsung_gpio_getpull_updown(chip, off); + + switch (pull) { + case 0x00: + pull = S3C_GPIO_PULL_UP; + break; + case 0x01: + case 0x03: + pull = S3C_GPIO_PULL_NONE; + break; + case 0x02: + pull = S3C_GPIO_PULL_DOWN; + break; + } + + return pull; +} + +static int s3c24xx_gpio_setpull_1(struct samsung_gpio_chip *chip, + unsigned int off, samsung_gpio_pull_t pull, + samsung_gpio_pull_t updown) +{ + void __iomem *reg = chip->base + 0x08; + u32 pup = __raw_readl(reg); + + if (pull == updown) + pup &= ~(1 << off); + else if (pull == S3C_GPIO_PULL_NONE) + pup |= (1 << off); + else + return -EINVAL; + + __raw_writel(pup, reg); + return 0; +} + +static samsung_gpio_pull_t s3c24xx_gpio_getpull_1(struct samsung_gpio_chip *chip, + unsigned int off, + samsung_gpio_pull_t updown) +{ + void __iomem *reg = chip->base + 0x08; + u32 pup = __raw_readl(reg); + + pup &= (1 << off); + return pup ? S3C_GPIO_PULL_NONE : updown; +} + +samsung_gpio_pull_t s3c24xx_gpio_getpull_1up(struct samsung_gpio_chip *chip, + unsigned int off) +{ + return s3c24xx_gpio_getpull_1(chip, off, S3C_GPIO_PULL_UP); +} + +int s3c24xx_gpio_setpull_1up(struct samsung_gpio_chip *chip, + unsigned int off, samsung_gpio_pull_t pull) +{ + return s3c24xx_gpio_setpull_1(chip, off, pull, S3C_GPIO_PULL_UP); +} + +samsung_gpio_pull_t s3c24xx_gpio_getpull_1down(struct samsung_gpio_chip *chip, + unsigned int off) +{ + return s3c24xx_gpio_getpull_1(chip, off, S3C_GPIO_PULL_DOWN); +} + +int s3c24xx_gpio_setpull_1down(struct samsung_gpio_chip *chip, + unsigned int off, samsung_gpio_pull_t pull) +{ + return s3c24xx_gpio_setpull_1(chip, off, pull, S3C_GPIO_PULL_DOWN); +} + +static int exynos4_gpio_setpull(struct samsung_gpio_chip *chip, + unsigned int off, samsung_gpio_pull_t pull) +{ + if (pull == S3C_GPIO_PULL_UP) + pull = 3; + + return samsung_gpio_setpull_updown(chip, off, pull); +} + +static samsung_gpio_pull_t exynos4_gpio_getpull(struct samsung_gpio_chip *chip, + unsigned int off) +{ + samsung_gpio_pull_t pull; + + pull = samsung_gpio_getpull_updown(chip, off); + + if (pull == 3) + pull = S3C_GPIO_PULL_UP; + + return pull; +} + +/* + * samsung_gpio_setcfg_2bit - Samsung 2bit style GPIO configuration. + * @chip: The gpio chip that is being configured. + * @off: The offset for the GPIO being configured. + * @cfg: The configuration value to set. + * + * This helper deal with the GPIO cases where the control register + * has two bits of configuration per gpio, which have the following + * functions: + * 00 = input + * 01 = output + * 1x = special function + */ + +static int samsung_gpio_setcfg_2bit(struct samsung_gpio_chip *chip, + unsigned int off, unsigned int cfg) +{ + void __iomem *reg = chip->base; + unsigned int shift = off * 2; + u32 con; + + if (samsung_gpio_is_cfg_special(cfg)) { + cfg &= 0xf; + if (cfg > 3) + return -EINVAL; + + cfg <<= shift; + } + + con = __raw_readl(reg); + con &= ~(0x3 << shift); + con |= cfg; + __raw_writel(con, reg); + + return 0; +} + +/* + * samsung_gpio_getcfg_2bit - Samsung 2bit style GPIO configuration read. + * @chip: The gpio chip that is being configured. + * @off: The offset for the GPIO being configured. + * + * The reverse of samsung_gpio_setcfg_2bit(). Will return a value whicg + * could be directly passed back to samsung_gpio_setcfg_2bit(), from the + * S3C_GPIO_SPECIAL() macro. + */ + +static unsigned int samsung_gpio_getcfg_2bit(struct samsung_gpio_chip *chip, + unsigned int off) +{ + u32 con; + + con = __raw_readl(chip->base); + con >>= off * 2; + con &= 3; + + /* this conversion works for IN and OUT as well as special mode */ + return S3C_GPIO_SPECIAL(con); +} + +/* + * samsung_gpio_setcfg_4bit - Samsung 4bit single register GPIO config. + * @chip: The gpio chip that is being configured. + * @off: The offset for the GPIO being configured. + * @cfg: The configuration value to set. + * + * This helper deal with the GPIO cases where the control register has 4 bits + * of control per GPIO, generally in the form of: + * 0000 = Input + * 0001 = Output + * others = Special functions (dependent on bank) + * + * Note, since the code to deal with the case where there are two control + * registers instead of one, we do not have a separate set of functions for + * each case. + */ + +static int samsung_gpio_setcfg_4bit(struct samsung_gpio_chip *chip, + unsigned int off, unsigned int cfg) +{ + void __iomem *reg = chip->base; + unsigned int shift = (off & 7) * 4; + u32 con; + + if (off < 8 && chip->chip.ngpio > 8) + reg -= 4; + + if (samsung_gpio_is_cfg_special(cfg)) { + cfg &= 0xf; + cfg <<= shift; + } + + con = __raw_readl(reg); + con &= ~(0xf << shift); + con |= cfg; + __raw_writel(con, reg); + + return 0; +} + +/* + * samsung_gpio_getcfg_4bit - Samsung 4bit single register GPIO config read. + * @chip: The gpio chip that is being configured. + * @off: The offset for the GPIO being configured. + * + * The reverse of samsung_gpio_setcfg_4bit(), turning a gpio configuration + * register setting into a value the software can use, such as could be passed + * to samsung_gpio_setcfg_4bit(). + * + * @sa samsung_gpio_getcfg_2bit + */ + +static unsigned samsung_gpio_getcfg_4bit(struct samsung_gpio_chip *chip, + unsigned int off) +{ + void __iomem *reg = chip->base; + unsigned int shift = (off & 7) * 4; + u32 con; + + if (off < 8 && chip->chip.ngpio > 8) + reg -= 4; + + con = __raw_readl(reg); + con >>= shift; + con &= 0xf; + + /* this conversion works for IN and OUT as well as special mode */ + return S3C_GPIO_SPECIAL(con); +} + +/* + * s3c24xx_gpio_setcfg_abank - S3C24XX style GPIO configuration (Bank A) + * @chip: The gpio chip that is being configured. + * @off: The offset for the GPIO being configured. + * @cfg: The configuration value to set. + * + * This helper deal with the GPIO cases where the control register + * has one bit of configuration for the gpio, where setting the bit + * means the pin is in special function mode and unset means output. + */ + +static int s3c24xx_gpio_setcfg_abank(struct samsung_gpio_chip *chip, + unsigned int off, unsigned int cfg) +{ + void __iomem *reg = chip->base; + unsigned int shift = off; + u32 con; + + if (samsung_gpio_is_cfg_special(cfg)) { + cfg &= 0xf; + + /* Map output to 0, and SFN2 to 1 */ + cfg -= 1; + if (cfg > 1) + return -EINVAL; + + cfg <<= shift; + } + + con = __raw_readl(reg); + con &= ~(0x1 << shift); + con |= cfg; + __raw_writel(con, reg); + + return 0; +} + +/* + * s3c24xx_gpio_getcfg_abank - S3C24XX style GPIO configuration read (Bank A) + * @chip: The gpio chip that is being configured. + * @off: The offset for the GPIO being configured. + * + * The reverse of s3c24xx_gpio_setcfg_abank() turning an GPIO into a usable + * GPIO configuration value. + * + * @sa samsung_gpio_getcfg_2bit + * @sa samsung_gpio_getcfg_4bit + */ + +static unsigned s3c24xx_gpio_getcfg_abank(struct samsung_gpio_chip *chip, + unsigned int off) +{ + u32 con; + + con = __raw_readl(chip->base); + con >>= off; + con &= 1; + con++; + + return S3C_GPIO_SFN(con); +} + +static int s5p64x0_gpio_setcfg_rbank(struct samsung_gpio_chip *chip, + unsigned int off, unsigned int cfg) +{ + void __iomem *reg = chip->base; + unsigned int shift; + u32 con; + + switch (off) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + shift = (off & 7) * 4; + reg -= 4; + break; + case 6: + shift = ((off + 1) & 7) * 4; + reg -= 4; + default: + shift = ((off + 1) & 7) * 4; + break; + } + + if (samsung_gpio_is_cfg_special(cfg)) { + cfg &= 0xf; + cfg <<= shift; + } + + con = __raw_readl(reg); + con &= ~(0xf << shift); + con |= cfg; + __raw_writel(con, reg); + + return 0; +} + +static void __init samsung_gpiolib_set_cfg(struct samsung_gpio_cfg *chipcfg, + int nr_chips) +{ + for (; nr_chips > 0; nr_chips--, chipcfg++) { + if (!chipcfg->set_config) + chipcfg->set_config = samsung_gpio_setcfg_4bit; + if (!chipcfg->get_config) + chipcfg->get_config = samsung_gpio_getcfg_4bit; + if (!chipcfg->set_pull) + chipcfg->set_pull = samsung_gpio_setpull_updown; + if (!chipcfg->get_pull) + chipcfg->get_pull = samsung_gpio_getpull_updown; + } +} + +struct samsung_gpio_cfg s3c24xx_gpiocfg_default = { + .set_config = samsung_gpio_setcfg_2bit, + .get_config = samsung_gpio_getcfg_2bit, +}; + +static struct samsung_gpio_cfg s3c24xx_gpiocfg_banka = { + .set_config = s3c24xx_gpio_setcfg_abank, + .get_config = s3c24xx_gpio_getcfg_abank, +}; + +static struct samsung_gpio_cfg exynos4_gpio_cfg = { + .set_pull = exynos4_gpio_setpull, + .get_pull = exynos4_gpio_getpull, +}; + +static struct samsung_gpio_cfg s5p64x0_gpio_cfg_rbank = { + .cfg_eint = 0x3, + .set_config = s5p64x0_gpio_setcfg_rbank, + .get_config = samsung_gpio_getcfg_4bit, + .set_pull = samsung_gpio_setpull_updown, + .get_pull = samsung_gpio_getpull_updown, +}; + +static struct samsung_gpio_cfg samsung_gpio_cfgs[] = { + { + .cfg_eint = 0x0, + }, { + .cfg_eint = 0x3, + }, { + .cfg_eint = 0x7, + }, { + .cfg_eint = 0xF, + }, { + .cfg_eint = 0x0, + .set_config = samsung_gpio_setcfg_2bit, + .get_config = samsung_gpio_getcfg_2bit, + }, { + .cfg_eint = 0x2, + .set_config = samsung_gpio_setcfg_2bit, + .get_config = samsung_gpio_getcfg_2bit, + }, { + .cfg_eint = 0x3, + .set_config = samsung_gpio_setcfg_2bit, + .get_config = samsung_gpio_getcfg_2bit, + }, { + .set_config = samsung_gpio_setcfg_2bit, + .get_config = samsung_gpio_getcfg_2bit, + }, +}; + +/* + * Default routines for controlling GPIO, based on the original S3C24XX + * GPIO functions which deal with the case where each gpio bank of the + * chip is as following: + * + * base + 0x00: Control register, 2 bits per gpio + * gpio n: 2 bits starting at (2*n) + * 00 = input, 01 = output, others mean special-function + * base + 0x04: Data register, 1 bit per gpio + * bit n: data bit n +*/ + +static int samsung_gpiolib_2bit_input(struct gpio_chip *chip, unsigned offset) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + void __iomem *base = ourchip->base; + unsigned long flags; + unsigned long con; + + samsung_gpio_lock(ourchip, flags); + + con = __raw_readl(base + 0x00); + con &= ~(3 << (offset * 2)); + + __raw_writel(con, base + 0x00); + + samsung_gpio_unlock(ourchip, flags); + return 0; +} + +static int samsung_gpiolib_2bit_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + void __iomem *base = ourchip->base; + unsigned long flags; + unsigned long dat; + unsigned long con; + + samsung_gpio_lock(ourchip, flags); + + dat = __raw_readl(base + 0x04); + dat &= ~(1 << offset); + if (value) + dat |= 1 << offset; + __raw_writel(dat, base + 0x04); + + con = __raw_readl(base + 0x00); + con &= ~(3 << (offset * 2)); + con |= 1 << (offset * 2); + + __raw_writel(con, base + 0x00); + __raw_writel(dat, base + 0x04); + + samsung_gpio_unlock(ourchip, flags); + return 0; +} + +/* + * The samsung_gpiolib_4bit routines are to control the gpio banks where + * the gpio configuration register (GPxCON) has 4 bits per GPIO, as the + * following example: + * + * base + 0x00: Control register, 4 bits per gpio + * gpio n: 4 bits starting at (4*n) + * 0000 = input, 0001 = output, others mean special-function + * base + 0x04: Data register, 1 bit per gpio + * bit n: data bit n + * + * Note, since the data register is one bit per gpio and is at base + 0x4 + * we can use samsung_gpiolib_get and samsung_gpiolib_set to change the + * state of the output. + */ + +static int samsung_gpiolib_4bit_input(struct gpio_chip *chip, + unsigned int offset) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + void __iomem *base = ourchip->base; + unsigned long con; + + con = __raw_readl(base + GPIOCON_OFF); + con &= ~(0xf << con_4bit_shift(offset)); + __raw_writel(con, base + GPIOCON_OFF); + + gpio_dbg("%s: %p: CON now %08lx\n", __func__, base, con); + + return 0; +} + +static int samsung_gpiolib_4bit_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + void __iomem *base = ourchip->base; + unsigned long con; + unsigned long dat; + + con = __raw_readl(base + GPIOCON_OFF); + con &= ~(0xf << con_4bit_shift(offset)); + con |= 0x1 << con_4bit_shift(offset); + + dat = __raw_readl(base + GPIODAT_OFF); + + if (value) + dat |= 1 << offset; + else + dat &= ~(1 << offset); + + __raw_writel(dat, base + GPIODAT_OFF); + __raw_writel(con, base + GPIOCON_OFF); + __raw_writel(dat, base + GPIODAT_OFF); + + gpio_dbg("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat); + + return 0; +} + +/* + * The next set of routines are for the case where the GPIO configuration + * registers are 4 bits per GPIO but there is more than one register (the + * bank has more than 8 GPIOs. + * + * This case is the similar to the 4 bit case, but the registers are as + * follows: + * + * base + 0x00: Control register, 4 bits per gpio (lower 8 GPIOs) + * gpio n: 4 bits starting at (4*n) + * 0000 = input, 0001 = output, others mean special-function + * base + 0x04: Control register, 4 bits per gpio (up to 8 additions GPIOs) + * gpio n: 4 bits starting at (4*n) + * 0000 = input, 0001 = output, others mean special-function + * base + 0x08: Data register, 1 bit per gpio + * bit n: data bit n + * + * To allow us to use the samsung_gpiolib_get and samsung_gpiolib_set + * routines we store the 'base + 0x4' address so that these routines see + * the data register at ourchip->base + 0x04. + */ + +static int samsung_gpiolib_4bit2_input(struct gpio_chip *chip, + unsigned int offset) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + void __iomem *base = ourchip->base; + void __iomem *regcon = base; + unsigned long con; + + if (offset > 7) + offset -= 8; + else + regcon -= 4; + + con = __raw_readl(regcon); + con &= ~(0xf << con_4bit_shift(offset)); + __raw_writel(con, regcon); + + gpio_dbg("%s: %p: CON %08lx\n", __func__, base, con); + + return 0; +} + +static int samsung_gpiolib_4bit2_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + void __iomem *base = ourchip->base; + void __iomem *regcon = base; + unsigned long con; + unsigned long dat; + unsigned con_offset = offset; + + if (con_offset > 7) + con_offset -= 8; + else + regcon -= 4; + + con = __raw_readl(regcon); + con &= ~(0xf << con_4bit_shift(con_offset)); + con |= 0x1 << con_4bit_shift(con_offset); + + dat = __raw_readl(base + GPIODAT_OFF); + + if (value) + dat |= 1 << offset; + else + dat &= ~(1 << offset); + + __raw_writel(dat, base + GPIODAT_OFF); + __raw_writel(con, regcon); + __raw_writel(dat, base + GPIODAT_OFF); + + gpio_dbg("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat); + + return 0; +} + +/* The next set of routines are for the case of s3c24xx bank a */ + +static int s3c24xx_gpiolib_banka_input(struct gpio_chip *chip, unsigned offset) +{ + return -EINVAL; +} + +static int s3c24xx_gpiolib_banka_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + void __iomem *base = ourchip->base; + unsigned long flags; + unsigned long dat; + unsigned long con; + + local_irq_save(flags); + + con = __raw_readl(base + 0x00); + dat = __raw_readl(base + 0x04); + + dat &= ~(1 << offset); + if (value) + dat |= 1 << offset; + + __raw_writel(dat, base + 0x04); + + con &= ~(1 << offset); + + __raw_writel(con, base + 0x00); + __raw_writel(dat, base + 0x04); + + local_irq_restore(flags); + return 0; +} + +/* The next set of routines are for the case of s5p64x0 bank r */ + +static int s5p64x0_gpiolib_rbank_input(struct gpio_chip *chip, + unsigned int offset) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + void __iomem *base = ourchip->base; + void __iomem *regcon = base; + unsigned long con; + unsigned long flags; + + switch (offset) { + case 6: + offset += 1; + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + regcon -= 4; + break; + default: + offset -= 7; + break; + } + + samsung_gpio_lock(ourchip, flags); + + con = __raw_readl(regcon); + con &= ~(0xf << con_4bit_shift(offset)); + __raw_writel(con, regcon); + + samsung_gpio_unlock(ourchip, flags); + + return 0; +} + +static int s5p64x0_gpiolib_rbank_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + void __iomem *base = ourchip->base; + void __iomem *regcon = base; + unsigned long con; + unsigned long dat; + unsigned long flags; + unsigned con_offset = offset; + + switch (con_offset) { + case 6: + con_offset += 1; + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + regcon -= 4; + break; + default: + con_offset -= 7; + break; + } + + samsung_gpio_lock(ourchip, flags); + + con = __raw_readl(regcon); + con &= ~(0xf << con_4bit_shift(con_offset)); + con |= 0x1 << con_4bit_shift(con_offset); + + dat = __raw_readl(base + GPIODAT_OFF); + if (value) + dat |= 1 << offset; + else + dat &= ~(1 << offset); + + __raw_writel(con, regcon); + __raw_writel(dat, base + GPIODAT_OFF); + + samsung_gpio_unlock(ourchip, flags); + + return 0; +} + +static void samsung_gpiolib_set(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + void __iomem *base = ourchip->base; + unsigned long flags; + unsigned long dat; + + samsung_gpio_lock(ourchip, flags); + + dat = __raw_readl(base + 0x04); + dat &= ~(1 << offset); + if (value) + dat |= 1 << offset; + __raw_writel(dat, base + 0x04); + + samsung_gpio_unlock(ourchip, flags); +} + +static int samsung_gpiolib_get(struct gpio_chip *chip, unsigned offset) +{ + struct samsung_gpio_chip *ourchip = to_samsung_gpio(chip); + unsigned long val; + + val = __raw_readl(ourchip->base + 0x04); + val >>= offset; + val &= 1; + + return val; +} + +/* + * CONFIG_S3C_GPIO_TRACK enables the tracking of the s3c specific gpios + * for use with the configuration calls, and other parts of the s3c gpiolib + * support code. + * + * Not all s3c support code will need this, as some configurations of cpu + * may only support one or two different configuration options and have an + * easy gpio to samsung_gpio_chip mapping function. If this is the case, then + * the machine support file should provide its own samsung_gpiolib_getchip() + * and any other necessary functions. + */ + +#ifdef CONFIG_S3C_GPIO_TRACK +struct samsung_gpio_chip *s3c_gpios[S3C_GPIO_END]; + +static __init void s3c_gpiolib_track(struct samsung_gpio_chip *chip) +{ + unsigned int gpn; + int i; + + gpn = chip->chip.base; + for (i = 0; i < chip->chip.ngpio; i++, gpn++) { + BUG_ON(gpn >= ARRAY_SIZE(s3c_gpios)); + s3c_gpios[gpn] = chip; + } +} +#endif /* CONFIG_S3C_GPIO_TRACK */ + +/* + * samsung_gpiolib_add() - add the Samsung gpio_chip. + * @chip: The chip to register + * + * This is a wrapper to gpiochip_add() that takes our specific gpio chip + * information and makes the necessary alterations for the platform and + * notes the information for use with the configuration systems and any + * other parts of the system. + */ + +static void __init samsung_gpiolib_add(struct samsung_gpio_chip *chip) +{ + struct gpio_chip *gc = &chip->chip; + int ret; + + BUG_ON(!chip->base); + BUG_ON(!gc->label); + BUG_ON(!gc->ngpio); + + spin_lock_init(&chip->lock); + + if (!gc->direction_input) + gc->direction_input = samsung_gpiolib_2bit_input; + if (!gc->direction_output) + gc->direction_output = samsung_gpiolib_2bit_output; + if (!gc->set) + gc->set = samsung_gpiolib_set; + if (!gc->get) + gc->get = samsung_gpiolib_get; + +#ifdef CONFIG_PM + if (chip->pm != NULL) { + if (!chip->pm->save || !chip->pm->resume) + printk(KERN_ERR "gpio: %s has missing PM functions\n", + gc->label); + } else + printk(KERN_ERR "gpio: %s has no PM function\n", gc->label); +#endif + + /* gpiochip_add() prints own failure message on error. */ + ret = gpiochip_add(gc); + if (ret >= 0) + s3c_gpiolib_track(chip); +} + +static void __init s3c24xx_gpiolib_add_chips(struct samsung_gpio_chip *chip, + int nr_chips, void __iomem *base) +{ + int i; + struct gpio_chip *gc = &chip->chip; + + for (i = 0 ; i < nr_chips; i++, chip++) { + if (!chip->config) + chip->config = &s3c24xx_gpiocfg_default; + if (!chip->pm) + chip->pm = __gpio_pm(&samsung_gpio_pm_2bit); + if ((base != NULL) && (chip->base == NULL)) + chip->base = base + ((i) * 0x10); + + if (!gc->direction_input) + gc->direction_input = samsung_gpiolib_2bit_input; + if (!gc->direction_output) + gc->direction_output = samsung_gpiolib_2bit_output; + + samsung_gpiolib_add(chip); + } +} + +static void __init samsung_gpiolib_add_2bit_chips(struct samsung_gpio_chip *chip, + int nr_chips, void __iomem *base, + unsigned int offset) +{ + int i; + + for (i = 0 ; i < nr_chips; i++, chip++) { + chip->chip.direction_input = samsung_gpiolib_2bit_input; + chip->chip.direction_output = samsung_gpiolib_2bit_output; + + if (!chip->config) + chip->config = &samsung_gpio_cfgs[7]; + if (!chip->pm) + chip->pm = __gpio_pm(&samsung_gpio_pm_2bit); + if ((base != NULL) && (chip->base == NULL)) + chip->base = base + ((i) * offset); + + samsung_gpiolib_add(chip); + } +} + +/* + * samsung_gpiolib_add_4bit_chips - 4bit single register GPIO config. + * @chip: The gpio chip that is being configured. + * @nr_chips: The no of chips (gpio ports) for the GPIO being configured. + * + * This helper deal with the GPIO cases where the control register has 4 bits + * of control per GPIO, generally in the form of: + * 0000 = Input + * 0001 = Output + * others = Special functions (dependent on bank) + * + * Note, since the code to deal with the case where there are two control + * registers instead of one, we do not have a separate set of function + * (samsung_gpiolib_add_4bit2_chips)for each case. + */ + +static void __init samsung_gpiolib_add_4bit_chips(struct samsung_gpio_chip *chip, + int nr_chips, void __iomem *base) +{ + int i; + + for (i = 0 ; i < nr_chips; i++, chip++) { + chip->chip.direction_input = samsung_gpiolib_4bit_input; + chip->chip.direction_output = samsung_gpiolib_4bit_output; + + if (!chip->config) + chip->config = &samsung_gpio_cfgs[2]; + if (!chip->pm) + chip->pm = __gpio_pm(&samsung_gpio_pm_4bit); + if ((base != NULL) && (chip->base == NULL)) + chip->base = base + ((i) * 0x20); + + samsung_gpiolib_add(chip); + } +} + +static void __init samsung_gpiolib_add_4bit2_chips(struct samsung_gpio_chip *chip, + int nr_chips) +{ + for (; nr_chips > 0; nr_chips--, chip++) { + chip->chip.direction_input = samsung_gpiolib_4bit2_input; + chip->chip.direction_output = samsung_gpiolib_4bit2_output; + + if (!chip->config) + chip->config = &samsung_gpio_cfgs[2]; + if (!chip->pm) + chip->pm = __gpio_pm(&samsung_gpio_pm_4bit); + + samsung_gpiolib_add(chip); + } +} + +static void __init s5p64x0_gpiolib_add_rbank(struct samsung_gpio_chip *chip, + int nr_chips) +{ + for (; nr_chips > 0; nr_chips--, chip++) { + chip->chip.direction_input = s5p64x0_gpiolib_rbank_input; + chip->chip.direction_output = s5p64x0_gpiolib_rbank_output; + + if (!chip->pm) + chip->pm = __gpio_pm(&samsung_gpio_pm_4bit); + + samsung_gpiolib_add(chip); + } +} + +int samsung_gpiolib_to_irq(struct gpio_chip *chip, unsigned int offset) +{ + struct samsung_gpio_chip *samsung_chip = container_of(chip, struct samsung_gpio_chip, chip); + + return samsung_chip->irq_base + offset; +} + +#ifdef CONFIG_PLAT_S3C24XX +static int s3c24xx_gpiolib_fbank_to_irq(struct gpio_chip *chip, unsigned offset) +{ + if (offset < 4) + return IRQ_EINT0 + offset; + + if (offset < 8) + return IRQ_EINT4 + offset - 4; + + return -EINVAL; +} +#endif + +#ifdef CONFIG_PLAT_S3C64XX +static int s3c64xx_gpiolib_mbank_to_irq(struct gpio_chip *chip, unsigned pin) +{ + return pin < 5 ? IRQ_EINT(23) + pin : -ENXIO; +} + +static int s3c64xx_gpiolib_lbank_to_irq(struct gpio_chip *chip, unsigned pin) +{ + return pin >= 8 ? IRQ_EINT(16) + pin - 8 : -ENXIO; +} +#endif + +struct samsung_gpio_chip s3c24xx_gpios[] = { +#ifdef CONFIG_PLAT_S3C24XX + { + .config = &s3c24xx_gpiocfg_banka, + .chip = { + .base = S3C2410_GPA(0), + .owner = THIS_MODULE, + .label = "GPIOA", + .ngpio = 24, + .direction_input = s3c24xx_gpiolib_banka_input, + .direction_output = s3c24xx_gpiolib_banka_output, + }, + }, { + .chip = { + .base = S3C2410_GPB(0), + .owner = THIS_MODULE, + .label = "GPIOB", + .ngpio = 16, + }, + }, { + .chip = { + .base = S3C2410_GPC(0), + .owner = THIS_MODULE, + .label = "GPIOC", + .ngpio = 16, + }, + }, { + .chip = { + .base = S3C2410_GPD(0), + .owner = THIS_MODULE, + .label = "GPIOD", + .ngpio = 16, + }, + }, { + .chip = { + .base = S3C2410_GPE(0), + .label = "GPIOE", + .owner = THIS_MODULE, + .ngpio = 16, + }, + }, { + .chip = { + .base = S3C2410_GPF(0), + .owner = THIS_MODULE, + .label = "GPIOF", + .ngpio = 8, + .to_irq = s3c24xx_gpiolib_fbank_to_irq, + }, + }, { + .irq_base = IRQ_EINT8, + .chip = { + .base = S3C2410_GPG(0), + .owner = THIS_MODULE, + .label = "GPIOG", + .ngpio = 16, + .to_irq = samsung_gpiolib_to_irq, + }, + }, { + .chip = { + .base = S3C2410_GPH(0), + .owner = THIS_MODULE, + .label = "GPIOH", + .ngpio = 11, + }, + }, + /* GPIOS for the S3C2443 and later devices. */ + { + .base = S3C2440_GPJCON, + .chip = { + .base = S3C2410_GPJ(0), + .owner = THIS_MODULE, + .label = "GPIOJ", + .ngpio = 16, + }, + }, { + .base = S3C2443_GPKCON, + .chip = { + .base = S3C2410_GPK(0), + .owner = THIS_MODULE, + .label = "GPIOK", + .ngpio = 16, + }, + }, { + .base = S3C2443_GPLCON, + .chip = { + .base = S3C2410_GPL(0), + .owner = THIS_MODULE, + .label = "GPIOL", + .ngpio = 15, + }, + }, { + .base = S3C2443_GPMCON, + .chip = { + .base = S3C2410_GPM(0), + .owner = THIS_MODULE, + .label = "GPIOM", + .ngpio = 2, + }, + }, +#endif +}; + +/* + * GPIO bank summary: + * + * Bank GPIOs Style SlpCon ExtInt Group + * A 8 4Bit Yes 1 + * B 7 4Bit Yes 1 + * C 8 4Bit Yes 2 + * D 5 4Bit Yes 3 + * E 5 4Bit Yes None + * F 16 2Bit Yes 4 [1] + * G 7 4Bit Yes 5 + * H 10 4Bit[2] Yes 6 + * I 16 2Bit Yes None + * J 12 2Bit Yes None + * K 16 4Bit[2] No None + * L 15 4Bit[2] No None + * M 6 4Bit No IRQ_EINT + * N 16 2Bit No IRQ_EINT + * O 16 2Bit Yes 7 + * P 15 2Bit Yes 8 + * Q 9 2Bit Yes 9 + * + * [1] BANKF pins 14,15 do not form part of the external interrupt sources + * [2] BANK has two control registers, GPxCON0 and GPxCON1 + */ + +static struct samsung_gpio_chip s3c64xx_gpios_4bit[] = { +#ifdef CONFIG_PLAT_S3C64XX + { + .chip = { + .base = S3C64XX_GPA(0), + .ngpio = S3C64XX_GPIO_A_NR, + .label = "GPA", + }, + }, { + .chip = { + .base = S3C64XX_GPB(0), + .ngpio = S3C64XX_GPIO_B_NR, + .label = "GPB", + }, + }, { + .chip = { + .base = S3C64XX_GPC(0), + .ngpio = S3C64XX_GPIO_C_NR, + .label = "GPC", + }, + }, { + .chip = { + .base = S3C64XX_GPD(0), + .ngpio = S3C64XX_GPIO_D_NR, + .label = "GPD", + }, + }, { + .config = &samsung_gpio_cfgs[0], + .chip = { + .base = S3C64XX_GPE(0), + .ngpio = S3C64XX_GPIO_E_NR, + .label = "GPE", + }, + }, { + .base = S3C64XX_GPG_BASE, + .chip = { + .base = S3C64XX_GPG(0), + .ngpio = S3C64XX_GPIO_G_NR, + .label = "GPG", + }, + }, { + .base = S3C64XX_GPM_BASE, + .config = &samsung_gpio_cfgs[1], + .chip = { + .base = S3C64XX_GPM(0), + .ngpio = S3C64XX_GPIO_M_NR, + .label = "GPM", + .to_irq = s3c64xx_gpiolib_mbank_to_irq, + }, + }, +#endif +}; + +static struct samsung_gpio_chip s3c64xx_gpios_4bit2[] = { +#ifdef CONFIG_PLAT_S3C64XX + { + .base = S3C64XX_GPH_BASE + 0x4, + .chip = { + .base = S3C64XX_GPH(0), + .ngpio = S3C64XX_GPIO_H_NR, + .label = "GPH", + }, + }, { + .base = S3C64XX_GPK_BASE + 0x4, + .config = &samsung_gpio_cfgs[0], + .chip = { + .base = S3C64XX_GPK(0), + .ngpio = S3C64XX_GPIO_K_NR, + .label = "GPK", + }, + }, { + .base = S3C64XX_GPL_BASE + 0x4, + .config = &samsung_gpio_cfgs[1], + .chip = { + .base = S3C64XX_GPL(0), + .ngpio = S3C64XX_GPIO_L_NR, + .label = "GPL", + .to_irq = s3c64xx_gpiolib_lbank_to_irq, + }, + }, +#endif +}; + +static struct samsung_gpio_chip s3c64xx_gpios_2bit[] = { +#ifdef CONFIG_PLAT_S3C64XX + { + .base = S3C64XX_GPF_BASE, + .config = &samsung_gpio_cfgs[6], + .chip = { + .base = S3C64XX_GPF(0), + .ngpio = S3C64XX_GPIO_F_NR, + .label = "GPF", + }, + }, { + .config = &samsung_gpio_cfgs[7], + .chip = { + .base = S3C64XX_GPI(0), + .ngpio = S3C64XX_GPIO_I_NR, + .label = "GPI", + }, + }, { + .config = &samsung_gpio_cfgs[7], + .chip = { + .base = S3C64XX_GPJ(0), + .ngpio = S3C64XX_GPIO_J_NR, + .label = "GPJ", + }, + }, { + .config = &samsung_gpio_cfgs[6], + .chip = { + .base = S3C64XX_GPO(0), + .ngpio = S3C64XX_GPIO_O_NR, + .label = "GPO", + }, + }, { + .config = &samsung_gpio_cfgs[6], + .chip = { + .base = S3C64XX_GPP(0), + .ngpio = S3C64XX_GPIO_P_NR, + .label = "GPP", + }, + }, { + .config = &samsung_gpio_cfgs[6], + .chip = { + .base = S3C64XX_GPQ(0), + .ngpio = S3C64XX_GPIO_Q_NR, + .label = "GPQ", + }, + }, { + .base = S3C64XX_GPN_BASE, + .irq_base = IRQ_EINT(0), + .config = &samsung_gpio_cfgs[5], + .chip = { + .base = S3C64XX_GPN(0), + .ngpio = S3C64XX_GPIO_N_NR, + .label = "GPN", + .to_irq = samsung_gpiolib_to_irq, + }, + }, +#endif +}; + +/* + * S5P6440 GPIO bank summary: + * + * Bank GPIOs Style SlpCon ExtInt Group + * A 6 4Bit Yes 1 + * B 7 4Bit Yes 1 + * C 8 4Bit Yes 2 + * F 2 2Bit Yes 4 [1] + * G 7 4Bit Yes 5 + * H 10 4Bit[2] Yes 6 + * I 16 2Bit Yes None + * J 12 2Bit Yes None + * N 16 2Bit No IRQ_EINT + * P 8 2Bit Yes 8 + * R 15 4Bit[2] Yes 8 + */ + +static struct samsung_gpio_chip s5p6440_gpios_4bit[] = { +#ifdef CONFIG_CPU_S5P6440 + { + .chip = { + .base = S5P6440_GPA(0), + .ngpio = S5P6440_GPIO_A_NR, + .label = "GPA", + }, + }, { + .chip = { + .base = S5P6440_GPB(0), + .ngpio = S5P6440_GPIO_B_NR, + .label = "GPB", + }, + }, { + .chip = { + .base = S5P6440_GPC(0), + .ngpio = S5P6440_GPIO_C_NR, + .label = "GPC", + }, + }, { + .base = S5P64X0_GPG_BASE, + .chip = { + .base = S5P6440_GPG(0), + .ngpio = S5P6440_GPIO_G_NR, + .label = "GPG", + }, + }, +#endif +}; + +static struct samsung_gpio_chip s5p6440_gpios_4bit2[] = { +#ifdef CONFIG_CPU_S5P6440 + { + .base = S5P64X0_GPH_BASE + 0x4, + .chip = { + .base = S5P6440_GPH(0), + .ngpio = S5P6440_GPIO_H_NR, + .label = "GPH", + }, + }, +#endif +}; + +static struct samsung_gpio_chip s5p6440_gpios_rbank[] = { +#ifdef CONFIG_CPU_S5P6440 + { + .base = S5P64X0_GPR_BASE + 0x4, + .config = &s5p64x0_gpio_cfg_rbank, + .chip = { + .base = S5P6440_GPR(0), + .ngpio = S5P6440_GPIO_R_NR, + .label = "GPR", + }, + }, +#endif +}; + +static struct samsung_gpio_chip s5p6440_gpios_2bit[] = { +#ifdef CONFIG_CPU_S5P6440 + { + .base = S5P64X0_GPF_BASE, + .config = &samsung_gpio_cfgs[6], + .chip = { + .base = S5P6440_GPF(0), + .ngpio = S5P6440_GPIO_F_NR, + .label = "GPF", + }, + }, { + .base = S5P64X0_GPI_BASE, + .config = &samsung_gpio_cfgs[4], + .chip = { + .base = S5P6440_GPI(0), + .ngpio = S5P6440_GPIO_I_NR, + .label = "GPI", + }, + }, { + .base = S5P64X0_GPJ_BASE, + .config = &samsung_gpio_cfgs[4], + .chip = { + .base = S5P6440_GPJ(0), + .ngpio = S5P6440_GPIO_J_NR, + .label = "GPJ", + }, + }, { + .base = S5P64X0_GPN_BASE, + .config = &samsung_gpio_cfgs[5], + .chip = { + .base = S5P6440_GPN(0), + .ngpio = S5P6440_GPIO_N_NR, + .label = "GPN", + }, + }, { + .base = S5P64X0_GPP_BASE, + .config = &samsung_gpio_cfgs[6], + .chip = { + .base = S5P6440_GPP(0), + .ngpio = S5P6440_GPIO_P_NR, + .label = "GPP", + }, + }, +#endif +}; + +/* + * S5P6450 GPIO bank summary: + * + * Bank GPIOs Style SlpCon ExtInt Group + * A 6 4Bit Yes 1 + * B 7 4Bit Yes 1 + * C 8 4Bit Yes 2 + * D 8 4Bit Yes None + * F 2 2Bit Yes None + * G 14 4Bit[2] Yes 5 + * H 10 4Bit[2] Yes 6 + * I 16 2Bit Yes None + * J 12 2Bit Yes None + * K 5 4Bit Yes None + * N 16 2Bit No IRQ_EINT + * P 11 2Bit Yes 8 + * Q 14 2Bit Yes None + * R 15 4Bit[2] Yes None + * S 8 2Bit Yes None + * + * [1] BANKF pins 14,15 do not form part of the external interrupt sources + * [2] BANK has two control registers, GPxCON0 and GPxCON1 + */ + +static struct samsung_gpio_chip s5p6450_gpios_4bit[] = { +#ifdef CONFIG_CPU_S5P6450 + { + .chip = { + .base = S5P6450_GPA(0), + .ngpio = S5P6450_GPIO_A_NR, + .label = "GPA", + }, + }, { + .chip = { + .base = S5P6450_GPB(0), + .ngpio = S5P6450_GPIO_B_NR, + .label = "GPB", + }, + }, { + .chip = { + .base = S5P6450_GPC(0), + .ngpio = S5P6450_GPIO_C_NR, + .label = "GPC", + }, + }, { + .chip = { + .base = S5P6450_GPD(0), + .ngpio = S5P6450_GPIO_D_NR, + .label = "GPD", + }, + }, { + .base = S5P6450_GPK_BASE, + .chip = { + .base = S5P6450_GPK(0), + .ngpio = S5P6450_GPIO_K_NR, + .label = "GPK", + }, + }, +#endif +}; + +static struct samsung_gpio_chip s5p6450_gpios_4bit2[] = { +#ifdef CONFIG_CPU_S5P6450 + { + .base = S5P64X0_GPG_BASE + 0x4, + .chip = { + .base = S5P6450_GPG(0), + .ngpio = S5P6450_GPIO_G_NR, + .label = "GPG", + }, + }, { + .base = S5P64X0_GPH_BASE + 0x4, + .chip = { + .base = S5P6450_GPH(0), + .ngpio = S5P6450_GPIO_H_NR, + .label = "GPH", + }, + }, +#endif +}; + +static struct samsung_gpio_chip s5p6450_gpios_rbank[] = { +#ifdef CONFIG_CPU_S5P6450 + { + .base = S5P64X0_GPR_BASE + 0x4, + .config = &s5p64x0_gpio_cfg_rbank, + .chip = { + .base = S5P6450_GPR(0), + .ngpio = S5P6450_GPIO_R_NR, + .label = "GPR", + }, + }, +#endif +}; + +static struct samsung_gpio_chip s5p6450_gpios_2bit[] = { +#ifdef CONFIG_CPU_S5P6450 + { + .base = S5P64X0_GPF_BASE, + .config = &samsung_gpio_cfgs[6], + .chip = { + .base = S5P6450_GPF(0), + .ngpio = S5P6450_GPIO_F_NR, + .label = "GPF", + }, + }, { + .base = S5P64X0_GPI_BASE, + .config = &samsung_gpio_cfgs[4], + .chip = { + .base = S5P6450_GPI(0), + .ngpio = S5P6450_GPIO_I_NR, + .label = "GPI", + }, + }, { + .base = S5P64X0_GPJ_BASE, + .config = &samsung_gpio_cfgs[4], + .chip = { + .base = S5P6450_GPJ(0), + .ngpio = S5P6450_GPIO_J_NR, + .label = "GPJ", + }, + }, { + .base = S5P64X0_GPN_BASE, + .config = &samsung_gpio_cfgs[5], + .chip = { + .base = S5P6450_GPN(0), + .ngpio = S5P6450_GPIO_N_NR, + .label = "GPN", + }, + }, { + .base = S5P64X0_GPP_BASE, + .config = &samsung_gpio_cfgs[6], + .chip = { + .base = S5P6450_GPP(0), + .ngpio = S5P6450_GPIO_P_NR, + .label = "GPP", + }, + }, { + .base = S5P6450_GPQ_BASE, + .config = &samsung_gpio_cfgs[5], + .chip = { + .base = S5P6450_GPQ(0), + .ngpio = S5P6450_GPIO_Q_NR, + .label = "GPQ", + }, + }, { + .base = S5P6450_GPS_BASE, + .config = &samsung_gpio_cfgs[6], + .chip = { + .base = S5P6450_GPS(0), + .ngpio = S5P6450_GPIO_S_NR, + .label = "GPS", + }, + }, +#endif +}; + +/* + * S5PC100 GPIO bank summary: + * + * Bank GPIOs Style INT Type + * A0 8 4Bit GPIO_INT0 + * A1 5 4Bit GPIO_INT1 + * B 8 4Bit GPIO_INT2 + * C 5 4Bit GPIO_INT3 + * D 7 4Bit GPIO_INT4 + * E0 8 4Bit GPIO_INT5 + * E1 6 4Bit GPIO_INT6 + * F0 8 4Bit GPIO_INT7 + * F1 8 4Bit GPIO_INT8 + * F2 8 4Bit GPIO_INT9 + * F3 4 4Bit GPIO_INT10 + * G0 8 4Bit GPIO_INT11 + * G1 3 4Bit GPIO_INT12 + * G2 7 4Bit GPIO_INT13 + * G3 7 4Bit GPIO_INT14 + * H0 8 4Bit WKUP_INT + * H1 8 4Bit WKUP_INT + * H2 8 4Bit WKUP_INT + * H3 8 4Bit WKUP_INT + * I 8 4Bit GPIO_INT15 + * J0 8 4Bit GPIO_INT16 + * J1 5 4Bit GPIO_INT17 + * J2 8 4Bit GPIO_INT18 + * J3 8 4Bit GPIO_INT19 + * J4 4 4Bit GPIO_INT20 + * K0 8 4Bit None + * K1 6 4Bit None + * K2 8 4Bit None + * K3 8 4Bit None + * L0 8 4Bit None + * L1 8 4Bit None + * L2 8 4Bit None + * L3 8 4Bit None + */ + +static struct samsung_gpio_chip s5pc100_gpios_4bit[] = { +#ifdef CONFIG_CPU_S5PC100 + { + .chip = { + .base = S5PC100_GPA0(0), + .ngpio = S5PC100_GPIO_A0_NR, + .label = "GPA0", + }, + }, { + .chip = { + .base = S5PC100_GPA1(0), + .ngpio = S5PC100_GPIO_A1_NR, + .label = "GPA1", + }, + }, { + .chip = { + .base = S5PC100_GPB(0), + .ngpio = S5PC100_GPIO_B_NR, + .label = "GPB", + }, + }, { + .chip = { + .base = S5PC100_GPC(0), + .ngpio = S5PC100_GPIO_C_NR, + .label = "GPC", + }, + }, { + .chip = { + .base = S5PC100_GPD(0), + .ngpio = S5PC100_GPIO_D_NR, + .label = "GPD", + }, + }, { + .chip = { + .base = S5PC100_GPE0(0), + .ngpio = S5PC100_GPIO_E0_NR, + .label = "GPE0", + }, + }, { + .chip = { + .base = S5PC100_GPE1(0), + .ngpio = S5PC100_GPIO_E1_NR, + .label = "GPE1", + }, + }, { + .chip = { + .base = S5PC100_GPF0(0), + .ngpio = S5PC100_GPIO_F0_NR, + .label = "GPF0", + }, + }, { + .chip = { + .base = S5PC100_GPF1(0), + .ngpio = S5PC100_GPIO_F1_NR, + .label = "GPF1", + }, + }, { + .chip = { + .base = S5PC100_GPF2(0), + .ngpio = S5PC100_GPIO_F2_NR, + .label = "GPF2", + }, + }, { + .chip = { + .base = S5PC100_GPF3(0), + .ngpio = S5PC100_GPIO_F3_NR, + .label = "GPF3", + }, + }, { + .chip = { + .base = S5PC100_GPG0(0), + .ngpio = S5PC100_GPIO_G0_NR, + .label = "GPG0", + }, + }, { + .chip = { + .base = S5PC100_GPG1(0), + .ngpio = S5PC100_GPIO_G1_NR, + .label = "GPG1", + }, + }, { + .chip = { + .base = S5PC100_GPG2(0), + .ngpio = S5PC100_GPIO_G2_NR, + .label = "GPG2", + }, + }, { + .chip = { + .base = S5PC100_GPG3(0), + .ngpio = S5PC100_GPIO_G3_NR, + .label = "GPG3", + }, + }, { + .chip = { + .base = S5PC100_GPI(0), + .ngpio = S5PC100_GPIO_I_NR, + .label = "GPI", + }, + }, { + .chip = { + .base = S5PC100_GPJ0(0), + .ngpio = S5PC100_GPIO_J0_NR, + .label = "GPJ0", + }, + }, { + .chip = { + .base = S5PC100_GPJ1(0), + .ngpio = S5PC100_GPIO_J1_NR, + .label = "GPJ1", + }, + }, { + .chip = { + .base = S5PC100_GPJ2(0), + .ngpio = S5PC100_GPIO_J2_NR, + .label = "GPJ2", + }, + }, { + .chip = { + .base = S5PC100_GPJ3(0), + .ngpio = S5PC100_GPIO_J3_NR, + .label = "GPJ3", + }, + }, { + .chip = { + .base = S5PC100_GPJ4(0), + .ngpio = S5PC100_GPIO_J4_NR, + .label = "GPJ4", + }, + }, { + .chip = { + .base = S5PC100_GPK0(0), + .ngpio = S5PC100_GPIO_K0_NR, + .label = "GPK0", + }, + }, { + .chip = { + .base = S5PC100_GPK1(0), + .ngpio = S5PC100_GPIO_K1_NR, + .label = "GPK1", + }, + }, { + .chip = { + .base = S5PC100_GPK2(0), + .ngpio = S5PC100_GPIO_K2_NR, + .label = "GPK2", + }, + }, { + .chip = { + .base = S5PC100_GPK3(0), + .ngpio = S5PC100_GPIO_K3_NR, + .label = "GPK3", + }, + }, { + .chip = { + .base = S5PC100_GPL0(0), + .ngpio = S5PC100_GPIO_L0_NR, + .label = "GPL0", + }, + }, { + .chip = { + .base = S5PC100_GPL1(0), + .ngpio = S5PC100_GPIO_L1_NR, + .label = "GPL1", + }, + }, { + .chip = { + .base = S5PC100_GPL2(0), + .ngpio = S5PC100_GPIO_L2_NR, + .label = "GPL2", + }, + }, { + .chip = { + .base = S5PC100_GPL3(0), + .ngpio = S5PC100_GPIO_L3_NR, + .label = "GPL3", + }, + }, { + .chip = { + .base = S5PC100_GPL4(0), + .ngpio = S5PC100_GPIO_L4_NR, + .label = "GPL4", + }, + }, { + .base = (S5P_VA_GPIO + 0xC00), + .irq_base = IRQ_EINT(0), + .chip = { + .base = S5PC100_GPH0(0), + .ngpio = S5PC100_GPIO_H0_NR, + .label = "GPH0", + .to_irq = samsung_gpiolib_to_irq, + }, + }, { + .base = (S5P_VA_GPIO + 0xC20), + .irq_base = IRQ_EINT(8), + .chip = { + .base = S5PC100_GPH1(0), + .ngpio = S5PC100_GPIO_H1_NR, + .label = "GPH1", + .to_irq = samsung_gpiolib_to_irq, + }, + }, { + .base = (S5P_VA_GPIO + 0xC40), + .irq_base = IRQ_EINT(16), + .chip = { + .base = S5PC100_GPH2(0), + .ngpio = S5PC100_GPIO_H2_NR, + .label = "GPH2", + .to_irq = samsung_gpiolib_to_irq, + }, + }, { + .base = (S5P_VA_GPIO + 0xC60), + .irq_base = IRQ_EINT(24), + .chip = { + .base = S5PC100_GPH3(0), + .ngpio = S5PC100_GPIO_H3_NR, + .label = "GPH3", + .to_irq = samsung_gpiolib_to_irq, + }, + }, +#endif +}; + +/* + * Followings are the gpio banks in S5PV210/S5PC110 + * + * The 'config' member when left to NULL, is initialized to the default + * structure samsung_gpio_cfgs[4] in the init function below. + * + * The 'base' member is also initialized in the init function below. + * Note: The initialization of 'base' member of samsung_gpio_chip structure + * uses the above macro and depends on the banks being listed in order here. + */ + +static struct samsung_gpio_chip s5pv210_gpios_4bit[] = { +#ifdef CONFIG_CPU_S5PV210 + { + .chip = { + .base = S5PV210_GPA0(0), + .ngpio = S5PV210_GPIO_A0_NR, + .label = "GPA0", + }, + }, { + .chip = { + .base = S5PV210_GPA1(0), + .ngpio = S5PV210_GPIO_A1_NR, + .label = "GPA1", + }, + }, { + .chip = { + .base = S5PV210_GPB(0), + .ngpio = S5PV210_GPIO_B_NR, + .label = "GPB", + }, + }, { + .chip = { + .base = S5PV210_GPC0(0), + .ngpio = S5PV210_GPIO_C0_NR, + .label = "GPC0", + }, + }, { + .chip = { + .base = S5PV210_GPC1(0), + .ngpio = S5PV210_GPIO_C1_NR, + .label = "GPC1", + }, + }, { + .chip = { + .base = S5PV210_GPD0(0), + .ngpio = S5PV210_GPIO_D0_NR, + .label = "GPD0", + }, + }, { + .chip = { + .base = S5PV210_GPD1(0), + .ngpio = S5PV210_GPIO_D1_NR, + .label = "GPD1", + }, + }, { + .chip = { + .base = S5PV210_GPE0(0), + .ngpio = S5PV210_GPIO_E0_NR, + .label = "GPE0", + }, + }, { + .chip = { + .base = S5PV210_GPE1(0), + .ngpio = S5PV210_GPIO_E1_NR, + .label = "GPE1", + }, + }, { + .chip = { + .base = S5PV210_GPF0(0), + .ngpio = S5PV210_GPIO_F0_NR, + .label = "GPF0", + }, + }, { + .chip = { + .base = S5PV210_GPF1(0), + .ngpio = S5PV210_GPIO_F1_NR, + .label = "GPF1", + }, + }, { + .chip = { + .base = S5PV210_GPF2(0), + .ngpio = S5PV210_GPIO_F2_NR, + .label = "GPF2", + }, + }, { + .chip = { + .base = S5PV210_GPF3(0), + .ngpio = S5PV210_GPIO_F3_NR, + .label = "GPF3", + }, + }, { + .chip = { + .base = S5PV210_GPG0(0), + .ngpio = S5PV210_GPIO_G0_NR, + .label = "GPG0", + }, + }, { + .chip = { + .base = S5PV210_GPG1(0), + .ngpio = S5PV210_GPIO_G1_NR, + .label = "GPG1", + }, + }, { + .chip = { + .base = S5PV210_GPG2(0), + .ngpio = S5PV210_GPIO_G2_NR, + .label = "GPG2", + }, + }, { + .chip = { + .base = S5PV210_GPG3(0), + .ngpio = S5PV210_GPIO_G3_NR, + .label = "GPG3", + }, + }, { + .chip = { + .base = S5PV210_GPI(0), + .ngpio = S5PV210_GPIO_I_NR, + .label = "GPI", + }, + }, { + .chip = { + .base = S5PV210_GPJ0(0), + .ngpio = S5PV210_GPIO_J0_NR, + .label = "GPJ0", + }, + }, { + .chip = { + .base = S5PV210_GPJ1(0), + .ngpio = S5PV210_GPIO_J1_NR, + .label = "GPJ1", + }, + }, { + .chip = { + .base = S5PV210_GPJ2(0), + .ngpio = S5PV210_GPIO_J2_NR, + .label = "GPJ2", + }, + }, { + .chip = { + .base = S5PV210_GPJ3(0), + .ngpio = S5PV210_GPIO_J3_NR, + .label = "GPJ3", + }, + }, { + .chip = { + .base = S5PV210_GPJ4(0), + .ngpio = S5PV210_GPIO_J4_NR, + .label = "GPJ4", + }, + }, { + .chip = { + .base = S5PV210_MP01(0), + .ngpio = S5PV210_GPIO_MP01_NR, + .label = "MP01", + }, + }, { + .chip = { + .base = S5PV210_MP02(0), + .ngpio = S5PV210_GPIO_MP02_NR, + .label = "MP02", + }, + }, { + .chip = { + .base = S5PV210_MP03(0), + .ngpio = S5PV210_GPIO_MP03_NR, + .label = "MP03", + }, + }, { + .chip = { + .base = S5PV210_MP04(0), + .ngpio = S5PV210_GPIO_MP04_NR, + .label = "MP04", + }, + }, { + .chip = { + .base = S5PV210_MP05(0), + .ngpio = S5PV210_GPIO_MP05_NR, + .label = "MP05", + }, + }, { + .base = (S5P_VA_GPIO + 0xC00), + .irq_base = IRQ_EINT(0), + .chip = { + .base = S5PV210_GPH0(0), + .ngpio = S5PV210_GPIO_H0_NR, + .label = "GPH0", + .to_irq = samsung_gpiolib_to_irq, + }, + }, { + .base = (S5P_VA_GPIO + 0xC20), + .irq_base = IRQ_EINT(8), + .chip = { + .base = S5PV210_GPH1(0), + .ngpio = S5PV210_GPIO_H1_NR, + .label = "GPH1", + .to_irq = samsung_gpiolib_to_irq, + }, + }, { + .base = (S5P_VA_GPIO + 0xC40), + .irq_base = IRQ_EINT(16), + .chip = { + .base = S5PV210_GPH2(0), + .ngpio = S5PV210_GPIO_H2_NR, + .label = "GPH2", + .to_irq = samsung_gpiolib_to_irq, + }, + }, { + .base = (S5P_VA_GPIO + 0xC60), + .irq_base = IRQ_EINT(24), + .chip = { + .base = S5PV210_GPH3(0), + .ngpio = S5PV210_GPIO_H3_NR, + .label = "GPH3", + .to_irq = samsung_gpiolib_to_irq, + }, + }, +#endif +}; + +/* + * Followings are the gpio banks in EXYNOS4210 + * + * The 'config' member when left to NULL, is initialized to the default + * structure samsung_gpio_cfgs[4] in the init function below. + * + * The 'base' member is also initialized in the init function below. + * Note: The initialization of 'base' member of samsung_gpio_chip structure + * uses the above macro and depends on the banks being listed in order here. + */ + +static struct samsung_gpio_chip exynos4_gpios_1[] = { +#ifdef CONFIG_ARCH_EXYNOS4 + { + .chip = { + .base = EXYNOS4_GPA0(0), + .ngpio = EXYNOS4_GPIO_A0_NR, + .label = "GPA0", + }, + }, { + .chip = { + .base = EXYNOS4_GPA1(0), + .ngpio = EXYNOS4_GPIO_A1_NR, + .label = "GPA1", + }, + }, { + .chip = { + .base = EXYNOS4_GPB(0), + .ngpio = EXYNOS4_GPIO_B_NR, + .label = "GPB", + }, + }, { + .chip = { + .base = EXYNOS4_GPC0(0), + .ngpio = EXYNOS4_GPIO_C0_NR, + .label = "GPC0", + }, + }, { + .chip = { + .base = EXYNOS4_GPC1(0), + .ngpio = EXYNOS4_GPIO_C1_NR, + .label = "GPC1", + }, + }, { + .chip = { + .base = EXYNOS4_GPD0(0), + .ngpio = EXYNOS4_GPIO_D0_NR, + .label = "GPD0", + }, + }, { + .chip = { + .base = EXYNOS4_GPD1(0), + .ngpio = EXYNOS4_GPIO_D1_NR, + .label = "GPD1", + }, + }, { + .chip = { + .base = EXYNOS4_GPE0(0), + .ngpio = EXYNOS4_GPIO_E0_NR, + .label = "GPE0", + }, + }, { + .chip = { + .base = EXYNOS4_GPE1(0), + .ngpio = EXYNOS4_GPIO_E1_NR, + .label = "GPE1", + }, + }, { + .chip = { + .base = EXYNOS4_GPE2(0), + .ngpio = EXYNOS4_GPIO_E2_NR, + .label = "GPE2", + }, + }, { + .chip = { + .base = EXYNOS4_GPE3(0), + .ngpio = EXYNOS4_GPIO_E3_NR, + .label = "GPE3", + }, + }, { + .chip = { + .base = EXYNOS4_GPE4(0), + .ngpio = EXYNOS4_GPIO_E4_NR, + .label = "GPE4", + }, + }, { + .chip = { + .base = EXYNOS4_GPF0(0), + .ngpio = EXYNOS4_GPIO_F0_NR, + .label = "GPF0", + }, + }, { + .chip = { + .base = EXYNOS4_GPF1(0), + .ngpio = EXYNOS4_GPIO_F1_NR, + .label = "GPF1", + }, + }, { + .chip = { + .base = EXYNOS4_GPF2(0), + .ngpio = EXYNOS4_GPIO_F2_NR, + .label = "GPF2", + }, + }, { + .chip = { + .base = EXYNOS4_GPF3(0), + .ngpio = EXYNOS4_GPIO_F3_NR, + .label = "GPF3", + }, + }, +#endif +}; + +static struct samsung_gpio_chip exynos4_gpios_2[] = { +#ifdef CONFIG_ARCH_EXYNOS4 + { + .chip = { + .base = EXYNOS4_GPJ0(0), + .ngpio = EXYNOS4_GPIO_J0_NR, + .label = "GPJ0", + }, + }, { + .chip = { + .base = EXYNOS4_GPJ1(0), + .ngpio = EXYNOS4_GPIO_J1_NR, + .label = "GPJ1", + }, + }, { + .chip = { + .base = EXYNOS4_GPK0(0), + .ngpio = EXYNOS4_GPIO_K0_NR, + .label = "GPK0", + }, + }, { + .chip = { + .base = EXYNOS4_GPK1(0), + .ngpio = EXYNOS4_GPIO_K1_NR, + .label = "GPK1", + }, + }, { + .chip = { + .base = EXYNOS4_GPK2(0), + .ngpio = EXYNOS4_GPIO_K2_NR, + .label = "GPK2", + }, + }, { + .chip = { + .base = EXYNOS4_GPK3(0), + .ngpio = EXYNOS4_GPIO_K3_NR, + .label = "GPK3", + }, + }, { + .chip = { + .base = EXYNOS4_GPL0(0), + .ngpio = EXYNOS4_GPIO_L0_NR, + .label = "GPL0", + }, + }, { + .chip = { + .base = EXYNOS4_GPL1(0), + .ngpio = EXYNOS4_GPIO_L1_NR, + .label = "GPL1", + }, + }, { + .chip = { + .base = EXYNOS4_GPL2(0), + .ngpio = EXYNOS4_GPIO_L2_NR, + .label = "GPL2", + }, + }, { + .config = &samsung_gpio_cfgs[4], + .chip = { + .base = EXYNOS4_GPY0(0), + .ngpio = EXYNOS4_GPIO_Y0_NR, + .label = "GPY0", + }, + }, { + .config = &samsung_gpio_cfgs[4], + .chip = { + .base = EXYNOS4_GPY1(0), + .ngpio = EXYNOS4_GPIO_Y1_NR, + .label = "GPY1", + }, + }, { + .config = &samsung_gpio_cfgs[4], + .chip = { + .base = EXYNOS4_GPY2(0), + .ngpio = EXYNOS4_GPIO_Y2_NR, + .label = "GPY2", + }, + }, { + .config = &samsung_gpio_cfgs[4], + .chip = { + .base = EXYNOS4_GPY3(0), + .ngpio = EXYNOS4_GPIO_Y3_NR, + .label = "GPY3", + }, + }, { + .config = &samsung_gpio_cfgs[4], + .chip = { + .base = EXYNOS4_GPY4(0), + .ngpio = EXYNOS4_GPIO_Y4_NR, + .label = "GPY4", + }, + }, { + .config = &samsung_gpio_cfgs[4], + .chip = { + .base = EXYNOS4_GPY5(0), + .ngpio = EXYNOS4_GPIO_Y5_NR, + .label = "GPY5", + }, + }, { + .config = &samsung_gpio_cfgs[4], + .chip = { + .base = EXYNOS4_GPY6(0), + .ngpio = EXYNOS4_GPIO_Y6_NR, + .label = "GPY6", + }, + }, { + .base = (S5P_VA_GPIO2 + 0xC00), + .config = &samsung_gpio_cfgs[4], + .irq_base = IRQ_EINT(0), + .chip = { + .base = EXYNOS4_GPX0(0), + .ngpio = EXYNOS4_GPIO_X0_NR, + .label = "GPX0", + .to_irq = samsung_gpiolib_to_irq, + }, + }, { + .base = (S5P_VA_GPIO2 + 0xC20), + .config = &samsung_gpio_cfgs[4], + .irq_base = IRQ_EINT(8), + .chip = { + .base = EXYNOS4_GPX1(0), + .ngpio = EXYNOS4_GPIO_X1_NR, + .label = "GPX1", + .to_irq = samsung_gpiolib_to_irq, + }, + }, { + .base = (S5P_VA_GPIO2 + 0xC40), + .config = &samsung_gpio_cfgs[4], + .irq_base = IRQ_EINT(16), + .chip = { + .base = EXYNOS4_GPX2(0), + .ngpio = EXYNOS4_GPIO_X2_NR, + .label = "GPX2", + .to_irq = samsung_gpiolib_to_irq, + }, + }, { + .base = (S5P_VA_GPIO2 + 0xC60), + .config = &samsung_gpio_cfgs[4], + .irq_base = IRQ_EINT(24), + .chip = { + .base = EXYNOS4_GPX3(0), + .ngpio = EXYNOS4_GPIO_X3_NR, + .label = "GPX3", + .to_irq = samsung_gpiolib_to_irq, + }, + }, +#endif +}; + +static struct samsung_gpio_chip exynos4_gpios_3[] = { +#ifdef CONFIG_ARCH_EXYNOS4 + { + .chip = { + .base = EXYNOS4_GPZ(0), + .ngpio = EXYNOS4_GPIO_Z_NR, + .label = "GPZ", + }, + }, +#endif +}; + +/* TODO: cleanup soc_is_* */ +static __init int samsung_gpiolib_init(void) +{ + struct samsung_gpio_chip *chip; + int i, nr_chips; + int group = 0; + + samsung_gpiolib_set_cfg(samsung_gpio_cfgs, ARRAY_SIZE(samsung_gpio_cfgs)); + + if (soc_is_s3c24xx()) { + s3c24xx_gpiolib_add_chips(s3c24xx_gpios, + ARRAY_SIZE(s3c24xx_gpios), S3C24XX_VA_GPIO); + } else if (soc_is_s3c64xx()) { + samsung_gpiolib_add_2bit_chips(s3c64xx_gpios_2bit, + ARRAY_SIZE(s3c64xx_gpios_2bit), + S3C64XX_VA_GPIO + 0xE0, 0x20); + samsung_gpiolib_add_4bit_chips(s3c64xx_gpios_4bit, + ARRAY_SIZE(s3c64xx_gpios_4bit), + S3C64XX_VA_GPIO); + samsung_gpiolib_add_4bit2_chips(s3c64xx_gpios_4bit2, + ARRAY_SIZE(s3c64xx_gpios_4bit2)); + } else if (soc_is_s5p6440()) { + samsung_gpiolib_add_2bit_chips(s5p6440_gpios_2bit, + ARRAY_SIZE(s5p6440_gpios_2bit), NULL, 0x0); + samsung_gpiolib_add_4bit_chips(s5p6440_gpios_4bit, + ARRAY_SIZE(s5p6440_gpios_4bit), S5P_VA_GPIO); + samsung_gpiolib_add_4bit2_chips(s5p6440_gpios_4bit2, + ARRAY_SIZE(s5p6440_gpios_4bit2)); + s5p64x0_gpiolib_add_rbank(s5p6440_gpios_rbank, + ARRAY_SIZE(s5p6440_gpios_rbank)); + } else if (soc_is_s5p6450()) { + samsung_gpiolib_add_2bit_chips(s5p6450_gpios_2bit, + ARRAY_SIZE(s5p6450_gpios_2bit), NULL, 0x0); + samsung_gpiolib_add_4bit_chips(s5p6450_gpios_4bit, + ARRAY_SIZE(s5p6450_gpios_4bit), S5P_VA_GPIO); + samsung_gpiolib_add_4bit2_chips(s5p6450_gpios_4bit2, + ARRAY_SIZE(s5p6450_gpios_4bit2)); + s5p64x0_gpiolib_add_rbank(s5p6450_gpios_rbank, + ARRAY_SIZE(s5p6450_gpios_rbank)); + } else if (soc_is_s5pc100()) { + group = 0; + chip = s5pc100_gpios_4bit; + nr_chips = ARRAY_SIZE(s5pc100_gpios_4bit); + + for (i = 0; i < nr_chips; i++, chip++) { + if (!chip->config) { + chip->config = &samsung_gpio_cfgs[4]; + chip->group = group++; + } + } + samsung_gpiolib_add_4bit_chips(s5pc100_gpios_4bit, nr_chips, S5P_VA_GPIO); +#if defined(CONFIG_CPU_S5PC100) && defined(CONFIG_S5P_GPIO_INT) + s5p_register_gpioint_bank(IRQ_GPIOINT, 0, S5P_GPIOINT_GROUP_MAXNR); +#endif + } else if (soc_is_s5pv210()) { + group = 0; + chip = s5pv210_gpios_4bit; + nr_chips = ARRAY_SIZE(s5pv210_gpios_4bit); + + for (i = 0; i < nr_chips; i++, chip++) { + if (!chip->config) { + chip->config = &samsung_gpio_cfgs[4]; + chip->group = group++; + } + } + samsung_gpiolib_add_4bit_chips(s5pv210_gpios_4bit, nr_chips, S5P_VA_GPIO); +#if defined(CONFIG_CPU_S5PV210) && defined(CONFIG_S5P_GPIO_INT) + s5p_register_gpioint_bank(IRQ_GPIOINT, 0, S5P_GPIOINT_GROUP_MAXNR); +#endif + } else if (soc_is_exynos4210()) { + group = 0; + + /* gpio part1 */ + chip = exynos4_gpios_1; + nr_chips = ARRAY_SIZE(exynos4_gpios_1); + + for (i = 0; i < nr_chips; i++, chip++) { + if (!chip->config) { + chip->config = &exynos4_gpio_cfg; + chip->group = group++; + } + } + samsung_gpiolib_add_4bit_chips(exynos4_gpios_1, nr_chips, S5P_VA_GPIO1); + + /* gpio part2 */ + chip = exynos4_gpios_2; + nr_chips = ARRAY_SIZE(exynos4_gpios_2); + + for (i = 0; i < nr_chips; i++, chip++) { + if (!chip->config) { + chip->config = &exynos4_gpio_cfg; + chip->group = group++; + } + } + samsung_gpiolib_add_4bit_chips(exynos4_gpios_2, nr_chips, S5P_VA_GPIO2); + + /* gpio part3 */ + chip = exynos4_gpios_3; + nr_chips = ARRAY_SIZE(exynos4_gpios_3); + + for (i = 0; i < nr_chips; i++, chip++) { + if (!chip->config) { + chip->config = &exynos4_gpio_cfg; + chip->group = group++; + } + } + samsung_gpiolib_add_4bit_chips(exynos4_gpios_3, nr_chips, S5P_VA_GPIO3); + +#if defined(CONFIG_SOC_EXYNOS4210) && defined(CONFIG_S5P_GPIO_INT) + s5p_register_gpioint_bank(IRQ_GPIO_XA, 0, IRQ_GPIO1_NR_GROUPS); + s5p_register_gpioint_bank(IRQ_GPIO_XB, IRQ_GPIO1_NR_GROUPS, IRQ_GPIO2_NR_GROUPS); +#endif + } + + return 0; +} +core_initcall(samsung_gpiolib_init); + +int s3c_gpio_cfgpin(unsigned int pin, unsigned int config) +{ + struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin); + unsigned long flags; + int offset; + int ret; + + if (!chip) + return -EINVAL; + + offset = pin - chip->chip.base; + + samsung_gpio_lock(chip, flags); + ret = samsung_gpio_do_setcfg(chip, offset, config); + samsung_gpio_unlock(chip, flags); + + return ret; +} +EXPORT_SYMBOL(s3c_gpio_cfgpin); + +int s3c_gpio_cfgpin_range(unsigned int start, unsigned int nr, + unsigned int cfg) +{ + int ret; + + for (; nr > 0; nr--, start++) { + ret = s3c_gpio_cfgpin(start, cfg); + if (ret != 0) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(s3c_gpio_cfgpin_range); + +int s3c_gpio_cfgall_range(unsigned int start, unsigned int nr, + unsigned int cfg, samsung_gpio_pull_t pull) +{ + int ret; + + for (; nr > 0; nr--, start++) { + s3c_gpio_setpull(start, pull); + ret = s3c_gpio_cfgpin(start, cfg); + if (ret != 0) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(s3c_gpio_cfgall_range); + +unsigned s3c_gpio_getcfg(unsigned int pin) +{ + struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin); + unsigned long flags; + unsigned ret = 0; + int offset; + + if (chip) { + offset = pin - chip->chip.base; + + samsung_gpio_lock(chip, flags); + ret = samsung_gpio_do_getcfg(chip, offset); + samsung_gpio_unlock(chip, flags); + } + + return ret; +} +EXPORT_SYMBOL(s3c_gpio_getcfg); + +int s3c_gpio_setpull(unsigned int pin, samsung_gpio_pull_t pull) +{ + struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin); + unsigned long flags; + int offset, ret; + + if (!chip) + return -EINVAL; + + offset = pin - chip->chip.base; + + samsung_gpio_lock(chip, flags); + ret = samsung_gpio_do_setpull(chip, offset, pull); + samsung_gpio_unlock(chip, flags); + + return ret; +} +EXPORT_SYMBOL(s3c_gpio_setpull); + +samsung_gpio_pull_t s3c_gpio_getpull(unsigned int pin) +{ + struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin); + unsigned long flags; + int offset; + u32 pup = 0; + + if (chip) { + offset = pin - chip->chip.base; + + samsung_gpio_lock(chip, flags); + pup = samsung_gpio_do_getpull(chip, offset); + samsung_gpio_unlock(chip, flags); + } + + return (__force samsung_gpio_pull_t)pup; +} +EXPORT_SYMBOL(s3c_gpio_getpull); + +/* gpiolib wrappers until these are totally eliminated */ + +void s3c2410_gpio_pullup(unsigned int pin, unsigned int to) +{ + int ret; + + WARN_ON(to); /* should be none of these left */ + + if (!to) { + /* if pull is enabled, try first with up, and if that + * fails, try using down */ + + ret = s3c_gpio_setpull(pin, S3C_GPIO_PULL_UP); + if (ret) + s3c_gpio_setpull(pin, S3C_GPIO_PULL_DOWN); + } else { + s3c_gpio_setpull(pin, S3C_GPIO_PULL_NONE); + } +} +EXPORT_SYMBOL(s3c2410_gpio_pullup); + +void s3c2410_gpio_setpin(unsigned int pin, unsigned int to) +{ + /* do this via gpiolib until all users removed */ + + gpio_request(pin, "temporary"); + gpio_set_value(pin, to); + gpio_free(pin); +} +EXPORT_SYMBOL(s3c2410_gpio_setpin); + +unsigned int s3c2410_gpio_getpin(unsigned int pin) +{ + struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin); + unsigned long offs = pin - chip->chip.base; + + return __raw_readl(chip->base + 0x04) & (1 << offs); +} +EXPORT_SYMBOL(s3c2410_gpio_getpin); + +#ifdef CONFIG_S5P_GPIO_DRVSTR +s5p_gpio_drvstr_t s5p_gpio_get_drvstr(unsigned int pin) +{ + struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin); + unsigned int off; + void __iomem *reg; + int shift; + u32 drvstr; + + if (!chip) + return -EINVAL; + + off = pin - chip->chip.base; + shift = off * 2; + reg = chip->base + 0x0C; + + drvstr = __raw_readl(reg); + drvstr = drvstr >> shift; + drvstr &= 0x3; + + return (__force s5p_gpio_drvstr_t)drvstr; +} +EXPORT_SYMBOL(s5p_gpio_get_drvstr); + +int s5p_gpio_set_drvstr(unsigned int pin, s5p_gpio_drvstr_t drvstr) +{ + struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin); + unsigned int off; + void __iomem *reg; + int shift; + u32 tmp; + + if (!chip) + return -EINVAL; + + off = pin - chip->chip.base; + shift = off * 2; + reg = chip->base + 0x0C; + + tmp = __raw_readl(reg); + tmp &= ~(0x3 << shift); + tmp |= drvstr << shift; + + __raw_writel(tmp, reg); + + return 0; +} +EXPORT_SYMBOL(s5p_gpio_set_drvstr); +#endif /* CONFIG_S5P_GPIO_DRVSTR */ + +#ifdef CONFIG_PLAT_S3C24XX +unsigned int s3c2410_modify_misccr(unsigned int clear, unsigned int change) +{ + unsigned long flags; + unsigned long misccr; + + local_irq_save(flags); + misccr = __raw_readl(S3C24XX_MISCCR); + misccr &= ~clear; + misccr ^= change; + __raw_writel(misccr, S3C24XX_MISCCR); + local_irq_restore(flags); + + return misccr; +} +EXPORT_SYMBOL(s3c2410_modify_misccr); +#endif From 782d8a3c0bdf23ec24fc8facb5af8510b2cf6de1 Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Tue, 30 Aug 2011 20:47:32 +0900 Subject: [PATCH 0233/1531] ARM: SAMSUNG: Update the name of regarding Samsung GPIO According to gpio-samsung.c, this patch updates the name of regarding Samsung GPIO. Basically the samsung_xxx prefix is used in gpio-samsung.c instead of s3c_xxx, because unified name can reduce its complexity. Note: some s3c_xxx stil remains because it is used widely. It will be updated next time. Cc: Ben Dooks Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos4/include/mach/pm-core.h | 2 +- arch/arm/mach-s3c2410/include/mach/gpio-fns.h | 99 +--------- .../mach-s3c2410/include/mach/gpio-track.h | 6 +- arch/arm/mach-s3c2410/include/mach/pm-core.h | 2 +- arch/arm/mach-s3c2410/s3c2410.c | 4 +- arch/arm/mach-s3c2412/gpio.c | 2 +- arch/arm/mach-s3c2416/s3c2416.c | 4 +- arch/arm/mach-s3c2440/s3c2440.c | 4 +- arch/arm/mach-s3c2440/s3c2442.c | 4 +- arch/arm/mach-s3c2443/s3c2443.c | 4 +- arch/arm/mach-s3c64xx/include/mach/pm-core.h | 2 +- arch/arm/mach-s5pv210/include/mach/pm-core.h | 2 +- arch/arm/plat-s5p/irq-gpioint.c | 10 +- .../include/plat/gpio-cfg-helpers.h | 172 +++++------------- arch/arm/plat-samsung/include/plat/gpio-cfg.h | 34 ++-- .../arm/plat-samsung/include/plat/gpio-core.h | 97 +++------- arch/arm/plat-samsung/include/plat/gpio-fns.h | 98 ++++++++++ arch/arm/plat-samsung/include/plat/pm.h | 10 +- arch/arm/plat-samsung/pm-gpio.c | 72 ++++---- arch/arm/plat-samsung/pm.c | 6 +- 20 files changed, 250 insertions(+), 384 deletions(-) create mode 100644 arch/arm/plat-samsung/include/plat/gpio-fns.h diff --git a/arch/arm/mach-exynos4/include/mach/pm-core.h b/arch/arm/mach-exynos4/include/mach/pm-core.h index 1df3b81f96e8..3499ec737206 100644 --- a/arch/arm/mach-exynos4/include/mach/pm-core.h +++ b/arch/arm/mach-exynos4/include/mach/pm-core.h @@ -53,7 +53,7 @@ static inline void s3c_pm_restored_gpios(void) /* nothing here yet */ } -static inline void s3c_pm_saved_gpios(void) +static inline void samsung_pm_saved_gpios(void) { /* nothing here yet */ } diff --git a/arch/arm/mach-s3c2410/include/mach/gpio-fns.h b/arch/arm/mach-s3c2410/include/mach/gpio-fns.h index bab139201761..c53ad34c6579 100644 --- a/arch/arm/mach-s3c2410/include/mach/gpio-fns.h +++ b/arch/arm/mach-s3c2410/include/mach/gpio-fns.h @@ -1,98 +1 @@ -/* arch/arm/mach-s3c2410/include/mach/gpio-fns.h - * - * Copyright (c) 2003-2009 Simtec Electronics - * Ben Dooks - * - * S3C2410 - hardware - * - * 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. -*/ - -#ifndef __MACH_GPIO_FNS_H -#define __MACH_GPIO_FNS_H __FILE__ - -/* These functions are in the to-be-removed category and it is strongly - * encouraged not to use these in new code. They will be marked deprecated - * very soon. - * - * Most of the functionality can be either replaced by the gpiocfg calls - * for the s3c platform or by the generic GPIOlib API. - * - * As of 2.6.35-rc, these will be removed, with the few drivers using them - * either replaced or given a wrapper until the calls can be removed. -*/ - -#include - -static inline void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int cfg) -{ - /* 1:1 mapping between cfgpin and setcfg calls at the moment */ - s3c_gpio_cfgpin(pin, cfg); -} - -/* external functions for GPIO support - * - * These allow various different clients to access the same GPIO - * registers without conflicting. If your driver only owns the entire - * GPIO register, then it is safe to ioremap/__raw_{read|write} to it. -*/ - -extern unsigned int s3c2410_gpio_getcfg(unsigned int pin); - -/* s3c2410_gpio_getirq - * - * turn the given pin number into the corresponding IRQ number - * - * returns: - * < 0 = no interrupt for this pin - * >=0 = interrupt number for the pin -*/ - -extern int s3c2410_gpio_getirq(unsigned int pin); - -/* s3c2410_gpio_irqfilter - * - * set the irq filtering on the given pin - * - * on = 0 => disable filtering - * 1 => enable filtering - * - * config = S3C2410_EINTFLT_PCLK or S3C2410_EINTFLT_EXTCLK orred with - * width of filter (0 through 63) - * - * -*/ - -extern int s3c2410_gpio_irqfilter(unsigned int pin, unsigned int on, - unsigned int config); - -/* s3c2410_gpio_pullup - * - * This call should be replaced with s3c_gpio_setpull(). - * - * As a note, there is currently no distinction between pull-up and pull-down - * in the s3c24xx series devices with only an on/off configuration. - */ - -/* s3c2410_gpio_pullup - * - * configure the pull-up control on the given pin - * - * to = 1 => disable the pull-up - * 0 => enable the pull-up - * - * eg; - * - * s3c2410_gpio_pullup(S3C2410_GPB(0), 0); - * s3c2410_gpio_pullup(S3C2410_GPE(8), 0); -*/ - -extern void s3c2410_gpio_pullup(unsigned int pin, unsigned int to); - -extern void s3c2410_gpio_setpin(unsigned int pin, unsigned int to); - -extern unsigned int s3c2410_gpio_getpin(unsigned int pin); - -#endif /* __MACH_GPIO_FNS_H */ +#include diff --git a/arch/arm/mach-s3c2410/include/mach/gpio-track.h b/arch/arm/mach-s3c2410/include/mach/gpio-track.h index d67819dde42a..c410a078622c 100644 --- a/arch/arm/mach-s3c2410/include/mach/gpio-track.h +++ b/arch/arm/mach-s3c2410/include/mach/gpio-track.h @@ -17,11 +17,11 @@ #include -extern struct s3c_gpio_chip s3c24xx_gpios[]; +extern struct samsung_gpio_chip s3c24xx_gpios[]; -static inline struct s3c_gpio_chip *s3c_gpiolib_getchip(unsigned int pin) +static inline struct samsung_gpio_chip *samsung_gpiolib_getchip(unsigned int pin) { - struct s3c_gpio_chip *chip; + struct samsung_gpio_chip *chip; if (pin > S3C_GPIO_END) return NULL; diff --git a/arch/arm/mach-s3c2410/include/mach/pm-core.h b/arch/arm/mach-s3c2410/include/mach/pm-core.h index 45eea5210c87..2eef7e6f7675 100644 --- a/arch/arm/mach-s3c2410/include/mach/pm-core.h +++ b/arch/arm/mach-s3c2410/include/mach/pm-core.h @@ -64,4 +64,4 @@ static inline void s3c_pm_arch_update_uart(void __iomem *regs, } static inline void s3c_pm_restored_gpios(void) { } -static inline void s3c_pm_saved_gpios(void) { } +static inline void samsung_pm_saved_gpios(void) { } diff --git a/arch/arm/mach-s3c2410/s3c2410.c b/arch/arm/mach-s3c2410/s3c2410.c index f1d3bd8f6f17..a99c2f4a523f 100644 --- a/arch/arm/mach-s3c2410/s3c2410.c +++ b/arch/arm/mach-s3c2410/s3c2410.c @@ -72,8 +72,8 @@ void __init s3c2410_init_uarts(struct s3c2410_uartcfg *cfg, int no) void __init s3c2410_map_io(void) { - s3c24xx_gpiocfg_default.set_pull = s3c_gpio_setpull_1up; - s3c24xx_gpiocfg_default.get_pull = s3c_gpio_getpull_1up; + s3c24xx_gpiocfg_default.set_pull = s3c24xx_gpio_setpull_1up; + s3c24xx_gpiocfg_default.get_pull = s3c24xx_gpio_getpull_1up; iotable_init(s3c2410_iodesc, ARRAY_SIZE(s3c2410_iodesc)); } diff --git a/arch/arm/mach-s3c2412/gpio.c b/arch/arm/mach-s3c2412/gpio.c index 3404a876b33e..4526f6ba31a8 100644 --- a/arch/arm/mach-s3c2412/gpio.c +++ b/arch/arm/mach-s3c2412/gpio.c @@ -28,7 +28,7 @@ int s3c2412_gpio_set_sleepcfg(unsigned int pin, unsigned int state) { - struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin); + struct samsung_gpio_chip *chip = samsung_gpiolib_getchip(pin); unsigned long offs = pin - chip->chip.base; unsigned long flags; unsigned long slpcon; diff --git a/arch/arm/mach-s3c2416/s3c2416.c b/arch/arm/mach-s3c2416/s3c2416.c index 494ce913dc95..3156b7a71371 100644 --- a/arch/arm/mach-s3c2416/s3c2416.c +++ b/arch/arm/mach-s3c2416/s3c2416.c @@ -118,8 +118,8 @@ void __init s3c2416_init_uarts(struct s3c2410_uartcfg *cfg, int no) void __init s3c2416_map_io(void) { - s3c24xx_gpiocfg_default.set_pull = s3c_gpio_setpull_updown; - s3c24xx_gpiocfg_default.get_pull = s3c_gpio_getpull_updown; + s3c24xx_gpiocfg_default.set_pull = samsung_gpio_setpull_updown; + s3c24xx_gpiocfg_default.get_pull = samsung_gpio_getpull_updown; /* initialize device information early */ s3c2416_default_sdhci0(); diff --git a/arch/arm/mach-s3c2440/s3c2440.c b/arch/arm/mach-s3c2440/s3c2440.c index ce99ff72838d..fc84e481efcf 100644 --- a/arch/arm/mach-s3c2440/s3c2440.c +++ b/arch/arm/mach-s3c2440/s3c2440.c @@ -68,6 +68,6 @@ void __init s3c2440_map_io(void) { s3c244x_map_io(); - s3c24xx_gpiocfg_default.set_pull = s3c_gpio_setpull_1up; - s3c24xx_gpiocfg_default.get_pull = s3c_gpio_getpull_1up; + s3c24xx_gpiocfg_default.set_pull = s3c24xx_gpio_setpull_1up; + s3c24xx_gpiocfg_default.get_pull = s3c24xx_gpio_getpull_1up; } diff --git a/arch/arm/mach-s3c2440/s3c2442.c b/arch/arm/mach-s3c2440/s3c2442.c index 9ad99f8016a1..48e273ce9f9a 100644 --- a/arch/arm/mach-s3c2440/s3c2442.c +++ b/arch/arm/mach-s3c2440/s3c2442.c @@ -180,6 +180,6 @@ void __init s3c2442_map_io(void) { s3c244x_map_io(); - s3c24xx_gpiocfg_default.set_pull = s3c_gpio_setpull_1down; - s3c24xx_gpiocfg_default.get_pull = s3c_gpio_getpull_1down; + s3c24xx_gpiocfg_default.set_pull = s3c24xx_gpio_setpull_1down; + s3c24xx_gpiocfg_default.get_pull = s3c24xx_gpio_getpull_1down; } diff --git a/arch/arm/mach-s3c2443/s3c2443.c b/arch/arm/mach-s3c2443/s3c2443.c index e6a28ba52c7d..5df6458ddd42 100644 --- a/arch/arm/mach-s3c2443/s3c2443.c +++ b/arch/arm/mach-s3c2443/s3c2443.c @@ -90,8 +90,8 @@ void __init s3c2443_init_uarts(struct s3c2410_uartcfg *cfg, int no) void __init s3c2443_map_io(void) { - s3c24xx_gpiocfg_default.set_pull = s3c_gpio_setpull_s3c2443; - s3c24xx_gpiocfg_default.get_pull = s3c_gpio_getpull_s3c2443; + s3c24xx_gpiocfg_default.set_pull = s3c2443_gpio_setpull; + s3c24xx_gpiocfg_default.get_pull = s3c2443_gpio_getpull; iotable_init(s3c2443_iodesc, ARRAY_SIZE(s3c2443_iodesc)); } diff --git a/arch/arm/mach-s3c64xx/include/mach/pm-core.h b/arch/arm/mach-s3c64xx/include/mach/pm-core.h index 38659bebe4b1..fcf3dcabb694 100644 --- a/arch/arm/mach-s3c64xx/include/mach/pm-core.h +++ b/arch/arm/mach-s3c64xx/include/mach/pm-core.h @@ -104,7 +104,7 @@ static inline void s3c_pm_restored_gpios(void) __raw_writel(0, S3C64XX_SLPEN); } -static inline void s3c_pm_saved_gpios(void) +static inline void samsung_pm_saved_gpios(void) { /* turn on the sleep mode and keep it there, as it seems that during * suspend the xCON registers get re-set and thus you can end up with diff --git a/arch/arm/mach-s5pv210/include/mach/pm-core.h b/arch/arm/mach-s5pv210/include/mach/pm-core.h index 3e22109e1b7b..eba8aea63ed8 100644 --- a/arch/arm/mach-s5pv210/include/mach/pm-core.h +++ b/arch/arm/mach-s5pv210/include/mach/pm-core.h @@ -43,4 +43,4 @@ static inline void s3c_pm_arch_update_uart(void __iomem *regs, } static inline void s3c_pm_restored_gpios(void) { } -static inline void s3c_pm_saved_gpios(void) { } +static inline void samsung_pm_saved_gpios(void) { } diff --git a/arch/arm/plat-s5p/irq-gpioint.c b/arch/arm/plat-s5p/irq-gpioint.c index f71078ef6bb5..0455519aecdf 100644 --- a/arch/arm/plat-s5p/irq-gpioint.c +++ b/arch/arm/plat-s5p/irq-gpioint.c @@ -37,7 +37,7 @@ struct s5p_gpioint_bank { int start; int nr_groups; int irq; - struct s3c_gpio_chip **chips; + struct samsung_gpio_chip **chips; void (*handler)(unsigned int, struct irq_desc *); }; @@ -87,7 +87,7 @@ static void s5p_gpioint_handler(unsigned int irq, struct irq_desc *desc) chained_irq_enter(chip, desc); for (group = 0; group < bank->nr_groups; group++) { - struct s3c_gpio_chip *chip = bank->chips[group]; + struct samsung_gpio_chip *chip = bank->chips[group]; if (!chip) continue; @@ -110,7 +110,7 @@ static void s5p_gpioint_handler(unsigned int irq, struct irq_desc *desc) chained_irq_exit(chip, desc); } -static __init int s5p_gpioint_add(struct s3c_gpio_chip *chip) +static __init int s5p_gpioint_add(struct samsung_gpio_chip *chip) { static int used_gpioint_groups = 0; int group = chip->group; @@ -130,7 +130,7 @@ static __init int s5p_gpioint_add(struct s3c_gpio_chip *chip) return -EINVAL; if (!bank->handler) { - bank->chips = kzalloc(sizeof(struct s3c_gpio_chip *) * + bank->chips = kzalloc(sizeof(struct samsung_gpio_chip *) * bank->nr_groups, GFP_KERNEL); if (!bank->chips) return -ENOMEM; @@ -173,7 +173,7 @@ static __init int s5p_gpioint_add(struct s3c_gpio_chip *chip) int __init s5p_register_gpio_interrupt(int pin) { - struct s3c_gpio_chip *my_chip = s3c_gpiolib_getchip(pin); + struct samsung_gpio_chip *my_chip = samsung_gpiolib_getchip(pin); int offset, group; int ret; diff --git a/arch/arm/plat-samsung/include/plat/gpio-cfg-helpers.h b/arch/arm/plat-samsung/include/plat/gpio-cfg-helpers.h index 9a4e53d52967..a181d7ce81cf 100644 --- a/arch/arm/plat-samsung/include/plat/gpio-cfg-helpers.h +++ b/arch/arm/plat-samsung/include/plat/gpio-cfg-helpers.h @@ -1,11 +1,11 @@ -/* linux/arch/arm/plat-s3c/include/plat/gpio-cfg-helper.h +/* linux/arch/arm/plat-samsung/include/plat/gpio-cfg-helper.h * * Copyright 2008 Openmoko, Inc. * Copyright 2008 Simtec Electronics * http://armlinux.simtec.co.uk/ * Ben Dooks * - * S3C Platform - GPIO pin configuration helper definitions + * Samsung Platform - GPIO pin configuration helper definitions * * 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 @@ -24,120 +24,30 @@ * by disabling interrupts. */ -static inline int s3c_gpio_do_setcfg(struct s3c_gpio_chip *chip, - unsigned int off, unsigned int config) +static inline int samsung_gpio_do_setcfg(struct samsung_gpio_chip *chip, + unsigned int off, unsigned int config) { return (chip->config->set_config)(chip, off, config); } -static inline unsigned s3c_gpio_do_getcfg(struct s3c_gpio_chip *chip, - unsigned int off) +static inline unsigned samsung_gpio_do_getcfg(struct samsung_gpio_chip *chip, + unsigned int off) { return (chip->config->get_config)(chip, off); } -static inline int s3c_gpio_do_setpull(struct s3c_gpio_chip *chip, - unsigned int off, s3c_gpio_pull_t pull) +static inline int samsung_gpio_do_setpull(struct samsung_gpio_chip *chip, + unsigned int off, samsung_gpio_pull_t pull) { return (chip->config->set_pull)(chip, off, pull); } -static inline s3c_gpio_pull_t s3c_gpio_do_getpull(struct s3c_gpio_chip *chip, - unsigned int off) +static inline samsung_gpio_pull_t samsung_gpio_do_getpull(struct samsung_gpio_chip *chip, + unsigned int off) { return chip->config->get_pull(chip, off); } -/** - * s3c_gpio_setcfg_s3c24xx - S3C24XX style GPIO configuration. - * @chip: The gpio chip that is being configured. - * @off: The offset for the GPIO being configured. - * @cfg: The configuration value to set. - * - * This helper deal with the GPIO cases where the control register - * has two bits of configuration per gpio, which have the following - * functions: - * 00 = input - * 01 = output - * 1x = special function -*/ -extern int s3c_gpio_setcfg_s3c24xx(struct s3c_gpio_chip *chip, - unsigned int off, unsigned int cfg); - -/** - * s3c_gpio_getcfg_s3c24xx - S3C24XX style GPIO configuration read. - * @chip: The gpio chip that is being configured. - * @off: The offset for the GPIO being configured. - * - * The reverse of s3c_gpio_setcfg_s3c24xx(). Will return a value whicg - * could be directly passed back to s3c_gpio_setcfg_s3c24xx(), from the - * S3C_GPIO_SPECIAL() macro. - */ -unsigned int s3c_gpio_getcfg_s3c24xx(struct s3c_gpio_chip *chip, - unsigned int off); - -/** - * s3c_gpio_setcfg_s3c24xx_a - S3C24XX style GPIO configuration (Bank A) - * @chip: The gpio chip that is being configured. - * @off: The offset for the GPIO being configured. - * @cfg: The configuration value to set. - * - * This helper deal with the GPIO cases where the control register - * has one bit of configuration for the gpio, where setting the bit - * means the pin is in special function mode and unset means output. -*/ -extern int s3c_gpio_setcfg_s3c24xx_a(struct s3c_gpio_chip *chip, - unsigned int off, unsigned int cfg); - - -/** - * s3c_gpio_getcfg_s3c24xx_a - S3C24XX style GPIO configuration read (Bank A) - * @chip: The gpio chip that is being configured. - * @off: The offset for the GPIO being configured. - * - * The reverse of s3c_gpio_setcfg_s3c24xx_a() turning an GPIO into a usable - * GPIO configuration value. - * - * @sa s3c_gpio_getcfg_s3c24xx - * @sa s3c_gpio_getcfg_s3c64xx_4bit - */ -extern unsigned s3c_gpio_getcfg_s3c24xx_a(struct s3c_gpio_chip *chip, - unsigned int off); - -/** - * s3c_gpio_setcfg_s3c64xx_4bit - S3C64XX 4bit single register GPIO config. - * @chip: The gpio chip that is being configured. - * @off: The offset for the GPIO being configured. - * @cfg: The configuration value to set. - * - * This helper deal with the GPIO cases where the control register has 4 bits - * of control per GPIO, generally in the form of: - * 0000 = Input - * 0001 = Output - * others = Special functions (dependent on bank) - * - * Note, since the code to deal with the case where there are two control - * registers instead of one, we do not have a separate set of functions for - * each case. -*/ -extern int s3c_gpio_setcfg_s3c64xx_4bit(struct s3c_gpio_chip *chip, - unsigned int off, unsigned int cfg); - - -/** - * s3c_gpio_getcfg_s3c64xx_4bit - S3C64XX 4bit single register GPIO config read. - * @chip: The gpio chip that is being configured. - * @off: The offset for the GPIO being configured. - * - * The reverse of s3c_gpio_setcfg_s3c64xx_4bit(), turning a gpio configuration - * register setting into a value the software can use, such as could be passed - * to s3c_gpio_setcfg_s3c64xx_4bit(). - * - * @sa s3c_gpio_getcfg_s3c24xx - */ -extern unsigned s3c_gpio_getcfg_s3c64xx_4bit(struct s3c_gpio_chip *chip, - unsigned int off); - /* Pull-{up,down} resistor controls. * * S3C2410,S3C2440 = Pull-UP, @@ -147,7 +57,7 @@ extern unsigned s3c_gpio_getcfg_s3c64xx_4bit(struct s3c_gpio_chip *chip, */ /** - * s3c_gpio_setpull_1up() - Pull configuration for choice of up or none. + * s3c24xx_gpio_setpull_1up() - Pull configuration for choice of up or none. * @chip: The gpio chip that is being configured. * @off: The offset for the GPIO being configured. * @param: pull: The pull mode being requested. @@ -155,11 +65,11 @@ extern unsigned s3c_gpio_getcfg_s3c64xx_4bit(struct s3c_gpio_chip *chip, * This is a helper function for the case where we have GPIOs with one * bit configuring the presence of a pull-up resistor. */ -extern int s3c_gpio_setpull_1up(struct s3c_gpio_chip *chip, - unsigned int off, s3c_gpio_pull_t pull); +extern int s3c24xx_gpio_setpull_1up(struct samsung_gpio_chip *chip, + unsigned int off, samsung_gpio_pull_t pull); /** - * s3c_gpio_setpull_1down() - Pull configuration for choice of down or none + * s3c24xx_gpio_setpull_1down() - Pull configuration for choice of down or none * @chip: The gpio chip that is being configured * @off: The offset for the GPIO being configured * @param: pull: The pull mode being requested @@ -167,11 +77,13 @@ extern int s3c_gpio_setpull_1up(struct s3c_gpio_chip *chip, * This is a helper function for the case where we have GPIOs with one * bit configuring the presence of a pull-down resistor. */ -extern int s3c_gpio_setpull_1down(struct s3c_gpio_chip *chip, - unsigned int off, s3c_gpio_pull_t pull); +extern int s3c24xx_gpio_setpull_1down(struct samsung_gpio_chip *chip, + unsigned int off, samsung_gpio_pull_t pull); /** - * s3c_gpio_setpull_upown() - Pull configuration for choice of up, down or none + * samsung_gpio_setpull_upown() - Pull configuration for choice of up, + * down or none + * * @chip: The gpio chip that is being configured. * @off: The offset for the GPIO being configured. * @param: pull: The pull mode being requested. @@ -183,45 +95,46 @@ extern int s3c_gpio_setpull_1down(struct s3c_gpio_chip *chip, * 01 = Pull-up resistor connected * 10 = Pull-down resistor connected */ -extern int s3c_gpio_setpull_updown(struct s3c_gpio_chip *chip, - unsigned int off, s3c_gpio_pull_t pull); - +extern int samsung_gpio_setpull_updown(struct samsung_gpio_chip *chip, + unsigned int off, samsung_gpio_pull_t pull); /** - * s3c_gpio_getpull_updown() - Get configuration for choice of up, down or none + * samsung_gpio_getpull_updown() - Get configuration for choice of up, + * down or none + * * @chip: The gpio chip that the GPIO pin belongs to * @off: The offset to the pin to get the configuration of. * - * This helper function reads the state of the pull-{up,down} resistor for the - * given GPIO in the same case as s3c_gpio_setpull_upown. + * This helper function reads the state of the pull-{up,down} resistor + * for the given GPIO in the same case as samsung_gpio_setpull_upown. */ -extern s3c_gpio_pull_t s3c_gpio_getpull_updown(struct s3c_gpio_chip *chip, - unsigned int off); +extern samsung_gpio_pull_t samsung_gpio_getpull_updown(struct samsung_gpio_chip *chip, + unsigned int off); /** - * s3c_gpio_getpull_1up() - Get configuration for choice of up or none + * s3c24xx_gpio_getpull_1up() - Get configuration for choice of up or none * @chip: The gpio chip that the GPIO pin belongs to * @off: The offset to the pin to get the configuration of. * * This helper function reads the state of the pull-up resistor for the - * given GPIO in the same case as s3c_gpio_setpull_1up. + * given GPIO in the same case as s3c24xx_gpio_setpull_1up. */ -extern s3c_gpio_pull_t s3c_gpio_getpull_1up(struct s3c_gpio_chip *chip, - unsigned int off); +extern samsung_gpio_pull_t s3c24xx_gpio_getpull_1up(struct samsung_gpio_chip *chip, + unsigned int off); /** - * s3c_gpio_getpull_1down() - Get configuration for choice of down or none + * s3c24xx_gpio_getpull_1down() - Get configuration for choice of down or none * @chip: The gpio chip that the GPIO pin belongs to * @off: The offset to the pin to get the configuration of. * * This helper function reads the state of the pull-down resistor for the - * given GPIO in the same case as s3c_gpio_setpull_1down. + * given GPIO in the same case as s3c24xx_gpio_setpull_1down. */ -extern s3c_gpio_pull_t s3c_gpio_getpull_1down(struct s3c_gpio_chip *chip, - unsigned int off); +extern samsung_gpio_pull_t s3c24xx_gpio_getpull_1down(struct samsung_gpio_chip *chip, + unsigned int off); /** - * s3c_gpio_setpull_s3c2443() - Pull configuration for s3c2443. + * s3c2443_gpio_setpull() - Pull configuration for s3c2443. * @chip: The gpio chip that is being configured. * @off: The offset for the GPIO being configured. * @param: pull: The pull mode being requested. @@ -233,19 +146,18 @@ extern s3c_gpio_pull_t s3c_gpio_getpull_1down(struct s3c_gpio_chip *chip, * 10 = Pull-down resistor connected * x1 = No pull up resistor */ -extern int s3c_gpio_setpull_s3c2443(struct s3c_gpio_chip *chip, - unsigned int off, s3c_gpio_pull_t pull); +extern int s3c2443_gpio_setpull(struct samsung_gpio_chip *chip, + unsigned int off, samsung_gpio_pull_t pull); /** - * s3c_gpio_getpull_s3c2443() - Get configuration for s3c2443 pull resistors + * s3c2443_gpio_getpull() - Get configuration for s3c2443 pull resistors * @chip: The gpio chip that the GPIO pin belongs to. * @off: The offset to the pin to get the configuration of. * * This helper function reads the state of the pull-{up,down} resistor for the - * given GPIO in the same case as s3c_gpio_setpull_upown. + * given GPIO in the same case as samsung_gpio_setpull_upown. */ -extern s3c_gpio_pull_t s3c_gpio_getpull_s3c2443(struct s3c_gpio_chip *chip, +extern samsung_gpio_pull_t s3c2443_gpio_getpull(struct samsung_gpio_chip *chip, unsigned int off); #endif /* __PLAT_GPIO_CFG_HELPERS_H */ - diff --git a/arch/arm/plat-samsung/include/plat/gpio-cfg.h b/arch/arm/plat-samsung/include/plat/gpio-cfg.h index 1762dcb4cb9e..d48245bb02b3 100644 --- a/arch/arm/plat-samsung/include/plat/gpio-cfg.h +++ b/arch/arm/plat-samsung/include/plat/gpio-cfg.h @@ -24,14 +24,14 @@ #ifndef __PLAT_GPIO_CFG_H #define __PLAT_GPIO_CFG_H __FILE__ -typedef unsigned int __bitwise__ s3c_gpio_pull_t; +typedef unsigned int __bitwise__ samsung_gpio_pull_t; typedef unsigned int __bitwise__ s5p_gpio_drvstr_t; /* forward declaration if gpio-core.h hasn't been included */ -struct s3c_gpio_chip; +struct samsung_gpio_chip; /** - * struct s3c_gpio_cfg GPIO configuration + * struct samsung_gpio_cfg GPIO configuration * @cfg_eint: Configuration setting when used for external interrupt source * @get_pull: Read the current pull configuration for the GPIO * @set_pull: Set the current pull configuraiton for the GPIO @@ -44,20 +44,20 @@ struct s3c_gpio_chip; * per-bank configuration information that other systems such as the * external interrupt code will need. * - * @sa s3c_gpio_cfgpin + * @sa samsung_gpio_cfgpin * @sa s3c_gpio_getcfg * @sa s3c_gpio_setpull * @sa s3c_gpio_getpull */ -struct s3c_gpio_cfg { +struct samsung_gpio_cfg { unsigned int cfg_eint; - s3c_gpio_pull_t (*get_pull)(struct s3c_gpio_chip *chip, unsigned offs); - int (*set_pull)(struct s3c_gpio_chip *chip, unsigned offs, - s3c_gpio_pull_t pull); + samsung_gpio_pull_t (*get_pull)(struct samsung_gpio_chip *chip, unsigned offs); + int (*set_pull)(struct samsung_gpio_chip *chip, unsigned offs, + samsung_gpio_pull_t pull); - unsigned (*get_config)(struct s3c_gpio_chip *chip, unsigned offs); - int (*set_config)(struct s3c_gpio_chip *chip, unsigned offs, + unsigned (*get_config)(struct samsung_gpio_chip *chip, unsigned offs); + int (*set_config)(struct samsung_gpio_chip *chip, unsigned offs, unsigned config); }; @@ -69,7 +69,7 @@ struct s3c_gpio_cfg { #define S3C_GPIO_OUTPUT (S3C_GPIO_SPECIAL(1)) #define S3C_GPIO_SFN(x) (S3C_GPIO_SPECIAL(x)) -#define s3c_gpio_is_cfg_special(_cfg) \ +#define samsung_gpio_is_cfg_special(_cfg) \ (((_cfg) & S3C_GPIO_SPECIAL_MARK) == S3C_GPIO_SPECIAL_MARK) /** @@ -128,9 +128,9 @@ extern int s3c_gpio_cfgpin_range(unsigned int start, unsigned int nr, * up or down settings, and it may be dependent on the chip that is being * used to whether the particular mode is available. */ -#define S3C_GPIO_PULL_NONE ((__force s3c_gpio_pull_t)0x00) -#define S3C_GPIO_PULL_DOWN ((__force s3c_gpio_pull_t)0x01) -#define S3C_GPIO_PULL_UP ((__force s3c_gpio_pull_t)0x02) +#define S3C_GPIO_PULL_NONE ((__force samsung_gpio_pull_t)0x00) +#define S3C_GPIO_PULL_DOWN ((__force samsung_gpio_pull_t)0x01) +#define S3C_GPIO_PULL_UP ((__force samsung_gpio_pull_t)0x02) /** * s3c_gpio_setpull() - set the state of a gpio pin pull resistor @@ -143,7 +143,7 @@ extern int s3c_gpio_cfgpin_range(unsigned int start, unsigned int nr, * * @pull is one of S3C_GPIO_PULL_NONE, S3C_GPIO_PULL_DOWN or S3C_GPIO_PULL_UP. */ -extern int s3c_gpio_setpull(unsigned int pin, s3c_gpio_pull_t pull); +extern int s3c_gpio_setpull(unsigned int pin, samsung_gpio_pull_t pull); /** * s3c_gpio_getpull() - get the pull resistor state of a gpio pin @@ -151,7 +151,7 @@ extern int s3c_gpio_setpull(unsigned int pin, s3c_gpio_pull_t pull); * * Read the pull resistor value for the specified pin. */ -extern s3c_gpio_pull_t s3c_gpio_getpull(unsigned int pin); +extern samsung_gpio_pull_t s3c_gpio_getpull(unsigned int pin); /* configure `all` aspects of an gpio */ @@ -170,7 +170,7 @@ extern s3c_gpio_pull_t s3c_gpio_getpull(unsigned int pin); * @sa s3c_gpio_cfgpin_range */ extern int s3c_gpio_cfgall_range(unsigned int start, unsigned int nr, - unsigned int cfg, s3c_gpio_pull_t pull); + unsigned int cfg, samsung_gpio_pull_t pull); static inline int s3c_gpio_cfgrange_nopull(unsigned int pin, unsigned int size, unsigned int cfg) diff --git a/arch/arm/plat-samsung/include/plat/gpio-core.h b/arch/arm/plat-samsung/include/plat/gpio-core.h index 8cad4cf19c3c..1fe6917f6a2a 100644 --- a/arch/arm/plat-samsung/include/plat/gpio-core.h +++ b/arch/arm/plat-samsung/include/plat/gpio-core.h @@ -25,22 +25,22 @@ * specific code. */ -struct s3c_gpio_chip; +struct samsung_gpio_chip; /** - * struct s3c_gpio_pm - power management (suspend/resume) information + * struct samsung_gpio_pm - power management (suspend/resume) information * @save: Routine to save the state of the GPIO block * @resume: Routine to resume the GPIO block. */ -struct s3c_gpio_pm { - void (*save)(struct s3c_gpio_chip *chip); - void (*resume)(struct s3c_gpio_chip *chip); +struct samsung_gpio_pm { + void (*save)(struct samsung_gpio_chip *chip); + void (*resume)(struct samsung_gpio_chip *chip); }; -struct s3c_gpio_cfg; +struct samsung_gpio_cfg; /** - * struct s3c_gpio_chip - wrapper for specific implementation of gpio + * struct samsung_gpio_chip - wrapper for specific implementation of gpio * @chip: The chip structure to be exported via gpiolib. * @base: The base pointer to the gpio configuration registers. * @group: The group register number for gpio interrupt support. @@ -60,10 +60,10 @@ struct s3c_gpio_cfg; * CPU cores trying to get one lock for different GPIO banks, where each * bank of GPIO has its own register space and configuration registers. */ -struct s3c_gpio_chip { +struct samsung_gpio_chip { struct gpio_chip chip; - struct s3c_gpio_cfg *config; - struct s3c_gpio_pm *pm; + struct samsung_gpio_cfg *config; + struct samsung_gpio_pm *pm; void __iomem *base; int irq_base; int group; @@ -73,58 +73,11 @@ struct s3c_gpio_chip { #endif }; -static inline struct s3c_gpio_chip *to_s3c_gpio(struct gpio_chip *gpc) +static inline struct samsung_gpio_chip *to_samsung_gpio(struct gpio_chip *gpc) { - return container_of(gpc, struct s3c_gpio_chip, chip); + return container_of(gpc, struct samsung_gpio_chip, chip); } -/** s3c_gpiolib_add() - add the s3c specific version of a gpio_chip. - * @chip: The chip to register - * - * This is a wrapper to gpiochip_add() that takes our specific gpio chip - * information and makes the necessary alterations for the platform and - * notes the information for use with the configuration systems and any - * other parts of the system. - */ -extern void s3c_gpiolib_add(struct s3c_gpio_chip *chip); - -/* CONFIG_S3C_GPIO_TRACK enables the tracking of the s3c specific gpios - * for use with the configuration calls, and other parts of the s3c gpiolib - * support code. - * - * Not all s3c support code will need this, as some configurations of cpu - * may only support one or two different configuration options and have an - * easy gpio to s3c_gpio_chip mapping function. If this is the case, then - * the machine support file should provide its own s3c_gpiolib_getchip() - * and any other necessary functions. - */ - -/** - * samsung_gpiolib_add_4bit_chips - 4bit single register GPIO config. - * @chip: The gpio chip that is being configured. - * @nr_chips: The no of chips (gpio ports) for the GPIO being configured. - * - * This helper deal with the GPIO cases where the control register has 4 bits - * of control per GPIO, generally in the form of: - * 0000 = Input - * 0001 = Output - * others = Special functions (dependent on bank) - * - * Note, since the code to deal with the case where there are two control - * registers instead of one, we do not have a separate set of function - * (samsung_gpiolib_add_4bit2_chips)for each case. - */ -extern void samsung_gpiolib_add_4bit_chips(struct s3c_gpio_chip *chip, - int nr_chips); -extern void samsung_gpiolib_add_4bit2_chips(struct s3c_gpio_chip *chip, - int nr_chips); -extern void samsung_gpiolib_add_2bit_chips(struct s3c_gpio_chip *chip, - int nr_chips); - -extern void samsung_gpiolib_add_4bit(struct s3c_gpio_chip *chip); -extern void samsung_gpiolib_add_4bit2(struct s3c_gpio_chip *chip); - - /** * samsung_gpiolib_to_irq - convert gpio pin to irq number * @chip: The gpio chip that the pin belongs to. @@ -136,36 +89,36 @@ extern void samsung_gpiolib_add_4bit2(struct s3c_gpio_chip *chip); extern int samsung_gpiolib_to_irq(struct gpio_chip *chip, unsigned int offset); /* exported for core SoC support to change */ -extern struct s3c_gpio_cfg s3c24xx_gpiocfg_default; +extern struct samsung_gpio_cfg s3c24xx_gpiocfg_default; #ifdef CONFIG_S3C_GPIO_TRACK -extern struct s3c_gpio_chip *s3c_gpios[S3C_GPIO_END]; +extern struct samsung_gpio_chip *s3c_gpios[S3C_GPIO_END]; -static inline struct s3c_gpio_chip *s3c_gpiolib_getchip(unsigned int chip) +static inline struct samsung_gpio_chip *samsung_gpiolib_getchip(unsigned int chip) { return (chip < S3C_GPIO_END) ? s3c_gpios[chip] : NULL; } #else -/* machine specific code should provide s3c_gpiolib_getchip */ +/* machine specific code should provide samsung_gpiolib_getchip */ #include -static inline void s3c_gpiolib_track(struct s3c_gpio_chip *chip) { } +static inline void s3c_gpiolib_track(struct samsung_gpio_chip *chip) { } #endif #ifdef CONFIG_PM -extern struct s3c_gpio_pm s3c_gpio_pm_1bit; -extern struct s3c_gpio_pm s3c_gpio_pm_2bit; -extern struct s3c_gpio_pm s3c_gpio_pm_4bit; +extern struct samsung_gpio_pm samsung_gpio_pm_1bit; +extern struct samsung_gpio_pm samsung_gpio_pm_2bit; +extern struct samsung_gpio_pm samsung_gpio_pm_4bit; #define __gpio_pm(x) x #else -#define s3c_gpio_pm_1bit NULL -#define s3c_gpio_pm_2bit NULL -#define s3c_gpio_pm_4bit NULL +#define samsung_gpio_pm_1bit NULL +#define samsung_gpio_pm_2bit NULL +#define samsung_gpio_pm_4bit NULL #define __gpio_pm(x) NULL #endif /* CONFIG_PM */ /* locking wrappers to deal with multiple access to the same gpio bank */ -#define s3c_gpio_lock(_oc, _fl) spin_lock_irqsave(&(_oc)->lock, _fl) -#define s3c_gpio_unlock(_oc, _fl) spin_unlock_irqrestore(&(_oc)->lock, _fl) +#define samsung_gpio_lock(_oc, _fl) spin_lock_irqsave(&(_oc)->lock, _fl) +#define samsung_gpio_unlock(_oc, _fl) spin_unlock_irqrestore(&(_oc)->lock, _fl) diff --git a/arch/arm/plat-samsung/include/plat/gpio-fns.h b/arch/arm/plat-samsung/include/plat/gpio-fns.h new file mode 100644 index 000000000000..bab139201761 --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/gpio-fns.h @@ -0,0 +1,98 @@ +/* arch/arm/mach-s3c2410/include/mach/gpio-fns.h + * + * Copyright (c) 2003-2009 Simtec Electronics + * Ben Dooks + * + * S3C2410 - hardware + * + * 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. +*/ + +#ifndef __MACH_GPIO_FNS_H +#define __MACH_GPIO_FNS_H __FILE__ + +/* These functions are in the to-be-removed category and it is strongly + * encouraged not to use these in new code. They will be marked deprecated + * very soon. + * + * Most of the functionality can be either replaced by the gpiocfg calls + * for the s3c platform or by the generic GPIOlib API. + * + * As of 2.6.35-rc, these will be removed, with the few drivers using them + * either replaced or given a wrapper until the calls can be removed. +*/ + +#include + +static inline void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int cfg) +{ + /* 1:1 mapping between cfgpin and setcfg calls at the moment */ + s3c_gpio_cfgpin(pin, cfg); +} + +/* external functions for GPIO support + * + * These allow various different clients to access the same GPIO + * registers without conflicting. If your driver only owns the entire + * GPIO register, then it is safe to ioremap/__raw_{read|write} to it. +*/ + +extern unsigned int s3c2410_gpio_getcfg(unsigned int pin); + +/* s3c2410_gpio_getirq + * + * turn the given pin number into the corresponding IRQ number + * + * returns: + * < 0 = no interrupt for this pin + * >=0 = interrupt number for the pin +*/ + +extern int s3c2410_gpio_getirq(unsigned int pin); + +/* s3c2410_gpio_irqfilter + * + * set the irq filtering on the given pin + * + * on = 0 => disable filtering + * 1 => enable filtering + * + * config = S3C2410_EINTFLT_PCLK or S3C2410_EINTFLT_EXTCLK orred with + * width of filter (0 through 63) + * + * +*/ + +extern int s3c2410_gpio_irqfilter(unsigned int pin, unsigned int on, + unsigned int config); + +/* s3c2410_gpio_pullup + * + * This call should be replaced with s3c_gpio_setpull(). + * + * As a note, there is currently no distinction between pull-up and pull-down + * in the s3c24xx series devices with only an on/off configuration. + */ + +/* s3c2410_gpio_pullup + * + * configure the pull-up control on the given pin + * + * to = 1 => disable the pull-up + * 0 => enable the pull-up + * + * eg; + * + * s3c2410_gpio_pullup(S3C2410_GPB(0), 0); + * s3c2410_gpio_pullup(S3C2410_GPE(8), 0); +*/ + +extern void s3c2410_gpio_pullup(unsigned int pin, unsigned int to); + +extern void s3c2410_gpio_setpin(unsigned int pin, unsigned int to); + +extern unsigned int s3c2410_gpio_getpin(unsigned int pin); + +#endif /* __MACH_GPIO_FNS_H */ diff --git a/arch/arm/plat-samsung/include/plat/pm.h b/arch/arm/plat-samsung/include/plat/pm.h index f6749916d194..dcf68709f9cf 100644 --- a/arch/arm/plat-samsung/include/plat/pm.h +++ b/arch/arm/plat-samsung/include/plat/pm.h @@ -165,20 +165,20 @@ extern void s3c_pm_check_store(void); extern void s3c_pm_configure_extint(void); /** - * s3c_pm_restore_gpios() - restore the state of the gpios after sleep. + * samsung_pm_restore_gpios() - restore the state of the gpios after sleep. * * Restore the state of the GPIO pins after sleep, which may involve ensuring * that we do not glitch the state of the pins from that the bootloader's * resume code has done. */ -extern void s3c_pm_restore_gpios(void); +extern void samsung_pm_restore_gpios(void); /** - * s3c_pm_save_gpios() - save the state of the GPIOs for restoring after sleep. + * samsung_pm_save_gpios() - save the state of the GPIOs for restoring after sleep. * - * Save the GPIO states for resotration on resume. See s3c_pm_restore_gpios(). + * Save the GPIO states for resotration on resume. See samsung_pm_restore_gpios(). */ -extern void s3c_pm_save_gpios(void); +extern void samsung_pm_save_gpios(void); extern void s3c_pm_save_core(void); extern void s3c_pm_restore_core(void); diff --git a/arch/arm/plat-samsung/pm-gpio.c b/arch/arm/plat-samsung/pm-gpio.c index 96528200eb79..4be016eaa6db 100644 --- a/arch/arm/plat-samsung/pm-gpio.c +++ b/arch/arm/plat-samsung/pm-gpio.c @@ -28,13 +28,13 @@ #define OFFS_DAT (0x04) #define OFFS_UP (0x08) -static void s3c_gpio_pm_1bit_save(struct s3c_gpio_chip *chip) +static void samsung_gpio_pm_1bit_save(struct samsung_gpio_chip *chip) { chip->pm_save[0] = __raw_readl(chip->base + OFFS_CON); chip->pm_save[1] = __raw_readl(chip->base + OFFS_DAT); } -static void s3c_gpio_pm_1bit_resume(struct s3c_gpio_chip *chip) +static void samsung_gpio_pm_1bit_resume(struct samsung_gpio_chip *chip) { void __iomem *base = chip->base; u32 old_gpcon = __raw_readl(base + OFFS_CON); @@ -60,12 +60,12 @@ static void s3c_gpio_pm_1bit_resume(struct s3c_gpio_chip *chip) chip->chip.label, old_gpcon, gps_gpcon, old_gpdat, gps_gpdat); } -struct s3c_gpio_pm s3c_gpio_pm_1bit = { - .save = s3c_gpio_pm_1bit_save, - .resume = s3c_gpio_pm_1bit_resume, +struct samsung_gpio_pm samsung_gpio_pm_1bit = { + .save = samsung_gpio_pm_1bit_save, + .resume = samsung_gpio_pm_1bit_resume, }; -static void s3c_gpio_pm_2bit_save(struct s3c_gpio_chip *chip) +static void samsung_gpio_pm_2bit_save(struct samsung_gpio_chip *chip) { chip->pm_save[0] = __raw_readl(chip->base + OFFS_CON); chip->pm_save[1] = __raw_readl(chip->base + OFFS_DAT); @@ -95,7 +95,7 @@ static inline int is_out(unsigned long con) } /** - * s3c_gpio_pm_2bit_resume() - restore the given GPIO bank + * samsung_gpio_pm_2bit_resume() - restore the given GPIO bank * @chip: The chip information to resume. * * Restore one of the GPIO banks that was saved during suspend. This is @@ -121,7 +121,7 @@ static inline int is_out(unsigned long con) * [1] this assumes that writing to a pin DAT whilst in SFN will set the * state for when it is next output. */ -static void s3c_gpio_pm_2bit_resume(struct s3c_gpio_chip *chip) +static void samsung_gpio_pm_2bit_resume(struct samsung_gpio_chip *chip) { void __iomem *base = chip->base; u32 old_gpcon = __raw_readl(base + OFFS_CON); @@ -187,13 +187,13 @@ static void s3c_gpio_pm_2bit_resume(struct s3c_gpio_chip *chip) chip->chip.label, old_gpcon, gps_gpcon, old_gpdat, gps_gpdat); } -struct s3c_gpio_pm s3c_gpio_pm_2bit = { - .save = s3c_gpio_pm_2bit_save, - .resume = s3c_gpio_pm_2bit_resume, +struct samsung_gpio_pm samsung_gpio_pm_2bit = { + .save = samsung_gpio_pm_2bit_save, + .resume = samsung_gpio_pm_2bit_resume, }; #if defined(CONFIG_ARCH_S3C64XX) || defined(CONFIG_PLAT_S5P) -static void s3c_gpio_pm_4bit_save(struct s3c_gpio_chip *chip) +static void samsung_gpio_pm_4bit_save(struct samsung_gpio_chip *chip) { chip->pm_save[1] = __raw_readl(chip->base + OFFS_CON); chip->pm_save[2] = __raw_readl(chip->base + OFFS_DAT); @@ -203,7 +203,7 @@ static void s3c_gpio_pm_4bit_save(struct s3c_gpio_chip *chip) chip->pm_save[0] = __raw_readl(chip->base - 4); } -static u32 s3c_gpio_pm_4bit_mask(u32 old_gpcon, u32 gps_gpcon) +static u32 samsung_gpio_pm_4bit_mask(u32 old_gpcon, u32 gps_gpcon) { u32 old, new, mask; u32 change_mask = 0x0; @@ -242,14 +242,14 @@ static u32 s3c_gpio_pm_4bit_mask(u32 old_gpcon, u32 gps_gpcon) return change_mask; } -static void s3c_gpio_pm_4bit_con(struct s3c_gpio_chip *chip, int index) +static void samsung_gpio_pm_4bit_con(struct samsung_gpio_chip *chip, int index) { void __iomem *con = chip->base + (index * 4); u32 old_gpcon = __raw_readl(con); u32 gps_gpcon = chip->pm_save[index + 1]; u32 gpcon, mask; - mask = s3c_gpio_pm_4bit_mask(old_gpcon, gps_gpcon); + mask = samsung_gpio_pm_4bit_mask(old_gpcon, gps_gpcon); gpcon = old_gpcon & ~mask; gpcon |= gps_gpcon & mask; @@ -257,7 +257,7 @@ static void s3c_gpio_pm_4bit_con(struct s3c_gpio_chip *chip, int index) __raw_writel(gpcon, con); } -static void s3c_gpio_pm_4bit_resume(struct s3c_gpio_chip *chip) +static void samsung_gpio_pm_4bit_resume(struct samsung_gpio_chip *chip) { void __iomem *base = chip->base; u32 old_gpcon[2]; @@ -269,10 +269,10 @@ static void s3c_gpio_pm_4bit_resume(struct s3c_gpio_chip *chip) old_gpcon[0] = 0; old_gpcon[1] = __raw_readl(base + OFFS_CON); - s3c_gpio_pm_4bit_con(chip, 0); + samsung_gpio_pm_4bit_con(chip, 0); if (chip->chip.ngpio > 8) { old_gpcon[0] = __raw_readl(base - 4); - s3c_gpio_pm_4bit_con(chip, -1); + samsung_gpio_pm_4bit_con(chip, -1); } /* Now change the configurations that require DAT,CON */ @@ -298,19 +298,19 @@ static void s3c_gpio_pm_4bit_resume(struct s3c_gpio_chip *chip) old_gpdat, gps_gpdat); } -struct s3c_gpio_pm s3c_gpio_pm_4bit = { - .save = s3c_gpio_pm_4bit_save, - .resume = s3c_gpio_pm_4bit_resume, +struct samsung_gpio_pm samsung_gpio_pm_4bit = { + .save = samsung_gpio_pm_4bit_save, + .resume = samsung_gpio_pm_4bit_resume, }; #endif /* CONFIG_ARCH_S3C64XX || CONFIG_PLAT_S5P */ /** - * s3c_pm_save_gpio() - save gpio chip data for suspend + * samsung_pm_save_gpio() - save gpio chip data for suspend * @ourchip: The chip for suspend. */ -static void s3c_pm_save_gpio(struct s3c_gpio_chip *ourchip) +static void samsung_pm_save_gpio(struct samsung_gpio_chip *ourchip) { - struct s3c_gpio_pm *pm = ourchip->pm; + struct samsung_gpio_pm *pm = ourchip->pm; if (pm == NULL || pm->save == NULL) S3C_PMDBG("%s: no pm for %s\n", __func__, ourchip->chip.label); @@ -319,24 +319,24 @@ static void s3c_pm_save_gpio(struct s3c_gpio_chip *ourchip) } /** - * s3c_pm_save_gpios() - Save the state of the GPIO banks. + * samsung_pm_save_gpios() - Save the state of the GPIO banks. * * For all the GPIO banks, save the state of each one ready for going * into a suspend mode. */ -void s3c_pm_save_gpios(void) +void samsung_pm_save_gpios(void) { - struct s3c_gpio_chip *ourchip; + struct samsung_gpio_chip *ourchip; unsigned int gpio_nr; for (gpio_nr = 0; gpio_nr < S3C_GPIO_END;) { - ourchip = s3c_gpiolib_getchip(gpio_nr); + ourchip = samsung_gpiolib_getchip(gpio_nr); if (!ourchip) { gpio_nr++; continue; } - s3c_pm_save_gpio(ourchip); + samsung_pm_save_gpio(ourchip); S3C_PMDBG("%s: save %08x,%08x,%08x,%08x\n", ourchip->chip.label, @@ -351,12 +351,12 @@ void s3c_pm_save_gpios(void) } /** - * s3c_pm_resume_gpio() - restore gpio chip data after suspend + * samsung_pm_resume_gpio() - restore gpio chip data after suspend * @ourchip: The suspended chip. */ -static void s3c_pm_resume_gpio(struct s3c_gpio_chip *ourchip) +static void samsung_pm_resume_gpio(struct samsung_gpio_chip *ourchip) { - struct s3c_gpio_pm *pm = ourchip->pm; + struct samsung_gpio_pm *pm = ourchip->pm; if (pm == NULL || pm->resume == NULL) S3C_PMDBG("%s: no pm for %s\n", __func__, ourchip->chip.label); @@ -364,19 +364,19 @@ static void s3c_pm_resume_gpio(struct s3c_gpio_chip *ourchip) pm->resume(ourchip); } -void s3c_pm_restore_gpios(void) +void samsung_pm_restore_gpios(void) { - struct s3c_gpio_chip *ourchip; + struct samsung_gpio_chip *ourchip; unsigned int gpio_nr; for (gpio_nr = 0; gpio_nr < S3C_GPIO_END;) { - ourchip = s3c_gpiolib_getchip(gpio_nr); + ourchip = samsung_gpiolib_getchip(gpio_nr); if (!ourchip) { gpio_nr++; continue; } - s3c_pm_resume_gpio(ourchip); + samsung_pm_resume_gpio(ourchip); gpio_nr += ourchip->chip.ngpio; gpio_nr += CONFIG_S3C_GPIO_SPACE; diff --git a/arch/arm/plat-samsung/pm.c b/arch/arm/plat-samsung/pm.c index ae6f99834cdd..64ab65f0fdbc 100644 --- a/arch/arm/plat-samsung/pm.c +++ b/arch/arm/plat-samsung/pm.c @@ -268,8 +268,8 @@ static int s3c_pm_enter(suspend_state_t state) /* save all necessary core registers not covered by the drivers */ - s3c_pm_save_gpios(); - s3c_pm_saved_gpios(); + samsung_pm_save_gpios(); + samsung_pm_saved_gpios(); s3c_pm_save_uarts(); s3c_pm_save_core(); @@ -306,7 +306,7 @@ static int s3c_pm_enter(suspend_state_t state) s3c_pm_restore_core(); s3c_pm_restore_uarts(); - s3c_pm_restore_gpios(); + samsung_pm_restore_gpios(); s3c_pm_restored_gpios(); s3c_pm_debug_init(); From 5ec7414494ed1204c9e2ed0b8232b29860d0986f Mon Sep 17 00:00:00 2001 From: Kukjin Kim Date: Tue, 30 Aug 2011 20:35:05 +0900 Subject: [PATCH 0234/1531] ARM: SAMSUNG: Remove useless Samsung GPIO related CONFIGs Cc: Ben Dooks Signed-off-by: Kukjin Kim --- arch/arm/Kconfig | 3 --- arch/arm/mach-s3c2410/Kconfig | 1 - arch/arm/mach-s3c2416/Kconfig | 1 - arch/arm/mach-s3c2440/Kconfig | 2 -- arch/arm/mach-s3c2443/Kconfig | 1 - arch/arm/plat-s3c24xx/Kconfig | 1 - arch/arm/plat-s5p/Kconfig | 3 --- arch/arm/plat-samsung/Kconfig | 27 --------------------------- 8 files changed, 39 deletions(-) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 3269576dbfa8..42401daa165f 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -724,9 +724,6 @@ config ARCH_S3C64XX select SAMSUNG_IRQ_VIC_TIMER select SAMSUNG_IRQ_UART select S3C_GPIO_TRACK - select S3C_GPIO_PULL_UPDOWN - select S3C_GPIO_CFG_S3C24XX - select S3C_GPIO_CFG_S3C64XX select S3C_DEV_NAND select USB_ARCH_HAS_OHCI select SAMSUNG_GPIOLIB_4BIT diff --git a/arch/arm/mach-s3c2410/Kconfig b/arch/arm/mach-s3c2410/Kconfig index 3700cf32af0f..5261a7ed0999 100644 --- a/arch/arm/mach-s3c2410/Kconfig +++ b/arch/arm/mach-s3c2410/Kconfig @@ -6,7 +6,6 @@ config CPU_S3C2410 bool depends on ARCH_S3C2410 select CPU_ARM920T - select S3C_GPIO_PULL_UP select S3C2410_CLOCK select CPU_LLSERIAL_S3C2410 select S3C2410_PM if PM diff --git a/arch/arm/mach-s3c2416/Kconfig b/arch/arm/mach-s3c2416/Kconfig index 69b48a7d1dbd..84c7b03e5a30 100644 --- a/arch/arm/mach-s3c2416/Kconfig +++ b/arch/arm/mach-s3c2416/Kconfig @@ -13,7 +13,6 @@ config CPU_S3C2416 select CPU_ARM926T select S3C2416_DMA if S3C2410_DMA select CPU_LLSERIAL_S3C2440 - select S3C_GPIO_PULL_UPDOWN select SAMSUNG_CLKSRC select S3C2443_CLOCK help diff --git a/arch/arm/mach-s3c2440/Kconfig b/arch/arm/mach-s3c2440/Kconfig index c461fb8e15c0..914e620f1257 100644 --- a/arch/arm/mach-s3c2440/Kconfig +++ b/arch/arm/mach-s3c2440/Kconfig @@ -5,7 +5,6 @@ config CPU_S3C2440 bool select CPU_ARM920T - select S3C_GPIO_PULL_UP select S3C2410_CLOCK select S3C2410_PM if PM select S3C2440_DMA if S3C2410_DMA @@ -17,7 +16,6 @@ config CPU_S3C2440 config CPU_S3C2442 bool select CPU_ARM920T - select S3C_GPIO_PULL_DOWN select S3C2410_CLOCK select S3C2410_PM if PM select CPU_S3C244X diff --git a/arch/arm/mach-s3c2443/Kconfig b/arch/arm/mach-s3c2443/Kconfig index d8eb86823df7..8814031516ce 100644 --- a/arch/arm/mach-s3c2443/Kconfig +++ b/arch/arm/mach-s3c2443/Kconfig @@ -10,7 +10,6 @@ config CPU_S3C2443 select CPU_LLSERIAL_S3C2440 select SAMSUNG_CLKSRC select S3C2443_CLOCK - select S3C_GPIO_PULL_S3C2443 help Support for the S3C2443 SoC from the S3C24XX line diff --git a/arch/arm/plat-s3c24xx/Kconfig b/arch/arm/plat-s3c24xx/Kconfig index 8c5b3029b39f..d8973ac46bc4 100644 --- a/arch/arm/plat-s3c24xx/Kconfig +++ b/arch/arm/plat-s3c24xx/Kconfig @@ -9,7 +9,6 @@ config PLAT_S3C24XX select NO_IOPORT select ARCH_REQUIRE_GPIOLIB select S3C_DEV_NAND - select S3C_GPIO_CFG_S3C24XX help Base platform code for any Samsung S3C24XX device diff --git a/arch/arm/plat-s5p/Kconfig b/arch/arm/plat-s5p/Kconfig index 9843c954c042..ac610bee2cb3 100644 --- a/arch/arm/plat-s5p/Kconfig +++ b/arch/arm/plat-s5p/Kconfig @@ -16,9 +16,6 @@ config PLAT_S5P select S3C_GPIO_TRACK select S5P_GPIO_DRVSTR select SAMSUNG_GPIOLIB_4BIT - select S3C_GPIO_CFG_S3C64XX - select S3C_GPIO_PULL_UPDOWN - select S3C_GPIO_CFG_S3C24XX select PLAT_SAMSUNG select SAMSUNG_CLKSRC select SAMSUNG_IRQ_VIC_TIMER diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig index b3e10659e4b8..1f346d22323d 100644 --- a/arch/arm/plat-samsung/Kconfig +++ b/arch/arm/plat-samsung/Kconfig @@ -79,39 +79,12 @@ config SAMSUNG_GPIOLIB_4BIT configuration. GPIOlib shall be compiled only for S3C64XX and S5P series of processors. -config S3C_GPIO_CFG_S3C24XX - bool - help - Internal configuration to enable S3C24XX style GPIO configuration - functions. - config S3C_GPIO_CFG_S3C64XX bool help Internal configuration to enable S3C64XX style GPIO configuration functions. -config S3C_GPIO_PULL_UPDOWN - bool - help - Internal configuration to enable the correct GPIO pull helper - -config S3C_GPIO_PULL_S3C2443 - bool - select S3C_GPIO_PULL_UPDOWN - help - Internal configuration to enable the correct GPIO pull helper for S3C2443-style GPIO - -config S3C_GPIO_PULL_DOWN - bool - help - Internal configuration to enable the correct GPIO pull helper - -config S3C_GPIO_PULL_UP - bool - help - Internal configuration to enable the correct GPIO pull helper - config S5P_GPIO_DRVSTR bool help From 75df713627f28f88b901b329c8857747545fd4ab Mon Sep 17 00:00:00 2001 From: Suresh Jayaraman Date: Wed, 21 Sep 2011 10:00:16 +0200 Subject: [PATCH 0235/1531] block: document blk-plug Thus spake Andrew Morton: "And I have the usual maintainability whine. If someone comes up to vmscan.c and sees it calling blk_start_plug(), how are they supposed to work out why that call is there? They go look at the blk_start_plug() definition and it is undocumented. I think we can do better than this?" Adapted from the LWN article - http://lwn.net/Articles/438256/ by Jens Axboe and from an earlier attempt by Shaohua Li to document blk-plug. [akpm@linux-foundation.org: grammatical and spelling tweaks] Signed-off-by: Suresh Jayaraman Cc: Shaohua Li Cc: Jonathan Corbet Signed-off-by: Andrew Morton Signed-off-by: Jens Axboe --- block/blk-core.c | 14 ++++++++++++++ include/linux/blkdev.h | 24 +++++++++++++++--------- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/block/blk-core.c b/block/blk-core.c index 684d7eb33d43..97e9e5405b83 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -2595,6 +2595,20 @@ EXPORT_SYMBOL(kblockd_schedule_delayed_work); #define PLUG_MAGIC 0x91827364 +/** + * blk_start_plug - initialize blk_plug and track it inside the task_struct + * @plug: The &struct blk_plug that needs to be initialized + * + * Description: + * Tracking blk_plug inside the task_struct will help with auto-flushing the + * pending I/O should the task end up blocking between blk_start_plug() and + * blk_finish_plug(). This is important from a performance perspective, but + * also ensures that we don't deadlock. For instance, if the task is blocking + * for a memory allocation, memory reclaim could end up wanting to free a + * page belonging to that request that is currently residing in our private + * plug. By flushing the pending I/O when the process goes to sleep, we avoid + * this kind of deadlock. + */ void blk_start_plug(struct blk_plug *plug) { struct task_struct *tsk = current; diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index c712efdafc3f..1978655faa3b 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -860,17 +860,23 @@ struct request_queue *blk_alloc_queue_node(gfp_t, int); extern void blk_put_queue(struct request_queue *); /* - * Note: Code in between changing the blk_plug list/cb_list or element of such - * lists is preemptable, but such code can't do sleep (or be very careful), - * otherwise data is corrupted. For details, please check schedule() where - * blk_schedule_flush_plug() is called. + * blk_plug permits building a queue of related requests by holding the I/O + * fragments for a short period. This allows merging of sequential requests + * into single larger request. As the requests are moved from a per-task list to + * the device's request_queue in a batch, this results in improved scalability + * as the lock contention for request_queue lock is reduced. + * + * It is ok not to disable preemption when adding the request to the plug list + * or when attempting a merge, because blk_schedule_flush_list() will only flush + * the plug list when the task sleeps by itself. For details, please see + * schedule() where blk_schedule_flush_plug() is called. */ struct blk_plug { - unsigned long magic; - struct list_head list; - struct list_head cb_list; - unsigned int should_sort; - unsigned int count; + unsigned long magic; /* detect uninitialized use-cases */ + struct list_head list; /* requests */ + struct list_head cb_list; /* md requires an unplug callback */ + unsigned int should_sort; /* list to be sorted before flushing? */ + unsigned int count; /* number of queued requests */ }; #define BLK_MAX_REQUEST_COUNT 16 From 499337bb6511e665a236a6a947f819d98ea340c6 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Wed, 21 Sep 2011 10:01:22 +0200 Subject: [PATCH 0236/1531] block/blk-sysfs.c: fix kerneldoc references The kerneldoc for blk_release_queue() is referring to blk_cleanup_queue(). Cc: Jens Axboe Signed-off-by: Andrew Morton Signed-off-by: Jens Axboe --- block/blk-sysfs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index 0ee17b5e7fb6..adc923e9d1f8 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -455,11 +455,11 @@ queue_attr_store(struct kobject *kobj, struct attribute *attr, } /** - * blk_cleanup_queue: - release a &struct request_queue when it is no longer needed - * @kobj: the kobj belonging of the request queue to be released + * blk_release_queue: - release a &struct request_queue when it is no longer needed + * @kobj: the kobj belonging to the request queue to be released * * Description: - * blk_cleanup_queue is the pair to blk_init_queue() or + * blk_release_queue is the pair to blk_init_queue() or * blk_queue_make_request(). It should be called when a request queue is * being released; typically when a block device is being de-registered. * Currently, its primary task it to free all the &struct request From 5a3a76e6c35fa236fc234d17a98edeb41d49130f Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Wed, 21 Sep 2011 10:02:13 +0200 Subject: [PATCH 0237/1531] drivers/block/cpqarray.c: use pci_dev->revision This driver uses PCI_CLASS_REVISION instead of PCI_REVISION_ID, so it wasn't converted by commit 44c10138fd4bbc4b6 ("PCI: Change all drivers to use pci_device->revision"). Signed-off-by: Sergei Shtylyov Acked-by: Mike Miller Cc: Chirag Kantharia Cc: Jens Axboe Signed-off-by: Andrew Morton Signed-off-by: Jens Axboe --- drivers/block/cpqarray.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c index b2fceb53e809..9125bbeacd4d 100644 --- a/drivers/block/cpqarray.c +++ b/drivers/block/cpqarray.c @@ -620,6 +620,7 @@ static int cpqarray_pci_init(ctlr_info_t *c, struct pci_dev *pdev) } vendor_id = pdev->vendor; device_id = pdev->device; + revision = pdev->revision; irq = pdev->irq; for(i=0; i<6; i++) @@ -632,7 +633,6 @@ static int cpqarray_pci_init(ctlr_info_t *c, struct pci_dev *pdev) } pci_read_config_word(pdev, PCI_COMMAND, &command); - pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision); pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cache_line_size); pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &latency_timer); From 8a9c594422ecad912d6470888acdee9a1236ad68 Mon Sep 17 00:00:00 2001 From: Phillip Susi Date: Wed, 21 Sep 2011 10:02:13 +0200 Subject: [PATCH 0238/1531] drivers/block/loop.c: emit uevent on auto release The loopback driver failed to emit the change uevent when auto releasing the device. Fixed lo_release() to pass the bdev to loop_clr_fd() so it can emit the event. Signed-off-by: Phillip Susi Cc: Jens Axboe Cc: Ayan George Signed-off-by: Andrew Morton Signed-off-by: Jens Axboe --- drivers/block/loop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/block/loop.c b/drivers/block/loop.c index b336433f8157..c2ce03cf3a58 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -1583,7 +1583,7 @@ static int lo_release(struct gendisk *disk, fmode_t mode) * In autoclear mode, stop the loop thread * and remove configuration after last close. */ - err = loop_clr_fd(lo, NULL); + err = loop_clr_fd(lo, lo->lo_device); if (!err) goto out_unlocked; } else { From 4c823cc3d568277aa6340d8df6981e34f4c4dee5 Mon Sep 17 00:00:00 2001 From: Ayan George Date: Wed, 21 Sep 2011 10:02:13 +0200 Subject: [PATCH 0239/1531] drivers/block/loop.c: remove unnecessary bdev argument from loop_clr_fd() If the loop device is associated (lo->lo_state == Lo_bound), it will have a valid bdev pointed to by lo->lo_device. There is no reason to ever pass an additional block_device pointer. Signed-off-by: Ayan George Cc: Phillip Susi Cc: Jens Axboe Signed-off-by: Andrew Morton Signed-off-by: Jens Axboe --- drivers/block/loop.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/block/loop.c b/drivers/block/loop.c index c2ce03cf3a58..9b2f5d3c19ab 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -1051,10 +1051,11 @@ loop_init_xfer(struct loop_device *lo, struct loop_func_table *xfer, return err; } -static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev) +static int loop_clr_fd(struct loop_device *lo) { struct file *filp = lo->lo_backing_file; gfp_t gfp = lo->old_gfp_mask; + struct block_device *bdev = lo->lo_device; if (lo->lo_state != Lo_bound) return -ENXIO; @@ -1372,7 +1373,7 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode, break; case LOOP_CLR_FD: /* loop_clr_fd would have unlocked lo_ctl_mutex on success */ - err = loop_clr_fd(lo, bdev); + err = loop_clr_fd(lo); if (!err) goto out_unlocked; break; @@ -1583,7 +1584,7 @@ static int lo_release(struct gendisk *disk, fmode_t mode) * In autoclear mode, stop the loop thread * and remove configuration after last close. */ - err = loop_clr_fd(lo, lo->lo_device); + err = loop_clr_fd(lo); if (!err) goto out_unlocked; } else { From 315d8f5ccdbb2abb609d1ca1119fb32273a09cf8 Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Sun, 4 Sep 2011 23:19:51 +0300 Subject: [PATCH 0240/1531] hwspinlock/core: simplify Kconfig Simplify hwspinlock's Kconfig by making the global CONFIG_HWSPINLOCK entry invisible; users will just select it when needed. This also prepares the ground for adding hwspinlock support for other platforms (the 'depends on ARCH_OMAP4' was rather hideous, and while we're at it, a dedicated menu is added). Signed-off-by: Ohad Ben-Cohen --- drivers/hwspinlock/Kconfig | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/hwspinlock/Kconfig b/drivers/hwspinlock/Kconfig index 1f29bab6b3e5..c8e7bdac1404 100644 --- a/drivers/hwspinlock/Kconfig +++ b/drivers/hwspinlock/Kconfig @@ -2,22 +2,20 @@ # Generic HWSPINLOCK framework # +# HWSPINLOCK always gets selected by whoever wants it. config HWSPINLOCK - tristate "Generic Hardware Spinlock framework" - depends on ARCH_OMAP4 - help - Say y here to support the generic hardware spinlock framework. - You only need to enable this if you have hardware spinlock module - on your system (usually only relevant if your system has remote slave - coprocessors). + tristate - If unsure, say N. +menu "Hardware Spinlock drivers" config HWSPINLOCK_OMAP tristate "OMAP Hardware Spinlock device" - depends on HWSPINLOCK && ARCH_OMAP4 + depends on ARCH_OMAP4 + select HWSPINLOCK help Say y here to support the OMAP Hardware Spinlock device (firstly introduced in OMAP4). If unsure, say N. + +endmenu From e467b6421435f467e274d4f25d62900e1e0e4286 Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Mon, 5 Sep 2011 16:42:36 +0300 Subject: [PATCH 0241/1531] hwspinlock/core: simplify 'owner' handling Use struct device_driver's owner member instead of asking drivers to explicitly pass the owner again. This simplifies drivers and also save some memory, since there's no point now in maintaining a separate owner pointer per hwspinlock. Signed-off-by: Ohad Ben-Cohen --- Documentation/hwspinlock.txt | 6 ++---- drivers/hwspinlock/hwspinlock_core.c | 4 ++-- drivers/hwspinlock/hwspinlock_internal.h | 4 +--- drivers/hwspinlock/omap_hwspinlock.c | 2 +- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/Documentation/hwspinlock.txt b/Documentation/hwspinlock.txt index 7dcd1a4e726c..bbaa4649b637 100644 --- a/Documentation/hwspinlock.txt +++ b/Documentation/hwspinlock.txt @@ -256,18 +256,16 @@ underlying hwspinlock implementation using the hwspin_lock_register() API. * @ops: vendor-specific hwspinlock handlers * @id: a global, unique, system-wide, index of the lock. * @lock: initialized and used by hwspinlock core - * @owner: underlying implementation module, used to maintain module ref count */ struct hwspinlock { struct device *dev; const struct hwspinlock_ops *ops; int id; spinlock_t lock; - struct module *owner; }; -The underlying implementation is responsible to assign the dev, ops, id and -owner members. The lock member, OTOH, is initialized and used by the hwspinlock +The underlying implementation is responsible to assign the dev, ops and id +members. The lock member, OTOH, is initialized and used by the hwspinlock core. 6. Implementation callbacks diff --git a/drivers/hwspinlock/hwspinlock_core.c b/drivers/hwspinlock/hwspinlock_core.c index 43a62714b4fb..af5175c5d5f4 100644 --- a/drivers/hwspinlock/hwspinlock_core.c +++ b/drivers/hwspinlock/hwspinlock_core.c @@ -352,7 +352,7 @@ static int __hwspin_lock_request(struct hwspinlock *hwlock) int ret; /* prevent underlying implementation from being removed */ - if (!try_module_get(hwlock->owner)) { + if (!try_module_get(hwlock->dev->driver->owner)) { dev_err(hwlock->dev, "%s: can't get owner\n", __func__); return -EINVAL; } @@ -535,7 +535,7 @@ int hwspin_lock_free(struct hwspinlock *hwlock) /* sanity check (this shouldn't happen) */ WARN_ON(tmp != hwlock); - module_put(hwlock->owner); + module_put(hwlock->dev->driver->owner); out: spin_unlock(&hwspinlock_tree_lock); diff --git a/drivers/hwspinlock/hwspinlock_internal.h b/drivers/hwspinlock/hwspinlock_internal.h index 69935e6b93e5..fb25830c2ee7 100644 --- a/drivers/hwspinlock/hwspinlock_internal.h +++ b/drivers/hwspinlock/hwspinlock_internal.h @@ -44,10 +44,9 @@ struct hwspinlock_ops { * @ops: platform-specific hwspinlock handlers * @id: a global, unique, system-wide, index of the lock. * @lock: initialized and used by hwspinlock core - * @owner: underlying implementation module, used to maintain module ref count * * Note: currently simplicity was opted for, but later we can squeeze some - * memory bytes by grouping the dev, ops and owner members in a single + * memory bytes by grouping dev, ops in a single * per-platform struct, and have all hwspinlocks point at it. */ struct hwspinlock { @@ -55,7 +54,6 @@ struct hwspinlock { const struct hwspinlock_ops *ops; int id; spinlock_t lock; - struct module *owner; }; #endif /* __HWSPINLOCK_HWSPINLOCK_H */ diff --git a/drivers/hwspinlock/omap_hwspinlock.c b/drivers/hwspinlock/omap_hwspinlock.c index a8f02734c026..1d19fe9324a4 100644 --- a/drivers/hwspinlock/omap_hwspinlock.c +++ b/drivers/hwspinlock/omap_hwspinlock.c @@ -143,7 +143,6 @@ static int __devinit omap_hwspinlock_probe(struct platform_device *pdev) } omap_lock->lock.dev = &pdev->dev; - omap_lock->lock.owner = THIS_MODULE; omap_lock->lock.id = i; omap_lock->lock.ops = &omap_hwspinlock_ops; omap_lock->addr = io_base + LOCK_BASE_OFFSET + sizeof(u32) * i; @@ -208,6 +207,7 @@ static struct platform_driver omap_hwspinlock_driver = { .remove = omap_hwspinlock_remove, .driver = { .name = "omap_hwspinlock", + .owner = THIS_MODULE, }, }; From c97f6dd0fe21dfd658c59c144a1b7fd5d8db04ac Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Mon, 5 Sep 2011 17:30:34 +0300 Subject: [PATCH 0242/1531] hwspinlock/omap: simplify allocation scheme Instead of allocating every hwspinlock separately, allocate them all in one shot. This both simplifies the driver and helps achieving better slab utilization. Reported-by: Arnd Bergmann Signed-off-by: Ohad Ben-Cohen --- drivers/hwspinlock/omap_hwspinlock.c | 51 ++++++++++------------------ 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/drivers/hwspinlock/omap_hwspinlock.c b/drivers/hwspinlock/omap_hwspinlock.c index 1d19fe9324a4..d0583480fe33 100644 --- a/drivers/hwspinlock/omap_hwspinlock.c +++ b/drivers/hwspinlock/omap_hwspinlock.c @@ -52,6 +52,7 @@ struct omap_hwspinlock { struct omap_hwspinlock_state { int num_locks; /* Total number of locks in system */ void __iomem *io_base; /* Mapped base address */ + struct omap_hwspinlock lock[0]; /* Array of 'num_locks' locks */ }; static int omap_hwspinlock_trylock(struct hwspinlock *lock) @@ -95,7 +96,6 @@ static int __devinit omap_hwspinlock_probe(struct platform_device *pdev) { struct omap_hwspinlock *omap_lock; struct omap_hwspinlock_state *state; - struct hwspinlock *lock; struct resource *res; void __iomem *io_base; int i, ret; @@ -104,15 +104,9 @@ static int __devinit omap_hwspinlock_probe(struct platform_device *pdev) if (!res) return -ENODEV; - state = kzalloc(sizeof(*state), GFP_KERNEL); - if (!state) - return -ENOMEM; - io_base = ioremap(res->start, resource_size(res)); - if (!io_base) { - ret = -ENOMEM; - goto free_state; - } + if (!io_base) + return -ENOMEM; /* Determine number of locks */ i = readl(io_base + SYSSTATUS_OFFSET); @@ -124,7 +118,15 @@ static int __devinit omap_hwspinlock_probe(struct platform_device *pdev) goto iounmap_base; } - state->num_locks = i * 32; + i *= 32; /* actual number of locks in this device */ + + state = kzalloc(sizeof(*state) + i * sizeof(*omap_lock), GFP_KERNEL); + if (!state) { + ret = -ENOMEM; + goto iounmap_base; + } + + state->num_locks = i; state->io_base = io_base; platform_set_drvdata(pdev, state); @@ -136,11 +138,7 @@ static int __devinit omap_hwspinlock_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); for (i = 0; i < state->num_locks; i++) { - omap_lock = kzalloc(sizeof(*omap_lock), GFP_KERNEL); - if (!omap_lock) { - ret = -ENOMEM; - goto free_locks; - } + omap_lock = &state->lock[i]; omap_lock->lock.dev = &pdev->dev; omap_lock->lock.id = i; @@ -148,30 +146,19 @@ static int __devinit omap_hwspinlock_probe(struct platform_device *pdev) omap_lock->addr = io_base + LOCK_BASE_OFFSET + sizeof(u32) * i; ret = hwspin_lock_register(&omap_lock->lock); - if (ret) { - kfree(omap_lock); + if (ret) goto free_locks; - } } return 0; free_locks: - while (--i >= 0) { - lock = hwspin_lock_unregister(i); - /* this should't happen, but let's give our best effort */ - if (!lock) { - dev_err(&pdev->dev, "%s: cleanups failed\n", __func__); - continue; - } - omap_lock = to_omap_hwspinlock(lock); - kfree(omap_lock); - } + while (--i >= 0) + hwspin_lock_unregister(i); pm_runtime_disable(&pdev->dev); + kfree(state); iounmap_base: iounmap(io_base); -free_state: - kfree(state); return ret; } @@ -179,7 +166,6 @@ static int omap_hwspinlock_remove(struct platform_device *pdev) { struct omap_hwspinlock_state *state = platform_get_drvdata(pdev); struct hwspinlock *lock; - struct omap_hwspinlock *omap_lock; int i; for (i = 0; i < state->num_locks; i++) { @@ -190,9 +176,6 @@ static int omap_hwspinlock_remove(struct platform_device *pdev) dev_err(&pdev->dev, "%s: failed on %d\n", __func__, i); return -EBUSY; } - - omap_lock = to_omap_hwspinlock(lock); - kfree(omap_lock); } pm_runtime_disable(&pdev->dev); From c3c1250e93a7ab1327a9fc49d2a22405672f4204 Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Mon, 5 Sep 2011 23:15:06 +0300 Subject: [PATCH 0243/1531] hwspinlock/core/omap: fix id issues on multiple hwspinlock devices hwspinlock devices provide system-wide hardware locks that are used by remote processors that have no other way to achieve synchronization. To achieve that, each physical lock must have a system-wide id number that is agreed upon, otherwise remote processors can't possibly assume they're using the same hardware lock. Usually boards have a single hwspinlock device, which provides several hwspinlocks, and in this case, they can be trivially numbered 0 to (num-of-locks - 1). In case boards have several hwspinlocks devices, a different base id should be used for each hwspinlock device (they can't all use 0 as a starting id!). While this is certainly not common, it's just plain wrong to just silently use 0 as a base id whenever the hwspinlock driver is probed. This patch provides a hwspinlock_pdata structure, that boards can use to set a different base id for each of the hwspinlock devices they may have, and demonstrates how to use it with the omap hwspinlock driver. While we're at it, make sure the hwspinlock core prints an explicit error message in case an hwspinlock is registered with an id number that already exists; this will help users catch such base id issues. Reported-by: Arnd Bergmann Signed-off-by: Ohad Ben-Cohen Acked-by: Tony Lindgren --- arch/arm/mach-omap2/hwspinlock.c | 8 +++++++- drivers/hwspinlock/hwspinlock_core.c | 2 ++ drivers/hwspinlock/omap_hwspinlock.c | 6 +++++- include/linux/hwspinlock.h | 28 ++++++++++++++++++++++++++++ 4 files changed, 42 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-omap2/hwspinlock.c b/arch/arm/mach-omap2/hwspinlock.c index 06d4a80660a5..eb7e509957db 100644 --- a/arch/arm/mach-omap2/hwspinlock.c +++ b/arch/arm/mach-omap2/hwspinlock.c @@ -19,10 +19,15 @@ #include #include #include +#include #include #include +static struct hwspinlock_pdata omap_hwspinlock_pdata __initdata = { + .base_id = 0, +}; + struct omap_device_pm_latency omap_spinlock_latency[] = { { .deactivate_func = omap_device_idle_hwmods, @@ -48,7 +53,8 @@ int __init hwspinlocks_init(void) if (oh == NULL) return -EINVAL; - od = omap_device_build(dev_name, 0, oh, NULL, 0, + od = omap_device_build(dev_name, 0, oh, &omap_hwspinlock_pdata, + sizeof(struct hwspinlock_pdata), omap_spinlock_latency, ARRAY_SIZE(omap_spinlock_latency), false); if (IS_ERR(od)) { diff --git a/drivers/hwspinlock/hwspinlock_core.c b/drivers/hwspinlock/hwspinlock_core.c index af5175c5d5f4..4eb85b4a320e 100644 --- a/drivers/hwspinlock/hwspinlock_core.c +++ b/drivers/hwspinlock/hwspinlock_core.c @@ -282,6 +282,8 @@ int hwspin_lock_register(struct hwspinlock *hwlock) spin_lock(&hwspinlock_tree_lock); ret = radix_tree_insert(&hwspinlock_tree, hwlock->id, hwlock); + if (ret == -EEXIST) + pr_err("hwspinlock id %d already exists!\n", hwlock->id); if (ret) goto out; diff --git a/drivers/hwspinlock/omap_hwspinlock.c b/drivers/hwspinlock/omap_hwspinlock.c index d0583480fe33..2044d181e49d 100644 --- a/drivers/hwspinlock/omap_hwspinlock.c +++ b/drivers/hwspinlock/omap_hwspinlock.c @@ -94,12 +94,16 @@ static const struct hwspinlock_ops omap_hwspinlock_ops = { static int __devinit omap_hwspinlock_probe(struct platform_device *pdev) { + struct hwspinlock_pdata *pdata = pdev->dev.platform_data; struct omap_hwspinlock *omap_lock; struct omap_hwspinlock_state *state; struct resource *res; void __iomem *io_base; int i, ret; + if (!pdata) + return -ENODEV; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -ENODEV; @@ -141,7 +145,7 @@ static int __devinit omap_hwspinlock_probe(struct platform_device *pdev) omap_lock = &state->lock[i]; omap_lock->lock.dev = &pdev->dev; - omap_lock->lock.id = i; + omap_lock->lock.id = pdata->base_id + i; omap_lock->lock.ops = &omap_hwspinlock_ops; omap_lock->addr = io_base + LOCK_BASE_OFFSET + sizeof(u32) * i; diff --git a/include/linux/hwspinlock.h b/include/linux/hwspinlock.h index 8390efc457eb..f85cef5452f2 100644 --- a/include/linux/hwspinlock.h +++ b/include/linux/hwspinlock.h @@ -27,6 +27,34 @@ struct hwspinlock; +/** + * struct hwspinlock_pdata - platform data for hwspinlock drivers + * @base_id: base id for this hwspinlock device + * + * hwspinlock devices provide system-wide hardware locks that are used + * by remote processors that have no other way to achieve synchronization. + * + * To achieve that, each physical lock must have a system-wide id number + * that is agreed upon, otherwise remote processors can't possibly assume + * they're using the same hardware lock. + * + * Usually boards have a single hwspinlock device, which provides several + * hwspinlocks, and in this case, they can be trivially numbered 0 to + * (num-of-locks - 1). + * + * In case boards have several hwspinlocks devices, a different base id + * should be used for each hwspinlock device (they can't all use 0 as + * a starting id!). + * + * This platform data structure should be used to provide the base id + * for each device (which is trivially 0 when only a single hwspinlock + * device exists). It can be shared between different platforms, hence + * its location. + */ +struct hwspinlock_pdata { + int base_id; +}; + #if defined(CONFIG_HWSPINLOCK) || defined(CONFIG_HWSPINLOCK_MODULE) int hwspin_lock_register(struct hwspinlock *lock); From 93b465c2e186d96fb90012ba0f9372eb9952e732 Mon Sep 17 00:00:00 2001 From: Juan Gutierrez Date: Tue, 6 Sep 2011 09:30:16 +0300 Subject: [PATCH 0244/1531] hwspinlock/core: use a mutex to protect the radix tree Since we're using non-atomic radix tree allocations, we should be protecting the tree using a mutex and not a spinlock. Non-atomic allocations and process context locking is good enough, as the tree is manipulated only when locks are registered/ unregistered/requested/freed. The locks themselves are still protected by spinlocks of course, and mutexes are not involved in the locking/unlocking paths. Cc: Signed-off-by: Juan Gutierrez [ohad@wizery.com: rewrite the commit log, #include mutex.h, add minor commentary] [ohad@wizery.com: update register/unregister parts in hwspinlock.txt] Signed-off-by: Ohad Ben-Cohen --- Documentation/hwspinlock.txt | 18 +++++------ drivers/hwspinlock/hwspinlock_core.c | 45 +++++++++++++--------------- 2 files changed, 27 insertions(+), 36 deletions(-) diff --git a/Documentation/hwspinlock.txt b/Documentation/hwspinlock.txt index bbaa4649b637..9171f9120143 100644 --- a/Documentation/hwspinlock.txt +++ b/Documentation/hwspinlock.txt @@ -39,23 +39,20 @@ independent, drivers. in case an unused hwspinlock isn't available. Users of this API will usually want to communicate the lock's id to the remote core before it can be used to achieve synchronization. - Can be called from an atomic context (this function will not sleep) but - not from within interrupt context. + Should be called from a process context (might sleep). struct hwspinlock *hwspin_lock_request_specific(unsigned int id); - assign a specific hwspinlock id and return its address, or NULL if that hwspinlock is already in use. Usually board code will be calling this function in order to reserve specific hwspinlock ids for predefined purposes. - Can be called from an atomic context (this function will not sleep) but - not from within interrupt context. + Should be called from a process context (might sleep). int hwspin_lock_free(struct hwspinlock *hwlock); - free a previously-assigned hwspinlock; returns 0 on success, or an appropriate error code on failure (e.g. -EINVAL if the hwspinlock is already free). - Can be called from an atomic context (this function will not sleep) but - not from within interrupt context. + Should be called from a process context (might sleep). int hwspin_lock_timeout(struct hwspinlock *hwlock, unsigned int timeout); - lock a previously-assigned hwspinlock with a timeout limit (specified in @@ -232,15 +229,14 @@ int hwspinlock_example2(void) int hwspin_lock_register(struct hwspinlock *hwlock); - to be called from the underlying platform-specific implementation, in - order to register a new hwspinlock instance. Can be called from an atomic - context (this function will not sleep) but not from within interrupt - context. Returns 0 on success, or appropriate error code on failure. + order to register a new hwspinlock instance. Should be called from + a process context (this function might sleep). + Returns 0 on success, or appropriate error code on failure. struct hwspinlock *hwspin_lock_unregister(unsigned int id); - to be called from the underlying vendor-specific implementation, in order to unregister an existing (and unused) hwspinlock instance. - Can be called from an atomic context (will not sleep) but not from - within interrupt context. + Should be called from a process context (this function might sleep). Returns the address of hwspinlock on success, or NULL on error (e.g. if the hwspinlock is sill in use). diff --git a/drivers/hwspinlock/hwspinlock_core.c b/drivers/hwspinlock/hwspinlock_core.c index 4eb85b4a320e..0d20b82df0a7 100644 --- a/drivers/hwspinlock/hwspinlock_core.c +++ b/drivers/hwspinlock/hwspinlock_core.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "hwspinlock_internal.h" @@ -52,10 +53,12 @@ static RADIX_TREE(hwspinlock_tree, GFP_KERNEL); /* - * Synchronization of access to the tree is achieved using this spinlock, + * Synchronization of access to the tree is achieved using this mutex, * as the radix-tree API requires that users provide all synchronisation. + * A mutex is needed because we're using non-atomic radix tree allocations. */ -static DEFINE_SPINLOCK(hwspinlock_tree_lock); +static DEFINE_MUTEX(hwspinlock_tree_lock); + /** * __hwspin_trylock() - attempt to lock a specific hwspinlock @@ -261,8 +264,7 @@ EXPORT_SYMBOL_GPL(__hwspin_unlock); * This function should be called from the underlying platform-specific * implementation, to register a new hwspinlock instance. * - * Can be called from an atomic context (will not sleep) but not from - * within interrupt context. + * Should be called from a process context (might sleep) * * Returns 0 on success, or an appropriate error code on failure */ @@ -279,7 +281,7 @@ int hwspin_lock_register(struct hwspinlock *hwlock) spin_lock_init(&hwlock->lock); - spin_lock(&hwspinlock_tree_lock); + mutex_lock(&hwspinlock_tree_lock); ret = radix_tree_insert(&hwspinlock_tree, hwlock->id, hwlock); if (ret == -EEXIST) @@ -295,7 +297,7 @@ int hwspin_lock_register(struct hwspinlock *hwlock) WARN_ON(tmp != hwlock); out: - spin_unlock(&hwspinlock_tree_lock); + mutex_unlock(&hwspinlock_tree_lock); return ret; } EXPORT_SYMBOL_GPL(hwspin_lock_register); @@ -307,8 +309,7 @@ EXPORT_SYMBOL_GPL(hwspin_lock_register); * This function should be called from the underlying platform-specific * implementation, to unregister an existing (and unused) hwspinlock. * - * Can be called from an atomic context (will not sleep) but not from - * within interrupt context. + * Should be called from a process context (might sleep) * * Returns the address of hwspinlock @id on success, or NULL on failure */ @@ -317,7 +318,7 @@ struct hwspinlock *hwspin_lock_unregister(unsigned int id) struct hwspinlock *hwlock = NULL; int ret; - spin_lock(&hwspinlock_tree_lock); + mutex_lock(&hwspinlock_tree_lock); /* make sure the hwspinlock is not in use (tag is set) */ ret = radix_tree_tag_get(&hwspinlock_tree, id, HWSPINLOCK_UNUSED); @@ -333,7 +334,7 @@ struct hwspinlock *hwspin_lock_unregister(unsigned int id) } out: - spin_unlock(&hwspinlock_tree_lock); + mutex_unlock(&hwspinlock_tree_lock); return hwlock; } EXPORT_SYMBOL_GPL(hwspin_lock_unregister); @@ -402,9 +403,7 @@ EXPORT_SYMBOL_GPL(hwspin_lock_get_id); * to the remote core before it can be used for synchronization (to get the * id of a given hwlock, use hwspin_lock_get_id()). * - * Can be called from an atomic context (will not sleep) but not from - * within interrupt context (simply because there is no use case for - * that yet). + * Should be called from a process context (might sleep) * * Returns the address of the assigned hwspinlock, or NULL on error */ @@ -413,7 +412,7 @@ struct hwspinlock *hwspin_lock_request(void) struct hwspinlock *hwlock; int ret; - spin_lock(&hwspinlock_tree_lock); + mutex_lock(&hwspinlock_tree_lock); /* look for an unused lock */ ret = radix_tree_gang_lookup_tag(&hwspinlock_tree, (void **)&hwlock, @@ -433,7 +432,7 @@ struct hwspinlock *hwspin_lock_request(void) hwlock = NULL; out: - spin_unlock(&hwspinlock_tree_lock); + mutex_unlock(&hwspinlock_tree_lock); return hwlock; } EXPORT_SYMBOL_GPL(hwspin_lock_request); @@ -447,9 +446,7 @@ EXPORT_SYMBOL_GPL(hwspin_lock_request); * Usually early board code will be calling this function in order to * reserve specific hwspinlock ids for predefined purposes. * - * Can be called from an atomic context (will not sleep) but not from - * within interrupt context (simply because there is no use case for - * that yet). + * Should be called from a process context (might sleep) * * Returns the address of the assigned hwspinlock, or NULL on error */ @@ -458,7 +455,7 @@ struct hwspinlock *hwspin_lock_request_specific(unsigned int id) struct hwspinlock *hwlock; int ret; - spin_lock(&hwspinlock_tree_lock); + mutex_lock(&hwspinlock_tree_lock); /* make sure this hwspinlock exists */ hwlock = radix_tree_lookup(&hwspinlock_tree, id); @@ -484,7 +481,7 @@ struct hwspinlock *hwspin_lock_request_specific(unsigned int id) hwlock = NULL; out: - spin_unlock(&hwspinlock_tree_lock); + mutex_unlock(&hwspinlock_tree_lock); return hwlock; } EXPORT_SYMBOL_GPL(hwspin_lock_request_specific); @@ -497,9 +494,7 @@ EXPORT_SYMBOL_GPL(hwspin_lock_request_specific); * Should only be called with an @hwlock that was retrieved from * an earlier call to omap_hwspin_lock_request{_specific}. * - * Can be called from an atomic context (will not sleep) but not from - * within interrupt context (simply because there is no use case for - * that yet). + * Should be called from a process context (might sleep) * * Returns 0 on success, or an appropriate error code on failure */ @@ -513,7 +508,7 @@ int hwspin_lock_free(struct hwspinlock *hwlock) return -EINVAL; } - spin_lock(&hwspinlock_tree_lock); + mutex_lock(&hwspinlock_tree_lock); /* make sure the hwspinlock is used */ ret = radix_tree_tag_get(&hwspinlock_tree, hwlock->id, @@ -540,7 +535,7 @@ int hwspin_lock_free(struct hwspinlock *hwlock) module_put(hwlock->dev->driver->owner); out: - spin_unlock(&hwspinlock_tree_lock); + mutex_unlock(&hwspinlock_tree_lock); return ret; } EXPORT_SYMBOL_GPL(hwspin_lock_free); From c536abfdf5227987b8a72ff955b64e62fd58fe91 Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Thu, 8 Sep 2011 21:16:17 +0300 Subject: [PATCH 0245/1531] hwspinlock/core: remove stubs for register/unregister hwspinlock drivers must anyway select CONFIG_HWSPINLOCK, so there's no point in having register/unregister stubs. Removing those stubs will only make it easier for developers to catch CONFIG_HWSPINLOCK mis-.config-urations. Signed-off-by: Ohad Ben-Cohen --- include/linux/hwspinlock.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/include/linux/hwspinlock.h b/include/linux/hwspinlock.h index f85cef5452f2..c246522a9551 100644 --- a/include/linux/hwspinlock.h +++ b/include/linux/hwspinlock.h @@ -122,16 +122,6 @@ static inline int hwspin_lock_get_id(struct hwspinlock *hwlock) return 0; } -static inline int hwspin_lock_register(struct hwspinlock *hwlock) -{ - return -ENODEV; -} - -static inline struct hwspinlock *hwspin_lock_unregister(unsigned int id) -{ - return NULL; -} - #endif /* !CONFIG_HWSPINLOCK */ /** From 300bab9770e2bd10262bcc78e7249fdce2c74b38 Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Tue, 6 Sep 2011 15:39:21 +0300 Subject: [PATCH 0246/1531] hwspinlock/core: register a bank of hwspinlocks in a single API call Hardware Spinlock devices usually contain numerous locks (known devices today support between 32 to 256 locks). Originally hwspinlock core required drivers to register (and later, when needed, unregister) each lock separately. That worked, but required hwspinlocks drivers to do a bit extra work when they were probed/removed. This patch changes hwspin_lock_{un}register() to allow a bank of hwspinlocks to be {un}registered in a single invocation. A new 'struct hwspinlock_device', which contains an array of 'struct hwspinlock's is now being passed to the core upon registration (so instead of wrapping each struct hwspinlock, a priv member has been added to allow drivers to piggyback their private data with each hwspinlock). While at it, several per-lock members were moved to be per-device: 1. struct device *dev 2. struct hwspinlock_ops *ops In addition, now that the array of locks is handled by the core, there's no reason to maintain a per-lock 'int id' member: the id of the lock anyway equals to its index in the bank's array plus the bank's base_id. Remove this per-lock id member too, and instead use a simple pointers arithmetic to derive it. As a result of this change, hwspinlocks drivers are now simpler and smaller (about %20 code reduction) and the memory footprint of the hwspinlock framework is reduced. Signed-off-by: Ohad Ben-Cohen --- Documentation/hwspinlock.txt | 58 +++++--- drivers/hwspinlock/hwspinlock_core.c | 165 +++++++++++++++-------- drivers/hwspinlock/hwspinlock_internal.h | 38 ++++-- drivers/hwspinlock/omap_hwspinlock.c | 86 ++++-------- include/linux/hwspinlock.h | 8 +- 5 files changed, 211 insertions(+), 144 deletions(-) diff --git a/Documentation/hwspinlock.txt b/Documentation/hwspinlock.txt index 9171f9120143..a903ee5e9776 100644 --- a/Documentation/hwspinlock.txt +++ b/Documentation/hwspinlock.txt @@ -227,42 +227,62 @@ int hwspinlock_example2(void) 4. API for implementors - int hwspin_lock_register(struct hwspinlock *hwlock); + int hwspin_lock_register(struct hwspinlock_device *bank, struct device *dev, + const struct hwspinlock_ops *ops, int base_id, int num_locks); - to be called from the underlying platform-specific implementation, in - order to register a new hwspinlock instance. Should be called from - a process context (this function might sleep). + order to register a new hwspinlock device (which is usually a bank of + numerous locks). Should be called from a process context (this function + might sleep). Returns 0 on success, or appropriate error code on failure. - struct hwspinlock *hwspin_lock_unregister(unsigned int id); + int hwspin_lock_unregister(struct hwspinlock_device *bank); - to be called from the underlying vendor-specific implementation, in order - to unregister an existing (and unused) hwspinlock instance. + to unregister an hwspinlock device (which is usually a bank of numerous + locks). Should be called from a process context (this function might sleep). Returns the address of hwspinlock on success, or NULL on error (e.g. if the hwspinlock is sill in use). -5. struct hwspinlock +5. Important structs -This struct represents an hwspinlock instance. It is registered by the -underlying hwspinlock implementation using the hwspin_lock_register() API. +struct hwspinlock_device is a device which usually contains a bank +of hardware locks. It is registered by the underlying hwspinlock +implementation using the hwspin_lock_register() API. /** - * struct hwspinlock - vendor-specific hwspinlock implementation - * - * @dev: underlying device, will be used with runtime PM api - * @ops: vendor-specific hwspinlock handlers - * @id: a global, unique, system-wide, index of the lock. - * @lock: initialized and used by hwspinlock core + * struct hwspinlock_device - a device which usually spans numerous hwspinlocks + * @dev: underlying device, will be used to invoke runtime PM api + * @ops: platform-specific hwspinlock handlers + * @base_id: id index of the first lock in this device + * @num_locks: number of locks in this device + * @lock: dynamically allocated array of 'struct hwspinlock' */ -struct hwspinlock { +struct hwspinlock_device { struct device *dev; const struct hwspinlock_ops *ops; - int id; + int base_id; + int num_locks; + struct hwspinlock lock[0]; +}; + +struct hwspinlock_device contains an array of hwspinlock structs, each +of which represents a single hardware lock: + +/** + * struct hwspinlock - this struct represents a single hwspinlock instance + * @bank: the hwspinlock_device structure which owns this lock + * @lock: initialized and used by hwspinlock core + * @priv: private data, owned by the underlying platform-specific hwspinlock drv + */ +struct hwspinlock { + struct hwspinlock_device *bank; spinlock_t lock; + void *priv; }; -The underlying implementation is responsible to assign the dev, ops and id -members. The lock member, OTOH, is initialized and used by the hwspinlock -core. +When registering a bank of locks, the hwspinlock driver only needs to +set the priv members of the locks. The rest of the members are set and +initialized by the hwspinlock core itself. 6. Implementation callbacks diff --git a/drivers/hwspinlock/hwspinlock_core.c b/drivers/hwspinlock/hwspinlock_core.c index 0d20b82df0a7..61c9cf15fa52 100644 --- a/drivers/hwspinlock/hwspinlock_core.c +++ b/drivers/hwspinlock/hwspinlock_core.c @@ -117,7 +117,7 @@ int __hwspin_trylock(struct hwspinlock *hwlock, int mode, unsigned long *flags) return -EBUSY; /* try to take the hwspinlock device */ - ret = hwlock->ops->trylock(hwlock); + ret = hwlock->bank->ops->trylock(hwlock); /* if hwlock is already taken, undo spin_trylock_* and exit */ if (!ret) { @@ -199,8 +199,8 @@ int __hwspin_lock_timeout(struct hwspinlock *hwlock, unsigned int to, * Allow platform-specific relax handlers to prevent * hogging the interconnect (no sleeping, though) */ - if (hwlock->ops->relax) - hwlock->ops->relax(hwlock); + if (hwlock->bank->ops->relax) + hwlock->bank->ops->relax(hwlock); } return ret; @@ -245,7 +245,7 @@ void __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags) */ mb(); - hwlock->ops->unlock(hwlock); + hwlock->bank->ops->unlock(hwlock); /* Undo the spin_trylock{_irq, _irqsave} called while locking */ if (mode == HWLOCK_IRQSTATE) @@ -257,63 +257,32 @@ void __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags) } EXPORT_SYMBOL_GPL(__hwspin_unlock); -/** - * hwspin_lock_register() - register a new hw spinlock - * @hwlock: hwspinlock to register. - * - * This function should be called from the underlying platform-specific - * implementation, to register a new hwspinlock instance. - * - * Should be called from a process context (might sleep) - * - * Returns 0 on success, or an appropriate error code on failure - */ -int hwspin_lock_register(struct hwspinlock *hwlock) +static int hwspin_lock_register_single(struct hwspinlock *hwlock, int id) { struct hwspinlock *tmp; int ret; - if (!hwlock || !hwlock->ops || - !hwlock->ops->trylock || !hwlock->ops->unlock) { - pr_err("invalid parameters\n"); - return -EINVAL; - } - - spin_lock_init(&hwlock->lock); - mutex_lock(&hwspinlock_tree_lock); - ret = radix_tree_insert(&hwspinlock_tree, hwlock->id, hwlock); - if (ret == -EEXIST) - pr_err("hwspinlock id %d already exists!\n", hwlock->id); - if (ret) + ret = radix_tree_insert(&hwspinlock_tree, id, hwlock); + if (ret) { + if (ret == -EEXIST) + pr_err("hwspinlock id %d already exists!\n", id); goto out; + } /* mark this hwspinlock as available */ - tmp = radix_tree_tag_set(&hwspinlock_tree, hwlock->id, - HWSPINLOCK_UNUSED); + tmp = radix_tree_tag_set(&hwspinlock_tree, id, HWSPINLOCK_UNUSED); /* self-sanity check which should never fail */ WARN_ON(tmp != hwlock); out: mutex_unlock(&hwspinlock_tree_lock); - return ret; + return 0; } -EXPORT_SYMBOL_GPL(hwspin_lock_register); -/** - * hwspin_lock_unregister() - unregister an hw spinlock - * @id: index of the specific hwspinlock to unregister - * - * This function should be called from the underlying platform-specific - * implementation, to unregister an existing (and unused) hwspinlock. - * - * Should be called from a process context (might sleep) - * - * Returns the address of hwspinlock @id on success, or NULL on failure - */ -struct hwspinlock *hwspin_lock_unregister(unsigned int id) +static struct hwspinlock *hwspin_lock_unregister_single(unsigned int id) { struct hwspinlock *hwlock = NULL; int ret; @@ -337,6 +306,88 @@ struct hwspinlock *hwspin_lock_unregister(unsigned int id) mutex_unlock(&hwspinlock_tree_lock); return hwlock; } + +/** + * hwspin_lock_register() - register a new hw spinlock device + * @bank: the hwspinlock device, which usually provides numerous hw locks + * @dev: the backing device + * @ops: hwspinlock handlers for this device + * @base_id: id of the first hardware spinlock in this bank + * @num_locks: number of hwspinlocks provided by this device + * + * This function should be called from the underlying platform-specific + * implementation, to register a new hwspinlock device instance. + * + * Should be called from a process context (might sleep) + * + * Returns 0 on success, or an appropriate error code on failure + */ +int hwspin_lock_register(struct hwspinlock_device *bank, struct device *dev, + const struct hwspinlock_ops *ops, int base_id, int num_locks) +{ + struct hwspinlock *hwlock; + int ret = 0, i; + + if (!bank || !ops || !dev || !num_locks || !ops->trylock || + !ops->unlock) { + pr_err("invalid parameters\n"); + return -EINVAL; + } + + bank->dev = dev; + bank->ops = ops; + bank->base_id = base_id; + bank->num_locks = num_locks; + + for (i = 0; i < num_locks; i++) { + hwlock = &bank->lock[i]; + + spin_lock_init(&hwlock->lock); + hwlock->bank = bank; + + ret = hwspin_lock_register_single(hwlock, i); + if (ret) + goto reg_failed; + } + + return 0; + +reg_failed: + while (--i >= 0) + hwspin_lock_unregister_single(i); + return ret; +} +EXPORT_SYMBOL_GPL(hwspin_lock_register); + +/** + * hwspin_lock_unregister() - unregister an hw spinlock device + * @bank: the hwspinlock device, which usually provides numerous hw locks + * + * This function should be called from the underlying platform-specific + * implementation, to unregister an existing (and unused) hwspinlock. + * + * Should be called from a process context (might sleep) + * + * Returns 0 on success, or an appropriate error code on failure + */ +int hwspin_lock_unregister(struct hwspinlock_device *bank) +{ + struct hwspinlock *hwlock, *tmp; + int i; + + for (i = 0; i < bank->num_locks; i++) { + hwlock = &bank->lock[i]; + + tmp = hwspin_lock_unregister_single(bank->base_id + i); + if (!tmp) + return -EBUSY; + + /* self-sanity check that should never fail */ + WARN_ON(tmp != hwlock); + } + + return 0; +} EXPORT_SYMBOL_GPL(hwspin_lock_unregister); /** @@ -351,24 +402,25 @@ EXPORT_SYMBOL_GPL(hwspin_lock_unregister); */ static int __hwspin_lock_request(struct hwspinlock *hwlock) { + struct device *dev = hwlock->bank->dev; struct hwspinlock *tmp; int ret; /* prevent underlying implementation from being removed */ - if (!try_module_get(hwlock->dev->driver->owner)) { - dev_err(hwlock->dev, "%s: can't get owner\n", __func__); + if (!try_module_get(dev->driver->owner)) { + dev_err(dev, "%s: can't get owner\n", __func__); return -EINVAL; } /* notify PM core that power is now needed */ - ret = pm_runtime_get_sync(hwlock->dev); + ret = pm_runtime_get_sync(dev); if (ret < 0) { - dev_err(hwlock->dev, "%s: can't power on device\n", __func__); + dev_err(dev, "%s: can't power on device\n", __func__); return ret; } /* mark hwspinlock as used, should not fail */ - tmp = radix_tree_tag_clear(&hwspinlock_tree, hwlock->id, + tmp = radix_tree_tag_clear(&hwspinlock_tree, hwlock_to_id(hwlock), HWSPINLOCK_UNUSED); /* self-sanity check that should never fail */ @@ -390,7 +442,7 @@ int hwspin_lock_get_id(struct hwspinlock *hwlock) return -EINVAL; } - return hwlock->id; + return hwlock_to_id(hwlock); } EXPORT_SYMBOL_GPL(hwspin_lock_get_id); @@ -465,7 +517,7 @@ struct hwspinlock *hwspin_lock_request_specific(unsigned int id) } /* sanity check (this shouldn't happen) */ - WARN_ON(hwlock->id != id); + WARN_ON(hwlock_to_id(hwlock) != id); /* make sure this hwspinlock is unused */ ret = radix_tree_tag_get(&hwspinlock_tree, id, HWSPINLOCK_UNUSED); @@ -500,6 +552,7 @@ EXPORT_SYMBOL_GPL(hwspin_lock_request_specific); */ int hwspin_lock_free(struct hwspinlock *hwlock) { + struct device *dev = hwlock->bank->dev; struct hwspinlock *tmp; int ret; @@ -511,28 +564,28 @@ int hwspin_lock_free(struct hwspinlock *hwlock) mutex_lock(&hwspinlock_tree_lock); /* make sure the hwspinlock is used */ - ret = radix_tree_tag_get(&hwspinlock_tree, hwlock->id, + ret = radix_tree_tag_get(&hwspinlock_tree, hwlock_to_id(hwlock), HWSPINLOCK_UNUSED); if (ret == 1) { - dev_err(hwlock->dev, "%s: hwlock is already free\n", __func__); + dev_err(dev, "%s: hwlock is already free\n", __func__); dump_stack(); ret = -EINVAL; goto out; } /* notify the underlying device that power is not needed */ - ret = pm_runtime_put(hwlock->dev); + ret = pm_runtime_put(dev); if (ret < 0) goto out; /* mark this hwspinlock as available */ - tmp = radix_tree_tag_set(&hwspinlock_tree, hwlock->id, + tmp = radix_tree_tag_set(&hwspinlock_tree, hwlock_to_id(hwlock), HWSPINLOCK_UNUSED); /* sanity check (this shouldn't happen) */ WARN_ON(tmp != hwlock); - module_put(hwlock->dev->driver->owner); + module_put(dev->driver->owner); out: mutex_unlock(&hwspinlock_tree_lock); diff --git a/drivers/hwspinlock/hwspinlock_internal.h b/drivers/hwspinlock/hwspinlock_internal.h index fb25830c2ee7..d26f78b8f214 100644 --- a/drivers/hwspinlock/hwspinlock_internal.h +++ b/drivers/hwspinlock/hwspinlock_internal.h @@ -21,6 +21,8 @@ #include #include +struct hwspinlock_device; + /** * struct hwspinlock_ops - platform-specific hwspinlock handlers * @@ -39,21 +41,37 @@ struct hwspinlock_ops { /** * struct hwspinlock - this struct represents a single hwspinlock instance - * - * @dev: underlying device, will be used to invoke runtime PM api - * @ops: platform-specific hwspinlock handlers - * @id: a global, unique, system-wide, index of the lock. + * @bank: the hwspinlock_device structure which owns this lock * @lock: initialized and used by hwspinlock core - * - * Note: currently simplicity was opted for, but later we can squeeze some - * memory bytes by grouping dev, ops in a single - * per-platform struct, and have all hwspinlocks point at it. + * @priv: private data, owned by the underlying platform-specific hwspinlock drv */ struct hwspinlock { + struct hwspinlock_device *bank; + spinlock_t lock; + void *priv; +}; + +/** + * struct hwspinlock_device - a device which usually spans numerous hwspinlocks + * @dev: underlying device, will be used to invoke runtime PM api + * @ops: platform-specific hwspinlock handlers + * @base_id: id index of the first lock in this device + * @num_locks: number of locks in this device + * @lock: dynamically allocated array of 'struct hwspinlock' + */ +struct hwspinlock_device { struct device *dev; const struct hwspinlock_ops *ops; - int id; - spinlock_t lock; + int base_id; + int num_locks; + struct hwspinlock lock[0]; }; +static inline int hwlock_to_id(struct hwspinlock *hwlock) +{ + int local_id = hwlock - &hwlock->bank->lock[0]; + + return hwlock->bank->base_id + local_id; +} + #endif /* __HWSPINLOCK_HWSPINLOCK_H */ diff --git a/drivers/hwspinlock/omap_hwspinlock.c b/drivers/hwspinlock/omap_hwspinlock.c index 2044d181e49d..aec30064a466 100644 --- a/drivers/hwspinlock/omap_hwspinlock.c +++ b/drivers/hwspinlock/omap_hwspinlock.c @@ -41,34 +41,20 @@ #define SPINLOCK_NOTTAKEN (0) /* free */ #define SPINLOCK_TAKEN (1) /* locked */ -#define to_omap_hwspinlock(lock) \ - container_of(lock, struct omap_hwspinlock, lock) - -struct omap_hwspinlock { - struct hwspinlock lock; - void __iomem *addr; -}; - -struct omap_hwspinlock_state { - int num_locks; /* Total number of locks in system */ - void __iomem *io_base; /* Mapped base address */ - struct omap_hwspinlock lock[0]; /* Array of 'num_locks' locks */ -}; - static int omap_hwspinlock_trylock(struct hwspinlock *lock) { - struct omap_hwspinlock *omap_lock = to_omap_hwspinlock(lock); + void __iomem *lock_addr = lock->priv; /* attempt to acquire the lock by reading its value */ - return (SPINLOCK_NOTTAKEN == readl(omap_lock->addr)); + return (SPINLOCK_NOTTAKEN == readl(lock_addr)); } static void omap_hwspinlock_unlock(struct hwspinlock *lock) { - struct omap_hwspinlock *omap_lock = to_omap_hwspinlock(lock); + void __iomem *lock_addr = lock->priv; /* release the lock by writing 0 to it */ - writel(SPINLOCK_NOTTAKEN, omap_lock->addr); + writel(SPINLOCK_NOTTAKEN, lock_addr); } /* @@ -95,11 +81,11 @@ static const struct hwspinlock_ops omap_hwspinlock_ops = { static int __devinit omap_hwspinlock_probe(struct platform_device *pdev) { struct hwspinlock_pdata *pdata = pdev->dev.platform_data; - struct omap_hwspinlock *omap_lock; - struct omap_hwspinlock_state *state; + struct hwspinlock_device *bank; + struct hwspinlock *hwlock; struct resource *res; void __iomem *io_base; - int i, ret; + int num_locks, i, ret; if (!pdata) return -ENODEV; @@ -122,18 +108,18 @@ static int __devinit omap_hwspinlock_probe(struct platform_device *pdev) goto iounmap_base; } - i *= 32; /* actual number of locks in this device */ + num_locks = i * 32; /* actual number of locks in this device */ - state = kzalloc(sizeof(*state) + i * sizeof(*omap_lock), GFP_KERNEL); - if (!state) { + bank = kzalloc(sizeof(*bank) + num_locks * sizeof(*hwlock), GFP_KERNEL); + if (!bank) { ret = -ENOMEM; goto iounmap_base; } - state->num_locks = i; - state->io_base = io_base; + platform_set_drvdata(pdev, bank); - platform_set_drvdata(pdev, state); + for (i = 0, hwlock = &bank->lock[0]; i < num_locks; i++, hwlock++) + hwlock->priv = io_base + LOCK_BASE_OFFSET + sizeof(u32) * i; /* * runtime PM will make sure the clock of this module is @@ -141,26 +127,16 @@ static int __devinit omap_hwspinlock_probe(struct platform_device *pdev) */ pm_runtime_enable(&pdev->dev); - for (i = 0; i < state->num_locks; i++) { - omap_lock = &state->lock[i]; - - omap_lock->lock.dev = &pdev->dev; - omap_lock->lock.id = pdata->base_id + i; - omap_lock->lock.ops = &omap_hwspinlock_ops; - omap_lock->addr = io_base + LOCK_BASE_OFFSET + sizeof(u32) * i; - - ret = hwspin_lock_register(&omap_lock->lock); - if (ret) - goto free_locks; - } + ret = hwspin_lock_register(bank, &pdev->dev, &omap_hwspinlock_ops, + pdata->base_id, num_locks); + if (ret) + goto reg_fail; return 0; -free_locks: - while (--i >= 0) - hwspin_lock_unregister(i); +reg_fail: pm_runtime_disable(&pdev->dev); - kfree(state); + kfree(bank); iounmap_base: iounmap(io_base); return ret; @@ -168,23 +144,19 @@ static int __devinit omap_hwspinlock_probe(struct platform_device *pdev) static int omap_hwspinlock_remove(struct platform_device *pdev) { - struct omap_hwspinlock_state *state = platform_get_drvdata(pdev); - struct hwspinlock *lock; - int i; - - for (i = 0; i < state->num_locks; i++) { - lock = hwspin_lock_unregister(i); - /* this shouldn't happen at this point. if it does, at least - * don't continue with the remove */ - if (!lock) { - dev_err(&pdev->dev, "%s: failed on %d\n", __func__, i); - return -EBUSY; - } + struct hwspinlock_device *bank = platform_get_drvdata(pdev); + void __iomem *io_base = bank->lock[0].priv - LOCK_BASE_OFFSET; + int ret; + + ret = hwspin_lock_unregister(bank); + if (ret) { + dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret); + return ret; } pm_runtime_disable(&pdev->dev); - iounmap(state->io_base); - kfree(state); + iounmap(io_base); + kfree(bank); return 0; } diff --git a/include/linux/hwspinlock.h b/include/linux/hwspinlock.h index c246522a9551..08a2fee40659 100644 --- a/include/linux/hwspinlock.h +++ b/include/linux/hwspinlock.h @@ -20,12 +20,15 @@ #include #include +#include /* hwspinlock mode argument */ #define HWLOCK_IRQSTATE 0x01 /* Disable interrupts, save state */ #define HWLOCK_IRQ 0x02 /* Disable interrupts, don't save state */ struct hwspinlock; +struct hwspinlock_device; +struct hwspinlock_ops; /** * struct hwspinlock_pdata - platform data for hwspinlock drivers @@ -57,8 +60,9 @@ struct hwspinlock_pdata { #if defined(CONFIG_HWSPINLOCK) || defined(CONFIG_HWSPINLOCK_MODULE) -int hwspin_lock_register(struct hwspinlock *lock); -struct hwspinlock *hwspin_lock_unregister(unsigned int id); +int hwspin_lock_register(struct hwspinlock_device *bank, struct device *dev, + const struct hwspinlock_ops *ops, int base_id, int num_locks); +int hwspin_lock_unregister(struct hwspinlock_device *bank); struct hwspinlock *hwspin_lock_request(void); struct hwspinlock *hwspin_lock_request_specific(unsigned int id); int hwspin_lock_free(struct hwspinlock *hwlock); From f84a8ecfca9229e9227c6ec84123b114ee634959 Mon Sep 17 00:00:00 2001 From: "Mathieu J. Poirier" Date: Thu, 8 Sep 2011 22:47:40 +0300 Subject: [PATCH 0247/1531] hwspinlock/u8500: add hwspinlock driver Add hwspinlock driver for U8500's Hsem hardware. At this point only HSem's protocol 1 is used (i.e. no interrupts). Signed-off-by: Mathieu Poirier Acked-by: Linus Walleij [ohad@wizery.com: adopt recent hwspin_lock_{un}register API changes] [ohad@wizery.com: set the owner member of the driver] [ohad@wizery.com: mark ->remove() function as __devexit] [ohad@wizery.com: write commit log] [ohad@wizery.com: small cleanups] Signed-off-by: Ohad Ben-Cohen --- drivers/hwspinlock/Kconfig | 11 ++ drivers/hwspinlock/Makefile | 1 + drivers/hwspinlock/u8500_hsem.c | 198 ++++++++++++++++++++++++++++++++ 3 files changed, 210 insertions(+) create mode 100644 drivers/hwspinlock/u8500_hsem.c diff --git a/drivers/hwspinlock/Kconfig b/drivers/hwspinlock/Kconfig index c8e7bdac1404..c7c3128393d1 100644 --- a/drivers/hwspinlock/Kconfig +++ b/drivers/hwspinlock/Kconfig @@ -18,4 +18,15 @@ config HWSPINLOCK_OMAP If unsure, say N. +config HSEM_U8500 + tristate "STE Hardware Semaphore functionality" + depends on ARCH_U8500 + select HWSPINLOCK + help + Say y here to support the STE Hardware Semaphore functionality, which + provides a synchronisation mechanism for the various processor on the + SoC. + + If unsure, say N. + endmenu diff --git a/drivers/hwspinlock/Makefile b/drivers/hwspinlock/Makefile index 5729a3f7ed3d..93eb64b66486 100644 --- a/drivers/hwspinlock/Makefile +++ b/drivers/hwspinlock/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_HWSPINLOCK) += hwspinlock_core.o obj-$(CONFIG_HWSPINLOCK_OMAP) += omap_hwspinlock.o +obj-$(CONFIG_HSEM_U8500) += u8500_hsem.o diff --git a/drivers/hwspinlock/u8500_hsem.c b/drivers/hwspinlock/u8500_hsem.c new file mode 100644 index 000000000000..143461a95ae4 --- /dev/null +++ b/drivers/hwspinlock/u8500_hsem.c @@ -0,0 +1,198 @@ +/* + * u8500 HWSEM driver + * + * Copyright (C) 2010-2011 ST-Ericsson + * + * Implements u8500 semaphore handling for protocol 1, no interrupts. + * + * Author: Mathieu Poirier + * Heavily borrowed from the work of : + * Simon Que + * Hari Kanigeri + * Ohad Ben-Cohen + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "hwspinlock_internal.h" + +/* + * Implementation of STE's HSem protocol 1 without interrutps. + * The only masterID we allow is '0x01' to force people to use + * HSems for synchronisation between processors rather than processes + * on the ARM core. + */ + +#define U8500_MAX_SEMAPHORE 32 /* a total of 32 semaphore */ +#define RESET_SEMAPHORE (0) /* free */ + +/* + * CPU ID for master running u8500 kernel. + * Hswpinlocks should only be used to synchonise operations + * between the Cortex A9 core and the other CPUs. Hence + * forcing the masterID to a preset value. + */ +#define HSEM_MASTER_ID 0x01 + +#define HSEM_REGISTER_OFFSET 0x08 + +#define HSEM_CTRL_REG 0x00 +#define HSEM_ICRALL 0x90 +#define HSEM_PROTOCOL_1 0x01 + +static int u8500_hsem_trylock(struct hwspinlock *lock) +{ + void __iomem *lock_addr = lock->priv; + + writel(HSEM_MASTER_ID, lock_addr); + + /* get only first 4 bit and compare to masterID. + * if equal, we have the semaphore, otherwise + * someone else has it. + */ + return (HSEM_MASTER_ID == (0x0F & readl(lock_addr))); +} + +static void u8500_hsem_unlock(struct hwspinlock *lock) +{ + void __iomem *lock_addr = lock->priv; + + /* release the lock by writing 0 to it */ + writel(RESET_SEMAPHORE, lock_addr); +} + +/* + * u8500: what value is recommended here ? + */ +static void u8500_hsem_relax(struct hwspinlock *lock) +{ + ndelay(50); +} + +static const struct hwspinlock_ops u8500_hwspinlock_ops = { + .trylock = u8500_hsem_trylock, + .unlock = u8500_hsem_unlock, + .relax = u8500_hsem_relax, +}; + +static int __devinit u8500_hsem_probe(struct platform_device *pdev) +{ + struct hwspinlock_pdata *pdata = pdev->dev.platform_data; + struct hwspinlock_device *bank; + struct hwspinlock *hwlock; + struct resource *res; + void __iomem *io_base; + int i, ret, num_locks = U8500_MAX_SEMAPHORE; + ulong val; + + if (!pdata) + return -ENODEV; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + io_base = ioremap(res->start, resource_size(res)); + if (!io_base) { + ret = -ENOMEM; + goto free_state; + } + + /* make sure protocol 1 is selected */ + val = readl(io_base + HSEM_CTRL_REG); + writel((val & ~HSEM_PROTOCOL_1), io_base + HSEM_CTRL_REG); + + /* clear all interrupts */ + writel(0xFFFF, io_base + HSEM_ICRALL); + + bank = kzalloc(sizeof(*bank) + num_locks * sizeof(*hwlock), GFP_KERNEL); + if (!bank) { + ret = -ENOMEM; + goto iounmap_base; + } + + platform_set_drvdata(pdev, bank); + + for (i = 0, hwlock = &bank->lock[0]; i < num_locks; i++, hwlock++) + hwlock->priv = io_base + HSEM_REGISTER_OFFSET + sizeof(u32) * i; + + /* no pm needed for HSem but required to comply with hwspilock core */ + pm_runtime_enable(&pdev->dev); + + ret = hwspin_lock_register(bank, &pdev->dev, &u8500_hwspinlock_ops, + pdata->base_id, num_locks); + if (ret) + goto reg_fail; + + return 0; + +reg_fail: + pm_runtime_disable(&pdev->dev); + kfree(bank); +iounmap_base: + iounmap(io_base); + return ret; +} + +static int __devexit u8500_hsem_remove(struct platform_device *pdev) +{ + struct hwspinlock_device *bank = platform_get_drvdata(pdev); + void __iomem *io_base = bank->lock[0].priv - HSEM_REGISTER_OFFSET; + int ret; + + /* clear all interrupts */ + writel(0xFFFF, io_base + HSEM_ICRALL); + + ret = hwspin_lock_unregister(bank); + if (ret) { + dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret); + return ret; + } + + pm_runtime_disable(&pdev->dev); + iounmap(io_base); + kfree(bank); + + return 0; +} + +static struct platform_driver u8500_hsem_driver = { + .probe = u8500_hsem_probe, + .remove = __devexit_p(u8500_hsem_remove), + .driver = { + .name = "u8500_hsem", + .owner = THIS_MODULE, + }, +}; + +static int __init u8500_hsem_init(void) +{ + return platform_driver_register(&u8500_hsem_driver); +} +/* board init code might need to reserve hwspinlocks for predefined purposes */ +postcore_initcall(u8500_hsem_init); + +static void __exit u8500_hsem_exit(void) +{ + platform_driver_unregister(&u8500_hsem_driver); +} +module_exit(u8500_hsem_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Hardware Spinlock driver for u8500"); +MODULE_AUTHOR("Mathieu Poirier "); From 9efb4a1bb9e46d26a15116e3c72b1b81c62d8337 Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Sun, 11 Sep 2011 18:54:50 +0300 Subject: [PATCH 0248/1531] hwspinlock/omap: omap_hwspinlock_remove should be __devexit Mark omap_hwspinlock_remove with __devexit (and use __devexit_p appropriately) so the function can be discarded when the conditions are met. Signed-off-by: Ohad Ben-Cohen --- drivers/hwspinlock/omap_hwspinlock.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwspinlock/omap_hwspinlock.c b/drivers/hwspinlock/omap_hwspinlock.c index aec30064a466..887d34effb3a 100644 --- a/drivers/hwspinlock/omap_hwspinlock.c +++ b/drivers/hwspinlock/omap_hwspinlock.c @@ -142,7 +142,7 @@ static int __devinit omap_hwspinlock_probe(struct platform_device *pdev) return ret; } -static int omap_hwspinlock_remove(struct platform_device *pdev) +static int __devexit omap_hwspinlock_remove(struct platform_device *pdev) { struct hwspinlock_device *bank = platform_get_drvdata(pdev); void __iomem *io_base = bank->lock[0].priv - LOCK_BASE_OFFSET; @@ -163,7 +163,7 @@ static int omap_hwspinlock_remove(struct platform_device *pdev) static struct platform_driver omap_hwspinlock_driver = { .probe = omap_hwspinlock_probe, - .remove = omap_hwspinlock_remove, + .remove = __devexit_p(omap_hwspinlock_remove), .driver = { .name = "omap_hwspinlock", .owner = THIS_MODULE, From 8b37fcfc9b3400b647748783a2cafff67793e0ad Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Mon, 12 Sep 2011 10:43:01 +0300 Subject: [PATCH 0249/1531] hwspinlock: add MAINTAINERS entries Update MAINTAINERS with entries for hwspinlock/core and hwspinlock/omap files. Signed-off-by: Ohad Ben-Cohen --- MAINTAINERS | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 28f65c249b97..c79a96692d98 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2953,6 +2953,13 @@ F: Documentation/hw_random.txt F: drivers/char/hw_random/ F: include/linux/hw_random.h +HARDWARE SPINLOCK CORE +M: Ohad Ben-Cohen +S: Maintained +F: Documentation/hwspinlock.txt +F: drivers/hwspinlock/hwspinlock_* +F: include/linux/hwspinlock.h + HARMONY SOUND DRIVER M: Kyle McMartin L: linux-parisc@vger.kernel.org @@ -4625,6 +4632,13 @@ S: Maintained F: drivers/video/omap2/ F: Documentation/arm/OMAP/DSS +OMAP HARDWARE SPINLOCK SUPPORT +M: Ohad Ben-Cohen +L: linux-omap@vger.kernel.org +S: Maintained +F: drivers/hwspinlock/omap_hwspinlock.c +F: arch/arm/mach-omap2/hwspinlock.c + OMAP MMC SUPPORT M: Jarkko Lavinen L: linux-omap@vger.kernel.org From 6cf1d0b806a9e621ef1a8e90009e37f3446c2b9e Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Mon, 16 May 2011 18:02:43 +0200 Subject: [PATCH 0250/1531] powerpc/5200: mpc5200b.dtsi: add spi node address- and size-cells properties Both, #address-cells and #size-cells properties are required for spi bus node, so add them. Signed-off-by: Anatolij Gustschin --- arch/powerpc/boot/dts/mpc5200b.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/powerpc/boot/dts/mpc5200b.dtsi b/arch/powerpc/boot/dts/mpc5200b.dtsi index bc27548e895d..7ab286ab5300 100644 --- a/arch/powerpc/boot/dts/mpc5200b.dtsi +++ b/arch/powerpc/boot/dts/mpc5200b.dtsi @@ -147,6 +147,8 @@ }; spi@f00 { + #address-cells = <1>; + #size-cells = <0>; compatible = "fsl,mpc5200b-spi","fsl,mpc5200-spi"; reg = <0xf00 0x20>; interrupts = <2 13 0 2 14 0>; From aa4593f2741946df5a47cbd679e1bc7da476e4bf Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Mon, 16 May 2011 18:02:44 +0200 Subject: [PATCH 0251/1531] powerpc/5200: dts: digsy_mtc.dts: update to add can, pci, serial and spi Add new nodes to describe more hardware the board is equipped with: - two can nodes for SJA1000 on localbus - pci node to support Coral-PA graphics controller - serial node for SC28L92 DUART on localbus - spi node for MSP430 device Also correct i2c eeprom node name. Signed-off-by: Heiko Schocher Signed-off-by: Anatolij Gustschin --- arch/powerpc/boot/dts/digsy_mtc.dts | 50 +++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/boot/dts/digsy_mtc.dts b/arch/powerpc/boot/dts/digsy_mtc.dts index 27bd267d631c..e205d17fd1f5 100644 --- a/arch/powerpc/boot/dts/digsy_mtc.dts +++ b/arch/powerpc/boot/dts/digsy_mtc.dts @@ -38,6 +38,14 @@ status = "disabled"; }; + spi@f00 { + msp430@0 { + compatible = "spidev"; + spi-max-frequency = <32000>; + reg = <0>; + }; + }; + psc@2000 { // PSC1 status = "disabled"; }; @@ -73,11 +81,16 @@ }; i2c@3d00 { - rtc@50 { + eeprom@50 { compatible = "at,24c08"; reg = <0x50>; }; + rtc@56 { + compatible = "mc,rv3029c2"; + reg = <0x56>; + }; + rtc@68 { compatible = "dallas,ds1339"; reg = <0x68>; @@ -90,11 +103,22 @@ }; pci@f0000d00 { - status = "disabled"; + interrupt-map-mask = <0xf800 0 0 7>; + interrupt-map = <0xc000 0 0 1 &mpc5200_pic 0 0 3 + 0xc000 0 0 2 &mpc5200_pic 0 0 3 + 0xc000 0 0 3 &mpc5200_pic 0 0 3 + 0xc000 0 0 4 &mpc5200_pic 0 0 3>; + clock-frequency = <0>; // From boot loader + interrupts = <2 8 0 2 9 0 2 10 0>; + bus-range = <0 0>; + ranges = <0x42000000 0 0x80000000 0x80000000 0 0x10000000 + 0x02000000 0 0x90000000 0x90000000 0 0x10000000 + 0x01000000 0 0x00000000 0xa0000000 0 0x01000000>; }; localbus { - ranges = <0 0 0xff000000 0x1000000>; + ranges = <0 0 0xff000000 0x1000000 + 4 0 0x60000000 0x0001000>; // 16-bit flash device at LocalPlus Bus CS0 flash@0,0 { @@ -122,5 +146,25 @@ reg = <0x00f00000 0x100000>; }; }; + + can@4,0 { + compatible = "nxp,sja1000"; + reg = <4 0x000 0x80>; + nxp,external-clock-frequency = <24000000>; + interrupts = <1 2 3>; // Level-low + }; + + can@4,100 { + compatible = "nxp,sja1000"; + reg = <4 0x100 0x80>; + nxp,external-clock-frequency = <24000000>; + interrupts = <1 2 3>; // Level-low + }; + + serial@4,200 { + compatible = "nxp,sc28l92"; + reg = <4 0x200 0x10>; + interrupts = <1 3 3>; + }; }; }; From 1982c09a64b10c4407ef0b177ea7fcabc03d4add Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Tue, 7 Jun 2011 00:27:05 +0200 Subject: [PATCH 0252/1531] powerpc/5200: dts: digsy_mtc.dts: add timer0 and timer1 gpio properties timer0 and timer1 pins are used as simple GPIO on this board. Add gpio-controller and #gpio-cells properties to timer nodes so that we can control gpio lines using available MPC52xx GPT driver. Signed-off-by: Anatolij Gustschin --- arch/powerpc/boot/dts/digsy_mtc.dts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/powerpc/boot/dts/digsy_mtc.dts b/arch/powerpc/boot/dts/digsy_mtc.dts index e205d17fd1f5..2aad7ae435d4 100644 --- a/arch/powerpc/boot/dts/digsy_mtc.dts +++ b/arch/powerpc/boot/dts/digsy_mtc.dts @@ -23,7 +23,14 @@ soc5200@f0000000 { timer@600 { // General Purpose Timer + #gpio-cells = <2>; fsl,has-wdt; + gpio-controller; + }; + + timer@610 { + #gpio-cells = <2>; + gpio-controller; }; rtc@800 { From 5d9e6ac84363f28a7461e4afe4b7f24f4824a233 Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Thu, 7 Jul 2011 14:49:50 +0200 Subject: [PATCH 0253/1531] powerpc/5200: dts: digsy_mtc.dts: enable both MSCAN nodes We use both MSCAN controllers on this board, so do not disable them in the device tree. Signed-off-by: Anatolij Gustschin --- arch/powerpc/boot/dts/digsy_mtc.dts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/arch/powerpc/boot/dts/digsy_mtc.dts b/arch/powerpc/boot/dts/digsy_mtc.dts index 2aad7ae435d4..a7511f2d844d 100644 --- a/arch/powerpc/boot/dts/digsy_mtc.dts +++ b/arch/powerpc/boot/dts/digsy_mtc.dts @@ -37,14 +37,6 @@ status = "disabled"; }; - can@900 { - status = "disabled"; - }; - - can@980 { - status = "disabled"; - }; - spi@f00 { msp430@0 { compatible = "spidev"; From 40dc7e8b4ac5cdfcf13e3d80b71240f90ceb1f5b Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Thu, 1 Sep 2011 17:31:22 -0500 Subject: [PATCH 0254/1531] powerpc/5200: enable audio in the defconfig Audio support for the MPC5200 exists, so enable it by default. Signed-off-by: Timur Tabi Acked-by: Wolfram Sang Signed-off-by: Anatolij Gustschin --- arch/powerpc/configs/mpc5200_defconfig | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/powerpc/configs/mpc5200_defconfig b/arch/powerpc/configs/mpc5200_defconfig index e63f537b854a..2a1320fb2723 100644 --- a/arch/powerpc/configs/mpc5200_defconfig +++ b/arch/powerpc/configs/mpc5200_defconfig @@ -88,6 +88,18 @@ CONFIG_FB_RADEON=y # CONFIG_VGA_CONSOLE is not set CONFIG_FRAMEBUFFER_CONSOLE=y CONFIG_LOGO=y +CONFIG_SOUND=y +CONFIG_SND=y +# CONFIG_SND_SUPPORT_OLD_API is not set +# CONFIG_SND_DRIVERS is not set +# CONFIG_SND_PCI is not set +# CONFIG_SND_PPC is not set +# CONFIG_SND_SPI is not set +# CONFIG_SND_USB is not set +CONFIG_SND_SOC=y +CONFIG_SND_SOC_MPC5200_I2S=y +CONFIG_SND_MPC52xx_SOC_PCM030=y +CONFIG_SND_MPC52xx_SOC_EFIKA=y CONFIG_HID_DRAGONRISE=y CONFIG_HID_GYRATION=y CONFIG_HID_TWINHAN=y From c68308dd50c3827a4ce77a1d70e0eb2d2521cafd Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 21 Sep 2011 12:49:20 +0200 Subject: [PATCH 0255/1531] gpio: move mpc8xxx/512x gpio driver to drivers/gpio Move the driver to the place where it is expected to be nowadays. Also rename its CONFIG-name to match the rest and adapt the defconfigs. Finally, move selection of REQUIRE_GPIOLIB or WANTS_OPTIONAL_GPIOLIB to the platforms, because this option is per-platform and not per-driver. Signed-off-by: Wolfram Sang Cc: Anatolij Gustschin Cc: Grant Likely Cc: Benjamin Herrenschmidt Acked-by: Grant Likely Signed-off-by: Anatolij Gustschin --- arch/powerpc/configs/85xx/p1023rds_defconfig | 2 +- arch/powerpc/configs/85xx/xes_mpc85xx_defconfig | 2 +- arch/powerpc/configs/mpc85xx_defconfig | 2 +- arch/powerpc/configs/mpc85xx_smp_defconfig | 2 +- arch/powerpc/configs/ppc6xx_defconfig | 2 +- arch/powerpc/platforms/512x/Kconfig | 1 + arch/powerpc/platforms/83xx/Kconfig | 9 ++++++--- arch/powerpc/platforms/85xx/Kconfig | 12 ++++++++---- arch/powerpc/platforms/86xx/Kconfig | 1 + arch/powerpc/platforms/Kconfig | 10 ---------- arch/powerpc/sysdev/Makefile | 1 - drivers/gpio/Kconfig | 8 ++++++++ drivers/gpio/Makefile | 1 + .../mpc8xxx_gpio.c => drivers/gpio/gpio-mpc8xxx.c | 0 14 files changed, 30 insertions(+), 23 deletions(-) rename arch/powerpc/sysdev/mpc8xxx_gpio.c => drivers/gpio/gpio-mpc8xxx.c (100%) diff --git a/arch/powerpc/configs/85xx/p1023rds_defconfig b/arch/powerpc/configs/85xx/p1023rds_defconfig index 3ff5a81c709f..c091aaf7685f 100644 --- a/arch/powerpc/configs/85xx/p1023rds_defconfig +++ b/arch/powerpc/configs/85xx/p1023rds_defconfig @@ -24,7 +24,7 @@ CONFIG_P1023_RDS=y CONFIG_QUICC_ENGINE=y CONFIG_QE_GPIO=y CONFIG_CPM2=y -CONFIG_MPC8xxx_GPIO=y +CONFIG_GPIO_MPC8XXX=y CONFIG_HIGHMEM=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y diff --git a/arch/powerpc/configs/85xx/xes_mpc85xx_defconfig b/arch/powerpc/configs/85xx/xes_mpc85xx_defconfig index 5ea3124518fd..1cd6fcb368e9 100644 --- a/arch/powerpc/configs/85xx/xes_mpc85xx_defconfig +++ b/arch/powerpc/configs/85xx/xes_mpc85xx_defconfig @@ -20,7 +20,7 @@ CONFIG_MODULE_FORCE_UNLOAD=y CONFIG_MODVERSIONS=y # CONFIG_BLK_DEV_BSG is not set CONFIG_XES_MPC85xx=y -CONFIG_MPC8xxx_GPIO=y +CONFIG_GPIO_MPC8XXX=y CONFIG_HIGHMEM=y CONFIG_MATH_EMULATION=y CONFIG_SPARSE_IRQ=y diff --git a/arch/powerpc/configs/mpc85xx_defconfig b/arch/powerpc/configs/mpc85xx_defconfig index a3467bfb7671..250091207452 100644 --- a/arch/powerpc/configs/mpc85xx_defconfig +++ b/arch/powerpc/configs/mpc85xx_defconfig @@ -41,7 +41,7 @@ CONFIG_TQM8560=y CONFIG_SBC8548=y CONFIG_QUICC_ENGINE=y CONFIG_QE_GPIO=y -CONFIG_MPC8xxx_GPIO=y +CONFIG_GPIO_MPC8XXX=y CONFIG_HIGHMEM=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y diff --git a/arch/powerpc/configs/mpc85xx_smp_defconfig b/arch/powerpc/configs/mpc85xx_smp_defconfig index 9693f6ed3da0..a4ba13b0ef1f 100644 --- a/arch/powerpc/configs/mpc85xx_smp_defconfig +++ b/arch/powerpc/configs/mpc85xx_smp_defconfig @@ -42,7 +42,7 @@ CONFIG_TQM8560=y CONFIG_SBC8548=y CONFIG_QUICC_ENGINE=y CONFIG_QE_GPIO=y -CONFIG_MPC8xxx_GPIO=y +CONFIG_GPIO_MPC8XXX=y CONFIG_HIGHMEM=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y diff --git a/arch/powerpc/configs/ppc6xx_defconfig b/arch/powerpc/configs/ppc6xx_defconfig index 04360f9b0109..c47f2becfbc3 100644 --- a/arch/powerpc/configs/ppc6xx_defconfig +++ b/arch/powerpc/configs/ppc6xx_defconfig @@ -70,7 +70,7 @@ CONFIG_TAU_AVERAGE=y CONFIG_QUICC_ENGINE=y CONFIG_QE_GPIO=y CONFIG_PPC_BESTCOMM=y -CONFIG_MPC8xxx_GPIO=y +CONFIG_GPIO_MPC8XXX=y CONFIG_MCU_MPC8349EMITX=m CONFIG_HIGHMEM=y CONFIG_NO_HZ=y diff --git a/arch/powerpc/platforms/512x/Kconfig b/arch/powerpc/platforms/512x/Kconfig index 27b0651221d1..b3ebce1aec07 100644 --- a/arch/powerpc/platforms/512x/Kconfig +++ b/arch/powerpc/platforms/512x/Kconfig @@ -6,6 +6,7 @@ config PPC_MPC512x select PPC_CLOCK select PPC_PCI_CHOICE select FSL_PCI if PCI + select ARCH_WANT_OPTIONAL_GPIOLIB config MPC5121_ADS bool "Freescale MPC5121E ADS" diff --git a/arch/powerpc/platforms/83xx/Kconfig b/arch/powerpc/platforms/83xx/Kconfig index 73f4135f3a1a..670a033264c0 100644 --- a/arch/powerpc/platforms/83xx/Kconfig +++ b/arch/powerpc/platforms/83xx/Kconfig @@ -114,18 +114,21 @@ config KMETER1 endif -# used for usb +# used for usb & gpio config PPC_MPC831x bool + select ARCH_WANT_OPTIONAL_GPIOLIB # used for math-emu config PPC_MPC832x bool -# used for usb +# used for usb & gpio config PPC_MPC834x bool + select ARCH_WANT_OPTIONAL_GPIOLIB -# used for usb +# used for usb & gpio config PPC_MPC837x bool + select ARCH_WANT_OPTIONAL_GPIOLIB diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig index 498534cd5265..1b393f40c639 100644 --- a/arch/powerpc/platforms/85xx/Kconfig +++ b/arch/powerpc/platforms/85xx/Kconfig @@ -177,7 +177,8 @@ config P2040_RDB select PPC_E500MC select PHYS_64BIT select SWIOTLB - select MPC8xxx_GPIO + select ARCH_REQUIRE_GPIOLIB + select GPIO_MPC8XXX select HAS_RAPIDIO select PPC_EPAPR_HV_PIC help @@ -189,7 +190,8 @@ config P3041_DS select PPC_E500MC select PHYS_64BIT select SWIOTLB - select MPC8xxx_GPIO + select ARCH_REQUIRE_GPIOLIB + select GPIO_MPC8XXX select HAS_RAPIDIO select PPC_EPAPR_HV_PIC help @@ -201,7 +203,8 @@ config P4080_DS select PPC_E500MC select PHYS_64BIT select SWIOTLB - select MPC8xxx_GPIO + select ARCH_REQUIRE_GPIOLIB + select GPIO_MPC8XXX select HAS_RAPIDIO select PPC_EPAPR_HV_PIC help @@ -216,7 +219,8 @@ config P5020_DS select PPC_E500MC select PHYS_64BIT select SWIOTLB - select MPC8xxx_GPIO + select ARCH_REQUIRE_GPIOLIB + select GPIO_MPC8XXX select HAS_RAPIDIO select PPC_EPAPR_HV_PIC help diff --git a/arch/powerpc/platforms/86xx/Kconfig b/arch/powerpc/platforms/86xx/Kconfig index a0b5638c5dc8..8d6599d54ea6 100644 --- a/arch/powerpc/platforms/86xx/Kconfig +++ b/arch/powerpc/platforms/86xx/Kconfig @@ -4,6 +4,7 @@ menuconfig PPC_86xx depends on 6xx select FSL_SOC select ALTIVEC + select ARCH_WANT_OPTIONAL_GPIOLIB help The Freescale E600 SoCs have 74xx cores. diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index 6de27d275728..9fdb9b6ab371 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -334,16 +334,6 @@ config OF_RTC source "arch/powerpc/sysdev/bestcomm/Kconfig" -config MPC8xxx_GPIO - bool "MPC512x/MPC8xxx GPIO support" - depends on PPC_MPC512x || PPC_MPC831x || PPC_MPC834x || PPC_MPC837x || \ - FSL_SOC_BOOKE || PPC_86xx - select GENERIC_GPIO - select ARCH_REQUIRE_GPIOLIB - help - Say Y here if you're going to use hardware that connects to the - MPC512x/831x/834x/837x/8572/8610 GPIOs. - config SIMPLE_GPIO bool "Support for simple, memory-mapped GPIO controllers" depends on PPC diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index cf736ca0cf05..84e13253aec5 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile @@ -18,7 +18,6 @@ obj-$(CONFIG_FSL_PCI) += fsl_pci.o $(fsl-msi-obj-y) obj-$(CONFIG_FSL_PMC) += fsl_pmc.o obj-$(CONFIG_FSL_LBC) += fsl_lbc.o obj-$(CONFIG_FSL_GTM) += fsl_gtm.o -obj-$(CONFIG_MPC8xxx_GPIO) += mpc8xxx_gpio.o obj-$(CONFIG_FSL_85XX_CACHE_SRAM) += fsl_85xx_l2ctlr.o fsl_85xx_cache_sram.o obj-$(CONFIG_SIMPLE_GPIO) += simple_gpio.o obj-$(CONFIG_FSL_RIO) += fsl_rio.o diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d539efd96d4b..4744cf246d4a 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -103,6 +103,14 @@ config GPIO_MPC5200 def_bool y depends on PPC_MPC52xx +config GPIO_MPC8XXX + bool "MPC512x/MPC8xxx GPIO support" + depends on PPC_MPC512x || PPC_MPC831x || PPC_MPC834x || PPC_MPC837x || \ + FSL_SOC_BOOKE || PPC_86xx + help + Say Y here if you're going to use hardware that connects to the + MPC512x/831x/834x/837x/8572/8610 GPIOs. + config GPIO_MSM_V1 tristate "Qualcomm MSM GPIO v1" depends on GPIOLIB && ARCH_MSM diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 9588948c96f0..828bba9883c3 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_GPIO_MC33880) += gpio-mc33880.o obj-$(CONFIG_GPIO_MCP23S08) += gpio-mcp23s08.o obj-$(CONFIG_GPIO_ML_IOH) += gpio-ml-ioh.o obj-$(CONFIG_GPIO_MPC5200) += gpio-mpc5200.o +obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o obj-$(CONFIG_GPIO_MSM_V1) += gpio-msm-v1.o obj-$(CONFIG_GPIO_MSM_V2) += gpio-msm-v2.o obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o diff --git a/arch/powerpc/sysdev/mpc8xxx_gpio.c b/drivers/gpio/gpio-mpc8xxx.c similarity index 100% rename from arch/powerpc/sysdev/mpc8xxx_gpio.c rename to drivers/gpio/gpio-mpc8xxx.c From 7b72c9f8750e32b3b08fab1bfef43350866ae8d1 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 21 Sep 2011 12:49:21 +0200 Subject: [PATCH 0256/1531] powerpc: update 512x-defconfig Activate all MPC512x related boards. Also enable GPIO-driver, SPI driver and at25 to test SPI. Enable DEVTMPFS. Bump to 3.1-rc6. Signed-off-by: Wolfram Sang Cc: Anatolij Gustschin Cc: Benjamin Herrenschmidt Signed-off-by: Anatolij Gustschin --- arch/powerpc/configs/mpc512x_defconfig | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/arch/powerpc/configs/mpc512x_defconfig b/arch/powerpc/configs/mpc512x_defconfig index c02bbb2fddf8..211fcc9ed700 100644 --- a/arch/powerpc/configs/mpc512x_defconfig +++ b/arch/powerpc/configs/mpc512x_defconfig @@ -1,9 +1,9 @@ CONFIG_EXPERIMENTAL=y # CONFIG_SWAP is not set CONFIG_SYSVIPC=y +CONFIG_SPARSE_IRQ=y CONFIG_LOG_BUF_SHIFT=16 CONFIG_BLK_DEV_INITRD=y -# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set # CONFIG_COMPAT_BRK is not set CONFIG_SLAB=y CONFIG_MODULES=y @@ -13,10 +13,11 @@ CONFIG_MODULE_UNLOAD=y # CONFIG_PPC_CHRP is not set CONFIG_PPC_MPC512x=y CONFIG_MPC5121_ADS=y +CONFIG_MPC5121_GENERIC=y +CONFIG_PDM360NG=y # CONFIG_PPC_PMAC is not set CONFIG_NO_HZ=y CONFIG_HZ_1000=y -CONFIG_SPARSE_IRQ=y # CONFIG_MIGRATION is not set # CONFIG_SECCOMP is not set # CONFIG_PCI is not set @@ -35,18 +36,16 @@ CONFIG_CAN=y CONFIG_CAN_RAW=y CONFIG_CAN_BCM=y CONFIG_CAN_VCAN=y -CONFIG_CAN_DEV=y CONFIG_CAN_MSCAN=y CONFIG_CAN_DEBUG_DEVICES=y # CONFIG_WIRELESS is not set CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y # CONFIG_PREVENT_FIRMWARE_BUILD is not set # CONFIG_FIRMWARE_IN_KERNEL is not set CONFIG_MTD=y -CONFIG_MTD_CONCAT=y -CONFIG_MTD_PARTITIONS=y CONFIG_MTD_CMDLINE_PARTS=y -CONFIG_MTD_OF_PARTS=y CONFIG_MTD_CHAR=y CONFIG_MTD_BLOCK=y CONFIG_MTD_CFI=y @@ -63,6 +62,7 @@ CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_BLK_DEV_XIP=y CONFIG_MISC_DEVICES=y CONFIG_EEPROM_AT24=y +CONFIG_EEPROM_AT25=y CONFIG_SCSI=y # CONFIG_SCSI_PROC_FS is not set CONFIG_BLK_DEV_SD=y @@ -99,10 +99,14 @@ CONFIG_SERIAL_MPC52xx_CONSOLE_BAUD=115200 CONFIG_I2C=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_MPC=y +CONFIG_SPI=y +CONFIG_SPI_MPC512x_PSC=y +CONFIG_GPIOLIB=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_MPC8XXX=y # CONFIG_HWMON is not set CONFIG_MEDIA_SUPPORT=y CONFIG_VIDEO_DEV=y -# CONFIG_VIDEO_ALLOW_V4L1 is not set CONFIG_VIDEO_ADV_DEBUG=y # CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set CONFIG_VIDEO_SAA711X=y @@ -132,6 +136,5 @@ CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ISO8859_1=y # CONFIG_ENABLE_WARN_DEPRECATED is not set # CONFIG_ENABLE_MUST_CHECK is not set -# CONFIG_RCU_CPU_STALL_DETECTOR is not set # CONFIG_CRYPTO_ANSI_CPRNG is not set # CONFIG_CRYPTO_HW is not set From 25c29f9e3242071bca1bee7ad919baf1888ae436 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 20 Sep 2011 19:58:10 +0000 Subject: [PATCH 0257/1531] powerpc: Fix hugetlb with CONFIG_PPC_MM_SLICES=y Commit 41151e77a4 ("powerpc: Hugetlb for BookE") added some #ifdef CONFIG_MM_SLICES conditionals to hugetlb_get_unmapped_area() and vma_mmu_pagesize(). Unfortunately this is not the correct config symbol; it should be CONFIG_PPC_MM_SLICES. The result is that attempting to use hugetlbfs on 64-bit Power server processors results in an infinite stack recursion between get_unmapped_area() and hugetlb_get_unmapped_area(). This fixes it by changing the #ifdef to use CONFIG_PPC_MM_SLICES in those functions and also in book3e_hugetlb_preload(). Signed-off-by: Paul Mackerras Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/mm/hugetlbpage-book3e.c | 2 +- arch/powerpc/mm/hugetlbpage.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/mm/hugetlbpage-book3e.c b/arch/powerpc/mm/hugetlbpage-book3e.c index 1295b7c1cdac..343ad0b87261 100644 --- a/arch/powerpc/mm/hugetlbpage-book3e.c +++ b/arch/powerpc/mm/hugetlbpage-book3e.c @@ -52,7 +52,7 @@ void book3e_hugetlb_preload(struct mm_struct *mm, unsigned long ea, pte_t pte) if (unlikely(is_kernel_addr(ea))) return; -#ifdef CONFIG_MM_SLICES +#ifdef CONFIG_PPC_MM_SLICES psize = mmu_get_tsize(get_slice_psize(mm, ea)); tsize = mmu_get_psize(psize); shift = mmu_psize_defs[psize].shift; diff --git a/arch/powerpc/mm/hugetlbpage.c b/arch/powerpc/mm/hugetlbpage.c index 3a5f59dcbb33..48b65bedc04d 100644 --- a/arch/powerpc/mm/hugetlbpage.c +++ b/arch/powerpc/mm/hugetlbpage.c @@ -690,7 +690,7 @@ unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags) { -#ifdef CONFIG_MM_SLICES +#ifdef CONFIG_PPC_MM_SLICES struct hstate *hstate = hstate_file(file); int mmu_psize = shift_to_mmu_psize(huge_page_shift(hstate)); @@ -702,7 +702,7 @@ unsigned long hugetlb_get_unmapped_area(struct file *file, unsigned long addr, unsigned long vma_mmu_pagesize(struct vm_area_struct *vma) { -#ifdef CONFIG_MM_SLICES +#ifdef CONFIG_PPC_MM_SLICES unsigned int psize = get_slice_psize(vma->vm_mm, vma->vm_start); return 1UL << mmu_psize_to_shift(psize); From d12b524f8b2f4e45cabe8bc1501e8b967d543111 Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Tue, 20 Sep 2011 03:07:24 +0000 Subject: [PATCH 0258/1531] powerpc: Reserve iommu page 0 Some devices have a dma-window that starts at the address 0. This allows DMA addresses to be mapped to this address and returned to drivers as a valid DMA address. Some drivers may not behave well in this case, since the address 0 is considered an error or not allocated. The solution to avoid this kind of error from happening is reserve the page addressed as 0 so it cannot be allocated for a DMA mapping. Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/iommu.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c index 961bb03413f3..0cfcf98aafca 100644 --- a/arch/powerpc/kernel/iommu.c +++ b/arch/powerpc/kernel/iommu.c @@ -501,6 +501,14 @@ struct iommu_table *iommu_init_table(struct iommu_table *tbl, int nid) tbl->it_map = page_address(page); memset(tbl->it_map, 0, sz); + /* + * Reserve page 0 so it will not be used for any mappings. + * This avoids buggy drivers that consider page 0 to be invalid + * to crash the machine or even lose data. + */ + if (tbl->it_offset == 0) + set_bit(0, tbl->it_map); + tbl->it_hint = 0; tbl->it_largehint = tbl->it_halfpoint; spin_lock_init(&tbl->it_lock); From d5160ecffce97ad354caa5a8762edb8c350f9e84 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 26 Sep 2011 13:18:28 +0900 Subject: [PATCH 0259/1531] ARM: S3C64xx: Add LDOVDD supply for CODEC on Cragganmore Signed-off-by: Mark Brown Signed-off-by: Kukjin Kim --- arch/arm/mach-s3c64xx/mach-crag6410.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-s3c64xx/mach-crag6410.c b/arch/arm/mach-s3c64xx/mach-crag6410.c index a0422a719996..ed332dd7bf4f 100644 --- a/arch/arm/mach-s3c64xx/mach-crag6410.c +++ b/arch/arm/mach-s3c64xx/mach-crag6410.c @@ -567,6 +567,7 @@ static struct regulator_init_data pvdd_1v2 __initdata = { }; static struct regulator_consumer_supply pvdd_1v8_consumers[] __initdata = { + REGULATOR_SUPPLY("LDOVDD", "1-001a"), REGULATOR_SUPPLY("PLLVDD", "1-001a"), REGULATOR_SUPPLY("DBVDD", "1-001a"), REGULATOR_SUPPLY("DBVDD1", "1-001a"), From bb36c44557a4fcbaa17c0f2776e12a05a691b432 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 26 Sep 2011 14:22:39 +1000 Subject: [PATCH 0260/1531] powerpc/pci: Don't configure PCIe settings when PCI_PROBE_ONLY is set We don't want to configure PCI Express Max Payload Size or Max Read Request Size on systems that set that flag. The firmware will have done it for us, and under hypervisors such as pHyp we don't even see the parent switches and bridges and thus can make no assumption on what values are safe to use. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/pci-common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 1bd47f36b25f..677ecccbe10d 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -1732,7 +1732,7 @@ void __devinit pcibios_scan_phb(struct pci_controller *hose) hose->last_busno = bus->subordinate = pci_scan_child_bus(bus); /* Configure PCI Express settings */ - if (bus) { + if (bus && !pci_has_flag(PCI_PROBE_ONLY)) { struct pci_bus *child; list_for_each_entry(child, &bus->children, node) { struct pci_dev *self = child->self; From b4dae6e1adaedc9c343b5f00332312d649600bdc Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Sun, 25 Sep 2011 16:12:18 +0200 Subject: [PATCH 0261/1531] dmaengine: shdma: protect against the IRQ handler The IRQ handler of the shdma driver accesses common hardware registers, that are also accessed from other contexts. Therefore access to them has to be performed with interrupts disabled, not only with disabled bottom halves. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Vinod Koul --- drivers/dma/shdma.c | 55 +++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c index 7f49235d14b9..e7bb7479b187 100644 --- a/drivers/dma/shdma.c +++ b/drivers/dma/shdma.c @@ -265,8 +265,9 @@ static dma_cookie_t sh_dmae_tx_submit(struct dma_async_tx_descriptor *tx) struct sh_dmae_chan *sh_chan = to_sh_chan(tx->chan); dma_async_tx_callback callback = tx->callback; dma_cookie_t cookie; + unsigned long flags; - spin_lock_bh(&sh_chan->desc_lock); + spin_lock_irqsave(&sh_chan->desc_lock, flags); cookie = sh_chan->common.cookie; cookie++; @@ -302,7 +303,7 @@ static dma_cookie_t sh_dmae_tx_submit(struct dma_async_tx_descriptor *tx) tx->cookie, &last->async_tx, sh_chan->id, desc->hw.sar, desc->hw.tcr, desc->hw.dar); - spin_unlock_bh(&sh_chan->desc_lock); + spin_unlock_irqrestore(&sh_chan->desc_lock, flags); return cookie; } @@ -374,24 +375,18 @@ static int sh_dmae_alloc_chan_resources(struct dma_chan *chan) dmae_init(sh_chan); } - spin_lock_bh(&sh_chan->desc_lock); while (sh_chan->descs_allocated < NR_DESCS_PER_CHANNEL) { - spin_unlock_bh(&sh_chan->desc_lock); desc = kzalloc(sizeof(struct sh_desc), GFP_KERNEL); - if (!desc) { - spin_lock_bh(&sh_chan->desc_lock); + if (!desc) break; - } dma_async_tx_descriptor_init(&desc->async_tx, &sh_chan->common); desc->async_tx.tx_submit = sh_dmae_tx_submit; desc->mark = DESC_IDLE; - spin_lock_bh(&sh_chan->desc_lock); list_add(&desc->node, &sh_chan->ld_free); sh_chan->descs_allocated++; } - spin_unlock_bh(&sh_chan->desc_lock); if (!sh_chan->descs_allocated) { ret = -ENOMEM; @@ -405,6 +400,7 @@ static int sh_dmae_alloc_chan_resources(struct dma_chan *chan) clear_bit(param->slave_id, sh_dmae_slave_used); etestused: efindslave: + chan->private = NULL; pm_runtime_put(sh_chan->dev); return ret; } @@ -437,12 +433,12 @@ static void sh_dmae_free_chan_resources(struct dma_chan *chan) chan->private = NULL; } - spin_lock_bh(&sh_chan->desc_lock); + spin_lock_irq(&sh_chan->desc_lock); list_splice_init(&sh_chan->ld_free, &list); sh_chan->descs_allocated = 0; - spin_unlock_bh(&sh_chan->desc_lock); + spin_unlock_irq(&sh_chan->desc_lock); if (descs > 0) pm_runtime_put(sh_chan->dev); @@ -534,6 +530,7 @@ static struct dma_async_tx_descriptor *sh_dmae_prep_sg(struct sh_dmae_chan *sh_c struct sh_desc *first = NULL, *new = NULL /* compiler... */; LIST_HEAD(tx_list); int chunks = 0; + unsigned long irq_flags; int i; if (!sg_len) @@ -544,7 +541,7 @@ static struct dma_async_tx_descriptor *sh_dmae_prep_sg(struct sh_dmae_chan *sh_c (SH_DMA_TCR_MAX + 1); /* Have to lock the whole loop to protect against concurrent release */ - spin_lock_bh(&sh_chan->desc_lock); + spin_lock_irqsave(&sh_chan->desc_lock, irq_flags); /* * Chaining: @@ -590,7 +587,7 @@ static struct dma_async_tx_descriptor *sh_dmae_prep_sg(struct sh_dmae_chan *sh_c /* Put them back on the free list, so, they don't get lost */ list_splice_tail(&tx_list, &sh_chan->ld_free); - spin_unlock_bh(&sh_chan->desc_lock); + spin_unlock_irqrestore(&sh_chan->desc_lock, irq_flags); return &first->async_tx; @@ -599,7 +596,7 @@ static struct dma_async_tx_descriptor *sh_dmae_prep_sg(struct sh_dmae_chan *sh_c new->mark = DESC_IDLE; list_splice(&tx_list, &sh_chan->ld_free); - spin_unlock_bh(&sh_chan->desc_lock); + spin_unlock_irqrestore(&sh_chan->desc_lock, irq_flags); return NULL; } @@ -661,6 +658,7 @@ static int sh_dmae_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg) { struct sh_dmae_chan *sh_chan = to_sh_chan(chan); + unsigned long flags; /* Only supports DMA_TERMINATE_ALL */ if (cmd != DMA_TERMINATE_ALL) @@ -669,7 +667,7 @@ static int sh_dmae_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, if (!chan) return -EINVAL; - spin_lock_bh(&sh_chan->desc_lock); + spin_lock_irqsave(&sh_chan->desc_lock, flags); dmae_halt(sh_chan); if (!list_empty(&sh_chan->ld_queue)) { @@ -680,7 +678,7 @@ static int sh_dmae_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, sh_chan->xmit_shift; } - spin_unlock_bh(&sh_chan->desc_lock); + spin_unlock_irqrestore(&sh_chan->desc_lock, flags); sh_dmae_chan_ld_cleanup(sh_chan, true); @@ -695,8 +693,9 @@ static dma_async_tx_callback __ld_cleanup(struct sh_dmae_chan *sh_chan, bool all dma_cookie_t cookie = 0; dma_async_tx_callback callback = NULL; void *param = NULL; + unsigned long flags; - spin_lock_bh(&sh_chan->desc_lock); + spin_lock_irqsave(&sh_chan->desc_lock, flags); list_for_each_entry_safe(desc, _desc, &sh_chan->ld_queue, node) { struct dma_async_tx_descriptor *tx = &desc->async_tx; @@ -773,7 +772,7 @@ static dma_async_tx_callback __ld_cleanup(struct sh_dmae_chan *sh_chan, bool all */ sh_chan->completed_cookie = sh_chan->common.cookie; - spin_unlock_bh(&sh_chan->desc_lock); + spin_unlock_irqrestore(&sh_chan->desc_lock, flags); if (callback) callback(param); @@ -796,10 +795,12 @@ static void sh_chan_xfer_ld_queue(struct sh_dmae_chan *sh_chan) { struct sh_desc *desc; - spin_lock_bh(&sh_chan->desc_lock); + spin_lock_irq(&sh_chan->desc_lock); /* DMA work check */ - if (dmae_is_busy(sh_chan)) - goto sh_chan_xfer_ld_queue_end; + if (dmae_is_busy(sh_chan)) { + spin_unlock_irq(&sh_chan->desc_lock); + return; + } /* Find the first not transferred descriptor */ list_for_each_entry(desc, &sh_chan->ld_queue, node) @@ -813,8 +814,7 @@ static void sh_chan_xfer_ld_queue(struct sh_dmae_chan *sh_chan) break; } -sh_chan_xfer_ld_queue_end: - spin_unlock_bh(&sh_chan->desc_lock); + spin_unlock_irq(&sh_chan->desc_lock); } static void sh_dmae_memcpy_issue_pending(struct dma_chan *chan) @@ -831,6 +831,7 @@ static enum dma_status sh_dmae_tx_status(struct dma_chan *chan, dma_cookie_t last_used; dma_cookie_t last_complete; enum dma_status status; + unsigned long flags; sh_dmae_chan_ld_cleanup(sh_chan, false); @@ -841,7 +842,7 @@ static enum dma_status sh_dmae_tx_status(struct dma_chan *chan, BUG_ON(last_complete < 0); dma_set_tx_state(txstate, last_complete, last_used, 0); - spin_lock_bh(&sh_chan->desc_lock); + spin_lock_irqsave(&sh_chan->desc_lock, flags); status = dma_async_is_complete(cookie, last_complete, last_used); @@ -859,7 +860,7 @@ static enum dma_status sh_dmae_tx_status(struct dma_chan *chan, } } - spin_unlock_bh(&sh_chan->desc_lock); + spin_unlock_irqrestore(&sh_chan->desc_lock, flags); return status; } @@ -952,7 +953,7 @@ static void dmae_do_tasklet(unsigned long data) u32 sar_buf = sh_dmae_readl(sh_chan, SAR); u32 dar_buf = sh_dmae_readl(sh_chan, DAR); - spin_lock(&sh_chan->desc_lock); + 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 && @@ -965,7 +966,7 @@ static void dmae_do_tasklet(unsigned long data) break; } } - spin_unlock(&sh_chan->desc_lock); + spin_unlock_irq(&sh_chan->desc_lock); /* Next desc */ sh_chan_xfer_ld_queue(sh_chan); From 7a1cd9ad87979744e1510782b25c38feb9602739 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 18 Aug 2011 16:55:27 +0200 Subject: [PATCH 0262/1531] dma: shdma: transfer based runtime PM Currently the shdma dmaengine driver uses runtime PM to save power, when no channel on the specific controller is requested by a user. This patch switches the driver to count individual DMA transfers. That way the controller can be powered down between transfers, even if some of its channels are in use. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Vinod Koul --- drivers/dma/shdma.c | 94 ++++++++++++++++++++++++++++++++------------- drivers/dma/shdma.h | 7 ++++ 2 files changed, 75 insertions(+), 26 deletions(-) diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c index e7bb7479b187..81809c2b46ab 100644 --- a/drivers/dma/shdma.c +++ b/drivers/dma/shdma.c @@ -259,15 +259,23 @@ 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; struct sh_dmae_chan *sh_chan = to_sh_chan(tx->chan); + struct sh_dmae_slave *param = tx->chan->private; dma_async_tx_callback callback = tx->callback; dma_cookie_t cookie; - unsigned long flags; + bool power_up; - spin_lock_irqsave(&sh_chan->desc_lock, flags); + spin_lock_irq(&sh_chan->desc_lock); + + if (list_empty(&sh_chan->ld_queue)) + power_up = true; + else + power_up = false; cookie = sh_chan->common.cookie; cookie++; @@ -303,7 +311,38 @@ static dma_cookie_t sh_dmae_tx_submit(struct dma_async_tx_descriptor *tx) tx->cookie, &last->async_tx, sh_chan->id, desc->hw.sar, desc->hw.tcr, desc->hw.dar); - spin_unlock_irqrestore(&sh_chan->desc_lock, flags); + if (power_up) { + sh_chan->pm_state = DMAE_PM_BUSY; + + pm_runtime_get(sh_chan->dev); + + spin_unlock_irq(&sh_chan->desc_lock); + + pm_runtime_barrier(sh_chan->dev); + + spin_lock_irq(&sh_chan->desc_lock); + + /* Have we been reset, while waiting? */ + if (sh_chan->pm_state != DMAE_PM_ESTABLISHED) { + dev_dbg(sh_chan->dev, "Bring up channel %d\n", + sh_chan->id); + if (param) { + const struct sh_dmae_slave_config *cfg = + param->config; + + dmae_set_dmars(sh_chan, cfg->mid_rid); + dmae_set_chcr(sh_chan, cfg->chcr); + } else { + dmae_init(sh_chan); + } + + if (sh_chan->pm_state == DMAE_PM_PENDING) + sh_chan_xfer_ld_queue(sh_chan); + sh_chan->pm_state = DMAE_PM_ESTABLISHED; + } + } + + spin_unlock_irq(&sh_chan->desc_lock); return cookie; } @@ -347,8 +386,6 @@ static int sh_dmae_alloc_chan_resources(struct dma_chan *chan) struct sh_dmae_slave *param = chan->private; int ret; - pm_runtime_get_sync(sh_chan->dev); - /* * This relies on the guarantee from dmaengine that alloc_chan_resources * never runs concurrently with itself or free_chan_resources. @@ -368,11 +405,6 @@ static int sh_dmae_alloc_chan_resources(struct dma_chan *chan) } param->config = cfg; - - dmae_set_dmars(sh_chan, cfg->mid_rid); - dmae_set_chcr(sh_chan, cfg->chcr); - } else { - dmae_init(sh_chan); } while (sh_chan->descs_allocated < NR_DESCS_PER_CHANNEL) { @@ -401,7 +433,6 @@ static int sh_dmae_alloc_chan_resources(struct dma_chan *chan) etestused: efindslave: chan->private = NULL; - pm_runtime_put(sh_chan->dev); return ret; } @@ -413,7 +444,6 @@ static void sh_dmae_free_chan_resources(struct dma_chan *chan) struct sh_dmae_chan *sh_chan = to_sh_chan(chan); struct sh_desc *desc, *_desc; LIST_HEAD(list); - int descs = sh_chan->descs_allocated; /* Protect against ISR */ spin_lock_irq(&sh_chan->desc_lock); @@ -440,9 +470,6 @@ static void sh_dmae_free_chan_resources(struct dma_chan *chan) spin_unlock_irq(&sh_chan->desc_lock); - if (descs > 0) - pm_runtime_put(sh_chan->dev); - list_for_each_entry_safe(desc, _desc, &list, node) kfree(desc); } @@ -676,7 +703,6 @@ static int sh_dmae_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, struct sh_desc, node); desc->partial = (desc->hw.tcr - sh_dmae_readl(sh_chan, TCR)) << sh_chan->xmit_shift; - } spin_unlock_irqrestore(&sh_chan->desc_lock, flags); @@ -761,7 +787,13 @@ static dma_async_tx_callback __ld_cleanup(struct sh_dmae_chan *sh_chan, bool all async_tx_test_ack(&desc->async_tx)) || all) { /* Remove from ld_queue list */ desc->mark = DESC_IDLE; + list_move(&desc->node, &sh_chan->ld_free); + + if (list_empty(&sh_chan->ld_queue)) { + dev_dbg(sh_chan->dev, "Bring down channel %d\n", sh_chan->id); + pm_runtime_put(sh_chan->dev); + } } } @@ -791,16 +823,14 @@ static void sh_dmae_chan_ld_cleanup(struct sh_dmae_chan *sh_chan, bool all) ; } +/* Called under spin_lock_irq(&sh_chan->desc_lock) */ static void sh_chan_xfer_ld_queue(struct sh_dmae_chan *sh_chan) { struct sh_desc *desc; - spin_lock_irq(&sh_chan->desc_lock); /* DMA work check */ - if (dmae_is_busy(sh_chan)) { - spin_unlock_irq(&sh_chan->desc_lock); + if (dmae_is_busy(sh_chan)) return; - } /* Find the first not transferred descriptor */ list_for_each_entry(desc, &sh_chan->ld_queue, node) @@ -813,14 +843,18 @@ static void sh_chan_xfer_ld_queue(struct sh_dmae_chan *sh_chan) dmae_start(sh_chan); break; } - - spin_unlock_irq(&sh_chan->desc_lock); } static void sh_dmae_memcpy_issue_pending(struct dma_chan *chan) { struct sh_dmae_chan *sh_chan = to_sh_chan(chan); - sh_chan_xfer_ld_queue(sh_chan); + + spin_lock_irq(&sh_chan->desc_lock); + if (sh_chan->pm_state == DMAE_PM_ESTABLISHED) + sh_chan_xfer_ld_queue(sh_chan); + else + sh_chan->pm_state = DMAE_PM_PENDING; + spin_unlock_irq(&sh_chan->desc_lock); } static enum dma_status sh_dmae_tx_status(struct dma_chan *chan, @@ -913,6 +947,12 @@ static bool sh_dmae_reset(struct sh_dmae_device *shdev) list_splice_init(&sh_chan->ld_queue, &dl); + if (!list_empty(&dl)) { + dev_dbg(sh_chan->dev, "Bring down channel %d\n", sh_chan->id); + pm_runtime_put(sh_chan->dev); + } + sh_chan->pm_state = DMAE_PM_ESTABLISHED; + spin_unlock(&sh_chan->desc_lock); /* Complete all */ @@ -966,10 +1006,10 @@ static void dmae_do_tasklet(unsigned long data) break; } } - spin_unlock_irq(&sh_chan->desc_lock); - /* Next desc */ sh_chan_xfer_ld_queue(sh_chan); + spin_unlock_irq(&sh_chan->desc_lock); + sh_dmae_chan_ld_cleanup(sh_chan, false); } @@ -1037,7 +1077,9 @@ static int __devinit sh_dmae_chan_probe(struct sh_dmae_device *shdev, int id, return -ENOMEM; } - /* copy struct dma_device */ + new_sh_chan->pm_state = DMAE_PM_ESTABLISHED; + + /* reference struct dma_device */ new_sh_chan->common.device = &shdev->common; new_sh_chan->dev = shdev->common.dev; diff --git a/drivers/dma/shdma.h b/drivers/dma/shdma.h index dc56576f9fdb..2b55a276dc5b 100644 --- a/drivers/dma/shdma.h +++ b/drivers/dma/shdma.h @@ -23,6 +23,12 @@ struct device; +enum dmae_pm_state { + DMAE_PM_ESTABLISHED, + DMAE_PM_BUSY, + DMAE_PM_PENDING, +}; + struct sh_dmae_chan { dma_cookie_t completed_cookie; /* The maximum cookie completed */ spinlock_t desc_lock; /* Descriptor operation lock */ @@ -38,6 +44,7 @@ struct sh_dmae_chan { u32 __iomem *base; char dev_id[16]; /* unique name per DMAC of channel */ int pm_error; + enum dmae_pm_state pm_state; }; struct sh_dmae_device { From 66857b3a9e88ac6f6e279eaa06b84367e662c0dd Mon Sep 17 00:00:00 2001 From: Jimi Xenidis Date: Fri, 23 Sep 2011 05:40:46 +0000 Subject: [PATCH 0263/1531] powerpc: Fix xmon for systems without MSR[RI] Based on patch by David Gibson xmon has a longstanding bug on systems which are SMP-capable but lack the MSR[RI] bit. In these cases, xmon invoked by IPI on secondary CPUs will not properly keep quiet, but will print stuff, thereby garbling the primary xmon's output. This patch fixes it, by ignoring the RI bit if the processor does not support it. There's already a version of this for 4xx upstream, which we'll need to extend to other RI-lacking CPUs at some point. For now this adds Book3e processors to the mix. Signed-off-by: Jimi Xenidis Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/xmon/xmon.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c index 42541bbcc7fa..13f82f847669 100644 --- a/arch/powerpc/xmon/xmon.c +++ b/arch/powerpc/xmon/xmon.c @@ -340,8 +340,8 @@ int cpus_are_in_xmon(void) static inline int unrecoverable_excp(struct pt_regs *regs) { -#ifdef CONFIG_4xx - /* We have no MSR_RI bit on 4xx, so we simply return false */ +#if defined(CONFIG_4xx) || defined(CONFIG_BOOK3E) + /* We have no MSR_RI bit on 4xx or Book3e, so we simply return false */ return 0; #else return ((regs->msr & MSR_RI) == 0); From e69b742a6793dc5bf16f6eedca534d4bc10d68b2 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 26 Sep 2011 19:37:57 +0000 Subject: [PATCH 0264/1531] powerpc/ptrace: Fix build with gcc 4.6 gcc (rightfully) complains that we are accessing beyond the end of the fpr array (we do, to access the fpscr). The only sane thing to do (whether anything in that code can be called remotely sane is debatable) is to special case fpscr and handle it as a separate statement. I initially tried to do it it by making the array access conditional to index < PT_FPSCR and using a 3rd else leg but for some reason gcc was unable to understand it and still spewed the warning. So I ended up with something a tad more intricated but it seems to build on 32-bit and on 64-bit with and without VSX. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/ptrace.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index 05b7dd217f60..18447c4fbad3 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c @@ -1497,9 +1497,14 @@ long arch_ptrace(struct task_struct *child, long request, if (index < PT_FPR0) { tmp = ptrace_get_reg(child, (int) index); } else { + unsigned int fpidx = index - PT_FPR0; + flush_fp_to_thread(child); - tmp = ((unsigned long *)child->thread.fpr) - [TS_FPRWIDTH * (index - PT_FPR0)]; + if (fpidx < (PT_FPSCR - PT_FPR0)) + tmp = ((unsigned long *)child->thread.fpr) + [fpidx * TS_FPRWIDTH]; + else + tmp = child->thread.fpscr.val; } ret = put_user(tmp, datalp); break; @@ -1525,9 +1530,14 @@ long arch_ptrace(struct task_struct *child, long request, if (index < PT_FPR0) { ret = ptrace_put_reg(child, index, data); } else { + unsigned int fpidx = index - PT_FPR0; + flush_fp_to_thread(child); - ((unsigned long *)child->thread.fpr) - [TS_FPRWIDTH * (index - PT_FPR0)] = data; + if (fpidx < (PT_FPSCR - PT_FPR0)) + ((unsigned long *)child->thread.fpr) + [fpidx * TS_FPRWIDTH] = data; + else + child->thread.fpscr.val = data; ret = 0; } break; From d15f02eb4e8992cfacfca2ff306e5585bcf721d1 Mon Sep 17 00:00:00 2001 From: "Carl E. Love" Date: Wed, 28 Sep 2011 11:23:33 +0000 Subject: [PATCH 0265/1531] powerpc/perf_event: Fix Power6 L1 cache read & write event codes] The current L1 cache read event code 0x80082 only counts for thread 0. The event code 0x280030 should be used to count events on thread 0 and 1. The patch fixes the event code for the L1 cache read. The current L1 cache write event code 0x80086 only counts for thread 0. The event code 0x180032 should be used to count events on thread 0 and 1. The patch fixes the event code for the L1 cache write. FYI, the documentation lists three event codes for the L1 cache read event and three event codes for the L1 cache write event. The event description for the event codes is as follows: L1 cache read requests 0x80082 LSU 0 only L1 cache read requests 0x8008A LSU 1 only L1 cache read requests 0x80030 LSU 1 or LSU 0, counter 2 only. L1 cache store requests 0x80086 LSU 0 only L1 cache store requests 0x8008E LSU 1 only L1 cache store requests 0x80032 LSU 0 or LSU 1, counter 1 only. There can only be one request from either LSU 0 or 1 active at a time. Signed-off-by: Carl Love Acked-by: Paul Mackerras Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/power6-pmu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/kernel/power6-pmu.c b/arch/powerpc/kernel/power6-pmu.c index 03b95e2c6d65..0bbc901e7efc 100644 --- a/arch/powerpc/kernel/power6-pmu.c +++ b/arch/powerpc/kernel/power6-pmu.c @@ -487,8 +487,8 @@ static int power6_generic_events[] = { */ static int power6_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = { [C(L1D)] = { /* RESULT_ACCESS RESULT_MISS */ - [C(OP_READ)] = { 0x80082, 0x80080 }, - [C(OP_WRITE)] = { 0x80086, 0x80088 }, + [C(OP_READ)] = { 0x280030, 0x80080 }, + [C(OP_WRITE)] = { 0x180032, 0x80088 }, [C(OP_PREFETCH)] = { 0x810a4, 0 }, }, [C(L1I)] = { /* RESULT_ACCESS RESULT_MISS */ From 7680057cc4c7d9caada12767831bfd9738dd7b43 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 28 Sep 2011 20:51:46 +0000 Subject: [PATCH 0266/1531] powerpc: Don't try OPAL takeover on old 970 blades The firmware on old 970 blades supports some kind of takeover called "TNK takeover" which will crash if we try to probe for OPAL takeover, so don't do it. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/kernel/prom_init.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index e96f5d0d2c78..b4fa66127495 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -1313,6 +1313,16 @@ static void prom_query_opal(void) { long rc; + /* We must not query for OPAL presence on a machine that + * supports TNK takeover (970 blades), as this uses the same + * h-call with different arguments and will crash + */ + if (PHANDLE_VALID(call_prom("finddevice", 1, 1, + ADDR("/tnk-memory-map")))) { + prom_printf("TNK takeover detected, skipping OPAL check\n"); + return; + } + prom_printf("Querying for OPAL presence... "); rc = opal_query_takeover(&RELOC(prom_opal_size), &RELOC(prom_opal_align)); From 4dcaebbf6586d299be8513512a1253f177b803d7 Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Thu, 29 Sep 2011 16:53:29 +0100 Subject: [PATCH 0267/1531] xen: use generic functions instead of xen_{alloc, free}_vm_area() Replace calls to the Xen-specific xen_alloc_vm_area() and xen_free_vm_area() functions with the generic equivalent (alloc_vm_area() and free_vm_area()). On x86, these were identical already. Signed-off-by: David Vrabel Signed-off-by: Konrad Rzeszutek Wilk --- arch/ia64/include/asm/xen/grant_table.h | 29 ------------ arch/ia64/xen/grant-table.c | 62 ------------------------- arch/x86/include/asm/xen/grant_table.h | 7 --- arch/x86/xen/grant-table.c | 2 +- drivers/xen/xenbus/xenbus_client.c | 6 +-- include/xen/grant_table.h | 1 - 6 files changed, 4 insertions(+), 103 deletions(-) delete mode 100644 arch/ia64/include/asm/xen/grant_table.h delete mode 100644 arch/x86/include/asm/xen/grant_table.h diff --git a/arch/ia64/include/asm/xen/grant_table.h b/arch/ia64/include/asm/xen/grant_table.h deleted file mode 100644 index 2b1fae0e2d11..000000000000 --- a/arch/ia64/include/asm/xen/grant_table.h +++ /dev/null @@ -1,29 +0,0 @@ -/****************************************************************************** - * arch/ia64/include/asm/xen/grant_table.h - * - * Copyright (c) 2008 Isaku Yamahata - * VA Linux Systems Japan K.K. - * - * 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 - * - */ - -#ifndef _ASM_IA64_XEN_GRANT_TABLE_H -#define _ASM_IA64_XEN_GRANT_TABLE_H - -struct vm_struct *xen_alloc_vm_area(unsigned long size); -void xen_free_vm_area(struct vm_struct *area); - -#endif /* _ASM_IA64_XEN_GRANT_TABLE_H */ diff --git a/arch/ia64/xen/grant-table.c b/arch/ia64/xen/grant-table.c index 48cca37625eb..c18281332f84 100644 --- a/arch/ia64/xen/grant-table.c +++ b/arch/ia64/xen/grant-table.c @@ -31,68 +31,6 @@ #include -struct vm_struct *xen_alloc_vm_area(unsigned long size) -{ - int order; - unsigned long virt; - unsigned long nr_pages; - struct vm_struct *area; - - order = get_order(size); - virt = __get_free_pages(GFP_KERNEL, order); - if (virt == 0) - goto err0; - nr_pages = 1 << order; - scrub_pages(virt, nr_pages); - - area = kmalloc(sizeof(*area), GFP_KERNEL); - if (area == NULL) - goto err1; - - area->flags = VM_IOREMAP; - area->addr = (void *)virt; - area->size = size; - area->pages = NULL; - area->nr_pages = nr_pages; - area->phys_addr = 0; /* xenbus_map_ring_valloc uses this field! */ - - return area; - -err1: - free_pages(virt, order); -err0: - return NULL; -} -EXPORT_SYMBOL_GPL(xen_alloc_vm_area); - -void xen_free_vm_area(struct vm_struct *area) -{ - unsigned int order = get_order(area->size); - unsigned long i; - unsigned long phys_addr = __pa(area->addr); - - /* This area is used for foreign page mappping. - * So underlying machine page may not be assigned. */ - for (i = 0; i < (1 << order); i++) { - unsigned long ret; - unsigned long gpfn = (phys_addr >> PAGE_SHIFT) + i; - struct xen_memory_reservation reservation = { - .nr_extents = 1, - .address_bits = 0, - .extent_order = 0, - .domid = DOMID_SELF - }; - set_xen_guest_handle(reservation.extent_start, &gpfn); - ret = HYPERVISOR_memory_op(XENMEM_populate_physmap, - &reservation); - BUG_ON(ret != 1); - } - free_pages((unsigned long)area->addr, order); - kfree(area); -} -EXPORT_SYMBOL_GPL(xen_free_vm_area); - - /**************************************************************************** * grant table hack * cmd: GNTTABOP_xxx diff --git a/arch/x86/include/asm/xen/grant_table.h b/arch/x86/include/asm/xen/grant_table.h deleted file mode 100644 index fdbbb45767a6..000000000000 --- a/arch/x86/include/asm/xen/grant_table.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef _ASM_X86_XEN_GRANT_TABLE_H -#define _ASM_X86_XEN_GRANT_TABLE_H - -#define xen_alloc_vm_area(size) alloc_vm_area(size) -#define xen_free_vm_area(area) free_vm_area(area) - -#endif /* _ASM_X86_XEN_GRANT_TABLE_H */ diff --git a/arch/x86/xen/grant-table.c b/arch/x86/xen/grant-table.c index 49ba9b5224d1..6bbfd7ac5e81 100644 --- a/arch/x86/xen/grant-table.c +++ b/arch/x86/xen/grant-table.c @@ -71,7 +71,7 @@ int arch_gnttab_map_shared(unsigned long *frames, unsigned long nr_gframes, if (shared == NULL) { struct vm_struct *area = - xen_alloc_vm_area(PAGE_SIZE * max_nr_gframes); + alloc_vm_area(PAGE_SIZE * max_nr_gframes); BUG_ON(area == NULL); shared = area->addr; *__shared = shared; diff --git a/drivers/xen/xenbus/xenbus_client.c b/drivers/xen/xenbus/xenbus_client.c index cdacf923e073..229d3adce85d 100644 --- a/drivers/xen/xenbus/xenbus_client.c +++ b/drivers/xen/xenbus/xenbus_client.c @@ -443,7 +443,7 @@ int xenbus_map_ring_valloc(struct xenbus_device *dev, int gnt_ref, void **vaddr) *vaddr = NULL; - area = xen_alloc_vm_area(PAGE_SIZE); + area = alloc_vm_area(PAGE_SIZE); if (!area) return -ENOMEM; @@ -453,7 +453,7 @@ int xenbus_map_ring_valloc(struct xenbus_device *dev, int gnt_ref, void **vaddr) BUG(); if (op.status != GNTST_okay) { - xen_free_vm_area(area); + free_vm_area(area); xenbus_dev_fatal(dev, op.status, "mapping in shared page %d from domain %d", gnt_ref, dev->otherend_id); @@ -552,7 +552,7 @@ int xenbus_unmap_ring_vfree(struct xenbus_device *dev, void *vaddr) BUG(); if (op.status == GNTST_okay) - xen_free_vm_area(area); + free_vm_area(area); else xenbus_dev_error(dev, op.status, "unmapping page at handle %d error %d", diff --git a/include/xen/grant_table.h b/include/xen/grant_table.h index b1fab6b5b3ef..8a8bb7626f7c 100644 --- a/include/xen/grant_table.h +++ b/include/xen/grant_table.h @@ -43,7 +43,6 @@ #include #include -#include #include From 24f9e1f31471abbd2af62eb529deee90bad4c65c Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 31 Aug 2011 15:47:16 +0900 Subject: [PATCH 0268/1531] ARM: EXYNOS4: Add USB EHCI device to ORIGEN board Signed-off-by: Pankaj Dubey Signed-off-by: Sachin Kamat [kgene.kim@samsung.com: re-ordering changes in Kconfig] Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos4/Kconfig | 2 ++ arch/arm/mach-exynos4/mach-origen.c | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/arch/arm/mach-exynos4/Kconfig b/arch/arm/mach-exynos4/Kconfig index 3ceefdbadd19..3034a736f823 100644 --- a/arch/arm/mach-exynos4/Kconfig +++ b/arch/arm/mach-exynos4/Kconfig @@ -224,7 +224,9 @@ config MACH_ORIGEN select S3C_DEV_RTC select S3C_DEV_WDT select S3C_DEV_HSMMC2 + select S5P_DEV_USB_EHCI select EXYNOS4_SETUP_SDHCI + select EXYNOS4_SETUP_USB_PHY help Machine support for ORIGEN based on Samsung EXYNOS4210 diff --git a/arch/arm/mach-exynos4/mach-origen.c b/arch/arm/mach-exynos4/mach-origen.c index ed59f86001ac..d101756ef7dc 100644 --- a/arch/arm/mach-exynos4/mach-origen.c +++ b/arch/arm/mach-exynos4/mach-origen.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include @@ -79,10 +81,21 @@ static struct s3c_sdhci_platdata origen_hsmmc2_pdata __initdata = { .clk_type = S3C_SDHCI_CLK_DIV_EXTERNAL, }; +/* USB EHCI */ +static struct s5p_ehci_platdata origen_ehci_pdata; + +static void __init origen_ehci_init(void) +{ + struct s5p_ehci_platdata *pdata = &origen_ehci_pdata; + + s5p_ehci_set_platdata(pdata); +} + static struct platform_device *origen_devices[] __initdata = { &s3c_device_hsmmc2, &s3c_device_rtc, &s3c_device_wdt, + &s5p_device_ehci, }; static void __init origen_map_io(void) @@ -95,6 +108,10 @@ static void __init origen_map_io(void) static void __init origen_machine_init(void) { s3c_sdhci2_set_platdata(&origen_hsmmc2_pdata); + + origen_ehci_init(); + clk_xusbxti.rate = 24000000; + platform_add_devices(origen_devices, ARRAY_SIZE(origen_devices)); } From 6f8eb324a395da5bd0c0b113944f2906272e14bd Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 31 Aug 2011 15:52:27 +0900 Subject: [PATCH 0269/1531] ARM: EXYNOS4: Add FIMC device on ORIGEN board This patch adds definitions to enable support for s5p-fimc driver on ORIGEN board. Signed-off-by: Sachin Kamat [kgene.kim@samsung.com: re-ordering changes] Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos4/Kconfig | 4 ++++ arch/arm/mach-exynos4/mach-origen.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/arch/arm/mach-exynos4/Kconfig b/arch/arm/mach-exynos4/Kconfig index 3034a736f823..9c920b2acc36 100644 --- a/arch/arm/mach-exynos4/Kconfig +++ b/arch/arm/mach-exynos4/Kconfig @@ -224,6 +224,10 @@ config MACH_ORIGEN select S3C_DEV_RTC select S3C_DEV_WDT select S3C_DEV_HSMMC2 + select S5P_DEV_FIMC0 + select S5P_DEV_FIMC1 + select S5P_DEV_FIMC2 + select S5P_DEV_FIMC3 select S5P_DEV_USB_EHCI select EXYNOS4_SETUP_SDHCI select EXYNOS4_SETUP_USB_PHY diff --git a/arch/arm/mach-exynos4/mach-origen.c b/arch/arm/mach-exynos4/mach-origen.c index d101756ef7dc..7ac71878767b 100644 --- a/arch/arm/mach-exynos4/mach-origen.c +++ b/arch/arm/mach-exynos4/mach-origen.c @@ -96,6 +96,10 @@ static struct platform_device *origen_devices[] __initdata = { &s3c_device_rtc, &s3c_device_wdt, &s5p_device_ehci, + &s5p_device_fimc0, + &s5p_device_fimc1, + &s5p_device_fimc2, + &s5p_device_fimc3, }; static void __init origen_map_io(void) From 9edff0f79f5b98e6abfabfa27b31d155e3c994e1 Mon Sep 17 00:00:00 2001 From: Giridhar Maruthy Date: Wed, 31 Aug 2011 15:58:52 +0900 Subject: [PATCH 0270/1531] ARM: EXYNOS4: Add PWM backlight support on ORIGEN This patch adds support for LCD backlight using PWM timer on ORIGEN board. Signed-off-by: Giridhar Maruthy Signed-off-by: Sachin Kamat Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos4/Kconfig | 2 ++ arch/arm/mach-exynos4/mach-origen.c | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/arch/arm/mach-exynos4/Kconfig b/arch/arm/mach-exynos4/Kconfig index 9c920b2acc36..86cf4fd47b35 100644 --- a/arch/arm/mach-exynos4/Kconfig +++ b/arch/arm/mach-exynos4/Kconfig @@ -229,6 +229,8 @@ config MACH_ORIGEN select S5P_DEV_FIMC2 select S5P_DEV_FIMC3 select S5P_DEV_USB_EHCI + select SAMSUNG_DEV_BACKLIGHT + select SAMSUNG_DEV_PWM select EXYNOS4_SETUP_SDHCI select EXYNOS4_SETUP_USB_PHY help diff --git a/arch/arm/mach-exynos4/mach-origen.c b/arch/arm/mach-exynos4/mach-origen.c index 7ac71878767b..937db75688d0 100644 --- a/arch/arm/mach-exynos4/mach-origen.c +++ b/arch/arm/mach-exynos4/mach-origen.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -26,6 +27,8 @@ #include #include #include +#include +#include #include @@ -102,6 +105,17 @@ static struct platform_device *origen_devices[] __initdata = { &s5p_device_fimc3, }; +/* LCD Backlight data */ +static struct samsung_bl_gpio_info origen_bl_gpio_info = { + .no = EXYNOS4_GPD0(0), + .func = S3C_GPIO_SFN(2), +}; + +static struct platform_pwm_backlight_data origen_bl_data = { + .pwm_id = 0, + .pwm_period_ns = 1000, +}; + static void __init origen_map_io(void) { s5p_init_io(NULL, 0, S5P_VA_CHIPID); @@ -117,6 +131,8 @@ static void __init origen_machine_init(void) clk_xusbxti.rate = 24000000; platform_add_devices(origen_devices, ARRAY_SIZE(origen_devices)); + + samsung_bl_set(&origen_bl_gpio_info, &origen_bl_data); } MACHINE_START(ORIGEN, "ORIGEN") From 92e41efd73918d28ad48a937489e6f2a02140057 Mon Sep 17 00:00:00 2001 From: Tushar Behera Date: Wed, 31 Aug 2011 16:01:15 +0900 Subject: [PATCH 0271/1531] ARM: EXYNOS4: Fix sdhci card detection for ORIGEN Fix incorrect value of cd_type field in platform data for sdhci device. Signed-off-by: Tushar Behera Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos4/mach-origen.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/arm/mach-exynos4/mach-origen.c b/arch/arm/mach-exynos4/mach-origen.c index 937db75688d0..0e3b81634c24 100644 --- a/arch/arm/mach-exynos4/mach-origen.c +++ b/arch/arm/mach-exynos4/mach-origen.c @@ -78,9 +78,7 @@ static struct s3c2410_uartcfg origen_uartcfgs[] __initdata = { }; static struct s3c_sdhci_platdata origen_hsmmc2_pdata __initdata = { - .cd_type = S3C_SDHCI_CD_GPIO, - .ext_cd_gpio = EXYNOS4_GPK2(2), - .ext_cd_gpio_invert = 1, + .cd_type = S3C_SDHCI_CD_INTERNAL, .clk_type = S3C_SDHCI_CLK_DIV_EXTERNAL, }; From cf1dad9d7f14ddf555baa0fcc82d17d5f29d3ae2 Mon Sep 17 00:00:00 2001 From: Tushar Behera Date: Wed, 31 Aug 2011 16:57:37 +0900 Subject: [PATCH 0272/1531] ARM: EXYNOS4: Add support for secondary MMC port on ORIGEN Secondary MMC port on ORIGEN is connected to sdhci instance 0. Support for secondary MMC port is extended by registering sdhci instance 0. Since sdhci instance 2 can contain a bootable media, sdhci instance 0 is registered after instance 2. Signed-off-by: Tushar Behera [kgene.kim@samsung.com: Added comments in registering sdhci] Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos4/Kconfig | 1 + arch/arm/mach-exynos4/mach-origen.c | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/arch/arm/mach-exynos4/Kconfig b/arch/arm/mach-exynos4/Kconfig index 86cf4fd47b35..00c645426d3f 100644 --- a/arch/arm/mach-exynos4/Kconfig +++ b/arch/arm/mach-exynos4/Kconfig @@ -223,6 +223,7 @@ config MACH_ORIGEN select CPU_EXYNOS4210 select S3C_DEV_RTC select S3C_DEV_WDT + select S3C_DEV_HSMMC select S3C_DEV_HSMMC2 select S5P_DEV_FIMC0 select S5P_DEV_FIMC1 diff --git a/arch/arm/mach-exynos4/mach-origen.c b/arch/arm/mach-exynos4/mach-origen.c index 0e3b81634c24..cd5195f93548 100644 --- a/arch/arm/mach-exynos4/mach-origen.c +++ b/arch/arm/mach-exynos4/mach-origen.c @@ -77,6 +77,11 @@ static struct s3c2410_uartcfg origen_uartcfgs[] __initdata = { }, }; +static struct s3c_sdhci_platdata origen_hsmmc0_pdata __initdata = { + .cd_type = S3C_SDHCI_CD_INTERNAL, + .clk_type = S3C_SDHCI_CLK_DIV_EXTERNAL, +}; + static struct s3c_sdhci_platdata origen_hsmmc2_pdata __initdata = { .cd_type = S3C_SDHCI_CD_INTERNAL, .clk_type = S3C_SDHCI_CLK_DIV_EXTERNAL, @@ -94,6 +99,7 @@ static void __init origen_ehci_init(void) static struct platform_device *origen_devices[] __initdata = { &s3c_device_hsmmc2, + &s3c_device_hsmmc0, &s3c_device_rtc, &s3c_device_wdt, &s5p_device_ehci, @@ -123,7 +129,12 @@ static void __init origen_map_io(void) static void __init origen_machine_init(void) { + /* + * Since sdhci instance 2 can contain a bootable media, + * sdhci instance 0 is registered after instance 2. + */ s3c_sdhci2_set_platdata(&origen_hsmmc2_pdata); + s3c_sdhci0_set_platdata(&origen_hsmmc0_pdata); origen_ehci_init(); clk_xusbxti.rate = 24000000; From c86cfdd012498ddab2b2eb23fad201637a77acf4 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 16 Sep 2011 21:41:25 +0900 Subject: [PATCH 0273/1531] ARM: EXYNOS4: Add keypad support for ORIGEN This patch adds keypad support for ORIGEN board as GPIO keys. Signed-off-by: Sachin Kamat Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos4/mach-origen.c | 58 +++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/arch/arm/mach-exynos4/mach-origen.c b/arch/arm/mach-exynos4/mach-origen.c index cd5195f93548..3d67140aaea9 100644 --- a/arch/arm/mach-exynos4/mach-origen.c +++ b/arch/arm/mach-exynos4/mach-origen.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -97,6 +98,62 @@ static void __init origen_ehci_init(void) s5p_ehci_set_platdata(pdata); } +static struct gpio_keys_button origen_gpio_keys_table[] = { + { + .code = KEY_MENU, + .gpio = EXYNOS4_GPX1(5), + .desc = "gpio-keys: KEY_MENU", + .type = EV_KEY, + .active_low = 1, + .wakeup = 1, + .debounce_interval = 1, + }, { + .code = KEY_HOME, + .gpio = EXYNOS4_GPX1(6), + .desc = "gpio-keys: KEY_HOME", + .type = EV_KEY, + .active_low = 1, + .wakeup = 1, + .debounce_interval = 1, + }, { + .code = KEY_BACK, + .gpio = EXYNOS4_GPX1(7), + .desc = "gpio-keys: KEY_BACK", + .type = EV_KEY, + .active_low = 1, + .wakeup = 1, + .debounce_interval = 1, + }, { + .code = KEY_UP, + .gpio = EXYNOS4_GPX2(0), + .desc = "gpio-keys: KEY_UP", + .type = EV_KEY, + .active_low = 1, + .wakeup = 1, + .debounce_interval = 1, + }, { + .code = KEY_DOWN, + .gpio = EXYNOS4_GPX2(1), + .desc = "gpio-keys: KEY_DOWN", + .type = EV_KEY, + .active_low = 1, + .wakeup = 1, + .debounce_interval = 1, + }, +}; + +static struct gpio_keys_platform_data origen_gpio_keys_data = { + .buttons = origen_gpio_keys_table, + .nbuttons = ARRAY_SIZE(origen_gpio_keys_table), +}; + +static struct platform_device origen_device_gpiokeys = { + .name = "gpio-keys", + .dev = { + .platform_data = &origen_gpio_keys_data, + }, +}; + static struct platform_device *origen_devices[] __initdata = { &s3c_device_hsmmc2, &s3c_device_hsmmc0, @@ -107,6 +164,7 @@ static struct platform_device *origen_devices[] __initdata = { &s5p_device_fimc1, &s5p_device_fimc2, &s5p_device_fimc3, + &origen_device_gpiokeys, }; /* LCD Backlight data */ From 6ca3f8bdf8253504426935c97cabb44bdc6f5888 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sat, 17 Sep 2011 11:42:43 +0900 Subject: [PATCH 0274/1531] ARM: EXYNOS4: Add HDMI support for ORIGEN This patch adds HDMI (TVout) support for ORIGEN board. Signed-off-by: Sachin Kamat Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos4/Kconfig | 2 ++ arch/arm/mach-exynos4/mach-origen.c | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/arch/arm/mach-exynos4/Kconfig b/arch/arm/mach-exynos4/Kconfig index 00c645426d3f..257dbaaf149b 100644 --- a/arch/arm/mach-exynos4/Kconfig +++ b/arch/arm/mach-exynos4/Kconfig @@ -229,7 +229,9 @@ config MACH_ORIGEN select S5P_DEV_FIMC1 select S5P_DEV_FIMC2 select S5P_DEV_FIMC3 + select S5P_DEV_I2C_HDMIPHY select S5P_DEV_USB_EHCI + select S5P_DEV_TV select SAMSUNG_DEV_BACKLIGHT select SAMSUNG_DEV_PWM select EXYNOS4_SETUP_SDHCI diff --git a/arch/arm/mach-exynos4/mach-origen.c b/arch/arm/mach-exynos4/mach-origen.c index 3d67140aaea9..c8121fcf198d 100644 --- a/arch/arm/mach-exynos4/mach-origen.c +++ b/arch/arm/mach-exynos4/mach-origen.c @@ -164,6 +164,9 @@ static struct platform_device *origen_devices[] __initdata = { &s5p_device_fimc1, &s5p_device_fimc2, &s5p_device_fimc3, + &s5p_device_hdmi, + &s5p_device_i2c_hdmiphy, + &s5p_device_mixer, &origen_device_gpiokeys, }; @@ -197,6 +200,8 @@ static void __init origen_machine_init(void) origen_ehci_init(); clk_xusbxti.rate = 24000000; + s5p_i2c_hdmiphy_set_platdata(NULL); + platform_add_devices(origen_devices, ARRAY_SIZE(origen_devices)); samsung_bl_set(&origen_bl_gpio_info, &origen_bl_data); From c8e28ce049faa53a470c132893abbc9f2bde9420 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Sun, 23 Jan 2011 10:07:47 -0600 Subject: [PATCH 0275/1531] writeback: account per-bdi accumulated dirtied pages Introduce the BDI_DIRTIED counter. It will be used for estimating the bdi's dirty bandwidth. CC: Jan Kara CC: Michael Rubin CC: Peter Zijlstra Signed-off-by: Wu Fengguang --- include/linux/backing-dev.h | 1 + mm/backing-dev.c | 2 ++ mm/page-writeback.c | 1 + 3 files changed, 4 insertions(+) diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index 3b2f9cb82986..9ca241a70c49 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -40,6 +40,7 @@ typedef int (congested_fn)(void *, int); enum bdi_stat_item { BDI_RECLAIMABLE, BDI_WRITEBACK, + BDI_DIRTIED, BDI_WRITTEN, NR_BDI_STAT_ITEMS }; diff --git a/mm/backing-dev.c b/mm/backing-dev.c index a87da524a4a0..fea7e6efd1d7 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -97,6 +97,7 @@ static int bdi_debug_stats_show(struct seq_file *m, void *v) "BdiDirtyThresh: %10lu kB\n" "DirtyThresh: %10lu kB\n" "BackgroundThresh: %10lu kB\n" + "BdiDirtied: %10lu kB\n" "BdiWritten: %10lu kB\n" "BdiWriteBandwidth: %10lu kBps\n" "b_dirty: %10lu\n" @@ -109,6 +110,7 @@ static int bdi_debug_stats_show(struct seq_file *m, void *v) K(bdi_thresh), K(dirty_thresh), K(background_thresh), + (unsigned long) K(bdi_stat(bdi, BDI_DIRTIED)), (unsigned long) K(bdi_stat(bdi, BDI_WRITTEN)), (unsigned long) K(bdi->write_bandwidth), nr_dirty, diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 0e309cd1b5b9..0e6dd5c2ed31 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -1322,6 +1322,7 @@ void account_page_dirtied(struct page *page, struct address_space *mapping) __inc_zone_page_state(page, NR_FILE_DIRTY); __inc_zone_page_state(page, NR_DIRTIED); __inc_bdi_stat(mapping->backing_dev_info, BDI_RECLAIMABLE); + __inc_bdi_stat(mapping->backing_dev_info, BDI_DIRTIED); task_dirty_inc(current); task_io_account_write(PAGE_CACHE_SIZE); } From 6c14ae1e92c77eabd3e7527cf2e7836cde8b8487 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 2 Mar 2011 16:04:18 -0600 Subject: [PATCH 0276/1531] writeback: dirty position control bdi_position_ratio() provides a scale factor to bdi->dirty_ratelimit, so that the resulted task rate limit can drive the dirty pages back to the global/bdi setpoints. Old scheme is, | free run area | throttle area ----------------------------------------+----------------------------> thresh^ dirty pages New scheme is, ^ task rate limit | | * | * | * |[free run] * [smooth throttled] | * | * | * ..bdi->dirty_ratelimit..........* | . * | . * | . * | . * | . * +-------------------------------.-----------------------*------------> setpoint^ limit^ dirty pages The slope of the bdi control line should be 1) large enough to pull the dirty pages to setpoint reasonably fast 2) small enough to avoid big fluctuations in the resulted pos_ratio and hence task ratelimit Since the fluctuation range of the bdi dirty pages is typically observed to be within 1-second worth of data, the bdi control line's slope is selected to be a linear function of bdi write bandwidth, so that it can adapt to slow/fast storage devices well. Assume the bdi control line pos_ratio = 1.0 + k * (dirty - bdi_setpoint) where k is the negative slope. If targeting for 12.5% fluctuation range in pos_ratio when dirty pages are fluctuating in range [bdi_setpoint - write_bw/2, bdi_setpoint + write_bw/2], we get slope k = - 1 / (8 * write_bw) Let pos_ratio(x_intercept) = 0, we get the parameter used in code: x_intercept = bdi_setpoint + 8 * write_bw The global/bdi slopes are nicely complementing each other when the system has only one major bdi (indicated by bdi_thresh ~= thresh): 1) slope of global control line => scaling to the control scope size 2) slope of main bdi control line => scaling to the writeout bandwidth so that - in memory tight systems, (1) becomes strong enough to squeeze dirty pages inside the control scope - in large memory systems where the "gravity" of (1) for pulling the dirty pages to setpoint is too weak, (2) can back (1) up and drive dirty pages to bdi_setpoint ~= setpoint reasonably fast. Unfortunately in JBOD setups, the fluctuation range of bdi threshold is related to memory size due to the interferences between disks. In this case, the bdi slope will be weighted sum of write_bw and bdi_thresh. Given equations span = x_intercept - bdi_setpoint k = df/dx = - 1 / span and the extremum values span = bdi_thresh dx = bdi_thresh we get df = - dx / span = - 1.0 That means, when bdi_dirty deviates bdi_thresh up, pos_ratio and hence task ratelimit will fluctuate by -100%. peter: use 3rd order polynomial for the global control line CC: Peter Zijlstra Acked-by: Jan Kara Signed-off-by: Wu Fengguang --- mm/page-writeback.c | 191 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 190 insertions(+), 1 deletion(-) diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 0e6dd5c2ed31..c16ddd8f5cb6 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -46,6 +46,8 @@ */ #define BANDWIDTH_INTERVAL max(HZ/5, 1) +#define RATELIMIT_CALC_SHIFT 10 + /* * After a CPU has dirtied this many pages, balance_dirty_pages_ratelimited * will look to see if it needs to force writeback or throttling. @@ -411,6 +413,12 @@ unsigned long determine_dirtyable_memory(void) return x + 1; /* Ensure that we never return 0 */ } +static unsigned long dirty_freerun_ceiling(unsigned long thresh, + unsigned long bg_thresh) +{ + return (thresh + bg_thresh) / 2; +} + static unsigned long hard_dirty_limit(unsigned long thresh) { return max(thresh, global_dirty_limit); @@ -495,6 +503,184 @@ unsigned long bdi_dirty_limit(struct backing_dev_info *bdi, unsigned long dirty) return bdi_dirty; } +/* + * Dirty position control. + * + * (o) global/bdi setpoints + * + * We want the dirty pages be balanced around the global/bdi setpoints. + * When the number of dirty pages is higher/lower than the setpoint, the + * dirty position control ratio (and hence task dirty ratelimit) will be + * decreased/increased to bring the dirty pages back to the setpoint. + * + * pos_ratio = 1 << RATELIMIT_CALC_SHIFT + * + * if (dirty < setpoint) scale up pos_ratio + * if (dirty > setpoint) scale down pos_ratio + * + * if (bdi_dirty < bdi_setpoint) scale up pos_ratio + * if (bdi_dirty > bdi_setpoint) scale down pos_ratio + * + * task_ratelimit = dirty_ratelimit * pos_ratio >> RATELIMIT_CALC_SHIFT + * + * (o) global control line + * + * ^ pos_ratio + * | + * | |<===== global dirty control scope ======>| + * 2.0 .............* + * | .* + * | . * + * | . * + * | . * + * | . * + * | . * + * 1.0 ................................* + * | . . * + * | . . * + * | . . * + * | . . * + * | . . * + * 0 +------------.------------------.----------------------*-------------> + * freerun^ setpoint^ limit^ dirty pages + * + * (o) bdi control line + * + * ^ pos_ratio + * | + * | * + * | * + * | * + * | * + * | * |<=========== span ============>| + * 1.0 .......................* + * | . * + * | . * + * | . * + * | . * + * | . * + * | . * + * | . * + * | . * + * | . * + * | . * + * | . * + * 1/4 ...............................................* * * * * * * * * * * * + * | . . + * | . . + * | . . + * 0 +----------------------.-------------------------------.-------------> + * bdi_setpoint^ x_intercept^ + * + * The bdi control line won't drop below pos_ratio=1/4, so that bdi_dirty can + * be smoothly throttled down to normal if it starts high in situations like + * - start writing to a slow SD card and a fast disk at the same time. The SD + * card's bdi_dirty may rush to many times higher than bdi_setpoint. + * - the bdi dirty thresh drops quickly due to change of JBOD workload + */ +static unsigned long bdi_position_ratio(struct backing_dev_info *bdi, + unsigned long thresh, + unsigned long bg_thresh, + unsigned long dirty, + unsigned long bdi_thresh, + unsigned long bdi_dirty) +{ + unsigned long write_bw = bdi->avg_write_bandwidth; + unsigned long freerun = dirty_freerun_ceiling(thresh, bg_thresh); + unsigned long limit = hard_dirty_limit(thresh); + unsigned long x_intercept; + unsigned long setpoint; /* dirty pages' target balance point */ + unsigned long bdi_setpoint; + unsigned long span; + long long pos_ratio; /* for scaling up/down the rate limit */ + long x; + + if (unlikely(dirty >= limit)) + return 0; + + /* + * global setpoint + * + * setpoint - dirty 3 + * f(dirty) := 1.0 + (----------------) + * limit - setpoint + * + * it's a 3rd order polynomial that subjects to + * + * (1) f(freerun) = 2.0 => rampup dirty_ratelimit reasonably fast + * (2) f(setpoint) = 1.0 => the balance point + * (3) f(limit) = 0 => the hard limit + * (4) df/dx <= 0 => negative feedback control + * (5) the closer to setpoint, the smaller |df/dx| (and the reverse) + * => fast response on large errors; small oscillation near setpoint + */ + setpoint = (freerun + limit) / 2; + x = div_s64((setpoint - dirty) << RATELIMIT_CALC_SHIFT, + limit - setpoint + 1); + pos_ratio = x; + pos_ratio = pos_ratio * x >> RATELIMIT_CALC_SHIFT; + pos_ratio = pos_ratio * x >> RATELIMIT_CALC_SHIFT; + pos_ratio += 1 << RATELIMIT_CALC_SHIFT; + + /* + * We have computed basic pos_ratio above based on global situation. If + * the bdi is over/under its share of dirty pages, we want to scale + * pos_ratio further down/up. That is done by the following mechanism. + */ + + /* + * bdi setpoint + * + * f(bdi_dirty) := 1.0 + k * (bdi_dirty - bdi_setpoint) + * + * x_intercept - bdi_dirty + * := -------------------------- + * x_intercept - bdi_setpoint + * + * The main bdi control line is a linear function that subjects to + * + * (1) f(bdi_setpoint) = 1.0 + * (2) k = - 1 / (8 * write_bw) (in single bdi case) + * or equally: x_intercept = bdi_setpoint + 8 * write_bw + * + * For single bdi case, the dirty pages are observed to fluctuate + * regularly within range + * [bdi_setpoint - write_bw/2, bdi_setpoint + write_bw/2] + * for various filesystems, where (2) can yield in a reasonable 12.5% + * fluctuation range for pos_ratio. + * + * For JBOD case, bdi_thresh (not bdi_dirty!) could fluctuate up to its + * own size, so move the slope over accordingly and choose a slope that + * yields 100% pos_ratio fluctuation on suddenly doubled bdi_thresh. + */ + if (unlikely(bdi_thresh > thresh)) + bdi_thresh = thresh; + /* + * scale global setpoint to bdi's: + * bdi_setpoint = setpoint * bdi_thresh / thresh + */ + x = div_u64((u64)bdi_thresh << 16, thresh + 1); + bdi_setpoint = setpoint * (u64)x >> 16; + /* + * Use span=(8*write_bw) in single bdi case as indicated by + * (thresh - bdi_thresh ~= 0) and transit to bdi_thresh in JBOD case. + * + * bdi_thresh thresh - bdi_thresh + * span = ---------- * (8 * write_bw) + ------------------- * bdi_thresh + * thresh thresh + */ + span = (thresh - bdi_thresh + 8 * write_bw) * (u64)x >> 16; + x_intercept = bdi_setpoint + span; + + if (bdi_dirty < x_intercept - span / 4) { + pos_ratio *= x_intercept - bdi_dirty; + do_div(pos_ratio, x_intercept - bdi_setpoint + 1); + } else + pos_ratio /= 4; + + return pos_ratio; +} + static void bdi_update_write_bandwidth(struct backing_dev_info *bdi, unsigned long elapsed, unsigned long written) @@ -655,6 +841,7 @@ static void balance_dirty_pages(struct address_space *mapping, unsigned long nr_reclaimable, bdi_nr_reclaimable; unsigned long nr_dirty; /* = file_dirty + writeback + unstable_nfs */ unsigned long bdi_dirty; + unsigned long freerun; unsigned long background_thresh; unsigned long dirty_thresh; unsigned long bdi_thresh; @@ -679,7 +866,9 @@ static void balance_dirty_pages(struct address_space *mapping, * catch-up. This avoids (excessively) small writeouts * when the bdi limits are ramping up. */ - if (nr_dirty <= (background_thresh + dirty_thresh) / 2) + freerun = dirty_freerun_ceiling(dirty_thresh, + background_thresh); + if (nr_dirty <= freerun) break; bdi_thresh = bdi_dirty_limit(bdi, dirty_thresh); From af6a311384bce6c88e15c80ab22ab051a918b4eb Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Mon, 3 Oct 2011 20:46:17 -0600 Subject: [PATCH 0277/1531] writeback: add bg_threshold parameter to __bdi_update_bandwidth() No behavior change. Signed-off-by: Wu Fengguang --- fs/fs-writeback.c | 2 +- include/linux/writeback.h | 1 + mm/page-writeback.c | 11 +++++++---- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 04cf3b91e501..28076562ada0 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -675,7 +675,7 @@ static inline bool over_bground_thresh(void) static void wb_update_bandwidth(struct bdi_writeback *wb, unsigned long start_time) { - __bdi_update_bandwidth(wb->bdi, 0, 0, 0, 0, start_time); + __bdi_update_bandwidth(wb->bdi, 0, 0, 0, 0, 0, start_time); } /* diff --git a/include/linux/writeback.h b/include/linux/writeback.h index 2b8963ff0f35..ddb4652cb337 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -143,6 +143,7 @@ unsigned long bdi_dirty_limit(struct backing_dev_info *bdi, void __bdi_update_bandwidth(struct backing_dev_info *bdi, unsigned long thresh, + unsigned long bg_thresh, unsigned long dirty, unsigned long bdi_thresh, unsigned long bdi_dirty, diff --git a/mm/page-writeback.c b/mm/page-writeback.c index c16ddd8f5cb6..4b954c9fe846 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -779,6 +779,7 @@ static void global_update_bandwidth(unsigned long thresh, void __bdi_update_bandwidth(struct backing_dev_info *bdi, unsigned long thresh, + unsigned long bg_thresh, unsigned long dirty, unsigned long bdi_thresh, unsigned long bdi_dirty, @@ -815,6 +816,7 @@ void __bdi_update_bandwidth(struct backing_dev_info *bdi, static void bdi_update_bandwidth(struct backing_dev_info *bdi, unsigned long thresh, + unsigned long bg_thresh, unsigned long dirty, unsigned long bdi_thresh, unsigned long bdi_dirty, @@ -823,8 +825,8 @@ static void bdi_update_bandwidth(struct backing_dev_info *bdi, if (time_is_after_eq_jiffies(bdi->bw_time_stamp + BANDWIDTH_INTERVAL)) return; spin_lock(&bdi->wb.list_lock); - __bdi_update_bandwidth(bdi, thresh, dirty, bdi_thresh, bdi_dirty, - start_time); + __bdi_update_bandwidth(bdi, thresh, bg_thresh, dirty, + bdi_thresh, bdi_dirty, start_time); spin_unlock(&bdi->wb.list_lock); } @@ -912,8 +914,9 @@ static void balance_dirty_pages(struct address_space *mapping, if (!bdi->dirty_exceeded) bdi->dirty_exceeded = 1; - bdi_update_bandwidth(bdi, dirty_thresh, nr_dirty, - bdi_thresh, bdi_dirty, start_time); + bdi_update_bandwidth(bdi, dirty_thresh, background_thresh, + nr_dirty, bdi_thresh, bdi_dirty, + start_time); /* Note: nr_reclaimable denotes nr_dirty + nr_unstable. * Unstable writes are a feature of certain networked From be3ffa276446e1b691a2bf84e7621e5a6fb49db9 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Sun, 12 Jun 2011 10:51:31 -0600 Subject: [PATCH 0278/1531] writeback: dirty rate control It's all about bdi->dirty_ratelimit, which aims to be (write_bw / N) when there are N dd tasks. On write() syscall, use bdi->dirty_ratelimit ============================================ balance_dirty_pages(pages_dirtied) { task_ratelimit = bdi->dirty_ratelimit * bdi_position_ratio(); pause = pages_dirtied / task_ratelimit; sleep(pause); } On every 200ms, update bdi->dirty_ratelimit =========================================== bdi_update_dirty_ratelimit() { task_ratelimit = bdi->dirty_ratelimit * bdi_position_ratio(); balanced_dirty_ratelimit = task_ratelimit * write_bw / dirty_rate; bdi->dirty_ratelimit = balanced_dirty_ratelimit } Estimation of balanced bdi->dirty_ratelimit =========================================== balanced task_ratelimit ----------------------- balance_dirty_pages() needs to throttle tasks dirtying pages such that the total amount of dirty pages stays below the specified dirty limit in order to avoid memory deadlocks. Furthermore we desire fairness in that tasks get throttled proportionally to the amount of pages they dirty. IOW we want to throttle tasks such that we match the dirty rate to the writeout bandwidth, this yields a stable amount of dirty pages: dirty_rate == write_bw (1) The fairness requirement gives us: task_ratelimit = balanced_dirty_ratelimit == write_bw / N (2) where N is the number of dd tasks. We don't know N beforehand, but still can estimate balanced_dirty_ratelimit within 200ms. Start by throttling each dd task at rate task_ratelimit = task_ratelimit_0 (3) (any non-zero initial value is OK) After 200ms, we measured dirty_rate = # of pages dirtied by all dd's / 200ms write_bw = # of pages written to the disk / 200ms For the aggressive dd dirtiers, the equality holds dirty_rate == N * task_rate == N * task_ratelimit_0 (4) Or task_ratelimit_0 == dirty_rate / N (5) Now we conclude that the balanced task ratelimit can be estimated by write_bw balanced_dirty_ratelimit = task_ratelimit_0 * ---------- (6) dirty_rate Because with (4) and (5) we can get the desired equality (1): write_bw balanced_dirty_ratelimit == (dirty_rate / N) * ---------- dirty_rate == write_bw / N Then using the balanced task ratelimit we can compute task pause times like: task_pause = task->nr_dirtied / task_ratelimit task_ratelimit with position control ------------------------------------ However, while the above gives us means of matching the dirty rate to the writeout bandwidth, it at best provides us with a stable dirty page count (assuming a static system). In order to control the dirty page count such that it is high enough to provide performance, but does not exceed the specified limit we need another control. The dirty position control works by extending (2) to task_ratelimit = balanced_dirty_ratelimit * pos_ratio (7) where pos_ratio is a negative feedback function that subjects to 1) f(setpoint) = 1.0 2) df/dx < 0 That is, if the dirty pages are ABOVE the setpoint, we throttle each task a bit more HEAVY than balanced_dirty_ratelimit, so that the dirty pages are created less fast than they are cleaned, thus DROP to the setpoints (and the reverse). Based on (7) and the assumption that both dirty_ratelimit and pos_ratio remains CONSTANT for the past 200ms, we get task_ratelimit_0 = balanced_dirty_ratelimit * pos_ratio (8) Putting (8) into (6), we get the formula used in bdi_update_dirty_ratelimit(): write_bw balanced_dirty_ratelimit *= pos_ratio * ---------- (9) dirty_rate Signed-off-by: Wu Fengguang --- include/linux/backing-dev.h | 7 ++++ mm/backing-dev.c | 1 + mm/page-writeback.c | 83 ++++++++++++++++++++++++++++++++++++- 3 files changed, 89 insertions(+), 2 deletions(-) diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index 9ca241a70c49..dff0ff78e878 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -75,10 +75,17 @@ struct backing_dev_info { struct percpu_counter bdi_stat[NR_BDI_STAT_ITEMS]; unsigned long bw_time_stamp; /* last time write bw is updated */ + unsigned long dirtied_stamp; unsigned long written_stamp; /* pages written at bw_time_stamp */ unsigned long write_bandwidth; /* the estimated write bandwidth */ unsigned long avg_write_bandwidth; /* further smoothed write bw */ + /* + * The base dirty throttle rate, re-calculated on every 200ms. + * All the bdi tasks' dirty rate will be curbed under it. + */ + unsigned long dirty_ratelimit; + struct prop_local_percpu completions; int dirty_exceeded; diff --git a/mm/backing-dev.c b/mm/backing-dev.c index fea7e6efd1d7..ba20f94cde93 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -686,6 +686,7 @@ int bdi_init(struct backing_dev_info *bdi) bdi->bw_time_stamp = jiffies; bdi->written_stamp = 0; + bdi->dirty_ratelimit = INIT_BW; bdi->write_bandwidth = INIT_BW; bdi->avg_write_bandwidth = INIT_BW; diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 4b954c9fe846..1721b6523c04 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -777,6 +777,79 @@ static void global_update_bandwidth(unsigned long thresh, spin_unlock(&dirty_lock); } +/* + * Maintain bdi->dirty_ratelimit, the base dirty throttle rate. + * + * Normal bdi tasks will be curbed at or below it in long term. + * Obviously it should be around (write_bw / N) when there are N dd tasks. + */ +static void bdi_update_dirty_ratelimit(struct backing_dev_info *bdi, + unsigned long thresh, + unsigned long bg_thresh, + unsigned long dirty, + unsigned long bdi_thresh, + unsigned long bdi_dirty, + unsigned long dirtied, + unsigned long elapsed) +{ + unsigned long write_bw = bdi->avg_write_bandwidth; + unsigned long dirty_ratelimit = bdi->dirty_ratelimit; + unsigned long dirty_rate; + unsigned long task_ratelimit; + unsigned long balanced_dirty_ratelimit; + unsigned long pos_ratio; + + /* + * The dirty rate will match the writeout rate in long term, except + * when dirty pages are truncated by userspace or re-dirtied by FS. + */ + dirty_rate = (dirtied - bdi->dirtied_stamp) * HZ / elapsed; + + pos_ratio = bdi_position_ratio(bdi, thresh, bg_thresh, dirty, + bdi_thresh, bdi_dirty); + /* + * task_ratelimit reflects each dd's dirty rate for the past 200ms. + */ + task_ratelimit = (u64)dirty_ratelimit * + pos_ratio >> RATELIMIT_CALC_SHIFT; + task_ratelimit++; /* it helps rampup dirty_ratelimit from tiny values */ + + /* + * A linear estimation of the "balanced" throttle rate. The theory is, + * if there are N dd tasks, each throttled at task_ratelimit, the bdi's + * dirty_rate will be measured to be (N * task_ratelimit). So the below + * formula will yield the balanced rate limit (write_bw / N). + * + * Note that the expanded form is not a pure rate feedback: + * rate_(i+1) = rate_(i) * (write_bw / dirty_rate) (1) + * but also takes pos_ratio into account: + * rate_(i+1) = rate_(i) * (write_bw / dirty_rate) * pos_ratio (2) + * + * (1) is not realistic because pos_ratio also takes part in balancing + * the dirty rate. Consider the state + * pos_ratio = 0.5 (3) + * rate = 2 * (write_bw / N) (4) + * If (1) is used, it will stuck in that state! Because each dd will + * be throttled at + * task_ratelimit = pos_ratio * rate = (write_bw / N) (5) + * yielding + * dirty_rate = N * task_ratelimit = write_bw (6) + * put (6) into (1) we get + * rate_(i+1) = rate_(i) (7) + * + * So we end up using (2) to always keep + * rate_(i+1) ~= (write_bw / N) (8) + * regardless of the value of pos_ratio. As long as (8) is satisfied, + * pos_ratio is able to drive itself to 1.0, which is not only where + * the dirty count meet the setpoint, but also where the slope of + * pos_ratio is most flat and hence task_ratelimit is least fluctuated. + */ + balanced_dirty_ratelimit = div_u64((u64)task_ratelimit * write_bw, + dirty_rate | 1); + + bdi->dirty_ratelimit = max(balanced_dirty_ratelimit, 1UL); +} + void __bdi_update_bandwidth(struct backing_dev_info *bdi, unsigned long thresh, unsigned long bg_thresh, @@ -787,6 +860,7 @@ void __bdi_update_bandwidth(struct backing_dev_info *bdi, { unsigned long now = jiffies; unsigned long elapsed = now - bdi->bw_time_stamp; + unsigned long dirtied; unsigned long written; /* @@ -795,6 +869,7 @@ void __bdi_update_bandwidth(struct backing_dev_info *bdi, if (elapsed < BANDWIDTH_INTERVAL) return; + dirtied = percpu_counter_read(&bdi->bdi_stat[BDI_DIRTIED]); written = percpu_counter_read(&bdi->bdi_stat[BDI_WRITTEN]); /* @@ -804,12 +879,16 @@ void __bdi_update_bandwidth(struct backing_dev_info *bdi, if (elapsed > HZ && time_before(bdi->bw_time_stamp, start_time)) goto snapshot; - if (thresh) + if (thresh) { global_update_bandwidth(thresh, dirty, now); - + bdi_update_dirty_ratelimit(bdi, thresh, bg_thresh, dirty, + bdi_thresh, bdi_dirty, + dirtied, elapsed); + } bdi_update_write_bandwidth(bdi, elapsed, written); snapshot: + bdi->dirtied_stamp = dirtied; bdi->written_stamp = written; bdi->bw_time_stamp = now; } From 7381131cbcf7e15d201a0ffd782a4698efe4e740 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 26 Aug 2011 15:53:24 -0600 Subject: [PATCH 0279/1531] writeback: stabilize bdi->dirty_ratelimit There are some imperfections in balanced_dirty_ratelimit. 1) large fluctuations The dirty_rate used for computing balanced_dirty_ratelimit is merely averaged in the past 200ms (very small comparing to the 3s estimation period for write_bw), which makes rather dispersed distribution of balanced_dirty_ratelimit. It's pretty hard to average out the singular points by increasing the estimation period. Considering that the averaging technique will introduce very undesirable time lags, I give it up totally. (btw, the 3s write_bw averaging time lag is much more acceptable because its impact is one-way and therefore won't lead to oscillations.) The more practical way is filtering -- most singular balanced_dirty_ratelimit points can be filtered out by remembering some prev_balanced_rate and prev_prev_balanced_rate. However the more reliable way is to guard balanced_dirty_ratelimit with task_ratelimit. 2) due to truncates and fs redirties, the (write_bw <=> dirty_rate) match could become unbalanced, which may lead to large systematical errors in balanced_dirty_ratelimit. The truncates, due to its possibly bumpy nature, can hardly be compensated smoothly. So let's face it. When some over-estimated balanced_dirty_ratelimit brings dirty_ratelimit high, dirty pages will go higher than the setpoint. task_ratelimit will in turn become lower than dirty_ratelimit. So if we consider both balanced_dirty_ratelimit and task_ratelimit and update dirty_ratelimit only when they are on the same side of dirty_ratelimit, the systematical errors in balanced_dirty_ratelimit won't be able to bring dirty_ratelimit far away. The balanced_dirty_ratelimit estimation may also be inaccurate near @limit or @freerun, however is less an issue. 3) since we ultimately want to - keep the fluctuations of task ratelimit as small as possible - keep the dirty pages around the setpoint as long time as possible the update policy used for (2) also serves the above goals nicely: if for some reason the dirty pages are high (task_ratelimit < dirty_ratelimit), and dirty_ratelimit is low (dirty_ratelimit < balanced_dirty_ratelimit), there is no point to bring up dirty_ratelimit in a hurry only to hurt both the above two goals. So, we make use of task_ratelimit to limit the update of dirty_ratelimit in two ways: 1) avoid changing dirty rate when it's against the position control target (the adjusted rate will slow down the progress of dirty pages going back to setpoint). 2) limit the step size. task_ratelimit is changing values step by step, leaving a consistent trace comparing to the randomly jumping balanced_dirty_ratelimit. task_ratelimit also has the nice smaller errors in stable state and typically larger errors when there are big errors in rate. So it's a pretty good limiting factor for the step size of dirty_ratelimit. Note that bdi->dirty_ratelimit is always tracking balanced_dirty_ratelimit. task_ratelimit is merely used as a limiting factor. Signed-off-by: Wu Fengguang --- include/linux/backing-dev.h | 3 ++ mm/backing-dev.c | 1 + mm/page-writeback.c | 71 ++++++++++++++++++++++++++++++++++++- 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index dff0ff78e878..c3b92010d894 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -83,8 +83,11 @@ struct backing_dev_info { /* * The base dirty throttle rate, re-calculated on every 200ms. * All the bdi tasks' dirty rate will be curbed under it. + * @dirty_ratelimit tracks the estimated @balanced_dirty_ratelimit + * in small steps and is much more smooth/stable than the latter. */ unsigned long dirty_ratelimit; + unsigned long balanced_dirty_ratelimit; struct prop_local_percpu completions; int dirty_exceeded; diff --git a/mm/backing-dev.c b/mm/backing-dev.c index ba20f94cde93..5dcaa3c756d1 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -686,6 +686,7 @@ int bdi_init(struct backing_dev_info *bdi) bdi->bw_time_stamp = jiffies; bdi->written_stamp = 0; + bdi->balanced_dirty_ratelimit = INIT_BW; bdi->dirty_ratelimit = INIT_BW; bdi->write_bandwidth = INIT_BW; bdi->avg_write_bandwidth = INIT_BW; diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 1721b6523c04..d4a6e91bd9e5 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -792,12 +792,17 @@ static void bdi_update_dirty_ratelimit(struct backing_dev_info *bdi, unsigned long dirtied, unsigned long elapsed) { + unsigned long freerun = dirty_freerun_ceiling(thresh, bg_thresh); + unsigned long limit = hard_dirty_limit(thresh); + unsigned long setpoint = (freerun + limit) / 2; unsigned long write_bw = bdi->avg_write_bandwidth; unsigned long dirty_ratelimit = bdi->dirty_ratelimit; unsigned long dirty_rate; unsigned long task_ratelimit; unsigned long balanced_dirty_ratelimit; unsigned long pos_ratio; + unsigned long step; + unsigned long x; /* * The dirty rate will match the writeout rate in long term, except @@ -847,7 +852,71 @@ static void bdi_update_dirty_ratelimit(struct backing_dev_info *bdi, balanced_dirty_ratelimit = div_u64((u64)task_ratelimit * write_bw, dirty_rate | 1); - bdi->dirty_ratelimit = max(balanced_dirty_ratelimit, 1UL); + /* + * We could safely do this and return immediately: + * + * bdi->dirty_ratelimit = balanced_dirty_ratelimit; + * + * However to get a more stable dirty_ratelimit, the below elaborated + * code makes use of task_ratelimit to filter out sigular points and + * limit the step size. + * + * The below code essentially only uses the relative value of + * + * task_ratelimit - dirty_ratelimit + * = (pos_ratio - 1) * dirty_ratelimit + * + * which reflects the direction and size of dirty position error. + */ + + /* + * dirty_ratelimit will follow balanced_dirty_ratelimit iff + * task_ratelimit is on the same side of dirty_ratelimit, too. + * For example, when + * - dirty_ratelimit > balanced_dirty_ratelimit + * - dirty_ratelimit > task_ratelimit (dirty pages are above setpoint) + * lowering dirty_ratelimit will help meet both the position and rate + * control targets. Otherwise, don't update dirty_ratelimit if it will + * only help meet the rate target. After all, what the users ultimately + * feel and care are stable dirty rate and small position error. + * + * |task_ratelimit - dirty_ratelimit| is used to limit the step size + * and filter out the sigular points of balanced_dirty_ratelimit. Which + * keeps jumping around randomly and can even leap far away at times + * due to the small 200ms estimation period of dirty_rate (we want to + * keep that period small to reduce time lags). + */ + step = 0; + if (dirty < setpoint) { + x = min(bdi->balanced_dirty_ratelimit, + min(balanced_dirty_ratelimit, task_ratelimit)); + if (dirty_ratelimit < x) + step = x - dirty_ratelimit; + } else { + x = max(bdi->balanced_dirty_ratelimit, + max(balanced_dirty_ratelimit, task_ratelimit)); + if (dirty_ratelimit > x) + step = dirty_ratelimit - x; + } + + /* + * Don't pursue 100% rate matching. It's impossible since the balanced + * rate itself is constantly fluctuating. So decrease the track speed + * when it gets close to the target. Helps eliminate pointless tremors. + */ + step >>= dirty_ratelimit / (2 * step + 1); + /* + * Limit the tracking speed to avoid overshooting. + */ + step = (step + 7) / 8; + + if (dirty_ratelimit < balanced_dirty_ratelimit) + dirty_ratelimit += step; + else + dirty_ratelimit -= step; + + bdi->dirty_ratelimit = max(dirty_ratelimit, 1UL); + bdi->balanced_dirty_ratelimit = balanced_dirty_ratelimit; } void __bdi_update_bandwidth(struct backing_dev_info *bdi, From 9d823e8f6b1b7b39f952d7d1795f29162143a433 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Sat, 11 Jun 2011 18:10:12 -0600 Subject: [PATCH 0280/1531] writeback: per task dirty rate limit Add two fields to task_struct. 1) account dirtied pages in the individual tasks, for accuracy 2) per-task balance_dirty_pages() call intervals, for flexibility The balance_dirty_pages() call interval (ie. nr_dirtied_pause) will scale near-sqrt to the safety gap between dirty pages and threshold. The main problem of per-task nr_dirtied is, if 1k+ tasks start dirtying pages at exactly the same time, each task will be assigned a large initial nr_dirtied_pause, so that the dirty threshold will be exceeded long before each task reached its nr_dirtied_pause and hence call balance_dirty_pages(). The solution is to watch for the number of pages dirtied on each CPU in between the calls into balance_dirty_pages(). If it exceeds ratelimit_pages (3% dirty threshold), force call balance_dirty_pages() for a chance to set bdi->dirty_exceeded. In normal situations, this safeguarding condition is not expected to trigger at all. On the sqrt in dirty_poll_interval(): It will serve as an initial guess when dirty pages are still in the freerun area. When dirty pages are floating inside the dirty control scope [freerun, limit], a followup patch will use some refined dirty poll interval to get the desired pause time. thresh-dirty (MB) sqrt 1 16 2 22 4 32 8 45 16 64 32 90 64 128 128 181 256 256 512 362 1024 512 The above table means, given 1MB (or 1GB) gap and the dd tasks polling balance_dirty_pages() on every 16 (or 512) pages, the dirty limit won't be exceeded as long as there are less than 16 (or 512) concurrent dd's. So sqrt naturally leads to less overheads and more safe concurrent tasks for large memory servers, which have large (thresh-freerun) gaps. peter: keep the per-CPU ratelimit for safeguarding the 1k+ tasks case CC: Peter Zijlstra Reviewed-by: Andrea Righi Signed-off-by: Wu Fengguang --- include/linux/sched.h | 7 ++++ kernel/fork.c | 3 ++ mm/page-writeback.c | 89 ++++++++++++++++++++++++------------------- 3 files changed, 60 insertions(+), 39 deletions(-) diff --git a/include/linux/sched.h b/include/linux/sched.h index 41d0237fd449..a4a5582dc618 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1525,6 +1525,13 @@ struct task_struct { int make_it_fail; #endif struct prop_local_single dirties; + /* + * when (nr_dirtied >= nr_dirtied_pause), it's time to call + * balance_dirty_pages() for some dirty throttling pause + */ + int nr_dirtied; + int nr_dirtied_pause; + #ifdef CONFIG_LATENCYTOP int latency_record_count; struct latency_record latency_record[LT_SAVECOUNT]; diff --git a/kernel/fork.c b/kernel/fork.c index 8e6b6f4fb272..cc0815df99f2 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1302,6 +1302,9 @@ static struct task_struct *copy_process(unsigned long clone_flags, p->pdeath_signal = 0; p->exit_state = 0; + p->nr_dirtied = 0; + p->nr_dirtied_pause = 128 >> (PAGE_SHIFT - 10); + /* * Ok, make it visible to the rest of the system. * We dont wake it up yet. diff --git a/mm/page-writeback.c b/mm/page-writeback.c index d4a6e91bd9e5..daff320d263f 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -54,20 +54,6 @@ */ static long ratelimit_pages = 32; -/* - * When balance_dirty_pages decides that the caller needs to perform some - * non-background writeback, this is how many pages it will attempt to write. - * It should be somewhat larger than dirtied pages to ensure that reasonably - * large amounts of I/O are submitted. - */ -static inline long sync_writeback_pages(unsigned long dirtied) -{ - if (dirtied < ratelimit_pages) - dirtied = ratelimit_pages; - - return dirtied + dirtied / 2; -} - /* The following parameters are exported via /proc/sys/vm */ /* @@ -169,6 +155,8 @@ static void update_completion_period(void) int shift = calc_period_shift(); prop_change_shift(&vm_completions, shift); prop_change_shift(&vm_dirties, shift); + + writeback_set_ratelimit(); } int dirty_background_ratio_handler(struct ctl_table *table, int write, @@ -978,6 +966,23 @@ static void bdi_update_bandwidth(struct backing_dev_info *bdi, spin_unlock(&bdi->wb.list_lock); } +/* + * After a task dirtied this many pages, balance_dirty_pages_ratelimited_nr() + * will look to see if it needs to start dirty throttling. + * + * If dirty_poll_interval is too low, big NUMA machines will call the expensive + * global_page_state() too often. So scale it near-sqrt to the safety margin + * (the number of pages we may dirty without exceeding the dirty limits). + */ +static unsigned long dirty_poll_interval(unsigned long dirty, + unsigned long thresh) +{ + if (thresh > dirty) + return 1UL << (ilog2(thresh - dirty) >> 1); + + return 1; +} + /* * balance_dirty_pages() must be called by processes which are generating dirty * data. It looks at the number of dirty pages in the machine and will force @@ -1112,6 +1117,9 @@ static void balance_dirty_pages(struct address_space *mapping, if (clear_dirty_exceeded && bdi->dirty_exceeded) bdi->dirty_exceeded = 0; + current->nr_dirtied = 0; + current->nr_dirtied_pause = dirty_poll_interval(nr_dirty, dirty_thresh); + if (writeback_in_progress(bdi)) return; @@ -1138,7 +1146,7 @@ void set_page_dirty_balance(struct page *page, int page_mkwrite) } } -static DEFINE_PER_CPU(unsigned long, bdp_ratelimits) = 0; +static DEFINE_PER_CPU(int, bdp_ratelimits); /** * balance_dirty_pages_ratelimited_nr - balance dirty memory state @@ -1158,31 +1166,39 @@ void balance_dirty_pages_ratelimited_nr(struct address_space *mapping, unsigned long nr_pages_dirtied) { struct backing_dev_info *bdi = mapping->backing_dev_info; - unsigned long ratelimit; - unsigned long *p; + int ratelimit; + int *p; if (!bdi_cap_account_dirty(bdi)) return; - ratelimit = ratelimit_pages; - if (mapping->backing_dev_info->dirty_exceeded) - ratelimit = 8; + ratelimit = current->nr_dirtied_pause; + if (bdi->dirty_exceeded) + ratelimit = min(ratelimit, 32 >> (PAGE_SHIFT - 10)); + + current->nr_dirtied += nr_pages_dirtied; + preempt_disable(); /* - * Check the rate limiting. Also, we do not want to throttle real-time - * tasks in balance_dirty_pages(). Period. + * This prevents one CPU to accumulate too many dirtied pages without + * calling into balance_dirty_pages(), which can happen when there are + * 1000+ tasks, all of them start dirtying pages at exactly the same + * time, hence all honoured too large initial task->nr_dirtied_pause. */ - preempt_disable(); p = &__get_cpu_var(bdp_ratelimits); - *p += nr_pages_dirtied; - if (unlikely(*p >= ratelimit)) { - ratelimit = sync_writeback_pages(*p); + if (unlikely(current->nr_dirtied >= ratelimit)) *p = 0; - preempt_enable(); - balance_dirty_pages(mapping, ratelimit); - return; + else { + *p += nr_pages_dirtied; + if (unlikely(*p >= ratelimit_pages)) { + *p = 0; + ratelimit = 0; + } } preempt_enable(); + + if (unlikely(current->nr_dirtied >= ratelimit)) + balance_dirty_pages(mapping, current->nr_dirtied); } EXPORT_SYMBOL(balance_dirty_pages_ratelimited_nr); @@ -1277,22 +1293,17 @@ void laptop_sync_completion(void) * * Here we set ratelimit_pages to a level which ensures that when all CPUs are * dirtying in parallel, we cannot go more than 3% (1/32) over the dirty memory - * thresholds before writeback cuts in. - * - * But the limit should not be set too high. Because it also controls the - * amount of memory which the balance_dirty_pages() caller has to write back. - * If this is too large then the caller will block on the IO queue all the - * time. So limit it to four megabytes - the balance_dirty_pages() caller - * will write six megabyte chunks, max. + * thresholds. */ void writeback_set_ratelimit(void) { - ratelimit_pages = vm_total_pages / (num_online_cpus() * 32); + unsigned long background_thresh; + unsigned long dirty_thresh; + global_dirty_limits(&background_thresh, &dirty_thresh); + ratelimit_pages = dirty_thresh / (num_online_cpus() * 32); if (ratelimit_pages < 16) ratelimit_pages = 16; - if (ratelimit_pages * PAGE_CACHE_SIZE > 4096 * 1024) - ratelimit_pages = (4096 * 1024) / PAGE_CACHE_SIZE; } static int __cpuinit From 143dfe8611a63030ce0c79419dc362f7838be557 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Fri, 27 Aug 2010 18:45:12 -0600 Subject: [PATCH 0281/1531] writeback: IO-less balance_dirty_pages() As proposed by Chris, Dave and Jan, don't start foreground writeback IO inside balance_dirty_pages(). Instead, simply let it idle sleep for some time to throttle the dirtying task. In the mean while, kick off the per-bdi flusher thread to do background writeback IO. RATIONALS ========= - disk seeks on concurrent writeback of multiple inodes (Dave Chinner) If every thread doing writes and being throttled start foreground writeback, it leads to N IO submitters from at least N different inodes at the same time, end up with N different sets of IO being issued with potentially zero locality to each other, resulting in much lower elevator sort/merge efficiency and hence we seek the disk all over the place to service the different sets of IO. OTOH, if there is only one submission thread, it doesn't jump between inodes in the same way when congestion clears - it keeps writing to the same inode, resulting in large related chunks of sequential IOs being issued to the disk. This is more efficient than the above foreground writeback because the elevator works better and the disk seeks less. - lock contention and cache bouncing on concurrent IO submitters (Dave Chinner) With this patchset, the fs_mark benchmark on a 12-drive software RAID0 goes from CPU bound to IO bound, freeing "3-4 CPUs worth of spinlock contention". * "CPU usage has dropped by ~55%", "it certainly appears that most of the CPU time saving comes from the removal of contention on the inode_wb_list_lock" (IMHO at least 10% comes from the reduction of cacheline bouncing, because the new code is able to call much less frequently into balance_dirty_pages() and hence access the global page states) * the user space "App overhead" is reduced by 20%, by avoiding the cacheline pollution by the complex writeback code path * "for a ~5% throughput reduction", "the number of write IOs have dropped by ~25%", and the elapsed time reduced from 41:42.17 to 40:53.23. * On a simple test of 100 dd, it reduces the CPU %system time from 30% to 3%, and improves IO throughput from 38MB/s to 42MB/s. - IO size too small for fast arrays and too large for slow USB sticks The write_chunk used by current balance_dirty_pages() cannot be directly set to some large value (eg. 128MB) for better IO efficiency. Because it could lead to more than 1 second user perceivable stalls. Even the current 4MB write size may be too large for slow USB sticks. The fact that balance_dirty_pages() starts IO on itself couples the IO size to wait time, which makes it hard to do suitable IO size while keeping the wait time under control. Now it's possible to increase writeback chunk size proportional to the disk bandwidth. In a simple test of 50 dd's on XFS, 1-HDD, 3GB ram, the larger writeback size dramatically reduces the seek count to 1/10 (far beyond my expectation) and improves the write throughput by 24%. - long block time in balance_dirty_pages() hurts desktop responsiveness Many of us may have the experience: it often takes a couple of seconds or even long time to stop a heavy writing dd/cp/tar command with Ctrl-C or "kill -9". - IO pipeline broken by bumpy write() progress There are a broad class of "loop {read(buf); write(buf);}" applications whose read() pipeline will be under-utilized or even come to a stop if the write()s have long latencies _or_ don't progress in a constant rate. The current threshold based throttling inherently transfers the large low level IO completion fluctuations to bumpy application write()s, and further deteriorates with increasing number of dirtiers and/or bdi's. For example, when doing 50 dd's + 1 remote rsync to an XFS partition, the rsync progresses very bumpy in legacy kernel, and throughput is improved by 67% by this patchset. (plus the larger write chunk size, it will be 93% speedup). The new rate based throttling can support 1000+ dd's with excellent smoothness, low latency and low overheads. For the above reasons, it's much better to do IO-less and low latency pauses in balance_dirty_pages(). Jan Kara, Dave Chinner and me explored the scheme to let balance_dirty_pages() wait for enough writeback IO completions to safeguard the dirty limit. However it's found to have two problems: - in large NUMA systems, the per-cpu counters may have big accounting errors, leading to big throttle wait time and jitters. - NFS may kill large amount of unstable pages with one single COMMIT. Because NFS server serves COMMIT with expensive fsync() IOs, it is desirable to delay and reduce the number of COMMITs. So it's not likely to optimize away such kind of bursty IO completions, and the resulted large (and tiny) stall times in IO completion based throttling. So here is a pause time oriented approach, which tries to control the pause time in each balance_dirty_pages() invocations, by controlling the number of pages dirtied before calling balance_dirty_pages(), for smooth and efficient dirty throttling: - avoid useless (eg. zero pause time) balance_dirty_pages() calls - avoid too small pause time (less than 4ms, which burns CPU power) - avoid too large pause time (more than 200ms, which hurts responsiveness) - avoid big fluctuations of pause times It can control pause times at will. The default policy (in a followup patch) will be to do ~10ms pauses in 1-dd case, and increase to ~100ms in 1000-dd case. BEHAVIOR CHANGE =============== (1) dirty threshold Users will notice that the applications will get throttled once crossing the global (background + dirty)/2=15% threshold, and then balanced around 17.5%. Before patch, the behavior is to just throttle it at 20% dirtyable memory in 1-dd case. Since the task will be soft throttled earlier than before, it may be perceived by end users as performance "slow down" if his application happens to dirty more than 15% dirtyable memory. (2) smoothness/responsiveness Users will notice a more responsive system during heavy writeback. "killall dd" will take effect instantly. Signed-off-by: Wu Fengguang --- include/trace/events/writeback.h | 24 ----- mm/page-writeback.c | 161 +++++++++++-------------------- 2 files changed, 56 insertions(+), 129 deletions(-) diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h index 5f172703eb4f..178c23508d3d 100644 --- a/include/trace/events/writeback.h +++ b/include/trace/events/writeback.h @@ -104,30 +104,6 @@ DEFINE_WRITEBACK_EVENT(writeback_bdi_register); DEFINE_WRITEBACK_EVENT(writeback_bdi_unregister); DEFINE_WRITEBACK_EVENT(writeback_thread_start); DEFINE_WRITEBACK_EVENT(writeback_thread_stop); -DEFINE_WRITEBACK_EVENT(balance_dirty_start); -DEFINE_WRITEBACK_EVENT(balance_dirty_wait); - -TRACE_EVENT(balance_dirty_written, - - TP_PROTO(struct backing_dev_info *bdi, int written), - - TP_ARGS(bdi, written), - - TP_STRUCT__entry( - __array(char, name, 32) - __field(int, written) - ), - - TP_fast_assign( - strncpy(__entry->name, dev_name(bdi->dev), 32); - __entry->written = written; - ), - - TP_printk("bdi %s written %d", - __entry->name, - __entry->written - ) -); DECLARE_EVENT_CLASS(wbc_class, TP_PROTO(struct writeback_control *wbc, struct backing_dev_info *bdi), diff --git a/mm/page-writeback.c b/mm/page-writeback.c index daff320d263f..f32f25092c66 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -250,50 +250,6 @@ static void bdi_writeout_fraction(struct backing_dev_info *bdi, numerator, denominator); } -static inline void task_dirties_fraction(struct task_struct *tsk, - long *numerator, long *denominator) -{ - prop_fraction_single(&vm_dirties, &tsk->dirties, - numerator, denominator); -} - -/* - * task_dirty_limit - scale down dirty throttling threshold for one task - * - * task specific dirty limit: - * - * dirty -= (dirty/8) * p_{t} - * - * To protect light/slow dirtying tasks from heavier/fast ones, we start - * throttling individual tasks before reaching the bdi dirty limit. - * Relatively low thresholds will be allocated to heavy dirtiers. So when - * dirty pages grow large, heavy dirtiers will be throttled first, which will - * effectively curb the growth of dirty pages. Light dirtiers with high enough - * dirty threshold may never get throttled. - */ -#define TASK_LIMIT_FRACTION 8 -static unsigned long task_dirty_limit(struct task_struct *tsk, - unsigned long bdi_dirty) -{ - long numerator, denominator; - unsigned long dirty = bdi_dirty; - u64 inv = dirty / TASK_LIMIT_FRACTION; - - task_dirties_fraction(tsk, &numerator, &denominator); - inv *= numerator; - do_div(inv, denominator); - - dirty -= inv; - - return max(dirty, bdi_dirty/2); -} - -/* Minimum limit for any task */ -static unsigned long task_min_dirty_limit(unsigned long bdi_dirty) -{ - return bdi_dirty - bdi_dirty / TASK_LIMIT_FRACTION; -} - /* * */ @@ -986,30 +942,36 @@ static unsigned long dirty_poll_interval(unsigned long dirty, /* * balance_dirty_pages() must be called by processes which are generating dirty * data. It looks at the number of dirty pages in the machine and will force - * the caller to perform writeback if the system is over `vm_dirty_ratio'. + * the caller to wait once crossing the (background_thresh + dirty_thresh) / 2. * If we're over `background_thresh' then the writeback threads are woken to * perform some writeout. */ static void balance_dirty_pages(struct address_space *mapping, - unsigned long write_chunk) + unsigned long pages_dirtied) { - unsigned long nr_reclaimable, bdi_nr_reclaimable; + unsigned long nr_reclaimable; /* = file_dirty + unstable_nfs */ + unsigned long bdi_reclaimable; unsigned long nr_dirty; /* = file_dirty + writeback + unstable_nfs */ unsigned long bdi_dirty; unsigned long freerun; unsigned long background_thresh; unsigned long dirty_thresh; unsigned long bdi_thresh; - unsigned long task_bdi_thresh; - unsigned long min_task_bdi_thresh; - unsigned long pages_written = 0; - unsigned long pause = 1; + long pause = 0; bool dirty_exceeded = false; - bool clear_dirty_exceeded = true; + unsigned long task_ratelimit; + unsigned long dirty_ratelimit; + unsigned long pos_ratio; struct backing_dev_info *bdi = mapping->backing_dev_info; unsigned long start_time = jiffies; for (;;) { + /* + * Unstable writes are a feature of certain networked + * filesystems (i.e. NFS) in which data may have been + * written to the server's write cache, but has not yet + * been flushed to permanent storage. + */ nr_reclaimable = global_page_state(NR_FILE_DIRTY) + global_page_state(NR_UNSTABLE_NFS); nr_dirty = nr_reclaimable + global_page_state(NR_WRITEBACK); @@ -1026,9 +988,23 @@ static void balance_dirty_pages(struct address_space *mapping, if (nr_dirty <= freerun) break; + if (unlikely(!writeback_in_progress(bdi))) + bdi_start_background_writeback(bdi); + + /* + * bdi_thresh is not treated as some limiting factor as + * dirty_thresh, due to reasons + * - in JBOD setup, bdi_thresh can fluctuate a lot + * - in a system with HDD and USB key, the USB key may somehow + * go into state (bdi_dirty >> bdi_thresh) either because + * bdi_dirty starts high, or because bdi_thresh drops low. + * In this case we don't want to hard throttle the USB key + * dirtiers for 100 seconds until bdi_dirty drops under + * bdi_thresh. Instead the auxiliary bdi control line in + * bdi_position_ratio() will let the dirtier task progress + * at some rate <= (write_bw / 2) for bringing down bdi_dirty. + */ bdi_thresh = bdi_dirty_limit(bdi, dirty_thresh); - min_task_bdi_thresh = task_min_dirty_limit(bdi_thresh); - task_bdi_thresh = task_dirty_limit(current, bdi_thresh); /* * In order to avoid the stacked BDI deadlock we need @@ -1040,57 +1016,41 @@ static void balance_dirty_pages(struct address_space *mapping, * actually dirty; with m+n sitting in the percpu * deltas. */ - if (task_bdi_thresh < 2 * bdi_stat_error(bdi)) { - bdi_nr_reclaimable = bdi_stat_sum(bdi, BDI_RECLAIMABLE); - bdi_dirty = bdi_nr_reclaimable + + if (bdi_thresh < 2 * bdi_stat_error(bdi)) { + bdi_reclaimable = bdi_stat_sum(bdi, BDI_RECLAIMABLE); + bdi_dirty = bdi_reclaimable + bdi_stat_sum(bdi, BDI_WRITEBACK); } else { - bdi_nr_reclaimable = bdi_stat(bdi, BDI_RECLAIMABLE); - bdi_dirty = bdi_nr_reclaimable + + bdi_reclaimable = bdi_stat(bdi, BDI_RECLAIMABLE); + bdi_dirty = bdi_reclaimable + bdi_stat(bdi, BDI_WRITEBACK); } - /* - * The bdi thresh is somehow "soft" limit derived from the - * global "hard" limit. The former helps to prevent heavy IO - * bdi or process from holding back light ones; The latter is - * the last resort safeguard. - */ - dirty_exceeded = (bdi_dirty > task_bdi_thresh) || + dirty_exceeded = (bdi_dirty > bdi_thresh) || (nr_dirty > dirty_thresh); - clear_dirty_exceeded = (bdi_dirty <= min_task_bdi_thresh) && - (nr_dirty <= dirty_thresh); - - if (!dirty_exceeded) - break; - - if (!bdi->dirty_exceeded) + if (dirty_exceeded && !bdi->dirty_exceeded) bdi->dirty_exceeded = 1; bdi_update_bandwidth(bdi, dirty_thresh, background_thresh, nr_dirty, bdi_thresh, bdi_dirty, start_time); - /* Note: nr_reclaimable denotes nr_dirty + nr_unstable. - * Unstable writes are a feature of certain networked - * filesystems (i.e. NFS) in which data may have been - * written to the server's write cache, but has not yet - * been flushed to permanent storage. - * Only move pages to writeback if this bdi is over its - * threshold otherwise wait until the disk writes catch - * up. - */ - trace_balance_dirty_start(bdi); - if (bdi_nr_reclaimable > task_bdi_thresh) { - pages_written += writeback_inodes_wb(&bdi->wb, - write_chunk); - trace_balance_dirty_written(bdi, pages_written); - if (pages_written >= write_chunk) - break; /* We've done our duty */ + dirty_ratelimit = bdi->dirty_ratelimit; + pos_ratio = bdi_position_ratio(bdi, dirty_thresh, + background_thresh, nr_dirty, + bdi_thresh, bdi_dirty); + if (unlikely(pos_ratio == 0)) { + pause = MAX_PAUSE; + goto pause; } + task_ratelimit = (u64)dirty_ratelimit * + pos_ratio >> RATELIMIT_CALC_SHIFT; + pause = (HZ * pages_dirtied) / (task_ratelimit | 1); + pause = min_t(long, pause, MAX_PAUSE); + +pause: __set_current_state(TASK_UNINTERRUPTIBLE); io_schedule_timeout(pause); - trace_balance_dirty_wait(bdi); dirty_thresh = hard_dirty_limit(dirty_thresh); /* @@ -1099,22 +1059,11 @@ static void balance_dirty_pages(struct address_space *mapping, * 200ms is typically more than enough to curb heavy dirtiers; * (b) the pause time limit makes the dirtiers more responsive. */ - if (nr_dirty < dirty_thresh && - bdi_dirty < (task_bdi_thresh + bdi_thresh) / 2 && - time_after(jiffies, start_time + MAX_PAUSE)) + if (nr_dirty < dirty_thresh) break; - - /* - * Increase the delay for each loop, up to our previous - * default of taking a 100ms nap. - */ - pause <<= 1; - if (pause > HZ / 10) - pause = HZ / 10; } - /* Clear dirty_exceeded flag only when no task can exceed the limit */ - if (clear_dirty_exceeded && bdi->dirty_exceeded) + if (!dirty_exceeded && bdi->dirty_exceeded) bdi->dirty_exceeded = 0; current->nr_dirtied = 0; @@ -1131,8 +1080,10 @@ static void balance_dirty_pages(struct address_space *mapping, * In normal mode, we start background writeout at the lower * background_thresh, to keep the amount of dirty memory low. */ - if ((laptop_mode && pages_written) || - (!laptop_mode && (nr_reclaimable > background_thresh))) + if (laptop_mode) + return; + + if (nr_reclaimable > background_thresh) bdi_start_background_writeback(bdi); } From c8462cc9de9e92264ec647903772f6036a99b286 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Sat, 11 Jun 2011 19:21:43 -0600 Subject: [PATCH 0282/1531] writeback: limit max dirty pause time Apply two policies to scale down the max pause time for 1) small number of concurrent dirtiers 2) small memory system (comparing to storage bandwidth) MAX_PAUSE=200ms may only be suitable for high end servers with lots of concurrent dirtiers, where the large pause time can reduce much overheads. Otherwise, smaller pause time is desirable whenever possible, so as to get good responsiveness and smooth user experiences. It's actually required for good disk utilization in the case when all the dirty pages can be synced to disk within MAX_PAUSE=200ms. Signed-off-by: Wu Fengguang --- mm/page-writeback.c | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/mm/page-writeback.c b/mm/page-writeback.c index f32f25092c66..cc351e6f9ed9 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -939,6 +939,43 @@ static unsigned long dirty_poll_interval(unsigned long dirty, return 1; } +static unsigned long bdi_max_pause(struct backing_dev_info *bdi, + unsigned long bdi_dirty) +{ + unsigned long bw = bdi->avg_write_bandwidth; + unsigned long hi = ilog2(bw); + unsigned long lo = ilog2(bdi->dirty_ratelimit); + unsigned long t; + + /* target for 20ms max pause on 1-dd case */ + t = HZ / 50; + + /* + * Scale up pause time for concurrent dirtiers in order to reduce CPU + * overheads. + * + * (N * 20ms) on 2^N concurrent tasks. + */ + if (hi > lo) + t += (hi - lo) * (20 * HZ) / 1024; + + /* + * Limit pause time for small memory systems. If sleeping for too long + * time, a small pool of dirty/writeback pages may go empty and disk go + * idle. + * + * 8 serves as the safety ratio. + */ + if (bdi_dirty) + t = min(t, bdi_dirty * HZ / (8 * bw + 1)); + + /* + * The pause time will be settled within range (max_pause/4, max_pause). + * Apply a minimal value of 4 to get a non-zero max_pause/4. + */ + return clamp_val(t, 4, MAX_PAUSE); +} + /* * balance_dirty_pages() must be called by processes which are generating dirty * data. It looks at the number of dirty pages in the machine and will force @@ -958,6 +995,7 @@ static void balance_dirty_pages(struct address_space *mapping, unsigned long dirty_thresh; unsigned long bdi_thresh; long pause = 0; + long max_pause; bool dirty_exceeded = false; unsigned long task_ratelimit; unsigned long dirty_ratelimit; @@ -1035,18 +1073,20 @@ static void balance_dirty_pages(struct address_space *mapping, nr_dirty, bdi_thresh, bdi_dirty, start_time); + max_pause = bdi_max_pause(bdi, bdi_dirty); + dirty_ratelimit = bdi->dirty_ratelimit; pos_ratio = bdi_position_ratio(bdi, dirty_thresh, background_thresh, nr_dirty, bdi_thresh, bdi_dirty); if (unlikely(pos_ratio == 0)) { - pause = MAX_PAUSE; + pause = max_pause; goto pause; } task_ratelimit = (u64)dirty_ratelimit * pos_ratio >> RATELIMIT_CALC_SHIFT; pause = (HZ * pages_dirtied) / (task_ratelimit | 1); - pause = min_t(long, pause, MAX_PAUSE); + pause = min(pause, max_pause); pause: __set_current_state(TASK_UNINTERRUPTIBLE); From 57fc978cfb61ed40a7bbfe5a569359159ba31abd Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Sat, 11 Jun 2011 19:32:32 -0600 Subject: [PATCH 0283/1531] writeback: control dirty pause time The dirty pause time shall ultimately be controlled by adjusting nr_dirtied_pause, since there is relationship pause = pages_dirtied / task_ratelimit Assuming pages_dirtied ~= nr_dirtied_pause task_ratelimit ~= dirty_ratelimit We get nr_dirtied_pause ~= dirty_ratelimit * desired_pause Here dirty_ratelimit is preferred over task_ratelimit because it's more stable. It's also important to limit possible large transitional errors: - bw is changing quickly - pages_dirtied << nr_dirtied_pause on entering dirty exceeded area - pages_dirtied >> nr_dirtied_pause on btrfs (to be improved by a separate fix, but still expect non-trivial errors) So we end up using the above formula inside clamp_val(). The best test case for this code is to run 100 "dd bs=4M" tasks on btrfs and check its pause time distribution. Signed-off-by: Wu Fengguang --- mm/page-writeback.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/mm/page-writeback.c b/mm/page-writeback.c index cc351e6f9ed9..6a8bb693b429 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -1086,6 +1086,10 @@ static void balance_dirty_pages(struct address_space *mapping, task_ratelimit = (u64)dirty_ratelimit * pos_ratio >> RATELIMIT_CALC_SHIFT; pause = (HZ * pages_dirtied) / (task_ratelimit | 1); + if (unlikely(pause <= 0)) { + pause = 1; /* avoid resetting nr_dirtied_pause below */ + break; + } pause = min(pause, max_pause); pause: @@ -1107,7 +1111,21 @@ static void balance_dirty_pages(struct address_space *mapping, bdi->dirty_exceeded = 0; current->nr_dirtied = 0; - current->nr_dirtied_pause = dirty_poll_interval(nr_dirty, dirty_thresh); + if (pause == 0) { /* in freerun area */ + current->nr_dirtied_pause = + dirty_poll_interval(nr_dirty, dirty_thresh); + } else if (pause <= max_pause / 4 && + pages_dirtied >= current->nr_dirtied_pause) { + current->nr_dirtied_pause = clamp_val( + dirty_ratelimit * (max_pause / 2) / HZ, + pages_dirtied + pages_dirtied / 8, + pages_dirtied * 4); + } else if (pause >= max_pause) { + current->nr_dirtied_pause = 1 | clamp_val( + dirty_ratelimit * (max_pause / 2) / HZ, + pages_dirtied / 4, + pages_dirtied - pages_dirtied / 8); + } if (writeback_in_progress(bdi)) return; From 8927f66c4ede9a18b4b58f7e6f9debca67065f6b Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Thu, 4 Aug 2011 22:16:46 -0600 Subject: [PATCH 0284/1531] writeback: dirty position control - bdi reserve area Keep a minimal pool of dirty pages for each bdi, so that the disk IO queues won't underrun. Also gently increase a small bdi_thresh to avoid it stuck in 0 for some light dirtied bdi. It's particularly useful for JBOD and small memory system. It may result in (pos_ratio > 1) at the setpoint and push the dirty pages high. This is more or less intended because the bdi is in the danger of IO queue underflow. Signed-off-by: Wu Fengguang --- mm/page-writeback.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 6a8bb693b429..325f753c80ed 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -599,6 +599,7 @@ static unsigned long bdi_position_ratio(struct backing_dev_info *bdi, */ if (unlikely(bdi_thresh > thresh)) bdi_thresh = thresh; + bdi_thresh = max(bdi_thresh, (limit - dirty) / 8); /* * scale global setpoint to bdi's: * bdi_setpoint = setpoint * bdi_thresh / thresh @@ -622,6 +623,20 @@ static unsigned long bdi_position_ratio(struct backing_dev_info *bdi, } else pos_ratio /= 4; + /* + * bdi reserve area, safeguard against dirty pool underrun and disk idle + * It may push the desired control point of global dirty pages higher + * than setpoint. + */ + x_intercept = bdi_thresh / 2; + if (bdi_dirty < x_intercept) { + if (bdi_dirty > x_intercept / 8) { + pos_ratio *= x_intercept; + do_div(pos_ratio, bdi_dirty); + } else + pos_ratio *= 8; + } + return pos_ratio; } From b00949aa2df9970a912bf060bc95e99da356881c Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Thu, 18 Nov 2010 14:38:33 -0600 Subject: [PATCH 0285/1531] writeback: per-bdi background threshold One thing puzzled me is that in JBOD case, the per-disk writeout performance is smaller than the corresponding single-disk case even when they have comparable bdi_thresh. Tracing shows find that in single disk case, bdi_writeback is always kept high while in JBOD case, it could drop low from time to time and correspondingly bdi_reclaimable could sometimes rush high. The fix is to watch bdi_reclaimable and kick background writeback as soon as it goes high. This resembles the global background threshold but in per-bdi manner. The trick is, as long as bdi_reclaimable does not go high, bdi_writeback naturally won't go low because bdi_reclaimable+bdi_writeback ~= bdi_thresh. With less fluctuated writeback pages, JBOD performance is observed to increase noticeably in various cases. vmstat:nr_written values before/after patch: 3.1.0-rc4-wo-underrun+ 3.1.0-rc4-bgthresh3+ ------------------------ ------------------------ 125596480 +25.9% 158179363 JBOD-10HDD-16G/ext4-100dd-1M-24p-16384M-20:10-X 61790815 +110.4% 130032231 JBOD-10HDD-16G/ext4-10dd-1M-24p-16384M-20:10-X 58853546 -0.1% 58823828 JBOD-10HDD-16G/ext4-1dd-1M-24p-16384M-20:10-X 110159811 +24.7% 137355377 JBOD-10HDD-16G/xfs-100dd-1M-24p-16384M-20:10-X 69544762 +10.8% 77080047 JBOD-10HDD-16G/xfs-10dd-1M-24p-16384M-20:10-X 50644862 +0.5% 50890006 JBOD-10HDD-16G/xfs-1dd-1M-24p-16384M-20:10-X 42677090 +28.0% 54643527 JBOD-10HDD-thresh=100M/ext4-100dd-1M-24p-16384M-100M:10-X 47491324 +13.3% 53785605 JBOD-10HDD-thresh=100M/ext4-10dd-1M-24p-16384M-100M:10-X 52548986 +0.9% 53001031 JBOD-10HDD-thresh=100M/ext4-1dd-1M-24p-16384M-100M:10-X 26783091 +36.8% 36650248 JBOD-10HDD-thresh=100M/xfs-100dd-1M-24p-16384M-100M:10-X 35526347 +14.0% 40492312 JBOD-10HDD-thresh=100M/xfs-10dd-1M-24p-16384M-100M:10-X 44670723 -1.1% 44177606 JBOD-10HDD-thresh=100M/xfs-1dd-1M-24p-16384M-100M:10-X 127996037 +22.4% 156719990 JBOD-10HDD-thresh=2G/ext4-100dd-1M-24p-16384M-2048M:10-X 57518856 +3.8% 59677625 JBOD-10HDD-thresh=2G/ext4-10dd-1M-24p-16384M-2048M:10-X 51919909 +12.2% 58269894 JBOD-10HDD-thresh=2G/ext4-1dd-1M-24p-16384M-2048M:10-X 86410514 +79.0% 154660433 JBOD-10HDD-thresh=2G/xfs-100dd-1M-24p-16384M-2048M:10-X 40132519 +38.6% 55617893 JBOD-10HDD-thresh=2G/xfs-10dd-1M-24p-16384M-2048M:10-X 48423248 +7.5% 52042927 JBOD-10HDD-thresh=2G/xfs-1dd-1M-24p-16384M-2048M:10-X 206041046 +44.1% 296846536 JBOD-10HDD-thresh=4G/xfs-100dd-1M-24p-16384M-4096M:10-X 72312903 -19.4% 58272885 JBOD-10HDD-thresh=4G/xfs-10dd-1M-24p-16384M-4096M:10-X 50635672 -0.5% 50384787 JBOD-10HDD-thresh=4G/xfs-1dd-1M-24p-16384M-4096M:10-X 68308534 +115.7% 147324758 JBOD-10HDD-thresh=800M/ext4-100dd-1M-24p-16384M-800M:10-X 57882933 +14.5% 66269621 JBOD-10HDD-thresh=800M/ext4-10dd-1M-24p-16384M-800M:10-X 52183472 +12.8% 58855181 JBOD-10HDD-thresh=800M/ext4-1dd-1M-24p-16384M-800M:10-X 53788956 +94.2% 104460352 JBOD-10HDD-thresh=800M/xfs-100dd-1M-24p-16384M-800M:10-X 44493342 +35.5% 60298210 JBOD-10HDD-thresh=800M/xfs-10dd-1M-24p-16384M-800M:10-X 42641209 +18.9% 50681038 JBOD-10HDD-thresh=800M/xfs-1dd-1M-24p-16384M-800M:10-X Signed-off-by: Wu Fengguang --- fs/fs-writeback.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 28076562ada0..6401cd76f109 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -658,14 +658,21 @@ long writeback_inodes_wb(struct bdi_writeback *wb, long nr_pages) return nr_pages - work.nr_pages; } -static inline bool over_bground_thresh(void) +static bool over_bground_thresh(struct backing_dev_info *bdi) { unsigned long background_thresh, dirty_thresh; global_dirty_limits(&background_thresh, &dirty_thresh); - return (global_page_state(NR_FILE_DIRTY) + - global_page_state(NR_UNSTABLE_NFS) > background_thresh); + if (global_page_state(NR_FILE_DIRTY) + + global_page_state(NR_UNSTABLE_NFS) > background_thresh) + return true; + + if (bdi_stat(bdi, BDI_RECLAIMABLE) > + bdi_dirty_limit(bdi, background_thresh)) + return true; + + return false; } /* @@ -727,7 +734,7 @@ static long wb_writeback(struct bdi_writeback *wb, * For background writeout, stop when we are below the * background dirty threshold */ - if (work->for_background && !over_bground_thresh()) + if (work->for_background && !over_bground_thresh(wb->bdi)) break; if (work->for_kupdate) { @@ -811,7 +818,7 @@ static unsigned long get_nr_dirty_pages(void) static long wb_check_background_flush(struct bdi_writeback *wb) { - if (over_bground_thresh()) { + if (over_bground_thresh(wb->bdi)) { struct wb_writeback_work work = { .nr_pages = LONG_MAX, From 6e01280f34be02e5e7635debb045aab3705bb5bf Mon Sep 17 00:00:00 2001 From: Inderpal Singh Date: Mon, 3 Oct 2011 08:51:20 +0900 Subject: [PATCH 0286/1531] ARM: EXYNOS4: Configure MAX8997 PMIC for ORIGEN Configure MAX8997 PMIC and provide platform specific data for ORIGEN board. Signed-off-by: Inderpal Singh Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos4/mach-origen.c | 396 +++++++++++++++++++++++++++- 1 file changed, 392 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-exynos4/mach-origen.c b/arch/arm/mach-exynos4/mach-origen.c index c8121fcf198d..3da77766961e 100644 --- a/arch/arm/mach-exynos4/mach-origen.c +++ b/arch/arm/mach-exynos4/mach-origen.c @@ -16,6 +16,9 @@ #include #include #include +#include +#include +#include #include #include @@ -78,6 +81,378 @@ static struct s3c2410_uartcfg origen_uartcfgs[] __initdata = { }, }; +static struct regulator_consumer_supply __initdata ldo3_consumer[] = { + REGULATOR_SUPPLY("vdd11", "s5p-mipi-csis.0"), /* MIPI */ +}; +static struct regulator_consumer_supply __initdata ldo6_consumer[] = { + REGULATOR_SUPPLY("vdd18", "s5p-mipi-csis.0"), /* MIPI */ +}; +static struct regulator_consumer_supply __initdata ldo7_consumer[] = { + REGULATOR_SUPPLY("avdd", "alc5625"), /* Realtek ALC5625 */ +}; +static struct regulator_consumer_supply __initdata ldo8_consumer[] = { + REGULATOR_SUPPLY("vdd", "s5p-adc"), /* ADC */ +}; +static struct regulator_consumer_supply __initdata ldo9_consumer[] = { + REGULATOR_SUPPLY("dvdd", "swb-a31"), /* AR6003 WLAN & CSR 8810 BT */ +}; +static struct regulator_consumer_supply __initdata ldo11_consumer[] = { + REGULATOR_SUPPLY("dvdd", "alc5625"), /* Realtek ALC5625 */ +}; +static struct regulator_consumer_supply __initdata ldo14_consumer[] = { + REGULATOR_SUPPLY("avdd18", "swb-a31"), /* AR6003 WLAN & CSR 8810 BT */ +}; +static struct regulator_consumer_supply __initdata ldo17_consumer[] = { + REGULATOR_SUPPLY("vdd33", "swb-a31"), /* AR6003 WLAN & CSR 8810 BT */ +}; +static struct regulator_consumer_supply __initdata buck1_consumer[] = { + REGULATOR_SUPPLY("vdd_arm", NULL), /* CPUFREQ */ +}; +static struct regulator_consumer_supply __initdata buck2_consumer[] = { + REGULATOR_SUPPLY("vdd_int", NULL), /* CPUFREQ */ +}; +static struct regulator_consumer_supply __initdata buck3_consumer[] = { + REGULATOR_SUPPLY("vdd_g3d", "mali_drm"), /* G3D */ +}; +static struct regulator_consumer_supply __initdata buck7_consumer[] = { + REGULATOR_SUPPLY("vcc", "platform-lcd"), /* LCD */ +}; + +static struct regulator_init_data __initdata max8997_ldo1_data = { + .constraints = { + .name = "VDD_ABB_3.3V", + .min_uV = 3300000, + .max_uV = 3300000, + .apply_uV = 1, + .state_mem = { + .disabled = 1, + }, + }, +}; + +static struct regulator_init_data __initdata max8997_ldo2_data = { + .constraints = { + .name = "VDD_ALIVE_1.1V", + .min_uV = 1100000, + .max_uV = 1100000, + .apply_uV = 1, + .always_on = 1, + .state_mem = { + .enabled = 1, + }, + }, +}; + +static struct regulator_init_data __initdata max8997_ldo3_data = { + .constraints = { + .name = "VMIPI_1.1V", + .min_uV = 1100000, + .max_uV = 1100000, + .apply_uV = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .disabled = 1, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(ldo3_consumer), + .consumer_supplies = ldo3_consumer, +}; + +static struct regulator_init_data __initdata max8997_ldo4_data = { + .constraints = { + .name = "VDD_RTC_1.8V", + .min_uV = 1800000, + .max_uV = 1800000, + .apply_uV = 1, + .always_on = 1, + .state_mem = { + .disabled = 1, + }, + }, +}; + +static struct regulator_init_data __initdata max8997_ldo6_data = { + .constraints = { + .name = "VMIPI_1.8V", + .min_uV = 1800000, + .max_uV = 1800000, + .apply_uV = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .disabled = 1, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(ldo6_consumer), + .consumer_supplies = ldo6_consumer, +}; + +static struct regulator_init_data __initdata max8997_ldo7_data = { + .constraints = { + .name = "VDD_AUD_1.8V", + .min_uV = 1800000, + .max_uV = 1800000, + .apply_uV = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .disabled = 1, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(ldo7_consumer), + .consumer_supplies = ldo7_consumer, +}; + +static struct regulator_init_data __initdata max8997_ldo8_data = { + .constraints = { + .name = "VADC_3.3V", + .min_uV = 3300000, + .max_uV = 3300000, + .apply_uV = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .disabled = 1, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(ldo8_consumer), + .consumer_supplies = ldo8_consumer, +}; + +static struct regulator_init_data __initdata max8997_ldo9_data = { + .constraints = { + .name = "DVDD_SWB_2.8V", + .min_uV = 2800000, + .max_uV = 2800000, + .apply_uV = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .disabled = 1, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(ldo9_consumer), + .consumer_supplies = ldo9_consumer, +}; + +static struct regulator_init_data __initdata max8997_ldo10_data = { + .constraints = { + .name = "VDD_PLL_1.1V", + .min_uV = 1100000, + .max_uV = 1100000, + .apply_uV = 1, + .always_on = 1, + .state_mem = { + .disabled = 1, + }, + }, +}; + +static struct regulator_init_data __initdata max8997_ldo11_data = { + .constraints = { + .name = "VDD_AUD_3V", + .min_uV = 3000000, + .max_uV = 3000000, + .apply_uV = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .disabled = 1, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(ldo11_consumer), + .consumer_supplies = ldo11_consumer, +}; + +static struct regulator_init_data __initdata max8997_ldo14_data = { + .constraints = { + .name = "AVDD18_SWB_1.8V", + .min_uV = 1800000, + .max_uV = 1800000, + .apply_uV = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .disabled = 1, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(ldo14_consumer), + .consumer_supplies = ldo14_consumer, +}; + +static struct regulator_init_data __initdata max8997_ldo17_data = { + .constraints = { + .name = "VDD_SWB_3.3V", + .min_uV = 3300000, + .max_uV = 3300000, + .apply_uV = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .disabled = 1, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(ldo17_consumer), + .consumer_supplies = ldo17_consumer, +}; + +static struct regulator_init_data __initdata max8997_ldo21_data = { + .constraints = { + .name = "VDD_MIF_1.2V", + .min_uV = 1200000, + .max_uV = 1200000, + .apply_uV = 1, + .always_on = 1, + .state_mem = { + .disabled = 1, + }, + }, +}; + +static struct regulator_init_data __initdata max8997_buck1_data = { + .constraints = { + .name = "VDD_ARM_1.2V", + .min_uV = 950000, + .max_uV = 1350000, + .always_on = 1, + .boot_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + .state_mem = { + .disabled = 1, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(buck1_consumer), + .consumer_supplies = buck1_consumer, +}; + +static struct regulator_init_data __initdata max8997_buck2_data = { + .constraints = { + .name = "VDD_INT_1.1V", + .min_uV = 900000, + .max_uV = 1100000, + .always_on = 1, + .boot_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + .state_mem = { + .disabled = 1, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(buck2_consumer), + .consumer_supplies = buck2_consumer, +}; + +static struct regulator_init_data __initdata max8997_buck3_data = { + .constraints = { + .name = "VDD_G3D_1.1V", + .min_uV = 900000, + .max_uV = 1100000, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | + REGULATOR_CHANGE_STATUS, + .state_mem = { + .disabled = 1, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(buck3_consumer), + .consumer_supplies = buck3_consumer, +}; + +static struct regulator_init_data __initdata max8997_buck5_data = { + .constraints = { + .name = "VDDQ_M1M2_1.2V", + .min_uV = 1200000, + .max_uV = 1200000, + .apply_uV = 1, + .always_on = 1, + .state_mem = { + .disabled = 1, + }, + }, +}; + +static struct regulator_init_data __initdata max8997_buck7_data = { + .constraints = { + .name = "VDD_LCD_3.3V", + .min_uV = 3300000, + .max_uV = 3300000, + .boot_on = 1, + .apply_uV = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .disabled = 1 + }, + }, + .num_consumer_supplies = ARRAY_SIZE(buck7_consumer), + .consumer_supplies = buck7_consumer, +}; + +static struct max8997_regulator_data __initdata origen_max8997_regulators[] = { + { MAX8997_LDO1, &max8997_ldo1_data }, + { MAX8997_LDO2, &max8997_ldo2_data }, + { MAX8997_LDO3, &max8997_ldo3_data }, + { MAX8997_LDO4, &max8997_ldo4_data }, + { MAX8997_LDO6, &max8997_ldo6_data }, + { MAX8997_LDO7, &max8997_ldo7_data }, + { MAX8997_LDO8, &max8997_ldo8_data }, + { MAX8997_LDO9, &max8997_ldo9_data }, + { MAX8997_LDO10, &max8997_ldo10_data }, + { MAX8997_LDO11, &max8997_ldo11_data }, + { MAX8997_LDO14, &max8997_ldo14_data }, + { MAX8997_LDO17, &max8997_ldo17_data }, + { MAX8997_LDO21, &max8997_ldo21_data }, + { MAX8997_BUCK1, &max8997_buck1_data }, + { MAX8997_BUCK2, &max8997_buck2_data }, + { MAX8997_BUCK3, &max8997_buck3_data }, + { MAX8997_BUCK5, &max8997_buck5_data }, + { MAX8997_BUCK7, &max8997_buck7_data }, +}; + +struct max8997_platform_data __initdata origen_max8997_pdata = { + .num_regulators = ARRAY_SIZE(origen_max8997_regulators), + .regulators = origen_max8997_regulators, + + .wakeup = true, + .buck1_gpiodvs = false, + .buck2_gpiodvs = false, + .buck5_gpiodvs = false, + .irq_base = IRQ_GPIO_END + 1, + + .ignore_gpiodvs_side_effect = true, + .buck125_default_idx = 0x0, + + .buck125_gpios[0] = EXYNOS4_GPX0(0), + .buck125_gpios[1] = EXYNOS4_GPX0(1), + .buck125_gpios[2] = EXYNOS4_GPX0(2), + + .buck1_voltage[0] = 1350000, + .buck1_voltage[1] = 1300000, + .buck1_voltage[2] = 1250000, + .buck1_voltage[3] = 1200000, + .buck1_voltage[4] = 1150000, + .buck1_voltage[5] = 1100000, + .buck1_voltage[6] = 1000000, + .buck1_voltage[7] = 950000, + + .buck2_voltage[0] = 1100000, + .buck2_voltage[1] = 1100000, + .buck2_voltage[2] = 1100000, + .buck2_voltage[3] = 1100000, + .buck2_voltage[4] = 1000000, + .buck2_voltage[5] = 1000000, + .buck2_voltage[6] = 1000000, + .buck2_voltage[7] = 1000000, + + .buck5_voltage[0] = 1200000, + .buck5_voltage[1] = 1200000, + .buck5_voltage[2] = 1200000, + .buck5_voltage[3] = 1200000, + .buck5_voltage[4] = 1200000, + .buck5_voltage[5] = 1200000, + .buck5_voltage[6] = 1200000, + .buck5_voltage[7] = 1200000, +}; + +/* I2C0 */ +static struct i2c_board_info i2c0_devs[] __initdata = { + { + I2C_BOARD_INFO("max8997", (0xCC >> 1)), + .platform_data = &origen_max8997_pdata, + .irq = IRQ_EINT(4), + }, +}; + static struct s3c_sdhci_platdata origen_hsmmc0_pdata __initdata = { .cd_type = S3C_SDHCI_CD_INTERNAL, .clk_type = S3C_SDHCI_CLK_DIV_EXTERNAL, @@ -155,6 +530,7 @@ static struct platform_device origen_device_gpiokeys = { }; static struct platform_device *origen_devices[] __initdata = { + &s3c_device_i2c0, &s3c_device_hsmmc2, &s3c_device_hsmmc0, &s3c_device_rtc, @@ -172,13 +548,13 @@ static struct platform_device *origen_devices[] __initdata = { /* LCD Backlight data */ static struct samsung_bl_gpio_info origen_bl_gpio_info = { - .no = EXYNOS4_GPD0(0), - .func = S3C_GPIO_SFN(2), + .no = EXYNOS4_GPD0(0), + .func = S3C_GPIO_SFN(2), }; static struct platform_pwm_backlight_data origen_bl_data = { - .pwm_id = 0, - .pwm_period_ns = 1000, + .pwm_id = 0, + .pwm_period_ns = 1000, }; static void __init origen_map_io(void) @@ -188,8 +564,20 @@ static void __init origen_map_io(void) s3c24xx_init_uarts(origen_uartcfgs, ARRAY_SIZE(origen_uartcfgs)); } +static void __init origen_power_init(void) +{ + gpio_request(EXYNOS4_GPX0(4), "PMIC_IRQ"); + s3c_gpio_cfgpin(EXYNOS4_GPX0(4), S3C_GPIO_SFN(0xf)); + s3c_gpio_setpull(EXYNOS4_GPX0(4), S3C_GPIO_PULL_NONE); +} + static void __init origen_machine_init(void) { + origen_power_init(); + + s3c_i2c0_set_platdata(NULL); + i2c_register_board_info(0, i2c0_devs, ARRAY_SIZE(i2c0_devs)); + /* * Since sdhci instance 2 can contain a bootable media, * sdhci instance 0 is registered after instance 2. From 9421a76d2bfec62b89b395033d03eea44bb2a4ac Mon Sep 17 00:00:00 2001 From: Tushar Behera Date: Mon, 3 Oct 2011 09:12:56 +0900 Subject: [PATCH 0287/1531] ARM: EXYNOS4: Add machine support for 7" LCD on ORIGEN ORIGEN board is fitted with 7" LCD panel HV070WSA. The pixel resolution of the LCD panel is 1024x600. Also power domain device for LCD0 is registered. Signed-off-by: Tushar Behera Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos4/Kconfig | 5 ++- arch/arm/mach-exynos4/mach-origen.c | 63 ++++++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-exynos4/Kconfig b/arch/arm/mach-exynos4/Kconfig index 257dbaaf149b..6fc17228a636 100644 --- a/arch/arm/mach-exynos4/Kconfig +++ b/arch/arm/mach-exynos4/Kconfig @@ -229,11 +229,14 @@ config MACH_ORIGEN select S5P_DEV_FIMC1 select S5P_DEV_FIMC2 select S5P_DEV_FIMC3 + select S5P_DEV_FIMD0 select S5P_DEV_I2C_HDMIPHY - select S5P_DEV_USB_EHCI select S5P_DEV_TV + select S5P_DEV_USB_EHCI + select EXYNOS4_DEV_PD select SAMSUNG_DEV_BACKLIGHT select SAMSUNG_DEV_PWM + select EXYNOS4_SETUP_FIMD0 select EXYNOS4_SETUP_SDHCI select EXYNOS4_SETUP_USB_PHY help diff --git a/arch/arm/mach-exynos4/mach-origen.c b/arch/arm/mach-exynos4/mach-origen.c index 3da77766961e..349e2977b2ed 100644 --- a/arch/arm/mach-exynos4/mach-origen.c +++ b/arch/arm/mach-exynos4/mach-origen.c @@ -19,11 +19,15 @@ #include #include #include +#include #include #include +#include