diff options
468 files changed, 16060 insertions, 9245 deletions
diff --git a/Documentation/00-INDEX b/Documentation/00-INDEX index 40ac7759c3bb..d273b557a934 100644 --- a/Documentation/00-INDEX +++ b/Documentation/00-INDEX @@ -126,18 +126,16 @@ devices.txt - plain ASCII listing of all the nodes in /dev/ with major minor #'s. digiepca.txt - info on Digi Intl. {PC,PCI,EISA}Xx and Xem series cards. -dnotify.txt - - info about directory notification in Linux. dontdiff - file containing a list of files that should never be diff'ed. driver-model/ - directory with info about Linux driver model. -drivers/ - - directory with driver documentation (currently only EDAC). dvb/ - info on Linux Digital Video Broadcast (DVB) subsystem. early-userspace/ - info about initramfs, klibc, and userspace early during boot. +edac.txt + - information on EDAC - Error Detection And Correction eisa.txt - info on EISA bus support. exception.txt @@ -334,20 +332,8 @@ rtc.txt - notes on how to use the Real Time Clock (aka CMOS clock) driver. s390/ - directory with info on using Linux on the IBM S390. -sched-arch.txt - - CPU Scheduler implementation hints for architecture specific code. -sched-coding.txt - - reference for various scheduler-related methods in the O(1) scheduler. -sched-design.txt - - goals, design and implementation of the Linux O(1) scheduler. -sched-design-CFS.txt - - goals, design and implementation of the Complete Fair Scheduler. -sched-domains.txt - - information on scheduling domains. -sched-nice-design.txt - - How and why the scheduler's nice levels are implemented. -sched-stats.txt - - information on schedstats (Linux Scheduler Statistics). +scheduler/ + - directory with info on the scheduler. scsi/ - directory with info on Linux scsi support. serial/ @@ -360,8 +346,6 @@ sgi-visws.txt - short blurb on the SGI Visual Workstations. sh/ - directory with info on porting Linux to a new architecture. -sharedsubtree.txt - - a description of shared subtrees for namespaces. smart-config.txt - description of the Smart Config makefile feature. sony-laptop.txt diff --git a/Documentation/ABI/testing/sysfs-kernel-uids b/Documentation/ABI/testing/sysfs-kernel-uids index 648d65dbc0e7..28f14695a852 100644 --- a/Documentation/ABI/testing/sysfs-kernel-uids +++ b/Documentation/ABI/testing/sysfs-kernel-uids @@ -11,4 +11,4 @@ Description: example would be, if User A has shares = 1024 and user B has shares = 2048, User B will get twice the CPU bandwidth user A will. For more details refer - Documentation/sched-design-CFS.txt + Documentation/scheduler/sched-design-CFS.txt diff --git a/Documentation/DocBook/genericirq.tmpl b/Documentation/DocBook/genericirq.tmpl index 4215f69ce7e6..3a882d9a90a9 100644 --- a/Documentation/DocBook/genericirq.tmpl +++ b/Documentation/DocBook/genericirq.tmpl @@ -172,7 +172,7 @@ <listitem><para>Chiplevel hardware encapsulation</para></listitem> </orderedlist> </para> - <sect1> + <sect1 id="Interrupt_control_flow"> <title>Interrupt control flow</title> <para> Each interrupt is described by an interrupt descriptor structure @@ -190,7 +190,7 @@ referenced by the assigned chip descriptor structure. </para> </sect1> - <sect1> + <sect1 id="Highlevel_Driver_API"> <title>Highlevel Driver API</title> <para> The highlevel Driver API consists of following functions: @@ -210,7 +210,7 @@ See the autogenerated function documentation for details. </para> </sect1> - <sect1> + <sect1 id="Highlevel_IRQ_flow_handlers"> <title>Highlevel IRQ flow handlers</title> <para> The generic layer provides a set of pre-defined irq-flow methods: @@ -224,9 +224,9 @@ specific) are assigned to specific interrupts by the architecture either during bootup or during device initialization. </para> - <sect2> + <sect2 id="Default_flow_implementations"> <title>Default flow implementations</title> - <sect3> + <sect3 id="Helper_functions"> <title>Helper functions</title> <para> The helper functions call the chip primitives and @@ -267,9 +267,9 @@ noop(irq) </para> </sect3> </sect2> - <sect2> + <sect2 id="Default_flow_handler_implementations"> <title>Default flow handler implementations</title> - <sect3> + <sect3 id="Default_Level_IRQ_flow_handler"> <title>Default Level IRQ flow handler</title> <para> handle_level_irq provides a generic implementation @@ -284,7 +284,7 @@ desc->chip->end(); </programlisting> </para> </sect3> - <sect3> + <sect3 id="Default_Edge_IRQ_flow_handler"> <title>Default Edge IRQ flow handler</title> <para> handle_edge_irq provides a generic implementation @@ -311,7 +311,7 @@ desc->chip->end(); </programlisting> </para> </sect3> - <sect3> + <sect3 id="Default_simple_IRQ_flow_handler"> <title>Default simple IRQ flow handler</title> <para> handle_simple_irq provides a generic implementation @@ -328,7 +328,7 @@ handle_IRQ_event(desc->action); </programlisting> </para> </sect3> - <sect3> + <sect3 id="Default_per_CPU_flow_handler"> <title>Default per CPU flow handler</title> <para> handle_percpu_irq provides a generic implementation @@ -349,7 +349,7 @@ desc->chip->end(); </para> </sect3> </sect2> - <sect2> + <sect2 id="Quirks_and_optimizations"> <title>Quirks and optimizations</title> <para> The generic functions are intended for 'clean' architectures and chips, @@ -358,7 +358,7 @@ desc->chip->end(); overriding the highlevel irq-flow handler. </para> </sect2> - <sect2> + <sect2 id="Delayed_interrupt_disable"> <title>Delayed interrupt disable</title> <para> This per interrupt selectable feature, which was introduced by Russell @@ -380,7 +380,7 @@ desc->chip->end(); </para> </sect2> </sect1> - <sect1> + <sect1 id="Chiplevel_hardware_encapsulation"> <title>Chiplevel hardware encapsulation</title> <para> The chip level hardware descriptor structure irq_chip diff --git a/Documentation/DocBook/lsm.tmpl b/Documentation/DocBook/lsm.tmpl index f63822195871..fe7664ce9667 100644 --- a/Documentation/DocBook/lsm.tmpl +++ b/Documentation/DocBook/lsm.tmpl @@ -33,7 +33,7 @@ </authorgroup> </articleinfo> -<sect1><title>Introduction</title> +<sect1 id="Introduction"><title>Introduction</title> <para> In March 2001, the National Security Agency (NSA) gave a presentation diff --git a/Documentation/DocBook/mtdnand.tmpl b/Documentation/DocBook/mtdnand.tmpl index 957cf5c26831..8e145857fc9d 100644 --- a/Documentation/DocBook/mtdnand.tmpl +++ b/Documentation/DocBook/mtdnand.tmpl @@ -80,7 +80,7 @@ struct member has a short description which is marked with an [XXX] identifier. The following chapters explain the meaning of those identifiers. </para> - <sect1> + <sect1 id="Function_identifiers_XXX"> <title>Function identifiers [XXX]</title> <para> The functions are marked with [XXX] identifiers in the short @@ -115,7 +115,7 @@ </para></listitem> </itemizedlist> </sect1> - <sect1> + <sect1 id="Struct_member_identifiers_XXX"> <title>Struct member identifiers [XXX]</title> <para> The struct members are marked with [XXX] identifiers in the @@ -159,7 +159,7 @@ basic functions and fill out some really board dependent members in the nand chip description structure. </para> - <sect1> + <sect1 id="Basic_defines"> <title>Basic defines</title> <para> At least you have to provide a mtd structure and @@ -185,7 +185,7 @@ static struct nand_chip board_chip; static unsigned long baseaddr; </programlisting> </sect1> - <sect1> + <sect1 id="Partition_defines"> <title>Partition defines</title> <para> If you want to divide your device into partitions, then @@ -204,7 +204,7 @@ static struct mtd_partition partition_info[] = { }; </programlisting> </sect1> - <sect1> + <sect1 id="Hardware_control_functions"> <title>Hardware control function</title> <para> The hardware control function provides access to the @@ -246,7 +246,7 @@ static void board_hwcontrol(struct mtd_info *mtd, int cmd) } </programlisting> </sect1> - <sect1> + <sect1 id="Device_ready_function"> <title>Device ready function</title> <para> If the hardware interface has the ready busy pin of the NAND chip connected to a @@ -257,7 +257,7 @@ static void board_hwcontrol(struct mtd_info *mtd, int cmd) the function must not be defined and the function pointer this->dev_ready is set to NULL. </para> </sect1> - <sect1> + <sect1 id="Init_function"> <title>Init function</title> <para> The init function allocates memory and sets up all the board @@ -325,7 +325,7 @@ out: module_init(board_init); </programlisting> </sect1> - <sect1> + <sect1 id="Exit_function"> <title>Exit function</title> <para> The exit function is only neccecary if the driver is @@ -359,7 +359,7 @@ module_exit(board_cleanup); driver. For a list of functions which can be overridden by the board driver see the documentation of the nand_chip structure. </para> - <sect1> + <sect1 id="Multiple_chip_control"> <title>Multiple chip control</title> <para> The nand driver can control chip arrays. Therefor the @@ -419,9 +419,9 @@ static void board_select_chip (struct mtd_info *mtd, int chip) } </programlisting> </sect1> - <sect1> + <sect1 id="Hardware_ECC_support"> <title>Hardware ECC support</title> - <sect2> + <sect2 id="Functions_and_constants"> <title>Functions and constants</title> <para> The nand driver supports three different types of @@ -475,7 +475,7 @@ static void board_select_chip (struct mtd_info *mtd, int chip) </itemizedlist> </para> </sect2> - <sect2> + <sect2 id="Hardware_ECC_with_syndrome_calculation"> <title>Hardware ECC with syndrome calculation</title> <para> Many hardware ECC implementations provide Reed-Solomon @@ -500,7 +500,7 @@ static void board_select_chip (struct mtd_info *mtd, int chip) </para> </sect2> </sect1> - <sect1> + <sect1 id="Bad_Block_table_support"> <title>Bad block table support</title> <para> Most NAND chips mark the bad blocks at a defined @@ -552,7 +552,7 @@ static void board_select_chip (struct mtd_info *mtd, int chip) allows faster access than always checking the bad block information on the flash chip itself. </para> - <sect2> + <sect2 id="Flash_based_tables"> <title>Flash based tables</title> <para> It may be desired or neccecary to keep a bad block table in FLASH. @@ -587,7 +587,7 @@ static void board_select_chip (struct mtd_info *mtd, int chip) </itemizedlist> </para> </sect2> - <sect2> + <sect2 id="User_defined_tables"> <title>User defined tables</title> <para> User defined tables are created by filling out a @@ -676,7 +676,7 @@ static void board_select_chip (struct mtd_info *mtd, int chip) </para> </sect2> </sect1> - <sect1> + <sect1 id="Spare_area_placement"> <title>Spare area (auto)placement</title> <para> The nand driver implements different possibilities for @@ -730,7 +730,7 @@ struct nand_oobinfo { </para></listitem> </itemizedlist> </para> - <sect2> + <sect2 id="Placement_defined_by_fs_driver"> <title>Placement defined by fs driver</title> <para> The calling function provides a pointer to a nand_oobinfo @@ -760,7 +760,7 @@ struct nand_oobinfo { done according to the given scheme in the nand_oobinfo structure. </para> </sect2> - <sect2> + <sect2 id="Automatic_placement"> <title>Automatic placement</title> <para> Automatic placement uses the built in defaults to place the @@ -774,7 +774,7 @@ struct nand_oobinfo { done according to the default builtin scheme. </para> </sect2> - <sect2> + <sect2 id="User_space_placement_selection"> <title>User space placement selection</title> <para> All non ecc functions like mtd->read and mtd->write use an internal @@ -789,9 +789,9 @@ struct nand_oobinfo { </para> </sect2> </sect1> - <sect1> + <sect1 id="Spare_area_autoplacement_default"> <title>Spare area autoplacement default schemes</title> - <sect2> + <sect2 id="pagesize_256"> <title>256 byte pagesize</title> <informaltable><tgroup cols="3"><tbody> <row> @@ -843,7 +843,7 @@ pages this byte is reserved</entry> </row> </tbody></tgroup></informaltable> </sect2> - <sect2> + <sect2 id="pagesize_512"> <title>512 byte pagesize</title> <informaltable><tgroup cols="3"><tbody> <row> @@ -906,7 +906,7 @@ in this page</entry> </row> </tbody></tgroup></informaltable> </sect2> - <sect2> + <sect2 id="pagesize_2048"> <title>2048 byte pagesize</title> <informaltable><tgroup cols="3"><tbody> <row> @@ -1126,9 +1126,9 @@ in this page</entry> <para> This chapter describes the constants which might be relevant for a driver developer. </para> - <sect1> + <sect1 id="Chip_option_constants"> <title>Chip option constants</title> - <sect2> + <sect2 id="Constants_for_chip_id_table"> <title>Constants for chip id table</title> <para> These constants are defined in nand.h. They are ored together to describe @@ -1153,7 +1153,7 @@ in this page</entry> </programlisting> </para> </sect2> - <sect2> + <sect2 id="Constants_for_runtime_options"> <title>Constants for runtime options</title> <para> These constants are defined in nand.h. They are ored together to describe @@ -1171,7 +1171,7 @@ in this page</entry> </sect2> </sect1> - <sect1> + <sect1 id="EEC_selection_constants"> <title>ECC selection constants</title> <para> Use these constants to select the ECC algorithm. @@ -1192,7 +1192,7 @@ in this page</entry> </para> </sect1> - <sect1> + <sect1 id="Hardware_control_related_constants"> <title>Hardware control related constants</title> <para> These constants describe the requested hardware access function when @@ -1218,7 +1218,7 @@ in this page</entry> </para> </sect1> - <sect1> + <sect1 id="Bad_block_table_constants"> <title>Bad block table related constants</title> <para> These constants describe the options used for bad block diff --git a/Documentation/DocBook/procfs-guide.tmpl b/Documentation/DocBook/procfs-guide.tmpl index 2de84dc195a8..1fd6a1ec7591 100644 --- a/Documentation/DocBook/procfs-guide.tmpl +++ b/Documentation/DocBook/procfs-guide.tmpl @@ -85,7 +85,7 @@ - <preface> + <preface id="Preface"> <title>Preface</title> <para> @@ -230,7 +230,7 @@ - <sect1> + <sect1 id="Creating_a_symlink"> <title>Creating a symlink</title> <funcsynopsis> @@ -254,7 +254,7 @@ </para> </sect1> - <sect1> + <sect1 id="Creating_a_directory"> <title>Creating a directory</title> <funcsynopsis> @@ -274,7 +274,7 @@ - <sect1> + <sect1 id="Removing_an_entry"> <title>Removing an entry</title> <funcsynopsis> @@ -340,7 +340,7 @@ entry->write_proc = write_proc_foo; - <sect1> + <sect1 id="Reading_data"> <title>Reading data</title> <para> @@ -448,7 +448,7 @@ entry->write_proc = write_proc_foo; - <sect1> + <sect1 id="Writing_data"> <title>Writing data</title> <para> @@ -579,7 +579,7 @@ int foo_read_func(char *page, char **start, off_t off, - <sect1> + <sect1 id="Modules"> <title>Modules</title> <para> @@ -599,7 +599,7 @@ entry->owner = THIS_MODULE; - <sect1> + <sect1 id="Mode_and_ownership"> <title>Mode and ownership</title> <para> diff --git a/Documentation/DocBook/rapidio.tmpl b/Documentation/DocBook/rapidio.tmpl index a8b88c47e809..b9e143e28c64 100644 --- a/Documentation/DocBook/rapidio.tmpl +++ b/Documentation/DocBook/rapidio.tmpl @@ -77,11 +77,11 @@ <chapter id="bugs"> <title>Known Bugs and Limitations</title> - <sect1> + <sect1 id="known_bugs"> <title>Bugs</title> <para>None. ;)</para> </sect1> - <sect1> + <sect1 id="Limitations"> <title>Limitations</title> <para> <orderedlist> @@ -100,7 +100,7 @@ on devices, request/map memory region resources, and manage mailboxes/doorbells. </para> - <sect1> + <sect1 id="Functions"> <title>Functions</title> !Iinclude/linux/rio_drv.h !Edrivers/rapidio/rio-driver.c @@ -116,23 +116,23 @@ subsystem. </para> - <sect1><title>Structures</title> + <sect1 id="Structures"><title>Structures</title> !Iinclude/linux/rio.h </sect1> - <sect1><title>Enumeration and Discovery</title> + <sect1 id="Enumeration_and_Discovery"><title>Enumeration and Discovery</title> !Idrivers/rapidio/rio-scan.c </sect1> - <sect1><title>Driver functionality</title> + <sect1 id="Driver_functionality"><title>Driver functionality</title> !Idrivers/rapidio/rio.c !Idrivers/rapidio/rio-access.c </sect1> - <sect1><title>Device model support</title> + <sect1 id="Device_model_support"><title>Device model support</title> !Idrivers/rapidio/rio-driver.c </sect1> - <sect1><title>Sysfs support</title> + <sect1 id="Sysfs_support"><title>Sysfs support</title> !Idrivers/rapidio/rio-sysfs.c </sect1> - <sect1><title>PPC32 support</title> + <sect1 id="PPC32_support"><title>PPC32 support</title> !Iarch/powerpc/kernel/rio.c !Earch/powerpc/sysdev/fsl_rio.c !Iarch/powerpc/sysdev/fsl_rio.c diff --git a/Documentation/DocBook/videobook.tmpl b/Documentation/DocBook/videobook.tmpl index b3d93ee27693..89817795e668 100644 --- a/Documentation/DocBook/videobook.tmpl +++ b/Documentation/DocBook/videobook.tmpl @@ -170,7 +170,7 @@ int __init myradio_init(struct video_init *v) <para> The types available are </para> - <table frame="all"><title>Device Types</title> + <table frame="all" id="Device_Types"><title>Device Types</title> <tgroup cols="3" align="left"> <tbody> <row> @@ -291,7 +291,7 @@ static int radio_ioctl(struct video_device *dev, unsigned int cmd, void *arg) allows the applications to find out what sort of a card they have found and to figure out what they want to do about it. The fields in the structure are </para> - <table frame="all"><title>struct video_capability fields</title> + <table frame="all" id="video_capability_fields"><title>struct video_capability fields</title> <tgroup cols="2" align="left"> <tbody> <row> @@ -365,7 +365,7 @@ static int radio_ioctl(struct video_device *dev, unsigned int cmd, void *arg) <para> The video_tuner structure has the following fields </para> - <table frame="all"><title>struct video_tuner fields</title> + <table frame="all" id="video_tuner_fields"><title>struct video_tuner fields</title> <tgroup cols="2" align="left"> <tbody> <row> @@ -398,7 +398,7 @@ static int radio_ioctl(struct video_device *dev, unsigned int cmd, void *arg) </tgroup> </table> - <table frame="all"><title>struct video_tuner flags</title> + <table frame="all" id="video_tuner_flags"><title>struct video_tuner flags</title> <tgroup cols="2" align="left"> <tbody> <row> @@ -421,7 +421,7 @@ static int radio_ioctl(struct video_device *dev, unsigned int cmd, void *arg) </tgroup> </table> - <table frame="all"><title>struct video_tuner modes</title> + <table frame="all" id="video_tuner_modes"><title>struct video_tuner modes</title> <tgroup cols="2" align="left"> <tbody> <row> @@ -572,7 +572,7 @@ static int current_volume=0; <para> Then we fill in the video_audio structure. This has the following format </para> - <table frame="all"><title>struct video_audio fields</title> + <table frame="all" id="video_audio_fields"><title>struct video_audio fields</title> <tgroup cols="2" align="left"> <tbody> <row> @@ -607,7 +607,7 @@ static int current_volume=0; </tgroup> </table> - <table frame="all"><title>struct video_audio flags</title> + <table frame="all" id="video_audio_flags"><title>struct video_audio flags</title> <tgroup cols="2" align="left"> <tbody> <row> @@ -625,7 +625,7 @@ static int current_volume=0; </tgroup> </table> - <table frame="all"><title>struct video_audio modes</title> + <table frame="all" id="video_audio_modes"><title>struct video_audio modes</title> <tgroup cols="2" align="left"> <tbody> <row> @@ -775,7 +775,7 @@ module_exit(cleanup); </para> </sect1> </chapter> - <chapter> + <chapter id="Video_Capture_Devices"> <title>Video Capture Devices</title> <sect1 id="introvid"> <title>Video Capture Device Types</title> @@ -855,7 +855,7 @@ static struct video_device my_camera We use the extra video capability flags that did not apply to the radio interface. The video related flags are </para> - <table frame="all"><title>Capture Capabilities</title> + <table frame="all" id="Capture_Capabilities"><title>Capture Capabilities</title> <tgroup cols="2" align="left"> <tbody> <row> @@ -1195,7 +1195,7 @@ static int camera_ioctl(struct video_device *dev, unsigned int cmd, void *arg) inputs to the video card). Our example card has a single camera input. The fields in the structure are </para> - <table frame="all"><title>struct video_channel fields</title> + <table frame="all" id="video_channel_fields"><title>struct video_channel fields</title> <tgroup cols="2" align="left"> <tbody> <row> @@ -1218,7 +1218,7 @@ static int camera_ioctl(struct video_device *dev, unsigned int cmd, void *arg) </tbody> </tgroup> </table> - <table frame="all"><title>struct video_channel flags</title> + <table frame="all" id="video_channel_flags"><title>struct video_channel flags</title> <tgroup cols="2" align="left"> <tbody> <row> @@ -1229,7 +1229,7 @@ static int camera_ioctl(struct video_device *dev, unsigned int cmd, void *arg) </tbody> </tgroup> </table> - <table frame="all"><title>struct video_channel types</title> + <table frame="all" id="video_channel_types"><title>struct video_channel types</title> <tgroup cols="2" align="left"> <tbody> <row> @@ -1242,7 +1242,7 @@ static int camera_ioctl(struct video_device *dev, unsigned int cmd, void *arg) </tbody> </tgroup> </table> - <table frame="all"><title>struct video_channel norms</title> + <table frame="all" id="video_channel_norms"><title>struct video_channel norms</title> <tgroup cols="2" align="left"> <tbody> <row> @@ -1328,7 +1328,7 @@ static int camera_ioctl(struct video_device *dev, unsigned int cmd, void *arg) for every other pixel in the image. The other common formats the interface defines are </para> - <table frame="all"><title>Framebuffer Encodings</title> + <table frame="all" id="Framebuffer_Encodings"><title>Framebuffer Encodings</title> <tgroup cols="2" align="left"> <tbody> <row> @@ -1466,7 +1466,7 @@ static struct video_buffer capture_fb; display. The video_window structure is used to describe the way the image should be displayed. </para> - <table frame="all"><title>struct video_window fields</title> + <table frame="all" id="video_window_fields"><title>struct video_window fields</title> <tgroup cols="2" align="left"> <tbody> <row> @@ -1503,7 +1503,7 @@ static struct video_buffer capture_fb; <para> Each clip is a struct video_clip which has the following fields </para> - <table frame="all"><title>video_clip fields</title> + <table frame="all" id="video_clip_fields"><title>video_clip fields</title> <tgroup cols="2" align="left"> <tbody> <row> diff --git a/Documentation/DocBook/z8530book.tmpl b/Documentation/DocBook/z8530book.tmpl index a507876447aa..42c75ba71ba2 100644 --- a/Documentation/DocBook/z8530book.tmpl +++ b/Documentation/DocBook/z8530book.tmpl @@ -77,7 +77,7 @@ </para> </chapter> - <chapter> + <chapter id="Driver_Modes"> <title>Driver Modes</title> <para> The Z85230 driver layer can drive Z8530, Z85C30 and Z85230 devices @@ -108,7 +108,7 @@ </para> </chapter> - <chapter> + <chapter id="Using_the_Z85230_driver"> <title>Using the Z85230 driver</title> <para> The Z85230 driver provides the back end interface to your board. To @@ -174,7 +174,7 @@ </para> </chapter> - <chapter> + <chapter id="Attaching_Network_Interfaces"> <title>Attaching Network Interfaces</title> <para> If you wish to use the network interface facilities of the driver, @@ -216,7 +216,7 @@ </para> </chapter> - <chapter> + <chapter id="Configuring_And_Activating_The_Port"> <title>Configuring And Activating The Port</title> <para> The Z85230 driver provides helper functions and tables to load the @@ -300,7 +300,7 @@ </para> </chapter> - <chapter> + <chapter id="Network_Layer_Functions"> <title>Network Layer Functions</title> <para> The Z8530 layer provides functions to queue packets for @@ -327,7 +327,7 @@ </para> </chapter> - <chapter> + <chapter id="Porting_The_Z8530_Driver"> <title>Porting The Z8530 Driver</title> <para> The Z8530 driver is written to be portable. In DMA mode it makes diff --git a/Documentation/cgroups.txt b/Documentation/cgroups.txt index 98a26f81fa75..42d7c4cb39cd 100644 --- a/Documentation/cgroups.txt +++ b/Documentation/cgroups.txt @@ -456,7 +456,7 @@ methods are create/destroy. Any others that are null are presumed to be successful no-ops. struct cgroup_subsys_state *create(struct cgroup *cont) -LL=cgroup_mutex +(cgroup_mutex held by caller) Called to create a subsystem state object for a cgroup. The subsystem should allocate its subsystem state object for the passed @@ -471,14 +471,19 @@ it's the root of the hierarchy) and may be an appropriate place for initialization code. void destroy(struct cgroup *cont) -LL=cgroup_mutex +(cgroup_mutex held by caller) -The cgroup system is about to destroy the passed cgroup; the -subsystem should do any necessary cleanup +The cgroup system is about to destroy the passed cgroup; the subsystem +should do any necessary cleanup and free its subsystem state +object. By the time this method is called, the cgroup has already been +unlinked from the file system and from the child list of its parent; +cgroup->parent is still valid. (Note - can also be called for a +newly-created cgroup if an error occurs after this subsystem's +create() method has been called for the new cgroup). int can_attach(struct cgroup_subsys *ss, struct cgroup *cont, struct task_struct *task) -LL=cgroup_mutex +(cgroup_mutex held by caller) Called prior to moving a task into a cgroup; if the subsystem returns an error, this will abort the attach operation. If a NULL @@ -489,25 +494,20 @@ remain valid while the caller holds cgroup_mutex. void attach(struct cgroup_subsys *ss, struct cgroup *cont, struct cgroup *old_cont, struct task_struct *task) -LL=cgroup_mutex - Called after the task has been attached to the cgroup, to allow any post-attachment activity that requires memory allocations or blocking. void fork(struct cgroup_subsy *ss, struct task_struct *task) -LL=callback_mutex, maybe read_lock(tasklist_lock) Called when a task is forked into a cgroup. Also called during registration for all existing tasks. void exit(struct cgroup_subsys *ss, struct task_struct *task) -LL=callback_mutex Called during task exit int populate(struct cgroup_subsys *ss, struct cgroup *cont) -LL=none Called after creation of a cgroup to allow a subsystem to populate the cgroup directory with file entries. The subsystem should make @@ -524,7 +524,7 @@ example in cpusets, no task may attach before 'cpus' and 'mems' are set up. void bind(struct cgroup_subsys *ss, struct cgroup *root) -LL=callback_mutex +(cgroup_mutex held by caller) Called when a cgroup subsystem is rebound to a different hierarchy and root cgroup. Currently this will only involve movement between diff --git a/Documentation/controllers/memory.txt b/Documentation/controllers/memory.txt new file mode 100644 index 000000000000..b5bbea92a61a --- /dev/null +++ b/Documentation/controllers/memory.txt @@ -0,0 +1,279 @@ +Memory Controller + +Salient features + +a. Enable control of both RSS (mapped) and Page Cache (unmapped) pages +b. The infrastructure allows easy addition of other types of memory to control +c. Provides *zero overhead* for non memory controller users +d. Provides a double LRU: global memory pressure causes reclaim from the + global LRU; a cgroup on hitting a limit, reclaims from the per + cgroup LRU + +NOTE: Swap Cache (unmapped) is not accounted now. + +Benefits and Purpose of the memory controller + +The memory controller isolates the memory behaviour of a group of tasks +from the rest of the system. The article on LWN [12] mentions some probable +uses of the memory controller. The memory controller can be used to + +a. Isolate an application or a group of applications + Memory hungry applications can be isolated and limited to a smaller + amount of memory. +b. Create a cgroup with limited amount of memory, this can be used + as a good alternative to booting with mem=XXXX. +c. Virtualization solutions can control the amount of memory they want + to assign to a virtual machine instance. +d. A CD/DVD burner could control the amount of memory used by the + rest of the system to ensure that burning does not fail due to lack + of available memory. +e. There are several other use cases, find one or use the controller just + for fun (to learn and hack on the VM subsystem). + +1. History + +The memory controller has a long history. A request for comments for the memory +controller was posted by Balbir Singh [1]. At the time the RFC was posted +there were several implementations for memory control. The goal of the +RFC was to build consensus and agreement for the minimal features required +for memory control. The first RSS controller was posted by Balbir Singh[2] +in Feb 2007. Pavel Emelianov [3][4][5] has since posted three versions of the +RSS controller. At OLS, at the resource management BoF, everyone suggested +that we handle both page cache and RSS together. Another request was raised +to allow user space handling of OOM. The current memory controller is +at version 6; it combines both mapped (RSS) and unmapped Page +Cache Control [11]. + +2. Memory Control + +Memory is a unique resource in the sense that it is present in a limited +amount. If a task requires a lot of CPU processing, the task can spread +its processing over a period of hours, days, months or years, but with +memory, the same physical memory needs to be reused to accomplish the task. + +The memory controller implementation has been divided into phases. These +are: + +1. Memory controller +2. mlock(2) controller +3. Kernel user memory accounting and slab control +4. user mappings length controller + +The memory controller is the first controller developed. + +2.1. Design + +The core of the design is a counter called the res_counter. The res_counter +tracks the current memory usage and limit of the group of processes associated +with the controller. Each cgroup has a memory controller specific data +structure (mem_cgroup) associated with it. + +2.2. Accounting + + +--------------------+ + | mem_cgroup | + | (res_counter) | + +--------------------+ + / ^ \ + / | \ + +---------------+ | +---------------+ + | mm_struct | |.... | mm_struct | + | | | | | + +---------------+ | +---------------+ + | + + --------------+ + | + +---------------+ +------+--------+ + | page +----------> page_cgroup| + | | | | + +---------------+ +---------------+ + + (Figure 1: Hierarchy of Accounting) + + +Figure 1 shows the important aspects of the controller + +1. Accounting happens per cgroup +2. Each mm_struct knows about which cgroup it belongs to +3. Each page has a pointer to the page_cgroup, which in turn knows the + cgroup it belongs to + +The accounting is done as follows: mem_cgroup_charge() is invoked to setup +the necessary data structures and check if the cgroup that is being charged +is over its limit. If it is then reclaim is invoked on the cgroup. +More details can be found in the reclaim section of this document. +If everything goes well, a page meta-data-structure called page_cgroup is +allocated and associated with the page. This routine also adds the page to +the per cgroup LRU. + +2.2.1 Accounting details + +All mapped pages (RSS) and unmapped user pages (Page Cache) are accounted. +RSS pages are accounted at the time of page_add_*_rmap() unless they've already +been accounted for earlier. A file page will be accounted for as Page Cache; +it's mapped into the page tables of a process, duplicate accounting is carefully +avoided. Page Cache pages are accounted at the time of add_to_page_cache(). +The corresponding routines that remove a page from the page tables or removes +a page from Page Cache is used to decrement the accounting counters of the +cgroup. + +2.3 Shared Page Accounting + +Shared pages are accounted on the basis of the first touch approach. The +cgroup that first touches a page is accounted for the page. The principle +behind this approach is that a cgroup that aggressively uses a shared +page will eventually get charged for it (once it is uncharged from +the cgroup that brought it in -- this will happen on memory pressure). + +2.4 Reclaim + +Each cgroup maintains a per cgroup LRU that consists of an active +and inactive list. When a cgroup goes over its limit, we first try +to reclaim memory from the cgroup so as to make space for the new +pages that the cgroup has touched. If the reclaim is unsuccessful, +an OOM routine is invoked to select and kill the bulkiest task in the +cgroup. + +The reclaim algorithm has not been modified for cgroups, except that +pages that are selected for reclaiming come from the per cgroup LRU +list. + +2. Locking + +The memory controller uses the following hierarchy + +1. zone->lru_lock is used for selecting pages to be isolated +2. mem->per_zone->lru_lock protects the per cgroup LRU (per zone) +3. lock_page_cgroup() is used to protect page->page_cgroup + +3. User Interface + +0. Configuration + +a. Enable CONFIG_CGROUPS +b. Enable CONFIG_RESOURCE_COUNTERS +c. Enable CONFIG_CGROUP_MEM_CONT + +1. Prepare the cgroups +# mkdir -p /cgroups +# mount -t cgroup none /cgroups -o memory + +2. Make the new group and move bash into it +# mkdir /cgroups/0 +# echo $$ > /cgroups/0/tasks + +Since now we're in the 0 cgroup, +We can alter the memory limit: +# echo -n 4M > /cgroups/0/memory.limit_in_bytes + +NOTE: We can use a suffix (k, K, m, M, g or G) to indicate values in kilo, +mega or gigabytes. + +# cat /cgroups/0/memory.limit_in_bytes +4194304 Bytes + +NOTE: The interface has now changed to display the usage in bytes +instead of pages + +We can check the usage: +# cat /cgroups/0/memory.usage_in_bytes +1216512 Bytes + +A successful write to this file does not guarantee a successful set of +this limit to the value written into the file. This can be due to a +number of factors, such as rounding up to page boundaries or the total +availability of memory on the system. The user is required to re-read +this file after a write to guarantee the value committed by the kernel. + +# echo -n 1 > memory.limit_in_bytes +# cat memory.limit_in_bytes +4096 Bytes + +The memory.failcnt field gives the number of times that the cgroup limit was +exceeded. + +The memory.stat file gives accounting information. Now, the number of +caches, RSS and Active pages/Inactive pages are shown. + +The memory.force_empty gives an interface to drop *all* charges by force. + +# echo -n 1 > memory.force_empty + +will drop all charges in cgroup. Currently, this is maintained for test. + +4. Testing + +Balbir posted lmbench, AIM9, LTP and vmmstress results [10] and [11]. +Apart from that v6 has been tested with several applications and regular +daily use. The controller has also been tested on the PPC64, x86_64 and +UML platforms. + +4.1 Troubleshooting + +Sometimes a user might find that the application under a cgroup is +terminated. There are several causes for this: + +1. The cgroup limit is too low (just too low to do anything useful) +2. The user is using anonymous memory and swap is turned off or too low + +A sync followed by echo 1 > /proc/sys/vm/drop_caches will help get rid of +some of the pages cached in the cgroup (page cache pages). + +4.2 Task migration + +When a task migrates from one cgroup to another, it's charge is not +carried forward. The pages allocated from the original cgroup still +remain charged to it, the charge is dropped when the page is freed or +reclaimed. + +4.3 Removing a cgroup + +A cgroup can be removed by rmdir, but as discussed in sections 4.1 and 4.2, a +cgroup might have some charge associated with it, even though all +tasks have migrated away from it. Such charges are automatically dropped at +rmdir() if there are no tasks. + +4.4 Choosing what to account -- Page Cache (unmapped) vs RSS (mapped)? + +The type of memory accounted by the cgroup can be limited to just +mapped pages by writing "1" to memory.control_type field + +echo -n 1 > memory.control_type + +5. TODO + +1. Add support for accounting huge pages (as a separate controller) +2. Make per-cgroup scanner reclaim not-shared pages first +3. Teach controller to account for shared-pages +4. Start reclamation when the limit is lowered +5. Start reclamation in the background when the limit is + not yet hit but the usage is getting closer + +Summary + +Overall, the memory controller has been a stable controller and has been +commented and discussed quite extensively in the community. + +References + +1. Singh, Balbir. RFC: Memory Controller, http://lwn.net/Articles/206697/ +2. Singh, Balbir. Memory Controller (RSS Control), + http://lwn.net/Articles/222762/ +3. Emelianov, Pavel. Resource controllers based on process cgroups + http://lkml.org/lkml/2007/3/6/198 +4. Emelianov, Pavel. RSS controller based on process cgroups (v2) + http://lkml.org/lkml/2007/4/9/74 +5. Emelianov, Pavel. RSS controller based on process cgroups (v3) + http://lkml.org/lkml/2007/5/30/244 +6. Menage, Paul. Control Groups v10, http://lwn.net/Articles/236032/ +7. Vaidyanathan, Srinivasan, Control Groups: Pagecache accounting and control + subsystem (v3), http://lwn.net/Articles/235534/ +8. Singh, Balbir. RSS controller V2 test results (lmbench), + http://lkml.org/lkml/2007/5/17/232 +9. Singh, Balbir. RSS controller V2 AIM9 results + http://lkml.org/lkml/2007/5/18/1 +10. Singh, Balbir. Memory controller v6 results, + http://lkml.org/lkml/2007/8/19/36 +11. Singh, Balbir. Memory controller v6, http://lkml.org/lkml/2007/8/17/69 +12. Corbet, Jonathan, Controlling memory use in cgroups, + http://lwn.net/Articles/243795/ diff --git a/Documentation/cpusets.txt b/Documentation/cpusets.txt index 141bef1c8599..43db6fe12814 100644 --- a/Documentation/cpusets.txt +++ b/Documentation/cpusets.txt @@ -523,21 +523,14 @@ from one cpuset to another, then the kernel will adjust the tasks memory placement, as above, the next time that the kernel attempts to allocate a page of memory for that task. -If a cpuset has its CPUs modified, then each task using that -cpuset does _not_ change its behavior automatically. In order to -minimize the impact on the critical scheduling code in the kernel, -tasks will continue to use their prior CPU placement until they -are rebound to their cpuset, by rewriting their pid to the 'tasks' -file of their cpuset. If a task had been bound to some subset of its -cpuset using the sched_setaffinity() call, and if any of that subset -is still allowed in its new cpuset settings, then the task will be -restricted to the intersection of the CPUs it was allowed on before, -and its new cpuset CPU placement. If, on the other hand, there is -no overlap between a tasks prior placement and its new cpuset CPU -placement, then the task will be allowed to run on any CPU allowed -in its new cpuset. If a task is moved from one cpuset to another, -its CPU placement is updated in the same way as if the tasks pid is -rewritten to the 'tasks' file of its current cpuset. +If a cpuset has its 'cpus' modified, then each task in that cpuset +will have its allowed CPU placement changed immediately. Similarly, +if a tasks pid is written to a cpusets 'tasks' file, in either its +current cpuset or another cpuset, then its allowed CPU placement is +changed immediately. If such a task had been bound to some subset +of its cpuset using the sched_setaffinity() call, the task will be +allowed to run on any CPU allowed in its new cpuset, negating the +affect of the prior sched_setaffinity() call. In summary, the memory placement of a task whose cpuset is changed is updated by the kernel, on the next allocation of a page for that task, diff --git a/Documentation/drivers/edac/edac.txt b/Documentation/edac.txt index a5c36842ecef..a5c36842ecef 100644 --- a/Documentation/drivers/edac/edac.txt +++ b/Documentation/edac.txt diff --git a/Documentation/email-clients.txt b/Documentation/email-clients.txt index 113165b48305..2ebb94d6ed8e 100644 --- a/Documentation/email-clients.txt +++ b/Documentation/email-clients.txt @@ -170,7 +170,6 @@ Sylpheed (GUI) - Works well for inlining text (or using attachments). - Allows use of an external editor. -- Not good for IMAP. - Is slow on large folders. - Won't do TLS SMTP auth over a non-SSL connection. - Has a helpful ruler bar in the compose window. diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 68ce1300a360..17b1659bd3f8 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -6,14 +6,6 @@ be removed from this file. --------------------------- -What: MXSER -When: December 2007 -Why: Old mxser driver is obsoleted by the mxser_new. Give it some time yet - and remove it. -Who: Jiri Slaby <jirislaby@gmail.com> - ---------------------------- - What: dev->power.power_state When: July 2007 Why: Broken design for runtime control over driver power states, confusing diff --git a/Documentation/filesystems/00-INDEX b/Documentation/filesystems/00-INDEX index 1de155e2dc36..e68021c08fbd 100644 --- a/Documentation/filesystems/00-INDEX +++ b/Documentation/filesystems/00-INDEX @@ -32,6 +32,8 @@ directory-locking - info about the locking scheme used for directory operations. dlmfs.txt - info on the userspace interface to the OCFS2 DLM. +dnotify.txt + - info about directory notification in Linux. ecryptfs.txt - docs on eCryptfs: stacked cryptographic filesystem for Linux. ext2.txt @@ -80,6 +82,8 @@ relay.txt - info on relay, for efficient streaming from kernel to user space. romfs.txt - description of the ROMFS filesystem. +sharedsubtree.txt + - a description of shared subtrees for namespaces. smbfs.txt - info on using filesystems with the SMB protocol (Win 3.11 and NT). spufs.txt diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 37c10cba7177..42d4b30b1045 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -90,7 +90,6 @@ of the locking scheme for directory operations. prototypes: struct inode *(*alloc_inode)(struct super_block *sb); void (*destroy_inode)(struct inode *); - void (*read_inode) (struct inode *); void (*dirty_inode) (struct inode *); int (*write_inode) (struct inode *, int); void (*put_inode) (struct inode *); @@ -114,7 +113,6 @@ locking rules: BKL s_lock s_umount alloc_inode: no no no destroy_inode: no -read_inode: no (see below) dirty_inode: no (must not sleep) write_inode: no put_inode: no @@ -133,7 +131,6 @@ show_options: no (vfsmount->sem) quota_read: no no no (see below) quota_write: no no no (see below) -->read_inode() is not a method - it's a callback used in iget(). ->remount_fs() will have the s_umount lock if it's already mounted. When called from get_sb_single, it does NOT have the s_umount lock. ->quota_read() and ->quota_write() functions are both guaranteed to diff --git a/Documentation/dnotify.txt b/Documentation/filesystems/dnotify.txt index 6984fca6002a..9f5d338ddbb8 100644 --- a/Documentation/dnotify.txt +++ b/Documentation/filesystems/dnotify.txt @@ -69,24 +69,24 @@ Example #include <signal.h> #include <stdio.h> #include <unistd.h> - + static volatile int event_fd; - + static void handler(int sig, siginfo_t *si, void *data) { event_fd = si->si_fd; } - + int main(void) { struct sigaction act; int fd; - + act.sa_sigaction = handler; sigemptyset(&act.sa_mask); act.sa_flags = SA_SIGINFO; sigaction(SIGRTMIN + 1, &act, NULL); - + fd = open(".", O_RDONLY); fcntl(fd, F_SETSIG, SIGRTMIN + 1); fcntl(fd, F_NOTIFY, DN_MODIFY|DN_CREATE|DN_MULTISHOT); diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting index 0f33c77bc14b..92b888d540a6 100644 --- a/Documentation/filesystems/porting +++ b/Documentation/filesystems/porting @@ -34,8 +34,8 @@ FOO_I(inode) (see in-tree filesystems for examples). Make them ->alloc_inode and ->destroy_inode in your super_operations. -Keep in mind that now you need explicit initialization of private data - -typically in ->read_inode() and after getting an inode from new_inode(). +Keep in mind that now you need explicit initialization of private data +typically between calling iget_locked() and unlocking the inode. At some point that will become mandatory. @@ -173,10 +173,10 @@ should be a non-blocking function that initializes those parts of a newly created inode to allow the test function to succeed. 'data' is passed as an opaque value to both test and set functions. -When the inode has been created by iget5_locked(), it will be returned with -the I_NEW flag set and will still be locked. read_inode has not been -called so the file system still has to finalize the initialization. Once -the inode is initialized it must be unlocked by calling unlock_new_inode(). +When the inode has been created by iget5_locked(), it will be returned with the +I_NEW flag set and will still be locked. The filesystem then needs to finalize +the initialization. Once the inode is initialized it must be unlocked by +calling unlock_new_inode(). The filesystem is responsible for setting (and possibly testing) i_ino when appropriate. There is also a simpler iget_locked function that @@ -184,11 +184,19 @@ just takes the superblock and inode number as arguments and does the test and set for you. e.g. - inode = iget_locked(sb, ino); - if (inode->i_state & I_NEW) { - read_inode_from_disk(inode); - unlock_new_inode(inode); - } + inode = iget_locked(sb, ino); + if (inode->i_state & I_NEW) { + err = read_inode_from_disk(inode); + if (err < 0) { + iget_failed(inode); + return err; + } + unlock_new_inode(inode); + } + +Note that if the process of setting up a new inode fails, then iget_failed() +should be called on the inode to render it dead, and an appropriate error +should be passed back to the caller. --- [recommended] diff --git a/Documentation/sharedsubtree.txt b/Documentation/filesystems/sharedsubtree.txt index 736540045dc7..736540045dc7 100644 --- a/Documentation/sharedsubtree.txt +++ b/Documentation/filesystems/sharedsubtree.txt diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index 9d019d35728f..bd55038b56f5 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -203,8 +203,6 @@ struct super_operations { struct inode *(*alloc_inode)(struct super_block *sb); void (*destroy_inode)(struct inode *); - void (*read_inode) (struct inode *); - void (*dirty_inode) (struct inode *); int (*write_inode) (struct inode *, int); void (*put_inode) (struct inode *); @@ -242,15 +240,6 @@ or bottom half). ->alloc_inode was defined and simply undoes anything done by ->alloc_inode. - read_inode: this method is called to read a specific inode from the - mounted filesystem. The i_ino member in the struct inode is - initialized by the VFS to indicate which inode to read. Other - members are filled in by this method. - - You can set this to NULL and use iget5_locked() instead of iget() - to read inodes. This is necessary for filesystems for which the - inode number is not sufficient to identify an inode. - dirty_inode: this method is called by the VFS to mark an inode dirty. write_inode: this method is called when the VFS needs to write an @@ -308,9 +297,9 @@ or bottom half). quota_write: called by the VFS to write to filesystem quota file. -The read_inode() method is responsible for filling in the "i_op" -field. This is a pointer to a "struct inode_operations" which -describes the methods that can be performed on individual inodes. +Whoever sets up the inode is responsible for filling in the "i_op" field. This +is a pointer to a "struct inode_operations" which describes the methods that +can be performed on individual inodes. The Inode Object diff --git a/Documentation/powerpc/booting-without-of.txt b/Documentation/powerpc/booting-without-of.txt index b5e46efeba84..7b4e8a70882c 100644 --- a/Documentation/powerpc/booting-without-of.txt +++ b/Documentation/powerpc/booting-without-of.txt @@ -57,6 +57,7 @@ Table of Contents n) 4xx/Axon EMAC ethernet nodes o) Xilinx IP cores p) Freescale Synchronous Serial Interface + q) USB EHCI controllers VII - Specifying interrupt information for devices 1) interrupts property @@ -2577,6 +2578,20 @@ platforms are moved over to use the flattened-device-tree model. Requred properties: - current-speed : Baud rate of uartlite + v) Xilinx hwicap + + Xilinx hwicap devices provide access to the configuration logic + of the FPGA through the Internal Configuration Access Port + (ICAP). The ICAP enables partial reconfiguration of the FPGA, + readback of the configuration information, and some control over + 'warm boots' of the FPGA fabric. + + Required properties: + - xlnx,family : The family of the FPGA, necessary since the + capabilities of the underlying ICAP hardware + differ between different families. May be + 'virtex2p', 'virtex4', or 'virtex5'. + p) Freescale Synchronous Serial Interface The SSI is a serial device that communicates with audio codecs. It can @@ -2775,6 +2790,33 @@ platforms are moved over to use the flattened-device-tree model. interrupt-parent = < &ipic >; }; + q) USB EHCI controllers + + Required properties: + - compatible : should be "usb-ehci". + - reg : should contain at least address and length of the standard EHCI + register set for the device. Optional platform-dependent registers + (debug-port or other) can be also specified here, but only after + definition of standard EHCI registers. + - interrupts : one EHCI interrupt should be described here. + If device registers are implemented in big endian mode, the device + node should have "big-endian-regs" property. + If controller implementation operates with big endian descriptors, + "big-endian-desc" property should be specified. + If both big endian registers and descriptors are used by the controller + implementation, "big-endian" property can be specified instead of having + both "big-endian-regs" and "big-endian-desc". + + Example (Sequoia 440EPx): + ehci@e0000300 { + compatible = "ibm,usb-ehci-440epx", "usb-ehci"; + interrupt-parent = <&UIC0>; + interrupts = <1a 4>; + reg = <0 e0000300 90 0 e0000390 70>; + big-endian; + }; + + More devices will be defined as this spec matures. VII - Specifying interrupt information for devices diff --git a/Documentation/scheduler/00-INDEX b/Documentation/scheduler/00-INDEX new file mode 100644 index 000000000000..b5f5ca069b2d --- /dev/null +++ b/Documentation/scheduler/00-INDEX @@ -0,0 +1,16 @@ +00-INDEX + - this file. +sched-arch.txt + - CPU Scheduler implementation hints for architecture specific code. +sched-coding.txt + - reference for various scheduler-related methods in the O(1) scheduler. +sched-design.txt + - goals, design and implementation of the Linux O(1) scheduler. +sched-design-CFS.txt + - goals, design and implementation of the Complete Fair Scheduler. +sched-domains.txt + - information on scheduling domains. +sched-nice-design.txt + - How and why the scheduler's nice levels are implemented. +sched-stats.txt + - information on schedstats (Linux Scheduler Statistics). diff --git a/Documentation/sched-arch.txt b/Documentation/scheduler/sched-arch.txt index 941615a9769b..941615a9769b 100644 --- a/Documentation/sched-arch.txt +++ b/Documentation/scheduler/sched-arch.txt diff --git a/Documentation/sched-coding.txt b/Documentation/scheduler/sched-coding.txt index cbd8db752acf..cbd8db752acf 100644 --- a/Documentation/sched-coding.txt +++ b/Documentation/scheduler/sched-coding.txt diff --git a/Documentation/sched-design-CFS.txt b/Documentation/scheduler/sched-design-CFS.txt index 88bcb8767335..88bcb8767335 100644 --- a/Documentation/sched-design-CFS.txt +++ b/Documentation/scheduler/sched-design-CFS.txt diff --git a/Documentation/sched-design.txt b/Documentation/scheduler/sched-design.txt index 1605bf0cba8b..1605bf0cba8b 100644 --- a/Documentation/sched-design.txt +++ b/Documentation/scheduler/sched-design.txt diff --git a/Documentation/sched-domains.txt b/Documentation/scheduler/sched-domains.txt index a9e990ab980f..a9e990ab980f 100644 --- a/Documentation/sched-domains.txt +++ b/Documentation/scheduler/sched-domains.txt diff --git a/Documentation/sched-nice-design.txt b/Documentation/scheduler/sched-nice-design.txt index e2bae5a577e3..e2bae5a577e3 100644 --- a/Documentation/sched-nice-design.txt +++ b/Documentation/scheduler/sched-nice-design.txt diff --git a/Documentation/sched-stats.txt b/Documentation/scheduler/sched-stats.txt index 442e14d35dea..442e14d35dea 100644 --- a/Documentation/sched-stats.txt +++ b/Documentation/scheduler/sched-stats.txt diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt index 24eac1bc735d..8a4863c4edd4 100644 --- a/Documentation/sysctl/vm.txt +++ b/Documentation/sysctl/vm.txt @@ -32,6 +32,7 @@ Currently, these files are in /proc/sys/vm: - min_unmapped_ratio - min_slab_ratio - panic_on_oom +- oom_dump_tasks - oom_kill_allocating_task - mmap_min_address - numa_zonelist_order @@ -232,6 +233,27 @@ according to your policy of failover. ============================================================= +oom_dump_tasks + +Enables a system-wide task dump (excluding kernel threads) to be +produced when the kernel performs an OOM-killing and includes such +information as pid, uid, tgid, vm size, rss, cpu, oom_adj score, and +name. This is helpful to determine why the OOM killer was invoked +and to identify the rogue task that caused it. + +If this is set to zero, this information is suppressed. On very +large systems with thousands of tasks it may not be feasible to dump +the memory state information for each one. Such systems should not +be forced to incur a performance penalty in OOM conditions when the +information may not be desired. + +If this is set to non-zero, this information is shown whenever the +OOM killer actually kills a memory-hogging task. + +The default value is 0. + +============================================================= + oom_kill_allocating_task This enables or disables killing the OOM-triggering task in diff --git a/MAINTAINERS b/MAINTAINERS index 0885aa2b095a..a372c86fd07b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1371,6 +1371,11 @@ W: http://linuxtv.org/ T: git kernel.org:/pub/scm/linux/kernel/git/mchehab/v4l-dvb.git S: Maintained +DZ DECSTATION DZ11 SERIAL DRIVER +P: Maciej W. Rozycki +M: macro@linux-mips.org +S: Maintained + EATA-DMA SCSI DRIVER P: Michael Neuffer L: linux-eata@i-connect.net, linux-scsi@vger.kernel.org diff --git a/REPORTING-BUGS b/REPORTING-BUGS index ac02e42a2627..ab0c56630a8c 100644 --- a/REPORTING-BUGS +++ b/REPORTING-BUGS @@ -10,11 +10,12 @@ bug report. This explains what you should do with the "Oops" information to make it useful to the recipient. Send the output to the maintainer of the kernel area that seems to -be involved with the problem. Don't worry too much about getting the -wrong person. If you are unsure send it to the person responsible for the -code relevant to what you were doing. If it occurs repeatably try and -describe how to recreate it. That is worth even more than the oops itself. -The list of maintainers is in the MAINTAINERS file in this directory. +be involved with the problem, and cc the relevant mailing list. Don't +worry too much about getting the wrong person. If you are unsure send it +to the person responsible for the code relevant to what you were doing. +If it occurs repeatably try and describe how to recreate it. That is +worth even more than the oops itself. The list of maintainers and +mailing lists is in the MAINTAINERS file in this directory. If it is a security bug, please copy the Security Contact listed in the MAINTAINERS file. They can help coordinate bugfix and disclosure. diff --git a/arch/alpha/kernel/core_irongate.c b/arch/alpha/kernel/core_irongate.c index e4a0bcf1d28b..a872078497be 100644 --- a/arch/alpha/kernel/core_irongate.c +++ b/arch/alpha/kernel/core_irongate.c @@ -241,7 +241,8 @@ albacore_init_arch(void) size / 1024); } #endif - reserve_bootmem_node(NODE_DATA(0), pci_mem, memtop - pci_mem); + reserve_bootmem_node(NODE_DATA(0), pci_mem, memtop - + pci_mem, BOOTMEM_DEFAULT); printk("irongate_init_arch: temporarily reserving " "region %08lx-%08lx for PCI\n", pci_mem, memtop - 1); } diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c index beff6297f788..74c346625658 100644 --- a/arch/alpha/kernel/setup.c +++ b/arch/alpha/kernel/setup.c @@ -428,7 +428,8 @@ setup_memory(void *kernel_end) } /* Reserve the bootmap memory. */ - reserve_bootmem(PFN_PHYS(bootmap_start), bootmap_size); + reserve_bootmem(PFN_PHYS(bootmap_start), bootmap_size, + BOOTMEM_DEFAULT); printk("reserving pages %ld:%ld\n", bootmap_start, bootmap_start+PFN_UP(bootmap_size)); #ifdef CONFIG_BLK_DEV_INITRD @@ -446,7 +447,7 @@ setup_memory(void *kernel_end) phys_to_virt(PFN_PHYS(max_low_pfn))); } else { reserve_bootmem(virt_to_phys((void *)initrd_start), - INITRD_SIZE); + INITRD_SIZE, BOOTMEM_DEFAULT); } } #endif /* CONFIG_BLK_DEV_INITRD */ diff --git a/arch/alpha/mm/numa.c b/arch/alpha/mm/numa.c index e3e3806a6f25..10ab7833e83c 100644 --- a/arch/alpha/mm/numa.c +++ b/arch/alpha/mm/numa.c @@ -242,7 +242,8 @@ setup_memory_node(int nid, void *kernel_end) } /* Reserve the bootmap memory. */ - reserve_bootmem_node(NODE_DATA(nid), PFN_PHYS(bootmap_start), bootmap_size); + reserve_bootmem_node(NODE_DATA(nid), PFN_PHYS(bootmap_start), + bootmap_size, BOOTMEM_DEFAULT); printk(" reserving pages %ld:%ld\n", bootmap_start, bootmap_start+PFN_UP(bootmap_size)); node_set_online(nid); @@ -281,7 +282,7 @@ setup_memory(void *kernel_end) nid = kvaddr_to_nid(initrd_start); reserve_bootmem_node(NODE_DATA(nid), virt_to_phys((void *)initrd_start), - INITRD_SIZE); + INITRD_SIZE, BOOTMEM_DEFAULT); } } #endif /* CONFIG_BLK_DEV_INITRD */ diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index c0ad7c0fbae0..ec00f26bffa4 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -239,7 +239,7 @@ bootmem_init_node(int node, int initrd_node, struct meminfo *mi) * Reserve the bootmem bitmap for this node. */ reserve_bootmem_node(pgdat, boot_pfn << PAGE_SHIFT, - boot_pages << PAGE_SHIFT); + boot_pages << PAGE_SHIFT, BOOTMEM_DEFAULT); #ifdef CONFIG_BLK_DEV_INITRD /* @@ -247,7 +247,7 @@ bootmem_init_node(int node, int initrd_node, struct meminfo *mi) */ if (node == initrd_node) { reserve_bootmem_node(pgdat, phys_initrd_start, - phys_initrd_size); + phys_initrd_size, BOOTMEM_DEFAULT); initrd_start = __phys_to_virt(phys_initrd_start); initrd_end = initrd_start + phys_initrd_size; } diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index e5d61ee3d4a1..d41a75ed3dce 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -605,9 +605,11 @@ void __init reserve_node_zero(pg_data_t *pgdat) * Note that this can only be in node 0. */ #ifdef CONFIG_XIP_KERNEL - reserve_bootmem_node(pgdat, __pa(&__data_start), &_end - &__data_start); + reserve_bootmem_node(pgdat, __pa(&__data_start), &_end - &__data_start, + BOOTMEM_DEFAULT); #else - reserve_bootmem_node(pgdat, __pa(&_stext), &_end - &_stext); + reserve_bootmem_node(pgdat, __pa(&_stext), &_end - &_stext, + BOOTMEM_DEFAULT); #endif /* @@ -615,7 +617,7 @@ void __init reserve_node_zero(pg_data_t *pgdat) * and can only be in node 0. */ reserve_bootmem_node(pgdat, __pa(swapper_pg_dir), - PTRS_PER_PGD * sizeof(pgd_t)); + PTRS_PER_PGD * sizeof(pgd_t), BOOTMEM_DEFAULT); /* * Hmm... This should go elsewhere, but we really really need to @@ -638,8 +640,10 @@ void __init reserve_node_zero(pg_data_t *pgdat) /* H1940 and RX3715 need to reserve this for suspend */ if (machine_is_h1940() || machine_is_rx3715()) { - reserve_bootmem_node(pgdat, 0x30003000, 0x1000); - reserve_bootmem_node(pgdat, 0x30081000, 0x1000); + reserve_bootmem_node(pgdat, 0x30003000, 0x1000, + BOOTMEM_DEFAULT); + reserve_bootmem_node(pgdat, 0x30081000, 0x1000, + BOOTMEM_DEFAULT); } #ifdef CONFIG_SA1111 @@ -650,7 +654,8 @@ void __init reserve_node_zero(pg_data_t *pgdat) res_size = __pa(swapper_pg_dir) - PHYS_OFFSET; #endif if (res_size) - reserve_bootmem_node(pgdat, PHYS_OFFSET, res_size); + reserve_bootmem_node(pgdat, PHYS_OFFSET, res_size, + BOOTMEM_DEFAULT); } /* diff --git a/arch/arm/mm/nommu.c b/arch/arm/mm/nommu.c index 8cd3a60954f0..63c62fdea521 100644 --- a/arch/arm/mm/nommu.c +++ b/arch/arm/mm/nommu.c @@ -27,9 +27,11 @@ void __init reserve_node_zero(pg_data_t *pgdat) * Note that this can only be in node 0. */ #ifdef CONFIG_XIP_KERNEL - reserve_bootmem_node(pgdat, __pa(&__data_start), &_end - &__data_start); + reserve_bootmem_node(pgdat, __pa(&__data_start), &_end - &__data_start, + BOOTMEM_DEFAULT); #else - reserve_bootmem_node(pgdat, __pa(&_stext), &_end - &_stext); + reserve_bootmem_node(pgdat, __pa(&_stext), &_end - &_stext, + BOOTMEM_DEFAULT); #endif /* @@ -37,7 +39,8 @@ void __init reserve_node_zero(pg_data_t *pgdat) * some architectures which the DRAM is the exception vector to trap, * alloc_page breaks with error, although it is not NULL, but "0." */ - reserve_bootmem_node(pgdat, CONFIG_VECTORS_BASE, PAGE_SIZE); + reserve_bootmem_node(pgdat, CONFIG_VECTORS_BASE, PAGE_SIZE, + BOOTMEM_DEFAULT); } /* diff --git a/arch/arm/plat-omap/fb.c b/arch/arm/plat-omap/fb.c index ee40c1a0b83d..7854f19b77cf 100644 --- a/arch/arm/plat-omap/fb.c +++ b/arch/arm/plat-omap/fb.c @@ -207,7 +207,7 @@ void __init omapfb_reserve_sdram(void) return; } if (rg.paddr) - reserve_bootmem(rg.paddr, rg.size); + reserve_bootmem(rg.paddr, rg.size, BOOTMEM_DEFAULT); reserved += rg.size; omapfb_config.mem_desc.region[i] = rg; configured_regions++; diff --git a/arch/avr32/kernel/setup.c b/arch/avr32/kernel/setup.c index 4b4c1884e1c5..e66a07a928cd 100644 --- a/arch/avr32/kernel/setup.c +++ b/arch/avr32/kernel/setup.c @@ -489,7 +489,8 @@ static void __init setup_bootmem(void) /* Reserve space for the bootmem bitmap... */ reserve_bootmem_node(NODE_DATA(node), PFN_PHYS(bootmap_pfn), - bootmap_size); + bootmap_size, + BOOTMEM_DEFAULT); /* ...and any other reserved regions. */ for (res = reserved; res; res = res->sibling) { @@ -505,7 +506,8 @@ static void __init setup_bootmem(void) && res->end < PFN_PHYS(max_pfn)) reserve_bootmem_node( NODE_DATA(node), res->start, - res->end - res->start + 1); + res->end - res->start + 1, + BOOTMEM_DEFAULT); } node_set_online(node); diff --git a/arch/blackfin/kernel/setup.c b/arch/blackfin/kernel/setup.c index 462cae893757..6e106b3d7729 100644 --- a/arch/blackfin/kernel/setup.c +++ b/arch/blackfin/kernel/setup.c @@ -406,7 +406,7 @@ void __init setup_arch(char **cmdline_p) */ free_bootmem(memory_start, memory_end - memory_start); - reserve_bootmem(memory_start, bootmap_size); + reserve_bootmem(memory_start, bootmap_size, BOOTMEM_DEFAULT); /* * get kmalloc into gear */ diff --git a/arch/cris/kernel/setup.c b/arch/cris/kernel/setup.c index 65466c49d7a9..4da042e100a0 100644 --- a/arch/cris/kernel/setup.c +++ b/arch/cris/kernel/setup.c @@ -137,7 +137,7 @@ setup_arch(char **cmdline_p) * Arguments are start, size */ - reserve_bootmem(PFN_PHYS(start_pfn), bootmap_size); + reserve_bootmem(PFN_PHYS(start_pfn), bootmap_size, BOOTMEM_DEFAULT); /* paging_init() sets up the MMU and marks all pages as reserved */ diff --git a/arch/frv/kernel/setup.c b/arch/frv/kernel/setup.c index b38ae1fc15fd..6c01464db699 100644 --- a/arch/frv/kernel/setup.c +++ b/arch/frv/kernel/setup.c @@ -925,13 +925,15 @@ static void __init setup_linux_memory(void) #endif /* take back the memory occupied by the kernel image and the bootmem alloc map */ - reserve_bootmem(kstart, kend - kstart + bootmap_size); + reserve_bootmem(kstart, kend - kstart + bootmap_size, + BOOTMEM_DEFAULT); /* reserve the memory occupied by the initial ramdisk */ #ifdef CONFIG_BLK_DEV_INITRD if (LOADER_TYPE && INITRD_START) { if (INITRD_START + INITRD_SIZE <= (low_top_pfn << PAGE_SHIFT)) { - reserve_bootmem(INITRD_START, INITRD_SIZE); + reserve_bootmem(INITRD_START, INITRD_SIZE, + BOOTMEM_DEFAULT); initrd_start = INITRD_START + PAGE_OFFSET; initrd_end = initrd_start + INITRD_SIZE; } @@ -986,9 +988,10 @@ static void __init setup_uclinux_memory(void) /* now take back the bits the core kernel is occupying */ #ifndef CONFIG_PROTECT_KERNEL - reserve_bootmem(kend, bootmap_size); + reserve_bootmem(kend, bootmap_size, BOOTMEM_DEFAULT); reserve_bootmem((unsigned long) &__kernel_image_start, - kend - (unsigned long) &__kernel_image_start); + kend - (unsigned long) &__kernel_image_start, + BOOTMEM_DEFAULT); #else dampr = __get_DAMPR(0); @@ -996,14 +999,15 @@ static void __init setup_uclinux_memory(void) dampr = (dampr >> 4) + 17; dampr = 1 << dampr; - reserve_bootmem(__get_DAMPR(0) & xAMPRx_PPFN, dampr); + reserve_bootmem(__get_DAMPR(0) & xAMPRx_PPFN, dampr, BOOTMEM_DEFAULT); #endif /* reserve some memory to do uncached DMA through if requested */ #ifdef CONFIG_RESERVE_DMA_COHERENT if (dma_coherent_mem_start) reserve_bootmem(dma_coherent_mem_start, - dma_coherent_mem_end - dma_coherent_mem_start); + dma_coherent_mem_end - dma_coherent_mem_start, + BOOTMEM_DEFAULT); #endif } /* end setup_uclinux_memory() */ diff --git a/arch/h8300/kernel/setup.c b/arch/h8300/kernel/setup.c index b2e86d0255e6..cd3734614d9d 100644 --- a/arch/h8300/kernel/setup.c +++ b/arch/h8300/kernel/setup.c @@ -173,7 +173,7 @@ void __init setup_arch(char **cmdline_p) * the bootmem bitmap so we then reserve it after freeing it :-) */ free_bootmem(memory_start, memory_end - memory_start); - reserve_bootmem(memory_start, bootmap_size); + reserve_bootmem(memory_start, bootmap_size, BOOTMEM_DEFAULT); /* * get kmalloc into gear */ diff --git a/arch/ia64/kernel/machine_kexec.c b/arch/ia64/kernel/machine_kexec.c index d6cd45f4c6c7..0823de1f6ebe 100644 --- a/arch/ia64/kernel/machine_kexec.c +++ b/arch/ia64/kernel/machine_kexec.c @@ -129,13 +129,14 @@ void machine_kexec(struct kimage *image) void arch_crash_save_vmcoreinfo(void) { -#if defined(CONFIG_ARCH_DISCONTIGMEM_ENABLE) && defined(CONFIG_NUMA) +#if defined(CONFIG_DISCONTIGMEM) || defined(CONFIG_SPARSEMEM) VMCOREINFO_SYMBOL(pgdat_list); VMCOREINFO_LENGTH(pgdat_list, MAX_NUMNODES); - +#endif +#ifdef CONFIG_NUMA VMCOREINFO_SYMBOL(node_memblk); VMCOREINFO_LENGTH(node_memblk, NR_NODE_MEMBLKS); - VMCOREINFO_SIZE(node_memblk_s); + VMCOREINFO_STRUCT_SIZE(node_memblk_s); VMCOREINFO_OFFSET(node_memblk_s, start_paddr); VMCOREINFO_OFFSET(node_memblk_s, size); #endif diff --git a/arch/ia64/mm/contig.c b/arch/ia64/mm/contig.c index 7e9c275ea148..344f64eca7a9 100644 --- a/arch/ia64/mm/contig.c +++ b/arch/ia64/mm/contig.c @@ -218,7 +218,7 @@ find_memory (void) /* Free all available memory, then mark bootmem-map as being in use. */ efi_memmap_walk(filter_rsvd_memory, free_bootmem); - reserve_bootmem(bootmap_start, bootmap_size); + reserve_bootmem(bootmap_start, bootmap_size, BOOTMEM_DEFAULT); find_initrd(); diff --git a/arch/ia64/mm/discontig.c b/arch/ia64/mm/discontig.c index 0b567398f38e..ee5e68b2af94 100644 --- a/arch/ia64/mm/discontig.c +++ b/arch/ia64/mm/discontig.c @@ -299,12 +299,12 @@ static void __init reserve_pernode_space(void) pages = bdp->node_low_pfn - (bdp->node_boot_start>>PAGE_SHIFT); size = bootmem_bootmap_pages(pages) << PAGE_SHIFT; base = __pa(bdp->node_bootmem_map); - reserve_bootmem_node(pdp, base, size); + reserve_bootmem_node(pdp, base, size, BOOTMEM_DEFAULT); /* Now the per-node space */ size = mem_data[node].pernode_size; base = __pa(mem_data[node].pernode_addr); - reserve_bootmem_node(pdp, base, size); + reserve_bootmem_node(pdp, base, size, BOOTMEM_DEFAULT); } } diff --git a/arch/m32r/kernel/setup.c b/arch/m32r/kernel/setup.c index d64814385d70..f1f5db0c4084 100644 --- a/arch/m32r/kernel/setup.c +++ b/arch/m32r/kernel/setup.c @@ -177,25 +177,28 @@ static unsigned long __init setup_memory(void) */ reserve_bootmem(CONFIG_MEMORY_START + PAGE_SIZE, (PFN_PHYS(start_pfn) + bootmap_size + PAGE_SIZE - 1) - - CONFIG_MEMORY_START); + - CONFIG_MEMORY_START, + BOOTMEM_DEFAULT); /* * reserve physical page 0 - it's a special BIOS page on many boxes, * enabling clean reboots, SMP operation, laptop functions. */ - reserve_bootmem(CONFIG_MEMORY_START, PAGE_SIZE); + reserve_bootmem(CONFIG_MEMORY_START, PAGE_SIZE, BOOTMEM_DEFAULT); /* * reserve memory hole */ #ifdef CONFIG_MEMHOLE - reserve_bootmem(CONFIG_MEMHOLE_START, CONFIG_MEMHOLE_SIZE); + reserve_bootmem(CONFIG_MEMHOLE_START, CONFIG_MEMHOLE_SIZE, + BOOTMEM_DEFAULT); #endif #ifdef CONFIG_BLK_DEV_INITRD if (LOADER_TYPE && INITRD_START) { if (INITRD_START + INITRD_SIZE <= (max_low_pfn << PAGE_SHIFT)) { - reserve_bootmem(INITRD_START, INITRD_SIZE); + reserve_bootmem(INITRD_START, INITRD_SIZE, + BOOTMEM_DEFAULT); initrd_start = INITRD_START + PAGE_OFFSET; initrd_end = initrd_start + INITRD_SIZE; printk("initrd:start[%08lx],size[%08lx]\n", diff --git a/arch/m32r/kernel/smpboot.c b/arch/m32r/kernel/smpboot.c index 0e383da158e9..2c03ac1d005f 100644 --- a/arch/m32r/kernel/smpboot.c +++ b/arch/m32r/kernel/smpboot.c @@ -43,6 +43,7 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/mm.h> +#include <linux/sched.h> #include <linux/err.h> #include <linux/irq.h> #include <linux/bootmem.h> diff --git a/arch/m32r/mm/discontig.c b/arch/m32r/mm/discontig.c index c7efdb0aefc5..07c1af7dc0e2 100644 --- a/arch/m32r/mm/discontig.c +++ b/arch/m32r/mm/discontig.c @@ -91,7 +91,8 @@ unsigned long __init setup_memory(void) PFN_PHYS(mp->pages)); reserve_bootmem_node(NODE_DATA(nid), PFN_PHYS(mp->start_pfn), - PFN_PHYS(mp->free_pfn - mp->start_pfn) + bootmap_size); + PFN_PHYS(mp->free_pfn - mp->start_pfn) + bootmap_size, + BOOTMEM_DEFAULT); if (max_low_pfn < max_pfn) max_low_pfn = max_pfn; @@ -104,7 +105,7 @@ unsigned long __init setup_memory(void) if (LOADER_TYPE && INITRD_START) { if (INITRD_START + INITRD_SIZE <= PFN_PHYS(max_low_pfn)) { reserve_bootmem_node(NODE_DATA(0), INITRD_START, - INITRD_SIZE); + INITRD_SIZE, BOOTMEM_DEFAULT); initrd_start = INITRD_START + PAGE_OFFSET; initrd_end = initrd_start + INITRD_SIZE; printk("initrd:start[%08lx],size[%08lx]\n", diff --git a/arch/m68k/atari/stram.c b/arch/m68k/atari/stram.c index 8dda6515887a..0055a6c06f75 100644 --- a/arch/m68k/atari/stram.c +++ b/arch/m68k/atari/stram.c @@ -154,7 +154,7 @@ void __init atari_stram_reserve_pages(void *start_mem) /* always reserve first page of ST-RAM, the first 2 kB are * supervisor-only! */ if (!kernel_in_stram) - reserve_bootmem (0, PAGE_SIZE); + reserve_bootmem(0, PAGE_SIZE, BOOTMEM_DEFAULT); } diff --git a/arch/m68k/kernel/setup.c b/arch/m68k/kernel/setup.c index ed3a4caec620..9a06c48edcb3 100644 --- a/arch/m68k/kernel/setup.c +++ b/arch/m68k/kernel/setup.c @@ -323,7 +323,8 @@ void __init setup_arch(char **cmdline_p) #ifdef CONFIG_BLK_DEV_INITRD if (m68k_ramdisk.size) { reserve_bootmem_node(__virt_to_node(phys_to_virt(m68k_ramdisk.addr)), - m68k_ramdisk.addr, m68k_ramdisk.size); + m68k_ramdisk.addr, m68k_ramdisk.size, + BOOTMEM_DEFAULT); initrd_start = (unsigned long)phys_to_virt(m68k_ramdisk.addr); initrd_end = initrd_start + m68k_ramdisk.size; printk("initrd: %08lx - %08lx\n", initrd_start, initrd_end); diff --git a/arch/m68knommu/kernel/setup.c b/arch/m68knommu/kernel/setup.c index 81507c53d4a9..156c6c662c7e 100644 --- a/arch/m68knommu/kernel/setup.c +++ b/arch/m68knommu/kernel/setup.c @@ -203,7 +203,7 @@ void __init setup_arch(char **cmdline_p) * the bootmem bitmap so we then reserve it after freeing it :-) */ free_bootmem(memory_start, memory_end - memory_start); - reserve_bootmem(memory_start, bootmap_size); + reserve_bootmem(memory_start, bootmap_size, BOOTMEM_DEFAULT); /* * Get kmalloc into gear. diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index c032409cba9b..39f3dfe134fb 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -232,7 +232,7 @@ static void __init finalize_initrd(void) goto disable; } - reserve_bootmem(__pa(initrd_start), size); + reserve_bootmem(__pa(initrd_start), size, BOOTMEM_DEFAULT); initrd_below_start_ok = 1; printk(KERN_INFO "Initial ramdisk at: 0x%lx (%lu bytes)\n", @@ -413,7 +413,7 @@ static void __init bootmem_init(void) /* * Reserve the bootmap memory. */ - reserve_bootmem(PFN_PHYS(mapstart), bootmap_size); + reserve_bootmem(PFN_PHYS(mapstart), bootmap_size, BOOTMEM_DEFAULT); /* * Reserve initrd memory if needed. diff --git a/arch/mips/sgi-ip27/ip27-memory.c b/arch/mips/sgi-ip27/ip27-memory.c index e5e023f50a07..bf438d02366e 100644 --- a/arch/mips/sgi-ip27/ip27-memory.c +++ b/arch/mips/sgi-ip27/ip27-memory.c @@ -465,7 +465,8 @@ static void __init node_mem_init(cnodeid_t node) free_bootmem_node(NODE_DATA(node), slot_firstpfn << PAGE_SHIFT, (slot_lastpfn - slot_firstpfn) << PAGE_SHIFT); reserve_bootmem_node(NODE_DATA(node), slot_firstpfn << PAGE_SHIFT, - ((slot_freepfn - slot_firstpfn) << PAGE_SHIFT) + bootmap_size); + ((slot_freepfn - slot_firstpfn) << PAGE_SHIFT) + bootmap_size, + BOOTMEM_DEFAULT); } /* diff --git a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c index aa875fa43488..eb80f5e33d7d 100644 --- a/arch/parisc/mm/init.c +++ b/arch/parisc/mm/init.c @@ -315,11 +315,13 @@ static void __init setup_bootmem(void) #define PDC_CONSOLE_IO_IODC_SIZE 32768 reserve_bootmem_node(NODE_DATA(0), 0UL, - (unsigned long)(PAGE0->mem_free + PDC_CONSOLE_IO_IODC_SIZE)); + (unsigned long)(PAGE0->mem_free + + PDC_CONSOLE_IO_IODC_SIZE), BOOTMEM_DEFAULT); reserve_bootmem_node(NODE_DATA(0), __pa((unsigned long)_text), - (unsigned long)(_end - _text)); + (unsigned long)(_end - _text), BOOTMEM_DEFAULT); reserve_bootmem_node(NODE_DATA(0), (bootmap_start_pfn << PAGE_SHIFT), - ((bootmap_pfn - bootmap_start_pfn) << PAGE_SHIFT)); + ((bootmap_pfn - bootmap_start_pfn) << PAGE_SHIFT), + BOOTMEM_DEFAULT); #ifndef CONFIG_DISCONTIGMEM @@ -328,7 +330,8 @@ static void __init setup_bootmem(void) for (i = 0; i < npmem_holes; i++) { reserve_bootmem_node(NODE_DATA(0), (pmem_holes[i].start_pfn << PAGE_SHIFT), - (pmem_holes[i].pages << PAGE_SHIFT)); + (pmem_holes[i].pages << PAGE_SHIFT), + BOOTMEM_DEFAULT); } #endif @@ -346,7 +349,8 @@ static void __init setup_bootmem(void) initrd_below_start_ok = 1; printk(KERN_INFO "initrd: reserving %08lx-%08lx (mem_max %08lx)\n", __pa(initrd_start), __pa(initrd_start) + initrd_reserve, mem_max); - reserve_bootmem_node(NODE_DATA(0),__pa(initrd_start), initrd_reserve); + reserve_bootmem_node(NODE_DATA(0), __pa(initrd_start), + initrd_reserve, BOOTMEM_DEFAULT); } } #endif diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index cf030b004415..8dcac0b22d68 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -97,6 +97,7 @@ config EARLY_PRINTK config COMPAT bool default y if PPC64 + select COMPAT_BINFMT_ELF config SYSVIPC_COMPAT bool @@ -438,25 +439,6 @@ config WANT_DEVICE_TREE bool default n -config DEVICE_TREE - string "Static device tree source file" - depends on WANT_DEVICE_TREE - help - This specifies the device tree source (.dts) file to be - compiled and included when building the bootwrapper. If a - relative filename is given, then it will be relative to - arch/powerpc/boot/dts. If you are not using the bootwrapper, - or do not need to build a dts into the bootwrapper, this - field is ignored. - - For example, this is required when building a cuImage target - for an older U-Boot, which cannot pass a device tree itself. - Such a kernel will not work with a newer U-Boot that tries to - pass a device tree (unless you tell it not to). If your U-Boot - does not mention a device tree in "help bootm", then use the - cuImage target and specify a device tree here. Otherwise, use - the uImage target and leave this field blank. - endmenu config ISA_DMA_API @@ -512,7 +494,7 @@ config PCI bool "PCI support" if 40x || CPM2 || PPC_83xx || PPC_85xx || PPC_86xx \ || PPC_MPC52xx || (EMBEDDED && (PPC_PSERIES || PPC_ISERIES)) \ || PPC_PS3 || 44x - default y if !40x && !CPM2 && !8xx && !PPC_83xx \ + default y if !40x && !CPM2 && !8xx && !PPC_MPC512x && !PPC_83xx \ && !PPC_85xx && !PPC_86xx default PCI_PERMEDIA if !4xx && !CPM2 && !8xx default PCI_QSPAN if !4xx && !CPM2 && 8xx diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile index f70df9b64f8f..6845482f0093 100644 --- a/arch/powerpc/Makefile +++ b/arch/powerpc/Makefile @@ -151,14 +151,11 @@ core-$(CONFIG_XMON) += arch/powerpc/xmon/ drivers-$(CONFIG_OPROFILE) += arch/powerpc/oprofile/ # Default to zImage, override when needed -defaultimage-y := zImage -defaultimage-$(CONFIG_DEFAULT_UIMAGE) := uImage -KBUILD_IMAGE := $(defaultimage-y) -all: $(KBUILD_IMAGE) +all: zImage CPPFLAGS_vmlinux.lds := -Upowerpc -BOOT_TARGETS = zImage zImage.initrd uImage +BOOT_TARGETS = zImage zImage.initrd uImage treeImage.% cuImage.% PHONY += $(BOOT_TARGETS) @@ -180,7 +177,7 @@ define archhelp endef install: vdso_install - $(Q)$(MAKE) $(build)=$(boot) BOOTIMAGE=$(KBUILD_IMAGE) install + $(Q)$(MAKE) $(build)=$(boot) install vdso_install: ifeq ($(CONFIG_PPC64),y) diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile index 122a27078998..49797a45416c 100644 --- a/arch/powerpc/boot/Makefile +++ b/arch/powerpc/boot/Makefile @@ -60,8 +60,9 @@ src-wlib := string.S crt0.S stdio.c main.c \ src-plat := of.c cuboot-52xx.c cuboot-824x.c cuboot-83xx.c cuboot-85xx.c holly.c \ cuboot-ebony.c treeboot-ebony.c prpmc2800.c \ ps3-head.S ps3-hvcall.S ps3.c treeboot-bamboo.c cuboot-8xx.c \ - cuboot-pq2.c cuboot-sequoia.c treeboot-walnut.c cuboot-bamboo.c \ - fixed-head.S ep88xc.c cuboot-hpc2.c ep405.c cuboot-taishan.c \ + cuboot-pq2.c cuboot-sequoia.c treeboot-walnut.c \ + cuboot-bamboo.c cuboot-mpc7448hpc2.c cuboot-taishan.c \ + fixed-head.S ep88xc.c ep405.c \ cuboot-katmai.c cuboot-rainier.c redboot-8xx.c ep8248e.c \ cuboot-warp.c cuboot-85xx-cpm2.c src-boot := $(src-wlib) $(src-plat) empty.c @@ -123,6 +124,8 @@ targets += $(patsubst $(obj)/%,%,$(obj-boot) wrapper.a) extra-y := $(obj)/wrapper.a $(obj-plat) $(obj)/empty.o \ $(obj)/zImage.lds $(obj)/zImage.coff.lds $(obj)/zImage.ps3.lds +dtstree := $(srctree)/$(src)/dts + wrapper :=$(srctree)/$(src)/wrapper wrapperbits := $(extra-y) $(addprefix $(obj)/,addnote hack-coff mktree dtc) \ $(wrapper) FORCE @@ -181,7 +184,7 @@ quiet_cmd_wrap = WRAP $@ image-$(CONFIG_PPC_PSERIES) += zImage.pseries image-$(CONFIG_PPC_MAPLE) += zImage.pseries image-$(CONFIG_PPC_IBM_CELL_BLADE) += zImage.pseries -image-$(CONFIG_PPC_PS3) += zImage.ps3 +image-$(CONFIG_PPC_PS3) += zImage-dtb.ps3 image-$(CONFIG_PPC_CELLEB) += zImage.pseries image-$(CONFIG_PPC_CHRP) += zImage.chrp image-$(CONFIG_PPC_EFIKA) += zImage.chrp @@ -191,33 +194,69 @@ image-$(CONFIG_PPC_PRPMC2800) += zImage.prpmc2800 image-$(CONFIG_PPC_ISERIES) += zImage.iseries image-$(CONFIG_DEFAULT_UIMAGE) += uImage -ifneq ($(CONFIG_DEVICE_TREE),"") -image-$(CONFIG_PPC_8xx) += cuImage.8xx -image-$(CONFIG_PPC_EP88XC) += zImage.ep88xc -image-$(CONFIG_EP405) += zImage.ep405 -image-$(CONFIG_8260) += cuImage.pq2 -image-$(CONFIG_EP8248E) += zImage.ep8248e -image-$(CONFIG_PPC_MPC52xx) += cuImage.52xx -image-$(CONFIG_STORCENTER) += cuImage.824x -image-$(CONFIG_PPC_83xx) += cuImage.83xx -image-$(CONFIG_PPC_85xx) += cuImage.85xx -ifeq ($(CONFIG_CPM2),y) -image-$(CONFIG_PPC_85xx) += cuImage.85xx-cpm2 -endif -image-$(CONFIG_MPC7448HPC2) += cuImage.hpc2 +# +# Targets which embed a device tree blob +# +# Theses are default targets to build images which embed device tree blobs. +# They are only required on boards which do not have FDT support in firmware. +# Boards with newish u-boot firmare can use the uImage target above +# + +# Board ports in arch/powerpc/platform/40x/Kconfig +image-$(CONFIG_EP405) += zImage-dtb.ep405 +image-$(CONFIG_WALNUT) += treeImage.walnut + +# Board ports in arch/powerpc/platform/44x/Kconfig image-$(CONFIG_EBONY) += treeImage.ebony cuImage.ebony image-$(CONFIG_BAMBOO) += treeImage.bamboo cuImage.bamboo image-$(CONFIG_SEQUOIA) += cuImage.sequoia image-$(CONFIG_RAINIER) += cuImage.rainier -image-$(CONFIG_WALNUT) += treeImage.walnut image-$(CONFIG_TAISHAN) += cuImage.taishan image-$(CONFIG_KATMAI) += cuImage.katmai image-$(CONFIG_WARP) += cuImage.warp -endif -ifneq ($(CONFIG_REDBOOT),"") -image-$(CONFIG_PPC_8xx) += zImage.redboot-8xx -endif +# Board ports in arch/powerpc/platform/8xx/Kconfig +image-$(CONFIG_PPC_MPC86XADS) += cuImage.mpc866ads +image-$(CONFIG_PPC_MPC885ADS) += cuImage.mpc885ads +image-$(CONFIG_PPC_EP88XC) += zImage-dtb.ep88xc +image-$(CONFIG_PPC_ADDER875) += cuImage.adder875-uboot \ + zImage-dtb.adder875-redboot + +# Board ports in arch/powerpc/platform/52xx/Kconfig +image-$(CONFIG_PPC_LITE5200) += cuImage.lite5200 cuImage.lite5200b + +# Board ports in arch/powerpc/platform/82xx/Kconfig +image-$(CONFIG_MPC8272_ADS) += cuImage.mpc8272ads +image-$(CONFIG_PQ2FADS) += cuImage.pq2fads +image-$(CONFIG_EP8248E) += zImage-dtb.ep8248e + +# Board ports in arch/powerpc/platform/83xx/Kconfig +image-$(CONFIG_MPC832x_MDS) += cuImage.mpc832x_mds +image-$(CONFIG_MPC832x_RDB) += cuImage.mpc832x_rdb +image-$(CONFIG_MPC834x_ITX) += cuImage.mpc8349emitx \ + cuImage.mpc8349emitxgp +image-$(CONFIG_MPC834x_MDS) += cuImage.mpc834x_mds +image-$(CONFIG_MPC836x_MDS) += cuImage.mpc836x_mds + +# Board ports in arch/powerpc/platform/85xx/Kconfig +image-$(CONFIG_MPC8540_ADS) += cuImage.mpc8540ads +image-$(CONFIG_MPC8560_ADS) += cuImage.mpc8560ads +image-$(CONFIG_MPC85xx_CDS) += cuImage.mpc8541cds \ + cuImage.mpc8548cds \ + cuImage.mpc8555cds +image-$(CONFIG_MPC85xx_MDS) += cuImage.mpc8568mds +image-$(CONFIG_MPC85xx_DS) += cuImage.mpc8544ds \ + cuImage.mpc8572ds +image-$(CONFIG_TQM8540) += cuImage.tqm8540 +image-$(CONFIG_TQM8541) += cuImage.tqm8541 +image-$(CONFIG_TQM8555) += cuImage.tqm8555 +image-$(CONFIG_TQM8560) += cuImage.tqm8560 +image-$(CONFIG_SBC8548) += cuImage.tqm8548 +image-$(CONFIG_SBC8560) += cuImage.tqm8560 + +# Board ports in arch/powerpc/platform/embedded6xx/Kconfig +image-$(CONFIG_STORCENTER) += cuImage.storcenter +image-$(CONFIG_MPC7448HPC2) += cuImage.mpc7448hpc2 # For 32-bit powermacs, build the COFF and miboot images # as well as the ELF images. @@ -233,24 +272,20 @@ targets += $(image-y) $(initrd-y) $(addprefix $(obj)/, $(initrd-y)): $(obj)/ramdisk.image.gz -# If CONFIG_WANT_DEVICE_TREE is set and CONFIG_DEVICE_TREE isn't an -# empty string, define 'dts' to be path to the dts -# CONFIG_DEVICE_TREE will have "" around it, make sure to strip them -ifeq ($(CONFIG_WANT_DEVICE_TREE),y) -ifneq ($(CONFIG_DEVICE_TREE),"") -dts = $(if $(shell echo $(CONFIG_DEVICE_TREE) | grep '^/'),\ - ,$(srctree)/$(src)/dts/)$(CONFIG_DEVICE_TREE:"%"=%) -endif -endif - # Don't put the ramdisk on the pattern rule; when its missing make will try # the pattern rule with less dependencies that also matches (even with the # hard dependency listed). -$(obj)/zImage.initrd.%: vmlinux $(wrapperbits) $(dts) - $(call if_changed,wrap,$*,$(dts),,$(obj)/ramdisk.image.gz) +$(obj)/zImage.initrd.%: vmlinux $(wrapperbits) + $(call if_changed,wrap,$*,,,$(obj)/ramdisk.image.gz) -$(obj)/zImage.%: vmlinux $(wrapperbits) $(dts) - $(call if_changed,wrap,$*,$(dts)) +$(obj)/zImage.%: vmlinux $(wrapperbits) + $(call if_changed,wrap,$*) + +$(obj)/zImage-dtb.initrd.%: vmlinux $(wrapperbits) $(dtstree)/%.dts + $(call if_changed,wrap,$*,$(dtstree)/$*.dts,,$(obj)/ramdisk.image.gz) + +$(obj)/zImage-dtb.%: vmlinux $(wrapperbits) $(dtstree)/%.dts + $(call if_changed,wrap,$*,$(dtstree)/$*.dts) # This cannot be in the root of $(src) as the zImage rule always adds a $(obj) # prefix @@ -260,24 +295,17 @@ $(obj)/vmlinux.strip: vmlinux $(obj)/zImage.iseries: vmlinux $(STRIP) -s -R .comment $< -o $@ -$(obj)/zImage.ps3: vmlinux $(wrapper) $(wrapperbits) $(srctree)/$(src)/dts/ps3.dts - $(STRIP) -s -R .comment $< -o vmlinux.strip - $(call cmd,wrap,ps3,$(srctree)/$(src)/dts/ps3.dts,,) - -$(obj)/zImage.initrd.ps3: vmlinux $(wrapper) $(wrapperbits) $(srctree)/$(src)/dts/ps3.dts $(obj)/ramdisk.image.gz - $(call cmd,wrap,ps3,$(srctree)/$(src)/dts/ps3.dts,,$(obj)/ramdisk.image.gz) - $(obj)/uImage: vmlinux $(wrapperbits) $(call if_changed,wrap,uboot) -$(obj)/cuImage.%: vmlinux $(dts) $(wrapperbits) - $(call if_changed,wrap,cuboot-$*,$(dts)) +$(obj)/cuImage.%: vmlinux $(dtstree)/%.dts $(wrapperbits) + $(call if_changed,wrap,cuboot-$*,$(dtstree)/$*.dts) -$(obj)/treeImage.initrd.%: vmlinux $(dts) $(wrapperbits) - $(call if_changed,wrap,treeboot-$*,$(dts),,$(obj)/ramdisk.image.gz) +$(obj)/treeImage.initrd.%: vmlinux $(dtstree)/%.dts $(wrapperbits) + $(call if_changed,wrap,treeboot-$*,$(dtstree)/$*.dts,,$(obj)/ramdisk.image.gz) -$(obj)/treeImage.%: vmlinux $(dts) $(wrapperbits) - $(call if_changed,wrap,treeboot-$*,$(dts)) +$(obj)/treeImage.%: vmlinux $(dtstree)/%.dts $(wrapperbits) + $(call if_changed,wrap,treeboot-$*,$(dtstree)/$*.dts) # If there isn't a platform selected then just strip the vmlinux. ifeq (,$(image-y)) diff --git a/arch/powerpc/boot/cuboot-hpc2.c b/arch/powerpc/boot/cuboot-mpc7448hpc2.c index 1b8953259d75..1b8953259d75 100644 --- a/arch/powerpc/boot/cuboot-hpc2.c +++ b/arch/powerpc/boot/cuboot-mpc7448hpc2.c diff --git a/arch/powerpc/boot/dts/adder875-redboot.dts b/arch/powerpc/boot/dts/adder875-redboot.dts index 930bfb3894eb..28e9cd3d7a21 100644 --- a/arch/powerpc/boot/dts/adder875-redboot.dts +++ b/arch/powerpc/boot/dts/adder875-redboot.dts @@ -151,6 +151,7 @@ compatible = "fsl,mpc875-brg", "fsl,cpm1-brg", "fsl,cpm-brg"; + clock-frequency = <50000000>; reg = <0x9f0 0x10>; }; diff --git a/arch/powerpc/boot/dts/adder875-uboot.dts b/arch/powerpc/boot/dts/adder875-uboot.dts index 0197242dacfb..54fb60ec03e5 100644 --- a/arch/powerpc/boot/dts/adder875-uboot.dts +++ b/arch/powerpc/boot/dts/adder875-uboot.dts @@ -150,6 +150,7 @@ compatible = "fsl,mpc875-brg", "fsl,cpm1-brg", "fsl,cpm-brg"; + clock-frequency = <50000000>; reg = <0x9f0 0x10>; }; diff --git a/arch/powerpc/boot/dts/mpc5121ads.dts b/arch/powerpc/boot/dts/mpc5121ads.dts new file mode 100644 index 000000000000..94ad7b2b241e --- /dev/null +++ b/arch/powerpc/boot/dts/mpc5121ads.dts @@ -0,0 +1,122 @@ +/* + * MPC5121E MDS Device Tree Source + * + * Copyright 2007 Freescale Semiconductor Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +/dts-v1/; + +/ { + model = "mpc5121ads"; + compatible = "fsl,mpc5121ads"; + #address-cells = <1>; + #size-cells = <1>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + PowerPC,5121@0 { + device_type = "cpu"; + reg = <0>; + d-cache-line-size = <0x20>; // 32 bytes + i-cache-line-size = <0x20>; // 32 bytes + d-cache-size = <0x8000>; // L1, 32K + i-cache-size = <0x8000>; // L1, 32K + timebase-frequency = <49500000>;// 49.5 MHz (csb/4) + bus-frequency = <198000000>; // 198 MHz csb bus + clock-frequency = <396000000>; // 396 MHz ppc core + }; + }; + + memory { + device_type = "memory"; + reg = <0x00000000 0x10000000>; // 256MB at 0 + }; + + localbus@80000020 { + compatible = "fsl,mpc5121ads-localbus"; + #address-cells = <2>; + #size-cells = <1>; + reg = <0x80000020 0x40>; + + ranges = <0x0 0x0 0xfc000000 0x04000000 + 0x2 0x0 0x82000000 0x00008000>; + + flash@0,0 { + compatible = "cfi-flash"; + reg = <0 0x0 0x4000000>; + bank-width = <4>; + device-width = <1>; + }; + + board-control@2,0 { + compatible = "fsl,mpc5121ads-cpld"; + reg = <0x2 0x0 0x8000>; + }; + }; + + soc@80000000 { + compatible = "fsl,mpc5121-immr"; + #address-cells = <1>; + #size-cells = <1>; + #interrupt-cells = <2>; + ranges = <0x0 0x80000000 0x400000>; + reg = <0x80000000 0x400000>; + bus-frequency = <66000000>; // 66 MHz ips bus + + + // IPIC + // interrupts cell = <intr #, sense> + // sense values match linux IORESOURCE_IRQ_* defines: + // sense == 8: Level, low assertion + // sense == 2: Edge, high-to-low change + // + ipic: interrupt-controller@c00 { + compatible = "fsl,mpc5121-ipic", "fsl,ipic"; + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <2>; + reg = <0xc00 0x100>; + }; + + // 512x PSCs are not 52xx PSCs compatible + // PSC3 serial port A aka ttyPSC0 + serial@11300 { + device_type = "serial"; + compatible = "fsl,mpc5121-psc-uart"; + // Logical port assignment needed until driver + // learns to use aliases + port-number = <0>; + cell-index = <3>; + reg = <0x11300 0x100>; + interrupts = <0x28 0x8>; // actually the fifo irq + interrupt-parent = < &ipic >; + }; + + // PSC4 serial port B aka ttyPSC1 + serial@11400 { + device_type = "serial"; + compatible = "fsl,mpc5121-psc-uart"; + // Logical port assignment needed until driver + // learns to use aliases + port-number = <1>; + cell-index = <4>; + reg = <0x11400 0x100>; + interrupts = <0x28 0x8>; // actually the fifo irq + interrupt-parent = < &ipic >; + }; + + pscsfifo@11f00 { + compatible = "fsl,mpc5121-psc-fifo"; + reg = <0x11f00 0x100>; + interrupts = <0x28 0x8>; + interrupt-parent = < &ipic >; + }; + }; +}; diff --git a/arch/powerpc/boot/dts/mpc8313erdb.dts b/arch/powerpc/boot/dts/mpc8313erdb.dts index 2d6653fe72ff..e1f0dca8ac39 100644 --- a/arch/powerpc/boot/dts/mpc8313erdb.dts +++ b/arch/powerpc/boot/dts/mpc8313erdb.dts @@ -118,6 +118,10 @@ interrupts = <14 0x8>; interrupt-parent = <&ipic>; dfsrr; + rtc@68 { + compatible = "dallas,ds1339"; + reg = <0x68>; + }; }; i2c@3100 { diff --git a/arch/powerpc/boot/dts/mpc8315erdb.dts b/arch/powerpc/boot/dts/mpc8315erdb.dts index b582032ba3d6..d7a1ececa30f 100644 --- a/arch/powerpc/boot/dts/mpc8315erdb.dts +++ b/arch/powerpc/boot/dts/mpc8315erdb.dts @@ -96,7 +96,7 @@ #address-cells = <1>; #size-cells = <1>; device_type = "soc"; - compatible = "simple-bus"; + compatible = "fsl,mpc8315-immr", "simple-bus"; ranges = <0 0xe0000000 0x00100000>; reg = <0xe0000000 0x00000200>; bus-frequency = <0>; diff --git a/arch/powerpc/boot/dts/mpc834x_mds.dts b/arch/powerpc/boot/dts/mpc834x_mds.dts index 7480edae85ed..0199c5c548d8 100644 --- a/arch/powerpc/boot/dts/mpc834x_mds.dts +++ b/arch/powerpc/boot/dts/mpc834x_mds.dts @@ -332,7 +332,7 @@ 0xc000 0x0 0x0 0x3 &ipic 23 0x8 0xc000 0x0 0x0 0x4 &ipic 20 0x8>; interrupt-parent = <&ipic>; - interrupts = <66 0x8>; + interrupts = <67 0x8>; bus-range = <0 0>; ranges = <0x02000000 0x0 0xb0000000 0xb0000000 0x0 0x10000000 0x42000000 0x0 0xa0000000 0xa0000000 0x0 0x10000000 diff --git a/arch/powerpc/boot/dts/mpc8572ds.dts b/arch/powerpc/boot/dts/mpc8572ds.dts index 813c259abbe5..db37214aee37 100644 --- a/arch/powerpc/boot/dts/mpc8572ds.dts +++ b/arch/powerpc/boot/dts/mpc8572ds.dts @@ -42,6 +42,18 @@ bus-frequency = <0>; clock-frequency = <0>; }; + + PowerPC,8572@1 { + device_type = "cpu"; + reg = <1>; + d-cache-line-size = <20>; // 32 bytes + i-cache-line-size = <20>; // 32 bytes + d-cache-size = <8000>; // L1, 32K + i-cache-size = <8000>; // L1, 32K + timebase-frequency = <0>; + bus-frequency = <0>; + clock-frequency = <0>; + }; }; memory { diff --git a/arch/powerpc/boot/dts/mpc885ads.dts b/arch/powerpc/boot/dts/mpc885ads.dts index 8848e637293e..d84a012c2aaf 100644 --- a/arch/powerpc/boot/dts/mpc885ads.dts +++ b/arch/powerpc/boot/dts/mpc885ads.dts @@ -166,6 +166,7 @@ compatible = "fsl,mpc885-brg", "fsl,cpm1-brg", "fsl,cpm-brg"; + clock-frequency = <0>; reg = <9f0 10>; }; diff --git a/arch/powerpc/boot/dts/sequoia.dts b/arch/powerpc/boot/dts/sequoia.dts index d9046c1adcbe..5c13d46f441d 100644 --- a/arch/powerpc/boot/dts/sequoia.dts +++ b/arch/powerpc/boot/dts/sequoia.dts @@ -138,6 +138,14 @@ interrupts = <15 8>; }; + USB0: ehci@e0000300 { + compatible = "ibm,usb-ehci-440epx", "usb-ehci"; + interrupt-parent = <&UIC0>; + interrupts = <1a 4>; + reg = <0 e0000300 90 0 e0000390 70>; + big-endian; + }; + POB0: opb { compatible = "ibm,opb-440epx", "ibm,opb"; #address-cells = <1>; diff --git a/arch/powerpc/boot/dts/storcenter.dts b/arch/powerpc/boot/dts/storcenter.dts index 2204874ac5f3..5893816c0bce 100644 --- a/arch/powerpc/boot/dts/storcenter.dts +++ b/arch/powerpc/boot/dts/storcenter.dts @@ -15,7 +15,7 @@ / { model = "StorCenter"; - compatible = "storcenter"; + compatible = "iomega,storcenter"; #address-cells = <1>; #size-cells = <1>; @@ -62,12 +62,12 @@ #size-cells = <0>; compatible = "fsl-i2c"; reg = <0x3000 0x100>; - interrupts = <5 2>; + interrupts = <17 2>; interrupt-parent = <&mpic>; rtc@68 { compatible = "dallas,ds1337"; - reg = <68>; + reg = <0x68>; }; }; @@ -78,7 +78,7 @@ reg = <0x4500 0x20>; clock-frequency = <97553800>; /* Hz */ current-speed = <115200>; - interrupts = <9 2>; + interrupts = <25 2>; interrupt-parent = <&mpic>; }; @@ -89,7 +89,7 @@ reg = <0x4600 0x20>; clock-frequency = <97553800>; /* Hz */ current-speed = <9600>; - interrupts = <10 2>; + interrupts = <26 2>; interrupt-parent = <&mpic>; }; @@ -136,6 +136,6 @@ }; chosen { - linux,stdout-path = "/soc/serial@4500"; + linux,stdout-path = &serial0; }; }; diff --git a/arch/powerpc/boot/wrapper b/arch/powerpc/boot/wrapper index 763a0c46f441..c3178155311b 100755 --- a/arch/powerpc/boot/wrapper +++ b/arch/powerpc/boot/wrapper @@ -158,6 +158,29 @@ miboot|uboot) cuboot*) binary=y gzip= + case "$platform" in + *-mpc885ads|*-adder875*|*-ep88xc) + platformo=$object/cuboot-8xx.o + ;; + *5200*|*-motionpro) + platformo=$object/cuboot-52xx.o + ;; + *-pq2fads|*-ep8248e|*-mpc8272*|*-storcenter) + platformo=$object/cuboot-pq2.o + ;; + *-mpc824*) + platformo=$object/cuboot-824x.o + ;; + *-mpc83*) + platformo=$object/cuboot-83xx.o + ;; + *-tqm8541|*-mpc8560*|*-tqm8560|*-tqm8555*) + platformo=$object/cuboot-85xx-cpm2.o + ;; + *-mpc85*) + platformo=$object/cuboot-85xx.o + ;; + esac ;; ps3) platformo="$object/ps3-head.o $object/ps3-hvcall.o $object/ps3.o" diff --git a/arch/powerpc/configs/mpc83xx_defconfig b/arch/powerpc/configs/mpc83xx_defconfig index 31bdbf3f7566..a9807f083bc4 100644 --- a/arch/powerpc/configs/mpc83xx_defconfig +++ b/arch/powerpc/configs/mpc83xx_defconfig @@ -186,7 +186,7 @@ CONFIG_PREEMPT_NONE=y # CONFIG_PREEMPT is not set CONFIG_BINFMT_ELF=y # CONFIG_BINFMT_MISC is not set -# CONFIG_MATH_EMULATION is not set +CONFIG_MATH_EMULATION=y CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y CONFIG_ARCH_FLATMEM_ENABLE=y CONFIG_ARCH_POPULATES_NODE_MAP=y @@ -416,14 +416,14 @@ CONFIG_PHYLIB=y # MII PHY device drivers # CONFIG_MARVELL_PHY=y -# CONFIG_DAVICOM_PHY is not set +CONFIG_DAVICOM_PHY=y # CONFIG_QSEMI_PHY is not set # CONFIG_LXT_PHY is not set # CONFIG_CICADA_PHY is not set -# CONFIG_VITESSE_PHY is not set +CONFIG_VITESSE_PHY=y # CONFIG_SMSC_PHY is not set # CONFIG_BROADCOM_PHY is not set -# CONFIG_ICPLUS_PHY is not set +CONFIG_ICPLUS_PHY=y # CONFIG_FIXED_PHY is not set # CONFIG_MDIO_BITBANG is not set CONFIG_NET_ETHERNET=y @@ -436,7 +436,7 @@ CONFIG_MII=y CONFIG_NETDEV_1000=y CONFIG_GIANFAR=y # CONFIG_GFAR_NAPI is not set -# CONFIG_UCC_GETH is not set +CONFIG_UCC_GETH=y CONFIG_NETDEV_10000=y # diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index 58dbfeff9b4d..0662ae46f724 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -2,6 +2,8 @@ # Makefile for the linux kernel. # +CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"' + ifeq ($(CONFIG_PPC64),y) CFLAGS_prom_init.o += -mno-minimal-toc endif @@ -15,7 +17,7 @@ obj-y := semaphore.o cputable.o ptrace.o syscalls.o \ init_task.o process.o systbl.o idle.o \ signal.o obj-y += vdso32/ -obj-$(CONFIG_PPC64) += setup_64.o binfmt_elf32.o sys_ppc32.o \ +obj-$(CONFIG_PPC64) += setup_64.o sys_ppc32.o \ signal_64.o ptrace32.o \ paca.o cpu_setup_ppc970.o \ cpu_setup_pa6t.o \ diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index ed083feaf6f9..e6e49289f788 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -22,6 +22,7 @@ #include <linux/mman.h> #include <linux/mm.h> #include <linux/suspend.h> +#include <linux/hrtimer.h> #ifdef CONFIG_PPC64 #include <linux/time.h> #include <linux/hardirq.h> @@ -312,7 +313,7 @@ int main(void) DEFINE(CLOCK_REALTIME, CLOCK_REALTIME); DEFINE(CLOCK_MONOTONIC, CLOCK_MONOTONIC); DEFINE(NSEC_PER_SEC, NSEC_PER_SEC); - DEFINE(CLOCK_REALTIME_RES, TICK_NSEC); + DEFINE(CLOCK_REALTIME_RES, (KTIME_MONOTONIC_RES).tv64); #ifdef CONFIG_BUG DEFINE(BUG_ENTRY_SIZE, sizeof(struct bug_entry)); diff --git a/arch/powerpc/kernel/binfmt_elf32.c b/arch/powerpc/kernel/binfmt_elf32.c deleted file mode 100644 index 1d45d7782d4e..000000000000 --- a/arch/powerpc/kernel/binfmt_elf32.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * binfmt_elf32.c: Support 32-bit PPC ELF binaries on Power3 and followons. - * based on the SPARC64 version. - * Copyright (C) 1995, 1996, 1997, 1998 David S. Miller (davem@redhat.com) - * Copyright (C) 1995, 1996, 1997, 1998 Jakub Jelinek (jj@ultra.linux.cz) - * - * Copyright (C) 2000,2001 Ken Aaker (kdaaker@rchland.vnet.ibm.com), IBM Corp - * Copyright (C) 2001 Anton Blanchard (anton@au.ibm.com), IBM - * - * 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 <asm/processor.h> -#include <linux/module.h> -#include <linux/compat.h> -#include <linux/elfcore-compat.h> - -#undef ELF_ARCH -#undef ELF_CLASS -#define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_PPC - -#undef elfhdr -#undef elf_phdr -#undef elf_note -#undef elf_addr_t -#define elfhdr elf32_hdr -#define elf_phdr elf32_phdr -#define elf_note elf32_note -#define elf_addr_t Elf32_Off - -#define elf_prstatus compat_elf_prstatus -#define elf_prpsinfo compat_elf_prpsinfo - -#define elf_core_copy_regs compat_elf_core_copy_regs -static inline void compat_elf_core_copy_regs(compat_elf_gregset_t *elf_regs, - struct pt_regs *regs) -{ - PPC_ELF_CORE_COPY_REGS((*elf_regs), regs); -} - -#define elf_core_copy_task_regs compat_elf_core_copy_task_regs -static int compat_elf_core_copy_task_regs(struct task_struct *tsk, - compat_elf_gregset_t *elf_regs) -{ - struct pt_regs *regs = tsk->thread.regs; - if (regs) - compat_elf_core_copy_regs(elf_regs, regs); - return 1; -} - -#include <linux/time.h> - -#undef cputime_to_timeval -#define cputime_to_timeval cputime_to_compat_timeval -static __inline__ void -cputime_to_compat_timeval(const cputime_t cputime, struct compat_timeval *value) -{ - unsigned long jiffies = cputime_to_jiffies(cputime); - value->tv_usec = (jiffies % HZ) * (1000000L / HZ); - value->tv_sec = jiffies / HZ; -} - -#define init_elf_binfmt init_elf32_binfmt - -#include "../../../fs/binfmt_elf.c" diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c index a4c2771b5e62..2a8f5cc5184f 100644 --- a/arch/powerpc/kernel/cputable.c +++ b/arch/powerpc/kernel/cputable.c @@ -959,6 +959,9 @@ static struct cpu_spec __initdata cpu_specs[] = { .icache_bsize = 32, .dcache_bsize = 32, .cpu_setup = __setup_cpu_603, + .num_pmcs = 4, + .oprofile_cpu_type = "ppc/e300", + .oprofile_type = PPC_OPROFILE_FSL_EMB, .platform = "ppc603", }, { /* e300c4 (e300c1, plus one IU) */ @@ -971,6 +974,9 @@ static struct cpu_spec __initdata cpu_specs[] = { .dcache_bsize = 32, .cpu_setup = __setup_cpu_603, .machine_check = machine_check_generic, + .num_pmcs = 4, + .oprofile_cpu_type = "ppc/e300", + .oprofile_type = PPC_OPROFILE_FSL_EMB, .platform = "ppc603", }, { /* default match, we assume split I/D cache & TB (non-601)... */ @@ -1435,7 +1441,7 @@ static struct cpu_spec __initdata cpu_specs[] = { .dcache_bsize = 32, .num_pmcs = 4, .oprofile_cpu_type = "ppc/e500", - .oprofile_type = PPC_OPROFILE_BOOKE, + .oprofile_type = PPC_OPROFILE_FSL_EMB, .machine_check = machine_check_e500, .platform = "ppc8540", }, @@ -1453,7 +1459,7 @@ static struct cpu_spec __initdata cpu_specs[] = { .dcache_bsize = 32, .num_pmcs = 4, .oprofile_cpu_type = "ppc/e500", - .oprofile_type = PPC_OPROFILE_BOOKE, + .oprofile_type = PPC_OPROFILE_FSL_EMB, .machine_check = machine_check_e500, .platform = "ppc8548", }, diff --git a/arch/powerpc/kernel/legacy_serial.c b/arch/powerpc/kernel/legacy_serial.c index 76b862bd1fe9..61dd17449ddc 100644 --- a/arch/powerpc/kernel/legacy_serial.c +++ b/arch/powerpc/kernel/legacy_serial.c @@ -36,7 +36,8 @@ static struct legacy_serial_info { static struct __initdata of_device_id parents[] = { {.type = "soc",}, {.type = "tsi-bridge",}, - {.type = "opb", .compatible = "ibm,opb",}, + {.type = "opb", }, + {.compatible = "ibm,opb",}, {.compatible = "simple-bus",}, {.compatible = "wrs,epld-localbus",}, }; diff --git a/arch/powerpc/kernel/pmc.c b/arch/powerpc/kernel/pmc.c index ea04e0ab3f2f..0516e2d3e02e 100644 --- a/arch/powerpc/kernel/pmc.c +++ b/arch/powerpc/kernel/pmc.c @@ -26,7 +26,7 @@ static void dummy_perf(struct pt_regs *regs) { -#if defined(CONFIG_FSL_BOOKE) && !defined(CONFIG_E200) +#if defined(CONFIG_FSL_EMB_PERFMON) mtpmr(PMRN_PMGC0, mfpmr(PMRN_PMGC0) & ~PMGC0_PMIE); #elif defined(CONFIG_PPC64) || defined(CONFIG_6xx) if (cur_cpu_spec->pmc_type == PPC_PMC_IBM) diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index 8b056d2295cc..7673e9865733 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c @@ -21,6 +21,8 @@ #include <linux/smp.h> #include <linux/errno.h> #include <linux/ptrace.h> +#include <linux/regset.h> +#include <linux/elf.h> #include <linux/user.h> #include <linux/security.h> #include <linux/signal.h> @@ -58,20 +60,38 @@ #define PT_MAX_PUT_REG PT_CCR #endif +static unsigned long get_user_msr(struct task_struct *task) +{ + return task->thread.regs->msr | task->thread.fpexc_mode; +} + +static int set_user_msr(struct task_struct *task, unsigned long msr) +{ + task->thread.regs->msr &= ~MSR_DEBUGCHANGE; + task->thread.regs->msr |= msr & MSR_DEBUGCHANGE; + return 0; +} + +/* + * We prevent mucking around with the reserved area of trap + * which are used internally by the kernel. + */ +static int set_user_trap(struct task_struct *task, unsigned long trap) +{ + task->thread.regs->trap = trap & 0xfff0; + return 0; +} + /* * Get contents of register REGNO in task TASK. */ unsigned long ptrace_get_reg(struct task_struct *task, int regno) { - unsigned long tmp = 0; - if (task->thread.regs == NULL) return -EIO; - if (regno == PT_MSR) { - tmp = ((unsigned long *)task->thread.regs)[PT_MSR]; - return tmp | task->thread.fpexc_mode; - } + if (regno == PT_MSR) + return get_user_msr(task); if (regno < (sizeof(struct pt_regs) / sizeof(unsigned long))) return ((unsigned long *)task->thread.regs)[regno]; @@ -87,40 +107,134 @@ int ptrace_put_reg(struct task_struct *task, int regno, unsigned long data) if (task->thread.regs == NULL) return -EIO; - if (regno <= PT_MAX_PUT_REG || regno == PT_TRAP) { - if (regno == PT_MSR) - data = (data & MSR_DEBUGCHANGE) - | (task->thread.regs->msr & ~MSR_DEBUGCHANGE); - /* We prevent mucking around with the reserved area of trap - * which are used internally by the kernel - */ - if (regno == PT_TRAP) - data &= 0xfff0; + if (regno == PT_MSR) + return set_user_msr(task, data); + if (regno == PT_TRAP) + return set_user_trap(task, data); + + if (regno <= PT_MAX_PUT_REG) { ((unsigned long *)task->thread.regs)[regno] = data; return 0; } return -EIO; } +static int gpr_get(struct task_struct *target, const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + int ret; + + if (target->thread.regs == NULL) + return -EIO; + + CHECK_FULL_REGS(target->thread.regs); + + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + target->thread.regs, + 0, offsetof(struct pt_regs, msr)); + if (!ret) { + unsigned long msr = get_user_msr(target); + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &msr, + offsetof(struct pt_regs, msr), + offsetof(struct pt_regs, msr) + + sizeof(msr)); + } + + BUILD_BUG_ON(offsetof(struct pt_regs, orig_gpr3) != + offsetof(struct pt_regs, msr) + sizeof(long)); + + if (!ret) + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.regs->orig_gpr3, + offsetof(struct pt_regs, orig_gpr3), + sizeof(struct pt_regs)); + if (!ret) + ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + sizeof(struct pt_regs), -1); + + return ret; +} + +static int gpr_set(struct task_struct *target, const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + unsigned long reg; + int ret; + + if (target->thread.regs == NULL) + return -EIO; + + CHECK_FULL_REGS(target->thread.regs); + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + target->thread.regs, + 0, PT_MSR * sizeof(reg)); -static int get_fpregs(void __user *data, struct task_struct *task, - int has_fpscr) + if (!ret && count > 0) { + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, ®, + PT_MSR * sizeof(reg), + (PT_MSR + 1) * sizeof(reg)); + if (!ret) + ret = set_user_msr(target, reg); + } + + BUILD_BUG_ON(offsetof(struct pt_regs, orig_gpr3) != + offsetof(struct pt_regs, msr) + sizeof(long)); + + if (!ret) + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.regs->orig_gpr3, + PT_ORIG_R3 * sizeof(reg), + (PT_MAX_PUT_REG + 1) * sizeof(reg)); + + if (PT_MAX_PUT_REG + 1 < PT_TRAP && !ret) + ret = user_regset_copyin_ignore( + &pos, &count, &kbuf, &ubuf, + (PT_MAX_PUT_REG + 1) * sizeof(reg), + PT_TRAP * sizeof(reg)); + + if (!ret && count > 0) { + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, ®, + PT_TRAP * sizeof(reg), + (PT_TRAP + 1) * sizeof(reg)); + if (!ret) + ret = set_user_trap(target, reg); + } + + if (!ret) + ret = user_regset_copyin_ignore( + &pos, &count, &kbuf, &ubuf, + (PT_TRAP + 1) * sizeof(reg), -1); + + return ret; +} + +static int fpr_get(struct task_struct *target, const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) { - unsigned int count = has_fpscr ? 33 : 32; + flush_fp_to_thread(target); - if (copy_to_user(data, task->thread.fpr, count * sizeof(double))) - return -EFAULT; - return 0; + BUILD_BUG_ON(offsetof(struct thread_struct, fpscr) != + offsetof(struct thread_struct, fpr[32])); + + return user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.fpr, 0, -1); } -static int set_fpregs(void __user *data, struct task_struct *task, - int has_fpscr) +static int fpr_set(struct task_struct *target, const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) { - unsigned int count = has_fpscr ? 33 : 32; + flush_fp_to_thread(target); - if (copy_from_user(task->thread.fpr, data, count * sizeof(double))) - return -EFAULT; - return 0; + BUILD_BUG_ON(offsetof(struct thread_struct, fpscr) != + offsetof(struct thread_struct, fpr[32])); + + return user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.fpr, 0, -1); } @@ -138,56 +252,74 @@ static int set_fpregs(void __user *data, struct task_struct *task, * (combined (32- and 64-bit) gdb. */ -/* - * Get contents of AltiVec register state in task TASK - */ -static int get_vrregs(unsigned long __user *data, struct task_struct *task) +static int vr_active(struct task_struct *target, + const struct user_regset *regset) { - unsigned long regsize; + flush_altivec_to_thread(target); + return target->thread.used_vr ? regset->n : 0; +} - /* copy AltiVec registers VR[0] .. VR[31] */ - regsize = 32 * sizeof(vector128); - if (copy_to_user(data, task->thread.vr, regsize)) - return -EFAULT; - data += (regsize / sizeof(unsigned long)); +static int vr_get(struct task_struct *target, const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + int ret; - /* copy VSCR */ - regsize = 1 * sizeof(vector128); - if (copy_to_user(data, &task->thread.vscr, regsize)) - return -EFAULT; - data += (regsize / sizeof(unsigned long)); + flush_altivec_to_thread(target); - /* copy VRSAVE */ - if (put_user(task->thread.vrsave, (u32 __user *)data)) - return -EFAULT; + BUILD_BUG_ON(offsetof(struct thread_struct, vscr) != + offsetof(struct thread_struct, vr[32])); - return 0; + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.vr, 0, + 33 * sizeof(vector128)); + if (!ret) { + /* + * Copy out only the low-order word of vrsave. + */ + union { + elf_vrreg_t reg; + u32 word; + } vrsave; + memset(&vrsave, 0, sizeof(vrsave)); + vrsave.word = target->thread.vrsave; + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &vrsave, + 33 * sizeof(vector128), -1); + } + + return ret; } -/* - * Write contents of AltiVec register state into task TASK. - */ -static int set_vrregs(struct task_struct *task, unsigned long __user *data) +static int vr_set(struct task_struct *target, const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) { - unsigned long regsize; + int ret; - /* copy AltiVec registers VR[0] .. VR[31] */ - regsize = 32 * sizeof(vector128); - if (copy_from_user(task->thread.vr, data, regsize)) - return -EFAULT; - data += (regsize / sizeof(unsigned long)); + flush_altivec_to_thread(target); - /* copy VSCR */ - regsize = 1 * sizeof(vector128); - if (copy_from_user(&task->thread.vscr, data, regsize)) - return -EFAULT; - data += (regsize / sizeof(unsigned long)); + BUILD_BUG_ON(offsetof(struct thread_struct, vscr) != + offsetof(struct thread_struct, vr[32])); - /* copy VRSAVE */ - if (get_user(task->thread.vrsave, (u32 __user *)data)) - return -EFAULT; + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.vr, 0, 33 * sizeof(vector128)); + if (!ret && count > 0) { + /* + * We use only the first word of vrsave. + */ + union { + elf_vrreg_t reg; + u32 word; + } vrsave; + memset(&vrsave, 0, sizeof(vrsave)); + vrsave.word = target->thread.vrsave; + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &vrsave, + 33 * sizeof(vector128), -1); + if (!ret) + target->thread.vrsave = vrsave.word; + } - return 0; + return ret; } #endif /* CONFIG_ALTIVEC */ @@ -203,57 +335,273 @@ static int set_vrregs(struct task_struct *task, unsigned long __user *data) * } */ -/* - * Get contents of SPE register state in task TASK. - */ -static int get_evrregs(unsigned long *data, struct task_struct *task) +static int evr_active(struct task_struct *target, + const struct user_regset *regset) { - int i; + flush_spe_to_thread(target); + return target->thread.used_spe ? regset->n : 0; +} - if (!access_ok(VERIFY_WRITE, data, 35 * sizeof(unsigned long))) - return -EFAULT; +static int evr_get(struct task_struct *target, const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + int ret; - /* copy SPEFSCR */ - if (__put_user(task->thread.spefscr, &data[34])) - return -EFAULT; + flush_spe_to_thread(target); - /* copy SPE registers EVR[0] .. EVR[31] */ - for (i = 0; i < 32; i++, data++) - if (__put_user(task->thread.evr[i], data)) - return -EFAULT; + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.evr, + 0, sizeof(target->thread.evr)); - /* copy ACC */ - if (__put_user64(task->thread.acc, (unsigned long long *)data)) - return -EFAULT; + BUILD_BUG_ON(offsetof(struct thread_struct, acc) + sizeof(u64) != + offsetof(struct thread_struct, spefscr)); - return 0; + if (!ret) + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.acc, + sizeof(target->thread.evr), -1); + + return ret; +} + +static int evr_set(struct task_struct *target, const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + int ret; + + flush_spe_to_thread(target); + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.evr, + 0, sizeof(target->thread.evr)); + + BUILD_BUG_ON(offsetof(struct thread_struct, acc) + sizeof(u64) != + offsetof(struct thread_struct, spefscr)); + + if (!ret) + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.acc, + sizeof(target->thread.evr), -1); + + return ret; } +#endif /* CONFIG_SPE */ + /* - * Write contents of SPE register state into task TASK. + * These are our native regset flavors. */ -static int set_evrregs(struct task_struct *task, unsigned long *data) +enum powerpc_regset { + REGSET_GPR, + REGSET_FPR, +#ifdef CONFIG_ALTIVEC + REGSET_VMX, +#endif +#ifdef CONFIG_SPE + REGSET_SPE, +#endif +}; + +static const struct user_regset native_regsets[] = { + [REGSET_GPR] = { + .core_note_type = NT_PRSTATUS, .n = ELF_NGREG, + .size = sizeof(long), .align = sizeof(long), + .get = gpr_get, .set = gpr_set + }, + [REGSET_FPR] = { + .core_note_type = NT_PRFPREG, .n = ELF_NFPREG, + .size = sizeof(double), .align = sizeof(double), + .get = fpr_get, .set = fpr_set + }, +#ifdef CONFIG_ALTIVEC + [REGSET_VMX] = { + .core_note_type = NT_PPC_VMX, .n = 34, + .size = sizeof(vector128), .align = sizeof(vector128), + .active = vr_active, .get = vr_get, .set = vr_set + }, +#endif +#ifdef CONFIG_SPE + [REGSET_SPE] = { + .n = 35, + .size = sizeof(u32), .align = sizeof(u32), + .active = evr_active, .get = evr_get, .set = evr_set + }, +#endif +}; + +static const struct user_regset_view user_ppc_native_view = { + .name = UTS_MACHINE, .e_machine = ELF_ARCH, .ei_osabi = ELF_OSABI, + .regsets = native_regsets, .n = ARRAY_SIZE(native_regsets) +}; + +#ifdef CONFIG_PPC64 +#include <linux/compat.h> + +static int gpr32_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) { - int i; + const unsigned long *regs = &target->thread.regs->gpr[0]; + compat_ulong_t *k = kbuf; + compat_ulong_t __user *u = ubuf; + compat_ulong_t reg; + + if (target->thread.regs == NULL) + return -EIO; + + CHECK_FULL_REGS(target->thread.regs); - if (!access_ok(VERIFY_READ, data, 35 * sizeof(unsigned long))) - return -EFAULT; + pos /= sizeof(reg); + count /= sizeof(reg); - /* copy SPEFSCR */ - if (__get_user(task->thread.spefscr, &data[34])) - return -EFAULT; + if (kbuf) + for (; count > 0 && pos < PT_MSR; --count) + *k++ = regs[pos++]; + else + for (; count > 0 && pos < PT_MSR; --count) + if (__put_user((compat_ulong_t) regs[pos++], u++)) + return -EFAULT; - /* copy SPE registers EVR[0] .. EVR[31] */ - for (i = 0; i < 32; i++, data++) - if (__get_user(task->thread.evr[i], data)) + if (count > 0 && pos == PT_MSR) { + reg = get_user_msr(target); + if (kbuf) + *k++ = reg; + else if (__put_user(reg, u++)) return -EFAULT; - /* copy ACC */ - if (__get_user64(task->thread.acc, (unsigned long long*)data)) - return -EFAULT; + ++pos; + --count; + } - return 0; + if (kbuf) + for (; count > 0 && pos < PT_REGS_COUNT; --count) + *k++ = regs[pos++]; + else + for (; count > 0 && pos < PT_REGS_COUNT; --count) + if (__put_user((compat_ulong_t) regs[pos++], u++)) + return -EFAULT; + + kbuf = k; + ubuf = u; + pos *= sizeof(reg); + count *= sizeof(reg); + return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + PT_REGS_COUNT * sizeof(reg), -1); +} + +static int gpr32_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + unsigned long *regs = &target->thread.regs->gpr[0]; + const compat_ulong_t *k = kbuf; + const compat_ulong_t __user *u = ubuf; + compat_ulong_t reg; + + if (target->thread.regs == NULL) + return -EIO; + + CHECK_FULL_REGS(target->thread.regs); + + pos /= sizeof(reg); + count /= sizeof(reg); + + if (kbuf) + for (; count > 0 && pos < PT_MSR; --count) + regs[pos++] = *k++; + else + for (; count > 0 && pos < PT_MSR; --count) { + if (__get_user(reg, u++)) + return -EFAULT; + regs[pos++] = reg; + } + + + if (count > 0 && pos == PT_MSR) { + if (kbuf) + reg = *k++; + else if (__get_user(reg, u++)) + return -EFAULT; + set_user_msr(target, reg); + ++pos; + --count; + } + + if (kbuf) + for (; count > 0 && pos <= PT_MAX_PUT_REG; --count) + regs[pos++] = *k++; + else + for (; count > 0 && pos <= PT_MAX_PUT_REG; --count) { + if (__get_user(reg, u++)) + return -EFAULT; + regs[pos++] = reg; + } + + if (count > 0 && pos == PT_TRAP) { + if (kbuf) + reg = *k++; + else if (__get_user(reg, u++)) + return -EFAULT; + set_user_trap(target, reg); + ++pos; + --count; + } + + kbuf = k; + ubuf = u; + pos *= sizeof(reg); + count *= sizeof(reg); + return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + (PT_TRAP + 1) * sizeof(reg), -1); +} + +/* + * These are the regset flavors matching the CONFIG_PPC32 native set. + */ +static const struct user_regset compat_regsets[] = { + [REGSET_GPR] = { + .core_note_type = NT_PRSTATUS, .n = ELF_NGREG, + .size = sizeof(compat_long_t), .align = sizeof(compat_long_t), + .get = gpr32_get, .set = gpr32_set + }, + [REGSET_FPR] = { + .core_note_type = NT_PRFPREG, .n = ELF_NFPREG, + .size = sizeof(double), .align = sizeof(double), + .get = fpr_get, .set = fpr_set + }, +#ifdef CONFIG_ALTIVEC + [REGSET_VMX] = { + .core_note_type = NT_PPC_VMX, .n = 34, + .size = sizeof(vector128), .align = sizeof(vector128), + .active = vr_active, .get = vr_get, .set = vr_set + }, +#endif +#ifdef CONFIG_SPE + [REGSET_SPE] = { + .core_note_type = NT_PPC_SPE, .n = 35, + .size = sizeof(u32), .align = sizeof(u32), + .active = evr_active, .get = evr_get, .set = evr_set + }, +#endif +}; + +static const struct user_regset_view user_ppc_compat_view = { + .name = "ppc", .e_machine = EM_PPC, .ei_osabi = ELF_OSABI, + .regsets = compat_regsets, .n = ARRAY_SIZE(compat_regsets) +}; +#endif /* CONFIG_PPC64 */ + +const struct user_regset_view *task_user_regset_view(struct task_struct *task) +{ +#ifdef CONFIG_PPC64 + if (test_tsk_thread_flag(task, TIF_32BIT)) + return &user_ppc_compat_view; +#endif + return &user_ppc_native_view; } -#endif /* CONFIG_SPE */ void user_enable_single_step(struct task_struct *task) @@ -323,55 +671,29 @@ void ptrace_disable(struct task_struct *child) static long arch_ptrace_old(struct task_struct *child, long request, long addr, long data) { - int ret = -EPERM; - - switch(request) { - case PPC_PTRACE_GETREGS: { /* Get GPRs 0 - 31. */ - int i; - unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; - unsigned long __user *tmp = (unsigned long __user *)addr; - - CHECK_FULL_REGS(child->thread.regs); - for (i = 0; i < 32; i++) { - ret = put_user(*reg, tmp); - if (ret) - break; - reg++; - tmp++; - } - break; - } - - case PPC_PTRACE_SETREGS: { /* Set GPRs 0 - 31. */ - int i; - unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; - unsigned long __user *tmp = (unsigned long __user *)addr; - - CHECK_FULL_REGS(child->thread.regs); - for (i = 0; i < 32; i++) { - ret = get_user(*reg, tmp); - if (ret) - break; - reg++; - tmp++; - } - break; - } - - case PPC_PTRACE_GETFPREGS: { /* Get FPRs 0 - 31. */ - flush_fp_to_thread(child); - ret = get_fpregs((void __user *)addr, child, 0); - break; - } - - case PPC_PTRACE_SETFPREGS: { /* Get FPRs 0 - 31. */ - flush_fp_to_thread(child); - ret = set_fpregs((void __user *)addr, child, 0); - break; + switch (request) { + case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */ + return copy_regset_to_user(child, &user_ppc_native_view, + REGSET_GPR, 0, 32 * sizeof(long), + (void __user *) data); + + case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */ + return copy_regset_from_user(child, &user_ppc_native_view, + REGSET_GPR, 0, 32 * sizeof(long), + (const void __user *) data); + + case PPC_PTRACE_GETFPREGS: /* Get FPRs 0 - 31. */ + return copy_regset_to_user(child, &user_ppc_native_view, + REGSET_FPR, 0, 32 * sizeof(double), + (void __user *) data); + + case PPC_PTRACE_SETFPREGS: /* Set FPRs 0 - 31. */ + return copy_regset_from_user(child, &user_ppc_native_view, + REGSET_FPR, 0, 32 * sizeof(double), + (const void __user *) data); } - } - return ret; + return -EPERM; } long arch_ptrace(struct task_struct *child, long request, long addr, long data) @@ -379,12 +701,6 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) int ret = -EPERM; switch (request) { - /* when I and D space are separate, these will need to be fixed. */ - case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: - ret = generic_ptrace_peekdata(child, addr, data); - break; - /* read the word at location addr in the USER area. */ case PTRACE_PEEKUSR: { unsigned long index, tmp; @@ -412,12 +728,6 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) break; } - /* If I and D space are separate, this will have to be fixed. */ - case PTRACE_POKETEXT: /* write the word at location addr. */ - case PTRACE_POKEDATA: - ret = generic_ptrace_pokedata(child, addr, data); - break; - /* write the word at location addr in the USER area */ case PTRACE_POKEUSR: { unsigned long index; @@ -462,85 +772,60 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) #ifdef CONFIG_PPC64 case PTRACE_GETREGS64: #endif - case PTRACE_GETREGS: { /* Get all pt_regs from the child. */ - int ui; - if (!access_ok(VERIFY_WRITE, (void __user *)data, - sizeof(struct pt_regs))) { - ret = -EIO; - break; - } - CHECK_FULL_REGS(child->thread.regs); - ret = 0; - for (ui = 0; ui < PT_REGS_COUNT; ui ++) { - ret |= __put_user(ptrace_get_reg(child, ui), - (unsigned long __user *) data); - data += sizeof(long); - } - break; - } + case PTRACE_GETREGS: /* Get all pt_regs from the child. */ + return copy_regset_to_user(child, &user_ppc_native_view, + REGSET_GPR, + 0, sizeof(struct pt_regs), + (void __user *) data); #ifdef CONFIG_PPC64 case PTRACE_SETREGS64: #endif - case PTRACE_SETREGS: { /* Set all gp regs in the child. */ - unsigned long tmp; - int ui; - if (!access_ok(VERIFY_READ, (void __user *)data, - sizeof(struct pt_regs))) { - ret = -EIO; - break; - } - CHECK_FULL_REGS(child->thread.regs); - ret = 0; - for (ui = 0; ui < PT_REGS_COUNT; ui ++) { - ret = __get_user(tmp, (unsigned long __user *) data); - if (ret) - break; - ptrace_put_reg(child, ui, tmp); - data += sizeof(long); - } - break; - } - - case PTRACE_GETFPREGS: { /* Get the child FPU state (FPR0...31 + FPSCR) */ - flush_fp_to_thread(child); - ret = get_fpregs((void __user *)data, child, 1); - break; - } - - case PTRACE_SETFPREGS: { /* Set the child FPU state (FPR0...31 + FPSCR) */ - flush_fp_to_thread(child); - ret = set_fpregs((void __user *)data, child, 1); - break; - } + case PTRACE_SETREGS: /* Set all gp regs in the child. */ + return copy_regset_from_user(child, &user_ppc_native_view, + REGSET_GPR, + 0, sizeof(struct pt_regs), + (const void __user *) data); + + case PTRACE_GETFPREGS: /* Get the child FPU state (FPR0...31 + FPSCR) */ + return copy_regset_to_user(child, &user_ppc_native_view, + REGSET_FPR, + 0, sizeof(elf_fpregset_t), + (void __user *) data); + + case PTRACE_SETFPREGS: /* Set the child FPU state (FPR0...31 + FPSCR) */ + return copy_regset_from_user(child, &user_ppc_native_view, + REGSET_FPR, + 0, sizeof(elf_fpregset_t), + (const void __user *) data); #ifdef CONFIG_ALTIVEC case PTRACE_GETVRREGS: - /* Get the child altivec register state. */ - flush_altivec_to_thread(child); - ret = get_vrregs((unsigned long __user *)data, child); - break; + return copy_regset_to_user(child, &user_ppc_native_view, + REGSET_VMX, + 0, (33 * sizeof(vector128) + + sizeof(u32)), + (void __user *) data); case PTRACE_SETVRREGS: - /* Set the child altivec register state. */ - flush_altivec_to_thread(child); - ret = set_vrregs(child, (unsigned long __user *)data); - break; + return copy_regset_from_user(child, &user_ppc_native_view, + REGSET_VMX, + 0, (33 * sizeof(vector128) + + sizeof(u32)), + (const void __user *) data); #endif #ifdef CONFIG_SPE case PTRACE_GETEVRREGS: /* Get the child spe register state. */ - flush_spe_to_thread(child); - ret = get_evrregs((unsigned long __user *)data, child); - break; + return copy_regset_to_user(child, &user_ppc_native_view, + REGSET_SPE, 0, 35 * sizeof(u32), + (void __user *) data); case PTRACE_SETEVRREGS: /* Set the child spe register state. */ - /* this is to clear the MSR_SPE bit to force a reload - * of register state from memory */ - flush_spe_to_thread(child); - ret = set_evrregs(child, (unsigned long __user *)data); - break; + return copy_regset_from_user(child, &user_ppc_native_view, + REGSET_SPE, 0, 35 * sizeof(u32), + (const void __user *) data); #endif /* Old reverse args ptrace callss */ diff --git a/arch/powerpc/kernel/ptrace32.c b/arch/powerpc/kernel/ptrace32.c index fea6206ff90f..4c1de6af4c09 100644 --- a/arch/powerpc/kernel/ptrace32.c +++ b/arch/powerpc/kernel/ptrace32.c @@ -24,9 +24,11 @@ #include <linux/smp_lock.h> #include <linux/errno.h> #include <linux/ptrace.h> +#include <linux/regset.h> #include <linux/user.h> #include <linux/security.h> #include <linux/signal.h> +#include <linux/compat.h> #include <asm/uaccess.h> #include <asm/page.h> @@ -45,87 +47,31 @@ static long compat_ptrace_old(struct task_struct *child, long request, long addr, long data) { - int ret = -EPERM; - - switch(request) { - case PPC_PTRACE_GETREGS: { /* Get GPRs 0 - 31. */ - int i; - unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; - unsigned int __user *tmp = (unsigned int __user *)addr; - - CHECK_FULL_REGS(child->thread.regs); - for (i = 0; i < 32; i++) { - ret = put_user(*reg, tmp); - if (ret) - break; - reg++; - tmp++; - } - break; - } - - case PPC_PTRACE_SETREGS: { /* Set GPRs 0 - 31. */ - int i; - unsigned long *reg = &((unsigned long *)child->thread.regs)[0]; - unsigned int __user *tmp = (unsigned int __user *)addr; - - CHECK_FULL_REGS(child->thread.regs); - for (i = 0; i < 32; i++) { - ret = get_user(*reg, tmp); - if (ret) - break; - reg++; - tmp++; - } - break; + switch (request) { + case PPC_PTRACE_GETREGS: /* Get GPRs 0 - 31. */ + return copy_regset_to_user(child, + task_user_regset_view(current), 0, + 0, 32 * sizeof(compat_long_t), + compat_ptr(data)); + + case PPC_PTRACE_SETREGS: /* Set GPRs 0 - 31. */ + return copy_regset_from_user(child, + task_user_regset_view(current), 0, + 0, 32 * sizeof(compat_long_t), + compat_ptr(data)); } - } - return ret; + return -EPERM; } -long compat_sys_ptrace(int request, int pid, unsigned long addr, - unsigned long data) +long compat_arch_ptrace(struct task_struct *child, compat_long_t request, + compat_ulong_t caddr, compat_ulong_t cdata) { - struct task_struct *child; + unsigned long addr = caddr; + unsigned long data = cdata; int ret; - lock_kernel(); - if (request == PTRACE_TRACEME) { - ret = ptrace_traceme(); - goto out; - } - - child = ptrace_get_task_struct(pid); - if (IS_ERR(child)) { - ret = PTR_ERR(child); - goto out; - } - - if (request == PTRACE_ATTACH) { - ret = ptrace_attach(child); - goto out_tsk; - } - - ret = ptrace_check_attach(child, request == PTRACE_KILL); - if (ret < 0) - goto out_tsk; - switch (request) { - /* when I and D space are separate, these will need to be fixed. */ - case PTRACE_PEEKTEXT: /* read word at location addr. */ - case PTRACE_PEEKDATA: { - unsigned int tmp; - int copied; - - copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); - ret = -EIO; - if (copied != sizeof(tmp)) - break; - ret = put_user(tmp, (u32 __user *)data); - break; - } - /* * Read 4 bytes of the other process' storage * data is a pointer specifying where the user wants the @@ -225,19 +171,6 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr, break; } - /* If I and D space are separate, this will have to be fixed. */ - case PTRACE_POKETEXT: /* write the word at location addr. */ - case PTRACE_POKEDATA: { - unsigned int tmp; - tmp = data; - ret = 0; - if (access_process_vm(child, addr, &tmp, sizeof(tmp), 1) - == sizeof(tmp)) - break; - ret = -EIO; - break; - } - /* * Write 4 bytes into the other process' storage * data is the 4 bytes that the user wants written @@ -337,46 +270,17 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr, break; } - case PTRACE_GETEVENTMSG: - ret = put_user(child->ptrace_message, (unsigned int __user *) data); - break; + case PTRACE_GETREGS: /* Get all pt_regs from the child. */ + return copy_regset_to_user( + child, task_user_regset_view(current), 0, + 0, PT_REGS_COUNT * sizeof(compat_long_t), + compat_ptr(data)); - case PTRACE_GETREGS: { /* Get all pt_regs from the child. */ - int ui; - if (!access_ok(VERIFY_WRITE, (void __user *)data, - PT_REGS_COUNT * sizeof(int))) { - ret = -EIO; - break; - } - CHECK_FULL_REGS(child->thread.regs); - ret = 0; - for (ui = 0; ui < PT_REGS_COUNT; ui ++) { - ret |= __put_user(ptrace_get_reg(child, ui), - (unsigned int __user *) data); - data += sizeof(int); - } - break; - } - - case PTRACE_SETREGS: { /* Set all gp regs in the child. */ - unsigned long tmp; - int ui; - if (!access_ok(VERIFY_READ, (void __user *)data, - PT_REGS_COUNT * sizeof(int))) { - ret = -EIO; - break; - } - CHECK_FULL_REGS(child->thread.regs); - ret = 0; - for (ui = 0; ui < PT_REGS_COUNT; ui ++) { - ret = __get_user(tmp, (unsigned int __user *) data); - if (ret) - break; - ptrace_put_reg(child, ui, tmp); - data += sizeof(int); - } - break; - } + case PTRACE_SETREGS: /* Set all gp regs in the child. */ + return copy_regset_from_user( + child, task_user_regset_view(current), 0, + 0, PT_REGS_COUNT * sizeof(compat_long_t), + compat_ptr(data)); case PTRACE_GETFPREGS: case PTRACE_SETFPREGS: @@ -402,12 +306,9 @@ long compat_sys_ptrace(int request, int pid, unsigned long addr, break; default: - ret = ptrace_request(child, request, addr, data); + ret = compat_ptrace_request(child, request, addr, data); break; } -out_tsk: - put_task_struct(child); -out: - unlock_kernel(); + return ret; } diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 848a20475db8..4b5b7ff4f78b 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -54,7 +54,7 @@ #endif #include <asm/kexec.h> -#ifdef CONFIG_DEBUGGER +#if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC) int (*__debugger)(struct pt_regs *regs); int (*__debugger_ipi)(struct pt_regs *regs); int (*__debugger_bpt)(struct pt_regs *regs); diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c index f0bad7070fb5..f98867252ee2 100644 --- a/arch/powerpc/kernel/vio.c +++ b/arch/powerpc/kernel/vio.c @@ -176,7 +176,7 @@ static void __devinit vio_dev_release(struct device *dev) * Returns a pointer to the created vio_dev or NULL if node has * NULL device_type or compatible fields. */ -struct vio_dev * __devinit vio_register_device_node(struct device_node *of_node) +struct vio_dev *vio_register_device_node(struct device_node *of_node) { struct vio_dev *viodev; const unsigned int *unit_address; diff --git a/arch/powerpc/mm/mem.c b/arch/powerpc/mm/mem.c index e8122447f019..93a5c53e3423 100644 --- a/arch/powerpc/mm/mem.c +++ b/arch/powerpc/mm/mem.c @@ -220,12 +220,13 @@ void __init do_init_bootmem(void) lmb_size_bytes(&lmb.reserved, i) - 1; if (addr < total_lowmem) reserve_bootmem(lmb.reserved.region[i].base, - lmb_size_bytes(&lmb.reserved, i)); + lmb_size_bytes(&lmb.reserved, i), + BOOTMEM_DEFAULT); else if (lmb.reserved.region[i].base < total_lowmem) { unsigned long adjusted_size = total_lowmem - lmb.reserved.region[i].base; reserve_bootmem(lmb.reserved.region[i].base, - adjusted_size); + adjusted_size, BOOTMEM_DEFAULT); } } #else @@ -234,7 +235,8 @@ void __init do_init_bootmem(void) /* reserve the sections we're already using */ for (i = 0; i < lmb.reserved.cnt; i++) reserve_bootmem(lmb.reserved.region[i].base, - lmb_size_bytes(&lmb.reserved, i)); + lmb_size_bytes(&lmb.reserved, i), + BOOTMEM_DEFAULT); #endif /* XXX need to clip this if using highmem? */ @@ -483,7 +485,12 @@ void update_mmu_cache(struct vm_area_struct *vma, unsigned long address, */ _tlbie(address, 0 /* 8xx doesn't care about PID */); #endif - if (!PageReserved(page) + /* The _PAGE_USER test should really be _PAGE_EXEC, but + * older glibc versions execute some code from no-exec + * pages, which for now we are supporting. If exec-only + * pages are ever implemented, this will have to change. + */ + if (!PageReserved(page) && (pte_val(pte) & _PAGE_USER) && !test_bit(PG_arch_1, &page->flags)) { if (vma->vm_mm == current->active_mm) { __flush_dcache_icache((void *) address); diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c index c12adc3ddffd..a300d254aac6 100644 --- a/arch/powerpc/mm/numa.c +++ b/arch/powerpc/mm/numa.c @@ -24,6 +24,8 @@ static int numa_enabled = 1; +static char *cmdline __initdata; + static int numa_debug; #define dbg(args...) if (numa_debug) { printk(KERN_INFO args); } @@ -39,6 +41,53 @@ static bootmem_data_t __initdata plat_node_bdata[MAX_NUMNODES]; static int min_common_depth; static int n_mem_addr_cells, n_mem_size_cells; +static int __cpuinit fake_numa_create_new_node(unsigned long end_pfn, + unsigned int *nid) +{ + unsigned long long mem; + char *p = cmdline; + static unsigned int fake_nid; + static unsigned long long curr_boundary; + + /* + * Modify node id, iff we started creating NUMA nodes + * We want to continue from where we left of the last time + */ + if (fake_nid) + *nid = fake_nid; + /* + * In case there are no more arguments to parse, the + * node_id should be the same as the last fake node id + * (we've handled this above). + */ + if (!p) + return 0; + + mem = memparse(p, &p); + if (!mem) + return 0; + + if (mem < curr_boundary) + return 0; + + curr_boundary = mem; + + if ((end_pfn << PAGE_SHIFT) > mem) { + /* + * Skip commas and spaces + */ + while (*p == ',' || *p == ' ' || *p == '\t') + p++; + + cmdline = p; + fake_nid++; + *nid = fake_nid; + dbg("created new fake_node with id %d\n", fake_nid); + return 1; + } + return 0; +} + static void __cpuinit map_cpu_to_node(int cpu, int node) { numa_cpu_lookup_table[cpu] = node; @@ -344,6 +393,9 @@ static void __init parse_drconf_memory(struct device_node *memory) if (nid == 0xffff || nid >= MAX_NUMNODES) nid = default_nid; } + + fake_numa_create_new_node(((start + lmb_size) >> PAGE_SHIFT), + &nid); node_set_online(nid); size = numa_enforce_memory_limit(start, lmb_size); @@ -429,6 +481,8 @@ new_range: nid = of_node_to_nid_single(memory); if (nid < 0) nid = default_nid; + + fake_numa_create_new_node(((start + size) >> PAGE_SHIFT), &nid); node_set_online(nid); if (!(size = numa_enforce_memory_limit(start, size))) { @@ -461,7 +515,7 @@ static void __init setup_nonnuma(void) unsigned long top_of_ram = lmb_end_of_DRAM(); unsigned long total_ram = lmb_phys_mem_size(); unsigned long start_pfn, end_pfn; - unsigned int i; + unsigned int i, nid = 0; printk(KERN_DEBUG "Top of RAM: 0x%lx, Total RAM: 0x%lx\n", top_of_ram, total_ram); @@ -471,9 +525,11 @@ static void __init setup_nonnuma(void) for (i = 0; i < lmb.memory.cnt; ++i) { start_pfn = lmb.memory.region[i].base >> PAGE_SHIFT; end_pfn = start_pfn + lmb_size_pages(&lmb.memory, i); - add_active_range(0, start_pfn, end_pfn); + + fake_numa_create_new_node(end_pfn, &nid); + add_active_range(nid, start_pfn, end_pfn); + node_set_online(nid); } - node_set_online(0); } void __init dump_numa_cpu_topology(void) @@ -675,7 +731,7 @@ void __init do_init_bootmem(void) dbg("reserve_bootmem %lx %lx\n", physbase, size); reserve_bootmem_node(NODE_DATA(nid), physbase, - size); + size, BOOTMEM_DEFAULT); } } @@ -702,6 +758,10 @@ static int __init early_numa(char *p) if (strstr(p, "debug")) numa_debug = 1; + p = strstr(p, "fake="); + if (p) + cmdline = p + strlen("fake="); + return 0; } early_param("numa", early_numa); diff --git a/arch/powerpc/oprofile/Makefile b/arch/powerpc/oprofile/Makefile index c5f64c3bd668..2ef6b0dddd8c 100644 --- a/arch/powerpc/oprofile/Makefile +++ b/arch/powerpc/oprofile/Makefile @@ -15,5 +15,5 @@ oprofile-$(CONFIG_OPROFILE_CELL) += op_model_cell.o \ cell/spu_profiler.o cell/vma_map.o \ cell/spu_task_sync.o oprofile-$(CONFIG_PPC64) += op_model_rs64.o op_model_power4.o op_model_pa6t.o -oprofile-$(CONFIG_FSL_BOOKE) += op_model_fsl_booke.o +oprofile-$(CONFIG_FSL_EMB_PERFMON) += op_model_fsl_emb.o oprofile-$(CONFIG_6xx) += op_model_7450.o diff --git a/arch/powerpc/oprofile/common.c b/arch/powerpc/oprofile/common.c index a28cce1d6c24..4908dc98f9ca 100644 --- a/arch/powerpc/oprofile/common.c +++ b/arch/powerpc/oprofile/common.c @@ -202,9 +202,9 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) model = &op_model_7450; break; #endif -#ifdef CONFIG_FSL_BOOKE - case PPC_OPROFILE_BOOKE: - model = &op_model_fsl_booke; +#if defined(CONFIG_FSL_EMB_PERFMON) + case PPC_OPROFILE_FSL_EMB: + model = &op_model_fsl_emb; break; #endif default: diff --git a/arch/powerpc/oprofile/op_model_fsl_booke.c b/arch/powerpc/oprofile/op_model_fsl_emb.c index 183a28bb1812..91596f6ba1f4 100644 --- a/arch/powerpc/oprofile/op_model_fsl_booke.c +++ b/arch/powerpc/oprofile/op_model_fsl_emb.c @@ -1,7 +1,5 @@ /* - * arch/powerpc/oprofile/op_model_fsl_booke.c - * - * Freescale Book-E oprofile support, based on ppc64 oprofile support + * Freescale Embedded oprofile support, based on ppc64 oprofile support * Copyright (C) 2004 Anton Blanchard <anton@au.ibm.com>, IBM * * Copyright (c) 2004 Freescale Semiconductor, Inc @@ -22,7 +20,7 @@ #include <asm/system.h> #include <asm/processor.h> #include <asm/cputable.h> -#include <asm/reg_booke.h> +#include <asm/reg_fsl_emb.h> #include <asm/page.h> #include <asm/pmc.h> #include <asm/oprofile_impl.h> @@ -244,7 +242,7 @@ static void dump_pmcs(void) mfpmr(PMRN_PMLCA3), mfpmr(PMRN_PMLCB3)); } -static int fsl_booke_cpu_setup(struct op_counter_config *ctr) +static int fsl_emb_cpu_setup(struct op_counter_config *ctr) { int i; @@ -262,7 +260,7 @@ static int fsl_booke_cpu_setup(struct op_counter_config *ctr) return 0; } -static int fsl_booke_reg_setup(struct op_counter_config *ctr, +static int fsl_emb_reg_setup(struct op_counter_config *ctr, struct op_system_config *sys, int num_ctrs) { @@ -281,7 +279,7 @@ static int fsl_booke_reg_setup(struct op_counter_config *ctr, return 0; } -static int fsl_booke_start(struct op_counter_config *ctr) +static int fsl_emb_start(struct op_counter_config *ctr) { int i; @@ -315,7 +313,7 @@ static int fsl_booke_start(struct op_counter_config *ctr) return 0; } -static void fsl_booke_stop(void) +static void fsl_emb_stop(void) { /* freeze counters */ pmc_stop_ctrs(); @@ -329,7 +327,7 @@ static void fsl_booke_stop(void) } -static void fsl_booke_handle_interrupt(struct pt_regs *regs, +static void fsl_emb_handle_interrupt(struct pt_regs *regs, struct op_counter_config *ctr) { unsigned long pc; @@ -362,10 +360,10 @@ static void fsl_booke_handle_interrupt(struct pt_regs *regs, pmc_start_ctrs(1); } -struct op_powerpc_model op_model_fsl_booke = { - .reg_setup = fsl_booke_reg_setup, - .cpu_setup = fsl_booke_cpu_setup, - .start = fsl_booke_start, - .stop = fsl_booke_stop, - .handle_interrupt = fsl_booke_handle_interrupt, +struct op_powerpc_model op_model_fsl_emb = { + .reg_setup = fsl_emb_reg_setup, + .cpu_setup = fsl_emb_cpu_setup, + .start = fsl_emb_start, + .stop = fsl_emb_stop, + .handle_interrupt = fsl_emb_handle_interrupt, }; diff --git a/arch/powerpc/platforms/40x/Kconfig b/arch/powerpc/platforms/40x/Kconfig index 74f31177e47a..a9260e21451e 100644 --- a/arch/powerpc/platforms/40x/Kconfig +++ b/arch/powerpc/platforms/40x/Kconfig @@ -72,6 +72,7 @@ config WALNUT default y select 405GP select PCI + select OF_RTC help This option enables support for the IBM PPC405GP evaluation board. diff --git a/arch/powerpc/platforms/40x/virtex.c b/arch/powerpc/platforms/40x/virtex.c index 88b66444dfb2..0422590040db 100644 --- a/arch/powerpc/platforms/40x/virtex.c +++ b/arch/powerpc/platforms/40x/virtex.c @@ -37,7 +37,7 @@ static int __init virtex_probe(void) { unsigned long root = of_get_flat_dt_root(); - if (!of_flat_dt_is_compatible(root, "xilinx,virtex")) + if (!of_flat_dt_is_compatible(root, "xlnx,virtex")) return 0; return 1; diff --git a/arch/powerpc/platforms/40x/walnut.c b/arch/powerpc/platforms/40x/walnut.c index 5d9edd917f92..b8b257efeb77 100644 --- a/arch/powerpc/platforms/40x/walnut.c +++ b/arch/powerpc/platforms/40x/walnut.c @@ -18,6 +18,7 @@ #include <linux/init.h> #include <linux/of_platform.h> +#include <linux/rtc.h> #include <asm/machdep.h> #include <asm/prom.h> diff --git a/arch/powerpc/platforms/44x/warp.c b/arch/powerpc/platforms/44x/warp.c index 8f01563dbd2a..da5b7b7599db 100644 --- a/arch/powerpc/platforms/44x/warp.c +++ b/arch/powerpc/platforms/44x/warp.c @@ -137,7 +137,7 @@ static int __init pika_dtm_start(void) } of_node_put(np); - fpga = ioremap(res.start + 0x20, 4); + fpga = ioremap(res.start, 0x24); if (fpga == NULL) return -ENOENT; diff --git a/arch/powerpc/platforms/512x/Kconfig b/arch/powerpc/platforms/512x/Kconfig new file mode 100644 index 000000000000..c6fa49e23dc0 --- /dev/null +++ b/arch/powerpc/platforms/512x/Kconfig @@ -0,0 +1,20 @@ +config PPC_MPC512x + bool + select FSL_SOC + select IPIC + default n + +config PPC_MPC5121 + bool + select PPC_MPC512x + default n + +config MPC5121_ADS + bool "Freescale MPC5121E ADS" + depends on PPC_MULTIPLATFORM && PPC32 + select DEFAULT_UIMAGE + select WANT_DEVICE_TREE + select PPC_MPC5121 + help + This option enables support for the MPC5121E ADS board. + default n diff --git a/arch/powerpc/platforms/512x/Makefile b/arch/powerpc/platforms/512x/Makefile new file mode 100644 index 000000000000..232c89f2039a --- /dev/null +++ b/arch/powerpc/platforms/512x/Makefile @@ -0,0 +1,4 @@ +# +# Makefile for the Freescale PowerPC 512x linux kernel. +# +obj-$(CONFIG_MPC5121_ADS) += mpc5121_ads.o diff --git a/arch/powerpc/platforms/512x/mpc5121_ads.c b/arch/powerpc/platforms/512x/mpc5121_ads.c new file mode 100644 index 000000000000..50bd3a319022 --- /dev/null +++ b/arch/powerpc/platforms/512x/mpc5121_ads.c @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: John Rigby, <jrigby@freescale.com>, Thur Mar 29 2007 + * + * Description: + * MPC5121 ADS board setup + * + * This 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 <linux/kernel.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/of_platform.h> + +#include <asm/machdep.h> +#include <asm/ipic.h> +#include <asm/prom.h> +#include <asm/time.h> + +/** + * mpc512x_find_ips_freq - Find the IPS bus frequency for a device + * @node: device node + * + * Returns IPS bus frequency, or 0 if the bus frequency cannot be found. + */ +unsigned long +mpc512x_find_ips_freq(struct device_node *node) +{ + struct device_node *np; + const unsigned int *p_ips_freq = NULL; + + of_node_get(node); + while (node) { + p_ips_freq = of_get_property(node, "bus-frequency", NULL); + if (p_ips_freq) + break; + + np = of_get_parent(node); + of_node_put(node); + node = np; + } + if (node) + of_node_put(node); + + return p_ips_freq ? *p_ips_freq : 0; +} +EXPORT_SYMBOL(mpc512x_find_ips_freq); + +static struct of_device_id __initdata of_bus_ids[] = { + { .name = "soc", }, + { .name = "localbus", }, + {}, +}; + +static void __init mpc5121_ads_declare_of_platform_devices(void) +{ + /* Find every child of the SOC node and add it to of_platform */ + if (of_platform_bus_probe(NULL, of_bus_ids, NULL)) + printk(KERN_ERR __FILE__ ": " + "Error while probing of_platform bus\n"); +} + +static void __init mpc5121_ads_init_IRQ(void) +{ + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "fsl,ipic"); + if (!np) + return; + + ipic_init(np, 0); + of_node_put(np); + + /* + * Initialize the default interrupt mapping priorities, + * in case the boot rom changed something on us. + */ + ipic_set_default_priority(); +} + +/* + * Called very early, MMU is off, device-tree isn't unflattened + */ +static int __init mpc5121_ads_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + + return of_flat_dt_is_compatible(root, "fsl,mpc5121ads"); +} + +define_machine(mpc5121_ads) { + .name = "MPC5121 ADS", + .probe = mpc5121_ads_probe, + .init = mpc5121_ads_declare_of_platform_devices, + .init_IRQ = mpc5121_ads_init_IRQ, + .get_irq = ipic_get_irq, + .calibrate_decr = generic_calibrate_decr, +}; diff --git a/arch/powerpc/platforms/82xx/mpc8272_ads.c b/arch/powerpc/platforms/82xx/mpc8272_ads.c index 3fce6b375dbc..7d3018751988 100644 --- a/arch/powerpc/platforms/82xx/mpc8272_ads.c +++ b/arch/powerpc/platforms/82xx/mpc8272_ads.c @@ -134,13 +134,12 @@ static void __init mpc8272_ads_setup_arch(void) } bcsr = of_iomap(np, 0); + of_node_put(np); if (!bcsr) { printk(KERN_ERR "Cannot map BCSR registers\n"); return; } - of_node_put(np); - clrbits32(&bcsr[1], BCSR1_RS232_EN1 | BCSR1_RS232_EN2 | BCSR1_FETHIEN); setbits32(&bcsr[1], BCSR1_FETH_RST); diff --git a/arch/powerpc/platforms/82xx/pq2fads.c b/arch/powerpc/platforms/82xx/pq2fads.c index 68196e349994..e1dceeec4994 100644 --- a/arch/powerpc/platforms/82xx/pq2fads.c +++ b/arch/powerpc/platforms/82xx/pq2fads.c @@ -130,13 +130,12 @@ static void __init pq2fads_setup_arch(void) } bcsr = of_iomap(np, 0); + of_node_put(np); if (!bcsr) { printk(KERN_ERR "Cannot map BCSR registers\n"); return; } - of_node_put(np); - /* Enable the serial and ethernet ports */ clrbits32(&bcsr[1], BCSR1_RS232_EN1 | BCSR1_RS232_EN2 | BCSR1_FETHIEN); diff --git a/arch/powerpc/platforms/83xx/mpc832x_rdb.c b/arch/powerpc/platforms/83xx/mpc832x_rdb.c index 9f0fd88b2b1f..e7f706b624fe 100644 --- a/arch/powerpc/platforms/83xx/mpc832x_rdb.c +++ b/arch/powerpc/platforms/83xx/mpc832x_rdb.c @@ -101,7 +101,7 @@ static void __init mpc832x_rdb_setup_arch(void) #ifdef CONFIG_QUICC_ENGINE qe_reset(); - if ((np = of_find_node_by_name(np, "par_io")) != NULL) { + if ((np = of_find_node_by_name(NULL, "par_io")) != NULL) { par_io_init(np); of_node_put(np); diff --git a/arch/powerpc/platforms/83xx/mpc83xx.h b/arch/powerpc/platforms/83xx/mpc83xx.h index 88bb748aff0d..68065e62fc3d 100644 --- a/arch/powerpc/platforms/83xx/mpc83xx.h +++ b/arch/powerpc/platforms/83xx/mpc83xx.h @@ -14,6 +14,8 @@ #define MPC83XX_SCCR_USB_DRCM_11 0x00300000 #define MPC83XX_SCCR_USB_DRCM_01 0x00100000 #define MPC83XX_SCCR_USB_DRCM_10 0x00200000 +#define MPC8315_SCCR_USB_MASK 0x00c00000 +#define MPC8315_SCCR_USB_DRCM_11 0x00c00000 #define MPC837X_SCCR_USB_DRCM_11 0x00c00000 /* system i/o configuration register low */ diff --git a/arch/powerpc/platforms/83xx/usb.c b/arch/powerpc/platforms/83xx/usb.c index 681230a30acd..471fdd8f4108 100644 --- a/arch/powerpc/platforms/83xx/usb.c +++ b/arch/powerpc/platforms/83xx/usb.c @@ -104,6 +104,7 @@ int mpc831x_usb_cfg(void) u32 temp; void __iomem *immap, *usb_regs; struct device_node *np = NULL; + struct device_node *immr_node = NULL; const void *prop; struct resource res; int ret = 0; @@ -124,10 +125,15 @@ int mpc831x_usb_cfg(void) } /* Configure clock */ - temp = in_be32(immap + MPC83XX_SCCR_OFFS); - temp &= ~MPC83XX_SCCR_USB_MASK; - temp |= MPC83XX_SCCR_USB_DRCM_11; /* 1:3 */ - out_be32(immap + MPC83XX_SCCR_OFFS, temp); + immr_node = of_get_parent(np); + if (immr_node && of_device_is_compatible(immr_node, "fsl,mpc8315-immr")) + clrsetbits_be32(immap + MPC83XX_SCCR_OFFS, + MPC8315_SCCR_USB_MASK, + MPC8315_SCCR_USB_DRCM_11); + else + clrsetbits_be32(immap + MPC83XX_SCCR_OFFS, + MPC83XX_SCCR_USB_MASK, + MPC83XX_SCCR_USB_DRCM_11); /* Configure pin mux for ULPI. There is no pin mux for UTMI */ if (prop && !strcmp(prop, "ulpi")) { @@ -144,6 +150,9 @@ int mpc831x_usb_cfg(void) iounmap(immap); + if (immr_node) + of_node_put(immr_node); + /* Map USB SOC space */ ret = of_address_to_resource(np, 0, &res); if (ret) { diff --git a/arch/powerpc/platforms/8xx/adder875.c b/arch/powerpc/platforms/8xx/adder875.c index c6bc0783c3b0..82363e98f50e 100644 --- a/arch/powerpc/platforms/8xx/adder875.c +++ b/arch/powerpc/platforms/8xx/adder875.c @@ -15,12 +15,12 @@ #include <asm/time.h> #include <asm/machdep.h> -#include <asm/commproc.h> +#include <asm/cpm1.h> #include <asm/fs_pd.h> #include <asm/udbg.h> #include <asm/prom.h> -#include <sysdev/commproc.h> +#include "mpc8xx.h" struct cpm_pin { int port, pin, flags; @@ -108,7 +108,7 @@ define_machine(adder875) { .name = "Adder MPC875", .probe = adder875_probe, .setup_arch = adder875_setup, - .init_IRQ = m8xx_pic_init, + .init_IRQ = mpc8xx_pics_init, .get_irq = mpc8xx_get_irq, .restart = mpc8xx_restart, .calibrate_decr = generic_calibrate_decr, diff --git a/arch/powerpc/platforms/8xx/ep88xc.c b/arch/powerpc/platforms/8xx/ep88xc.c index a8dffa005775..7d9ac6040d63 100644 --- a/arch/powerpc/platforms/8xx/ep88xc.c +++ b/arch/powerpc/platforms/8xx/ep88xc.c @@ -15,7 +15,6 @@ #include <asm/machdep.h> #include <asm/io.h> #include <asm/udbg.h> -#include <asm/commproc.h> #include <asm/cpm1.h> #include "mpc8xx.h" diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index fdce10c4f074..fcedbec07f94 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -24,6 +24,7 @@ config PPC_83xx select MPC83xx select IPIC select WANT_DEVICE_TREE + select FSL_EMB_PERFMON config PPC_86xx bool "Freescale 86xx" @@ -41,6 +42,7 @@ config CLASSIC32 source "arch/powerpc/platforms/pseries/Kconfig" source "arch/powerpc/platforms/iseries/Kconfig" source "arch/powerpc/platforms/chrp/Kconfig" +source "arch/powerpc/platforms/512x/Kconfig" source "arch/powerpc/platforms/52xx/Kconfig" source "arch/powerpc/platforms/powermac/Kconfig" source "arch/powerpc/platforms/prep/Kconfig" diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype index 7fc41104d53e..69941ba70975 100644 --- a/arch/powerpc/platforms/Kconfig.cputype +++ b/arch/powerpc/platforms/Kconfig.cputype @@ -14,7 +14,7 @@ choice There are five families of 32 bit PowerPC chips supported. The most common ones are the desktop and server CPUs (601, 603, 604, 740, 750, 74xx) CPUs from Freescale and IBM, with their - embedded 52xx/82xx/83xx/86xx counterparts. + embedded 512x/52xx/82xx/83xx/86xx counterparts. The other embeeded parts, namely 4xx, 8xx, e200 (55xx) and e500 (85xx) each form a family of their own that is not compatible with the others. @@ -22,7 +22,7 @@ choice If unsure, select 52xx/6xx/7xx/74xx/82xx/83xx/86xx. config 6xx - bool "52xx/6xx/7xx/74xx/82xx/83xx/86xx" + bool "512x/52xx/6xx/7xx/74xx/82xx/83xx/86xx" select PPC_FPU config PPC_85xx @@ -94,6 +94,7 @@ config 8xx bool config E500 + select FSL_EMB_PERFMON bool config PPC_FPU @@ -115,6 +116,9 @@ config FSL_BOOKE depends on E200 || E500 default y +config FSL_EMB_PERFMON + bool + config PTE_64BIT bool depends on 44x || E500 @@ -221,7 +225,7 @@ config NR_CPUS config NOT_COHERENT_CACHE bool - depends on 4xx || 8xx || E200 + depends on 4xx || 8xx || E200 || PPC_MPC512x default y config CHECK_CACHE_COHERENCY diff --git a/arch/powerpc/platforms/Makefile b/arch/powerpc/platforms/Makefile index 6d9079da5f5a..a984894466d9 100644 --- a/arch/powerpc/platforms/Makefile +++ b/arch/powerpc/platforms/Makefile @@ -11,6 +11,7 @@ endif obj-$(CONFIG_PPC_CHRP) += chrp/ obj-$(CONFIG_40x) += 40x/ obj-$(CONFIG_44x) += 44x/ +obj-$(CONFIG_PPC_MPC512x) += 512x/ obj-$(CONFIG_PPC_MPC52xx) += 52xx/ obj-$(CONFIG_PPC_8xx) += 8xx/ obj-$(CONFIG_PPC_82xx) += 82xx/ diff --git a/arch/powerpc/platforms/cell/Kconfig b/arch/powerpc/platforms/cell/Kconfig index 3a963b4a9be0..2f169991896d 100644 --- a/arch/powerpc/platforms/cell/Kconfig +++ b/arch/powerpc/platforms/cell/Kconfig @@ -54,6 +54,13 @@ config SPU_FS_64K_LS uses 4K pages. This can improve performances of applications using multiple SPEs by lowering the TLB pressure on them. +config SPU_TRACE + tristate "SPU event tracing support" + depends on SPU_FS && MARKERS + help + This option allows reading a trace of spu-related events through + the sputrace file in procfs. + config SPU_BASE bool default n diff --git a/arch/powerpc/platforms/cell/axon_msi.c b/arch/powerpc/platforms/cell/axon_msi.c index 095988f13bf4..d95e71dee91f 100644 --- a/arch/powerpc/platforms/cell/axon_msi.c +++ b/arch/powerpc/platforms/cell/axon_msi.c @@ -13,7 +13,7 @@ #include <linux/kernel.h> #include <linux/pci.h> #include <linux/msi.h> -#include <linux/reboot.h> +#include <linux/of_platform.h> #include <asm/dcr.h> #include <asm/machdep.h> @@ -65,14 +65,12 @@ struct axon_msic { struct irq_host *irq_host; - __le32 *fifo; + __le32 *fifo_virt; + dma_addr_t fifo_phys; dcr_host_t dcr_host; - struct list_head list; u32 read_offset; }; -static LIST_HEAD(axon_msic_list); - static void msic_dcr_write(struct axon_msic *msic, unsigned int dcr_n, u32 val) { pr_debug("axon_msi: dcr_write(0x%x, 0x%x)\n", val, dcr_n); @@ -94,7 +92,7 @@ static void axon_msi_cascade(unsigned int irq, struct irq_desc *desc) while (msic->read_offset != write_offset) { idx = msic->read_offset / sizeof(__le32); - msi = le32_to_cpu(msic->fifo[idx]); + msi = le32_to_cpu(msic->fifo_virt[idx]); msi &= 0xFFFF; pr_debug("axon_msi: woff %x roff %x msi %x\n", @@ -139,6 +137,7 @@ static struct axon_msic *find_msi_translator(struct pci_dev *dev) tmp = dn; dn = of_find_node_by_phandle(*ph); + of_node_put(tmp); if (!dn) { dev_dbg(&dev->dev, "axon_msi: msi-translator doesn't point to a node\n"); @@ -156,7 +155,6 @@ static struct axon_msic *find_msi_translator(struct pci_dev *dev) out_error: of_node_put(dn); - of_node_put(tmp); return msic; } @@ -292,30 +290,24 @@ static struct irq_host_ops msic_host_ops = { .map = msic_host_map, }; -static int axon_msi_notify_reboot(struct notifier_block *nb, - unsigned long code, void *data) +static int axon_msi_shutdown(struct of_device *device) { - struct axon_msic *msic; + struct axon_msic *msic = device->dev.platform_data; u32 tmp; - list_for_each_entry(msic, &axon_msic_list, list) { - pr_debug("axon_msi: disabling %s\n", - msic->irq_host->of_node->full_name); - tmp = dcr_read(msic->dcr_host, MSIC_CTRL_REG); - tmp &= ~MSIC_CTRL_ENABLE & ~MSIC_CTRL_IRQ_ENABLE; - msic_dcr_write(msic, MSIC_CTRL_REG, tmp); - } + pr_debug("axon_msi: disabling %s\n", + msic->irq_host->of_node->full_name); + tmp = dcr_read(msic->dcr_host, MSIC_CTRL_REG); + tmp &= ~MSIC_CTRL_ENABLE & ~MSIC_CTRL_IRQ_ENABLE; + msic_dcr_write(msic, MSIC_CTRL_REG, tmp); return 0; } -static struct notifier_block axon_msi_reboot_notifier = { - .notifier_call = axon_msi_notify_reboot -}; - -static int axon_msi_setup_one(struct device_node *dn) +static int axon_msi_probe(struct of_device *device, + const struct of_device_id *device_id) { - struct page *page; + struct device_node *dn = device->node; struct axon_msic *msic; unsigned int virq; int dcr_base, dcr_len; @@ -346,16 +338,14 @@ static int axon_msi_setup_one(struct device_node *dn) goto out_free_msic; } - page = alloc_pages_node(of_node_to_nid(dn), GFP_KERNEL, - get_order(MSIC_FIFO_SIZE_BYTES)); - if (!page) { + msic->fifo_virt = dma_alloc_coherent(&device->dev, MSIC_FIFO_SIZE_BYTES, + &msic->fifo_phys, GFP_KERNEL); + if (!msic->fifo_virt) { printk(KERN_ERR "axon_msi: couldn't allocate fifo for %s\n", dn->full_name); goto out_free_msic; } - msic->fifo = page_address(page); - msic->irq_host = irq_alloc_host(of_node_get(dn), IRQ_HOST_MAP_NOMAP, NR_IRQS, &msic_host_ops, 0); if (!msic->irq_host) { @@ -378,14 +368,18 @@ static int axon_msi_setup_one(struct device_node *dn) pr_debug("axon_msi: irq 0x%x setup for axon_msi\n", virq); /* Enable the MSIC hardware */ - msic_dcr_write(msic, MSIC_BASE_ADDR_HI_REG, (u64)msic->fifo >> 32); + msic_dcr_write(msic, MSIC_BASE_ADDR_HI_REG, msic->fifo_phys >> 32); msic_dcr_write(msic, MSIC_BASE_ADDR_LO_REG, - (u64)msic->fifo & 0xFFFFFFFF); + msic->fifo_phys & 0xFFFFFFFF); msic_dcr_write(msic, MSIC_CTRL_REG, MSIC_CTRL_IRQ_ENABLE | MSIC_CTRL_ENABLE | MSIC_CTRL_FIFO_SIZE); - list_add(&msic->list, &axon_msic_list); + device->dev.platform_data = msic; + + ppc_md.setup_msi_irqs = axon_msi_setup_msi_irqs; + ppc_md.teardown_msi_irqs = axon_msi_teardown_msi_irqs; + ppc_md.msi_check_device = axon_msi_check_device; printk(KERN_DEBUG "axon_msi: setup MSIC on %s\n", dn->full_name); @@ -394,7 +388,8 @@ static int axon_msi_setup_one(struct device_node *dn) out_free_host: kfree(msic->irq_host); out_free_fifo: - __free_pages(virt_to_page(msic->fifo), get_order(MSIC_FIFO_SIZE_BYTES)); + dma_free_coherent(&device->dev, MSIC_FIFO_SIZE_BYTES, msic->fifo_virt, + msic->fifo_phys); out_free_msic: kfree(msic); out: @@ -402,28 +397,24 @@ out: return -1; } -static int axon_msi_init(void) -{ - struct device_node *dn; - int found = 0; - - pr_debug("axon_msi: initialising ...\n"); - - for_each_compatible_node(dn, NULL, "ibm,axon-msic") { - if (axon_msi_setup_one(dn) == 0) - found++; - } - - if (found) { - ppc_md.setup_msi_irqs = axon_msi_setup_msi_irqs; - ppc_md.teardown_msi_irqs = axon_msi_teardown_msi_irqs; - ppc_md.msi_check_device = axon_msi_check_device; - - register_reboot_notifier(&axon_msi_reboot_notifier); +static const struct of_device_id axon_msi_device_id[] = { + { + .compatible = "ibm,axon-msic" + }, + {} +}; - pr_debug("axon_msi: registered callbacks!\n"); - } +static struct of_platform_driver axon_msi_driver = { + .match_table = axon_msi_device_id, + .probe = axon_msi_probe, + .shutdown = axon_msi_shutdown, + .driver = { + .name = "axon-msi" + }, +}; - return 0; +static int __init axon_msi_init(void) +{ + return of_register_platform_driver(&axon_msi_driver); } -arch_initcall(axon_msi_init); +subsys_initcall(axon_msi_init); diff --git a/arch/powerpc/platforms/cell/setup.c b/arch/powerpc/platforms/cell/setup.c index e6534b519c9a..a7f609b3b876 100644 --- a/arch/powerpc/platforms/cell/setup.c +++ b/arch/powerpc/platforms/cell/setup.c @@ -98,7 +98,7 @@ static int __init cell_publish_devices(void) } return 0; } -machine_device_initcall(cell, cell_publish_devices); +machine_subsys_initcall(cell, cell_publish_devices); static void cell_mpic_cascade(unsigned int irq, struct irq_desc *desc) { diff --git a/arch/powerpc/platforms/cell/spufs/Makefile b/arch/powerpc/platforms/cell/spufs/Makefile index d3a349fb42e5..99610a6361f2 100644 --- a/arch/powerpc/platforms/cell/spufs/Makefile +++ b/arch/powerpc/platforms/cell/spufs/Makefile @@ -4,6 +4,8 @@ spufs-y += inode.o file.o context.o syscalls.o coredump.o spufs-y += sched.o backing_ops.o hw_ops.o run.o gang.o spufs-y += switch.o fault.o lscsa_alloc.o +obj-$(CONFIG_SPU_TRACE) += sputrace.o + # Rules to build switch.o with the help of SPU tool chain SPU_CROSS := spu- SPU_CC := $(SPU_CROSS)gcc diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c index 3fcd06418b01..1018acd1746b 100644 --- a/arch/powerpc/platforms/cell/spufs/file.c +++ b/arch/powerpc/platforms/cell/spufs/file.c @@ -29,6 +29,7 @@ #include <linux/poll.h> #include <linux/ptrace.h> #include <linux/seq_file.h> +#include <linux/marker.h> #include <asm/io.h> #include <asm/semaphore.h> @@ -358,6 +359,8 @@ static unsigned long spufs_ps_nopfn(struct vm_area_struct *vma, struct spu_context *ctx = vma->vm_file->private_data; unsigned long area, offset = address - vma->vm_start; + spu_context_nospu_trace(spufs_ps_nopfn__enter, ctx); + offset += vma->vm_pgoff << PAGE_SHIFT; if (offset >= ps_size) return NOPFN_SIGBUS; @@ -375,11 +378,14 @@ static unsigned long spufs_ps_nopfn(struct vm_area_struct *vma, if (ctx->state == SPU_STATE_SAVED) { up_read(¤t->mm->mmap_sem); + spu_context_nospu_trace(spufs_ps_nopfn__sleep, ctx); spufs_wait(ctx->run_wq, ctx->state == SPU_STATE_RUNNABLE); + spu_context_trace(spufs_ps_nopfn__wake, ctx, ctx->spu); down_read(¤t->mm->mmap_sem); } else { area = ctx->spu->problem_phys + ps_offs; vm_insert_pfn(vma, address, (area + offset) >> PAGE_SHIFT); + spu_context_trace(spufs_ps_nopfn__insert, ctx, ctx->spu); } spu_release(ctx); diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c index c0e968a4c211..90784c029f25 100644 --- a/arch/powerpc/platforms/cell/spufs/inode.c +++ b/arch/powerpc/platforms/cell/spufs/inode.c @@ -322,7 +322,7 @@ static struct spu_context * spufs_assert_affinity(unsigned int flags, struct spu_gang *gang, struct file *filp) { - struct spu_context *tmp, *neighbor; + struct spu_context *tmp, *neighbor, *err; int count, node; int aff_supp; @@ -354,11 +354,15 @@ spufs_assert_affinity(unsigned int flags, struct spu_gang *gang, if (!list_empty(&neighbor->aff_list) && !(neighbor->aff_head) && !list_is_last(&neighbor->aff_list, &gang->aff_list_head) && !list_entry(neighbor->aff_list.next, struct spu_context, - aff_list)->aff_head) - return ERR_PTR(-EEXIST); + aff_list)->aff_head) { + err = ERR_PTR(-EEXIST); + goto out_put_neighbor; + } - if (gang != neighbor->gang) - return ERR_PTR(-EINVAL); + if (gang != neighbor->gang) { + err = ERR_PTR(-EINVAL); + goto out_put_neighbor; + } count = 1; list_for_each_entry(tmp, &gang->aff_list_head, aff_list) @@ -372,11 +376,17 @@ spufs_assert_affinity(unsigned int flags, struct spu_gang *gang, break; } - if (node == MAX_NUMNODES) - return ERR_PTR(-EEXIST); + if (node == MAX_NUMNODES) { + err = ERR_PTR(-EEXIST); + goto out_put_neighbor; + } } return neighbor; + +out_put_neighbor: + put_spu_context(neighbor); + return err; } static void @@ -454,9 +464,12 @@ spufs_create_context(struct inode *inode, struct dentry *dentry, if (ret) goto out_aff_unlock; - if (affinity) + if (affinity) { spufs_set_affinity(flags, SPUFS_I(dentry->d_inode)->i_ctx, neighbor); + if (neighbor) + put_spu_context(neighbor); + } /* * get references for dget and mntget, will be released diff --git a/arch/powerpc/platforms/cell/spufs/run.c b/arch/powerpc/platforms/cell/spufs/run.c index c01a09da1e56..b4814c740d8a 100644 --- a/arch/powerpc/platforms/cell/spufs/run.c +++ b/arch/powerpc/platforms/cell/spufs/run.c @@ -410,8 +410,11 @@ long spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *event) * since we have TIF_SINGLESTEP set, thus the kernel will do * it upon return from the syscall anyawy */ - if ((status & SPU_STATUS_STOPPED_BY_STOP) - && (status >> SPU_STOP_STATUS_SHIFT) == 0x3fff) { + if (unlikely(status & SPU_STATUS_SINGLE_STEP)) + ret = -ERESTARTSYS; + + else if (unlikely((status & SPU_STATUS_STOPPED_BY_STOP) + && (status >> SPU_STOP_STATUS_SHIFT) == 0x3fff)) { force_sig(SIGTRAP, current); ret = -ERESTARTSYS; } diff --git a/arch/powerpc/platforms/cell/spufs/sched.c b/arch/powerpc/platforms/cell/spufs/sched.c index 00d914232af1..5915343e2599 100644 --- a/arch/powerpc/platforms/cell/spufs/sched.c +++ b/arch/powerpc/platforms/cell/spufs/sched.c @@ -39,6 +39,7 @@ #include <linux/pid_namespace.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> +#include <linux/marker.h> #include <asm/io.h> #include <asm/mmu_context.h> @@ -216,8 +217,8 @@ void do_notify_spus_active(void) */ static void spu_bind_context(struct spu *spu, struct spu_context *ctx) { - pr_debug("%s: pid=%d SPU=%d NODE=%d\n", __FUNCTION__, current->pid, - spu->number, spu->node); + spu_context_trace(spu_bind_context__enter, ctx, spu); + spuctx_switch_state(ctx, SPU_UTIL_SYSTEM); if (ctx->flags & SPU_CREATE_NOSCHED) @@ -399,8 +400,8 @@ static int has_affinity(struct spu_context *ctx) */ static void spu_unbind_context(struct spu *spu, struct spu_context *ctx) { - pr_debug("%s: unbind pid=%d SPU=%d NODE=%d\n", __FUNCTION__, - spu->pid, spu->number, spu->node); + spu_context_trace(spu_unbind_context__enter, ctx, spu); + spuctx_switch_state(ctx, SPU_UTIL_SYSTEM); if (spu->ctx->flags & SPU_CREATE_NOSCHED) @@ -528,6 +529,8 @@ static struct spu *spu_get_idle(struct spu_context *ctx) struct spu *spu, *aff_ref_spu; int node, n; + spu_context_nospu_trace(spu_get_idle__enter, ctx); + if (ctx->gang) { mutex_lock(&ctx->gang->aff_mutex); if (has_affinity(ctx)) { @@ -546,8 +549,7 @@ static struct spu *spu_get_idle(struct spu_context *ctx) if (atomic_dec_and_test(&ctx->gang->aff_sched_count)) ctx->gang->aff_ref_spu = NULL; mutex_unlock(&ctx->gang->aff_mutex); - - return NULL; + goto not_found; } mutex_unlock(&ctx->gang->aff_mutex); } @@ -565,12 +567,14 @@ static struct spu *spu_get_idle(struct spu_context *ctx) mutex_unlock(&cbe_spu_info[node].list_mutex); } + not_found: + spu_context_nospu_trace(spu_get_idle__not_found, ctx); return NULL; found: spu->alloc_state = SPU_USED; mutex_unlock(&cbe_spu_info[node].list_mutex); - pr_debug("Got SPU %d %d\n", spu->number, spu->node); + spu_context_trace(spu_get_idle__found, ctx, spu); spu_init_channels(spu); return spu; } @@ -587,6 +591,8 @@ static struct spu *find_victim(struct spu_context *ctx) struct spu *spu; int node, n; + spu_context_nospu_trace(spu_find_vitim__enter, ctx); + /* * Look for a possible preemption candidate on the local node first. * If there is no candidate look at the other nodes. This isn't @@ -640,6 +646,8 @@ static struct spu *find_victim(struct spu_context *ctx) goto restart; } + spu_context_trace(__spu_deactivate__unload, ctx, spu); + mutex_lock(&cbe_spu_info[node].list_mutex); cbe_spu_info[node].nr_active--; spu_unbind_context(spu, victim); @@ -822,6 +830,7 @@ static int __spu_deactivate(struct spu_context *ctx, int force, int max_prio) */ void spu_deactivate(struct spu_context *ctx) { + spu_context_nospu_trace(spu_deactivate__enter, ctx); __spu_deactivate(ctx, 1, MAX_PRIO); } @@ -835,6 +844,7 @@ void spu_deactivate(struct spu_context *ctx) */ void spu_yield(struct spu_context *ctx) { + spu_context_nospu_trace(spu_yield__enter, ctx); if (!(ctx->flags & SPU_CREATE_NOSCHED)) { mutex_lock(&ctx->state_mutex); __spu_deactivate(ctx, 0, MAX_PRIO); @@ -864,11 +874,15 @@ static noinline void spusched_tick(struct spu_context *ctx) goto out; spu = ctx->spu; + + spu_context_trace(spusched_tick__preempt, ctx, spu); + new = grab_runnable_context(ctx->prio + 1, spu->node); if (new) { spu_unschedule(spu, ctx); spu_add_to_rq(ctx); } else { + spu_context_nospu_trace(spusched_tick__newslice, ctx); ctx->time_slice++; } out: diff --git a/arch/powerpc/platforms/cell/spufs/spufs.h b/arch/powerpc/platforms/cell/spufs/spufs.h index 0e114038ea6f..795a1b52538b 100644 --- a/arch/powerpc/platforms/cell/spufs/spufs.h +++ b/arch/powerpc/platforms/cell/spufs/spufs.h @@ -325,4 +325,9 @@ extern void spu_free_lscsa(struct spu_state *csa); extern void spuctx_switch_state(struct spu_context *ctx, enum spu_utilization_state new_state); +#define spu_context_trace(name, ctx, spu) \ + trace_mark(name, "%p %p", ctx, spu); +#define spu_context_nospu_trace(name, ctx) \ + trace_mark(name, "%p", ctx); + #endif diff --git a/arch/powerpc/platforms/cell/spufs/sputrace.c b/arch/powerpc/platforms/cell/spufs/sputrace.c new file mode 100644 index 000000000000..2b1953f6f12e --- /dev/null +++ b/arch/powerpc/platforms/cell/spufs/sputrace.c @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2007 IBM Deutschland Entwicklung GmbH + * Released under GPL v2. + * + * Partially based on net/ipv4/tcp_probe.c. + * + * Simple tracing facility for spu contexts. + */ +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/marker.h> +#include <linux/proc_fs.h> +#include <linux/wait.h> +#include <asm/atomic.h> +#include <asm/uaccess.h> +#include "spufs.h" + +struct spu_probe { + const char *name; + const char *format; + marker_probe_func *probe_func; +}; + +struct sputrace { + ktime_t tstamp; + int owner_tid; /* owner */ + int curr_tid; + const char *name; + int number; +}; + +static int bufsize __read_mostly = 16384; +MODULE_PARM_DESC(bufsize, "Log buffer size (number of records)"); +module_param(bufsize, int, 0); + + +static DEFINE_SPINLOCK(sputrace_lock); +static DECLARE_WAIT_QUEUE_HEAD(sputrace_wait); +static ktime_t sputrace_start; +static unsigned long sputrace_head, sputrace_tail; +static struct sputrace *sputrace_log; + +static int sputrace_used(void) +{ + return (sputrace_head - sputrace_tail) % bufsize; +} + +static inline int sputrace_avail(void) +{ + return bufsize - sputrace_used(); +} + +static int sputrace_sprint(char *tbuf, int n) +{ + const struct sputrace *t = sputrace_log + sputrace_tail % bufsize; + struct timespec tv = + ktime_to_timespec(ktime_sub(t->tstamp, sputrace_start)); + + return snprintf(tbuf, n, + "[%lu.%09lu] %d: %s (thread = %d, spu = %d)\n", + (unsigned long) tv.tv_sec, + (unsigned long) tv.tv_nsec, + t->owner_tid, + t->name, + t->curr_tid, + t->number); +} + +static ssize_t sputrace_read(struct file *file, char __user *buf, + size_t len, loff_t *ppos) +{ + int error = 0, cnt = 0; + + if (!buf || len < 0) + return -EINVAL; + + while (cnt < len) { + char tbuf[128]; + int width; + + error = wait_event_interruptible(sputrace_wait, + sputrace_used() > 0); + if (error) + break; + + spin_lock(&sputrace_lock); + if (sputrace_head == sputrace_tail) { + spin_unlock(&sputrace_lock); + continue; + } + + width = sputrace_sprint(tbuf, sizeof(tbuf)); + if (width < len) + sputrace_tail = (sputrace_tail + 1) % bufsize; + spin_unlock(&sputrace_lock); + + if (width >= len) + break; + + error = copy_to_user(buf + cnt, tbuf, width); + if (error) + break; + cnt += width; + } + + return cnt == 0 ? error : cnt; +} + +static int sputrace_open(struct inode *inode, struct file *file) +{ + spin_lock(&sputrace_lock); + sputrace_head = sputrace_tail = 0; + sputrace_start = ktime_get(); + spin_unlock(&sputrace_lock); + + return 0; +} + +static const struct file_operations sputrace_fops = { + .owner = THIS_MODULE, + .open = sputrace_open, + .read = sputrace_read, +}; + +static void sputrace_log_item(const char *name, struct spu_context *ctx, + struct spu *spu) +{ + spin_lock(&sputrace_lock); + if (sputrace_avail() > 1) { + struct sputrace *t = sputrace_log + sputrace_head; + + t->tstamp = ktime_get(); + t->owner_tid = ctx->tid; + t->name = name; + t->curr_tid = current->pid; + t->number = spu ? spu->number : -1; + + sputrace_head = (sputrace_head + 1) % bufsize; + } else { + printk(KERN_WARNING + "sputrace: lost samples due to full buffer.\n"); + } + spin_unlock(&sputrace_lock); + + wake_up(&sputrace_wait); +} + +static void spu_context_event(const struct marker *mdata, + void *private, const char *format, ...) +{ + struct spu_probe *p = mdata->private; + va_list ap; + struct spu_context *ctx; + struct spu *spu; + + va_start(ap, format); + ctx = va_arg(ap, struct spu_context *); + spu = va_arg(ap, struct spu *); + + sputrace_log_item(p->name, ctx, spu); + va_end(ap); +} + +static void spu_context_nospu_event(const struct marker *mdata, + void *private, const char *format, ...) +{ + struct spu_probe *p = mdata->private; + va_list ap; + struct spu_context *ctx; + + va_start(ap, format); + ctx = va_arg(ap, struct spu_context *); + + sputrace_log_item(p->name, ctx, NULL); + va_end(ap); +} + +struct spu_probe spu_probes[] = { + { "spu_bind_context__enter", "%p %p", spu_context_event }, + { "spu_unbind_context__enter", "%p %p", spu_context_event }, + { "spu_get_idle__enter", "%p", spu_context_nospu_event }, + { "spu_get_idle__found", "%p %p", spu_context_event }, + { "spu_get_idle__not_found", "%p", spu_context_nospu_event }, + { "spu_find_victim__enter", "%p", spu_context_nospu_event }, + { "spusched_tick__preempt", "%p %p", spu_context_event }, + { "spusched_tick__newslice", "%p", spu_context_nospu_event }, + { "spu_yield__enter", "%p", spu_context_nospu_event }, + { "spu_deactivate__enter", "%p", spu_context_nospu_event }, + { "__spu_deactivate__unload", "%p %p", spu_context_event }, + { "spufs_ps_nopfn__enter", "%p", spu_context_nospu_event }, + { "spufs_ps_nopfn__sleep", "%p", spu_context_nospu_event }, + { "spufs_ps_nopfn__wake", "%p %p", spu_context_event }, + { "spufs_ps_nopfn__insert", "%p %p", spu_context_event }, + { "spu_acquire_saved__enter", "%p", spu_context_nospu_event }, + { "destroy_spu_context__enter", "%p", spu_context_nospu_event }, +}; + +static int __init sputrace_init(void) +{ + struct proc_dir_entry *entry; + int i, error = -ENOMEM; + + sputrace_log = kcalloc(sizeof(struct sputrace), + bufsize, GFP_KERNEL); + if (!sputrace_log) + goto out; + + entry = create_proc_entry("sputrace", S_IRUSR, NULL); + if (!entry) + goto out_free_log; + entry->proc_fops = &sputrace_fops; + + for (i = 0; i < ARRAY_SIZE(spu_probes); i++) { + struct spu_probe *p = &spu_probes[i]; + + error = marker_probe_register(p->name, p->format, + p->probe_func, p); + if (error) + printk(KERN_INFO "Unable to register probe %s\n", + p->name); + + error = marker_arm(p->name); + if (error) + printk(KERN_INFO "Unable to arm probe %s\n", p->name); + } + + return 0; + +out_free_log: + kfree(sputrace_log); +out: + return -ENOMEM; +} + +static void __exit sputrace_exit(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(spu_probes); i++) + marker_probe_unregister(spu_probes[i].name); + + remove_proc_entry("sputrace", NULL); + kfree(sputrace_log); +} + +module_init(sputrace_init); +module_exit(sputrace_exit); + +MODULE_LICENSE("GPL"); diff --git a/arch/powerpc/platforms/embedded6xx/storcenter.c b/arch/powerpc/platforms/embedded6xx/storcenter.c index e12e9d298716..8864e4884980 100644 --- a/arch/powerpc/platforms/embedded6xx/storcenter.c +++ b/arch/powerpc/platforms/embedded6xx/storcenter.c @@ -132,33 +132,18 @@ static void __init storcenter_init_IRQ(void) paddr = (phys_addr_t)of_translate_address(dnp, prop); mpic = mpic_alloc(dnp, paddr, MPIC_PRIMARY | MPIC_WANTS_RESET, - 4, 32, " EPIC "); + 16, 32, " OpenPIC "); of_node_put(dnp); BUG_ON(mpic == NULL); - /* PCI IRQs */ /* - * 2.6.12 patch: - * openpic_set_sources(0, 5, OpenPIC_Addr + 0x10200); - * openpic_set_sources(5, 2, OpenPIC_Addr + 0x11120); - * first_irq, num_irqs, __iomem first_ISR - * o_ss: i, src: 0, fdf50200 - * o_ss: i, src: 1, fdf50220 - * o_ss: i, src: 2, fdf50240 - * o_ss: i, src: 3, fdf50260 - * o_ss: i, src: 4, fdf50280 - * o_ss: i, src: 5, fdf51120 - * o_ss: i, src: 6, fdf51140 + * 16 Serial Interrupts followed by 16 Internal Interrupts. + * I2C is the second internal, so it is at 17, 0x11020. */ mpic_assign_isu(mpic, 0, paddr + 0x10200); - mpic_assign_isu(mpic, 1, paddr + 0x10220); - mpic_assign_isu(mpic, 2, paddr + 0x10240); - mpic_assign_isu(mpic, 3, paddr + 0x10260); - mpic_assign_isu(mpic, 4, paddr + 0x10280); - mpic_assign_isu(mpic, 5, paddr + 0x11120); - mpic_assign_isu(mpic, 6, paddr + 0x11140); + mpic_assign_isu(mpic, 1, paddr + 0x11000); mpic_init(mpic); } @@ -178,7 +163,7 @@ static int __init storcenter_probe(void) { unsigned long root = of_get_flat_dt_root(); - return of_flat_dt_is_compatible(root, "storcenter"); + return of_flat_dt_is_compatible(root, "iomega,storcenter"); } define_machine(storcenter){ diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c index c4ad54e0f288..1f032483c026 100644 --- a/arch/powerpc/platforms/pseries/hotplug-cpu.c +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c @@ -58,7 +58,7 @@ static void pseries_mach_cpu_die(void) { local_irq_disable(); idle_task_exit(); - xics_teardown_cpu(0); + xics_teardown_cpu(); unregister_slb_shadow(hard_smp_processor_id(), __pa(get_slb_shadow())); rtas_stop_self(); /* Should never get here... */ diff --git a/arch/powerpc/platforms/pseries/kexec.c b/arch/powerpc/platforms/pseries/kexec.c index 412a5e7aff2d..e9dd5fe081c9 100644 --- a/arch/powerpc/platforms/pseries/kexec.c +++ b/arch/powerpc/platforms/pseries/kexec.c @@ -54,7 +54,7 @@ void __init setup_kexec_cpu_down_mpic(void) static void pseries_kexec_cpu_down_xics(int crash_shutdown, int secondary) { pseries_kexec_cpu_down(crash_shutdown, secondary); - xics_teardown_cpu(secondary); + xics_kexec_teardown_cpu(secondary); } void __init setup_kexec_cpu_down_xics(void) diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c index c02f8742c54d..2800fced8c7c 100644 --- a/arch/powerpc/platforms/pseries/reconfig.c +++ b/arch/powerpc/platforms/pseries/reconfig.c @@ -167,6 +167,7 @@ static int pSeries_reconfig_remove_node(struct device_node *np) if ((child = of_get_next_child(np, NULL))) { of_node_put(child); + of_node_put(parent); return -EBUSY; } diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index 8f8dd9c3ca6b..ca52b587166d 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -160,6 +160,46 @@ static inline void lpar_qirr_info(int n_cpu , u8 value) /* High level handlers and init code */ +static void xics_update_irq_servers(void) +{ + int i, j; + struct device_node *np; + u32 ilen; + const u32 *ireg, *isize; + u32 hcpuid; + + /* Find the server numbers for the boot cpu. */ + np = of_get_cpu_node(boot_cpuid, NULL); + BUG_ON(!np); + + ireg = of_get_property(np, "ibm,ppc-interrupt-gserver#s", &ilen); + if (!ireg) { + of_node_put(np); + return; + } + + i = ilen / sizeof(int); + hcpuid = get_hard_smp_processor_id(boot_cpuid); + + /* Global interrupt distribution server is specified in the last + * entry of "ibm,ppc-interrupt-gserver#s" property. Get the last + * entry fom this property for current boot cpu id and use it as + * default distribution server + */ + for (j = 0; j < i; j += 2) { + if (ireg[j] == hcpuid) { + default_server = hcpuid; + default_distrib_server = ireg[j+1]; + + isize = of_get_property(np, + "ibm,interrupt-server#-size", NULL); + if (isize) + interrupt_server_size = *isize; + } + } + + of_node_put(np); +} #ifdef CONFIG_SMP static int get_irq_server(unsigned int virq, unsigned int strict_check) @@ -169,6 +209,9 @@ static int get_irq_server(unsigned int virq, unsigned int strict_check) cpumask_t cpumask = irq_desc[virq].affinity; cpumask_t tmp = CPU_MASK_NONE; + if (! cpu_isset(default_server, cpu_online_map)) + xics_update_irq_servers(); + if (!distribute_irqs) return default_server; @@ -658,39 +701,11 @@ static void __init xics_setup_8259_cascade(void) set_irq_chained_handler(cascade, pseries_8259_cascade); } -static struct device_node *cpuid_to_of_node(int cpu) -{ - struct device_node *np; - u32 hcpuid = get_hard_smp_processor_id(cpu); - - for_each_node_by_type(np, "cpu") { - int i, len; - const u32 *intserv; - - intserv = of_get_property(np, "ibm,ppc-interrupt-server#s", - &len); - - if (!intserv) - intserv = of_get_property(np, "reg", &len); - - i = len / sizeof(u32); - - while (i--) - if (intserv[i] == hcpuid) - return np; - } - - return NULL; -} - void __init xics_init_IRQ(void) { - int i, j; struct device_node *np; - u32 ilen, indx = 0; - const u32 *ireg, *isize; + u32 indx = 0; int found = 0; - u32 hcpuid; ppc64_boot_msg(0x20, "XICS Init"); @@ -709,34 +724,7 @@ void __init xics_init_IRQ(void) return; xics_init_host(); - - /* Find the server numbers for the boot cpu. */ - np = cpuid_to_of_node(boot_cpuid); - BUG_ON(!np); - ireg = of_get_property(np, "ibm,ppc-interrupt-gserver#s", &ilen); - if (!ireg) - goto skip_gserver_check; - i = ilen / sizeof(int); - hcpuid = get_hard_smp_processor_id(boot_cpuid); - - /* Global interrupt distribution server is specified in the last - * entry of "ibm,ppc-interrupt-gserver#s" property. Get the last - * entry fom this property for current boot cpu id and use it as - * default distribution server - */ - for (j = 0; j < i; j += 2) { - if (ireg[j] == hcpuid) { - default_server = hcpuid; - default_distrib_server = ireg[j+1]; - - isize = of_get_property(np, - "ibm,interrupt-server#-size", NULL); - if (isize) - interrupt_server_size = *isize; - } - } -skip_gserver_check: - of_node_put(np); + xics_update_irq_servers(); if (firmware_has_feature(FW_FEATURE_LPAR)) ppc_md.get_irq = xics_get_irq_lpar; @@ -775,11 +763,9 @@ void xics_request_IPIs(void) } #endif /* CONFIG_SMP */ -void xics_teardown_cpu(int secondary) +void xics_teardown_cpu() { int cpu = smp_processor_id(); - unsigned int ipi; - struct irq_desc *desc; xics_set_cpu_priority(0); @@ -790,9 +776,17 @@ void xics_teardown_cpu(int secondary) lpar_qirr_info(cpu, 0xff); else direct_qirr_info(cpu, 0xff); +} + +void xics_kexec_teardown_cpu(int secondary) +{ + unsigned int ipi; + struct irq_desc *desc; + + xics_teardown_cpu(); /* - * we need to EOI the IPI if we got here from kexec down IPI + * we need to EOI the IPI * * probably need to check all the other interrupts too * should we be flagging idle loop instead? @@ -880,8 +874,8 @@ void xics_migrate_irqs_away(void) virq, cpu); /* Reset affinity to all cpus */ + irq_desc[virq].affinity = CPU_MASK_ALL; desc->chip->set_affinity(virq, CPU_MASK_ALL); - irq_desc[irq].affinity = CPU_MASK_ALL; unlock: spin_unlock_irqrestore(&desc->lock, flags); } diff --git a/arch/powerpc/platforms/pseries/xics.h b/arch/powerpc/platforms/pseries/xics.h index 9ffd809d29e2..c26bcff47b6d 100644 --- a/arch/powerpc/platforms/pseries/xics.h +++ b/arch/powerpc/platforms/pseries/xics.h @@ -16,7 +16,8 @@ extern void xics_init_IRQ(void); extern void xics_setup_cpu(void); -extern void xics_teardown_cpu(int secondary); +extern void xics_teardown_cpu(void); +extern void xics_kexec_teardown_cpu(int secondary); extern void xics_cause_IPI(int cpu); extern void xics_request_IPIs(void); extern void xics_migrate_irqs_away(void); diff --git a/arch/powerpc/sysdev/dcr.c b/arch/powerpc/sysdev/dcr.c index 427027c7ea0f..437e48d3ae33 100644 --- a/arch/powerpc/sysdev/dcr.c +++ b/arch/powerpc/sysdev/dcr.c @@ -137,5 +137,6 @@ void dcr_unmap(dcr_host_t host, unsigned int dcr_c) h.token = NULL; } EXPORT_SYMBOL_GPL(dcr_unmap); - -#endif /* !defined(CONFIG_PPC_DCR_NATIVE) */ +#else /* defined(CONFIG_PPC_DCR_NATIVE) */ +DEFINE_SPINLOCK(dcr_ind_lock); +#endif /* !defined(CONFIG_PPC_DCR_NATIVE) */ diff --git a/arch/powerpc/sysdev/fsl_soc.c b/arch/powerpc/sysdev/fsl_soc.c index e48b20e934ca..2c5388ce902a 100644 --- a/arch/powerpc/sysdev/fsl_soc.c +++ b/arch/powerpc/sysdev/fsl_soc.c @@ -1342,7 +1342,7 @@ static int __init of_fsl_spi_probe(char *type, char *compatible, u32 sysclk, if (ret) goto unreg; - ret = platform_device_register(pdev); + ret = platform_device_add(pdev); if (ret) goto unreg; diff --git a/arch/powerpc/sysdev/mpc8xx_pic.c b/arch/powerpc/sysdev/mpc8xx_pic.c index 0e74a4bd9827..5d2d5522ef41 100644 --- a/arch/powerpc/sysdev/mpc8xx_pic.c +++ b/arch/powerpc/sysdev/mpc8xx_pic.c @@ -174,15 +174,19 @@ int mpc8xx_pic_init(void) goto out; siu_reg = ioremap(res.start, res.end - res.start + 1); - if (siu_reg == NULL) - return -EINVAL; + if (siu_reg == NULL) { + ret = -EINVAL; + goto out; + } - mpc8xx_pic_host = irq_alloc_host(of_node_get(np), IRQ_HOST_MAP_LINEAR, + mpc8xx_pic_host = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, 64, &mpc8xx_pic_host_ops, 64); if (mpc8xx_pic_host == NULL) { printk(KERN_ERR "MPC8xx PIC: failed to allocate irq host!\n"); ret = -ENOMEM; + goto out; } + return 0; out: of_node_put(np); diff --git a/arch/powerpc/sysdev/qe_lib/qe.c b/arch/powerpc/sysdev/qe_lib/qe.c index 5ef844da9355..6efbd5e5bb1b 100644 --- a/arch/powerpc/sysdev/qe_lib/qe.c +++ b/arch/powerpc/sysdev/qe_lib/qe.c @@ -66,7 +66,7 @@ phys_addr_t get_qe_base(void) { struct device_node *qe; unsigned int size; - const void *prop; + const u32 *prop; if (qebase != -1) return qebase; @@ -79,7 +79,8 @@ phys_addr_t get_qe_base(void) } prop = of_get_property(qe, "reg", &size); - qebase = of_translate_address(qe, prop); + if (prop && size >= sizeof(*prop)) + qebase = of_translate_address(qe, prop); of_node_put(qe); return qebase; @@ -172,10 +173,9 @@ unsigned int get_brg_clk(void) } prop = of_get_property(qe, "brg-frequency", &size); - if (!prop || size != sizeof(*prop)) - return brg_clk; + if (prop && size == sizeof(*prop)) + brg_clk = *prop; - brg_clk = *prop; of_node_put(qe); return brg_clk; diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 29ae165d1749..f9f8779022a0 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -649,21 +649,24 @@ setup_memory(void) /* * Reserve memory used for lowcore/command line/kernel image. */ - reserve_bootmem(0, (unsigned long)_ehead); + reserve_bootmem(0, (unsigned long)_ehead, BOOTMEM_DEFAULT); reserve_bootmem((unsigned long)_stext, - PFN_PHYS(start_pfn) - (unsigned long)_stext); + PFN_PHYS(start_pfn) - (unsigned long)_stext, + BOOTMEM_DEFAULT); /* * Reserve the bootmem bitmap itself as well. We do this in two * steps (first step was init_bootmem()) because this catches * the (very unlikely) case of us accidentally initializing the * bootmem allocator with an invalid RAM area. */ - reserve_bootmem(start_pfn << PAGE_SHIFT, bootmap_size); + reserve_bootmem(start_pfn << PAGE_SHIFT, bootmap_size, + BOOTMEM_DEFAULT); #ifdef CONFIG_BLK_DEV_INITRD if (INITRD_START && INITRD_SIZE) { if (INITRD_START + INITRD_SIZE <= memory_end) { - reserve_bootmem(INITRD_START, INITRD_SIZE); + reserve_bootmem(INITRD_START, INITRD_SIZE, + BOOTMEM_DEFAULT); initrd_start = INITRD_START; initrd_end = initrd_start + INITRD_SIZE; } else { diff --git a/arch/sh/kernel/setup.c b/arch/sh/kernel/setup.c index 855cdf9d85b1..18a5baf2cbad 100644 --- a/arch/sh/kernel/setup.c +++ b/arch/sh/kernel/setup.c @@ -140,18 +140,26 @@ static void __init reserve_crashkernel(void) ret = parse_crashkernel(boot_command_line, free_mem, &crash_size, &crash_base); if (ret == 0 && crash_size) { - if (crash_base > 0) { - printk(KERN_INFO "Reserving %ldMB of memory at %ldMB " - "for crashkernel (System RAM: %ldMB)\n", - (unsigned long)(crash_size >> 20), - (unsigned long)(crash_base >> 20), - (unsigned long)(free_mem >> 20)); - crashk_res.start = crash_base; - crashk_res.end = crash_base + crash_size - 1; - reserve_bootmem(crash_base, crash_size); - } else + if (crash_base <= 0) { printk(KERN_INFO "crashkernel reservation failed - " "you have to specify a base address\n"); + return; + } + + if (reserve_bootmem(crash_base, crash_size, + BOOTMEM_EXCLUSIVE) < 0) { + printk(KERN_INFO "crashkernel reservation failed - " + "memory is in use\n"); + return; + } + + printk(KERN_INFO "Reserving %ldMB of memory at %ldMB " + "for crashkernel (System RAM: %ldMB)\n", + (unsigned long)(crash_size >> 20), + (unsigned long)(crash_base >> 20), + (unsigned long)(free_mem >> 20)); + crashk_res.start = crash_base; + crashk_res.end = crash_base + crash_size - 1; } } #else @@ -184,13 +192,14 @@ void __init setup_bootmem_allocator(unsigned long free_pfn) * an invalid RAM area. */ reserve_bootmem(__MEMORY_START+PAGE_SIZE, - (PFN_PHYS(free_pfn)+bootmap_size+PAGE_SIZE-1)-__MEMORY_START); + (PFN_PHYS(free_pfn)+bootmap_size+PAGE_SIZE-1)-__MEMORY_START, + BOOTMEM_DEFAULT); /* * reserve physical page 0 - it's a special BIOS page on many boxes, * enabling clean reboots, SMP operation, laptop functions. */ - reserve_bootmem(__MEMORY_START, PAGE_SIZE); + reserve_bootmem(__MEMORY_START, PAGE_SIZE, BOOTMEM_DEFAULT); sparse_memory_present_with_active_regions(0); @@ -200,7 +209,7 @@ void __init setup_bootmem_allocator(unsigned long free_pfn) if (LOADER_TYPE && INITRD_START) { if (INITRD_START + INITRD_SIZE <= (max_low_pfn << PAGE_SHIFT)) { reserve_bootmem(INITRD_START + __MEMORY_START, - INITRD_SIZE); + INITRD_SIZE, BOOTMEM_DEFAULT); initrd_start = INITRD_START + PAGE_OFFSET + __MEMORY_START; initrd_end = initrd_start + INITRD_SIZE; diff --git a/arch/sh/mm/numa.c b/arch/sh/mm/numa.c index 8aff065dd307..2de7302724fc 100644 --- a/arch/sh/mm/numa.c +++ b/arch/sh/mm/numa.c @@ -80,9 +80,9 @@ void __init setup_bootmem_node(int nid, unsigned long start, unsigned long end) /* Reserve the pgdat and bootmap space with the bootmem allocator */ reserve_bootmem_node(NODE_DATA(nid), start_pfn << PAGE_SHIFT, - sizeof(struct pglist_data)); + sizeof(struct pglist_data), BOOTMEM_DEFAULT); reserve_bootmem_node(NODE_DATA(nid), free_pfn << PAGE_SHIFT, - bootmap_pages << PAGE_SHIFT); + bootmap_pages << PAGE_SHIFT, BOOTMEM_DEFAULT); /* It's up */ node_set_online(nid); diff --git a/arch/sparc/mm/init.c b/arch/sparc/mm/init.c index a1bef07755a9..b89837accc88 100644 --- a/arch/sparc/mm/init.c +++ b/arch/sparc/mm/init.c @@ -259,7 +259,7 @@ unsigned long __init bootmem_init(unsigned long *pages_avail) if (initrd_start) { /* Reserve the initrd image area. */ size = initrd_end - initrd_start; - reserve_bootmem(initrd_start, size); + reserve_bootmem(initrd_start, size, BOOTMEM_DEFAULT); *pages_avail -= PAGE_ALIGN(size) >> PAGE_SHIFT; initrd_start = (initrd_start - phys_base) + PAGE_OFFSET; @@ -268,7 +268,7 @@ unsigned long __init bootmem_init(unsigned long *pages_avail) #endif /* Reserve the kernel text/data/bss. */ size = (start_pfn << PAGE_SHIFT) - phys_base; - reserve_bootmem(phys_base, size); + reserve_bootmem(phys_base, size, BOOTMEM_DEFAULT); *pages_avail -= PAGE_ALIGN(size) >> PAGE_SHIFT; /* Reserve the bootmem map. We do not account for it @@ -276,7 +276,7 @@ unsigned long __init bootmem_init(unsigned long *pages_avail) * in free_all_bootmem. */ size = bootmap_size; - reserve_bootmem((bootmap_pfn << PAGE_SHIFT), size); + reserve_bootmem((bootmap_pfn << PAGE_SHIFT), size, BOOTMEM_DEFAULT); *pages_avail -= PAGE_ALIGN(size) >> PAGE_SHIFT; return max_pfn; diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c index 523e993ee90c..e726c45645ff 100644 --- a/arch/sparc64/mm/init.c +++ b/arch/sparc64/mm/init.c @@ -997,7 +997,7 @@ static unsigned long __init bootmem_init(unsigned long *pages_avail, prom_printf("reserve_bootmem(initrd): base[%llx] size[%lx]\n", initrd_start, initrd_end); #endif - reserve_bootmem(initrd_start, size); + reserve_bootmem(initrd_start, size, BOOTMEM_DEFAULT); initrd_start += PAGE_OFFSET; initrd_end += PAGE_OFFSET; @@ -1007,7 +1007,7 @@ static unsigned long __init bootmem_init(unsigned long *pages_avail, #ifdef CONFIG_DEBUG_BOOTMEM prom_printf("reserve_bootmem(kernel): base[%lx] size[%lx]\n", kern_base, kern_size); #endif - reserve_bootmem(kern_base, kern_size); + reserve_bootmem(kern_base, kern_size, BOOTMEM_DEFAULT); *pages_avail -= PAGE_ALIGN(kern_size) >> PAGE_SHIFT; /* Add back in the initmem pages. */ @@ -1024,7 +1024,7 @@ static unsigned long __init bootmem_init(unsigned long *pages_avail, prom_printf("reserve_bootmem(bootmap): base[%lx] size[%lx]\n", (bootmap_pfn << PAGE_SHIFT), size); #endif - reserve_bootmem((bootmap_pfn << PAGE_SHIFT), size); + reserve_bootmem((bootmap_pfn << PAGE_SHIFT), size, BOOTMEM_DEFAULT); for (i = 0; i < pavail_ents; i++) { unsigned long start_pfn, end_pfn; @@ -1489,7 +1489,7 @@ static void __init taint_real_pages(void) goto do_next_page; } } - reserve_bootmem(old_start, PAGE_SIZE); + reserve_bootmem(old_start, PAGE_SIZE, BOOTMEM_DEFAULT); do_next_page: old_start += PAGE_SIZE; diff --git a/arch/v850/kernel/anna.c b/arch/v850/kernel/anna.c index 0e429041a117..5978a25170fb 100644 --- a/arch/v850/kernel/anna.c +++ b/arch/v850/kernel/anna.c @@ -85,7 +85,8 @@ void __init mach_reserve_bootmem () /* The space between SRAM and SDRAM is filled with duplicate images of SRAM. Prevent the kernel from using them. */ reserve_bootmem (SRAM_ADDR + SRAM_SIZE, - SDRAM_ADDR - (SRAM_ADDR + SRAM_SIZE)); + SDRAM_ADDR - (SRAM_ADDR + SRAM_SIZE), + BOOTMEM_DEFAULT); } void mach_gettimeofday (struct timespec *tv) diff --git a/arch/v850/kernel/as85ep1.c b/arch/v850/kernel/as85ep1.c index 18437bc5c3ad..b525ecf3aea4 100644 --- a/arch/v850/kernel/as85ep1.c +++ b/arch/v850/kernel/as85ep1.c @@ -116,7 +116,8 @@ void __init mach_reserve_bootmem () if (SDRAM_ADDR < RAM_END && SDRAM_ADDR > RAM_START) /* We can't use the space between SRAM and SDRAM, so prevent the kernel from trying. */ - reserve_bootmem (SRAM_END, SDRAM_ADDR - SRAM_END); + reserve_bootmem(SRAM_END, SDRAM_ADDR - SRAM_END, + BOOTMEM_DEFAULT); } void mach_gettimeofday (struct timespec *tv) diff --git a/arch/v850/kernel/rte_ma1_cb.c b/arch/v850/kernel/rte_ma1_cb.c index 9a716f946421..08abf3d5f8df 100644 --- a/arch/v850/kernel/rte_ma1_cb.c +++ b/arch/v850/kernel/rte_ma1_cb.c @@ -46,13 +46,15 @@ void __init mach_reserve_bootmem () { #ifdef CONFIG_RTE_CB_MULTI /* Prevent the kernel from touching the monitor's scratch RAM. */ - reserve_bootmem (MON_SCRATCH_ADDR, MON_SCRATCH_SIZE); + reserve_bootmem(MON_SCRATCH_ADDR, MON_SCRATCH_SIZE, + BOOTMEM_DEFAULT); #endif /* The space between SRAM and SDRAM is filled with duplicate images of SRAM. Prevent the kernel from using them. */ reserve_bootmem (SRAM_ADDR + SRAM_SIZE, - SDRAM_ADDR - (SRAM_ADDR + SRAM_SIZE)); + SDRAM_ADDR - (SRAM_ADDR + SRAM_SIZE), + BOOTMEM_DEFAULT); } void mach_gettimeofday (struct timespec *tv) diff --git a/arch/v850/kernel/setup.c b/arch/v850/kernel/setup.c index a914f244f494..a0a8456a8430 100644 --- a/arch/v850/kernel/setup.c +++ b/arch/v850/kernel/setup.c @@ -241,15 +241,18 @@ init_bootmem_alloc (unsigned long ram_start, unsigned long ram_len) if (kram_end > kram_start) /* Reserve the RAM part of the kernel's address space, so it doesn't get allocated. */ - reserve_bootmem (kram_start, kram_end - kram_start); + reserve_bootmem(kram_start, kram_end - kram_start, + BOOTMEM_DEFAULT); if (intv_in_ram && !intv_in_kram) /* Reserve the interrupt vector space. */ - reserve_bootmem (intv_start, intv_end - intv_start); + reserve_bootmem(intv_start, intv_end - intv_start, + BOOTMEM_DEFAULT); if (bootmap >= ram_start && bootmap < ram_end) /* Reserve the bootmap space. */ - reserve_bootmem (bootmap, bootmap_len); + reserve_bootmem(bootmap, bootmap_len, + BOOTMEM_DEFAULT); /* Reserve the memory used by the root filesystem image if it's in RAM. */ @@ -257,7 +260,8 @@ init_bootmem_alloc (unsigned long ram_start, unsigned long ram_len) && (unsigned long)&_root_fs_image_start >= ram_start && (unsigned long)&_root_fs_image_start < ram_end) reserve_bootmem ((unsigned long)&_root_fs_image_start, - &_root_fs_image_end - &_root_fs_image_start); + &_root_fs_image_end - &_root_fs_image_start, + BOOTMEM_DEFAULT); /* Let the platform-dependent code reserve some too. */ if (mrb) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index e6728bd61cc1..923c3babd667 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -631,7 +631,6 @@ config TOSHIBA config I8K tristate "Dell laptop support" - depends on X86_32 ---help--- This adds a driver to safely access the System Management Mode of the CPU on the Dell Inspiron 8000. The System Management Mode diff --git a/arch/x86/ia32/ia32_aout.c b/arch/x86/ia32/ia32_aout.c index e4c12079171b..58cccb6483b0 100644 --- a/arch/x86/ia32/ia32_aout.c +++ b/arch/x86/ia32/ia32_aout.c @@ -172,8 +172,7 @@ static int aout_core_dump(long signr, struct pt_regs *regs, struct file *file, has_dumped = 1; current->flags |= PF_DUMPCORE; strncpy(dump.u_comm, current->comm, sizeof(current->comm)); - dump.u_ar0 = (u32)(((unsigned long)(&dump.regs)) - - ((unsigned long)(&dump))); + dump.u_ar0 = offsetof(struct user32, regs); dump.signal = signr; dump_thread32(regs, &dump); diff --git a/arch/x86/kernel/machine_kexec_32.c b/arch/x86/kernel/machine_kexec_32.c index c1cfd60639d4..d0b234c9fc31 100644 --- a/arch/x86/kernel/machine_kexec_32.c +++ b/arch/x86/kernel/machine_kexec_32.c @@ -151,7 +151,7 @@ NORET_TYPE void machine_kexec(struct kimage *image) void arch_crash_save_vmcoreinfo(void) { -#ifdef CONFIG_ARCH_DISCONTIGMEM_ENABLE +#ifdef CONFIG_NUMA VMCOREINFO_SYMBOL(node_data); VMCOREINFO_LENGTH(node_data, MAX_NUMNODES); #endif diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c index a1fef42f8cdb..236d2f8f7ddc 100644 --- a/arch/x86/kernel/machine_kexec_64.c +++ b/arch/x86/kernel/machine_kexec_64.c @@ -234,5 +234,10 @@ NORET_TYPE void machine_kexec(struct kimage *image) void arch_crash_save_vmcoreinfo(void) { VMCOREINFO_SYMBOL(init_level4_pgt); + +#ifdef CONFIG_NUMA + VMCOREINFO_SYMBOL(node_data); + VMCOREINFO_LENGTH(node_data, MAX_NUMNODES); +#endif } diff --git a/arch/x86/kernel/mpparse_32.c b/arch/x86/kernel/mpparse_32.c index 67009cdd5eca..f349e68e45a0 100644 --- a/arch/x86/kernel/mpparse_32.c +++ b/arch/x86/kernel/mpparse_32.c @@ -736,7 +736,8 @@ static int __init smp_scan_config (unsigned long base, unsigned long length) smp_found_config = 1; printk(KERN_INFO "found SMP MP-table at [%p] %08lx\n", mpf, virt_to_phys(mpf)); - reserve_bootmem(virt_to_phys(mpf), PAGE_SIZE); + reserve_bootmem(virt_to_phys(mpf), PAGE_SIZE, + BOOTMEM_DEFAULT); if (mpf->mpf_physptr) { /* * We cannot access to MPC table to compute @@ -751,7 +752,8 @@ static int __init smp_scan_config (unsigned long base, unsigned long length) unsigned long end = max_low_pfn * PAGE_SIZE; if (mpf->mpf_physptr + size > end) size = end - mpf->mpf_physptr; - reserve_bootmem(mpf->mpf_physptr, size); + reserve_bootmem(mpf->mpf_physptr, size, + BOOTMEM_DEFAULT); } mpf_found = mpf; diff --git a/arch/x86/kernel/setup_32.c b/arch/x86/kernel/setup_32.c index 62adc5f20be5..d1d8c347cc0b 100644 --- a/arch/x86/kernel/setup_32.c +++ b/arch/x86/kernel/setup_32.c @@ -390,7 +390,7 @@ static void __init reserve_ebda_region(void) unsigned int addr; addr = get_bios_ebda(); if (addr) - reserve_bootmem(addr, PAGE_SIZE); + reserve_bootmem(addr, PAGE_SIZE, BOOTMEM_DEFAULT); } #ifndef CONFIG_NEED_MULTIPLE_NODES @@ -484,7 +484,8 @@ static void __init reserve_crashkernel(void) (unsigned long)(total_mem >> 20)); crashk_res.start = crash_base; crashk_res.end = crash_base + crash_size - 1; - reserve_bootmem(crash_base, crash_size); + reserve_bootmem(crash_base, crash_size, + BOOTMEM_DEFAULT); } else printk(KERN_INFO "crashkernel reservation failed - " "you have to specify a base address\n"); @@ -525,7 +526,7 @@ static void __init reserve_initrd(void) } if (ramdisk_end <= end_of_lowmem) { /* All in lowmem, easy case */ - reserve_bootmem(ramdisk_image, ramdisk_size); + reserve_bootmem(ramdisk_image, ramdisk_size, BOOTMEM_DEFAULT); initrd_start = ramdisk_image + PAGE_OFFSET; initrd_end = initrd_start+ramdisk_size; return; @@ -536,7 +537,7 @@ static void __init reserve_initrd(void) /* Note: this includes all the lowmem currently occupied by the initrd, we rely on that fact to keep the data intact. */ - reserve_bootmem(ramdisk_here, ramdisk_size); + reserve_bootmem(ramdisk_here, ramdisk_size, BOOTMEM_DEFAULT); initrd_start = ramdisk_here + PAGE_OFFSET; initrd_end = initrd_start + ramdisk_size; @@ -606,13 +607,14 @@ void __init setup_bootmem_allocator(void) * bootmem allocator with an invalid RAM area. */ reserve_bootmem(__pa_symbol(_text), (PFN_PHYS(min_low_pfn) + - bootmap_size + PAGE_SIZE-1) - __pa_symbol(_text)); + bootmap_size + PAGE_SIZE-1) - __pa_symbol(_text), + BOOTMEM_DEFAULT); /* * reserve physical page 0 - it's a special BIOS page on many boxes, * enabling clean reboots, SMP operation, laptop functions. */ - reserve_bootmem(0, PAGE_SIZE); + reserve_bootmem(0, PAGE_SIZE, BOOTMEM_DEFAULT); /* reserve EBDA region, it's a 4K region */ reserve_ebda_region(); @@ -622,7 +624,7 @@ void __init setup_bootmem_allocator(void) unless you have no PS/2 mouse plugged in. */ if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD && boot_cpu_data.x86 == 6) - reserve_bootmem(0xa0000 - 4096, 4096); + reserve_bootmem(0xa0000 - 4096, 4096, BOOTMEM_DEFAULT); #ifdef CONFIG_SMP /* @@ -630,7 +632,7 @@ void __init setup_bootmem_allocator(void) * FIXME: Don't need the extra page at 4K, but need to fix * trampoline before removing it. (see the GDT stuff) */ - reserve_bootmem(PAGE_SIZE, PAGE_SIZE); + reserve_bootmem(PAGE_SIZE, PAGE_SIZE, BOOTMEM_DEFAULT); #endif #ifdef CONFIG_ACPI_SLEEP /* diff --git a/arch/x86/kernel/setup_64.c b/arch/x86/kernel/setup_64.c index c8939dfddfba..a49f5f734a5e 100644 --- a/arch/x86/kernel/setup_64.c +++ b/arch/x86/kernel/setup_64.c @@ -189,7 +189,7 @@ contig_initmem_init(unsigned long start_pfn, unsigned long end_pfn) bootmap_size = init_bootmem(bootmap >> PAGE_SHIFT, end_pfn); e820_register_active_regions(0, start_pfn, end_pfn); free_bootmem_with_active_regions(0, end_pfn); - reserve_bootmem(bootmap, bootmap_size); + reserve_bootmem(bootmap, bootmap_size, BOOTMEM_DEFAULT); } #endif @@ -220,28 +220,35 @@ static inline void copy_edd(void) #ifdef CONFIG_KEXEC static void __init reserve_crashkernel(void) { - unsigned long long free_mem; + unsigned long long total_mem; unsigned long long crash_size, crash_base; int ret; - free_mem = - ((unsigned long long)max_low_pfn - min_low_pfn) << PAGE_SHIFT; + total_mem = ((unsigned long long)max_low_pfn - min_low_pfn) << PAGE_SHIFT; - ret = parse_crashkernel(boot_command_line, free_mem, + ret = parse_crashkernel(boot_command_line, total_mem, &crash_size, &crash_base); if (ret == 0 && crash_size) { - if (crash_base > 0) { - printk(KERN_INFO "Reserving %ldMB of memory at %ldMB " - "for crashkernel (System RAM: %ldMB)\n", - (unsigned long)(crash_size >> 20), - (unsigned long)(crash_base >> 20), - (unsigned long)(free_mem >> 20)); - crashk_res.start = crash_base; - crashk_res.end = crash_base + crash_size - 1; - reserve_bootmem(crash_base, crash_size); - } else + if (crash_base <= 0) { printk(KERN_INFO "crashkernel reservation failed - " "you have to specify a base address\n"); + return; + } + + if (reserve_bootmem(crash_base, crash_size, + BOOTMEM_EXCLUSIVE) < 0) { + printk(KERN_INFO "crashkernel reservation failed - " + "memory is in use\n"); + return; + } + + printk(KERN_INFO "Reserving %ldMB of memory at %ldMB " + "for crashkernel (System RAM: %ldMB)\n", + (unsigned long)(crash_size >> 20), + (unsigned long)(crash_base >> 20), + (unsigned long)(total_mem >> 20)); + crashk_res.start = crash_base; + crashk_res.end = crash_base + crash_size - 1; } } #else diff --git a/arch/x86/mm/discontig_32.c b/arch/x86/mm/discontig_32.c index 04b1d20e2613..c394ca0720b8 100644 --- a/arch/x86/mm/discontig_32.c +++ b/arch/x86/mm/discontig_32.c @@ -391,7 +391,8 @@ unsigned long __init setup_memory(void) void __init numa_kva_reserve(void) { if (kva_pages) - reserve_bootmem(PFN_PHYS(kva_start_pfn), PFN_PHYS(kva_pages)); + reserve_bootmem(PFN_PHYS(kva_start_pfn), PFN_PHYS(kva_pages), + BOOTMEM_DEFAULT); } void __init zone_sizes_init(void) diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index 9b61c75a2355..5fe880fc305d 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -644,9 +644,9 @@ void __init reserve_bootmem_generic(unsigned long phys, unsigned len) /* Should check here against the e820 map to avoid double free */ #ifdef CONFIG_NUMA - reserve_bootmem_node(NODE_DATA(nid), phys, len); + reserve_bootmem_node(NODE_DATA(nid), phys, len, BOOTMEM_DEFAULT); #else - reserve_bootmem(phys, len); + reserve_bootmem(phys, len, BOOTMEM_DEFAULT); #endif if (phys+len <= MAX_DMA_PFN*PAGE_SIZE) { dma_reserve += len / PAGE_SIZE; diff --git a/arch/x86/mm/numa_64.c b/arch/x86/mm/numa_64.c index 5a02bf4c91ec..1aecc658cd7d 100644 --- a/arch/x86/mm/numa_64.c +++ b/arch/x86/mm/numa_64.c @@ -238,9 +238,10 @@ void __init setup_node_bootmem(int nodeid, unsigned long start, free_bootmem_with_active_regions(nodeid, end); - reserve_bootmem_node(NODE_DATA(nodeid), nodedata_phys, pgdat_size); + reserve_bootmem_node(NODE_DATA(nodeid), nodedata_phys, pgdat_size, + BOOTMEM_DEFAULT); reserve_bootmem_node(NODE_DATA(nodeid), bootmap_start, - bootmap_pages<<PAGE_SHIFT); + bootmap_pages<<PAGE_SHIFT, BOOTMEM_DEFAULT); #ifdef CONFIG_ACPI_NUMA srat_reserve_add_area(nodeid); #endif diff --git a/arch/x86/mm/srat_64.c b/arch/x86/mm/srat_64.c index 65416f843e59..ecd91ea8a8ae 100644 --- a/arch/x86/mm/srat_64.c +++ b/arch/x86/mm/srat_64.c @@ -488,7 +488,8 @@ void __init srat_reserve_add_area(int nodeid) printk(KERN_INFO "SRAT: This will cost you %Lu MB of " "pre-allocated memory.\n", (unsigned long long)total_mb); reserve_bootmem_node(NODE_DATA(nodeid), nodes_add[nodeid].start, - nodes_add[nodeid].end - nodes_add[nodeid].start); + nodes_add[nodeid].end - nodes_add[nodeid].start, + BOOTMEM_DEFAULT); } } diff --git a/crypto/cbc.c b/crypto/cbc.c index 6affff882cf8..61ac42e1e32b 100644 --- a/crypto/cbc.c +++ b/crypto/cbc.c @@ -224,7 +224,7 @@ static struct crypto_instance *crypto_cbc_alloc(struct rtattr **tb) alg = crypto_get_attr_alg(tb, CRYPTO_ALG_TYPE_CIPHER, CRYPTO_ALG_TYPE_MASK); if (IS_ERR(alg)) - return ERR_PTR(PTR_ERR(alg)); + return ERR_CAST(alg); inst = ERR_PTR(-EINVAL); if (!is_power_of_2(alg->cra_blocksize)) diff --git a/crypto/cryptd.c b/crypto/cryptd.c index 074298f2f8e3..250425263e00 100644 --- a/crypto/cryptd.c +++ b/crypto/cryptd.c @@ -230,7 +230,7 @@ static struct crypto_instance *cryptd_alloc_blkcipher( alg = crypto_get_attr_alg(tb, CRYPTO_ALG_TYPE_BLKCIPHER, CRYPTO_ALG_TYPE_MASK); if (IS_ERR(alg)) - return ERR_PTR(PTR_ERR(alg)); + return ERR_CAST(alg); inst = cryptd_alloc_instance(alg, state); if (IS_ERR(inst)) @@ -267,7 +267,7 @@ static struct crypto_instance *cryptd_alloc(struct rtattr **tb) algt = crypto_get_attr_type(tb); if (IS_ERR(algt)) - return ERR_PTR(PTR_ERR(algt)); + return ERR_CAST(algt); switch (algt->type & algt->mask & CRYPTO_ALG_TYPE_MASK) { case CRYPTO_ALG_TYPE_BLKCIPHER: diff --git a/crypto/ecb.c b/crypto/ecb.c index 6310387a872c..a46838e98a71 100644 --- a/crypto/ecb.c +++ b/crypto/ecb.c @@ -128,7 +128,7 @@ static struct crypto_instance *crypto_ecb_alloc(struct rtattr **tb) alg = crypto_get_attr_alg(tb, CRYPTO_ALG_TYPE_CIPHER, CRYPTO_ALG_TYPE_MASK); if (IS_ERR(alg)) - return ERR_PTR(PTR_ERR(alg)); + return ERR_CAST(alg); inst = crypto_alloc_instance("ecb", alg); if (IS_ERR(inst)) diff --git a/crypto/hmac.c b/crypto/hmac.c index a1d016a50e7d..b60c3c7aa320 100644 --- a/crypto/hmac.c +++ b/crypto/hmac.c @@ -213,7 +213,7 @@ static struct crypto_instance *hmac_alloc(struct rtattr **tb) alg = crypto_get_attr_alg(tb, CRYPTO_ALG_TYPE_HASH, CRYPTO_ALG_TYPE_HASH_MASK); if (IS_ERR(alg)) - return ERR_PTR(PTR_ERR(alg)); + return ERR_CAST(alg); inst = crypto_alloc_instance("hmac", alg); if (IS_ERR(inst)) diff --git a/crypto/lrw.c b/crypto/lrw.c index 621095db28b3..9d52e580d10a 100644 --- a/crypto/lrw.c +++ b/crypto/lrw.c @@ -241,7 +241,7 @@ static struct crypto_instance *alloc(struct rtattr **tb) alg = crypto_get_attr_alg(tb, CRYPTO_ALG_TYPE_CIPHER, CRYPTO_ALG_TYPE_MASK); if (IS_ERR(alg)) - return ERR_PTR(PTR_ERR(alg)); + return ERR_CAST(alg); inst = crypto_alloc_instance("lrw", alg); if (IS_ERR(inst)) diff --git a/crypto/pcbc.c b/crypto/pcbc.c index fe704775f88f..d1b8bdfb5855 100644 --- a/crypto/pcbc.c +++ b/crypto/pcbc.c @@ -234,7 +234,7 @@ static struct crypto_instance *crypto_pcbc_alloc(struct rtattr **tb) alg = crypto_get_attr_alg(tb, CRYPTO_ALG_TYPE_CIPHER, CRYPTO_ALG_TYPE_MASK); if (IS_ERR(alg)) - return ERR_PTR(PTR_ERR(alg)); + return ERR_CAST(alg); inst = crypto_alloc_instance("pcbc", alg); if (IS_ERR(inst)) diff --git a/crypto/xcbc.c b/crypto/xcbc.c index a82959df678c..86727403e5ab 100644 --- a/crypto/xcbc.c +++ b/crypto/xcbc.c @@ -301,7 +301,7 @@ static struct crypto_instance *xcbc_alloc(struct rtattr **tb) alg = crypto_get_attr_alg(tb, CRYPTO_ALG_TYPE_CIPHER, CRYPTO_ALG_TYPE_MASK); if (IS_ERR(alg)) - return ERR_PTR(PTR_ERR(alg)); + return ERR_CAST(alg); switch(alg->cra_blocksize) { case 16: diff --git a/drivers/block/xsysace.c b/drivers/block/xsysace.c index 78ebfffc77e3..4a7a059ebaf7 100644 --- a/drivers/block/xsysace.c +++ b/drivers/block/xsysace.c @@ -1202,8 +1202,10 @@ static int __devexit ace_of_remove(struct of_device *op) } /* Match table for of_platform binding */ -static struct of_device_id __devinit ace_of_match[] = { - { .compatible = "xilinx,xsysace", }, +static struct of_device_id ace_of_match[] __devinitdata = { + { .compatible = "xlnx,opb-sysace-1.00.b", }, + { .compatible = "xlnx,opb-sysace-1.00.c", }, + { .compatible = "xlnx,xps-sysace-1.00.a", }, {}, }; MODULE_DEVICE_TABLE(of, ace_of_match); diff --git a/drivers/cdrom/viocd.c b/drivers/cdrom/viocd.c index 8473b9f1da96..cac06bc1754b 100644 --- a/drivers/cdrom/viocd.c +++ b/drivers/cdrom/viocd.c @@ -558,7 +558,7 @@ static struct cdrom_device_ops viocd_dops = { .capability = CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_SELECT_SPEED | CDC_SELECT_DISC | CDC_MULTI_SESSION | CDC_MCN | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | CDC_RESET | CDC_DRIVE_STATUS | CDC_GENERIC_PACKET | CDC_CD_R | CDC_CD_RW | CDC_DVD | CDC_DVD_R | CDC_DVD_RAM | CDC_RAM }; -static int __init find_capability(const char *type) +static int find_capability(const char *type) { struct capability_entry *entry; diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 85bf9b2aa74a..f01ac9a07bf5 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -194,17 +194,6 @@ config MOXA_INTELLIO module will be called moxa. config MOXA_SMARTIO - tristate "Moxa SmartIO support (OBSOLETE)" - depends on SERIAL_NONSTANDARD - help - Say Y here if you have a Moxa SmartIO multiport serial card. - - This driver can also be built as a module ( = code which can be - inserted in and removed from the running kernel whenever you want). - The module will be called mxser. If you want to do that, say M - here. - -config MOXA_SMARTIO_NEW tristate "Moxa SmartIO support v. 2.0" depends on SERIAL_NONSTANDARD && (PCI || EISA || ISA) help @@ -215,7 +204,7 @@ config MOXA_SMARTIO_NEW changes finally resulting in PCI probing. This driver can also be built as a module. The module will be called - mxser_new. If you want to do that, say M here. + mxser. If you want to do that, say M here. config ISI tristate "Multi-Tech multiport card support (EXPERIMENTAL)" @@ -841,6 +830,16 @@ config DTLK To compile this driver as a module, choose M here: the module will be called dtlk. +config XILINX_HWICAP + tristate "Xilinx HWICAP Support" + depends on XILINX_VIRTEX + help + This option enables support for Xilinx Internal Configuration + Access Port (ICAP) driver. The ICAP is used on Xilinx Virtex + FPGA platforms to partially reconfigure the FPGA at runtime. + + If unsure, say N. + config R3964 tristate "Siemens R3964 line discipline" ---help--- diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 96fc01eddefe..5407b7615614 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -33,7 +33,6 @@ obj-$(CONFIG_MOXA_INTELLIO) += moxa.o obj-$(CONFIG_A2232) += ser_a2232.o generic_serial.o obj-$(CONFIG_ATARI_DSP56K) += dsp56k.o obj-$(CONFIG_MOXA_SMARTIO) += mxser.o -obj-$(CONFIG_MOXA_SMARTIO_NEW) += mxser_new.o obj-$(CONFIG_COMPUTONE) += ip2/ obj-$(CONFIG_RISCOM8) += riscom8.o obj-$(CONFIG_ISI) += isicom.o @@ -77,6 +76,7 @@ obj-$(CONFIG_EFI_RTC) += efirtc.o obj-$(CONFIG_SGI_DS1286) += ds1286.o obj-$(CONFIG_SGI_IP27_RTC) += ip27-rtc.o obj-$(CONFIG_DS1302) += ds1302.o +obj-$(CONFIG_XILINX_HWICAP) += xilinx_hwicap/ ifeq ($(CONFIG_GENERIC_NVRAM),y) obj-$(CONFIG_NVRAM) += generic_nvram.o else diff --git a/drivers/char/epca.h b/drivers/char/epca.h index a297238cd3ba..3c77c02b5d65 100644 --- a/drivers/char/epca.h +++ b/drivers/char/epca.h @@ -77,7 +77,6 @@ static char *board_desc[] = #define ON 1 #define FEPTIMEOUT 200000 -#define SERIAL_TYPE_NORMAL 1 #define SERIAL_TYPE_INFO 3 #define EPCA_EVENT_HANGUP 1 #define EPCA_MAGIC 0x5c6df104L diff --git a/drivers/char/esp.c b/drivers/char/esp.c index 28607763ae64..c01e26d9ee5e 100644 --- a/drivers/char/esp.c +++ b/drivers/char/esp.c @@ -111,9 +111,6 @@ static char serial_version[] __initdata = "2.2"; static struct tty_driver *esp_driver; -/* serial subtype definitions */ -#define SERIAL_TYPE_NORMAL 1 - /* * Serial driver configuration section. Here are the various options: * @@ -245,17 +242,6 @@ static void rs_start(struct tty_struct *tty) * ----------------------------------------------------------------------- */ -/* - * This routine is used by the interrupt handler to schedule - * processing in the software interrupt portion of the driver. - */ -static inline void rs_sched_event(struct esp_struct *info, - int event) -{ - info->event |= 1 << event; - schedule_work(&info->tqueue); -} - static DEFINE_SPINLOCK(pio_lock); static inline struct esp_pio_buffer *get_pio_buffer(void) @@ -477,7 +463,8 @@ static inline void transmit_chars_pio(struct esp_struct *info, } if (info->xmit_cnt < WAKEUP_CHARS) { - rs_sched_event(info, ESP_EVENT_WRITE_WAKEUP); + if (info->tty) + tty_wakeup(info->tty); #ifdef SERIAL_DEBUG_INTR printk("THRE..."); @@ -515,7 +502,8 @@ static inline void transmit_chars_dma(struct esp_struct *info, int num_bytes) info->xmit_tail = (info->xmit_tail + dma_bytes) & (ESP_XMIT_SIZE - 1); if (info->xmit_cnt < WAKEUP_CHARS) { - rs_sched_event(info, ESP_EVENT_WRITE_WAKEUP); + if (info->tty) + tty_wakeup(info->tty); #ifdef SERIAL_DEBUG_INTR printk("THRE..."); @@ -607,7 +595,7 @@ static inline void check_modem_status(struct esp_struct *info) #ifdef SERIAL_DEBUG_OPEN printk("scheduling hangup..."); #endif - schedule_work(&info->tqueue_hangup); + tty_hangup(info->tty); } } } @@ -723,41 +711,6 @@ static irqreturn_t rs_interrupt_single(int irq, void *dev_id) * ------------------------------------------------------------------- */ -static void do_softint(struct work_struct *work) -{ - struct esp_struct *info = - container_of(work, struct esp_struct, tqueue); - struct tty_struct *tty; - - tty = info->tty; - if (!tty) - return; - - if (test_and_clear_bit(ESP_EVENT_WRITE_WAKEUP, &info->event)) { - tty_wakeup(tty); - } -} - -/* - * This routine is called from the scheduler tqueue when the interrupt - * routine has signalled that a hangup has occurred. The path of - * hangup processing is: - * - * serial interrupt routine -> (scheduler tqueue) -> - * do_serial_hangup() -> tty->hangup() -> esp_hangup() - * - */ -static void do_serial_hangup(struct work_struct *work) -{ - struct esp_struct *info = - container_of(work, struct esp_struct, tqueue_hangup); - struct tty_struct *tty; - - tty = info->tty; - if (tty) - tty_hangup(tty); -} - /* * --------------------------------------------------------------- * Low level utility subroutines for the serial driver: routines to @@ -2041,7 +1994,6 @@ static void rs_close(struct tty_struct *tty, struct file * filp) tty->driver->flush_buffer(tty); tty_ldisc_flush(tty); tty->closing = 0; - info->event = 0; info->tty = NULL; if (info->blocked_open) { @@ -2109,7 +2061,6 @@ static void esp_hangup(struct tty_struct *tty) rs_flush_buffer(tty); shutdown(info); - info->event = 0; info->count = 0; info->flags &= ~ASYNC_NORMAL_ACTIVE; info->tty = NULL; @@ -2495,8 +2446,6 @@ static int __init espserial_init(void) info->magic = ESP_MAGIC; info->close_delay = 5*HZ/10; info->closing_wait = 30*HZ; - INIT_WORK(&info->tqueue, do_softint); - INIT_WORK(&info->tqueue_hangup, do_serial_hangup); info->config.rx_timeout = rx_timeout; info->config.flow_on = flow_on; info->config.flow_off = flow_off; diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c index 179223a17414..8609b8236c67 100644 --- a/drivers/char/i8k.c +++ b/drivers/char/i8k.c @@ -113,6 +113,33 @@ static int i8k_smm(struct smm_regs *regs) int rc; int eax = regs->eax; +#if defined(CONFIG_X86_64) + asm("pushq %%rax\n\t" + "movl 0(%%rax),%%edx\n\t" + "pushq %%rdx\n\t" + "movl 4(%%rax),%%ebx\n\t" + "movl 8(%%rax),%%ecx\n\t" + "movl 12(%%rax),%%edx\n\t" + "movl 16(%%rax),%%esi\n\t" + "movl 20(%%rax),%%edi\n\t" + "popq %%rax\n\t" + "out %%al,$0xb2\n\t" + "out %%al,$0x84\n\t" + "xchgq %%rax,(%%rsp)\n\t" + "movl %%ebx,4(%%rax)\n\t" + "movl %%ecx,8(%%rax)\n\t" + "movl %%edx,12(%%rax)\n\t" + "movl %%esi,16(%%rax)\n\t" + "movl %%edi,20(%%rax)\n\t" + "popq %%rdx\n\t" + "movl %%edx,0(%%rax)\n\t" + "lahf\n\t" + "shrl $8,%%eax\n\t" + "andl $1,%%eax\n" + :"=a"(rc) + : "a"(regs) + : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); +#else asm("pushl %%eax\n\t" "movl 0(%%eax),%%edx\n\t" "push %%edx\n\t" @@ -137,7 +164,7 @@ static int i8k_smm(struct smm_regs *regs) "andl $1,%%eax\n":"=a"(rc) : "a"(regs) : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); - +#endif if (rc != 0 || (regs->eax & 0xffff) == 0xffff || regs->eax == eax) return -EINVAL; @@ -446,6 +473,13 @@ static struct dmi_system_id __initdata i8k_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "MM061"), }, }, + { + .ident = "Dell Inspiron 3", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MP061"), + }, + }, { } }; diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c index 0f49ccf02a7f..b1d6cad84282 100644 --- a/drivers/char/ip2/ip2main.c +++ b/drivers/char/ip2/ip2main.c @@ -153,9 +153,6 @@ static char *pcVersion = "1.2.14"; static char *pcDriver_name = "ip2"; static char *pcIpl = "ip2ipl"; -/* Serial subtype definitions */ -#define SERIAL_TYPE_NORMAL 1 - // cheezy kludge or genius - you decide? int ip2_loadmain(int *, int *, unsigned char *, int); static unsigned char *Fip_firmware; diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 1f27be1ec3d4..c645455c3fd1 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -627,7 +627,6 @@ static int stli_initopen(struct stlibrd *brdp, struct stliport *portp); static int stli_rawopen(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait); static int stli_rawclose(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait); static int stli_waitcarrier(struct stlibrd *brdp, struct stliport *portp, struct file *filp); -static void stli_dohangup(struct work_struct *); static int stli_setport(struct stliport *portp); static int stli_cmdwait(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback); static void stli_sendcmd(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback); @@ -1824,25 +1823,6 @@ static void stli_start(struct tty_struct *tty) /*****************************************************************************/ /* - * Scheduler called hang up routine. This is called from the scheduler, - * not direct from the driver "poll" routine. We can't call it there - * since the real local hangup code will enable/disable the board and - * other things that we can't do while handling the poll. Much easier - * to deal with it some time later (don't really care when, hangups - * aren't that time critical). - */ - -static void stli_dohangup(struct work_struct *ugly_api) -{ - struct stliport *portp = container_of(ugly_api, struct stliport, tqhangup); - if (portp->tty != NULL) { - tty_hangup(portp->tty); - } -} - -/*****************************************************************************/ - -/* * Hangup this port. This is pretty much like closing the port, only * a little more brutal. No waiting for data to drain. Shutdown the * port and maybe drop signals. This is rather tricky really. We want @@ -2405,7 +2385,7 @@ static int stli_hostcmd(struct stlibrd *brdp, struct stliport *portp) ((portp->sigs & TIOCM_CD) == 0)) { if (portp->flags & ASYNC_CHECK_CD) { if (tty) - schedule_work(&portp->tqhangup); + tty_hangup(tty); } } } @@ -2733,7 +2713,6 @@ static int stli_initports(struct stlibrd *brdp) portp->baud_base = STL_BAUDBASE; portp->close_delay = STL_CLOSEDELAY; portp->closing_wait = 30 * HZ; - INIT_WORK(&portp->tqhangup, stli_dohangup); init_waitqueue_head(&portp->open_wait); init_waitqueue_head(&portp->close_wait); init_waitqueue_head(&portp->raw_wait); diff --git a/drivers/char/mbcs.c b/drivers/char/mbcs.c index 3c5802ae1716..f4716ad7348a 100644 --- a/drivers/char/mbcs.c +++ b/drivers/char/mbcs.c @@ -23,6 +23,7 @@ #include <linux/device.h> #include <linux/mm.h> #include <linux/uio.h> +#include <linux/mutex.h> #include <asm/io.h> #include <asm/uaccess.h> #include <asm/system.h> @@ -281,7 +282,7 @@ static inline int mbcs_algo_start(struct mbcs_soft *soft) void *mmr_base = soft->mmr_base; union cm_control cm_control; - if (down_interruptible(&soft->algolock)) + if (mutex_lock_interruptible(&soft->algolock)) return -ERESTARTSYS; atomic_set(&soft->algo_done, 0); @@ -298,7 +299,7 @@ static inline int mbcs_algo_start(struct mbcs_soft *soft) cm_control.alg_go = 1; MBCS_MMR_SET(mmr_base, MBCS_CM_CONTROL, cm_control.cm_control_reg); - up(&soft->algolock); + mutex_unlock(&soft->algolock); return 0; } @@ -309,7 +310,7 @@ do_mbcs_sram_dmawrite(struct mbcs_soft *soft, uint64_t hostAddr, { int rv = 0; - if (down_interruptible(&soft->dmawritelock)) + if (mutex_lock_interruptible(&soft->dmawritelock)) return -ERESTARTSYS; atomic_set(&soft->dmawrite_done, 0); @@ -335,7 +336,7 @@ do_mbcs_sram_dmawrite(struct mbcs_soft *soft, uint64_t hostAddr, *off += len; dmawrite_exit: - up(&soft->dmawritelock); + mutex_unlock(&soft->dmawritelock); return rv; } @@ -346,7 +347,7 @@ do_mbcs_sram_dmaread(struct mbcs_soft *soft, uint64_t hostAddr, { int rv = 0; - if (down_interruptible(&soft->dmareadlock)) + if (mutex_lock_interruptible(&soft->dmareadlock)) return -ERESTARTSYS; atomic_set(&soft->dmawrite_done, 0); @@ -371,7 +372,7 @@ do_mbcs_sram_dmaread(struct mbcs_soft *soft, uint64_t hostAddr, *off += len; dmaread_exit: - up(&soft->dmareadlock); + mutex_unlock(&soft->dmareadlock); return rv; } @@ -762,9 +763,9 @@ static int mbcs_probe(struct cx_dev *dev, const struct cx_device_id *id) init_waitqueue_head(&soft->dmaread_queue); init_waitqueue_head(&soft->algo_queue); - init_MUTEX(&soft->dmawritelock); - init_MUTEX(&soft->dmareadlock); - init_MUTEX(&soft->algolock); + mutex_init(&soft->dmawritelock); + mutex_init(&soft->dmareadlock); + mutex_init(&soft->algolock); mbcs_getdma_init(&soft->getdma); mbcs_putdma_init(&soft->putdma); diff --git a/drivers/char/mbcs.h b/drivers/char/mbcs.h index c9905a3c3353..ba671589f4cb 100644 --- a/drivers/char/mbcs.h +++ b/drivers/char/mbcs.h @@ -537,9 +537,9 @@ struct mbcs_soft { atomic_t dmawrite_done; atomic_t dmaread_done; atomic_t algo_done; - struct semaphore dmawritelock; - struct semaphore dmareadlock; - struct semaphore algolock; + struct mutex dmawritelock; + struct mutex dmareadlock; + struct mutex algolock; }; static int mbcs_open(struct inode *ip, struct file *fp); diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index 47420787a017..68c2e9234691 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -1,41 +1,24 @@ /* * mxser.c -- MOXA Smartio/Industio family multiport serial driver. * - * Copyright (C) 1999-2001 Moxa Technologies (support@moxa.com.tw). + * Copyright (C) 1999-2006 Moxa Technologies (support@moxa.com). + * Copyright (C) 2006-2008 Jiri Slaby <jirislaby@gmail.com> * - * This code is loosely based on the Linux serial driver, written by - * Linus Torvalds, Theodore T'so and others. + * This code is loosely based on the 1.8 moxa driver which is based on + * Linux serial driver, written by Linus Torvalds, Theodore T'so and + * others. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Original release 10/26/00 - * - * 02/06/01 Support MOXA Industio family boards. - * 02/06/01 Support TIOCGICOUNT. - * 02/06/01 Fix the problem for connecting to serial mouse. - * 02/06/01 Fix the problem for H/W flow control. - * 02/06/01 Fix the compling warning when CONFIG_PCI - * don't be defined. - * * Fed through a cleanup, indent and remove of non 2.6 code by Alan Cox * <alan@redhat.com>. The original 1.8 code is available on www.moxa.com. * - Fixed x86_64 cleanness * - Fixed sleep with spinlock held in mxser_send_break */ - #include <linux/module.h> #include <linux/errno.h> #include <linux/signal.h> @@ -64,33 +47,37 @@ #include "mxser.h" -#define MXSER_VERSION "1.8" +#define MXSER_VERSION "2.0.3" /* 1.11 */ #define MXSERMAJOR 174 #define MXSERCUMAJOR 175 -#define MXSER_EVENT_TXLOW 1 -#define MXSER_EVENT_HANGUP 2 - #define MXSER_BOARDS 4 /* Max. boards */ -#define MXSER_PORTS 32 /* Max. ports */ #define MXSER_PORTS_PER_BOARD 8 /* Max. ports per board */ -#define MXSER_ISR_PASS_LIMIT 256 +#define MXSER_PORTS (MXSER_BOARDS * MXSER_PORTS_PER_BOARD) +#define MXSER_ISR_PASS_LIMIT 100 #define MXSER_ERR_IOADDR -1 #define MXSER_ERR_IRQ -2 #define MXSER_ERR_IRQ_CONFLIT -3 #define MXSER_ERR_VECTOR -4 -#define SERIAL_TYPE_NORMAL 1 -#define SERIAL_TYPE_CALLOUT 2 +/*CheckIsMoxaMust return value*/ +#define MOXA_OTHER_UART 0x00 +#define MOXA_MUST_MU150_HWID 0x01 +#define MOXA_MUST_MU860_HWID 0x02 #define WAKEUP_CHARS 256 #define UART_MCR_AFE 0x20 #define UART_LSR_SPECIAL 0x1E +#define PCI_DEVICE_ID_CB108 0x1080 +#define PCI_DEVICE_ID_CB114 0x1142 +#define PCI_DEVICE_ID_CP114UL 0x1143 +#define PCI_DEVICE_ID_CB134I 0x1341 +#define PCI_DEVICE_ID_CP138U 0x1380 +#define PCI_DEVICE_ID_POS104UL 0x1044 -#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? IRQF_SHARED : IRQF_DISABLED) #define C168_ASIC_ID 1 #define C104_ASIC_ID 2 @@ -99,88 +86,11 @@ #define CI134_ASIC_ID 3 #define CI104J_ASIC_ID 5 -enum { - MXSER_BOARD_C168_ISA = 1, - MXSER_BOARD_C104_ISA, - MXSER_BOARD_CI104J, - MXSER_BOARD_C168_PCI, - MXSER_BOARD_C104_PCI, - MXSER_BOARD_C102_ISA, - MXSER_BOARD_CI132, - MXSER_BOARD_CI134, - MXSER_BOARD_CP132, - MXSER_BOARD_CP114, - MXSER_BOARD_CT114, - MXSER_BOARD_CP102, - MXSER_BOARD_CP104U, - MXSER_BOARD_CP168U, - MXSER_BOARD_CP132U, - MXSER_BOARD_CP134U, - MXSER_BOARD_CP104JU, - MXSER_BOARD_RC7000, - MXSER_BOARD_CP118U, - MXSER_BOARD_CP102UL, - MXSER_BOARD_CP102U, -}; - -static char *mxser_brdname[] = { - "C168 series", - "C104 series", - "CI-104J series", - "C168H/PCI series", - "C104H/PCI series", - "C102 series", - "CI-132 series", - "CI-134 series", - "CP-132 series", - "CP-114 series", - "CT-114 series", - "CP-102 series", - "CP-104U series", - "CP-168U series", - "CP-132U series", - "CP-134U series", - "CP-104JU series", - "Moxa UC7000 Serial", - "CP-118U series", - "CP-102UL series", - "CP-102U series", -}; - -static int mxser_numports[] = { - 8, /* C168-ISA */ - 4, /* C104-ISA */ - 4, /* CI104J */ - 8, /* C168-PCI */ - 4, /* C104-PCI */ - 2, /* C102-ISA */ - 2, /* CI132 */ - 4, /* CI134 */ - 2, /* CP132 */ - 4, /* CP114 */ - 4, /* CT114 */ - 2, /* CP102 */ - 4, /* CP104U */ - 8, /* CP168U */ - 2, /* CP132U */ - 4, /* CP134U */ - 4, /* CP104JU */ - 8, /* RC7000 */ - 8, /* CP118U */ - 2, /* CP102UL */ - 2, /* CP102U */ -}; - -#define UART_TYPE_NUM 2 - -static const unsigned int Gmoxa_uart_id[UART_TYPE_NUM] = { - MOXA_MUST_MU150_HWID, - MOXA_MUST_MU860_HWID -}; +#define MXSER_HIGHBAUD 1 +#define MXSER_HAS2 2 /* This is only for PCI */ -#define UART_INFO_NUM 3 -struct mxpciuart_info { +static const struct { int type; int tx_fifo; int rx_fifo; @@ -189,51 +99,85 @@ struct mxpciuart_info { int rx_trigger; int rx_low_water; long max_baud; -}; - -static const struct mxpciuart_info Gpci_uart_info[UART_INFO_NUM] = { +} Gpci_uart_info[] = { {MOXA_OTHER_UART, 16, 16, 16, 14, 14, 1, 921600L}, {MOXA_MUST_MU150_HWID, 64, 64, 64, 48, 48, 16, 230400L}, {MOXA_MUST_MU860_HWID, 128, 128, 128, 96, 96, 32, 921600L} }; +#define UART_INFO_NUM ARRAY_SIZE(Gpci_uart_info) +struct mxser_cardinfo { + char *name; + unsigned int nports; + unsigned int flags; +}; -#ifdef CONFIG_PCI +static const struct mxser_cardinfo mxser_cards[] = { +/* 0*/ { "C168 series", 8, }, + { "C104 series", 4, }, + { "CI-104J series", 4, }, + { "C168H/PCI series", 8, }, + { "C104H/PCI series", 4, }, +/* 5*/ { "C102 series", 4, MXSER_HAS2 }, /* C102-ISA */ + { "CI-132 series", 4, MXSER_HAS2 }, + { "CI-134 series", 4, }, + { "CP-132 series", 2, }, + { "CP-114 series", 4, }, +/*10*/ { "CT-114 series", 4, }, + { "CP-102 series", 2, MXSER_HIGHBAUD }, + { "CP-104U series", 4, }, + { "CP-168U series", 8, }, + { "CP-132U series", 2, }, +/*15*/ { "CP-134U series", 4, }, + { "CP-104JU series", 4, }, + { "Moxa UC7000 Serial", 8, }, /* RC7000 */ + { "CP-118U series", 8, }, + { "CP-102UL series", 2, }, +/*20*/ { "CP-102U series", 2, }, + { "CP-118EL series", 8, }, + { "CP-168EL series", 8, }, + { "CP-104EL series", 4, }, + { "CB-108 series", 8, }, +/*25*/ { "CB-114 series", 4, }, + { "CB-134I series", 4, }, + { "CP-138U series", 8, }, + { "POS-104UL series", 4, }, + { "CP-114UL series", 4, } +}; +/* driver_data correspond to the lines in the structure above + see also ISA probe function before you change something */ static struct pci_device_id mxser_pcibrds[] = { - {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_C168, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_C168_PCI}, - {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_C104, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_C104_PCI}, - {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP132}, - {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP114, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP114}, - {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CT114, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CT114}, - {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP102}, - {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP104U, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP104U}, - {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP168U, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP168U}, - {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP132U, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP132U}, - {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP134U, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP134U}, - {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP104JU, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP104JU}, - {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_RC7000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_RC7000}, - {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP118U, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP118U}, - {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP102UL, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP102UL}, - {PCI_VENDOR_ID_MOXA, PCI_DEVICE_ID_MOXA_CP102U, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MXSER_BOARD_CP102U}, - {0} + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_C168), .driver_data = 3 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_C104), .driver_data = 4 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132), .driver_data = 8 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP114), .driver_data = 9 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CT114), .driver_data = 10 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102), .driver_data = 11 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104U), .driver_data = 12 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP168U), .driver_data = 13 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132U), .driver_data = 14 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP134U), .driver_data = 15 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104JU),.driver_data = 16 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_RC7000), .driver_data = 17 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118U), .driver_data = 18 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102UL),.driver_data = 19 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102U), .driver_data = 20 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118EL),.driver_data = 21 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP168EL),.driver_data = 22 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104EL),.driver_data = 23 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CB108), .driver_data = 24 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CB114), .driver_data = 25 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CB134I), .driver_data = 26 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP138U), .driver_data = 27 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_POS104UL), .driver_data = 28 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP114UL), .driver_data = 29 }, + { } }; - MODULE_DEVICE_TABLE(pci, mxser_pcibrds); - -#endif - -typedef struct _moxa_pci_info { - unsigned short busNum; - unsigned short devNum; - struct pci_dev *pdev; /* add by Victor Yu. 06-23-2003 */ -} moxa_pci_info; - static int ioaddr[MXSER_BOARDS] = { 0, 0, 0, 0 }; static int ttymajor = MXSERMAJOR; -static int calloutmajor = MXSERCUMAJOR; -static int verbose = 0; /* Variables for insmod */ @@ -241,8 +185,6 @@ MODULE_AUTHOR("Casper Yang"); MODULE_DESCRIPTION("MOXA Smartio/Industio Family Multiport Board Device Driver"); module_param_array(ioaddr, int, NULL, 0); module_param(ttymajor, int, 0); -module_param(calloutmajor, int, 0); -module_param(verbose, bool, 0); MODULE_LICENSE("GPL"); struct mxser_log { @@ -277,67 +219,69 @@ struct mxser_mon_ext { int iftype[32]; }; -struct mxser_hwconf { - int board_type; - int ports; - int irq; - int vector; - int vector_mask; - int uart_type; - int ioaddr[MXSER_PORTS_PER_BOARD]; - int baud_base[MXSER_PORTS_PER_BOARD]; - moxa_pci_info pciInfo; - int IsMoxaMustChipFlag; /* add by Victor Yu. 08-30-2002 */ - int MaxCanSetBaudRate[MXSER_PORTS_PER_BOARD]; /* add by Victor Yu. 09-04-2002 */ - int opmode_ioaddr[MXSER_PORTS_PER_BOARD]; /* add by Victor Yu. 01-05-2004 */ -}; +struct mxser_board; + +struct mxser_port { + struct mxser_board *board; + struct tty_struct *tty; + + unsigned long ioaddr; + unsigned long opmode_ioaddr; + int max_baud; -struct mxser_struct { - int port; - int base; /* port base address */ - int irq; /* port using irq no. */ - int vector; /* port irq vector */ - int vectormask; /* port vector mask */ int rx_high_water; int rx_trigger; /* Rx fifo trigger level */ int rx_low_water; int baud_base; /* max. speed */ - int flags; /* defined in tty.h */ int type; /* UART type */ - struct tty_struct *tty; - int read_status_mask; - int ignore_status_mask; - int xmit_fifo_size; - int custom_divisor; + int flags; /* defined in tty.h */ + int x_char; /* xon/xoff character */ - int close_delay; - unsigned short closing_wait; int IER; /* Interrupt Enable Register */ int MCR; /* Modem control register */ + + unsigned char stop_rx; + unsigned char ldisc_stop_rx; + + int custom_divisor; + int close_delay; + unsigned short closing_wait; + unsigned char err_shadow; unsigned long event; + int count; /* # of fd on device */ int blocked_open; /* # of blocked opens */ + struct async_icount icount; /* kernel counters for 4 input interrupts */ + int timeout; + + int read_status_mask; + int ignore_status_mask; + int xmit_fifo_size; unsigned char *xmit_buf; int xmit_head; int xmit_tail; int xmit_cnt; - struct work_struct tqueue; + struct ktermios normal_termios; - struct ktermios callout_termios; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; - wait_queue_head_t delta_msr_wait; - struct async_icount icount; /* kernel counters for the 4 input interrupts */ - int timeout; - int IsMoxaMustChipFlag; /* add by Victor Yu. 08-30-2002 */ - int MaxCanSetBaudRate; /* add by Victor Yu. 09-04-2002 */ - int opmode_ioaddr; /* add by Victor Yu. 01-05-2004 */ - unsigned char stop_rx; - unsigned char ldisc_stop_rx; - long realbaud; + struct mxser_mon mon_data; - unsigned char err_shadow; + spinlock_t slock; + wait_queue_head_t open_wait; + wait_queue_head_t delta_msr_wait; +}; + +struct mxser_board { + unsigned int idx; + int irq; + const struct mxser_cardinfo *info; + unsigned long vector; + unsigned long vector_mask; + + int chip_flag; + int uart_type; + + struct mxser_port ports[MXSER_PORTS_PER_BOARD]; }; struct mxser_mstatus { @@ -355,73 +299,16 @@ static int mxserBoardCAP[MXSER_BOARDS] = { /* 0x180, 0x280, 0x200, 0x320 */ }; +static struct mxser_board mxser_boards[MXSER_BOARDS]; static struct tty_driver *mxvar_sdriver; -static struct mxser_struct mxvar_table[MXSER_PORTS]; -static struct tty_struct *mxvar_tty[MXSER_PORTS + 1]; -static struct ktermios *mxvar_termios[MXSER_PORTS + 1]; -static struct ktermios *mxvar_termios_locked[MXSER_PORTS + 1]; static struct mxser_log mxvar_log; static int mxvar_diagflag; static unsigned char mxser_msr[MXSER_PORTS + 1]; static struct mxser_mon_ext mon_data_ext; static int mxser_set_baud_method[MXSER_PORTS + 1]; -static spinlock_t gm_lock; - -/* - * This is used to figure out the divisor speeds and the timeouts - */ - -static struct mxser_hwconf mxsercfg[MXSER_BOARDS]; - -/* - * static functions: - */ - -static void mxser_getcfg(int board, struct mxser_hwconf *hwconf); -static int mxser_init(void); - -/* static void mxser_poll(unsigned long); */ -static int mxser_get_ISA_conf(int, struct mxser_hwconf *); -static void mxser_do_softint(struct work_struct *); -static int mxser_open(struct tty_struct *, struct file *); -static void mxser_close(struct tty_struct *, struct file *); -static int mxser_write(struct tty_struct *, const unsigned char *, int); -static int mxser_write_room(struct tty_struct *); -static void mxser_flush_buffer(struct tty_struct *); -static int mxser_chars_in_buffer(struct tty_struct *); -static void mxser_flush_chars(struct tty_struct *); -static void mxser_put_char(struct tty_struct *, unsigned char); -static int mxser_ioctl(struct tty_struct *, struct file *, uint, ulong); -static int mxser_ioctl_special(unsigned int, void __user *); -static void mxser_throttle(struct tty_struct *); -static void mxser_unthrottle(struct tty_struct *); -static void mxser_set_termios(struct tty_struct *, struct ktermios *); -static void mxser_stop(struct tty_struct *); -static void mxser_start(struct tty_struct *); -static void mxser_hangup(struct tty_struct *); -static void mxser_rs_break(struct tty_struct *, int); -static irqreturn_t mxser_interrupt(int, void *); -static void mxser_receive_chars(struct mxser_struct *, int *); -static void mxser_transmit_chars(struct mxser_struct *); -static void mxser_check_modem_status(struct mxser_struct *, int); -static int mxser_block_til_ready(struct tty_struct *, struct file *, struct mxser_struct *); -static int mxser_startup(struct mxser_struct *); -static void mxser_shutdown(struct mxser_struct *); -static int mxser_change_speed(struct mxser_struct *, struct ktermios *old_termios); -static int mxser_get_serial_info(struct mxser_struct *, struct serial_struct __user *); -static int mxser_set_serial_info(struct mxser_struct *, struct serial_struct __user *); -static int mxser_get_lsr_info(struct mxser_struct *, unsigned int __user *); -static void mxser_send_break(struct mxser_struct *, int); -static int mxser_tiocmget(struct tty_struct *, struct file *); -static int mxser_tiocmset(struct tty_struct *, struct file *, unsigned int, unsigned int); -static int mxser_set_baud(struct mxser_struct *info, long newspd); -static void mxser_wait_until_sent(struct tty_struct *tty, int timeout); - -static void mxser_startrx(struct tty_struct *tty); -static void mxser_stoprx(struct tty_struct *tty); #ifdef CONFIG_PCI -static int CheckIsMoxaMust(int io) +static int __devinit CheckIsMoxaMust(unsigned long io) { u8 oldmcr, hwid; int i; @@ -437,90 +324,15 @@ static int CheckIsMoxaMust(int io) } GET_MOXA_MUST_HARDWARE_ID(io, &hwid); - for (i = 0; i < UART_TYPE_NUM; i++) { - if (hwid == Gmoxa_uart_id[i]) + for (i = 1; i < UART_INFO_NUM; i++) { /* 0 = OTHER_UART */ + if (hwid == Gpci_uart_info[i].type) return (int)hwid; } return MOXA_OTHER_UART; } #endif -/* above is modified by Victor Yu. 08-15-2002 */ - -static const struct tty_operations mxser_ops = { - .open = mxser_open, - .close = mxser_close, - .write = mxser_write, - .put_char = mxser_put_char, - .flush_chars = mxser_flush_chars, - .write_room = mxser_write_room, - .chars_in_buffer = mxser_chars_in_buffer, - .flush_buffer = mxser_flush_buffer, - .ioctl = mxser_ioctl, - .throttle = mxser_throttle, - .unthrottle = mxser_unthrottle, - .set_termios = mxser_set_termios, - .stop = mxser_stop, - .start = mxser_start, - .hangup = mxser_hangup, - .break_ctl = mxser_rs_break, - .wait_until_sent = mxser_wait_until_sent, - .tiocmget = mxser_tiocmget, - .tiocmset = mxser_tiocmset, -}; - -/* - * The MOXA Smartio/Industio serial driver boot-time initialization code! - */ - -static int __init mxser_module_init(void) -{ - int ret; - - if (verbose) - printk(KERN_DEBUG "Loading module mxser ...\n"); - ret = mxser_init(); - if (verbose) - printk(KERN_DEBUG "Done.\n"); - return ret; -} - -static void __exit mxser_module_exit(void) -{ - int i, err; - - if (verbose) - printk(KERN_DEBUG "Unloading module mxser ...\n"); - - err = tty_unregister_driver(mxvar_sdriver); - if (!err) - put_tty_driver(mxvar_sdriver); - else - printk(KERN_ERR "Couldn't unregister MOXA Smartio/Industio family serial driver\n"); - - for (i = 0; i < MXSER_BOARDS; i++) { - struct pci_dev *pdev; - - if (mxsercfg[i].board_type == -1) - continue; - else { - pdev = mxsercfg[i].pciInfo.pdev; - free_irq(mxsercfg[i].irq, &mxvar_table[i * MXSER_PORTS_PER_BOARD]); - if (pdev != NULL) { /* PCI */ - release_region(pci_resource_start(pdev, 2), pci_resource_len(pdev, 2)); - release_region(pci_resource_start(pdev, 3), pci_resource_len(pdev, 3)); - pci_dev_put(pdev); - } else { - release_region(mxsercfg[i].ioaddr[0], 8 * mxsercfg[i].ports); - release_region(mxsercfg[i].vector, 1); - } - } - } - if (verbose) - printk(KERN_DEBUG "Done.\n"); -} - -static void process_txrx_fifo(struct mxser_struct *info) +static void process_txrx_fifo(struct mxser_port *info) { int i; @@ -529,424 +341,548 @@ static void process_txrx_fifo(struct mxser_struct *info) info->rx_high_water = 1; info->rx_low_water = 1; info->xmit_fifo_size = 1; - } else { - for (i = 0; i < UART_INFO_NUM; i++) { - if (info->IsMoxaMustChipFlag == Gpci_uart_info[i].type) { + } else + for (i = 0; i < UART_INFO_NUM; i++) + if (info->board->chip_flag == Gpci_uart_info[i].type) { info->rx_trigger = Gpci_uart_info[i].rx_trigger; info->rx_low_water = Gpci_uart_info[i].rx_low_water; info->rx_high_water = Gpci_uart_info[i].rx_high_water; info->xmit_fifo_size = Gpci_uart_info[i].xmit_fifo_size; break; } - } - } } -static int mxser_initbrd(int board, struct mxser_hwconf *hwconf) +static unsigned char mxser_get_msr(int baseaddr, int mode, int port) { - struct mxser_struct *info; - int retval; - int i, n; + unsigned char status = 0; - n = board * MXSER_PORTS_PER_BOARD; - info = &mxvar_table[n]; - /*if (verbose) */ { - printk(KERN_DEBUG " ttyMI%d - ttyMI%d ", - n, n + hwconf->ports - 1); - printk(" max. baud rate = %d bps.\n", - hwconf->MaxCanSetBaudRate[0]); - } - - for (i = 0; i < hwconf->ports; i++, n++, info++) { - info->port = n; - info->base = hwconf->ioaddr[i]; - info->irq = hwconf->irq; - info->vector = hwconf->vector; - info->vectormask = hwconf->vector_mask; - info->opmode_ioaddr = hwconf->opmode_ioaddr[i]; /* add by Victor Yu. 01-05-2004 */ - info->stop_rx = 0; - info->ldisc_stop_rx = 0; + status = inb(baseaddr + UART_MSR); - info->IsMoxaMustChipFlag = hwconf->IsMoxaMustChipFlag; - /* Enhance mode enabled here */ - if (info->IsMoxaMustChipFlag != MOXA_OTHER_UART) { - ENABLE_MOXA_MUST_ENCHANCE_MODE(info->base); - } + mxser_msr[port] &= 0x0F; + mxser_msr[port] |= status; + status = mxser_msr[port]; + if (mode) + mxser_msr[port] = 0; - info->flags = ASYNC_SHARE_IRQ; - info->type = hwconf->uart_type; - info->baud_base = hwconf->baud_base[i]; + return status; +} - info->MaxCanSetBaudRate = hwconf->MaxCanSetBaudRate[i]; +static int mxser_block_til_ready(struct tty_struct *tty, struct file *filp, + struct mxser_port *port) +{ + DECLARE_WAITQUEUE(wait, current); + int retval; + int do_clocal = 0; + unsigned long flags; - process_txrx_fifo(info); + /* + * If non-blocking mode is set, or the port is not enabled, + * then make the check up front and then exit. + */ + if ((filp->f_flags & O_NONBLOCK) || + test_bit(TTY_IO_ERROR, &tty->flags)) { + port->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + if (tty->termios->c_cflag & CLOCAL) + do_clocal = 1; - info->custom_divisor = hwconf->baud_base[i] * 16; - info->close_delay = 5 * HZ / 10; - info->closing_wait = 30 * HZ; - INIT_WORK(&info->tqueue, mxser_do_softint); - info->normal_termios = mxvar_sdriver->init_termios; - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); - init_waitqueue_head(&info->delta_msr_wait); - memset(&info->mon_data, 0, sizeof(struct mxser_mon)); - info->err_shadow = 0; - spin_lock_init(&info->slock); - } /* - * Allocate the IRQ if necessary + * Block waiting for the carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, port->count is dropped by one, so that + * mxser_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. */ + retval = 0; + add_wait_queue(&port->open_wait, &wait); - - /* before set INT ISR, disable all int */ - for (i = 0; i < hwconf->ports; i++) { - outb(inb(hwconf->ioaddr[i] + UART_IER) & 0xf0, - hwconf->ioaddr[i] + UART_IER); + spin_lock_irqsave(&port->slock, flags); + if (!tty_hung_up_p(filp)) + port->count--; + spin_unlock_irqrestore(&port->slock, flags); + port->blocked_open++; + while (1) { + spin_lock_irqsave(&port->slock, flags); + outb(inb(port->ioaddr + UART_MCR) | + UART_MCR_DTR | UART_MCR_RTS, port->ioaddr + UART_MCR); + spin_unlock_irqrestore(&port->slock, flags); + set_current_state(TASK_INTERRUPTIBLE); + if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) { + if (port->flags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; + break; + } + if (!(port->flags & ASYNC_CLOSING) && + (do_clocal || + (inb(port->ioaddr + UART_MSR) & UART_MSR_DCD))) + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); } - - n = board * MXSER_PORTS_PER_BOARD; - info = &mxvar_table[n]; - - retval = request_irq(hwconf->irq, mxser_interrupt, IRQ_T(info), - "mxser", info); - if (retval) { - printk(KERN_ERR "Board %d: %s", - board, mxser_brdname[hwconf->board_type - 1]); - printk(" Request irq failed, IRQ (%d) may conflict with" - " another device.\n", info->irq); + set_current_state(TASK_RUNNING); + remove_wait_queue(&port->open_wait, &wait); + if (!tty_hung_up_p(filp)) + port->count++; + port->blocked_open--; + if (retval) return retval; - } + port->flags |= ASYNC_NORMAL_ACTIVE; return 0; } -static void mxser_getcfg(int board, struct mxser_hwconf *hwconf) +static int mxser_set_baud(struct mxser_port *info, long newspd) { - mxsercfg[board] = *hwconf; -} + int quot = 0, baud; + unsigned char cval; -#ifdef CONFIG_PCI -static int mxser_get_PCI_conf(int busnum, int devnum, int board_type, struct mxser_hwconf *hwconf) -{ - int i, j; - /* unsigned int val; */ - unsigned int ioaddress; - struct pci_dev *pdev = hwconf->pciInfo.pdev; + if (!info->tty || !info->tty->termios) + return -1; - /* io address */ - hwconf->board_type = board_type; - hwconf->ports = mxser_numports[board_type - 1]; - ioaddress = pci_resource_start(pdev, 2); - request_region(pci_resource_start(pdev, 2), pci_resource_len(pdev, 2), - "mxser(IO)"); + if (!(info->ioaddr)) + return -1; - for (i = 0; i < hwconf->ports; i++) - hwconf->ioaddr[i] = ioaddress + 8 * i; + if (newspd > info->max_baud) + return -1; - /* vector */ - ioaddress = pci_resource_start(pdev, 3); - request_region(pci_resource_start(pdev, 3), pci_resource_len(pdev, 3), - "mxser(vector)"); - hwconf->vector = ioaddress; + if (newspd == 134) { + quot = 2 * info->baud_base / 269; + tty_encode_baud_rate(info->tty, 134, 134); + } else if (newspd) { + quot = info->baud_base / newspd; + if (quot == 0) + quot = 1; + baud = info->baud_base/quot; + tty_encode_baud_rate(info->tty, baud, baud); + } else { + quot = 0; + } - /* irq */ - hwconf->irq = hwconf->pciInfo.pdev->irq; + info->timeout = ((info->xmit_fifo_size * HZ * 10 * quot) / info->baud_base); + info->timeout += HZ / 50; /* Add .02 seconds of slop */ - hwconf->IsMoxaMustChipFlag = CheckIsMoxaMust(hwconf->ioaddr[0]); - hwconf->uart_type = PORT_16550A; - hwconf->vector_mask = 0; + if (quot) { + info->MCR |= UART_MCR_DTR; + outb(info->MCR, info->ioaddr + UART_MCR); + } else { + info->MCR &= ~UART_MCR_DTR; + outb(info->MCR, info->ioaddr + UART_MCR); + return 0; + } + cval = inb(info->ioaddr + UART_LCR); - for (i = 0; i < hwconf->ports; i++) { - for (j = 0; j < UART_INFO_NUM; j++) { - if (Gpci_uart_info[j].type == hwconf->IsMoxaMustChipFlag) { - hwconf->MaxCanSetBaudRate[i] = Gpci_uart_info[j].max_baud; + outb(cval | UART_LCR_DLAB, info->ioaddr + UART_LCR); /* set DLAB */ - /* exception....CP-102 */ - if (board_type == MXSER_BOARD_CP102) - hwconf->MaxCanSetBaudRate[i] = 921600; - break; - } - } - } + outb(quot & 0xff, info->ioaddr + UART_DLL); /* LS of divisor */ + outb(quot >> 8, info->ioaddr + UART_DLM); /* MS of divisor */ + outb(cval, info->ioaddr + UART_LCR); /* reset DLAB */ - if (hwconf->IsMoxaMustChipFlag == MOXA_MUST_MU860_HWID) { - for (i = 0; i < hwconf->ports; i++) { - if (i < 4) - hwconf->opmode_ioaddr[i] = ioaddress + 4; - else - hwconf->opmode_ioaddr[i] = ioaddress + 0x0c; - } - outb(0, ioaddress + 4); /* default set to RS232 mode */ - outb(0, ioaddress + 0x0c); /* default set to RS232 mode */ - } +#ifdef BOTHER + if (C_BAUD(info->tty) == BOTHER) { + quot = info->baud_base % newspd; + quot *= 8; + if (quot % newspd > newspd / 2) { + quot /= newspd; + quot++; + } else + quot /= newspd; + + SET_MOXA_MUST_ENUM_VALUE(info->ioaddr, quot); + } else +#endif + SET_MOXA_MUST_ENUM_VALUE(info->ioaddr, 0); - for (i = 0; i < hwconf->ports; i++) { - hwconf->vector_mask |= (1 << i); - hwconf->baud_base[i] = 921600; - } return 0; } -#endif -static int mxser_init(void) +/* + * This routine is called to set the UART divisor registers to match + * the specified baud rate for a serial port. + */ +static int mxser_change_speed(struct mxser_port *info, + struct ktermios *old_termios) { - int i, m, retval, b, n; - struct pci_dev *pdev = NULL; - int index; - unsigned char busnum, devnum; - struct mxser_hwconf hwconf; - - mxvar_sdriver = alloc_tty_driver(MXSER_PORTS + 1); - if (!mxvar_sdriver) - return -ENOMEM; - spin_lock_init(&gm_lock); - - for (i = 0; i < MXSER_BOARDS; i++) { - mxsercfg[i].board_type = -1; - } + unsigned cflag, cval, fcr; + int ret = 0; + unsigned char status; - printk(KERN_INFO "MOXA Smartio/Industio family driver version %s\n", - MXSER_VERSION); + if (!info->tty || !info->tty->termios) + return ret; + cflag = info->tty->termios->c_cflag; + if (!(info->ioaddr)) + return ret; - /* Initialize the tty_driver structure */ - memset(mxvar_sdriver, 0, sizeof(struct tty_driver)); - mxvar_sdriver->owner = THIS_MODULE; - mxvar_sdriver->magic = TTY_DRIVER_MAGIC; - mxvar_sdriver->name = "ttyMI"; - mxvar_sdriver->major = ttymajor; - mxvar_sdriver->minor_start = 0; - mxvar_sdriver->num = MXSER_PORTS + 1; - mxvar_sdriver->type = TTY_DRIVER_TYPE_SERIAL; - mxvar_sdriver->subtype = SERIAL_TYPE_NORMAL; - mxvar_sdriver->init_termios = tty_std_termios; - mxvar_sdriver->init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL; - mxvar_sdriver->init_termios.c_ispeed = 9600; - mxvar_sdriver->init_termios.c_ospeed = 9600; - mxvar_sdriver->flags = TTY_DRIVER_REAL_RAW; - tty_set_operations(mxvar_sdriver, &mxser_ops); - mxvar_sdriver->ttys = mxvar_tty; - mxvar_sdriver->termios = mxvar_termios; - mxvar_sdriver->termios_locked = mxvar_termios_locked; + if (mxser_set_baud_method[info->tty->index] == 0) + mxser_set_baud(info, tty_get_baud_rate(info->tty)); - mxvar_diagflag = 0; - memset(mxvar_table, 0, MXSER_PORTS * sizeof(struct mxser_struct)); - memset(&mxvar_log, 0, sizeof(struct mxser_log)); + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS5: + cval = 0x00; + break; + case CS6: + cval = 0x01; + break; + case CS7: + cval = 0x02; + break; + case CS8: + cval = 0x03; + break; + default: + cval = 0x00; + break; /* too keep GCC shut... */ + } + if (cflag & CSTOPB) + cval |= 0x04; + if (cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(cflag & PARODD)) + cval |= UART_LCR_EPAR; + if (cflag & CMSPAR) + cval |= UART_LCR_SPAR; - memset(&mxser_msr, 0, sizeof(unsigned char) * (MXSER_PORTS + 1)); - memset(&mon_data_ext, 0, sizeof(struct mxser_mon_ext)); - memset(&mxser_set_baud_method, 0, sizeof(int) * (MXSER_PORTS + 1)); - memset(&hwconf, 0, sizeof(struct mxser_hwconf)); + if ((info->type == PORT_8250) || (info->type == PORT_16450)) { + if (info->board->chip_flag) { + fcr = UART_FCR_ENABLE_FIFO; + fcr |= MOXA_MUST_FCR_GDA_MODE_ENABLE; + SET_MOXA_MUST_FIFO_VALUE(info); + } else + fcr = 0; + } else { + fcr = UART_FCR_ENABLE_FIFO; + if (info->board->chip_flag) { + fcr |= MOXA_MUST_FCR_GDA_MODE_ENABLE; + SET_MOXA_MUST_FIFO_VALUE(info); + } else { + switch (info->rx_trigger) { + case 1: + fcr |= UART_FCR_TRIGGER_1; + break; + case 4: + fcr |= UART_FCR_TRIGGER_4; + break; + case 8: + fcr |= UART_FCR_TRIGGER_8; + break; + default: + fcr |= UART_FCR_TRIGGER_14; + break; + } + } + } - m = 0; - /* Start finding ISA boards here */ - for (b = 0; b < MXSER_BOARDS && m < MXSER_BOARDS; b++) { - int cap; - - if (!(cap = mxserBoardCAP[b])) - continue; - - retval = mxser_get_ISA_conf(cap, &hwconf); - - if (retval != 0) - printk(KERN_INFO "Found MOXA %s board (CAP=0x%x)\n", - mxser_brdname[hwconf.board_type - 1], ioaddr[b]); - - if (retval <= 0) { - if (retval == MXSER_ERR_IRQ) - printk(KERN_ERR "Invalid interrupt number, " - "board not configured\n"); - else if (retval == MXSER_ERR_IRQ_CONFLIT) - printk(KERN_ERR "Invalid interrupt number, " - "board not configured\n"); - else if (retval == MXSER_ERR_VECTOR) - printk(KERN_ERR "Invalid interrupt vector, " - "board not configured\n"); - else if (retval == MXSER_ERR_IOADDR) - printk(KERN_ERR "Invalid I/O address, " - "board not configured\n"); - - continue; + /* CTS flow control flag and modem status interrupts */ + info->IER &= ~UART_IER_MSI; + info->MCR &= ~UART_MCR_AFE; + if (cflag & CRTSCTS) { + info->flags |= ASYNC_CTS_FLOW; + info->IER |= UART_IER_MSI; + if ((info->type == PORT_16550A) || (info->board->chip_flag)) { + info->MCR |= UART_MCR_AFE; + } else { + status = inb(info->ioaddr + UART_MSR); + if (info->tty->hw_stopped) { + if (status & UART_MSR_CTS) { + info->tty->hw_stopped = 0; + if (info->type != PORT_16550A && + !info->board->chip_flag) { + outb(info->IER & ~UART_IER_THRI, + info->ioaddr + + UART_IER); + info->IER |= UART_IER_THRI; + outb(info->IER, info->ioaddr + + UART_IER); + } + tty_wakeup(info->tty); + } + } else { + if (!(status & UART_MSR_CTS)) { + info->tty->hw_stopped = 1; + if ((info->type != PORT_16550A) && + (!info->board->chip_flag)) { + info->IER &= ~UART_IER_THRI; + outb(info->IER, info->ioaddr + + UART_IER); + } + } + } } + } else { + info->flags &= ~ASYNC_CTS_FLOW; + } + outb(info->MCR, info->ioaddr + UART_MCR); + if (cflag & CLOCAL) { + info->flags &= ~ASYNC_CHECK_CD; + } else { + info->flags |= ASYNC_CHECK_CD; + info->IER |= UART_IER_MSI; + } + outb(info->IER, info->ioaddr + UART_IER); - hwconf.pciInfo.busNum = 0; - hwconf.pciInfo.devNum = 0; - hwconf.pciInfo.pdev = NULL; + /* + * Set up parity check flag + */ + info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (I_INPCK(info->tty)) + info->read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) + info->read_status_mask |= UART_LSR_BI; + + info->ignore_status_mask = 0; - mxser_getcfg(m, &hwconf); + if (I_IGNBRK(info->tty)) { + info->ignore_status_mask |= UART_LSR_BI; + info->read_status_mask |= UART_LSR_BI; /* - * init mxsercfg first, - * or mxsercfg data is not correct on ISR. + * If we're ignore parity and break indicators, ignore + * overruns too. (For real raw support). */ - /* mxser_initbrd will hook ISR. */ - if (mxser_initbrd(m, &hwconf) < 0) - continue; - - m++; + if (I_IGNPAR(info->tty)) { + info->ignore_status_mask |= + UART_LSR_OE | + UART_LSR_PE | + UART_LSR_FE; + info->read_status_mask |= + UART_LSR_OE | + UART_LSR_PE | + UART_LSR_FE; + } + } + if (info->board->chip_flag) { + SET_MOXA_MUST_XON1_VALUE(info->ioaddr, START_CHAR(info->tty)); + SET_MOXA_MUST_XOFF1_VALUE(info->ioaddr, STOP_CHAR(info->tty)); + if (I_IXON(info->tty)) { + ENABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL(info->ioaddr); + } else { + DISABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL(info->ioaddr); + } + if (I_IXOFF(info->tty)) { + ENABLE_MOXA_MUST_TX_SOFTWARE_FLOW_CONTROL(info->ioaddr); + } else { + DISABLE_MOXA_MUST_TX_SOFTWARE_FLOW_CONTROL(info->ioaddr); + } } - /* Start finding ISA boards from module arg */ - for (b = 0; b < MXSER_BOARDS && m < MXSER_BOARDS; b++) { - int cap; - if (!(cap = ioaddr[b])) - continue; + outb(fcr, info->ioaddr + UART_FCR); /* set fcr */ + outb(cval, info->ioaddr + UART_LCR); - retval = mxser_get_ISA_conf(cap, &hwconf); + return ret; +} - if (retval != 0) - printk(KERN_INFO "Found MOXA %s board (CAP=0x%x)\n", - mxser_brdname[hwconf.board_type - 1], ioaddr[b]); +static void mxser_check_modem_status(struct mxser_port *port, int status) +{ + /* update input line counters */ + if (status & UART_MSR_TERI) + port->icount.rng++; + if (status & UART_MSR_DDSR) + port->icount.dsr++; + if (status & UART_MSR_DDCD) + port->icount.dcd++; + if (status & UART_MSR_DCTS) + port->icount.cts++; + port->mon_data.modem_status = status; + wake_up_interruptible(&port->delta_msr_wait); - if (retval <= 0) { - if (retval == MXSER_ERR_IRQ) - printk(KERN_ERR "Invalid interrupt number, " - "board not configured\n"); - else if (retval == MXSER_ERR_IRQ_CONFLIT) - printk(KERN_ERR "Invalid interrupt number, " - "board not configured\n"); - else if (retval == MXSER_ERR_VECTOR) - printk(KERN_ERR "Invalid interrupt vector, " - "board not configured\n"); - else if (retval == MXSER_ERR_IOADDR) - printk(KERN_ERR "Invalid I/O address, " - "board not configured\n"); + if ((port->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { + if (status & UART_MSR_DCD) + wake_up_interruptible(&port->open_wait); + } - continue; + if (port->flags & ASYNC_CTS_FLOW) { + if (port->tty->hw_stopped) { + if (status & UART_MSR_CTS) { + port->tty->hw_stopped = 0; + + if ((port->type != PORT_16550A) && + (!port->board->chip_flag)) { + outb(port->IER & ~UART_IER_THRI, + port->ioaddr + UART_IER); + port->IER |= UART_IER_THRI; + outb(port->IER, port->ioaddr + + UART_IER); + } + tty_wakeup(port->tty); + } + } else { + if (!(status & UART_MSR_CTS)) { + port->tty->hw_stopped = 1; + if (port->type != PORT_16550A && + !port->board->chip_flag) { + port->IER &= ~UART_IER_THRI; + outb(port->IER, port->ioaddr + + UART_IER); + } + } } + } +} - hwconf.pciInfo.busNum = 0; - hwconf.pciInfo.devNum = 0; - hwconf.pciInfo.pdev = NULL; +static int mxser_startup(struct mxser_port *info) +{ + unsigned long page; + unsigned long flags; - mxser_getcfg(m, &hwconf); - /* - * init mxsercfg first, - * or mxsercfg data is not correct on ISR. - */ - /* mxser_initbrd will hook ISR. */ - if (mxser_initbrd(m, &hwconf) < 0) - continue; + page = __get_free_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + spin_lock_irqsave(&info->slock, flags); - m++; + if (info->flags & ASYNC_INITIALIZED) { + free_page(page); + spin_unlock_irqrestore(&info->slock, flags); + return 0; } - /* start finding PCI board here */ -#ifdef CONFIG_PCI - n = ARRAY_SIZE(mxser_pcibrds) - 1; - index = 0; - b = 0; - while (b < n) { - pdev = pci_get_device(mxser_pcibrds[b].vendor, - mxser_pcibrds[b].device, pdev); - if (pdev == NULL) { - b++; - continue; - } - hwconf.pciInfo.busNum = busnum = pdev->bus->number; - hwconf.pciInfo.devNum = devnum = PCI_SLOT(pdev->devfn) << 3; - hwconf.pciInfo.pdev = pdev; - printk(KERN_INFO "Found MOXA %s board(BusNo=%d,DevNo=%d)\n", - mxser_brdname[(int) (mxser_pcibrds[b].driver_data) - 1], - busnum, devnum >> 3); - index++; - if (m >= MXSER_BOARDS) - printk(KERN_ERR - "Too many Smartio/Industio family boards find " - "(maximum %d), board not configured\n", - MXSER_BOARDS); - else { - if (pci_enable_device(pdev)) { - printk(KERN_ERR "Moxa SmartI/O PCI enable " - "fail !\n"); - continue; - } - retval = mxser_get_PCI_conf(busnum, devnum, - (int)mxser_pcibrds[b].driver_data, - &hwconf); - if (retval < 0) { - if (retval == MXSER_ERR_IRQ) - printk(KERN_ERR - "Invalid interrupt number, " - "board not configured\n"); - else if (retval == MXSER_ERR_IRQ_CONFLIT) - printk(KERN_ERR - "Invalid interrupt number, " - "board not configured\n"); - else if (retval == MXSER_ERR_VECTOR) - printk(KERN_ERR - "Invalid interrupt vector, " - "board not configured\n"); - else if (retval == MXSER_ERR_IOADDR) - printk(KERN_ERR - "Invalid I/O address, " - "board not configured\n"); - continue; - } - mxser_getcfg(m, &hwconf); - /* init mxsercfg first, - * or mxsercfg data is not correct on ISR. - */ - /* mxser_initbrd will hook ISR. */ - if (mxser_initbrd(m, &hwconf) < 0) - continue; - m++; - /* Keep an extra reference if we succeeded. It will - be returned at unload time */ - pci_dev_get(pdev); - } + if (!info->ioaddr || !info->type) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + free_page(page); + spin_unlock_irqrestore(&info->slock, flags); + return 0; } -#endif + if (info->xmit_buf) + free_page(page); + else + info->xmit_buf = (unsigned char *) page; - retval = tty_register_driver(mxvar_sdriver); - if (retval) { - printk(KERN_ERR "Couldn't install MOXA Smartio/Industio family" - " driver !\n"); - put_tty_driver(mxvar_sdriver); + /* + * Clear the FIFO buffers and disable them + * (they will be reenabled in mxser_change_speed()) + */ + if (info->board->chip_flag) + outb((UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT | + MOXA_MUST_FCR_GDA_MODE_ENABLE), info->ioaddr + UART_FCR); + else + outb((UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT), + info->ioaddr + UART_FCR); - for (i = 0; i < MXSER_BOARDS; i++) { - if (mxsercfg[i].board_type == -1) - continue; - else { - free_irq(mxsercfg[i].irq, &mxvar_table[i * MXSER_PORTS_PER_BOARD]); - /* todo: release io, vector */ - } - } - return retval; + /* + * At this point there's no way the LSR could still be 0xFF; + * if it is, then bail out, because there's likely no UART + * here. + */ + if (inb(info->ioaddr + UART_LSR) == 0xff) { + spin_unlock_irqrestore(&info->slock, flags); + if (capable(CAP_SYS_ADMIN)) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + return 0; + } else + return -ENODEV; } + /* + * Clear the interrupt registers. + */ + (void) inb(info->ioaddr + UART_LSR); + (void) inb(info->ioaddr + UART_RX); + (void) inb(info->ioaddr + UART_IIR); + (void) inb(info->ioaddr + UART_MSR); + + /* + * Now, initialize the UART + */ + outb(UART_LCR_WLEN8, info->ioaddr + UART_LCR); /* reset DLAB */ + info->MCR = UART_MCR_DTR | UART_MCR_RTS; + outb(info->MCR, info->ioaddr + UART_MCR); + + /* + * Finally, enable interrupts + */ + info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; + + if (info->board->chip_flag) + info->IER |= MOXA_MUST_IER_EGDAI; + outb(info->IER, info->ioaddr + UART_IER); /* enable interrupts */ + + /* + * And clear the interrupt registers again for luck. + */ + (void) inb(info->ioaddr + UART_LSR); + (void) inb(info->ioaddr + UART_RX); + (void) inb(info->ioaddr + UART_IIR); + (void) inb(info->ioaddr + UART_MSR); + + if (info->tty) + clear_bit(TTY_IO_ERROR, &info->tty->flags); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + + /* + * and set the speed of the serial port + */ + mxser_change_speed(info, NULL); + info->flags |= ASYNC_INITIALIZED; + spin_unlock_irqrestore(&info->slock, flags); + return 0; } -static void mxser_do_softint(struct work_struct *work) +/* + * This routine will shutdown a serial port; interrupts maybe disabled, and + * DTR is dropped if the hangup on close termio flag is on. + */ +static void mxser_shutdown(struct mxser_port *info) { - struct mxser_struct *info = - container_of(work, struct mxser_struct, tqueue); - struct tty_struct *tty; + unsigned long flags; - tty = info->tty; + if (!(info->flags & ASYNC_INITIALIZED)) + return; + + spin_lock_irqsave(&info->slock, flags); + + /* + * clear delta_msr_wait queue to avoid mem leaks: we may free the irq + * here so the queue might never be waken up + */ + wake_up_interruptible(&info->delta_msr_wait); - if (tty) { - if (test_and_clear_bit(MXSER_EVENT_TXLOW, &info->event)) - tty_wakeup(tty); - if (test_and_clear_bit(MXSER_EVENT_HANGUP, &info->event)) - tty_hangup(tty); + /* + * Free the IRQ, if necessary + */ + if (info->xmit_buf) { + free_page((unsigned long) info->xmit_buf); + info->xmit_buf = NULL; } -} -static unsigned char mxser_get_msr(int baseaddr, int mode, int port, struct mxser_struct *info) -{ - unsigned char status = 0; + info->IER = 0; + outb(0x00, info->ioaddr + UART_IER); - status = inb(baseaddr + UART_MSR); + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) + info->MCR &= ~(UART_MCR_DTR | UART_MCR_RTS); + outb(info->MCR, info->ioaddr + UART_MCR); - mxser_msr[port] &= 0x0F; - mxser_msr[port] |= status; - status = mxser_msr[port]; - if (mode) - mxser_msr[port] = 0; + /* clear Rx/Tx FIFO's */ + if (info->board->chip_flag) + outb(UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT | + MOXA_MUST_FCR_GDA_MODE_ENABLE, + info->ioaddr + UART_FCR); + else + outb(UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, + info->ioaddr + UART_FCR); - return status; + /* read data port to reset things */ + (void) inb(info->ioaddr + UART_RX); + + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + info->flags &= ~ASYNC_INITIALIZED; + + if (info->board->chip_flag) + SET_MOXA_MUST_NO_SOFTWARE_FLOW_CONTROL(info->ioaddr); + + spin_unlock_irqrestore(&info->slock, flags); } /* @@ -957,19 +893,17 @@ static unsigned char mxser_get_msr(int baseaddr, int mode, int port, struct mxse */ static int mxser_open(struct tty_struct *tty, struct file *filp) { - struct mxser_struct *info; + struct mxser_port *info; + unsigned long flags; int retval, line; - /* initialize driver_data in case something fails */ - tty->driver_data = NULL; - line = tty->index; if (line == MXSER_PORTS) return 0; if (line < 0 || line > MXSER_PORTS) return -ENODEV; - info = mxvar_table + line; - if (!info->base) + info = &mxser_boards[line / MXSER_PORTS_PER_BOARD].ports[line % MXSER_PORTS_PER_BOARD]; + if (!info->ioaddr) return -ENODEV; tty->driver_data = info; @@ -977,6 +911,9 @@ static int mxser_open(struct tty_struct *tty, struct file *filp) /* * Start up serial port */ + spin_lock_irqsave(&info->slock, flags); + info->count++; + spin_unlock_irqrestore(&info->slock, flags); retval = mxser_startup(info); if (retval) return retval; @@ -985,21 +922,6 @@ static int mxser_open(struct tty_struct *tty, struct file *filp) if (retval) return retval; - info->count++; - - if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) { - if (tty->driver->subtype == SERIAL_TYPE_NORMAL) - *tty->termios = info->normal_termios; - else - *tty->termios = info->callout_termios; - mxser_change_speed(info, NULL); - } - - /* - status = mxser_get_msr(info->base, 0, info->port); - mxser_check_modem_status(info, status); - */ - /* unmark here for very high baud rate (ex. 921600 bps) used */ tty->low_latency = 1; return 0; @@ -1013,11 +935,10 @@ static int mxser_open(struct tty_struct *tty, struct file *filp) */ static void mxser_close(struct tty_struct *tty, struct file *filp) { - struct mxser_struct *info = tty->driver_data; + struct mxser_port *info = tty->driver_data; unsigned long timeout; unsigned long flags; - struct tty_ldisc *ld; if (tty->index == MXSER_PORTS) return; @@ -1044,7 +965,7 @@ static void mxser_close(struct tty_struct *tty, struct file *filp) } if (--info->count < 0) { printk(KERN_ERR "mxser_close: bad serial port count for " - "ttys%d: %d\n", info->port, info->count); + "ttys%d: %d\n", tty->index, info->count); info->count = 0; } if (info->count) { @@ -1073,20 +994,18 @@ static void mxser_close(struct tty_struct *tty, struct file *filp) * line status register. */ info->IER &= ~UART_IER_RLSI; - if (info->IsMoxaMustChipFlag) + if (info->board->chip_flag) info->IER &= ~MOXA_MUST_RECV_ISR; -/* by William - info->read_status_mask &= ~UART_LSR_DR; -*/ + if (info->flags & ASYNC_INITIALIZED) { - outb(info->IER, info->base + UART_IER); + outb(info->IER, info->ioaddr + UART_IER); /* * Before we drop DTR, make sure the UART transmitter * has completely drained; this is especially * important if there is a transmit FIFO! */ timeout = jiffies + HZ; - while (!(inb(info->base + UART_LSR) & UART_LSR_TEMT)) { + while (!(inb(info->ioaddr + UART_LSR) & UART_LSR_TEMT)) { schedule_timeout_interruptible(5); if (time_after(jiffies, timeout)) break; @@ -1096,14 +1015,9 @@ static void mxser_close(struct tty_struct *tty, struct file *filp) if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); - - ld = tty_ldisc_ref(tty); - if (ld) { - if (ld->flush_buffer) - ld->flush_buffer(tty); - tty_ldisc_deref(ld); - } - + + tty_ldisc_flush(tty); + tty->closing = 0; info->event = 0; info->tty = NULL; @@ -1114,14 +1028,12 @@ static void mxser_close(struct tty_struct *tty, struct file *filp) } info->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); - wake_up_interruptible(&info->close_wait); - } static int mxser_write(struct tty_struct *tty, const unsigned char *buf, int count) { int c, total = 0; - struct mxser_struct *info = tty->driver_data; + struct mxser_port *info = tty->driver_data; unsigned long flags; if (!info->xmit_buf) @@ -1145,13 +1057,15 @@ static int mxser_write(struct tty_struct *tty, const unsigned char *buf, int cou total += c; } - if (info->xmit_cnt && !tty->stopped && !(info->IER & UART_IER_THRI)) { + if (info->xmit_cnt && !tty->stopped) { if (!tty->hw_stopped || (info->type == PORT_16550A) || - (info->IsMoxaMustChipFlag)) { + (info->board->chip_flag)) { spin_lock_irqsave(&info->slock, flags); + outb(info->IER & ~UART_IER_THRI, info->ioaddr + + UART_IER); info->IER |= UART_IER_THRI; - outb(info->IER, info->base + UART_IER); + outb(info->IER, info->ioaddr + UART_IER); spin_unlock_irqrestore(&info->slock, flags); } } @@ -1160,7 +1074,7 @@ static int mxser_write(struct tty_struct *tty, const unsigned char *buf, int cou static void mxser_put_char(struct tty_struct *tty, unsigned char ch) { - struct mxser_struct *info = tty->driver_data; + struct mxser_port *info = tty->driver_data; unsigned long flags; if (!info->xmit_buf) @@ -1174,13 +1088,14 @@ static void mxser_put_char(struct tty_struct *tty, unsigned char ch) info->xmit_head &= SERIAL_XMIT_SIZE - 1; info->xmit_cnt++; spin_unlock_irqrestore(&info->slock, flags); - if (!tty->stopped && !(info->IER & UART_IER_THRI)) { + if (!tty->stopped) { if (!tty->hw_stopped || (info->type == PORT_16550A) || - info->IsMoxaMustChipFlag) { + info->board->chip_flag) { spin_lock_irqsave(&info->slock, flags); + outb(info->IER & ~UART_IER_THRI, info->ioaddr + UART_IER); info->IER |= UART_IER_THRI; - outb(info->IER, info->base + UART_IER); + outb(info->IER, info->ioaddr + UART_IER); spin_unlock_irqrestore(&info->slock, flags); } } @@ -1189,7 +1104,7 @@ static void mxser_put_char(struct tty_struct *tty, unsigned char ch) static void mxser_flush_chars(struct tty_struct *tty) { - struct mxser_struct *info = tty->driver_data; + struct mxser_port *info = tty->driver_data; unsigned long flags; if (info->xmit_cnt <= 0 || @@ -1197,21 +1112,22 @@ static void mxser_flush_chars(struct tty_struct *tty) !info->xmit_buf || (tty->hw_stopped && (info->type != PORT_16550A) && - (!info->IsMoxaMustChipFlag) + (!info->board->chip_flag) )) return; spin_lock_irqsave(&info->slock, flags); + outb(info->IER & ~UART_IER_THRI, info->ioaddr + UART_IER); info->IER |= UART_IER_THRI; - outb(info->IER, info->base + UART_IER); + outb(info->IER, info->ioaddr + UART_IER); spin_unlock_irqrestore(&info->slock, flags); } static int mxser_write_room(struct tty_struct *tty) { - struct mxser_struct *info = tty->driver_data; + struct mxser_port *info = tty->driver_data; int ret; ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; @@ -1222,13 +1138,13 @@ static int mxser_write_room(struct tty_struct *tty) static int mxser_chars_in_buffer(struct tty_struct *tty) { - struct mxser_struct *info = tty->driver_data; + struct mxser_port *info = tty->driver_data; return info->xmit_cnt; } static void mxser_flush_buffer(struct tty_struct *tty) { - struct mxser_struct *info = tty->driver_data; + struct mxser_port *info = tty->driver_data; char fcr; unsigned long flags; @@ -1236,39 +1152,497 @@ static void mxser_flush_buffer(struct tty_struct *tty) spin_lock_irqsave(&info->slock, flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - /* below added by shinhay */ - fcr = inb(info->base + UART_FCR); + fcr = inb(info->ioaddr + UART_FCR); outb((fcr | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT), - info->base + UART_FCR); - outb(fcr, info->base + UART_FCR); + info->ioaddr + UART_FCR); + outb(fcr, info->ioaddr + UART_FCR); spin_unlock_irqrestore(&info->slock, flags); - /* above added by shinhay */ tty_wakeup(tty); } -static int mxser_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) +/* + * ------------------------------------------------------------ + * friends of mxser_ioctl() + * ------------------------------------------------------------ + */ +static int mxser_get_serial_info(struct mxser_port *info, + struct serial_struct __user *retinfo) { - struct mxser_struct *info = tty->driver_data; - int retval; - struct async_icount cprev, cnow; /* kernel counter temps */ + struct serial_struct tmp = { + .type = info->type, + .line = info->tty->index, + .port = info->ioaddr, + .irq = info->board->irq, + .flags = info->flags, + .baud_base = info->baud_base, + .close_delay = info->close_delay, + .closing_wait = info->closing_wait, + .custom_divisor = info->custom_divisor, + .hub6 = 0 + }; + if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) + return -EFAULT; + return 0; +} + +static int mxser_set_serial_info(struct mxser_port *info, + struct serial_struct __user *new_info) +{ + struct serial_struct new_serial; + speed_t baud; + unsigned long sl_flags; + unsigned int flags; + int retval = 0; + + if (!new_info || !info->ioaddr) + return -ENODEV; + if (copy_from_user(&new_serial, new_info, sizeof(new_serial))) + return -EFAULT; + + if (new_serial.irq != info->board->irq || + new_serial.port != info->ioaddr) + return -EINVAL; + + flags = info->flags & ASYNC_SPD_MASK; + + if (!capable(CAP_SYS_ADMIN)) { + if ((new_serial.baud_base != info->baud_base) || + (new_serial.close_delay != info->close_delay) || + ((new_serial.flags & ~ASYNC_USR_MASK) != (info->flags & ~ASYNC_USR_MASK))) + return -EPERM; + info->flags = ((info->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + } else { + /* + * OK, past this point, all the error checking has been done. + * At this point, we start making changes..... + */ + info->flags = ((info->flags & ~ASYNC_FLAGS) | + (new_serial.flags & ASYNC_FLAGS)); + info->close_delay = new_serial.close_delay * HZ / 100; + info->closing_wait = new_serial.closing_wait * HZ / 100; + info->tty->low_latency = + (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; + info->tty->low_latency = 0; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST && + (new_serial.baud_base != info->baud_base || + new_serial.custom_divisor != + info->custom_divisor)) { + baud = new_serial.baud_base / new_serial.custom_divisor; + tty_encode_baud_rate(info->tty, baud, baud); + } + } + + info->type = new_serial.type; + + process_txrx_fifo(info); + + if (info->flags & ASYNC_INITIALIZED) { + if (flags != (info->flags & ASYNC_SPD_MASK)) { + spin_lock_irqsave(&info->slock, sl_flags); + mxser_change_speed(info, NULL); + spin_unlock_irqrestore(&info->slock, sl_flags); + } + } else + retval = mxser_startup(info); + + return retval; +} + +/* + * mxser_get_lsr_info - get line status register info + * + * Purpose: Let user call ioctl() to get info when the UART physically + * is emptied. On bus types like RS485, the transmitter must + * release the bus after transmitting. This must be done when + * the transmit shift register is empty, not be done when the + * transmit holding register is empty. This functionality + * allows an RS485 driver to be written in user space. + */ +static int mxser_get_lsr_info(struct mxser_port *info, + unsigned int __user *value) +{ + unsigned char status; + unsigned int result; + unsigned long flags; + + spin_lock_irqsave(&info->slock, flags); + status = inb(info->ioaddr + UART_LSR); + spin_unlock_irqrestore(&info->slock, flags); + result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); + return put_user(result, value); +} + +/* + * This routine sends a break character out the serial port. + */ +static void mxser_send_break(struct mxser_port *info, int duration) +{ + unsigned long flags; + + if (!info->ioaddr) + return; + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&info->slock, flags); + outb(inb(info->ioaddr + UART_LCR) | UART_LCR_SBC, + info->ioaddr + UART_LCR); + spin_unlock_irqrestore(&info->slock, flags); + schedule_timeout(duration); + spin_lock_irqsave(&info->slock, flags); + outb(inb(info->ioaddr + UART_LCR) & ~UART_LCR_SBC, + info->ioaddr + UART_LCR); + spin_unlock_irqrestore(&info->slock, flags); +} + +static int mxser_tiocmget(struct tty_struct *tty, struct file *file) +{ + struct mxser_port *info = tty->driver_data; + unsigned char control, status; + unsigned long flags; + + + if (tty->index == MXSER_PORTS) + return -ENOIOCTLCMD; + if (test_bit(TTY_IO_ERROR, &tty->flags)) + return -EIO; + + control = info->MCR; + + spin_lock_irqsave(&info->slock, flags); + status = inb(info->ioaddr + UART_MSR); + if (status & UART_MSR_ANY_DELTA) + mxser_check_modem_status(info, status); + spin_unlock_irqrestore(&info->slock, flags); + return ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) | + ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) | + ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) | + ((status & UART_MSR_RI) ? TIOCM_RNG : 0) | + ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) | + ((status & UART_MSR_CTS) ? TIOCM_CTS : 0); +} + +static int mxser_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear) +{ + struct mxser_port *info = tty->driver_data; + unsigned long flags; + + + if (tty->index == MXSER_PORTS) + return -ENOIOCTLCMD; + if (test_bit(TTY_IO_ERROR, &tty->flags)) + return -EIO; + + spin_lock_irqsave(&info->slock, flags); + + if (set & TIOCM_RTS) + info->MCR |= UART_MCR_RTS; + if (set & TIOCM_DTR) + info->MCR |= UART_MCR_DTR; + + if (clear & TIOCM_RTS) + info->MCR &= ~UART_MCR_RTS; + if (clear & TIOCM_DTR) + info->MCR &= ~UART_MCR_DTR; + + outb(info->MCR, info->ioaddr + UART_MCR); + spin_unlock_irqrestore(&info->slock, flags); + return 0; +} + +static int __init mxser_program_mode(int port) +{ + int id, i, j, n; + + outb(0, port); + outb(0, port); + outb(0, port); + (void)inb(port); + (void)inb(port); + outb(0, port); + (void)inb(port); + + id = inb(port + 1) & 0x1F; + if ((id != C168_ASIC_ID) && + (id != C104_ASIC_ID) && + (id != C102_ASIC_ID) && + (id != CI132_ASIC_ID) && + (id != CI134_ASIC_ID) && + (id != CI104J_ASIC_ID)) + return -1; + for (i = 0, j = 0; i < 4; i++) { + n = inb(port + 2); + if (n == 'M') { + j = 1; + } else if ((j == 1) && (n == 1)) { + j = 2; + break; + } else + j = 0; + } + if (j != 2) + id = -2; + return id; +} + +static void __init mxser_normal_mode(int port) +{ + int i, n; + + outb(0xA5, port + 1); + outb(0x80, port + 3); + outb(12, port + 0); /* 9600 bps */ + outb(0, port + 1); + outb(0x03, port + 3); /* 8 data bits */ + outb(0x13, port + 4); /* loop back mode */ + for (i = 0; i < 16; i++) { + n = inb(port + 5); + if ((n & 0x61) == 0x60) + break; + if ((n & 1) == 1) + (void)inb(port); + } + outb(0x00, port + 4); +} + +#define CHIP_SK 0x01 /* Serial Data Clock in Eprom */ +#define CHIP_DO 0x02 /* Serial Data Output in Eprom */ +#define CHIP_CS 0x04 /* Serial Chip Select in Eprom */ +#define CHIP_DI 0x08 /* Serial Data Input in Eprom */ +#define EN_CCMD 0x000 /* Chip's command register */ +#define EN0_RSARLO 0x008 /* Remote start address reg 0 */ +#define EN0_RSARHI 0x009 /* Remote start address reg 1 */ +#define EN0_RCNTLO 0x00A /* Remote byte count reg WR */ +#define EN0_RCNTHI 0x00B /* Remote byte count reg WR */ +#define EN0_DCFG 0x00E /* Data configuration reg WR */ +#define EN0_PORT 0x010 /* Rcv missed frame error counter RD */ +#define ENC_PAGE0 0x000 /* Select page 0 of chip registers */ +#define ENC_PAGE3 0x0C0 /* Select page 3 of chip registers */ +static int __init mxser_read_register(int port, unsigned short *regs) +{ + int i, k, value, id; + unsigned int j; + + id = mxser_program_mode(port); + if (id < 0) + return id; + for (i = 0; i < 14; i++) { + k = (i & 0x3F) | 0x180; + for (j = 0x100; j > 0; j >>= 1) { + outb(CHIP_CS, port); + if (k & j) { + outb(CHIP_CS | CHIP_DO, port); + outb(CHIP_CS | CHIP_DO | CHIP_SK, port); /* A? bit of read */ + } else { + outb(CHIP_CS, port); + outb(CHIP_CS | CHIP_SK, port); /* A? bit of read */ + } + } + (void)inb(port); + value = 0; + for (k = 0, j = 0x8000; k < 16; k++, j >>= 1) { + outb(CHIP_CS, port); + outb(CHIP_CS | CHIP_SK, port); + if (inb(port) & CHIP_DI) + value |= j; + } + regs[i] = value; + outb(0, port); + } + mxser_normal_mode(port); + return id; +} + +static int mxser_ioctl_special(unsigned int cmd, void __user *argp) +{ + struct mxser_port *port; + int result, status; + unsigned int i, j; + + switch (cmd) { + case MOXA_GET_MAJOR: + return put_user(ttymajor, (int __user *)argp); + + case MOXA_CHKPORTENABLE: + result = 0; + + for (i = 0; i < MXSER_BOARDS; i++) + for (j = 0; j < MXSER_PORTS_PER_BOARD; j++) + if (mxser_boards[i].ports[j].ioaddr) + result |= (1 << i); + + return put_user(result, (unsigned long __user *)argp); + case MOXA_GETDATACOUNT: + if (copy_to_user(argp, &mxvar_log, sizeof(mxvar_log))) + return -EFAULT; + return 0; + case MOXA_GETMSTATUS: + for (i = 0; i < MXSER_BOARDS; i++) + for (j = 0; j < MXSER_PORTS_PER_BOARD; j++) { + port = &mxser_boards[i].ports[j]; + + GMStatus[i].ri = 0; + if (!port->ioaddr) { + GMStatus[i].dcd = 0; + GMStatus[i].dsr = 0; + GMStatus[i].cts = 0; + continue; + } + + if (!port->tty || !port->tty->termios) + GMStatus[i].cflag = + port->normal_termios.c_cflag; + else + GMStatus[i].cflag = + port->tty->termios->c_cflag; + + status = inb(port->ioaddr + UART_MSR); + if (status & 0x80 /*UART_MSR_DCD */ ) + GMStatus[i].dcd = 1; + else + GMStatus[i].dcd = 0; + + if (status & 0x20 /*UART_MSR_DSR */ ) + GMStatus[i].dsr = 1; + else + GMStatus[i].dsr = 0; + + + if (status & 0x10 /*UART_MSR_CTS */ ) + GMStatus[i].cts = 1; + else + GMStatus[i].cts = 0; + } + if (copy_to_user(argp, GMStatus, + sizeof(struct mxser_mstatus) * MXSER_PORTS)) + return -EFAULT; + return 0; + case MOXA_ASPP_MON_EXT: { + int p, shiftbit; + unsigned long opmode; + unsigned cflag, iflag; + + for (i = 0; i < MXSER_BOARDS; i++) + for (j = 0; j < MXSER_PORTS_PER_BOARD; j++) { + port = &mxser_boards[i].ports[j]; + if (!port->ioaddr) + continue; + + status = mxser_get_msr(port->ioaddr, 0, i); + + if (status & UART_MSR_TERI) + port->icount.rng++; + if (status & UART_MSR_DDSR) + port->icount.dsr++; + if (status & UART_MSR_DDCD) + port->icount.dcd++; + if (status & UART_MSR_DCTS) + port->icount.cts++; + + port->mon_data.modem_status = status; + mon_data_ext.rx_cnt[i] = port->mon_data.rxcnt; + mon_data_ext.tx_cnt[i] = port->mon_data.txcnt; + mon_data_ext.up_rxcnt[i] = + port->mon_data.up_rxcnt; + mon_data_ext.up_txcnt[i] = + port->mon_data.up_txcnt; + mon_data_ext.modem_status[i] = + port->mon_data.modem_status; + mon_data_ext.baudrate[i] = + tty_get_baud_rate(port->tty); + + if (!port->tty || !port->tty->termios) { + cflag = port->normal_termios.c_cflag; + iflag = port->normal_termios.c_iflag; + } else { + cflag = port->tty->termios->c_cflag; + iflag = port->tty->termios->c_iflag; + } + + mon_data_ext.databits[i] = cflag & CSIZE; + + mon_data_ext.stopbits[i] = cflag & CSTOPB; + + mon_data_ext.parity[i] = + cflag & (PARENB | PARODD | CMSPAR); + + mon_data_ext.flowctrl[i] = 0x00; + + if (cflag & CRTSCTS) + mon_data_ext.flowctrl[i] |= 0x03; + + if (iflag & (IXON | IXOFF)) + mon_data_ext.flowctrl[i] |= 0x0C; + + if (port->type == PORT_16550A) + mon_data_ext.fifo[i] = 1; + else + mon_data_ext.fifo[i] = 0; + + p = i % 4; + shiftbit = p * 2; + opmode = inb(port->opmode_ioaddr) >> shiftbit; + opmode &= OP_MODE_MASK; + + mon_data_ext.iftype[i] = opmode; + + } + if (copy_to_user(argp, &mon_data_ext, + sizeof(mon_data_ext))) + return -EFAULT; + + return 0; + + } default: + return -ENOIOCTLCMD; + } + return 0; +} + +static int mxser_cflags_changed(struct mxser_port *info, unsigned long arg, + struct async_icount *cprev) +{ + struct async_icount cnow; + unsigned long flags; + int ret; + + spin_lock_irqsave(&info->slock, flags); + cnow = info->icount; /* atomic copy */ + spin_unlock_irqrestore(&info->slock, flags); + + ret = ((arg & TIOCM_RNG) && (cnow.rng != cprev->rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev->dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev->dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev->cts)); + + *cprev = cnow; + + return ret; +} + +static int mxser_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct mxser_port *info = tty->driver_data; + struct async_icount cnow; struct serial_icounter_struct __user *p_cuser; - unsigned long templ; unsigned long flags; void __user *argp = (void __user *)arg; + int retval; if (tty->index == MXSER_PORTS) return mxser_ioctl_special(cmd, argp); - /* following add by Victor Yu. 01-05-2004 */ if (cmd == MOXA_SET_OP_MODE || cmd == MOXA_GET_OP_MODE) { - int opmode, p; + int p; + unsigned long opmode; static unsigned char ModeMask[] = { 0xfc, 0xf3, 0xcf, 0x3f }; int shiftbit; unsigned char val, mask; - p = info->port % 4; + p = tty->index % 4; if (cmd == MOXA_SET_OP_MODE) { if (get_user(opmode, (int __user *) argp)) return -EFAULT; @@ -1287,17 +1661,16 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, unsigned int c shiftbit = p * 2; opmode = inb(info->opmode_ioaddr) >> shiftbit; opmode &= OP_MODE_MASK; - if (copy_to_user(argp, &opmode, sizeof(int))) + if (put_user(opmode, (int __user *)argp)) return -EFAULT; } return 0; } - /* above add by Victor Yu. 01-05-2004 */ - if ((cmd != TIOCGSERIAL) && (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - } + if (cmd != TIOCGSERIAL && cmd != TIOCMIWAIT && cmd != TIOCGICOUNT && + test_bit(TTY_IO_ERROR, &tty->flags)) + return -EIO; + switch (cmd) { case TCSBRK: /* SVID version: non-zero arg --> no break */ retval = tty_check_change(tty); @@ -1315,11 +1688,10 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, unsigned int c mxser_send_break(info, arg ? arg * (HZ / 10) : HZ / 4); return 0; case TIOCGSOFTCAR: - return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long __user *)argp); + return put_user(!!C_CLOCAL(tty), (unsigned long __user *)argp); case TIOCSSOFTCAR: - if (get_user(templ, (unsigned long __user *) argp)) + if (get_user(arg, (unsigned long __user *)argp)) return -EFAULT; - arg = templ; tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); return 0; case TIOCGSERIAL: @@ -1339,30 +1711,19 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, unsigned int c cnow = info->icount; /* note the counters on entry */ spin_unlock_irqrestore(&info->slock, flags); - wait_event_interruptible(info->delta_msr_wait, ({ - cprev = cnow; - spin_lock_irqsave(&info->slock, flags); - cnow = info->icount; /* atomic copy */ - spin_unlock_irqrestore(&info->slock, flags); - - ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || - ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || - ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || - ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)); - })); - break; - /* - * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) - * Return: write counters to the user passed counter struct - * NB: both 1->0 and 0->1 transitions are counted except for - * RI where only 0->1 is counted. - */ + return wait_event_interruptible(info->delta_msr_wait, + mxser_cflags_changed(info, arg, &cnow)); + /* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ case TIOCGICOUNT: spin_lock_irqsave(&info->slock, flags); cnow = info->icount; spin_unlock_irqrestore(&info->slock, flags); p_cuser = argp; - /* modified by casper 1/11/2000 */ if (put_user(cnow.frame, &p_cuser->frame)) return -EFAULT; if (put_user(cnow.brk, &p_cuser->brk)) @@ -1384,240 +1745,65 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, unsigned int c return 0; case MOXA_HighSpeedOn: return put_user(info->baud_base != 115200 ? 1 : 0, (int __user *)argp); - case MOXA_SDS_RSTICOUNTER: { - info->mon_data.rxcnt = 0; - info->mon_data.txcnt = 0; - return 0; - } -/* (above) added by James. */ - case MOXA_ASPP_SETBAUD:{ - long baud; - if (get_user(baud, (long __user *)argp)) - return -EFAULT; - mxser_set_baud(info, baud); - return 0; - } - case MOXA_ASPP_GETBAUD: - if (copy_to_user(argp, &info->realbaud, sizeof(long))) - return -EFAULT; - + case MOXA_SDS_RSTICOUNTER: + info->mon_data.rxcnt = 0; + info->mon_data.txcnt = 0; return 0; case MOXA_ASPP_OQUEUE:{ - int len, lsr; + int len, lsr; - len = mxser_chars_in_buffer(tty); + len = mxser_chars_in_buffer(tty); - lsr = inb(info->base + UART_LSR) & UART_LSR_TEMT; + lsr = inb(info->ioaddr + UART_LSR) & UART_LSR_TEMT; - len += (lsr ? 0 : 1); + len += (lsr ? 0 : 1); - if (copy_to_user(argp, &len, sizeof(int))) - return -EFAULT; - - return 0; - } + return put_user(len, (int __user *)argp); + } case MOXA_ASPP_MON: { - int mcr, status; - - /* info->mon_data.ser_param = tty->termios->c_cflag; */ + int mcr, status; - status = mxser_get_msr(info->base, 1, info->port, info); - mxser_check_modem_status(info, status); - - mcr = inb(info->base + UART_MCR); - if (mcr & MOXA_MUST_MCR_XON_FLAG) - info->mon_data.hold_reason &= ~NPPI_NOTIFY_XOFFHOLD; - else - info->mon_data.hold_reason |= NPPI_NOTIFY_XOFFHOLD; - - if (mcr & MOXA_MUST_MCR_TX_XON) - info->mon_data.hold_reason &= ~NPPI_NOTIFY_XOFFXENT; - else - info->mon_data.hold_reason |= NPPI_NOTIFY_XOFFXENT; - - if (info->tty->hw_stopped) - info->mon_data.hold_reason |= NPPI_NOTIFY_CTSHOLD; - else - info->mon_data.hold_reason &= ~NPPI_NOTIFY_CTSHOLD; - - if (copy_to_user(argp, &info->mon_data, - sizeof(struct mxser_mon))) - return -EFAULT; - - return 0; - } - - case MOXA_ASPP_LSTATUS: { - if (copy_to_user(argp, &info->err_shadow, - sizeof(unsigned char))) - return -EFAULT; - - info->err_shadow = 0; - return 0; - } - case MOXA_SET_BAUD_METHOD: { - int method; - - if (get_user(method, (int __user *)argp)) - return -EFAULT; - mxser_set_baud_method[info->port] = method; - if (copy_to_user(argp, &method, sizeof(int))) - return -EFAULT; + status = mxser_get_msr(info->ioaddr, 1, tty->index); + mxser_check_modem_status(info, status); - return 0; - } - default: - return -ENOIOCTLCMD; - } - return 0; -} + mcr = inb(info->ioaddr + UART_MCR); + if (mcr & MOXA_MUST_MCR_XON_FLAG) + info->mon_data.hold_reason &= ~NPPI_NOTIFY_XOFFHOLD; + else + info->mon_data.hold_reason |= NPPI_NOTIFY_XOFFHOLD; -#ifndef CMSPAR -#define CMSPAR 010000000000 -#endif + if (mcr & MOXA_MUST_MCR_TX_XON) + info->mon_data.hold_reason &= ~NPPI_NOTIFY_XOFFXENT; + else + info->mon_data.hold_reason |= NPPI_NOTIFY_XOFFXENT; -static int mxser_ioctl_special(unsigned int cmd, void __user *argp) -{ - int i, result, status; + if (info->tty->hw_stopped) + info->mon_data.hold_reason |= NPPI_NOTIFY_CTSHOLD; + else + info->mon_data.hold_reason &= ~NPPI_NOTIFY_CTSHOLD; - switch (cmd) { - case MOXA_GET_CONF: - if (copy_to_user(argp, mxsercfg, - sizeof(struct mxser_hwconf) * 4)) + if (copy_to_user(argp, &info->mon_data, + sizeof(struct mxser_mon))) return -EFAULT; - return 0; - case MOXA_GET_MAJOR: - if (copy_to_user(argp, &ttymajor, sizeof(int))) - return -EFAULT; - return 0; - case MOXA_GET_CUMAJOR: - if (copy_to_user(argp, &calloutmajor, sizeof(int))) - return -EFAULT; return 0; - - case MOXA_CHKPORTENABLE: - result = 0; - for (i = 0; i < MXSER_PORTS; i++) { - if (mxvar_table[i].base) - result |= (1 << i); - } - return put_user(result, (unsigned long __user *)argp); - case MOXA_GETDATACOUNT: - if (copy_to_user(argp, &mxvar_log, sizeof(mxvar_log))) + } + case MOXA_ASPP_LSTATUS: { + if (put_user(info->err_shadow, (unsigned char __user *)argp)) return -EFAULT; - return 0; - case MOXA_GETMSTATUS: - for (i = 0; i < MXSER_PORTS; i++) { - GMStatus[i].ri = 0; - if (!mxvar_table[i].base) { - GMStatus[i].dcd = 0; - GMStatus[i].dsr = 0; - GMStatus[i].cts = 0; - continue; - } - - if (!mxvar_table[i].tty || !mxvar_table[i].tty->termios) - GMStatus[i].cflag = mxvar_table[i].normal_termios.c_cflag; - else - GMStatus[i].cflag = mxvar_table[i].tty->termios->c_cflag; - - status = inb(mxvar_table[i].base + UART_MSR); - if (status & 0x80 /*UART_MSR_DCD */ ) - GMStatus[i].dcd = 1; - else - GMStatus[i].dcd = 0; - - if (status & 0x20 /*UART_MSR_DSR */ ) - GMStatus[i].dsr = 1; - else - GMStatus[i].dsr = 0; - - if (status & 0x10 /*UART_MSR_CTS */ ) - GMStatus[i].cts = 1; - else - GMStatus[i].cts = 0; - } - if (copy_to_user(argp, GMStatus, - sizeof(struct mxser_mstatus) * MXSER_PORTS)) - return -EFAULT; + info->err_shadow = 0; return 0; - case MOXA_ASPP_MON_EXT: { - int status; - int opmode, p; - int shiftbit; - unsigned cflag, iflag; - - for (i = 0; i < MXSER_PORTS; i++) { - if (!mxvar_table[i].base) - continue; - - status = mxser_get_msr(mxvar_table[i].base, 0, - i, &(mxvar_table[i])); - /* - mxser_check_modem_status(&mxvar_table[i], - status); - */ - if (status & UART_MSR_TERI) - mxvar_table[i].icount.rng++; - if (status & UART_MSR_DDSR) - mxvar_table[i].icount.dsr++; - if (status & UART_MSR_DDCD) - mxvar_table[i].icount.dcd++; - if (status & UART_MSR_DCTS) - mxvar_table[i].icount.cts++; - - mxvar_table[i].mon_data.modem_status = status; - mon_data_ext.rx_cnt[i] = mxvar_table[i].mon_data.rxcnt; - mon_data_ext.tx_cnt[i] = mxvar_table[i].mon_data.txcnt; - mon_data_ext.up_rxcnt[i] = mxvar_table[i].mon_data.up_rxcnt; - mon_data_ext.up_txcnt[i] = mxvar_table[i].mon_data.up_txcnt; - mon_data_ext.modem_status[i] = mxvar_table[i].mon_data.modem_status; - mon_data_ext.baudrate[i] = mxvar_table[i].realbaud; - - if (!mxvar_table[i].tty || !mxvar_table[i].tty->termios) { - cflag = mxvar_table[i].normal_termios.c_cflag; - iflag = mxvar_table[i].normal_termios.c_iflag; - } else { - cflag = mxvar_table[i].tty->termios->c_cflag; - iflag = mxvar_table[i].tty->termios->c_iflag; - } - - mon_data_ext.databits[i] = cflag & CSIZE; - - mon_data_ext.stopbits[i] = cflag & CSTOPB; - - mon_data_ext.parity[i] = cflag & (PARENB | PARODD | CMSPAR); - - mon_data_ext.flowctrl[i] = 0x00; - - if (cflag & CRTSCTS) - mon_data_ext.flowctrl[i] |= 0x03; - - if (iflag & (IXON | IXOFF)) - mon_data_ext.flowctrl[i] |= 0x0C; - - if (mxvar_table[i].type == PORT_16550A) - mon_data_ext.fifo[i] = 1; - else - mon_data_ext.fifo[i] = 0; - - p = i % 4; - shiftbit = p * 2; - opmode = inb(mxvar_table[i].opmode_ioaddr) >> shiftbit; - opmode &= OP_MODE_MASK; - - mon_data_ext.iftype[i] = opmode; - - } - if (copy_to_user(argp, &mon_data_ext, sizeof(struct mxser_mon_ext))) - return -EFAULT; - - return 0; + } + case MOXA_SET_BAUD_METHOD: { + int method; - } + if (get_user(method, (int __user *)argp)) + return -EFAULT; + mxser_set_baud_method[tty->index] = method; + return put_user(method, (int __user *)argp); + } default: return -ENOIOCTLCMD; } @@ -1626,107 +1812,105 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp) static void mxser_stoprx(struct tty_struct *tty) { - struct mxser_struct *info = tty->driver_data; - /* unsigned long flags; */ + struct mxser_port *info = tty->driver_data; info->ldisc_stop_rx = 1; if (I_IXOFF(tty)) { - /* MX_LOCK(&info->slock); */ - /* following add by Victor Yu. 09-02-2002 */ - if (info->IsMoxaMustChipFlag) { + if (info->board->chip_flag) { info->IER &= ~MOXA_MUST_RECV_ISR; - outb(info->IER, info->base + UART_IER); + outb(info->IER, info->ioaddr + UART_IER); } else { - /* above add by Victor Yu. 09-02-2002 */ info->x_char = STOP_CHAR(tty); - /* mask by Victor Yu. 09-02-2002 */ - /* outb(info->IER, 0); */ - outb(0, info->base + UART_IER); + outb(0, info->ioaddr + UART_IER); info->IER |= UART_IER_THRI; - /* force Tx interrupt */ - outb(info->IER, info->base + UART_IER); - } /* add by Victor Yu. 09-02-2002 */ - /* MX_UNLOCK(&info->slock); */ + outb(info->IER, info->ioaddr + UART_IER); + } } if (info->tty->termios->c_cflag & CRTSCTS) { - /* MX_LOCK(&info->slock); */ info->MCR &= ~UART_MCR_RTS; - outb(info->MCR, info->base + UART_MCR); - /* MX_UNLOCK(&info->slock); */ + outb(info->MCR, info->ioaddr + UART_MCR); } } -static void mxser_startrx(struct tty_struct *tty) +/* + * This routine is called by the upper-layer tty layer to signal that + * incoming characters should be throttled. + */ +static void mxser_throttle(struct tty_struct *tty) +{ + mxser_stoprx(tty); +} + +static void mxser_unthrottle(struct tty_struct *tty) { - struct mxser_struct *info = tty->driver_data; - /* unsigned long flags; */ + struct mxser_port *info = tty->driver_data; + /* startrx */ info->ldisc_stop_rx = 0; if (I_IXOFF(tty)) { if (info->x_char) info->x_char = 0; else { - /* MX_LOCK(&info->slock); */ - - /* following add by Victor Yu. 09-02-2002 */ - if (info->IsMoxaMustChipFlag) { + if (info->board->chip_flag) { info->IER |= MOXA_MUST_RECV_ISR; - outb(info->IER, info->base + UART_IER); + outb(info->IER, info->ioaddr + UART_IER); } else { - /* above add by Victor Yu. 09-02-2002 */ - info->x_char = START_CHAR(tty); - /* mask by Victor Yu. 09-02-2002 */ - /* outb(info->IER, 0); */ - /* add by Victor Yu. 09-02-2002 */ - outb(0, info->base + UART_IER); - /* force Tx interrupt */ + outb(0, info->ioaddr + UART_IER); info->IER |= UART_IER_THRI; - outb(info->IER, info->base + UART_IER); - } /* add by Victor Yu. 09-02-2002 */ - /* MX_UNLOCK(&info->slock); */ + outb(info->IER, info->ioaddr + UART_IER); + } } } if (info->tty->termios->c_cflag & CRTSCTS) { - /* MX_LOCK(&info->slock); */ info->MCR |= UART_MCR_RTS; - outb(info->MCR, info->base + UART_MCR); - /* MX_UNLOCK(&info->slock); */ + outb(info->MCR, info->ioaddr + UART_MCR); } } /* - * This routine is called by the upper-layer tty layer to signal that - * incoming characters should be throttled. + * mxser_stop() and mxser_start() + * + * This routines are called before setting or resetting tty->stopped. + * They enable or disable transmitter interrupts, as necessary. */ -static void mxser_throttle(struct tty_struct *tty) +static void mxser_stop(struct tty_struct *tty) { - /* struct mxser_struct *info = tty->driver_data; */ - /* unsigned long flags; */ + struct mxser_port *info = tty->driver_data; + unsigned long flags; - /* MX_LOCK(&info->slock); */ - mxser_stoprx(tty); - /* MX_UNLOCK(&info->slock); */ + spin_lock_irqsave(&info->slock, flags); + if (info->IER & UART_IER_THRI) { + info->IER &= ~UART_IER_THRI; + outb(info->IER, info->ioaddr + UART_IER); + } + spin_unlock_irqrestore(&info->slock, flags); } -static void mxser_unthrottle(struct tty_struct *tty) +static void mxser_start(struct tty_struct *tty) { - /* struct mxser_struct *info = tty->driver_data; */ - /* unsigned long flags; */ + struct mxser_port *info = tty->driver_data; + unsigned long flags; - /* MX_LOCK(&info->slock); */ - mxser_startrx(tty); - /* MX_UNLOCK(&info->slock); */ + spin_lock_irqsave(&info->slock, flags); + if (info->xmit_cnt && info->xmit_buf) { + outb(info->IER & ~UART_IER_THRI, info->ioaddr + UART_IER); + info->IER |= UART_IER_THRI; + outb(info->IER, info->ioaddr + UART_IER); + } + spin_unlock_irqrestore(&info->slock, flags); } static void mxser_set_termios(struct tty_struct *tty, struct ktermios *old_termios) { - struct mxser_struct *info = tty->driver_data; + struct mxser_port *info = tty->driver_data; unsigned long flags; + spin_lock_irqsave(&info->slock, flags); mxser_change_speed(info, old_termios); + spin_unlock_irqrestore(&info->slock, flags); if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { @@ -1734,61 +1918,27 @@ static void mxser_set_termios(struct tty_struct *tty, struct ktermios *old_termi mxser_start(tty); } -/* Handle sw stopped */ + /* Handle sw stopped */ if ((old_termios->c_iflag & IXON) && !(tty->termios->c_iflag & IXON)) { tty->stopped = 0; - /* following add by Victor Yu. 09-02-2002 */ - if (info->IsMoxaMustChipFlag) { + if (info->board->chip_flag) { spin_lock_irqsave(&info->slock, flags); - DISABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL(info->base); + DISABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL(info->ioaddr); spin_unlock_irqrestore(&info->slock, flags); } - /* above add by Victor Yu. 09-02-2002 */ mxser_start(tty); } } /* - * mxser_stop() and mxser_start() - * - * This routines are called before setting or resetting tty->stopped. - * They enable or disable transmitter interrupts, as necessary. - */ -static void mxser_stop(struct tty_struct *tty) -{ - struct mxser_struct *info = tty->driver_data; - unsigned long flags; - - spin_lock_irqsave(&info->slock, flags); - if (info->IER & UART_IER_THRI) { - info->IER &= ~UART_IER_THRI; - outb(info->IER, info->base + UART_IER); - } - spin_unlock_irqrestore(&info->slock, flags); -} - -static void mxser_start(struct tty_struct *tty) -{ - struct mxser_struct *info = tty->driver_data; - unsigned long flags; - - spin_lock_irqsave(&info->slock, flags); - if (info->xmit_cnt && info->xmit_buf && !(info->IER & UART_IER_THRI)) { - info->IER |= UART_IER_THRI; - outb(info->IER, info->base + UART_IER); - } - spin_unlock_irqrestore(&info->slock, flags); -} - -/* * mxser_wait_until_sent() --- wait until the transmitter is empty */ static void mxser_wait_until_sent(struct tty_struct *tty, int timeout) { - struct mxser_struct *info = tty->driver_data; + struct mxser_port *info = tty->driver_data; unsigned long orig_jiffies, char_time; int lsr; @@ -1829,7 +1979,7 @@ static void mxser_wait_until_sent(struct tty_struct *tty, int timeout) timeout, char_time); printk("jiff=%lu...", jiffies); #endif - while (!((lsr = inb(info->base + UART_LSR)) & UART_LSR_TEMT)) { + while (!((lsr = inb(info->ioaddr + UART_LSR)) & UART_LSR_TEMT)) { #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT printk("lsr = %d (jiff=%lu)...", lsr, jiffies); #endif @@ -1846,13 +1996,12 @@ static void mxser_wait_until_sent(struct tty_struct *tty, int timeout) #endif } - /* * This routine is called by tty_hangup() when a hangup is signaled. */ -void mxser_hangup(struct tty_struct *tty) +static void mxser_hangup(struct tty_struct *tty) { - struct mxser_struct *info = tty->driver_data; + struct mxser_port *info = tty->driver_data; mxser_flush_buffer(tty); mxser_shutdown(info); @@ -1863,231 +2012,73 @@ void mxser_hangup(struct tty_struct *tty) wake_up_interruptible(&info->open_wait); } - -/* added by James 03-12-2004. */ /* * mxser_rs_break() --- routine which turns the break handling on or off */ static void mxser_rs_break(struct tty_struct *tty, int break_state) { - struct mxser_struct *info = tty->driver_data; + struct mxser_port *info = tty->driver_data; unsigned long flags; spin_lock_irqsave(&info->slock, flags); if (break_state == -1) - outb(inb(info->base + UART_LCR) | UART_LCR_SBC, - info->base + UART_LCR); + outb(inb(info->ioaddr + UART_LCR) | UART_LCR_SBC, + info->ioaddr + UART_LCR); else - outb(inb(info->base + UART_LCR) & ~UART_LCR_SBC, - info->base + UART_LCR); + outb(inb(info->ioaddr + UART_LCR) & ~UART_LCR_SBC, + info->ioaddr + UART_LCR); spin_unlock_irqrestore(&info->slock, flags); } -/* (above) added by James. */ - - -/* - * This is the serial driver's generic interrupt routine - */ -static irqreturn_t mxser_interrupt(int irq, void *dev_id) +static void mxser_receive_chars(struct mxser_port *port, int *status) { - int status, iir, i; - struct mxser_struct *info; - struct mxser_struct *port; - int max, irqbits, bits, msr; - int pass_counter = 0; - int handled = IRQ_NONE; - - port = NULL; - /* spin_lock(&gm_lock); */ - - for (i = 0; i < MXSER_BOARDS; i++) { - if (dev_id == &(mxvar_table[i * MXSER_PORTS_PER_BOARD])) { - port = dev_id; - break; - } - } - - if (i == MXSER_BOARDS) - goto irq_stop; - if (port == 0) - goto irq_stop; - max = mxser_numports[mxsercfg[i].board_type - 1]; - while (1) { - irqbits = inb(port->vector) & port->vectormask; - if (irqbits == port->vectormask) - break; - - handled = IRQ_HANDLED; - for (i = 0, bits = 1; i < max; i++, irqbits |= bits, bits <<= 1) { - if (irqbits == port->vectormask) - break; - if (bits & irqbits) - continue; - info = port + i; - - /* following add by Victor Yu. 09-13-2002 */ - iir = inb(info->base + UART_IIR); - if (iir & UART_IIR_NO_INT) - continue; - iir &= MOXA_MUST_IIR_MASK; - if (!info->tty) { - status = inb(info->base + UART_LSR); - outb(0x27, info->base + UART_FCR); - inb(info->base + UART_MSR); - continue; - } - - /* mask by Victor Yu. 09-13-2002 - if ( !info->tty || - (inb(info->base + UART_IIR) & UART_IIR_NO_INT) ) - continue; - */ - /* mask by Victor Yu. 09-02-2002 - status = inb(info->base + UART_LSR) & info->read_status_mask; - */ - - /* following add by Victor Yu. 09-02-2002 */ - status = inb(info->base + UART_LSR); - - if (status & UART_LSR_PE) - info->err_shadow |= NPPI_NOTIFY_PARITY; - if (status & UART_LSR_FE) - info->err_shadow |= NPPI_NOTIFY_FRAMING; - if (status & UART_LSR_OE) - info->err_shadow |= NPPI_NOTIFY_HW_OVERRUN; - if (status & UART_LSR_BI) - info->err_shadow |= NPPI_NOTIFY_BREAK; - - if (info->IsMoxaMustChipFlag) { - /* - if ( (status & 0x02) && !(status & 0x01) ) { - outb(info->base+UART_FCR, 0x23); - continue; - } - */ - if (iir == MOXA_MUST_IIR_GDA || - iir == MOXA_MUST_IIR_RDA || - iir == MOXA_MUST_IIR_RTO || - iir == MOXA_MUST_IIR_LSR) - mxser_receive_chars(info, &status); - - } else { - /* above add by Victor Yu. 09-02-2002 */ - - status &= info->read_status_mask; - if (status & UART_LSR_DR) - mxser_receive_chars(info, &status); - } - msr = inb(info->base + UART_MSR); - if (msr & UART_MSR_ANY_DELTA) { - mxser_check_modem_status(info, msr); - } - /* following add by Victor Yu. 09-13-2002 */ - if (info->IsMoxaMustChipFlag) { - if ((iir == 0x02) && (status & UART_LSR_THRE)) { - mxser_transmit_chars(info); - } - } else { - /* above add by Victor Yu. 09-13-2002 */ - - if (status & UART_LSR_THRE) { -/* 8-2-99 by William - if ( info->x_char || (info->xmit_cnt > 0) ) -*/ - mxser_transmit_chars(info); - } - } - } - if (pass_counter++ > MXSER_ISR_PASS_LIMIT) { - break; /* Prevent infinite loops */ - } - } - - irq_stop: - /* spin_unlock(&gm_lock); */ - return handled; -} - -static void mxser_receive_chars(struct mxser_struct *info, int *status) -{ - struct tty_struct *tty = info->tty; + struct tty_struct *tty = port->tty; unsigned char ch, gdl; int ignored = 0; int cnt = 0; int recv_room; int max = 256; - unsigned long flags; - - spin_lock_irqsave(&info->slock, flags); recv_room = tty->receive_room; - if ((recv_room == 0) && (!info->ldisc_stop_rx)) { - /* mxser_throttle(tty); */ + if ((recv_room == 0) && (!port->ldisc_stop_rx)) mxser_stoprx(tty); - /* return; */ - } - /* following add by Victor Yu. 09-02-2002 */ - if (info->IsMoxaMustChipFlag != MOXA_OTHER_UART) { + if (port->board->chip_flag != MOXA_OTHER_UART) { - if (*status & UART_LSR_SPECIAL) { + if (*status & UART_LSR_SPECIAL) goto intr_old; - } - /* following add by Victor Yu. 02-11-2004 */ - if (info->IsMoxaMustChipFlag == MOXA_MUST_MU860_HWID && + if (port->board->chip_flag == MOXA_MUST_MU860_HWID && (*status & MOXA_MUST_LSR_RERR)) goto intr_old; - /* above add by Victor Yu. 02-14-2004 */ if (*status & MOXA_MUST_LSR_RERR) goto intr_old; - gdl = inb(info->base + MOXA_MUST_GDL_REGISTER); + gdl = inb(port->ioaddr + MOXA_MUST_GDL_REGISTER); - /* add by Victor Yu. 02-11-2004 */ - if (info->IsMoxaMustChipFlag == MOXA_MUST_MU150_HWID) + if (port->board->chip_flag == MOXA_MUST_MU150_HWID) gdl &= MOXA_MUST_GDL_MASK; if (gdl >= recv_room) { - if (!info->ldisc_stop_rx) { - /* mxser_throttle(tty); */ + if (!port->ldisc_stop_rx) mxser_stoprx(tty); - } - /* return; */ } while (gdl--) { - ch = inb(info->base + UART_RX); + ch = inb(port->ioaddr + UART_RX); tty_insert_flip_char(tty, ch, 0); cnt++; - /* - if ((cnt >= HI_WATER) && (info->stop_rx == 0)) { - mxser_stoprx(tty); - info->stop_rx = 1; - break; - } */ } goto end_intr; } - intr_old: - /* above add by Victor Yu. 09-02-2002 */ +intr_old: do { if (max-- < 0) break; - /* - if ((cnt >= HI_WATER) && (info->stop_rx == 0)) { - mxser_stoprx(tty); - info->stop_rx=1; - break; - } - */ - ch = inb(info->base + UART_RX); - /* following add by Victor Yu. 09-02-2002 */ - if (info->IsMoxaMustChipFlag && (*status & UART_LSR_OE) /*&& !(*status&UART_LSR_DR) */ ) - outb(0x23, info->base + UART_FCR); - *status &= info->read_status_mask; - /* above add by Victor Yu. 09-02-2002 */ - if (*status & info->ignore_status_mask) { + ch = inb(port->ioaddr + UART_RX); + if (port->board->chip_flag && (*status & UART_LSR_OE)) + outb(0x23, port->ioaddr + UART_FCR); + *status &= port->read_status_mask; + if (*status & port->ignore_status_mask) { if (++ignored > 100) break; } else { @@ -2095,1038 +2086,652 @@ static void mxser_receive_chars(struct mxser_struct *info, int *status) if (*status & UART_LSR_SPECIAL) { if (*status & UART_LSR_BI) { flag = TTY_BREAK; -/* added by casper 1/11/2000 */ - info->icount.brk++; -/* */ - if (info->flags & ASYNC_SAK) + port->icount.brk++; + + if (port->flags & ASYNC_SAK) do_SAK(tty); } else if (*status & UART_LSR_PE) { flag = TTY_PARITY; -/* added by casper 1/11/2000 */ - info->icount.parity++; -/* */ + port->icount.parity++; } else if (*status & UART_LSR_FE) { flag = TTY_FRAME; -/* added by casper 1/11/2000 */ - info->icount.frame++; -/* */ + port->icount.frame++; } else if (*status & UART_LSR_OE) { flag = TTY_OVERRUN; -/* added by casper 1/11/2000 */ - info->icount.overrun++; -/* */ - } + port->icount.overrun++; + } else + flag = TTY_BREAK; } tty_insert_flip_char(tty, ch, flag); cnt++; if (cnt >= recv_room) { - if (!info->ldisc_stop_rx) { - /* mxser_throttle(tty); */ + if (!port->ldisc_stop_rx) mxser_stoprx(tty); - } break; } } - /* following add by Victor Yu. 09-02-2002 */ - if (info->IsMoxaMustChipFlag) + if (port->board->chip_flag) break; - /* above add by Victor Yu. 09-02-2002 */ - /* mask by Victor Yu. 09-02-2002 - *status = inb(info->base + UART_LSR) & info->read_status_mask; - */ - /* following add by Victor Yu. 09-02-2002 */ - *status = inb(info->base + UART_LSR); - /* above add by Victor Yu. 09-02-2002 */ + *status = inb(port->ioaddr + UART_LSR); } while (*status & UART_LSR_DR); -end_intr: /* add by Victor Yu. 09-02-2002 */ - mxvar_log.rxcnt[info->port] += cnt; - info->mon_data.rxcnt += cnt; - info->mon_data.up_rxcnt += cnt; - spin_unlock_irqrestore(&info->slock, flags); +end_intr: + mxvar_log.rxcnt[port->tty->index] += cnt; + port->mon_data.rxcnt += cnt; + port->mon_data.up_rxcnt += cnt; + /* + * We are called from an interrupt context with &port->slock + * being held. Drop it temporarily in order to prevent + * recursive locking. + */ + spin_unlock(&port->slock); tty_flip_buffer_push(tty); + spin_lock(&port->slock); } -static void mxser_transmit_chars(struct mxser_struct *info) +static void mxser_transmit_chars(struct mxser_port *port) { int count, cnt; - unsigned long flags; - spin_lock_irqsave(&info->slock, flags); - - if (info->x_char) { - outb(info->x_char, info->base + UART_TX); - info->x_char = 0; - mxvar_log.txcnt[info->port]++; - info->mon_data.txcnt++; - info->mon_data.up_txcnt++; - -/* added by casper 1/11/2000 */ - info->icount.tx++; -/* */ - spin_unlock_irqrestore(&info->slock, flags); + if (port->x_char) { + outb(port->x_char, port->ioaddr + UART_TX); + port->x_char = 0; + mxvar_log.txcnt[port->tty->index]++; + port->mon_data.txcnt++; + port->mon_data.up_txcnt++; + port->icount.tx++; return; } - if (info->xmit_buf == 0) { - spin_unlock_irqrestore(&info->slock, flags); + if (port->xmit_buf == NULL) return; - } - if ((info->xmit_cnt <= 0) || info->tty->stopped || - (info->tty->hw_stopped && - (info->type != PORT_16550A) && - (!info->IsMoxaMustChipFlag))) { - info->IER &= ~UART_IER_THRI; - outb(info->IER, info->base + UART_IER); - spin_unlock_irqrestore(&info->slock, flags); + if ((port->xmit_cnt <= 0) || port->tty->stopped || + (port->tty->hw_stopped && + (port->type != PORT_16550A) && + (!port->board->chip_flag))) { + port->IER &= ~UART_IER_THRI; + outb(port->IER, port->ioaddr + UART_IER); return; } - cnt = info->xmit_cnt; - count = info->xmit_fifo_size; + cnt = port->xmit_cnt; + count = port->xmit_fifo_size; do { - outb(info->xmit_buf[info->xmit_tail++], - info->base + UART_TX); - info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE - 1); - if (--info->xmit_cnt <= 0) + outb(port->xmit_buf[port->xmit_tail++], + port->ioaddr + UART_TX); + port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE - 1); + if (--port->xmit_cnt <= 0) break; } while (--count > 0); - mxvar_log.txcnt[info->port] += (cnt - info->xmit_cnt); + mxvar_log.txcnt[port->tty->index] += (cnt - port->xmit_cnt); -/* added by James 03-12-2004. */ - info->mon_data.txcnt += (cnt - info->xmit_cnt); - info->mon_data.up_txcnt += (cnt - info->xmit_cnt); -/* (above) added by James. */ + port->mon_data.txcnt += (cnt - port->xmit_cnt); + port->mon_data.up_txcnt += (cnt - port->xmit_cnt); + port->icount.tx += (cnt - port->xmit_cnt); -/* added by casper 1/11/2000 */ - info->icount.tx += (cnt - info->xmit_cnt); -/* */ + if (port->xmit_cnt < WAKEUP_CHARS) + tty_wakeup(port->tty); - if (info->xmit_cnt < WAKEUP_CHARS) { - set_bit(MXSER_EVENT_TXLOW, &info->event); - schedule_work(&info->tqueue); + if (port->xmit_cnt <= 0) { + port->IER &= ~UART_IER_THRI; + outb(port->IER, port->ioaddr + UART_IER); } - if (info->xmit_cnt <= 0) { - info->IER &= ~UART_IER_THRI; - outb(info->IER, info->base + UART_IER); - } - spin_unlock_irqrestore(&info->slock, flags); } -static void mxser_check_modem_status(struct mxser_struct *info, int status) +/* + * This is the serial driver's generic interrupt routine + */ +static irqreturn_t mxser_interrupt(int irq, void *dev_id) { - /* update input line counters */ - if (status & UART_MSR_TERI) - info->icount.rng++; - if (status & UART_MSR_DDSR) - info->icount.dsr++; - if (status & UART_MSR_DDCD) - info->icount.dcd++; - if (status & UART_MSR_DCTS) - info->icount.cts++; - info->mon_data.modem_status = status; - wake_up_interruptible(&info->delta_msr_wait); - - if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { - if (status & UART_MSR_DCD) - wake_up_interruptible(&info->open_wait); - schedule_work(&info->tqueue); - } - - if (info->flags & ASYNC_CTS_FLOW) { - if (info->tty->hw_stopped) { - if (status & UART_MSR_CTS) { - info->tty->hw_stopped = 0; + int status, iir, i; + struct mxser_board *brd = NULL; + struct mxser_port *port; + int max, irqbits, bits, msr; + unsigned int int_cnt, pass_counter = 0; + int handled = IRQ_NONE; - if ((info->type != PORT_16550A) && - (!info->IsMoxaMustChipFlag)) { - info->IER |= UART_IER_THRI; - outb(info->IER, info->base + UART_IER); - } - set_bit(MXSER_EVENT_TXLOW, &info->event); - schedule_work(&info->tqueue); } - } else { - if (!(status & UART_MSR_CTS)) { - info->tty->hw_stopped = 1; - if ((info->type != PORT_16550A) && - (!info->IsMoxaMustChipFlag)) { - info->IER &= ~UART_IER_THRI; - outb(info->IER, info->base + UART_IER); - } - } + for (i = 0; i < MXSER_BOARDS; i++) + if (dev_id == &mxser_boards[i]) { + brd = dev_id; + break; } - } -} -static int mxser_block_til_ready(struct tty_struct *tty, struct file *filp, struct mxser_struct *info) -{ - DECLARE_WAITQUEUE(wait, current); - int retval; - int do_clocal = 0; - unsigned long flags; - - /* - * If non-blocking mode is set, or the port is not enabled, - * then make the check up front and then exit. - */ - if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { - info->flags |= ASYNC_NORMAL_ACTIVE; - return 0; - } + if (i == MXSER_BOARDS) + goto irq_stop; + if (brd == NULL) + goto irq_stop; + max = brd->info->nports; + while (pass_counter++ < MXSER_ISR_PASS_LIMIT) { + irqbits = inb(brd->vector) & brd->vector_mask; + if (irqbits == brd->vector_mask) + break; - if (tty->termios->c_cflag & CLOCAL) - do_clocal = 1; + handled = IRQ_HANDLED; + for (i = 0, bits = 1; i < max; i++, irqbits |= bits, bits <<= 1) { + if (irqbits == brd->vector_mask) + break; + if (bits & irqbits) + continue; + port = &brd->ports[i]; + + int_cnt = 0; + spin_lock(&port->slock); + do { + iir = inb(port->ioaddr + UART_IIR); + if (iir & UART_IIR_NO_INT) + break; + iir &= MOXA_MUST_IIR_MASK; + if (!port->tty || + (port->flags & ASYNC_CLOSING) || + !(port->flags & + ASYNC_INITIALIZED)) { + status = inb(port->ioaddr + UART_LSR); + outb(0x27, port->ioaddr + UART_FCR); + inb(port->ioaddr + UART_MSR); + break; + } - /* - * Block waiting for the carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, info->count is dropped by one, so that - * mxser_close() knows when to free things. We restore it upon - * exit, either normal or abnormal. - */ - retval = 0; - add_wait_queue(&info->open_wait, &wait); + status = inb(port->ioaddr + UART_LSR); + + if (status & UART_LSR_PE) + port->err_shadow |= NPPI_NOTIFY_PARITY; + if (status & UART_LSR_FE) + port->err_shadow |= NPPI_NOTIFY_FRAMING; + if (status & UART_LSR_OE) + port->err_shadow |= + NPPI_NOTIFY_HW_OVERRUN; + if (status & UART_LSR_BI) + port->err_shadow |= NPPI_NOTIFY_BREAK; + + if (port->board->chip_flag) { + if (iir == MOXA_MUST_IIR_GDA || + iir == MOXA_MUST_IIR_RDA || + iir == MOXA_MUST_IIR_RTO || + iir == MOXA_MUST_IIR_LSR) + mxser_receive_chars(port, + &status); - spin_lock_irqsave(&info->slock, flags); - if (!tty_hung_up_p(filp)) - info->count--; - spin_unlock_irqrestore(&info->slock, flags); - info->blocked_open++; - while (1) { - spin_lock_irqsave(&info->slock, flags); - outb(inb(info->base + UART_MCR) | - UART_MCR_DTR | UART_MCR_RTS, info->base + UART_MCR); - spin_unlock_irqrestore(&info->slock, flags); - set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED)) { - if (info->flags & ASYNC_HUP_NOTIFY) - retval = -EAGAIN; - else - retval = -ERESTARTSYS; - break; - } - if (!(info->flags & ASYNC_CLOSING) && - (do_clocal || - (inb(info->base + UART_MSR) & UART_MSR_DCD))) - break; - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; + } else { + status &= port->read_status_mask; + if (status & UART_LSR_DR) + mxser_receive_chars(port, + &status); + } + msr = inb(port->ioaddr + UART_MSR); + if (msr & UART_MSR_ANY_DELTA) + mxser_check_modem_status(port, msr); + + if (port->board->chip_flag) { + if (iir == 0x02 && (status & + UART_LSR_THRE)) + mxser_transmit_chars(port); + } else { + if (status & UART_LSR_THRE) + mxser_transmit_chars(port); + } + } while (int_cnt++ < MXSER_ISR_PASS_LIMIT); + spin_unlock(&port->slock); } - schedule(); } - set_current_state(TASK_RUNNING); - remove_wait_queue(&info->open_wait, &wait); - if (!tty_hung_up_p(filp)) - info->count++; - info->blocked_open--; - if (retval) - return retval; - info->flags |= ASYNC_NORMAL_ACTIVE; - return 0; -} -static int mxser_startup(struct mxser_struct *info) -{ - unsigned long page; - unsigned long flags; - - page = __get_free_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - - spin_lock_irqsave(&info->slock, flags); - - if (info->flags & ASYNC_INITIALIZED) { - free_page(page); - spin_unlock_irqrestore(&info->slock, flags); - return 0; - } +irq_stop: + return handled; +} - if (!info->base || !info->type) { - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); - free_page(page); - spin_unlock_irqrestore(&info->slock, flags); - return 0; - } - if (info->xmit_buf) - free_page(page); - else - info->xmit_buf = (unsigned char *) page; +static const struct tty_operations mxser_ops = { + .open = mxser_open, + .close = mxser_close, + .write = mxser_write, + .put_char = mxser_put_char, + .flush_chars = mxser_flush_chars, + .write_room = mxser_write_room, + .chars_in_buffer = mxser_chars_in_buffer, + .flush_buffer = mxser_flush_buffer, + .ioctl = mxser_ioctl, + .throttle = mxser_throttle, + .unthrottle = mxser_unthrottle, + .set_termios = mxser_set_termios, + .stop = mxser_stop, + .start = mxser_start, + .hangup = mxser_hangup, + .break_ctl = mxser_rs_break, + .wait_until_sent = mxser_wait_until_sent, + .tiocmget = mxser_tiocmget, + .tiocmset = mxser_tiocmset, +}; - /* - * Clear the FIFO buffers and disable them - * (they will be reenabled in mxser_change_speed()) - */ - if (info->IsMoxaMustChipFlag) - outb((UART_FCR_CLEAR_RCVR | - UART_FCR_CLEAR_XMIT | - MOXA_MUST_FCR_GDA_MODE_ENABLE), info->base + UART_FCR); - else - outb((UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT), - info->base + UART_FCR); +/* + * The MOXA Smartio/Industio serial driver boot-time initialization code! + */ - /* - * At this point there's no way the LSR could still be 0xFF; - * if it is, then bail out, because there's likely no UART - * here. - */ - if (inb(info->base + UART_LSR) == 0xff) { - spin_unlock_irqrestore(&info->slock, flags); - if (capable(CAP_SYS_ADMIN)) { - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); - return 0; - } else - return -ENODEV; +static void mxser_release_res(struct mxser_board *brd, struct pci_dev *pdev, + unsigned int irq) +{ + if (irq) + free_irq(brd->irq, brd); + if (pdev != NULL) { /* PCI */ +#ifdef CONFIG_PCI + pci_release_region(pdev, 2); + pci_release_region(pdev, 3); +#endif + } else { + release_region(brd->ports[0].ioaddr, 8 * brd->info->nports); + release_region(brd->vector, 1); } - - /* - * Clear the interrupt registers. - */ - (void) inb(info->base + UART_LSR); - (void) inb(info->base + UART_RX); - (void) inb(info->base + UART_IIR); - (void) inb(info->base + UART_MSR); - - /* - * Now, initialize the UART - */ - outb(UART_LCR_WLEN8, info->base + UART_LCR); /* reset DLAB */ - info->MCR = UART_MCR_DTR | UART_MCR_RTS; - outb(info->MCR, info->base + UART_MCR); - - /* - * Finally, enable interrupts - */ - info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; - /* info->IER = UART_IER_RLSI | UART_IER_RDI; */ - - /* following add by Victor Yu. 08-30-2002 */ - if (info->IsMoxaMustChipFlag) - info->IER |= MOXA_MUST_IER_EGDAI; - /* above add by Victor Yu. 08-30-2002 */ - outb(info->IER, info->base + UART_IER); /* enable interrupts */ - - /* - * And clear the interrupt registers again for luck. - */ - (void) inb(info->base + UART_LSR); - (void) inb(info->base + UART_RX); - (void) inb(info->base + UART_IIR); - (void) inb(info->base + UART_MSR); - - if (info->tty) - clear_bit(TTY_IO_ERROR, &info->tty->flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - - /* - * and set the speed of the serial port - */ - spin_unlock_irqrestore(&info->slock, flags); - mxser_change_speed(info, NULL); - - info->flags |= ASYNC_INITIALIZED; - return 0; } -/* - * This routine will shutdown a serial port; interrupts maybe disabled, and - * DTR is dropped if the hangup on close termio flag is on. - */ -static void mxser_shutdown(struct mxser_struct *info) +static int __devinit mxser_initbrd(struct mxser_board *brd, + struct pci_dev *pdev) { - unsigned long flags; - - if (!(info->flags & ASYNC_INITIALIZED)) - return; - - spin_lock_irqsave(&info->slock, flags); - - /* - * clear delta_msr_wait queue to avoid mem leaks: we may free the irq - * here so the queue might never be waken up - */ - wake_up_interruptible(&info->delta_msr_wait); - - /* - * Free the IRQ, if necessary - */ - if (info->xmit_buf) { - free_page((unsigned long) info->xmit_buf); - info->xmit_buf = NULL; - } + struct mxser_port *info; + unsigned int i; + int retval; - info->IER = 0; - outb(0x00, info->base + UART_IER); + printk(KERN_INFO "max. baud rate = %d bps.\n", brd->ports[0].max_baud); - if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) - info->MCR &= ~(UART_MCR_DTR | UART_MCR_RTS); - outb(info->MCR, info->base + UART_MCR); + for (i = 0; i < brd->info->nports; i++) { + info = &brd->ports[i]; + info->board = brd; + info->stop_rx = 0; + info->ldisc_stop_rx = 0; - /* clear Rx/Tx FIFO's */ - /* following add by Victor Yu. 08-30-2002 */ - if (info->IsMoxaMustChipFlag) - outb((UART_FCR_CLEAR_RCVR | - UART_FCR_CLEAR_XMIT | - MOXA_MUST_FCR_GDA_MODE_ENABLE), info->base + UART_FCR); - else - /* above add by Victor Yu. 08-30-2002 */ - outb((UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT), - info->base + UART_FCR); + /* Enhance mode enabled here */ + if (brd->chip_flag != MOXA_OTHER_UART) + ENABLE_MOXA_MUST_ENCHANCE_MODE(info->ioaddr); - /* read data port to reset things */ - (void) inb(info->base + UART_RX); + info->flags = ASYNC_SHARE_IRQ; + info->type = brd->uart_type; - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); + process_txrx_fifo(info); - info->flags &= ~ASYNC_INITIALIZED; + info->custom_divisor = info->baud_base * 16; + info->close_delay = 5 * HZ / 10; + info->closing_wait = 30 * HZ; + info->normal_termios = mxvar_sdriver->init_termios; + init_waitqueue_head(&info->open_wait); + init_waitqueue_head(&info->delta_msr_wait); + memset(&info->mon_data, 0, sizeof(struct mxser_mon)); + info->err_shadow = 0; + spin_lock_init(&info->slock); - /* following add by Victor Yu. 09-23-2002 */ - if (info->IsMoxaMustChipFlag) - SET_MOXA_MUST_NO_SOFTWARE_FLOW_CONTROL(info->base); - /* above add by Victor Yu. 09-23-2002 */ + /* before set INT ISR, disable all int */ + outb(inb(info->ioaddr + UART_IER) & 0xf0, + info->ioaddr + UART_IER); + } - spin_unlock_irqrestore(&info->slock, flags); + retval = request_irq(brd->irq, mxser_interrupt, IRQF_SHARED, "mxser", + brd); + if (retval) { + printk(KERN_ERR "Board %s: Request irq failed, IRQ (%d) may " + "conflict with another device.\n", + brd->info->name, brd->irq); + /* We hold resources, we need to release them. */ + mxser_release_res(brd, pdev, 0); + } + return retval; } -/* - * This routine is called to set the UART divisor registers to match - * the specified baud rate for a serial port. - */ -static int mxser_change_speed(struct mxser_struct *info, struct ktermios *old_termios) +static int __init mxser_get_ISA_conf(int cap, struct mxser_board *brd) { - unsigned cflag, cval, fcr; - int ret = 0; - unsigned char status; - long baud; - unsigned long flags; - - if (!info->tty || !info->tty->termios) - return ret; - cflag = info->tty->termios->c_cflag; - if (!(info->base)) - return ret; + int id, i, bits; + unsigned short regs[16], irq; + unsigned char scratch, scratch2; -#ifndef B921600 -#define B921600 (B460800 +1) -#endif - if (mxser_set_baud_method[info->port] == 0) { - baud = tty_get_baud_rate(info->tty); - mxser_set_baud(info, baud); - } + brd->chip_flag = MOXA_OTHER_UART; - /* byte size and parity */ - switch (cflag & CSIZE) { - case CS5: - cval = 0x00; + id = mxser_read_register(cap, regs); + switch (id) { + case C168_ASIC_ID: + brd->info = &mxser_cards[0]; break; - case CS6: - cval = 0x01; + case C104_ASIC_ID: + brd->info = &mxser_cards[1]; break; - case CS7: - cval = 0x02; + case CI104J_ASIC_ID: + brd->info = &mxser_cards[2]; break; - case CS8: - cval = 0x03; + case C102_ASIC_ID: + brd->info = &mxser_cards[5]; + break; + case CI132_ASIC_ID: + brd->info = &mxser_cards[6]; + break; + case CI134_ASIC_ID: + brd->info = &mxser_cards[7]; break; default: - cval = 0x00; - break; /* too keep GCC shut... */ + return 0; } - if (cflag & CSTOPB) - cval |= 0x04; - if (cflag & PARENB) - cval |= UART_LCR_PARITY; - if (!(cflag & PARODD)) - cval |= UART_LCR_EPAR; - if (cflag & CMSPAR) - cval |= UART_LCR_SPAR; - if ((info->type == PORT_8250) || (info->type == PORT_16450)) { - if (info->IsMoxaMustChipFlag) { - fcr = UART_FCR_ENABLE_FIFO; - fcr |= MOXA_MUST_FCR_GDA_MODE_ENABLE; - SET_MOXA_MUST_FIFO_VALUE(info); - } else - fcr = 0; - } else { - fcr = UART_FCR_ENABLE_FIFO; - /* following add by Victor Yu. 08-30-2002 */ - if (info->IsMoxaMustChipFlag) { - fcr |= MOXA_MUST_FCR_GDA_MODE_ENABLE; - SET_MOXA_MUST_FIFO_VALUE(info); - } else { - /* above add by Victor Yu. 08-30-2002 */ - switch (info->rx_trigger) { - case 1: - fcr |= UART_FCR_TRIGGER_1; - break; - case 4: - fcr |= UART_FCR_TRIGGER_4; - break; - case 8: - fcr |= UART_FCR_TRIGGER_8; - break; - default: - fcr |= UART_FCR_TRIGGER_14; - break; - } - } + irq = 0; + /* some ISA cards have 2 ports, but we want to see them as 4-port (why?) + Flag-hack checks if configuration should be read as 2-port here. */ + if (brd->info->nports == 2 || (brd->info->flags & MXSER_HAS2)) { + irq = regs[9] & 0xF000; + irq = irq | (irq >> 4); + if (irq != (regs[9] & 0xFF00)) + return MXSER_ERR_IRQ_CONFLIT; + } else if (brd->info->nports == 4) { + irq = regs[9] & 0xF000; + irq = irq | (irq >> 4); + irq = irq | (irq >> 8); + if (irq != regs[9]) + return MXSER_ERR_IRQ_CONFLIT; + } else if (brd->info->nports == 8) { + irq = regs[9] & 0xF000; + irq = irq | (irq >> 4); + irq = irq | (irq >> 8); + if ((irq != regs[9]) || (irq != regs[10])) + return MXSER_ERR_IRQ_CONFLIT; } - /* CTS flow control flag and modem status interrupts */ - info->IER &= ~UART_IER_MSI; - info->MCR &= ~UART_MCR_AFE; - if (cflag & CRTSCTS) { - info->flags |= ASYNC_CTS_FLOW; - info->IER |= UART_IER_MSI; - if ((info->type == PORT_16550A) || (info->IsMoxaMustChipFlag)) { - info->MCR |= UART_MCR_AFE; + if (!irq) + return MXSER_ERR_IRQ; + brd->irq = ((int)(irq & 0xF000) >> 12); + for (i = 0; i < 8; i++) + brd->ports[i].ioaddr = (int) regs[i + 1] & 0xFFF8; + if ((regs[12] & 0x80) == 0) + return MXSER_ERR_VECTOR; + brd->vector = (int)regs[11]; /* interrupt vector */ + if (id == 1) + brd->vector_mask = 0x00FF; + else + brd->vector_mask = 0x000F; + for (i = 7, bits = 0x0100; i >= 0; i--, bits <<= 1) { + if (regs[12] & bits) { + brd->ports[i].baud_base = 921600; + brd->ports[i].max_baud = 921600; } else { - status = inb(info->base + UART_MSR); - if (info->tty->hw_stopped) { - if (status & UART_MSR_CTS) { - info->tty->hw_stopped = 0; - if ((info->type != PORT_16550A) && - (!info->IsMoxaMustChipFlag)) { - info->IER |= UART_IER_THRI; - outb(info->IER, info->base + UART_IER); - } - set_bit(MXSER_EVENT_TXLOW, &info->event); - schedule_work(&info->tqueue); } - } else { - if (!(status & UART_MSR_CTS)) { - info->tty->hw_stopped = 1; - if ((info->type != PORT_16550A) && - (!info->IsMoxaMustChipFlag)) { - info->IER &= ~UART_IER_THRI; - outb(info->IER, info->base + UART_IER); - } - } - } + brd->ports[i].baud_base = 115200; + brd->ports[i].max_baud = 115200; } - } else { - info->flags &= ~ASYNC_CTS_FLOW; - } - outb(info->MCR, info->base + UART_MCR); - if (cflag & CLOCAL) { - info->flags &= ~ASYNC_CHECK_CD; - } else { - info->flags |= ASYNC_CHECK_CD; - info->IER |= UART_IER_MSI; } - outb(info->IER, info->base + UART_IER); - - /* - * Set up parity check flag - */ - info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; - if (I_INPCK(info->tty)) - info->read_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) - info->read_status_mask |= UART_LSR_BI; - - info->ignore_status_mask = 0; + scratch2 = inb(cap + UART_LCR) & (~UART_LCR_DLAB); + outb(scratch2 | UART_LCR_DLAB, cap + UART_LCR); + outb(0, cap + UART_EFR); /* EFR is the same as FCR */ + outb(scratch2, cap + UART_LCR); + outb(UART_FCR_ENABLE_FIFO, cap + UART_FCR); + scratch = inb(cap + UART_IIR); - if (I_IGNBRK(info->tty)) { - info->ignore_status_mask |= UART_LSR_BI; - info->read_status_mask |= UART_LSR_BI; - /* - * If we're ignore parity and break indicators, ignore - * overruns too. (For real raw support). - */ - if (I_IGNPAR(info->tty)) { - info->ignore_status_mask |= - UART_LSR_OE | - UART_LSR_PE | - UART_LSR_FE; - info->read_status_mask |= - UART_LSR_OE | - UART_LSR_PE | - UART_LSR_FE; - } - } - /* following add by Victor Yu. 09-02-2002 */ - if (info->IsMoxaMustChipFlag) { - spin_lock_irqsave(&info->slock, flags); - SET_MOXA_MUST_XON1_VALUE(info->base, START_CHAR(info->tty)); - SET_MOXA_MUST_XOFF1_VALUE(info->base, STOP_CHAR(info->tty)); - if (I_IXON(info->tty)) { - ENABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL(info->base); - } else { - DISABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL(info->base); - } - if (I_IXOFF(info->tty)) { - ENABLE_MOXA_MUST_TX_SOFTWARE_FLOW_CONTROL(info->base); - } else { - DISABLE_MOXA_MUST_TX_SOFTWARE_FLOW_CONTROL(info->base); - } - /* - if ( I_IXANY(info->tty) ) { - info->MCR |= MOXA_MUST_MCR_XON_ANY; - ENABLE_MOXA_MUST_XON_ANY_FLOW_CONTROL(info->base); - } else { - info->MCR &= ~MOXA_MUST_MCR_XON_ANY; - DISABLE_MOXA_MUST_XON_ANY_FLOW_CONTROL(info->base); - } - */ - spin_unlock_irqrestore(&info->slock, flags); + if (scratch & 0xC0) + brd->uart_type = PORT_16550A; + else + brd->uart_type = PORT_16450; + if (!request_region(brd->ports[0].ioaddr, 8 * brd->info->nports, + "mxser(IO)")) + return MXSER_ERR_IOADDR; + if (!request_region(brd->vector, 1, "mxser(vector)")) { + release_region(brd->ports[0].ioaddr, 8 * brd->info->nports); + return MXSER_ERR_VECTOR; } - /* above add by Victor Yu. 09-02-2002 */ - - - outb(fcr, info->base + UART_FCR); /* set fcr */ - outb(cval, info->base + UART_LCR); - - return ret; + return brd->info->nports; } - -static int mxser_set_baud(struct mxser_struct *info, long newspd) +static int __devinit mxser_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) { - int quot = 0; - unsigned char cval; - int ret = 0; - unsigned long flags; - - if (!info->tty || !info->tty->termios) - return ret; - - if (!(info->base)) - return ret; +#ifdef CONFIG_PCI + struct mxser_board *brd; + unsigned int i, j; + unsigned long ioaddress; + int retval = -EINVAL; - if (newspd > info->MaxCanSetBaudRate) - return 0; + for (i = 0; i < MXSER_BOARDS; i++) + if (mxser_boards[i].info == NULL) + break; - info->realbaud = newspd; - if (newspd == 134) { - quot = (2 * info->baud_base / 269); - } else if (newspd) { - quot = info->baud_base / newspd; - if (quot == 0) - quot = 1; - } else { - quot = 0; + if (i >= MXSER_BOARDS) { + printk(KERN_ERR "Too many Smartio/Industio family boards found " + "(maximum %d), board not configured\n", MXSER_BOARDS); + goto err; } - info->timeout = ((info->xmit_fifo_size * HZ * 10 * quot) / info->baud_base); - info->timeout += HZ / 50; /* Add .02 seconds of slop */ + brd = &mxser_boards[i]; + brd->idx = i * MXSER_PORTS_PER_BOARD; + printk(KERN_INFO "Found MOXA %s board (BusNo=%d, DevNo=%d)\n", + mxser_cards[ent->driver_data].name, + pdev->bus->number, PCI_SLOT(pdev->devfn)); - if (quot) { - spin_lock_irqsave(&info->slock, flags); - info->MCR |= UART_MCR_DTR; - outb(info->MCR, info->base + UART_MCR); - spin_unlock_irqrestore(&info->slock, flags); - } else { - spin_lock_irqsave(&info->slock, flags); - info->MCR &= ~UART_MCR_DTR; - outb(info->MCR, info->base + UART_MCR); - spin_unlock_irqrestore(&info->slock, flags); - return ret; + retval = pci_enable_device(pdev); + if (retval) { + printk(KERN_ERR "Moxa SmartI/O PCI enable fail !\n"); + goto err; } - cval = inb(info->base + UART_LCR); - - outb(cval | UART_LCR_DLAB, info->base + UART_LCR); /* set DLAB */ - - outb(quot & 0xff, info->base + UART_DLL); /* LS of divisor */ - outb(quot >> 8, info->base + UART_DLM); /* MS of divisor */ - outb(cval, info->base + UART_LCR); /* reset DLAB */ - + /* io address */ + ioaddress = pci_resource_start(pdev, 2); + retval = pci_request_region(pdev, 2, "mxser(IO)"); + if (retval) + goto err; - return ret; -} + brd->info = &mxser_cards[ent->driver_data]; + for (i = 0; i < brd->info->nports; i++) + brd->ports[i].ioaddr = ioaddress + 8 * i; -/* - * ------------------------------------------------------------ - * friends of mxser_ioctl() - * ------------------------------------------------------------ - */ -static int mxser_get_serial_info(struct mxser_struct *info, struct serial_struct __user *retinfo) -{ - struct serial_struct tmp; + /* vector */ + ioaddress = pci_resource_start(pdev, 3); + retval = pci_request_region(pdev, 3, "mxser(vector)"); + if (retval) + goto err_relio; + brd->vector = ioaddress; - if (!retinfo) - return -EFAULT; - memset(&tmp, 0, sizeof(tmp)); - tmp.type = info->type; - tmp.line = info->port; - tmp.port = info->base; - tmp.irq = info->irq; - tmp.flags = info->flags; - tmp.baud_base = info->baud_base; - tmp.close_delay = info->close_delay; - tmp.closing_wait = info->closing_wait; - tmp.custom_divisor = info->custom_divisor; - tmp.hub6 = 0; - if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) - return -EFAULT; - return 0; -} + /* irq */ + brd->irq = pdev->irq; -static int mxser_set_serial_info(struct mxser_struct *info, struct serial_struct __user *new_info) -{ - struct serial_struct new_serial; - unsigned int flags; - int retval = 0; + brd->chip_flag = CheckIsMoxaMust(brd->ports[0].ioaddr); + brd->uart_type = PORT_16550A; + brd->vector_mask = 0; - if (!new_info || !info->base) - return -EFAULT; - if (copy_from_user(&new_serial, new_info, sizeof(new_serial))) - return -EFAULT; + for (i = 0; i < brd->info->nports; i++) { + for (j = 0; j < UART_INFO_NUM; j++) { + if (Gpci_uart_info[j].type == brd->chip_flag) { + brd->ports[i].max_baud = + Gpci_uart_info[j].max_baud; - if ((new_serial.irq != info->irq) || - (new_serial.port != info->base) || - (new_serial.custom_divisor != info->custom_divisor) || - (new_serial.baud_base != info->baud_base)) - return -EPERM; + /* exception....CP-102 */ + if (brd->info->flags & MXSER_HIGHBAUD) + brd->ports[i].max_baud = 921600; + break; + } + } + } - flags = info->flags & ASYNC_SPD_MASK; + if (brd->chip_flag == MOXA_MUST_MU860_HWID) { + for (i = 0; i < brd->info->nports; i++) { + if (i < 4) + brd->ports[i].opmode_ioaddr = ioaddress + 4; + else + brd->ports[i].opmode_ioaddr = ioaddress + 0x0c; + } + outb(0, ioaddress + 4); /* default set to RS232 mode */ + outb(0, ioaddress + 0x0c); /* default set to RS232 mode */ + } - if (!capable(CAP_SYS_ADMIN)) { - if ((new_serial.baud_base != info->baud_base) || - (new_serial.close_delay != info->close_delay) || - ((new_serial.flags & ~ASYNC_USR_MASK) != (info->flags & ~ASYNC_USR_MASK))) - return -EPERM; - info->flags = ((info->flags & ~ASYNC_USR_MASK) | - (new_serial.flags & ASYNC_USR_MASK)); - } else { - /* - * OK, past this point, all the error checking has been done. - * At this point, we start making changes..... - */ - info->flags = ((info->flags & ~ASYNC_FLAGS) | - (new_serial.flags & ASYNC_FLAGS)); - info->close_delay = new_serial.close_delay * HZ / 100; - info->closing_wait = new_serial.closing_wait * HZ / 100; - info->tty->low_latency = - (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; - info->tty->low_latency = 0; /* (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; */ + for (i = 0; i < brd->info->nports; i++) { + brd->vector_mask |= (1 << i); + brd->ports[i].baud_base = 921600; } - /* added by casper, 3/17/2000, for mouse */ - info->type = new_serial.type; + /* mxser_initbrd will hook ISR. */ + retval = mxser_initbrd(brd, pdev); + if (retval) + goto err_null; - process_txrx_fifo(info); + for (i = 0; i < brd->info->nports; i++) + tty_register_device(mxvar_sdriver, brd->idx + i, &pdev->dev); - if (info->flags & ASYNC_INITIALIZED) { - if (flags != (info->flags & ASYNC_SPD_MASK)) { - mxser_change_speed(info, NULL); - } - } else { - retval = mxser_startup(info); - } + pci_set_drvdata(pdev, brd); + + return 0; +err_relio: + pci_release_region(pdev, 2); +err_null: + brd->info = NULL; +err: return retval; +#else + return -ENODEV; +#endif } -/* - * mxser_get_lsr_info - get line status register info - * - * Purpose: Let user call ioctl() to get info when the UART physically - * is emptied. On bus types like RS485, the transmitter must - * release the bus after transmitting. This must be done when - * the transmit shift register is empty, not be done when the - * transmit holding register is empty. This functionality - * allows an RS485 driver to be written in user space. - */ -static int mxser_get_lsr_info(struct mxser_struct *info, unsigned int __user *value) +static void __devexit mxser_remove(struct pci_dev *pdev) { - unsigned char status; - unsigned int result; - unsigned long flags; + struct mxser_board *brd = pci_get_drvdata(pdev); + unsigned int i; - spin_lock_irqsave(&info->slock, flags); - status = inb(info->base + UART_LSR); - spin_unlock_irqrestore(&info->slock, flags); - result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); - return put_user(result, value); -} + for (i = 0; i < brd->info->nports; i++) + tty_unregister_device(mxvar_sdriver, brd->idx + i); -/* - * This routine sends a break character out the serial port. - */ -static void mxser_send_break(struct mxser_struct *info, int duration) -{ - unsigned long flags; - - if (!info->base) - return; - set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irqsave(&info->slock, flags); - outb(inb(info->base + UART_LCR) | UART_LCR_SBC, - info->base + UART_LCR); - spin_unlock_irqrestore(&info->slock, flags); - schedule_timeout(duration); - spin_lock_irqsave(&info->slock, flags); - outb(inb(info->base + UART_LCR) & ~UART_LCR_SBC, - info->base + UART_LCR); - spin_unlock_irqrestore(&info->slock, flags); + mxser_release_res(brd, pdev, 1); + brd->info = NULL; } -static int mxser_tiocmget(struct tty_struct *tty, struct file *file) -{ - struct mxser_struct *info = tty->driver_data; - unsigned char control, status; - unsigned long flags; +static struct pci_driver mxser_driver = { + .name = "mxser", + .id_table = mxser_pcibrds, + .probe = mxser_probe, + .remove = __devexit_p(mxser_remove) +}; +static int __init mxser_module_init(void) +{ + struct mxser_board *brd; + unsigned long cap; + unsigned int i, m, isaloop; + int retval, b; - if (tty->index == MXSER_PORTS) - return -ENOIOCTLCMD; - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; + pr_debug("Loading module mxser ...\n"); - control = info->MCR; + mxvar_sdriver = alloc_tty_driver(MXSER_PORTS + 1); + if (!mxvar_sdriver) + return -ENOMEM; - spin_lock_irqsave(&info->slock, flags); - status = inb(info->base + UART_MSR); - if (status & UART_MSR_ANY_DELTA) - mxser_check_modem_status(info, status); - spin_unlock_irqrestore(&info->slock, flags); - return ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) | - ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) | - ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) | - ((status & UART_MSR_RI) ? TIOCM_RNG : 0) | - ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) | - ((status & UART_MSR_CTS) ? TIOCM_CTS : 0); -} + printk(KERN_INFO "MOXA Smartio/Industio family driver version %s\n", + MXSER_VERSION); -static int mxser_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) -{ - struct mxser_struct *info = tty->driver_data; - unsigned long flags; + /* Initialize the tty_driver structure */ + mxvar_sdriver->owner = THIS_MODULE; + mxvar_sdriver->magic = TTY_DRIVER_MAGIC; + mxvar_sdriver->name = "ttyMI"; + mxvar_sdriver->major = ttymajor; + mxvar_sdriver->minor_start = 0; + mxvar_sdriver->num = MXSER_PORTS + 1; + mxvar_sdriver->type = TTY_DRIVER_TYPE_SERIAL; + mxvar_sdriver->subtype = SERIAL_TYPE_NORMAL; + mxvar_sdriver->init_termios = tty_std_termios; + mxvar_sdriver->init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL; + mxvar_sdriver->flags = TTY_DRIVER_REAL_RAW|TTY_DRIVER_DYNAMIC_DEV; + tty_set_operations(mxvar_sdriver, &mxser_ops); + retval = tty_register_driver(mxvar_sdriver); + if (retval) { + printk(KERN_ERR "Couldn't install MOXA Smartio/Industio family " + "tty driver !\n"); + goto err_put; + } - if (tty->index == MXSER_PORTS) - return -ENOIOCTLCMD; - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; + mxvar_diagflag = 0; - spin_lock_irqsave(&info->slock, flags); + m = 0; + /* Start finding ISA boards here */ + for (isaloop = 0; isaloop < 2; isaloop++) + for (b = 0; b < MXSER_BOARDS && m < MXSER_BOARDS; b++) { + if (!isaloop) + cap = mxserBoardCAP[b]; /* predefined */ + else + cap = ioaddr[b]; /* module param */ - if (set & TIOCM_RTS) - info->MCR |= UART_MCR_RTS; - if (set & TIOCM_DTR) - info->MCR |= UART_MCR_DTR; + if (!cap) + continue; - if (clear & TIOCM_RTS) - info->MCR &= ~UART_MCR_RTS; - if (clear & TIOCM_DTR) - info->MCR &= ~UART_MCR_DTR; + brd = &mxser_boards[m]; + retval = mxser_get_ISA_conf(cap, brd); - outb(info->MCR, info->base + UART_MCR); - spin_unlock_irqrestore(&info->slock, flags); - return 0; -} + if (retval != 0) + printk(KERN_INFO "Found MOXA %s board " + "(CAP=0x%x)\n", + brd->info->name, ioaddr[b]); + if (retval <= 0) { + if (retval == MXSER_ERR_IRQ) + printk(KERN_ERR "Invalid interrupt " + "number, board not " + "configured\n"); + else if (retval == MXSER_ERR_IRQ_CONFLIT) + printk(KERN_ERR "Invalid interrupt " + "number, board not " + "configured\n"); + else if (retval == MXSER_ERR_VECTOR) + printk(KERN_ERR "Invalid interrupt " + "vector, board not " + "configured\n"); + else if (retval == MXSER_ERR_IOADDR) + printk(KERN_ERR "Invalid I/O address, " + "board not configured\n"); -static int mxser_read_register(int, unsigned short *); -static int mxser_program_mode(int); -static void mxser_normal_mode(int); + brd->info = NULL; + continue; + } -static int mxser_get_ISA_conf(int cap, struct mxser_hwconf *hwconf) -{ - int id, i, bits; - unsigned short regs[16], irq; - unsigned char scratch, scratch2; + /* mxser_initbrd will hook ISR. */ + if (mxser_initbrd(brd, NULL) < 0) { + brd->info = NULL; + continue; + } - hwconf->IsMoxaMustChipFlag = MOXA_OTHER_UART; + brd->idx = m * MXSER_PORTS_PER_BOARD; + for (i = 0; i < brd->info->nports; i++) + tty_register_device(mxvar_sdriver, brd->idx + i, + NULL); - id = mxser_read_register(cap, regs); - if (id == C168_ASIC_ID) { - hwconf->board_type = MXSER_BOARD_C168_ISA; - hwconf->ports = 8; - } else if (id == C104_ASIC_ID) { - hwconf->board_type = MXSER_BOARD_C104_ISA; - hwconf->ports = 4; - } else if (id == C102_ASIC_ID) { - hwconf->board_type = MXSER_BOARD_C102_ISA; - hwconf->ports = 2; - } else if (id == CI132_ASIC_ID) { - hwconf->board_type = MXSER_BOARD_CI132; - hwconf->ports = 2; - } else if (id == CI134_ASIC_ID) { - hwconf->board_type = MXSER_BOARD_CI134; - hwconf->ports = 4; - } else if (id == CI104J_ASIC_ID) { - hwconf->board_type = MXSER_BOARD_CI104J; - hwconf->ports = 4; - } else - return 0; + m++; + } - irq = 0; - if (hwconf->ports == 2) { - irq = regs[9] & 0xF000; - irq = irq | (irq >> 4); - if (irq != (regs[9] & 0xFF00)) - return MXSER_ERR_IRQ_CONFLIT; - } else if (hwconf->ports == 4) { - irq = regs[9] & 0xF000; - irq = irq | (irq >> 4); - irq = irq | (irq >> 8); - if (irq != regs[9]) - return MXSER_ERR_IRQ_CONFLIT; - } else if (hwconf->ports == 8) { - irq = regs[9] & 0xF000; - irq = irq | (irq >> 4); - irq = irq | (irq >> 8); - if ((irq != regs[9]) || (irq != regs[10])) - return MXSER_ERR_IRQ_CONFLIT; + retval = pci_register_driver(&mxser_driver); + if (retval) { + printk(KERN_ERR "Can't register pci driver\n"); + if (!m) { + retval = -ENODEV; + goto err_unr; + } /* else: we have some ISA cards under control */ } - if (!irq) - return MXSER_ERR_IRQ; - hwconf->irq = ((int)(irq & 0xF000) >> 12); - for (i = 0; i < 8; i++) - hwconf->ioaddr[i] = (int) regs[i + 1] & 0xFFF8; - if ((regs[12] & 0x80) == 0) - return MXSER_ERR_VECTOR; - hwconf->vector = (int)regs[11]; /* interrupt vector */ - if (id == 1) - hwconf->vector_mask = 0x00FF; - else - hwconf->vector_mask = 0x000F; - for (i = 7, bits = 0x0100; i >= 0; i--, bits <<= 1) { - if (regs[12] & bits) { - hwconf->baud_base[i] = 921600; - hwconf->MaxCanSetBaudRate[i] = 921600; /* add by Victor Yu. 09-04-2002 */ - } else { - hwconf->baud_base[i] = 115200; - hwconf->MaxCanSetBaudRate[i] = 115200; /* add by Victor Yu. 09-04-2002 */ - } - } - scratch2 = inb(cap + UART_LCR) & (~UART_LCR_DLAB); - outb(scratch2 | UART_LCR_DLAB, cap + UART_LCR); - outb(0, cap + UART_EFR); /* EFR is the same as FCR */ - outb(scratch2, cap + UART_LCR); - outb(UART_FCR_ENABLE_FIFO, cap + UART_FCR); - scratch = inb(cap + UART_IIR); + pr_debug("Done.\n"); - if (scratch & 0xC0) - hwconf->uart_type = PORT_16550A; - else - hwconf->uart_type = PORT_16450; - if (id == 1) - hwconf->ports = 8; - else - hwconf->ports = 4; - request_region(hwconf->ioaddr[0], 8 * hwconf->ports, "mxser(IO)"); - request_region(hwconf->vector, 1, "mxser(vector)"); - return hwconf->ports; + return 0; +err_unr: + tty_unregister_driver(mxvar_sdriver); +err_put: + put_tty_driver(mxvar_sdriver); + return retval; } -#define CHIP_SK 0x01 /* Serial Data Clock in Eprom */ -#define CHIP_DO 0x02 /* Serial Data Output in Eprom */ -#define CHIP_CS 0x04 /* Serial Chip Select in Eprom */ -#define CHIP_DI 0x08 /* Serial Data Input in Eprom */ -#define EN_CCMD 0x000 /* Chip's command register */ -#define EN0_RSARLO 0x008 /* Remote start address reg 0 */ -#define EN0_RSARHI 0x009 /* Remote start address reg 1 */ -#define EN0_RCNTLO 0x00A /* Remote byte count reg WR */ -#define EN0_RCNTHI 0x00B /* Remote byte count reg WR */ -#define EN0_DCFG 0x00E /* Data configuration reg WR */ -#define EN0_PORT 0x010 /* Rcv missed frame error counter RD */ -#define ENC_PAGE0 0x000 /* Select page 0 of chip registers */ -#define ENC_PAGE3 0x0C0 /* Select page 3 of chip registers */ -static int mxser_read_register(int port, unsigned short *regs) +static void __exit mxser_module_exit(void) { - int i, k, value, id; - unsigned int j; - - id = mxser_program_mode(port); - if (id < 0) - return id; - for (i = 0; i < 14; i++) { - k = (i & 0x3F) | 0x180; - for (j = 0x100; j > 0; j >>= 1) { - outb(CHIP_CS, port); - if (k & j) { - outb(CHIP_CS | CHIP_DO, port); - outb(CHIP_CS | CHIP_DO | CHIP_SK, port); /* A? bit of read */ - } else { - outb(CHIP_CS, port); - outb(CHIP_CS | CHIP_SK, port); /* A? bit of read */ - } - } - (void)inb(port); - value = 0; - for (k = 0, j = 0x8000; k < 16; k++, j >>= 1) { - outb(CHIP_CS, port); - outb(CHIP_CS | CHIP_SK, port); - if (inb(port) & CHIP_DI) - value |= j; - } - regs[i] = value; - outb(0, port); - } - mxser_normal_mode(port); - return id; -} + unsigned int i, j; -static int mxser_program_mode(int port) -{ - int id, i, j, n; - /* unsigned long flags; */ + pr_debug("Unloading module mxser ...\n"); - spin_lock(&gm_lock); - outb(0, port); - outb(0, port); - outb(0, port); - (void)inb(port); - (void)inb(port); - outb(0, port); - (void)inb(port); - /* restore_flags(flags); */ - spin_unlock(&gm_lock); + pci_unregister_driver(&mxser_driver); - id = inb(port + 1) & 0x1F; - if ((id != C168_ASIC_ID) && - (id != C104_ASIC_ID) && - (id != C102_ASIC_ID) && - (id != CI132_ASIC_ID) && - (id != CI134_ASIC_ID) && - (id != CI104J_ASIC_ID)) - return -1; - for (i = 0, j = 0; i < 4; i++) { - n = inb(port + 2); - if (n == 'M') { - j = 1; - } else if ((j == 1) && (n == 1)) { - j = 2; - break; - } else - j = 0; - } - if (j != 2) - id = -2; - return id; -} + for (i = 0; i < MXSER_BOARDS; i++) /* ISA remains */ + if (mxser_boards[i].info != NULL) + for (j = 0; j < mxser_boards[i].info->nports; j++) + tty_unregister_device(mxvar_sdriver, + mxser_boards[i].idx + j); + tty_unregister_driver(mxvar_sdriver); + put_tty_driver(mxvar_sdriver); -static void mxser_normal_mode(int port) -{ - int i, n; + for (i = 0; i < MXSER_BOARDS; i++) + if (mxser_boards[i].info != NULL) + mxser_release_res(&mxser_boards[i], NULL, 1); - outb(0xA5, port + 1); - outb(0x80, port + 3); - outb(12, port + 0); /* 9600 bps */ - outb(0, port + 1); - outb(0x03, port + 3); /* 8 data bits */ - outb(0x13, port + 4); /* loop back mode */ - for (i = 0; i < 16; i++) { - n = inb(port + 5); - if ((n & 0x61) == 0x60) - break; - if ((n & 1) == 1) - (void)inb(port); - } - outb(0x00, port + 4); + pr_debug("Done.\n"); } module_init(mxser_module_init); diff --git a/drivers/char/mxser.h b/drivers/char/mxser.h index 1f4aa45ec004..844171115954 100644 --- a/drivers/char/mxser.h +++ b/drivers/char/mxser.h @@ -4,19 +4,17 @@ /* * Semi-public control interfaces */ - + /* * MOXA ioctls */ #define MOXA 0x400 #define MOXA_GETDATACOUNT (MOXA + 23) -#define MOXA_GET_CONF (MOXA + 35) #define MOXA_DIAGNOSE (MOXA + 50) #define MOXA_CHKPORTENABLE (MOXA + 60) #define MOXA_HighSpeedOn (MOXA + 61) #define MOXA_GET_MAJOR (MOXA + 63) -#define MOXA_GET_CUMAJOR (MOXA + 64) #define MOXA_GETMSTATUS (MOXA + 65) #define MOXA_SET_OP_MODE (MOXA + 66) #define MOXA_GET_OP_MODE (MOXA + 67) @@ -26,26 +24,14 @@ #define RS422_MODE 2 #define RS485_4WIRE_MODE 3 #define OP_MODE_MASK 3 -// above add by Victor Yu. 01-05-2004 - -#define TTY_THRESHOLD_THROTTLE 128 - -#define HI_WATER 768 - -// added by James. 03-11-2004. -#define MOXA_SDS_GETICOUNTER (MOXA + 68) -#define MOXA_SDS_RSTICOUNTER (MOXA + 69) -// (above) added by James. +#define MOXA_SDS_RSTICOUNTER (MOXA + 69) #define MOXA_ASPP_OQUEUE (MOXA + 70) -#define MOXA_ASPP_SETBAUD (MOXA + 71) -#define MOXA_ASPP_GETBAUD (MOXA + 72) #define MOXA_ASPP_MON (MOXA + 73) #define MOXA_ASPP_LSTATUS (MOXA + 74) #define MOXA_ASPP_MON_EXT (MOXA + 75) #define MOXA_SET_BAUD_METHOD (MOXA + 76) - /* --------------------------------------------------- */ #define NPPI_NOTIFY_PARITY 0x01 @@ -54,51 +40,46 @@ #define NPPI_NOTIFY_SW_OVERRUN 0x08 #define NPPI_NOTIFY_BREAK 0x10 -#define NPPI_NOTIFY_CTSHOLD 0x01 // Tx hold by CTS low -#define NPPI_NOTIFY_DSRHOLD 0x02 // Tx hold by DSR low -#define NPPI_NOTIFY_XOFFHOLD 0x08 // Tx hold by Xoff received -#define NPPI_NOTIFY_XOFFXENT 0x10 // Xoff Sent - -//CheckIsMoxaMust return value -#define MOXA_OTHER_UART 0x00 -#define MOXA_MUST_MU150_HWID 0x01 -#define MOXA_MUST_MU860_HWID 0x02 - -// follow just for Moxa Must chip define. -// -// when LCR register (offset 0x03) write following value, -// the Must chip will enter enchance mode. And write value -// on EFR (offset 0x02) bit 6,7 to change bank. +#define NPPI_NOTIFY_CTSHOLD 0x01 /* Tx hold by CTS low */ +#define NPPI_NOTIFY_DSRHOLD 0x02 /* Tx hold by DSR low */ +#define NPPI_NOTIFY_XOFFHOLD 0x08 /* Tx hold by Xoff received */ +#define NPPI_NOTIFY_XOFFXENT 0x10 /* Xoff Sent */ + +/* follow just for Moxa Must chip define. */ +/* */ +/* when LCR register (offset 0x03) write following value, */ +/* the Must chip will enter enchance mode. And write value */ +/* on EFR (offset 0x02) bit 6,7 to change bank. */ #define MOXA_MUST_ENTER_ENCHANCE 0xBF -// when enhance mode enable, access on general bank register +/* when enhance mode enable, access on general bank register */ #define MOXA_MUST_GDL_REGISTER 0x07 #define MOXA_MUST_GDL_MASK 0x7F #define MOXA_MUST_GDL_HAS_BAD_DATA 0x80 -#define MOXA_MUST_LSR_RERR 0x80 // error in receive FIFO -// enchance register bank select and enchance mode setting register -// when LCR register equal to 0xBF +#define MOXA_MUST_LSR_RERR 0x80 /* error in receive FIFO */ +/* enchance register bank select and enchance mode setting register */ +/* when LCR register equal to 0xBF */ #define MOXA_MUST_EFR_REGISTER 0x02 -// enchance mode enable +/* enchance mode enable */ #define MOXA_MUST_EFR_EFRB_ENABLE 0x10 -// enchance reister bank set 0, 1, 2 +/* enchance reister bank set 0, 1, 2 */ #define MOXA_MUST_EFR_BANK0 0x00 #define MOXA_MUST_EFR_BANK1 0x40 #define MOXA_MUST_EFR_BANK2 0x80 #define MOXA_MUST_EFR_BANK3 0xC0 #define MOXA_MUST_EFR_BANK_MASK 0xC0 -// set XON1 value register, when LCR=0xBF and change to bank0 +/* set XON1 value register, when LCR=0xBF and change to bank0 */ #define MOXA_MUST_XON1_REGISTER 0x04 -// set XON2 value register, when LCR=0xBF and change to bank0 +/* set XON2 value register, when LCR=0xBF and change to bank0 */ #define MOXA_MUST_XON2_REGISTER 0x05 -// set XOFF1 value register, when LCR=0xBF and change to bank0 +/* set XOFF1 value register, when LCR=0xBF and change to bank0 */ #define MOXA_MUST_XOFF1_REGISTER 0x06 -// set XOFF2 value register, when LCR=0xBF and change to bank0 +/* set XOFF2 value register, when LCR=0xBF and change to bank0 */ #define MOXA_MUST_XOFF2_REGISTER 0x07 #define MOXA_MUST_RBRTL_REGISTER 0x04 @@ -110,32 +91,32 @@ #define MOXA_MUST_ECR_REGISTER 0x06 #define MOXA_MUST_CSR_REGISTER 0x07 -// good data mode enable +/* good data mode enable */ #define MOXA_MUST_FCR_GDA_MODE_ENABLE 0x20 -// only good data put into RxFIFO +/* only good data put into RxFIFO */ #define MOXA_MUST_FCR_GDA_ONLY_ENABLE 0x10 -// enable CTS interrupt +/* enable CTS interrupt */ #define MOXA_MUST_IER_ECTSI 0x80 -// enable RTS interrupt +/* enable RTS interrupt */ #define MOXA_MUST_IER_ERTSI 0x40 -// enable Xon/Xoff interrupt +/* enable Xon/Xoff interrupt */ #define MOXA_MUST_IER_XINT 0x20 -// enable GDA interrupt +/* enable GDA interrupt */ #define MOXA_MUST_IER_EGDAI 0x10 #define MOXA_MUST_RECV_ISR (UART_IER_RDI | MOXA_MUST_IER_EGDAI) -// GDA interrupt pending +/* GDA interrupt pending */ #define MOXA_MUST_IIR_GDA 0x1C #define MOXA_MUST_IIR_RDA 0x04 #define MOXA_MUST_IIR_RTO 0x0C #define MOXA_MUST_IIR_LSR 0x06 -// recieved Xon/Xoff or specical interrupt pending +/* recieved Xon/Xoff or specical interrupt pending */ #define MOXA_MUST_IIR_XSC 0x10 -// RTS/CTS change state interrupt pending +/* RTS/CTS change state interrupt pending */ #define MOXA_MUST_IIR_RTSCTS 0x20 #define MOXA_MUST_IIR_MASK 0x3E @@ -143,299 +124,164 @@ #define MOXA_MUST_MCR_XON_ANY 0x80 #define MOXA_MUST_MCR_TX_XON 0x08 - -// software flow control on chip mask value +/* software flow control on chip mask value */ #define MOXA_MUST_EFR_SF_MASK 0x0F -// send Xon1/Xoff1 +/* send Xon1/Xoff1 */ #define MOXA_MUST_EFR_SF_TX1 0x08 -// send Xon2/Xoff2 +/* send Xon2/Xoff2 */ #define MOXA_MUST_EFR_SF_TX2 0x04 -// send Xon1,Xon2/Xoff1,Xoff2 +/* send Xon1,Xon2/Xoff1,Xoff2 */ #define MOXA_MUST_EFR_SF_TX12 0x0C -// don't send Xon/Xoff +/* don't send Xon/Xoff */ #define MOXA_MUST_EFR_SF_TX_NO 0x00 -// Tx software flow control mask +/* Tx software flow control mask */ #define MOXA_MUST_EFR_SF_TX_MASK 0x0C -// don't receive Xon/Xoff +/* don't receive Xon/Xoff */ #define MOXA_MUST_EFR_SF_RX_NO 0x00 -// receive Xon1/Xoff1 +/* receive Xon1/Xoff1 */ #define MOXA_MUST_EFR_SF_RX1 0x02 -// receive Xon2/Xoff2 +/* receive Xon2/Xoff2 */ #define MOXA_MUST_EFR_SF_RX2 0x01 -// receive Xon1,Xon2/Xoff1,Xoff2 +/* receive Xon1,Xon2/Xoff1,Xoff2 */ #define MOXA_MUST_EFR_SF_RX12 0x03 -// Rx software flow control mask +/* Rx software flow control mask */ #define MOXA_MUST_EFR_SF_RX_MASK 0x03 -//#define MOXA_MUST_MIN_XOFFLIMIT 66 -//#define MOXA_MUST_MIN_XONLIMIT 20 -//#define ID1_RX_TRIG 120 - - -#define CHECK_MOXA_MUST_XOFFLIMIT(info) { \ - if ( (info)->IsMoxaMustChipFlag && \ - (info)->HandFlow.XoffLimit < MOXA_MUST_MIN_XOFFLIMIT ) { \ - (info)->HandFlow.XoffLimit = MOXA_MUST_MIN_XOFFLIMIT; \ - (info)->HandFlow.XonLimit = MOXA_MUST_MIN_XONLIMIT; \ - } \ -} - -#define ENABLE_MOXA_MUST_ENCHANCE_MODE(baseio) { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ +#define ENABLE_MOXA_MUST_ENCHANCE_MODE(baseio) do { \ + u8 __oldlcr, __efr; \ + __oldlcr = inb((baseio)+UART_LCR); \ outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr |= MOXA_MUST_EFR_EFRB_ENABLE; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} - -#define DISABLE_MOXA_MUST_ENCHANCE_MODE(baseio) { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ + __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ + __efr |= MOXA_MUST_EFR_EFRB_ENABLE; \ + outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ + outb(__oldlcr, (baseio)+UART_LCR); \ +} while (0) + +#define DISABLE_MOXA_MUST_ENCHANCE_MODE(baseio) do { \ + u8 __oldlcr, __efr; \ + __oldlcr = inb((baseio)+UART_LCR); \ outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_EFRB_ENABLE; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} - -#define SET_MOXA_MUST_XON1_VALUE(baseio, Value) { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ + __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ + __efr &= ~MOXA_MUST_EFR_EFRB_ENABLE; \ + outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ + outb(__oldlcr, (baseio)+UART_LCR); \ +} while (0) + +#define SET_MOXA_MUST_XON1_VALUE(baseio, Value) do { \ + u8 __oldlcr, __efr; \ + __oldlcr = inb((baseio)+UART_LCR); \ outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_BANK_MASK; \ - __efr |= MOXA_MUST_EFR_BANK0; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ + __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ + __efr &= ~MOXA_MUST_EFR_BANK_MASK; \ + __efr |= MOXA_MUST_EFR_BANK0; \ + outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ outb((u8)(Value), (baseio)+MOXA_MUST_XON1_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} + outb(__oldlcr, (baseio)+UART_LCR); \ +} while (0) -#define SET_MOXA_MUST_XON2_VALUE(baseio, Value) { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ +#define SET_MOXA_MUST_XOFF1_VALUE(baseio, Value) do { \ + u8 __oldlcr, __efr; \ + __oldlcr = inb((baseio)+UART_LCR); \ outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_BANK_MASK; \ - __efr |= MOXA_MUST_EFR_BANK0; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb((u8)(Value), (baseio)+MOXA_MUST_XON2_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} - -#define SET_MOXA_MUST_XOFF1_VALUE(baseio, Value) { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_BANK_MASK; \ - __efr |= MOXA_MUST_EFR_BANK0; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ + __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ + __efr &= ~MOXA_MUST_EFR_BANK_MASK; \ + __efr |= MOXA_MUST_EFR_BANK0; \ + outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ outb((u8)(Value), (baseio)+MOXA_MUST_XOFF1_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} - -#define SET_MOXA_MUST_XOFF2_VALUE(baseio, Value) { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_BANK_MASK; \ - __efr |= MOXA_MUST_EFR_BANK0; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb((u8)(Value), (baseio)+MOXA_MUST_XOFF2_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} - -#define SET_MOXA_MUST_RBRTL_VALUE(baseio, Value) { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_BANK_MASK; \ - __efr |= MOXA_MUST_EFR_BANK1; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb((u8)(Value), (baseio)+MOXA_MUST_RBRTL_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} - -#define SET_MOXA_MUST_RBRTH_VALUE(baseio, Value) { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_BANK_MASK; \ - __efr |= MOXA_MUST_EFR_BANK1; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb((u8)(Value), (baseio)+MOXA_MUST_RBRTH_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} - -#define SET_MOXA_MUST_RBRTI_VALUE(baseio, Value) { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ + outb(__oldlcr, (baseio)+UART_LCR); \ +} while (0) + +#define SET_MOXA_MUST_FIFO_VALUE(info) do { \ + u8 __oldlcr, __efr; \ + __oldlcr = inb((info)->ioaddr+UART_LCR); \ + outb(MOXA_MUST_ENTER_ENCHANCE, (info)->ioaddr+UART_LCR);\ + __efr = inb((info)->ioaddr+MOXA_MUST_EFR_REGISTER); \ + __efr &= ~MOXA_MUST_EFR_BANK_MASK; \ + __efr |= MOXA_MUST_EFR_BANK1; \ + outb(__efr, (info)->ioaddr+MOXA_MUST_EFR_REGISTER); \ + outb((u8)((info)->rx_high_water), (info)->ioaddr+ \ + MOXA_MUST_RBRTH_REGISTER); \ + outb((u8)((info)->rx_trigger), (info)->ioaddr+ \ + MOXA_MUST_RBRTI_REGISTER); \ + outb((u8)((info)->rx_low_water), (info)->ioaddr+ \ + MOXA_MUST_RBRTL_REGISTER); \ + outb(__oldlcr, (info)->ioaddr+UART_LCR); \ +} while (0) + +#define SET_MOXA_MUST_ENUM_VALUE(baseio, Value) do { \ + u8 __oldlcr, __efr; \ + __oldlcr = inb((baseio)+UART_LCR); \ outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_BANK_MASK; \ - __efr |= MOXA_MUST_EFR_BANK1; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb((u8)(Value), (baseio)+MOXA_MUST_RBRTI_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} - -#define SET_MOXA_MUST_THRTL_VALUE(baseio, Value) { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_BANK_MASK; \ - __efr |= MOXA_MUST_EFR_BANK1; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb((u8)(Value), (baseio)+MOXA_MUST_THRTL_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} - -//#define MOXA_MUST_RBRL_VALUE 4 -#define SET_MOXA_MUST_FIFO_VALUE(info) { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((info)->base+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (info)->base+UART_LCR); \ - __efr = inb((info)->base+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_BANK_MASK; \ - __efr |= MOXA_MUST_EFR_BANK1; \ - outb(__efr, (info)->base+MOXA_MUST_EFR_REGISTER); \ - outb((u8)((info)->rx_high_water), (info)->base+MOXA_MUST_RBRTH_REGISTER); \ - outb((u8)((info)->rx_trigger), (info)->base+MOXA_MUST_RBRTI_REGISTER); \ - outb((u8)((info)->rx_low_water), (info)->base+MOXA_MUST_RBRTL_REGISTER); \ - outb(__oldlcr, (info)->base+UART_LCR); \ -} - - - -#define SET_MOXA_MUST_ENUM_VALUE(baseio, Value) { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_BANK_MASK; \ - __efr |= MOXA_MUST_EFR_BANK2; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ + __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ + __efr &= ~MOXA_MUST_EFR_BANK_MASK; \ + __efr |= MOXA_MUST_EFR_BANK2; \ + outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ outb((u8)(Value), (baseio)+MOXA_MUST_ENUM_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} + outb(__oldlcr, (baseio)+UART_LCR); \ +} while (0) -#define GET_MOXA_MUST_HARDWARE_ID(baseio, pId) { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_BANK_MASK; \ - __efr |= MOXA_MUST_EFR_BANK2; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - *pId = inb((baseio)+MOXA_MUST_HWID_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} - -#define SET_MOXA_MUST_NO_SOFTWARE_FLOW_CONTROL(baseio) { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_SF_MASK; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} - -#define SET_MOXA_MUST_JUST_TX_SOFTWARE_FLOW_CONTROL(baseio) { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_SF_MASK; \ - __efr |= MOXA_MUST_EFR_SF_TX1; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} - -#define ENABLE_MOXA_MUST_TX_SOFTWARE_FLOW_CONTROL(baseio) { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ +#define GET_MOXA_MUST_HARDWARE_ID(baseio, pId) do { \ + u8 __oldlcr, __efr; \ + __oldlcr = inb((baseio)+UART_LCR); \ outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_SF_TX_MASK; \ - __efr |= MOXA_MUST_EFR_SF_TX1; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} - -#define DISABLE_MOXA_MUST_TX_SOFTWARE_FLOW_CONTROL(baseio) { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ + __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ + __efr &= ~MOXA_MUST_EFR_BANK_MASK; \ + __efr |= MOXA_MUST_EFR_BANK2; \ + outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ + *pId = inb((baseio)+MOXA_MUST_HWID_REGISTER); \ + outb(__oldlcr, (baseio)+UART_LCR); \ +} while (0) + +#define SET_MOXA_MUST_NO_SOFTWARE_FLOW_CONTROL(baseio) do { \ + u8 __oldlcr, __efr; \ + __oldlcr = inb((baseio)+UART_LCR); \ outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_SF_TX_MASK; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} - -#define SET_MOXA_MUST_JUST_RX_SOFTWARE_FLOW_CONTROL(baseio) { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ + __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ + __efr &= ~MOXA_MUST_EFR_SF_MASK; \ + outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ + outb(__oldlcr, (baseio)+UART_LCR); \ +} while (0) + +#define ENABLE_MOXA_MUST_TX_SOFTWARE_FLOW_CONTROL(baseio) do { \ + u8 __oldlcr, __efr; \ + __oldlcr = inb((baseio)+UART_LCR); \ outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_SF_MASK; \ - __efr |= MOXA_MUST_EFR_SF_RX1; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} - -#define ENABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL(baseio) { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ + __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ + __efr &= ~MOXA_MUST_EFR_SF_TX_MASK; \ + __efr |= MOXA_MUST_EFR_SF_TX1; \ + outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ + outb(__oldlcr, (baseio)+UART_LCR); \ +} while (0) + +#define DISABLE_MOXA_MUST_TX_SOFTWARE_FLOW_CONTROL(baseio) do { \ + u8 __oldlcr, __efr; \ + __oldlcr = inb((baseio)+UART_LCR); \ outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_SF_RX_MASK; \ - __efr |= MOXA_MUST_EFR_SF_RX1; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} - -#define DISABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL(baseio) { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ + __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ + __efr &= ~MOXA_MUST_EFR_SF_TX_MASK; \ + outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ + outb(__oldlcr, (baseio)+UART_LCR); \ +} while (0) + +#define ENABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL(baseio) do { \ + u8 __oldlcr, __efr; \ + __oldlcr = inb((baseio)+UART_LCR); \ outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_SF_RX_MASK; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} - -#define ENABLE_MOXA_MUST_TX_RX_SOFTWARE_FLOW_CONTROL(baseio) { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ + __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ + __efr &= ~MOXA_MUST_EFR_SF_RX_MASK; \ + __efr |= MOXA_MUST_EFR_SF_RX1; \ + outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ + outb(__oldlcr, (baseio)+UART_LCR); \ +} while (0) + +#define DISABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL(baseio) do { \ + u8 __oldlcr, __efr; \ + __oldlcr = inb((baseio)+UART_LCR); \ outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_SF_MASK; \ - __efr |= (MOXA_MUST_EFR_SF_RX1|MOXA_MUST_EFR_SF_TX1); \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} - -#define ENABLE_MOXA_MUST_XON_ANY_FLOW_CONTROL(baseio) { \ - u8 __oldmcr; \ - __oldmcr = inb((baseio)+UART_MCR); \ - __oldmcr |= MOXA_MUST_MCR_XON_ANY; \ - outb(__oldmcr, (baseio)+UART_MCR); \ -} - -#define DISABLE_MOXA_MUST_XON_ANY_FLOW_CONTROL(baseio) { \ - u8 __oldmcr; \ - __oldmcr = inb((baseio)+UART_MCR); \ - __oldmcr &= ~MOXA_MUST_MCR_XON_ANY; \ - outb(__oldmcr, (baseio)+UART_MCR); \ -} - -#define READ_MOXA_MUST_GDL(baseio) inb((baseio)+MOXA_MUST_GDL_REGISTER) + __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ + __efr &= ~MOXA_MUST_EFR_SF_RX_MASK; \ + outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ + outb(__oldlcr, (baseio)+UART_LCR); \ +} while (0) #endif diff --git a/drivers/char/mxser_new.c b/drivers/char/mxser_new.c deleted file mode 100644 index bf1bee4e1f5e..000000000000 --- a/drivers/char/mxser_new.c +++ /dev/null @@ -1,2816 +0,0 @@ -/* - * mxser.c -- MOXA Smartio/Industio family multiport serial driver. - * - * Copyright (C) 1999-2006 Moxa Technologies (support@moxa.com.tw). - * Copyright (C) 2006-2007 Jiri Slaby <jirislaby@gmail.com> - * - * This code is loosely based on the 1.8 moxa driver which is based on - * Linux serial driver, written by Linus Torvalds, Theodore T'so and - * others. - * - * 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. - * - * Fed through a cleanup, indent and remove of non 2.6 code by Alan Cox - * <alan@redhat.com>. The original 1.8 code is available on www.moxa.com. - * - Fixed x86_64 cleanness - * - Fixed sleep with spinlock held in mxser_send_break - */ - -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/signal.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/interrupt.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/serial.h> -#include <linux/serial_reg.h> -#include <linux/major.h> -#include <linux/string.h> -#include <linux/fcntl.h> -#include <linux/ptrace.h> -#include <linux/gfp.h> -#include <linux/ioport.h> -#include <linux/mm.h> -#include <linux/delay.h> -#include <linux/pci.h> -#include <linux/bitops.h> - -#include <asm/system.h> -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/uaccess.h> - -#include "mxser_new.h" - -#define MXSER_VERSION "2.0.2" /* 1.10 */ -#define MXSERMAJOR 174 -#define MXSERCUMAJOR 175 - -#define MXSER_BOARDS 4 /* Max. boards */ -#define MXSER_PORTS_PER_BOARD 8 /* Max. ports per board */ -#define MXSER_PORTS (MXSER_BOARDS * MXSER_PORTS_PER_BOARD) -#define MXSER_ISR_PASS_LIMIT 100 - -#define MXSER_ERR_IOADDR -1 -#define MXSER_ERR_IRQ -2 -#define MXSER_ERR_IRQ_CONFLIT -3 -#define MXSER_ERR_VECTOR -4 - -/*CheckIsMoxaMust return value*/ -#define MOXA_OTHER_UART 0x00 -#define MOXA_MUST_MU150_HWID 0x01 -#define MOXA_MUST_MU860_HWID 0x02 - -#define WAKEUP_CHARS 256 - -#define UART_MCR_AFE 0x20 -#define UART_LSR_SPECIAL 0x1E - -#define PCI_DEVICE_ID_CB108 0x1080 -#define PCI_DEVICE_ID_CB114 0x1142 -#define PCI_DEVICE_ID_CB134I 0x1341 -#define PCI_DEVICE_ID_CP138U 0x1380 -#define PCI_DEVICE_ID_POS104UL 0x1044 - - -#define C168_ASIC_ID 1 -#define C104_ASIC_ID 2 -#define C102_ASIC_ID 0xB -#define CI132_ASIC_ID 4 -#define CI134_ASIC_ID 3 -#define CI104J_ASIC_ID 5 - -#define MXSER_HIGHBAUD 1 -#define MXSER_HAS2 2 - -/* This is only for PCI */ -static const struct { - int type; - int tx_fifo; - int rx_fifo; - int xmit_fifo_size; - int rx_high_water; - int rx_trigger; - int rx_low_water; - long max_baud; -} Gpci_uart_info[] = { - {MOXA_OTHER_UART, 16, 16, 16, 14, 14, 1, 921600L}, - {MOXA_MUST_MU150_HWID, 64, 64, 64, 48, 48, 16, 230400L}, - {MOXA_MUST_MU860_HWID, 128, 128, 128, 96, 96, 32, 921600L} -}; -#define UART_INFO_NUM ARRAY_SIZE(Gpci_uart_info) - -struct mxser_cardinfo { - unsigned int nports; - char *name; - unsigned int flags; -}; - -static const struct mxser_cardinfo mxser_cards[] = { -/* 0*/ { 8, "C168 series", }, - { 4, "C104 series", }, - { 4, "CI-104J series", }, - { 8, "C168H/PCI series", }, - { 4, "C104H/PCI series", }, -/* 5*/ { 4, "C102 series", MXSER_HAS2 }, /* C102-ISA */ - { 4, "CI-132 series", MXSER_HAS2 }, - { 4, "CI-134 series", }, - { 2, "CP-132 series", }, - { 4, "CP-114 series", }, -/*10*/ { 4, "CT-114 series", }, - { 2, "CP-102 series", MXSER_HIGHBAUD }, - { 4, "CP-104U series", }, - { 8, "CP-168U series", }, - { 2, "CP-132U series", }, -/*15*/ { 4, "CP-134U series", }, - { 4, "CP-104JU series", }, - { 8, "Moxa UC7000 Serial", }, /* RC7000 */ - { 8, "CP-118U series", }, - { 2, "CP-102UL series", }, -/*20*/ { 2, "CP-102U series", }, - { 8, "CP-118EL series", }, - { 8, "CP-168EL series", }, - { 4, "CP-104EL series", }, - { 8, "CB-108 series", }, -/*25*/ { 4, "CB-114 series", }, - { 4, "CB-134I series", }, - { 8, "CP-138U series", }, - { 4, "POS-104UL series", } -}; - -/* driver_data correspond to the lines in the structure above - see also ISA probe function before you change something */ -static struct pci_device_id mxser_pcibrds[] = { - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_C168), .driver_data = 3 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_C104), .driver_data = 4 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132), .driver_data = 8 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP114), .driver_data = 9 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CT114), .driver_data = 10 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102), .driver_data = 11 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104U), .driver_data = 12 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP168U), .driver_data = 13 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP132U), .driver_data = 14 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP134U), .driver_data = 15 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104JU),.driver_data = 16 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_RC7000), .driver_data = 17 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118U), .driver_data = 18 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102UL),.driver_data = 19 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP102U), .driver_data = 20 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP118EL),.driver_data = 21 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP168EL),.driver_data = 22 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_MOXA_CP104EL),.driver_data = 23 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CB108), .driver_data = 24 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CB114), .driver_data = 25 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CB134I), .driver_data = 26 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP138U), .driver_data = 27 }, - { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_POS104UL), .driver_data = 28 }, - { } -}; -MODULE_DEVICE_TABLE(pci, mxser_pcibrds); - -static int mxvar_baud_table[] = { - 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, - 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600 -}; -static unsigned int mxvar_baud_table1[] = { - 0, B50, B75, B110, B134, B150, B200, B300, B600, B1200, B1800, B2400, - B4800, B9600, B19200, B38400, B57600, B115200, B230400, B460800, B921600 -}; -#define BAUD_TABLE_NO ARRAY_SIZE(mxvar_baud_table) - -#define B_SPEC B2000000 - -static int ioaddr[MXSER_BOARDS] = { 0, 0, 0, 0 }; -static int ttymajor = MXSERMAJOR; -static int calloutmajor = MXSERCUMAJOR; - -/* Variables for insmod */ - -MODULE_AUTHOR("Casper Yang"); -MODULE_DESCRIPTION("MOXA Smartio/Industio Family Multiport Board Device Driver"); -module_param_array(ioaddr, int, NULL, 0); -module_param(ttymajor, int, 0); -MODULE_LICENSE("GPL"); - -struct mxser_log { - int tick; - unsigned long rxcnt[MXSER_PORTS]; - unsigned long txcnt[MXSER_PORTS]; -}; - - -struct mxser_mon { - unsigned long rxcnt; - unsigned long txcnt; - unsigned long up_rxcnt; - unsigned long up_txcnt; - int modem_status; - unsigned char hold_reason; -}; - -struct mxser_mon_ext { - unsigned long rx_cnt[32]; - unsigned long tx_cnt[32]; - unsigned long up_rxcnt[32]; - unsigned long up_txcnt[32]; - int modem_status[32]; - - long baudrate[32]; - int databits[32]; - int stopbits[32]; - int parity[32]; - int flowctrl[32]; - int fifo[32]; - int iftype[32]; -}; - -struct mxser_board; - -struct mxser_port { - struct mxser_board *board; - struct tty_struct *tty; - - unsigned long ioaddr; - unsigned long opmode_ioaddr; - int max_baud; - - int rx_high_water; - int rx_trigger; /* Rx fifo trigger level */ - int rx_low_water; - int baud_base; /* max. speed */ - long realbaud; - int type; /* UART type */ - int flags; /* defined in tty.h */ - int speed; - - int x_char; /* xon/xoff character */ - int IER; /* Interrupt Enable Register */ - int MCR; /* Modem control register */ - - unsigned char stop_rx; - unsigned char ldisc_stop_rx; - - int custom_divisor; - int close_delay; - unsigned short closing_wait; - unsigned char err_shadow; - unsigned long event; - - int count; /* # of fd on device */ - int blocked_open; /* # of blocked opens */ - struct async_icount icount; /* kernel counters for 4 input interrupts */ - int timeout; - - int read_status_mask; - int ignore_status_mask; - int xmit_fifo_size; - unsigned char *xmit_buf; - int xmit_head; - int xmit_tail; - int xmit_cnt; - - struct ktermios normal_termios; - - struct mxser_mon mon_data; - - spinlock_t slock; - wait_queue_head_t open_wait; - wait_queue_head_t delta_msr_wait; -}; - -struct mxser_board { - unsigned int idx; - int irq; - const struct mxser_cardinfo *info; - unsigned long vector; - unsigned long vector_mask; - - int chip_flag; - int uart_type; - - struct mxser_port ports[MXSER_PORTS_PER_BOARD]; -}; - -struct mxser_mstatus { - tcflag_t cflag; - int cts; - int dsr; - int ri; - int dcd; -}; - -static struct mxser_mstatus GMStatus[MXSER_PORTS]; - -static int mxserBoardCAP[MXSER_BOARDS] = { - 0, 0, 0, 0 - /* 0x180, 0x280, 0x200, 0x320 */ -}; - -static struct mxser_board mxser_boards[MXSER_BOARDS]; -static struct tty_driver *mxvar_sdriver; -static struct mxser_log mxvar_log; -static int mxvar_diagflag; -static unsigned char mxser_msr[MXSER_PORTS + 1]; -static struct mxser_mon_ext mon_data_ext; -static int mxser_set_baud_method[MXSER_PORTS + 1]; - -#ifdef CONFIG_PCI -static int __devinit CheckIsMoxaMust(int io) -{ - u8 oldmcr, hwid; - int i; - - outb(0, io + UART_LCR); - DISABLE_MOXA_MUST_ENCHANCE_MODE(io); - oldmcr = inb(io + UART_MCR); - outb(0, io + UART_MCR); - SET_MOXA_MUST_XON1_VALUE(io, 0x11); - if ((hwid = inb(io + UART_MCR)) != 0) { - outb(oldmcr, io + UART_MCR); - return MOXA_OTHER_UART; - } - - GET_MOXA_MUST_HARDWARE_ID(io, &hwid); - for (i = 1; i < UART_INFO_NUM; i++) { /* 0 = OTHER_UART */ - if (hwid == Gpci_uart_info[i].type) - return (int)hwid; - } - return MOXA_OTHER_UART; -} -#endif - -static void process_txrx_fifo(struct mxser_port *info) -{ - int i; - - if ((info->type == PORT_16450) || (info->type == PORT_8250)) { - info->rx_trigger = 1; - info->rx_high_water = 1; - info->rx_low_water = 1; - info->xmit_fifo_size = 1; - } else - for (i = 0; i < UART_INFO_NUM; i++) - if (info->board->chip_flag == Gpci_uart_info[i].type) { - info->rx_trigger = Gpci_uart_info[i].rx_trigger; - info->rx_low_water = Gpci_uart_info[i].rx_low_water; - info->rx_high_water = Gpci_uart_info[i].rx_high_water; - info->xmit_fifo_size = Gpci_uart_info[i].xmit_fifo_size; - break; - } -} - -static unsigned char mxser_get_msr(int baseaddr, int mode, int port) -{ - unsigned char status = 0; - - status = inb(baseaddr + UART_MSR); - - mxser_msr[port] &= 0x0F; - mxser_msr[port] |= status; - status = mxser_msr[port]; - if (mode) - mxser_msr[port] = 0; - - return status; -} - -static int mxser_block_til_ready(struct tty_struct *tty, struct file *filp, - struct mxser_port *port) -{ - DECLARE_WAITQUEUE(wait, current); - int retval; - int do_clocal = 0; - unsigned long flags; - - /* - * If non-blocking mode is set, or the port is not enabled, - * then make the check up front and then exit. - */ - if ((filp->f_flags & O_NONBLOCK) || - test_bit(TTY_IO_ERROR, &tty->flags)) { - port->flags |= ASYNC_NORMAL_ACTIVE; - return 0; - } - - if (tty->termios->c_cflag & CLOCAL) - do_clocal = 1; - - /* - * Block waiting for the carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, port->count is dropped by one, so that - * mxser_close() knows when to free things. We restore it upon - * exit, either normal or abnormal. - */ - retval = 0; - add_wait_queue(&port->open_wait, &wait); - - spin_lock_irqsave(&port->slock, flags); - if (!tty_hung_up_p(filp)) - port->count--; - spin_unlock_irqrestore(&port->slock, flags); - port->blocked_open++; - while (1) { - spin_lock_irqsave(&port->slock, flags); - outb(inb(port->ioaddr + UART_MCR) | - UART_MCR_DTR | UART_MCR_RTS, port->ioaddr + UART_MCR); - spin_unlock_irqrestore(&port->slock, flags); - set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) { - if (port->flags & ASYNC_HUP_NOTIFY) - retval = -EAGAIN; - else - retval = -ERESTARTSYS; - break; - } - if (!(port->flags & ASYNC_CLOSING) && - (do_clocal || - (inb(port->ioaddr + UART_MSR) & UART_MSR_DCD))) - break; - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - schedule(); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&port->open_wait, &wait); - if (!tty_hung_up_p(filp)) - port->count++; - port->blocked_open--; - if (retval) - return retval; - port->flags |= ASYNC_NORMAL_ACTIVE; - return 0; -} - -static int mxser_set_baud(struct mxser_port *info, long newspd) -{ - unsigned int i; - int quot = 0; - unsigned char cval; - int ret = 0; - - if (!info->tty || !info->tty->termios) - return ret; - - if (!(info->ioaddr)) - return ret; - - if (newspd > info->max_baud) - return 0; - - info->realbaud = newspd; - for (i = 0; i < BAUD_TABLE_NO; i++) - if (newspd == mxvar_baud_table[i]) - break; - if (i == BAUD_TABLE_NO) { - quot = info->baud_base / info->speed; - if (info->speed <= 0 || info->speed > info->max_baud) - quot = 0; - } else { - if (newspd == 134) { - quot = (2 * info->baud_base / 269); - } else if (newspd) { - quot = info->baud_base / newspd; - if (quot == 0) - quot = 1; - } else { - quot = 0; - } - } - - info->timeout = ((info->xmit_fifo_size * HZ * 10 * quot) / info->baud_base); - info->timeout += HZ / 50; /* Add .02 seconds of slop */ - - if (quot) { - info->MCR |= UART_MCR_DTR; - outb(info->MCR, info->ioaddr + UART_MCR); - } else { - info->MCR &= ~UART_MCR_DTR; - outb(info->MCR, info->ioaddr + UART_MCR); - return ret; - } - - cval = inb(info->ioaddr + UART_LCR); - - outb(cval | UART_LCR_DLAB, info->ioaddr + UART_LCR); /* set DLAB */ - - outb(quot & 0xff, info->ioaddr + UART_DLL); /* LS of divisor */ - outb(quot >> 8, info->ioaddr + UART_DLM); /* MS of divisor */ - outb(cval, info->ioaddr + UART_LCR); /* reset DLAB */ - - if (i == BAUD_TABLE_NO) { - quot = info->baud_base % info->speed; - quot *= 8; - if ((quot % info->speed) > (info->speed / 2)) { - quot /= info->speed; - quot++; - } else { - quot /= info->speed; - } - SET_MOXA_MUST_ENUM_VALUE(info->ioaddr, quot); - } else - SET_MOXA_MUST_ENUM_VALUE(info->ioaddr, 0); - - return ret; -} - -/* - * This routine is called to set the UART divisor registers to match - * the specified baud rate for a serial port. - */ -static int mxser_change_speed(struct mxser_port *info, - struct ktermios *old_termios) -{ - unsigned cflag, cval, fcr; - int ret = 0; - unsigned char status; - long baud; - - if (!info->tty || !info->tty->termios) - return ret; - cflag = info->tty->termios->c_cflag; - if (!(info->ioaddr)) - return ret; - - if (mxser_set_baud_method[info->tty->index] == 0) { - if ((cflag & CBAUD) == B_SPEC) - baud = info->speed; - else - baud = tty_get_baud_rate(info->tty); - mxser_set_baud(info, baud); - } - - /* byte size and parity */ - switch (cflag & CSIZE) { - case CS5: - cval = 0x00; - break; - case CS6: - cval = 0x01; - break; - case CS7: - cval = 0x02; - break; - case CS8: - cval = 0x03; - break; - default: - cval = 0x00; - break; /* too keep GCC shut... */ - } - if (cflag & CSTOPB) - cval |= 0x04; - if (cflag & PARENB) - cval |= UART_LCR_PARITY; - if (!(cflag & PARODD)) - cval |= UART_LCR_EPAR; - if (cflag & CMSPAR) - cval |= UART_LCR_SPAR; - - if ((info->type == PORT_8250) || (info->type == PORT_16450)) { - if (info->board->chip_flag) { - fcr = UART_FCR_ENABLE_FIFO; - fcr |= MOXA_MUST_FCR_GDA_MODE_ENABLE; - SET_MOXA_MUST_FIFO_VALUE(info); - } else - fcr = 0; - } else { - fcr = UART_FCR_ENABLE_FIFO; - if (info->board->chip_flag) { - fcr |= MOXA_MUST_FCR_GDA_MODE_ENABLE; - SET_MOXA_MUST_FIFO_VALUE(info); - } else { - switch (info->rx_trigger) { - case 1: - fcr |= UART_FCR_TRIGGER_1; - break; - case 4: - fcr |= UART_FCR_TRIGGER_4; - break; - case 8: - fcr |= UART_FCR_TRIGGER_8; - break; - default: - fcr |= UART_FCR_TRIGGER_14; - break; - } - } - } - - /* CTS flow control flag and modem status interrupts */ - info->IER &= ~UART_IER_MSI; - info->MCR &= ~UART_MCR_AFE; - if (cflag & CRTSCTS) { - info->flags |= ASYNC_CTS_FLOW; - info->IER |= UART_IER_MSI; - if ((info->type == PORT_16550A) || (info->board->chip_flag)) { - info->MCR |= UART_MCR_AFE; - } else { - status = inb(info->ioaddr + UART_MSR); - if (info->tty->hw_stopped) { - if (status & UART_MSR_CTS) { - info->tty->hw_stopped = 0; - if (info->type != PORT_16550A && - !info->board->chip_flag) { - outb(info->IER & ~UART_IER_THRI, - info->ioaddr + - UART_IER); - info->IER |= UART_IER_THRI; - outb(info->IER, info->ioaddr + - UART_IER); - } - tty_wakeup(info->tty); - } - } else { - if (!(status & UART_MSR_CTS)) { - info->tty->hw_stopped = 1; - if ((info->type != PORT_16550A) && - (!info->board->chip_flag)) { - info->IER &= ~UART_IER_THRI; - outb(info->IER, info->ioaddr + - UART_IER); - } - } - } - } - } else { - info->flags &= ~ASYNC_CTS_FLOW; - } - outb(info->MCR, info->ioaddr + UART_MCR); - if (cflag & CLOCAL) { - info->flags &= ~ASYNC_CHECK_CD; - } else { - info->flags |= ASYNC_CHECK_CD; - info->IER |= UART_IER_MSI; - } - outb(info->IER, info->ioaddr + UART_IER); - - /* - * Set up parity check flag - */ - info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; - if (I_INPCK(info->tty)) - info->read_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) - info->read_status_mask |= UART_LSR_BI; - - info->ignore_status_mask = 0; - - if (I_IGNBRK(info->tty)) { - info->ignore_status_mask |= UART_LSR_BI; - info->read_status_mask |= UART_LSR_BI; - /* - * If we're ignore parity and break indicators, ignore - * overruns too. (For real raw support). - */ - if (I_IGNPAR(info->tty)) { - info->ignore_status_mask |= - UART_LSR_OE | - UART_LSR_PE | - UART_LSR_FE; - info->read_status_mask |= - UART_LSR_OE | - UART_LSR_PE | - UART_LSR_FE; - } - } - if (info->board->chip_flag) { - SET_MOXA_MUST_XON1_VALUE(info->ioaddr, START_CHAR(info->tty)); - SET_MOXA_MUST_XOFF1_VALUE(info->ioaddr, STOP_CHAR(info->tty)); - if (I_IXON(info->tty)) { - ENABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL(info->ioaddr); - } else { - DISABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL(info->ioaddr); - } - if (I_IXOFF(info->tty)) { - ENABLE_MOXA_MUST_TX_SOFTWARE_FLOW_CONTROL(info->ioaddr); - } else { - DISABLE_MOXA_MUST_TX_SOFTWARE_FLOW_CONTROL(info->ioaddr); - } - } - - - outb(fcr, info->ioaddr + UART_FCR); /* set fcr */ - outb(cval, info->ioaddr + UART_LCR); - - return ret; -} - -static void mxser_check_modem_status(struct mxser_port *port, int status) -{ - /* update input line counters */ - if (status & UART_MSR_TERI) - port->icount.rng++; - if (status & UART_MSR_DDSR) - port->icount.dsr++; - if (status & UART_MSR_DDCD) - port->icount.dcd++; - if (status & UART_MSR_DCTS) - port->icount.cts++; - port->mon_data.modem_status = status; - wake_up_interruptible(&port->delta_msr_wait); - - if ((port->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { - if (status & UART_MSR_DCD) - wake_up_interruptible(&port->open_wait); - } - - if (port->flags & ASYNC_CTS_FLOW) { - if (port->tty->hw_stopped) { - if (status & UART_MSR_CTS) { - port->tty->hw_stopped = 0; - - if ((port->type != PORT_16550A) && - (!port->board->chip_flag)) { - outb(port->IER & ~UART_IER_THRI, - port->ioaddr + UART_IER); - port->IER |= UART_IER_THRI; - outb(port->IER, port->ioaddr + - UART_IER); - } - tty_wakeup(port->tty); - } - } else { - if (!(status & UART_MSR_CTS)) { - port->tty->hw_stopped = 1; - if (port->type != PORT_16550A && - !port->board->chip_flag) { - port->IER &= ~UART_IER_THRI; - outb(port->IER, port->ioaddr + - UART_IER); - } - } - } - } -} - -static int mxser_startup(struct mxser_port *info) -{ - unsigned long page; - unsigned long flags; - - page = __get_free_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - - spin_lock_irqsave(&info->slock, flags); - - if (info->flags & ASYNC_INITIALIZED) { - free_page(page); - spin_unlock_irqrestore(&info->slock, flags); - return 0; - } - - if (!info->ioaddr || !info->type) { - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); - free_page(page); - spin_unlock_irqrestore(&info->slock, flags); - return 0; - } - if (info->xmit_buf) - free_page(page); - else - info->xmit_buf = (unsigned char *) page; - - /* - * Clear the FIFO buffers and disable them - * (they will be reenabled in mxser_change_speed()) - */ - if (info->board->chip_flag) - outb((UART_FCR_CLEAR_RCVR | - UART_FCR_CLEAR_XMIT | - MOXA_MUST_FCR_GDA_MODE_ENABLE), info->ioaddr + UART_FCR); - else - outb((UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT), - info->ioaddr + UART_FCR); - - /* - * At this point there's no way the LSR could still be 0xFF; - * if it is, then bail out, because there's likely no UART - * here. - */ - if (inb(info->ioaddr + UART_LSR) == 0xff) { - spin_unlock_irqrestore(&info->slock, flags); - if (capable(CAP_SYS_ADMIN)) { - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); - return 0; - } else - return -ENODEV; - } - - /* - * Clear the interrupt registers. - */ - (void) inb(info->ioaddr + UART_LSR); - (void) inb(info->ioaddr + UART_RX); - (void) inb(info->ioaddr + UART_IIR); - (void) inb(info->ioaddr + UART_MSR); - - /* - * Now, initialize the UART - */ - outb(UART_LCR_WLEN8, info->ioaddr + UART_LCR); /* reset DLAB */ - info->MCR = UART_MCR_DTR | UART_MCR_RTS; - outb(info->MCR, info->ioaddr + UART_MCR); - - /* - * Finally, enable interrupts - */ - info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; - - if (info->board->chip_flag) - info->IER |= MOXA_MUST_IER_EGDAI; - outb(info->IER, info->ioaddr + UART_IER); /* enable interrupts */ - - /* - * And clear the interrupt registers again for luck. - */ - (void) inb(info->ioaddr + UART_LSR); - (void) inb(info->ioaddr + UART_RX); - (void) inb(info->ioaddr + UART_IIR); - (void) inb(info->ioaddr + UART_MSR); - - if (info->tty) - clear_bit(TTY_IO_ERROR, &info->tty->flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - - /* - * and set the speed of the serial port - */ - mxser_change_speed(info, NULL); - info->flags |= ASYNC_INITIALIZED; - spin_unlock_irqrestore(&info->slock, flags); - - return 0; -} - -/* - * This routine will shutdown a serial port; interrupts maybe disabled, and - * DTR is dropped if the hangup on close termio flag is on. - */ -static void mxser_shutdown(struct mxser_port *info) -{ - unsigned long flags; - - if (!(info->flags & ASYNC_INITIALIZED)) - return; - - spin_lock_irqsave(&info->slock, flags); - - /* - * clear delta_msr_wait queue to avoid mem leaks: we may free the irq - * here so the queue might never be waken up - */ - wake_up_interruptible(&info->delta_msr_wait); - - /* - * Free the IRQ, if necessary - */ - if (info->xmit_buf) { - free_page((unsigned long) info->xmit_buf); - info->xmit_buf = NULL; - } - - info->IER = 0; - outb(0x00, info->ioaddr + UART_IER); - - if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) - info->MCR &= ~(UART_MCR_DTR | UART_MCR_RTS); - outb(info->MCR, info->ioaddr + UART_MCR); - - /* clear Rx/Tx FIFO's */ - if (info->board->chip_flag) - outb(UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT | - MOXA_MUST_FCR_GDA_MODE_ENABLE, - info->ioaddr + UART_FCR); - else - outb(UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, - info->ioaddr + UART_FCR); - - /* read data port to reset things */ - (void) inb(info->ioaddr + UART_RX); - - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); - - info->flags &= ~ASYNC_INITIALIZED; - - if (info->board->chip_flag) - SET_MOXA_MUST_NO_SOFTWARE_FLOW_CONTROL(info->ioaddr); - - spin_unlock_irqrestore(&info->slock, flags); -} - -/* - * This routine is called whenever a serial port is opened. It - * enables interrupts for a serial port, linking in its async structure into - * the IRQ chain. It also performs the serial-specific - * initialization for the tty structure. - */ -static int mxser_open(struct tty_struct *tty, struct file *filp) -{ - struct mxser_port *info; - unsigned long flags; - int retval, line; - - line = tty->index; - if (line == MXSER_PORTS) - return 0; - if (line < 0 || line > MXSER_PORTS) - return -ENODEV; - info = &mxser_boards[line / MXSER_PORTS_PER_BOARD].ports[line % MXSER_PORTS_PER_BOARD]; - if (!info->ioaddr) - return -ENODEV; - - tty->driver_data = info; - info->tty = tty; - /* - * Start up serial port - */ - spin_lock_irqsave(&info->slock, flags); - info->count++; - spin_unlock_irqrestore(&info->slock, flags); - retval = mxser_startup(info); - if (retval) - return retval; - - retval = mxser_block_til_ready(tty, filp, info); - if (retval) - return retval; - - /* unmark here for very high baud rate (ex. 921600 bps) used */ - tty->low_latency = 1; - return 0; -} - -/* - * This routine is called when the serial port gets closed. First, we - * wait for the last remaining data to be sent. Then, we unlink its - * async structure from the interrupt chain if necessary, and we free - * that IRQ if nothing is left in the chain. - */ -static void mxser_close(struct tty_struct *tty, struct file *filp) -{ - struct mxser_port *info = tty->driver_data; - - unsigned long timeout; - unsigned long flags; - - if (tty->index == MXSER_PORTS) - return; - if (!info) - return; - - spin_lock_irqsave(&info->slock, flags); - - if (tty_hung_up_p(filp)) { - spin_unlock_irqrestore(&info->slock, flags); - return; - } - if ((tty->count == 1) && (info->count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. Info->count should always - * be one in these conditions. If it's greater than - * one, we've got real problems, since it means the - * serial port won't be shutdown. - */ - printk(KERN_ERR "mxser_close: bad serial port count; " - "tty->count is 1, info->count is %d\n", info->count); - info->count = 1; - } - if (--info->count < 0) { - printk(KERN_ERR "mxser_close: bad serial port count for " - "ttys%d: %d\n", tty->index, info->count); - info->count = 0; - } - if (info->count) { - spin_unlock_irqrestore(&info->slock, flags); - return; - } - info->flags |= ASYNC_CLOSING; - spin_unlock_irqrestore(&info->slock, flags); - /* - * Save the termios structure, since this port may have - * separate termios for callout and dialin. - */ - if (info->flags & ASYNC_NORMAL_ACTIVE) - info->normal_termios = *tty->termios; - /* - * Now we wait for the transmit buffer to clear; and we notify - * the line discipline to only process XON/XOFF characters. - */ - tty->closing = 1; - if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, info->closing_wait); - /* - * At this point we stop accepting input. To do this, we - * disable the receive line status interrupts, and tell the - * interrupt driver to stop checking the data ready bit in the - * line status register. - */ - info->IER &= ~UART_IER_RLSI; - if (info->board->chip_flag) - info->IER &= ~MOXA_MUST_RECV_ISR; - - if (info->flags & ASYNC_INITIALIZED) { - outb(info->IER, info->ioaddr + UART_IER); - /* - * Before we drop DTR, make sure the UART transmitter - * has completely drained; this is especially - * important if there is a transmit FIFO! - */ - timeout = jiffies + HZ; - while (!(inb(info->ioaddr + UART_LSR) & UART_LSR_TEMT)) { - schedule_timeout_interruptible(5); - if (time_after(jiffies, timeout)) - break; - } - } - mxser_shutdown(info); - - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); - - tty_ldisc_flush(tty); - - tty->closing = 0; - info->event = 0; - info->tty = NULL; - if (info->blocked_open) { - if (info->close_delay) - schedule_timeout_interruptible(info->close_delay); - wake_up_interruptible(&info->open_wait); - } - - info->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); -} - -static int mxser_write(struct tty_struct *tty, const unsigned char *buf, int count) -{ - int c, total = 0; - struct mxser_port *info = tty->driver_data; - unsigned long flags; - - if (!info->xmit_buf) - return 0; - - while (1) { - c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, - SERIAL_XMIT_SIZE - info->xmit_head)); - if (c <= 0) - break; - - memcpy(info->xmit_buf + info->xmit_head, buf, c); - spin_lock_irqsave(&info->slock, flags); - info->xmit_head = (info->xmit_head + c) & - (SERIAL_XMIT_SIZE - 1); - info->xmit_cnt += c; - spin_unlock_irqrestore(&info->slock, flags); - - buf += c; - count -= c; - total += c; - } - - if (info->xmit_cnt && !tty->stopped) { - if (!tty->hw_stopped || - (info->type == PORT_16550A) || - (info->board->chip_flag)) { - spin_lock_irqsave(&info->slock, flags); - outb(info->IER & ~UART_IER_THRI, info->ioaddr + - UART_IER); - info->IER |= UART_IER_THRI; - outb(info->IER, info->ioaddr + UART_IER); - spin_unlock_irqrestore(&info->slock, flags); - } - } - return total; -} - -static void mxser_put_char(struct tty_struct *tty, unsigned char ch) -{ - struct mxser_port *info = tty->driver_data; - unsigned long flags; - - if (!info->xmit_buf) - return; - - if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) - return; - - spin_lock_irqsave(&info->slock, flags); - info->xmit_buf[info->xmit_head++] = ch; - info->xmit_head &= SERIAL_XMIT_SIZE - 1; - info->xmit_cnt++; - spin_unlock_irqrestore(&info->slock, flags); - if (!tty->stopped) { - if (!tty->hw_stopped || - (info->type == PORT_16550A) || - info->board->chip_flag) { - spin_lock_irqsave(&info->slock, flags); - outb(info->IER & ~UART_IER_THRI, info->ioaddr + UART_IER); - info->IER |= UART_IER_THRI; - outb(info->IER, info->ioaddr + UART_IER); - spin_unlock_irqrestore(&info->slock, flags); - } - } -} - - -static void mxser_flush_chars(struct tty_struct *tty) -{ - struct mxser_port *info = tty->driver_data; - unsigned long flags; - - if (info->xmit_cnt <= 0 || - tty->stopped || - !info->xmit_buf || - (tty->hw_stopped && - (info->type != PORT_16550A) && - (!info->board->chip_flag) - )) - return; - - spin_lock_irqsave(&info->slock, flags); - - outb(info->IER & ~UART_IER_THRI, info->ioaddr + UART_IER); - info->IER |= UART_IER_THRI; - outb(info->IER, info->ioaddr + UART_IER); - - spin_unlock_irqrestore(&info->slock, flags); -} - -static int mxser_write_room(struct tty_struct *tty) -{ - struct mxser_port *info = tty->driver_data; - int ret; - - ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; - if (ret < 0) - ret = 0; - return ret; -} - -static int mxser_chars_in_buffer(struct tty_struct *tty) -{ - struct mxser_port *info = tty->driver_data; - return info->xmit_cnt; -} - -static void mxser_flush_buffer(struct tty_struct *tty) -{ - struct mxser_port *info = tty->driver_data; - char fcr; - unsigned long flags; - - - spin_lock_irqsave(&info->slock, flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - - fcr = inb(info->ioaddr + UART_FCR); - outb((fcr | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT), - info->ioaddr + UART_FCR); - outb(fcr, info->ioaddr + UART_FCR); - - spin_unlock_irqrestore(&info->slock, flags); - - tty_wakeup(tty); -} - -/* - * ------------------------------------------------------------ - * friends of mxser_ioctl() - * ------------------------------------------------------------ - */ -static int mxser_get_serial_info(struct mxser_port *info, - struct serial_struct __user *retinfo) -{ - struct serial_struct tmp; - - if (!retinfo) - return -EFAULT; - memset(&tmp, 0, sizeof(tmp)); - tmp.type = info->type; - tmp.line = info->tty->index; - tmp.port = info->ioaddr; - tmp.irq = info->board->irq; - tmp.flags = info->flags; - tmp.baud_base = info->baud_base; - tmp.close_delay = info->close_delay; - tmp.closing_wait = info->closing_wait; - tmp.custom_divisor = info->custom_divisor; - tmp.hub6 = 0; - if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) - return -EFAULT; - return 0; -} - -static int mxser_set_serial_info(struct mxser_port *info, - struct serial_struct __user *new_info) -{ - struct serial_struct new_serial; - unsigned long sl_flags; - unsigned int flags; - int retval = 0; - - if (!new_info || !info->ioaddr) - return -EFAULT; - if (copy_from_user(&new_serial, new_info, sizeof(new_serial))) - return -EFAULT; - - if ((new_serial.irq != info->board->irq) || - (new_serial.port != info->ioaddr) || - (new_serial.custom_divisor != info->custom_divisor) || - (new_serial.baud_base != info->baud_base)) - return -EPERM; - - flags = info->flags & ASYNC_SPD_MASK; - - if (!capable(CAP_SYS_ADMIN)) { - if ((new_serial.baud_base != info->baud_base) || - (new_serial.close_delay != info->close_delay) || - ((new_serial.flags & ~ASYNC_USR_MASK) != (info->flags & ~ASYNC_USR_MASK))) - return -EPERM; - info->flags = ((info->flags & ~ASYNC_USR_MASK) | - (new_serial.flags & ASYNC_USR_MASK)); - } else { - /* - * OK, past this point, all the error checking has been done. - * At this point, we start making changes..... - */ - info->flags = ((info->flags & ~ASYNC_FLAGS) | - (new_serial.flags & ASYNC_FLAGS)); - info->close_delay = new_serial.close_delay * HZ / 100; - info->closing_wait = new_serial.closing_wait * HZ / 100; - info->tty->low_latency = - (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; - info->tty->low_latency = 0; - } - - info->type = new_serial.type; - - process_txrx_fifo(info); - - if (info->flags & ASYNC_INITIALIZED) { - if (flags != (info->flags & ASYNC_SPD_MASK)) { - spin_lock_irqsave(&info->slock, sl_flags); - mxser_change_speed(info, NULL); - spin_unlock_irqrestore(&info->slock, sl_flags); - } - } else - retval = mxser_startup(info); - - return retval; -} - -/* - * mxser_get_lsr_info - get line status register info - * - * Purpose: Let user call ioctl() to get info when the UART physically - * is emptied. On bus types like RS485, the transmitter must - * release the bus after transmitting. This must be done when - * the transmit shift register is empty, not be done when the - * transmit holding register is empty. This functionality - * allows an RS485 driver to be written in user space. - */ -static int mxser_get_lsr_info(struct mxser_port *info, - unsigned int __user *value) -{ - unsigned char status; - unsigned int result; - unsigned long flags; - - spin_lock_irqsave(&info->slock, flags); - status = inb(info->ioaddr + UART_LSR); - spin_unlock_irqrestore(&info->slock, flags); - result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); - return put_user(result, value); -} - -/* - * This routine sends a break character out the serial port. - */ -static void mxser_send_break(struct mxser_port *info, int duration) -{ - unsigned long flags; - - if (!info->ioaddr) - return; - set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irqsave(&info->slock, flags); - outb(inb(info->ioaddr + UART_LCR) | UART_LCR_SBC, - info->ioaddr + UART_LCR); - spin_unlock_irqrestore(&info->slock, flags); - schedule_timeout(duration); - spin_lock_irqsave(&info->slock, flags); - outb(inb(info->ioaddr + UART_LCR) & ~UART_LCR_SBC, - info->ioaddr + UART_LCR); - spin_unlock_irqrestore(&info->slock, flags); -} - -static int mxser_tiocmget(struct tty_struct *tty, struct file *file) -{ - struct mxser_port *info = tty->driver_data; - unsigned char control, status; - unsigned long flags; - - - if (tty->index == MXSER_PORTS) - return -ENOIOCTLCMD; - if (test_bit(TTY_IO_ERROR, &tty->flags)) - return -EIO; - - control = info->MCR; - - spin_lock_irqsave(&info->slock, flags); - status = inb(info->ioaddr + UART_MSR); - if (status & UART_MSR_ANY_DELTA) - mxser_check_modem_status(info, status); - spin_unlock_irqrestore(&info->slock, flags); - return ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) | - ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) | - ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) | - ((status & UART_MSR_RI) ? TIOCM_RNG : 0) | - ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) | - ((status & UART_MSR_CTS) ? TIOCM_CTS : 0); -} - -static int mxser_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) -{ - struct mxser_port *info = tty->driver_data; - unsigned long flags; - - - if (tty->index == MXSER_PORTS) - return -ENOIOCTLCMD; - if (test_bit(TTY_IO_ERROR, &tty->flags)) - return -EIO; - - spin_lock_irqsave(&info->slock, flags); - - if (set & TIOCM_RTS) - info->MCR |= UART_MCR_RTS; - if (set & TIOCM_DTR) - info->MCR |= UART_MCR_DTR; - - if (clear & TIOCM_RTS) - info->MCR &= ~UART_MCR_RTS; - if (clear & TIOCM_DTR) - info->MCR &= ~UART_MCR_DTR; - - outb(info->MCR, info->ioaddr + UART_MCR); - spin_unlock_irqrestore(&info->slock, flags); - return 0; -} - -static int __init mxser_program_mode(int port) -{ - int id, i, j, n; - - outb(0, port); - outb(0, port); - outb(0, port); - (void)inb(port); - (void)inb(port); - outb(0, port); - (void)inb(port); - - id = inb(port + 1) & 0x1F; - if ((id != C168_ASIC_ID) && - (id != C104_ASIC_ID) && - (id != C102_ASIC_ID) && - (id != CI132_ASIC_ID) && - (id != CI134_ASIC_ID) && - (id != CI104J_ASIC_ID)) - return -1; - for (i = 0, j = 0; i < 4; i++) { - n = inb(port + 2); - if (n == 'M') { - j = 1; - } else if ((j == 1) && (n == 1)) { - j = 2; - break; - } else - j = 0; - } - if (j != 2) - id = -2; - return id; -} - -static void __init mxser_normal_mode(int port) -{ - int i, n; - - outb(0xA5, port + 1); - outb(0x80, port + 3); - outb(12, port + 0); /* 9600 bps */ - outb(0, port + 1); - outb(0x03, port + 3); /* 8 data bits */ - outb(0x13, port + 4); /* loop back mode */ - for (i = 0; i < 16; i++) { - n = inb(port + 5); - if ((n & 0x61) == 0x60) - break; - if ((n & 1) == 1) - (void)inb(port); - } - outb(0x00, port + 4); -} - -#define CHIP_SK 0x01 /* Serial Data Clock in Eprom */ -#define CHIP_DO 0x02 /* Serial Data Output in Eprom */ -#define CHIP_CS 0x04 /* Serial Chip Select in Eprom */ -#define CHIP_DI 0x08 /* Serial Data Input in Eprom */ -#define EN_CCMD 0x000 /* Chip's command register */ -#define EN0_RSARLO 0x008 /* Remote start address reg 0 */ -#define EN0_RSARHI 0x009 /* Remote start address reg 1 */ -#define EN0_RCNTLO 0x00A /* Remote byte count reg WR */ -#define EN0_RCNTHI 0x00B /* Remote byte count reg WR */ -#define EN0_DCFG 0x00E /* Data configuration reg WR */ -#define EN0_PORT 0x010 /* Rcv missed frame error counter RD */ -#define ENC_PAGE0 0x000 /* Select page 0 of chip registers */ -#define ENC_PAGE3 0x0C0 /* Select page 3 of chip registers */ -static int __init mxser_read_register(int port, unsigned short *regs) -{ - int i, k, value, id; - unsigned int j; - - id = mxser_program_mode(port); - if (id < 0) - return id; - for (i = 0; i < 14; i++) { - k = (i & 0x3F) | 0x180; - for (j = 0x100; j > 0; j >>= 1) { - outb(CHIP_CS, port); - if (k & j) { - outb(CHIP_CS | CHIP_DO, port); - outb(CHIP_CS | CHIP_DO | CHIP_SK, port); /* A? bit of read */ - } else { - outb(CHIP_CS, port); - outb(CHIP_CS | CHIP_SK, port); /* A? bit of read */ - } - } - (void)inb(port); - value = 0; - for (k = 0, j = 0x8000; k < 16; k++, j >>= 1) { - outb(CHIP_CS, port); - outb(CHIP_CS | CHIP_SK, port); - if (inb(port) & CHIP_DI) - value |= j; - } - regs[i] = value; - outb(0, port); - } - mxser_normal_mode(port); - return id; -} - -static int mxser_ioctl_special(unsigned int cmd, void __user *argp) -{ - struct mxser_port *port; - int result, status; - unsigned int i, j; - - switch (cmd) { - case MOXA_GET_CONF: -/* if (copy_to_user(argp, mxsercfg, - sizeof(struct mxser_hwconf) * 4)) - return -EFAULT; - return 0;*/ - return -ENXIO; - case MOXA_GET_MAJOR: - if (copy_to_user(argp, &ttymajor, sizeof(int))) - return -EFAULT; - return 0; - - case MOXA_GET_CUMAJOR: - if (copy_to_user(argp, &calloutmajor, sizeof(int))) - return -EFAULT; - return 0; - - case MOXA_CHKPORTENABLE: - result = 0; - - for (i = 0; i < MXSER_BOARDS; i++) - for (j = 0; j < MXSER_PORTS_PER_BOARD; j++) - if (mxser_boards[i].ports[j].ioaddr) - result |= (1 << i); - - return put_user(result, (unsigned long __user *)argp); - case MOXA_GETDATACOUNT: - if (copy_to_user(argp, &mxvar_log, sizeof(mxvar_log))) - return -EFAULT; - return 0; - case MOXA_GETMSTATUS: - for (i = 0; i < MXSER_BOARDS; i++) - for (j = 0; j < MXSER_PORTS_PER_BOARD; j++) { - port = &mxser_boards[i].ports[j]; - - GMStatus[i].ri = 0; - if (!port->ioaddr) { - GMStatus[i].dcd = 0; - GMStatus[i].dsr = 0; - GMStatus[i].cts = 0; - continue; - } - - if (!port->tty || !port->tty->termios) - GMStatus[i].cflag = - port->normal_termios.c_cflag; - else - GMStatus[i].cflag = - port->tty->termios->c_cflag; - - status = inb(port->ioaddr + UART_MSR); - if (status & 0x80 /*UART_MSR_DCD */ ) - GMStatus[i].dcd = 1; - else - GMStatus[i].dcd = 0; - - if (status & 0x20 /*UART_MSR_DSR */ ) - GMStatus[i].dsr = 1; - else - GMStatus[i].dsr = 0; - - - if (status & 0x10 /*UART_MSR_CTS */ ) - GMStatus[i].cts = 1; - else - GMStatus[i].cts = 0; - } - if (copy_to_user(argp, GMStatus, - sizeof(struct mxser_mstatus) * MXSER_PORTS)) - return -EFAULT; - return 0; - case MOXA_ASPP_MON_EXT: { - int p, shiftbit; - unsigned long opmode; - unsigned cflag, iflag; - - for (i = 0; i < MXSER_BOARDS; i++) - for (j = 0; j < MXSER_PORTS_PER_BOARD; j++) { - port = &mxser_boards[i].ports[j]; - if (!port->ioaddr) - continue; - - status = mxser_get_msr(port->ioaddr, 0, i); - - if (status & UART_MSR_TERI) - port->icount.rng++; - if (status & UART_MSR_DDSR) - port->icount.dsr++; - if (status & UART_MSR_DDCD) - port->icount.dcd++; - if (status & UART_MSR_DCTS) - port->icount.cts++; - - port->mon_data.modem_status = status; - mon_data_ext.rx_cnt[i] = port->mon_data.rxcnt; - mon_data_ext.tx_cnt[i] = port->mon_data.txcnt; - mon_data_ext.up_rxcnt[i] = - port->mon_data.up_rxcnt; - mon_data_ext.up_txcnt[i] = - port->mon_data.up_txcnt; - mon_data_ext.modem_status[i] = - port->mon_data.modem_status; - mon_data_ext.baudrate[i] = port->realbaud; - - if (!port->tty || !port->tty->termios) { - cflag = port->normal_termios.c_cflag; - iflag = port->normal_termios.c_iflag; - } else { - cflag = port->tty->termios->c_cflag; - iflag = port->tty->termios->c_iflag; - } - - mon_data_ext.databits[i] = cflag & CSIZE; - - mon_data_ext.stopbits[i] = cflag & CSTOPB; - - mon_data_ext.parity[i] = - cflag & (PARENB | PARODD | CMSPAR); - - mon_data_ext.flowctrl[i] = 0x00; - - if (cflag & CRTSCTS) - mon_data_ext.flowctrl[i] |= 0x03; - - if (iflag & (IXON | IXOFF)) - mon_data_ext.flowctrl[i] |= 0x0C; - - if (port->type == PORT_16550A) - mon_data_ext.fifo[i] = 1; - else - mon_data_ext.fifo[i] = 0; - - p = i % 4; - shiftbit = p * 2; - opmode = inb(port->opmode_ioaddr) >> shiftbit; - opmode &= OP_MODE_MASK; - - mon_data_ext.iftype[i] = opmode; - - } - if (copy_to_user(argp, &mon_data_ext, - sizeof(mon_data_ext))) - return -EFAULT; - - return 0; - - } default: - return -ENOIOCTLCMD; - } - return 0; -} - -static int mxser_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct mxser_port *info = tty->driver_data; - struct async_icount cprev, cnow; /* kernel counter temps */ - struct serial_icounter_struct __user *p_cuser; - unsigned long templ; - unsigned long flags; - unsigned int i; - void __user *argp = (void __user *)arg; - int retval; - - if (tty->index == MXSER_PORTS) - return mxser_ioctl_special(cmd, argp); - - if (cmd == MOXA_SET_OP_MODE || cmd == MOXA_GET_OP_MODE) { - int p; - unsigned long opmode; - static unsigned char ModeMask[] = { 0xfc, 0xf3, 0xcf, 0x3f }; - int shiftbit; - unsigned char val, mask; - - p = tty->index % 4; - if (cmd == MOXA_SET_OP_MODE) { - if (get_user(opmode, (int __user *) argp)) - return -EFAULT; - if (opmode != RS232_MODE && - opmode != RS485_2WIRE_MODE && - opmode != RS422_MODE && - opmode != RS485_4WIRE_MODE) - return -EFAULT; - mask = ModeMask[p]; - shiftbit = p * 2; - val = inb(info->opmode_ioaddr); - val &= mask; - val |= (opmode << shiftbit); - outb(val, info->opmode_ioaddr); - } else { - shiftbit = p * 2; - opmode = inb(info->opmode_ioaddr) >> shiftbit; - opmode &= OP_MODE_MASK; - if (copy_to_user(argp, &opmode, sizeof(int))) - return -EFAULT; - } - return 0; - } - - if (cmd == MOXA_SET_SPECIAL_BAUD_RATE) { - int speed; - - if (get_user(speed, (int __user *)argp)) - return -EFAULT; - if (speed <= 0 || speed > info->max_baud) - return -EFAULT; - if (!info->tty || !info->tty->termios || !info->ioaddr) - return 0; - info->tty->termios->c_cflag &= ~(CBAUD | CBAUDEX); - for (i = 0; i < BAUD_TABLE_NO; i++) - if (speed == mxvar_baud_table[i]) - break; - if (i == BAUD_TABLE_NO) { - info->tty->termios->c_cflag |= B_SPEC; - } else if (speed != 0) - info->tty->termios->c_cflag |= mxvar_baud_table1[i]; - - info->speed = speed; - spin_lock_irqsave(&info->slock, flags); - mxser_change_speed(info, NULL); - spin_unlock_irqrestore(&info->slock, flags); - - return 0; - } else if (cmd == MOXA_GET_SPECIAL_BAUD_RATE) { - if (copy_to_user(argp, &info->speed, sizeof(int))) - return -EFAULT; - return 0; - } - - if (cmd != TIOCGSERIAL && cmd != TIOCMIWAIT && cmd != TIOCGICOUNT && - test_bit(TTY_IO_ERROR, &tty->flags)) - return -EIO; - - switch (cmd) { - case TCSBRK: /* SVID version: non-zero arg --> no break */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - if (!arg) - mxser_send_break(info, HZ / 4); /* 1/4 second */ - return 0; - case TCSBRKP: /* support for POSIX tcsendbreak() */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - mxser_send_break(info, arg ? arg * (HZ / 10) : HZ / 4); - return 0; - case TIOCGSOFTCAR: - return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long __user *)argp); - case TIOCSSOFTCAR: - if (get_user(templ, (unsigned long __user *) argp)) - return -EFAULT; - arg = templ; - tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); - return 0; - case TIOCGSERIAL: - return mxser_get_serial_info(info, argp); - case TIOCSSERIAL: - return mxser_set_serial_info(info, argp); - case TIOCSERGETLSR: /* Get line status register */ - return mxser_get_lsr_info(info, argp); - /* - * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change - * - mask passed in arg for lines of interest - * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) - * Caller should use TIOCGICOUNT to see which one it was - */ - case TIOCMIWAIT: - spin_lock_irqsave(&info->slock, flags); - cnow = info->icount; /* note the counters on entry */ - spin_unlock_irqrestore(&info->slock, flags); - - wait_event_interruptible(info->delta_msr_wait, ({ - cprev = cnow; - spin_lock_irqsave(&info->slock, flags); - cnow = info->icount; /* atomic copy */ - spin_unlock_irqrestore(&info->slock, flags); - - ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || - ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || - ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || - ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)); - })); - break; - /* - * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) - * Return: write counters to the user passed counter struct - * NB: both 1->0 and 0->1 transitions are counted except for - * RI where only 0->1 is counted. - */ - case TIOCGICOUNT: - spin_lock_irqsave(&info->slock, flags); - cnow = info->icount; - spin_unlock_irqrestore(&info->slock, flags); - p_cuser = argp; - if (put_user(cnow.frame, &p_cuser->frame)) - return -EFAULT; - if (put_user(cnow.brk, &p_cuser->brk)) - return -EFAULT; - if (put_user(cnow.overrun, &p_cuser->overrun)) - return -EFAULT; - if (put_user(cnow.buf_overrun, &p_cuser->buf_overrun)) - return -EFAULT; - if (put_user(cnow.parity, &p_cuser->parity)) - return -EFAULT; - if (put_user(cnow.rx, &p_cuser->rx)) - return -EFAULT; - if (put_user(cnow.tx, &p_cuser->tx)) - return -EFAULT; - put_user(cnow.cts, &p_cuser->cts); - put_user(cnow.dsr, &p_cuser->dsr); - put_user(cnow.rng, &p_cuser->rng); - put_user(cnow.dcd, &p_cuser->dcd); - return 0; - case MOXA_HighSpeedOn: - return put_user(info->baud_base != 115200 ? 1 : 0, (int __user *)argp); - case MOXA_SDS_RSTICOUNTER: - info->mon_data.rxcnt = 0; - info->mon_data.txcnt = 0; - return 0; - case MOXA_ASPP_SETBAUD:{ - long baud; - if (get_user(baud, (long __user *)argp)) - return -EFAULT; - spin_lock_irqsave(&info->slock, flags); - mxser_set_baud(info, baud); - spin_unlock_irqrestore(&info->slock, flags); - return 0; - } - case MOXA_ASPP_GETBAUD: - if (copy_to_user(argp, &info->realbaud, sizeof(long))) - return -EFAULT; - - return 0; - - case MOXA_ASPP_OQUEUE:{ - int len, lsr; - - len = mxser_chars_in_buffer(tty); - - lsr = inb(info->ioaddr + UART_LSR) & UART_LSR_TEMT; - - len += (lsr ? 0 : 1); - - if (copy_to_user(argp, &len, sizeof(int))) - return -EFAULT; - - return 0; - } - case MOXA_ASPP_MON: { - int mcr, status; - - status = mxser_get_msr(info->ioaddr, 1, tty->index); - mxser_check_modem_status(info, status); - - mcr = inb(info->ioaddr + UART_MCR); - if (mcr & MOXA_MUST_MCR_XON_FLAG) - info->mon_data.hold_reason &= ~NPPI_NOTIFY_XOFFHOLD; - else - info->mon_data.hold_reason |= NPPI_NOTIFY_XOFFHOLD; - - if (mcr & MOXA_MUST_MCR_TX_XON) - info->mon_data.hold_reason &= ~NPPI_NOTIFY_XOFFXENT; - else - info->mon_data.hold_reason |= NPPI_NOTIFY_XOFFXENT; - - if (info->tty->hw_stopped) - info->mon_data.hold_reason |= NPPI_NOTIFY_CTSHOLD; - else - info->mon_data.hold_reason &= ~NPPI_NOTIFY_CTSHOLD; - - if (copy_to_user(argp, &info->mon_data, - sizeof(struct mxser_mon))) - return -EFAULT; - - return 0; - } - case MOXA_ASPP_LSTATUS: { - if (copy_to_user(argp, &info->err_shadow, - sizeof(unsigned char))) - return -EFAULT; - - info->err_shadow = 0; - return 0; - } - case MOXA_SET_BAUD_METHOD: { - int method; - - if (get_user(method, (int __user *)argp)) - return -EFAULT; - mxser_set_baud_method[tty->index] = method; - if (copy_to_user(argp, &method, sizeof(int))) - return -EFAULT; - - return 0; - } - default: - return -ENOIOCTLCMD; - } - return 0; -} - -static void mxser_stoprx(struct tty_struct *tty) -{ - struct mxser_port *info = tty->driver_data; - - info->ldisc_stop_rx = 1; - if (I_IXOFF(tty)) { - if (info->board->chip_flag) { - info->IER &= ~MOXA_MUST_RECV_ISR; - outb(info->IER, info->ioaddr + UART_IER); - } else { - info->x_char = STOP_CHAR(tty); - outb(0, info->ioaddr + UART_IER); - info->IER |= UART_IER_THRI; - outb(info->IER, info->ioaddr + UART_IER); - } - } - - if (info->tty->termios->c_cflag & CRTSCTS) { - info->MCR &= ~UART_MCR_RTS; - outb(info->MCR, info->ioaddr + UART_MCR); - } -} - -/* - * This routine is called by the upper-layer tty layer to signal that - * incoming characters should be throttled. - */ -static void mxser_throttle(struct tty_struct *tty) -{ - mxser_stoprx(tty); -} - -static void mxser_unthrottle(struct tty_struct *tty) -{ - struct mxser_port *info = tty->driver_data; - - /* startrx */ - info->ldisc_stop_rx = 0; - if (I_IXOFF(tty)) { - if (info->x_char) - info->x_char = 0; - else { - if (info->board->chip_flag) { - info->IER |= MOXA_MUST_RECV_ISR; - outb(info->IER, info->ioaddr + UART_IER); - } else { - info->x_char = START_CHAR(tty); - outb(0, info->ioaddr + UART_IER); - info->IER |= UART_IER_THRI; - outb(info->IER, info->ioaddr + UART_IER); - } - } - } - - if (info->tty->termios->c_cflag & CRTSCTS) { - info->MCR |= UART_MCR_RTS; - outb(info->MCR, info->ioaddr + UART_MCR); - } -} - -/* - * mxser_stop() and mxser_start() - * - * This routines are called before setting or resetting tty->stopped. - * They enable or disable transmitter interrupts, as necessary. - */ -static void mxser_stop(struct tty_struct *tty) -{ - struct mxser_port *info = tty->driver_data; - unsigned long flags; - - spin_lock_irqsave(&info->slock, flags); - if (info->IER & UART_IER_THRI) { - info->IER &= ~UART_IER_THRI; - outb(info->IER, info->ioaddr + UART_IER); - } - spin_unlock_irqrestore(&info->slock, flags); -} - -static void mxser_start(struct tty_struct *tty) -{ - struct mxser_port *info = tty->driver_data; - unsigned long flags; - - spin_lock_irqsave(&info->slock, flags); - if (info->xmit_cnt && info->xmit_buf) { - outb(info->IER & ~UART_IER_THRI, info->ioaddr + UART_IER); - info->IER |= UART_IER_THRI; - outb(info->IER, info->ioaddr + UART_IER); - } - spin_unlock_irqrestore(&info->slock, flags); -} - -static void mxser_set_termios(struct tty_struct *tty, struct ktermios *old_termios) -{ - struct mxser_port *info = tty->driver_data; - unsigned long flags; - - spin_lock_irqsave(&info->slock, flags); - mxser_change_speed(info, old_termios); - spin_unlock_irqrestore(&info->slock, flags); - - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; - mxser_start(tty); - } - - /* Handle sw stopped */ - if ((old_termios->c_iflag & IXON) && - !(tty->termios->c_iflag & IXON)) { - tty->stopped = 0; - - if (info->board->chip_flag) { - spin_lock_irqsave(&info->slock, flags); - DISABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL(info->ioaddr); - spin_unlock_irqrestore(&info->slock, flags); - } - - mxser_start(tty); - } -} - -/* - * mxser_wait_until_sent() --- wait until the transmitter is empty - */ -static void mxser_wait_until_sent(struct tty_struct *tty, int timeout) -{ - struct mxser_port *info = tty->driver_data; - unsigned long orig_jiffies, char_time; - int lsr; - - if (info->type == PORT_UNKNOWN) - return; - - if (info->xmit_fifo_size == 0) - return; /* Just in case.... */ - - orig_jiffies = jiffies; - /* - * Set the check interval to be 1/5 of the estimated time to - * send a single character, and make it at least 1. The check - * interval should also be less than the timeout. - * - * Note: we have to use pretty tight timings here to satisfy - * the NIST-PCTS. - */ - char_time = (info->timeout - HZ / 50) / info->xmit_fifo_size; - char_time = char_time / 5; - if (char_time == 0) - char_time = 1; - if (timeout && timeout < char_time) - char_time = timeout; - /* - * If the transmitter hasn't cleared in twice the approximate - * amount of time to send the entire FIFO, it probably won't - * ever clear. This assumes the UART isn't doing flow - * control, which is currently the case. Hence, if it ever - * takes longer than info->timeout, this is probably due to a - * UART bug of some kind. So, we clamp the timeout parameter at - * 2*info->timeout. - */ - if (!timeout || timeout > 2 * info->timeout) - timeout = 2 * info->timeout; -#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT - printk(KERN_DEBUG "In rs_wait_until_sent(%d) check=%lu...", - timeout, char_time); - printk("jiff=%lu...", jiffies); -#endif - while (!((lsr = inb(info->ioaddr + UART_LSR)) & UART_LSR_TEMT)) { -#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT - printk("lsr = %d (jiff=%lu)...", lsr, jiffies); -#endif - schedule_timeout_interruptible(char_time); - if (signal_pending(current)) - break; - if (timeout && time_after(jiffies, orig_jiffies + timeout)) - break; - } - set_current_state(TASK_RUNNING); - -#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT - printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); -#endif -} - -/* - * This routine is called by tty_hangup() when a hangup is signaled. - */ -static void mxser_hangup(struct tty_struct *tty) -{ - struct mxser_port *info = tty->driver_data; - - mxser_flush_buffer(tty); - mxser_shutdown(info); - info->event = 0; - info->count = 0; - info->flags &= ~ASYNC_NORMAL_ACTIVE; - info->tty = NULL; - wake_up_interruptible(&info->open_wait); -} - -/* - * mxser_rs_break() --- routine which turns the break handling on or off - */ -static void mxser_rs_break(struct tty_struct *tty, int break_state) -{ - struct mxser_port *info = tty->driver_data; - unsigned long flags; - - spin_lock_irqsave(&info->slock, flags); - if (break_state == -1) - outb(inb(info->ioaddr + UART_LCR) | UART_LCR_SBC, - info->ioaddr + UART_LCR); - else - outb(inb(info->ioaddr + UART_LCR) & ~UART_LCR_SBC, - info->ioaddr + UART_LCR); - spin_unlock_irqrestore(&info->slock, flags); -} - -static void mxser_receive_chars(struct mxser_port *port, int *status) -{ - struct tty_struct *tty = port->tty; - unsigned char ch, gdl; - int ignored = 0; - int cnt = 0; - int recv_room; - int max = 256; - - recv_room = tty->receive_room; - if ((recv_room == 0) && (!port->ldisc_stop_rx)) - mxser_stoprx(tty); - - if (port->board->chip_flag != MOXA_OTHER_UART) { - - if (*status & UART_LSR_SPECIAL) - goto intr_old; - if (port->board->chip_flag == MOXA_MUST_MU860_HWID && - (*status & MOXA_MUST_LSR_RERR)) - goto intr_old; - if (*status & MOXA_MUST_LSR_RERR) - goto intr_old; - - gdl = inb(port->ioaddr + MOXA_MUST_GDL_REGISTER); - - if (port->board->chip_flag == MOXA_MUST_MU150_HWID) - gdl &= MOXA_MUST_GDL_MASK; - if (gdl >= recv_room) { - if (!port->ldisc_stop_rx) - mxser_stoprx(tty); - } - while (gdl--) { - ch = inb(port->ioaddr + UART_RX); - tty_insert_flip_char(tty, ch, 0); - cnt++; - } - goto end_intr; - } -intr_old: - - do { - if (max-- < 0) - break; - - ch = inb(port->ioaddr + UART_RX); - if (port->board->chip_flag && (*status & UART_LSR_OE)) - outb(0x23, port->ioaddr + UART_FCR); - *status &= port->read_status_mask; - if (*status & port->ignore_status_mask) { - if (++ignored > 100) - break; - } else { - char flag = 0; - if (*status & UART_LSR_SPECIAL) { - if (*status & UART_LSR_BI) { - flag = TTY_BREAK; - port->icount.brk++; - - if (port->flags & ASYNC_SAK) - do_SAK(tty); - } else if (*status & UART_LSR_PE) { - flag = TTY_PARITY; - port->icount.parity++; - } else if (*status & UART_LSR_FE) { - flag = TTY_FRAME; - port->icount.frame++; - } else if (*status & UART_LSR_OE) { - flag = TTY_OVERRUN; - port->icount.overrun++; - } else - flag = TTY_BREAK; - } - tty_insert_flip_char(tty, ch, flag); - cnt++; - if (cnt >= recv_room) { - if (!port->ldisc_stop_rx) - mxser_stoprx(tty); - break; - } - - } - - if (port->board->chip_flag) - break; - - *status = inb(port->ioaddr + UART_LSR); - } while (*status & UART_LSR_DR); - -end_intr: - mxvar_log.rxcnt[port->tty->index] += cnt; - port->mon_data.rxcnt += cnt; - port->mon_data.up_rxcnt += cnt; - - /* - * We are called from an interrupt context with &port->slock - * being held. Drop it temporarily in order to prevent - * recursive locking. - */ - spin_unlock(&port->slock); - tty_flip_buffer_push(tty); - spin_lock(&port->slock); -} - -static void mxser_transmit_chars(struct mxser_port *port) -{ - int count, cnt; - - if (port->x_char) { - outb(port->x_char, port->ioaddr + UART_TX); - port->x_char = 0; - mxvar_log.txcnt[port->tty->index]++; - port->mon_data.txcnt++; - port->mon_data.up_txcnt++; - port->icount.tx++; - return; - } - - if (port->xmit_buf == 0) - return; - - if ((port->xmit_cnt <= 0) || port->tty->stopped || - (port->tty->hw_stopped && - (port->type != PORT_16550A) && - (!port->board->chip_flag))) { - port->IER &= ~UART_IER_THRI; - outb(port->IER, port->ioaddr + UART_IER); - return; - } - - cnt = port->xmit_cnt; - count = port->xmit_fifo_size; - do { - outb(port->xmit_buf[port->xmit_tail++], - port->ioaddr + UART_TX); - port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE - 1); - if (--port->xmit_cnt <= 0) - break; - } while (--count > 0); - mxvar_log.txcnt[port->tty->index] += (cnt - port->xmit_cnt); - - port->mon_data.txcnt += (cnt - port->xmit_cnt); - port->mon_data.up_txcnt += (cnt - port->xmit_cnt); - port->icount.tx += (cnt - port->xmit_cnt); - - if (port->xmit_cnt < WAKEUP_CHARS) - tty_wakeup(port->tty); - - if (port->xmit_cnt <= 0) { - port->IER &= ~UART_IER_THRI; - outb(port->IER, port->ioaddr + UART_IER); - } -} - -/* - * This is the serial driver's generic interrupt routine - */ -static irqreturn_t mxser_interrupt(int irq, void *dev_id) -{ - int status, iir, i; - struct mxser_board *brd = NULL; - struct mxser_port *port; - int max, irqbits, bits, msr; - unsigned int int_cnt, pass_counter = 0; - int handled = IRQ_NONE; - - for (i = 0; i < MXSER_BOARDS; i++) - if (dev_id == &mxser_boards[i]) { - brd = dev_id; - break; - } - - if (i == MXSER_BOARDS) - goto irq_stop; - if (brd == NULL) - goto irq_stop; - max = brd->info->nports; - while (pass_counter++ < MXSER_ISR_PASS_LIMIT) { - irqbits = inb(brd->vector) & brd->vector_mask; - if (irqbits == brd->vector_mask) - break; - - handled = IRQ_HANDLED; - for (i = 0, bits = 1; i < max; i++, irqbits |= bits, bits <<= 1) { - if (irqbits == brd->vector_mask) - break; - if (bits & irqbits) - continue; - port = &brd->ports[i]; - - int_cnt = 0; - spin_lock(&port->slock); - do { - iir = inb(port->ioaddr + UART_IIR); - if (iir & UART_IIR_NO_INT) - break; - iir &= MOXA_MUST_IIR_MASK; - if (!port->tty || - (port->flags & ASYNC_CLOSING) || - !(port->flags & - ASYNC_INITIALIZED)) { - status = inb(port->ioaddr + UART_LSR); - outb(0x27, port->ioaddr + UART_FCR); - inb(port->ioaddr + UART_MSR); - break; - } - - status = inb(port->ioaddr + UART_LSR); - - if (status & UART_LSR_PE) - port->err_shadow |= NPPI_NOTIFY_PARITY; - if (status & UART_LSR_FE) - port->err_shadow |= NPPI_NOTIFY_FRAMING; - if (status & UART_LSR_OE) - port->err_shadow |= - NPPI_NOTIFY_HW_OVERRUN; - if (status & UART_LSR_BI) - port->err_shadow |= NPPI_NOTIFY_BREAK; - - if (port->board->chip_flag) { - if (iir == MOXA_MUST_IIR_GDA || - iir == MOXA_MUST_IIR_RDA || - iir == MOXA_MUST_IIR_RTO || - iir == MOXA_MUST_IIR_LSR) - mxser_receive_chars(port, - &status); - - } else { - status &= port->read_status_mask; - if (status & UART_LSR_DR) - mxser_receive_chars(port, - &status); - } - msr = inb(port->ioaddr + UART_MSR); - if (msr & UART_MSR_ANY_DELTA) - mxser_check_modem_status(port, msr); - - if (port->board->chip_flag) { - if (iir == 0x02 && (status & - UART_LSR_THRE)) - mxser_transmit_chars(port); - } else { - if (status & UART_LSR_THRE) - mxser_transmit_chars(port); - } - } while (int_cnt++ < MXSER_ISR_PASS_LIMIT); - spin_unlock(&port->slock); - } - } - -irq_stop: - return handled; -} - -static const struct tty_operations mxser_ops = { - .open = mxser_open, - .close = mxser_close, - .write = mxser_write, - .put_char = mxser_put_char, - .flush_chars = mxser_flush_chars, - .write_room = mxser_write_room, - .chars_in_buffer = mxser_chars_in_buffer, - .flush_buffer = mxser_flush_buffer, - .ioctl = mxser_ioctl, - .throttle = mxser_throttle, - .unthrottle = mxser_unthrottle, - .set_termios = mxser_set_termios, - .stop = mxser_stop, - .start = mxser_start, - .hangup = mxser_hangup, - .break_ctl = mxser_rs_break, - .wait_until_sent = mxser_wait_until_sent, - .tiocmget = mxser_tiocmget, - .tiocmset = mxser_tiocmset, -}; - -/* - * The MOXA Smartio/Industio serial driver boot-time initialization code! - */ - -static void mxser_release_res(struct mxser_board *brd, struct pci_dev *pdev, - unsigned int irq) -{ - if (irq) - free_irq(brd->irq, brd); - if (pdev != NULL) { /* PCI */ -#ifdef CONFIG_PCI - pci_release_region(pdev, 2); - pci_release_region(pdev, 3); -#endif - } else { - release_region(brd->ports[0].ioaddr, 8 * brd->info->nports); - release_region(brd->vector, 1); - } -} - -static int __devinit mxser_initbrd(struct mxser_board *brd, - struct pci_dev *pdev) -{ - struct mxser_port *info; - unsigned int i; - int retval; - - printk(KERN_INFO "max. baud rate = %d bps.\n", brd->ports[0].max_baud); - - for (i = 0; i < brd->info->nports; i++) { - info = &brd->ports[i]; - info->board = brd; - info->stop_rx = 0; - info->ldisc_stop_rx = 0; - - /* Enhance mode enabled here */ - if (brd->chip_flag != MOXA_OTHER_UART) - ENABLE_MOXA_MUST_ENCHANCE_MODE(info->ioaddr); - - info->flags = ASYNC_SHARE_IRQ; - info->type = brd->uart_type; - - process_txrx_fifo(info); - - info->custom_divisor = info->baud_base * 16; - info->close_delay = 5 * HZ / 10; - info->closing_wait = 30 * HZ; - info->normal_termios = mxvar_sdriver->init_termios; - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->delta_msr_wait); - info->speed = 9600; - memset(&info->mon_data, 0, sizeof(struct mxser_mon)); - info->err_shadow = 0; - spin_lock_init(&info->slock); - - /* before set INT ISR, disable all int */ - outb(inb(info->ioaddr + UART_IER) & 0xf0, - info->ioaddr + UART_IER); - } - - retval = request_irq(brd->irq, mxser_interrupt, IRQF_SHARED, "mxser", - brd); - if (retval) { - printk(KERN_ERR "Board %s: Request irq failed, IRQ (%d) may " - "conflict with another device.\n", - brd->info->name, brd->irq); - /* We hold resources, we need to release them. */ - mxser_release_res(brd, pdev, 0); - } - return retval; -} - -static int __init mxser_get_ISA_conf(int cap, struct mxser_board *brd) -{ - int id, i, bits; - unsigned short regs[16], irq; - unsigned char scratch, scratch2; - - brd->chip_flag = MOXA_OTHER_UART; - - id = mxser_read_register(cap, regs); - switch (id) { - case C168_ASIC_ID: - brd->info = &mxser_cards[0]; - break; - case C104_ASIC_ID: - brd->info = &mxser_cards[1]; - break; - case CI104J_ASIC_ID: - brd->info = &mxser_cards[2]; - break; - case C102_ASIC_ID: - brd->info = &mxser_cards[5]; - break; - case CI132_ASIC_ID: - brd->info = &mxser_cards[6]; - break; - case CI134_ASIC_ID: - brd->info = &mxser_cards[7]; - break; - default: - return 0; - } - - irq = 0; - /* some ISA cards have 2 ports, but we want to see them as 4-port (why?) - Flag-hack checks if configuration should be read as 2-port here. */ - if (brd->info->nports == 2 || (brd->info->flags & MXSER_HAS2)) { - irq = regs[9] & 0xF000; - irq = irq | (irq >> 4); - if (irq != (regs[9] & 0xFF00)) - return MXSER_ERR_IRQ_CONFLIT; - } else if (brd->info->nports == 4) { - irq = regs[9] & 0xF000; - irq = irq | (irq >> 4); - irq = irq | (irq >> 8); - if (irq != regs[9]) - return MXSER_ERR_IRQ_CONFLIT; - } else if (brd->info->nports == 8) { - irq = regs[9] & 0xF000; - irq = irq | (irq >> 4); - irq = irq | (irq >> 8); - if ((irq != regs[9]) || (irq != regs[10])) - return MXSER_ERR_IRQ_CONFLIT; - } - - if (!irq) - return MXSER_ERR_IRQ; - brd->irq = ((int)(irq & 0xF000) >> 12); - for (i = 0; i < 8; i++) - brd->ports[i].ioaddr = (int) regs[i + 1] & 0xFFF8; - if ((regs[12] & 0x80) == 0) - return MXSER_ERR_VECTOR; - brd->vector = (int)regs[11]; /* interrupt vector */ - if (id == 1) - brd->vector_mask = 0x00FF; - else - brd->vector_mask = 0x000F; - for (i = 7, bits = 0x0100; i >= 0; i--, bits <<= 1) { - if (regs[12] & bits) { - brd->ports[i].baud_base = 921600; - brd->ports[i].max_baud = 921600; - } else { - brd->ports[i].baud_base = 115200; - brd->ports[i].max_baud = 115200; - } - } - scratch2 = inb(cap + UART_LCR) & (~UART_LCR_DLAB); - outb(scratch2 | UART_LCR_DLAB, cap + UART_LCR); - outb(0, cap + UART_EFR); /* EFR is the same as FCR */ - outb(scratch2, cap + UART_LCR); - outb(UART_FCR_ENABLE_FIFO, cap + UART_FCR); - scratch = inb(cap + UART_IIR); - - if (scratch & 0xC0) - brd->uart_type = PORT_16550A; - else - brd->uart_type = PORT_16450; - if (!request_region(brd->ports[0].ioaddr, 8 * brd->info->nports, - "mxser(IO)")) - return MXSER_ERR_IOADDR; - if (!request_region(brd->vector, 1, "mxser(vector)")) { - release_region(brd->ports[0].ioaddr, 8 * brd->info->nports); - return MXSER_ERR_VECTOR; - } - return brd->info->nports; -} - -static int __devinit mxser_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ -#ifdef CONFIG_PCI - struct mxser_board *brd; - unsigned int i, j; - unsigned long ioaddress; - int retval = -EINVAL; - - for (i = 0; i < MXSER_BOARDS; i++) - if (mxser_boards[i].info == NULL) - break; - - if (i >= MXSER_BOARDS) { - printk(KERN_ERR "Too many Smartio/Industio family boards found " - "(maximum %d), board not configured\n", MXSER_BOARDS); - goto err; - } - - brd = &mxser_boards[i]; - brd->idx = i * MXSER_PORTS_PER_BOARD; - printk(KERN_INFO "Found MOXA %s board (BusNo=%d, DevNo=%d)\n", - mxser_cards[ent->driver_data].name, - pdev->bus->number, PCI_SLOT(pdev->devfn)); - - retval = pci_enable_device(pdev); - if (retval) { - printk(KERN_ERR "Moxa SmartI/O PCI enable fail !\n"); - goto err; - } - - /* io address */ - ioaddress = pci_resource_start(pdev, 2); - retval = pci_request_region(pdev, 2, "mxser(IO)"); - if (retval) - goto err; - - brd->info = &mxser_cards[ent->driver_data]; - for (i = 0; i < brd->info->nports; i++) - brd->ports[i].ioaddr = ioaddress + 8 * i; - - /* vector */ - ioaddress = pci_resource_start(pdev, 3); - retval = pci_request_region(pdev, 3, "mxser(vector)"); - if (retval) - goto err_relio; - brd->vector = ioaddress; - - /* irq */ - brd->irq = pdev->irq; - - brd->chip_flag = CheckIsMoxaMust(brd->ports[0].ioaddr); - brd->uart_type = PORT_16550A; - brd->vector_mask = 0; - - for (i = 0; i < brd->info->nports; i++) { - for (j = 0; j < UART_INFO_NUM; j++) { - if (Gpci_uart_info[j].type == brd->chip_flag) { - brd->ports[i].max_baud = - Gpci_uart_info[j].max_baud; - - /* exception....CP-102 */ - if (brd->info->flags & MXSER_HIGHBAUD) - brd->ports[i].max_baud = 921600; - break; - } - } - } - - if (brd->chip_flag == MOXA_MUST_MU860_HWID) { - for (i = 0; i < brd->info->nports; i++) { - if (i < 4) - brd->ports[i].opmode_ioaddr = ioaddress + 4; - else - brd->ports[i].opmode_ioaddr = ioaddress + 0x0c; - } - outb(0, ioaddress + 4); /* default set to RS232 mode */ - outb(0, ioaddress + 0x0c); /* default set to RS232 mode */ - } - - for (i = 0; i < brd->info->nports; i++) { - brd->vector_mask |= (1 << i); - brd->ports[i].baud_base = 921600; - } - - /* mxser_initbrd will hook ISR. */ - retval = mxser_initbrd(brd, pdev); - if (retval) - goto err_null; - - for (i = 0; i < brd->info->nports; i++) - tty_register_device(mxvar_sdriver, brd->idx + i, &pdev->dev); - - pci_set_drvdata(pdev, brd); - - return 0; -err_relio: - pci_release_region(pdev, 2); -err_null: - brd->info = NULL; -err: - return retval; -#else - return -ENODEV; -#endif -} - -static void __devexit mxser_remove(struct pci_dev *pdev) -{ - struct mxser_board *brd = pci_get_drvdata(pdev); - unsigned int i; - - for (i = 0; i < brd->info->nports; i++) - tty_unregister_device(mxvar_sdriver, brd->idx + i); - - mxser_release_res(brd, pdev, 1); - brd->info = NULL; -} - -static struct pci_driver mxser_driver = { - .name = "mxser", - .id_table = mxser_pcibrds, - .probe = mxser_probe, - .remove = __devexit_p(mxser_remove) -}; - -static int __init mxser_module_init(void) -{ - struct mxser_board *brd; - unsigned long cap; - unsigned int i, m, isaloop; - int retval, b; - - pr_debug("Loading module mxser ...\n"); - - mxvar_sdriver = alloc_tty_driver(MXSER_PORTS + 1); - if (!mxvar_sdriver) - return -ENOMEM; - - printk(KERN_INFO "MOXA Smartio/Industio family driver version %s\n", - MXSER_VERSION); - - /* Initialize the tty_driver structure */ - mxvar_sdriver->owner = THIS_MODULE; - mxvar_sdriver->magic = TTY_DRIVER_MAGIC; - mxvar_sdriver->name = "ttyMI"; - mxvar_sdriver->major = ttymajor; - mxvar_sdriver->minor_start = 0; - mxvar_sdriver->num = MXSER_PORTS + 1; - mxvar_sdriver->type = TTY_DRIVER_TYPE_SERIAL; - mxvar_sdriver->subtype = SERIAL_TYPE_NORMAL; - mxvar_sdriver->init_termios = tty_std_termios; - mxvar_sdriver->init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL; - mxvar_sdriver->flags = TTY_DRIVER_REAL_RAW|TTY_DRIVER_DYNAMIC_DEV; - tty_set_operations(mxvar_sdriver, &mxser_ops); - - retval = tty_register_driver(mxvar_sdriver); - if (retval) { - printk(KERN_ERR "Couldn't install MOXA Smartio/Industio family " - "tty driver !\n"); - goto err_put; - } - - mxvar_diagflag = 0; - - m = 0; - /* Start finding ISA boards here */ - for (isaloop = 0; isaloop < 2; isaloop++) - for (b = 0; b < MXSER_BOARDS && m < MXSER_BOARDS; b++) { - if (!isaloop) - cap = mxserBoardCAP[b]; /* predefined */ - else - cap = ioaddr[b]; /* module param */ - - if (!cap) - continue; - - brd = &mxser_boards[m]; - retval = mxser_get_ISA_conf(cap, brd); - - if (retval != 0) - printk(KERN_INFO "Found MOXA %s board " - "(CAP=0x%x)\n", - brd->info->name, ioaddr[b]); - - if (retval <= 0) { - if (retval == MXSER_ERR_IRQ) - printk(KERN_ERR "Invalid interrupt " - "number, board not " - "configured\n"); - else if (retval == MXSER_ERR_IRQ_CONFLIT) - printk(KERN_ERR "Invalid interrupt " - "number, board not " - "configured\n"); - else if (retval == MXSER_ERR_VECTOR) - printk(KERN_ERR "Invalid interrupt " - "vector, board not " - "configured\n"); - else if (retval == MXSER_ERR_IOADDR) - printk(KERN_ERR "Invalid I/O address, " - "board not configured\n"); - - brd->info = NULL; - continue; - } - - /* mxser_initbrd will hook ISR. */ - if (mxser_initbrd(brd, NULL) < 0) { - brd->info = NULL; - continue; - } - - brd->idx = m * MXSER_PORTS_PER_BOARD; - for (i = 0; i < brd->info->nports; i++) - tty_register_device(mxvar_sdriver, brd->idx + i, - NULL); - - m++; - } - - retval = pci_register_driver(&mxser_driver); - if (retval) { - printk(KERN_ERR "Can't register pci driver\n"); - if (!m) { - retval = -ENODEV; - goto err_unr; - } /* else: we have some ISA cards under control */ - } - - pr_debug("Done.\n"); - - return 0; -err_unr: - tty_unregister_driver(mxvar_sdriver); -err_put: - put_tty_driver(mxvar_sdriver); - return retval; -} - -static void __exit mxser_module_exit(void) -{ - unsigned int i, j; - - pr_debug("Unloading module mxser ...\n"); - - pci_unregister_driver(&mxser_driver); - - for (i = 0; i < MXSER_BOARDS; i++) /* ISA remains */ - if (mxser_boards[i].info != NULL) - for (j = 0; j < mxser_boards[i].info->nports; j++) - tty_unregister_device(mxvar_sdriver, - mxser_boards[i].idx + j); - tty_unregister_driver(mxvar_sdriver); - put_tty_driver(mxvar_sdriver); - - for (i = 0; i < MXSER_BOARDS; i++) - if (mxser_boards[i].info != NULL) - mxser_release_res(&mxser_boards[i], NULL, 1); - - pr_debug("Done.\n"); -} - -module_init(mxser_module_init); -module_exit(mxser_module_exit); diff --git a/drivers/char/mxser_new.h b/drivers/char/mxser_new.h deleted file mode 100644 index d42f7766c652..000000000000 --- a/drivers/char/mxser_new.h +++ /dev/null @@ -1,293 +0,0 @@ -#ifndef _MXSER_H -#define _MXSER_H - -/* - * Semi-public control interfaces - */ - -/* - * MOXA ioctls - */ - -#define MOXA 0x400 -#define MOXA_GETDATACOUNT (MOXA + 23) -#define MOXA_GET_CONF (MOXA + 35) -#define MOXA_DIAGNOSE (MOXA + 50) -#define MOXA_CHKPORTENABLE (MOXA + 60) -#define MOXA_HighSpeedOn (MOXA + 61) -#define MOXA_GET_MAJOR (MOXA + 63) -#define MOXA_GET_CUMAJOR (MOXA + 64) -#define MOXA_GETMSTATUS (MOXA + 65) -#define MOXA_SET_OP_MODE (MOXA + 66) -#define MOXA_GET_OP_MODE (MOXA + 67) - -#define RS232_MODE 0 -#define RS485_2WIRE_MODE 1 -#define RS422_MODE 2 -#define RS485_4WIRE_MODE 3 -#define OP_MODE_MASK 3 - -#define MOXA_SDS_RSTICOUNTER (MOXA + 69) -#define MOXA_ASPP_OQUEUE (MOXA + 70) -#define MOXA_ASPP_SETBAUD (MOXA + 71) -#define MOXA_ASPP_GETBAUD (MOXA + 72) -#define MOXA_ASPP_MON (MOXA + 73) -#define MOXA_ASPP_LSTATUS (MOXA + 74) -#define MOXA_ASPP_MON_EXT (MOXA + 75) -#define MOXA_SET_BAUD_METHOD (MOXA + 76) -#define MOXA_SET_SPECIAL_BAUD_RATE (MOXA + 77) -#define MOXA_GET_SPECIAL_BAUD_RATE (MOXA + 78) - -/* --------------------------------------------------- */ - -#define NPPI_NOTIFY_PARITY 0x01 -#define NPPI_NOTIFY_FRAMING 0x02 -#define NPPI_NOTIFY_HW_OVERRUN 0x04 -#define NPPI_NOTIFY_SW_OVERRUN 0x08 -#define NPPI_NOTIFY_BREAK 0x10 - -#define NPPI_NOTIFY_CTSHOLD 0x01 /* Tx hold by CTS low */ -#define NPPI_NOTIFY_DSRHOLD 0x02 /* Tx hold by DSR low */ -#define NPPI_NOTIFY_XOFFHOLD 0x08 /* Tx hold by Xoff received */ -#define NPPI_NOTIFY_XOFFXENT 0x10 /* Xoff Sent */ - -/* follow just for Moxa Must chip define. */ -/* */ -/* when LCR register (offset 0x03) write following value, */ -/* the Must chip will enter enchance mode. And write value */ -/* on EFR (offset 0x02) bit 6,7 to change bank. */ -#define MOXA_MUST_ENTER_ENCHANCE 0xBF - -/* when enhance mode enable, access on general bank register */ -#define MOXA_MUST_GDL_REGISTER 0x07 -#define MOXA_MUST_GDL_MASK 0x7F -#define MOXA_MUST_GDL_HAS_BAD_DATA 0x80 - -#define MOXA_MUST_LSR_RERR 0x80 /* error in receive FIFO */ -/* enchance register bank select and enchance mode setting register */ -/* when LCR register equal to 0xBF */ -#define MOXA_MUST_EFR_REGISTER 0x02 -/* enchance mode enable */ -#define MOXA_MUST_EFR_EFRB_ENABLE 0x10 -/* enchance reister bank set 0, 1, 2 */ -#define MOXA_MUST_EFR_BANK0 0x00 -#define MOXA_MUST_EFR_BANK1 0x40 -#define MOXA_MUST_EFR_BANK2 0x80 -#define MOXA_MUST_EFR_BANK3 0xC0 -#define MOXA_MUST_EFR_BANK_MASK 0xC0 - -/* set XON1 value register, when LCR=0xBF and change to bank0 */ -#define MOXA_MUST_XON1_REGISTER 0x04 - -/* set XON2 value register, when LCR=0xBF and change to bank0 */ -#define MOXA_MUST_XON2_REGISTER 0x05 - -/* set XOFF1 value register, when LCR=0xBF and change to bank0 */ -#define MOXA_MUST_XOFF1_REGISTER 0x06 - -/* set XOFF2 value register, when LCR=0xBF and change to bank0 */ -#define MOXA_MUST_XOFF2_REGISTER 0x07 - -#define MOXA_MUST_RBRTL_REGISTER 0x04 -#define MOXA_MUST_RBRTH_REGISTER 0x05 -#define MOXA_MUST_RBRTI_REGISTER 0x06 -#define MOXA_MUST_THRTL_REGISTER 0x07 -#define MOXA_MUST_ENUM_REGISTER 0x04 -#define MOXA_MUST_HWID_REGISTER 0x05 -#define MOXA_MUST_ECR_REGISTER 0x06 -#define MOXA_MUST_CSR_REGISTER 0x07 - -/* good data mode enable */ -#define MOXA_MUST_FCR_GDA_MODE_ENABLE 0x20 -/* only good data put into RxFIFO */ -#define MOXA_MUST_FCR_GDA_ONLY_ENABLE 0x10 - -/* enable CTS interrupt */ -#define MOXA_MUST_IER_ECTSI 0x80 -/* enable RTS interrupt */ -#define MOXA_MUST_IER_ERTSI 0x40 -/* enable Xon/Xoff interrupt */ -#define MOXA_MUST_IER_XINT 0x20 -/* enable GDA interrupt */ -#define MOXA_MUST_IER_EGDAI 0x10 - -#define MOXA_MUST_RECV_ISR (UART_IER_RDI | MOXA_MUST_IER_EGDAI) - -/* GDA interrupt pending */ -#define MOXA_MUST_IIR_GDA 0x1C -#define MOXA_MUST_IIR_RDA 0x04 -#define MOXA_MUST_IIR_RTO 0x0C -#define MOXA_MUST_IIR_LSR 0x06 - -/* recieved Xon/Xoff or specical interrupt pending */ -#define MOXA_MUST_IIR_XSC 0x10 - -/* RTS/CTS change state interrupt pending */ -#define MOXA_MUST_IIR_RTSCTS 0x20 -#define MOXA_MUST_IIR_MASK 0x3E - -#define MOXA_MUST_MCR_XON_FLAG 0x40 -#define MOXA_MUST_MCR_XON_ANY 0x80 -#define MOXA_MUST_MCR_TX_XON 0x08 - -/* software flow control on chip mask value */ -#define MOXA_MUST_EFR_SF_MASK 0x0F -/* send Xon1/Xoff1 */ -#define MOXA_MUST_EFR_SF_TX1 0x08 -/* send Xon2/Xoff2 */ -#define MOXA_MUST_EFR_SF_TX2 0x04 -/* send Xon1,Xon2/Xoff1,Xoff2 */ -#define MOXA_MUST_EFR_SF_TX12 0x0C -/* don't send Xon/Xoff */ -#define MOXA_MUST_EFR_SF_TX_NO 0x00 -/* Tx software flow control mask */ -#define MOXA_MUST_EFR_SF_TX_MASK 0x0C -/* don't receive Xon/Xoff */ -#define MOXA_MUST_EFR_SF_RX_NO 0x00 -/* receive Xon1/Xoff1 */ -#define MOXA_MUST_EFR_SF_RX1 0x02 -/* receive Xon2/Xoff2 */ -#define MOXA_MUST_EFR_SF_RX2 0x01 -/* receive Xon1,Xon2/Xoff1,Xoff2 */ -#define MOXA_MUST_EFR_SF_RX12 0x03 -/* Rx software flow control mask */ -#define MOXA_MUST_EFR_SF_RX_MASK 0x03 - -#define ENABLE_MOXA_MUST_ENCHANCE_MODE(baseio) do { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr |= MOXA_MUST_EFR_EFRB_ENABLE; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} while (0) - -#define DISABLE_MOXA_MUST_ENCHANCE_MODE(baseio) do { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_EFRB_ENABLE; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} while (0) - -#define SET_MOXA_MUST_XON1_VALUE(baseio, Value) do { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_BANK_MASK; \ - __efr |= MOXA_MUST_EFR_BANK0; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb((u8)(Value), (baseio)+MOXA_MUST_XON1_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} while (0) - -#define SET_MOXA_MUST_XOFF1_VALUE(baseio, Value) do { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_BANK_MASK; \ - __efr |= MOXA_MUST_EFR_BANK0; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb((u8)(Value), (baseio)+MOXA_MUST_XOFF1_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} while (0) - -#define SET_MOXA_MUST_FIFO_VALUE(info) do { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((info)->ioaddr+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (info)->ioaddr+UART_LCR);\ - __efr = inb((info)->ioaddr+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_BANK_MASK; \ - __efr |= MOXA_MUST_EFR_BANK1; \ - outb(__efr, (info)->ioaddr+MOXA_MUST_EFR_REGISTER); \ - outb((u8)((info)->rx_high_water), (info)->ioaddr+ \ - MOXA_MUST_RBRTH_REGISTER); \ - outb((u8)((info)->rx_trigger), (info)->ioaddr+ \ - MOXA_MUST_RBRTI_REGISTER); \ - outb((u8)((info)->rx_low_water), (info)->ioaddr+ \ - MOXA_MUST_RBRTL_REGISTER); \ - outb(__oldlcr, (info)->ioaddr+UART_LCR); \ -} while (0) - -#define SET_MOXA_MUST_ENUM_VALUE(baseio, Value) do { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_BANK_MASK; \ - __efr |= MOXA_MUST_EFR_BANK2; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb((u8)(Value), (baseio)+MOXA_MUST_ENUM_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} while (0) - -#define GET_MOXA_MUST_HARDWARE_ID(baseio, pId) do { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_BANK_MASK; \ - __efr |= MOXA_MUST_EFR_BANK2; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - *pId = inb((baseio)+MOXA_MUST_HWID_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} while (0) - -#define SET_MOXA_MUST_NO_SOFTWARE_FLOW_CONTROL(baseio) do { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_SF_MASK; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} while (0) - -#define ENABLE_MOXA_MUST_TX_SOFTWARE_FLOW_CONTROL(baseio) do { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_SF_TX_MASK; \ - __efr |= MOXA_MUST_EFR_SF_TX1; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} while (0) - -#define DISABLE_MOXA_MUST_TX_SOFTWARE_FLOW_CONTROL(baseio) do { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_SF_TX_MASK; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} while (0) - -#define ENABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL(baseio) do { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_SF_RX_MASK; \ - __efr |= MOXA_MUST_EFR_SF_RX1; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} while (0) - -#define DISABLE_MOXA_MUST_RX_SOFTWARE_FLOW_CONTROL(baseio) do { \ - u8 __oldlcr, __efr; \ - __oldlcr = inb((baseio)+UART_LCR); \ - outb(MOXA_MUST_ENTER_ENCHANCE, (baseio)+UART_LCR); \ - __efr = inb((baseio)+MOXA_MUST_EFR_REGISTER); \ - __efr &= ~MOXA_MUST_EFR_SF_RX_MASK; \ - outb(__efr, (baseio)+MOXA_MUST_EFR_REGISTER); \ - outb(__oldlcr, (baseio)+UART_LCR); \ -} while (0) - -#endif diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index d130b87d8ed7..8fc4fe4e38f1 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c @@ -78,8 +78,6 @@ ASYNC_SPD_HI | ASYNC_SPEED_VHI | ASYNC_SESSION_LOCKOUT | \ ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP) -#define RS_EVENT_WRITE_WAKEUP 0 - static struct tty_driver *riscom_driver; static DEFINE_SPINLOCK(riscom_lock); @@ -314,12 +312,6 @@ out_release: * */ -static inline void rc_mark_event(struct riscom_port * port, int event) -{ - set_bit(event, &port->event); - schedule_work(&port->tqueue); -} - static inline struct riscom_port * rc_get_port(struct riscom_board const * bp, unsigned char const * what) { @@ -486,7 +478,7 @@ static inline void rc_transmit(struct riscom_board const * bp) rc_out(bp, CD180_IER, port->IER); } if (port->xmit_cnt <= port->wakeup_chars) - rc_mark_event(port, RS_EVENT_WRITE_WAKEUP); + tty_wakeup(tty); } static inline void rc_check_modem(struct riscom_board const * bp) @@ -505,7 +497,7 @@ static inline void rc_check_modem(struct riscom_board const * bp) if (rc_in(bp, CD180_MSVR) & MSVR_CD) wake_up_interruptible(&port->open_wait); else - schedule_work(&port->tqueue_hangup); + tty_hangup(tty); } #ifdef RISCOM_BRAIN_DAMAGED_CTS @@ -514,7 +506,7 @@ static inline void rc_check_modem(struct riscom_board const * bp) tty->hw_stopped = 0; port->IER |= IER_TXRDY; if (port->xmit_cnt <= port->wakeup_chars) - rc_mark_event(port, RS_EVENT_WRITE_WAKEUP); + tty_wakeup(tty); } else { tty->hw_stopped = 1; port->IER &= ~IER_TXRDY; @@ -526,7 +518,7 @@ static inline void rc_check_modem(struct riscom_board const * bp) tty->hw_stopped = 0; port->IER |= IER_TXRDY; if (port->xmit_cnt <= port->wakeup_chars) - rc_mark_event(port, RS_EVENT_WRITE_WAKEUP); + tty_wakeup(tty); } else { tty->hw_stopped = 1; port->IER &= ~IER_TXRDY; @@ -1091,7 +1083,6 @@ static void rc_close(struct tty_struct * tty, struct file * filp) tty_ldisc_flush(tty); tty->closing = 0; - port->event = 0; port->tty = NULL; if (port->blocked_open) { if (port->close_delay) { @@ -1526,25 +1517,6 @@ static void rc_start(struct tty_struct * tty) spin_unlock_irqrestore(&riscom_lock, flags); } -/* - * This routine is called from the work queue when the interrupt - * routine has signalled that a hangup has occurred. The path of - * hangup processing is: - * - * serial interrupt routine -> (workqueue) -> - * do_rc_hangup() -> tty->hangup() -> rc_hangup() - * - */ -static void do_rc_hangup(struct work_struct *ugly_api) -{ - struct riscom_port *port = container_of(ugly_api, struct riscom_port, tqueue_hangup); - struct tty_struct *tty; - - tty = port->tty; - if (tty) - tty_hangup(tty); /* FIXME: module removal race still here */ -} - static void rc_hangup(struct tty_struct * tty) { struct riscom_port *port = (struct riscom_port *)tty->driver_data; @@ -1556,7 +1528,6 @@ static void rc_hangup(struct tty_struct * tty) bp = port_Board(port); rc_shutdown_port(bp, port); - port->event = 0; port->count = 0; port->flags &= ~ASYNC_NORMAL_ACTIVE; port->tty = NULL; @@ -1586,18 +1557,6 @@ static void rc_set_termios(struct tty_struct * tty, struct ktermios * old_termio } } -static void do_softint(struct work_struct *ugly_api) -{ - struct riscom_port *port = container_of(ugly_api, struct riscom_port, tqueue); - struct tty_struct *tty; - - if(!(tty = port->tty)) - return; - - if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) - tty_wakeup(tty); -} - static const struct tty_operations riscom_ops = { .open = rc_open, .close = rc_close, @@ -1618,7 +1577,7 @@ static const struct tty_operations riscom_ops = { .tiocmset = rc_tiocmset, }; -static inline int rc_init_drivers(void) +static int __init rc_init_drivers(void) { int error; int i; @@ -1650,8 +1609,6 @@ static inline int rc_init_drivers(void) memset(rc_port, 0, sizeof(rc_port)); for (i = 0; i < RC_NPORT * RC_NBOARD; i++) { rc_port[i].magic = RISCOM8_MAGIC; - INIT_WORK(&rc_port[i].tqueue, do_softint); - INIT_WORK(&rc_port[i].tqueue_hangup, do_rc_hangup); rc_port[i].close_delay = 50 * HZ/100; rc_port[i].closing_wait = 3000 * HZ/100; init_waitqueue_head(&rc_port[i].open_wait); diff --git a/drivers/char/riscom8.h b/drivers/char/riscom8.h index 9cc1313d5e67..cdfdf4394477 100644 --- a/drivers/char/riscom8.h +++ b/drivers/char/riscom8.h @@ -71,7 +71,6 @@ struct riscom_port { struct tty_struct * tty; int count; int blocked_open; - unsigned long event; /* long req'd for set_bit --RR */ int timeout; int close_delay; unsigned char * xmit_buf; @@ -81,8 +80,6 @@ struct riscom_port { int xmit_cnt; wait_queue_head_t open_wait; wait_queue_head_t close_wait; - struct work_struct tqueue; - struct work_struct tqueue_hangup; short wakeup_chars; short break_length; unsigned short closing_wait; diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c index d83419c3857e..68c289fe2dc2 100644 --- a/drivers/char/rocket.c +++ b/drivers/char/rocket.c @@ -40,12 +40,6 @@ */ /****** Defines ******/ -#ifdef PCI_NUM_RESOURCES -#define PCI_BASE_ADDRESS(dev, r) ((dev)->resource[r].start) -#else -#define PCI_BASE_ADDRESS(dev, r) ((dev)->base_address[r]) -#endif - #define ROCKET_PARANOIA_CHECK #define ROCKET_DISABLE_SIMUSAGE @@ -305,8 +299,8 @@ static inline int rocket_paranoia_check(struct r_port *info, if (!info) return 1; if (info->magic != RPORT_MAGIC) { - printk(KERN_INFO "Warning: bad magic number for rocketport struct in %s\n", - routine); + printk(KERN_WARNING "Warning: bad magic number for rocketport " + "struct in %s\n", routine); return 1; } #endif @@ -328,7 +322,7 @@ static void rp_do_receive(struct r_port *info, ToRecv = sGetRxCnt(cp); #ifdef ROCKET_DEBUG_INTR - printk(KERN_INFO "rp_do_receive(%d)...", ToRecv); + printk(KERN_INFO "rp_do_receive(%d)...\n", ToRecv); #endif if (ToRecv == 0) return; @@ -341,7 +335,7 @@ static void rp_do_receive(struct r_port *info, if (ChanStatus & (RXFOVERFL | RXBREAK | RXFRAME | RXPARITY)) { if (!(ChanStatus & STATMODE)) { #ifdef ROCKET_DEBUG_RECEIVE - printk(KERN_INFO "Entering STATMODE..."); + printk(KERN_INFO "Entering STATMODE...\n"); #endif ChanStatus |= STATMODE; sEnRxStatusMode(cp); @@ -355,15 +349,15 @@ static void rp_do_receive(struct r_port *info, */ if (ChanStatus & STATMODE) { #ifdef ROCKET_DEBUG_RECEIVE - printk(KERN_INFO "Ignore %x, read %x...", info->ignore_status_mask, - info->read_status_mask); + printk(KERN_INFO "Ignore %x, read %x...\n", + info->ignore_status_mask, info->read_status_mask); #endif while (ToRecv) { char flag; CharNStat = sInW(sGetTxRxDataIO(cp)); #ifdef ROCKET_DEBUG_RECEIVE - printk(KERN_INFO "%x...", CharNStat); + printk(KERN_INFO "%x...\n", CharNStat); #endif if (CharNStat & STMBREAKH) CharNStat &= ~(STMFRAMEH | STMPARITYH); @@ -435,12 +429,13 @@ static void rp_do_transmit(struct r_port *info) unsigned long flags; #ifdef ROCKET_DEBUG_INTR - printk(KERN_INFO "rp_do_transmit "); + printk(KERN_DEBUG "%s\n", __func__); #endif if (!info) return; if (!info->tty) { - printk(KERN_INFO "rp: WARNING rp_do_transmit called with info->tty==NULL\n"); + printk(KERN_WARNING "rp: WARNING %s called with " + "info->tty==NULL\n", __func__); clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); return; } @@ -464,7 +459,7 @@ static void rp_do_transmit(struct r_port *info) info->xmit_cnt -= c; info->xmit_fifo_room -= c; #ifdef ROCKET_DEBUG_INTR - printk(KERN_INFO "tx %d chars...", c); + printk(KERN_INFO "tx %d chars...\n", c); #endif } @@ -481,7 +476,7 @@ static void rp_do_transmit(struct r_port *info) spin_unlock_irqrestore(&info->slock, flags); #ifdef ROCKET_DEBUG_INTR - printk(KERN_INFO "(%d,%d,%d,%d)...", info->xmit_cnt, info->xmit_head, + printk(KERN_DEBUG "(%d,%d,%d,%d)...\n", info->xmit_cnt, info->xmit_head, info->xmit_tail, info->xmit_fifo_room); #endif } @@ -501,11 +496,13 @@ static void rp_handle_port(struct r_port *info) return; if ((info->flags & ROCKET_INITIALIZED) == 0) { - printk(KERN_INFO "rp: WARNING: rp_handle_port called with info->flags & NOT_INIT\n"); + printk(KERN_WARNING "rp: WARNING: rp_handle_port called with " + "info->flags & NOT_INIT\n"); return; } if (!info->tty) { - printk(KERN_INFO "rp: WARNING: rp_handle_port called with info->tty==NULL\n"); + printk(KERN_WARNING "rp: WARNING: rp_handle_port called with " + "info->tty==NULL\n"); return; } cp = &info->channel; @@ -513,7 +510,7 @@ static void rp_handle_port(struct r_port *info) IntMask = sGetChanIntID(cp) & info->intmask; #ifdef ROCKET_DEBUG_INTR - printk(KERN_INFO "rp_interrupt %02x...", IntMask); + printk(KERN_INFO "rp_interrupt %02x...\n", IntMask); #endif ChanStatus = sGetChanStatus(cp); if (IntMask & RXF_TRIG) { /* Rx FIFO trigger level */ @@ -521,7 +518,7 @@ static void rp_handle_port(struct r_port *info) } if (IntMask & DELTA_CD) { /* CD change */ #if (defined(ROCKET_DEBUG_OPEN) || defined(ROCKET_DEBUG_INTR) || defined(ROCKET_DEBUG_HANGUP)) - printk(KERN_INFO "ttyR%d CD now %s...", info->line, + printk(KERN_INFO "ttyR%d CD now %s...\n", info->line, (ChanStatus & CD_ACT) ? "on" : "off"); #endif if (!(ChanStatus & CD_ACT) && info->cd_status) { @@ -638,7 +635,8 @@ static void init_r_port(int board, int aiop, int chan, struct pci_dev *pci_dev) /* Get a r_port struct for the port, fill it in and save it globally, indexed by line number */ info = kzalloc(sizeof (struct r_port), GFP_KERNEL); if (!info) { - printk(KERN_INFO "Couldn't allocate info struct for line #%d\n", line); + printk(KERN_ERR "Couldn't allocate info struct for line #%d\n", + line); return; } @@ -668,7 +666,8 @@ static void init_r_port(int board, int aiop, int chan, struct pci_dev *pci_dev) info->intmask = RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD | DELTA_CTS | DELTA_DSR; if (sInitChan(ctlp, &info->channel, aiop, chan) == 0) { - printk(KERN_INFO "RocketPort sInitChan(%d, %d, %d) failed!\n", board, aiop, chan); + printk(KERN_ERR "RocketPort sInitChan(%d, %d, %d) failed!\n", + board, aiop, chan); kfree(info); return; } @@ -976,7 +975,7 @@ static int rp_open(struct tty_struct *tty, struct file *filp) CHANNEL_t *cp; unsigned long page; - line = TTY_GET_LINE(tty); + line = tty->index; if ((line < 0) || (line >= MAX_RP_PORTS) || ((info = rp_table[line]) == NULL)) return -ENXIO; @@ -1007,7 +1006,8 @@ static int rp_open(struct tty_struct *tty, struct file *filp) atomic_inc(&rp_num_ports_open); #ifdef ROCKET_DEBUG_OPEN - printk(KERN_INFO "rocket mod++ = %d...", atomic_read(&rp_num_ports_open)); + printk(KERN_INFO "rocket mod++ = %d...\n", + atomic_read(&rp_num_ports_open)); #endif } #ifdef ROCKET_DEBUG_OPEN @@ -1103,13 +1103,13 @@ static void rp_close(struct tty_struct *tty, struct file *filp) * one, we've got real problems, since it means the * serial port won't be shutdown. */ - printk(KERN_INFO "rp_close: bad serial port count; tty->count is 1, " - "info->count is %d\n", info->count); + printk(KERN_WARNING "rp_close: bad serial port count; " + "tty->count is 1, info->count is %d\n", info->count); info->count = 1; } if (--info->count < 0) { - printk(KERN_INFO "rp_close: bad serial port count for ttyR%d: %d\n", - info->line, info->count); + printk(KERN_WARNING "rp_close: bad serial port count for " + "ttyR%d: %d\n", info->line, info->count); info->count = 0; } if (info->count) { @@ -1160,8 +1160,7 @@ static void rp_close(struct tty_struct *tty, struct file *filp) if (C_HUPCL(tty)) sClrDTR(cp); - if (TTY_DRIVER_FLUSH_BUFFER_EXISTS(tty)) - TTY_DRIVER_FLUSH_BUFFER(tty); + rp_flush_buffer(tty); tty_ldisc_flush(tty); @@ -1184,7 +1183,8 @@ static void rp_close(struct tty_struct *tty, struct file *filp) atomic_dec(&rp_num_ports_open); #ifdef ROCKET_DEBUG_OPEN - printk(KERN_INFO "rocket mod-- = %d...", atomic_read(&rp_num_ports_open)); + printk(KERN_INFO "rocket mod-- = %d...\n", + atomic_read(&rp_num_ports_open)); printk(KERN_INFO "rp_close ttyR%d complete shutdown\n", info->line); #endif @@ -1569,9 +1569,9 @@ static void rp_wait_until_sent(struct tty_struct *tty, int timeout) orig_jiffies = jiffies; #ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT - printk(KERN_INFO "In RP_wait_until_sent(%d) (jiff=%lu)...", timeout, + printk(KERN_INFO "In RP_wait_until_sent(%d) (jiff=%lu)...\n", timeout, jiffies); - printk(KERN_INFO "cps=%d...", info->cps); + printk(KERN_INFO "cps=%d...\n", info->cps); #endif while (1) { txcnt = sGetTxCnt(cp); @@ -1592,7 +1592,8 @@ static void rp_wait_until_sent(struct tty_struct *tty, int timeout) if (check_time == 0) check_time = 1; #ifdef ROCKET_DEBUG_WAIT_UNTIL_SENT - printk(KERN_INFO "txcnt = %d (jiff=%lu,check=%d)...", txcnt, jiffies, check_time); + printk(KERN_INFO "txcnt = %d (jiff=%lu,check=%d)...\n", txcnt, + jiffies, check_time); #endif msleep_interruptible(jiffies_to_msecs(check_time)); if (signal_pending(current)) @@ -1616,7 +1617,7 @@ static void rp_hangup(struct tty_struct *tty) return; #if (defined(ROCKET_DEBUG_OPEN) || defined(ROCKET_DEBUG_HANGUP)) - printk(KERN_INFO "rp_hangup of ttyR%d...", info->line); + printk(KERN_INFO "rp_hangup of ttyR%d...\n", info->line); #endif rp_flush_buffer(tty); if (info->flags & ROCKET_CLOSING) @@ -1664,7 +1665,7 @@ static void rp_put_char(struct tty_struct *tty, unsigned char ch) mutex_lock(&info->write_mtx); #ifdef ROCKET_DEBUG_WRITE - printk(KERN_INFO "rp_put_char %c...", ch); + printk(KERN_INFO "rp_put_char %c...\n", ch); #endif spin_lock_irqsave(&info->slock, flags); @@ -1709,7 +1710,7 @@ static int rp_write(struct tty_struct *tty, return -ERESTARTSYS; #ifdef ROCKET_DEBUG_WRITE - printk(KERN_INFO "rp_write %d chars...", count); + printk(KERN_INFO "rp_write %d chars...\n", count); #endif cp = &info->channel; @@ -1798,7 +1799,7 @@ static int rp_write_room(struct tty_struct *tty) if (ret < 0) ret = 0; #ifdef ROCKET_DEBUG_WRITE - printk(KERN_INFO "rp_write_room returns %d...", ret); + printk(KERN_INFO "rp_write_room returns %d...\n", ret); #endif return ret; } @@ -1818,7 +1819,7 @@ static int rp_chars_in_buffer(struct tty_struct *tty) cp = &info->channel; #ifdef ROCKET_DEBUG_WRITE - printk(KERN_INFO "rp_chars_in_buffer returns %d...", info->xmit_cnt); + printk(KERN_INFO "rp_chars_in_buffer returns %d...\n", info->xmit_cnt); #endif return info->xmit_cnt; } @@ -2161,14 +2162,11 @@ static __init int register_PCI(int i, struct pci_dev *dev) for (aiop = 0; aiop < max_num_aiops; aiop++) ctlp->AiopNumChan[aiop] = ports_per_aiop; - printk("Comtrol PCI controller #%d ID 0x%x found in bus:slot:fn %s at address %04lx, " - "%d AIOP(s) (%s)\n", i, dev->device, pci_name(dev), - rcktpt_io_addr[i], num_aiops, rocketModel[i].modelString); - printk(KERN_INFO "Installing %s, creating /dev/ttyR%d - %ld\n", - rocketModel[i].modelString, - rocketModel[i].startingPortNumber, - rocketModel[i].startingPortNumber + - rocketModel[i].numPorts - 1); + dev_info(&dev->dev, "comtrol PCI controller #%d found at " + "address %04lx, %d AIOP(s) (%s), creating ttyR%d - %ld\n", + i, rcktpt_io_addr[i], num_aiops, rocketModel[i].modelString, + rocketModel[i].startingPortNumber, + rocketModel[i].startingPortNumber + rocketModel[i].numPorts-1); if (num_aiops <= 0) { rcktpt_io_addr[i] = 0; @@ -2191,10 +2189,10 @@ static __init int register_PCI(int i, struct pci_dev *dev) num_chan = ports_per_aiop; for (chan = 0; chan < num_chan; chan++) sPCIModemReset(ctlp, chan, 1); - mdelay(500); + msleep(500); for (chan = 0; chan < num_chan; chan++) sPCIModemReset(ctlp, chan, 0); - mdelay(500); + msleep(500); rmSpeakerReset(ctlp, rocketModel[i].model); } return (1); @@ -2240,7 +2238,9 @@ static int __init init_ISA(int i) /* Reserve the IO region */ if (!request_region(rcktpt_io_addr[i], 64, "Comtrol RocketPort")) { - printk(KERN_INFO "Unable to reserve IO region for configured ISA RocketPort at address 0x%lx, board not installed...\n", rcktpt_io_addr[i]); + printk(KERN_ERR "Unable to reserve IO region for configured " + "ISA RocketPort at address 0x%lx, board not " + "installed...\n", rcktpt_io_addr[i]); rcktpt_io_addr[i] = 0; return (0); } @@ -2309,10 +2309,10 @@ static int __init init_ISA(int i) total_num_chan = num_chan; for (chan = 0; chan < num_chan; chan++) sModemReset(ctlp, chan, 1); - mdelay(500); + msleep(500); for (chan = 0; chan < num_chan; chan++) sModemReset(ctlp, chan, 0); - mdelay(500); + msleep(500); strcpy(rocketModel[i].modelString, "RocketModem ISA"); } else { strcpy(rocketModel[i].modelString, "RocketPort ISA"); @@ -2480,7 +2480,7 @@ static void rp_cleanup_module(void) retval = tty_unregister_driver(rocket_driver); if (retval) - printk(KERN_INFO "Error %d while trying to unregister " + printk(KERN_ERR "Error %d while trying to unregister " "rocketport driver\n", -retval); for (i = 0; i < MAX_RP_PORTS; i++) diff --git a/drivers/char/rocket_int.h b/drivers/char/rocket_int.h index 55b8f2d71a96..f3a75791b811 100644 --- a/drivers/char/rocket_int.h +++ b/drivers/char/rocket_int.h @@ -42,7 +42,7 @@ typedef unsigned int DWordIO_t; static inline void sOutB(unsigned short port, unsigned char value) { #ifdef ROCKET_DEBUG_IO - printk("sOutB(%x, %x)...", port, value); + printk(KERN_DEBUG "sOutB(%x, %x)...\n", port, value); #endif outb_p(value, port); } @@ -50,7 +50,7 @@ static inline void sOutB(unsigned short port, unsigned char value) static inline void sOutW(unsigned short port, unsigned short value) { #ifdef ROCKET_DEBUG_IO - printk("sOutW(%x, %x)...", port, value); + printk(KERN_DEBUG "sOutW(%x, %x)...\n", port, value); #endif outw_p(value, port); } @@ -58,7 +58,7 @@ static inline void sOutW(unsigned short port, unsigned short value) static inline void sOutDW(unsigned short port, unsigned long value) { #ifdef ROCKET_DEBUG_IO - printk("sOutDW(%x, %lx)...", port, value); + printk(KERN_DEBUG "sOutDW(%x, %lx)...\n", port, value); #endif outl_p(cpu_to_le32(value), port); } @@ -105,12 +105,6 @@ static inline unsigned short sInW(unsigned short port) #define AIOPID_NULL -1 /* no AIOP or channel exists */ #define AIOPID_0001 0x0001 /* AIOP release 1 */ -#define NULLDEV -1 /* identifies non-existant device */ -#define NULLCTL -1 /* identifies non-existant controller */ -#define NULLCTLPTR (CONTROLLER_T *)0 /* identifies non-existant controller */ -#define NULLAIOP -1 /* identifies non-existant AIOP */ -#define NULLCHAN -1 /* identifies non-existant channel */ - /************************************************************************ Global Register Offsets - Direct Access - Fixed values ************************************************************************/ @@ -1187,9 +1181,6 @@ struct r_port { #define ROCKET_CLOSING 0x40000000 /* Serial port is closing */ #define ROCKET_NORMAL_ACTIVE 0x20000000 /* Normal port is active */ -/* tty subtypes */ -#define SERIAL_TYPE_NORMAL 1 - /* * Assigned major numbers for the Comtrol Rocketport */ @@ -1240,12 +1231,3 @@ struct r_port { /* Compact PCI device */ #define PCI_DEVICE_ID_CRP16INTF 0x0903 /* Rocketport Compact PCI 16 port w/external I/F */ -#define TTY_GET_LINE(t) t->index -#define TTY_DRIVER_MINOR_START(t) t->driver->minor_start -#define TTY_DRIVER_SUBTYPE(t) t->driver->subtype -#define TTY_DRIVER_NAME(t) t->driver->name -#define TTY_DRIVER_NAME_BASE(t) t->driver->name_base -#define TTY_DRIVER_FLUSH_BUFFER_EXISTS(t) t->driver->flush_buffer -#define TTY_DRIVER_FLUSH_BUFFER(t) t->driver->flush_buffer(t) - - diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c index f1497cecffd8..df8cd0ca97eb 100644 --- a/drivers/char/serial167.c +++ b/drivers/char/serial167.c @@ -90,8 +90,6 @@ #define STD_COM_FLAGS (0) -#define SERIAL_TYPE_NORMAL 1 - static struct tty_driver *cy_serial_driver; extern int serial_console; static struct cyclades_port *serial_console_info = NULL; @@ -359,18 +357,6 @@ static void cy_start(struct tty_struct *tty) local_irq_restore(flags); } /* cy_start */ -/* - * This routine is used by the interrupt handler to schedule - * processing in the software interrupt portion of the driver - * (also known as the "bottom half"). This can be called any - * number of times for any channel without harm. - */ -static inline void cy_sched_event(struct cyclades_port *info, int event) -{ - info->event |= 1 << event; /* remember what kind of event and who */ - schedule_work(&info->tqueue); -} /* cy_sched_event */ - /* The real interrupt service routines are called whenever the card wants its hand held--chars received, out buffer empty, modem change, etc. @@ -485,10 +471,12 @@ static irqreturn_t cd2401_modem_interrupt(int irq, void *dev_id) && (info->flags & ASYNC_CHECK_CD)) { if (mdm_status & CyDCD) { /* CP('!'); */ - cy_sched_event(info, Cy_EVENT_OPEN_WAKEUP); + wake_up_interruptible(&info->open_wait); } else { /* CP('@'); */ - cy_sched_event(info, Cy_EVENT_HANGUP); + tty_hangup(info->tty); + wake_up_interruptible(&info->open_wait); + info->flags &= ~ASYNC_NORMAL_ACTIVE; } } if ((mdm_change & CyCTS) @@ -498,8 +486,7 @@ static irqreturn_t cd2401_modem_interrupt(int irq, void *dev_id) /* !!! cy_start isn't used because... */ info->tty->stopped = 0; base_addr[CyIER] |= CyTxMpty; - cy_sched_event(info, - Cy_EVENT_WRITE_WAKEUP); + tty_wakeup(info->tty); } } else { if (!(mdm_status & CyCTS)) { @@ -545,9 +532,6 @@ static irqreturn_t cd2401_tx_interrupt(int irq, void *dev_id) info->last_active = jiffies; if (info->tty == 0) { base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy); - if (info->xmit_cnt < WAKEUP_CHARS) { - cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP); - } base_addr[CyTEOIR] = CyNOTRANS; return IRQ_HANDLED; } @@ -629,9 +613,9 @@ static irqreturn_t cd2401_tx_interrupt(int irq, void *dev_id) } } - if (info->xmit_cnt < WAKEUP_CHARS) { - cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP); - } + if (info->xmit_cnt < WAKEUP_CHARS) + tty_wakeup(info->tty); + base_addr[CyTEOIR] = (char_count != saved_cnt) ? 0 : CyNOTRANS; return IRQ_HANDLED; } /* cy_tx_interrupt */ @@ -692,49 +676,6 @@ static irqreturn_t cd2401_rx_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } /* cy_rx_interrupt */ -/* - * This routine is used to handle the "bottom half" processing for the - * serial driver, known also the "software interrupt" processing. - * This processing is done at the kernel interrupt level, after the - * cy#/_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This - * is where time-consuming activities which can not be done in the - * interrupt driver proper are done; the interrupt driver schedules - * them using cy_sched_event(), and they get done here. - * - * This is done through one level of indirection--the task queue. - * When a hardware interrupt service routine wants service by the - * driver's bottom half, it enqueues the appropriate tq_struct (one - * per port) to the keventd work queue and sets a request flag - * that the work queue be processed. - * - * Although this may seem unwieldy, it gives the system a way to - * pass an argument (in this case the pointer to the cyclades_port - * structure) to the bottom half of the driver. Previous kernels - * had to poll every port to see if that port needed servicing. - */ -static void do_softint(struct work_struct *ugly_api) -{ - struct cyclades_port *info = - container_of(ugly_api, struct cyclades_port, tqueue); - struct tty_struct *tty; - - tty = info->tty; - if (!tty) - return; - - if (test_and_clear_bit(Cy_EVENT_HANGUP, &info->event)) { - tty_hangup(info->tty); - wake_up_interruptible(&info->open_wait); - info->flags &= ~ASYNC_NORMAL_ACTIVE; - } - if (test_and_clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) { - wake_up_interruptible(&info->open_wait); - } - if (test_and_clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) { - tty_wakeup(tty); - } -} /* do_softint */ - /* This is called whenever a port becomes active; interrupts are enabled and DTR & RTS are turned on. */ @@ -1745,7 +1686,6 @@ static void cy_close(struct tty_struct *tty, struct file *filp) if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); tty_ldisc_flush(tty); - info->event = 0; info->tty = NULL; if (info->blocked_open) { if (info->close_delay) { @@ -2236,7 +2176,6 @@ static int __init serial167_init(void) info->rco = baud_co[DefSpeed] >> 5; /* Rx CO */ info->close_delay = 0; info->x_char = 0; - info->event = 0; info->count = 0; #ifdef SERIAL_DEBUG_COUNT printk("cyc: %d: setting count to 0\n", @@ -2245,7 +2184,6 @@ static int __init serial167_init(void) info->blocked_open = 0; info->default_threshold = 0; info->default_timeout = 0; - INIT_WORK(&info->tqueue, do_softint); init_waitqueue_head(&info->open_wait); init_waitqueue_head(&info->close_wait); /* info->session */ diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c index 455855631aef..c0e08c7bca2f 100644 --- a/drivers/char/specialix.c +++ b/drivers/char/specialix.c @@ -178,9 +178,6 @@ static int sx_poll = HZ; ASYNC_SPD_HI | ASYNC_SPEED_VHI | ASYNC_SESSION_LOCKOUT | \ ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP) -#undef RS_EVENT_WRITE_WAKEUP -#define RS_EVENT_WRITE_WAKEUP 0 - static struct tty_driver *specialix_driver; static struct specialix_board sx_board[SX_NBOARD] = { @@ -602,17 +599,6 @@ static int sx_probe(struct specialix_board *bp) * Interrupt processing routines. * */ -static inline void sx_mark_event(struct specialix_port * port, int event) -{ - func_enter(); - - set_bit(event, &port->event); - schedule_work(&port->tqueue); - - func_exit(); -} - - static inline struct specialix_port * sx_get_port(struct specialix_board * bp, unsigned char const * what) { @@ -809,7 +795,7 @@ static inline void sx_transmit(struct specialix_board * bp) sx_out(bp, CD186x_IER, port->IER); } if (port->xmit_cnt <= port->wakeup_chars) - sx_mark_event(port, RS_EVENT_WRITE_WAKEUP); + tty_wakeup(tty); func_exit(); } @@ -839,7 +825,7 @@ static inline void sx_check_modem(struct specialix_board * bp) wake_up_interruptible(&port->open_wait); } else { dprintk (SX_DEBUG_SIGNALS, "Sending HUP.\n"); - schedule_work(&port->tqueue_hangup); + tty_hangup(tty); } } @@ -849,7 +835,7 @@ static inline void sx_check_modem(struct specialix_board * bp) tty->hw_stopped = 0; port->IER |= IER_TXRDY; if (port->xmit_cnt <= port->wakeup_chars) - sx_mark_event(port, RS_EVENT_WRITE_WAKEUP); + tty_wakeup(tty); } else { tty->hw_stopped = 1; port->IER &= ~IER_TXRDY; @@ -861,7 +847,7 @@ static inline void sx_check_modem(struct specialix_board * bp) tty->hw_stopped = 0; port->IER |= IER_TXRDY; if (port->xmit_cnt <= port->wakeup_chars) - sx_mark_event(port, RS_EVENT_WRITE_WAKEUP); + tty_wakeup(tty); } else { tty->hw_stopped = 1; port->IER &= ~IER_TXRDY; @@ -1618,7 +1604,6 @@ static void sx_close(struct tty_struct * tty, struct file * filp) tty_ldisc_flush(tty); spin_lock_irqsave(&port->lock, flags); tty->closing = 0; - port->event = 0; port->tty = NULL; spin_unlock_irqrestore(&port->lock, flags); if (port->blocked_open) { @@ -2235,32 +2220,6 @@ static void sx_start(struct tty_struct * tty) func_exit(); } - -/* - * This routine is called from the work-queue when the interrupt - * routine has signalled that a hangup has occurred. The path of - * hangup processing is: - * - * serial interrupt routine -> (workqueue) -> - * do_sx_hangup() -> tty->hangup() -> sx_hangup() - * - */ -static void do_sx_hangup(struct work_struct *work) -{ - struct specialix_port *port = - container_of(work, struct specialix_port, tqueue_hangup); - struct tty_struct *tty; - - func_enter(); - - tty = port->tty; - if (tty) - tty_hangup(tty); /* FIXME: module removal race here */ - - func_exit(); -} - - static void sx_hangup(struct tty_struct * tty) { struct specialix_port *port = (struct specialix_port *)tty->driver_data; @@ -2278,7 +2237,6 @@ static void sx_hangup(struct tty_struct * tty) sx_shutdown_port(bp, port); spin_lock_irqsave(&port->lock, flags); - port->event = 0; bp->count -= port->count; if (bp->count < 0) { printk(KERN_ERR "sx%d: sx_hangup: bad board count: %d port: %d\n", @@ -2320,26 +2278,6 @@ static void sx_set_termios(struct tty_struct * tty, struct ktermios * old_termio } } - -static void do_softint(struct work_struct *work) -{ - struct specialix_port *port = - container_of(work, struct specialix_port, tqueue); - struct tty_struct *tty; - - func_enter(); - - if(!(tty = port->tty)) { - func_exit(); - return; - } - - if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) - tty_wakeup(tty); - - func_exit(); -} - static const struct tty_operations sx_ops = { .open = sx_open, .close = sx_close, @@ -2397,8 +2335,6 @@ static int sx_init_drivers(void) memset(sx_port, 0, sizeof(sx_port)); for (i = 0; i < SX_NPORT * SX_NBOARD; i++) { sx_port[i].magic = SPECIALIX_MAGIC; - INIT_WORK(&sx_port[i].tqueue, do_softint); - INIT_WORK(&sx_port[i].tqueue_hangup, do_sx_hangup); sx_port[i].close_delay = 50 * HZ/100; sx_port[i].closing_wait = 3000 * HZ/100; init_waitqueue_head(&sx_port[i].open_wait); diff --git a/drivers/char/specialix_io8.h b/drivers/char/specialix_io8.h index 895bd90de363..3f2f85bdf516 100644 --- a/drivers/char/specialix_io8.h +++ b/drivers/char/specialix_io8.h @@ -112,7 +112,6 @@ struct specialix_port { struct tty_struct * tty; int count; int blocked_open; - ulong event; int timeout; int close_delay; unsigned char * xmit_buf; @@ -122,8 +121,6 @@ struct specialix_port { int xmit_cnt; wait_queue_head_t open_wait; wait_queue_head_t close_wait; - struct work_struct tqueue; - struct work_struct tqueue_hangup; short wakeup_chars; short break_length; unsigned short closing_wait; diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index 45758d5b56ef..feac54e32a12 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -145,8 +145,7 @@ static struct stlbrd *stl_brds[STL_MAXBRDS]; */ #define ASYI_TXBUSY 1 #define ASYI_TXLOW 2 -#define ASYI_DCDCHANGE 3 -#define ASYI_TXFLOWED 4 +#define ASYI_TXFLOWED 3 /* * Define an array of board names as printable strings. Handy for @@ -610,6 +609,23 @@ static const struct file_operations stl_fsiomem = { static struct class *stallion_class; +static void stl_cd_change(struct stlport *portp) +{ + unsigned int oldsigs = portp->sigs; + + if (!portp->tty) + return; + + portp->sigs = stl_getsignals(portp); + + if ((portp->sigs & TIOCM_CD) && ((oldsigs & TIOCM_CD) == 0)) + wake_up_interruptible(&portp->open_wait); + + if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) + if (portp->flags & ASYNC_CHECK_CD) + tty_hangup(portp->tty); +} + /* * Check for any arguments passed in on the module load command line. */ @@ -1771,41 +1787,6 @@ static int stl_echpci64intr(struct stlbrd *brdp) /*****************************************************************************/ /* - * Service an off-level request for some channel. - */ -static void stl_offintr(struct work_struct *work) -{ - struct stlport *portp = container_of(work, struct stlport, tqueue); - struct tty_struct *tty; - unsigned int oldsigs; - - pr_debug("stl_offintr(portp=%p)\n", portp); - - if (portp == NULL) - return; - - tty = portp->tty; - if (tty == NULL) - return; - - if (test_bit(ASYI_TXLOW, &portp->istate)) - tty_wakeup(tty); - - if (test_bit(ASYI_DCDCHANGE, &portp->istate)) { - clear_bit(ASYI_DCDCHANGE, &portp->istate); - oldsigs = portp->sigs; - portp->sigs = stl_getsignals(portp); - if ((portp->sigs & TIOCM_CD) && ((oldsigs & TIOCM_CD) == 0)) - wake_up_interruptible(&portp->open_wait); - if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) - if (portp->flags & ASYNC_CHECK_CD) - tty_hangup(tty); /* FIXME: module removal race here - AKPM */ - } -} - -/*****************************************************************************/ - -/* * Initialize all the ports on a panel. */ @@ -1840,7 +1821,6 @@ static int __devinit stl_initports(struct stlbrd *brdp, struct stlpanel *panelp) portp->baud_base = STL_BAUDBASE; portp->close_delay = STL_CLOSEDELAY; portp->closing_wait = 30 * HZ; - INIT_WORK(&portp->tqueue, stl_offintr); init_waitqueue_head(&portp->open_wait); init_waitqueue_head(&portp->close_wait); portp->stats.brd = portp->brdnr; @@ -3530,7 +3510,8 @@ static void stl_cd1400txisr(struct stlpanel *panelp, int ioaddr) if ((len == 0) || ((len < STL_TXBUFLOW) && (test_bit(ASYI_TXLOW, &portp->istate) == 0))) { set_bit(ASYI_TXLOW, &portp->istate); - schedule_work(&portp->tqueue); + if (portp->tty) + tty_wakeup(portp->tty); } if (len == 0) { @@ -3546,7 +3527,8 @@ static void stl_cd1400txisr(struct stlpanel *panelp, int ioaddr) } else { len = min(len, CD1400_TXFIFOSIZE); portp->stats.txtotal += len; - stlen = min(len, ((portp->tx.buf + STL_TXBUFSIZE) - tail)); + stlen = min_t(unsigned int, len, + (portp->tx.buf + STL_TXBUFSIZE) - tail); outb((TDR + portp->uartaddr), ioaddr); outsb((ioaddr + EREG_DATA), tail, stlen); len -= stlen; @@ -3599,7 +3581,7 @@ static void stl_cd1400rxisr(struct stlpanel *panelp, int ioaddr) outb((RDCR + portp->uartaddr), ioaddr); len = inb(ioaddr + EREG_DATA); if (tty == NULL || (buflen = tty_buffer_request_room(tty, len)) == 0) { - len = min(len, sizeof(stl_unwanted)); + len = min_t(unsigned int, len, sizeof(stl_unwanted)); outb((RDSR + portp->uartaddr), ioaddr); insb((ioaddr + EREG_DATA), &stl_unwanted[0], len); portp->stats.rxlost += len; @@ -3692,8 +3674,7 @@ static void stl_cd1400mdmisr(struct stlpanel *panelp, int ioaddr) outb((MISR + portp->uartaddr), ioaddr); misr = inb(ioaddr + EREG_DATA); if (misr & MISR_DCD) { - set_bit(ASYI_DCDCHANGE, &portp->istate); - schedule_work(&portp->tqueue); + stl_cd_change(portp); portp->stats.modem++; } @@ -4447,7 +4428,8 @@ static void stl_sc26198txisr(struct stlport *portp) if ((len == 0) || ((len < STL_TXBUFLOW) && (test_bit(ASYI_TXLOW, &portp->istate) == 0))) { set_bit(ASYI_TXLOW, &portp->istate); - schedule_work(&portp->tqueue); + if (portp->tty) + tty_wakeup(portp->tty); } if (len == 0) { @@ -4465,7 +4447,8 @@ static void stl_sc26198txisr(struct stlport *portp) } else { len = min(len, SC26198_TXFIFOSIZE); portp->stats.txtotal += len; - stlen = min(len, ((portp->tx.buf + STL_TXBUFSIZE) - tail)); + stlen = min_t(unsigned int, len, + (portp->tx.buf + STL_TXBUFSIZE) - tail); outb(GTXFIFO, (ioaddr + XP_ADDR)); outsb((ioaddr + XP_DATA), tail, stlen); len -= stlen; @@ -4506,7 +4489,7 @@ static void stl_sc26198rxisr(struct stlport *portp, unsigned int iack) if ((iack & IVR_TYPEMASK) == IVR_RXDATA) { if (tty == NULL || (buflen = tty_buffer_request_room(tty, len)) == 0) { - len = min(len, sizeof(stl_unwanted)); + len = min_t(unsigned int, len, sizeof(stl_unwanted)); outb(GRXFIFO, (ioaddr + XP_ADDR)); insb((ioaddr + XP_DATA), &stl_unwanted[0], len); portp->stats.rxlost += len; @@ -4647,8 +4630,7 @@ static void stl_sc26198otherisr(struct stlport *portp, unsigned int iack) case CIR_SUBCOS: ipr = stl_sc26198getreg(portp, IPR); if (ipr & IPR_DCDCHANGE) { - set_bit(ASYI_DCDCHANGE, &portp->istate); - schedule_work(&portp->tqueue); + stl_cd_change(portp); portp->stats.modem++; } break; diff --git a/drivers/char/sx.h b/drivers/char/sx.h index 70d9783c7323..87c2defdead7 100644 --- a/drivers/char/sx.h +++ b/drivers/char/sx.h @@ -88,8 +88,6 @@ struct vpd_prom { #define IS_CF_BOARD(board) (board->flags & (SX_CFISA_BOARD | SX_CFPCI_BOARD)) -#define SERIAL_TYPE_NORMAL 1 - /* The SI processor clock is required to calculate the cc_int_count register value for the SI cards. */ #define SI_PROCESSOR_CLOCK 25000000 diff --git a/drivers/char/xilinx_hwicap/Makefile b/drivers/char/xilinx_hwicap/Makefile new file mode 100644 index 000000000000..5491cbc42f43 --- /dev/null +++ b/drivers/char/xilinx_hwicap/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the Xilinx OPB hwicap driver +# + +obj-$(CONFIG_XILINX_HWICAP) += xilinx_hwicap_m.o + +xilinx_hwicap_m-y := xilinx_hwicap.o fifo_icap.o buffer_icap.o diff --git a/drivers/char/xilinx_hwicap/buffer_icap.c b/drivers/char/xilinx_hwicap/buffer_icap.c new file mode 100644 index 000000000000..dfea2bde162b --- /dev/null +++ b/drivers/char/xilinx_hwicap/buffer_icap.c @@ -0,0 +1,380 @@ +/***************************************************************************** + * + * Author: Xilinx, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" + * AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND + * SOLUTIONS FOR XILINX DEVICES. BY PROVIDING THIS DESIGN, CODE, + * OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, + * APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION + * THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT, + * AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE + * FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY DISCLAIMS ANY + * WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE + * IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR + * REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF + * INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE. + * + * Xilinx products are not intended for use in life support appliances, + * devices, or systems. Use in such applications is expressly prohibited. + * + * (c) Copyright 2003-2008 Xilinx Inc. + * All rights reserved. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + *****************************************************************************/ + +#include "buffer_icap.h" + +/* Indicates how many bytes will fit in a buffer. (1 BRAM) */ +#define XHI_MAX_BUFFER_BYTES 2048 +#define XHI_MAX_BUFFER_INTS (XHI_MAX_BUFFER_BYTES >> 2) + +/* File access and error constants */ +#define XHI_DEVICE_READ_ERROR -1 +#define XHI_DEVICE_WRITE_ERROR -2 +#define XHI_BUFFER_OVERFLOW_ERROR -3 + +#define XHI_DEVICE_READ 0x1 +#define XHI_DEVICE_WRITE 0x0 + +/* Constants for checking transfer status */ +#define XHI_CYCLE_DONE 0 +#define XHI_CYCLE_EXECUTING 1 + +/* buffer_icap register offsets */ + +/* Size of transfer, read & write */ +#define XHI_SIZE_REG_OFFSET 0x800L +/* offset into bram, read & write */ +#define XHI_BRAM_OFFSET_REG_OFFSET 0x804L +/* Read not Configure, direction of transfer. Write only */ +#define XHI_RNC_REG_OFFSET 0x808L +/* Indicates transfer complete. Read only */ +#define XHI_STATUS_REG_OFFSET 0x80CL + +/* Constants for setting the RNC register */ +#define XHI_CONFIGURE 0x0UL +#define XHI_READBACK 0x1UL + +/* Constants for the Done register */ +#define XHI_NOT_FINISHED 0x0UL +#define XHI_FINISHED 0x1UL + +#define XHI_BUFFER_START 0 + +/** + * buffer_icap_get_status: Get the contents of the status register. + * @parameter base_address: is the base address of the device + * + * The status register contains the ICAP status and the done bit. + * + * D8 - cfgerr + * D7 - dalign + * D6 - rip + * D5 - in_abort_l + * D4 - Always 1 + * D3 - Always 1 + * D2 - Always 1 + * D1 - Always 1 + * D0 - Done bit + **/ +static inline u32 buffer_icap_get_status(void __iomem *base_address) +{ + return in_be32(base_address + XHI_STATUS_REG_OFFSET); +} + +/** + * buffer_icap_get_bram: Reads data from the storage buffer bram. + * @parameter base_address: contains the base address of the component. + * @parameter offset: The word offset from which the data should be read. + * + * A bram is used as a configuration memory cache. One frame of data can + * be stored in this "storage buffer". + **/ +static inline u32 buffer_icap_get_bram(void __iomem *base_address, + u32 offset) +{ + return in_be32(base_address + (offset << 2)); +} + +/** + * buffer_icap_busy: Return true if the icap device is busy + * @parameter base_address: is the base address of the device + * + * The queries the low order bit of the status register, which + * indicates whether the current configuration or readback operation + * has completed. + **/ +static inline bool buffer_icap_busy(void __iomem *base_address) +{ + return (buffer_icap_get_status(base_address) & 1) == XHI_NOT_FINISHED; +} + +/** + * buffer_icap_busy: Return true if the icap device is not busy + * @parameter base_address: is the base address of the device + * + * The queries the low order bit of the status register, which + * indicates whether the current configuration or readback operation + * has completed. + **/ +static inline bool buffer_icap_done(void __iomem *base_address) +{ + return (buffer_icap_get_status(base_address) & 1) == XHI_FINISHED; +} + +/** + * buffer_icap_set_size: Set the size register. + * @parameter base_address: is the base address of the device + * @parameter data: The size in bytes. + * + * The size register holds the number of 8 bit bytes to transfer between + * bram and the icap (or icap to bram). + **/ +static inline void buffer_icap_set_size(void __iomem *base_address, + u32 data) +{ + out_be32(base_address + XHI_SIZE_REG_OFFSET, data); +} + +/** + * buffer_icap_mSetoffsetReg: Set the bram offset register. + * @parameter base_address: contains the base address of the device. + * @parameter data: is the value to be written to the data register. + * + * The bram offset register holds the starting bram address to transfer + * data from during configuration or write data to during readback. + **/ +static inline void buffer_icap_set_offset(void __iomem *base_address, + u32 data) +{ + out_be32(base_address + XHI_BRAM_OFFSET_REG_OFFSET, data); +} + +/** + * buffer_icap_set_rnc: Set the RNC (Readback not Configure) register. + * @parameter base_address: contains the base address of the device. + * @parameter data: is the value to be written to the data register. + * + * The RNC register determines the direction of the data transfer. It + * controls whether a configuration or readback take place. Writing to + * this register initiates the transfer. A value of 1 initiates a + * readback while writing a value of 0 initiates a configuration. + **/ +static inline void buffer_icap_set_rnc(void __iomem *base_address, + u32 data) +{ + out_be32(base_address + XHI_RNC_REG_OFFSET, data); +} + +/** + * buffer_icap_set_bram: Write data to the storage buffer bram. + * @parameter base_address: contains the base address of the component. + * @parameter offset: The word offset at which the data should be written. + * @parameter data: The value to be written to the bram offset. + * + * A bram is used as a configuration memory cache. One frame of data can + * be stored in this "storage buffer". + **/ +static inline void buffer_icap_set_bram(void __iomem *base_address, + u32 offset, u32 data) +{ + out_be32(base_address + (offset << 2), data); +} + +/** + * buffer_icap_device_read: Transfer bytes from ICAP to the storage buffer. + * @parameter drvdata: a pointer to the drvdata. + * @parameter offset: The storage buffer start address. + * @parameter count: The number of words (32 bit) to read from the + * device (ICAP). + **/ +static int buffer_icap_device_read(struct hwicap_drvdata *drvdata, + u32 offset, u32 count) +{ + + s32 retries = 0; + void __iomem *base_address = drvdata->base_address; + + if (buffer_icap_busy(base_address)) + return -EBUSY; + + if ((offset + count) > XHI_MAX_BUFFER_INTS) + return -EINVAL; + + /* setSize count*4 to get bytes. */ + buffer_icap_set_size(base_address, (count << 2)); + buffer_icap_set_offset(base_address, offset); + buffer_icap_set_rnc(base_address, XHI_READBACK); + + while (buffer_icap_busy(base_address)) { + retries++; + if (retries > XHI_MAX_RETRIES) + return -EBUSY; + } + return 0; + +}; + +/** + * buffer_icap_device_write: Transfer bytes from ICAP to the storage buffer. + * @parameter drvdata: a pointer to the drvdata. + * @parameter offset: The storage buffer start address. + * @parameter count: The number of words (32 bit) to read from the + * device (ICAP). + **/ +static int buffer_icap_device_write(struct hwicap_drvdata *drvdata, + u32 offset, u32 count) +{ + + s32 retries = 0; + void __iomem *base_address = drvdata->base_address; + + if (buffer_icap_busy(base_address)) + return -EBUSY; + + if ((offset + count) > XHI_MAX_BUFFER_INTS) + return -EINVAL; + + /* setSize count*4 to get bytes. */ + buffer_icap_set_size(base_address, count << 2); + buffer_icap_set_offset(base_address, offset); + buffer_icap_set_rnc(base_address, XHI_CONFIGURE); + + while (buffer_icap_busy(base_address)) { + retries++; + if (retries > XHI_MAX_RETRIES) + return -EBUSY; + } + return 0; + +}; + +/** + * buffer_icap_reset: Reset the logic of the icap device. + * @parameter drvdata: a pointer to the drvdata. + * + * Writing to the status register resets the ICAP logic in an internal + * version of the core. For the version of the core published in EDK, + * this is a noop. + **/ +void buffer_icap_reset(struct hwicap_drvdata *drvdata) +{ + out_be32(drvdata->base_address + XHI_STATUS_REG_OFFSET, 0xFEFE); +} + +/** + * buffer_icap_set_configuration: Load a partial bitstream from system memory. + * @parameter drvdata: a pointer to the drvdata. + * @parameter data: Kernel address of the partial bitstream. + * @parameter size: the size of the partial bitstream in 32 bit words. + **/ +int buffer_icap_set_configuration(struct hwicap_drvdata *drvdata, u32 *data, + u32 size) +{ + int status; + s32 buffer_count = 0; + s32 num_writes = 0; + bool dirty = 0; + u32 i; + void __iomem *base_address = drvdata->base_address; + + /* Loop through all the data */ + for (i = 0, buffer_count = 0; i < size; i++) { + + /* Copy data to bram */ + buffer_icap_set_bram(base_address, buffer_count, data[i]); + dirty = 1; + + if (buffer_count < XHI_MAX_BUFFER_INTS - 1) { + buffer_count++; + continue; + } + + /* Write data to ICAP */ + status = buffer_icap_device_write( + drvdata, + XHI_BUFFER_START, + XHI_MAX_BUFFER_INTS); + if (status != 0) { + /* abort. */ + buffer_icap_reset(drvdata); + return status; + } + + buffer_count = 0; + num_writes++; + dirty = 0; + } + + /* Write unwritten data to ICAP */ + if (dirty) { + /* Write data to ICAP */ + status = buffer_icap_device_write(drvdata, XHI_BUFFER_START, + buffer_count); + if (status != 0) { + /* abort. */ + buffer_icap_reset(drvdata); + } + return status; + } + + return 0; +}; + +/** + * buffer_icap_get_configuration: Read configuration data from the device. + * @parameter drvdata: a pointer to the drvdata. + * @parameter data: Address of the data representing the partial bitstream + * @parameter size: the size of the partial bitstream in 32 bit words. + **/ +int buffer_icap_get_configuration(struct hwicap_drvdata *drvdata, u32 *data, + u32 size) +{ + int status; + s32 buffer_count = 0; + s32 read_count = 0; + u32 i; + void __iomem *base_address = drvdata->base_address; + + /* Loop through all the data */ + for (i = 0, buffer_count = XHI_MAX_BUFFER_INTS; i < size; i++) { + if (buffer_count == XHI_MAX_BUFFER_INTS) { + u32 words_remaining = size - i; + u32 words_to_read = + words_remaining < + XHI_MAX_BUFFER_INTS ? words_remaining : + XHI_MAX_BUFFER_INTS; + + /* Read data from ICAP */ + status = buffer_icap_device_read( + drvdata, + XHI_BUFFER_START, + words_to_read); + if (status != 0) { + /* abort. */ + buffer_icap_reset(drvdata); + return status; + } + + buffer_count = 0; + read_count++; + } + + /* Copy data from bram */ + data[i] = buffer_icap_get_bram(base_address, buffer_count); + buffer_count++; + } + + return 0; +}; diff --git a/drivers/char/xilinx_hwicap/buffer_icap.h b/drivers/char/xilinx_hwicap/buffer_icap.h new file mode 100644 index 000000000000..03184959fa00 --- /dev/null +++ b/drivers/char/xilinx_hwicap/buffer_icap.h @@ -0,0 +1,57 @@ +/***************************************************************************** + * + * Author: Xilinx, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" + * AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND + * SOLUTIONS FOR XILINX DEVICES. BY PROVIDING THIS DESIGN, CODE, + * OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, + * APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION + * THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT, + * AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE + * FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY DISCLAIMS ANY + * WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE + * IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR + * REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF + * INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE. + * + * Xilinx products are not intended for use in life support appliances, + * devices, or systems. Use in such applications is expressly prohibited. + * + * (c) Copyright 2003-2008 Xilinx Inc. + * All rights reserved. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + *****************************************************************************/ + +#ifndef XILINX_BUFFER_ICAP_H_ /* prevent circular inclusions */ +#define XILINX_BUFFER_ICAP_H_ /* by using protection macros */ + +#include <linux/types.h> +#include <linux/cdev.h> +#include <linux/version.h> +#include <linux/platform_device.h> + +#include <asm/io.h> +#include "xilinx_hwicap.h" + +void buffer_icap_reset(struct hwicap_drvdata *drvdata); + +/* Loads a partial bitstream from system memory. */ +int buffer_icap_set_configuration(struct hwicap_drvdata *drvdata, u32 *data, + u32 Size); + +/* Loads a partial bitstream from system memory. */ +int buffer_icap_get_configuration(struct hwicap_drvdata *drvdata, u32 *data, + u32 Size); + +#endif diff --git a/drivers/char/xilinx_hwicap/fifo_icap.c b/drivers/char/xilinx_hwicap/fifo_icap.c new file mode 100644 index 000000000000..0988314694a6 --- /dev/null +++ b/drivers/char/xilinx_hwicap/fifo_icap.c @@ -0,0 +1,381 @@ +/***************************************************************************** + * + * Author: Xilinx, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" + * AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND + * SOLUTIONS FOR XILINX DEVICES. BY PROVIDING THIS DESIGN, CODE, + * OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, + * APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION + * THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT, + * AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE + * FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY DISCLAIMS ANY + * WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE + * IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR + * REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF + * INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE. + * + * Xilinx products are not intended for use in life support appliances, + * devices, or systems. Use in such applications is expressly prohibited. + * + * (c) Copyright 2007-2008 Xilinx Inc. + * All rights reserved. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + *****************************************************************************/ + +#include "fifo_icap.h" + +/* Register offsets for the XHwIcap device. */ +#define XHI_GIER_OFFSET 0x1C /* Device Global Interrupt Enable Reg */ +#define XHI_IPISR_OFFSET 0x20 /* Interrupt Status Register */ +#define XHI_IPIER_OFFSET 0x28 /* Interrupt Enable Register */ +#define XHI_WF_OFFSET 0x100 /* Write FIFO */ +#define XHI_RF_OFFSET 0x104 /* Read FIFO */ +#define XHI_SZ_OFFSET 0x108 /* Size Register */ +#define XHI_CR_OFFSET 0x10C /* Control Register */ +#define XHI_SR_OFFSET 0x110 /* Status Register */ +#define XHI_WFV_OFFSET 0x114 /* Write FIFO Vacancy Register */ +#define XHI_RFO_OFFSET 0x118 /* Read FIFO Occupancy Register */ + +/* Device Global Interrupt Enable Register (GIER) bit definitions */ + +#define XHI_GIER_GIE_MASK 0x80000000 /* Global Interrupt enable Mask */ + +/** + * HwIcap Device Interrupt Status/Enable Registers + * + * Interrupt Status Register (IPISR) : This register holds the + * interrupt status flags for the device. These bits are toggle on + * write. + * + * Interrupt Enable Register (IPIER) : This register is used to enable + * interrupt sources for the device. + * Writing a '1' to a bit enables the corresponding interrupt. + * Writing a '0' to a bit disables the corresponding interrupt. + * + * IPISR/IPIER registers have the same bit definitions and are only defined + * once. + */ +#define XHI_IPIXR_RFULL_MASK 0x00000008 /* Read FIFO Full */ +#define XHI_IPIXR_WEMPTY_MASK 0x00000004 /* Write FIFO Empty */ +#define XHI_IPIXR_RDP_MASK 0x00000002 /* Read FIFO half full */ +#define XHI_IPIXR_WRP_MASK 0x00000001 /* Write FIFO half full */ +#define XHI_IPIXR_ALL_MASK 0x0000000F /* Mask of all interrupts */ + +/* Control Register (CR) */ +#define XHI_CR_SW_RESET_MASK 0x00000008 /* SW Reset Mask */ +#define XHI_CR_FIFO_CLR_MASK 0x00000004 /* FIFO Clear Mask */ +#define XHI_CR_READ_MASK 0x00000002 /* Read from ICAP to FIFO */ +#define XHI_CR_WRITE_MASK 0x00000001 /* Write from FIFO to ICAP */ + +/* Status Register (SR) */ +#define XHI_SR_CFGERR_N_MASK 0x00000100 /* Config Error Mask */ +#define XHI_SR_DALIGN_MASK 0x00000080 /* Data Alignment Mask */ +#define XHI_SR_RIP_MASK 0x00000040 /* Read back Mask */ +#define XHI_SR_IN_ABORT_N_MASK 0x00000020 /* Select Map Abort Mask */ +#define XHI_SR_DONE_MASK 0x00000001 /* Done bit Mask */ + + +#define XHI_WFO_MAX_VACANCY 1024 /* Max Write FIFO Vacancy, in words */ +#define XHI_RFO_MAX_OCCUPANCY 256 /* Max Read FIFO Occupancy, in words */ +/* The maximum amount we can request from fifo_icap_get_configuration + at once, in bytes. */ +#define XHI_MAX_READ_TRANSACTION_WORDS 0xFFF + + +/** + * fifo_icap_fifo_write: Write data to the write FIFO. + * @parameter drvdata: a pointer to the drvdata. + * @parameter data: the 32-bit value to be written to the FIFO. + * + * This function will silently fail if the fifo is full. + **/ +static inline void fifo_icap_fifo_write(struct hwicap_drvdata *drvdata, + u32 data) +{ + dev_dbg(drvdata->dev, "fifo_write: %x\n", data); + out_be32(drvdata->base_address + XHI_WF_OFFSET, data); +} + +/** + * fifo_icap_fifo_read: Read data from the Read FIFO. + * @parameter drvdata: a pointer to the drvdata. + * + * This function will silently fail if the fifo is empty. + **/ +static inline u32 fifo_icap_fifo_read(struct hwicap_drvdata *drvdata) +{ + u32 data = in_be32(drvdata->base_address + XHI_RF_OFFSET); + dev_dbg(drvdata->dev, "fifo_read: %x\n", data); + return data; +} + +/** + * fifo_icap_set_read_size: Set the the size register. + * @parameter drvdata: a pointer to the drvdata. + * @parameter data: the size of the following read transaction, in words. + **/ +static inline void fifo_icap_set_read_size(struct hwicap_drvdata *drvdata, + u32 data) +{ + out_be32(drvdata->base_address + XHI_SZ_OFFSET, data); +} + +/** + * fifo_icap_start_config: Initiate a configuration (write) to the device. + * @parameter drvdata: a pointer to the drvdata. + **/ +static inline void fifo_icap_start_config(struct hwicap_drvdata *drvdata) +{ + out_be32(drvdata->base_address + XHI_CR_OFFSET, XHI_CR_WRITE_MASK); + dev_dbg(drvdata->dev, "configuration started\n"); +} + +/** + * fifo_icap_start_readback: Initiate a readback from the device. + * @parameter drvdata: a pointer to the drvdata. + **/ +static inline void fifo_icap_start_readback(struct hwicap_drvdata *drvdata) +{ + out_be32(drvdata->base_address + XHI_CR_OFFSET, XHI_CR_READ_MASK); + dev_dbg(drvdata->dev, "readback started\n"); +} + +/** + * fifo_icap_busy: Return true if the ICAP is still processing a transaction. + * @parameter drvdata: a pointer to the drvdata. + **/ +static inline u32 fifo_icap_busy(struct hwicap_drvdata *drvdata) +{ + u32 status = in_be32(drvdata->base_address + XHI_SR_OFFSET); + dev_dbg(drvdata->dev, "Getting status = %x\n", status); + return (status & XHI_SR_DONE_MASK) ? 0 : 1; +} + +/** + * fifo_icap_write_fifo_vacancy: Query the write fifo available space. + * @parameter drvdata: a pointer to the drvdata. + * + * Return the number of words that can be safely pushed into the write fifo. + **/ +static inline u32 fifo_icap_write_fifo_vacancy( + struct hwicap_drvdata *drvdata) +{ + return in_be32(drvdata->base_address + XHI_WFV_OFFSET); +} + +/** + * fifo_icap_read_fifo_occupancy: Query the read fifo available data. + * @parameter drvdata: a pointer to the drvdata. + * + * Return the number of words that can be safely read from the read fifo. + **/ +static inline u32 fifo_icap_read_fifo_occupancy( + struct hwicap_drvdata *drvdata) +{ + return in_be32(drvdata->base_address + XHI_RFO_OFFSET); +} + +/** + * fifo_icap_set_configuration: Send configuration data to the ICAP. + * @parameter drvdata: a pointer to the drvdata. + * @parameter frame_buffer: a pointer to the data to be written to the + * ICAP device. + * @parameter num_words: the number of words (32 bit) to write to the ICAP + * device. + + * This function writes the given user data to the Write FIFO in + * polled mode and starts the transfer of the data to + * the ICAP device. + **/ +int fifo_icap_set_configuration(struct hwicap_drvdata *drvdata, + u32 *frame_buffer, u32 num_words) +{ + + u32 write_fifo_vacancy = 0; + u32 retries = 0; + u32 remaining_words; + + dev_dbg(drvdata->dev, "fifo_set_configuration\n"); + + /* + * Check if the ICAP device is Busy with the last Read/Write + */ + if (fifo_icap_busy(drvdata)) + return -EBUSY; + + /* + * Set up the buffer pointer and the words to be transferred. + */ + remaining_words = num_words; + + while (remaining_words > 0) { + /* + * Wait until we have some data in the fifo. + */ + while (write_fifo_vacancy == 0) { + write_fifo_vacancy = + fifo_icap_write_fifo_vacancy(drvdata); + retries++; + if (retries > XHI_MAX_RETRIES) + return -EIO; + } + + /* + * Write data into the Write FIFO. + */ + while ((write_fifo_vacancy != 0) && + (remaining_words > 0)) { + fifo_icap_fifo_write(drvdata, *frame_buffer); + + remaining_words--; + write_fifo_vacancy--; + frame_buffer++; + } + /* Start pushing whatever is in the FIFO into the ICAP. */ + fifo_icap_start_config(drvdata); + } + + /* Wait until the write has finished. */ + while (fifo_icap_busy(drvdata)) { + retries++; + if (retries > XHI_MAX_RETRIES) + break; + } + + dev_dbg(drvdata->dev, "done fifo_set_configuration\n"); + + /* + * If the requested number of words have not been read from + * the device then indicate failure. + */ + if (remaining_words != 0) + return -EIO; + + return 0; +} + +/** + * fifo_icap_get_configuration: Read configuration data from the device. + * @parameter drvdata: a pointer to the drvdata. + * @parameter data: Address of the data representing the partial bitstream + * @parameter size: the size of the partial bitstream in 32 bit words. + * + * This function reads the specified number of words from the ICAP device in + * the polled mode. + */ +int fifo_icap_get_configuration(struct hwicap_drvdata *drvdata, + u32 *frame_buffer, u32 num_words) +{ + + u32 read_fifo_occupancy = 0; + u32 retries = 0; + u32 *data = frame_buffer; + u32 remaining_words; + u32 words_to_read; + + dev_dbg(drvdata->dev, "fifo_get_configuration\n"); + + /* + * Check if the ICAP device is Busy with the last Write/Read + */ + if (fifo_icap_busy(drvdata)) + return -EBUSY; + + remaining_words = num_words; + + while (remaining_words > 0) { + words_to_read = remaining_words; + /* The hardware has a limit on the number of words + that can be read at one time. */ + if (words_to_read > XHI_MAX_READ_TRANSACTION_WORDS) + words_to_read = XHI_MAX_READ_TRANSACTION_WORDS; + + remaining_words -= words_to_read; + + fifo_icap_set_read_size(drvdata, words_to_read); + fifo_icap_start_readback(drvdata); + + while (words_to_read > 0) { + /* Wait until we have some data in the fifo. */ + while (read_fifo_occupancy == 0) { + read_fifo_occupancy = + fifo_icap_read_fifo_occupancy(drvdata); + retries++; + if (retries > XHI_MAX_RETRIES) + return -EIO; + } + + if (read_fifo_occupancy > words_to_read) + read_fifo_occupancy = words_to_read; + + words_to_read -= read_fifo_occupancy; + + /* Read the data from the Read FIFO. */ + while (read_fifo_occupancy != 0) { + *data++ = fifo_icap_fifo_read(drvdata); + read_fifo_occupancy--; + } + } + } + + dev_dbg(drvdata->dev, "done fifo_get_configuration\n"); + + return 0; +} + +/** + * buffer_icap_reset: Reset the logic of the icap device. + * @parameter drvdata: a pointer to the drvdata. + * + * This function forces the software reset of the complete HWICAP device. + * All the registers will return to the default value and the FIFO is also + * flushed as a part of this software reset. + */ +void fifo_icap_reset(struct hwicap_drvdata *drvdata) +{ + u32 reg_data; + /* + * Reset the device by setting/clearing the RESET bit in the + * Control Register. + */ + reg_data = in_be32(drvdata->base_address + XHI_CR_OFFSET); + + out_be32(drvdata->base_address + XHI_CR_OFFSET, + reg_data | XHI_CR_SW_RESET_MASK); + + out_be32(drvdata->base_address + XHI_CR_OFFSET, + reg_data & (~XHI_CR_SW_RESET_MASK)); + +} + +/** + * fifo_icap_flush_fifo: This function flushes the FIFOs in the device. + * @parameter drvdata: a pointer to the drvdata. + */ +void fifo_icap_flush_fifo(struct hwicap_drvdata *drvdata) +{ + u32 reg_data; + /* + * Flush the FIFO by setting/clearing the FIFO Clear bit in the + * Control Register. + */ + reg_data = in_be32(drvdata->base_address + XHI_CR_OFFSET); + + out_be32(drvdata->base_address + XHI_CR_OFFSET, + reg_data | XHI_CR_FIFO_CLR_MASK); + + out_be32(drvdata->base_address + XHI_CR_OFFSET, + reg_data & (~XHI_CR_FIFO_CLR_MASK)); +} + diff --git a/drivers/char/xilinx_hwicap/fifo_icap.h b/drivers/char/xilinx_hwicap/fifo_icap.h new file mode 100644 index 000000000000..4d3068dd0405 --- /dev/null +++ b/drivers/char/xilinx_hwicap/fifo_icap.h @@ -0,0 +1,62 @@ +/***************************************************************************** + * + * Author: Xilinx, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" + * AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND + * SOLUTIONS FOR XILINX DEVICES. BY PROVIDING THIS DESIGN, CODE, + * OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, + * APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION + * THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT, + * AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE + * FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY DISCLAIMS ANY + * WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE + * IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR + * REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF + * INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE. + * + * Xilinx products are not intended for use in life support appliances, + * devices, or systems. Use in such applications is expressly prohibited. + * + * (c) Copyright 2007-2008 Xilinx Inc. + * All rights reserved. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + *****************************************************************************/ + +#ifndef XILINX_FIFO_ICAP_H_ /* prevent circular inclusions */ +#define XILINX_FIFO_ICAP_H_ /* by using protection macros */ + +#include <linux/types.h> +#include <linux/cdev.h> +#include <linux/version.h> +#include <linux/platform_device.h> + +#include <asm/io.h> +#include "xilinx_hwicap.h" + +/* Reads integers from the device into the storage buffer. */ +int fifo_icap_get_configuration( + struct hwicap_drvdata *drvdata, + u32 *FrameBuffer, + u32 NumWords); + +/* Writes integers to the device from the storage buffer. */ +int fifo_icap_set_configuration( + struct hwicap_drvdata *drvdata, + u32 *FrameBuffer, + u32 NumWords); + +void fifo_icap_reset(struct hwicap_drvdata *drvdata); +void fifo_icap_flush_fifo(struct hwicap_drvdata *drvdata); + +#endif diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c new file mode 100644 index 000000000000..24f6aef0fd3c --- /dev/null +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c @@ -0,0 +1,904 @@ +/***************************************************************************** + * + * Author: Xilinx, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" + * AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND + * SOLUTIONS FOR XILINX DEVICES. BY PROVIDING THIS DESIGN, CODE, + * OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, + * APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION + * THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT, + * AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE + * FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY DISCLAIMS ANY + * WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE + * IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR + * REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF + * INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE. + * + * Xilinx products are not intended for use in life support appliances, + * devices, or systems. Use in such applications is expressly prohibited. + * + * (c) Copyright 2002 Xilinx Inc., Systems Engineering Group + * (c) Copyright 2004 Xilinx Inc., Systems Engineering Group + * (c) Copyright 2007-2008 Xilinx Inc. + * All rights reserved. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + *****************************************************************************/ + +/* + * This is the code behind /dev/xilinx_icap -- it allows a user-space + * application to use the Xilinx ICAP subsystem. + * + * The following operations are possible: + * + * open open the port and initialize for access. + * release release port + * write Write a bitstream to the configuration processor. + * read Read a data stream from the configuration processor. + * + * After being opened, the port is initialized and accessed to avoid a + * corrupted first read which may occur with some hardware. The port + * is left in a desynched state, requiring that a synch sequence be + * transmitted before any valid configuration data. A user will have + * exclusive access to the device while it remains open, and the state + * of the ICAP cannot be guaranteed after the device is closed. Note + * that a complete reset of the core and the state of the ICAP cannot + * be performed on many versions of the cores, hence users of this + * device should avoid making inconsistent accesses to the device. In + * particular, accessing the read interface, without first generating + * a write containing a readback packet can leave the ICAP in an + * inaccessible state. + * + * Note that in order to use the read interface, it is first necessary + * to write a request packet to the write interface. i.e., it is not + * possible to simply readback the bitstream (or any configuration + * bits) from a device without specifically requesting them first. + * The code to craft such packets is intended to be part of the + * user-space application code that uses this device. The simplest + * way to use this interface is simply: + * + * cp foo.bit /dev/xilinx_icap + * + * Note that unless foo.bit is an appropriately constructed partial + * bitstream, this has a high likelyhood of overwriting the design + * currently programmed in the FPGA. + */ + +#include <linux/version.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/fcntl.h> +#include <linux/init.h> +#include <linux/poll.h> +#include <linux/proc_fs.h> +#include <asm/semaphore.h> +#include <linux/sysctl.h> +#include <linux/version.h> +#include <linux/fs.h> +#include <linux/cdev.h> +#include <linux/platform_device.h> + +#include <asm/io.h> +#include <asm/uaccess.h> +#include <asm/system.h> + +#ifdef CONFIG_OF +/* For open firmware. */ +#include <linux/of_device.h> +#include <linux/of_platform.h> +#endif + +#include "xilinx_hwicap.h" +#include "buffer_icap.h" +#include "fifo_icap.h" + +#define DRIVER_NAME "xilinx_icap" + +#define HWICAP_REGS (0x10000) + +/* dynamically allocate device number */ +static int xhwicap_major; +static int xhwicap_minor; +#define HWICAP_DEVICES 1 + +module_param(xhwicap_major, int, S_IRUGO); +module_param(xhwicap_minor, int, S_IRUGO); + +/* An array, which is set to true when the device is registered. */ +static bool probed_devices[HWICAP_DEVICES]; + +static struct class *icap_class; + +#define UNIMPLEMENTED 0xFFFF + +static const struct config_registers v2_config_registers = { + .CRC = 0, + .FAR = 1, + .FDRI = 2, + .FDRO = 3, + .CMD = 4, + .CTL = 5, + .MASK = 6, + .STAT = 7, + .LOUT = 8, + .COR = 9, + .MFWR = 10, + .FLR = 11, + .KEY = 12, + .CBC = 13, + .IDCODE = 14, + .AXSS = UNIMPLEMENTED, + .C0R_1 = UNIMPLEMENTED, + .CSOB = UNIMPLEMENTED, + .WBSTAR = UNIMPLEMENTED, + .TIMER = UNIMPLEMENTED, + .BOOTSTS = UNIMPLEMENTED, + .CTL_1 = UNIMPLEMENTED, +}; + +static const struct config_registers v4_config_registers = { + .CRC = 0, + .FAR = 1, + .FDRI = 2, + .FDRO = 3, + .CMD = 4, + .CTL = 5, + .MASK = 6, + .STAT = 7, + .LOUT = 8, + .COR = 9, + .MFWR = 10, + .FLR = UNIMPLEMENTED, + .KEY = UNIMPLEMENTED, + .CBC = 11, + .IDCODE = 12, + .AXSS = 13, + .C0R_1 = UNIMPLEMENTED, + .CSOB = UNIMPLEMENTED, + .WBSTAR = UNIMPLEMENTED, + .TIMER = UNIMPLEMENTED, + .BOOTSTS = UNIMPLEMENTED, + .CTL_1 = UNIMPLEMENTED, +}; +static const struct config_registers v5_config_registers = { + .CRC = 0, + .FAR = 1, + .FDRI = 2, + .FDRO = 3, + .CMD = 4, + .CTL = 5, + .MASK = 6, + .STAT = 7, + .LOUT = 8, + .COR = 9, + .MFWR = 10, + .FLR = UNIMPLEMENTED, + .KEY = UNIMPLEMENTED, + .CBC = 11, + .IDCODE = 12, + .AXSS = 13, + .C0R_1 = 14, + .CSOB = 15, + .WBSTAR = 16, + .TIMER = 17, + .BOOTSTS = 18, + .CTL_1 = 19, +}; + +/** + * hwicap_command_desync: Send a DESYNC command to the ICAP port. + * @parameter drvdata: a pointer to the drvdata. + * + * This command desynchronizes the ICAP After this command, a + * bitstream containing a NULL packet, followed by a SYNCH packet is + * required before the ICAP will recognize commands. + */ +int hwicap_command_desync(struct hwicap_drvdata *drvdata) +{ + u32 buffer[4]; + u32 index = 0; + + /* + * Create the data to be written to the ICAP. + */ + buffer[index++] = hwicap_type_1_write(drvdata->config_regs->CMD) | 1; + buffer[index++] = XHI_CMD_DESYNCH; + buffer[index++] = XHI_NOOP_PACKET; + buffer[index++] = XHI_NOOP_PACKET; + + /* + * Write the data to the FIFO and intiate the transfer of data present + * in the FIFO to the ICAP device. + */ + return drvdata->config->set_configuration(drvdata, + &buffer[0], index); +} + +/** + * hwicap_command_capture: Send a CAPTURE command to the ICAP port. + * @parameter drvdata: a pointer to the drvdata. + * + * This command captures all of the flip flop states so they will be + * available during readback. One can use this command instead of + * enabling the CAPTURE block in the design. + */ +int hwicap_command_capture(struct hwicap_drvdata *drvdata) +{ + u32 buffer[7]; + u32 index = 0; + + /* + * Create the data to be written to the ICAP. + */ + buffer[index++] = XHI_DUMMY_PACKET; + buffer[index++] = XHI_SYNC_PACKET; + buffer[index++] = XHI_NOOP_PACKET; + buffer[index++] = hwicap_type_1_write(drvdata->config_regs->CMD) | 1; + buffer[index++] = XHI_CMD_GCAPTURE; + buffer[index++] = XHI_DUMMY_PACKET; + buffer[index++] = XHI_DUMMY_PACKET; + + /* + * Write the data to the FIFO and intiate the transfer of data + * present in the FIFO to the ICAP device. + */ + return drvdata->config->set_configuration(drvdata, + &buffer[0], index); + +} + +/** + * hwicap_get_configuration_register: Query a configuration register. + * @parameter drvdata: a pointer to the drvdata. + * @parameter reg: a constant which represents the configuration + * register value to be returned. + * Examples: XHI_IDCODE, XHI_FLR. + * @parameter RegData: returns the value of the register. + * + * Sends a query packet to the ICAP and then receives the response. + * The icap is left in Synched state. + */ +int hwicap_get_configuration_register(struct hwicap_drvdata *drvdata, + u32 reg, u32 *RegData) +{ + int status; + u32 buffer[6]; + u32 index = 0; + + /* + * Create the data to be written to the ICAP. + */ + buffer[index++] = XHI_DUMMY_PACKET; + buffer[index++] = XHI_SYNC_PACKET; + buffer[index++] = XHI_NOOP_PACKET; + buffer[index++] = hwicap_type_1_read(reg) | 1; + buffer[index++] = XHI_NOOP_PACKET; + buffer[index++] = XHI_NOOP_PACKET; + + /* + * Write the data to the FIFO and intiate the transfer of data present + * in the FIFO to the ICAP device. + */ + status = drvdata->config->set_configuration(drvdata, + &buffer[0], index); + if (status) + return status; + + /* + * Read the configuration register + */ + status = drvdata->config->get_configuration(drvdata, RegData, 1); + if (status) + return status; + + return 0; +} + +int hwicap_initialize_hwicap(struct hwicap_drvdata *drvdata) +{ + int status; + u32 idcode; + + dev_dbg(drvdata->dev, "initializing\n"); + + /* Abort any current transaction, to make sure we have the + * ICAP in a good state. */ + dev_dbg(drvdata->dev, "Reset...\n"); + drvdata->config->reset(drvdata); + + dev_dbg(drvdata->dev, "Desync...\n"); + status = hwicap_command_desync(drvdata); + if (status) + return status; + + /* Attempt to read the IDCODE from ICAP. This + * may not be returned correctly, due to the design of the + * hardware. + */ + dev_dbg(drvdata->dev, "Reading IDCODE...\n"); + status = hwicap_get_configuration_register( + drvdata, drvdata->config_regs->IDCODE, &idcode); + dev_dbg(drvdata->dev, "IDCODE = %x\n", idcode); + if (status) + return status; + + dev_dbg(drvdata->dev, "Desync...\n"); + status = hwicap_command_desync(drvdata); + if (status) + return status; + + return 0; +} + +static ssize_t +hwicap_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + struct hwicap_drvdata *drvdata = file->private_data; + ssize_t bytes_to_read = 0; + u32 *kbuf; + u32 words; + u32 bytes_remaining; + int status; + + if (down_interruptible(&drvdata->sem)) + return -ERESTARTSYS; + + if (drvdata->read_buffer_in_use) { + /* If there are leftover bytes in the buffer, just */ + /* return them and don't try to read more from the */ + /* ICAP device. */ + bytes_to_read = + (count < drvdata->read_buffer_in_use) ? count : + drvdata->read_buffer_in_use; + + /* Return the data currently in the read buffer. */ + if (copy_to_user(buf, drvdata->read_buffer, bytes_to_read)) { + status = -EFAULT; + goto error; + } + drvdata->read_buffer_in_use -= bytes_to_read; + memcpy(drvdata->read_buffer + bytes_to_read, + drvdata->read_buffer, 4 - bytes_to_read); + } else { + /* Get new data from the ICAP, and return was was requested. */ + kbuf = (u32 *) get_zeroed_page(GFP_KERNEL); + if (!kbuf) { + status = -ENOMEM; + goto error; + } + + /* The ICAP device is only able to read complete */ + /* words. If a number of bytes that do not correspond */ + /* to complete words is requested, then we read enough */ + /* words to get the required number of bytes, and then */ + /* save the remaining bytes for the next read. */ + + /* Determine the number of words to read, rounding up */ + /* if necessary. */ + words = ((count + 3) >> 2); + bytes_to_read = words << 2; + + if (bytes_to_read > PAGE_SIZE) + bytes_to_read = PAGE_SIZE; + + /* Ensure we only read a complete number of words. */ + bytes_remaining = bytes_to_read & 3; + bytes_to_read &= ~3; + words = bytes_to_read >> 2; + + status = drvdata->config->get_configuration(drvdata, + kbuf, words); + + /* If we didn't read correctly, then bail out. */ + if (status) { + free_page((unsigned long)kbuf); + goto error; + } + + /* If we fail to return the data to the user, then bail out. */ + if (copy_to_user(buf, kbuf, bytes_to_read)) { + free_page((unsigned long)kbuf); + status = -EFAULT; + goto error; + } + memcpy(kbuf, drvdata->read_buffer, bytes_remaining); + drvdata->read_buffer_in_use = bytes_remaining; + free_page((unsigned long)kbuf); + } + status = bytes_to_read; + error: + up(&drvdata->sem); + return status; +} + +static ssize_t +hwicap_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + struct hwicap_drvdata *drvdata = file->private_data; + ssize_t written = 0; + ssize_t left = count; + u32 *kbuf; + ssize_t len; + ssize_t status; + + if (down_interruptible(&drvdata->sem)) + return -ERESTARTSYS; + + left += drvdata->write_buffer_in_use; + + /* Only write multiples of 4 bytes. */ + if (left < 4) { + status = 0; + goto error; + } + + kbuf = (u32 *) __get_free_page(GFP_KERNEL); + if (!kbuf) { + status = -ENOMEM; + goto error; + } + + while (left > 3) { + /* only write multiples of 4 bytes, so there might */ + /* be as many as 3 bytes left (at the end). */ + len = left; + + if (len > PAGE_SIZE) + len = PAGE_SIZE; + len &= ~3; + + if (drvdata->write_buffer_in_use) { + memcpy(kbuf, drvdata->write_buffer, + drvdata->write_buffer_in_use); + if (copy_from_user( + (((char *)kbuf) + (drvdata->write_buffer_in_use)), + buf + written, + len - (drvdata->write_buffer_in_use))) { + free_page((unsigned long)kbuf); + status = -EFAULT; + goto error; + } + } else { + if (copy_from_user(kbuf, buf + written, len)) { + free_page((unsigned long)kbuf); + status = -EFAULT; + goto error; + } + } + + status = drvdata->config->set_configuration(drvdata, + kbuf, len >> 2); + + if (status) { + free_page((unsigned long)kbuf); + status = -EFAULT; + goto error; + } + if (drvdata->write_buffer_in_use) { + len -= drvdata->write_buffer_in_use; + left -= drvdata->write_buffer_in_use; + drvdata->write_buffer_in_use = 0; + } + written += len; + left -= len; + } + if ((left > 0) && (left < 4)) { + if (!copy_from_user(drvdata->write_buffer, + buf + written, left)) { + drvdata->write_buffer_in_use = left; + written += left; + left = 0; + } + } + + free_page((unsigned long)kbuf); + status = written; + error: + up(&drvdata->sem); + return status; +} + +static int hwicap_open(struct inode *inode, struct file *file) +{ + struct hwicap_drvdata *drvdata; + int status; + + drvdata = container_of(inode->i_cdev, struct hwicap_drvdata, cdev); + + if (down_interruptible(&drvdata->sem)) + return -ERESTARTSYS; + + if (drvdata->is_open) { + status = -EBUSY; + goto error; + } + + status = hwicap_initialize_hwicap(drvdata); + if (status) { + dev_err(drvdata->dev, "Failed to open file"); + goto error; + } + + file->private_data = drvdata; + drvdata->write_buffer_in_use = 0; + drvdata->read_buffer_in_use = 0; + drvdata->is_open = 1; + + error: + up(&drvdata->sem); + return status; +} + +static int hwicap_release(struct inode *inode, struct file *file) +{ + struct hwicap_drvdata *drvdata = file->private_data; + int i; + int status = 0; + + if (down_interruptible(&drvdata->sem)) + return -ERESTARTSYS; + + if (drvdata->write_buffer_in_use) { + /* Flush write buffer. */ + for (i = drvdata->write_buffer_in_use; i < 4; i++) + drvdata->write_buffer[i] = 0; + + status = drvdata->config->set_configuration(drvdata, + (u32 *) drvdata->write_buffer, 1); + if (status) + goto error; + } + + status = hwicap_command_desync(drvdata); + if (status) + goto error; + + error: + drvdata->is_open = 0; + up(&drvdata->sem); + return status; +} + +static struct file_operations hwicap_fops = { + .owner = THIS_MODULE, + .write = hwicap_write, + .read = hwicap_read, + .open = hwicap_open, + .release = hwicap_release, +}; + +static int __devinit hwicap_setup(struct device *dev, int id, + const struct resource *regs_res, + const struct hwicap_driver_config *config, + const struct config_registers *config_regs) +{ + dev_t devt; + struct hwicap_drvdata *drvdata = NULL; + int retval = 0; + + dev_info(dev, "Xilinx icap port driver\n"); + + if (id < 0) { + for (id = 0; id < HWICAP_DEVICES; id++) + if (!probed_devices[id]) + break; + } + if (id < 0 || id >= HWICAP_DEVICES) { + dev_err(dev, "%s%i too large\n", DRIVER_NAME, id); + return -EINVAL; + } + if (probed_devices[id]) { + dev_err(dev, "cannot assign to %s%i; it is already in use\n", + DRIVER_NAME, id); + return -EBUSY; + } + + probed_devices[id] = 1; + + devt = MKDEV(xhwicap_major, xhwicap_minor + id); + + drvdata = kmalloc(sizeof(struct hwicap_drvdata), GFP_KERNEL); + if (!drvdata) { + dev_err(dev, "Couldn't allocate device private record\n"); + return -ENOMEM; + } + memset((void *)drvdata, 0, sizeof(struct hwicap_drvdata)); + dev_set_drvdata(dev, (void *)drvdata); + + if (!regs_res) { + dev_err(dev, "Couldn't get registers resource\n"); + retval = -EFAULT; + goto failed1; + } + + drvdata->mem_start = regs_res->start; + drvdata->mem_end = regs_res->end; + drvdata->mem_size = regs_res->end - regs_res->start + 1; + + if (!request_mem_region(drvdata->mem_start, + drvdata->mem_size, DRIVER_NAME)) { + dev_err(dev, "Couldn't lock memory region at %p\n", + (void *)regs_res->start); + retval = -EBUSY; + goto failed1; + } + + drvdata->devt = devt; + drvdata->dev = dev; + drvdata->base_address = ioremap(drvdata->mem_start, drvdata->mem_size); + if (!drvdata->base_address) { + dev_err(dev, "ioremap() failed\n"); + goto failed2; + } + + drvdata->config = config; + drvdata->config_regs = config_regs; + + init_MUTEX(&drvdata->sem); + drvdata->is_open = 0; + + dev_info(dev, "ioremap %lx to %p with size %x\n", + (unsigned long int)drvdata->mem_start, + drvdata->base_address, drvdata->mem_size); + + cdev_init(&drvdata->cdev, &hwicap_fops); + drvdata->cdev.owner = THIS_MODULE; + retval = cdev_add(&drvdata->cdev, devt, 1); + if (retval) { + dev_err(dev, "cdev_add() failed\n"); + goto failed3; + } + /* devfs_mk_cdev(devt, S_IFCHR|S_IRUGO|S_IWUGO, DRIVER_NAME); */ + class_device_create(icap_class, NULL, devt, NULL, DRIVER_NAME); + return 0; /* success */ + + failed3: + iounmap(drvdata->base_address); + + failed2: + release_mem_region(regs_res->start, drvdata->mem_size); + + failed1: + kfree(drvdata); + + return retval; +} + +static struct hwicap_driver_config buffer_icap_config = { + .get_configuration = buffer_icap_get_configuration, + .set_configuration = buffer_icap_set_configuration, + .reset = buffer_icap_reset, +}; + +static struct hwicap_driver_config fifo_icap_config = { + .get_configuration = fifo_icap_get_configuration, + .set_configuration = fifo_icap_set_configuration, + .reset = fifo_icap_reset, +}; + +static int __devexit hwicap_remove(struct device *dev) +{ + struct hwicap_drvdata *drvdata; + + drvdata = (struct hwicap_drvdata *)dev_get_drvdata(dev); + + if (!drvdata) + return 0; + + class_device_destroy(icap_class, drvdata->devt); + cdev_del(&drvdata->cdev); + iounmap(drvdata->base_address); + release_mem_region(drvdata->mem_start, drvdata->mem_size); + kfree(drvdata); + dev_set_drvdata(dev, NULL); + probed_devices[MINOR(dev->devt)-xhwicap_minor] = 0; + + return 0; /* success */ +} + +static int __devinit hwicap_drv_probe(struct platform_device *pdev) +{ + struct resource *res; + const struct config_registers *regs; + const char *family; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + /* It's most likely that we're using V4, if the family is not + specified */ + regs = &v4_config_registers; + family = pdev->dev.platform_data; + + if (family) { + if (!strcmp(family, "virtex2p")) { + regs = &v2_config_registers; + } else if (!strcmp(family, "virtex4")) { + regs = &v4_config_registers; + } else if (!strcmp(family, "virtex5")) { + regs = &v5_config_registers; + } + } + + return hwicap_setup(&pdev->dev, pdev->id, res, + &buffer_icap_config, regs); +} + +static int __devexit hwicap_drv_remove(struct platform_device *pdev) +{ + return hwicap_remove(&pdev->dev); +} + +static struct platform_driver hwicap_platform_driver = { + .probe = hwicap_drv_probe, + .remove = hwicap_drv_remove, + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + }, +}; + +/* --------------------------------------------------------------------- + * OF bus binding + */ + +#if defined(CONFIG_OF) +static int __devinit +hwicap_of_probe(struct of_device *op, const struct of_device_id *match) +{ + struct resource res; + const unsigned int *id; + const char *family; + int rc; + const struct hwicap_driver_config *config = match->data; + const struct config_registers *regs; + + dev_dbg(&op->dev, "hwicap_of_probe(%p, %p)\n", op, match); + + rc = of_address_to_resource(op->node, 0, &res); + if (rc) { + dev_err(&op->dev, "invalid address\n"); + return rc; + } + + id = of_get_property(op->node, "port-number", NULL); + + /* It's most likely that we're using V4, if the family is not + specified */ + regs = &v4_config_registers; + family = of_get_property(op->node, "xlnx,family", NULL); + + if (family) { + if (!strcmp(family, "virtex2p")) { + regs = &v2_config_registers; + } else if (!strcmp(family, "virtex4")) { + regs = &v4_config_registers; + } else if (!strcmp(family, "virtex5")) { + regs = &v5_config_registers; + } + } + return hwicap_setup(&op->dev, id ? *id : -1, &res, config, + regs); +} + +static int __devexit hwicap_of_remove(struct of_device *op) +{ + return hwicap_remove(&op->dev); +} + +/* Match table for of_platform binding */ +static const struct of_device_id __devinit hwicap_of_match[] = { + { .compatible = "xlnx,opb-hwicap-1.00.b", .data = &buffer_icap_config}, + { .compatible = "xlnx,xps-hwicap-1.00.a", .data = &fifo_icap_config}, + {}, +}; +MODULE_DEVICE_TABLE(of, hwicap_of_match); + +static struct of_platform_driver hwicap_of_driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .match_table = hwicap_of_match, + .probe = hwicap_of_probe, + .remove = __devexit_p(hwicap_of_remove), + .driver = { + .name = DRIVER_NAME, + }, +}; + +/* Registration helpers to keep the number of #ifdefs to a minimum */ +static inline int __devinit hwicap_of_register(void) +{ + pr_debug("hwicap: calling of_register_platform_driver()\n"); + return of_register_platform_driver(&hwicap_of_driver); +} + +static inline void __devexit hwicap_of_unregister(void) +{ + of_unregister_platform_driver(&hwicap_of_driver); +} +#else /* CONFIG_OF */ +/* CONFIG_OF not enabled; do nothing helpers */ +static inline int __devinit hwicap_of_register(void) { return 0; } +static inline void __devexit hwicap_of_unregister(void) { } +#endif /* CONFIG_OF */ + +static int __devinit hwicap_module_init(void) +{ + dev_t devt; + int retval; + + icap_class = class_create(THIS_MODULE, "xilinx_config"); + + if (xhwicap_major) { + devt = MKDEV(xhwicap_major, xhwicap_minor); + retval = register_chrdev_region( + devt, + HWICAP_DEVICES, + DRIVER_NAME); + if (retval < 0) + return retval; + } else { + retval = alloc_chrdev_region(&devt, + xhwicap_minor, + HWICAP_DEVICES, + DRIVER_NAME); + if (retval < 0) + return retval; + xhwicap_major = MAJOR(devt); + } + + retval = platform_driver_register(&hwicap_platform_driver); + + if (retval) + goto failed1; + + retval = hwicap_of_register(); + + if (retval) + goto failed2; + + return retval; + + failed2: + platform_driver_unregister(&hwicap_platform_driver); + + failed1: + unregister_chrdev_region(devt, HWICAP_DEVICES); + + return retval; +} + +static void __devexit hwicap_module_cleanup(void) +{ + dev_t devt = MKDEV(xhwicap_major, xhwicap_minor); + + class_destroy(icap_class); + + platform_driver_unregister(&hwicap_platform_driver); + + hwicap_of_unregister(); + + unregister_chrdev_region(devt, HWICAP_DEVICES); +} + +module_init(hwicap_module_init); +module_exit(hwicap_module_cleanup); + +MODULE_AUTHOR("Xilinx, Inc; Xilinx Research Labs Group"); +MODULE_DESCRIPTION("Xilinx ICAP Port Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.h b/drivers/char/xilinx_hwicap/xilinx_hwicap.h new file mode 100644 index 000000000000..ae771cac1629 --- /dev/null +++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.h @@ -0,0 +1,193 @@ +/***************************************************************************** + * + * Author: Xilinx, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" + * AS A COURTESY TO YOU, SOLELY FOR USE IN DEVELOPING PROGRAMS AND + * SOLUTIONS FOR XILINX DEVICES. BY PROVIDING THIS DESIGN, CODE, + * OR INFORMATION AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, + * APPLICATION OR STANDARD, XILINX IS MAKING NO REPRESENTATION + * THAT THIS IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT, + * AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE + * FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY DISCLAIMS ANY + * WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE + * IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR + * REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF + * INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE. + * + * Xilinx products are not intended for use in life support appliances, + * devices, or systems. Use in such applications is expressly prohibited. + * + * (c) Copyright 2003-2007 Xilinx Inc. + * All rights reserved. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + *****************************************************************************/ + +#ifndef XILINX_HWICAP_H_ /* prevent circular inclusions */ +#define XILINX_HWICAP_H_ /* by using protection macros */ + +#include <linux/types.h> +#include <linux/cdev.h> +#include <linux/version.h> +#include <linux/platform_device.h> + +#include <asm/io.h> + +struct hwicap_drvdata { + u32 write_buffer_in_use; /* Always in [0,3] */ + u8 write_buffer[4]; + u32 read_buffer_in_use; /* Always in [0,3] */ + u8 read_buffer[4]; + u32 mem_start; /* phys. address of the control registers */ + u32 mem_end; /* phys. address of the control registers */ + u32 mem_size; + void __iomem *base_address;/* virt. address of the control registers */ + + struct device *dev; + struct cdev cdev; /* Char device structure */ + dev_t devt; + + const struct hwicap_driver_config *config; + const struct config_registers *config_regs; + void *private_data; + bool is_open; + struct semaphore sem; +}; + +struct hwicap_driver_config { + int (*get_configuration)(struct hwicap_drvdata *drvdata, u32 *data, + u32 size); + int (*set_configuration)(struct hwicap_drvdata *drvdata, u32 *data, + u32 size); + void (*reset)(struct hwicap_drvdata *drvdata); +}; + +/* Number of times to poll the done regsiter */ +#define XHI_MAX_RETRIES 10 + +/************ Constant Definitions *************/ + +#define XHI_PAD_FRAMES 0x1 + +/* Mask for calculating configuration packet headers */ +#define XHI_WORD_COUNT_MASK_TYPE_1 0x7FFUL +#define XHI_WORD_COUNT_MASK_TYPE_2 0x1FFFFFUL +#define XHI_TYPE_MASK 0x7 +#define XHI_REGISTER_MASK 0xF +#define XHI_OP_MASK 0x3 + +#define XHI_TYPE_SHIFT 29 +#define XHI_REGISTER_SHIFT 13 +#define XHI_OP_SHIFT 27 + +#define XHI_TYPE_1 1 +#define XHI_TYPE_2 2 +#define XHI_OP_WRITE 2 +#define XHI_OP_READ 1 + +/* Address Block Types */ +#define XHI_FAR_CLB_BLOCK 0 +#define XHI_FAR_BRAM_BLOCK 1 +#define XHI_FAR_BRAM_INT_BLOCK 2 + +struct config_registers { + u32 CRC; + u32 FAR; + u32 FDRI; + u32 FDRO; + u32 CMD; + u32 CTL; + u32 MASK; + u32 STAT; + u32 LOUT; + u32 COR; + u32 MFWR; + u32 FLR; + u32 KEY; + u32 CBC; + u32 IDCODE; + u32 AXSS; + u32 C0R_1; + u32 CSOB; + u32 WBSTAR; + u32 TIMER; + u32 BOOTSTS; + u32 CTL_1; +}; + +/* Configuration Commands */ +#define XHI_CMD_NULL 0 +#define XHI_CMD_WCFG 1 +#define XHI_CMD_MFW 2 +#define XHI_CMD_DGHIGH 3 +#define XHI_CMD_RCFG 4 +#define XHI_CMD_START 5 +#define XHI_CMD_RCAP 6 +#define XHI_CMD_RCRC 7 +#define XHI_CMD_AGHIGH 8 +#define XHI_CMD_SWITCH 9 +#define XHI_CMD_GRESTORE 10 +#define XHI_CMD_SHUTDOWN 11 +#define XHI_CMD_GCAPTURE 12 +#define XHI_CMD_DESYNCH 13 +#define XHI_CMD_IPROG 15 /* Only in Virtex5 */ +#define XHI_CMD_CRCC 16 /* Only in Virtex5 */ +#define XHI_CMD_LTIMER 17 /* Only in Virtex5 */ + +/* Packet constants */ +#define XHI_SYNC_PACKET 0xAA995566UL +#define XHI_DUMMY_PACKET 0xFFFFFFFFUL +#define XHI_NOOP_PACKET (XHI_TYPE_1 << XHI_TYPE_SHIFT) +#define XHI_TYPE_2_READ ((XHI_TYPE_2 << XHI_TYPE_SHIFT) | \ + (XHI_OP_READ << XHI_OP_SHIFT)) + +#define XHI_TYPE_2_WRITE ((XHI_TYPE_2 << XHI_TYPE_SHIFT) | \ + (XHI_OP_WRITE << XHI_OP_SHIFT)) + +#define XHI_TYPE2_CNT_MASK 0x07FFFFFF + +#define XHI_TYPE_1_PACKET_MAX_WORDS 2047UL +#define XHI_TYPE_1_HEADER_BYTES 4 +#define XHI_TYPE_2_HEADER_BYTES 8 + +/* Constant to use for CRC check when CRC has been disabled */ +#define XHI_DISABLED_AUTO_CRC 0x0000DEFCUL + +/** + * hwicap_type_1_read: Generates a Type 1 read packet header. + * @parameter: Register is the address of the register to be read back. + * + * Generates a Type 1 read packet header, which is used to indirectly + * read registers in the configuration logic. This packet must then + * be sent through the icap device, and a return packet received with + * the information. + **/ +static inline u32 hwicap_type_1_read(u32 Register) +{ + return (XHI_TYPE_1 << XHI_TYPE_SHIFT) | + (Register << XHI_REGISTER_SHIFT) | + (XHI_OP_READ << XHI_OP_SHIFT); +} + +/** + * hwicap_type_1_write: Generates a Type 1 write packet header + * @parameter: Register is the address of the register to be read back. + **/ +static inline u32 hwicap_type_1_write(u32 Register) +{ + return (XHI_TYPE_1 << XHI_TYPE_SHIFT) | + (Register << XHI_REGISTER_SHIFT) | + (XHI_OP_WRITE << XHI_OP_SHIFT); +} + +#endif diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 98b6b4fb4257..2b382990fe58 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -97,7 +97,7 @@ config EDAC_I82975X config EDAC_I3000 tristate "Intel 3000/3010" - depends on EDAC_MM_EDAC && PCI && X86_32 + depends on EDAC_MM_EDAC && PCI && X86 help Support for error detection and correction on the Intel 3000 and 3010 server chipsets. @@ -123,6 +123,20 @@ config EDAC_I5000 Support for error detection and correction the Intel Greekcreek/Blackford chipsets. +config EDAC_MPC85XX + tristate "Freescale MPC85xx" + depends on EDAC_MM_EDAC && FSL_SOC && MPC85xx + help + Support for error detection and correction on the Freescale + MPC8560, MPC8540, MPC8548 + +config EDAC_MV64X60 + tristate "Marvell MV64x60" + depends on EDAC_MM_EDAC && MV64X60 + help + Support for error detection and correction on the Marvell + MV64360 and MV64460 chipsets. + config EDAC_PASEMI tristate "PA Semi PWRficient" depends on EDAC_MM_EDAC && PCI @@ -131,5 +145,12 @@ config EDAC_PASEMI Support for error detection and correction on PA Semi PWRficient. +config EDAC_CELL + tristate "Cell Broadband Engine memory controller" + depends on EDAC_MM_EDAC && PPC_CELL_NATIVE + help + Support for error detection and correction on the + Cell Broadband Engine internal memory controller + on platform without a hypervisor endif # EDAC diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index 02c09f0ff157..83807731d4a9 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -28,4 +28,7 @@ obj-$(CONFIG_EDAC_I3000) += i3000_edac.o obj-$(CONFIG_EDAC_I82860) += i82860_edac.o obj-$(CONFIG_EDAC_R82600) += r82600_edac.o obj-$(CONFIG_EDAC_PASEMI) += pasemi_edac.o +obj-$(CONFIG_EDAC_MPC85XX) += mpc85xx_edac.o +obj-$(CONFIG_EDAC_MV64X60) += mv64x60_edac.o +obj-$(CONFIG_EDAC_CELL) += cell_edac.o diff --git a/drivers/edac/cell_edac.c b/drivers/edac/cell_edac.c new file mode 100644 index 000000000000..b54112ffd282 --- /dev/null +++ b/drivers/edac/cell_edac.c @@ -0,0 +1,258 @@ +/* + * Cell MIC driver for ECC counting + * + * Copyright 2007 Benjamin Herrenschmidt, IBM Corp. + * <benh@kernel.crashing.org> + * + * This file may be distributed under the terms of the + * GNU General Public License. + */ +#undef DEBUG + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/stop_machine.h> +#include <linux/io.h> +#include <asm/machdep.h> +#include <asm/cell-regs.h> + +#include "edac_core.h" + +struct cell_edac_priv +{ + struct cbe_mic_tm_regs __iomem *regs; + int node; + int chanmask; +#ifdef DEBUG + u64 prev_fir; +#endif +}; + +static void cell_edac_count_ce(struct mem_ctl_info *mci, int chan, u64 ar) +{ + struct cell_edac_priv *priv = mci->pvt_info; + struct csrow_info *csrow = &mci->csrows[0]; + unsigned long address, pfn, offset; + + dev_dbg(mci->dev, "ECC CE err on node %d, channel %d, ar = 0x%016lx\n", + priv->node, chan, ar); + + /* Address decoding is likely a bit bogus, to dbl check */ + address = (ar & 0xffffffffe0000000ul) >> 29; + if (priv->chanmask == 0x3) + address = (address << 1) | chan; + pfn = address >> PAGE_SHIFT; + offset = address & ~PAGE_MASK; + + /* TODO: Decoding of the error addresss */ + edac_mc_handle_ce(mci, csrow->first_page + pfn, offset, + 0, 0, chan, ""); +} + +static void cell_edac_count_ue(struct mem_ctl_info *mci, int chan, u64 ar) +{ + struct cell_edac_priv *priv = mci->pvt_info; + struct csrow_info *csrow = &mci->csrows[0]; + unsigned long address, pfn, offset; + + dev_dbg(mci->dev, "ECC UE err on node %d, channel %d, ar = 0x%016lx\n", + priv->node, chan, ar); + + /* Address decoding is likely a bit bogus, to dbl check */ + address = (ar & 0xffffffffe0000000ul) >> 29; + if (priv->chanmask == 0x3) + address = (address << 1) | chan; + pfn = address >> PAGE_SHIFT; + offset = address & ~PAGE_MASK; + + /* TODO: Decoding of the error addresss */ + edac_mc_handle_ue(mci, csrow->first_page + pfn, offset, 0, ""); +} + +static void cell_edac_check(struct mem_ctl_info *mci) +{ + struct cell_edac_priv *priv = mci->pvt_info; + u64 fir, addreg, clear = 0; + + fir = in_be64(&priv->regs->mic_fir); +#ifdef DEBUG + if (fir != priv->prev_fir) { + dev_dbg(mci->dev, "fir change : 0x%016lx\n", fir); + priv->prev_fir = fir; + } +#endif + if ((priv->chanmask & 0x1) && (fir & CBE_MIC_FIR_ECC_SINGLE_0_ERR)) { + addreg = in_be64(&priv->regs->mic_df_ecc_address_0); + clear |= CBE_MIC_FIR_ECC_SINGLE_0_RESET; + cell_edac_count_ce(mci, 0, addreg); + } + if ((priv->chanmask & 0x2) && (fir & CBE_MIC_FIR_ECC_SINGLE_1_ERR)) { + addreg = in_be64(&priv->regs->mic_df_ecc_address_1); + clear |= CBE_MIC_FIR_ECC_SINGLE_1_RESET; + cell_edac_count_ce(mci, 1, addreg); + } + if ((priv->chanmask & 0x1) && (fir & CBE_MIC_FIR_ECC_MULTI_0_ERR)) { + addreg = in_be64(&priv->regs->mic_df_ecc_address_0); + clear |= CBE_MIC_FIR_ECC_MULTI_0_RESET; + cell_edac_count_ue(mci, 0, addreg); + } + if ((priv->chanmask & 0x2) && (fir & CBE_MIC_FIR_ECC_MULTI_1_ERR)) { + addreg = in_be64(&priv->regs->mic_df_ecc_address_1); + clear |= CBE_MIC_FIR_ECC_MULTI_1_RESET; + cell_edac_count_ue(mci, 1, addreg); + } + + /* The procedure for clearing FIR bits is a bit ... weird */ + if (clear) { + fir &= ~(CBE_MIC_FIR_ECC_ERR_MASK | CBE_MIC_FIR_ECC_SET_MASK); + fir |= CBE_MIC_FIR_ECC_RESET_MASK; + fir &= ~clear; + out_be64(&priv->regs->mic_fir, fir); + (void)in_be64(&priv->regs->mic_fir); + + mb(); /* sync up */ +#ifdef DEBUG + fir = in_be64(&priv->regs->mic_fir); + dev_dbg(mci->dev, "fir clear : 0x%016lx\n", fir); +#endif + } +} + +static void __devinit cell_edac_init_csrows(struct mem_ctl_info *mci) +{ + struct csrow_info *csrow = &mci->csrows[0]; + struct cell_edac_priv *priv = mci->pvt_info; + struct device_node *np; + + for (np = NULL; + (np = of_find_node_by_name(np, "memory")) != NULL;) { + struct resource r; + + /* We "know" that the Cell firmware only creates one entry + * in the "memory" nodes. If that changes, this code will + * need to be adapted. + */ + if (of_address_to_resource(np, 0, &r)) + continue; + if (of_node_to_nid(np) != priv->node) + continue; + csrow->first_page = r.start >> PAGE_SHIFT; + csrow->nr_pages = (r.end - r.start + 1) >> PAGE_SHIFT; + csrow->last_page = csrow->first_page + csrow->nr_pages - 1; + csrow->mtype = MEM_XDR; + csrow->edac_mode = EDAC_FLAG_EC | EDAC_FLAG_SECDED; + dev_dbg(mci->dev, + "Initialized on node %d, chanmask=0x%x," + " first_page=0x%lx, nr_pages=0x%x\n", + priv->node, priv->chanmask, + csrow->first_page, csrow->nr_pages); + break; + } +} + +static int __devinit cell_edac_probe(struct platform_device *pdev) +{ + struct cbe_mic_tm_regs __iomem *regs; + struct mem_ctl_info *mci; + struct cell_edac_priv *priv; + u64 reg; + int rc, chanmask; + + regs = cbe_get_cpu_mic_tm_regs(cbe_node_to_cpu(pdev->id)); + if (regs == NULL) + return -ENODEV; + + /* Get channel population */ + reg = in_be64(®s->mic_mnt_cfg); + dev_dbg(&pdev->dev, "MIC_MNT_CFG = 0x%016lx\n", reg); + chanmask = 0; + if (reg & CBE_MIC_MNT_CFG_CHAN_0_POP) + chanmask |= 0x1; + if (reg & CBE_MIC_MNT_CFG_CHAN_1_POP) + chanmask |= 0x2; + if (chanmask == 0) { + dev_warn(&pdev->dev, + "Yuck ! No channel populated ? Aborting !\n"); + return -ENODEV; + } + dev_dbg(&pdev->dev, "Initial FIR = 0x%016lx\n", + in_be64(®s->mic_fir)); + + /* Allocate & init EDAC MC data structure */ + mci = edac_mc_alloc(sizeof(struct cell_edac_priv), 1, + chanmask == 3 ? 2 : 1, pdev->id); + if (mci == NULL) + return -ENOMEM; + priv = mci->pvt_info; + priv->regs = regs; + priv->node = pdev->id; + priv->chanmask = chanmask; + mci->dev = &pdev->dev; + mci->mtype_cap = MEM_FLAG_XDR; + mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_EC | EDAC_FLAG_SECDED; + mci->edac_cap = EDAC_FLAG_EC | EDAC_FLAG_SECDED; + mci->mod_name = "cell_edac"; + mci->ctl_name = "MIC"; + mci->dev_name = pdev->dev.bus_id; + mci->edac_check = cell_edac_check; + cell_edac_init_csrows(mci); + + /* Register with EDAC core */ + rc = edac_mc_add_mc(mci); + if (rc) { + dev_err(&pdev->dev, "failed to register with EDAC core\n"); + edac_mc_free(mci); + return rc; + } + + return 0; +} + +static int __devexit cell_edac_remove(struct platform_device *pdev) +{ + struct mem_ctl_info *mci = edac_mc_del_mc(&pdev->dev); + if (mci) + edac_mc_free(mci); + return 0; +} + +static struct platform_driver cell_edac_driver = { + .driver = { + .name = "cbe-mic", + .owner = THIS_MODULE, + }, + .probe = cell_edac_probe, + .remove = cell_edac_remove, +}; + +static int __init cell_edac_init(void) +{ + /* Sanity check registers data structure */ + BUILD_BUG_ON(offsetof(struct cbe_mic_tm_regs, + mic_df_ecc_address_0) != 0xf8); + BUILD_BUG_ON(offsetof(struct cbe_mic_tm_regs, + mic_df_ecc_address_1) != 0x1b8); + BUILD_BUG_ON(offsetof(struct cbe_mic_tm_regs, + mic_df_config) != 0x218); + BUILD_BUG_ON(offsetof(struct cbe_mic_tm_regs, + mic_fir) != 0x230); + BUILD_BUG_ON(offsetof(struct cbe_mic_tm_regs, + mic_mnt_cfg) != 0x210); + BUILD_BUG_ON(offsetof(struct cbe_mic_tm_regs, + mic_exc) != 0x208); + + return platform_driver_register(&cell_edac_driver); +} + +static void __exit cell_edac_exit(void) +{ + platform_driver_unregister(&cell_edac_driver); +} + +module_init(cell_edac_init); +module_exit(cell_edac_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); +MODULE_DESCRIPTION("ECC counting for Cell MIC"); diff --git a/drivers/edac/edac_core.h b/drivers/edac/edac_core.h index 2d23e304f5ec..a9aa845dbe74 100644 --- a/drivers/edac/edac_core.h +++ b/drivers/edac/edac_core.h @@ -136,6 +136,7 @@ enum mem_type { MEM_DDR2, /* DDR2 RAM */ MEM_FB_DDR2, /* fully buffered DDR2 */ MEM_RDDR2, /* Registered DDR2 RAM */ + MEM_XDR, /* Rambus XDR */ }; #define MEM_FLAG_EMPTY BIT(MEM_EMPTY) @@ -152,6 +153,7 @@ enum mem_type { #define MEM_FLAG_DDR2 BIT(MEM_DDR2) #define MEM_FLAG_FB_DDR2 BIT(MEM_FB_DDR2) #define MEM_FLAG_RDDR2 BIT(MEM_RDDR2) +#define MEM_FLAG_XDR BIT(MEM_XDR) /* chipset Error Detection and Correction capabilities and mode */ enum edac_type { diff --git a/drivers/edac/edac_device.c b/drivers/edac/edac_device.c index f3690a697cf9..b9552bc03dea 100644 --- a/drivers/edac/edac_device.c +++ b/drivers/edac/edac_device.c @@ -155,6 +155,10 @@ struct edac_device_ctl_info *edac_device_alloc_ctl_info( dev_ctl->instances = dev_inst; dev_ctl->pvt_info = pvt; + /* Default logging of CEs and UEs */ + dev_ctl->log_ce = 1; + dev_ctl->log_ue = 1; + /* Name of this edac device */ snprintf(dev_ctl->name,sizeof(dev_ctl->name),"%s",edac_device_name); @@ -436,7 +440,7 @@ static void edac_device_workq_function(struct work_struct *work_req) */ if (edac_dev->poll_msec == 1000) queue_delayed_work(edac_workqueue, &edac_dev->work, - round_jiffies(edac_dev->delay)); + round_jiffies_relative(edac_dev->delay)); else queue_delayed_work(edac_workqueue, &edac_dev->work, edac_dev->delay); @@ -468,7 +472,7 @@ void edac_device_workq_setup(struct edac_device_ctl_info *edac_dev, */ if (edac_dev->poll_msec == 1000) queue_delayed_work(edac_workqueue, &edac_dev->work, - round_jiffies(edac_dev->delay)); + round_jiffies_relative(edac_dev->delay)); else queue_delayed_work(edac_workqueue, &edac_dev->work, edac_dev->delay); diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index 9aac88027fb3..021d18795145 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -73,7 +73,8 @@ static const char *mem_types[] = { [MEM_RMBS] = "RMBS", [MEM_DDR2] = "Unbuffered-DDR2", [MEM_FB_DDR2] = "FullyBuffered-DDR2", - [MEM_RDDR2] = "Registered-DDR2" + [MEM_RDDR2] = "Registered-DDR2", + [MEM_XDR] = "XDR" }; static const char *dev_types[] = { diff --git a/drivers/edac/edac_pci.c b/drivers/edac/edac_pci.c index e0b47b74ec45..32be43576a8e 100644 --- a/drivers/edac/edac_pci.c +++ b/drivers/edac/edac_pci.c @@ -246,7 +246,7 @@ static void edac_pci_workq_function(struct work_struct *work_req) /* if we are on a one second period, then use round */ msec = edac_pci_get_poll_msec(); if (msec == 1000) - delay = round_jiffies(msecs_to_jiffies(msec)); + delay = round_jiffies_relative(msecs_to_jiffies(msec)); else delay = msecs_to_jiffies(msec); diff --git a/drivers/edac/edac_pci_sysfs.c b/drivers/edac/edac_pci_sysfs.c index 5b075da99145..71c3195d3704 100644 --- a/drivers/edac/edac_pci_sysfs.c +++ b/drivers/edac/edac_pci_sysfs.c @@ -558,8 +558,10 @@ static void edac_pci_dev_parity_test(struct pci_dev *dev) debugf4("PCI STATUS= 0x%04x %s\n", status, dev->dev.bus_id); - /* check the status reg for errors */ - if (status) { + /* check the status reg for errors on boards NOT marked as broken + * if broken, we cannot trust any of the status bits + */ + if (status && !dev->broken_parity_status) { if (status & (PCI_STATUS_SIG_SYSTEM_ERROR)) { edac_printk(KERN_CRIT, EDAC_PCI, "Signaled System Error on %s\n", @@ -593,8 +595,10 @@ static void edac_pci_dev_parity_test(struct pci_dev *dev) debugf4("PCI SEC_STATUS= 0x%04x %s\n", status, dev->dev.bus_id); - /* check the secondary status reg for errors */ - if (status) { + /* check the secondary status reg for errors, + * on NOT broken boards + */ + if (status && !dev->broken_parity_status) { if (status & (PCI_STATUS_SIG_SYSTEM_ERROR)) { edac_printk(KERN_CRIT, EDAC_PCI, "Bridge " "Signaled System Error on %s\n", diff --git a/drivers/edac/i3000_edac.c b/drivers/edac/i3000_edac.c index e895f9f887ab..5d4292811c14 100644 --- a/drivers/edac/i3000_edac.c +++ b/drivers/edac/i3000_edac.c @@ -14,6 +14,7 @@ #include <linux/pci.h> #include <linux/pci_ids.h> #include <linux/slab.h> +#include <linux/edac.h> #include "edac_core.h" #define I3000_REVISION "1.1" @@ -30,105 +31,139 @@ #define I3000_MCHBAR_MASK 0xffffc000 #define I3000_MMR_WINDOW_SIZE 16384 -#define I3000_EDEAP 0x70 /* Extended DRAM Error Address Pointer (8b) - * - * 7:1 reserved - * 0 bit 32 of address - */ -#define I3000_DEAP 0x58 /* DRAM Error Address Pointer (32b) - * - * 31:7 address - * 6:1 reserved - * 0 Error channel 0/1 - */ -#define I3000_DEAP_GRAIN (1 << 7) -#define I3000_DEAP_PFN(edeap, deap) ((((edeap) & 1) << (32 - PAGE_SHIFT)) | \ - ((deap) >> PAGE_SHIFT)) -#define I3000_DEAP_OFFSET(deap) ((deap) & ~(I3000_DEAP_GRAIN-1) & ~PAGE_MASK) -#define I3000_DEAP_CHANNEL(deap) ((deap) & 1) - -#define I3000_DERRSYN 0x5c /* DRAM Error Syndrome (8b) - * - * 7:0 DRAM ECC Syndrome - */ - -#define I3000_ERRSTS 0xc8 /* Error Status Register (16b) - * - * 15:12 reserved - * 11 MCH Thermal Sensor Event for SMI/SCI/SERR - * 10 reserved - * 9 LOCK to non-DRAM Memory Flag (LCKF) - * 8 Received Refresh Timeout Flag (RRTOF) - * 7:2 reserved - * 1 Multiple-bit DRAM ECC Error Flag (DMERR) - * 0 Single-bit DRAM ECC Error Flag (DSERR) - */ +#define I3000_EDEAP 0x70 /* Extended DRAM Error Address Pointer (8b) + * + * 7:1 reserved + * 0 bit 32 of address + */ +#define I3000_DEAP 0x58 /* DRAM Error Address Pointer (32b) + * + * 31:7 address + * 6:1 reserved + * 0 Error channel 0/1 + */ +#define I3000_DEAP_GRAIN (1 << 7) + +/* + * Helper functions to decode the DEAP/EDEAP hardware registers. + * + * The type promotion here is deliberate; we're deriving an + * unsigned long pfn and offset from hardware regs which are u8/u32. + */ + +static inline unsigned long deap_pfn(u8 edeap, u32 deap) +{ + deap >>= PAGE_SHIFT; + deap |= (edeap & 1) << (32 - PAGE_SHIFT); + return deap; +} + +static inline unsigned long deap_offset(u32 deap) +{ + return deap & ~(I3000_DEAP_GRAIN - 1) & ~PAGE_MASK; +} + +static inline int deap_channel(u32 deap) +{ + return deap & 1; +} + +#define I3000_DERRSYN 0x5c /* DRAM Error Syndrome (8b) + * + * 7:0 DRAM ECC Syndrome + */ + +#define I3000_ERRSTS 0xc8 /* Error Status Register (16b) + * + * 15:12 reserved + * 11 MCH Thermal Sensor Event + * for SMI/SCI/SERR + * 10 reserved + * 9 LOCK to non-DRAM Memory Flag (LCKF) + * 8 Received Refresh Timeout Flag (RRTOF) + * 7:2 reserved + * 1 Multi-bit DRAM ECC Error Flag (DMERR) + * 0 Single-bit DRAM ECC Error Flag (DSERR) + */ #define I3000_ERRSTS_BITS 0x0b03 /* bits which indicate errors */ #define I3000_ERRSTS_UE 0x0002 #define I3000_ERRSTS_CE 0x0001 -#define I3000_ERRCMD 0xca /* Error Command (16b) - * - * 15:12 reserved - * 11 SERR on MCH Thermal Sensor Event (TSESERR) - * 10 reserved - * 9 SERR on LOCK to non-DRAM Memory (LCKERR) - * 8 SERR on DRAM Refresh Timeout (DRTOERR) - * 7:2 reserved - * 1 SERR Multiple-Bit DRAM ECC Error (DMERR) - * 0 SERR on Single-Bit ECC Error (DSERR) - */ +#define I3000_ERRCMD 0xca /* Error Command (16b) + * + * 15:12 reserved + * 11 SERR on MCH Thermal Sensor Event + * (TSESERR) + * 10 reserved + * 9 SERR on LOCK to non-DRAM Memory + * (LCKERR) + * 8 SERR on DRAM Refresh Timeout + * (DRTOERR) + * 7:2 reserved + * 1 SERR Multi-Bit DRAM ECC Error + * (DMERR) + * 0 SERR on Single-Bit ECC Error + * (DSERR) + */ /* Intel MMIO register space - device 0 function 0 - MMR space */ #define I3000_DRB_SHIFT 25 /* 32MiB grain */ -#define I3000_C0DRB 0x100 /* Channel 0 DRAM Rank Boundary (8b x 4) - * - * 7:0 Channel 0 DRAM Rank Boundary Address - */ -#define I3000_C1DRB 0x180 /* Channel 1 DRAM Rank Boundary (8b x 4) - * - * 7:0 Channel 1 DRAM Rank Boundary Address - */ - -#define I3000_C0DRA 0x108 /* Channel 0 DRAM Rank Attribute (8b x 2) - * - * 7 reserved - * 6:4 DRAM odd Rank Attribute - * 3 reserved - * 2:0 DRAM even Rank Attribute - * - * Each attribute defines the page - * size of the corresponding rank: - * 000: unpopulated - * 001: reserved - * 010: 4 KB - * 011: 8 KB - * 100: 16 KB - * Others: reserved - */ -#define I3000_C1DRA 0x188 /* Channel 1 DRAM Rank Attribute (8b x 2) */ -#define ODD_RANK_ATTRIB(dra) (((dra) & 0x70) >> 4) -#define EVEN_RANK_ATTRIB(dra) ((dra) & 0x07) - -#define I3000_C0DRC0 0x120 /* DRAM Controller Mode 0 (32b) - * - * 31:30 reserved - * 29 Initialization Complete (IC) - * 28:11 reserved - * 10:8 Refresh Mode Select (RMS) - * 7 reserved - * 6:4 Mode Select (SMS) - * 3:2 reserved - * 1:0 DRAM Type (DT) - */ - -#define I3000_C0DRC1 0x124 /* DRAM Controller Mode 1 (32b) - * - * 31 Enhanced Addressing Enable (ENHADE) - * 30:0 reserved - */ +#define I3000_C0DRB 0x100 /* Channel 0 DRAM Rank Boundary (8b x 4) + * + * 7:0 Channel 0 DRAM Rank Boundary Address + */ +#define I3000_C1DRB 0x180 /* Channel 1 DRAM Rank Boundary (8b x 4) + * + * 7:0 Channel 1 DRAM Rank Boundary Address + */ + +#define I3000_C0DRA 0x108 /* Channel 0 DRAM Rank Attribute (8b x 2) + * + * 7 reserved + * 6:4 DRAM odd Rank Attribute + * 3 reserved + * 2:0 DRAM even Rank Attribute + * + * Each attribute defines the page + * size of the corresponding rank: + * 000: unpopulated + * 001: reserved + * 010: 4 KB + * 011: 8 KB + * 100: 16 KB + * Others: reserved + */ +#define I3000_C1DRA 0x188 /* Channel 1 DRAM Rank Attribute (8b x 2) */ + +static inline unsigned char odd_rank_attrib(unsigned char dra) +{ + return (dra & 0x70) >> 4; +} + +static inline unsigned char even_rank_attrib(unsigned char dra) +{ + return dra & 0x07; +} + +#define I3000_C0DRC0 0x120 /* DRAM Controller Mode 0 (32b) + * + * 31:30 reserved + * 29 Initialization Complete (IC) + * 28:11 reserved + * 10:8 Refresh Mode Select (RMS) + * 7 reserved + * 6:4 Mode Select (SMS) + * 3:2 reserved + * 1:0 DRAM Type (DT) + */ + +#define I3000_C0DRC1 0x124 /* DRAM Controller Mode 1 (32b) + * + * 31 Enhanced Addressing Enable (ENHADE) + * 30:0 reserved + */ enum i3000p_chips { I3000 = 0, @@ -187,7 +222,8 @@ static void i3000_get_error_info(struct mem_ctl_info *mci, pci_read_config_byte(pdev, I3000_DERRSYN, &info->derrsyn); } - /* Clear any error bits. + /* + * Clear any error bits. * (Yes, we really clear bits by writing 1 to them.) */ pci_write_bits16(pdev, I3000_ERRSTS, I3000_ERRSTS_BITS, @@ -198,8 +234,8 @@ static int i3000_process_error_info(struct mem_ctl_info *mci, struct i3000_error_info *info, int handle_errors) { - int row, multi_chan; - int pfn, offset, channel; + int row, multi_chan, channel; + unsigned long pfn, offset; multi_chan = mci->csrows[0].nr_channels - 1; @@ -214,9 +250,9 @@ static int i3000_process_error_info(struct mem_ctl_info *mci, info->errsts = info->errsts2; } - pfn = I3000_DEAP_PFN(info->edeap, info->deap); - offset = I3000_DEAP_OFFSET(info->deap); - channel = I3000_DEAP_CHANNEL(info->deap); + pfn = deap_pfn(info->edeap, info->deap); + offset = deap_offset(info->deap); + channel = deap_channel(info->deap); row = edac_mc_find_csrow_by_page(mci, pfn); @@ -245,16 +281,18 @@ static int i3000_is_interleaved(const unsigned char *c0dra, { int i; - /* If the channels aren't populated identically then + /* + * If the channels aren't populated identically then * we're not interleaved. */ for (i = 0; i < I3000_RANKS_PER_CHANNEL / 2; i++) - if (ODD_RANK_ATTRIB(c0dra[i]) != ODD_RANK_ATTRIB(c1dra[i]) || - EVEN_RANK_ATTRIB(c0dra[i]) != - EVEN_RANK_ATTRIB(c1dra[i])) + if (odd_rank_attrib(c0dra[i]) != odd_rank_attrib(c1dra[i]) || + even_rank_attrib(c0dra[i]) != + even_rank_attrib(c1dra[i])) return 0; - /* If the rank boundaries for the two channels are different + /* + * If the rank boundaries for the two channels are different * then we're not interleaved. */ for (i = 0; i < I3000_RANKS_PER_CHANNEL; i++) @@ -288,6 +326,15 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx) return -ENODEV; } + switch (edac_op_state) { + case EDAC_OPSTATE_POLL: + case EDAC_OPSTATE_NMI: + break; + default: + edac_op_state = EDAC_OPSTATE_POLL; + break; + } + c0dra[0] = readb(window + I3000_C0DRA + 0); /* ranks 0,1 */ c0dra[1] = readb(window + I3000_C0DRA + 1); /* ranks 2,3 */ c1dra[0] = readb(window + I3000_C1DRA + 0); /* ranks 0,1 */ @@ -300,7 +347,8 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx) iounmap(window); - /* Figure out how many channels we have. + /* + * Figure out how many channels we have. * * If we have what the datasheet calls "asymmetric channels" * (essentially the same as what was called "virtual single @@ -363,7 +411,8 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx) csrow->edac_mode = EDAC_UNKNOWN; } - /* Clear any error bits. + /* + * Clear any error bits. * (Yes, we really clear bits by writing 1 to them.) */ pci_write_bits16(pdev, I3000_ERRSTS, I3000_ERRSTS_BITS, @@ -390,7 +439,7 @@ static int i3000_probe1(struct pci_dev *pdev, int dev_idx) debugf3("MC: %s(): success\n", __func__); return 0; - fail: +fail: if (mci) edac_mc_free(mci); @@ -409,7 +458,7 @@ static int __devinit i3000_init_one(struct pci_dev *pdev, return -EIO; rc = i3000_probe1(pdev, ent->driver_data); - if (mci_pdev == NULL) + if (!mci_pdev) mci_pdev = pci_dev_get(pdev); return rc; @@ -424,7 +473,8 @@ static void __devexit i3000_remove_one(struct pci_dev *pdev) if (i3000_pci) edac_pci_release_generic_ctl(i3000_pci); - if ((mci = edac_mc_del_mc(&pdev->dev)) == NULL) + mci = edac_mc_del_mc(&pdev->dev); + if (!mci) return; edac_mc_free(mci); @@ -457,7 +507,7 @@ static int __init i3000_init(void) if (pci_rc < 0) goto fail0; - if (mci_pdev == NULL) { + if (!mci_pdev) { i3000_registered = 0; mci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_3000_HB, NULL); @@ -504,3 +554,6 @@ module_exit(i3000_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Akamai Technologies Arthur Ulfeldt/Jason Uhlenkott"); MODULE_DESCRIPTION("MC support for Intel 3000 memory hub controllers"); + +module_param(edac_op_state, int, 0444); +MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll,1=NMI"); diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c new file mode 100644 index 000000000000..065732ddf40c --- /dev/null +++ b/drivers/edac/mpc85xx_edac.c @@ -0,0 +1,1043 @@ +/* + * Freescale MPC85xx Memory Controller kenel module + * + * Author: Dave Jiang <djiang@mvista.com> + * + * 2006-2007 (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 <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/ctype.h> +#include <linux/io.h> +#include <linux/mod_devicetable.h> +#include <linux/edac.h> + +#include <linux/of_platform.h> +#include <linux/of_device.h> +#include <asm/mpc85xx.h> +#include "edac_module.h" +#include "edac_core.h" +#include "mpc85xx_edac.h" + +static int edac_dev_idx; +static int edac_pci_idx; +static int edac_mc_idx; + +static u32 orig_ddr_err_disable; +static u32 orig_ddr_err_sbe; + +/* + * PCI Err defines + */ +#ifdef CONFIG_PCI +static u32 orig_pci_err_cap_dr; +static u32 orig_pci_err_en; +#endif + +static u32 orig_l2_err_disable; +static u32 orig_hid1; + +static const char *mpc85xx_ctl_name = "MPC85xx"; + +/************************ MC SYSFS parts ***********************************/ + +static ssize_t mpc85xx_mc_inject_data_hi_show(struct mem_ctl_info *mci, + char *data) +{ + struct mpc85xx_mc_pdata *pdata = mci->pvt_info; + return sprintf(data, "0x%08x", + in_be32(pdata->mc_vbase + + MPC85XX_MC_DATA_ERR_INJECT_HI)); +} + +static ssize_t mpc85xx_mc_inject_data_lo_show(struct mem_ctl_info *mci, + char *data) +{ + struct mpc85xx_mc_pdata *pdata = mci->pvt_info; + return sprintf(data, "0x%08x", + in_be32(pdata->mc_vbase + + MPC85XX_MC_DATA_ERR_INJECT_LO)); +} + +static ssize_t mpc85xx_mc_inject_ctrl_show(struct mem_ctl_info *mci, char *data) +{ + struct mpc85xx_mc_pdata *pdata = mci->pvt_info; + return sprintf(data, "0x%08x", + in_be32(pdata->mc_vbase + MPC85XX_MC_ECC_ERR_INJECT)); +} + +static ssize_t mpc85xx_mc_inject_data_hi_store(struct mem_ctl_info *mci, + const char *data, size_t count) +{ + struct mpc85xx_mc_pdata *pdata = mci->pvt_info; + if (isdigit(*data)) { + out_be32(pdata->mc_vbase + MPC85XX_MC_DATA_ERR_INJECT_HI, + simple_strtoul(data, NULL, 0)); + return count; + } + return 0; +} + +static ssize_t mpc85xx_mc_inject_data_lo_store(struct mem_ctl_info *mci, + const char *data, size_t count) +{ + struct mpc85xx_mc_pdata *pdata = mci->pvt_info; + if (isdigit(*data)) { + out_be32(pdata->mc_vbase + MPC85XX_MC_DATA_ERR_INJECT_LO, + simple_strtoul(data, NULL, 0)); + return count; + } + return 0; +} + +static ssize_t mpc85xx_mc_inject_ctrl_store(struct mem_ctl_info *mci, + const char *data, size_t count) +{ + struct mpc85xx_mc_pdata *pdata = mci->pvt_info; + if (isdigit(*data)) { + out_be32(pdata->mc_vbase + MPC85XX_MC_ECC_ERR_INJECT, + simple_strtoul(data, NULL, 0)); + return count; + } + return 0; +} + +static struct mcidev_sysfs_attribute mpc85xx_mc_sysfs_attributes[] = { + { + .attr = { + .name = "inject_data_hi", + .mode = (S_IRUGO | S_IWUSR) + }, + .show = mpc85xx_mc_inject_data_hi_show, + .store = mpc85xx_mc_inject_data_hi_store}, + { + .attr = { + .name = "inject_data_lo", + .mode = (S_IRUGO | S_IWUSR) + }, + .show = mpc85xx_mc_inject_data_lo_show, + .store = mpc85xx_mc_inject_data_lo_store}, + { + .attr = { + .name = "inject_ctrl", + .mode = (S_IRUGO | S_IWUSR) + }, + .show = mpc85xx_mc_inject_ctrl_show, + .store = mpc85xx_mc_inject_ctrl_store}, + + /* End of list */ + { + .attr = {.name = NULL} + } +}; + +static void mpc85xx_set_mc_sysfs_attributes(struct mem_ctl_info *mci) +{ + mci->mc_driver_sysfs_attributes = mpc85xx_mc_sysfs_attributes; +} + +/**************************** PCI Err device ***************************/ +#ifdef CONFIG_PCI + +static void mpc85xx_pci_check(struct edac_pci_ctl_info *pci) +{ + struct mpc85xx_pci_pdata *pdata = pci->pvt_info; + u32 err_detect; + + err_detect = in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR); + + /* master aborts can happen during PCI config cycles */ + if (!(err_detect & ~(PCI_EDE_MULTI_ERR | PCI_EDE_MST_ABRT))) { + out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR, err_detect); + return; + } + + printk(KERN_ERR "PCI error(s) detected\n"); + printk(KERN_ERR "PCI/X ERR_DR register: %#08x\n", err_detect); + + printk(KERN_ERR "PCI/X ERR_ATTRIB register: %#08x\n", + in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_ATTRIB)); + printk(KERN_ERR "PCI/X ERR_ADDR register: %#08x\n", + in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_ADDR)); + printk(KERN_ERR "PCI/X ERR_EXT_ADDR register: %#08x\n", + in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EXT_ADDR)); + printk(KERN_ERR "PCI/X ERR_DL register: %#08x\n", + in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DL)); + printk(KERN_ERR "PCI/X ERR_DH register: %#08x\n", + in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DH)); + + /* clear error bits */ + out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR, err_detect); + + if (err_detect & PCI_EDE_PERR_MASK) + edac_pci_handle_pe(pci, pci->ctl_name); + + if ((err_detect & ~PCI_EDE_MULTI_ERR) & ~PCI_EDE_PERR_MASK) + edac_pci_handle_npe(pci, pci->ctl_name); +} + +static irqreturn_t mpc85xx_pci_isr(int irq, void *dev_id) +{ + struct edac_pci_ctl_info *pci = dev_id; + struct mpc85xx_pci_pdata *pdata = pci->pvt_info; + u32 err_detect; + + err_detect = in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR); + + if (!err_detect) + return IRQ_NONE; + + mpc85xx_pci_check(pci); + + return IRQ_HANDLED; +} + +static int __devinit mpc85xx_pci_err_probe(struct platform_device *pdev) +{ + struct edac_pci_ctl_info *pci; + struct mpc85xx_pci_pdata *pdata; + struct resource *r; + int res = 0; + + if (!devres_open_group(&pdev->dev, mpc85xx_pci_err_probe, GFP_KERNEL)) + return -ENOMEM; + + pci = edac_pci_alloc_ctl_info(sizeof(*pdata), "mpc85xx_pci_err"); + if (!pci) + return -ENOMEM; + + pdata = pci->pvt_info; + pdata->name = "mpc85xx_pci_err"; + pdata->irq = NO_IRQ; + platform_set_drvdata(pdev, pci); + pci->dev = &pdev->dev; + pci->mod_name = EDAC_MOD_STR; + pci->ctl_name = pdata->name; + pci->dev_name = pdev->dev.bus_id; + + if (edac_op_state == EDAC_OPSTATE_POLL) + pci->edac_check = mpc85xx_pci_check; + + pdata->edac_idx = edac_pci_idx++; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + printk(KERN_ERR "%s: Unable to get resource for " + "PCI err regs\n", __func__); + goto err; + } + + if (!devm_request_mem_region(&pdev->dev, r->start, + r->end - r->start + 1, pdata->name)) { + printk(KERN_ERR "%s: Error while requesting mem region\n", + __func__); + res = -EBUSY; + goto err; + } + + pdata->pci_vbase = devm_ioremap(&pdev->dev, r->start, + r->end - r->start + 1); + if (!pdata->pci_vbase) { + printk(KERN_ERR "%s: Unable to setup PCI err regs\n", __func__); + res = -ENOMEM; + goto err; + } + + orig_pci_err_cap_dr = + in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_CAP_DR); + + /* PCI master abort is expected during config cycles */ + out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_CAP_DR, 0x40); + + orig_pci_err_en = in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN); + + /* disable master abort reporting */ + out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN, ~0x40); + + /* clear error bits */ + out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR, ~0); + + if (edac_pci_add_device(pci, pdata->edac_idx) > 0) { + debugf3("%s(): failed edac_pci_add_device()\n", __func__); + goto err; + } + + if (edac_op_state == EDAC_OPSTATE_INT) { + pdata->irq = platform_get_irq(pdev, 0); + res = devm_request_irq(&pdev->dev, pdata->irq, + mpc85xx_pci_isr, IRQF_DISABLED, + "[EDAC] PCI err", pci); + if (res < 0) { + printk(KERN_ERR + "%s: Unable to requiest irq %d for " + "MPC85xx PCI err\n", __func__, pdata->irq); + res = -ENODEV; + goto err2; + } + + printk(KERN_INFO EDAC_MOD_STR " acquired irq %d for PCI Err\n", + pdata->irq); + } + + devres_remove_group(&pdev->dev, mpc85xx_pci_err_probe); + debugf3("%s(): success\n", __func__); + printk(KERN_INFO EDAC_MOD_STR " PCI err registered\n"); + + return 0; + +err2: + edac_pci_del_device(&pdev->dev); +err: + edac_pci_free_ctl_info(pci); + devres_release_group(&pdev->dev, mpc85xx_pci_err_probe); + return res; +} + +static int mpc85xx_pci_err_remove(struct platform_device *pdev) +{ + struct edac_pci_ctl_info *pci = platform_get_drvdata(pdev); + struct mpc85xx_pci_pdata *pdata = pci->pvt_info; + + debugf0("%s()\n", __func__); + + out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_CAP_DR, + orig_pci_err_cap_dr); + + out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN, orig_pci_err_en); + + edac_pci_del_device(pci->dev); + + if (edac_op_state == EDAC_OPSTATE_INT) + irq_dispose_mapping(pdata->irq); + + edac_pci_free_ctl_info(pci); + + return 0; +} + +static struct platform_driver mpc85xx_pci_err_driver = { + .probe = mpc85xx_pci_err_probe, + .remove = __devexit_p(mpc85xx_pci_err_remove), + .driver = { + .name = "mpc85xx_pci_err", + } +}; + +#endif /* CONFIG_PCI */ + +/**************************** L2 Err device ***************************/ + +/************************ L2 SYSFS parts ***********************************/ + +static ssize_t mpc85xx_l2_inject_data_hi_show(struct edac_device_ctl_info + *edac_dev, char *data) +{ + struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; + return sprintf(data, "0x%08x", + in_be32(pdata->l2_vbase + MPC85XX_L2_ERRINJHI)); +} + +static ssize_t mpc85xx_l2_inject_data_lo_show(struct edac_device_ctl_info + *edac_dev, char *data) +{ + struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; + return sprintf(data, "0x%08x", + in_be32(pdata->l2_vbase + MPC85XX_L2_ERRINJLO)); +} + +static ssize_t mpc85xx_l2_inject_ctrl_show(struct edac_device_ctl_info + *edac_dev, char *data) +{ + struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; + return sprintf(data, "0x%08x", + in_be32(pdata->l2_vbase + MPC85XX_L2_ERRINJCTL)); +} + +static ssize_t mpc85xx_l2_inject_data_hi_store(struct edac_device_ctl_info + *edac_dev, const char *data, + size_t count) +{ + struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; + if (isdigit(*data)) { + out_be32(pdata->l2_vbase + MPC85XX_L2_ERRINJHI, + simple_strtoul(data, NULL, 0)); + return count; + } + return 0; +} + +static ssize_t mpc85xx_l2_inject_data_lo_store(struct edac_device_ctl_info + *edac_dev, const char *data, + size_t count) +{ + struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; + if (isdigit(*data)) { + out_be32(pdata->l2_vbase + MPC85XX_L2_ERRINJLO, + simple_strtoul(data, NULL, 0)); + return count; + } + return 0; +} + +static ssize_t mpc85xx_l2_inject_ctrl_store(struct edac_device_ctl_info + *edac_dev, const char *data, + size_t count) +{ + struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; + if (isdigit(*data)) { + out_be32(pdata->l2_vbase + MPC85XX_L2_ERRINJCTL, + simple_strtoul(data, NULL, 0)); + return count; + } + return 0; +} + +static struct edac_dev_sysfs_attribute mpc85xx_l2_sysfs_attributes[] = { + { + .attr = { + .name = "inject_data_hi", + .mode = (S_IRUGO | S_IWUSR) + }, + .show = mpc85xx_l2_inject_data_hi_show, + .store = mpc85xx_l2_inject_data_hi_store}, + { + .attr = { + .name = "inject_data_lo", + .mode = (S_IRUGO | S_IWUSR) + }, + .show = mpc85xx_l2_inject_data_lo_show, + .store = mpc85xx_l2_inject_data_lo_store}, + { + .attr = { + .name = "inject_ctrl", + .mode = (S_IRUGO | S_IWUSR) + }, + .show = mpc85xx_l2_inject_ctrl_show, + .store = mpc85xx_l2_inject_ctrl_store}, + + /* End of list */ + { + .attr = {.name = NULL} + } +}; + +static void mpc85xx_set_l2_sysfs_attributes(struct edac_device_ctl_info + *edac_dev) +{ + edac_dev->sysfs_attributes = mpc85xx_l2_sysfs_attributes; +} + +/***************************** L2 ops ***********************************/ + +static void mpc85xx_l2_check(struct edac_device_ctl_info *edac_dev) +{ + struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; + u32 err_detect; + + err_detect = in_be32(pdata->l2_vbase + MPC85XX_L2_ERRDET); + + if (!(err_detect & L2_EDE_MASK)) + return; + + printk(KERN_ERR "ECC Error in CPU L2 cache\n"); + printk(KERN_ERR "L2 Error Detect Register: 0x%08x\n", err_detect); + printk(KERN_ERR "L2 Error Capture Data High Register: 0x%08x\n", + in_be32(pdata->l2_vbase + MPC85XX_L2_CAPTDATAHI)); + printk(KERN_ERR "L2 Error Capture Data Lo Register: 0x%08x\n", + in_be32(pdata->l2_vbase + MPC85XX_L2_CAPTDATALO)); + printk(KERN_ERR "L2 Error Syndrome Register: 0x%08x\n", + in_be32(pdata->l2_vbase + MPC85XX_L2_CAPTECC)); + printk(KERN_ERR "L2 Error Attributes Capture Register: 0x%08x\n", + in_be32(pdata->l2_vbase + MPC85XX_L2_ERRATTR)); + printk(KERN_ERR "L2 Error Address Capture Register: 0x%08x\n", + in_be32(pdata->l2_vbase + MPC85XX_L2_ERRADDR)); + + /* clear error detect register */ + out_be32(pdata->l2_vbase + MPC85XX_L2_ERRDET, err_detect); + + if (err_detect & L2_EDE_CE_MASK) + edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name); + + if (err_detect & L2_EDE_UE_MASK) + edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name); +} + +static irqreturn_t mpc85xx_l2_isr(int irq, void *dev_id) +{ + struct edac_device_ctl_info *edac_dev = dev_id; + struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; + u32 err_detect; + + err_detect = in_be32(pdata->l2_vbase + MPC85XX_L2_ERRDET); + + if (!(err_detect & L2_EDE_MASK)) + return IRQ_NONE; + + mpc85xx_l2_check(edac_dev); + + return IRQ_HANDLED; +} + +static int __devinit mpc85xx_l2_err_probe(struct of_device *op, + const struct of_device_id *match) +{ + struct edac_device_ctl_info *edac_dev; + struct mpc85xx_l2_pdata *pdata; + struct resource r; + int res; + + if (!devres_open_group(&op->dev, mpc85xx_l2_err_probe, GFP_KERNEL)) + return -ENOMEM; + + edac_dev = edac_device_alloc_ctl_info(sizeof(*pdata), + "cpu", 1, "L", 1, 2, NULL, 0, + edac_dev_idx); + if (!edac_dev) { + devres_release_group(&op->dev, mpc85xx_l2_err_probe); + return -ENOMEM; + } + + pdata = edac_dev->pvt_info; + pdata->name = "mpc85xx_l2_err"; + pdata->irq = NO_IRQ; + edac_dev->dev = &op->dev; + dev_set_drvdata(edac_dev->dev, edac_dev); + edac_dev->ctl_name = pdata->name; + edac_dev->dev_name = pdata->name; + + res = of_address_to_resource(op->node, 0, &r); + if (res) { + printk(KERN_ERR "%s: Unable to get resource for " + "L2 err regs\n", __func__); + goto err; + } + + /* we only need the error registers */ + r.start += 0xe00; + + if (!devm_request_mem_region(&op->dev, r.start, + r.end - r.start + 1, pdata->name)) { + printk(KERN_ERR "%s: Error while requesting mem region\n", + __func__); + res = -EBUSY; + goto err; + } + + pdata->l2_vbase = devm_ioremap(&op->dev, r.start, r.end - r.start + 1); + if (!pdata->l2_vbase) { + printk(KERN_ERR "%s: Unable to setup L2 err regs\n", __func__); + res = -ENOMEM; + goto err; + } + + out_be32(pdata->l2_vbase + MPC85XX_L2_ERRDET, ~0); + + orig_l2_err_disable = in_be32(pdata->l2_vbase + MPC85XX_L2_ERRDIS); + + /* clear the err_dis */ + out_be32(pdata->l2_vbase + MPC85XX_L2_ERRDIS, 0); + + edac_dev->mod_name = EDAC_MOD_STR; + + if (edac_op_state == EDAC_OPSTATE_POLL) + edac_dev->edac_check = mpc85xx_l2_check; + + mpc85xx_set_l2_sysfs_attributes(edac_dev); + + pdata->edac_idx = edac_dev_idx++; + + if (edac_device_add_device(edac_dev) > 0) { + debugf3("%s(): failed edac_device_add_device()\n", __func__); + goto err; + } + + if (edac_op_state == EDAC_OPSTATE_INT) { + pdata->irq = irq_of_parse_and_map(op->node, 0); + res = devm_request_irq(&op->dev, pdata->irq, + mpc85xx_l2_isr, IRQF_DISABLED, + "[EDAC] L2 err", edac_dev); + if (res < 0) { + printk(KERN_ERR + "%s: Unable to requiest irq %d for " + "MPC85xx L2 err\n", __func__, pdata->irq); + irq_dispose_mapping(pdata->irq); + res = -ENODEV; + goto err2; + } + + printk(KERN_INFO EDAC_MOD_STR " acquired irq %d for L2 Err\n", + pdata->irq); + + edac_dev->op_state = OP_RUNNING_INTERRUPT; + + out_be32(pdata->l2_vbase + MPC85XX_L2_ERRINTEN, L2_EIE_MASK); + } + + devres_remove_group(&op->dev, mpc85xx_l2_err_probe); + + debugf3("%s(): success\n", __func__); + printk(KERN_INFO EDAC_MOD_STR " L2 err registered\n"); + + return 0; + +err2: + edac_device_del_device(&op->dev); +err: + devres_release_group(&op->dev, mpc85xx_l2_err_probe); + edac_device_free_ctl_info(edac_dev); + return res; +} + +static int mpc85xx_l2_err_remove(struct of_device *op) +{ + struct edac_device_ctl_info *edac_dev = dev_get_drvdata(&op->dev); + struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; + + debugf0("%s()\n", __func__); + + if (edac_op_state == EDAC_OPSTATE_INT) { + out_be32(pdata->l2_vbase + MPC85XX_L2_ERRINTEN, 0); + irq_dispose_mapping(pdata->irq); + } + + out_be32(pdata->l2_vbase + MPC85XX_L2_ERRDIS, orig_l2_err_disable); + edac_device_del_device(&op->dev); + edac_device_free_ctl_info(edac_dev); + return 0; +} + +static struct of_device_id mpc85xx_l2_err_of_match[] = { + { + .compatible = "fsl,8540-l2-cache-controller", + }, + { + .compatible = "fsl,8541-l2-cache-controller", + }, + { + .compatible = "fsl,8544-l2-cache-controller", + }, + { + .compatible = "fsl,8548-l2-cache-controller", + }, + { + .compatible = "fsl,8555-l2-cache-controller", + }, + { + .compatible = "fsl,8568-l2-cache-controller", + }, + {}, +}; + +static struct of_platform_driver mpc85xx_l2_err_driver = { + .owner = THIS_MODULE, + .name = "mpc85xx_l2_err", + .match_table = mpc85xx_l2_err_of_match, + .probe = mpc85xx_l2_err_probe, + .remove = mpc85xx_l2_err_remove, + .driver = { + .name = "mpc85xx_l2_err", + .owner = THIS_MODULE, + }, +}; + +/**************************** MC Err device ***************************/ + +static void mpc85xx_mc_check(struct mem_ctl_info *mci) +{ + struct mpc85xx_mc_pdata *pdata = mci->pvt_info; + struct csrow_info *csrow; + u32 err_detect; + u32 syndrome; + u32 err_addr; + u32 pfn; + int row_index; + + err_detect = in_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DETECT); + if (err_detect) + return; + + mpc85xx_mc_printk(mci, KERN_ERR, "Err Detect Register: %#8.8x\n", + err_detect); + + /* no more processing if not ECC bit errors */ + if (!(err_detect & (DDR_EDE_SBE | DDR_EDE_MBE))) { + out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DETECT, err_detect); + return; + } + + syndrome = in_be32(pdata->mc_vbase + MPC85XX_MC_CAPTURE_ECC); + err_addr = in_be32(pdata->mc_vbase + MPC85XX_MC_CAPTURE_ADDRESS); + pfn = err_addr >> PAGE_SHIFT; + + for (row_index = 0; row_index < mci->nr_csrows; row_index++) { + csrow = &mci->csrows[row_index]; + if ((pfn >= csrow->first_page) && (pfn <= csrow->last_page)) + break; + } + + mpc85xx_mc_printk(mci, KERN_ERR, "Capture Data High: %#8.8x\n", + in_be32(pdata->mc_vbase + + MPC85XX_MC_CAPTURE_DATA_HI)); + mpc85xx_mc_printk(mci, KERN_ERR, "Capture Data Low: %#8.8x\n", + in_be32(pdata->mc_vbase + + MPC85XX_MC_CAPTURE_DATA_LO)); + mpc85xx_mc_printk(mci, KERN_ERR, "syndrome: %#8.8x\n", syndrome); + mpc85xx_mc_printk(mci, KERN_ERR, "err addr: %#8.8x\n", err_addr); + mpc85xx_mc_printk(mci, KERN_ERR, "PFN: %#8.8x\n", pfn); + + /* we are out of range */ + if (row_index == mci->nr_csrows) + mpc85xx_mc_printk(mci, KERN_ERR, "PFN out of range!\n"); + + if (err_detect & DDR_EDE_SBE) + edac_mc_handle_ce(mci, pfn, err_addr & PAGE_MASK, + syndrome, row_index, 0, mci->ctl_name); + + if (err_detect & DDR_EDE_MBE) + edac_mc_handle_ue(mci, pfn, err_addr & PAGE_MASK, + row_index, mci->ctl_name); + + out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DETECT, err_detect); +} + +static irqreturn_t mpc85xx_mc_isr(int irq, void *dev_id) +{ + struct mem_ctl_info *mci = dev_id; + struct mpc85xx_mc_pdata *pdata = mci->pvt_info; + u32 err_detect; + + err_detect = in_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DETECT); + if (!err_detect) + return IRQ_NONE; + + mpc85xx_mc_check(mci); + + return IRQ_HANDLED; +} + +static void __devinit mpc85xx_init_csrows(struct mem_ctl_info *mci) +{ + struct mpc85xx_mc_pdata *pdata = mci->pvt_info; + struct csrow_info *csrow; + u32 sdram_ctl; + u32 sdtype; + enum mem_type mtype; + u32 cs_bnds; + int index; + + sdram_ctl = in_be32(pdata->mc_vbase + MPC85XX_MC_DDR_SDRAM_CFG); + + sdtype = sdram_ctl & DSC_SDTYPE_MASK; + if (sdram_ctl & DSC_RD_EN) { + switch (sdtype) { + case DSC_SDTYPE_DDR: + mtype = MEM_RDDR; + break; + case DSC_SDTYPE_DDR2: + mtype = MEM_RDDR2; + break; + default: + mtype = MEM_UNKNOWN; + break; + } + } else { + switch (sdtype) { + case DSC_SDTYPE_DDR: + mtype = MEM_DDR; + break; + case DSC_SDTYPE_DDR2: + mtype = MEM_DDR2; + break; + default: + mtype = MEM_UNKNOWN; + break; + } + } + + for (index = 0; index < mci->nr_csrows; index++) { + u32 start; + u32 end; + + csrow = &mci->csrows[index]; + cs_bnds = in_be32(pdata->mc_vbase + MPC85XX_MC_CS_BNDS_0 + + (index * MPC85XX_MC_CS_BNDS_OFS)); + start = (cs_bnds & 0xfff0000) << 4; + end = ((cs_bnds & 0xfff) << 20); + if (start) + start |= 0xfffff; + if (end) + end |= 0xfffff; + + if (start == end) + continue; /* not populated */ + + csrow->first_page = start >> PAGE_SHIFT; + csrow->last_page = end >> PAGE_SHIFT; + csrow->nr_pages = csrow->last_page + 1 - csrow->first_page; + csrow->grain = 8; + csrow->mtype = mtype; + csrow->dtype = DEV_UNKNOWN; + if (sdram_ctl & DSC_X32_EN) + csrow->dtype = DEV_X32; + csrow->edac_mode = EDAC_SECDED; + } +} + +static int __devinit mpc85xx_mc_err_probe(struct of_device *op, + const struct of_device_id *match) +{ + struct mem_ctl_info *mci; + struct mpc85xx_mc_pdata *pdata; + struct resource r; + u32 sdram_ctl; + int res; + + if (!devres_open_group(&op->dev, mpc85xx_mc_err_probe, GFP_KERNEL)) + return -ENOMEM; + + mci = edac_mc_alloc(sizeof(*pdata), 4, 1, edac_mc_idx); + if (!mci) { + devres_release_group(&op->dev, mpc85xx_mc_err_probe); + return -ENOMEM; + } + + pdata = mci->pvt_info; + pdata->name = "mpc85xx_mc_err"; + pdata->irq = NO_IRQ; + mci->dev = &op->dev; + pdata->edac_idx = edac_mc_idx++; + dev_set_drvdata(mci->dev, mci); + mci->ctl_name = pdata->name; + mci->dev_name = pdata->name; + + res = of_address_to_resource(op->node, 0, &r); + if (res) { + printk(KERN_ERR "%s: Unable to get resource for MC err regs\n", + __func__); + goto err; + } + + if (!devm_request_mem_region(&op->dev, r.start, + r.end - r.start + 1, pdata->name)) { + printk(KERN_ERR "%s: Error while requesting mem region\n", + __func__); + res = -EBUSY; + goto err; + } + + pdata->mc_vbase = devm_ioremap(&op->dev, r.start, r.end - r.start + 1); + if (!pdata->mc_vbase) { + printk(KERN_ERR "%s: Unable to setup MC err regs\n", __func__); + res = -ENOMEM; + goto err; + } + + sdram_ctl = in_be32(pdata->mc_vbase + MPC85XX_MC_DDR_SDRAM_CFG); + if (!(sdram_ctl & DSC_ECC_EN)) { + /* no ECC */ + printk(KERN_WARNING "%s: No ECC DIMMs discovered\n", __func__); + res = -ENODEV; + goto err; + } + + debugf3("%s(): init mci\n", __func__); + mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_RDDR2 | + MEM_FLAG_DDR | MEM_FLAG_DDR2; + mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; + mci->edac_cap = EDAC_FLAG_SECDED; + mci->mod_name = EDAC_MOD_STR; + mci->mod_ver = MPC85XX_REVISION; + + if (edac_op_state == EDAC_OPSTATE_POLL) + mci->edac_check = mpc85xx_mc_check; + + mci->ctl_page_to_phys = NULL; + + mci->scrub_mode = SCRUB_SW_SRC; + + mpc85xx_set_mc_sysfs_attributes(mci); + + mpc85xx_init_csrows(mci); + +#ifdef CONFIG_EDAC_DEBUG + edac_mc_register_mcidev_debug((struct attribute **)debug_attr); +#endif + + /* store the original error disable bits */ + orig_ddr_err_disable = + in_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DISABLE); + out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DISABLE, 0); + + /* clear all error bits */ + out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DETECT, ~0); + + if (edac_mc_add_mc(mci)) { + debugf3("%s(): failed edac_mc_add_mc()\n", __func__); + goto err; + } + + if (edac_op_state == EDAC_OPSTATE_INT) { + out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_INT_EN, + DDR_EIE_MBEE | DDR_EIE_SBEE); + + /* store the original error management threshold */ + orig_ddr_err_sbe = in_be32(pdata->mc_vbase + + MPC85XX_MC_ERR_SBE) & 0xff0000; + + /* set threshold to 1 error per interrupt */ + out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_SBE, 0x10000); + + /* register interrupts */ + pdata->irq = irq_of_parse_and_map(op->node, 0); + res = devm_request_irq(&op->dev, pdata->irq, + mpc85xx_mc_isr, IRQF_DISABLED, + "[EDAC] MC err", mci); + if (res < 0) { + printk(KERN_ERR "%s: Unable to request irq %d for " + "MPC85xx DRAM ERR\n", __func__, pdata->irq); + irq_dispose_mapping(pdata->irq); + res = -ENODEV; + goto err2; + } + + printk(KERN_INFO EDAC_MOD_STR " acquired irq %d for MC\n", + pdata->irq); + } + + devres_remove_group(&op->dev, mpc85xx_mc_err_probe); + debugf3("%s(): success\n", __func__); + printk(KERN_INFO EDAC_MOD_STR " MC err registered\n"); + + return 0; + +err2: + edac_mc_del_mc(&op->dev); +err: + devres_release_group(&op->dev, mpc85xx_mc_err_probe); + edac_mc_free(mci); + return res; +} + +static int mpc85xx_mc_err_remove(struct of_device *op) +{ + struct mem_ctl_info *mci = dev_get_drvdata(&op->dev); + struct mpc85xx_mc_pdata *pdata = mci->pvt_info; + + debugf0("%s()\n", __func__); + + if (edac_op_state == EDAC_OPSTATE_INT) { + out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_INT_EN, 0); + irq_dispose_mapping(pdata->irq); + } + + out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DISABLE, + orig_ddr_err_disable); + out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_SBE, orig_ddr_err_sbe); + + edac_mc_del_mc(&op->dev); + edac_mc_free(mci); + return 0; +} + +static struct of_device_id mpc85xx_mc_err_of_match[] = { + { + .compatible = "fsl,8540-memory-controller", + }, + { + .compatible = "fsl,8541-memory-controller", + }, + { + .compatible = "fsl,8544-memory-controller", + }, + { + .compatible = "fsl,8548-memory-controller", + }, + { + .compatible = "fsl,8555-memory-controller", + }, + { + .compatible = "fsl,8568-memory-controller", + }, + {}, +}; + +static struct of_platform_driver mpc85xx_mc_err_driver = { + .owner = THIS_MODULE, + .name = "mpc85xx_mc_err", + .match_table = mpc85xx_mc_err_of_match, + .probe = mpc85xx_mc_err_probe, + .remove = mpc85xx_mc_err_remove, + .driver = { + .name = "mpc85xx_mc_err", + .owner = THIS_MODULE, + }, +}; + +static int __init mpc85xx_mc_init(void) +{ + int res = 0; + + printk(KERN_INFO "Freescale(R) MPC85xx EDAC driver, " + "(C) 2006 Montavista Software\n"); + + /* make sure error reporting method is sane */ + switch (edac_op_state) { + case EDAC_OPSTATE_POLL: + case EDAC_OPSTATE_INT: + break; + default: + edac_op_state = EDAC_OPSTATE_INT; + break; + } + + res = of_register_platform_driver(&mpc85xx_mc_err_driver); + if (res) + printk(KERN_WARNING EDAC_MOD_STR "MC fails to register\n"); + + res = of_register_platform_driver(&mpc85xx_l2_err_driver); + if (res) + printk(KERN_WARNING EDAC_MOD_STR "L2 fails to register\n"); + +#ifdef CONFIG_PCI + res = platform_driver_register(&mpc85xx_pci_err_driver); + if (res) + printk(KERN_WARNING EDAC_MOD_STR "PCI fails to register\n"); +#endif + + /* + * need to clear HID1[RFXE] to disable machine check int + * so we can catch it + */ + if (edac_op_state == EDAC_OPSTATE_INT) { + orig_hid1 = mfspr(SPRN_HID1); + mtspr(SPRN_HID1, (orig_hid1 & ~0x20000)); + } + + return 0; +} + +module_init(mpc85xx_mc_init); + +static void __exit mpc85xx_mc_exit(void) +{ + mtspr(SPRN_HID1, orig_hid1); +#ifdef CONFIG_PCI + platform_driver_unregister(&mpc85xx_pci_err_driver); +#endif + of_unregister_platform_driver(&mpc85xx_l2_err_driver); + of_unregister_platform_driver(&mpc85xx_mc_err_driver); +} + +module_exit(mpc85xx_mc_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Montavista Software, Inc."); +module_param(edac_op_state, int, 0444); +MODULE_PARM_DESC(edac_op_state, + "EDAC Error Reporting state: 0=Poll, 2=Interrupt"); diff --git a/drivers/edac/mpc85xx_edac.h b/drivers/edac/mpc85xx_edac.h new file mode 100644 index 000000000000..135b3539a030 --- /dev/null +++ b/drivers/edac/mpc85xx_edac.h @@ -0,0 +1,162 @@ +/* + * Freescale MPC85xx Memory Controller kenel module + * Author: Dave Jiang <djiang@mvista.com> + * + * 2006-2007 (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. + * + */ +#ifndef _MPC85XX_EDAC_H_ +#define _MPC85XX_EDAC_H_ + +#define MPC85XX_REVISION " Ver: 2.0.0 " __DATE__ +#define EDAC_MOD_STR "MPC85xx_edac" + +#define mpc85xx_printk(level, fmt, arg...) \ + edac_printk(level, "MPC85xx", fmt, ##arg) + +#define mpc85xx_mc_printk(mci, level, fmt, arg...) \ + edac_mc_chipset_printk(mci, level, "MPC85xx", fmt, ##arg) + +/* + * DRAM error defines + */ + +/* DDR_SDRAM_CFG */ +#define MPC85XX_MC_DDR_SDRAM_CFG 0x0110 +#define MPC85XX_MC_CS_BNDS_0 0x0000 +#define MPC85XX_MC_CS_BNDS_1 0x0008 +#define MPC85XX_MC_CS_BNDS_2 0x0010 +#define MPC85XX_MC_CS_BNDS_3 0x0018 +#define MPC85XX_MC_CS_BNDS_OFS 0x0008 + +#define MPC85XX_MC_DATA_ERR_INJECT_HI 0x0e00 +#define MPC85XX_MC_DATA_ERR_INJECT_LO 0x0e04 +#define MPC85XX_MC_ECC_ERR_INJECT 0x0e08 +#define MPC85XX_MC_CAPTURE_DATA_HI 0x0e20 +#define MPC85XX_MC_CAPTURE_DATA_LO 0x0e24 +#define MPC85XX_MC_CAPTURE_ECC 0x0e28 +#define MPC85XX_MC_ERR_DETECT 0x0e40 +#define MPC85XX_MC_ERR_DISABLE 0x0e44 +#define MPC85XX_MC_ERR_INT_EN 0x0e48 +#define MPC85XX_MC_CAPTURE_ATRIBUTES 0x0e4c +#define MPC85XX_MC_CAPTURE_ADDRESS 0x0e50 +#define MPC85XX_MC_ERR_SBE 0x0e58 + +#define DSC_MEM_EN 0x80000000 +#define DSC_ECC_EN 0x20000000 +#define DSC_RD_EN 0x10000000 + +#define DSC_SDTYPE_MASK 0x07000000 + +#define DSC_SDTYPE_DDR 0x02000000 +#define DSC_SDTYPE_DDR2 0x03000000 +#define DSC_X32_EN 0x00000020 + +/* Err_Int_En */ +#define DDR_EIE_MSEE 0x1 /* memory select */ +#define DDR_EIE_SBEE 0x4 /* single-bit ECC error */ +#define DDR_EIE_MBEE 0x8 /* multi-bit ECC error */ + +/* Err_Detect */ +#define DDR_EDE_MSE 0x1 /* memory select */ +#define DDR_EDE_SBE 0x4 /* single-bit ECC error */ +#define DDR_EDE_MBE 0x8 /* multi-bit ECC error */ +#define DDR_EDE_MME 0x80000000 /* multiple memory errors */ + +/* Err_Disable */ +#define DDR_EDI_MSED 0x1 /* memory select disable */ +#define DDR_EDI_SBED 0x4 /* single-bit ECC error disable */ +#define DDR_EDI_MBED 0x8 /* multi-bit ECC error disable */ + +/* + * L2 Err defines + */ +#define MPC85XX_L2_ERRINJHI 0x0000 +#define MPC85XX_L2_ERRINJLO 0x0004 +#define MPC85XX_L2_ERRINJCTL 0x0008 +#define MPC85XX_L2_CAPTDATAHI 0x0020 +#define MPC85XX_L2_CAPTDATALO 0x0024 +#define MPC85XX_L2_CAPTECC 0x0028 +#define MPC85XX_L2_ERRDET 0x0040 +#define MPC85XX_L2_ERRDIS 0x0044 +#define MPC85XX_L2_ERRINTEN 0x0048 +#define MPC85XX_L2_ERRATTR 0x004c +#define MPC85XX_L2_ERRADDR 0x0050 +#define MPC85XX_L2_ERRCTL 0x0058 + +/* Error Interrupt Enable */ +#define L2_EIE_L2CFGINTEN 0x1 +#define L2_EIE_SBECCINTEN 0x4 +#define L2_EIE_MBECCINTEN 0x8 +#define L2_EIE_TPARINTEN 0x10 +#define L2_EIE_MASK (L2_EIE_L2CFGINTEN | L2_EIE_SBECCINTEN | \ + L2_EIE_MBECCINTEN | L2_EIE_TPARINTEN) + +/* Error Detect */ +#define L2_EDE_L2CFGERR 0x1 +#define L2_EDE_SBECCERR 0x4 +#define L2_EDE_MBECCERR 0x8 +#define L2_EDE_TPARERR 0x10 +#define L2_EDE_MULL2ERR 0x80000000 + +#define L2_EDE_CE_MASK L2_EDE_SBECCERR +#define L2_EDE_UE_MASK (L2_EDE_L2CFGERR | L2_EDE_MBECCERR | \ + L2_EDE_TPARERR) +#define L2_EDE_MASK (L2_EDE_L2CFGERR | L2_EDE_SBECCERR | \ + L2_EDE_MBECCERR | L2_EDE_TPARERR | L2_EDE_MULL2ERR) + +/* + * PCI Err defines + */ +#define PCI_EDE_TOE 0x00000001 +#define PCI_EDE_SCM 0x00000002 +#define PCI_EDE_IRMSV 0x00000004 +#define PCI_EDE_ORMSV 0x00000008 +#define PCI_EDE_OWMSV 0x00000010 +#define PCI_EDE_TGT_ABRT 0x00000020 +#define PCI_EDE_MST_ABRT 0x00000040 +#define PCI_EDE_TGT_PERR 0x00000080 +#define PCI_EDE_MST_PERR 0x00000100 +#define PCI_EDE_RCVD_SERR 0x00000200 +#define PCI_EDE_ADDR_PERR 0x00000400 +#define PCI_EDE_MULTI_ERR 0x80000000 + +#define PCI_EDE_PERR_MASK (PCI_EDE_TGT_PERR | PCI_EDE_MST_PERR | \ + PCI_EDE_ADDR_PERR) + +#define MPC85XX_PCI_ERR_DR 0x0000 +#define MPC85XX_PCI_ERR_CAP_DR 0x0004 +#define MPC85XX_PCI_ERR_EN 0x0008 +#define MPC85XX_PCI_ERR_ATTRIB 0x000c +#define MPC85XX_PCI_ERR_ADDR 0x0010 +#define MPC85XX_PCI_ERR_EXT_ADDR 0x0014 +#define MPC85XX_PCI_ERR_DL 0x0018 +#define MPC85XX_PCI_ERR_DH 0x001c +#define MPC85XX_PCI_GAS_TIMR 0x0020 +#define MPC85XX_PCI_PCIX_TIMR 0x0024 + +struct mpc85xx_mc_pdata { + char *name; + int edac_idx; + void __iomem *mc_vbase; + int irq; +}; + +struct mpc85xx_l2_pdata { + char *name; + int edac_idx; + void __iomem *l2_vbase; + int irq; +}; + +struct mpc85xx_pci_pdata { + char *name; + int edac_idx; + void __iomem *pci_vbase; + int irq; +}; + +#endif diff --git a/drivers/edac/mv64x60_edac.c b/drivers/edac/mv64x60_edac.c new file mode 100644 index 000000000000..bf071f140a05 --- /dev/null +++ b/drivers/edac/mv64x60_edac.c @@ -0,0 +1,855 @@ +/* + * Marvell MV64x60 Memory Controller kernel module for PPC platforms + * + * Author: Dave Jiang <djiang@mvista.com> + * + * 2006-2007 (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 <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/edac.h> + +#include "edac_core.h" +#include "edac_module.h" +#include "mv64x60_edac.h" + +static const char *mv64x60_ctl_name = "MV64x60"; +static int edac_dev_idx; +static int edac_pci_idx; +static int edac_mc_idx; + +/*********************** PCI err device **********************************/ +#ifdef CONFIG_PCI +static void mv64x60_pci_check(struct edac_pci_ctl_info *pci) +{ + struct mv64x60_pci_pdata *pdata = pci->pvt_info; + u32 cause; + + cause = in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_CAUSE); + if (!cause) + return; + + printk(KERN_ERR "Error in PCI %d Interface\n", pdata->pci_hose); + printk(KERN_ERR "Cause register: 0x%08x\n", cause); + printk(KERN_ERR "Address Low: 0x%08x\n", + in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_ADDR_LO)); + printk(KERN_ERR "Address High: 0x%08x\n", + in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_ADDR_HI)); + printk(KERN_ERR "Attribute: 0x%08x\n", + in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_ATTR)); + printk(KERN_ERR "Command: 0x%08x\n", + in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_CMD)); + out_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_CAUSE, ~cause); + + if (cause & MV64X60_PCI_PE_MASK) + edac_pci_handle_pe(pci, pci->ctl_name); + + if (!(cause & MV64X60_PCI_PE_MASK)) + edac_pci_handle_npe(pci, pci->ctl_name); +} + +static irqreturn_t mv64x60_pci_isr(int irq, void *dev_id) +{ + struct edac_pci_ctl_info *pci = dev_id; + struct mv64x60_pci_pdata *pdata = pci->pvt_info; + u32 val; + + val = in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_CAUSE); + if (!val) + return IRQ_NONE; + + mv64x60_pci_check(pci); + + return IRQ_HANDLED; +} + +static int __devinit mv64x60_pci_err_probe(struct platform_device *pdev) +{ + struct edac_pci_ctl_info *pci; + struct mv64x60_pci_pdata *pdata; + struct resource *r; + int res = 0; + + if (!devres_open_group(&pdev->dev, mv64x60_pci_err_probe, GFP_KERNEL)) + return -ENOMEM; + + pci = edac_pci_alloc_ctl_info(sizeof(*pdata), "mv64x60_pci_err"); + if (!pci) + return -ENOMEM; + + pdata = pci->pvt_info; + + pdata->pci_hose = pdev->id; + pdata->name = "mpc85xx_pci_err"; + pdata->irq = NO_IRQ; + platform_set_drvdata(pdev, pci); + pci->dev = &pdev->dev; + pci->dev_name = pdev->dev.bus_id; + pci->mod_name = EDAC_MOD_STR; + pci->ctl_name = pdata->name; + + if (edac_op_state == EDAC_OPSTATE_POLL) + pci->edac_check = mv64x60_pci_check; + + pdata->edac_idx = edac_pci_idx++; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + printk(KERN_ERR "%s: Unable to get resource for " + "PCI err regs\n", __func__); + res = -ENOENT; + goto err; + } + + if (!devm_request_mem_region(&pdev->dev, + r->start, + r->end - r->start + 1, + pdata->name)) { + printk(KERN_ERR "%s: Error while requesting mem region\n", + __func__); + res = -EBUSY; + goto err; + } + + pdata->pci_vbase = devm_ioremap(&pdev->dev, + r->start, + r->end - r->start + 1); + if (!pdata->pci_vbase) { + printk(KERN_ERR "%s: Unable to setup PCI err regs\n", __func__); + res = -ENOMEM; + goto err; + } + + out_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_CAUSE, 0); + out_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_MASK, 0); + out_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_MASK, + MV64X60_PCIx_ERR_MASK_VAL); + + if (edac_pci_add_device(pci, pdata->edac_idx) > 0) { + debugf3("%s(): failed edac_pci_add_device()\n", __func__); + goto err; + } + + if (edac_op_state == EDAC_OPSTATE_INT) { + pdata->irq = platform_get_irq(pdev, 0); + res = devm_request_irq(&pdev->dev, + pdata->irq, + mv64x60_pci_isr, + IRQF_DISABLED, + "[EDAC] PCI err", + pci); + if (res < 0) { + printk(KERN_ERR "%s: Unable to request irq %d for " + "MV64x60 PCI ERR\n", __func__, pdata->irq); + res = -ENODEV; + goto err2; + } + printk(KERN_INFO EDAC_MOD_STR " acquired irq %d for PCI Err\n", + pdata->irq); + } + + devres_remove_group(&pdev->dev, mv64x60_pci_err_probe); + + /* get this far and it's successful */ + debugf3("%s(): success\n", __func__); + + return 0; + +err2: + edac_pci_del_device(&pdev->dev); +err: + edac_pci_free_ctl_info(pci); + devres_release_group(&pdev->dev, mv64x60_pci_err_probe); + return res; +} + +static int mv64x60_pci_err_remove(struct platform_device *pdev) +{ + struct edac_pci_ctl_info *pci = platform_get_drvdata(pdev); + + debugf0("%s()\n", __func__); + + edac_pci_del_device(&pdev->dev); + + edac_pci_free_ctl_info(pci); + + return 0; +} + +static struct platform_driver mv64x60_pci_err_driver = { + .probe = mv64x60_pci_err_probe, + .remove = __devexit_p(mv64x60_pci_err_remove), + .driver = { + .name = "mv64x60_pci_err", + } +}; + +#endif /* CONFIG_PCI */ + +/*********************** SRAM err device **********************************/ +static void mv64x60_sram_check(struct edac_device_ctl_info *edac_dev) +{ + struct mv64x60_sram_pdata *pdata = edac_dev->pvt_info; + u32 cause; + + cause = in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_CAUSE); + if (!cause) + return; + + printk(KERN_ERR "Error in internal SRAM\n"); + printk(KERN_ERR "Cause register: 0x%08x\n", cause); + printk(KERN_ERR "Address Low: 0x%08x\n", + in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_ADDR_LO)); + printk(KERN_ERR "Address High: 0x%08x\n", + in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_ADDR_HI)); + printk(KERN_ERR "Data Low: 0x%08x\n", + in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_DATA_LO)); + printk(KERN_ERR "Data High: 0x%08x\n", + in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_DATA_HI)); + printk(KERN_ERR "Parity: 0x%08x\n", + in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_PARITY)); + out_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_CAUSE, 0); + + edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name); +} + +static irqreturn_t mv64x60_sram_isr(int irq, void *dev_id) +{ + struct edac_device_ctl_info *edac_dev = dev_id; + struct mv64x60_sram_pdata *pdata = edac_dev->pvt_info; + u32 cause; + + cause = in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_CAUSE); + if (!cause) + return IRQ_NONE; + + mv64x60_sram_check(edac_dev); + + return IRQ_HANDLED; +} + +static int __devinit mv64x60_sram_err_probe(struct platform_device *pdev) +{ + struct edac_device_ctl_info *edac_dev; + struct mv64x60_sram_pdata *pdata; + struct resource *r; + int res = 0; + + if (!devres_open_group(&pdev->dev, mv64x60_sram_err_probe, GFP_KERNEL)) + return -ENOMEM; + + edac_dev = edac_device_alloc_ctl_info(sizeof(*pdata), + "sram", 1, NULL, 0, 0, NULL, 0, + edac_dev_idx); + if (!edac_dev) { + devres_release_group(&pdev->dev, mv64x60_sram_err_probe); + return -ENOMEM; + } + + pdata = edac_dev->pvt_info; + pdata->name = "mv64x60_sram_err"; + pdata->irq = NO_IRQ; + edac_dev->dev = &pdev->dev; + platform_set_drvdata(pdev, edac_dev); + edac_dev->dev_name = pdev->dev.bus_id; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + printk(KERN_ERR "%s: Unable to get resource for " + "SRAM err regs\n", __func__); + res = -ENOENT; + goto err; + } + + if (!devm_request_mem_region(&pdev->dev, + r->start, + r->end - r->start + 1, + pdata->name)) { + printk(KERN_ERR "%s: Error while request mem region\n", + __func__); + res = -EBUSY; + goto err; + } + + pdata->sram_vbase = devm_ioremap(&pdev->dev, + r->start, + r->end - r->start + 1); + if (!pdata->sram_vbase) { + printk(KERN_ERR "%s: Unable to setup SRAM err regs\n", + __func__); + res = -ENOMEM; + goto err; + } + + /* setup SRAM err registers */ + out_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_CAUSE, 0); + + edac_dev->mod_name = EDAC_MOD_STR; + edac_dev->ctl_name = pdata->name; + + if (edac_op_state == EDAC_OPSTATE_POLL) + edac_dev->edac_check = mv64x60_sram_check; + + pdata->edac_idx = edac_dev_idx++; + + if (edac_device_add_device(edac_dev) > 0) { + debugf3("%s(): failed edac_device_add_device()\n", __func__); + goto err; + } + + if (edac_op_state == EDAC_OPSTATE_INT) { + pdata->irq = platform_get_irq(pdev, 0); + res = devm_request_irq(&pdev->dev, + pdata->irq, + mv64x60_sram_isr, + IRQF_DISABLED, + "[EDAC] SRAM err", + edac_dev); + if (res < 0) { + printk(KERN_ERR + "%s: Unable to request irq %d for " + "MV64x60 SRAM ERR\n", __func__, pdata->irq); + res = -ENODEV; + goto err2; + } + + printk(KERN_INFO EDAC_MOD_STR " acquired irq %d for SRAM Err\n", + pdata->irq); + } + + devres_remove_group(&pdev->dev, mv64x60_sram_err_probe); + + /* get this far and it's successful */ + debugf3("%s(): success\n", __func__); + + return 0; + +err2: + edac_device_del_device(&pdev->dev); +err: + devres_release_group(&pdev->dev, mv64x60_sram_err_probe); + edac_device_free_ctl_info(edac_dev); + return res; +} + +static int mv64x60_sram_err_remove(struct platform_device *pdev) +{ + struct edac_device_ctl_info *edac_dev = platform_get_drvdata(pdev); + + debugf0("%s()\n", __func__); + + edac_device_del_device(&pdev->dev); + edac_device_free_ctl_info(edac_dev); + + return 0; +} + +static struct platform_driver mv64x60_sram_err_driver = { + .probe = mv64x60_sram_err_probe, + .remove = mv64x60_sram_err_remove, + .driver = { + .name = "mv64x60_sram_err", + } +}; + +/*********************** CPU err device **********************************/ +static void mv64x60_cpu_check(struct edac_device_ctl_info *edac_dev) +{ + struct mv64x60_cpu_pdata *pdata = edac_dev->pvt_info; + u32 cause; + + cause = in_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_CAUSE) & + MV64x60_CPU_CAUSE_MASK; + if (!cause) + return; + + printk(KERN_ERR "Error on CPU interface\n"); + printk(KERN_ERR "Cause register: 0x%08x\n", cause); + printk(KERN_ERR "Address Low: 0x%08x\n", + in_le32(pdata->cpu_vbase[0] + MV64x60_CPU_ERR_ADDR_LO)); + printk(KERN_ERR "Address High: 0x%08x\n", + in_le32(pdata->cpu_vbase[0] + MV64x60_CPU_ERR_ADDR_HI)); + printk(KERN_ERR "Data Low: 0x%08x\n", + in_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_DATA_LO)); + printk(KERN_ERR "Data High: 0x%08x\n", + in_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_DATA_HI)); + printk(KERN_ERR "Parity: 0x%08x\n", + in_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_PARITY)); + out_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_CAUSE, 0); + + edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name); +} + +static irqreturn_t mv64x60_cpu_isr(int irq, void *dev_id) +{ + struct edac_device_ctl_info *edac_dev = dev_id; + struct mv64x60_cpu_pdata *pdata = edac_dev->pvt_info; + u32 cause; + + cause = in_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_CAUSE) & + MV64x60_CPU_CAUSE_MASK; + if (!cause) + return IRQ_NONE; + + mv64x60_cpu_check(edac_dev); + + return IRQ_HANDLED; +} + +static int __devinit mv64x60_cpu_err_probe(struct platform_device *pdev) +{ + struct edac_device_ctl_info *edac_dev; + struct resource *r; + struct mv64x60_cpu_pdata *pdata; + int res = 0; + + if (!devres_open_group(&pdev->dev, mv64x60_cpu_err_probe, GFP_KERNEL)) + return -ENOMEM; + + edac_dev = edac_device_alloc_ctl_info(sizeof(*pdata), + "cpu", 1, NULL, 0, 0, NULL, 0, + edac_dev_idx); + if (!edac_dev) { + devres_release_group(&pdev->dev, mv64x60_cpu_err_probe); + return -ENOMEM; + } + + pdata = edac_dev->pvt_info; + pdata->name = "mv64x60_cpu_err"; + pdata->irq = NO_IRQ; + edac_dev->dev = &pdev->dev; + platform_set_drvdata(pdev, edac_dev); + edac_dev->dev_name = pdev->dev.bus_id; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + printk(KERN_ERR "%s: Unable to get resource for " + "CPU err regs\n", __func__); + res = -ENOENT; + goto err; + } + + if (!devm_request_mem_region(&pdev->dev, + r->start, + r->end - r->start + 1, + pdata->name)) { + printk(KERN_ERR "%s: Error while requesting mem region\n", + __func__); + res = -EBUSY; + goto err; + } + + pdata->cpu_vbase[0] = devm_ioremap(&pdev->dev, + r->start, + r->end - r->start + 1); + if (!pdata->cpu_vbase[0]) { + printk(KERN_ERR "%s: Unable to setup CPU err regs\n", __func__); + res = -ENOMEM; + goto err; + } + + r = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!r) { + printk(KERN_ERR "%s: Unable to get resource for " + "CPU err regs\n", __func__); + res = -ENOENT; + goto err; + } + + if (!devm_request_mem_region(&pdev->dev, + r->start, + r->end - r->start + 1, + pdata->name)) { + printk(KERN_ERR "%s: Error while requesting mem region\n", + __func__); + res = -EBUSY; + goto err; + } + + pdata->cpu_vbase[1] = devm_ioremap(&pdev->dev, + r->start, + r->end - r->start + 1); + if (!pdata->cpu_vbase[1]) { + printk(KERN_ERR "%s: Unable to setup CPU err regs\n", __func__); + res = -ENOMEM; + goto err; + } + + /* setup CPU err registers */ + out_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_CAUSE, 0); + out_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_MASK, 0); + out_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_MASK, 0x000000ff); + + edac_dev->mod_name = EDAC_MOD_STR; + edac_dev->ctl_name = pdata->name; + if (edac_op_state == EDAC_OPSTATE_POLL) + edac_dev->edac_check = mv64x60_cpu_check; + + pdata->edac_idx = edac_dev_idx++; + + if (edac_device_add_device(edac_dev) > 0) { + debugf3("%s(): failed edac_device_add_device()\n", __func__); + goto err; + } + + if (edac_op_state == EDAC_OPSTATE_INT) { + pdata->irq = platform_get_irq(pdev, 0); + res = devm_request_irq(&pdev->dev, + pdata->irq, + mv64x60_cpu_isr, + IRQF_DISABLED, + "[EDAC] CPU err", + edac_dev); + if (res < 0) { + printk(KERN_ERR + "%s: Unable to request irq %d for MV64x60 " + "CPU ERR\n", __func__, pdata->irq); + res = -ENODEV; + goto err2; + } + + printk(KERN_INFO EDAC_MOD_STR + " acquired irq %d for CPU Err\n", pdata->irq); + } + + devres_remove_group(&pdev->dev, mv64x60_cpu_err_probe); + + /* get this far and it's successful */ + debugf3("%s(): success\n", __func__); + + return 0; + +err2: + edac_device_del_device(&pdev->dev); +err: + devres_release_group(&pdev->dev, mv64x60_cpu_err_probe); + edac_device_free_ctl_info(edac_dev); + return res; +} + +static int mv64x60_cpu_err_remove(struct platform_device *pdev) +{ + struct edac_device_ctl_info *edac_dev = platform_get_drvdata(pdev); + + debugf0("%s()\n", __func__); + + edac_device_del_device(&pdev->dev); + edac_device_free_ctl_info(edac_dev); + return 0; +} + +static struct platform_driver mv64x60_cpu_err_driver = { + .probe = mv64x60_cpu_err_probe, + .remove = mv64x60_cpu_err_remove, + .driver = { + .name = "mv64x60_cpu_err", + } +}; + +/*********************** DRAM err device **********************************/ + +static void mv64x60_mc_check(struct mem_ctl_info *mci) +{ + struct mv64x60_mc_pdata *pdata = mci->pvt_info; + u32 reg; + u32 err_addr; + u32 sdram_ecc; + u32 comp_ecc; + u32 syndrome; + + reg = in_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR); + if (!reg) + return; + + err_addr = reg & ~0x3; + sdram_ecc = in_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_RCVD); + comp_ecc = in_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_CALC); + syndrome = sdram_ecc ^ comp_ecc; + + /* first bit clear in ECC Err Reg, 1 bit error, correctable by HW */ + if (!(reg & 0x1)) + edac_mc_handle_ce(mci, err_addr >> PAGE_SHIFT, + err_addr & PAGE_MASK, syndrome, 0, 0, + mci->ctl_name); + else /* 2 bit error, UE */ + edac_mc_handle_ue(mci, err_addr >> PAGE_SHIFT, + err_addr & PAGE_MASK, 0, mci->ctl_name); + + /* clear the error */ + out_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR, 0); +} + +static irqreturn_t mv64x60_mc_isr(int irq, void *dev_id) +{ + struct mem_ctl_info *mci = dev_id; + struct mv64x60_mc_pdata *pdata = mci->pvt_info; + u32 reg; + + reg = in_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR); + if (!reg) + return IRQ_NONE; + + /* writing 0's to the ECC err addr in check function clears irq */ + mv64x60_mc_check(mci); + + return IRQ_HANDLED; +} + +static void get_total_mem(struct mv64x60_mc_pdata *pdata) +{ + struct device_node *np = NULL; + const unsigned int *reg; + + np = of_find_node_by_type(NULL, "memory"); + if (!np) + return; + + reg = get_property(np, "reg", NULL); + + pdata->total_mem = reg[1]; +} + +static void mv64x60_init_csrows(struct mem_ctl_info *mci, + struct mv64x60_mc_pdata *pdata) +{ + struct csrow_info *csrow; + u32 devtype; + u32 ctl; + + get_total_mem(pdata); + + ctl = in_le32(pdata->mc_vbase + MV64X60_SDRAM_CONFIG); + + csrow = &mci->csrows[0]; + csrow->first_page = 0; + csrow->nr_pages = pdata->total_mem >> PAGE_SHIFT; + csrow->last_page = csrow->first_page + csrow->nr_pages - 1; + csrow->grain = 8; + + csrow->mtype = (ctl & MV64X60_SDRAM_REGISTERED) ? MEM_RDDR : MEM_DDR; + + devtype = (ctl >> 20) & 0x3; + switch (devtype) { + case 0x0: + csrow->dtype = DEV_X32; + break; + case 0x2: /* could be X8 too, but no way to tell */ + csrow->dtype = DEV_X16; + break; + case 0x3: + csrow->dtype = DEV_X4; + break; + default: + csrow->dtype = DEV_UNKNOWN; + break; + } + + csrow->edac_mode = EDAC_SECDED; +} + +static int __devinit mv64x60_mc_err_probe(struct platform_device *pdev) +{ + struct mem_ctl_info *mci; + struct mv64x60_mc_pdata *pdata; + struct resource *r; + u32 ctl; + int res = 0; + + if (!devres_open_group(&pdev->dev, mv64x60_mc_err_probe, GFP_KERNEL)) + return -ENOMEM; + + mci = edac_mc_alloc(sizeof(struct mv64x60_mc_pdata), 1, 1, edac_mc_idx); + if (!mci) { + printk(KERN_ERR "%s: No memory for CPU err\n", __func__); + devres_release_group(&pdev->dev, mv64x60_mc_err_probe); + return -ENOMEM; + } + + pdata = mci->pvt_info; + mci->dev = &pdev->dev; + platform_set_drvdata(pdev, mci); + pdata->name = "mv64x60_mc_err"; + pdata->irq = NO_IRQ; + mci->dev_name = pdev->dev.bus_id; + pdata->edac_idx = edac_mc_idx++; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + printk(KERN_ERR "%s: Unable to get resource for " + "MC err regs\n", __func__); + res = -ENOENT; + goto err; + } + + if (!devm_request_mem_region(&pdev->dev, + r->start, + r->end - r->start + 1, + pdata->name)) { + printk(KERN_ERR "%s: Error while requesting mem region\n", + __func__); + res = -EBUSY; + goto err; + } + + pdata->mc_vbase = devm_ioremap(&pdev->dev, + r->start, + r->end - r->start + 1); + if (!pdata->mc_vbase) { + printk(KERN_ERR "%s: Unable to setup MC err regs\n", __func__); + res = -ENOMEM; + goto err; + } + + ctl = in_le32(pdata->mc_vbase + MV64X60_SDRAM_CONFIG); + if (!(ctl & MV64X60_SDRAM_ECC)) { + /* Non-ECC RAM? */ + printk(KERN_WARNING "%s: No ECC DIMMs discovered\n", __func__); + res = -ENODEV; + goto err2; + } + + debugf3("%s(): init mci\n", __func__); + mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_DDR; + mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; + mci->edac_cap = EDAC_FLAG_SECDED; + mci->mod_name = EDAC_MOD_STR; + mci->mod_ver = MV64x60_REVISION; + mci->ctl_name = mv64x60_ctl_name; + + if (edac_op_state == EDAC_OPSTATE_POLL) + mci->edac_check = mv64x60_mc_check; + + mci->ctl_page_to_phys = NULL; + + mci->scrub_mode = SCRUB_SW_SRC; + + mv64x60_init_csrows(mci, pdata); + + /* setup MC registers */ + out_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR, 0); + ctl = in_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_CNTL); + ctl = (ctl & 0xff00ffff) | 0x10000; + out_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_CNTL, ctl); + + if (edac_mc_add_mc(mci)) { + debugf3("%s(): failed edac_mc_add_mc()\n", __func__); + goto err; + } + + if (edac_op_state == EDAC_OPSTATE_INT) { + /* acquire interrupt that reports errors */ + pdata->irq = platform_get_irq(pdev, 0); + res = devm_request_irq(&pdev->dev, + pdata->irq, + mv64x60_mc_isr, + IRQF_DISABLED, + "[EDAC] MC err", + mci); + if (res < 0) { + printk(KERN_ERR "%s: Unable to request irq %d for " + "MV64x60 DRAM ERR\n", __func__, pdata->irq); + res = -ENODEV; + goto err2; + } + + printk(KERN_INFO EDAC_MOD_STR " acquired irq %d for MC Err\n", + pdata->irq); + } + + /* get this far and it's successful */ + debugf3("%s(): success\n", __func__); + + return 0; + +err2: + edac_mc_del_mc(&pdev->dev); +err: + devres_release_group(&pdev->dev, mv64x60_mc_err_probe); + edac_mc_free(mci); + return res; +} + +static int mv64x60_mc_err_remove(struct platform_device *pdev) +{ + struct mem_ctl_info *mci = platform_get_drvdata(pdev); + + debugf0("%s()\n", __func__); + + edac_mc_del_mc(&pdev->dev); + edac_mc_free(mci); + return 0; +} + +static struct platform_driver mv64x60_mc_err_driver = { + .probe = mv64x60_mc_err_probe, + .remove = mv64x60_mc_err_remove, + .driver = { + .name = "mv64x60_mc_err", + } +}; + +static int __init mv64x60_edac_init(void) +{ + int ret = 0; + + printk(KERN_INFO "Marvell MV64x60 EDAC driver " MV64x60_REVISION "\n"); + printk(KERN_INFO "\t(C) 2006-2007 MontaVista Software\n"); + /* make sure error reporting method is sane */ + switch (edac_op_state) { + case EDAC_OPSTATE_POLL: + case EDAC_OPSTATE_INT: + break; + default: + edac_op_state = EDAC_OPSTATE_INT; + break; + } + + ret = platform_driver_register(&mv64x60_mc_err_driver); + if (ret) + printk(KERN_WARNING EDAC_MOD_STR "MC err failed to register\n"); + + ret = platform_driver_register(&mv64x60_cpu_err_driver); + if (ret) + printk(KERN_WARNING EDAC_MOD_STR + "CPU err failed to register\n"); + + ret = platform_driver_register(&mv64x60_sram_err_driver); + if (ret) + printk(KERN_WARNING EDAC_MOD_STR + "SRAM err failed to register\n"); + +#ifdef CONFIG_PCI + ret = platform_driver_register(&mv64x60_pci_err_driver); + if (ret) + printk(KERN_WARNING EDAC_MOD_STR + "PCI err failed to register\n"); +#endif + + return ret; +} +module_init(mv64x60_edac_init); + +static void __exit mv64x60_edac_exit(void) +{ +#ifdef CONFIG_PCI + platform_driver_unregister(&mv64x60_pci_err_driver); +#endif + platform_driver_unregister(&mv64x60_sram_err_driver); + platform_driver_unregister(&mv64x60_cpu_err_driver); + platform_driver_unregister(&mv64x60_mc_err_driver); +} +module_exit(mv64x60_edac_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Montavista Software, Inc."); +module_param(edac_op_state, int, 0444); +MODULE_PARM_DESC(edac_op_state, + "EDAC Error Reporting state: 0=Poll, 2=Interrupt"); diff --git a/drivers/edac/mv64x60_edac.h b/drivers/edac/mv64x60_edac.h new file mode 100644 index 000000000000..e042e2daa8f4 --- /dev/null +++ b/drivers/edac/mv64x60_edac.h @@ -0,0 +1,114 @@ +/* + * EDAC defs for Marvell MV64x60 bridge chip + * + * Author: Dave Jiang <djiang@mvista.com> + * + * 2007 (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. + * + */ +#ifndef _MV64X60_EDAC_H_ +#define _MV64X60_EDAC_H_ + +#define MV64x60_REVISION " Ver: 2.0.0 " __DATE__ +#define EDAC_MOD_STR "MV64x60_edac" + +#define mv64x60_printk(level, fmt, arg...) \ + edac_printk(level, "MV64x60", fmt, ##arg) + +#define mv64x60_mc_printk(mci, level, fmt, arg...) \ + edac_mc_chipset_printk(mci, level, "MV64x60", fmt, ##arg) + +/* CPU Error Report Registers */ +#define MV64x60_CPU_ERR_ADDR_LO 0x00 /* 0x0070 */ +#define MV64x60_CPU_ERR_ADDR_HI 0x08 /* 0x0078 */ +#define MV64x60_CPU_ERR_DATA_LO 0x00 /* 0x0128 */ +#define MV64x60_CPU_ERR_DATA_HI 0x08 /* 0x0130 */ +#define MV64x60_CPU_ERR_PARITY 0x10 /* 0x0138 */ +#define MV64x60_CPU_ERR_CAUSE 0x18 /* 0x0140 */ +#define MV64x60_CPU_ERR_MASK 0x20 /* 0x0148 */ + +#define MV64x60_CPU_CAUSE_MASK 0x07ffffff + +/* SRAM Error Report Registers */ +#define MV64X60_SRAM_ERR_CAUSE 0x08 /* 0x0388 */ +#define MV64X60_SRAM_ERR_ADDR_LO 0x10 /* 0x0390 */ +#define MV64X60_SRAM_ERR_ADDR_HI 0x78 /* 0x03f8 */ +#define MV64X60_SRAM_ERR_DATA_LO 0x18 /* 0x0398 */ +#define MV64X60_SRAM_ERR_DATA_HI 0x20 /* 0x03a0 */ +#define MV64X60_SRAM_ERR_PARITY 0x28 /* 0x03a8 */ + +/* SDRAM Controller Registers */ +#define MV64X60_SDRAM_CONFIG 0x00 /* 0x1400 */ +#define MV64X60_SDRAM_ERR_DATA_HI 0x40 /* 0x1440 */ +#define MV64X60_SDRAM_ERR_DATA_LO 0x44 /* 0x1444 */ +#define MV64X60_SDRAM_ERR_ECC_RCVD 0x48 /* 0x1448 */ +#define MV64X60_SDRAM_ERR_ECC_CALC 0x4c /* 0x144c */ +#define MV64X60_SDRAM_ERR_ADDR 0x50 /* 0x1450 */ +#define MV64X60_SDRAM_ERR_ECC_CNTL 0x54 /* 0x1454 */ +#define MV64X60_SDRAM_ERR_ECC_ERR_CNT 0x58 /* 0x1458 */ + +#define MV64X60_SDRAM_REGISTERED 0x20000 +#define MV64X60_SDRAM_ECC 0x40000 + +#ifdef CONFIG_PCI +/* + * Bit 0 of MV64x60_PCIx_ERR_MASK does not exist on the 64360 and because of + * errata FEr-#11 and FEr-##16 for the 64460, it should be 0 on that chip as + * well. IOW, don't set bit 0. + */ +#define MV64X60_PCIx_ERR_MASK_VAL 0x00a50c24 + +/* Register offsets from PCIx error address low register */ +#define MV64X60_PCI_ERROR_ADDR_LO 0x00 +#define MV64X60_PCI_ERROR_ADDR_HI 0x04 +#define MV64X60_PCI_ERROR_ATTR 0x08 +#define MV64X60_PCI_ERROR_CMD 0x10 +#define MV64X60_PCI_ERROR_CAUSE 0x18 +#define MV64X60_PCI_ERROR_MASK 0x1c + +#define MV64X60_PCI_ERR_SWrPerr 0x0002 +#define MV64X60_PCI_ERR_SRdPerr 0x0004 +#define MV64X60_PCI_ERR_MWrPerr 0x0020 +#define MV64X60_PCI_ERR_MRdPerr 0x0040 + +#define MV64X60_PCI_PE_MASK (MV64X60_PCI_ERR_SWrPerr | \ + MV64X60_PCI_ERR_SRdPerr | \ + MV64X60_PCI_ERR_MWrPerr | \ + MV64X60_PCI_ERR_MRdPerr) + +struct mv64x60_pci_pdata { + int pci_hose; + void __iomem *pci_vbase; + char *name; + int irq; + int edac_idx; +}; + +#endif /* CONFIG_PCI */ + +struct mv64x60_mc_pdata { + void __iomem *mc_vbase; + int total_mem; + char *name; + int irq; + int edac_idx; +}; + +struct mv64x60_cpu_pdata { + void __iomem *cpu_vbase[2]; + char *name; + int irq; + int edac_idx; +}; + +struct mv64x60_sram_pdata { + void __iomem *sram_vbase; + char *name; + int irq; + int edac_idx; +}; + +#endif diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 25716193a534..0c886c882385 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -15,6 +15,13 @@ config MFD_SM501 interface. The device may be connected by PCI or local bus with varying functions enabled. +config MFD_ASIC3 + bool "Support for Compaq ASIC3" + depends on GENERIC_HARDIRQS && ARM + ---help--- + This driver supports the ASIC3 multifunction chip found on many + PDAs (mainly iPAQ and HTC based ones) + endmenu menu "Multimedia Capabilities Port drivers" diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 51432091b323..521cd5cb68af 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -3,6 +3,7 @@ # obj-$(CONFIG_MFD_SM501) += sm501.o +obj-$(CONFIG_MFD_ASIC3) += asic3.o obj-$(CONFIG_MCP) += mcp-core.o obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c new file mode 100644 index 000000000000..63fb1ff3ad10 --- /dev/null +++ b/drivers/mfd/asic3.c @@ -0,0 +1,588 @@ +/* + * driver/mfd/asic3.c + * + * Compaq ASIC3 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. + * + * Copyright 2001 Compaq Computer Corporation. + * Copyright 2004-2005 Phil Blundell + * Copyright 2007 OpenedHand Ltd. + * + * Authors: Phil Blundell <pb@handhelds.org>, + * Samuel Ortiz <sameo@openedhand.com> + * + */ + +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <linux/spinlock.h> +#include <linux/platform_device.h> + +#include <linux/mfd/asic3.h> + +static inline void asic3_write_register(struct asic3 *asic, + unsigned int reg, u32 value) +{ + iowrite16(value, (unsigned long)asic->mapping + + (reg >> asic->bus_shift)); +} + +static inline u32 asic3_read_register(struct asic3 *asic, + unsigned int reg) +{ + return ioread16((unsigned long)asic->mapping + + (reg >> asic->bus_shift)); +} + +/* IRQs */ +#define MAX_ASIC_ISR_LOOPS 20 +#define ASIC3_GPIO_Base_INCR \ + (ASIC3_GPIO_B_Base - ASIC3_GPIO_A_Base) + +static void asic3_irq_flip_edge(struct asic3 *asic, + u32 base, int bit) +{ + u16 edge; + unsigned long flags; + + spin_lock_irqsave(&asic->lock, flags); + edge = asic3_read_register(asic, + base + ASIC3_GPIO_EdgeTrigger); + edge ^= bit; + asic3_write_register(asic, + base + ASIC3_GPIO_EdgeTrigger, edge); + spin_unlock_irqrestore(&asic->lock, flags); +} + +static void asic3_irq_demux(unsigned int irq, struct irq_desc *desc) +{ + int iter, i; + unsigned long flags; + struct asic3 *asic; + + desc->chip->ack(irq); + + asic = desc->handler_data; + + for (iter = 0 ; iter < MAX_ASIC_ISR_LOOPS; iter++) { + u32 status; + int bank; + + spin_lock_irqsave(&asic->lock, flags); + status = asic3_read_register(asic, + ASIC3_OFFSET(INTR, PIntStat)); + spin_unlock_irqrestore(&asic->lock, flags); + + /* Check all ten register bits */ + if ((status & 0x3ff) == 0) + break; + + /* Handle GPIO IRQs */ + for (bank = 0; bank < ASIC3_NUM_GPIO_BANKS; bank++) { + if (status & (1 << bank)) { + unsigned long base, istat; + + base = ASIC3_GPIO_A_Base + + bank * ASIC3_GPIO_Base_INCR; + + spin_lock_irqsave(&asic->lock, flags); + istat = asic3_read_register(asic, + base + + ASIC3_GPIO_IntStatus); + /* Clearing IntStatus */ + asic3_write_register(asic, + base + + ASIC3_GPIO_IntStatus, 0); + spin_unlock_irqrestore(&asic->lock, flags); + + for (i = 0; i < ASIC3_GPIOS_PER_BANK; i++) { + int bit = (1 << i); + unsigned int irqnr; + + if (!(istat & bit)) + continue; + + irqnr = asic->irq_base + + (ASIC3_GPIOS_PER_BANK * bank) + + i; + desc = irq_desc + irqnr; + desc->handle_irq(irqnr, desc); + if (asic->irq_bothedge[bank] & bit) + asic3_irq_flip_edge(asic, base, + bit); + } + } + } + + /* Handle remaining IRQs in the status register */ + for (i = ASIC3_NUM_GPIOS; i < ASIC3_NR_IRQS; i++) { + /* They start at bit 4 and go up */ + if (status & (1 << (i - ASIC3_NUM_GPIOS + 4))) { + desc = irq_desc + + i; + desc->handle_irq(asic->irq_base + i, + desc); + } + } + } + + if (iter >= MAX_ASIC_ISR_LOOPS) + printk(KERN_ERR "%s: interrupt processing overrun\n", + __FUNCTION__); +} + +static inline int asic3_irq_to_bank(struct asic3 *asic, int irq) +{ + int n; + + n = (irq - asic->irq_base) >> 4; + + return (n * (ASIC3_GPIO_B_Base - ASIC3_GPIO_A_Base)); +} + +static inline int asic3_irq_to_index(struct asic3 *asic, int irq) +{ + return (irq - asic->irq_base) & 0xf; +} + +static void asic3_mask_gpio_irq(unsigned int irq) +{ + struct asic3 *asic = get_irq_chip_data(irq); + u32 val, bank, index; + unsigned long flags; + + bank = asic3_irq_to_bank(asic, irq); + index = asic3_irq_to_index(asic, irq); + + spin_lock_irqsave(&asic->lock, flags); + val = asic3_read_register(asic, bank + ASIC3_GPIO_Mask); + val |= 1 << index; + asic3_write_register(asic, bank + ASIC3_GPIO_Mask, val); + spin_unlock_irqrestore(&asic->lock, flags); +} + +static void asic3_mask_irq(unsigned int irq) +{ + struct asic3 *asic = get_irq_chip_data(irq); + int regval; + unsigned long flags; + + spin_lock_irqsave(&asic->lock, flags); + regval = asic3_read_register(asic, + ASIC3_INTR_Base + + ASIC3_INTR_IntMask); + + regval &= ~(ASIC3_INTMASK_MASK0 << + (irq - (asic->irq_base + ASIC3_NUM_GPIOS))); + + asic3_write_register(asic, + ASIC3_INTR_Base + + ASIC3_INTR_IntMask, + regval); + spin_unlock_irqrestore(&asic->lock, flags); +} + +static void asic3_unmask_gpio_irq(unsigned int irq) +{ + struct asic3 *asic = get_irq_chip_data(irq); + u32 val, bank, index; + unsigned long flags; + + bank = asic3_irq_to_bank(asic, irq); + index = asic3_irq_to_index(asic, irq); + + spin_lock_irqsave(&asic->lock, flags); + val = asic3_read_register(asic, bank + ASIC3_GPIO_Mask); + val &= ~(1 << index); + asic3_write_register(asic, bank + ASIC3_GPIO_Mask, val); + spin_unlock_irqrestore(&asic->lock, flags); +} + +static void asic3_unmask_irq(unsigned int irq) +{ + struct asic3 *asic = get_irq_chip_data(irq); + int regval; + unsigned long flags; + + spin_lock_irqsave(&asic->lock, flags); + regval = asic3_read_register(asic, + ASIC3_INTR_Base + + ASIC3_INTR_IntMask); + + regval |= (ASIC3_INTMASK_MASK0 << + (irq - (asic->irq_base + ASIC3_NUM_GPIOS))); + + asic3_write_register(asic, + ASIC3_INTR_Base + + ASIC3_INTR_IntMask, + regval); + spin_unlock_irqrestore(&asic->lock, flags); +} + +static int asic3_gpio_irq_type(unsigned int irq, unsigned int type) +{ + struct asic3 *asic = get_irq_chip_data(irq); + u32 bank, index; + u16 trigger, level, edge, bit; + unsigned long flags; + + bank = asic3_irq_to_bank(asic, irq); + index = asic3_irq_to_index(asic, irq); + bit = 1<<index; + + spin_lock_irqsave(&asic->lock, flags); + level = asic3_read_register(asic, + bank + ASIC3_GPIO_LevelTrigger); + edge = asic3_read_register(asic, + bank + ASIC3_GPIO_EdgeTrigger); + trigger = asic3_read_register(asic, + bank + ASIC3_GPIO_TriggerType); + asic->irq_bothedge[(irq - asic->irq_base) >> 4] &= ~bit; + + if (type == IRQT_RISING) { + trigger |= bit; + edge |= bit; + } else if (type == IRQT_FALLING) { + trigger |= bit; + edge &= ~bit; + } else if (type == IRQT_BOTHEDGE) { + trigger |= bit; + if (asic3_gpio_get_value(asic, irq - asic->irq_base)) + edge &= ~bit; + else + edge |= bit; + asic->irq_bothedge[(irq - asic->irq_base) >> 4] |= bit; + } else if (type == IRQT_LOW) { + trigger &= ~bit; + level &= ~bit; + } else if (type == IRQT_HIGH) { + trigger &= ~bit; + level |= bit; + } else { + /* + * if type == IRQT_NOEDGE, we should mask interrupts, but + * be careful to not unmask them if mask was also called. + * Probably need internal state for mask. + */ + printk(KERN_NOTICE "asic3: irq type not changed.\n"); + } + asic3_write_register(asic, bank + ASIC3_GPIO_LevelTrigger, + level); + asic3_write_register(asic, bank + ASIC3_GPIO_EdgeTrigger, + edge); + asic3_write_register(asic, bank + ASIC3_GPIO_TriggerType, + trigger); + spin_unlock_irqrestore(&asic->lock, flags); + return 0; +} + +static struct irq_chip asic3_gpio_irq_chip = { + .name = "ASIC3-GPIO", + .ack = asic3_mask_gpio_irq, + .mask = asic3_mask_gpio_irq, + .unmask = asic3_unmask_gpio_irq, + .set_type = asic3_gpio_irq_type, +}; + +static struct irq_chip asic3_irq_chip = { + .name = "ASIC3", + .ack = asic3_mask_irq, + .mask = asic3_mask_irq, + .unmask = asic3_unmask_irq, +}; + +static int asic3_irq_probe(struct platform_device *pdev) +{ + struct asic3 *asic = platform_get_drvdata(pdev); + unsigned long clksel = 0; + unsigned int irq, irq_base; + + asic->irq_nr = platform_get_irq(pdev, 0); + if (asic->irq_nr < 0) + return asic->irq_nr; + + /* turn on clock to IRQ controller */ + clksel |= CLOCK_SEL_CX; + asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL), + clksel); + + irq_base = asic->irq_base; + + for (irq = irq_base; irq < irq_base + ASIC3_NR_IRQS; irq++) { + if (irq < asic->irq_base + ASIC3_NUM_GPIOS) + set_irq_chip(irq, &asic3_gpio_irq_chip); + else + set_irq_chip(irq, &asic3_irq_chip); + + set_irq_chip_data(irq, asic); + set_irq_handler(irq, handle_level_irq); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + } + + asic3_write_register(asic, ASIC3_OFFSET(INTR, IntMask), + ASIC3_INTMASK_GINTMASK); + + set_irq_chained_handler(asic->irq_nr, asic3_irq_demux); + set_irq_type(asic->irq_nr, IRQT_RISING); + set_irq_data(asic->irq_nr, asic); + + return 0; +} + +static void asic3_irq_remove(struct platform_device *pdev) +{ + struct asic3 *asic = platform_get_drvdata(pdev); + unsigned int irq, irq_base; + + irq_base = asic->irq_base; + + for (irq = irq_base; irq < irq_base + ASIC3_NR_IRQS; irq++) { + set_irq_flags(irq, 0); + set_irq_handler(irq, NULL); + set_irq_chip(irq, NULL); + set_irq_chip_data(irq, NULL); + } + set_irq_chained_handler(asic->irq_nr, NULL); +} + +/* GPIOs */ +static inline u32 asic3_get_gpio(struct asic3 *asic, unsigned int base, + unsigned int function) +{ + return asic3_read_register(asic, base + function); +} + +static void asic3_set_gpio(struct asic3 *asic, unsigned int base, + unsigned int function, u32 bits, u32 val) +{ + unsigned long flags; + + spin_lock_irqsave(&asic->lock, flags); + val |= (asic3_read_register(asic, base + function) & ~bits); + + asic3_write_register(asic, base + function, val); + spin_unlock_irqrestore(&asic->lock, flags); +} + +#define asic3_get_gpio_a(asic, fn) \ + asic3_get_gpio(asic, ASIC3_GPIO_A_Base, ASIC3_GPIO_##fn) +#define asic3_get_gpio_b(asic, fn) \ + asic3_get_gpio(asic, ASIC3_GPIO_B_Base, ASIC3_GPIO_##fn) +#define asic3_get_gpio_c(asic, fn) \ + asic3_get_gpio(asic, ASIC3_GPIO_C_Base, ASIC3_GPIO_##fn) +#define asic3_get_gpio_d(asic, fn) \ + asic3_get_gpio(asic, ASIC3_GPIO_D_Base, ASIC3_GPIO_##fn) + +#define asic3_set_gpio_a(asic, fn, bits, val) \ + asic3_set_gpio(asic, ASIC3_GPIO_A_Base, ASIC3_GPIO_##fn, bits, val) +#define asic3_set_gpio_b(asic, fn, bits, val) \ + asic3_set_gpio(asic, ASIC3_GPIO_B_Base, ASIC3_GPIO_##fn, bits, val) +#define asic3_set_gpio_c(asic, fn, bits, val) \ + asic3_set_gpio(asic, ASIC3_GPIO_C_Base, ASIC3_GPIO_##fn, bits, val) +#define asic3_set_gpio_d(asic, fn, bits, val) \ + asic3_set_gpio(asic, ASIC3_GPIO_D_Base, ASIC3_GPIO_##fn, bits, val) + +#define asic3_set_gpio_banks(asic, fn, bits, pdata, field) \ + do { \ + asic3_set_gpio_a((asic), fn, (bits), (pdata)->gpio_a.field); \ + asic3_set_gpio_b((asic), fn, (bits), (pdata)->gpio_b.field); \ + asic3_set_gpio_c((asic), fn, (bits), (pdata)->gpio_c.field); \ + asic3_set_gpio_d((asic), fn, (bits), (pdata)->gpio_d.field); \ + } while (0) + +int asic3_gpio_get_value(struct asic3 *asic, unsigned gpio) +{ + u32 mask = ASIC3_GPIO_bit(gpio); + + switch (gpio >> 4) { + case ASIC3_GPIO_BANK_A: + return asic3_get_gpio_a(asic, Status) & mask; + case ASIC3_GPIO_BANK_B: + return asic3_get_gpio_b(asic, Status) & mask; + case ASIC3_GPIO_BANK_C: + return asic3_get_gpio_c(asic, Status) & mask; + case ASIC3_GPIO_BANK_D: + return asic3_get_gpio_d(asic, Status) & mask; + default: + printk(KERN_ERR "%s: invalid GPIO value 0x%x", + __FUNCTION__, gpio); + return -EINVAL; + } +} +EXPORT_SYMBOL(asic3_gpio_get_value); + +void asic3_gpio_set_value(struct asic3 *asic, unsigned gpio, int val) +{ + u32 mask = ASIC3_GPIO_bit(gpio); + u32 bitval = 0; + if (val) + bitval = mask; + + switch (gpio >> 4) { + case ASIC3_GPIO_BANK_A: + asic3_set_gpio_a(asic, Out, mask, bitval); + return; + case ASIC3_GPIO_BANK_B: + asic3_set_gpio_b(asic, Out, mask, bitval); + return; + case ASIC3_GPIO_BANK_C: + asic3_set_gpio_c(asic, Out, mask, bitval); + return; + case ASIC3_GPIO_BANK_D: + asic3_set_gpio_d(asic, Out, mask, bitval); + return; + default: + printk(KERN_ERR "%s: invalid GPIO value 0x%x", + __FUNCTION__, gpio); + return; + } +} +EXPORT_SYMBOL(asic3_gpio_set_value); + +static int asic3_gpio_probe(struct platform_device *pdev) +{ + struct asic3_platform_data *pdata = pdev->dev.platform_data; + struct asic3 *asic = platform_get_drvdata(pdev); + + asic3_write_register(asic, ASIC3_GPIO_OFFSET(A, Mask), 0xffff); + asic3_write_register(asic, ASIC3_GPIO_OFFSET(B, Mask), 0xffff); + asic3_write_register(asic, ASIC3_GPIO_OFFSET(C, Mask), 0xffff); + asic3_write_register(asic, ASIC3_GPIO_OFFSET(D, Mask), 0xffff); + + asic3_set_gpio_a(asic, SleepMask, 0xffff, 0xffff); + asic3_set_gpio_b(asic, SleepMask, 0xffff, 0xffff); + asic3_set_gpio_c(asic, SleepMask, 0xffff, 0xffff); + asic3_set_gpio_d(asic, SleepMask, 0xffff, 0xffff); + + if (pdata) { + asic3_set_gpio_banks(asic, Out, 0xffff, pdata, init); + asic3_set_gpio_banks(asic, Direction, 0xffff, pdata, dir); + asic3_set_gpio_banks(asic, SleepMask, 0xffff, pdata, + sleep_mask); + asic3_set_gpio_banks(asic, SleepOut, 0xffff, pdata, sleep_out); + asic3_set_gpio_banks(asic, BattFaultOut, 0xffff, pdata, + batt_fault_out); + asic3_set_gpio_banks(asic, SleepConf, 0xffff, pdata, + sleep_conf); + asic3_set_gpio_banks(asic, AltFunction, 0xffff, pdata, + alt_function); + } + + return 0; +} + +static void asic3_gpio_remove(struct platform_device *pdev) +{ + return; +} + + +/* Core */ +static int asic3_probe(struct platform_device *pdev) +{ + struct asic3_platform_data *pdata = pdev->dev.platform_data; + struct asic3 *asic; + struct resource *mem; + unsigned long clksel; + int ret; + + asic = kzalloc(sizeof(struct asic3), GFP_KERNEL); + if (!asic) + return -ENOMEM; + + spin_lock_init(&asic->lock); + platform_set_drvdata(pdev, asic); + asic->dev = &pdev->dev; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + ret = -ENOMEM; + printk(KERN_ERR "asic3: no MEM resource\n"); + goto err_out_1; + } + + asic->mapping = ioremap(mem->start, PAGE_SIZE); + if (!asic->mapping) { + ret = -ENOMEM; + printk(KERN_ERR "asic3: couldn't ioremap\n"); + goto err_out_1; + } + + asic->irq_base = pdata->irq_base; + + if (pdata && pdata->bus_shift) + asic->bus_shift = 2 - pdata->bus_shift; + else + asic->bus_shift = 0; + + clksel = 0; + asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL), clksel); + + ret = asic3_irq_probe(pdev); + if (ret < 0) { + printk(KERN_ERR "asic3: couldn't probe IRQs\n"); + goto err_out_2; + } + asic3_gpio_probe(pdev); + + if (pdata->children) { + int i; + for (i = 0; i < pdata->n_children; i++) { + pdata->children[i]->dev.parent = &pdev->dev; + platform_device_register(pdata->children[i]); + } + } + + printk(KERN_INFO "ASIC3 Core driver\n"); + + return 0; + + err_out_2: + iounmap(asic->mapping); + err_out_1: + kfree(asic); + + return ret; +} + +static int asic3_remove(struct platform_device *pdev) +{ + struct asic3 *asic = platform_get_drvdata(pdev); + + asic3_gpio_remove(pdev); + asic3_irq_remove(pdev); + + asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL), 0); + + iounmap(asic->mapping); + + kfree(asic); + + return 0; +} + +static void asic3_shutdown(struct platform_device *pdev) +{ +} + +static struct platform_driver asic3_device_driver = { + .driver = { + .name = "asic3", + }, + .probe = asic3_probe, + .remove = __devexit_p(asic3_remove), + .shutdown = asic3_shutdown, +}; + +static int __init asic3_init(void) +{ + int retval = 0; + retval = platform_driver_register(&asic3_device_driver); + return retval; +} + +subsys_initcall(asic3_init); diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 7d170cd381c3..9cc25fd80b60 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1737,10 +1737,8 @@ config SC92031 config CPMAC tristate "TI AR7 CPMAC Ethernet support (EXPERIMENTAL)" - depends on NET_ETHERNET && EXPERIMENTAL && AR7 + depends on NET_ETHERNET && EXPERIMENTAL && AR7 && BROKEN select PHYLIB - select FIXED_PHY - select FIXED_MII_100_FDX help TI AR7 CPMAC Ethernet support diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c index 6ccebb830ff9..c85194f2cd2d 100644 --- a/drivers/net/cpmac.c +++ b/drivers/net/cpmac.c @@ -845,15 +845,6 @@ static void cpmac_adjust_link(struct net_device *dev) spin_unlock(&priv->lock); } -static int cpmac_link_update(struct net_device *dev, - struct fixed_phy_status *status) -{ - status->link = 1; - status->speed = 100; - status->duplex = 1; - return 0; -} - static int cpmac_open(struct net_device *dev) { int i, size, res; @@ -996,11 +987,11 @@ static int external_switch; static int __devinit cpmac_probe(struct platform_device *pdev) { int rc, phy_id, i; + int mdio_bus_id = cpmac_mii.id; struct resource *mem; struct cpmac_priv *priv; struct net_device *dev; struct plat_cpmac_data *pdata; - struct fixed_info *fixed_phy; DECLARE_MAC_BUF(mac); pdata = pdev->dev.platform_data; @@ -1014,9 +1005,23 @@ static int __devinit cpmac_probe(struct platform_device *pdev) } if (phy_id == PHY_MAX_ADDR) { - if (external_switch || dumb_switch) + if (external_switch || dumb_switch) { + struct fixed_phy_status status = {}; + + mdio_bus_id = 0; + + /* + * FIXME: this should be in the platform code! + * Since there is not platform code at all (that is, + * no mainline users of that driver), place it here + * for now. + */ phy_id = 0; - else { + status.link = 1; + status.duplex = 1; + status.speed = 100; + fixed_phy_add(PHY_POLL, phy_id, &status); + } else { printk(KERN_ERR "cpmac: no PHY present\n"); return -ENODEV; } @@ -1060,32 +1065,8 @@ static int __devinit cpmac_probe(struct platform_device *pdev) priv->msg_enable = netif_msg_init(debug_level, 0xff); memcpy(dev->dev_addr, pdata->dev_addr, sizeof(dev->dev_addr)); - if (phy_id == 31) { - snprintf(priv->phy_name, BUS_ID_SIZE, PHY_ID_FMT, cpmac_mii.id, - phy_id); - } else { - /* Let's try to get a free fixed phy... */ - for (i = 0; i < MAX_PHY_AMNT; i++) { - fixed_phy = fixed_mdio_get_phydev(i); - if (!fixed_phy) - continue; - if (!fixed_phy->phydev->attached_dev) { - strncpy(priv->phy_name, - fixed_phy->phydev->dev.bus_id, - BUS_ID_SIZE); - fixed_mdio_set_link_update(fixed_phy->phydev, - &cpmac_link_update); - goto phy_found; - } - } - if (netif_msg_drv(priv)) - printk(KERN_ERR "%s: Could not find fixed PHY\n", - dev->name); - rc = -ENODEV; - goto fail; - } + snprintf(priv->phy_name, BUS_ID_SIZE, PHY_ID_FMT, mdio_bus_id, phy_id); -phy_found: priv->phy = phy_connect(dev, priv->phy_name, &cpmac_adjust_link, 0, PHY_INTERFACE_MODE_MII); if (IS_ERR(priv->phy)) { diff --git a/drivers/of/base.c b/drivers/of/base.c index b306fef1ac41..80c9deca5f35 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -138,6 +138,31 @@ struct device_node *of_get_parent(const struct device_node *node) EXPORT_SYMBOL(of_get_parent); /** + * of_get_next_parent - Iterate to a node's parent + * @node: Node to get parent of + * + * This is like of_get_parent() except that it drops the + * refcount on the passed node, making it suitable for iterating + * through a node's parents. + * + * Returns a node pointer with refcount incremented, use + * of_node_put() on it when done. + */ +struct device_node *of_get_next_parent(struct device_node *node) +{ + struct device_node *parent; + + if (!node) + return NULL; + + read_lock(&devtree_lock); + parent = of_node_get(node->parent); + of_node_put(node); + read_unlock(&devtree_lock); + return parent; +} + +/** * of_get_next_child - Iterate a node childs * @node: parent node * @prev: previous child of the parent node, or NULL to get first diff --git a/drivers/of/platform.c b/drivers/of/platform.c index b47bb2d7476a..ca09a63a64db 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -85,6 +85,15 @@ static int of_platform_device_resume(struct device * dev) return error; } +static void of_platform_device_shutdown(struct device *dev) +{ + struct of_device *of_dev = to_of_device(dev); + struct of_platform_driver *drv = to_of_platform_driver(dev->driver); + + if (dev->driver && drv->shutdown) + drv->shutdown(of_dev); +} + int of_bus_type_init(struct bus_type *bus, const char *name) { bus->name = name; @@ -93,6 +102,7 @@ int of_bus_type_init(struct bus_type *bus, const char *name) bus->remove = of_platform_device_remove; bus->suspend = of_platform_device_suspend; bus->resume = of_platform_device_resume; + bus->shutdown = of_platform_device_shutdown; return bus_register(bus); } diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index d4824840c5bf..13399d133b94 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -106,7 +106,6 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(energy_now), POWER_SUPPLY_ATTR(energy_avg), POWER_SUPPLY_ATTR(capacity), - POWER_SUPPLY_ATTR(capacity_level), POWER_SUPPLY_ATTR(temp), POWER_SUPPLY_ATTR(temp_ambient), POWER_SUPPLY_ATTR(time_to_empty_now), diff --git a/drivers/serial/68328serial.c b/drivers/serial/68328serial.c index 0d99120ab5a2..2b8a410e0959 100644 --- a/drivers/serial/68328serial.c +++ b/drivers/serial/68328serial.c @@ -84,9 +84,6 @@ extern wait_queue_head_t keypress_wait; struct tty_driver *serial_driver; -/* serial subtype definitions */ -#define SERIAL_TYPE_NORMAL 1 - /* number of characters left in xmit buffer before we ask for more */ #define WAKEUP_CHARS 256 diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 6a44fb1dc167..84a054d7e986 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -465,20 +465,24 @@ config SERIAL_DZ bool "DECstation DZ serial driver" depends on MACH_DECSTATION && 32BIT select SERIAL_CORE - help - DZ11-family serial controllers for VAXstations, including the - DC7085, M7814, and M7819. + default y + ---help--- + DZ11-family serial controllers for DECstations and VAXstations, + including the DC7085, M7814, and M7819. config SERIAL_DZ_CONSOLE bool "Support console on DECstation DZ serial driver" depends on SERIAL_DZ=y select SERIAL_CORE_CONSOLE - help + default y + ---help--- If you say Y here, it will be possible to use a serial port as the system console (the system console is the device which receives all kernel messages and warnings and which allows logins in single user - mode). Note that the firmware uses ttyS0 as the serial console on - the Maxine and ttyS2 on the others. + mode). + + Note that the firmware uses ttyS3 as the serial console on + DECstations that use this driver. If unsure, say Y. @@ -1138,17 +1142,17 @@ config SERIAL_SGI_L1_CONSOLE say Y. Otherwise, say N. config SERIAL_MPC52xx - tristate "Freescale MPC52xx family PSC serial support" - depends on PPC_MPC52xx + tristate "Freescale MPC52xx/MPC512x family PSC serial support" + depends on PPC_MPC52xx || PPC_MPC512x select SERIAL_CORE help - This drivers support the MPC52xx PSC serial ports. If you would - like to use them, you must answer Y or M to this option. Not that + This driver supports MPC52xx and MPC512x PSC serial ports. If you would + like to use them, you must answer Y or M to this option. Note that for use as console, it must be included in kernel and not as a module. config SERIAL_MPC52xx_CONSOLE - bool "Console on a Freescale MPC52xx family PSC serial port" + bool "Console on a Freescale MPC52xx/MPC512x family PSC serial port" depends on SERIAL_MPC52xx=y select SERIAL_CORE_CONSOLE help @@ -1156,7 +1160,7 @@ config SERIAL_MPC52xx_CONSOLE of the Freescale MPC52xx family as a console. config SERIAL_MPC52xx_CONSOLE_BAUD - int "Freescale MPC52xx family PSC serial port baud" + int "Freescale MPC52xx/MPC512x family PSC serial port baud" depends on SERIAL_MPC52xx_CONSOLE=y default "9600" help diff --git a/drivers/serial/crisv10.c b/drivers/serial/crisv10.c index a4e23cf47906..383c4e660cd5 100644 --- a/drivers/serial/crisv10.c +++ b/drivers/serial/crisv10.c @@ -68,11 +68,6 @@ static char *serial_version = "$Revision: 1.25 $"; struct tty_driver *serial_driver; -/* serial subtype definitions */ -#ifndef SERIAL_TYPE_NORMAL -#define SERIAL_TYPE_NORMAL 1 -#endif - /* number of characters left in xmit buffer before we ask for more */ #define WAKEUP_CHARS 256 diff --git a/drivers/serial/dz.c b/drivers/serial/dz.c index bbae5a220219..116211fcd36f 100644 --- a/drivers/serial/dz.c +++ b/drivers/serial/dz.c @@ -6,7 +6,7 @@ * * Email: olivier.lebaillif@ifrsys.com * - * Copyright (C) 2004, 2006 Maciej W. Rozycki + * Copyright (C) 2004, 2006, 2007 Maciej W. Rozycki * * [31-AUG-98] triemer * Changed IRQ to use Harald's dec internals interrupts.h @@ -32,38 +32,63 @@ #define SUPPORT_SYSRQ #endif +#include <linux/bitops.h> +#include <linux/compiler.h> +#include <linux/console.h> #include <linux/delay.h> -#include <linux/module.h> -#include <linux/interrupt.h> +#include <linux/errno.h> #include <linux/init.h> -#include <linux/console.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/major.h> +#include <linux/module.h> +#include <linux/serial.h> +#include <linux/serial_core.h> #include <linux/sysrq.h> #include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/serial_core.h> -#include <linux/serial.h> +#include <asm/atomic.h> #include <asm/bootinfo.h> +#include <asm/io.h> +#include <asm/system.h> + #include <asm/dec/interrupts.h> #include <asm/dec/kn01.h> #include <asm/dec/kn02.h> #include <asm/dec/machtype.h> #include <asm/dec/prom.h> -#include <asm/irq.h> -#include <asm/system.h> -#include <asm/uaccess.h> +#include <asm/dec/system.h> #include "dz.h" -static char *dz_name = "DECstation DZ serial driver version "; -static char *dz_version = "1.03"; + +MODULE_DESCRIPTION("DECstation DZ serial driver"); +MODULE_LICENSE("GPL"); + + +static char dz_name[] __initdata = "DECstation DZ serial driver version "; +static char dz_version[] __initdata = "1.04"; struct dz_port { + struct dz_mux *mux; struct uart_port port; unsigned int cflag; }; -static struct dz_port dz_ports[DZ_NB_PORT]; +struct dz_mux { + struct dz_port dport[DZ_NB_PORT]; + atomic_t map_guard; + atomic_t irq_guard; + int initialised; +}; + +static struct dz_mux dz_mux; + +static inline struct dz_port *to_dport(struct uart_port *uport) +{ + return container_of(uport, struct dz_port, port); +} /* * ------------------------------------------------------------ @@ -74,21 +99,18 @@ static struct dz_port dz_ports[DZ_NB_PORT]; * ------------------------------------------------------------ */ -static inline unsigned short dz_in(struct dz_port *dport, unsigned offset) +static u16 dz_in(struct dz_port *dport, unsigned offset) { - volatile unsigned short *addr = - (volatile unsigned short *) (dport->port.membase + offset); + void __iomem *addr = dport->port.membase + offset; - return *addr; + return readw(addr); } -static inline void dz_out(struct dz_port *dport, unsigned offset, - unsigned short value) +static void dz_out(struct dz_port *dport, unsigned offset, u16 value) { - volatile unsigned short *addr = - (volatile unsigned short *) (dport->port.membase + offset); + void __iomem *addr = dport->port.membase + offset; - *addr = value; + writew(value, addr); } /* @@ -103,42 +125,33 @@ static inline void dz_out(struct dz_port *dport, unsigned offset, static void dz_stop_tx(struct uart_port *uport) { - struct dz_port *dport = (struct dz_port *)uport; - unsigned short tmp, mask = 1 << dport->port.line; - unsigned long flags; + struct dz_port *dport = to_dport(uport); + u16 tmp, mask = 1 << dport->port.line; - spin_lock_irqsave(&dport->port.lock, flags); tmp = dz_in(dport, DZ_TCR); /* read the TX flag */ tmp &= ~mask; /* clear the TX flag */ dz_out(dport, DZ_TCR, tmp); - spin_unlock_irqrestore(&dport->port.lock, flags); } static void dz_start_tx(struct uart_port *uport) { - struct dz_port *dport = (struct dz_port *)uport; - unsigned short tmp, mask = 1 << dport->port.line; - unsigned long flags; + struct dz_port *dport = to_dport(uport); + u16 tmp, mask = 1 << dport->port.line; - spin_lock_irqsave(&dport->port.lock, flags); tmp = dz_in(dport, DZ_TCR); /* read the TX flag */ tmp |= mask; /* set the TX flag */ dz_out(dport, DZ_TCR, tmp); - spin_unlock_irqrestore(&dport->port.lock, flags); } static void dz_stop_rx(struct uart_port *uport) { - struct dz_port *dport = (struct dz_port *)uport; - unsigned long flags; + struct dz_port *dport = to_dport(uport); - spin_lock_irqsave(&dport->port.lock, flags); - dport->cflag &= ~DZ_CREAD; - dz_out(dport, DZ_LPR, dport->cflag | dport->port.line); - spin_unlock_irqrestore(&dport->port.lock, flags); + dport->cflag &= ~DZ_RXENAB; + dz_out(dport, DZ_LPR, dport->cflag); } -static void dz_enable_ms(struct uart_port *port) +static void dz_enable_ms(struct uart_port *uport) { /* nothing to do */ } @@ -170,73 +183,73 @@ static void dz_enable_ms(struct uart_port *port) * This routine deals with inputs from any lines. * ------------------------------------------------------------ */ -static inline void dz_receive_chars(struct dz_port *dport_in) +static inline void dz_receive_chars(struct dz_mux *mux) { - struct dz_port *dport; + struct uart_port *uport; + struct dz_port *dport = &mux->dport[0]; struct tty_struct *tty = NULL; struct uart_icount *icount; int lines_rx[DZ_NB_PORT] = { [0 ... DZ_NB_PORT - 1] = 0 }; - unsigned short status; unsigned char ch, flag; + u16 status; int i; - while ((status = dz_in(dport_in, DZ_RBUF)) & DZ_DVAL) { - dport = &dz_ports[LINE(status)]; - tty = dport->port.info->tty; /* point to the proper dev */ + while ((status = dz_in(dport, DZ_RBUF)) & DZ_DVAL) { + dport = &mux->dport[LINE(status)]; + uport = &dport->port; + tty = uport->info->tty; /* point to the proper dev */ ch = UCHAR(status); /* grab the char */ + flag = TTY_NORMAL; - icount = &dport->port.icount; + icount = &uport->icount; icount->rx++; - flag = TTY_NORMAL; - if (status & DZ_FERR) { /* frame error */ + if (unlikely(status & (DZ_OERR | DZ_FERR | DZ_PERR))) { + /* - * There is no separate BREAK status bit, so - * treat framing errors as BREAKs for Magic SysRq - * and SAK; normally, otherwise. + * There is no separate BREAK status bit, so treat + * null characters with framing errors as BREAKs; + * normally, otherwise. For this move the Framing + * Error bit to a simulated BREAK bit. */ - if (uart_handle_break(&dport->port)) - continue; - if (dport->port.flags & UPF_SAK) + if (!ch) { + status |= (status & DZ_FERR) >> + (ffs(DZ_FERR) - ffs(DZ_BREAK)); + status &= ~DZ_FERR; + } + + /* Handle SysRq/SAK & keep track of the statistics. */ + if (status & DZ_BREAK) { + icount->brk++; + if (uart_handle_break(uport)) + continue; + } else if (status & DZ_FERR) + icount->frame++; + else if (status & DZ_PERR) + icount->parity++; + if (status & DZ_OERR) + icount->overrun++; + + status &= uport->read_status_mask; + if (status & DZ_BREAK) flag = TTY_BREAK; - else + else if (status & DZ_FERR) flag = TTY_FRAME; - } else if (status & DZ_OERR) /* overrun error */ - flag = TTY_OVERRUN; - else if (status & DZ_PERR) /* parity error */ - flag = TTY_PARITY; - - /* keep track of the statistics */ - switch (flag) { - case TTY_FRAME: - icount->frame++; - break; - case TTY_PARITY: - icount->parity++; - break; - case TTY_OVERRUN: - icount->overrun++; - break; - case TTY_BREAK: - icount->brk++; - break; - default: - break; + else if (status & DZ_PERR) + flag = TTY_PARITY; + } - if (uart_handle_sysrq_char(&dport->port, ch)) + if (uart_handle_sysrq_char(uport, ch)) continue; - if ((status & dport->port.ignore_status_mask) == 0) { - uart_insert_char(&dport->port, - status, DZ_OERR, ch, flag); - lines_rx[LINE(status)] = 1; - } + uart_insert_char(uport, status, DZ_OERR, ch, flag); + lines_rx[LINE(status)] = 1; } for (i = 0; i < DZ_NB_PORT; i++) if (lines_rx[i]) - tty_flip_buffer_push(dz_ports[i].port.info->tty); + tty_flip_buffer_push(mux->dport[i].port.info->tty); } /* @@ -246,15 +259,15 @@ static inline void dz_receive_chars(struct dz_port *dport_in) * This routine deals with outputs to any lines. * ------------------------------------------------------------ */ -static inline void dz_transmit_chars(struct dz_port *dport_in) +static inline void dz_transmit_chars(struct dz_mux *mux) { - struct dz_port *dport; + struct dz_port *dport = &mux->dport[0]; struct circ_buf *xmit; - unsigned short status; unsigned char tmp; + u16 status; - status = dz_in(dport_in, DZ_CSR); - dport = &dz_ports[LINE(status)]; + status = dz_in(dport, DZ_CSR); + dport = &mux->dport[LINE(status)]; xmit = &dport->port.info->xmit; if (dport->port.x_char) { /* XON/XOFF chars */ @@ -265,7 +278,9 @@ static inline void dz_transmit_chars(struct dz_port *dport_in) } /* If nothing to do or stopped or hardware stopped. */ if (uart_circ_empty(xmit) || uart_tx_stopped(&dport->port)) { + spin_lock(&dport->port.lock); dz_stop_tx(&dport->port); + spin_unlock(&dport->port.lock); return; } @@ -282,8 +297,11 @@ static inline void dz_transmit_chars(struct dz_port *dport_in) uart_write_wakeup(&dport->port); /* Are we are done. */ - if (uart_circ_empty(xmit)) + if (uart_circ_empty(xmit)) { + spin_lock(&dport->port.lock); dz_stop_tx(&dport->port); + spin_unlock(&dport->port.lock); + } } /* @@ -301,7 +319,7 @@ static inline void check_modem_status(struct dz_port *dport) * 1. No status change interrupt; use a timer. * 2. Handle the 3100/5000 as appropriate. --macro */ - unsigned short status; + u16 status; /* If not the modem line just return. */ if (dport->port.line != DZ_MODEM) @@ -322,19 +340,20 @@ static inline void check_modem_status(struct dz_port *dport) * It deals with the multiple ports. * ------------------------------------------------------------ */ -static irqreturn_t dz_interrupt(int irq, void *dev) +static irqreturn_t dz_interrupt(int irq, void *dev_id) { - struct dz_port *dport = dev; - unsigned short status; + struct dz_mux *mux = dev_id; + struct dz_port *dport = &mux->dport[0]; + u16 status; /* get the reason why we just got an irq */ status = dz_in(dport, DZ_CSR); if ((status & (DZ_RDONE | DZ_RIE)) == (DZ_RDONE | DZ_RIE)) - dz_receive_chars(dport); + dz_receive_chars(mux); if ((status & (DZ_TRDY | DZ_TIE)) == (DZ_TRDY | DZ_TIE)) - dz_transmit_chars(dport); + dz_transmit_chars(mux); return IRQ_HANDLED; } @@ -350,7 +369,7 @@ static unsigned int dz_get_mctrl(struct uart_port *uport) /* * FIXME: Handle the 3100/5000 as appropriate. --macro */ - struct dz_port *dport = (struct dz_port *)uport; + struct dz_port *dport = to_dport(uport); unsigned int mctrl = TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; if (dport->port.line == DZ_MODEM) { @@ -366,8 +385,8 @@ static void dz_set_mctrl(struct uart_port *uport, unsigned int mctrl) /* * FIXME: Handle the 3100/5000 as appropriate. --macro */ - struct dz_port *dport = (struct dz_port *)uport; - unsigned short tmp; + struct dz_port *dport = to_dport(uport); + u16 tmp; if (dport->port.line == DZ_MODEM) { tmp = dz_in(dport, DZ_TCR); @@ -388,15 +407,30 @@ static void dz_set_mctrl(struct uart_port *uport, unsigned int mctrl) */ static int dz_startup(struct uart_port *uport) { - struct dz_port *dport = (struct dz_port *)uport; + struct dz_port *dport = to_dport(uport); + struct dz_mux *mux = dport->mux; unsigned long flags; - unsigned short tmp; + int irq_guard; + int ret; + u16 tmp; + + irq_guard = atomic_add_return(1, &mux->irq_guard); + if (irq_guard != 1) + return 0; + + ret = request_irq(dport->port.irq, dz_interrupt, + IRQF_SHARED, "dz", mux); + if (ret) { + atomic_add(-1, &mux->irq_guard); + printk(KERN_ERR "dz: Cannot get IRQ %d!\n", dport->port.irq); + return ret; + } spin_lock_irqsave(&dport->port.lock, flags); - /* enable the interrupt and the scanning */ + /* Enable interrupts. */ tmp = dz_in(dport, DZ_CSR); - tmp |= DZ_RIE | DZ_TIE | DZ_MSE; + tmp |= DZ_RIE | DZ_TIE; dz_out(dport, DZ_CSR, tmp); spin_unlock_irqrestore(&dport->port.lock, flags); @@ -414,7 +448,25 @@ static int dz_startup(struct uart_port *uport) */ static void dz_shutdown(struct uart_port *uport) { - dz_stop_tx(uport); + struct dz_port *dport = to_dport(uport); + struct dz_mux *mux = dport->mux; + unsigned long flags; + int irq_guard; + u16 tmp; + + spin_lock_irqsave(&dport->port.lock, flags); + dz_stop_tx(&dport->port); + spin_unlock_irqrestore(&dport->port.lock, flags); + + irq_guard = atomic_add_return(-1, &mux->irq_guard); + if (!irq_guard) { + /* Disable interrupts. */ + tmp = dz_in(dport, DZ_CSR); + tmp &= ~(DZ_RIE | DZ_TIE); + dz_out(dport, DZ_CSR, tmp); + + free_irq(dport->port.irq, mux); + } } /* @@ -431,7 +483,7 @@ static void dz_shutdown(struct uart_port *uport) */ static unsigned int dz_tx_empty(struct uart_port *uport) { - struct dz_port *dport = (struct dz_port *)uport; + struct dz_port *dport = to_dport(uport); unsigned short tmp, mask = 1 << dport->port.line; tmp = dz_in(dport, DZ_TCR); @@ -446,7 +498,7 @@ static void dz_break_ctl(struct uart_port *uport, int break_state) * FIXME: Can't access BREAK bits in TDR easily; * reuse the code for polled TX. --macro */ - struct dz_port *dport = (struct dz_port *)uport; + struct dz_port *dport = to_dport(uport); unsigned long flags; unsigned short tmp, mask = 1 << dport->port.line; @@ -460,12 +512,69 @@ static void dz_break_ctl(struct uart_port *uport, int break_state) spin_unlock_irqrestore(&uport->lock, flags); } +static int dz_encode_baud_rate(unsigned int baud) +{ + switch (baud) { + case 50: + return DZ_B50; + case 75: + return DZ_B75; + case 110: + return DZ_B110; + case 134: + return DZ_B134; + case 150: + return DZ_B150; + case 300: + return DZ_B300; + case 600: + return DZ_B600; + case 1200: + return DZ_B1200; + case 1800: + return DZ_B1800; + case 2000: + return DZ_B2000; + case 2400: + return DZ_B2400; + case 3600: + return DZ_B3600; + case 4800: + return DZ_B4800; + case 7200: + return DZ_B7200; + case 9600: + return DZ_B9600; + default: + return -1; + } +} + + +static void dz_reset(struct dz_port *dport) +{ + struct dz_mux *mux = dport->mux; + + if (mux->initialised) + return; + + dz_out(dport, DZ_CSR, DZ_CLR); + while (dz_in(dport, DZ_CSR) & DZ_CLR); + iob(); + + /* Enable scanning. */ + dz_out(dport, DZ_CSR, DZ_MSE); + + mux->initialised = 1; +} + static void dz_set_termios(struct uart_port *uport, struct ktermios *termios, struct ktermios *old_termios) { - struct dz_port *dport = (struct dz_port *)uport; + struct dz_port *dport = to_dport(uport); unsigned long flags; unsigned int cflag, baud; + int bflag; cflag = dport->port.line; @@ -492,105 +601,127 @@ static void dz_set_termios(struct uart_port *uport, struct ktermios *termios, cflag |= DZ_PARODD; baud = uart_get_baud_rate(uport, termios, old_termios, 50, 9600); - switch (baud) { - case 50: - cflag |= DZ_B50; - break; - case 75: - cflag |= DZ_B75; - break; - case 110: - cflag |= DZ_B110; - break; - case 134: - cflag |= DZ_B134; - break; - case 150: - cflag |= DZ_B150; - break; - case 300: - cflag |= DZ_B300; - break; - case 600: - cflag |= DZ_B600; - break; - case 1200: - cflag |= DZ_B1200; - break; - case 1800: - cflag |= DZ_B1800; - break; - case 2000: - cflag |= DZ_B2000; - break; - case 2400: - cflag |= DZ_B2400; - break; - case 3600: - cflag |= DZ_B3600; - break; - case 4800: - cflag |= DZ_B4800; - break; - case 7200: - cflag |= DZ_B7200; - break; - case 9600: - default: - cflag |= DZ_B9600; + bflag = dz_encode_baud_rate(baud); + if (bflag < 0) { /* Try to keep unchanged. */ + baud = uart_get_baud_rate(uport, old_termios, NULL, 50, 9600); + bflag = dz_encode_baud_rate(baud); + if (bflag < 0) { /* Resort to 9600. */ + baud = 9600; + bflag = DZ_B9600; + } + tty_termios_encode_baud_rate(termios, baud, baud); } + cflag |= bflag; if (termios->c_cflag & CREAD) cflag |= DZ_RXENAB; spin_lock_irqsave(&dport->port.lock, flags); - dz_out(dport, DZ_LPR, cflag | dport->port.line); + uart_update_timeout(uport, termios->c_cflag, baud); + + dz_out(dport, DZ_LPR, cflag); dport->cflag = cflag; /* setup accept flag */ dport->port.read_status_mask = DZ_OERR; if (termios->c_iflag & INPCK) dport->port.read_status_mask |= DZ_FERR | DZ_PERR; + if (termios->c_iflag & (BRKINT | PARMRK)) + dport->port.read_status_mask |= DZ_BREAK; /* characters to ignore */ uport->ignore_status_mask = 0; + if ((termios->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) + dport->port.ignore_status_mask |= DZ_OERR; if (termios->c_iflag & IGNPAR) dport->port.ignore_status_mask |= DZ_FERR | DZ_PERR; + if (termios->c_iflag & IGNBRK) + dport->port.ignore_status_mask |= DZ_BREAK; spin_unlock_irqrestore(&dport->port.lock, flags); } -static const char *dz_type(struct uart_port *port) +static const char *dz_type(struct uart_port *uport) { return "DZ"; } -static void dz_release_port(struct uart_port *port) +static void dz_release_port(struct uart_port *uport) { - /* nothing to do */ + struct dz_mux *mux = to_dport(uport)->mux; + int map_guard; + + iounmap(uport->membase); + uport->membase = NULL; + + map_guard = atomic_add_return(-1, &mux->map_guard); + if (!map_guard) + release_mem_region(uport->mapbase, dec_kn_slot_size); } -static int dz_request_port(struct uart_port *port) +static int dz_map_port(struct uart_port *uport) { + if (!uport->membase) + uport->membase = ioremap_nocache(uport->mapbase, + dec_kn_slot_size); + if (!uport->membase) { + printk(KERN_ERR "dz: Cannot map MMIO\n"); + return -ENOMEM; + } return 0; } -static void dz_config_port(struct uart_port *port, int flags) +static int dz_request_port(struct uart_port *uport) { - if (flags & UART_CONFIG_TYPE) - port->type = PORT_DZ; + struct dz_mux *mux = to_dport(uport)->mux; + int map_guard; + int ret; + + map_guard = atomic_add_return(1, &mux->map_guard); + if (map_guard == 1) { + if (!request_mem_region(uport->mapbase, dec_kn_slot_size, + "dz")) { + atomic_add(-1, &mux->map_guard); + printk(KERN_ERR + "dz: Unable to reserve MMIO resource\n"); + return -EBUSY; + } + } + ret = dz_map_port(uport); + if (ret) { + map_guard = atomic_add_return(-1, &mux->map_guard); + if (!map_guard) + release_mem_region(uport->mapbase, dec_kn_slot_size); + return ret; + } + return 0; +} + +static void dz_config_port(struct uart_port *uport, int flags) +{ + struct dz_port *dport = to_dport(uport); + + if (flags & UART_CONFIG_TYPE) { + if (dz_request_port(uport)) + return; + + uport->type = PORT_DZ; + + dz_reset(dport); + } } /* - * verify the new serial_struct (for TIOCSSERIAL). + * Verify the new serial_struct (for TIOCSSERIAL). */ -static int dz_verify_port(struct uart_port *port, struct serial_struct *ser) +static int dz_verify_port(struct uart_port *uport, struct serial_struct *ser) { int ret = 0; + if (ser->type != PORT_UNKNOWN && ser->type != PORT_DZ) ret = -EINVAL; - if (ser->irq != port->irq) + if (ser->irq != uport->irq) ret = -EINVAL; return ret; } @@ -617,40 +748,32 @@ static struct uart_ops dz_ops = { static void __init dz_init_ports(void) { static int first = 1; - struct dz_port *dport; unsigned long base; - int i; + int line; if (!first) return; first = 0; - if (mips_machtype == MACH_DS23100 || - mips_machtype == MACH_DS5100) - base = CKSEG1ADDR(KN01_SLOT_BASE + KN01_DZ11); + if (mips_machtype == MACH_DS23100 || mips_machtype == MACH_DS5100) + base = dec_kn_slot_base + KN01_DZ11; else - base = CKSEG1ADDR(KN02_SLOT_BASE + KN02_DZ11); - - for (i = 0, dport = dz_ports; i < DZ_NB_PORT; i++, dport++) { - spin_lock_init(&dport->port.lock); - dport->port.membase = (char *) base; - dport->port.iotype = UPIO_MEM; - dport->port.irq = dec_interrupt[DEC_IRQ_DZ11]; - dport->port.line = i; - dport->port.fifosize = 1; - dport->port.ops = &dz_ops; - dport->port.flags = UPF_BOOT_AUTOCONF; - } -} + base = dec_kn_slot_base + KN02_DZ11; -static void dz_reset(struct dz_port *dport) -{ - dz_out(dport, DZ_CSR, DZ_CLR); - while (dz_in(dport, DZ_CSR) & DZ_CLR); - iob(); + for (line = 0; line < DZ_NB_PORT; line++) { + struct dz_port *dport = &dz_mux.dport[line]; + struct uart_port *uport = &dport->port; - /* enable scanning */ - dz_out(dport, DZ_CSR, DZ_MSE); + dport->mux = &dz_mux; + + uport->irq = dec_interrupt[DEC_IRQ_DZ11]; + uport->fifosize = 1; + uport->iotype = UPIO_MEM; + uport->flags = UPF_BOOT_AUTOCONF; + uport->ops = &dz_ops; + uport->line = line; + uport->mapbase = base; + } } #ifdef CONFIG_SERIAL_DZ_CONSOLE @@ -670,7 +793,7 @@ static void dz_reset(struct dz_port *dport) */ static void dz_console_putchar(struct uart_port *uport, int ch) { - struct dz_port *dport = (struct dz_port *)uport; + struct dz_port *dport = to_dport(uport); unsigned long flags; unsigned short csr, tcr, trdy, mask; int loops = 10000; @@ -685,7 +808,7 @@ static void dz_console_putchar(struct uart_port *uport, int ch) iob(); spin_unlock_irqrestore(&dport->port.lock, flags); - while (loops--) { + do { trdy = dz_in(dport, DZ_CSR); if (!(trdy & DZ_TRDY)) continue; @@ -696,7 +819,7 @@ static void dz_console_putchar(struct uart_port *uport, int ch) dz_out(dport, DZ_TCR, mask); iob(); udelay(2); - } + } while (loops--); if (loops) /* Cannot send otherwise. */ dz_out(dport, DZ_TDR, ch); @@ -717,7 +840,7 @@ static void dz_console_print(struct console *co, const char *str, unsigned int count) { - struct dz_port *dport = &dz_ports[co->index]; + struct dz_port *dport = &dz_mux.dport[co->index]; #ifdef DEBUG_DZ prom_printf((char *) str); #endif @@ -726,22 +849,28 @@ static void dz_console_print(struct console *co, static int __init dz_console_setup(struct console *co, char *options) { - struct dz_port *dport = &dz_ports[co->index]; + struct dz_port *dport = &dz_mux.dport[co->index]; + struct uart_port *uport = &dport->port; int baud = 9600; int bits = 8; int parity = 'n'; int flow = 'n'; + int ret; - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); + ret = dz_map_port(uport); + if (ret) + return ret; dz_reset(dport); + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + return uart_set_options(&dport->port, co, baud, parity, bits, flow); } static struct uart_driver dz_reg; -static struct console dz_sercons = { +static struct console dz_console = { .name = "ttyS", .write = dz_console_print, .device = uart_console_device, @@ -755,7 +884,7 @@ static int __init dz_serial_console_init(void) { if (!IOASIC) { dz_init_ports(); - register_console(&dz_sercons); + register_console(&dz_console); return 0; } else return -ENXIO; @@ -763,7 +892,7 @@ static int __init dz_serial_console_init(void) console_initcall(dz_serial_console_init); -#define SERIAL_DZ_CONSOLE &dz_sercons +#define SERIAL_DZ_CONSOLE &dz_console #else #define SERIAL_DZ_CONSOLE NULL #endif /* CONFIG_SERIAL_DZ_CONSOLE */ @@ -789,26 +918,14 @@ static int __init dz_init(void) dz_init_ports(); -#ifndef CONFIG_SERIAL_DZ_CONSOLE - /* reset the chip */ - dz_reset(&dz_ports[0]); -#endif - - if (request_irq(dz_ports[0].port.irq, dz_interrupt, - IRQF_DISABLED, "DZ", &dz_ports[0])) - panic("Unable to register DZ interrupt"); - ret = uart_register_driver(&dz_reg); - if (ret != 0) + if (ret) return ret; for (i = 0; i < DZ_NB_PORT; i++) - uart_add_one_port(&dz_reg, &dz_ports[i].port); + uart_add_one_port(&dz_reg, &dz_mux.dport[i].port); - return ret; + return 0; } module_init(dz_init); - -MODULE_DESCRIPTION("DECstation DZ serial driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/serial/dz.h b/drivers/serial/dz.h index 9674d4e49872..faf169ed27b3 100644 --- a/drivers/serial/dz.h +++ b/drivers/serial/dz.h @@ -33,6 +33,8 @@ #define DZ_FERR 0x2000 /* Frame error indicator */ #define DZ_PERR 0x1000 /* Parity error indicator */ +#define DZ_BREAK 0x0800 /* BREAK event software flag */ + #define LINE(x) ((x & DZ_LINE_MASK) >> 8) /* Get the line number from the input buffer */ #define UCHAR(x) ((unsigned char)(x & DZ_RBUF_MASK)) @@ -107,8 +109,8 @@ #define DZ_B7200 0x0D00 #define DZ_B9600 0x0E00 -#define DZ_CREAD 0x1000 /* Enable receiver */ -#define DZ_RXENAB 0x1000 /* enable receive char */ +#define DZ_RXENAB 0x1000 /* Receiver Enable */ + /* * Addresses for the DZ registers */ @@ -124,9 +126,4 @@ #define DZ_XMIT_SIZE 4096 /* buffer size */ #define DZ_WAKEUP_CHARS DZ_XMIT_SIZE/4 -#ifdef MODULE -int init_module (void) -void cleanup_module (void) -#endif - #endif /* DZ_SERIAL_H */ diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c index 3c4d29e59b2c..a638f23c6c61 100644 --- a/drivers/serial/mpc52xx_uart.c +++ b/drivers/serial/mpc52xx_uart.c @@ -16,6 +16,9 @@ * Some of the code has been inspired/copied from the 2.4 code written * by Dale Farnsworth <dfarnsworth@mvista.com>. * + * Copyright (C) 2008 Freescale Semiconductor Inc. + * John Rigby <jrigby@gmail.com> + * Added support for MPC5121 * Copyright (C) 2006 Secret Lab Technologies Ltd. * Grant Likely <grant.likely@secretlab.ca> * Copyright (C) 2004-2006 Sylvain Munaut <tnt@246tNt.com> @@ -67,7 +70,6 @@ #include <linux/serial.h> #include <linux/sysrq.h> #include <linux/console.h> - #include <linux/delay.h> #include <linux/io.h> @@ -79,6 +81,7 @@ #endif #include <asm/mpc52xx.h> +#include <asm/mpc512x.h> #include <asm/mpc52xx_psc.h> #if defined(CONFIG_SERIAL_MPC52xx_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) @@ -111,8 +114,8 @@ static struct device_node *mpc52xx_uart_nodes[MPC52xx_PSC_MAXNUM]; static void mpc52xx_uart_of_enumerate(void); #endif + #define PSC(port) ((struct mpc52xx_psc __iomem *)((port)->membase)) -#define FIFO(port) ((struct mpc52xx_psc_fifo __iomem *)(PSC(port)+1)) /* Forward declaration of the interruption handling routine */ @@ -128,15 +131,301 @@ static irqreturn_t mpc52xx_uart_int(int irq, void *dev_id); #define uart_console(port) (0) #endif +/* ======================================================================== */ +/* PSC fifo operations for isolating differences between 52xx and 512x */ +/* ======================================================================== */ + +struct psc_ops { + void (*fifo_init)(struct uart_port *port); + int (*raw_rx_rdy)(struct uart_port *port); + int (*raw_tx_rdy)(struct uart_port *port); + int (*rx_rdy)(struct uart_port *port); + int (*tx_rdy)(struct uart_port *port); + int (*tx_empty)(struct uart_port *port); + void (*stop_rx)(struct uart_port *port); + void (*start_tx)(struct uart_port *port); + void (*stop_tx)(struct uart_port *port); + void (*rx_clr_irq)(struct uart_port *port); + void (*tx_clr_irq)(struct uart_port *port); + void (*write_char)(struct uart_port *port, unsigned char c); + unsigned char (*read_char)(struct uart_port *port); + void (*cw_disable_ints)(struct uart_port *port); + void (*cw_restore_ints)(struct uart_port *port); + unsigned long (*getuartclk)(void *p); +}; + +#ifdef CONFIG_PPC_MPC52xx +#define FIFO_52xx(port) ((struct mpc52xx_psc_fifo __iomem *)(PSC(port)+1)) +static void mpc52xx_psc_fifo_init(struct uart_port *port) +{ + struct mpc52xx_psc __iomem *psc = PSC(port); + struct mpc52xx_psc_fifo __iomem *fifo = FIFO_52xx(port); + + /* /32 prescaler */ + out_be16(&psc->mpc52xx_psc_clock_select, 0xdd00); + + out_8(&fifo->rfcntl, 0x00); + out_be16(&fifo->rfalarm, 0x1ff); + out_8(&fifo->tfcntl, 0x07); + out_be16(&fifo->tfalarm, 0x80); + + port->read_status_mask |= MPC52xx_PSC_IMR_RXRDY | MPC52xx_PSC_IMR_TXRDY; + out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); +} + +static int mpc52xx_psc_raw_rx_rdy(struct uart_port *port) +{ + return in_be16(&PSC(port)->mpc52xx_psc_status) + & MPC52xx_PSC_SR_RXRDY; +} + +static int mpc52xx_psc_raw_tx_rdy(struct uart_port *port) +{ + return in_be16(&PSC(port)->mpc52xx_psc_status) + & MPC52xx_PSC_SR_TXRDY; +} + + +static int mpc52xx_psc_rx_rdy(struct uart_port *port) +{ + return in_be16(&PSC(port)->mpc52xx_psc_isr) + & port->read_status_mask + & MPC52xx_PSC_IMR_RXRDY; +} + +static int mpc52xx_psc_tx_rdy(struct uart_port *port) +{ + return in_be16(&PSC(port)->mpc52xx_psc_isr) + & port->read_status_mask + & MPC52xx_PSC_IMR_TXRDY; +} + +static int mpc52xx_psc_tx_empty(struct uart_port *port) +{ + return in_be16(&PSC(port)->mpc52xx_psc_status) + & MPC52xx_PSC_SR_TXEMP; +} + +static void mpc52xx_psc_start_tx(struct uart_port *port) +{ + port->read_status_mask |= MPC52xx_PSC_IMR_TXRDY; + out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); +} + +static void mpc52xx_psc_stop_tx(struct uart_port *port) +{ + port->read_status_mask &= ~MPC52xx_PSC_IMR_TXRDY; + out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); +} + +static void mpc52xx_psc_stop_rx(struct uart_port *port) +{ + port->read_status_mask &= ~MPC52xx_PSC_IMR_RXRDY; + out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); +} + +static void mpc52xx_psc_rx_clr_irq(struct uart_port *port) +{ +} + +static void mpc52xx_psc_tx_clr_irq(struct uart_port *port) +{ +} + +static void mpc52xx_psc_write_char(struct uart_port *port, unsigned char c) +{ + out_8(&PSC(port)->mpc52xx_psc_buffer_8, c); +} + +static unsigned char mpc52xx_psc_read_char(struct uart_port *port) +{ + return in_8(&PSC(port)->mpc52xx_psc_buffer_8); +} + +static void mpc52xx_psc_cw_disable_ints(struct uart_port *port) +{ + out_be16(&PSC(port)->mpc52xx_psc_imr, 0); +} + +static void mpc52xx_psc_cw_restore_ints(struct uart_port *port) +{ + out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); +} + +/* Search for bus-frequency property in this node or a parent */ +static unsigned long mpc52xx_getuartclk(void *p) +{ #if defined(CONFIG_PPC_MERGE) -static struct of_device_id mpc52xx_uart_of_match[] = { - { .type = "serial", .compatible = "fsl,mpc5200-psc-uart", }, - { .type = "serial", .compatible = "mpc5200-psc-uart", }, /* lite5200 */ - { .type = "serial", .compatible = "mpc5200-serial", }, /* efika */ - {}, + /* + * 5200 UARTs have a / 32 prescaler + * but the generic serial code assumes 16 + * so return ipb freq / 2 + */ + return mpc52xx_find_ipb_freq(p) / 2; +#else + pr_debug("unexpected call to mpc52xx_getuartclk with arch/ppc\n"); + return NULL; +#endif +} + +static struct psc_ops mpc52xx_psc_ops = { + .fifo_init = mpc52xx_psc_fifo_init, + .raw_rx_rdy = mpc52xx_psc_raw_rx_rdy, + .raw_tx_rdy = mpc52xx_psc_raw_tx_rdy, + .rx_rdy = mpc52xx_psc_rx_rdy, + .tx_rdy = mpc52xx_psc_tx_rdy, + .tx_empty = mpc52xx_psc_tx_empty, + .stop_rx = mpc52xx_psc_stop_rx, + .start_tx = mpc52xx_psc_start_tx, + .stop_tx = mpc52xx_psc_stop_tx, + .rx_clr_irq = mpc52xx_psc_rx_clr_irq, + .tx_clr_irq = mpc52xx_psc_tx_clr_irq, + .write_char = mpc52xx_psc_write_char, + .read_char = mpc52xx_psc_read_char, + .cw_disable_ints = mpc52xx_psc_cw_disable_ints, + .cw_restore_ints = mpc52xx_psc_cw_restore_ints, + .getuartclk = mpc52xx_getuartclk, +}; + +#endif /* CONFIG_MPC52xx */ + +#ifdef CONFIG_PPC_MPC512x +#define FIFO_512x(port) ((struct mpc512x_psc_fifo __iomem *)(PSC(port)+1)) +static void mpc512x_psc_fifo_init(struct uart_port *port) +{ + out_be32(&FIFO_512x(port)->txcmd, MPC512x_PSC_FIFO_RESET_SLICE); + out_be32(&FIFO_512x(port)->txcmd, MPC512x_PSC_FIFO_ENABLE_SLICE); + out_be32(&FIFO_512x(port)->txalarm, 1); + out_be32(&FIFO_512x(port)->tximr, 0); + + out_be32(&FIFO_512x(port)->rxcmd, MPC512x_PSC_FIFO_RESET_SLICE); + out_be32(&FIFO_512x(port)->rxcmd, MPC512x_PSC_FIFO_ENABLE_SLICE); + out_be32(&FIFO_512x(port)->rxalarm, 1); + out_be32(&FIFO_512x(port)->rximr, 0); + + out_be32(&FIFO_512x(port)->tximr, MPC512x_PSC_FIFO_ALARM); + out_be32(&FIFO_512x(port)->rximr, MPC512x_PSC_FIFO_ALARM); +} + +static int mpc512x_psc_raw_rx_rdy(struct uart_port *port) +{ + return !(in_be32(&FIFO_512x(port)->rxsr) & MPC512x_PSC_FIFO_EMPTY); +} + +static int mpc512x_psc_raw_tx_rdy(struct uart_port *port) +{ + return !(in_be32(&FIFO_512x(port)->txsr) & MPC512x_PSC_FIFO_FULL); +} + +static int mpc512x_psc_rx_rdy(struct uart_port *port) +{ + return in_be32(&FIFO_512x(port)->rxsr) + & in_be32(&FIFO_512x(port)->rximr) + & MPC512x_PSC_FIFO_ALARM; +} + +static int mpc512x_psc_tx_rdy(struct uart_port *port) +{ + return in_be32(&FIFO_512x(port)->txsr) + & in_be32(&FIFO_512x(port)->tximr) + & MPC512x_PSC_FIFO_ALARM; +} + +static int mpc512x_psc_tx_empty(struct uart_port *port) +{ + return in_be32(&FIFO_512x(port)->txsr) + & MPC512x_PSC_FIFO_EMPTY; +} + +static void mpc512x_psc_stop_rx(struct uart_port *port) +{ + unsigned long rx_fifo_imr; + + rx_fifo_imr = in_be32(&FIFO_512x(port)->rximr); + rx_fifo_imr &= ~MPC512x_PSC_FIFO_ALARM; + out_be32(&FIFO_512x(port)->rximr, rx_fifo_imr); +} + +static void mpc512x_psc_start_tx(struct uart_port *port) +{ + unsigned long tx_fifo_imr; + + tx_fifo_imr = in_be32(&FIFO_512x(port)->tximr); + tx_fifo_imr |= MPC512x_PSC_FIFO_ALARM; + out_be32(&FIFO_512x(port)->tximr, tx_fifo_imr); +} + +static void mpc512x_psc_stop_tx(struct uart_port *port) +{ + unsigned long tx_fifo_imr; + + tx_fifo_imr = in_be32(&FIFO_512x(port)->tximr); + tx_fifo_imr &= ~MPC512x_PSC_FIFO_ALARM; + out_be32(&FIFO_512x(port)->tximr, tx_fifo_imr); +} + +static void mpc512x_psc_rx_clr_irq(struct uart_port *port) +{ + out_be32(&FIFO_512x(port)->rxisr, in_be32(&FIFO_512x(port)->rxisr)); +} + +static void mpc512x_psc_tx_clr_irq(struct uart_port *port) +{ + out_be32(&FIFO_512x(port)->txisr, in_be32(&FIFO_512x(port)->txisr)); +} + +static void mpc512x_psc_write_char(struct uart_port *port, unsigned char c) +{ + out_8(&FIFO_512x(port)->txdata_8, c); +} + +static unsigned char mpc512x_psc_read_char(struct uart_port *port) +{ + return in_8(&FIFO_512x(port)->rxdata_8); +} + +static void mpc512x_psc_cw_disable_ints(struct uart_port *port) +{ + port->read_status_mask = + in_be32(&FIFO_512x(port)->tximr) << 16 | + in_be32(&FIFO_512x(port)->rximr); + out_be32(&FIFO_512x(port)->tximr, 0); + out_be32(&FIFO_512x(port)->rximr, 0); +} + +static void mpc512x_psc_cw_restore_ints(struct uart_port *port) +{ + out_be32(&FIFO_512x(port)->tximr, + (port->read_status_mask >> 16) & 0x7f); + out_be32(&FIFO_512x(port)->rximr, port->read_status_mask & 0x7f); +} + +static unsigned long mpc512x_getuartclk(void *p) +{ + return mpc512x_find_ips_freq(p); +} + +static struct psc_ops mpc512x_psc_ops = { + .fifo_init = mpc512x_psc_fifo_init, + .raw_rx_rdy = mpc512x_psc_raw_rx_rdy, + .raw_tx_rdy = mpc512x_psc_raw_tx_rdy, + .rx_rdy = mpc512x_psc_rx_rdy, + .tx_rdy = mpc512x_psc_tx_rdy, + .tx_empty = mpc512x_psc_tx_empty, + .stop_rx = mpc512x_psc_stop_rx, + .start_tx = mpc512x_psc_start_tx, + .stop_tx = mpc512x_psc_stop_tx, + .rx_clr_irq = mpc512x_psc_rx_clr_irq, + .tx_clr_irq = mpc512x_psc_tx_clr_irq, + .write_char = mpc512x_psc_write_char, + .read_char = mpc512x_psc_read_char, + .cw_disable_ints = mpc512x_psc_cw_disable_ints, + .cw_restore_ints = mpc512x_psc_cw_restore_ints, + .getuartclk = mpc512x_getuartclk, }; #endif +static struct psc_ops *psc_ops; /* ======================================================================== */ /* UART operations */ @@ -145,8 +434,7 @@ static struct of_device_id mpc52xx_uart_of_match[] = { static unsigned int mpc52xx_uart_tx_empty(struct uart_port *port) { - int status = in_be16(&PSC(port)->mpc52xx_psc_status); - return (status & MPC52xx_PSC_SR_TXEMP) ? TIOCSER_TEMT : 0; + return psc_ops->tx_empty(port) ? TIOCSER_TEMT : 0; } static void @@ -166,16 +454,14 @@ static void mpc52xx_uart_stop_tx(struct uart_port *port) { /* port->lock taken by caller */ - port->read_status_mask &= ~MPC52xx_PSC_IMR_TXRDY; - out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); + psc_ops->stop_tx(port); } static void mpc52xx_uart_start_tx(struct uart_port *port) { /* port->lock taken by caller */ - port->read_status_mask |= MPC52xx_PSC_IMR_TXRDY; - out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); + psc_ops->start_tx(port); } static void @@ -188,8 +474,7 @@ mpc52xx_uart_send_xchar(struct uart_port *port, char ch) if (ch) { /* Make sure tx interrupts are on */ /* Truly necessary ??? They should be anyway */ - port->read_status_mask |= MPC52xx_PSC_IMR_TXRDY; - out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); + psc_ops->start_tx(port); } spin_unlock_irqrestore(&port->lock, flags); @@ -199,8 +484,7 @@ static void mpc52xx_uart_stop_rx(struct uart_port *port) { /* port->lock taken by caller */ - port->read_status_mask &= ~MPC52xx_PSC_IMR_RXRDY; - out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); + psc_ops->stop_rx(port); } static void @@ -227,12 +511,12 @@ static int mpc52xx_uart_startup(struct uart_port *port) { struct mpc52xx_psc __iomem *psc = PSC(port); - struct mpc52xx_psc_fifo __iomem *fifo = FIFO(port); int ret; /* Request IRQ */ ret = request_irq(port->irq, mpc52xx_uart_int, - IRQF_DISABLED | IRQF_SAMPLE_RANDOM, "mpc52xx_psc_uart", port); + IRQF_DISABLED | IRQF_SAMPLE_RANDOM | IRQF_SHARED, + "mpc52xx_psc_uart", port); if (ret) return ret; @@ -242,15 +526,7 @@ mpc52xx_uart_startup(struct uart_port *port) out_be32(&psc->sicr, 0); /* UART mode DCD ignored */ - out_be16(&psc->mpc52xx_psc_clock_select, 0xdd00); /* /16 prescaler on */ - - out_8(&fifo->rfcntl, 0x00); - out_be16(&fifo->rfalarm, 0x1ff); - out_8(&fifo->tfcntl, 0x07); - out_be16(&fifo->tfalarm, 0x80); - - port->read_status_mask |= MPC52xx_PSC_IMR_RXRDY | MPC52xx_PSC_IMR_TXRDY; - out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); + psc_ops->fifo_init(port); out_8(&psc->command, MPC52xx_PSC_TX_ENABLE); out_8(&psc->command, MPC52xx_PSC_RX_ENABLE); @@ -333,8 +609,7 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, * boot for the console, all stuff is not yet ready to receive at that * time and that just makes the kernel oops */ /* while (j-- && mpc52xx_uart_int_rx_chars(port)); */ - while (!(in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_TXEMP) && - --j) + while (!mpc52xx_uart_tx_empty(port) && --j) udelay(1); if (!j) @@ -462,11 +737,9 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port) unsigned short status; /* While we can read, do so ! */ - while ((status = in_be16(&PSC(port)->mpc52xx_psc_status)) & - MPC52xx_PSC_SR_RXRDY) { - + while (psc_ops->raw_rx_rdy(port)) { /* Get the char */ - ch = in_8(&PSC(port)->mpc52xx_psc_buffer_8); + ch = psc_ops->read_char(port); /* Handle sysreq char */ #ifdef SUPPORT_SYSRQ @@ -481,6 +754,8 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port) flag = TTY_NORMAL; port->icount.rx++; + status = in_be16(&PSC(port)->mpc52xx_psc_status); + if (status & (MPC52xx_PSC_SR_PE | MPC52xx_PSC_SR_FE | MPC52xx_PSC_SR_RB)) { @@ -510,7 +785,7 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port) tty_flip_buffer_push(tty); - return in_be16(&PSC(port)->mpc52xx_psc_status) & MPC52xx_PSC_SR_RXRDY; + return psc_ops->raw_rx_rdy(port); } static inline int @@ -520,7 +795,7 @@ mpc52xx_uart_int_tx_chars(struct uart_port *port) /* Process out of band chars */ if (port->x_char) { - out_8(&PSC(port)->mpc52xx_psc_buffer_8, port->x_char); + psc_ops->write_char(port, port->x_char); port->icount.tx++; port->x_char = 0; return 1; @@ -533,8 +808,8 @@ mpc52xx_uart_int_tx_chars(struct uart_port *port) } /* Send chars */ - while (in_be16(&PSC(port)->mpc52xx_psc_status) & MPC52xx_PSC_SR_TXRDY) { - out_8(&PSC(port)->mpc52xx_psc_buffer_8, xmit->buf[xmit->tail]); + while (psc_ops->raw_tx_rdy(port)) { + psc_ops->write_char(port, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; if (uart_circ_empty(xmit)) @@ -560,7 +835,6 @@ mpc52xx_uart_int(int irq, void *dev_id) struct uart_port *port = dev_id; unsigned long pass = ISR_PASS_LIMIT; unsigned int keepgoing; - unsigned short status; spin_lock(&port->lock); @@ -569,18 +843,12 @@ mpc52xx_uart_int(int irq, void *dev_id) /* If we don't find anything to do, we stop */ keepgoing = 0; - /* Read status */ - status = in_be16(&PSC(port)->mpc52xx_psc_isr); - status &= port->read_status_mask; - - /* Do we need to receive chars ? */ - /* For this RX interrupts must be on and some chars waiting */ - if (status & MPC52xx_PSC_IMR_RXRDY) + psc_ops->rx_clr_irq(port); + if (psc_ops->rx_rdy(port)) keepgoing |= mpc52xx_uart_int_rx_chars(port); - /* Do we need to send chars ? */ - /* For this, TX must be ready and TX interrupt enabled */ - if (status & MPC52xx_PSC_IMR_TXRDY) + psc_ops->tx_clr_irq(port); + if (psc_ops->tx_rdy(port)) keepgoing |= mpc52xx_uart_int_tx_chars(port); /* Limit number of iteration */ @@ -647,36 +915,33 @@ static void mpc52xx_console_write(struct console *co, const char *s, unsigned int count) { struct uart_port *port = &mpc52xx_uart_ports[co->index]; - struct mpc52xx_psc __iomem *psc = PSC(port); unsigned int i, j; /* Disable interrupts */ - out_be16(&psc->mpc52xx_psc_imr, 0); + psc_ops->cw_disable_ints(port); /* Wait the TX buffer to be empty */ j = 5000000; /* Maximum wait */ - while (!(in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_TXEMP) && - --j) + while (!mpc52xx_uart_tx_empty(port) && --j) udelay(1); /* Write all the chars */ for (i = 0; i < count; i++, s++) { /* Line return handling */ if (*s == '\n') - out_8(&psc->mpc52xx_psc_buffer_8, '\r'); + psc_ops->write_char(port, '\r'); /* Send the char */ - out_8(&psc->mpc52xx_psc_buffer_8, *s); + psc_ops->write_char(port, *s); /* Wait the TX buffer to be empty */ j = 20000; /* Maximum wait */ - while (!(in_be16(&psc->mpc52xx_psc_status) & - MPC52xx_PSC_SR_TXEMP) && --j) + while (!mpc52xx_uart_tx_empty(port) && --j) udelay(1); } /* Restore interrupt state */ - out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); + psc_ops->cw_restore_ints(port); } #if !defined(CONFIG_PPC_MERGE) @@ -721,7 +986,7 @@ mpc52xx_console_setup(struct console *co, char *options) { struct uart_port *port = &mpc52xx_uart_ports[co->index]; struct device_node *np = mpc52xx_uart_nodes[co->index]; - unsigned int ipb_freq; + unsigned int uartclk; struct resource res; int ret; @@ -753,17 +1018,16 @@ mpc52xx_console_setup(struct console *co, char *options) return ret; } - /* Search for bus-frequency property in this node or a parent */ - ipb_freq = mpc52xx_find_ipb_freq(np); - if (ipb_freq == 0) { - pr_debug("Could not find IPB bus frequency!\n"); + uartclk = psc_ops->getuartclk(np); + if (uartclk == 0) { + pr_debug("Could not find uart clock frequency!\n"); return -EINVAL; } /* Basic port init. Needed since we use some uart_??? func before * real init for early access */ spin_lock_init(&port->lock); - port->uartclk = ipb_freq / 2; + port->uartclk = uartclk; port->ops = &mpc52xx_uart_ops; port->mapbase = res.start; port->membase = ioremap(res.start, sizeof(struct mpc52xx_psc)); @@ -945,11 +1209,25 @@ static struct platform_driver mpc52xx_uart_platform_driver = { /* OF Platform Driver */ /* ======================================================================== */ +static struct of_device_id mpc52xx_uart_of_match[] = { +#ifdef CONFIG_PPC_MPC52xx + { .compatible = "fsl,mpc5200-psc-uart", .data = &mpc52xx_psc_ops, }, + /* binding used by old lite5200 device trees: */ + { .compatible = "mpc5200-psc-uart", .data = &mpc52xx_psc_ops, }, + /* binding used by efika: */ + { .compatible = "mpc5200-serial", .data = &mpc52xx_psc_ops, }, +#endif +#ifdef CONFIG_PPC_MPC512x + { .compatible = "fsl,mpc5121-psc-uart", .data = &mpc512x_psc_ops, }, + {}, +#endif +}; + static int __devinit mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match) { int idx = -1; - unsigned int ipb_freq; + unsigned int uartclk; struct uart_port *port = NULL; struct resource res; int ret; @@ -965,10 +1243,9 @@ mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match) pr_debug("Found %s assigned to ttyPSC%x\n", mpc52xx_uart_nodes[idx]->full_name, idx); - /* Search for bus-frequency property in this node or a parent */ - ipb_freq = mpc52xx_find_ipb_freq(op->node); - if (ipb_freq == 0) { - dev_dbg(&op->dev, "Could not find IPB bus frequency!\n"); + uartclk = psc_ops->getuartclk(op->node); + if (uartclk == 0) { + dev_dbg(&op->dev, "Could not find uart clock frequency!\n"); return -EINVAL; } @@ -976,7 +1253,7 @@ mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match) port = &mpc52xx_uart_ports[idx]; spin_lock_init(&port->lock); - port->uartclk = ipb_freq / 2; + port->uartclk = uartclk; port->fifosize = 512; port->iotype = UPIO_MEM; port->flags = UPF_BOOT_AUTOCONF | @@ -1080,15 +1357,19 @@ mpc52xx_uart_of_enumerate(void) static int enum_done; struct device_node *np; const unsigned int *devno; + const struct of_device_id *match; int i; if (enum_done) return; for_each_node_by_type(np, "serial") { - if (!of_match_node(mpc52xx_uart_of_match, np)) + match = of_match_node(mpc52xx_uart_of_match, np); + if (!match) continue; + psc_ops = match->data; + /* Is a particular device number requested? */ devno = of_get_property(np, "port-number", NULL); mpc52xx_uart_of_assign(np, devno ? *devno : -1); @@ -1149,6 +1430,7 @@ mpc52xx_uart_init(void) return ret; } #else + psc_ops = &mpc52xx_psc_ops; ret = platform_driver_register(&mpc52xx_uart_platform_driver); if (ret) { printk(KERN_ERR "%s: platform_driver_register failed (%i)\n", diff --git a/drivers/serial/uartlite.c b/drivers/serial/uartlite.c index bacf68dca01a..4e06ab6bcb6e 100644 --- a/drivers/serial/uartlite.c +++ b/drivers/serial/uartlite.c @@ -17,10 +17,21 @@ #include <linux/tty.h> #include <linux/delay.h> #include <linux/interrupt.h> +#include <linux/init.h> #include <asm/io.h> #if defined(CONFIG_OF) +#include <linux/of.h> #include <linux/of_device.h> #include <linux/of_platform.h> + +/* Match table for of_platform binding */ +static struct of_device_id ulite_of_match[] __devinitdata = { + { .compatible = "xlnx,opb-uartlite-1.00.b", }, + { .compatible = "xlnx,xps-uartlite-1.00.a", }, + {} +}; +MODULE_DEVICE_TABLE(of, ulite_of_match); + #endif #define ULITE_NAME "ttyUL" @@ -275,6 +286,9 @@ static void ulite_release_port(struct uart_port *port) static int ulite_request_port(struct uart_port *port) { + pr_debug("ulite console: port=%p; port->mapbase=%x\n", + port, port->mapbase); + if (!request_mem_region(port->mapbase, ULITE_REGION, "uartlite")) { dev_err(port->dev, "Memory region busy\n"); return -EBUSY; @@ -375,32 +389,6 @@ static void ulite_console_write(struct console *co, const char *s, spin_unlock_irqrestore(&port->lock, flags); } -#if defined(CONFIG_OF) -static inline void __init ulite_console_of_find_device(int id) -{ - struct device_node *np; - struct resource res; - const unsigned int *of_id; - int rc; - - for_each_compatible_node(np, NULL, "xilinx,uartlite") { - of_id = of_get_property(np, "port-number", NULL); - if ((!of_id) || (*of_id != id)) - continue; - - rc = of_address_to_resource(np, 0, &res); - if (rc) - continue; - - ulite_ports[id].mapbase = res.start; - of_node_put(np); - return; - } -} -#else /* CONFIG_OF */ -static inline void __init ulite_console_of_find_device(int id) { /* do nothing */ } -#endif /* CONFIG_OF */ - static int __init ulite_console_setup(struct console *co, char *options) { struct uart_port *port; @@ -414,11 +402,7 @@ static int __init ulite_console_setup(struct console *co, char *options) port = &ulite_ports[co->index]; - /* Check if it is an OF device */ - if (!port->mapbase) - ulite_console_of_find_device(co->index); - - /* Do we have a device now? */ + /* Has the device been initialized yet? */ if (!port->mapbase) { pr_debug("console on ttyUL%i not present\n", co->index); return -ENODEV; @@ -617,13 +601,6 @@ static int __devexit ulite_of_remove(struct of_device *op) return ulite_release(&op->dev); } -/* Match table for of_platform binding */ -static struct of_device_id __devinit ulite_of_match[] = { - { .type = "serial", .compatible = "xilinx,uartlite", }, - {}, -}; -MODULE_DEVICE_TABLE(of, ulite_of_match); - static struct of_platform_driver ulite_of_driver = { .owner = THIS_MODULE, .name = "uartlite", diff --git a/drivers/video/xilinxfb.c b/drivers/video/xilinxfb.c index e38d3b7c3ad7..7b3a8423f485 100644 --- a/drivers/video/xilinxfb.c +++ b/drivers/video/xilinxfb.c @@ -459,8 +459,8 @@ static int __devexit xilinxfb_of_remove(struct of_device *op) } /* Match table for of_platform binding */ -static struct of_device_id __devinit xilinxfb_of_match[] = { - { .compatible = "xilinx,ml300-fb", }, +static struct of_device_id xilinxfb_of_match[] __devinitdata = { + { .compatible = "xlnx,plb-tft-cntlr-ref-1.00.a", }, {}, }; MODULE_DEVICE_TABLE(of, xilinxfb_of_match); diff --git a/drivers/w1/masters/ds1wm.c b/drivers/w1/masters/ds1wm.c index 5747997f8d7d..688e435b4d9a 100644 --- a/drivers/w1/masters/ds1wm.c +++ b/drivers/w1/masters/ds1wm.c @@ -361,11 +361,12 @@ static int ds1wm_probe(struct platform_device *pdev) goto err1; } ds1wm_data->irq = res->start; - ds1wm_data->active_high = (res->flags & IORESOURCE_IRQ_HIGHEDGE) ? - 1 : 0; + ds1wm_data->active_high = plat->active_high; - set_irq_type(ds1wm_data->irq, ds1wm_data->active_high ? - IRQ_TYPE_EDGE_RISING : IRQ_TYPE_EDGE_FALLING); + if (res->flags & IORESOURCE_IRQ_HIGHEDGE) + set_irq_type(ds1wm_data->irq, IRQ_TYPE_EDGE_RISING); + if (res->flags & IORESOURCE_IRQ_LOWEDGE) + set_irq_type(ds1wm_data->irq, IRQ_TYPE_EDGE_FALLING); ret = request_irq(ds1wm_data->irq, ds1wm_isr, IRQF_DISABLED, "ds1wm", ds1wm_data); diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 5c5137c11484..6a28842052ea 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -573,7 +573,7 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, v9ses = v9fs_inode2v9ses(dir); dfid = v9fs_fid_lookup(dentry->d_parent); if (IS_ERR(dfid)) - return ERR_PTR(PTR_ERR(dfid)); + return ERR_CAST(dfid); name = (char *) dentry->d_name.name; fid = p9_client_walk(dfid, 1, &name, 1); diff --git a/fs/Kconfig b/fs/Kconfig index ea5b35947623..3bf6ace1720c 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -463,40 +463,18 @@ config OCFS2_DEBUG_FS this option for debugging only as it is likely to decrease performance of the filesystem. -config MINIX_FS - tristate "Minix fs support" - help - Minix is a simple operating system used in many classes about OS's. - The minix file system (method to organize files on a hard disk - partition or a floppy disk) was the original file system for Linux, - but has been superseded by the second extended file system ext2fs. - You don't want to use the minix file system on your hard disk - because of certain built-in restrictions, but it is sometimes found - on older Linux floppy disks. This option will enlarge your kernel - by about 28 KB. If unsure, say N. - - To compile this file system support as a module, choose M here: the - module will be called minix. Note that the file system of your root - partition (the one containing the directory /) cannot be compiled as - a module. - -config ROMFS_FS - tristate "ROM file system support" - ---help--- - This is a very small read-only file system mainly intended for - initial ram disks of installation disks, but it could be used for - other read-only media as well. Read - <file:Documentation/filesystems/romfs.txt> for details. - - To compile this file system support as a module, choose M here: the - module will be called romfs. Note that the file system of your - root partition (the one containing the directory /) cannot be a - module. +endif # BLOCK - If you don't know whether you need it, then you don't need it: - answer N. +config DNOTIFY + bool "Dnotify support" + default y + help + Dnotify is a directory-based per-fd file change notification system + that uses signals to communicate events to user-space. There exist + superior alternatives, but some applications may still rely on + dnotify. -endif + If unsure, say Y. config INOTIFY bool "Inotify file change notification support" @@ -577,17 +555,6 @@ config QUOTACTL depends on XFS_QUOTA || QUOTA default y -config DNOTIFY - bool "Dnotify support" - default y - help - Dnotify is a directory-based per-fd file change notification system - that uses signals to communicate events to user-space. There exist - superior alternatives, but some applications may still rely on - dnotify. - - If unsure, say Y. - config AUTOFS_FS tristate "Kernel automounter support" help @@ -713,7 +680,7 @@ config UDF_NLS depends on (UDF_FS=m && NLS) || (UDF_FS=y && NLS=y) endmenu -endif +endif # BLOCK if BLOCK menu "DOS/FAT/NT Filesystems" @@ -896,7 +863,7 @@ config NTFS_RW It is perfectly safe to say N here. endmenu -endif +endif # BLOCK menu "Pseudo filesystems" @@ -1417,6 +1384,24 @@ config VXFS_FS To compile this as a module, choose M here: the module will be called freevxfs. If unsure, say N. +config MINIX_FS + tristate "Minix file system support" + depends on BLOCK + help + Minix is a simple operating system used in many classes about OS's. + The minix file system (method to organize files on a hard disk + partition or a floppy disk) was the original file system for Linux, + but has been superseded by the second extended file system ext2fs. + You don't want to use the minix file system on your hard disk + because of certain built-in restrictions, but it is sometimes found + on older Linux floppy disks. This option will enlarge your kernel + by about 28 KB. If unsure, say N. + + To compile this file system support as a module, choose M here: the + module will be called minix. Note that the file system of your root + partition (the one containing the directory /) cannot be compiled as + a module. + config HPFS_FS tristate "OS/2 HPFS file system support" @@ -1434,7 +1419,6 @@ config HPFS_FS module will be called hpfs. If unsure, say N. - config QNX4FS_FS tristate "QNX4 file system support (read only)" depends on BLOCK @@ -1461,6 +1445,22 @@ config QNX4FS_RW It's currently broken, so for now: answer N. +config ROMFS_FS + tristate "ROM file system support" + depends on BLOCK + ---help--- + This is a very small read-only file system mainly intended for + initial ram disks of installation disks, but it could be used for + other read-only media as well. Read + <file:Documentation/filesystems/romfs.txt> for details. + + To compile this file system support as a module, choose M here: the + module will be called romfs. Note that the file system of your + root partition (the one containing the directory /) cannot be a + module. + + If you don't know whether you need it, then you don't need it: + answer N. config SYSV_FS @@ -1501,7 +1501,6 @@ config SYSV_FS If you haven't heard about all of this before, it's safe to say N. - config UFS_FS tristate "UFS file system support (read only)" depends on BLOCK diff --git a/fs/affs/affs.h b/fs/affs/affs.h index 232c69493683..d5bd497ab9cb 100644 --- a/fs/affs/affs.h +++ b/fs/affs/affs.h @@ -174,7 +174,8 @@ extern void affs_put_inode(struct inode *inode); extern void affs_drop_inode(struct inode *inode); extern void affs_delete_inode(struct inode *inode); extern void affs_clear_inode(struct inode *inode); -extern void affs_read_inode(struct inode *inode); +extern struct inode *affs_iget(struct super_block *sb, + unsigned long ino); extern int affs_write_inode(struct inode *inode, int); extern int affs_add_entry(struct inode *dir, struct inode *inode, struct dentry *dentry, s32 type); diff --git a/fs/affs/amigaffs.c b/fs/affs/amigaffs.c index f4de4b98004f..805573005de6 100644 --- a/fs/affs/amigaffs.c +++ b/fs/affs/amigaffs.c @@ -170,9 +170,11 @@ affs_remove_link(struct dentry *dentry) if (!link_bh) goto done; - dir = iget(sb, be32_to_cpu(AFFS_TAIL(sb, link_bh)->parent)); - if (!dir) + dir = affs_iget(sb, be32_to_cpu(AFFS_TAIL(sb, link_bh)->parent)); + if (IS_ERR(dir)) { + retval = PTR_ERR(dir); goto done; + } affs_lock_dir(dir); affs_fix_dcache(dentry, link_ino); diff --git a/fs/affs/inode.c b/fs/affs/inode.c index 4609a6c13fe9..27fe6cbe43ae 100644 --- a/fs/affs/inode.c +++ b/fs/affs/inode.c @@ -15,20 +15,25 @@ extern const struct inode_operations affs_symlink_inode_operations; extern struct timezone sys_tz; -void -affs_read_inode(struct inode *inode) +struct inode *affs_iget(struct super_block *sb, unsigned long ino) { - struct super_block *sb = inode->i_sb; struct affs_sb_info *sbi = AFFS_SB(sb); struct buffer_head *bh; struct affs_head *head; struct affs_tail *tail; + struct inode *inode; u32 block; u32 size; u32 prot; u16 id; - pr_debug("AFFS: read_inode(%lu)\n",inode->i_ino); + inode = iget_locked(sb, ino); + if (!inode) + return ERR_PTR(-ENOMEM); + if (!(inode->i_state & I_NEW)) + return inode; + + pr_debug("AFFS: affs_iget(%lu)\n", inode->i_ino); block = inode->i_ino; bh = affs_bread(sb, block); @@ -154,12 +159,13 @@ affs_read_inode(struct inode *inode) sys_tz.tz_minuteswest * 60; inode->i_mtime.tv_nsec = inode->i_ctime.tv_nsec = inode->i_atime.tv_nsec = 0; affs_brelse(bh); - return; + unlock_new_inode(inode); + return inode; bad_inode: - make_bad_inode(inode); affs_brelse(bh); - return; + iget_failed(inode); + return ERR_PTR(-EIO); } int diff --git a/fs/affs/namei.c b/fs/affs/namei.c index a42143ca0169..2218f1ee71ce 100644 --- a/fs/affs/namei.c +++ b/fs/affs/namei.c @@ -208,9 +208,8 @@ affs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) affs_lock_dir(dir); bh = affs_find_entry(dir, dentry); affs_unlock_dir(dir); - if (IS_ERR(bh)) { - return ERR_PTR(PTR_ERR(bh)); - } + if (IS_ERR(bh)) + return ERR_CAST(bh); if (bh) { u32 ino = bh->b_blocknr; @@ -223,10 +222,9 @@ affs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) ino = be32_to_cpu(AFFS_TAIL(sb, bh)->original); } affs_brelse(bh); - inode = iget(sb, ino); - if (!inode) { - return ERR_PTR(-EACCES); - } + inode = affs_iget(sb, ino); + if (IS_ERR(inode)) + return ERR_PTR(PTR_ERR(inode)); } dentry->d_op = AFFS_SB(sb)->s_flags & SF_INTL ? &affs_intl_dentry_operations : &affs_dentry_operations; d_add(dentry, inode); diff --git a/fs/affs/super.c b/fs/affs/super.c index b53e5d0ec65c..3c45d49c0d26 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -113,7 +113,6 @@ static void destroy_inodecache(void) static const struct super_operations affs_sops = { .alloc_inode = affs_alloc_inode, .destroy_inode = affs_destroy_inode, - .read_inode = affs_read_inode, .write_inode = affs_write_inode, .put_inode = affs_put_inode, .drop_inode = affs_drop_inode, @@ -271,6 +270,7 @@ static int affs_fill_super(struct super_block *sb, void *data, int silent) unsigned long mount_flags; int tmp_flags; /* fix remount prototype... */ u8 sig[4]; + int ret = -EINVAL; pr_debug("AFFS: read_super(%s)\n",data ? (const char *)data : "no options"); @@ -444,7 +444,12 @@ got_root: /* set up enough so that it can read an inode */ - root_inode = iget(sb, root_block); + root_inode = affs_iget(sb, root_block); + if (IS_ERR(root_inode)) { + ret = PTR_ERR(root_inode); + goto out_error_noinode; + } + sb->s_root = d_alloc_root(root_inode); if (!sb->s_root) { printk(KERN_ERR "AFFS: Get root inode failed\n"); @@ -461,12 +466,13 @@ got_root: out_error: if (root_inode) iput(root_inode); +out_error_noinode: kfree(sbi->s_bitmap); affs_brelse(root_bh); kfree(sbi->s_prefix); kfree(sbi); sb->s_fs_info = NULL; - return -EINVAL; + return ret; } static int diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 0cc3597c1197..b58af8f18bc4 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -512,7 +512,7 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, key = afs_request_key(vnode->volume->cell); if (IS_ERR(key)) { _leave(" = %ld [key]", PTR_ERR(key)); - return ERR_PTR(PTR_ERR(key)); + return ERR_CAST(key); } ret = afs_validate(vnode, key); @@ -540,7 +540,7 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, key_put(key); if (IS_ERR(inode)) { _leave(" = %ld", PTR_ERR(inode)); - return ERR_PTR(PTR_ERR(inode)); + return ERR_CAST(inode); } dentry->d_op = &afs_fs_dentry_operations; diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 84750c8e9f95..08db82e1343a 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -196,10 +196,7 @@ struct inode *afs_iget(struct super_block *sb, struct key *key, /* failure */ bad_inode: - make_bad_inode(inode); - unlock_new_inode(inode); - iput(inode); - + iget_failed(inode); _leave(" = %d [bad]", ret); return ERR_PTR(ret); } diff --git a/fs/afs/security.c b/fs/afs/security.c index 566fe712c682..9446a1fd108a 100644 --- a/fs/afs/security.c +++ b/fs/afs/security.c @@ -95,7 +95,7 @@ static struct afs_vnode *afs_get_auth_inode(struct afs_vnode *vnode, auth_inode = afs_iget(vnode->vfs_inode.i_sb, key, &vnode->status.parent, NULL, NULL); if (IS_ERR(auth_inode)) - return ERR_PTR(PTR_ERR(auth_inode)); + return ERR_CAST(auth_inode); } auth_vnode = AFS_FS_I(auth_inode); diff --git a/fs/autofs/autofs_i.h b/fs/autofs/autofs_i.h index 8b4cca3c4705..901a3e67ec45 100644 --- a/fs/autofs/autofs_i.h +++ b/fs/autofs/autofs_i.h @@ -150,6 +150,7 @@ extern const struct file_operations autofs_root_operations; int autofs_fill_super(struct super_block *, void *, int); void autofs_kill_sb(struct super_block *sb); +struct inode *autofs_iget(struct super_block *, unsigned long); /* Queue management functions */ diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c index 45f5992a0957..708bdb89fea1 100644 --- a/fs/autofs/inode.c +++ b/fs/autofs/inode.c @@ -52,10 +52,7 @@ out_kill_sb: kill_anon_super(sb); } -static void autofs_read_inode(struct inode *inode); - static const struct super_operations autofs_sops = { - .read_inode = autofs_read_inode, .statfs = simple_statfs, }; @@ -164,7 +161,9 @@ int autofs_fill_super(struct super_block *s, void *data, int silent) s->s_time_gran = 1; sbi->sb = s; - root_inode = iget(s, AUTOFS_ROOT_INO); + root_inode = autofs_iget(s, AUTOFS_ROOT_INO); + if (IS_ERR(root_inode)) + goto fail_free; root = d_alloc_root(root_inode); pipe = NULL; @@ -230,11 +229,17 @@ fail_unlock: return -EINVAL; } -static void autofs_read_inode(struct inode *inode) +struct inode *autofs_iget(struct super_block *sb, unsigned long ino) { - ino_t ino = inode->i_ino; unsigned int n; - struct autofs_sb_info *sbi = autofs_sbi(inode->i_sb); + struct autofs_sb_info *sbi = autofs_sbi(sb); + struct inode *inode; + + inode = iget_locked(sb, ino); + if (!inode) + return ERR_PTR(-ENOMEM); + if (!(inode->i_state & I_NEW)) + return inode; /* Initialize to the default case (stub directory) */ @@ -250,7 +255,7 @@ static void autofs_read_inode(struct inode *inode) inode->i_op = &autofs_root_inode_operations; inode->i_fop = &autofs_root_operations; inode->i_uid = inode->i_gid = 0; /* Changed in read_super */ - return; + goto done; } inode->i_uid = inode->i_sb->s_root->d_inode->i_uid; @@ -263,7 +268,7 @@ static void autofs_read_inode(struct inode *inode) n = ino - AUTOFS_FIRST_SYMLINK; if (n >= AUTOFS_MAX_SYMLINKS || !test_bit(n,sbi->symlink_bitmap)) { printk("autofs: Looking for bad symlink inode %u\n", (unsigned int) ino); - return; + goto done; } inode->i_op = &autofs_symlink_inode_operations; @@ -275,4 +280,8 @@ static void autofs_read_inode(struct inode *inode) inode->i_size = sl->len; inode->i_nlink = 1; } + +done: + unlock_new_inode(inode); + return inode; } diff --git a/fs/autofs/root.c b/fs/autofs/root.c index 5efff3c0d886..8aacade56956 100644 --- a/fs/autofs/root.c +++ b/fs/autofs/root.c @@ -114,8 +114,8 @@ static int try_to_fill_dentry(struct dentry *dentry, struct super_block *sb, str dentry->d_time = (unsigned long) ent; if (!dentry->d_inode) { - inode = iget(sb, ent->ino); - if (!inode) { + inode = autofs_iget(sb, ent->ino); + if (IS_ERR(inode)) { /* Failed, but leave pending for next time */ return 1; } @@ -274,6 +274,7 @@ static int autofs_root_symlink(struct inode *dir, struct dentry *dentry, const c unsigned int n; int slsize; struct autofs_symlink *sl; + struct inode *inode; DPRINTK(("autofs_root_symlink: %s <- ", symname)); autofs_say(dentry->d_name.name,dentry->d_name.len); @@ -331,7 +332,12 @@ static int autofs_root_symlink(struct inode *dir, struct dentry *dentry, const c ent->dentry = NULL; /* We don't keep the dentry for symlinks */ autofs_hash_insert(dh,ent); - d_instantiate(dentry, iget(dir->i_sb,ent->ino)); + + inode = autofs_iget(dir->i_sb, ent->ino); + if (IS_ERR(inode)) + return PTR_ERR(inode); + + d_instantiate(dentry, inode); unlock_kernel(); return 0; } @@ -428,6 +434,7 @@ static int autofs_root_mkdir(struct inode *dir, struct dentry *dentry, int mode) struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb); struct autofs_dirhash *dh = &sbi->dirhash; struct autofs_dir_ent *ent; + struct inode *inode; ino_t ino; lock_kernel(); @@ -469,7 +476,14 @@ static int autofs_root_mkdir(struct inode *dir, struct dentry *dentry, int mode) autofs_hash_insert(dh,ent); inc_nlink(dir); - d_instantiate(dentry, iget(dir->i_sb,ino)); + + inode = autofs_iget(dir->i_sb, ino); + if (IS_ERR(inode)) { + drop_nlink(dir); + return PTR_ERR(inode); + } + + d_instantiate(dentry, inode); unlock_kernel(); return 0; diff --git a/fs/bad_inode.c b/fs/bad_inode.c index 521ff7caadbd..f1c2ea8342f5 100644 --- a/fs/bad_inode.c +++ b/fs/bad_inode.c @@ -359,3 +359,17 @@ int is_bad_inode(struct inode *inode) } EXPORT_SYMBOL(is_bad_inode); + +/** + * iget_failed - Mark an under-construction inode as dead and release it + * @inode: The inode to discard + * + * Mark an under-construction inode as dead and release it. + */ +void iget_failed(struct inode *inode) +{ + make_bad_inode(inode); + unlock_new_inode(inode); + iput(inode); +} +EXPORT_SYMBOL(iget_failed); diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c index b28a20e61b80..403fe661c144 100644 --- a/fs/befs/linuxvfs.c +++ b/fs/befs/linuxvfs.c @@ -35,7 +35,7 @@ static int befs_get_block(struct inode *, sector_t, struct buffer_head *, int); static int befs_readpage(struct file *file, struct page *page); static sector_t befs_bmap(struct address_space *mapping, sector_t block); static struct dentry *befs_lookup(struct inode *, struct dentry *, struct nameidata *); -static void befs_read_inode(struct inode *ino); +static struct inode *befs_iget(struct super_block *, unsigned long); static struct inode *befs_alloc_inode(struct super_block *sb); static void befs_destroy_inode(struct inode *inode); static int befs_init_inodecache(void); @@ -52,7 +52,6 @@ static int befs_statfs(struct dentry *, struct kstatfs *); static int parse_options(char *, befs_mount_options *); static const struct super_operations befs_sops = { - .read_inode = befs_read_inode, /* initialize & read inode */ .alloc_inode = befs_alloc_inode, /* allocate a new inode */ .destroy_inode = befs_destroy_inode, /* deallocate an inode */ .put_super = befs_put_super, /* uninit super */ @@ -198,9 +197,9 @@ befs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) return ERR_PTR(-ENODATA); } - inode = iget(dir->i_sb, (ino_t) offset); - if (!inode) - return ERR_PTR(-EACCES); + inode = befs_iget(dir->i_sb, (ino_t) offset); + if (IS_ERR(inode)) + return ERR_CAST(inode); d_add(dentry, inode); @@ -296,17 +295,23 @@ static void init_once(struct kmem_cache *cachep, void *foo) inode_init_once(&bi->vfs_inode); } -static void -befs_read_inode(struct inode *inode) +static struct inode *befs_iget(struct super_block *sb, unsigned long ino) { struct buffer_head *bh = NULL; befs_inode *raw_inode = NULL; - struct super_block *sb = inode->i_sb; befs_sb_info *befs_sb = BEFS_SB(sb); befs_inode_info *befs_ino = NULL; + struct inode *inode; + long ret = -EIO; - befs_debug(sb, "---> befs_read_inode() " "inode = %lu", inode->i_ino); + befs_debug(sb, "---> befs_read_inode() " "inode = %lu", ino); + + inode = iget_locked(sb, ino); + if (IS_ERR(inode)) + return inode; + if (!(inode->i_state & I_NEW)) + return inode; befs_ino = BEFS_I(inode); @@ -402,15 +407,16 @@ befs_read_inode(struct inode *inode) brelse(bh); befs_debug(sb, "<--- befs_read_inode()"); - return; + unlock_new_inode(inode); + return inode; unacquire_bh: brelse(bh); unacquire_none: - make_bad_inode(inode); + iget_failed(inode); befs_debug(sb, "<--- befs_read_inode() - Bad inode"); - return; + return ERR_PTR(ret); } /* Initialize the inode cache. Called at fs setup. @@ -752,6 +758,7 @@ befs_fill_super(struct super_block *sb, void *data, int silent) befs_sb_info *befs_sb; befs_super_block *disk_sb; struct inode *root; + long ret = -EINVAL; const unsigned long sb_block = 0; const off_t x86_sb_off = 512; @@ -833,7 +840,11 @@ befs_fill_super(struct super_block *sb, void *data, int silent) /* Set real blocksize of fs */ sb_set_blocksize(sb, (ulong) befs_sb->block_size); sb->s_op = (struct super_operations *) &befs_sops; - root = iget(sb, iaddr2blockno(sb, &(befs_sb->root_dir))); + root = befs_iget(sb, iaddr2blockno(sb, &(befs_sb->root_dir))); + if (IS_ERR(root)) { + ret = PTR_ERR(root); + goto unacquire_priv_sbp; + } sb->s_root = d_alloc_root(root); if (!sb->s_root) { iput(root); @@ -868,7 +879,7 @@ befs_fill_super(struct super_block *sb, void *data, int silent) unacquire_none: sb->s_fs_info = NULL; - return -EINVAL; + return ret; } static int diff --git a/fs/bfs/bfs.h b/fs/bfs/bfs.h index ac7a8b1d6c3a..71faf4d23908 100644 --- a/fs/bfs/bfs.h +++ b/fs/bfs/bfs.h @@ -44,6 +44,8 @@ static inline struct bfs_inode_info *BFS_I(struct inode *inode) #define printf(format, args...) \ printk(KERN_ERR "BFS-fs: %s(): " format, __FUNCTION__, ## args) +/* inode.c */ +extern struct inode *bfs_iget(struct super_block *sb, unsigned long ino); /* file.c */ extern const struct inode_operations bfs_file_inops; diff --git a/fs/bfs/dir.c b/fs/bfs/dir.c index 1fd056d0fc3d..034950cb3cbe 100644 --- a/fs/bfs/dir.c +++ b/fs/bfs/dir.c @@ -148,10 +148,10 @@ static struct dentry *bfs_lookup(struct inode *dir, struct dentry *dentry, if (bh) { unsigned long ino = (unsigned long)le16_to_cpu(de->ino); brelse(bh); - inode = iget(dir->i_sb, ino); - if (!inode) { + inode = bfs_iget(dir->i_sb, ino); + if (IS_ERR(inode)) { unlock_kernel(); - return ERR_PTR(-EACCES); + return ERR_CAST(inode); } } unlock_kernel(); diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c index a64a71d444f5..8db623838b50 100644 --- a/fs/bfs/inode.c +++ b/fs/bfs/inode.c @@ -32,17 +32,22 @@ MODULE_LICENSE("GPL"); void dump_imap(const char *prefix, struct super_block *s); -static void bfs_read_inode(struct inode *inode) +struct inode *bfs_iget(struct super_block *sb, unsigned long ino) { - unsigned long ino = inode->i_ino; struct bfs_inode *di; + struct inode *inode; struct buffer_head *bh; int block, off; + inode = iget_locked(sb, ino); + if (IS_ERR(inode)) + return ERR_PTR(-ENOMEM); + if (!(inode->i_state & I_NEW)) + return inode; + if ((ino < BFS_ROOT_INO) || (ino > BFS_SB(inode->i_sb)->si_lasti)) { printf("Bad inode number %s:%08lx\n", inode->i_sb->s_id, ino); - make_bad_inode(inode); - return; + goto error; } block = (ino - BFS_ROOT_INO) / BFS_INODES_PER_BLOCK + 1; @@ -50,8 +55,7 @@ static void bfs_read_inode(struct inode *inode) if (!bh) { printf("Unable to read inode %s:%08lx\n", inode->i_sb->s_id, ino); - make_bad_inode(inode); - return; + goto error; } off = (ino - BFS_ROOT_INO) % BFS_INODES_PER_BLOCK; @@ -85,6 +89,12 @@ static void bfs_read_inode(struct inode *inode) inode->i_ctime.tv_nsec = 0; brelse(bh); + unlock_new_inode(inode); + return inode; + +error: + iget_failed(inode); + return ERR_PTR(-EIO); } static int bfs_write_inode(struct inode *inode, int unused) @@ -276,7 +286,6 @@ static void destroy_inodecache(void) static const struct super_operations bfs_sops = { .alloc_inode = bfs_alloc_inode, .destroy_inode = bfs_destroy_inode, - .read_inode = bfs_read_inode, .write_inode = bfs_write_inode, .delete_inode = bfs_delete_inode, .put_super = bfs_put_super, @@ -312,6 +321,7 @@ static int bfs_fill_super(struct super_block *s, void *data, int silent) struct inode *inode; unsigned i, imap_len; struct bfs_sb_info *info; + long ret = -EINVAL; info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) @@ -346,14 +356,16 @@ static int bfs_fill_super(struct super_block *s, void *data, int silent) set_bit(i, info->si_imap); s->s_op = &bfs_sops; - inode = iget(s, BFS_ROOT_INO); - if (!inode) { + inode = bfs_iget(s, BFS_ROOT_INO); + if (IS_ERR(inode)) { + ret = PTR_ERR(inode); kfree(info->si_imap); goto out; } s->s_root = d_alloc_root(inode); if (!s->s_root) { iput(inode); + ret = -ENOMEM; kfree(info->si_imap); goto out; } @@ -404,7 +416,7 @@ out: brelse(bh); kfree(info); s->s_fs_info = NULL; - return -EINVAL; + return ret; } static int bfs_get_sb(struct file_system_type *fs_type, diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c index 7596e1e94cde..7f65e71bf859 100644 --- a/fs/binfmt_aout.c +++ b/fs/binfmt_aout.c @@ -115,7 +115,7 @@ static int aout_core_dump(long signr, struct pt_regs *regs, struct file *file, u current->flags |= PF_DUMPCORE; strncpy(dump.u_comm, current->comm, sizeof(dump.u_comm)); #ifndef __sparc__ - dump.u_ar0 = (void *)(((unsigned long)(&dump.regs)) - ((unsigned long)(&dump))); + dump.u_ar0 = offsetof(struct user, regs); #endif dump.signal = signr; dump_thread(regs, &dump); diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index e9f4ec701092..fcc434227691 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -147,10 +147,11 @@ cifs_read_super(struct super_block *sb, void *data, #endif sb->s_blocksize = CIFS_MAX_MSGSIZE; sb->s_blocksize_bits = 14; /* default 2**14 = CIFS_MAX_MSGSIZE */ - inode = iget(sb, ROOT_I); + inode = cifs_iget(sb, ROOT_I); - if (!inode) { - rc = -ENOMEM; + if (IS_ERR(inode)) { + rc = PTR_ERR(inode); + inode = NULL; goto out_no_root; } @@ -520,7 +521,6 @@ static int cifs_remount(struct super_block *sb, int *flags, char *data) } static const struct super_operations cifs_super_ops = { - .read_inode = cifs_read_inode, .put_super = cifs_put_super, .statfs = cifs_statfs, .alloc_inode = cifs_alloc_inode, diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 195b14de5567..68978306c3ca 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -44,6 +44,7 @@ extern void cifs_read_inode(struct inode *); /* Functions related to inodes */ extern const struct inode_operations cifs_dir_inode_ops; +extern struct inode *cifs_iget(struct super_block *, unsigned long); extern int cifs_create(struct inode *, struct dentry *, int, struct nameidata *); extern struct dentry *cifs_lookup(struct inode *, struct dentry *, diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 47f2621001e4..b1a4a65eaa08 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -586,10 +586,18 @@ static const struct inode_operations cifs_ipc_inode_ops = { }; /* gets root inode */ -void cifs_read_inode(struct inode *inode) +struct inode *cifs_iget(struct super_block *sb, unsigned long ino) { - int xid, rc; + int xid; struct cifs_sb_info *cifs_sb; + struct inode *inode; + long rc; + + inode = iget_locked(sb, ino); + if (!inode) + return ERR_PTR(-ENOMEM); + if (!(inode->i_state & I_NEW)) + return inode; cifs_sb = CIFS_SB(inode->i_sb); xid = GetXid(); @@ -606,10 +614,18 @@ void cifs_read_inode(struct inode *inode) inode->i_fop = &simple_dir_operations; inode->i_uid = cifs_sb->mnt_uid; inode->i_gid = cifs_sb->mnt_gid; + _FreeXid(xid); + iget_failed(inode); + return ERR_PTR(rc); } - /* can not call macro FreeXid here since in a void func */ + unlock_new_inode(inode); + + /* can not call macro FreeXid here since in a void func + * TODO: This is no longer true + */ _FreeXid(xid); + return inode; } int cifs_unlink(struct inode *inode, struct dentry *direntry) diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index ffdc022cae64..614bd75b5a4a 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -2986,7 +2986,7 @@ asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd, } do_ioctl: - error = vfs_ioctl(filp, fd, cmd, arg); + error = do_vfs_ioctl(filp, fd, cmd, arg); out_fput: fput_light(filp, fput_needed); out: diff --git a/fs/efs/inode.c b/fs/efs/inode.c index 174696f9bf14..627c3026946d 100644 --- a/fs/efs/inode.c +++ b/fs/efs/inode.c @@ -45,17 +45,26 @@ static inline void extent_copy(efs_extent *src, efs_extent *dst) { return; } -void efs_read_inode(struct inode *inode) +struct inode *efs_iget(struct super_block *super, unsigned long ino) { int i, inode_index; dev_t device; u32 rdev; struct buffer_head *bh; - struct efs_sb_info *sb = SUPER_INFO(inode->i_sb); - struct efs_inode_info *in = INODE_INFO(inode); + struct efs_sb_info *sb = SUPER_INFO(super); + struct efs_inode_info *in; efs_block_t block, offset; struct efs_dinode *efs_inode; - + struct inode *inode; + + inode = iget_locked(super, ino); + if (IS_ERR(inode)) + return ERR_PTR(-ENOMEM); + if (!(inode->i_state & I_NEW)) + return inode; + + in = INODE_INFO(inode); + /* ** EFS layout: ** @@ -159,13 +168,13 @@ void efs_read_inode(struct inode *inode) break; } - return; + unlock_new_inode(inode); + return inode; read_inode_error: printk(KERN_WARNING "EFS: failed to read inode %lu\n", inode->i_ino); - make_bad_inode(inode); - - return; + iget_failed(inode); + return ERR_PTR(-EIO); } static inline efs_block_t diff --git a/fs/efs/namei.c b/fs/efs/namei.c index f7f407075be1..e26704742d41 100644 --- a/fs/efs/namei.c +++ b/fs/efs/namei.c @@ -66,9 +66,10 @@ struct dentry *efs_lookup(struct inode *dir, struct dentry *dentry, struct namei lock_kernel(); inodenum = efs_find_entry(dir, dentry->d_name.name, dentry->d_name.len); if (inodenum) { - if (!(inode = iget(dir->i_sb, inodenum))) { + inode = efs_iget(dir->i_sb, inodenum); + if (IS_ERR(inode)) { unlock_kernel(); - return ERR_PTR(-EACCES); + return ERR_CAST(inode); } } unlock_kernel(); @@ -84,12 +85,11 @@ static struct inode *efs_nfs_get_inode(struct super_block *sb, u64 ino, if (ino == 0) return ERR_PTR(-ESTALE); - inode = iget(sb, ino); - if (inode == NULL) - return ERR_PTR(-ENOMEM); + inode = efs_iget(sb, ino); + if (IS_ERR(inode)) + return ERR_CAST(inode); - if (is_bad_inode(inode) || - (generation && inode->i_generation != generation)) { + if (generation && inode->i_generation != generation) { iput(inode); return ERR_PTR(-ESTALE); } @@ -116,7 +116,7 @@ struct dentry *efs_get_parent(struct dentry *child) struct dentry *parent; struct inode *inode; efs_ino_t ino; - int error; + long error; lock_kernel(); @@ -125,10 +125,11 @@ struct dentry *efs_get_parent(struct dentry *child) if (!ino) goto fail; - error = -EACCES; - inode = iget(child->d_inode->i_sb, ino); - if (!inode) + inode = efs_iget(child->d_inode->i_sb, ino); + if (IS_ERR(inode)) { + error = PTR_ERR(inode); goto fail; + } error = -ENOMEM; parent = d_alloc_anon(inode); diff --git a/fs/efs/super.c b/fs/efs/super.c index c79bc627f107..14082405cdd1 100644 --- a/fs/efs/super.c +++ b/fs/efs/super.c @@ -107,7 +107,6 @@ static int efs_remount(struct super_block *sb, int *flags, char *data) static const struct super_operations efs_superblock_operations = { .alloc_inode = efs_alloc_inode, .destroy_inode = efs_destroy_inode, - .read_inode = efs_read_inode, .put_super = efs_put_super, .statfs = efs_statfs, .remount_fs = efs_remount, @@ -247,6 +246,7 @@ static int efs_fill_super(struct super_block *s, void *d, int silent) struct efs_sb_info *sb; struct buffer_head *bh; struct inode *root; + int ret = -EINVAL; sb = kzalloc(sizeof(struct efs_sb_info), GFP_KERNEL); if (!sb) @@ -303,12 +303,18 @@ static int efs_fill_super(struct super_block *s, void *d, int silent) } s->s_op = &efs_superblock_operations; s->s_export_op = &efs_export_ops; - root = iget(s, EFS_ROOTINODE); + root = efs_iget(s, EFS_ROOTINODE); + if (IS_ERR(root)) { + printk(KERN_ERR "EFS: get root inode failed\n"); + ret = PTR_ERR(root); + goto out_no_fs; + } + s->s_root = d_alloc_root(root); - if (!(s->s_root)) { - printk(KERN_ERR "EFS: get root inode failed\n"); + printk(KERN_ERR "EFS: get root dentry failed\n"); iput(root); + ret = -ENOMEM; goto out_no_fs; } @@ -318,7 +324,7 @@ out_no_fs_ul: out_no_fs: s->s_fs_info = NULL; kfree(sb); - return -EINVAL; + return ret; } static int efs_statfs(struct dentry *dentry, struct kstatfs *buf) { diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h index bb9948cdd50f..f1e5705e75f1 100644 --- a/fs/ext2/ext2.h +++ b/fs/ext2/ext2.h @@ -124,7 +124,7 @@ extern void ext2_check_inodes_bitmap (struct super_block *); extern unsigned long ext2_count_free (struct buffer_head *, unsigned); /* inode.c */ -extern void ext2_read_inode (struct inode *); +extern struct inode *ext2_iget (struct super_block *, unsigned long); extern int ext2_write_inode (struct inode *, int); extern void ext2_put_inode (struct inode *); extern void ext2_delete_inode (struct inode *); diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 03978ec2a91c..c62006805427 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -1181,22 +1181,33 @@ void ext2_get_inode_flags(struct ext2_inode_info *ei) ei->i_flags |= EXT2_DIRSYNC_FL; } -void ext2_read_inode (struct inode * inode) +struct inode *ext2_iget (struct super_block *sb, unsigned long ino) { - struct ext2_inode_info *ei = EXT2_I(inode); - ino_t ino = inode->i_ino; + struct ext2_inode_info *ei; struct buffer_head * bh; - struct ext2_inode * raw_inode = ext2_get_inode(inode->i_sb, ino, &bh); + struct ext2_inode *raw_inode; + struct inode *inode; + long ret = -EIO; int n; + inode = iget_locked(sb, ino); + if (!inode) + return ERR_PTR(-ENOMEM); + if (!(inode->i_state & I_NEW)) + return inode; + + ei = EXT2_I(inode); #ifdef CONFIG_EXT2_FS_POSIX_ACL ei->i_acl = EXT2_ACL_NOT_CACHED; ei->i_default_acl = EXT2_ACL_NOT_CACHED; #endif ei->i_block_alloc_info = NULL; - if (IS_ERR(raw_inode)) + raw_inode = ext2_get_inode(inode->i_sb, ino, &bh); + if (IS_ERR(raw_inode)) { + ret = PTR_ERR(raw_inode); goto bad_inode; + } inode->i_mode = le16_to_cpu(raw_inode->i_mode); inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low); @@ -1220,6 +1231,7 @@ void ext2_read_inode (struct inode * inode) if (inode->i_nlink == 0 && (inode->i_mode == 0 || ei->i_dtime)) { /* this inode is deleted */ brelse (bh); + ret = -ESTALE; goto bad_inode; } inode->i_blocks = le32_to_cpu(raw_inode->i_blocks); @@ -1286,11 +1298,12 @@ void ext2_read_inode (struct inode * inode) } brelse (bh); ext2_set_inode_flags(inode); - return; + unlock_new_inode(inode); + return inode; bad_inode: - make_bad_inode(inode); - return; + iget_failed(inode); + return ERR_PTR(ret); } static int ext2_update_inode(struct inode * inode, int do_sync) diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index e69beed839ac..80c97fd8c571 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -63,9 +63,9 @@ static struct dentry *ext2_lookup(struct inode * dir, struct dentry *dentry, str ino = ext2_inode_by_name(dir, dentry); inode = NULL; if (ino) { - inode = iget(dir->i_sb, ino); - if (!inode) - return ERR_PTR(-EACCES); + inode = ext2_iget(dir->i_sb, ino); + if (IS_ERR(inode)) + return ERR_CAST(inode); } return d_splice_alias(inode, dentry); } @@ -83,10 +83,10 @@ struct dentry *ext2_get_parent(struct dentry *child) ino = ext2_inode_by_name(child->d_inode, &dotdot); if (!ino) return ERR_PTR(-ENOENT); - inode = iget(child->d_inode->i_sb, ino); + inode = ext2_iget(child->d_inode->i_sb, ino); - if (!inode) - return ERR_PTR(-EACCES); + if (IS_ERR(inode)) + return ERR_CAST(inode); parent = d_alloc_anon(inode); if (!parent) { iput(inode); diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 1ba18b72d43a..22f1010bf79f 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -296,7 +296,6 @@ static ssize_t ext2_quota_write(struct super_block *sb, int type, const char *da static const struct super_operations ext2_sops = { .alloc_inode = ext2_alloc_inode, .destroy_inode = ext2_destroy_inode, - .read_inode = ext2_read_inode, .write_inode = ext2_write_inode, .delete_inode = ext2_delete_inode, .put_super = ext2_put_super, @@ -326,11 +325,10 @@ static struct inode *ext2_nfs_get_inode(struct super_block *sb, * it might be "neater" to call ext2_get_inode first and check * if the inode is valid..... */ - inode = iget(sb, ino); - if (inode == NULL) - return ERR_PTR(-ENOMEM); - if (is_bad_inode(inode) || - (generation && inode->i_generation != generation)) { + inode = ext2_iget(sb, ino); + if (IS_ERR(inode)) + return ERR_CAST(inode); + if (generation && inode->i_generation != generation) { /* we didn't find the right inode.. */ iput(inode); return ERR_PTR(-ESTALE); @@ -746,6 +744,7 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) unsigned long logic_sb_block; unsigned long offset = 0; unsigned long def_mount_opts; + long ret = -EINVAL; int blocksize = BLOCK_SIZE; int db_count; int i, j; @@ -1041,19 +1040,24 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) sb->s_op = &ext2_sops; sb->s_export_op = &ext2_export_ops; sb->s_xattr = ext2_xattr_handlers; - root = iget(sb, EXT2_ROOT_INO); - sb->s_root = d_alloc_root(root); - if (!sb->s_root) { - iput(root); - printk(KERN_ERR "EXT2-fs: get root inode failed\n"); + root = ext2_iget(sb, EXT2_ROOT_INO); + if (IS_ERR(root)) { + ret = PTR_ERR(root); goto failed_mount3; } if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) { - dput(sb->s_root); - sb->s_root = NULL; + iput(root); printk(KERN_ERR "EXT2-fs: corrupt root inode, run e2fsck\n"); goto failed_mount3; } + + sb->s_root = d_alloc_root(root); + if (!sb->s_root) { + iput(root); + printk(KERN_ERR "EXT2-fs: get root inode failed\n"); + ret = -ENOMEM; + goto failed_mount3; + } if (EXT2_HAS_COMPAT_FEATURE(sb, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) ext2_warning(sb, __FUNCTION__, "mounting ext3 filesystem as ext2"); @@ -1080,7 +1084,7 @@ failed_mount: failed_sbi: sb->s_fs_info = NULL; kfree(sbi); - return -EINVAL; + return ret; } static void ext2_commit_super (struct super_block * sb, diff --git a/fs/ext3/ialloc.c b/fs/ext3/ialloc.c index 1bc8cd89c51d..58ae2f943f12 100644 --- a/fs/ext3/ialloc.c +++ b/fs/ext3/ialloc.c @@ -642,14 +642,15 @@ struct inode *ext3_orphan_get(struct super_block *sb, unsigned long ino) unsigned long max_ino = le32_to_cpu(EXT3_SB(sb)->s_es->s_inodes_count); unsigned long block_group; int bit; - struct buffer_head *bitmap_bh = NULL; + struct buffer_head *bitmap_bh; struct inode *inode = NULL; + long err = -EIO; /* Error cases - e2fsck has already cleaned up for us */ if (ino > max_ino) { ext3_warning(sb, __FUNCTION__, "bad orphan ino %lu! e2fsck was run?", ino); - goto out; + goto error; } block_group = (ino - 1) / EXT3_INODES_PER_GROUP(sb); @@ -658,38 +659,49 @@ struct inode *ext3_orphan_get(struct super_block *sb, unsigned long ino) if (!bitmap_bh) { ext3_warning(sb, __FUNCTION__, "inode bitmap error for orphan %lu", ino); - goto out; + goto error; } /* Having the inode bit set should be a 100% indicator that this * is a valid orphan (no e2fsck run on fs). Orphans also include * inodes that were being truncated, so we can't check i_nlink==0. */ - if (!ext3_test_bit(bit, bitmap_bh->b_data) || - !(inode = iget(sb, ino)) || is_bad_inode(inode) || - NEXT_ORPHAN(inode) > max_ino) { - ext3_warning(sb, __FUNCTION__, - "bad orphan inode %lu! e2fsck was run?", ino); - printk(KERN_NOTICE "ext3_test_bit(bit=%d, block=%llu) = %d\n", - bit, (unsigned long long)bitmap_bh->b_blocknr, - ext3_test_bit(bit, bitmap_bh->b_data)); - printk(KERN_NOTICE "inode=%p\n", inode); - if (inode) { - printk(KERN_NOTICE "is_bad_inode(inode)=%d\n", - is_bad_inode(inode)); - printk(KERN_NOTICE "NEXT_ORPHAN(inode)=%u\n", - NEXT_ORPHAN(inode)); - printk(KERN_NOTICE "max_ino=%lu\n", max_ino); - } + if (!ext3_test_bit(bit, bitmap_bh->b_data)) + goto bad_orphan; + + inode = ext3_iget(sb, ino); + if (IS_ERR(inode)) + goto iget_failed; + + if (NEXT_ORPHAN(inode) > max_ino) + goto bad_orphan; + brelse(bitmap_bh); + return inode; + +iget_failed: + err = PTR_ERR(inode); + inode = NULL; +bad_orphan: + ext3_warning(sb, __FUNCTION__, + "bad orphan inode %lu! e2fsck was run?", ino); + printk(KERN_NOTICE "ext3_test_bit(bit=%d, block=%llu) = %d\n", + bit, (unsigned long long)bitmap_bh->b_blocknr, + ext3_test_bit(bit, bitmap_bh->b_data)); + printk(KERN_NOTICE "inode=%p\n", inode); + if (inode) { + printk(KERN_NOTICE "is_bad_inode(inode)=%d\n", + is_bad_inode(inode)); + printk(KERN_NOTICE "NEXT_ORPHAN(inode)=%u\n", + NEXT_ORPHAN(inode)); + printk(KERN_NOTICE "max_ino=%lu\n", max_ino); /* Avoid freeing blocks if we got a bad deleted inode */ - if (inode && inode->i_nlink == 0) + if (inode->i_nlink == 0) inode->i_blocks = 0; iput(inode); - inode = NULL; } -out: brelse(bitmap_bh); - return inode; +error: + return ERR_PTR(err); } unsigned long ext3_count_free_inodes (struct super_block * sb) diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 8a9ce2d09bde..eb95670a27eb 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -2654,21 +2654,31 @@ void ext3_get_inode_flags(struct ext3_inode_info *ei) ei->i_flags |= EXT3_DIRSYNC_FL; } -void ext3_read_inode(struct inode * inode) +struct inode *ext3_iget(struct super_block *sb, unsigned long ino) { struct ext3_iloc iloc; struct ext3_inode *raw_inode; - struct ext3_inode_info *ei = EXT3_I(inode); + struct ext3_inode_info *ei; struct buffer_head *bh; + struct inode *inode; + long ret; int block; + inode = iget_locked(sb, ino); + if (!inode) + return ERR_PTR(-ENOMEM); + if (!(inode->i_state & I_NEW)) + return inode; + + ei = EXT3_I(inode); #ifdef CONFIG_EXT3_FS_POSIX_ACL ei->i_acl = EXT3_ACL_NOT_CACHED; ei->i_default_acl = EXT3_ACL_NOT_CACHED; #endif ei->i_block_alloc_info = NULL; - if (__ext3_get_inode_loc(inode, &iloc, 0)) + ret = __ext3_get_inode_loc(inode, &iloc, 0); + if (ret < 0) goto bad_inode; bh = iloc.bh; raw_inode = ext3_raw_inode(&iloc); @@ -2699,6 +2709,7 @@ void ext3_read_inode(struct inode * inode) !(EXT3_SB(inode->i_sb)->s_mount_state & EXT3_ORPHAN_FS)) { /* this inode is deleted */ brelse (bh); + ret = -ESTALE; goto bad_inode; } /* The only unlinked inodes we let through here have @@ -2742,6 +2753,7 @@ void ext3_read_inode(struct inode * inode) if (EXT3_GOOD_OLD_INODE_SIZE + ei->i_extra_isize > EXT3_INODE_SIZE(inode->i_sb)) { brelse (bh); + ret = -EIO; goto bad_inode; } if (ei->i_extra_isize == 0) { @@ -2783,11 +2795,12 @@ void ext3_read_inode(struct inode * inode) } brelse (iloc.bh); ext3_set_inode_flags(inode); - return; + unlock_new_inode(inode); + return inode; bad_inode: - make_bad_inode(inode); - return; + iget_failed(inode); + return ERR_PTR(ret); } /* diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c index 92b83b004dd8..dec3e0d88ab1 100644 --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c @@ -1037,17 +1037,11 @@ static struct dentry *ext3_lookup(struct inode * dir, struct dentry *dentry, str if (!ext3_valid_inum(dir->i_sb, ino)) { ext3_error(dir->i_sb, "ext3_lookup", "bad inode number: %lu", ino); - inode = NULL; - } else - inode = iget(dir->i_sb, ino); - - if (!inode) - return ERR_PTR(-EACCES); - - if (is_bad_inode(inode)) { - iput(inode); - return ERR_PTR(-ENOENT); + return ERR_PTR(-EIO); } + inode = ext3_iget(dir->i_sb, ino); + if (IS_ERR(inode)) + return ERR_CAST(inode); } return d_splice_alias(inode, dentry); } @@ -1076,18 +1070,13 @@ struct dentry *ext3_get_parent(struct dentry *child) if (!ext3_valid_inum(child->d_inode->i_sb, ino)) { ext3_error(child->d_inode->i_sb, "ext3_get_parent", "bad inode number: %lu", ino); - inode = NULL; - } else - inode = iget(child->d_inode->i_sb, ino); - - if (!inode) - return ERR_PTR(-EACCES); - - if (is_bad_inode(inode)) { - iput(inode); - return ERR_PTR(-ENOENT); + return ERR_PTR(-EIO); } + inode = ext3_iget(child->d_inode->i_sb, ino); + if (IS_ERR(inode)) + return ERR_CAST(inode); + parent = d_alloc_anon(inode); if (!parent) { iput(inode); diff --git a/fs/ext3/resize.c b/fs/ext3/resize.c index 44de1453c301..ebc05af7343a 100644 --- a/fs/ext3/resize.c +++ b/fs/ext3/resize.c @@ -795,12 +795,11 @@ int ext3_group_add(struct super_block *sb, struct ext3_new_group_data *input) "No reserved GDT blocks, can't resize"); return -EPERM; } - inode = iget(sb, EXT3_RESIZE_INO); - if (!inode || is_bad_inode(inode)) { + inode = ext3_iget(sb, EXT3_RESIZE_INO); + if (IS_ERR(inode)) { ext3_warning(sb, __FUNCTION__, "Error opening resize inode"); - iput(inode); - return -ENOENT; + return PTR_ERR(inode); } } diff --git a/fs/ext3/super.c b/fs/ext3/super.c index 343677e8c350..cf2a2c3660ec 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -649,11 +649,10 @@ static struct inode *ext3_nfs_get_inode(struct super_block *sb, * Currently we don't know the generation for parent directory, so * a generation of 0 means "accept any" */ - inode = iget(sb, ino); - if (inode == NULL) - return ERR_PTR(-ENOMEM); - if (is_bad_inode(inode) || - (generation && inode->i_generation != generation)) { + inode = ext3_iget(sb, ino); + if (IS_ERR(inode)) + return ERR_CAST(inode); + if (generation && inode->i_generation != generation) { iput(inode); return ERR_PTR(-ESTALE); } @@ -722,7 +721,6 @@ static struct quotactl_ops ext3_qctl_operations = { static const struct super_operations ext3_sops = { .alloc_inode = ext3_alloc_inode, .destroy_inode = ext3_destroy_inode, - .read_inode = ext3_read_inode, .write_inode = ext3_write_inode, .dirty_inode = ext3_dirty_inode, .delete_inode = ext3_delete_inode, @@ -1378,8 +1376,8 @@ static void ext3_orphan_cleanup (struct super_block * sb, while (es->s_last_orphan) { struct inode *inode; - if (!(inode = - ext3_orphan_get(sb, le32_to_cpu(es->s_last_orphan)))) { + inode = ext3_orphan_get(sb, le32_to_cpu(es->s_last_orphan)); + if (IS_ERR(inode)) { es->s_last_orphan = 0; break; } @@ -1508,6 +1506,7 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent) int db_count; int i; int needs_recovery; + int ret = -EINVAL; __le32 features; int err; @@ -1877,19 +1876,24 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent) * so we can safely mount the rest of the filesystem now. */ - root = iget(sb, EXT3_ROOT_INO); - sb->s_root = d_alloc_root(root); - if (!sb->s_root) { + root = ext3_iget(sb, EXT3_ROOT_INO); + if (IS_ERR(root)) { printk(KERN_ERR "EXT3-fs: get root inode failed\n"); - iput(root); + ret = PTR_ERR(root); goto failed_mount4; } if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) { - dput(sb->s_root); - sb->s_root = NULL; + iput(root); printk(KERN_ERR "EXT3-fs: corrupt root inode, run e2fsck\n"); goto failed_mount4; } + sb->s_root = d_alloc_root(root); + if (!sb->s_root) { + printk(KERN_ERR "EXT3-fs: get root dentry failed\n"); + iput(root); + ret = -ENOMEM; + goto failed_mount4; + } ext3_setup_super (sb, es, sb->s_flags & MS_RDONLY); /* @@ -1941,7 +1945,7 @@ out_fail: sb->s_fs_info = NULL; kfree(sbi); lock_kernel(); - return -EINVAL; + return ret; } /* @@ -1977,8 +1981,8 @@ static journal_t *ext3_get_journal(struct super_block *sb, * things happen if we iget() an unused inode, as the subsequent * iput() will try to delete it. */ - journal_inode = iget(sb, journal_inum); - if (!journal_inode) { + journal_inode = ext3_iget(sb, journal_inum); + if (IS_ERR(journal_inode)) { printk(KERN_ERR "EXT3-fs: no journal found.\n"); return NULL; } @@ -1991,7 +1995,7 @@ static journal_t *ext3_get_journal(struct super_block *sb, jbd_debug(2, "Journal inode found at %p: %Ld bytes\n", journal_inode, journal_inode->i_size); - if (is_bad_inode(journal_inode) || !S_ISREG(journal_inode->i_mode)) { + if (!S_ISREG(journal_inode->i_mode)) { printk(KERN_ERR "EXT3-fs: invalid journal inode.\n"); iput(journal_inode); return NULL; diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index 575b5215c808..da18a74b966a 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -782,14 +782,15 @@ struct inode *ext4_orphan_get(struct super_block *sb, unsigned long ino) unsigned long max_ino = le32_to_cpu(EXT4_SB(sb)->s_es->s_inodes_count); ext4_group_t block_group; int bit; - struct buffer_head *bitmap_bh = NULL; + struct buffer_head *bitmap_bh; struct inode *inode = NULL; + long err = -EIO; /* Error cases - e2fsck has already cleaned up for us */ if (ino > max_ino) { ext4_warning(sb, __FUNCTION__, "bad orphan ino %lu! e2fsck was run?", ino); - goto out; + goto error; } block_group = (ino - 1) / EXT4_INODES_PER_GROUP(sb); @@ -798,38 +799,49 @@ struct inode *ext4_orphan_get(struct super_block *sb, unsigned long ino) if (!bitmap_bh) { ext4_warning(sb, __FUNCTION__, "inode bitmap error for orphan %lu", ino); - goto out; + goto error; } /* Having the inode bit set should be a 100% indicator that this * is a valid orphan (no e2fsck run on fs). Orphans also include * inodes that were being truncated, so we can't check i_nlink==0. */ - if (!ext4_test_bit(bit, bitmap_bh->b_data) || - !(inode = iget(sb, ino)) || is_bad_inode(inode) || - NEXT_ORPHAN(inode) > max_ino) { - ext4_warning(sb, __FUNCTION__, - "bad orphan inode %lu! e2fsck was run?", ino); - printk(KERN_NOTICE "ext4_test_bit(bit=%d, block=%llu) = %d\n", - bit, (unsigned long long)bitmap_bh->b_blocknr, - ext4_test_bit(bit, bitmap_bh->b_data)); - printk(KERN_NOTICE "inode=%p\n", inode); - if (inode) { - printk(KERN_NOTICE "is_bad_inode(inode)=%d\n", - is_bad_inode(inode)); - printk(KERN_NOTICE "NEXT_ORPHAN(inode)=%u\n", - NEXT_ORPHAN(inode)); - printk(KERN_NOTICE "max_ino=%lu\n", max_ino); - } + if (!ext4_test_bit(bit, bitmap_bh->b_data)) + goto bad_orphan; + + inode = ext4_iget(sb, ino); + if (IS_ERR(inode)) + goto iget_failed; + + if (NEXT_ORPHAN(inode) > max_ino) + goto bad_orphan; + brelse(bitmap_bh); + return inode; + +iget_failed: + err = PTR_ERR(inode); + inode = NULL; +bad_orphan: + ext4_warning(sb, __FUNCTION__, + "bad orphan inode %lu! e2fsck was run?", ino); + printk(KERN_NOTICE "ext4_test_bit(bit=%d, block=%llu) = %d\n", + bit, (unsigned long long)bitmap_bh->b_blocknr, + ext4_test_bit(bit, bitmap_bh->b_data)); + printk(KERN_NOTICE "inode=%p\n", inode); + if (inode) { + printk(KERN_NOTICE "is_bad_inode(inode)=%d\n", + is_bad_inode(inode)); + printk(KERN_NOTICE "NEXT_ORPHAN(inode)=%u\n", + NEXT_ORPHAN(inode)); + printk(KERN_NOTICE "max_ino=%lu\n", max_ino); /* Avoid freeing blocks if we got a bad deleted inode */ - if (inode && inode->i_nlink == 0) + if (inode->i_nlink == 0) inode->i_blocks = 0; iput(inode); - inode = NULL; } -out: brelse(bitmap_bh); - return inode; +error: + return ERR_PTR(err); } unsigned long ext4_count_free_inodes (struct super_block * sb) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 0e9055cf700e..f4e387452246 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -2680,21 +2680,31 @@ static blkcnt_t ext4_inode_blocks(struct ext4_inode *raw_inode, } } -void ext4_read_inode(struct inode * inode) +struct inode *ext4_iget(struct super_block *sb, unsigned long ino) { struct ext4_iloc iloc; struct ext4_inode *raw_inode; - struct ext4_inode_info *ei = EXT4_I(inode); + struct ext4_inode_info *ei; struct buffer_head *bh; + struct inode *inode; + long ret; int block; + inode = iget_locked(sb, ino); + if (!inode) + return ERR_PTR(-ENOMEM); + if (!(inode->i_state & I_NEW)) + return inode; + + ei = EXT4_I(inode); #ifdef CONFIG_EXT4DEV_FS_POSIX_ACL ei->i_acl = EXT4_ACL_NOT_CACHED; ei->i_default_acl = EXT4_ACL_NOT_CACHED; #endif ei->i_block_alloc_info = NULL; - if (__ext4_get_inode_loc(inode, &iloc, 0)) + ret = __ext4_get_inode_loc(inode, &iloc, 0); + if (ret < 0) goto bad_inode; bh = iloc.bh; raw_inode = ext4_raw_inode(&iloc); @@ -2720,6 +2730,7 @@ void ext4_read_inode(struct inode * inode) !(EXT4_SB(inode->i_sb)->s_mount_state & EXT4_ORPHAN_FS)) { /* this inode is deleted */ brelse (bh); + ret = -ESTALE; goto bad_inode; } /* The only unlinked inodes we let through here have @@ -2758,6 +2769,7 @@ void ext4_read_inode(struct inode * inode) if (EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize > EXT4_INODE_SIZE(inode->i_sb)) { brelse (bh); + ret = -EIO; goto bad_inode; } if (ei->i_extra_isize == 0) { @@ -2811,11 +2823,12 @@ void ext4_read_inode(struct inode * inode) } brelse (iloc.bh); ext4_set_inode_flags(inode); - return; + unlock_new_inode(inode); + return inode; bad_inode: - make_bad_inode(inode); - return; + iget_failed(inode); + return ERR_PTR(ret); } static int ext4_inode_blocks_set(handle_t *handle, diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 67b6d8a1ceff..d153bb5922fc 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -1039,17 +1039,11 @@ static struct dentry *ext4_lookup(struct inode * dir, struct dentry *dentry, str if (!ext4_valid_inum(dir->i_sb, ino)) { ext4_error(dir->i_sb, "ext4_lookup", "bad inode number: %lu", ino); - inode = NULL; - } else - inode = iget(dir->i_sb, ino); - - if (!inode) - return ERR_PTR(-EACCES); - - if (is_bad_inode(inode)) { - iput(inode); - return ERR_PTR(-ENOENT); + return ERR_PTR(-EIO); } + inode = ext4_iget(dir->i_sb, ino); + if (IS_ERR(inode)) + return ERR_CAST(inode); } return d_splice_alias(inode, dentry); } @@ -1078,18 +1072,13 @@ struct dentry *ext4_get_parent(struct dentry *child) if (!ext4_valid_inum(child->d_inode->i_sb, ino)) { ext4_error(child->d_inode->i_sb, "ext4_get_parent", "bad inode number: %lu", ino); - inode = NULL; - } else - inode = iget(child->d_inode->i_sb, ino); - - if (!inode) - return ERR_PTR(-EACCES); - - if (is_bad_inode(inode)) { - iput(inode); - return ERR_PTR(-ENOENT); + return ERR_PTR(-EIO); } + inode = ext4_iget(child->d_inode->i_sb, ino); + if (IS_ERR(inode)) + return ERR_CAST(inode); + parent = d_alloc_anon(inode); if (!parent) { iput(inode); diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c index 4fbba60816f4..9477a2bd6ff2 100644 --- a/fs/ext4/resize.c +++ b/fs/ext4/resize.c @@ -779,12 +779,11 @@ int ext4_group_add(struct super_block *sb, struct ext4_new_group_data *input) "No reserved GDT blocks, can't resize"); return -EPERM; } - inode = iget(sb, EXT4_RESIZE_INO); - if (!inode || is_bad_inode(inode)) { + inode = ext4_iget(sb, EXT4_RESIZE_INO); + if (IS_ERR(inode)) { ext4_warning(sb, __FUNCTION__, "Error opening resize inode"); - iput(inode); - return -ENOENT; + return PTR_ERR(inode); } } diff --git a/fs/ext4/super.c b/fs/ext4/super.c index c89bb8797765..93beb865c20d 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -777,11 +777,10 @@ static struct inode *ext4_nfs_get_inode(struct super_block *sb, * Currently we don't know the generation for parent directory, so * a generation of 0 means "accept any" */ - inode = iget(sb, ino); - if (inode == NULL) - return ERR_PTR(-ENOMEM); - if (is_bad_inode(inode) || - (generation && inode->i_generation != generation)) { + inode = ext4_iget(sb, ino); + if (IS_ERR(inode)) + return ERR_CAST(inode); + if (generation && inode->i_generation != generation) { iput(inode); return ERR_PTR(-ESTALE); } @@ -850,7 +849,6 @@ static struct quotactl_ops ext4_qctl_operations = { static const struct super_operations ext4_sops = { .alloc_inode = ext4_alloc_inode, .destroy_inode = ext4_destroy_inode, - .read_inode = ext4_read_inode, .write_inode = ext4_write_inode, .dirty_inode = ext4_dirty_inode, .delete_inode = ext4_delete_inode, @@ -1805,6 +1803,7 @@ static int ext4_fill_super (struct super_block *sb, void *data, int silent) unsigned long journal_devnum = 0; unsigned long def_mount_opts; struct inode *root; + int ret = -EINVAL; int blocksize; int db_count; int i; @@ -2237,19 +2236,24 @@ static int ext4_fill_super (struct super_block *sb, void *data, int silent) * so we can safely mount the rest of the filesystem now. */ - root = iget(sb, EXT4_ROOT_INO); - sb->s_root = d_alloc_root(root); - if (!sb->s_root) { + root = ext4_iget(sb, EXT4_ROOT_INO); + if (IS_ERR(root)) { printk(KERN_ERR "EXT4-fs: get root inode failed\n"); - iput(root); + ret = PTR_ERR(root); goto failed_mount4; } if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) { - dput(sb->s_root); - sb->s_root = NULL; + iput(root); printk(KERN_ERR "EXT4-fs: corrupt root inode, run e2fsck\n"); goto failed_mount4; } + sb->s_root = d_alloc_root(root); + if (!sb->s_root) { + printk(KERN_ERR "EXT4-fs: get root dentry failed\n"); + iput(root); + ret = -ENOMEM; + goto failed_mount4; + } ext4_setup_super (sb, es, sb->s_flags & MS_RDONLY); @@ -2330,7 +2334,7 @@ out_fail: sb->s_fs_info = NULL; kfree(sbi); lock_kernel(); - return -EINVAL; + return ret; } /* @@ -2366,8 +2370,8 @@ static journal_t *ext4_get_journal(struct super_block *sb, * things happen if we iget() an unused inode, as the subsequent * iput() will try to delete it. */ - journal_inode = iget(sb, journal_inum); - if (!journal_inode) { + journal_inode = ext4_iget(sb, journal_inum); + if (IS_ERR(journal_inode)) { printk(KERN_ERR "EXT4-fs: no journal found.\n"); return NULL; } @@ -2380,7 +2384,7 @@ static journal_t *ext4_get_journal(struct super_block *sb, jbd_debug(2, "Journal inode found at %p: %Ld bytes\n", journal_inode, journal_inode->i_size); - if (is_bad_inode(journal_inode) || !S_ISREG(journal_inode->i_mode)) { + if (!S_ISREG(journal_inode->i_mode)) { printk(KERN_ERR "EXT4-fs: invalid journal inode.\n"); iput(journal_inode); return NULL; diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 24c0aaa5ae80..085269e07fb3 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -634,8 +634,6 @@ static const struct super_operations fat_sops = { .clear_inode = fat_clear_inode, .remount_fs = fat_remount, - .read_inode = make_bad_inode, - .show_options = fat_show_options, }; @@ -663,8 +661,8 @@ static struct dentry *fat_fh_to_dentry(struct super_block *sb, if (fh_len < 5 || fh_type != 3) return NULL; - inode = iget(sb, fh[0]); - if (!inode || is_bad_inode(inode) || inode->i_generation != fh[1]) { + inode = ilookup(sb, fh[0]); + if (!inode || inode->i_generation != fh[1]) { if (inode) iput(inode); inode = NULL; @@ -760,7 +758,7 @@ static struct dentry *fat_get_parent(struct dentry *child) inode = fat_build_inode(child->d_sb, de, i_pos); brelse(bh); if (IS_ERR(inode)) { - parent = ERR_PTR(PTR_ERR(inode)); + parent = ERR_CAST(inode); goto out; } parent = d_alloc_anon(inode); diff --git a/fs/freevxfs/vxfs_extern.h b/fs/freevxfs/vxfs_extern.h index 91ccee8723f7..2b46064f66b2 100644 --- a/fs/freevxfs/vxfs_extern.h +++ b/fs/freevxfs/vxfs_extern.h @@ -58,7 +58,7 @@ extern struct inode * vxfs_get_fake_inode(struct super_block *, extern void vxfs_put_fake_inode(struct inode *); extern struct vxfs_inode_info * vxfs_blkiget(struct super_block *, u_long, ino_t); extern struct vxfs_inode_info * vxfs_stiget(struct super_block *, ino_t); -extern void vxfs_read_inode(struct inode *); +extern struct inode * vxfs_iget(struct super_block *, ino_t); extern void vxfs_clear_inode(struct inode *); /* vxfs_lookup.c */ diff --git a/fs/freevxfs/vxfs_inode.c b/fs/freevxfs/vxfs_inode.c index d1f7c5b5b3c3..ad88d2364bc2 100644 --- a/fs/freevxfs/vxfs_inode.c +++ b/fs/freevxfs/vxfs_inode.c @@ -129,7 +129,7 @@ fail: * Description: * Search the for inode number @ino in the filesystem * described by @sbp. Use the specified inode table (@ilistp). - * Returns the matching VxFS inode on success, else a NULL pointer. + * Returns the matching VxFS inode on success, else an error code. */ static struct vxfs_inode_info * __vxfs_iget(ino_t ino, struct inode *ilistp) @@ -157,12 +157,12 @@ __vxfs_iget(ino_t ino, struct inode *ilistp) } printk(KERN_WARNING "vxfs: error on page %p\n", pp); - return NULL; + return ERR_CAST(pp); fail: printk(KERN_WARNING "vxfs: unable to read inode %ld\n", (unsigned long)ino); vxfs_put_page(pp); - return NULL; + return ERR_PTR(-ENOMEM); } /** @@ -178,7 +178,10 @@ fail: struct vxfs_inode_info * vxfs_stiget(struct super_block *sbp, ino_t ino) { - return __vxfs_iget(ino, VXFS_SBI(sbp)->vsi_stilist); + struct vxfs_inode_info *vip; + + vip = __vxfs_iget(ino, VXFS_SBI(sbp)->vsi_stilist); + return IS_ERR(vip) ? NULL : vip; } /** @@ -282,23 +285,32 @@ vxfs_put_fake_inode(struct inode *ip) } /** - * vxfs_read_inode - fill in inode information - * @ip: inode pointer to fill + * vxfs_iget - get an inode + * @sbp: the superblock to get the inode for + * @ino: the number of the inode to get * * Description: - * vxfs_read_inode reads the disk inode for @ip and fills - * in all relevant fields in @ip. + * vxfs_read_inode creates an inode, reads the disk inode for @ino and fills + * in all relevant fields in the new inode. */ -void -vxfs_read_inode(struct inode *ip) +struct inode * +vxfs_iget(struct super_block *sbp, ino_t ino) { - struct super_block *sbp = ip->i_sb; struct vxfs_inode_info *vip; const struct address_space_operations *aops; - ino_t ino = ip->i_ino; - - if (!(vip = __vxfs_iget(ino, VXFS_SBI(sbp)->vsi_ilist))) - return; + struct inode *ip; + + ip = iget_locked(sbp, ino); + if (!ip) + return ERR_PTR(-ENOMEM); + if (!(ip->i_state & I_NEW)) + return ip; + + vip = __vxfs_iget(ino, VXFS_SBI(sbp)->vsi_ilist); + if (IS_ERR(vip)) { + iget_failed(ip); + return ERR_CAST(vip); + } vxfs_iinit(ip, vip); @@ -323,7 +335,8 @@ vxfs_read_inode(struct inode *ip) } else init_special_inode(ip, ip->i_mode, old_decode_dev(vip->vii_rdev)); - return; + unlock_new_inode(ip); + return ip; } /** diff --git a/fs/freevxfs/vxfs_lookup.c b/fs/freevxfs/vxfs_lookup.c index bf86e5444ea6..aee049cb9f84 100644 --- a/fs/freevxfs/vxfs_lookup.c +++ b/fs/freevxfs/vxfs_lookup.c @@ -213,10 +213,10 @@ vxfs_lookup(struct inode *dip, struct dentry *dp, struct nameidata *nd) lock_kernel(); ino = vxfs_inode_by_name(dip, dp); if (ino) { - ip = iget(dip->i_sb, ino); - if (!ip) { + ip = vxfs_iget(dip->i_sb, ino); + if (IS_ERR(ip)) { unlock_kernel(); - return ERR_PTR(-EACCES); + return ERR_CAST(ip); } } unlock_kernel(); diff --git a/fs/freevxfs/vxfs_super.c b/fs/freevxfs/vxfs_super.c index 4f95572d2722..1dacda831577 100644 --- a/fs/freevxfs/vxfs_super.c +++ b/fs/freevxfs/vxfs_super.c @@ -60,7 +60,6 @@ static int vxfs_statfs(struct dentry *, struct kstatfs *); static int vxfs_remount(struct super_block *, int *, char *); static const struct super_operations vxfs_super_ops = { - .read_inode = vxfs_read_inode, .clear_inode = vxfs_clear_inode, .put_super = vxfs_put_super, .statfs = vxfs_statfs, @@ -153,6 +152,7 @@ static int vxfs_fill_super(struct super_block *sbp, void *dp, int silent) struct buffer_head *bp = NULL; u_long bsize; struct inode *root; + int ret = -EINVAL; sbp->s_flags |= MS_RDONLY; @@ -219,7 +219,11 @@ static int vxfs_fill_super(struct super_block *sbp, void *dp, int silent) } sbp->s_op = &vxfs_super_ops; - root = iget(sbp, VXFS_ROOT_INO); + root = vxfs_iget(sbp, VXFS_ROOT_INO); + if (IS_ERR(root)) { + ret = PTR_ERR(root); + goto out; + } sbp->s_root = d_alloc_root(root); if (!sbp->s_root) { iput(root); @@ -236,7 +240,7 @@ out_free_ilist: out: brelse(bp); kfree(infp); - return -EINVAL; + return ret; } /* diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index f56f91bd38be..7fb514b6d852 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -269,12 +269,12 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, req = fuse_get_req(fc); if (IS_ERR(req)) - return ERR_PTR(PTR_ERR(req)); + return ERR_CAST(req); forget_req = fuse_get_req(fc); if (IS_ERR(forget_req)) { fuse_put_request(fc, req); - return ERR_PTR(PTR_ERR(forget_req)); + return ERR_CAST(forget_req); } attr_version = fuse_get_attr_version(fc); @@ -1006,7 +1006,7 @@ static char *read_link(struct dentry *dentry) char *link; if (IS_ERR(req)) - return ERR_PTR(PTR_ERR(req)); + return ERR_CAST(req); link = (char *) __get_free_page(GFP_KERNEL); if (!link) { diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index c90f633d0b57..574707409bbf 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -76,11 +76,6 @@ static void fuse_destroy_inode(struct inode *inode) kmem_cache_free(fuse_inode_cachep, inode); } -static void fuse_read_inode(struct inode *inode) -{ - /* No op */ -} - void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req, unsigned long nodeid, u64 nlookup) { @@ -515,7 +510,6 @@ static struct inode *get_root_inode(struct super_block *sb, unsigned mode) static const struct super_operations fuse_super_operations = { .alloc_inode = fuse_alloc_inode, .destroy_inode = fuse_destroy_inode, - .read_inode = fuse_read_inode, .clear_inode = fuse_clear_inode, .drop_inode = generic_delete_inode, .remount_fs = fuse_remount_fs, diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c index 57e2ed932adc..c34709512b19 100644 --- a/fs/gfs2/dir.c +++ b/fs/gfs2/dir.c @@ -1498,7 +1498,7 @@ struct inode *gfs2_dir_search(struct inode *dir, const struct qstr *name) dent = gfs2_dirent_search(dir, name, gfs2_dirent_find, &bh); if (dent) { if (IS_ERR(dent)) - return ERR_PTR(PTR_ERR(dent)); + return ERR_CAST(dent); inode = gfs2_inode_lookup(dir->i_sb, be16_to_cpu(dent->de_type), be64_to_cpu(dent->de_inum.no_addr), diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 80e09c50590a..7175a4d06435 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -334,7 +334,7 @@ int gfs2_glock_get(struct gfs2_sbd *sdp, u64 number, gl->gl_state = LM_ST_UNLOCKED; gl->gl_demote_state = LM_ST_EXCLUSIVE; gl->gl_hash = hash; - gl->gl_owner_pid = 0; + gl->gl_owner_pid = NULL; gl->gl_ip = 0; gl->gl_ops = glops; gl->gl_req_gh = NULL; @@ -399,7 +399,7 @@ void gfs2_holder_init(struct gfs2_glock *gl, unsigned int state, unsigned flags, INIT_LIST_HEAD(&gh->gh_list); gh->gh_gl = gl; gh->gh_ip = (unsigned long)__builtin_return_address(0); - gh->gh_owner_pid = current->pid; + gh->gh_owner_pid = get_pid(task_pid(current)); gh->gh_state = state; gh->gh_flags = flags; gh->gh_error = 0; @@ -433,6 +433,7 @@ void gfs2_holder_reinit(unsigned int state, unsigned flags, struct gfs2_holder * void gfs2_holder_uninit(struct gfs2_holder *gh) { + put_pid(gh->gh_owner_pid); gfs2_glock_put(gh->gh_gl); gh->gh_gl = NULL; gh->gh_ip = 0; @@ -631,7 +632,7 @@ static void gfs2_glmutex_lock(struct gfs2_glock *gl) wait_on_holder(&gh); gfs2_holder_uninit(&gh); } else { - gl->gl_owner_pid = current->pid; + gl->gl_owner_pid = get_pid(task_pid(current)); gl->gl_ip = (unsigned long)__builtin_return_address(0); spin_unlock(&gl->gl_spin); } @@ -652,7 +653,7 @@ static int gfs2_glmutex_trylock(struct gfs2_glock *gl) if (test_and_set_bit(GLF_LOCK, &gl->gl_flags)) { acquired = 0; } else { - gl->gl_owner_pid = current->pid; + gl->gl_owner_pid = get_pid(task_pid(current)); gl->gl_ip = (unsigned long)__builtin_return_address(0); } spin_unlock(&gl->gl_spin); @@ -668,12 +669,17 @@ static int gfs2_glmutex_trylock(struct gfs2_glock *gl) static void gfs2_glmutex_unlock(struct gfs2_glock *gl) { + struct pid *pid; + spin_lock(&gl->gl_spin); clear_bit(GLF_LOCK, &gl->gl_flags); - gl->gl_owner_pid = 0; + pid = gl->gl_owner_pid; + gl->gl_owner_pid = NULL; gl->gl_ip = 0; run_queue(gl); spin_unlock(&gl->gl_spin); + + put_pid(pid); } /** @@ -1045,7 +1051,7 @@ static int glock_wait_internal(struct gfs2_holder *gh) } static inline struct gfs2_holder * -find_holder_by_owner(struct list_head *head, pid_t pid) +find_holder_by_owner(struct list_head *head, struct pid *pid) { struct gfs2_holder *gh; @@ -1082,7 +1088,7 @@ static void add_to_queue(struct gfs2_holder *gh) struct gfs2_glock *gl = gh->gh_gl; struct gfs2_holder *existing; - BUG_ON(!gh->gh_owner_pid); + BUG_ON(gh->gh_owner_pid == NULL); if (test_and_set_bit(HIF_WAIT, &gh->gh_iflags)) BUG(); @@ -1092,12 +1098,14 @@ static void add_to_queue(struct gfs2_holder *gh) if (existing) { print_symbol(KERN_WARNING "original: %s\n", existing->gh_ip); - printk(KERN_INFO "pid : %d\n", existing->gh_owner_pid); + printk(KERN_INFO "pid : %d\n", + pid_nr(existing->gh_owner_pid)); printk(KERN_INFO "lock type : %d lock state : %d\n", existing->gh_gl->gl_name.ln_type, existing->gh_gl->gl_state); print_symbol(KERN_WARNING "new: %s\n", gh->gh_ip); - printk(KERN_INFO "pid : %d\n", gh->gh_owner_pid); + printk(KERN_INFO "pid : %d\n", + pid_nr(gh->gh_owner_pid)); printk(KERN_INFO "lock type : %d lock state : %d\n", gl->gl_name.ln_type, gl->gl_state); BUG(); @@ -1798,8 +1806,9 @@ static int dump_holder(struct glock_iter *gi, char *str, print_dbg(gi, " %s\n", str); if (gh->gh_owner_pid) { - print_dbg(gi, " owner = %ld ", (long)gh->gh_owner_pid); - gh_owner = find_task_by_pid(gh->gh_owner_pid); + print_dbg(gi, " owner = %ld ", + (long)pid_nr(gh->gh_owner_pid)); + gh_owner = pid_task(gh->gh_owner_pid, PIDTYPE_PID); if (gh_owner) print_dbg(gi, "(%s)\n", gh_owner->comm); else @@ -1877,13 +1886,13 @@ static int dump_glock(struct glock_iter *gi, struct gfs2_glock *gl) print_dbg(gi, " gl_ref = %d\n", atomic_read(&gl->gl_ref)); print_dbg(gi, " gl_state = %u\n", gl->gl_state); if (gl->gl_owner_pid) { - gl_owner = find_task_by_pid(gl->gl_owner_pid); + gl_owner = pid_task(gl->gl_owner_pid, PIDTYPE_PID); if (gl_owner) print_dbg(gi, " gl_owner = pid %d (%s)\n", - gl->gl_owner_pid, gl_owner->comm); + pid_nr(gl->gl_owner_pid), gl_owner->comm); else print_dbg(gi, " gl_owner = %d (ended)\n", - gl->gl_owner_pid); + pid_nr(gl->gl_owner_pid)); } else print_dbg(gi, " gl_owner = -1\n"); print_dbg(gi, " gl_ip = %lu\n", gl->gl_ip); diff --git a/fs/gfs2/glock.h b/fs/gfs2/glock.h index b16f604eea9f..2f9c6d136b37 100644 --- a/fs/gfs2/glock.h +++ b/fs/gfs2/glock.h @@ -36,11 +36,13 @@ static inline int gfs2_glock_is_locked_by_me(struct gfs2_glock *gl) { struct gfs2_holder *gh; int locked = 0; + struct pid *pid; /* Look in glock's list of holders for one with current task as owner */ spin_lock(&gl->gl_spin); + pid = task_pid(current); list_for_each_entry(gh, &gl->gl_holders, gh_list) { - if (gh->gh_owner_pid == current->pid) { + if (gh->gh_owner_pid == pid) { locked = 1; break; } diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index 513aaf0dc0ab..525dcae352d6 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -151,7 +151,7 @@ struct gfs2_holder { struct list_head gh_list; struct gfs2_glock *gh_gl; - pid_t gh_owner_pid; + struct pid *gh_owner_pid; unsigned int gh_state; unsigned gh_flags; @@ -182,7 +182,7 @@ struct gfs2_glock { unsigned int gl_hash; unsigned int gl_demote_state; /* state requested by remote node */ unsigned long gl_demote_time; /* time of first demote request */ - pid_t gl_owner_pid; + struct pid *gl_owner_pid; unsigned long gl_ip; struct list_head gl_holders; struct list_head gl_waiters1; /* HIF_MUTEX */ diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 728d3169e7bd..37725ade3c51 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -240,7 +240,7 @@ fail_put: ip->i_gl->gl_object = NULL; gfs2_glock_put(ip->i_gl); fail: - iput(inode); + iget_failed(inode); return ERR_PTR(error); } diff --git a/fs/gfs2/ops_export.c b/fs/gfs2/ops_export.c index b9da62348a87..334c7f85351b 100644 --- a/fs/gfs2/ops_export.c +++ b/fs/gfs2/ops_export.c @@ -143,7 +143,7 @@ static struct dentry *gfs2_get_parent(struct dentry *child) * have to return that as a(n invalid) pointer to dentry. */ if (IS_ERR(inode)) - return ERR_PTR(PTR_ERR(inode)); + return ERR_CAST(inode); dentry = d_alloc_anon(inode); if (!dentry) { diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c index 9f71372c1757..e87412902bed 100644 --- a/fs/gfs2/ops_inode.c +++ b/fs/gfs2/ops_inode.c @@ -111,7 +111,7 @@ static struct dentry *gfs2_lookup(struct inode *dir, struct dentry *dentry, inode = gfs2_lookupi(dir, &dentry->d_name, 0, nd); if (inode && IS_ERR(inode)) - return ERR_PTR(PTR_ERR(inode)); + return ERR_CAST(inode); if (inode) { struct gfs2_glock *gl = GFS2_I(inode)->i_gl; diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c index 050d29c0a5b5..bb5433608a42 100644 --- a/fs/hfsplus/btree.c +++ b/fs/hfsplus/btree.c @@ -22,6 +22,7 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id) struct hfs_btree *tree; struct hfs_btree_header_rec *head; struct address_space *mapping; + struct inode *inode; struct page *page; unsigned int size; @@ -33,9 +34,10 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id) spin_lock_init(&tree->hash_lock); tree->sb = sb; tree->cnid = id; - tree->inode = iget(sb, id); - if (!tree->inode) + inode = hfsplus_iget(sb, id); + if (IS_ERR(inode)) goto free_tree; + tree->inode = inode; mapping = tree->inode->i_mapping; page = read_mapping_page(mapping, 0, NULL); diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c index 1955ee61251c..29683645fa0a 100644 --- a/fs/hfsplus/dir.c +++ b/fs/hfsplus/dir.c @@ -97,9 +97,9 @@ again: goto fail; } hfs_find_exit(&fd); - inode = iget(dir->i_sb, cnid); - if (!inode) - return ERR_PTR(-EACCES); + inode = hfsplus_iget(dir->i_sb, cnid); + if (IS_ERR(inode)) + return ERR_CAST(inode); if (S_ISREG(inode->i_mode)) HFSPLUS_I(inode).dev = linkid; out: diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index d9f5eda6d039..d72d0a8b25aa 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -345,6 +345,9 @@ int hfsplus_parse_options(char *, struct hfsplus_sb_info *); void hfsplus_fill_defaults(struct hfsplus_sb_info *); int hfsplus_show_options(struct seq_file *, struct vfsmount *); +/* super.c */ +struct inode *hfsplus_iget(struct super_block *, unsigned long); + /* tables.c */ extern u16 hfsplus_case_fold_table[]; extern u16 hfsplus_decompose_table[]; diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index ecf70dafb643..b0f9ad362d1d 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -20,11 +20,18 @@ static void hfsplus_destroy_inode(struct inode *inode); #include "hfsplus_fs.h" -static void hfsplus_read_inode(struct inode *inode) +struct inode *hfsplus_iget(struct super_block *sb, unsigned long ino) { struct hfs_find_data fd; struct hfsplus_vh *vhdr; - int err; + struct inode *inode; + long err = -EIO; + + inode = iget_locked(sb, ino); + if (!inode) + return ERR_PTR(-ENOMEM); + if (!(inode->i_state & I_NEW)) + return inode; INIT_LIST_HEAD(&HFSPLUS_I(inode).open_dir_list); init_MUTEX(&HFSPLUS_I(inode).extents_lock); @@ -41,7 +48,7 @@ static void hfsplus_read_inode(struct inode *inode) hfs_find_exit(&fd); if (err) goto bad_inode; - return; + goto done; } vhdr = HFSPLUS_SB(inode->i_sb).s_vhdr; switch(inode->i_ino) { @@ -70,10 +77,13 @@ static void hfsplus_read_inode(struct inode *inode) goto bad_inode; } - return; +done: + unlock_new_inode(inode); + return inode; - bad_inode: - make_bad_inode(inode); +bad_inode: + iget_failed(inode); + return ERR_PTR(err); } static int hfsplus_write_inode(struct inode *inode, int unused) @@ -262,7 +272,6 @@ static int hfsplus_remount(struct super_block *sb, int *flags, char *data) static const struct super_operations hfsplus_sops = { .alloc_inode = hfsplus_alloc_inode, .destroy_inode = hfsplus_destroy_inode, - .read_inode = hfsplus_read_inode, .write_inode = hfsplus_write_inode, .clear_inode = hfsplus_clear_inode, .put_super = hfsplus_put_super, @@ -278,7 +287,7 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) struct hfsplus_sb_info *sbi; hfsplus_cat_entry entry; struct hfs_find_data fd; - struct inode *root; + struct inode *root, *inode; struct qstr str; struct nls_table *nls = NULL; int err = -EINVAL; @@ -366,18 +375,25 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) goto cleanup; } - HFSPLUS_SB(sb).alloc_file = iget(sb, HFSPLUS_ALLOC_CNID); - if (!HFSPLUS_SB(sb).alloc_file) { + inode = hfsplus_iget(sb, HFSPLUS_ALLOC_CNID); + if (IS_ERR(inode)) { printk(KERN_ERR "hfs: failed to load allocation file\n"); + err = PTR_ERR(inode); goto cleanup; } + HFSPLUS_SB(sb).alloc_file = inode; /* Load the root directory */ - root = iget(sb, HFSPLUS_ROOT_CNID); + root = hfsplus_iget(sb, HFSPLUS_ROOT_CNID); + if (IS_ERR(root)) { + printk(KERN_ERR "hfs: failed to load root directory\n"); + err = PTR_ERR(root); + goto cleanup; + } sb->s_root = d_alloc_root(root); if (!sb->s_root) { - printk(KERN_ERR "hfs: failed to load root directory\n"); iput(root); + err = -ENOMEM; goto cleanup; } sb->s_root->d_op = &hfsplus_dentry_operations; @@ -390,9 +406,12 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) hfs_find_exit(&fd); if (entry.type != cpu_to_be16(HFSPLUS_FOLDER)) goto cleanup; - HFSPLUS_SB(sb).hidden_dir = iget(sb, be32_to_cpu(entry.folder.id)); - if (!HFSPLUS_SB(sb).hidden_dir) + inode = hfsplus_iget(sb, be32_to_cpu(entry.folder.id)); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); goto cleanup; + } + HFSPLUS_SB(sb).hidden_dir = inode; } else hfs_find_exit(&fd); diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 8966b050196e..2b9b35733aac 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -202,7 +202,7 @@ static char *follow_link(char *link) return ERR_PTR(n); } -static int read_inode(struct inode *ino) +static int hostfs_read_inode(struct inode *ino) { char *name; int err = 0; @@ -233,6 +233,25 @@ static int read_inode(struct inode *ino) return err; } +static struct inode *hostfs_iget(struct super_block *sb) +{ + struct inode *inode; + long ret; + + inode = iget_locked(sb, 0); + if (!inode) + return ERR_PTR(-ENOMEM); + if (inode->i_state & I_NEW) { + ret = hostfs_read_inode(inode); + if (ret < 0) { + iget_failed(inode); + return ERR_PTR(ret); + } + unlock_new_inode(inode); + } + return inode; +} + int hostfs_statfs(struct dentry *dentry, struct kstatfs *sf) { /* @@ -303,17 +322,11 @@ static void hostfs_destroy_inode(struct inode *inode) kfree(HOSTFS_I(inode)); } -static void hostfs_read_inode(struct inode *inode) -{ - read_inode(inode); -} - static const struct super_operations hostfs_sbops = { .alloc_inode = hostfs_alloc_inode, .drop_inode = generic_delete_inode, .delete_inode = hostfs_delete_inode, .destroy_inode = hostfs_destroy_inode, - .read_inode = hostfs_read_inode, .statfs = hostfs_statfs, }; @@ -571,10 +584,11 @@ int hostfs_create(struct inode *dir, struct dentry *dentry, int mode, char *name; int error, fd; - error = -ENOMEM; - inode = iget(dir->i_sb, 0); - if (inode == NULL) + inode = hostfs_iget(dir->i_sb); + if (IS_ERR(inode)) { + error = PTR_ERR(inode); goto out; + } error = init_inode(inode, dentry); if (error) @@ -615,10 +629,11 @@ struct dentry *hostfs_lookup(struct inode *ino, struct dentry *dentry, char *name; int err; - err = -ENOMEM; - inode = iget(ino->i_sb, 0); - if (inode == NULL) + inode = hostfs_iget(ino->i_sb); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); goto out; + } err = init_inode(inode, dentry); if (err) @@ -736,11 +751,13 @@ int hostfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) { struct inode *inode; char *name; - int err = -ENOMEM; + int err; - inode = iget(dir->i_sb, 0); - if (inode == NULL) + inode = hostfs_iget(dir->i_sb); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); goto out; + } err = init_inode(inode, dentry); if (err) @@ -952,9 +969,11 @@ static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent) sprintf(host_root_path, "%s/%s", root_ino, req_root); - root_inode = iget(sb, 0); - if (root_inode == NULL) + root_inode = hostfs_iget(sb); + if (IS_ERR(root_inode)) { + err = PTR_ERR(root_inode); goto out_free; + } err = init_inode(root_inode, NULL); if (err) @@ -972,7 +991,7 @@ static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent) if (sb->s_root == NULL) goto out_put; - err = read_inode(root_inode); + err = hostfs_read_inode(root_inode); if (err) { /* No iput in this case because the dput does that for us */ dput(sb->s_root); diff --git a/fs/hppfs/hppfs_kern.c b/fs/hppfs/hppfs_kern.c index affb7412125e..a1e1f0f61aa5 100644 --- a/fs/hppfs/hppfs_kern.c +++ b/fs/hppfs/hppfs_kern.c @@ -155,6 +155,20 @@ static void hppfs_read_inode(struct inode *ino) ino->i_blocks = proc_ino->i_blocks; } +static struct inode *hppfs_iget(struct super_block *sb) +{ + struct inode *inode; + + inode = iget_locked(sb, 0); + if (!inode) + return ERR_PTR(-ENOMEM); + if (inode->i_state & I_NEW) { + hppfs_read_inode(inode); + unlock_new_inode(inode); + } + return inode; +} + static struct dentry *hppfs_lookup(struct inode *ino, struct dentry *dentry, struct nameidata *nd) { @@ -190,9 +204,11 @@ static struct dentry *hppfs_lookup(struct inode *ino, struct dentry *dentry, if(IS_ERR(proc_dentry)) return(proc_dentry); - inode = iget(ino->i_sb, 0); - if(inode == NULL) + inode = hppfs_iget(ino->i_sb); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); goto out_dput; + } err = init_inode(inode, proc_dentry); if(err) @@ -652,7 +668,6 @@ static void hppfs_destroy_inode(struct inode *inode) static const struct super_operations hppfs_sbops = { .alloc_inode = hppfs_alloc_inode, .destroy_inode = hppfs_destroy_inode, - .read_inode = hppfs_read_inode, .delete_inode = hppfs_delete_inode, .statfs = hppfs_statfs, }; @@ -745,9 +760,11 @@ static int hppfs_fill_super(struct super_block *sb, void *d, int silent) sb->s_magic = HPPFS_SUPER_MAGIC; sb->s_op = &hppfs_sbops; - root_inode = iget(sb, 0); - if(root_inode == NULL) + root_inode = hppfs_iget(sb); + if (IS_ERR(root_inode)) { + err = PTR_ERR(root_inode); goto out; + } err = init_inode(root_inode, proc_sb->s_root); if(err) diff --git a/fs/inode.c b/fs/inode.c index 276ffd6b6fdd..53245ffcf93d 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -928,8 +928,6 @@ EXPORT_SYMBOL(ilookup); * @set: callback used to initialize a new struct inode * @data: opaque data pointer to pass to @test and @set * - * This is iget() without the read_inode() portion of get_new_inode(). - * * iget5_locked() uses ifind() to search for the inode specified by @hashval * and @data in the inode cache and if present it is returned with an increased * reference count. This is a generalized version of iget_locked() for file @@ -966,8 +964,6 @@ EXPORT_SYMBOL(iget5_locked); * @sb: super block of file system * @ino: inode number to get * - * This is iget() without the read_inode() portion of get_new_inode_fast(). - * * iget_locked() uses ifind_fast() to search for the inode specified by @ino in * the inode cache and if present it is returned with an increased reference * count. This is for file systems where the inode number is sufficient for diff --git a/fs/ioctl.c b/fs/ioctl.c index c2a773e8620b..683002fefa55 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -12,12 +12,24 @@ #include <linux/fs.h> #include <linux/security.h> #include <linux/module.h> +#include <linux/uaccess.h> -#include <asm/uaccess.h> #include <asm/ioctls.h> -static long do_ioctl(struct file *filp, unsigned int cmd, - unsigned long arg) +/** + * vfs_ioctl - call filesystem specific ioctl methods + * @filp: [in] open file to invoke ioctl method on + * @cmd: [in] ioctl command to execute + * @arg: [in/out] command-specific argument for ioctl + * + * Invokes filesystem specific ->unlocked_ioctl, if one exists; otherwise + * invokes * filesystem specific ->ioctl method. If neither method exists, + * returns -ENOTTY. + * + * Returns 0 on success, -errno on error. + */ +long vfs_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) { int error = -ENOTTY; @@ -40,123 +52,148 @@ static long do_ioctl(struct file *filp, unsigned int cmd, return error; } +static int ioctl_fibmap(struct file *filp, int __user *p) +{ + struct address_space *mapping = filp->f_mapping; + int res, block; + + /* do we support this mess? */ + if (!mapping->a_ops->bmap) + return -EINVAL; + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + res = get_user(block, p); + if (res) + return res; + lock_kernel(); + res = mapping->a_ops->bmap(mapping, block); + unlock_kernel(); + return put_user(res, p); +} + static int file_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - int error; - int block; - struct inode * inode = filp->f_path.dentry->d_inode; + struct inode *inode = filp->f_path.dentry->d_inode; int __user *p = (int __user *)arg; switch (cmd) { - case FIBMAP: - { - struct address_space *mapping = filp->f_mapping; - int res; - /* do we support this mess? */ - if (!mapping->a_ops->bmap) - return -EINVAL; - if (!capable(CAP_SYS_RAWIO)) - return -EPERM; - if ((error = get_user(block, p)) != 0) - return error; + case FIBMAP: + return ioctl_fibmap(filp, p); + case FIGETBSZ: + return put_user(inode->i_sb->s_blocksize, p); + case FIONREAD: + return put_user(i_size_read(inode) - filp->f_pos, p); + } + return vfs_ioctl(filp, cmd, arg); +} + +static int ioctl_fionbio(struct file *filp, int __user *argp) +{ + unsigned int flag; + int on, error; + + error = get_user(on, argp); + if (error) + return error; + flag = O_NONBLOCK; +#ifdef __sparc__ + /* SunOS compatibility item. */ + if (O_NONBLOCK != O_NDELAY) + flag |= O_NDELAY; +#endif + if (on) + filp->f_flags |= flag; + else + filp->f_flags &= ~flag; + return error; +} + +static int ioctl_fioasync(unsigned int fd, struct file *filp, + int __user *argp) +{ + unsigned int flag; + int on, error; + + error = get_user(on, argp); + if (error) + return error; + flag = on ? FASYNC : 0; + + /* Did FASYNC state change ? */ + if ((flag ^ filp->f_flags) & FASYNC) { + if (filp->f_op && filp->f_op->fasync) { lock_kernel(); - res = mapping->a_ops->bmap(mapping, block); + error = filp->f_op->fasync(fd, filp, on); unlock_kernel(); - return put_user(res, p); - } - case FIGETBSZ: - return put_user(inode->i_sb->s_blocksize, p); - case FIONREAD: - return put_user(i_size_read(inode) - filp->f_pos, p); + } else + error = -ENOTTY; } + if (error) + return error; - return do_ioctl(filp, cmd, arg); + if (on) + filp->f_flags |= FASYNC; + else + filp->f_flags &= ~FASYNC; + return error; } /* * When you add any new common ioctls to the switches above and below * please update compat_sys_ioctl() too. * - * vfs_ioctl() is not for drivers and not intended to be EXPORT_SYMBOL()'d. + * do_vfs_ioctl() is not for drivers and not intended to be EXPORT_SYMBOL()'d. * It's just a simple helper for sys_ioctl and compat_sys_ioctl. */ -int vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd, unsigned long arg) +int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd, + unsigned long arg) { - unsigned int flag; - int on, error = 0; + int error = 0; + int __user *argp = (int __user *)arg; switch (cmd) { - case FIOCLEX: - set_close_on_exec(fd, 1); - break; - - case FIONCLEX: - set_close_on_exec(fd, 0); - break; - - case FIONBIO: - if ((error = get_user(on, (int __user *)arg)) != 0) - break; - flag = O_NONBLOCK; -#ifdef __sparc__ - /* SunOS compatibility item. */ - if(O_NONBLOCK != O_NDELAY) - flag |= O_NDELAY; -#endif - if (on) - filp->f_flags |= flag; - else - filp->f_flags &= ~flag; - break; - - case FIOASYNC: - if ((error = get_user(on, (int __user *)arg)) != 0) - break; - flag = on ? FASYNC : 0; - - /* Did FASYNC state change ? */ - if ((flag ^ filp->f_flags) & FASYNC) { - if (filp->f_op && filp->f_op->fasync) { - lock_kernel(); - error = filp->f_op->fasync(fd, filp, on); - unlock_kernel(); - } - else error = -ENOTTY; - } - if (error != 0) - break; - - if (on) - filp->f_flags |= FASYNC; - else - filp->f_flags &= ~FASYNC; - break; - - case FIOQSIZE: - if (S_ISDIR(filp->f_path.dentry->d_inode->i_mode) || - S_ISREG(filp->f_path.dentry->d_inode->i_mode) || - S_ISLNK(filp->f_path.dentry->d_inode->i_mode)) { - loff_t res = inode_get_bytes(filp->f_path.dentry->d_inode); - error = copy_to_user((loff_t __user *)arg, &res, sizeof(res)) ? -EFAULT : 0; - } - else - error = -ENOTTY; - break; - default: - if (S_ISREG(filp->f_path.dentry->d_inode->i_mode)) - error = file_ioctl(filp, cmd, arg); - else - error = do_ioctl(filp, cmd, arg); - break; + case FIOCLEX: + set_close_on_exec(fd, 1); + break; + + case FIONCLEX: + set_close_on_exec(fd, 0); + break; + + case FIONBIO: + error = ioctl_fionbio(filp, argp); + break; + + case FIOASYNC: + error = ioctl_fioasync(fd, filp, argp); + break; + + case FIOQSIZE: + if (S_ISDIR(filp->f_path.dentry->d_inode->i_mode) || + S_ISREG(filp->f_path.dentry->d_inode->i_mode) || + S_ISLNK(filp->f_path.dentry->d_inode->i_mode)) { + loff_t res = + inode_get_bytes(filp->f_path.dentry->d_inode); + error = copy_to_user((loff_t __user *)arg, &res, + sizeof(res)) ? -EFAULT : 0; + } else + error = -ENOTTY; + break; + default: + if (S_ISREG(filp->f_path.dentry->d_inode->i_mode)) + error = file_ioctl(filp, cmd, arg); + else + error = vfs_ioctl(filp, cmd, arg); + break; } return error; } asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) { - struct file * filp; + struct file *filp; int error = -EBADF; int fput_needed; @@ -168,7 +205,7 @@ asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) if (error) goto out_fput; - error = vfs_ioctl(filp, fd, cmd, arg); + error = do_vfs_ioctl(filp, fd, cmd, arg); out_fput: fput_light(filp, fput_needed); out: diff --git a/fs/isofs/export.c b/fs/isofs/export.c index 29f9753ae5e5..bb219138331a 100644 --- a/fs/isofs/export.c +++ b/fs/isofs/export.c @@ -26,11 +26,9 @@ isofs_export_iget(struct super_block *sb, if (block == 0) return ERR_PTR(-ESTALE); inode = isofs_iget(sb, block, offset); - if (inode == NULL) - return ERR_PTR(-ENOMEM); - if (is_bad_inode(inode) - || (generation && inode->i_generation != generation)) - { + if (IS_ERR(inode)) + return ERR_CAST(inode); + if (generation && inode->i_generation != generation) { iput(inode); return ERR_PTR(-ESTALE); } @@ -110,8 +108,10 @@ static struct dentry *isofs_export_get_parent(struct dentry *child) parent_inode = isofs_iget(child_inode->i_sb, parent_block, parent_offset); - if (parent_inode == NULL) { - rv = ERR_PTR(-EACCES); + if (IS_ERR(parent_inode)) { + rv = ERR_CAST(parent_inode); + if (rv != ERR_PTR(-ENOMEM)) + rv = ERR_PTR(-EACCES); goto out; } diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 09e3d306e96f..875d37fb6c70 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -54,7 +54,7 @@ static void isofs_put_super(struct super_block *sb) return; } -static void isofs_read_inode(struct inode *); +static int isofs_read_inode(struct inode *); static int isofs_statfs (struct dentry *, struct kstatfs *); static struct kmem_cache *isofs_inode_cachep; @@ -107,7 +107,6 @@ static int isofs_remount(struct super_block *sb, int *flags, char *data) static const struct super_operations isofs_sops = { .alloc_inode = isofs_alloc_inode, .destroy_inode = isofs_destroy_inode, - .read_inode = isofs_read_inode, .put_super = isofs_put_super, .statfs = isofs_statfs, .remount_fs = isofs_remount, @@ -552,7 +551,7 @@ static int isofs_fill_super(struct super_block *s, void *data, int silent) int joliet_level = 0; int iso_blknum, block; int orig_zonesize; - int table; + int table, error = -EINVAL; unsigned int vol_desc_start; sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); @@ -810,6 +809,8 @@ root_found: * we then decide whether to use the Joliet descriptor. */ inode = isofs_iget(s, sbi->s_firstdatazone, 0); + if (IS_ERR(inode)) + goto out_no_root; /* * If this disk has both Rock Ridge and Joliet on it, then we @@ -829,6 +830,8 @@ root_found: "ISOFS: changing to secondary root\n"); iput(inode); inode = isofs_iget(s, sbi->s_firstdatazone, 0); + if (IS_ERR(inode)) + goto out_no_root; } } @@ -842,8 +845,6 @@ root_found: sbi->s_joliet_level = joliet_level; /* check the root inode */ - if (!inode) - goto out_no_root; if (!inode->i_op) goto out_bad_root; @@ -876,11 +877,14 @@ root_found: */ out_bad_root: printk(KERN_WARNING "%s: root inode not initialized\n", __func__); - goto out_iput; -out_no_root: - printk(KERN_WARNING "%s: get root inode failed\n", __func__); out_iput: iput(inode); + goto out_no_inode; +out_no_root: + error = PTR_ERR(inode); + if (error != -ENOMEM) + printk(KERN_WARNING "%s: get root inode failed\n", __func__); +out_no_inode: #ifdef CONFIG_JOLIET if (sbi->s_nls_iocharset) unload_nls(sbi->s_nls_iocharset); @@ -908,7 +912,7 @@ out_freesbi: kfree(opt.iocharset); kfree(sbi); s->s_fs_info = NULL; - return -EINVAL; + return error; } static int isofs_statfs (struct dentry *dentry, struct kstatfs *buf) @@ -930,7 +934,7 @@ static int isofs_statfs (struct dentry *dentry, struct kstatfs *buf) /* * Get a set of blocks; filling in buffer_heads if already allocated * or getblk() if they are not. Returns the number of blocks inserted - * (0 == error.) + * (-ve == error.) */ int isofs_get_blocks(struct inode *inode, sector_t iblock_s, struct buffer_head **bh, unsigned long nblocks) @@ -940,11 +944,12 @@ int isofs_get_blocks(struct inode *inode, sector_t iblock_s, unsigned int firstext; unsigned long nextblk, nextoff; long iblock = (long)iblock_s; - int section, rv; + int section, rv, error; struct iso_inode_info *ei = ISOFS_I(inode); lock_kernel(); + error = -EIO; rv = 0; if (iblock < 0 || iblock != iblock_s) { printk(KERN_DEBUG "%s: block number too large\n", __func__); @@ -983,8 +988,10 @@ int isofs_get_blocks(struct inode *inode, sector_t iblock_s, offset += sect_size; ninode = isofs_iget(inode->i_sb, nextblk, nextoff); - if (!ninode) + if (IS_ERR(ninode)) { + error = PTR_ERR(ninode); goto abort; + } firstext = ISOFS_I(ninode)->i_first_extent; sect_size = ISOFS_I(ninode)->i_section_size >> ISOFS_BUFFER_BITS(ninode); nextblk = ISOFS_I(ninode)->i_next_section_block; @@ -1015,9 +1022,10 @@ int isofs_get_blocks(struct inode *inode, sector_t iblock_s, rv++; } + error = 0; abort: unlock_kernel(); - return rv; + return rv != 0 ? rv : error; } /* @@ -1026,12 +1034,15 @@ abort: static int isofs_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) { + int ret; + if (create) { printk(KERN_DEBUG "%s: Kernel tries to allocate a block\n", __func__); return -EROFS; } - return isofs_get_blocks(inode, iblock, &bh_result, 1) ? 0 : -EIO; + ret = isofs_get_blocks(inode, iblock, &bh_result, 1); + return ret < 0 ? ret : 0; } static int isofs_bmap(struct inode *inode, sector_t block) @@ -1186,7 +1197,7 @@ out_toomany: goto out; } -static void isofs_read_inode(struct inode *inode) +static int isofs_read_inode(struct inode *inode) { struct super_block *sb = inode->i_sb; struct isofs_sb_info *sbi = ISOFS_SB(sb); @@ -1199,6 +1210,7 @@ static void isofs_read_inode(struct inode *inode) unsigned int de_len; unsigned long offset; struct iso_inode_info *ei = ISOFS_I(inode); + int ret = -EIO; block = ei->i_iget5_block; bh = sb_bread(inode->i_sb, block); @@ -1216,6 +1228,7 @@ static void isofs_read_inode(struct inode *inode) tmpde = kmalloc(de_len, GFP_KERNEL); if (tmpde == NULL) { printk(KERN_INFO "%s: out of memory\n", __func__); + ret = -ENOMEM; goto fail; } memcpy(tmpde, bh->b_data + offset, frag1); @@ -1259,8 +1272,10 @@ static void isofs_read_inode(struct inode *inode) ei->i_section_size = isonum_733(de->size); if (de->flags[-high_sierra] & 0x80) { - if(isofs_read_level3_size(inode)) + ret = isofs_read_level3_size(inode); + if (ret < 0) goto fail; + ret = -EIO; } else { ei->i_next_section_block = 0; ei->i_next_section_offset = 0; @@ -1346,16 +1361,16 @@ static void isofs_read_inode(struct inode *inode) /* XXX - parse_rock_ridge_inode() had already set i_rdev. */ init_special_inode(inode, inode->i_mode, inode->i_rdev); + ret = 0; out: kfree(tmpde); if (bh) brelse(bh); - return; + return ret; out_badread: printk(KERN_WARNING "ISOFS: unable to read i-node block\n"); fail: - make_bad_inode(inode); goto out; } @@ -1394,9 +1409,10 @@ struct inode *isofs_iget(struct super_block *sb, unsigned long hashval; struct inode *inode; struct isofs_iget5_callback_data data; + long ret; if (offset >= 1ul << sb->s_blocksize_bits) - return NULL; + return ERR_PTR(-EINVAL); data.block = block; data.offset = offset; @@ -1406,9 +1422,17 @@ struct inode *isofs_iget(struct super_block *sb, inode = iget5_locked(sb, hashval, &isofs_iget5_test, &isofs_iget5_set, &data); - if (inode && (inode->i_state & I_NEW)) { - sb->s_op->read_inode(inode); - unlock_new_inode(inode); + if (!inode) + return ERR_PTR(-ENOMEM); + + if (inode->i_state & I_NEW) { + ret = isofs_read_inode(inode); + if (ret < 0) { + iget_failed(inode); + inode = ERR_PTR(ret); + } else { + unlock_new_inode(inode); + } } return inode; diff --git a/fs/isofs/namei.c b/fs/isofs/namei.c index e2b4dad39ca9..344b247bc29a 100644 --- a/fs/isofs/namei.c +++ b/fs/isofs/namei.c @@ -179,9 +179,9 @@ struct dentry *isofs_lookup(struct inode *dir, struct dentry *dentry, struct nam inode = NULL; if (found) { inode = isofs_iget(dir->i_sb, block, offset); - if (!inode) { + if (IS_ERR(inode)) { unlock_kernel(); - return ERR_PTR(-EACCES); + return ERR_CAST(inode); } } unlock_kernel(); diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c index f3a1db3098de..6bd48f0a7047 100644 --- a/fs/isofs/rock.c +++ b/fs/isofs/rock.c @@ -474,8 +474,10 @@ repeat: isofs_iget(inode->i_sb, ISOFS_I(inode)->i_first_extent, 0); - if (!reloc) + if (IS_ERR(reloc)) { + ret = PTR_ERR(reloc); goto out; + } inode->i_mode = reloc->i_mode; inode->i_nlink = reloc->i_nlink; inode->i_uid = reloc->i_uid; diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index 787e392ffd41..f948f7e6ec82 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -101,10 +101,10 @@ static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target, ino = fd->ino; up(&dir_f->sem); if (ino) { - inode = iget(dir_i->i_sb, ino); - if (!inode) { + inode = jffs2_iget(dir_i->i_sb, ino); + if (IS_ERR(inode)) { printk(KERN_WARNING "iget() failed for ino #%u\n", ino); - return (ERR_PTR(-EIO)); + return ERR_CAST(inode); } } diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index d2e06f7ea96f..6d1eaddde0ec 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -230,16 +230,23 @@ void jffs2_clear_inode (struct inode *inode) jffs2_do_clear_inode(c, f); } -void jffs2_read_inode (struct inode *inode) +struct inode *jffs2_iget(struct super_block *sb, unsigned long ino) { struct jffs2_inode_info *f; struct jffs2_sb_info *c; struct jffs2_raw_inode latest_node; union jffs2_device_node jdev; + struct inode *inode; dev_t rdev = 0; int ret; - D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino)); + D1(printk(KERN_DEBUG "jffs2_iget(): ino == %lu\n", ino)); + + inode = iget_locked(sb, ino); + if (!inode) + return ERR_PTR(-ENOMEM); + if (!(inode->i_state & I_NEW)) + return inode; f = JFFS2_INODE_INFO(inode); c = JFFS2_SB_INFO(inode->i_sb); @@ -250,9 +257,9 @@ void jffs2_read_inode (struct inode *inode) ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node); if (ret) { - make_bad_inode(inode); up(&f->sem); - return; + iget_failed(inode); + return ERR_PTR(ret); } inode->i_mode = jemode_to_cpu(latest_node.mode); inode->i_uid = je16_to_cpu(latest_node.uid); @@ -303,19 +310,14 @@ void jffs2_read_inode (struct inode *inode) if (f->metadata->size != sizeof(jdev.old) && f->metadata->size != sizeof(jdev.new)) { printk(KERN_NOTICE "Device node has strange size %d\n", f->metadata->size); - up(&f->sem); - jffs2_do_clear_inode(c, f); - make_bad_inode(inode); - return; + goto error_io; } D1(printk(KERN_DEBUG "Reading device numbers from flash\n")); - if (jffs2_read_dnode(c, f, f->metadata, (char *)&jdev, 0, f->metadata->size) < 0) { + ret = jffs2_read_dnode(c, f, f->metadata, (char *)&jdev, 0, f->metadata->size); + if (ret < 0) { /* Eep */ printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino); - up(&f->sem); - jffs2_do_clear_inode(c, f); - make_bad_inode(inode); - return; + goto error; } if (f->metadata->size == sizeof(jdev.old)) rdev = old_decode_dev(je16_to_cpu(jdev.old)); @@ -335,6 +337,16 @@ void jffs2_read_inode (struct inode *inode) up(&f->sem); D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n")); + unlock_new_inode(inode); + return inode; + +error_io: + ret = -EIO; +error: + up(&f->sem); + jffs2_do_clear_inode(c, f); + iget_failed(inode); + return ERR_PTR(ret); } void jffs2_dirty_inode(struct inode *inode) @@ -522,15 +534,16 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent) if ((ret = jffs2_do_mount_fs(c))) goto out_inohash; - ret = -EINVAL; - D1(printk(KERN_DEBUG "jffs2_do_fill_super(): Getting root inode\n")); - root_i = iget(sb, 1); - if (is_bad_inode(root_i)) { + root_i = jffs2_iget(sb, 1); + if (IS_ERR(root_i)) { D1(printk(KERN_WARNING "get root inode failed\n")); - goto out_root_i; + ret = PTR_ERR(root_i); + goto out_root; } + ret = -ENOMEM; + D1(printk(KERN_DEBUG "jffs2_do_fill_super(): d_alloc_root()\n")); sb->s_root = d_alloc_root(root_i); if (!sb->s_root) @@ -546,6 +559,7 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent) out_root_i: iput(root_i); +out_root: jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); if (jffs2_blocks_use_vmalloc(c)) @@ -615,9 +629,9 @@ struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c, jffs2_do_unlink() would need the alloc_sem and we have it. Just iget() it, and if read_inode() is necessary that's OK. */ - inode = iget(OFNI_BS_2SFFJ(c), inum); - if (!inode) - return ERR_PTR(-ENOMEM); + inode = jffs2_iget(OFNI_BS_2SFFJ(c), inum); + if (IS_ERR(inode)) + return ERR_CAST(inode); } if (is_bad_inode(inode)) { printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u. nlink %d\n", diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h index bf64686cf098..1b10d2594092 100644 --- a/fs/jffs2/os-linux.h +++ b/fs/jffs2/os-linux.h @@ -175,7 +175,7 @@ extern const struct inode_operations jffs2_symlink_inode_operations; /* fs.c */ int jffs2_setattr (struct dentry *, struct iattr *); int jffs2_do_setattr (struct inode *, struct iattr *); -void jffs2_read_inode (struct inode *); +struct inode *jffs2_iget(struct super_block *, unsigned long); void jffs2_clear_inode (struct inode *); void jffs2_dirty_inode(struct inode *inode); struct inode *jffs2_new_inode (struct inode *dir_i, int mode, diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index ffa447511e6a..4677355996cc 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -65,7 +65,6 @@ static const struct super_operations jffs2_super_operations = { .alloc_inode = jffs2_alloc_inode, .destroy_inode =jffs2_destroy_inode, - .read_inode = jffs2_read_inode, .put_super = jffs2_put_super, .write_super = jffs2_write_super, .statfs = jffs2_statfs, diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c index 147e2cbee9e4..1b88e6e734ef 100644 --- a/fs/jffs2/write.c +++ b/fs/jffs2/write.c @@ -177,7 +177,7 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2 void *hold_err = fn->raw; /* Release the full_dnode which is now useless, and return */ jffs2_free_full_dnode(fn); - return ERR_PTR(PTR_ERR(hold_err)); + return ERR_CAST(hold_err); } fn->ofs = je32_to_cpu(ri->offset); fn->size = je32_to_cpu(ri->dsize); @@ -313,7 +313,7 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff void *hold_err = fd->raw; /* Release the full_dirent which is now useless, and return */ jffs2_free_full_dirent(fd); - return ERR_PTR(PTR_ERR(hold_err)); + return ERR_CAST(hold_err); } if (retried) { diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c index 4672013802e1..210339784b56 100644 --- a/fs/jfs/inode.c +++ b/fs/jfs/inode.c @@ -31,11 +31,21 @@ #include "jfs_debug.h" -void jfs_read_inode(struct inode *inode) +struct inode *jfs_iget(struct super_block *sb, unsigned long ino) { - if (diRead(inode)) { - make_bad_inode(inode); - return; + struct inode *inode; + int ret; + + inode = iget_locked(sb, ino); + if (!inode) + return ERR_PTR(-ENOMEM); + if (!(inode->i_state & I_NEW)) + return inode; + + ret = diRead(inode); + if (ret < 0) { + iget_failed(inode); + return ERR_PTR(ret); } if (S_ISREG(inode->i_mode)) { @@ -55,6 +65,8 @@ void jfs_read_inode(struct inode *inode) inode->i_op = &jfs_file_inode_operations; init_special_inode(inode, inode->i_mode, inode->i_rdev); } + unlock_new_inode(inode); + return inode; } /* diff --git a/fs/jfs/jfs_inode.h b/fs/jfs/jfs_inode.h index 8e2cf2cde185..95a6a11425e5 100644 --- a/fs/jfs/jfs_inode.h +++ b/fs/jfs/jfs_inode.h @@ -24,7 +24,7 @@ extern struct inode *ialloc(struct inode *, umode_t); extern int jfs_fsync(struct file *, struct dentry *, int); extern int jfs_ioctl(struct inode *, struct file *, unsigned int, unsigned long); -extern void jfs_read_inode(struct inode *); +extern struct inode *jfs_iget(struct super_block *, unsigned long); extern int jfs_commit_inode(struct inode *, int); extern int jfs_write_inode(struct inode*, int); extern void jfs_delete_inode(struct inode *); diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c index f8718de3505e..403cfc24c6fe 100644 --- a/fs/jfs/namei.c +++ b/fs/jfs/namei.c @@ -1462,12 +1462,10 @@ static struct dentry *jfs_lookup(struct inode *dip, struct dentry *dentry, struc } } - ip = iget(dip->i_sb, inum); - if (ip == NULL || is_bad_inode(ip)) { + ip = jfs_iget(dip->i_sb, inum); + if (IS_ERR(ip)) { jfs_err("jfs_lookup: iget failed on inum %d", (uint) inum); - if (ip) - iput(ip); - return ERR_PTR(-EACCES); + return ERR_CAST(ip); } dentry = d_splice_alias(ip, dentry); @@ -1485,12 +1483,11 @@ static struct inode *jfs_nfs_get_inode(struct super_block *sb, if (ino == 0) return ERR_PTR(-ESTALE); - inode = iget(sb, ino); - if (inode == NULL) - return ERR_PTR(-ENOMEM); + inode = jfs_iget(sb, ino); + if (IS_ERR(inode)) + return ERR_CAST(inode); - if (is_bad_inode(inode) || - (generation && inode->i_generation != generation)) { + if (generation && inode->i_generation != generation) { iput(inode); return ERR_PTR(-ESTALE); } @@ -1521,17 +1518,14 @@ struct dentry *jfs_get_parent(struct dentry *dentry) parent_ino = le32_to_cpu(JFS_IP(dentry->d_inode)->i_dtroot.header.idotdot); - inode = iget(sb, parent_ino); - if (inode) { - if (is_bad_inode(inode)) { + inode = jfs_iget(sb, parent_ino); + if (IS_ERR(inode)) { + parent = ERR_CAST(inode); + } else { + parent = d_alloc_anon(inode); + if (!parent) { + parent = ERR_PTR(-ENOMEM); iput(inode); - parent = ERR_PTR(-EACCES); - } else { - parent = d_alloc_anon(inode); - if (!parent) { - parent = ERR_PTR(-ENOMEM); - iput(inode); - } } } diff --git a/fs/jfs/super.c b/fs/jfs/super.c index 70a14001c98f..50ea65451732 100644 --- a/fs/jfs/super.c +++ b/fs/jfs/super.c @@ -414,7 +414,7 @@ static int jfs_fill_super(struct super_block *sb, void *data, int silent) struct inode *inode; int rc; s64 newLVSize = 0; - int flag; + int flag, ret = -EINVAL; jfs_info("In jfs_read_super: s_flags=0x%lx", sb->s_flags); @@ -461,8 +461,10 @@ static int jfs_fill_super(struct super_block *sb, void *data, int silent) * Initialize direct-mapping inode/address-space */ inode = new_inode(sb); - if (inode == NULL) + if (inode == NULL) { + ret = -ENOMEM; goto out_kfree; + } inode->i_ino = 0; inode->i_nlink = 1; inode->i_size = sb->s_bdev->bd_inode->i_size; @@ -494,9 +496,11 @@ static int jfs_fill_super(struct super_block *sb, void *data, int silent) sb->s_magic = JFS_SUPER_MAGIC; - inode = iget(sb, ROOT_I); - if (!inode || is_bad_inode(inode)) + inode = jfs_iget(sb, ROOT_I); + if (IS_ERR(inode)) { + ret = PTR_ERR(inode); goto out_no_root; + } sb->s_root = d_alloc_root(inode); if (!sb->s_root) goto out_no_root; @@ -536,7 +540,7 @@ out_kfree: if (sbi->nls_tab) unload_nls(sbi->nls_tab); kfree(sbi); - return -EINVAL; + return ret; } static void jfs_write_super_lockfs(struct super_block *sb) @@ -726,7 +730,6 @@ out: static const struct super_operations jfs_super_operations = { .alloc_inode = jfs_alloc_inode, .destroy_inode = jfs_destroy_inode, - .read_inode = jfs_read_inode, .dirty_inode = jfs_dirty_inode, .write_inode = jfs_write_inode, .delete_inode = jfs_delete_inode, diff --git a/fs/minix/inode.c b/fs/minix/inode.c index bf4cd316af81..84f6242ba6fc 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -18,7 +18,6 @@ #include <linux/highuid.h> #include <linux/vfs.h> -static void minix_read_inode(struct inode * inode); static int minix_write_inode(struct inode * inode, int wait); static int minix_statfs(struct dentry *dentry, struct kstatfs *buf); static int minix_remount (struct super_block * sb, int * flags, char * data); @@ -96,7 +95,6 @@ static void destroy_inodecache(void) static const struct super_operations minix_sops = { .alloc_inode = minix_alloc_inode, .destroy_inode = minix_destroy_inode, - .read_inode = minix_read_inode, .write_inode = minix_write_inode, .delete_inode = minix_delete_inode, .put_super = minix_put_super, @@ -149,6 +147,7 @@ static int minix_fill_super(struct super_block *s, void *data, int silent) unsigned long i, block; struct inode *root_inode; struct minix_sb_info *sbi; + int ret = -EINVAL; sbi = kzalloc(sizeof(struct minix_sb_info), GFP_KERNEL); if (!sbi) @@ -246,10 +245,13 @@ static int minix_fill_super(struct super_block *s, void *data, int silent) /* set up enough so that it can read an inode */ s->s_op = &minix_sops; - root_inode = iget(s, MINIX_ROOT_INO); - if (!root_inode || is_bad_inode(root_inode)) + root_inode = minix_iget(s, MINIX_ROOT_INO); + if (IS_ERR(root_inode)) { + ret = PTR_ERR(root_inode); goto out_no_root; + } + ret = -ENOMEM; s->s_root = d_alloc_root(root_inode); if (!s->s_root) goto out_iput; @@ -290,6 +292,7 @@ out_freemap: goto out_release; out_no_map: + ret = -ENOMEM; if (!silent) printk("MINIX-fs: can't allocate map\n"); goto out_release; @@ -316,7 +319,7 @@ out_bad_sb: out: s->s_fs_info = NULL; kfree(sbi); - return -EINVAL; + return ret; } static int minix_statfs(struct dentry *dentry, struct kstatfs *buf) @@ -409,7 +412,7 @@ void minix_set_inode(struct inode *inode, dev_t rdev) /* * The minix V1 function to read an inode. */ -static void V1_minix_read_inode(struct inode * inode) +static struct inode *V1_minix_iget(struct inode *inode) { struct buffer_head * bh; struct minix_inode * raw_inode; @@ -418,8 +421,8 @@ static void V1_minix_read_inode(struct inode * inode) raw_inode = minix_V1_raw_inode(inode->i_sb, inode->i_ino, &bh); if (!raw_inode) { - make_bad_inode(inode); - return; + iget_failed(inode); + return ERR_PTR(-EIO); } inode->i_mode = raw_inode->i_mode; inode->i_uid = (uid_t)raw_inode->i_uid; @@ -435,12 +438,14 @@ static void V1_minix_read_inode(struct inode * inode) minix_inode->u.i1_data[i] = raw_inode->i_zone[i]; minix_set_inode(inode, old_decode_dev(raw_inode->i_zone[0])); brelse(bh); + unlock_new_inode(inode); + return inode; } /* * The minix V2 function to read an inode. */ -static void V2_minix_read_inode(struct inode * inode) +static struct inode *V2_minix_iget(struct inode *inode) { struct buffer_head * bh; struct minix2_inode * raw_inode; @@ -449,8 +454,8 @@ static void V2_minix_read_inode(struct inode * inode) raw_inode = minix_V2_raw_inode(inode->i_sb, inode->i_ino, &bh); if (!raw_inode) { - make_bad_inode(inode); - return; + iget_failed(inode); + return ERR_PTR(-EIO); } inode->i_mode = raw_inode->i_mode; inode->i_uid = (uid_t)raw_inode->i_uid; @@ -468,17 +473,27 @@ static void V2_minix_read_inode(struct inode * inode) minix_inode->u.i2_data[i] = raw_inode->i_zone[i]; minix_set_inode(inode, old_decode_dev(raw_inode->i_zone[0])); brelse(bh); + unlock_new_inode(inode); + return inode; } /* * The global function to read an inode. */ -static void minix_read_inode(struct inode * inode) +struct inode *minix_iget(struct super_block *sb, unsigned long ino) { + struct inode *inode; + + inode = iget_locked(sb, ino); + if (!inode) + return ERR_PTR(-ENOMEM); + if (!(inode->i_state & I_NEW)) + return inode; + if (INODE_VERSION(inode) == MINIX_V1) - V1_minix_read_inode(inode); + return V1_minix_iget(inode); else - V2_minix_read_inode(inode); + return V2_minix_iget(inode); } /* diff --git a/fs/minix/minix.h b/fs/minix/minix.h index ac5d3a75cb0d..326edfe96108 100644 --- a/fs/minix/minix.h +++ b/fs/minix/minix.h @@ -45,6 +45,7 @@ struct minix_sb_info { unsigned short s_version; }; +extern struct inode *minix_iget(struct super_block *, unsigned long); extern struct minix_inode * minix_V1_raw_inode(struct super_block *, ino_t, struct buffer_head **); extern struct minix2_inode * minix_V2_raw_inode(struct super_block *, ino_t, struct buffer_head **); extern struct inode * minix_new_inode(const struct inode * dir, int * error); diff --git a/fs/minix/namei.c b/fs/minix/namei.c index f4aa7a939040..102241bc9c79 100644 --- a/fs/minix/namei.c +++ b/fs/minix/namei.c @@ -54,10 +54,9 @@ static struct dentry *minix_lookup(struct inode * dir, struct dentry *dentry, st ino = minix_inode_by_name(dentry); if (ino) { - inode = iget(dir->i_sb, ino); - - if (!inode) - return ERR_PTR(-EACCES); + inode = minix_iget(dir->i_sb, ino); + if (IS_ERR(inode)) + return ERR_CAST(inode); } d_add(dentry, inode); return NULL; diff --git a/fs/nfs/getroot.c b/fs/nfs/getroot.c index e6242cdbaf91..fae97196daad 100644 --- a/fs/nfs/getroot.c +++ b/fs/nfs/getroot.c @@ -96,7 +96,7 @@ struct dentry *nfs_get_root(struct super_block *sb, struct nfs_fh *mntfh) inode = nfs_fhget(sb, mntfh, fsinfo.fattr); if (IS_ERR(inode)) { dprintk("nfs_get_root: get root inode failed\n"); - return ERR_PTR(PTR_ERR(inode)); + return ERR_CAST(inode); } error = nfs_superblock_set_dummy_root(sb, inode); @@ -266,7 +266,7 @@ struct dentry *nfs4_get_root(struct super_block *sb, struct nfs_fh *mntfh) inode = nfs_fhget(sb, mntfh, &fattr); if (IS_ERR(inode)) { dprintk("nfs_get_root: get root inode failed\n"); - return ERR_PTR(PTR_ERR(inode)); + return ERR_CAST(inode); } error = nfs_superblock_set_dummy_root(sb, inode); diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 79b4bf812960..346570f6d848 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -1218,13 +1218,13 @@ static struct svc_export *exp_find(struct auth_domain *clp, int fsid_type, struct svc_export *exp; struct svc_expkey *ek = exp_find_key(clp, fsid_type, fsidv, reqp); if (IS_ERR(ek)) - return ERR_PTR(PTR_ERR(ek)); + return ERR_CAST(ek); exp = exp_get_by_name(clp, ek->ek_mnt, ek->ek_dentry, reqp); cache_put(&ek->h, &svc_expkey_cache); if (IS_ERR(exp)) - return ERR_PTR(PTR_ERR(exp)); + return ERR_CAST(exp); return exp; } diff --git a/fs/openpromfs/inode.c b/fs/openpromfs/inode.c index 6b7ff1618945..d17b4fd204e1 100644 --- a/fs/openpromfs/inode.c +++ b/fs/openpromfs/inode.c @@ -38,6 +38,8 @@ struct op_inode_info { union op_inode_data u; }; +static struct inode *openprom_iget(struct super_block *sb, ino_t ino); + static inline struct op_inode_info *OP_I(struct inode *inode) { return container_of(inode, struct op_inode_info, vfs_inode); @@ -226,10 +228,10 @@ static struct dentry *openpromfs_lookup(struct inode *dir, struct dentry *dentry return ERR_PTR(-ENOENT); found: - inode = iget(dir->i_sb, ino); + inode = openprom_iget(dir->i_sb, ino); mutex_unlock(&op_mutex); - if (!inode) - return ERR_PTR(-EINVAL); + if (IS_ERR(inode)) + return ERR_CAST(inode); ent_oi = OP_I(inode); ent_oi->type = ent_type; ent_oi->u = ent_data; @@ -348,14 +350,23 @@ static void openprom_destroy_inode(struct inode *inode) kmem_cache_free(op_inode_cachep, OP_I(inode)); } -static void openprom_read_inode(struct inode * inode) +static struct inode *openprom_iget(struct super_block *sb, ino_t ino) { - inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; - if (inode->i_ino == OPENPROM_ROOT_INO) { - inode->i_op = &openprom_inode_operations; - inode->i_fop = &openprom_operations; - inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; + struct inode *inode; + + inode = iget_locked(sb, ino); + if (!inode) + return ERR_PTR(-ENOMEM); + if (inode->i_state & I_NEW) { + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + if (inode->i_ino == OPENPROM_ROOT_INO) { + inode->i_op = &openprom_inode_operations; + inode->i_fop = &openprom_operations; + inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; + } + unlock_new_inode(inode); } + return inode; } static int openprom_remount(struct super_block *sb, int *flags, char *data) @@ -367,7 +378,6 @@ static int openprom_remount(struct super_block *sb, int *flags, char *data) static const struct super_operations openprom_sops = { .alloc_inode = openprom_alloc_inode, .destroy_inode = openprom_destroy_inode, - .read_inode = openprom_read_inode, .statfs = simple_statfs, .remount_fs = openprom_remount, }; @@ -376,6 +386,7 @@ static int openprom_fill_super(struct super_block *s, void *data, int silent) { struct inode *root_inode; struct op_inode_info *oi; + int ret; s->s_flags |= MS_NOATIME; s->s_blocksize = 1024; @@ -383,9 +394,11 @@ static int openprom_fill_super(struct super_block *s, void *data, int silent) s->s_magic = OPENPROM_SUPER_MAGIC; s->s_op = &openprom_sops; s->s_time_gran = 1; - root_inode = iget(s, OPENPROM_ROOT_INO); - if (!root_inode) + root_inode = openprom_iget(s, OPENPROM_ROOT_INO); + if (IS_ERR(root_inode)) { + ret = PTR_ERR(root_inode); goto out_no_root; + } oi = OP_I(root_inode); oi->type = op_inode_node; @@ -393,13 +406,15 @@ static int openprom_fill_super(struct super_block *s, void *data, int silent) s->s_root = d_alloc_root(root_inode); if (!s->s_root) - goto out_no_root; + goto out_no_root_dentry; return 0; +out_no_root_dentry: + iput(root_inode); + ret = -ENOMEM; out_no_root: printk("openprom_fill_super: get root inode failed\n"); - iput(root_inode); - return -ENOMEM; + return ret; } static int openprom_get_sb(struct file_system_type *fs_type, diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 1a551d92e1d8..6ecf6396f072 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -73,11 +73,6 @@ static void proc_delete_inode(struct inode *inode) struct vfsmount *proc_mnt; -static void proc_read_inode(struct inode * inode) -{ - inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; -} - static struct kmem_cache * proc_inode_cachep; static struct inode *proc_alloc_inode(struct super_block *sb) @@ -128,7 +123,6 @@ static int proc_remount(struct super_block *sb, int *flags, char *data) static const struct super_operations proc_sops = { .alloc_inode = proc_alloc_inode, .destroy_inode = proc_destroy_inode, - .read_inode = proc_read_inode, .drop_inode = generic_delete_inode, .delete_inode = proc_delete_inode, .statfs = simple_statfs, @@ -401,39 +395,41 @@ struct inode *proc_get_inode(struct super_block *sb, unsigned int ino, if (de != NULL && !try_module_get(de->owner)) goto out_mod; - inode = iget(sb, ino); + inode = iget_locked(sb, ino); if (!inode) goto out_ino; - - PROC_I(inode)->fd = 0; - PROC_I(inode)->pde = de; - if (de) { - if (de->mode) { - inode->i_mode = de->mode; - inode->i_uid = de->uid; - inode->i_gid = de->gid; - } - if (de->size) - inode->i_size = de->size; - if (de->nlink) - inode->i_nlink = de->nlink; - if (de->proc_iops) - inode->i_op = de->proc_iops; - if (de->proc_fops) { - if (S_ISREG(inode->i_mode)) { + if (inode->i_state & I_NEW) { + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + PROC_I(inode)->fd = 0; + PROC_I(inode)->pde = de; + if (de) { + if (de->mode) { + inode->i_mode = de->mode; + inode->i_uid = de->uid; + inode->i_gid = de->gid; + } + if (de->size) + inode->i_size = de->size; + if (de->nlink) + inode->i_nlink = de->nlink; + if (de->proc_iops) + inode->i_op = de->proc_iops; + if (de->proc_fops) { + if (S_ISREG(inode->i_mode)) { #ifdef CONFIG_COMPAT - if (!de->proc_fops->compat_ioctl) - inode->i_fop = - &proc_reg_file_ops_no_compat; - else + if (!de->proc_fops->compat_ioctl) + inode->i_fop = + &proc_reg_file_ops_no_compat; + else #endif - inode->i_fop = &proc_reg_file_ops; + inode->i_fop = &proc_reg_file_ops; + } else { + inode->i_fop = de->proc_fops; + } } - else - inode->i_fop = de->proc_fops; } + unlock_new_inode(inode); } - return inode; out_ino: diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c index 638bdb963213..b31ab78052b3 100644 --- a/fs/qnx4/inode.c +++ b/fs/qnx4/inode.c @@ -125,7 +125,6 @@ static int qnx4_write_inode(struct inode *inode, int unused) static void qnx4_put_super(struct super_block *sb); static struct inode *qnx4_alloc_inode(struct super_block *sb); static void qnx4_destroy_inode(struct inode *inode); -static void qnx4_read_inode(struct inode *); static int qnx4_remount(struct super_block *sb, int *flags, char *data); static int qnx4_statfs(struct dentry *, struct kstatfs *); @@ -133,7 +132,6 @@ static const struct super_operations qnx4_sops = { .alloc_inode = qnx4_alloc_inode, .destroy_inode = qnx4_destroy_inode, - .read_inode = qnx4_read_inode, .put_super = qnx4_put_super, .statfs = qnx4_statfs, .remount_fs = qnx4_remount, @@ -357,6 +355,7 @@ static int qnx4_fill_super(struct super_block *s, void *data, int silent) struct inode *root; const char *errmsg; struct qnx4_sb_info *qs; + int ret = -EINVAL; qs = kzalloc(sizeof(struct qnx4_sb_info), GFP_KERNEL); if (!qs) @@ -396,12 +395,14 @@ static int qnx4_fill_super(struct super_block *s, void *data, int silent) } /* does root not have inode number QNX4_ROOT_INO ?? */ - root = iget(s, QNX4_ROOT_INO * QNX4_INODES_PER_BLOCK); - if (!root) { + root = qnx4_iget(s, QNX4_ROOT_INO * QNX4_INODES_PER_BLOCK); + if (IS_ERR(root)) { printk("qnx4: get inode failed\n"); + ret = PTR_ERR(root); goto out; } + ret = -ENOMEM; s->s_root = d_alloc_root(root); if (s->s_root == NULL) goto outi; @@ -417,7 +418,7 @@ static int qnx4_fill_super(struct super_block *s, void *data, int silent) outnobh: kfree(qs); s->s_fs_info = NULL; - return -EINVAL; + return ret; } static void qnx4_put_super(struct super_block *sb) @@ -462,29 +463,38 @@ static const struct address_space_operations qnx4_aops = { .bmap = qnx4_bmap }; -static void qnx4_read_inode(struct inode *inode) +struct inode *qnx4_iget(struct super_block *sb, unsigned long ino) { struct buffer_head *bh; struct qnx4_inode_entry *raw_inode; - int block, ino; - struct super_block *sb = inode->i_sb; - struct qnx4_inode_entry *qnx4_inode = qnx4_raw_inode(inode); + int block; + struct qnx4_inode_entry *qnx4_inode; + struct inode *inode; - ino = inode->i_ino; + inode = iget_locked(sb, ino); + if (!inode) + return ERR_PTR(-ENOMEM); + if (!(inode->i_state & I_NEW)) + return inode; + + qnx4_inode = qnx4_raw_inode(inode); inode->i_mode = 0; QNX4DEBUG(("Reading inode : [%d]\n", ino)); if (!ino) { - printk("qnx4: bad inode number on dev %s: %d is out of range\n", + printk(KERN_ERR "qnx4: bad inode number on dev %s: %lu is " + "out of range\n", sb->s_id, ino); - return; + iget_failed(inode); + return ERR_PTR(-EIO); } block = ino / QNX4_INODES_PER_BLOCK; if (!(bh = sb_bread(sb, block))) { printk("qnx4: major problem: unable to read inode from dev " "%s\n", sb->s_id); - return; + iget_failed(inode); + return ERR_PTR(-EIO); } raw_inode = ((struct qnx4_inode_entry *) bh->b_data) + (ino % QNX4_INODES_PER_BLOCK); @@ -515,9 +525,16 @@ static void qnx4_read_inode(struct inode *inode) inode->i_op = &page_symlink_inode_operations; inode->i_mapping->a_ops = &qnx4_aops; qnx4_i(inode)->mmu_private = inode->i_size; - } else - printk("qnx4: bad inode %d on dev %s\n",ino,sb->s_id); + } else { + printk(KERN_ERR "qnx4: bad inode %lu on dev %s\n", + ino, sb->s_id); + iget_failed(inode); + brelse(bh); + return ERR_PTR(-EIO); + } brelse(bh); + unlock_new_inode(inode); + return inode; } static struct kmem_cache *qnx4_inode_cachep; diff --git a/fs/qnx4/namei.c b/fs/qnx4/namei.c index 733cdf01d645..775eed3a4085 100644 --- a/fs/qnx4/namei.c +++ b/fs/qnx4/namei.c @@ -128,10 +128,12 @@ struct dentry * qnx4_lookup(struct inode *dir, struct dentry *dentry, struct nam } brelse(bh); - if ((foundinode = iget(dir->i_sb, ino)) == NULL) { + foundinode = qnx4_iget(dir->i_sb, ino); + if (IS_ERR(foundinode)) { unlock_kernel(); - QNX4DEBUG(("qnx4: lookup->iget -> NULL\n")); - return ERR_PTR(-EACCES); + QNX4DEBUG(("qnx4: lookup->iget -> error %ld\n", + PTR_ERR(foundinode))); + return ERR_CAST(foundinode); } out: unlock_kernel(); diff --git a/fs/quota.c b/fs/quota.c index 99b24b52bfc8..84f28dd72116 100644 --- a/fs/quota.c +++ b/fs/quota.c @@ -341,11 +341,11 @@ static inline struct super_block *quotactl_block(const char __user *special) char *tmp = getname(special); if (IS_ERR(tmp)) - return ERR_PTR(PTR_ERR(tmp)); + return ERR_CAST(tmp); bdev = lookup_bdev(tmp); putname(tmp); if (IS_ERR(bdev)) - return ERR_PTR(PTR_ERR(bdev)); + return ERR_CAST(bdev); sb = get_super(bdev); bdput(bdev); if (!sb) diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index 195309857e63..57917932212e 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -1536,7 +1536,7 @@ static struct dentry *reiserfs_get_dentry(struct super_block *sb, if (!inode) inode = ERR_PTR(-ESTALE); if (IS_ERR(inode)) - return ERR_PTR(PTR_ERR(inode)); + return ERR_CAST(inode); result = d_alloc_anon(inode); if (!result) { iput(inode); diff --git a/fs/reiserfs/xattr.c b/fs/reiserfs/xattr.c index a5bd23ce0e46..eba037b3338f 100644 --- a/fs/reiserfs/xattr.c +++ b/fs/reiserfs/xattr.c @@ -155,7 +155,7 @@ static struct dentry *get_xa_file_dentry(const struct inode *inode, xadir = open_xa_dir(inode, flags); if (IS_ERR(xadir)) { - return ERR_PTR(PTR_ERR(xadir)); + return ERR_CAST(xadir); } else if (xadir && !xadir->d_inode) { dput(xadir); return ERR_PTR(-ENODATA); @@ -164,7 +164,7 @@ static struct dentry *get_xa_file_dentry(const struct inode *inode, xafile = lookup_one_len(name, xadir, strlen(name)); if (IS_ERR(xafile)) { dput(xadir); - return ERR_PTR(PTR_ERR(xafile)); + return ERR_CAST(xafile); } if (xafile->d_inode) { /* file exists */ diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c index a49cf5b9a195..00b6f0a518c8 100644 --- a/fs/romfs/inode.c +++ b/fs/romfs/inode.c @@ -84,6 +84,8 @@ struct romfs_inode_info { struct inode vfs_inode; }; +static struct inode *romfs_iget(struct super_block *, unsigned long); + /* instead of private superblock data */ static inline unsigned long romfs_maxsize(struct super_block *sb) { @@ -117,7 +119,7 @@ static int romfs_fill_super(struct super_block *s, void *data, int silent) struct buffer_head *bh; struct romfs_super_block *rsb; struct inode *root; - int sz; + int sz, ret = -EINVAL; /* I would parse the options here, but there are none.. :) */ @@ -157,10 +159,13 @@ static int romfs_fill_super(struct super_block *s, void *data, int silent) & ROMFH_MASK; s->s_op = &romfs_ops; - root = iget(s, sz); - if (!root) + root = romfs_iget(s, sz); + if (IS_ERR(root)) { + ret = PTR_ERR(root); goto out; + } + ret = -ENOMEM; s->s_root = d_alloc_root(root); if (!s->s_root) goto outiput; @@ -173,7 +178,7 @@ outiput: out: brelse(bh); outnobh: - return -EINVAL; + return ret; } /* That's simple too. */ @@ -389,8 +394,11 @@ romfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) if ((be32_to_cpu(ri.next) & ROMFH_TYPE) == ROMFH_HRD) offset = be32_to_cpu(ri.spec) & ROMFH_MASK; - if ((inode = iget(dir->i_sb, offset))) - goto outi; + inode = romfs_iget(dir->i_sb, offset); + if (IS_ERR(inode)) { + res = PTR_ERR(inode); + goto out; + } /* * it's a bit funky, _lookup needs to return an error code @@ -402,7 +410,7 @@ romfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) */ out0: inode = NULL; -outi: res = 0; + res = 0; d_add (dentry, inode); out: unlock_kernel(); @@ -478,20 +486,29 @@ static mode_t romfs_modemap[] = S_IFBLK+0600, S_IFCHR+0600, S_IFSOCK+0644, S_IFIFO+0644 }; -static void -romfs_read_inode(struct inode *i) +static struct inode * +romfs_iget(struct super_block *sb, unsigned long ino) { - int nextfh, ino; + int nextfh; struct romfs_inode ri; + struct inode *i; + + ino &= ROMFH_MASK; + i = iget_locked(sb, ino); + if (!i) + return ERR_PTR(-ENOMEM); + if (!(i->i_state & I_NEW)) + return i; - ino = i->i_ino & ROMFH_MASK; i->i_mode = 0; /* Loop for finding the real hard link */ for(;;) { if (romfs_copyfrom(i, &ri, ino, ROMFH_SIZE) <= 0) { - printk("romfs: read error for inode 0x%x\n", ino); - return; + printk(KERN_ERR "romfs: read error for inode 0x%lx\n", + ino); + iget_failed(i); + return ERR_PTR(-EIO); } /* XXX: do romfs_checksum here too (with name) */ @@ -548,6 +565,8 @@ romfs_read_inode(struct inode *i) init_special_inode(i, ino, MKDEV(nextfh>>16,nextfh&0xffff)); } + unlock_new_inode(i); + return i; } static struct kmem_cache * romfs_inode_cachep; @@ -599,7 +618,6 @@ static int romfs_remount(struct super_block *sb, int *flags, char *data) static const struct super_operations romfs_ops = { .alloc_inode = romfs_alloc_inode, .destroy_inode = romfs_destroy_inode, - .read_inode = romfs_read_inode, .statfs = romfs_statfs, .remount_fs = romfs_remount, }; diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index 81ec6c548c07..c5d60de0658f 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -169,20 +169,27 @@ void sysv_set_inode(struct inode *inode, dev_t rdev) init_special_inode(inode, inode->i_mode, rdev); } -static void sysv_read_inode(struct inode *inode) +struct inode *sysv_iget(struct super_block *sb, unsigned int ino) { - struct super_block * sb = inode->i_sb; struct sysv_sb_info * sbi = SYSV_SB(sb); struct buffer_head * bh; struct sysv_inode * raw_inode; struct sysv_inode_info * si; - unsigned int block, ino = inode->i_ino; + struct inode *inode; + unsigned int block; if (!ino || ino > sbi->s_ninodes) { printk("Bad inode number on dev %s: %d is out of range\n", - inode->i_sb->s_id, ino); - goto bad_inode; + sb->s_id, ino); + return ERR_PTR(-EIO); } + + inode = iget_locked(sb, ino); + if (!inode) + return ERR_PTR(-ENOMEM); + if (!(inode->i_state & I_NEW)) + return inode; + raw_inode = sysv_raw_inode(sb, ino, &bh); if (!raw_inode) { printk("Major problem: unable to read inode from dev %s\n", @@ -214,11 +221,12 @@ static void sysv_read_inode(struct inode *inode) old_decode_dev(fs32_to_cpu(sbi, si->i_data[0]))); else sysv_set_inode(inode, 0); - return; + unlock_new_inode(inode); + return inode; bad_inode: - make_bad_inode(inode); - return; + iget_failed(inode); + return ERR_PTR(-EIO); } static struct buffer_head * sysv_update_inode(struct inode * inode) @@ -328,7 +336,6 @@ static void init_once(struct kmem_cache *cachep, void *p) const struct super_operations sysv_sops = { .alloc_inode = sysv_alloc_inode, .destroy_inode = sysv_destroy_inode, - .read_inode = sysv_read_inode, .write_inode = sysv_write_inode, .delete_inode = sysv_delete_inode, .put_super = sysv_put_super, diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c index 6bd850b7641a..a1f1ef33e81c 100644 --- a/fs/sysv/namei.c +++ b/fs/sysv/namei.c @@ -53,9 +53,9 @@ static struct dentry *sysv_lookup(struct inode * dir, struct dentry * dentry, st ino = sysv_inode_by_name(dentry); if (ino) { - inode = iget(dir->i_sb, ino); - if (!inode) - return ERR_PTR(-EACCES); + inode = sysv_iget(dir->i_sb, ino); + if (IS_ERR(inode)) + return ERR_CAST(inode); } d_add(dentry, inode); return NULL; diff --git a/fs/sysv/super.c b/fs/sysv/super.c index 6f9707a1b954..5a903da54551 100644 --- a/fs/sysv/super.c +++ b/fs/sysv/super.c @@ -332,8 +332,8 @@ static int complete_read_super(struct super_block *sb, int silent, int size) sb->s_magic = SYSV_MAGIC_BASE + sbi->s_type; /* set up enough so that it can read an inode */ sb->s_op = &sysv_sops; - root_inode = iget(sb,SYSV_ROOT_INO); - if (!root_inode || is_bad_inode(root_inode)) { + root_inode = sysv_iget(sb, SYSV_ROOT_INO); + if (IS_ERR(root_inode)) { printk("SysV FS: get root inode failed\n"); return 0; } diff --git a/fs/sysv/sysv.h b/fs/sysv/sysv.h index 64c03bdf06a5..42d51d1c05cd 100644 --- a/fs/sysv/sysv.h +++ b/fs/sysv/sysv.h @@ -141,6 +141,7 @@ extern int __sysv_write_begin(struct file *file, struct address_space *mapping, struct page **pagep, void **fsdata); /* inode.c */ +extern struct inode *sysv_iget(struct super_block *, unsigned int); extern int sysv_write_inode(struct inode *, int); extern int sysv_sync_inode(struct inode *); extern int sysv_sync_file(struct file *, struct dentry *, int); diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index 4320782761ae..489f26bc26d9 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -714,26 +714,30 @@ static int ufs2_read_inode(struct inode *inode, struct ufs2_inode *ufs2_inode) return 0; } -void ufs_read_inode(struct inode * inode) +struct inode *ufs_iget(struct super_block *sb, unsigned long ino) { - struct ufs_inode_info *ufsi = UFS_I(inode); - struct super_block * sb; - struct ufs_sb_private_info * uspi; + struct ufs_inode_info *ufsi; + struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi; struct buffer_head * bh; + struct inode *inode; int err; - UFSD("ENTER, ino %lu\n", inode->i_ino); - - sb = inode->i_sb; - uspi = UFS_SB(sb)->s_uspi; + UFSD("ENTER, ino %lu\n", ino); - if (inode->i_ino < UFS_ROOTINO || - inode->i_ino > (uspi->s_ncg * uspi->s_ipg)) { + if (ino < UFS_ROOTINO || ino > (uspi->s_ncg * uspi->s_ipg)) { ufs_warning(sb, "ufs_read_inode", "bad inode number (%lu)\n", - inode->i_ino); - goto bad_inode; + ino); + return ERR_PTR(-EIO); } + inode = iget_locked(sb, ino); + if (!inode) + return ERR_PTR(-ENOMEM); + if (!(inode->i_state & I_NEW)) + return inode; + + ufsi = UFS_I(inode); + bh = sb_bread(sb, uspi->s_sbbase + ufs_inotofsba(inode->i_ino)); if (!bh) { ufs_warning(sb, "ufs_read_inode", "unable to read inode %lu\n", @@ -765,10 +769,12 @@ void ufs_read_inode(struct inode * inode) brelse(bh); UFSD("EXIT\n"); - return; + unlock_new_inode(inode); + return inode; bad_inode: - make_bad_inode(inode); + iget_failed(inode); + return ERR_PTR(-EIO); } static void ufs1_update_inode(struct inode *inode, struct ufs_inode *ufs_inode) diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c index d8bfbee2fe2b..747a4de6c695 100644 --- a/fs/ufs/namei.c +++ b/fs/ufs/namei.c @@ -57,10 +57,10 @@ static struct dentry *ufs_lookup(struct inode * dir, struct dentry *dentry, stru lock_kernel(); ino = ufs_inode_by_name(dir, dentry); if (ino) { - inode = iget(dir->i_sb, ino); - if (!inode) { + inode = ufs_iget(dir->i_sb, ino); + if (IS_ERR(inode)) { unlock_kernel(); - return ERR_PTR(-EACCES); + return ERR_CAST(inode); } } unlock_kernel(); diff --git a/fs/ufs/super.c b/fs/ufs/super.c index 0072cb33ebec..73deff475e63 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -633,6 +633,7 @@ static int ufs_fill_super(struct super_block *sb, void *data, int silent) unsigned block_size, super_block_size; unsigned flags; unsigned super_block_offset; + int ret = -EINVAL; uspi = NULL; ubh = NULL; @@ -1065,12 +1066,16 @@ magic_found: uspi->s_maxsymlinklen = fs32_to_cpu(sb, usb3->fs_un2.fs_44.fs_maxsymlinklen); - inode = iget(sb, UFS_ROOTINO); - if (!inode || is_bad_inode(inode)) + inode = ufs_iget(sb, UFS_ROOTINO); + if (IS_ERR(inode)) { + ret = PTR_ERR(inode); goto failed; + } sb->s_root = d_alloc_root(inode); - if (!sb->s_root) + if (!sb->s_root) { + ret = -ENOMEM; goto dalloc_failed; + } ufs_setup_cstotal(sb); /* @@ -1092,7 +1097,7 @@ failed: kfree(sbi); sb->s_fs_info = NULL; UFSD("EXIT (FAILED)\n"); - return -EINVAL; + return ret; failed_nomem: UFSD("EXIT (NOMEM)\n"); @@ -1326,7 +1331,6 @@ static ssize_t ufs_quota_write(struct super_block *, int, const char *, size_t, static const struct super_operations ufs_super_ops = { .alloc_inode = ufs_alloc_inode, .destroy_inode = ufs_destroy_inode, - .read_inode = ufs_read_inode, .write_inode = ufs_write_inode, .delete_inode = ufs_delete_inode, .put_super = ufs_put_super, diff --git a/fs/ufs/ufs.h b/fs/ufs/ufs.h index 7faa4cd71a27..fcb9231bb9ed 100644 --- a/fs/ufs/ufs.h +++ b/fs/ufs/ufs.h @@ -106,7 +106,7 @@ extern void ufs_free_inode (struct inode *inode); extern struct inode * ufs_new_inode (struct inode *, int); /* inode.c */ -extern void ufs_read_inode (struct inode *); +extern struct inode *ufs_iget(struct super_block *, unsigned long); extern void ufs_put_inode (struct inode *); extern int ufs_write_inode (struct inode *, int); extern int ufs_sync_inode (struct inode *); diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c index c28add2fbe95..cd450bea9f1a 100644 --- a/fs/vfat/namei.c +++ b/fs/vfat/namei.c @@ -705,7 +705,7 @@ static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry, brelse(sinfo.bh); if (IS_ERR(inode)) { unlock_kernel(); - return ERR_PTR(PTR_ERR(inode)); + return ERR_CAST(inode); } alias = d_find_alias(inode); if (alias) { diff --git a/include/asm-alpha/elf.h b/include/asm-alpha/elf.h index 4b518e3b952c..fc1002ea1e0c 100644 --- a/include/asm-alpha/elf.h +++ b/include/asm-alpha/elf.h @@ -144,8 +144,6 @@ extern int dump_elf_task_fp(elf_fpreg_t *dest, struct task_struct *task); : amask (AMASK_CIX) ? "ev6" : "ev67"); \ }) -#ifdef __KERNEL__ - #define SET_PERSONALITY(EX, IBCS2) \ set_personality(((EX).e_flags & EF_ALPHA_32BIT) \ ? PER_LINUX_32BIT : (IBCS2) ? PER_SVR4 : PER_LINUX) @@ -164,5 +162,4 @@ extern int alpha_l3_cacheshape; NEW_AUX_ENT(AT_L3_CACHESHAPE, alpha_l3_cacheshape); \ } while (0) -#endif /* __KERNEL__ */ #endif /* __ASM_ALPHA_ELF_H */ diff --git a/include/asm-alpha/page.h b/include/asm-alpha/page.h index 8cc97bfd3789..05f09f997d82 100644 --- a/include/asm-alpha/page.h +++ b/include/asm-alpha/page.h @@ -1,8 +1,6 @@ #ifndef _ALPHA_PAGE_H #define _ALPHA_PAGE_H -#ifdef __KERNEL__ - #include <linux/const.h> #include <asm/pal.h> @@ -98,5 +96,4 @@ typedef unsigned long pgprot_t; #include <asm-generic/memory_model.h> #include <asm-generic/page.h> -#endif /* __KERNEL__ */ #endif /* _ALPHA_PAGE_H */ diff --git a/include/asm-alpha/system.h b/include/asm-alpha/system.h index fd9dc889f36c..ed221d6408fc 100644 --- a/include/asm-alpha/system.h +++ b/include/asm-alpha/system.h @@ -681,13 +681,18 @@ __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) return old; } -#define cmpxchg(ptr,o,n) \ +#define cmpxchg(ptr, o, n) \ ({ \ __typeof__(*(ptr)) _o_ = (o); \ __typeof__(*(ptr)) _n_ = (n); \ (__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_, \ (unsigned long)_n_, sizeof(*(ptr))); \ }) +#define cmpxchg64(ptr, o, n) \ + ({ \ + BUILD_BUG_ON(sizeof(*(ptr)) != 8); \ + cmpxchg((ptr), (o), (n)); \ + }) static inline unsigned long __cmpxchg_u8_local(volatile char *m, long old, long new) @@ -803,13 +808,19 @@ __cmpxchg_local(volatile void *ptr, unsigned long old, unsigned long new, return old; } -#define cmpxchg_local(ptr,o,n) \ +#define cmpxchg_local(ptr, o, n) \ ({ \ __typeof__(*(ptr)) _o_ = (o); \ __typeof__(*(ptr)) _n_ = (n); \ (__typeof__(*(ptr))) __cmpxchg_local((ptr), (unsigned long)_o_, \ (unsigned long)_n_, sizeof(*(ptr))); \ }) +#define cmpxchg64_local(ptr, o, n) \ + ({ \ + BUILD_BUG_ON(sizeof(*(ptr)) != 8); \ + cmpxchg_local((ptr), (o), (n)); \ + }) + #endif /* __ASSEMBLY__ */ diff --git a/include/asm-alpha/user.h b/include/asm-alpha/user.h index 7e417fc9d491..a4eb6a4ca8d1 100644 --- a/include/asm-alpha/user.h +++ b/include/asm-alpha/user.h @@ -39,7 +39,7 @@ struct user { unsigned long start_data; /* data starting address */ unsigned long start_stack; /* stack starting address */ long int signal; /* signal causing core dump */ - struct regs * u_ar0; /* help gdb find registers */ + unsigned long u_ar0; /* help gdb find registers */ unsigned long magic; /* identifies a core file */ char u_comm[32]; /* user command name */ }; diff --git a/include/asm-arm/elf.h b/include/asm-arm/elf.h index ec1c685562ce..4ca751627489 100644 --- a/include/asm-arm/elf.h +++ b/include/asm-arm/elf.h @@ -41,7 +41,6 @@ typedef struct user_fp elf_fpregset_t; #endif #define ELF_ARCH EM_ARM -#ifdef __KERNEL__ #ifndef __ASSEMBLY__ /* * This yields a string that ld.so will use to load implementation @@ -115,5 +114,3 @@ extern char elf_platform[]; } while (0) #endif - -#endif diff --git a/include/asm-arm/page.h b/include/asm-arm/page.h index 7e85db77d99b..31ff12f4ffb7 100644 --- a/include/asm-arm/page.h +++ b/include/asm-arm/page.h @@ -10,9 +10,6 @@ #ifndef _ASMARM_PAGE_H #define _ASMARM_PAGE_H - -#ifdef __KERNEL__ - /* PAGE_SHIFT determines the page size */ #define PAGE_SHIFT 12 #define PAGE_SIZE (1UL << PAGE_SHIFT) @@ -192,6 +189,4 @@ typedef unsigned long pgprot_t; #include <asm-generic/page.h> -#endif /* __KERNEL__ */ - #endif diff --git a/include/asm-arm/system.h b/include/asm-arm/system.h index 28425c473e71..6335de9a2bb3 100644 --- a/include/asm-arm/system.h +++ b/include/asm-arm/system.h @@ -363,6 +363,21 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size extern void disable_hlt(void); extern void enable_hlt(void); +#include <asm-generic/cmpxchg-local.h> + +/* + * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make + * them available. + */ +#define cmpxchg_local(ptr, o, n) \ + ((__typeof__(*(ptr)))__cmpxchg_local_generic((ptr), (unsigned long)(o),\ + (unsigned long)(n), sizeof(*(ptr)))) +#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) + +#ifndef CONFIG_SMP +#include <asm-generic/cmpxchg.h> +#endif + #endif /* __ASSEMBLY__ */ #define arch_align_stack(x) (x) diff --git a/include/asm-arm/user.h b/include/asm-arm/user.h index 3e8b0f879159..825c1e7c582d 100644 --- a/include/asm-arm/user.h +++ b/include/asm-arm/user.h @@ -67,7 +67,7 @@ struct user{ esp register. */ long int signal; /* Signal that caused the core dump. */ int reserved; /* No longer used */ - struct pt_regs * u_ar0; /* Used by gdb to help find the values for */ + unsigned long u_ar0; /* Used by gdb to help find the values for */ /* the registers. */ unsigned long magic; /* To uniquely identify a core file */ char u_comm[32]; /* User command that was responsible */ diff --git a/include/asm-avr32/elf.h b/include/asm-avr32/elf.h index d334b4994d2d..64ce40ee1d58 100644 --- a/include/asm-avr32/elf.h +++ b/include/asm-avr32/elf.h @@ -103,8 +103,6 @@ typedef struct user_fpu_struct elf_fpregset_t; #define ELF_PLATFORM (NULL) -#ifdef __KERNEL__ #define SET_PERSONALITY(ex, ibcs2) set_personality(PER_LINUX_32BIT) -#endif #endif /* __ASM_AVR32_ELF_H */ diff --git a/include/asm-avr32/page.h b/include/asm-avr32/page.h index 0f630b3e9932..ee23499cec34 100644 --- a/include/asm-avr32/page.h +++ b/include/asm-avr32/page.h @@ -8,8 +8,6 @@ #ifndef __ASM_AVR32_PAGE_H #define __ASM_AVR32_PAGE_H -#ifdef __KERNEL__ - /* PAGE_SHIFT determines the page size */ #define PAGE_SHIFT 12 #ifdef __ASSEMBLY__ @@ -107,6 +105,4 @@ static inline int get_order(unsigned long size) */ #define HIGHMEM_START 0x20000000UL -#endif /* __KERNEL__ */ - #endif /* __ASM_AVR32_PAGE_H */ diff --git a/include/asm-avr32/system.h b/include/asm-avr32/system.h index c600cc15cbcb..9702c2213e1e 100644 --- a/include/asm-avr32/system.h +++ b/include/asm-avr32/system.h @@ -145,6 +145,29 @@ static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, (unsigned long)(new), \ sizeof(*(ptr)))) +#include <asm-generic/cmpxchg-local.h> + +static inline unsigned long __cmpxchg_local(volatile void *ptr, + unsigned long old, + unsigned long new, int size) +{ + switch (size) { + case 4: + return __cmpxchg_u32(ptr, old, new); + default: + return __cmpxchg_local_generic(ptr, old, new, size); + } + + return old; +} + +#define cmpxchg_local(ptr, old, new) \ + ((typeof(*(ptr)))__cmpxchg_local((ptr), (unsigned long)(old), \ + (unsigned long)(new), \ + sizeof(*(ptr)))) + +#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) + struct pt_regs; void NORET_TYPE die(const char *str, struct pt_regs *regs, long err); void _exception(long signr, struct pt_regs *regs, int code, diff --git a/include/asm-avr32/user.h b/include/asm-avr32/user.h index 060fb3acee49..7e9152f81f5e 100644 --- a/include/asm-avr32/user.h +++ b/include/asm-avr32/user.h @@ -51,7 +51,7 @@ struct user { unsigned long start_data; /* data starting address */ unsigned long start_stack; /* stack starting address */ long int signal; /* signal causing core dump */ - struct regs * u_ar0; /* help gdb find registers */ + unsigned long u_ar0; /* help gdb find registers */ unsigned long magic; /* identifies a core file */ char u_comm[32]; /* user command name */ }; diff --git a/include/asm-blackfin/elf.h b/include/asm-blackfin/elf.h index 5264b5536a70..30303fc8292c 100644 --- a/include/asm-blackfin/elf.h +++ b/include/asm-blackfin/elf.h @@ -120,8 +120,6 @@ do { \ #define ELF_PLATFORM (NULL) -#ifdef __KERNEL__ #define SET_PERSONALITY(ex, ibcs2) set_personality((ibcs2)?PER_SVR4:PER_LINUX) -#endif #endif diff --git a/include/asm-blackfin/page.h b/include/asm-blackfin/page.h index 8bc86717021c..d5c9d1433781 100644 --- a/include/asm-blackfin/page.h +++ b/include/asm-blackfin/page.h @@ -11,8 +11,6 @@ #endif #define PAGE_MASK (~(PAGE_SIZE-1)) -#ifdef __KERNEL__ - #include <asm/setup.h> #ifndef __ASSEMBLY__ @@ -88,6 +86,5 @@ extern unsigned long memory_end; #include <asm-generic/page.h> #endif /* __ASSEMBLY__ */ -#endif /* __KERNEL__ */ #endif /* _BLACKFIN_PAGE_H */ diff --git a/include/asm-blackfin/system.h b/include/asm-blackfin/system.h index 4a927379ee1c..51494ef5bb41 100644 --- a/include/asm-blackfin/system.h +++ b/include/asm-blackfin/system.h @@ -183,55 +183,20 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr, return tmp; } +#include <asm-generic/cmpxchg-local.h> + /* - * Atomic compare and exchange. Compare OLD with MEM, if identical, - * store NEW in MEM. Return the initial value in MEM. Success is - * indicated by comparing RETURN with OLD. + * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make + * them available. */ -static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, - unsigned long new, int size) -{ - unsigned long tmp = 0; - unsigned long flags = 0; - - local_irq_save(flags); - - switch (size) { - case 1: - __asm__ __volatile__ - ("%0 = b%3 (z);\n\t" - "CC = %1 == %0;\n\t" - "IF !CC JUMP 1f;\n\t" - "b%3 = %2;\n\t" - "1:\n\t" - : "=&d" (tmp) : "d" (old), "d" (new), "m" (*__xg(ptr)) : "memory"); - break; - case 2: - __asm__ __volatile__ - ("%0 = w%3 (z);\n\t" - "CC = %1 == %0;\n\t" - "IF !CC JUMP 1f;\n\t" - "w%3 = %2;\n\t" - "1:\n\t" - : "=&d" (tmp) : "d" (old), "d" (new), "m" (*__xg(ptr)) : "memory"); - break; - case 4: - __asm__ __volatile__ - ("%0 = %3;\n\t" - "CC = %1 == %0;\n\t" - "IF !CC JUMP 1f;\n\t" - "%3 = %2;\n\t" - "1:\n\t" - : "=&d" (tmp) : "d" (old), "d" (new), "m" (*__xg(ptr)) : "memory"); - break; - } - local_irq_restore(flags); - return tmp; -} +#define cmpxchg_local(ptr, o, n) \ + ((__typeof__(*(ptr)))__cmpxchg_local_generic((ptr), (unsigned long)(o),\ + (unsigned long)(n), sizeof(*(ptr)))) +#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) -#define cmpxchg(ptr,o,n)\ - ((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o),\ - (unsigned long)(n),sizeof(*(ptr)))) +#ifndef CONFIG_SMP +#include <asm-generic/cmpxchg.h> +#endif #define prepare_to_switch() do { } while(0) diff --git a/include/asm-blackfin/user.h b/include/asm-blackfin/user.h index abc34629bd59..afe6a0e1f7ce 100644 --- a/include/asm-blackfin/user.h +++ b/include/asm-blackfin/user.h @@ -75,7 +75,7 @@ struct user { esp register. */ long int signal; /* Signal that caused the core dump. */ int reserved; /* No longer used */ - struct user_regs_struct *u_ar0; + unsigned long u_ar0; /* Used by gdb to help find the values for */ /* the registers. */ unsigned long magic; /* To uniquely identify a core file */ diff --git a/include/asm-cris/elf.h b/include/asm-cris/elf.h index 96a40c1de57c..001f64ad11e8 100644 --- a/include/asm-cris/elf.h +++ b/include/asm-cris/elf.h @@ -45,7 +45,6 @@ typedef unsigned long elf_fpregset_t; #define ELF_DATA ELFDATA2LSB #define ELF_ARCH EM_CRIS -#ifdef __KERNEL__ #include <asm/arch/elf.h> /* The master for these definitions is {binutils}/include/elf/cris.h: */ @@ -91,6 +90,4 @@ typedef unsigned long elf_fpregset_t; #define SET_PERSONALITY(ex, ibcs2) set_personality((ibcs2)?PER_SVR4:PER_LINUX) -#endif /* __KERNEL__ */ - #endif diff --git a/include/asm-cris/page.h b/include/asm-cris/page.h index b84353ef6998..3b0156c46311 100644 --- a/include/asm-cris/page.h +++ b/include/asm-cris/page.h @@ -1,8 +1,6 @@ #ifndef _CRIS_PAGE_H #define _CRIS_PAGE_H -#ifdef __KERNEL__ - #include <asm/arch/page.h> #include <linux/const.h> @@ -74,7 +72,5 @@ typedef struct { unsigned long pgprot; } pgprot_t; #include <asm-generic/memory_model.h> #include <asm-generic/page.h> -#endif /* __KERNEL__ */ - #endif /* _CRIS_PAGE_H */ diff --git a/include/asm-cris/system.h b/include/asm-cris/system.h index fea0e8d57cb5..5bcfe5a10907 100644 --- a/include/asm-cris/system.h +++ b/include/asm-cris/system.h @@ -66,6 +66,21 @@ static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int siz return x; } +#include <asm-generic/cmpxchg-local.h> + +/* + * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make + * them available. + */ +#define cmpxchg_local(ptr, o, n) \ + ((__typeof__(*(ptr)))__cmpxchg_local_generic((ptr), (unsigned long)(o),\ + (unsigned long)(n), sizeof(*(ptr)))) +#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) + +#ifndef CONFIG_SMP +#include <asm-generic/cmpxchg.h> +#endif + #define arch_align_stack(x) (x) void default_idle(void); diff --git a/include/asm-cris/user.h b/include/asm-cris/user.h index 2538e2a003df..73e60fcbcf38 100644 --- a/include/asm-cris/user.h +++ b/include/asm-cris/user.h @@ -38,7 +38,7 @@ struct user { unsigned long start_data; /* data starting address */ unsigned long start_stack; /* stack starting address */ long int signal; /* signal causing core dump */ - struct regs * u_ar0; /* help gdb find registers */ + unsigned long u_ar0; /* help gdb find registers */ unsigned long magic; /* identifies a core file */ char u_comm[32]; /* user command name */ }; diff --git a/include/asm-frv/Kbuild b/include/asm-frv/Kbuild index 966a9836d556..bc3f12c5b7e0 100644 --- a/include/asm-frv/Kbuild +++ b/include/asm-frv/Kbuild @@ -4,4 +4,3 @@ header-y += registers.h unifdef-y += termios.h unifdef-y += ptrace.h -unifdef-y += page.h diff --git a/include/asm-frv/elf.h b/include/asm-frv/elf.h index 7df58a3e6e4a..9fb946bb7dc9 100644 --- a/include/asm-frv/elf.h +++ b/include/asm-frv/elf.h @@ -137,8 +137,6 @@ do { \ #define ELF_PLATFORM (NULL) -#ifdef __KERNEL__ #define SET_PERSONALITY(ex, ibcs2) set_personality((ibcs2)?PER_SVR4:PER_LINUX) -#endif #endif diff --git a/include/asm-frv/page.h b/include/asm-frv/page.h index bd9bd2d9cc78..cacc045700de 100644 --- a/include/asm-frv/page.h +++ b/include/asm-frv/page.h @@ -1,8 +1,6 @@ #ifndef _ASM_PAGE_H #define _ASM_PAGE_H -#ifdef __KERNEL__ - #include <asm/virtconvert.h> #include <asm/mem-layout.h> #include <asm/sections.h> @@ -79,6 +77,4 @@ extern unsigned long max_pfn; #include <asm-generic/memory_model.h> #include <asm-generic/page.h> -#endif /* __KERNEL__ */ - #endif /* _ASM_PAGE_H */ diff --git a/include/asm-frv/system.h b/include/asm-frv/system.h index 9f5663ba19f8..59be5443a68f 100644 --- a/include/asm-frv/system.h +++ b/include/asm-frv/system.h @@ -268,5 +268,29 @@ extern uint32_t __cmpxchg_32(uint32_t *v, uint32_t test, uint32_t new); #endif +#include <asm-generic/cmpxchg-local.h> + +static inline unsigned long __cmpxchg_local(volatile void *ptr, + unsigned long old, + unsigned long new, int size) +{ + switch (size) { + case 4: + return cmpxchg(ptr, old, new); + default: + return __cmpxchg_local_generic(ptr, old, new, size); + } + + return old; +} + +/* + * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make + * them available. + */ +#define cmpxchg_local(ptr, o, n) \ + ((__typeof__(*(ptr)))__cmpxchg_local((ptr), (unsigned long)(o), \ + (unsigned long)(n), sizeof(*(ptr)))) +#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) #endif /* _ASM_SYSTEM_H */ diff --git a/include/asm-generic/Kbuild.asm b/include/asm-generic/Kbuild.asm index 8fd81713cfc0..57ba60635959 100644 --- a/include/asm-generic/Kbuild.asm +++ b/include/asm-generic/Kbuild.asm @@ -27,8 +27,3 @@ unifdef-y += termbits.h unifdef-y += termios.h unifdef-y += types.h unifdef-y += unistd.h -unifdef-y += user.h - -# These probably shouldn't be exported -unifdef-y += elf.h -unifdef-y += page.h diff --git a/include/asm-generic/cmpxchg-local.h b/include/asm-generic/cmpxchg-local.h new file mode 100644 index 000000000000..b2ba2fc8829a --- /dev/null +++ b/include/asm-generic/cmpxchg-local.h @@ -0,0 +1,65 @@ +#ifndef __ASM_GENERIC_CMPXCHG_LOCAL_H +#define __ASM_GENERIC_CMPXCHG_LOCAL_H + +#include <linux/types.h> + +extern unsigned long wrong_size_cmpxchg(volatile void *ptr); + +/* + * Generic version of __cmpxchg_local (disables interrupts). Takes an unsigned + * long parameter, supporting various types of architectures. + */ +static inline unsigned long __cmpxchg_local_generic(volatile void *ptr, + unsigned long old, unsigned long new, int size) +{ + unsigned long flags, prev; + + /* + * Sanity checking, compile-time. + */ + if (size == 8 && sizeof(unsigned long) != 8) + wrong_size_cmpxchg(ptr); + + local_irq_save(flags); + switch (size) { + case 1: prev = *(u8 *)ptr; + if (prev == old) + *(u8 *)ptr = (u8)new; + break; + case 2: prev = *(u16 *)ptr; + if (prev == old) + *(u16 *)ptr = (u16)new; + break; + case 4: prev = *(u32 *)ptr; + if (prev == old) + *(u32 *)ptr = (u32)new; + break; + case 8: prev = *(u64 *)ptr; + if (prev == old) + *(u64 *)ptr = (u64)new; + break; + default: + wrong_size_cmpxchg(ptr); + } + local_irq_restore(flags); + return prev; +} + +/* + * Generic version of __cmpxchg64_local. Takes an u64 parameter. + */ +static inline u64 __cmpxchg64_local_generic(volatile void *ptr, + u64 old, u64 new) +{ + u64 prev; + unsigned long flags; + + local_irq_save(flags); + prev = *(u64 *)ptr; + if (prev == old) + *(u64 *)ptr = new; + local_irq_restore(flags); + return prev; +} + +#endif diff --git a/include/asm-generic/cmpxchg.h b/include/asm-generic/cmpxchg.h new file mode 100644 index 000000000000..213ac6e8fe39 --- /dev/null +++ b/include/asm-generic/cmpxchg.h @@ -0,0 +1,22 @@ +#ifndef __ASM_GENERIC_CMPXCHG_H +#define __ASM_GENERIC_CMPXCHG_H + +/* + * Generic cmpxchg + * + * Uses the local cmpxchg. Does not support SMP. + */ +#ifdef CONFIG_SMP +#error "Cannot use generic cmpxchg on SMP" +#endif + +/* + * Atomic compare and exchange. + * + * Do not define __HAVE_ARCH_CMPXCHG because we want to use it to check whether + * a cmpxchg primitive faster than repeated local irq save/restore exists. + */ +#define cmpxchg(ptr, o, n) cmpxchg_local((ptr), (o), (n)) +#define cmpxchg64(ptr, o, n) cmpxchg64_local((ptr), (o), (n)) + +#endif diff --git a/include/asm-h8300/elf.h b/include/asm-h8300/elf.h index 7ba6a0af447c..26bfc7e641da 100644 --- a/include/asm-h8300/elf.h +++ b/include/asm-h8300/elf.h @@ -55,9 +55,7 @@ typedef unsigned long elf_fpregset_t; #define ELF_PLATFORM (NULL) -#ifdef __KERNEL__ #define SET_PERSONALITY(ex, ibcs2) set_personality(PER_LINUX) -#endif #define R_H8_NONE 0 #define R_H8_DIR32 1 diff --git a/include/asm-h8300/page.h b/include/asm-h8300/page.h index c8cc81a3aca5..a83492449130 100644 --- a/include/asm-h8300/page.h +++ b/include/asm-h8300/page.h @@ -1,8 +1,6 @@ #ifndef _H8300_PAGE_H #define _H8300_PAGE_H -#ifdef __KERNEL__ - /* PAGE_SHIFT determines the page size */ #define PAGE_SHIFT (12) @@ -79,6 +77,4 @@ extern unsigned long memory_end; #include <asm-generic/memory_model.h> #include <asm-generic/page.h> -#endif /* __KERNEL__ */ - #endif /* _H8300_PAGE_H */ diff --git a/include/asm-h8300/system.h b/include/asm-h8300/system.h index 2c1e83f7b419..4b8e475908ae 100644 --- a/include/asm-h8300/system.h +++ b/include/asm-h8300/system.h @@ -138,6 +138,21 @@ static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int siz asm("jmp @@0"); \ }) +#include <asm-generic/cmpxchg-local.h> + +/* + * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make + * them available. + */ +#define cmpxchg_local(ptr, o, n) \ + ((__typeof__(*(ptr)))__cmpxchg_local_generic((ptr), (unsigned long)(o),\ + (unsigned long)(n), sizeof(*(ptr)))) +#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) + +#ifndef CONFIG_SMP +#include <asm-generic/cmpxchg.h> +#endif + #define arch_align_stack(x) (x) #endif /* _H8300_SYSTEM_H */ diff --git a/include/asm-h8300/user.h b/include/asm-h8300/user.h index 6c64f99af3e1..14a9e18950f1 100644 --- a/include/asm-h8300/user.h +++ b/include/asm-h8300/user.h @@ -62,8 +62,7 @@ struct user{ esp register. */ long int signal; /* Signal that caused the core dump. */ int reserved; /* No longer used */ - struct user_regs_struct *u_ar0; - /* Used by gdb to help find the values for */ + unsigned long u_ar0; /* Used by gdb to help find the values for */ /* the registers. */ unsigned long magic; /* To uniquely identify a core file */ char u_comm[32]; /* User command that was responsible */ diff --git a/include/asm-ia64/elf.h b/include/asm-ia64/elf.h index f10e29b60b00..f8e83eca67a2 100644 --- a/include/asm-ia64/elf.h +++ b/include/asm-ia64/elf.h @@ -177,7 +177,6 @@ extern void ia64_elf_core_copy_regs (struct pt_regs *src, elf_gregset_t dst); relevant until we have real hardware to play with... */ #define ELF_PLATFORM NULL -#ifdef __KERNEL__ #define SET_PERSONALITY(ex, ibcs2) set_personality(PER_LINUX) #define elf_read_implies_exec(ex, executable_stack) \ ((executable_stack!=EXSTACK_DISABLE_X) && ((ex).e_flags & EF_IA_64_LINUX_EXECUTABLE_STACK) != 0) @@ -248,6 +247,4 @@ do { \ } \ } while (0) -#endif /* __KERNEL__ */ - #endif /* _ASM_IA64_ELF_H */ diff --git a/include/asm-ia64/intrinsics.h b/include/asm-ia64/intrinsics.h index 3a95aa432e99..f1135b5b94c3 100644 --- a/include/asm-ia64/intrinsics.h +++ b/include/asm-ia64/intrinsics.h @@ -153,11 +153,17 @@ extern long ia64_cmpxchg_called_with_bad_pointer (void); (__typeof__(old)) _r_; \ }) -#define cmpxchg_acq(ptr,o,n) ia64_cmpxchg(acq, (ptr), (o), (n), sizeof(*(ptr))) -#define cmpxchg_rel(ptr,o,n) ia64_cmpxchg(rel, (ptr), (o), (n), sizeof(*(ptr))) +#define cmpxchg_acq(ptr, o, n) \ + ia64_cmpxchg(acq, (ptr), (o), (n), sizeof(*(ptr))) +#define cmpxchg_rel(ptr, o, n) \ + ia64_cmpxchg(rel, (ptr), (o), (n), sizeof(*(ptr))) /* for compatibility with other platforms: */ -#define cmpxchg(ptr,o,n) cmpxchg_acq(ptr,o,n) +#define cmpxchg(ptr, o, n) cmpxchg_acq((ptr), (o), (n)) +#define cmpxchg64(ptr, o, n) cmpxchg_acq((ptr), (o), (n)) + +#define cmpxchg_local cmpxchg +#define cmpxchg64_local cmpxchg64 #ifdef CONFIG_IA64_DEBUG_CMPXCHG # define CMPXCHG_BUGCHECK_DECL int _cmpxchg_bugcheck_count = 128; diff --git a/include/asm-ia64/page.h b/include/asm-ia64/page.h index d6345464a2b3..8a8aa3fd7cd4 100644 --- a/include/asm-ia64/page.h +++ b/include/asm-ia64/page.h @@ -7,8 +7,6 @@ * David Mosberger-Tang <davidm@hpl.hp.com> */ -# ifdef __KERNEL__ - #include <asm/intrinsics.h> #include <asm/types.h> @@ -227,5 +225,4 @@ get_order (unsigned long size) (((current->personality & READ_IMPLIES_EXEC) != 0) \ ? VM_EXEC : 0)) -# endif /* __KERNEL__ */ #endif /* _ASM_IA64_PAGE_H */ diff --git a/include/asm-ia64/user.h b/include/asm-ia64/user.h index 78e5a20140aa..8b9821110348 100644 --- a/include/asm-ia64/user.h +++ b/include/asm-ia64/user.h @@ -44,7 +44,7 @@ struct user { unsigned long start_data; /* data starting address */ unsigned long start_stack; /* stack starting address */ long int signal; /* signal causing core dump */ - struct regs * u_ar0; /* help gdb find registers */ + unsigned long u_ar0; /* help gdb find registers */ unsigned long magic; /* identifies a core file */ char u_comm[32]; /* user command name */ }; diff --git a/include/asm-m32r/elf.h b/include/asm-m32r/elf.h index bbee8b25d175..67bcd77494a5 100644 --- a/include/asm-m32r/elf.h +++ b/include/asm-m32r/elf.h @@ -129,8 +129,6 @@ typedef elf_fpreg_t elf_fpregset_t; intent than poking at uname or /proc/cpuinfo. */ #define ELF_PLATFORM (NULL) -#ifdef __KERNEL__ #define SET_PERSONALITY(ex, ibcs2) set_personality(PER_LINUX) -#endif #endif /* _ASM_M32R__ELF_H */ diff --git a/include/asm-m32r/local.h b/include/asm-m32r/local.h index def29d095740..22256d138630 100644 --- a/include/asm-m32r/local.h +++ b/include/asm-m32r/local.h @@ -1,6 +1,366 @@ #ifndef __M32R_LOCAL_H #define __M32R_LOCAL_H -#include <asm-generic/local.h> +/* + * linux/include/asm-m32r/local.h + * + * M32R version: + * Copyright (C) 2001, 2002 Hitoshi Yamamoto + * Copyright (C) 2004 Hirokazu Takata <takata at linux-m32r.org> + * Copyright (C) 2007 Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca> + */ + +#include <linux/percpu.h> +#include <asm/assembler.h> +#include <asm/system.h> +#include <asm/local.h> + +/* + * Atomic operations that C can't guarantee us. Useful for + * resource counting etc.. + */ + +/* + * Make sure gcc doesn't try to be clever and move things around + * on us. We need to use _exactly_ the address the user gave us, + * not some alias that contains the same information. + */ +typedef struct { volatile int counter; } local_t; + +#define LOCAL_INIT(i) { (i) } + +/** + * local_read - read local variable + * @l: pointer of type local_t + * + * Atomically reads the value of @l. + */ +#define local_read(l) ((l)->counter) + +/** + * local_set - set local variable + * @l: pointer of type local_t + * @i: required value + * + * Atomically sets the value of @l to @i. + */ +#define local_set(l, i) (((l)->counter) = (i)) + +/** + * local_add_return - add long to local variable and return it + * @i: long value to add + * @l: pointer of type local_t + * + * Atomically adds @i to @l and return (@i + @l). + */ +static inline long local_add_return(long i, local_t *l) +{ + unsigned long flags; + long result; + + local_irq_save(flags); + __asm__ __volatile__ ( + "# local_add_return \n\t" + DCACHE_CLEAR("%0", "r4", "%1") + "ld %0, @%1; \n\t" + "add %0, %2; \n\t" + "st %0, @%1; \n\t" + : "=&r" (result) + : "r" (&l->counter), "r" (i) + : "memory" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r4" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); + local_irq_restore(flags); + + return result; +} + +/** + * local_sub_return - subtract long from local variable and return it + * @i: long value to subtract + * @l: pointer of type local_t + * + * Atomically subtracts @i from @l and return (@l - @i). + */ +static inline long local_sub_return(long i, local_t *l) +{ + unsigned long flags; + long result; + + local_irq_save(flags); + __asm__ __volatile__ ( + "# local_sub_return \n\t" + DCACHE_CLEAR("%0", "r4", "%1") + "ld %0, @%1; \n\t" + "sub %0, %2; \n\t" + "st %0, @%1; \n\t" + : "=&r" (result) + : "r" (&l->counter), "r" (i) + : "memory" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r4" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); + local_irq_restore(flags); + + return result; +} + +/** + * local_add - add long to local variable + * @i: long value to add + * @l: pointer of type local_t + * + * Atomically adds @i to @l. + */ +#define local_add(i, l) ((void) local_add_return((i), (l))) + +/** + * local_sub - subtract the local variable + * @i: long value to subtract + * @l: pointer of type local_t + * + * Atomically subtracts @i from @l. + */ +#define local_sub(i, l) ((void) local_sub_return((i), (l))) + +/** + * local_sub_and_test - subtract value from variable and test result + * @i: integer value to subtract + * @l: pointer of type local_t + * + * Atomically subtracts @i from @l and returns + * true if the result is zero, or false for all + * other cases. + */ +#define local_sub_and_test(i, l) (local_sub_return((i), (l)) == 0) + +/** + * local_inc_return - increment local variable and return it + * @l: pointer of type local_t + * + * Atomically increments @l by 1 and returns the result. + */ +static inline long local_inc_return(local_t *l) +{ + unsigned long flags; + long result; + + local_irq_save(flags); + __asm__ __volatile__ ( + "# local_inc_return \n\t" + DCACHE_CLEAR("%0", "r4", "%1") + "ld %0, @%1; \n\t" + "addi %0, #1; \n\t" + "st %0, @%1; \n\t" + : "=&r" (result) + : "r" (&l->counter) + : "memory" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r4" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); + local_irq_restore(flags); + + return result; +} + +/** + * local_dec_return - decrement local variable and return it + * @l: pointer of type local_t + * + * Atomically decrements @l by 1 and returns the result. + */ +static inline long local_dec_return(local_t *l) +{ + unsigned long flags; + long result; + + local_irq_save(flags); + __asm__ __volatile__ ( + "# local_dec_return \n\t" + DCACHE_CLEAR("%0", "r4", "%1") + "ld %0, @%1; \n\t" + "addi %0, #-1; \n\t" + "st %0, @%1; \n\t" + : "=&r" (result) + : "r" (&l->counter) + : "memory" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r4" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); + local_irq_restore(flags); + + return result; +} + +/** + * local_inc - increment local variable + * @l: pointer of type local_t + * + * Atomically increments @l by 1. + */ +#define local_inc(l) ((void)local_inc_return(l)) + +/** + * local_dec - decrement local variable + * @l: pointer of type local_t + * + * Atomically decrements @l by 1. + */ +#define local_dec(l) ((void)local_dec_return(l)) + +/** + * local_inc_and_test - increment and test + * @l: pointer of type local_t + * + * Atomically increments @l by 1 + * and returns true if the result is zero, or false for all + * other cases. + */ +#define local_inc_and_test(l) (local_inc_return(l) == 0) + +/** + * local_dec_and_test - decrement and test + * @l: pointer of type local_t + * + * Atomically decrements @l by 1 and + * returns true if the result is 0, or false for all + * other cases. + */ +#define local_dec_and_test(l) (local_dec_return(l) == 0) + +/** + * local_add_negative - add and test if negative + * @l: pointer of type local_t + * @i: integer value to add + * + * Atomically adds @i to @l and returns true + * if the result is negative, or false when + * result is greater than or equal to zero. + */ +#define local_add_negative(i, l) (local_add_return((i), (l)) < 0) + +#define local_cmpxchg(l, o, n) (cmpxchg_local(&((l)->counter), (o), (n))) +#define local_xchg(v, new) (xchg_local(&((l)->counter), new)) + +/** + * local_add_unless - add unless the number is a given value + * @l: pointer of type local_t + * @a: the amount to add to l... + * @u: ...unless l is equal to u. + * + * Atomically adds @a to @l, so long as it was not @u. + * Returns non-zero if @l was not @u, and zero otherwise. + */ +static inline int local_add_unless(local_t *l, long a, long u) +{ + long c, old; + c = local_read(l); + for (;;) { + if (unlikely(c == (u))) + break; + old = local_cmpxchg((l), c, c + (a)); + if (likely(old == c)) + break; + c = old; + } + return c != (u); +} + +#define local_inc_not_zero(l) local_add_unless((l), 1, 0) + +static inline void local_clear_mask(unsigned long mask, local_t *addr) +{ + unsigned long flags; + unsigned long tmp; + + local_irq_save(flags); + __asm__ __volatile__ ( + "# local_clear_mask \n\t" + DCACHE_CLEAR("%0", "r5", "%1") + "ld %0, @%1; \n\t" + "and %0, %2; \n\t" + "st %0, @%1; \n\t" + : "=&r" (tmp) + : "r" (addr), "r" (~mask) + : "memory" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r5" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); + local_irq_restore(flags); +} + +static inline void local_set_mask(unsigned long mask, local_t *addr) +{ + unsigned long flags; + unsigned long tmp; + + local_irq_save(flags); + __asm__ __volatile__ ( + "# local_set_mask \n\t" + DCACHE_CLEAR("%0", "r5", "%1") + "ld %0, @%1; \n\t" + "or %0, %2; \n\t" + "st %0, @%1; \n\t" + : "=&r" (tmp) + : "r" (addr), "r" (mask) + : "memory" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r5" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); + local_irq_restore(flags); +} + +/* Atomic operations are already serializing on m32r */ +#define smp_mb__before_local_dec() barrier() +#define smp_mb__after_local_dec() barrier() +#define smp_mb__before_local_inc() barrier() +#define smp_mb__after_local_inc() barrier() + +/* Use these for per-cpu local_t variables: on some archs they are + * much more efficient than these naive implementations. Note they take + * a variable, not an address. + */ + +#define __local_inc(l) ((l)->a.counter++) +#define __local_dec(l) ((l)->a.counter++) +#define __local_add(i, l) ((l)->a.counter += (i)) +#define __local_sub(i, l) ((l)->a.counter -= (i)) + +/* Use these for per-cpu local_t variables: on some archs they are + * much more efficient than these naive implementations. Note they take + * a variable, not an address. + */ + +/* Need to disable preemption for the cpu local counters otherwise we could + still access a variable of a previous CPU in a non local way. */ +#define cpu_local_wrap_v(l) \ + ({ local_t res__; \ + preempt_disable(); \ + res__ = (l); \ + preempt_enable(); \ + res__; }) +#define cpu_local_wrap(l) \ + ({ preempt_disable(); \ + l; \ + preempt_enable(); }) \ + +#define cpu_local_read(l) cpu_local_wrap_v(local_read(&__get_cpu_var(l))) +#define cpu_local_set(l, i) cpu_local_wrap(local_set(&__get_cpu_var(l), (i))) +#define cpu_local_inc(l) cpu_local_wrap(local_inc(&__get_cpu_var(l))) +#define cpu_local_dec(l) cpu_local_wrap(local_dec(&__get_cpu_var(l))) +#define cpu_local_add(i, l) cpu_local_wrap(local_add((i), &__get_cpu_var(l))) +#define cpu_local_sub(i, l) cpu_local_wrap(local_sub((i), &__get_cpu_var(l))) + +#define __cpu_local_inc(l) cpu_local_inc(l) +#define __cpu_local_dec(l) cpu_local_dec(l) +#define __cpu_local_add(i, l) cpu_local_add((i), (l)) +#define __cpu_local_sub(i, l) cpu_local_sub((i), (l)) #endif /* __M32R_LOCAL_H */ diff --git a/include/asm-m32r/page.h b/include/asm-m32r/page.h index 04fd183a2c58..05d43bbbf940 100644 --- a/include/asm-m32r/page.h +++ b/include/asm-m32r/page.h @@ -6,7 +6,6 @@ #define PAGE_SIZE (1UL << PAGE_SHIFT) #define PAGE_MASK (~(PAGE_SIZE-1)) -#ifdef __KERNEL__ #ifndef __ASSEMBLY__ extern void clear_page(void *to); @@ -87,5 +86,4 @@ typedef struct { unsigned long pgprot; } pgprot_t; #include <asm-generic/memory_model.h> #include <asm-generic/page.h> -#endif /* __KERNEL__ */ #endif /* _ASM_M32R_PAGE_H */ diff --git a/include/asm-m32r/system.h b/include/asm-m32r/system.h index 2365de5c2955..70a57c8c002b 100644 --- a/include/asm-m32r/system.h +++ b/include/asm-m32r/system.h @@ -121,12 +121,13 @@ static inline void local_irq_disable(void) #define nop() __asm__ __volatile__ ("nop" : : ) -#define xchg(ptr,x) \ - ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr)))) +#define xchg(ptr, x) \ + ((__typeof__(*(ptr)))__xchg((unsigned long)(x), (ptr), sizeof(*(ptr)))) +#define xchg_local(ptr, x) \ + ((__typeof__(*(ptr)))__xchg_local((unsigned long)(x), (ptr), \ + sizeof(*(ptr)))) -#ifdef CONFIG_SMP extern void __xchg_called_with_bad_pointer(void); -#endif #ifdef CONFIG_CHIP_M32700_TS1 #define DCACHE_CLEAR(reg0, reg1, addr) \ @@ -146,7 +147,7 @@ extern void __xchg_called_with_bad_pointer(void); #endif /* CONFIG_CHIP_M32700_TS1 */ static __always_inline unsigned long -__xchg(unsigned long x, volatile void * ptr, int size) +__xchg(unsigned long x, volatile void *ptr, int size) { unsigned long flags; unsigned long tmp = 0; @@ -186,9 +187,45 @@ __xchg(unsigned long x, volatile void * ptr, int size) #endif /* CONFIG_CHIP_M32700_TS1 */ ); break; +#endif /* CONFIG_SMP */ + default: + __xchg_called_with_bad_pointer(); + } + + local_irq_restore(flags); + + return (tmp); +} + +static __always_inline unsigned long +__xchg_local(unsigned long x, volatile void *ptr, int size) +{ + unsigned long flags; + unsigned long tmp = 0; + + local_irq_save(flags); + + switch (size) { + case 1: + __asm__ __volatile__ ( + "ldb %0, @%2 \n\t" + "stb %1, @%2 \n\t" + : "=&r" (tmp) : "r" (x), "r" (ptr) : "memory"); + break; + case 2: + __asm__ __volatile__ ( + "ldh %0, @%2 \n\t" + "sth %1, @%2 \n\t" + : "=&r" (tmp) : "r" (x), "r" (ptr) : "memory"); + break; + case 4: + __asm__ __volatile__ ( + "ld %0, @%2 \n\t" + "st %1, @%2 \n\t" + : "=&r" (tmp) : "r" (x), "r" (ptr) : "memory"); + break; default: __xchg_called_with_bad_pointer(); -#endif /* CONFIG_SMP */ } local_irq_restore(flags); @@ -228,6 +265,37 @@ __cmpxchg_u32(volatile unsigned int *p, unsigned int old, unsigned int new) return retval; } +static inline unsigned long +__cmpxchg_local_u32(volatile unsigned int *p, unsigned int old, + unsigned int new) +{ + unsigned long flags; + unsigned int retval; + + local_irq_save(flags); + __asm__ __volatile__ ( + DCACHE_CLEAR("%0", "r4", "%1") + "ld %0, @%1; \n" + " bne %0, %2, 1f; \n" + "st %3, @%1; \n" + " bra 2f; \n" + " .fillinsn \n" + "1:" + "st %0, @%1; \n" + " .fillinsn \n" + "2:" + : "=&r" (retval) + : "r" (p), "r" (old), "r" (new) + : "cbit", "memory" +#ifdef CONFIG_CHIP_M32700_TS1 + , "r4" +#endif /* CONFIG_CHIP_M32700_TS1 */ + ); + local_irq_restore(flags); + + return retval; +} + /* This function doesn't exist, so you'll get a linker error if something tries to do an invalid cmpxchg(). */ extern void __cmpxchg_called_with_bad_pointer(void); @@ -247,13 +315,34 @@ __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) return old; } -#define cmpxchg(ptr,o,n) \ - ({ \ - __typeof__(*(ptr)) _o_ = (o); \ - __typeof__(*(ptr)) _n_ = (n); \ - (__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_, \ - (unsigned long)_n_, sizeof(*(ptr))); \ - }) +#define cmpxchg(ptr, o, n) \ + ((__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)(o), \ + (unsigned long)(n), sizeof(*(ptr)))) + +#include <asm-generic/cmpxchg-local.h> + +static inline unsigned long __cmpxchg_local(volatile void *ptr, + unsigned long old, + unsigned long new, int size) +{ + switch (size) { + case 4: + return __cmpxchg_local_u32(ptr, old, new); + default: + return __cmpxchg_local_generic(ptr, old, new, size); + } + + return old; +} + +/* + * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make + * them available. + */ +#define cmpxchg_local(ptr, o, n) \ + ((__typeof__(*(ptr)))__cmpxchg_local((ptr), (unsigned long)(o), \ + (unsigned long)(n), sizeof(*(ptr)))) +#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) #endif /* __KERNEL__ */ diff --git a/include/asm-m32r/user.h b/include/asm-m32r/user.h index 035258d713d0..03b3c11c2aff 100644 --- a/include/asm-m32r/user.h +++ b/include/asm-m32r/user.h @@ -38,7 +38,7 @@ struct user { unsigned long start_data; /* data starting address */ unsigned long start_stack; /* stack starting address */ long int signal; /* signal causing core dump */ - struct regs * u_ar0; /* help gdb find registers */ + unsigned long u_ar0; /* help gdb find registers */ unsigned long magic; /* identifies a core file */ char u_comm[32]; /* user command name */ }; diff --git a/include/asm-m68k/elf.h b/include/asm-m68k/elf.h index eb63b85f9336..14ea42152b97 100644 --- a/include/asm-m68k/elf.h +++ b/include/asm-m68k/elf.h @@ -114,8 +114,6 @@ typedef struct user_m68kfp_struct elf_fpregset_t; #define ELF_PLATFORM (NULL) -#ifdef __KERNEL__ #define SET_PERSONALITY(ex, ibcs2) set_personality((ibcs2)?PER_SVR4:PER_LINUX) -#endif #endif diff --git a/include/asm-m68k/page.h b/include/asm-m68k/page.h index 1431ea0b59e0..3f29e2a03a43 100644 --- a/include/asm-m68k/page.h +++ b/include/asm-m68k/page.h @@ -1,9 +1,6 @@ #ifndef _M68K_PAGE_H #define _M68K_PAGE_H - -#ifdef __KERNEL__ - #include <linux/const.h> /* PAGE_SHIFT determines the page size */ @@ -230,6 +227,4 @@ static inline __attribute_const__ int __virt_to_node_shift(void) #include <asm-generic/page.h> -#endif /* __KERNEL__ */ - #endif /* _M68K_PAGE_H */ diff --git a/include/asm-m68k/system.h b/include/asm-m68k/system.h index caa9b1663e45..dbb6515ffd5b 100644 --- a/include/asm-m68k/system.h +++ b/include/asm-m68k/system.h @@ -154,6 +154,10 @@ static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int siz } #endif +#include <asm-generic/cmpxchg-local.h> + +#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) + /* * Atomic compare and exchange. Compare OLD with MEM, if identical, * store NEW in MEM. Return the initial value in MEM. Success is @@ -185,9 +189,26 @@ static inline unsigned long __cmpxchg(volatile void *p, unsigned long old, return old; } -#define cmpxchg(ptr,o,n)\ - ((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o),\ - (unsigned long)(n),sizeof(*(ptr)))) +#define cmpxchg(ptr, o, n) \ + ((__typeof__(*(ptr)))__cmpxchg((ptr), (unsigned long)(o), \ + (unsigned long)(n), sizeof(*(ptr)))) +#define cmpxchg_local(ptr, o, n) \ + ((__typeof__(*(ptr)))__cmpxchg((ptr), (unsigned long)(o), \ + (unsigned long)(n), sizeof(*(ptr)))) +#else + +/* + * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make + * them available. + */ +#define cmpxchg_local(ptr, o, n) \ + ((__typeof__(*(ptr)))__cmpxchg_local_generic((ptr), (unsigned long)(o),\ + (unsigned long)(n), sizeof(*(ptr)))) + +#ifndef CONFIG_SMP +#include <asm-generic/cmpxchg.h> +#endif + #endif #define arch_align_stack(x) (x) diff --git a/include/asm-m68k/user.h b/include/asm-m68k/user.h index 8c56ccab4849..f1f478d6e050 100644 --- a/include/asm-m68k/user.h +++ b/include/asm-m68k/user.h @@ -72,8 +72,7 @@ struct user{ esp register. */ long int signal; /* Signal that caused the core dump. */ int reserved; /* No longer used */ - struct user_regs_struct *u_ar0; - /* Used by gdb to help find the values for */ + unsigned long u_ar0; /* Used by gdb to help find the values for */ /* the registers. */ struct user_m68kfp_struct* u_fpstate; /* Math Co-processor pointer. */ unsigned long magic; /* To uniquely identify a core file */ diff --git a/include/asm-m68knommu/elf.h b/include/asm-m68knommu/elf.h index 40b1ed6827db..27f0ec70fba8 100644 --- a/include/asm-m68knommu/elf.h +++ b/include/asm-m68knommu/elf.h @@ -105,8 +105,6 @@ typedef struct user_m68kfp_struct elf_fpregset_t; #define ELF_PLATFORM (NULL) -#ifdef __KERNEL__ #define SET_PERSONALITY(ex, ibcs2) set_personality((ibcs2)?PER_SVR4:PER_LINUX) -#endif #endif diff --git a/include/asm-m68knommu/page.h b/include/asm-m68knommu/page.h index 9efa0a9851b1..6af480c7f291 100644 --- a/include/asm-m68knommu/page.h +++ b/include/asm-m68knommu/page.h @@ -1,8 +1,6 @@ #ifndef _M68KNOMMU_PAGE_H #define _M68KNOMMU_PAGE_H -#ifdef __KERNEL__ - /* PAGE_SHIFT determines the page size */ #define PAGE_SHIFT (12) @@ -78,6 +76,4 @@ extern unsigned long memory_end; #include <asm-generic/page.h> -#endif /* __KERNEL__ */ - #endif /* _M68KNOMMU_PAGE_H */ diff --git a/include/asm-m68knommu/system.h b/include/asm-m68knommu/system.h index ee2dc07bae0e..039ab3f81732 100644 --- a/include/asm-m68knommu/system.h +++ b/include/asm-m68knommu/system.h @@ -186,26 +186,20 @@ static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int siz } #endif +#include <asm-generic/cmpxchg-local.h> + /* - * Atomic compare and exchange. Compare OLD with MEM, if identical, - * store NEW in MEM. Return the initial value in MEM. Success is - * indicated by comparing RETURN with OLD. + * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make + * them available. */ -#define __HAVE_ARCH_CMPXCHG 1 - -static __inline__ unsigned long -cmpxchg(volatile int *p, int old, int new) -{ - unsigned long flags; - int prev; - - local_irq_save(flags); - if ((prev = *p) == old) - *p = new; - local_irq_restore(flags); - return(prev); -} +#define cmpxchg_local(ptr, o, n) \ + ((__typeof__(*(ptr)))__cmpxchg_local_generic((ptr), (unsigned long)(o),\ + (unsigned long)(n), sizeof(*(ptr)))) +#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) +#ifndef CONFIG_SMP +#include <asm-generic/cmpxchg.h> +#endif #if defined( CONFIG_M68328 ) || defined( CONFIG_M68EZ328 ) || \ defined (CONFIG_M68360) || defined( CONFIG_M68VZ328 ) diff --git a/include/asm-mips/cmpxchg.h b/include/asm-mips/cmpxchg.h index a5ec0e5dc5b8..4a812c3ceb90 100644 --- a/include/asm-mips/cmpxchg.h +++ b/include/asm-mips/cmpxchg.h @@ -104,4 +104,21 @@ extern void __cmpxchg_called_with_bad_pointer(void); #define cmpxchg(ptr, old, new) __cmpxchg(ptr, old, new, smp_llsc_mb()) #define cmpxchg_local(ptr, old, new) __cmpxchg(ptr, old, new, ) +#define cmpxchg64(ptr, o, n) \ + ({ \ + BUILD_BUG_ON(sizeof(*(ptr)) != 8); \ + cmpxchg((ptr), (o), (n)); \ + }) + +#ifdef CONFIG_64BIT +#define cmpxchg64_local(ptr, o, n) \ + ({ \ + BUILD_BUG_ON(sizeof(*(ptr)) != 8); \ + cmpxchg_local((ptr), (o), (n)); \ + }) +#else +#include <asm-generic/cmpxchg-local.h> +#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) +#endif + #endif /* __ASM_CMPXCHG_H */ diff --git a/include/asm-mips/elf.h b/include/asm-mips/elf.h index 766f91ad5cd3..f69f7acba637 100644 --- a/include/asm-mips/elf.h +++ b/include/asm-mips/elf.h @@ -239,8 +239,6 @@ typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG]; #endif /* !defined(ELF_ARCH) */ -#ifdef __KERNEL__ - struct mips_abi; extern struct mips_abi mips_abi; @@ -328,8 +326,6 @@ extern int dump_task_fpu(struct task_struct *, elf_fpregset_t *); #define ELF_CORE_COPY_FPREGS(tsk, elf_fpregs) \ dump_task_fpu(tsk, elf_fpregs) -#endif /* __KERNEL__ */ - #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE PAGE_SIZE diff --git a/include/asm-mips/page.h b/include/asm-mips/page.h index d2ea983bec06..635aa44d2290 100644 --- a/include/asm-mips/page.h +++ b/include/asm-mips/page.h @@ -9,9 +9,6 @@ #ifndef _ASM_PAGE_H #define _ASM_PAGE_H - -#ifdef __KERNEL__ - #include <spaces.h> /* @@ -190,6 +187,4 @@ typedef struct { unsigned long pgprot; } pgprot_t; #include <asm-generic/memory_model.h> #include <asm-generic/page.h> -#endif /* defined (__KERNEL__) */ - #endif /* _ASM_PAGE_H */ diff --git a/include/asm-mips/user.h b/include/asm-mips/user.h index 61f2a093b91b..afa83a4c1888 100644 --- a/include/asm-mips/user.h +++ b/include/asm-mips/user.h @@ -8,8 +8,6 @@ #ifndef _ASM_USER_H #define _ASM_USER_H -#ifdef __KERNEL__ - #include <asm/page.h> #include <asm/reg.h> @@ -46,7 +44,7 @@ struct user { unsigned long start_data; /* data starting address */ unsigned long start_stack; /* stack starting address */ long int signal; /* signal causing core dump */ - struct regs * u_ar0; /* help gdb find registers */ + unsigned long u_ar0; /* help gdb find registers */ unsigned long magic; /* identifies a core file */ char u_comm[32]; /* user command name */ }; @@ -57,6 +55,4 @@ struct user { #define HOST_DATA_START_ADDR (u.start_data) #define HOST_STACK_END_ADDR (u.start_stack + u.u_ssize * NBPG) -#endif /* __KERNEL__ */ - #endif /* _ASM_USER_H */ diff --git a/include/asm-parisc/atomic.h b/include/asm-parisc/atomic.h index e894ee35074b..57fcc4a5ebb4 100644 --- a/include/asm-parisc/atomic.h +++ b/include/asm-parisc/atomic.h @@ -122,6 +122,39 @@ __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new_, int size) (unsigned long)_n_, sizeof(*(ptr))); \ }) +#include <asm-generic/cmpxchg-local.h> + +static inline unsigned long __cmpxchg_local(volatile void *ptr, + unsigned long old, + unsigned long new_, int size) +{ + switch (size) { +#ifdef CONFIG_64BIT + case 8: return __cmpxchg_u64((unsigned long *)ptr, old, new_); +#endif + case 4: return __cmpxchg_u32(ptr, old, new_); + default: + return __cmpxchg_local_generic(ptr, old, new_, size); + } +} + +/* + * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make + * them available. + */ +#define cmpxchg_local(ptr, o, n) \ + ((__typeof__(*(ptr)))__cmpxchg_local((ptr), (unsigned long)(o), \ + (unsigned long)(n), sizeof(*(ptr)))) +#ifdef CONFIG_64BIT +#define cmpxchg64_local(ptr, o, n) \ + ({ \ + BUILD_BUG_ON(sizeof(*(ptr)) != 8); \ + cmpxchg_local((ptr), (o), (n)); \ + }) +#else +#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) +#endif + /* Note that we need not lock read accesses - aligned word writes/reads * are atomic, so a reader never sees unconsistent values. * diff --git a/include/asm-parisc/elf.h b/include/asm-parisc/elf.h index 8e7946a141de..ce0c0d844c7d 100644 --- a/include/asm-parisc/elf.h +++ b/include/asm-parisc/elf.h @@ -237,14 +237,11 @@ typedef unsigned long elf_greg_t; #define ELF_PLATFORM ("PARISC\0" /*+((boot_cpu_data.x86-3)*5) */) -#ifdef __KERNEL__ #define SET_PERSONALITY(ex, ibcs2) \ current->personality = PER_LINUX; \ current->thread.map_base = DEFAULT_MAP_BASE; \ current->thread.task_size = DEFAULT_TASK_SIZE \ -#endif - /* * Fill in general registers in a core dump. This saves pretty * much the same registers as hp-ux, although in a different order. diff --git a/include/asm-parisc/page.h b/include/asm-parisc/page.h index b59a1504fc7a..b08d9151c71e 100644 --- a/include/asm-parisc/page.h +++ b/include/asm-parisc/page.h @@ -1,8 +1,6 @@ #ifndef _PARISC_PAGE_H #define _PARISC_PAGE_H -#ifdef __KERNEL__ - #include <linux/const.h> #if defined(CONFIG_PARISC_PAGE_SIZE_4KB) @@ -175,6 +173,4 @@ extern int npmem_ranges; #include <asm-generic/memory_model.h> #include <asm-generic/page.h> -#endif /* __KERNEL__ */ - #endif /* _PARISC_PAGE_H */ diff --git a/include/asm-powerpc/cputable.h b/include/asm-powerpc/cputable.h index 528ef183c221..1e79673b7316 100644 --- a/include/asm-powerpc/cputable.h +++ b/include/asm-powerpc/cputable.h @@ -46,7 +46,7 @@ enum powerpc_oprofile_type { PPC_OPROFILE_RS64 = 1, PPC_OPROFILE_POWER4 = 2, PPC_OPROFILE_G4 = 3, - PPC_OPROFILE_BOOKE = 4, + PPC_OPROFILE_FSL_EMB = 4, PPC_OPROFILE_CELL = 5, PPC_OPROFILE_PA6T = 6, }; diff --git a/include/asm-powerpc/dcr-native.h b/include/asm-powerpc/dcr-native.h index af5fb31af559..be6c879e8760 100644 --- a/include/asm-powerpc/dcr-native.h +++ b/include/asm-powerpc/dcr-native.h @@ -59,25 +59,36 @@ do { \ /* R/W of indirect DCRs make use of standard naming conventions for DCRs */ extern spinlock_t dcr_ind_lock; -#define mfdcri(base, reg) \ -({ \ - unsigned long flags; \ - unsigned int val; \ - spin_lock_irqsave(&dcr_ind_lock, flags); \ - mtdcr(DCRN_ ## base ## _CONFIG_ADDR, reg); \ - val = mfdcr(DCRN_ ## base ## _CONFIG_DATA); \ - spin_unlock_irqrestore(&dcr_ind_lock, flags); \ - val; \ -}) +static inline unsigned __mfdcri(int base_addr, int base_data, int reg) +{ + unsigned long flags; + unsigned int val; -#define mtdcri(base, reg, data) \ -do { \ - unsigned long flags; \ - spin_lock_irqsave(&dcr_ind_lock, flags); \ - mtdcr(DCRN_ ## base ## _CONFIG_ADDR, reg); \ - mtdcr(DCRN_ ## base ## _CONFIG_DATA, data); \ - spin_unlock_irqrestore(&dcr_ind_lock, flags); \ -} while (0) + spin_lock_irqsave(&dcr_ind_lock, flags); + __mtdcr(base_addr, reg); + val = __mfdcr(base_data); + spin_unlock_irqrestore(&dcr_ind_lock, flags); + return val; +} + +static inline void __mtdcri(int base_addr, int base_data, int reg, + unsigned val) +{ + unsigned long flags; + + spin_lock_irqsave(&dcr_ind_lock, flags); + __mtdcr(base_addr, reg); + __mtdcr(base_data, val); + spin_unlock_irqrestore(&dcr_ind_lock, flags); +} + +#define mfdcri(base, reg) __mfdcri(DCRN_ ## base ## _CONFIG_ADDR, \ + DCRN_ ## base ## _CONFIG_DATA, \ + reg) + +#define mtdcri(base, reg, data) __mtdcri(DCRN_ ## base ## _CONFIG_ADDR, \ + DCRN_ ## base ## _CONFIG_DATA, \ + reg, data) #endif /* __ASSEMBLY__ */ #endif /* __KERNEL__ */ diff --git a/include/asm-powerpc/elf.h b/include/asm-powerpc/elf.h index 6bd07ef78ac4..9080d85cb9d0 100644 --- a/include/asm-powerpc/elf.h +++ b/include/asm-powerpc/elf.h @@ -165,8 +165,10 @@ typedef elf_vrreg_t elf_vrregset_t32[ELF_NVRREG32]; * This is used to ensure we don't load something for the wrong architecture. */ #define elf_check_arch(x) ((x)->e_machine == ELF_ARCH) +#define compat_elf_check_arch(x) ((x)->e_machine == EM_PPC) #define USE_ELF_CORE_DUMP +#define CORE_DUMP_USE_REGSET #define ELF_EXEC_PAGESIZE PAGE_SIZE /* This is the location that an ET_DYN program is loaded if exec'ed. Typical diff --git a/include/asm-powerpc/mpc512x.h b/include/asm-powerpc/mpc512x.h new file mode 100644 index 000000000000..c48a1658eeac --- /dev/null +++ b/include/asm-powerpc/mpc512x.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: John Rigby, <jrigby@freescale.com>, Friday Apr 13 2007 + * + * Description: + * MPC5121 Prototypes and definitions + * + * This 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 __ASM_POWERPC_MPC512x_H__ +#define __ASM_POWERPC_MPC512x_H__ + +extern unsigned long mpc512x_find_ips_freq(struct device_node *node); + +#endif /* __ASM_POWERPC_MPC512x_H__ */ + diff --git a/include/asm-powerpc/mpc52xx_psc.h b/include/asm-powerpc/mpc52xx_psc.h index bea42b95390f..710c5d36efaa 100644 --- a/include/asm-powerpc/mpc52xx_psc.h +++ b/include/asm-powerpc/mpc52xx_psc.h @@ -190,5 +190,53 @@ struct mpc52xx_psc_fifo { u16 tflwfptr; /* PSC + 0x9e */ }; +#define MPC512x_PSC_FIFO_RESET_SLICE 0x80 +#define MPC512x_PSC_FIFO_ENABLE_SLICE 0x01 +#define MPC512x_PSC_FIFO_ENABLE_DMA 0x04 + +#define MPC512x_PSC_FIFO_EMPTY 0x1 +#define MPC512x_PSC_FIFO_FULL 0x2 +#define MPC512x_PSC_FIFO_ALARM 0x4 +#define MPC512x_PSC_FIFO_URERR 0x8 +#define MPC512x_PSC_FIFO_ORERR 0x01 +#define MPC512x_PSC_FIFO_MEMERROR 0x02 + +struct mpc512x_psc_fifo { + u32 reserved1[10]; + u32 txcmd; /* PSC + 0x80 */ + u32 txalarm; /* PSC + 0x84 */ + u32 txsr; /* PSC + 0x88 */ + u32 txisr; /* PSC + 0x8c */ + u32 tximr; /* PSC + 0x90 */ + u32 txcnt; /* PSC + 0x94 */ + u32 txptr; /* PSC + 0x98 */ + u32 txsz; /* PSC + 0x9c */ + u32 reserved2[7]; + union { + u8 txdata_8; + u16 txdata_16; + u32 txdata_32; + } txdata; /* PSC + 0xbc */ +#define txdata_8 txdata.txdata_8 +#define txdata_16 txdata.txdata_16 +#define txdata_32 txdata.txdata_32 + u32 rxcmd; /* PSC + 0xc0 */ + u32 rxalarm; /* PSC + 0xc4 */ + u32 rxsr; /* PSC + 0xc8 */ + u32 rxisr; /* PSC + 0xcc */ + u32 rximr; /* PSC + 0xd0 */ + u32 rxcnt; /* PSC + 0xd4 */ + u32 rxptr; /* PSC + 0xd8 */ + u32 rxsz; /* PSC + 0xdc */ + u32 reserved3[7]; + union { + u8 rxdata_8; + u16 rxdata_16; + u32 rxdata_32; + } rxdata; /* PSC + 0xfc */ +#define rxdata_8 rxdata.rxdata_8 +#define rxdata_16 rxdata.rxdata_16 +#define rxdata_32 rxdata.rxdata_32 +}; #endif /* __ASM_MPC52xx_PSC_H__ */ diff --git a/include/asm-powerpc/oprofile_impl.h b/include/asm-powerpc/oprofile_impl.h index 938fefb4c4bc..95035c602ba6 100644 --- a/include/asm-powerpc/oprofile_impl.h +++ b/include/asm-powerpc/oprofile_impl.h @@ -54,7 +54,7 @@ struct op_powerpc_model { int num_counters; }; -extern struct op_powerpc_model op_model_fsl_booke; +extern struct op_powerpc_model op_model_fsl_emb; extern struct op_powerpc_model op_model_rs64; extern struct op_powerpc_model op_model_power4; extern struct op_powerpc_model op_model_7450; diff --git a/include/asm-powerpc/page.h b/include/asm-powerpc/page.h index 236a9210e5fc..61e3725bbd37 100644 --- a/include/asm-powerpc/page.h +++ b/include/asm-powerpc/page.h @@ -10,7 +10,6 @@ * 2 of the License, or (at your option) any later version. */ -#ifdef __KERNEL__ #include <asm/asm-compat.h> #include <asm/kdump.h> @@ -194,6 +193,4 @@ struct vm_area_struct; #include <asm-generic/memory_model.h> #endif /* __ASSEMBLY__ */ -#endif /* __KERNEL__ */ - #endif /* _ASM_POWERPC_PAGE_H */ diff --git a/include/asm-powerpc/page_32.h b/include/asm-powerpc/page_32.h index 17110aff26e7..65ea19eec956 100644 --- a/include/asm-powerpc/page_32.h +++ b/include/asm-powerpc/page_32.h @@ -1,6 +1,5 @@ #ifndef _ASM_POWERPC_PAGE_32_H #define _ASM_POWERPC_PAGE_32_H -#ifdef __KERNEL__ #define VM_DATA_DEFAULT_FLAGS VM_DATA_DEFAULT_FLAGS32 @@ -32,5 +31,4 @@ extern void copy_page(void *to, void *from); #endif /* __ASSEMBLY__ */ -#endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_PAGE_32_H */ diff --git a/include/asm-powerpc/page_64.h b/include/asm-powerpc/page_64.h index 4ee82c61e4d7..67834eae5702 100644 --- a/include/asm-powerpc/page_64.h +++ b/include/asm-powerpc/page_64.h @@ -1,6 +1,5 @@ #ifndef _ASM_POWERPC_PAGE_64_H #define _ASM_POWERPC_PAGE_64_H -#ifdef __KERNEL__ /* * Copyright (C) 2001 PPC64 Team, IBM Corp @@ -183,5 +182,4 @@ do { \ #include <asm-generic/page.h> -#endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_PAGE_64_H */ diff --git a/include/asm-powerpc/ptrace.h b/include/asm-powerpc/ptrace.h index ffc150f602b8..891d68932f39 100644 --- a/include/asm-powerpc/ptrace.h +++ b/include/asm-powerpc/ptrace.h @@ -55,6 +55,8 @@ struct pt_regs { #ifdef __powerpc64__ +#define __ARCH_WANT_COMPAT_SYS_PTRACE + #define STACK_FRAME_OVERHEAD 112 /* size of minimum stack frame */ /* Size of dummy stack frame allocated when calling signal handler. */ diff --git a/include/asm-powerpc/reg.h b/include/asm-powerpc/reg.h index 2408a29507e5..0d6238987df8 100644 --- a/include/asm-powerpc/reg.h +++ b/include/asm-powerpc/reg.h @@ -18,6 +18,10 @@ #include <asm/reg_booke.h> #endif /* CONFIG_BOOKE || CONFIG_40x */ +#ifdef CONFIG_FSL_EMB_PERFMON +#include <asm/reg_fsl_emb.h> +#endif + #ifdef CONFIG_8xx #include <asm/reg_8xx.h> #endif /* CONFIG_8xx */ diff --git a/include/asm-powerpc/reg_booke.h b/include/asm-powerpc/reg_booke.h index 0405ef479814..cf54a3f31753 100644 --- a/include/asm-powerpc/reg_booke.h +++ b/include/asm-powerpc/reg_booke.h @@ -9,68 +9,6 @@ #ifndef __ASM_POWERPC_REG_BOOKE_H__ #define __ASM_POWERPC_REG_BOOKE_H__ -#ifndef __ASSEMBLY__ -/* Performance Monitor Registers */ -#define mfpmr(rn) ({unsigned int rval; \ - asm volatile("mfpmr %0," __stringify(rn) \ - : "=r" (rval)); rval;}) -#define mtpmr(rn, v) asm volatile("mtpmr " __stringify(rn) ",%0" : : "r" (v)) -#endif /* __ASSEMBLY__ */ - -/* Freescale Book E Performance Monitor APU Registers */ -#define PMRN_PMC0 0x010 /* Performance Monitor Counter 0 */ -#define PMRN_PMC1 0x011 /* Performance Monitor Counter 1 */ -#define PMRN_PMC2 0x012 /* Performance Monitor Counter 1 */ -#define PMRN_PMC3 0x013 /* Performance Monitor Counter 1 */ -#define PMRN_PMLCA0 0x090 /* PM Local Control A0 */ -#define PMRN_PMLCA1 0x091 /* PM Local Control A1 */ -#define PMRN_PMLCA2 0x092 /* PM Local Control A2 */ -#define PMRN_PMLCA3 0x093 /* PM Local Control A3 */ - -#define PMLCA_FC 0x80000000 /* Freeze Counter */ -#define PMLCA_FCS 0x40000000 /* Freeze in Supervisor */ -#define PMLCA_FCU 0x20000000 /* Freeze in User */ -#define PMLCA_FCM1 0x10000000 /* Freeze when PMM==1 */ -#define PMLCA_FCM0 0x08000000 /* Freeze when PMM==0 */ -#define PMLCA_CE 0x04000000 /* Condition Enable */ - -#define PMLCA_EVENT_MASK 0x007f0000 /* Event field */ -#define PMLCA_EVENT_SHIFT 16 - -#define PMRN_PMLCB0 0x110 /* PM Local Control B0 */ -#define PMRN_PMLCB1 0x111 /* PM Local Control B1 */ -#define PMRN_PMLCB2 0x112 /* PM Local Control B2 */ -#define PMRN_PMLCB3 0x113 /* PM Local Control B3 */ - -#define PMLCB_THRESHMUL_MASK 0x0700 /* Threshhold Multiple Field */ -#define PMLCB_THRESHMUL_SHIFT 8 - -#define PMLCB_THRESHOLD_MASK 0x003f /* Threshold Field */ -#define PMLCB_THRESHOLD_SHIFT 0 - -#define PMRN_PMGC0 0x190 /* PM Global Control 0 */ - -#define PMGC0_FAC 0x80000000 /* Freeze all Counters */ -#define PMGC0_PMIE 0x40000000 /* Interrupt Enable */ -#define PMGC0_FCECE 0x20000000 /* Freeze countes on - Enabled Condition or - Event */ - -#define PMRN_UPMC0 0x000 /* User Performance Monitor Counter 0 */ -#define PMRN_UPMC1 0x001 /* User Performance Monitor Counter 1 */ -#define PMRN_UPMC2 0x002 /* User Performance Monitor Counter 1 */ -#define PMRN_UPMC3 0x003 /* User Performance Monitor Counter 1 */ -#define PMRN_UPMLCA0 0x080 /* User PM Local Control A0 */ -#define PMRN_UPMLCA1 0x081 /* User PM Local Control A1 */ -#define PMRN_UPMLCA2 0x082 /* User PM Local Control A2 */ -#define PMRN_UPMLCA3 0x083 /* User PM Local Control A3 */ -#define PMRN_UPMLCB0 0x100 /* User PM Local Control B0 */ -#define PMRN_UPMLCB1 0x101 /* User PM Local Control B1 */ -#define PMRN_UPMLCB2 0x102 /* User PM Local Control B2 */ -#define PMRN_UPMLCB3 0x103 /* User PM Local Control B3 */ -#define PMRN_UPMGC0 0x180 /* User PM Global Control 0 */ - - /* Machine State Register (MSR) Fields */ #define MSR_UCLE (1<<26) /* User-mode cache lock enable */ #define MSR_SPE (1<<25) /* Enable SPE */ diff --git a/include/asm-powerpc/reg_fsl_emb.h b/include/asm-powerpc/reg_fsl_emb.h new file mode 100644 index 000000000000..1e180a594589 --- /dev/null +++ b/include/asm-powerpc/reg_fsl_emb.h @@ -0,0 +1,72 @@ +/* + * Contains register definitions for the Freescale Embedded Performance + * Monitor. + */ +#ifdef __KERNEL__ +#ifndef __ASM_POWERPC_REG_FSL_EMB_H__ +#define __ASM_POWERPC_REG_FSL_EMB_H__ + +#ifndef __ASSEMBLY__ +/* Performance Monitor Registers */ +#define mfpmr(rn) ({unsigned int rval; \ + asm volatile("mfpmr %0," __stringify(rn) \ + : "=r" (rval)); rval;}) +#define mtpmr(rn, v) asm volatile("mtpmr " __stringify(rn) ",%0" : : "r" (v)) +#endif /* __ASSEMBLY__ */ + +/* Freescale Book E Performance Monitor APU Registers */ +#define PMRN_PMC0 0x010 /* Performance Monitor Counter 0 */ +#define PMRN_PMC1 0x011 /* Performance Monitor Counter 1 */ +#define PMRN_PMC2 0x012 /* Performance Monitor Counter 1 */ +#define PMRN_PMC3 0x013 /* Performance Monitor Counter 1 */ +#define PMRN_PMLCA0 0x090 /* PM Local Control A0 */ +#define PMRN_PMLCA1 0x091 /* PM Local Control A1 */ +#define PMRN_PMLCA2 0x092 /* PM Local Control A2 */ +#define PMRN_PMLCA3 0x093 /* PM Local Control A3 */ + +#define PMLCA_FC 0x80000000 /* Freeze Counter */ +#define PMLCA_FCS 0x40000000 /* Freeze in Supervisor */ +#define PMLCA_FCU 0x20000000 /* Freeze in User */ +#define PMLCA_FCM1 0x10000000 /* Freeze when PMM==1 */ +#define PMLCA_FCM0 0x08000000 /* Freeze when PMM==0 */ +#define PMLCA_CE 0x04000000 /* Condition Enable */ + +#define PMLCA_EVENT_MASK 0x007f0000 /* Event field */ +#define PMLCA_EVENT_SHIFT 16 + +#define PMRN_PMLCB0 0x110 /* PM Local Control B0 */ +#define PMRN_PMLCB1 0x111 /* PM Local Control B1 */ +#define PMRN_PMLCB2 0x112 /* PM Local Control B2 */ +#define PMRN_PMLCB3 0x113 /* PM Local Control B3 */ + +#define PMLCB_THRESHMUL_MASK 0x0700 /* Threshhold Multiple Field */ +#define PMLCB_THRESHMUL_SHIFT 8 + +#define PMLCB_THRESHOLD_MASK 0x003f /* Threshold Field */ +#define PMLCB_THRESHOLD_SHIFT 0 + +#define PMRN_PMGC0 0x190 /* PM Global Control 0 */ + +#define PMGC0_FAC 0x80000000 /* Freeze all Counters */ +#define PMGC0_PMIE 0x40000000 /* Interrupt Enable */ +#define PMGC0_FCECE 0x20000000 /* Freeze countes on + Enabled Condition or + Event */ + +#define PMRN_UPMC0 0x000 /* User Performance Monitor Counter 0 */ +#define PMRN_UPMC1 0x001 /* User Performance Monitor Counter 1 */ +#define PMRN_UPMC2 0x002 /* User Performance Monitor Counter 1 */ +#define PMRN_UPMC3 0x003 /* User Performance Monitor Counter 1 */ +#define PMRN_UPMLCA0 0x080 /* User PM Local Control A0 */ +#define PMRN_UPMLCA1 0x081 /* User PM Local Control A1 */ +#define PMRN_UPMLCA2 0x082 /* User PM Local Control A2 */ +#define PMRN_UPMLCA3 0x083 /* User PM Local Control A3 */ +#define PMRN_UPMLCB0 0x100 /* User PM Local Control B0 */ +#define PMRN_UPMLCB1 0x101 /* User PM Local Control B1 */ +#define PMRN_UPMLCB2 0x102 /* User PM Local Control B2 */ +#define PMRN_UPMLCB3 0x103 /* User PM Local Control B3 */ +#define PMRN_UPMGC0 0x180 /* User PM Global Control 0 */ + + +#endif /* __ASM_POWERPC_REG_FSL_EMB_H__ */ +#endif /* __KERNEL__ */ diff --git a/include/asm-powerpc/system.h b/include/asm-powerpc/system.h index bc9739dff5e7..29552ff182aa 100644 --- a/include/asm-powerpc/system.h +++ b/include/asm-powerpc/system.h @@ -65,7 +65,7 @@ struct task_struct; struct pt_regs; -#ifdef CONFIG_DEBUGGER +#if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC) extern int (*__debugger)(struct pt_regs *regs); extern int (*__debugger_ipi)(struct pt_regs *regs); @@ -463,7 +463,7 @@ __cmpxchg_local(volatile void *ptr, unsigned long old, unsigned long new, return old; } -#define cmpxchg(ptr,o,n) \ +#define cmpxchg(ptr, o, n) \ ({ \ __typeof__(*(ptr)) _o_ = (o); \ __typeof__(*(ptr)) _n_ = (n); \ @@ -472,7 +472,7 @@ __cmpxchg_local(volatile void *ptr, unsigned long old, unsigned long new, }) -#define cmpxchg_local(ptr,o,n) \ +#define cmpxchg_local(ptr, o, n) \ ({ \ __typeof__(*(ptr)) _o_ = (o); \ __typeof__(*(ptr)) _n_ = (n); \ @@ -492,6 +492,20 @@ __cmpxchg_local(volatile void *ptr, unsigned long old, unsigned long new, */ #define NET_IP_ALIGN 0 #define NET_SKB_PAD L1_CACHE_BYTES + +#define cmpxchg64(ptr, o, n) \ + ({ \ + BUILD_BUG_ON(sizeof(*(ptr)) != 8); \ + cmpxchg((ptr), (o), (n)); \ + }) +#define cmpxchg64_local(ptr, o, n) \ + ({ \ + BUILD_BUG_ON(sizeof(*(ptr)) != 8); \ + cmpxchg_local((ptr), (o), (n)); \ + }) +#else +#include <asm-generic/cmpxchg-local.h> +#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) #endif #define arch_align_stack(x) (x) diff --git a/include/asm-powerpc/user.h b/include/asm-powerpc/user.h index e59ade4b3dfb..3fd4545dd74e 100644 --- a/include/asm-powerpc/user.h +++ b/include/asm-powerpc/user.h @@ -1,8 +1,6 @@ #ifndef _ASM_POWERPC_USER_H #define _ASM_POWERPC_USER_H -#ifdef __KERNEL__ - #include <asm/ptrace.h> #include <asm/page.h> @@ -40,7 +38,7 @@ struct user { unsigned long start_data; /* data starting address */ unsigned long start_stack; /* stack starting address */ long int signal; /* signal causing core dump */ - struct regs * u_ar0; /* help gdb find registers */ + unsigned long u_ar0; /* help gdb find registers */ unsigned long magic; /* identifies a core file */ char u_comm[32]; /* user command name */ }; @@ -50,6 +48,4 @@ struct user { #define HOST_TEXT_START_ADDR (u.start_code) #define HOST_DATA_START_ADDR (u.start_data) #define HOST_STACK_END_ADDR (u.start_stack + u.u_ssize * NBPG) - -#endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_USER_H */ diff --git a/include/asm-powerpc/vio.h b/include/asm-powerpc/vio.h index 9204c15839c5..56512a968dab 100644 --- a/include/asm-powerpc/vio.h +++ b/include/asm-powerpc/vio.h @@ -66,7 +66,7 @@ extern void __devinit vio_unregister_device(struct vio_dev *dev); struct device_node; -extern struct vio_dev * __devinit vio_register_device_node( +extern struct vio_dev *vio_register_device_node( struct device_node *node_vdev); extern const void *vio_get_attribute(struct vio_dev *vdev, char *which, int *length); diff --git a/include/asm-ppc/system.h b/include/asm-ppc/system.h index 51df94c73846..0593cb889d45 100644 --- a/include/asm-ppc/system.h +++ b/include/asm-ppc/system.h @@ -209,12 +209,34 @@ __cmpxchg_u32(volatile unsigned int *p, unsigned int old, unsigned int new) return prev; } +static inline unsigned long +__cmpxchg_u32_local(volatile unsigned int *p, unsigned int old, + unsigned int new) +{ + unsigned int prev; + + __asm__ __volatile__ ("\n\ +1: lwarx %0,0,%2 \n\ + cmpw 0,%0,%3 \n\ + bne 2f \n" + PPC405_ERR77(0,%2) +" stwcx. %4,0,%2 \n\ + bne- 1b\n" +"2:" + : "=&r" (prev), "=m" (*p) + : "r" (p), "r" (old), "r" (new), "m" (*p) + : "cc", "memory"); + + return prev; +} + /* This function doesn't exist, so you'll get a linker error if something tries to do an invalid cmpxchg(). */ extern void __cmpxchg_called_with_bad_pointer(void); static __inline__ unsigned long -__cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) +__cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, + unsigned int size) { switch (size) { case 4: @@ -228,7 +250,7 @@ __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) return old; } -#define cmpxchg(ptr,o,n) \ +#define cmpxchg(ptr, o, n) \ ({ \ __typeof__(*(ptr)) _o_ = (o); \ __typeof__(*(ptr)) _n_ = (n); \ @@ -236,6 +258,31 @@ __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) (unsigned long)_n_, sizeof(*(ptr))); \ }) +#include <asm-generic/cmpxchg-local.h> + +static inline unsigned long __cmpxchg_local(volatile void *ptr, + unsigned long old, + unsigned long new, int size) +{ + switch (size) { + case 4: + return __cmpxchg_u32_local(ptr, old, new); + default: + return __cmpxchg_local_generic(ptr, old, new, size); + } + + return old; +} + +/* + * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make + * them available. + */ +#define cmpxchg_local(ptr, o, n) \ + ((__typeof__(*(ptr)))__cmpxchg_local((ptr), (unsigned long)(o), \ + (unsigned long)(n), sizeof(*(ptr)))) +#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) + #define arch_align_stack(x) (x) #endif /* __KERNEL__ */ diff --git a/include/asm-s390/elf.h b/include/asm-s390/elf.h index 91d06325cc79..b73a424d0f97 100644 --- a/include/asm-s390/elf.h +++ b/include/asm-s390/elf.h @@ -113,7 +113,6 @@ typedef s390_fp_regs elf_fpregset_t; typedef s390_regs elf_gregset_t; -#ifdef __KERNEL__ #include <linux/sched.h> /* for task_struct */ #include <asm/system.h> /* for save_access_regs */ @@ -214,6 +213,5 @@ do { \ clear_thread_flag(TIF_31BIT); \ } while (0) #endif /* __s390x__ */ -#endif #endif diff --git a/include/asm-s390/kexec.h b/include/asm-s390/kexec.h index 7592af708b41..f219c6411e0b 100644 --- a/include/asm-s390/kexec.h +++ b/include/asm-s390/kexec.h @@ -10,7 +10,9 @@ #ifndef _S390_KEXEC_H #define _S390_KEXEC_H +#ifdef __KERNEL__ #include <asm/page.h> +#endif #include <asm/processor.h> /* * KEXEC_SOURCE_MEMORY_LIMIT maximum page get_free_page can return. diff --git a/include/asm-s390/page.h b/include/asm-s390/page.h index 584d0ee3c7f6..a55f9d979dfb 100644 --- a/include/asm-s390/page.h +++ b/include/asm-s390/page.h @@ -19,7 +19,6 @@ #define PAGE_DEFAULT_ACC 0 #define PAGE_DEFAULT_KEY (PAGE_DEFAULT_ACC << 4) -#ifdef __KERNEL__ #include <asm/setup.h> #ifndef __ASSEMBLY__ @@ -172,6 +171,4 @@ static inline int pfn_valid(unsigned long pfn) #include <asm-generic/memory_model.h> #include <asm-generic/page.h> -#endif /* __KERNEL__ */ - #endif /* _S390_PAGE_H */ diff --git a/include/asm-s390/system.h b/include/asm-s390/system.h index 44bda786eef7..15aba30601a3 100644 --- a/include/asm-s390/system.h +++ b/include/asm-s390/system.h @@ -201,9 +201,9 @@ static inline unsigned long __xchg(unsigned long x, void * ptr, int size) #define __HAVE_ARCH_CMPXCHG 1 -#define cmpxchg(ptr,o,n)\ - ((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o),\ - (unsigned long)(n),sizeof(*(ptr)))) +#define cmpxchg(ptr, o, n) \ + ((__typeof__(*(ptr)))__cmpxchg((ptr), (unsigned long)(o), \ + (unsigned long)(n), sizeof(*(ptr)))) extern void __cmpxchg_called_with_bad_pointer(void); @@ -355,6 +355,44 @@ __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) #include <linux/irqflags.h> +#include <asm-generic/cmpxchg-local.h> + +static inline unsigned long __cmpxchg_local(volatile void *ptr, + unsigned long old, + unsigned long new, int size) +{ + switch (size) { + case 1: + case 2: + case 4: +#ifdef __s390x__ + case 8: +#endif + return __cmpxchg(ptr, old, new, size); + default: + return __cmpxchg_local_generic(ptr, old, new, size); + } + + return old; +} + +/* + * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make + * them available. + */ +#define cmpxchg_local(ptr, o, n) \ + ((__typeof__(*(ptr)))__cmpxchg_local((ptr), (unsigned long)(o), \ + (unsigned long)(n), sizeof(*(ptr)))) +#ifdef __s390x__ +#define cmpxchg64_local(ptr, o, n) \ + ({ \ + BUILD_BUG_ON(sizeof(*(ptr)) != 8); \ + cmpxchg_local((ptr), (o), (n)); \ + }) +#else +#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) +#endif + /* * Use to set psw mask except for the first byte which * won't be changed by this function. diff --git a/include/asm-s390/user.h b/include/asm-s390/user.h index 1dc74baf03c4..1b050e35fdc6 100644 --- a/include/asm-s390/user.h +++ b/include/asm-s390/user.h @@ -63,8 +63,7 @@ struct user { the top of the stack is always found in the esp register. */ long int signal; /* Signal that caused the core dump. */ - struct user_regs_struct *u_ar0; - /* Used by gdb to help find the values for */ + unsigned long u_ar0; /* Used by gdb to help find the values for */ /* the registers. */ unsigned long magic; /* To uniquely identify a core file */ char u_comm[32]; /* User command that was responsible */ diff --git a/include/asm-sh/page.h b/include/asm-sh/page.h index 002e64a4f049..e0fe02950f52 100644 --- a/include/asm-sh/page.h +++ b/include/asm-sh/page.h @@ -7,8 +7,6 @@ #include <linux/const.h> -#ifdef __KERNEL__ - /* PAGE_SHIFT determines the page size */ #if defined(CONFIG_PAGE_SIZE_4KB) # define PAGE_SHIFT 12 @@ -178,5 +176,4 @@ typedef struct { unsigned long pgd; } pgd_t; #define ARCH_SLAB_MINALIGN 8 #endif -#endif /* __KERNEL__ */ #endif /* __ASM_SH_PAGE_H */ diff --git a/include/asm-sh/user.h b/include/asm-sh/user.h index 1a4f43c75126..8fd3cf6c58d4 100644 --- a/include/asm-sh/user.h +++ b/include/asm-sh/user.h @@ -52,7 +52,7 @@ struct user { unsigned long start_data; /* data starting address */ unsigned long start_stack; /* stack starting address */ long int signal; /* signal causing core dump */ - struct regs * u_ar0; /* help gdb find registers */ + unsigned long u_ar0; /* help gdb find registers */ struct user_fpu_struct* u_fpstate; /* Math Co-processor pointer */ unsigned long magic; /* identifies a core file */ char u_comm[32]; /* user command name */ diff --git a/include/asm-sparc/atomic.h b/include/asm-sparc/atomic.h index 3328950dbfe6..5c944b5a8040 100644 --- a/include/asm-sparc/atomic.h +++ b/include/asm-sparc/atomic.h @@ -17,42 +17,6 @@ typedef struct { volatile int counter; } atomic_t; #ifdef __KERNEL__ -/* Emulate cmpxchg() the same way we emulate atomics, - * by hashing the object address and indexing into an array - * of spinlocks to get a bit of performance... - * - * See arch/sparc/lib/atomic32.c for implementation. - * - * Cribbed from <asm-parisc/atomic.h> - */ -#define __HAVE_ARCH_CMPXCHG 1 - -/* bug catcher for when unsupported size is used - won't link */ -extern void __cmpxchg_called_with_bad_pointer(void); -/* we only need to support cmpxchg of a u32 on sparc */ -extern unsigned long __cmpxchg_u32(volatile u32 *m, u32 old, u32 new_); - -/* don't worry...optimizer will get rid of most of this */ -static inline unsigned long -__cmpxchg(volatile void *ptr, unsigned long old, unsigned long new_, int size) -{ - switch(size) { - case 4: - return __cmpxchg_u32((u32 *)ptr, (u32)old, (u32)new_); - default: - __cmpxchg_called_with_bad_pointer(); - break; - } - return old; -} - -#define cmpxchg(ptr,o,n) ({ \ - __typeof__(*(ptr)) _o_ = (o); \ - __typeof__(*(ptr)) _n_ = (n); \ - (__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_, \ - (unsigned long)_n_, sizeof(*(ptr))); \ -}) - #define ATOMIC_INIT(i) { (i) } extern int __atomic_add_return(int, atomic_t *); diff --git a/include/asm-sparc/elf.h b/include/asm-sparc/elf.h index aaf6ef40ee2f..668814e1e539 100644 --- a/include/asm-sparc/elf.h +++ b/include/asm-sparc/elf.h @@ -85,7 +85,6 @@ typedef struct { unsigned int pr_q[64]; } elf_fpregset_t; -#ifdef __KERNEL__ #include <asm/mbus.h> #include <asm/uaccess.h> @@ -166,6 +165,4 @@ do { unsigned long *dest = &(__elf_regs[0]); \ #define SET_PERSONALITY(ex, ibcs2) set_personality((ibcs2)?PER_SVR4:PER_LINUX) -#endif /* __KERNEL__ */ - #endif /* !(__ASMSPARC_ELF_H) */ diff --git a/include/asm-sparc/page.h b/include/asm-sparc/page.h index ff57648eb8f8..cbc48c0c4e15 100644 --- a/include/asm-sparc/page.h +++ b/include/asm-sparc/page.h @@ -8,8 +8,6 @@ #ifndef _SPARC_PAGE_H #define _SPARC_PAGE_H -#ifdef __KERNEL__ - #ifdef CONFIG_SUN4 #define PAGE_SHIFT 13 #else @@ -163,6 +161,4 @@ extern unsigned long pfn_base; #include <asm-generic/memory_model.h> #include <asm-generic/page.h> -#endif /* __KERNEL__ */ - #endif /* _SPARC_PAGE_H */ diff --git a/include/asm-sparc/system.h b/include/asm-sparc/system.h index 2655d142b22d..45e47c159a6e 100644 --- a/include/asm-sparc/system.h +++ b/include/asm-sparc/system.h @@ -225,6 +225,54 @@ static inline unsigned long __xchg(unsigned long x, __volatile__ void * ptr, int return x; } +/* Emulate cmpxchg() the same way we emulate atomics, + * by hashing the object address and indexing into an array + * of spinlocks to get a bit of performance... + * + * See arch/sparc/lib/atomic32.c for implementation. + * + * Cribbed from <asm-parisc/atomic.h> + */ +#define __HAVE_ARCH_CMPXCHG 1 + +/* bug catcher for when unsupported size is used - won't link */ +extern void __cmpxchg_called_with_bad_pointer(void); +/* we only need to support cmpxchg of a u32 on sparc */ +extern unsigned long __cmpxchg_u32(volatile u32 *m, u32 old, u32 new_); + +/* don't worry...optimizer will get rid of most of this */ +static inline unsigned long +__cmpxchg(volatile void *ptr, unsigned long old, unsigned long new_, int size) +{ + switch (size) { + case 4: + return __cmpxchg_u32((u32 *)ptr, (u32)old, (u32)new_); + default: + __cmpxchg_called_with_bad_pointer(); + break; + } + return old; +} + +#define cmpxchg(ptr, o, n) \ +({ \ + __typeof__(*(ptr)) _o_ = (o); \ + __typeof__(*(ptr)) _n_ = (n); \ + (__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_, \ + (unsigned long)_n_, sizeof(*(ptr))); \ +}) + +#include <asm-generic/cmpxchg-local.h> + +/* + * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make + * them available. + */ +#define cmpxchg_local(ptr, o, n) \ + ((__typeof__(*(ptr)))__cmpxchg_local_generic((ptr), (unsigned long)(o),\ + (unsigned long)(n), sizeof(*(ptr)))) +#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) + extern void die_if_kernel(char *str, struct pt_regs *regs) __attribute__ ((noreturn)); #endif /* __KERNEL__ */ diff --git a/include/asm-sparc64/elf.h b/include/asm-sparc64/elf.h index 8653e8665009..dc7bc63e507e 100644 --- a/include/asm-sparc64/elf.h +++ b/include/asm-sparc64/elf.h @@ -7,11 +7,9 @@ */ #include <asm/ptrace.h> -#ifdef __KERNEL__ #include <asm/processor.h> #include <asm/uaccess.h> #include <asm/spitfire.h> -#endif /* * Sparc section types @@ -175,7 +173,6 @@ static inline unsigned int sparc64_elf_hwcap(void) #define ELF_PLATFORM (NULL) -#ifdef __KERNEL__ #define SET_PERSONALITY(ex, ibcs2) \ do { unsigned long new_flags = current_thread_info()->flags; \ new_flags &= _TIF_32BIT; \ @@ -194,6 +191,5 @@ do { unsigned long new_flags = current_thread_info()->flags; \ else if (current->personality != PER_LINUX32) \ set_personality(PER_LINUX); \ } while (0) -#endif #endif /* !(__ASM_SPARC64_ELF_H) */ diff --git a/include/asm-sparc64/page.h b/include/asm-sparc64/page.h index 7af1077451ff..cdf950e017ee 100644 --- a/include/asm-sparc64/page.h +++ b/include/asm-sparc64/page.h @@ -3,8 +3,6 @@ #ifndef _SPARC64_PAGE_H #define _SPARC64_PAGE_H -#ifdef __KERNEL__ - #include <linux/const.h> #if defined(CONFIG_SPARC64_PAGE_SIZE_8KB) @@ -143,5 +141,4 @@ typedef unsigned long pgprot_t; #include <asm-generic/page.h> -#endif /* __KERNEL__ */ #endif /* _SPARC64_PAGE_H */ diff --git a/include/asm-sparc64/system.h b/include/asm-sparc64/system.h index 99a669c190c7..1faefa6d3708 100644 --- a/include/asm-sparc64/system.h +++ b/include/asm-sparc64/system.h @@ -8,6 +8,7 @@ #ifndef __ASSEMBLY__ #include <linux/irqflags.h> +#include <asm-generic/cmpxchg-local.h> /* * Sparc (general) CPU types @@ -315,6 +316,34 @@ __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) (unsigned long)_n_, sizeof(*(ptr))); \ }) +/* + * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make + * them available. + */ + +static inline unsigned long __cmpxchg_local(volatile void *ptr, + unsigned long old, + unsigned long new, int size) +{ + switch (size) { + case 4: + case 8: return __cmpxchg(ptr, old, new, size); + default: + return __cmpxchg_local_generic(ptr, old, new, size); + } + + return old; +} + +#define cmpxchg_local(ptr, o, n) \ + ((__typeof__(*(ptr)))__cmpxchg_local((ptr), (unsigned long)(o), \ + (unsigned long)(n), sizeof(*(ptr)))) +#define cmpxchg64_local(ptr, o, n) \ + ({ \ + BUILD_BUG_ON(sizeof(*(ptr)) != 8); \ + cmpxchg_local((ptr), (o), (n)); \ + }) + #endif /* !(__ASSEMBLY__) */ #define arch_align_stack(x) (x) diff --git a/include/asm-v850/elf.h b/include/asm-v850/elf.h index 7db8edffb1c6..28f5b176ff1a 100644 --- a/include/asm-v850/elf.h +++ b/include/asm-v850/elf.h @@ -94,8 +94,6 @@ typedef struct user_fpu_struct elf_fpregset_t; 0; \ } while (0) -#ifdef __KERNEL__ #define SET_PERSONALITY(ex, ibcs2) set_personality(PER_LINUX_32BIT) -#endif #endif /* __V850_ELF_H__ */ diff --git a/include/asm-v850/page.h b/include/asm-v850/page.h index d693ffb1364d..661d8cd08839 100644 --- a/include/asm-v850/page.h +++ b/include/asm-v850/page.h @@ -14,8 +14,6 @@ #ifndef __V850_PAGE_H__ #define __V850_PAGE_H__ -#ifdef __KERNEL__ - #include <asm/machdep.h> @@ -126,6 +124,4 @@ typedef unsigned long pgprot_t; #include <asm-generic/memory_model.h> #include <asm-generic/page.h> -#endif /* KERNEL */ - #endif /* __V850_PAGE_H__ */ diff --git a/include/asm-v850/system.h b/include/asm-v850/system.h index a34ddfafd561..7daf1fdee119 100644 --- a/include/asm-v850/system.h +++ b/include/asm-v850/system.h @@ -103,6 +103,21 @@ static inline unsigned long __xchg (unsigned long with, return tmp; } +#include <asm-generic/cmpxchg-local.h> + +/* + * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make + * them available. + */ +#define cmpxchg_local(ptr, o, n) \ + ((__typeof__(*(ptr)))__cmpxchg_local_generic((ptr), (unsigned long)(o),\ + (unsigned long)(n), sizeof(*(ptr)))) +#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) + +#ifndef CONFIG_SMP +#include <asm-generic/cmpxchg.h> +#endif + #define arch_align_stack(x) (x) #endif /* __V850_SYSTEM_H__ */ diff --git a/include/asm-v850/user.h b/include/asm-v850/user.h index ccf4cea6dc9c..63cdc567d272 100644 --- a/include/asm-v850/user.h +++ b/include/asm-v850/user.h @@ -3,8 +3,6 @@ /* Adapted from <asm-ppc/user.h>. */ -#ifdef __KERNEL__ - #include <linux/ptrace.h> #include <asm/page.h> @@ -40,7 +38,7 @@ struct user { unsigned long start_data; /* data starting address */ unsigned long start_stack; /* stack starting address */ long int signal; /* signal causing core dump */ - struct regs * u_ar0; /* help gdb find registers */ + unsigned long u_ar0; /* help gdb find registers */ unsigned long magic; /* identifies a core file */ char u_comm[32]; /* user command name */ }; @@ -51,6 +49,4 @@ struct user { #define HOST_DATA_START_ADDR (u.start_data) #define HOST_STACK_END_ADDR (u.start_stack + u.u_ssize * NBPG) -#endif /* __KERNEL__ */ - #endif /* __V850_USER_H__ */ diff --git a/include/asm-x86/Kbuild b/include/asm-x86/Kbuild index 3c6f0f80e827..b04a7ff46df1 100644 --- a/include/asm-x86/Kbuild +++ b/include/asm-x86/Kbuild @@ -22,7 +22,5 @@ unifdef-y += posix_types_64.h unifdef-y += ptrace.h unifdef-y += unistd_32.h unifdef-y += unistd_64.h -unifdef-y += user_32.h -unifdef-y += user_64.h unifdef-y += vm86.h unifdef-y += vsyscall.h diff --git a/include/asm-x86/cmpxchg_64.h b/include/asm-x86/cmpxchg_64.h index 5e182062e6ec..56f5b41e071c 100644 --- a/include/asm-x86/cmpxchg_64.h +++ b/include/asm-x86/cmpxchg_64.h @@ -124,11 +124,21 @@ static inline unsigned long __cmpxchg_local(volatile void *ptr, return old; } -#define cmpxchg(ptr,o,n)\ - ((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o),\ - (unsigned long)(n),sizeof(*(ptr)))) -#define cmpxchg_local(ptr,o,n)\ - ((__typeof__(*(ptr)))__cmpxchg_local((ptr),(unsigned long)(o),\ - (unsigned long)(n),sizeof(*(ptr)))) +#define cmpxchg(ptr, o, n) \ + ((__typeof__(*(ptr)))__cmpxchg((ptr), (unsigned long)(o), \ + (unsigned long)(n), sizeof(*(ptr)))) +#define cmpxchg64(ptr, o, n) \ + ({ \ + BUILD_BUG_ON(sizeof(*(ptr)) != 8); \ + cmpxchg((ptr), (o), (n)); \ + }) +#define cmpxchg_local(ptr, o, n) \ + ((__typeof__(*(ptr)))__cmpxchg_local((ptr), (unsigned long)(o), \ + (unsigned long)(n), sizeof(*(ptr)))) +#define cmpxchg64_local(ptr, o, n) \ + ({ \ + BUILD_BUG_ON(sizeof(*(ptr)) != 8); \ + cmpxchg_local((ptr), (o), (n)); \ + }) #endif diff --git a/include/asm-x86/elf.h b/include/asm-x86/elf.h index d9c94e707289..fb62f9941e38 100644 --- a/include/asm-x86/elf.h +++ b/include/asm-x86/elf.h @@ -72,7 +72,6 @@ typedef struct user_fxsr_struct elf_fpxregset_t; #endif -#ifdef __KERNEL__ #include <asm/vdso.h> extern unsigned int vdso_enabled; @@ -321,6 +320,4 @@ extern int syscall32_setup_pages(struct linux_binprm *, int exstack); extern unsigned long arch_randomize_brk(struct mm_struct *mm); #define arch_randomize_brk arch_randomize_brk -#endif /* __KERNEL__ */ - #endif diff --git a/include/asm-x86/mmzone_32.h b/include/asm-x86/mmzone_32.h index 5d6f4ce6e6d6..274a59566c45 100644 --- a/include/asm-x86/mmzone_32.h +++ b/include/asm-x86/mmzone_32.h @@ -107,8 +107,8 @@ static inline int pfn_valid(int pfn) /* * Following are macros that are specific to this numa platform. */ -#define reserve_bootmem(addr, size) \ - reserve_bootmem_node(NODE_DATA(0), (addr), (size)) +#define reserve_bootmem(addr, size, flags) \ + reserve_bootmem_node(NODE_DATA(0), (addr), (size), (flags)) #define alloc_bootmem(x) \ __alloc_bootmem_node(NODE_DATA(0), (x), SMP_CACHE_BYTES, __pa(MAX_DMA_ADDRESS)) #define alloc_bootmem_low(x) \ diff --git a/include/asm-x86/termios.h b/include/asm-x86/termios.h index d501748700d2..f72956331c49 100644 --- a/include/asm-x86/termios.h +++ b/include/asm-x86/termios.h @@ -41,6 +41,8 @@ struct termio { #ifdef __KERNEL__ +#include <asm/uaccess.h> + /* intr=^C quit=^\ erase=del kill=^U eof=^D vtime=\0 vmin=\1 sxtc=\0 start=^Q stop=^S susp=^Z eol=\0 @@ -58,39 +60,53 @@ struct termio { *(unsigned short *) &(termios)->x = __tmp; \ } -#define user_termio_to_kernel_termios(termios, termio) \ -({ \ - SET_LOW_TERMIOS_BITS(termios, termio, c_iflag); \ - SET_LOW_TERMIOS_BITS(termios, termio, c_oflag); \ - SET_LOW_TERMIOS_BITS(termios, termio, c_cflag); \ - SET_LOW_TERMIOS_BITS(termios, termio, c_lflag); \ - copy_from_user((termios)->c_cc, (termio)->c_cc, NCC); \ -}) +static inline int user_termio_to_kernel_termios(struct ktermios *termios, + struct termio __user *termio) +{ + SET_LOW_TERMIOS_BITS(termios, termio, c_iflag); + SET_LOW_TERMIOS_BITS(termios, termio, c_oflag); + SET_LOW_TERMIOS_BITS(termios, termio, c_cflag); + SET_LOW_TERMIOS_BITS(termios, termio, c_lflag); + return copy_from_user(termios->c_cc, termio->c_cc, NCC); +} /* * Translate a "termios" structure into a "termio". Ugh. */ -#define kernel_termios_to_user_termio(termio, termios) \ -({ \ - put_user((termios)->c_iflag, &(termio)->c_iflag); \ - put_user((termios)->c_oflag, &(termio)->c_oflag); \ - put_user((termios)->c_cflag, &(termio)->c_cflag); \ - put_user((termios)->c_lflag, &(termio)->c_lflag); \ - put_user((termios)->c_line, &(termio)->c_line); \ - copy_to_user((termio)->c_cc, (termios)->c_cc, NCC); \ -}) - -#define user_termios_to_kernel_termios(k, u) \ - copy_from_user(k, u, sizeof(struct termios2)) - -#define kernel_termios_to_user_termios(u, k) \ - copy_to_user(u, k, sizeof(struct termios2)) - -#define user_termios_to_kernel_termios_1(k, u) \ - copy_from_user(k, u, sizeof(struct termios)) - -#define kernel_termios_to_user_termios_1(u, k) \ - copy_to_user(u, k, sizeof(struct termios)) +static inline int kernel_termios_to_user_termio(struct termio __user *termio, + struct ktermios *termios) +{ + put_user((termios)->c_iflag, &(termio)->c_iflag); + put_user((termios)->c_oflag, &(termio)->c_oflag); + put_user((termios)->c_cflag, &(termio)->c_cflag); + put_user((termios)->c_lflag, &(termio)->c_lflag); + put_user((termios)->c_line, &(termio)->c_line); + return copy_to_user((termio)->c_cc, (termios)->c_cc, NCC); +} + +static inline int user_termios_to_kernel_termios(struct ktermios *k, + struct termios2 __user *u) +{ + return copy_from_user(k, u, sizeof(struct termios2)); +} + +static inline int kernel_termios_to_user_termios(struct termios2 __user *u, + struct ktermios *k) +{ + return copy_to_user(u, k, sizeof(struct termios2)); +} + +static inline int user_termios_to_kernel_termios_1(struct ktermios *k, + struct termios __user *u) +{ + return copy_from_user(k, u, sizeof(struct termios)); +} + +static inline int kernel_termios_to_user_termios_1(struct termios __user *u, + struct ktermios *k) +{ + return copy_to_user(u, k, sizeof(struct termios)); +} #endif /* __KERNEL__ */ diff --git a/include/asm-x86/user.h b/include/asm-x86/user.h index 484715abe74a..999873b22e7f 100644 --- a/include/asm-x86/user.h +++ b/include/asm-x86/user.h @@ -1,13 +1,5 @@ -#ifdef __KERNEL__ -# ifdef CONFIG_X86_32 -# include "user_32.h" -# else -# include "user_64.h" -# endif +#ifdef CONFIG_X86_32 +# include "user_32.h" #else -# ifdef __i386__ -# include "user_32.h" -# else -# include "user_64.h" -# endif +# include "user_64.h" #endif diff --git a/include/asm-x86/user_32.h b/include/asm-x86/user_32.h index ed8b8fc6906c..6157da6f882c 100644 --- a/include/asm-x86/user_32.h +++ b/include/asm-x86/user_32.h @@ -116,7 +116,7 @@ struct user{ esp register. */ long int signal; /* Signal that caused the core dump. */ int reserved; /* No longer used */ - struct user_pt_regs * u_ar0; /* Used by gdb to help find the values for */ + unsigned long u_ar0; /* Used by gdb to help find the values for */ /* the registers. */ struct user_i387_struct* u_fpstate; /* Math Co-processor pointer. */ unsigned long magic; /* To uniquely identify a core file */ diff --git a/include/asm-x86/user_64.h b/include/asm-x86/user_64.h index a5449d456cc0..963616455609 100644 --- a/include/asm-x86/user_64.h +++ b/include/asm-x86/user_64.h @@ -118,7 +118,7 @@ struct user{ long int signal; /* Signal that caused the core dump. */ int reserved; /* No longer used */ int pad1; - struct user_pt_regs * u_ar0; /* Used by gdb to help find the values for */ + unsigned long u_ar0; /* Used by gdb to help find the values for */ /* the registers. */ struct user_i387_struct* u_fpstate; /* Math Co-processor pointer. */ unsigned long magic; /* To uniquely identify a core file */ diff --git a/include/asm-xtensa/elf.h b/include/asm-xtensa/elf.h index 7083d46766a8..467384542502 100644 --- a/include/asm-xtensa/elf.h +++ b/include/asm-xtensa/elf.h @@ -257,8 +257,6 @@ extern void xtensa_elf_core_copy_regs (xtensa_gregset_t *, struct pt_regs *); _r->areg[12]=0; _r->areg[13]=0; _r->areg[14]=0; _r->areg[15]=0; \ } while (0) -#ifdef __KERNEL__ - #define SET_PERSONALITY(ex, ibcs2) set_personality(PER_LINUX_32BIT) struct task_struct; @@ -272,5 +270,4 @@ extern void do_save_fpregs (elf_fpregset_t*, struct pt_regs*, extern int do_restore_fpregs (elf_fpregset_t*, struct pt_regs*, struct task_struct*); -#endif /* __KERNEL__ */ #endif /* _XTENSA_ELF_H */ diff --git a/include/asm-xtensa/page.h b/include/asm-xtensa/page.h index 55ce2c9749a3..1adedbf41d01 100644 --- a/include/asm-xtensa/page.h +++ b/include/asm-xtensa/page.h @@ -11,8 +11,6 @@ #ifndef _XTENSA_PAGE_H #define _XTENSA_PAGE_H -#ifdef __KERNEL__ - #include <asm/processor.h> #include <asm/types.h> #include <asm/cache.h> @@ -174,5 +172,4 @@ extern void copy_user_page(void*, void*, unsigned long, struct page*); VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) #include <asm-generic/memory_model.h> -#endif /* __KERNEL__ */ #endif /* _XTENSA_PAGE_H */ diff --git a/include/asm-xtensa/system.h b/include/asm-xtensa/system.h index ddc970847ae9..e0cb9116d8ab 100644 --- a/include/asm-xtensa/system.h +++ b/include/asm-xtensa/system.h @@ -156,8 +156,30 @@ __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) (unsigned long)_n_, sizeof (*(ptr))); \ }) +#include <asm-generic/cmpxchg-local.h> +static inline unsigned long __cmpxchg_local(volatile void *ptr, + unsigned long old, + unsigned long new, int size) +{ + switch (size) { + case 4: + return __cmpxchg_u32(ptr, old, new); + default: + return __cmpxchg_local_generic(ptr, old, new, size); + } + return old; +} + +/* + * cmpxchg_local and cmpxchg64_local are atomic wrt current CPU. Always make + * them available. + */ +#define cmpxchg_local(ptr, o, n) \ + ((__typeof__(*(ptr)))__cmpxchg_local_generic((ptr), (unsigned long)(o),\ + (unsigned long)(n), sizeof(*(ptr)))) +#define cmpxchg64_local(ptr, o, n) __cmpxchg64_local_generic((ptr), (o), (n)) /* * xchg_u32 diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 93631229fd5c..2ebf068ba504 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -18,7 +18,6 @@ header-y += usb/ header-y += affs_hardblocks.h header-y += aio_abi.h -header-y += a.out.h header-y += arcfb.h header-y += atmapi.h header-y += atmbr2684.h @@ -60,7 +59,6 @@ header-y += dqblk_v2.h header-y += dqblk_xfs.h header-y += efs_fs_sb.h header-y += elf-fdpic.h -header-y += elf.h header-y += elf-em.h header-y += fadvise.h header-y += fd.h @@ -190,6 +188,7 @@ unifdef-y += dccp.h unifdef-y += dirent.h unifdef-y += dlm.h unifdef-y += edd.h +unifdef-y += elf.h unifdef-y += elfcore.h unifdef-y += errno.h unifdef-y += errqueue.h @@ -344,7 +343,6 @@ unifdef-y += uinput.h unifdef-y += uio.h unifdef-y += unistd.h unifdef-y += usbdevice_fs.h -unifdef-y += user.h unifdef-y += utsname.h unifdef-y += videodev2.h unifdef-y += videodev.h diff --git a/include/linux/a.out.h b/include/linux/a.out.h index f913cc3e1b0d..82cd918f2ab7 100644 --- a/include/linux/a.out.h +++ b/include/linux/a.out.h @@ -128,12 +128,20 @@ enum machine_type { #endif #ifdef linux +#ifdef __KERNEL__ #include <asm/page.h> +#else +#include <unistd.h> +#endif #if defined(__i386__) || defined(__mc68000__) #define SEGMENT_SIZE 1024 #else #ifndef SEGMENT_SIZE +#ifdef __KERNEL__ #define SEGMENT_SIZE PAGE_SIZE +#else +#define SEGMENT_SIZE getpagesize() +#endif #endif #endif #endif diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index 0365ec9fc0c9..4e4e340592fb 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h @@ -60,8 +60,20 @@ extern void *__alloc_bootmem_core(struct bootmem_data *bdata, unsigned long goal, unsigned long limit); +/* + * flags for reserve_bootmem (also if CONFIG_HAVE_ARCH_BOOTMEM_NODE, + * the architecture-specific code should honor this) + */ +#define BOOTMEM_DEFAULT 0 +#define BOOTMEM_EXCLUSIVE (1<<0) + #ifndef CONFIG_HAVE_ARCH_BOOTMEM_NODE -extern void reserve_bootmem(unsigned long addr, unsigned long size); +/* + * If flags is 0, then the return value is always 0 (success). If + * flags contains BOOTMEM_EXCLUSIVE, then -EBUSY is returned if the + * memory already was reserved. + */ +extern int reserve_bootmem(unsigned long addr, unsigned long size, int flags); #define alloc_bootmem(x) \ __alloc_bootmem(x, SMP_CACHE_BYTES, __pa(MAX_DMA_ADDRESS)) #define alloc_bootmem_low(x) \ @@ -84,7 +96,8 @@ extern unsigned long init_bootmem_node(pg_data_t *pgdat, unsigned long endpfn); extern void reserve_bootmem_node(pg_data_t *pgdat, unsigned long physaddr, - unsigned long size); + unsigned long size, + int flags); extern void free_bootmem_node(pg_data_t *pgdat, unsigned long addr, unsigned long size); diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 87479328d46d..ff9055fc3d2a 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -14,6 +14,7 @@ #include <linux/nodemask.h> #include <linux/rcupdate.h> #include <linux/cgroupstats.h> +#include <linux/prio_heap.h> #ifdef CONFIG_CGROUPS @@ -207,6 +208,14 @@ struct cftype { int (*release) (struct inode *inode, struct file *file); }; +struct cgroup_scanner { + struct cgroup *cg; + int (*test_task)(struct task_struct *p, struct cgroup_scanner *scan); + void (*process_task)(struct task_struct *p, + struct cgroup_scanner *scan); + struct ptr_heap *heap; +}; + /* Add a new file to the given cgroup directory. Should only be * called by subsystems from within a populate() method */ int cgroup_add_file(struct cgroup *cont, struct cgroup_subsys *subsys, @@ -233,6 +242,7 @@ int cgroup_is_descendant(const struct cgroup *cont); struct cgroup_subsys { struct cgroup_subsys_state *(*create)(struct cgroup_subsys *ss, struct cgroup *cont); + void (*pre_destroy)(struct cgroup_subsys *ss, struct cgroup *cont); void (*destroy)(struct cgroup_subsys *ss, struct cgroup *cont); int (*can_attach)(struct cgroup_subsys *ss, struct cgroup *cont, struct task_struct *tsk); @@ -298,11 +308,17 @@ struct cgroup_iter { * returns NULL or until you want to end the iteration * * 3) call cgroup_iter_end() to destroy the iterator. + * + * Or, call cgroup_scan_tasks() to iterate through every task in a cpuset. + * - cgroup_scan_tasks() holds the css_set_lock when calling the test_task() + * callback, but not while calling the process_task() callback. */ void cgroup_iter_start(struct cgroup *cont, struct cgroup_iter *it); struct task_struct *cgroup_iter_next(struct cgroup *cont, struct cgroup_iter *it); void cgroup_iter_end(struct cgroup *cont, struct cgroup_iter *it); +int cgroup_scan_tasks(struct cgroup_scanner *scan); +int cgroup_attach_task(struct cgroup *, struct task_struct *); #else /* !CONFIG_CGROUPS */ diff --git a/include/linux/cgroup_subsys.h b/include/linux/cgroup_subsys.h index 9ec43186ba80..228235c5ae53 100644 --- a/include/linux/cgroup_subsys.h +++ b/include/linux/cgroup_subsys.h @@ -37,3 +37,8 @@ SUBSYS(cpuacct) /* */ +#ifdef CONFIG_CGROUP_MEM_CONT +SUBSYS(mem_cgroup) +#endif + +/* */ diff --git a/include/linux/ds1wm.h b/include/linux/ds1wm.h index 31f6e3c427fb..d3c65e48a2e7 100644 --- a/include/linux/ds1wm.h +++ b/include/linux/ds1wm.h @@ -6,6 +6,7 @@ struct ds1wm_platform_data { * e.g. on h5xxx and h2200 this is 2 * (registers aligned to 4-byte boundaries), * while on hx4700 this is 1 */ + int active_high; void (*enable)(struct platform_device *pdev); void (*disable)(struct platform_device *pdev); }; diff --git a/include/linux/efs_fs.h b/include/linux/efs_fs.h index dd57fe523e97..a695d63a07af 100644 --- a/include/linux/efs_fs.h +++ b/include/linux/efs_fs.h @@ -41,7 +41,7 @@ extern const struct inode_operations efs_dir_inode_operations; extern const struct file_operations efs_dir_operations; extern const struct address_space_operations efs_symlink_aops; -extern void efs_read_inode(struct inode *); +extern struct inode *efs_iget(struct super_block *, unsigned long); extern efs_block_t efs_map_block(struct inode *, efs_block_t); extern int efs_get_block(struct inode *, sector_t, struct buffer_head *, int); diff --git a/include/linux/elf.h b/include/linux/elf.h index 7ceb24d87c1a..bad1b16ec49a 100644 --- a/include/linux/elf.h +++ b/include/linux/elf.h @@ -3,7 +3,9 @@ #include <linux/types.h> #include <linux/elf-em.h> +#ifdef __KERNEL__ #include <asm/elf.h> +#endif struct file; @@ -355,6 +357,7 @@ typedef struct elf64_shdr { #define NT_AUXV 6 #define NT_PRXFPREG 0x46e62b7f /* copied from gdb5.1/include/elf/common.h */ #define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */ +#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */ #define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */ diff --git a/include/linux/elfcore.h b/include/linux/elfcore.h index 9631dddae348..5ca54d77079f 100644 --- a/include/linux/elfcore.h +++ b/include/linux/elfcore.h @@ -4,7 +4,9 @@ #include <linux/types.h> #include <linux/signal.h> #include <linux/time.h> +#ifdef __KERNEL__ #include <linux/user.h> +#endif #include <linux/ptrace.h> struct elf_siginfo @@ -14,7 +16,9 @@ struct elf_siginfo int si_errno; /* errno */ }; +#ifdef __KERNEL__ #include <asm/elf.h> +#endif #ifndef __KERNEL__ typedef elf_greg_t greg_t; diff --git a/include/linux/err.h b/include/linux/err.h index 1ab1d44f8d3b..ec87f3142bf3 100644 --- a/include/linux/err.h +++ b/include/linux/err.h @@ -34,6 +34,19 @@ static inline long IS_ERR(const void *ptr) return IS_ERR_VALUE((unsigned long)ptr); } +/** + * ERR_CAST - Explicitly cast an error-valued pointer to another pointer type + * @ptr: The pointer to cast. + * + * Explicitly cast an error-valued pointer to another pointer type in such a + * way as to make it clear that's what's going on. + */ +static inline void *ERR_CAST(const void *ptr) +{ + /* cast away the const */ + return (void *) ptr; +} + #endif #endif /* _LINUX_ERR_H */ diff --git a/include/linux/ext3_fs.h b/include/linux/ext3_fs.h index 241c01cb92b2..36c540396377 100644 --- a/include/linux/ext3_fs.h +++ b/include/linux/ext3_fs.h @@ -823,7 +823,7 @@ int ext3_get_blocks_handle(handle_t *handle, struct inode *inode, sector_t iblock, unsigned long maxblocks, struct buffer_head *bh_result, int create, int extend_disksize); -extern void ext3_read_inode (struct inode *); +extern struct inode *ext3_iget(struct super_block *, unsigned long); extern int ext3_write_inode (struct inode *, int); extern int ext3_setattr (struct dentry *, struct iattr *); extern void ext3_delete_inode (struct inode *); diff --git a/include/linux/ext4_fs.h b/include/linux/ext4_fs.h index 1852313fc7c7..c4f635a4dd25 100644 --- a/include/linux/ext4_fs.h +++ b/include/linux/ext4_fs.h @@ -1024,7 +1024,7 @@ int ext4_get_blocks_handle(handle_t *handle, struct inode *inode, struct buffer_head *bh_result, int create, int extend_disksize); -extern void ext4_read_inode (struct inode *); +extern struct inode *ext4_iget(struct super_block *, unsigned long); extern int ext4_write_inode (struct inode *, int); extern int ext4_setattr (struct dentry *, struct iattr *); extern void ext4_delete_inode (struct inode *); diff --git a/include/linux/fs.h b/include/linux/fs.h index 109734bf6377..36b7abefacbe 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1241,8 +1241,6 @@ struct super_operations { struct inode *(*alloc_inode)(struct super_block *sb); void (*destroy_inode)(struct inode *); - void (*read_inode) (struct inode *); - void (*dirty_inode) (struct inode *); int (*write_inode) (struct inode *, int); void (*put_inode) (struct inode *); @@ -1767,19 +1765,8 @@ extern struct inode * iget5_locked(struct super_block *, unsigned long, int (*te extern struct inode * iget_locked(struct super_block *, unsigned long); extern void unlock_new_inode(struct inode *); -static inline struct inode *iget(struct super_block *sb, unsigned long ino) -{ - struct inode *inode = iget_locked(sb, ino); - - if (inode && (inode->i_state & I_NEW)) { - sb->s_op->read_inode(inode); - unlock_new_inode(inode); - } - - return inode; -} - extern void __iget(struct inode * inode); +extern void iget_failed(struct inode *); extern void clear_inode(struct inode *); extern void destroy_inode(struct inode *); extern struct inode *new_inode(struct super_block *); @@ -1941,7 +1928,9 @@ extern int vfs_stat_fd(int dfd, char __user *, struct kstat *); extern int vfs_lstat_fd(int dfd, char __user *, struct kstat *); extern int vfs_fstat(unsigned int, struct kstat *); -extern int vfs_ioctl(struct file *, unsigned int, unsigned int, unsigned long); +extern long vfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +extern int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd, + unsigned long arg); extern void get_filesystem(struct file_system_type *fs); extern void put_filesystem(struct file_system_type *fs); diff --git a/include/linux/hayesesp.h b/include/linux/hayesesp.h index b436be7a7fff..2177ee5b2fe2 100644 --- a/include/linux/hayesesp.h +++ b/include/linux/hayesesp.h @@ -71,7 +71,6 @@ struct hayes_esp_config { #define ESP_STAT_NEVER_DMA 0x08 #define ESP_STAT_USE_PIO 0x10 -#define ESP_EVENT_WRITE_WAKEUP 0 #define ESP_MAGIC 0x53ee #define ESP_XMIT_SIZE 4096 @@ -92,7 +91,6 @@ struct esp_struct { unsigned short closing_wait2; int IER; /* Interrupt Enable Register */ int MCR; /* Modem control register */ - unsigned long event; unsigned long last_active; int line; int count; /* # of fd on device */ @@ -101,8 +99,6 @@ struct esp_struct { int xmit_head; int xmit_tail; int xmit_cnt; - struct work_struct tqueue; - struct work_struct tqueue_hangup; wait_queue_head_t open_wait; wait_queue_head_t close_wait; wait_queue_head_t delta_msr_wait; diff --git a/include/linux/isicom.h b/include/linux/isicom.h index 45b3d48f0978..8f4c71759d73 100644 --- a/include/linux/isicom.h +++ b/include/linux/isicom.h @@ -37,8 +37,6 @@ #define BOARD_COUNT 4 #define PORT_COUNT (BOARD_COUNT*16) -#define SERIAL_TYPE_NORMAL 1 - /* character sizes */ #define ISICOM_CS5 0x0000 diff --git a/include/linux/istallion.h b/include/linux/istallion.h index 106a5e85e5c4..5a84fe944b74 100644 --- a/include/linux/istallion.h +++ b/include/linux/istallion.h @@ -71,7 +71,6 @@ struct stliport { wait_queue_head_t open_wait; wait_queue_head_t close_wait; wait_queue_head_t raw_wait; - struct work_struct tqhangup; struct asysigs asig; unsigned long addr; unsigned long rxoffset; diff --git a/include/linux/kexec.h b/include/linux/kexec.h index 2d9c448d8c52..3265968cd2cd 100644 --- a/include/linux/kexec.h +++ b/include/linux/kexec.h @@ -127,17 +127,21 @@ void vmcoreinfo_append_str(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); unsigned long paddr_vmcoreinfo_note(void); +#define VMCOREINFO_OSRELEASE(name) \ + vmcoreinfo_append_str("OSRELEASE=%s\n", #name) +#define VMCOREINFO_PAGESIZE(value) \ + vmcoreinfo_append_str("PAGESIZE=%ld\n", value) #define VMCOREINFO_SYMBOL(name) \ vmcoreinfo_append_str("SYMBOL(%s)=%lx\n", #name, (unsigned long)&name) #define VMCOREINFO_SIZE(name) \ vmcoreinfo_append_str("SIZE(%s)=%lu\n", #name, \ - (unsigned long)sizeof(struct name)) -#define VMCOREINFO_TYPEDEF_SIZE(name) \ - vmcoreinfo_append_str("SIZE(%s)=%lu\n", #name, \ (unsigned long)sizeof(name)) +#define VMCOREINFO_STRUCT_SIZE(name) \ + vmcoreinfo_append_str("SIZE(%s)=%lu\n", #name, \ + (unsigned long)sizeof(struct name)) #define VMCOREINFO_OFFSET(name, field) \ vmcoreinfo_append_str("OFFSET(%s.%s)=%lu\n", #name, #field, \ - (unsigned long)&(((struct name *)0)->field)) + (unsigned long)offsetof(struct name, field)) #define VMCOREINFO_LENGTH(name, value) \ vmcoreinfo_append_str("LENGTH(%s)=%lu\n", #name, (unsigned long)value) #define VMCOREINFO_NUMBER(name) \ diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h new file mode 100644 index 000000000000..9815951ec995 --- /dev/null +++ b/include/linux/memcontrol.h @@ -0,0 +1,190 @@ +/* memcontrol.h - Memory Controller + * + * Copyright IBM Corporation, 2007 + * Author Balbir Singh <balbir@linux.vnet.ibm.com> + * + * Copyright 2007 OpenVZ SWsoft Inc + * Author: Pavel Emelianov <xemul@openvz.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _LINUX_MEMCONTROL_H +#define _LINUX_MEMCONTROL_H + +#include <linux/rcupdate.h> +#include <linux/mm.h> + +struct mem_cgroup; +struct page_cgroup; +struct page; +struct mm_struct; + +#ifdef CONFIG_CGROUP_MEM_CONT + +extern void mm_init_cgroup(struct mm_struct *mm, struct task_struct *p); +extern void mm_free_cgroup(struct mm_struct *mm); +extern void page_assign_page_cgroup(struct page *page, + struct page_cgroup *pc); +extern struct page_cgroup *page_get_page_cgroup(struct page *page); +extern int mem_cgroup_charge(struct page *page, struct mm_struct *mm, + gfp_t gfp_mask); +extern void mem_cgroup_uncharge(struct page_cgroup *pc); +extern void mem_cgroup_uncharge_page(struct page *page); +extern void mem_cgroup_move_lists(struct page_cgroup *pc, bool active); +extern unsigned long mem_cgroup_isolate_pages(unsigned long nr_to_scan, + struct list_head *dst, + unsigned long *scanned, int order, + int mode, struct zone *z, + struct mem_cgroup *mem_cont, + int active); +extern void mem_cgroup_out_of_memory(struct mem_cgroup *mem, gfp_t gfp_mask); +extern int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm, + gfp_t gfp_mask); +int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *mem); + +static inline struct mem_cgroup *mm_cgroup(const struct mm_struct *mm) +{ + return rcu_dereference(mm->mem_cgroup); +} + +extern int mem_cgroup_prepare_migration(struct page *page); +extern void mem_cgroup_end_migration(struct page *page); +extern void mem_cgroup_page_migration(struct page *page, struct page *newpage); + +/* + * For memory reclaim. + */ +extern int mem_cgroup_calc_mapped_ratio(struct mem_cgroup *mem); +extern long mem_cgroup_reclaim_imbalance(struct mem_cgroup *mem); + +extern int mem_cgroup_get_reclaim_priority(struct mem_cgroup *mem); +extern void mem_cgroup_note_reclaim_priority(struct mem_cgroup *mem, + int priority); +extern void mem_cgroup_record_reclaim_priority(struct mem_cgroup *mem, + int priority); + +extern long mem_cgroup_calc_reclaim_active(struct mem_cgroup *mem, + struct zone *zone, int priority); +extern long mem_cgroup_calc_reclaim_inactive(struct mem_cgroup *mem, + struct zone *zone, int priority); + +#else /* CONFIG_CGROUP_MEM_CONT */ +static inline void mm_init_cgroup(struct mm_struct *mm, + struct task_struct *p) +{ +} + +static inline void mm_free_cgroup(struct mm_struct *mm) +{ +} + +static inline void page_assign_page_cgroup(struct page *page, + struct page_cgroup *pc) +{ +} + +static inline struct page_cgroup *page_get_page_cgroup(struct page *page) +{ + return NULL; +} + +static inline int mem_cgroup_charge(struct page *page, struct mm_struct *mm, + gfp_t gfp_mask) +{ + return 0; +} + +static inline void mem_cgroup_uncharge(struct page_cgroup *pc) +{ +} + +static inline void mem_cgroup_uncharge_page(struct page *page) +{ +} + +static inline void mem_cgroup_move_lists(struct page_cgroup *pc, + bool active) +{ +} + +static inline int mem_cgroup_cache_charge(struct page *page, + struct mm_struct *mm, + gfp_t gfp_mask) +{ + return 0; +} + +static inline struct mem_cgroup *mm_cgroup(const struct mm_struct *mm) +{ + return NULL; +} + +static inline int task_in_mem_cgroup(struct task_struct *task, + const struct mem_cgroup *mem) +{ + return 1; +} + +static inline int mem_cgroup_prepare_migration(struct page *page) +{ + return 0; +} + +static inline void mem_cgroup_end_migration(struct page *page) +{ +} + +static inline void +mem_cgroup_page_migration(struct page *page, struct page *newpage) +{ +} + +static inline int mem_cgroup_calc_mapped_ratio(struct mem_cgroup *mem) +{ + return 0; +} + +static inline int mem_cgroup_reclaim_imbalance(struct mem_cgroup *mem) +{ + return 0; +} + +static inline int mem_cgroup_get_reclaim_priority(struct mem_cgroup *mem) +{ + return 0; +} + +static inline void mem_cgroup_note_reclaim_priority(struct mem_cgroup *mem, + int priority) +{ +} + +static inline void mem_cgroup_record_reclaim_priority(struct mem_cgroup *mem, + int priority) +{ +} + +static inline long mem_cgroup_calc_reclaim_active(struct mem_cgroup *mem, + struct zone *zone, int priority) +{ + return 0; +} + +static inline long mem_cgroup_calc_reclaim_inactive(struct mem_cgroup *mem, + struct zone *zone, int priority) +{ + return 0; +} +#endif /* CONFIG_CGROUP_MEM_CONT */ + +#endif /* _LINUX_MEMCONTROL_H */ + diff --git a/include/linux/mfd/asic3.h b/include/linux/mfd/asic3.h new file mode 100644 index 000000000000..4ab2162db13b --- /dev/null +++ b/include/linux/mfd/asic3.h @@ -0,0 +1,497 @@ +/* + * include/linux/mfd/asic3.h + * + * Compaq ASIC3 headers. + * + * 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. + * + * Copyright 2001 Compaq Computer Corporation. + * Copyright 2007 OpendHand. + */ + +#ifndef __ASIC3_H__ +#define __ASIC3_H__ + +#include <linux/types.h> + +struct asic3 { + void __iomem *mapping; + unsigned int bus_shift; + unsigned int irq_nr; + unsigned int irq_base; + spinlock_t lock; + u16 irq_bothedge[4]; + struct device *dev; +}; + +struct asic3_platform_data { + struct { + u32 dir; + u32 init; + u32 sleep_mask; + u32 sleep_out; + u32 batt_fault_out; + u32 sleep_conf; + u32 alt_function; + } gpio_a, gpio_b, gpio_c, gpio_d; + + unsigned int bus_shift; + + unsigned int irq_base; + + struct platform_device **children; + unsigned int n_children; +}; + +int asic3_gpio_get_value(struct asic3 *asic, unsigned gpio); +void asic3_gpio_set_value(struct asic3 *asic, unsigned gpio, int val); + +#define ASIC3_NUM_GPIO_BANKS 4 +#define ASIC3_GPIOS_PER_BANK 16 +#define ASIC3_NUM_GPIOS 64 +#define ASIC3_NR_IRQS ASIC3_NUM_GPIOS + 6 + +#define ASIC3_GPIO_BANK_A 0 +#define ASIC3_GPIO_BANK_B 1 +#define ASIC3_GPIO_BANK_C 2 +#define ASIC3_GPIO_BANK_D 3 + +#define ASIC3_GPIO(bank, gpio) \ + ((ASIC3_GPIOS_PER_BANK * ASIC3_GPIO_BANK_##bank) + (gpio)) +#define ASIC3_GPIO_bit(gpio) (1 << (gpio & 0xf)) +/* All offsets below are specified with this address bus shift */ +#define ASIC3_DEFAULT_ADDR_SHIFT 2 + +#define ASIC3_OFFSET(base, reg) (ASIC3_##base##_Base + ASIC3_##base##_##reg) +#define ASIC3_GPIO_OFFSET(base, reg) \ + (ASIC3_GPIO_##base##_Base + ASIC3_GPIO_##reg) + +#define ASIC3_GPIO_A_Base 0x0000 +#define ASIC3_GPIO_B_Base 0x0100 +#define ASIC3_GPIO_C_Base 0x0200 +#define ASIC3_GPIO_D_Base 0x0300 + +#define ASIC3_GPIO_Mask 0x00 /* R/W 0:don't mask */ +#define ASIC3_GPIO_Direction 0x04 /* R/W 0:input */ +#define ASIC3_GPIO_Out 0x08 /* R/W 0:output low */ +#define ASIC3_GPIO_TriggerType 0x0c /* R/W 0:level */ +#define ASIC3_GPIO_EdgeTrigger 0x10 /* R/W 0:falling */ +#define ASIC3_GPIO_LevelTrigger 0x14 /* R/W 0:low level detect */ +#define ASIC3_GPIO_SleepMask 0x18 /* R/W 0:don't mask in sleep mode */ +#define ASIC3_GPIO_SleepOut 0x1c /* R/W level 0:low in sleep mode */ +#define ASIC3_GPIO_BattFaultOut 0x20 /* R/W level 0:low in batt_fault */ +#define ASIC3_GPIO_IntStatus 0x24 /* R/W 0:none, 1:detect */ +#define ASIC3_GPIO_AltFunction 0x28 /* R/W 1:LED register control */ +#define ASIC3_GPIO_SleepConf 0x2c /* + * R/W bit 1: autosleep + * 0: disable gposlpout in normal mode, + * enable gposlpout in sleep mode. + */ +#define ASIC3_GPIO_Status 0x30 /* R Pin status */ + +#define ASIC3_SPI_Base 0x0400 +#define ASIC3_SPI_Control 0x0000 +#define ASIC3_SPI_TxData 0x0004 +#define ASIC3_SPI_RxData 0x0008 +#define ASIC3_SPI_Int 0x000c +#define ASIC3_SPI_Status 0x0010 + +#define SPI_CONTROL_SPR(clk) ((clk) & 0x0f) /* Clock rate */ + +#define ASIC3_PWM_0_Base 0x0500 +#define ASIC3_PWM_1_Base 0x0600 +#define ASIC3_PWM_TimeBase 0x0000 +#define ASIC3_PWM_PeriodTime 0x0004 +#define ASIC3_PWM_DutyTime 0x0008 + +#define PWM_TIMEBASE_VALUE(x) ((x)&0xf) /* Low 4 bits sets time base */ +#define PWM_TIMEBASE_ENABLE (1 << 4) /* Enable clock */ + +#define ASIC3_LED_0_Base 0x0700 +#define ASIC3_LED_1_Base 0x0800 +#define ASIC3_LED_2_Base 0x0900 +#define ASIC3_LED_TimeBase 0x0000 /* R/W 7 bits */ +#define ASIC3_LED_PeriodTime 0x0004 /* R/W 12 bits */ +#define ASIC3_LED_DutyTime 0x0008 /* R/W 12 bits */ +#define ASIC3_LED_AutoStopCount 0x000c /* R/W 16 bits */ + +/* LED TimeBase bits - match ASIC2 */ +#define LED_TBS 0x0f /* Low 4 bits sets time base, max = 13 */ + /* Note: max = 5 on hx4700 */ + /* 0: maximum time base */ + /* 1: maximum time base / 2 */ + /* n: maximum time base / 2^n */ + +#define LED_EN (1 << 4) /* LED ON/OFF 0:off, 1:on */ +#define LED_AUTOSTOP (1 << 5) /* LED ON/OFF auto stop 0:disable, 1:enable */ +#define LED_ALWAYS (1 << 6) /* LED Interrupt Mask 0:No mask, 1:mask */ + +#define ASIC3_CLOCK_Base 0x0A00 +#define ASIC3_CLOCK_CDEX 0x00 +#define ASIC3_CLOCK_SEL 0x04 + +#define CLOCK_CDEX_SOURCE (1 << 0) /* 2 bits */ +#define CLOCK_CDEX_SOURCE0 (1 << 0) +#define CLOCK_CDEX_SOURCE1 (1 << 1) +#define CLOCK_CDEX_SPI (1 << 2) +#define CLOCK_CDEX_OWM (1 << 3) +#define CLOCK_CDEX_PWM0 (1 << 4) +#define CLOCK_CDEX_PWM1 (1 << 5) +#define CLOCK_CDEX_LED0 (1 << 6) +#define CLOCK_CDEX_LED1 (1 << 7) +#define CLOCK_CDEX_LED2 (1 << 8) + +/* Clocks settings: 1 for 24.576 MHz, 0 for 12.288Mhz */ +#define CLOCK_CDEX_SD_HOST (1 << 9) /* R/W: SD host clock source */ +#define CLOCK_CDEX_SD_BUS (1 << 10) /* R/W: SD bus clock source ctrl */ +#define CLOCK_CDEX_SMBUS (1 << 11) +#define CLOCK_CDEX_CONTROL_CX (1 << 12) + +#define CLOCK_CDEX_EX0 (1 << 13) /* R/W: 32.768 kHz crystal */ +#define CLOCK_CDEX_EX1 (1 << 14) /* R/W: 24.576 MHz crystal */ + +#define CLOCK_SEL_SD_HCLK_SEL (1 << 0) /* R/W: SDIO host clock select */ +#define CLOCK_SEL_SD_BCLK_SEL (1 << 1) /* R/W: SDIO bus clock select */ + +/* R/W: INT clock source control (32.768 kHz) */ +#define CLOCK_SEL_CX (1 << 2) + + +#define ASIC3_INTR_Base 0x0B00 + +#define ASIC3_INTR_IntMask 0x00 /* Interrupt mask control */ +#define ASIC3_INTR_PIntStat 0x04 /* Peripheral interrupt status */ +#define ASIC3_INTR_IntCPS 0x08 /* Interrupt timer clock pre-scale */ +#define ASIC3_INTR_IntTBS 0x0c /* Interrupt timer set */ + +#define ASIC3_INTMASK_GINTMASK (1 << 0) /* Global INTs mask 1:enable */ +#define ASIC3_INTMASK_GINTEL (1 << 1) /* 1: rising edge, 0: hi level */ +#define ASIC3_INTMASK_MASK0 (1 << 2) +#define ASIC3_INTMASK_MASK1 (1 << 3) +#define ASIC3_INTMASK_MASK2 (1 << 4) +#define ASIC3_INTMASK_MASK3 (1 << 5) +#define ASIC3_INTMASK_MASK4 (1 << 6) +#define ASIC3_INTMASK_MASK5 (1 << 7) + +#define ASIC3_INTR_PERIPHERAL_A (1 << 0) +#define ASIC3_INTR_PERIPHERAL_B (1 << 1) +#define ASIC3_INTR_PERIPHERAL_C (1 << 2) +#define ASIC3_INTR_PERIPHERAL_D (1 << 3) +#define ASIC3_INTR_LED0 (1 << 4) +#define ASIC3_INTR_LED1 (1 << 5) +#define ASIC3_INTR_LED2 (1 << 6) +#define ASIC3_INTR_SPI (1 << 7) +#define ASIC3_INTR_SMBUS (1 << 8) +#define ASIC3_INTR_OWM (1 << 9) + +#define ASIC3_INTR_CPS(x) ((x)&0x0f) /* 4 bits, max 14 */ +#define ASIC3_INTR_CPS_SET (1 << 4) /* Time base enable */ + + +/* Basic control of the SD ASIC */ +#define ASIC3_SDHWCTRL_Base 0x0E00 +#define ASIC3_SDHWCTRL_SDConf 0x00 + +#define ASIC3_SDHWCTRL_SUSPEND (1 << 0) /* 1=suspend all SD operations */ +#define ASIC3_SDHWCTRL_CLKSEL (1 << 1) /* 1=SDICK, 0=HCLK */ +#define ASIC3_SDHWCTRL_PCLR (1 << 2) /* All registers of SDIO cleared */ +#define ASIC3_SDHWCTRL_LEVCD (1 << 3) /* SD card detection: 0:low */ + +/* SD card write protection: 0=high */ +#define ASIC3_SDHWCTRL_LEVWP (1 << 4) +#define ASIC3_SDHWCTRL_SDLED (1 << 5) /* SD card LED signal 0=disable */ + +/* SD card power supply ctrl 1=enable */ +#define ASIC3_SDHWCTRL_SDPWR (1 << 6) + +#define ASIC3_EXTCF_Base 0x1100 + +#define ASIC3_EXTCF_Select 0x00 +#define ASIC3_EXTCF_Reset 0x04 + +#define ASIC3_EXTCF_SMOD0 (1 << 0) /* slot number of mode 0 */ +#define ASIC3_EXTCF_SMOD1 (1 << 1) /* slot number of mode 1 */ +#define ASIC3_EXTCF_SMOD2 (1 << 2) /* slot number of mode 2 */ +#define ASIC3_EXTCF_OWM_EN (1 << 4) /* enable onewire module */ +#define ASIC3_EXTCF_OWM_SMB (1 << 5) /* OWM bus selection */ +#define ASIC3_EXTCF_OWM_RESET (1 << 6) /* ?? used by OWM and CF */ +#define ASIC3_EXTCF_CF0_SLEEP_MODE (1 << 7) /* CF0 sleep state */ +#define ASIC3_EXTCF_CF1_SLEEP_MODE (1 << 8) /* CF1 sleep state */ +#define ASIC3_EXTCF_CF0_PWAIT_EN (1 << 10) /* CF0 PWAIT_n control */ +#define ASIC3_EXTCF_CF1_PWAIT_EN (1 << 11) /* CF1 PWAIT_n control */ +#define ASIC3_EXTCF_CF0_BUF_EN (1 << 12) /* CF0 buffer control */ +#define ASIC3_EXTCF_CF1_BUF_EN (1 << 13) /* CF1 buffer control */ +#define ASIC3_EXTCF_SD_MEM_ENABLE (1 << 14) +#define ASIC3_EXTCF_CF_SLEEP (1 << 15) /* CF sleep mode control */ + +/********************************************* + * The Onewire interface registers + * + * OWM_CMD + * OWM_DAT + * OWM_INTR + * OWM_INTEN + * OWM_CLKDIV + * + *********************************************/ + +#define ASIC3_OWM_Base 0xC00 + +#define ASIC3_OWM_CMD 0x00 +#define ASIC3_OWM_DAT 0x04 +#define ASIC3_OWM_INTR 0x08 +#define ASIC3_OWM_INTEN 0x0C +#define ASIC3_OWM_CLKDIV 0x10 + +#define ASIC3_OWM_CMD_ONEWR (1 << 0) +#define ASIC3_OWM_CMD_SRA (1 << 1) +#define ASIC3_OWM_CMD_DQO (1 << 2) +#define ASIC3_OWM_CMD_DQI (1 << 3) + +#define ASIC3_OWM_INTR_PD (1 << 0) +#define ASIC3_OWM_INTR_PDR (1 << 1) +#define ASIC3_OWM_INTR_TBE (1 << 2) +#define ASIC3_OWM_INTR_TEMP (1 << 3) +#define ASIC3_OWM_INTR_RBF (1 << 4) + +#define ASIC3_OWM_INTEN_EPD (1 << 0) +#define ASIC3_OWM_INTEN_IAS (1 << 1) +#define ASIC3_OWM_INTEN_ETBE (1 << 2) +#define ASIC3_OWM_INTEN_ETMT (1 << 3) +#define ASIC3_OWM_INTEN_ERBF (1 << 4) + +#define ASIC3_OWM_CLKDIV_PRE (3 << 0) /* two bits wide at bit 0 */ +#define ASIC3_OWM_CLKDIV_DIV (7 << 2) /* 3 bits wide at bit 2 */ + + +/***************************************************************************** + * The SD configuration registers are at a completely different location + * in memory. They are divided into three sets of registers: + * + * SD_CONFIG Core configuration register + * SD_CTRL Control registers for SD operations + * SDIO_CTRL Control registers for SDIO operations + * + *****************************************************************************/ +#define ASIC3_SD_CONFIG_Base 0x0400 /* Assumes 32 bit addressing */ + +#define ASIC3_SD_CONFIG_Command 0x08 /* R/W: Command */ + +/* [0:8] SD Control Register Base Address */ +#define ASIC3_SD_CONFIG_Addr0 0x20 + +/* [9:31] SD Control Register Base Address */ +#define ASIC3_SD_CONFIG_Addr1 0x24 + +/* R/O: interrupt assigned to pin */ +#define ASIC3_SD_CONFIG_IntPin 0x78 + +/* + * Set to 0x1f to clock SD controller, 0 otherwise. + * At 0x82 - Gated Clock Ctrl + */ +#define ASIC3_SD_CONFIG_ClkStop 0x80 + +/* Control clock of SD controller */ +#define ASIC3_SD_CONFIG_ClockMode 0x84 +#define ASIC3_SD_CONFIG_SDHC_PinStatus 0x88 /* R/0: SD pins status */ +#define ASIC3_SD_CONFIG_SDHC_Power1 0x90 /* Power1 - manual pwr ctrl */ + +/* auto power up after card inserted */ +#define ASIC3_SD_CONFIG_SDHC_Power2 0x92 + +/* auto power down when card removed */ +#define ASIC3_SD_CONFIG_SDHC_Power3 0x94 +#define ASIC3_SD_CONFIG_SDHC_CardDetect 0x98 +#define ASIC3_SD_CONFIG_SDHC_Slot 0xA0 /* R/O: support slot number */ +#define ASIC3_SD_CONFIG_SDHC_ExtGateClk1 0x1E0 /* Not used */ +#define ASIC3_SD_CONFIG_SDHC_ExtGateClk2 0x1E2 /* Not used*/ + +/* GPIO Output Reg. , at 0x1EA - GPIO Output Enable Reg. */ +#define ASIC3_SD_CONFIG_SDHC_GPIO_OutAndEnable 0x1E8 +#define ASIC3_SD_CONFIG_SDHC_GPIO_Status 0x1EC /* GPIO Status Reg. */ + +/* Bit 1: double buffer/single buffer */ +#define ASIC3_SD_CONFIG_SDHC_ExtGateClk3 0x1F0 + +/* Memory access enable (set to 1 to access SD Controller) */ +#define SD_CONFIG_COMMAND_MAE (1<<1) + +#define SD_CONFIG_CLK_ENABLE_ALL 0x1f + +#define SD_CONFIG_POWER1_PC_33V 0x0200 /* Set for 3.3 volts */ +#define SD_CONFIG_POWER1_PC_OFF 0x0000 /* Turn off power */ + + /* two bits - number of cycles for card detection */ +#define SD_CONFIG_CARDDETECTMODE_CLK ((x) & 0x3) + + +#define ASIC3_SD_CTRL_Base 0x1000 + +#define ASIC3_SD_CTRL_Cmd 0x00 +#define ASIC3_SD_CTRL_Arg0 0x08 +#define ASIC3_SD_CTRL_Arg1 0x0C +#define ASIC3_SD_CTRL_StopInternal 0x10 +#define ASIC3_SD_CTRL_TransferSectorCount 0x14 +#define ASIC3_SD_CTRL_Response0 0x18 +#define ASIC3_SD_CTRL_Response1 0x1C +#define ASIC3_SD_CTRL_Response2 0x20 +#define ASIC3_SD_CTRL_Response3 0x24 +#define ASIC3_SD_CTRL_Response4 0x28 +#define ASIC3_SD_CTRL_Response5 0x2C +#define ASIC3_SD_CTRL_Response6 0x30 +#define ASIC3_SD_CTRL_Response7 0x34 +#define ASIC3_SD_CTRL_CardStatus 0x38 +#define ASIC3_SD_CTRL_BufferCtrl 0x3C +#define ASIC3_SD_CTRL_IntMaskCard 0x40 +#define ASIC3_SD_CTRL_IntMaskBuffer 0x44 +#define ASIC3_SD_CTRL_CardClockCtrl 0x48 +#define ASIC3_SD_CTRL_MemCardXferDataLen 0x4C +#define ASIC3_SD_CTRL_MemCardOptionSetup 0x50 +#define ASIC3_SD_CTRL_ErrorStatus0 0x58 +#define ASIC3_SD_CTRL_ErrorStatus1 0x5C +#define ASIC3_SD_CTRL_DataPort 0x60 +#define ASIC3_SD_CTRL_TransactionCtrl 0x68 +#define ASIC3_SD_CTRL_SoftwareReset 0x1C0 + +#define SD_CTRL_SOFTWARE_RESET_CLEAR (1<<0) + +#define SD_CTRL_TRANSACTIONCONTROL_SET (1<<8) + +#define SD_CTRL_CARDCLOCKCONTROL_FOR_SD_CARD (1<<15) +#define SD_CTRL_CARDCLOCKCONTROL_ENABLE_CLOCK (1<<8) +#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_512 (1<<7) +#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_256 (1<<6) +#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_128 (1<<5) +#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_64 (1<<4) +#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_32 (1<<3) +#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_16 (1<<2) +#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_8 (1<<1) +#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_4 (1<<0) +#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_2 (0<<0) + +#define MEM_CARD_OPTION_REQUIRED 0x000e +#define MEM_CARD_OPTION_DATA_RESPONSE_TIMEOUT(x) (((x) & 0x0f) << 4) +#define MEM_CARD_OPTION_C2_MODULE_NOT_PRESENT (1<<14) +#define MEM_CARD_OPTION_DATA_XFR_WIDTH_1 (1<<15) +#define MEM_CARD_OPTION_DATA_XFR_WIDTH_4 0 + +#define SD_CTRL_COMMAND_INDEX(x) ((x) & 0x3f) +#define SD_CTRL_COMMAND_TYPE_CMD (0 << 6) +#define SD_CTRL_COMMAND_TYPE_ACMD (1 << 6) +#define SD_CTRL_COMMAND_TYPE_AUTHENTICATION (2 << 6) +#define SD_CTRL_COMMAND_RESPONSE_TYPE_NORMAL (0 << 8) +#define SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R1 (4 << 8) +#define SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R1B (5 << 8) +#define SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R2 (6 << 8) +#define SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R3 (7 << 8) +#define SD_CTRL_COMMAND_DATA_PRESENT (1 << 11) +#define SD_CTRL_COMMAND_TRANSFER_READ (1 << 12) +#define SD_CTRL_COMMAND_TRANSFER_WRITE (0 << 12) +#define SD_CTRL_COMMAND_MULTI_BLOCK (1 << 13) +#define SD_CTRL_COMMAND_SECURITY_CMD (1 << 14) + +#define SD_CTRL_STOP_INTERNAL_ISSSUE_CMD12 (1 << 0) +#define SD_CTRL_STOP_INTERNAL_AUTO_ISSUE_CMD12 (1 << 8) + +#define SD_CTRL_CARDSTATUS_RESPONSE_END (1 << 0) +#define SD_CTRL_CARDSTATUS_RW_END (1 << 2) +#define SD_CTRL_CARDSTATUS_CARD_REMOVED_0 (1 << 3) +#define SD_CTRL_CARDSTATUS_CARD_INSERTED_0 (1 << 4) +#define SD_CTRL_CARDSTATUS_SIGNAL_STATE_PRESENT_0 (1 << 5) +#define SD_CTRL_CARDSTATUS_WRITE_PROTECT (1 << 7) +#define SD_CTRL_CARDSTATUS_CARD_REMOVED_3 (1 << 8) +#define SD_CTRL_CARDSTATUS_CARD_INSERTED_3 (1 << 9) +#define SD_CTRL_CARDSTATUS_SIGNAL_STATE_PRESENT_3 (1 << 10) + +#define SD_CTRL_BUFFERSTATUS_CMD_INDEX_ERROR (1 << 0) +#define SD_CTRL_BUFFERSTATUS_CRC_ERROR (1 << 1) +#define SD_CTRL_BUFFERSTATUS_STOP_BIT_END_ERROR (1 << 2) +#define SD_CTRL_BUFFERSTATUS_DATA_TIMEOUT (1 << 3) +#define SD_CTRL_BUFFERSTATUS_BUFFER_OVERFLOW (1 << 4) +#define SD_CTRL_BUFFERSTATUS_BUFFER_UNDERFLOW (1 << 5) +#define SD_CTRL_BUFFERSTATUS_CMD_TIMEOUT (1 << 6) +#define SD_CTRL_BUFFERSTATUS_UNK7 (1 << 7) +#define SD_CTRL_BUFFERSTATUS_BUFFER_READ_ENABLE (1 << 8) +#define SD_CTRL_BUFFERSTATUS_BUFFER_WRITE_ENABLE (1 << 9) +#define SD_CTRL_BUFFERSTATUS_ILLEGAL_FUNCTION (1 << 13) +#define SD_CTRL_BUFFERSTATUS_CMD_BUSY (1 << 14) +#define SD_CTRL_BUFFERSTATUS_ILLEGAL_ACCESS (1 << 15) + +#define SD_CTRL_INTMASKCARD_RESPONSE_END (1 << 0) +#define SD_CTRL_INTMASKCARD_RW_END (1 << 2) +#define SD_CTRL_INTMASKCARD_CARD_REMOVED_0 (1 << 3) +#define SD_CTRL_INTMASKCARD_CARD_INSERTED_0 (1 << 4) +#define SD_CTRL_INTMASKCARD_SIGNAL_STATE_PRESENT_0 (1 << 5) +#define SD_CTRL_INTMASKCARD_UNK6 (1 << 6) +#define SD_CTRL_INTMASKCARD_WRITE_PROTECT (1 << 7) +#define SD_CTRL_INTMASKCARD_CARD_REMOVED_3 (1 << 8) +#define SD_CTRL_INTMASKCARD_CARD_INSERTED_3 (1 << 9) +#define SD_CTRL_INTMASKCARD_SIGNAL_STATE_PRESENT_3 (1 << 10) + +#define SD_CTRL_INTMASKBUFFER_CMD_INDEX_ERROR (1 << 0) +#define SD_CTRL_INTMASKBUFFER_CRC_ERROR (1 << 1) +#define SD_CTRL_INTMASKBUFFER_STOP_BIT_END_ERROR (1 << 2) +#define SD_CTRL_INTMASKBUFFER_DATA_TIMEOUT (1 << 3) +#define SD_CTRL_INTMASKBUFFER_BUFFER_OVERFLOW (1 << 4) +#define SD_CTRL_INTMASKBUFFER_BUFFER_UNDERFLOW (1 << 5) +#define SD_CTRL_INTMASKBUFFER_CMD_TIMEOUT (1 << 6) +#define SD_CTRL_INTMASKBUFFER_UNK7 (1 << 7) +#define SD_CTRL_INTMASKBUFFER_BUFFER_READ_ENABLE (1 << 8) +#define SD_CTRL_INTMASKBUFFER_BUFFER_WRITE_ENABLE (1 << 9) +#define SD_CTRL_INTMASKBUFFER_ILLEGAL_FUNCTION (1 << 13) +#define SD_CTRL_INTMASKBUFFER_CMD_BUSY (1 << 14) +#define SD_CTRL_INTMASKBUFFER_ILLEGAL_ACCESS (1 << 15) + +#define SD_CTRL_DETAIL0_RESPONSE_CMD_ERROR (1 << 0) +#define SD_CTRL_DETAIL0_END_BIT_ERROR_FOR_RESPONSE_NON_CMD12 (1 << 2) +#define SD_CTRL_DETAIL0_END_BIT_ERROR_FOR_RESPONSE_CMD12 (1 << 3) +#define SD_CTRL_DETAIL0_END_BIT_ERROR_FOR_READ_DATA (1 << 4) +#define SD_CTRL_DETAIL0_END_BIT_ERROR_FOR_WRITE_CRC_STATUS (1 << 5) +#define SD_CTRL_DETAIL0_CRC_ERROR_FOR_RESPONSE_NON_CMD12 (1 << 8) +#define SD_CTRL_DETAIL0_CRC_ERROR_FOR_RESPONSE_CMD12 (1 << 9) +#define SD_CTRL_DETAIL0_CRC_ERROR_FOR_READ_DATA (1 << 10) +#define SD_CTRL_DETAIL0_CRC_ERROR_FOR_WRITE_CMD (1 << 11) + +#define SD_CTRL_DETAIL1_NO_CMD_RESPONSE (1 << 0) +#define SD_CTRL_DETAIL1_TIMEOUT_READ_DATA (1 << 4) +#define SD_CTRL_DETAIL1_TIMEOUT_CRS_STATUS (1 << 5) +#define SD_CTRL_DETAIL1_TIMEOUT_CRC_BUSY (1 << 6) + +#define ASIC3_SDIO_CTRL_Base 0x1200 + +#define ASIC3_SDIO_CTRL_Cmd 0x00 +#define ASIC3_SDIO_CTRL_CardPortSel 0x04 +#define ASIC3_SDIO_CTRL_Arg0 0x08 +#define ASIC3_SDIO_CTRL_Arg1 0x0C +#define ASIC3_SDIO_CTRL_TransferBlockCount 0x14 +#define ASIC3_SDIO_CTRL_Response0 0x18 +#define ASIC3_SDIO_CTRL_Response1 0x1C +#define ASIC3_SDIO_CTRL_Response2 0x20 +#define ASIC3_SDIO_CTRL_Response3 0x24 +#define ASIC3_SDIO_CTRL_Response4 0x28 +#define ASIC3_SDIO_CTRL_Response5 0x2C +#define ASIC3_SDIO_CTRL_Response6 0x30 +#define ASIC3_SDIO_CTRL_Response7 0x34 +#define ASIC3_SDIO_CTRL_CardStatus 0x38 +#define ASIC3_SDIO_CTRL_BufferCtrl 0x3C +#define ASIC3_SDIO_CTRL_IntMaskCard 0x40 +#define ASIC3_SDIO_CTRL_IntMaskBuffer 0x44 +#define ASIC3_SDIO_CTRL_CardXferDataLen 0x4C +#define ASIC3_SDIO_CTRL_CardOptionSetup 0x50 +#define ASIC3_SDIO_CTRL_ErrorStatus0 0x54 +#define ASIC3_SDIO_CTRL_ErrorStatus1 0x58 +#define ASIC3_SDIO_CTRL_DataPort 0x60 +#define ASIC3_SDIO_CTRL_TransactionCtrl 0x68 +#define ASIC3_SDIO_CTRL_CardIntCtrl 0x6C +#define ASIC3_SDIO_CTRL_ClocknWaitCtrl 0x70 +#define ASIC3_SDIO_CTRL_HostInformation 0x74 +#define ASIC3_SDIO_CTRL_ErrorCtrl 0x78 +#define ASIC3_SDIO_CTRL_LEDCtrl 0x7C +#define ASIC3_SDIO_CTRL_SoftwareReset 0x1C0 + +#define ASIC3_MAP_SIZE 0x2000 + +#endif /* __ASIC3_H__ */ diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index f4c03e0b355e..34023c65d466 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -88,6 +88,9 @@ struct page { void *virtual; /* Kernel virtual address (NULL if not kmapped, ie. highmem) */ #endif /* WANT_PAGE_VIRTUAL */ +#ifdef CONFIG_CGROUP_MEM_CONT + unsigned long page_cgroup; +#endif }; /* @@ -219,6 +222,9 @@ struct mm_struct { /* aio bits */ rwlock_t ioctx_list_lock; struct kioctx *ioctx_list; +#ifdef CONFIG_CGROUP_MEM_CONT + struct mem_cgroup *mem_cgroup; +#endif }; #endif /* _LINUX_MM_TYPES_H */ diff --git a/include/linux/of.h b/include/linux/of.h index b5f33efcb8e2..6981016dcc25 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -50,6 +50,7 @@ extern struct device_node *of_find_matching_node(struct device_node *from, extern struct device_node *of_find_node_by_path(const char *path); extern struct device_node *of_find_node_by_phandle(phandle handle); extern struct device_node *of_get_parent(const struct device_node *node); +extern struct device_node *of_get_next_parent(struct device_node *node); extern struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev); #define for_each_child_of_node(parent, child) \ diff --git a/include/linux/qnx4_fs.h b/include/linux/qnx4_fs.h index 19bc9b8b6191..34a196ee7941 100644 --- a/include/linux/qnx4_fs.h +++ b/include/linux/qnx4_fs.h @@ -110,6 +110,7 @@ struct qnx4_inode_info { struct inode vfs_inode; }; +extern struct inode *qnx4_iget(struct super_block *, unsigned long); extern struct dentry *qnx4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd); extern unsigned long qnx4_count_free_blocks(struct super_block *sb); extern unsigned long qnx4_block_map(struct inode *inode, long iblock); diff --git a/include/linux/res_counter.h b/include/linux/res_counter.h new file mode 100644 index 000000000000..61363ce896d5 --- /dev/null +++ b/include/linux/res_counter.h @@ -0,0 +1,127 @@ +#ifndef __RES_COUNTER_H__ +#define __RES_COUNTER_H__ + +/* + * Resource Counters + * Contain common data types and routines for resource accounting + * + * Copyright 2007 OpenVZ SWsoft Inc + * + * Author: Pavel Emelianov <xemul@openvz.org> + * + */ + +#include <linux/cgroup.h> + +/* + * The core object. the cgroup that wishes to account for some + * resource may include this counter into its structures and use + * the helpers described beyond + */ + +struct res_counter { + /* + * the current resource consumption level + */ + unsigned long long usage; + /* + * the limit that usage cannot exceed + */ + unsigned long long limit; + /* + * the number of unsuccessful attempts to consume the resource + */ + unsigned long long failcnt; + /* + * the lock to protect all of the above. + * the routines below consider this to be IRQ-safe + */ + spinlock_t lock; +}; + +/* + * Helpers to interact with userspace + * res_counter_read/_write - put/get the specified fields from the + * res_counter struct to/from the user + * + * @counter: the counter in question + * @member: the field to work with (see RES_xxx below) + * @buf: the buffer to opeate on,... + * @nbytes: its size... + * @pos: and the offset. + */ + +ssize_t res_counter_read(struct res_counter *counter, int member, + const char __user *buf, size_t nbytes, loff_t *pos, + int (*read_strategy)(unsigned long long val, char *s)); +ssize_t res_counter_write(struct res_counter *counter, int member, + const char __user *buf, size_t nbytes, loff_t *pos, + int (*write_strategy)(char *buf, unsigned long long *val)); + +/* + * the field descriptors. one for each member of res_counter + */ + +enum { + RES_USAGE, + RES_LIMIT, + RES_FAILCNT, +}; + +/* + * helpers for accounting + */ + +void res_counter_init(struct res_counter *counter); + +/* + * charge - try to consume more resource. + * + * @counter: the counter + * @val: the amount of the resource. each controller defines its own + * units, e.g. numbers, bytes, Kbytes, etc + * + * returns 0 on success and <0 if the counter->usage will exceed the + * counter->limit _locked call expects the counter->lock to be taken + */ + +int res_counter_charge_locked(struct res_counter *counter, unsigned long val); +int res_counter_charge(struct res_counter *counter, unsigned long val); + +/* + * uncharge - tell that some portion of the resource is released + * + * @counter: the counter + * @val: the amount of the resource + * + * these calls check for usage underflow and show a warning on the console + * _locked call expects the counter->lock to be taken + */ + +void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val); +void res_counter_uncharge(struct res_counter *counter, unsigned long val); + +static inline bool res_counter_limit_check_locked(struct res_counter *cnt) +{ + if (cnt->usage < cnt->limit) + return true; + + return false; +} + +/* + * Helper function to detect if the cgroup is within it's limit or + * not. It's currently called from cgroup_rss_prepare() + */ +static inline bool res_counter_check_under_limit(struct res_counter *cnt) +{ + bool ret; + unsigned long flags; + + spin_lock_irqsave(&cnt->lock, flags); + ret = res_counter_limit_check_locked(cnt); + spin_unlock_irqrestore(&cnt->lock, flags); + return ret; +} + +#endif diff --git a/include/linux/rmap.h b/include/linux/rmap.h index 97347f22fc20..1383692ac5bd 100644 --- a/include/linux/rmap.h +++ b/include/linux/rmap.h @@ -8,6 +8,7 @@ #include <linux/slab.h> #include <linux/mm.h> #include <linux/spinlock.h> +#include <linux/memcontrol.h> /* * The anon_vma heads a list of private "related" vmas, to scan if @@ -86,7 +87,7 @@ static inline void page_dup_rmap(struct page *page, struct vm_area_struct *vma, /* * Called from mm/vmscan.c to handle paging out */ -int page_referenced(struct page *, int is_locked); +int page_referenced(struct page *, int is_locked, struct mem_cgroup *cnt); int try_to_unmap(struct page *, int ignore_refs); /* @@ -114,7 +115,7 @@ int page_mkclean(struct page *); #define anon_vma_prepare(vma) (0) #define anon_vma_link(vma) do {} while (0) -#define page_referenced(page,l) TestClearPageReferenced(page) +#define page_referenced(page,l,cnt) TestClearPageReferenced(page) #define try_to_unmap(page, refs) SWAP_FAIL static inline int page_mkclean(struct page *page) diff --git a/include/linux/sched.h b/include/linux/sched.h index 7c8ca05c3cae..8a4812c1c038 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -92,6 +92,7 @@ struct sched_param { #include <asm/processor.h> +struct mem_cgroup; struct exec_domain; struct futex_pi_state; struct robust_list_head; diff --git a/include/linux/serial167.h b/include/linux/serial167.h index 71b6df2516a6..59c81b708562 100644 --- a/include/linux/serial167.h +++ b/include/linux/serial167.h @@ -37,7 +37,6 @@ struct cyclades_port { int ignore_status_mask; int close_delay; int IER; /* Interrupt Enable Register */ - unsigned long event; unsigned long last_active; int count; /* # of fd on device */ int x_char; /* to be pushed out ASAP */ @@ -49,7 +48,6 @@ struct cyclades_port { int xmit_cnt; int default_threshold; int default_timeout; - struct work_struct tqueue; wait_queue_head_t open_wait; wait_queue_head_t close_wait; struct cyclades_monitor mon; @@ -67,18 +65,6 @@ struct cyclades_port { #define CYGETDEFTIMEOUT 0x435908 #define CYSETDEFTIMEOUT 0x435909 -/* - * Events are used to schedule things to happen at timer-interrupt - * time, instead of at cy interrupt time. - */ -#define Cy_EVENT_READ_PROCESS 0 -#define Cy_EVENT_WRITE_WAKEUP 1 -#define Cy_EVENT_HANGUP 2 -#define Cy_EVENT_BREAK 3 -#define Cy_EVENT_OPEN_WAKEUP 4 - - - #define CyMaxChipsPerCard 1 /**** cd2401 registers ****/ diff --git a/include/linux/shm.h b/include/linux/shm.h index eeaed921a1dc..eca6235a46c0 100644 --- a/include/linux/shm.h +++ b/include/linux/shm.h @@ -3,7 +3,11 @@ #include <linux/ipc.h> #include <linux/errno.h> +#ifdef __KERNEL__ #include <asm/page.h> +#else +#include <unistd.h> +#endif /* * SHMMAX, SHMMNI and SHMALL are upper limits are defaults which can @@ -13,7 +17,11 @@ #define SHMMAX 0x2000000 /* max shared seg size (bytes) */ #define SHMMIN 1 /* min shared seg size (bytes) */ #define SHMMNI 4096 /* max num of segs system wide */ +#ifdef __KERNEL__ #define SHMALL (SHMMAX/PAGE_SIZE*(SHMMNI/16)) /* max shm system wide (pages) */ +#else +#define SHMALL (SHMMAX/getpagesize()*(SHMMNI/16)) +#endif #define SHMSEG SHMMNI /* max shared segs per process */ #ifdef __KERNEL__ diff --git a/include/linux/stallion.h b/include/linux/stallion.h index 94b4a10b912f..0424d75a5aaa 100644 --- a/include/linux/stallion.h +++ b/include/linux/stallion.h @@ -95,7 +95,6 @@ struct stlport { struct tty_struct *tty; wait_queue_head_t open_wait; wait_queue_head_t close_wait; - struct work_struct tqueue; comstats_t stats; struct stlrq tx; }; diff --git a/include/linux/swap.h b/include/linux/swap.h index 353153ea0bd5..3ca5c4bd6d3f 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -5,6 +5,7 @@ #include <linux/linkage.h> #include <linux/mmzone.h> #include <linux/list.h> +#include <linux/memcontrol.h> #include <linux/sched.h> #include <asm/atomic.h> @@ -182,6 +183,9 @@ extern void swap_setup(void); /* linux/mm/vmscan.c */ extern unsigned long try_to_free_pages(struct zone **zones, int order, gfp_t gfp_mask); +extern unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *mem, + gfp_t gfp_mask); +extern int __isolate_lru_page(struct page *page, int mode); extern unsigned long shrink_all_memory(unsigned long nr_pages); extern int vm_swappiness; extern int remove_mapping(struct address_space *mapping, struct page *page); diff --git a/include/linux/tty.h b/include/linux/tty.h index 5824a9777ad7..dd8e08fe8855 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -53,13 +53,6 @@ */ #define __DISABLED_CHAR '\0' -/* - * This is the flip buffer used for the tty driver. The buffer is - * located in the tty structure, and is used as a high speed interface - * between the tty driver and the tty line discipline. - */ -#define TTY_FLIPBUF_SIZE 512 - struct tty_buffer { struct tty_buffer *next; char *char_buf_ptr; diff --git a/init/Kconfig b/init/Kconfig index 92b23e256614..95ac2657b0f4 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -369,6 +369,13 @@ config CGROUP_CPUACCT Provides a simple Resource Controller for monitoring the total CPU consumed by the tasks in a cgroup +config RESOURCE_COUNTERS + bool "Resource counters" + help + This option enables controller independent resource accounting + infrastructure that works with cgroups + depends on CGROUPS + config SYSFS_DEPRECATED bool "Create deprecated sysfs files" depends on SYSFS @@ -390,6 +397,13 @@ config SYSFS_DEPRECATED If you are using a distro that was released in 2006 or later, it should be safe to say N here. +config CGROUP_MEM_CONT + bool "Memory controller for cgroups" + depends on CGROUPS && RESOURCE_COUNTERS + help + Provides a memory controller that manages both page cache and + RSS memory. + config PROC_PID_CPUSET bool "Include legacy /proc/<pid>/cpuset file" depends on CPUSETS diff --git a/kernel/Makefile b/kernel/Makefile index 135a1b943446..685697c0a181 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_CGROUP_DEBUG) += cgroup_debug.o obj-$(CONFIG_CPUSETS) += cpuset.o obj-$(CONFIG_CGROUP_NS) += ns_cgroup.o obj-$(CONFIG_IKCONFIG) += configs.o +obj-$(CONFIG_RESOURCE_COUNTERS) += res_counter.o obj-$(CONFIG_STOP_MACHINE) += stop_machine.o obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o obj-$(CONFIG_AUDIT) += audit.o auditfilter.o diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 1a3c23936d43..4766bb65e4d9 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -141,7 +141,7 @@ enum { ROOT_NOPREFIX, /* mounted subsystems have no named prefix */ }; -inline int cgroup_is_releasable(const struct cgroup *cgrp) +static int cgroup_is_releasable(const struct cgroup *cgrp) { const int bits = (1 << CGRP_RELEASABLE) | @@ -149,7 +149,7 @@ inline int cgroup_is_releasable(const struct cgroup *cgrp) return (cgrp->flags & bits) == bits; } -inline int notify_on_release(const struct cgroup *cgrp) +static int notify_on_release(const struct cgroup *cgrp) { return test_bit(CGRP_NOTIFY_ON_RELEASE, &cgrp->flags); } @@ -489,7 +489,7 @@ static struct css_set *find_css_set( * Any task can increment and decrement the count field without lock. * So in general, code holding cgroup_mutex can't rely on the count * field not changing. However, if the count goes to zero, then only - * attach_task() can increment it again. Because a count of zero + * cgroup_attach_task() can increment it again. Because a count of zero * means that no tasks are currently attached, therefore there is no * way a task attached to that cgroup can fork (the other way to * increment the count). So code holding cgroup_mutex can safely @@ -520,17 +520,17 @@ static struct css_set *find_css_set( * The task_lock() exception * * The need for this exception arises from the action of - * attach_task(), which overwrites one tasks cgroup pointer with + * cgroup_attach_task(), which overwrites one tasks cgroup pointer with * another. It does so using cgroup_mutexe, however there are * several performance critical places that need to reference * task->cgroup without the expense of grabbing a system global * mutex. Therefore except as noted below, when dereferencing or, as - * in attach_task(), modifying a task'ss cgroup pointer we use + * in cgroup_attach_task(), modifying a task'ss cgroup pointer we use * task_lock(), which acts on a spinlock (task->alloc_lock) already in * the task_struct routinely used for such matters. * * P.S. One more locking exception. RCU is used to guard the - * update of a tasks cgroup pointer by attach_task() + * update of a tasks cgroup pointer by cgroup_attach_task() */ /** @@ -586,11 +586,27 @@ static struct inode *cgroup_new_inode(mode_t mode, struct super_block *sb) return inode; } +/* + * Call subsys's pre_destroy handler. + * This is called before css refcnt check. + */ + +static void cgroup_call_pre_destroy(struct cgroup *cgrp) +{ + struct cgroup_subsys *ss; + for_each_subsys(cgrp->root, ss) + if (ss->pre_destroy && cgrp->subsys[ss->subsys_id]) + ss->pre_destroy(ss, cgrp); + return; +} + + static void cgroup_diput(struct dentry *dentry, struct inode *inode) { /* is dentry a directory ? if so, kfree() associated cgroup */ if (S_ISDIR(inode->i_mode)) { struct cgroup *cgrp = dentry->d_fsdata; + struct cgroup_subsys *ss; BUG_ON(!(cgroup_is_removed(cgrp))); /* It's possible for external users to be holding css * reference counts on a cgroup; css_put() needs to @@ -599,6 +615,23 @@ static void cgroup_diput(struct dentry *dentry, struct inode *inode) * queue the cgroup to be handled by the release * agent */ synchronize_rcu(); + + mutex_lock(&cgroup_mutex); + /* + * Release the subsystem state objects. + */ + for_each_subsys(cgrp->root, ss) { + if (cgrp->subsys[ss->subsys_id]) + ss->destroy(ss, cgrp); + } + + cgrp->root->number_of_cgroups--; + mutex_unlock(&cgroup_mutex); + + /* Drop the active superblock reference that we took when we + * created the cgroup */ + deactivate_super(cgrp->root->sb); + kfree(cgrp); } iput(inode); @@ -1161,7 +1194,7 @@ static void get_first_subsys(const struct cgroup *cgrp, * Call holding cgroup_mutex. May take task_lock of * the task 'pid' during call. */ -static int attach_task(struct cgroup *cgrp, struct task_struct *tsk) +int cgroup_attach_task(struct cgroup *cgrp, struct task_struct *tsk) { int retval = 0; struct cgroup_subsys *ss; @@ -1181,9 +1214,8 @@ static int attach_task(struct cgroup *cgrp, struct task_struct *tsk) for_each_subsys(root, ss) { if (ss->can_attach) { retval = ss->can_attach(ss, cgrp, tsk); - if (retval) { + if (retval) return retval; - } } } @@ -1192,9 +1224,8 @@ static int attach_task(struct cgroup *cgrp, struct task_struct *tsk) * based on its final set of cgroups */ newcg = find_css_set(cg, cgrp); - if (!newcg) { + if (!newcg) return -ENOMEM; - } task_lock(tsk); if (tsk->flags & PF_EXITING) { @@ -1214,9 +1245,8 @@ static int attach_task(struct cgroup *cgrp, struct task_struct *tsk) write_unlock(&css_set_lock); for_each_subsys(root, ss) { - if (ss->attach) { + if (ss->attach) ss->attach(ss, cgrp, oldcgrp, tsk); - } } set_bit(CGRP_RELEASABLE, &oldcgrp->flags); synchronize_rcu(); @@ -1239,7 +1269,7 @@ static int attach_task_by_pid(struct cgroup *cgrp, char *pidbuf) if (pid) { rcu_read_lock(); - tsk = find_task_by_pid(pid); + tsk = find_task_by_vpid(pid); if (!tsk || tsk->flags & PF_EXITING) { rcu_read_unlock(); return -ESRCH; @@ -1257,7 +1287,7 @@ static int attach_task_by_pid(struct cgroup *cgrp, char *pidbuf) get_task_struct(tsk); } - ret = attach_task(cgrp, tsk); + ret = cgroup_attach_task(cgrp, tsk); put_task_struct(tsk); return ret; } @@ -1329,9 +1359,14 @@ static ssize_t cgroup_common_file_write(struct cgroup *cgrp, goto out1; } buffer[nbytes] = 0; /* nul-terminate */ + strstrip(buffer); /* strip -just- trailing whitespace */ mutex_lock(&cgroup_mutex); + /* + * This was already checked for in cgroup_file_write(), but + * check again now we're holding cgroup_mutex. + */ if (cgroup_is_removed(cgrp)) { retval = -ENODEV; goto out2; @@ -1349,24 +1384,9 @@ static ssize_t cgroup_common_file_write(struct cgroup *cgrp, clear_bit(CGRP_NOTIFY_ON_RELEASE, &cgrp->flags); break; case FILE_RELEASE_AGENT: - { - struct cgroupfs_root *root = cgrp->root; - /* Strip trailing newline */ - if (nbytes && (buffer[nbytes-1] == '\n')) { - buffer[nbytes-1] = 0; - } - if (nbytes < sizeof(root->release_agent_path)) { - /* We never write anything other than '\0' - * into the last char of release_agent_path, - * so it always remains a NUL-terminated - * string */ - strncpy(root->release_agent_path, buffer, nbytes); - root->release_agent_path[nbytes] = 0; - } else { - retval = -ENOSPC; - } + BUILD_BUG_ON(sizeof(cgrp->root->release_agent_path) < PATH_MAX); + strcpy(cgrp->root->release_agent_path, buffer); break; - } default: retval = -EINVAL; goto out2; @@ -1387,7 +1407,7 @@ static ssize_t cgroup_file_write(struct file *file, const char __user *buf, struct cftype *cft = __d_cft(file->f_dentry); struct cgroup *cgrp = __d_cgrp(file->f_dentry->d_parent); - if (!cft) + if (!cft || cgroup_is_removed(cgrp)) return -ENODEV; if (cft->write) return cft->write(cgrp, cft, file, buf, nbytes, ppos); @@ -1457,7 +1477,7 @@ static ssize_t cgroup_file_read(struct file *file, char __user *buf, struct cftype *cft = __d_cft(file->f_dentry); struct cgroup *cgrp = __d_cgrp(file->f_dentry->d_parent); - if (!cft) + if (!cft || cgroup_is_removed(cgrp)) return -ENODEV; if (cft->read) @@ -1675,6 +1695,29 @@ static void cgroup_advance_iter(struct cgroup *cgrp, it->task = cg->tasks.next; } +/* + * To reduce the fork() overhead for systems that are not actually + * using their cgroups capability, we don't maintain the lists running + * through each css_set to its tasks until we see the list actually + * used - in other words after the first call to cgroup_iter_start(). + * + * The tasklist_lock is not held here, as do_each_thread() and + * while_each_thread() are protected by RCU. + */ +void cgroup_enable_task_cg_lists(void) +{ + struct task_struct *p, *g; + write_lock(&css_set_lock); + use_task_css_set_links = 1; + do_each_thread(g, p) { + task_lock(p); + if (list_empty(&p->cg_list)) + list_add(&p->cg_list, &p->cgroups->tasks); + task_unlock(p); + } while_each_thread(g, p); + write_unlock(&css_set_lock); +} + void cgroup_iter_start(struct cgroup *cgrp, struct cgroup_iter *it) { /* @@ -1682,18 +1725,9 @@ void cgroup_iter_start(struct cgroup *cgrp, struct cgroup_iter *it) * we need to enable the list linking each css_set to its * tasks, and fix up all existing tasks. */ - if (!use_task_css_set_links) { - struct task_struct *p, *g; - write_lock(&css_set_lock); - use_task_css_set_links = 1; - do_each_thread(g, p) { - task_lock(p); - if (list_empty(&p->cg_list)) - list_add(&p->cg_list, &p->cgroups->tasks); - task_unlock(p); - } while_each_thread(g, p); - write_unlock(&css_set_lock); - } + if (!use_task_css_set_links) + cgroup_enable_task_cg_lists(); + read_lock(&css_set_lock); it->cg_link = &cgrp->css_sets; cgroup_advance_iter(cgrp, it); @@ -1726,6 +1760,166 @@ void cgroup_iter_end(struct cgroup *cgrp, struct cgroup_iter *it) read_unlock(&css_set_lock); } +static inline int started_after_time(struct task_struct *t1, + struct timespec *time, + struct task_struct *t2) +{ + int start_diff = timespec_compare(&t1->start_time, time); + if (start_diff > 0) { + return 1; + } else if (start_diff < 0) { + return 0; + } else { + /* + * Arbitrarily, if two processes started at the same + * time, we'll say that the lower pointer value + * started first. Note that t2 may have exited by now + * so this may not be a valid pointer any longer, but + * that's fine - it still serves to distinguish + * between two tasks started (effectively) simultaneously. + */ + return t1 > t2; + } +} + +/* + * This function is a callback from heap_insert() and is used to order + * the heap. + * In this case we order the heap in descending task start time. + */ +static inline int started_after(void *p1, void *p2) +{ + struct task_struct *t1 = p1; + struct task_struct *t2 = p2; + return started_after_time(t1, &t2->start_time, t2); +} + +/** + * cgroup_scan_tasks - iterate though all the tasks in a cgroup + * @scan: struct cgroup_scanner containing arguments for the scan + * + * Arguments include pointers to callback functions test_task() and + * process_task(). + * Iterate through all the tasks in a cgroup, calling test_task() for each, + * and if it returns true, call process_task() for it also. + * The test_task pointer may be NULL, meaning always true (select all tasks). + * Effectively duplicates cgroup_iter_{start,next,end}() + * but does not lock css_set_lock for the call to process_task(). + * The struct cgroup_scanner may be embedded in any structure of the caller's + * creation. + * It is guaranteed that process_task() will act on every task that + * is a member of the cgroup for the duration of this call. This + * function may or may not call process_task() for tasks that exit + * or move to a different cgroup during the call, or are forked or + * move into the cgroup during the call. + * + * Note that test_task() may be called with locks held, and may in some + * situations be called multiple times for the same task, so it should + * be cheap. + * If the heap pointer in the struct cgroup_scanner is non-NULL, a heap has been + * pre-allocated and will be used for heap operations (and its "gt" member will + * be overwritten), else a temporary heap will be used (allocation of which + * may cause this function to fail). + */ +int cgroup_scan_tasks(struct cgroup_scanner *scan) +{ + int retval, i; + struct cgroup_iter it; + struct task_struct *p, *dropped; + /* Never dereference latest_task, since it's not refcounted */ + struct task_struct *latest_task = NULL; + struct ptr_heap tmp_heap; + struct ptr_heap *heap; + struct timespec latest_time = { 0, 0 }; + + if (scan->heap) { + /* The caller supplied our heap and pre-allocated its memory */ + heap = scan->heap; + heap->gt = &started_after; + } else { + /* We need to allocate our own heap memory */ + heap = &tmp_heap; + retval = heap_init(heap, PAGE_SIZE, GFP_KERNEL, &started_after); + if (retval) + /* cannot allocate the heap */ + return retval; + } + + again: + /* + * Scan tasks in the cgroup, using the scanner's "test_task" callback + * to determine which are of interest, and using the scanner's + * "process_task" callback to process any of them that need an update. + * Since we don't want to hold any locks during the task updates, + * gather tasks to be processed in a heap structure. + * The heap is sorted by descending task start time. + * If the statically-sized heap fills up, we overflow tasks that + * started later, and in future iterations only consider tasks that + * started after the latest task in the previous pass. This + * guarantees forward progress and that we don't miss any tasks. + */ + heap->size = 0; + cgroup_iter_start(scan->cg, &it); + while ((p = cgroup_iter_next(scan->cg, &it))) { + /* + * Only affect tasks that qualify per the caller's callback, + * if he provided one + */ + if (scan->test_task && !scan->test_task(p, scan)) + continue; + /* + * Only process tasks that started after the last task + * we processed + */ + if (!started_after_time(p, &latest_time, latest_task)) + continue; + dropped = heap_insert(heap, p); + if (dropped == NULL) { + /* + * The new task was inserted; the heap wasn't + * previously full + */ + get_task_struct(p); + } else if (dropped != p) { + /* + * The new task was inserted, and pushed out a + * different task + */ + get_task_struct(p); + put_task_struct(dropped); + } + /* + * Else the new task was newer than anything already in + * the heap and wasn't inserted + */ + } + cgroup_iter_end(scan->cg, &it); + + if (heap->size) { + for (i = 0; i < heap->size; i++) { + struct task_struct *p = heap->ptrs[i]; + if (i == 0) { + latest_time = p->start_time; + latest_task = p; + } + /* Process the task per the caller's callback */ + scan->process_task(p, scan); + put_task_struct(p); + } + /* + * If we had to process any tasks at all, scan again + * in case some of them were in the middle of forking + * children that didn't get processed. + * Not the most efficient way to do it, but it avoids + * having to take callback_mutex in the fork path + */ + goto again; + } + if (heap == &tmp_heap) + heap_free(&tmp_heap); + return 0; +} + /* * Stuff for reading the 'tasks' file. * @@ -1761,7 +1955,7 @@ static int pid_array_load(pid_t *pidarray, int npids, struct cgroup *cgrp) while ((tsk = cgroup_iter_next(cgrp, &it))) { if (unlikely(n == npids)) break; - pidarray[n++] = task_pid_nr(tsk); + pidarray[n++] = task_pid_vnr(tsk); } cgroup_iter_end(cgrp, &it); return n; @@ -2126,9 +2320,8 @@ static inline int cgroup_has_css_refs(struct cgroup *cgrp) * matter, since it can only happen if the cgroup * has been deleted and hence no longer needs the * release agent to be called anyway. */ - if (css && atomic_read(&css->refcnt)) { + if (css && atomic_read(&css->refcnt)) return 1; - } } return 0; } @@ -2138,7 +2331,6 @@ static int cgroup_rmdir(struct inode *unused_dir, struct dentry *dentry) struct cgroup *cgrp = dentry->d_fsdata; struct dentry *d; struct cgroup *parent; - struct cgroup_subsys *ss; struct super_block *sb; struct cgroupfs_root *root; @@ -2157,17 +2349,19 @@ static int cgroup_rmdir(struct inode *unused_dir, struct dentry *dentry) parent = cgrp->parent; root = cgrp->root; sb = root->sb; + /* + * Call pre_destroy handlers of subsys + */ + cgroup_call_pre_destroy(cgrp); + /* + * Notify subsyses that rmdir() request comes. + */ if (cgroup_has_css_refs(cgrp)) { mutex_unlock(&cgroup_mutex); return -EBUSY; } - for_each_subsys(root, ss) { - if (cgrp->subsys[ss->subsys_id]) - ss->destroy(ss, cgrp); - } - spin_lock(&release_list_lock); set_bit(CGRP_REMOVED, &cgrp->flags); if (!list_empty(&cgrp->release_list)) @@ -2182,15 +2376,11 @@ static int cgroup_rmdir(struct inode *unused_dir, struct dentry *dentry) cgroup_d_remove_dir(d); dput(d); - root->number_of_cgroups--; set_bit(CGRP_RELEASABLE, &parent->flags); check_for_release(parent); mutex_unlock(&cgroup_mutex); - /* Drop the active superblock reference that we took when we - * created the cgroup */ - deactivate_super(sb); return 0; } @@ -2324,7 +2514,7 @@ out: * - Used for /proc/<pid>/cgroup. * - No need to task_lock(tsk) on this tsk->cgroup reference, as it * doesn't really matter if tsk->cgroup changes after we read it, - * and we take cgroup_mutex, keeping attach_task() from changing it + * and we take cgroup_mutex, keeping cgroup_attach_task() from changing it * anyway. No need to check that tsk->cgroup != NULL, thanks to * the_top_cgroup_hack in cgroup_exit(), which sets an exiting tasks * cgroup to top_cgroup. @@ -2435,7 +2625,7 @@ static struct file_operations proc_cgroupstats_operations = { * A pointer to the shared css_set was automatically copied in * fork.c by dup_task_struct(). However, we ignore that copy, since * it was not made under the protection of RCU or cgroup_mutex, so - * might no longer be a valid cgroup pointer. attach_task() might + * might no longer be a valid cgroup pointer. cgroup_attach_task() might * have already changed current->cgroups, allowing the previously * referenced cgroup group to be removed and freed. * @@ -2514,8 +2704,8 @@ void cgroup_post_fork(struct task_struct *child) * attach us to a different cgroup, decrementing the count on * the first cgroup that we never incremented. But in this case, * top_cgroup isn't going away, and either task has PF_EXITING set, - * which wards off any attach_task() attempts, or task is a failed - * fork, never visible to attach_task. + * which wards off any cgroup_attach_task() attempts, or task is a failed + * fork, never visible to cgroup_attach_task. * */ void cgroup_exit(struct task_struct *tsk, int run_callbacks) @@ -2655,7 +2845,7 @@ int cgroup_clone(struct task_struct *tsk, struct cgroup_subsys *subsys) } /* All seems fine. Finish by moving the task into the new cgroup */ - ret = attach_task(child, tsk); + ret = cgroup_attach_task(child, tsk); mutex_unlock(&cgroup_mutex); out_release: diff --git a/kernel/cpuset.c b/kernel/cpuset.c index cfaf6419d817..67b2bfe27814 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -38,7 +38,6 @@ #include <linux/mount.h> #include <linux/namei.h> #include <linux/pagemap.h> -#include <linux/prio_heap.h> #include <linux/proc_fs.h> #include <linux/rcupdate.h> #include <linux/sched.h> @@ -56,6 +55,8 @@ #include <asm/atomic.h> #include <linux/mutex.h> #include <linux/kfifo.h> +#include <linux/workqueue.h> +#include <linux/cgroup.h> /* * Tracks how many cpusets are currently defined in system. @@ -64,7 +65,7 @@ */ int number_of_cpusets __read_mostly; -/* Retrieve the cpuset from a cgroup */ +/* Forward declare cgroup structures */ struct cgroup_subsys cpuset_subsys; struct cpuset; @@ -96,6 +97,9 @@ struct cpuset { /* partition number for rebuild_sched_domains() */ int pn; + + /* used for walking a cpuset heirarchy */ + struct list_head stack_list; }; /* Retrieve the cpuset for a cgroup */ @@ -111,7 +115,10 @@ static inline struct cpuset *task_cs(struct task_struct *task) return container_of(task_subsys_state(task, cpuset_subsys_id), struct cpuset, css); } - +struct cpuset_hotplug_scanner { + struct cgroup_scanner scan; + struct cgroup *to; +}; /* bits in struct cpuset flags field */ typedef enum { @@ -160,17 +167,17 @@ static inline int is_spread_slab(const struct cpuset *cs) * number, and avoid having to lock and reload mems_allowed unless * the cpuset they're using changes generation. * - * A single, global generation is needed because attach_task() could + * A single, global generation is needed because cpuset_attach_task() could * reattach a task to a different cpuset, which must not have its * generation numbers aliased with those of that tasks previous cpuset. * * Generations are needed for mems_allowed because one task cannot - * modify anothers memory placement. So we must enable every task, + * modify another's memory placement. So we must enable every task, * on every visit to __alloc_pages(), to efficiently check whether * its current->cpuset->mems_allowed has changed, requiring an update * of its current->mems_allowed. * - * Since cpuset_mems_generation is guarded by manage_mutex, + * Since writes to cpuset_mems_generation are guarded by the cgroup lock * there is no need to mark it atomic. */ static int cpuset_mems_generation; @@ -182,17 +189,20 @@ static struct cpuset top_cpuset = { }; /* - * We have two global cpuset mutexes below. They can nest. - * It is ok to first take manage_mutex, then nest callback_mutex. We also - * require taking task_lock() when dereferencing a tasks cpuset pointer. - * See "The task_lock() exception", at the end of this comment. + * There are two global mutexes guarding cpuset structures. The first + * is the main control groups cgroup_mutex, accessed via + * cgroup_lock()/cgroup_unlock(). The second is the cpuset-specific + * callback_mutex, below. They can nest. It is ok to first take + * cgroup_mutex, then nest callback_mutex. We also require taking + * task_lock() when dereferencing a task's cpuset pointer. See "The + * task_lock() exception", at the end of this comment. * * A task must hold both mutexes to modify cpusets. If a task - * holds manage_mutex, then it blocks others wanting that mutex, + * holds cgroup_mutex, then it blocks others wanting that mutex, * ensuring that it is the only task able to also acquire callback_mutex * and be able to modify cpusets. It can perform various checks on * the cpuset structure first, knowing nothing will change. It can - * also allocate memory while just holding manage_mutex. While it is + * also allocate memory while just holding cgroup_mutex. While it is * performing these checks, various callback routines can briefly * acquire callback_mutex to query cpusets. Once it is ready to make * the changes, it takes callback_mutex, blocking everyone else. @@ -208,60 +218,16 @@ static struct cpuset top_cpuset = { * The task_struct fields mems_allowed and mems_generation may only * be accessed in the context of that task, so require no locks. * - * Any task can increment and decrement the count field without lock. - * So in general, code holding manage_mutex or callback_mutex can't rely - * on the count field not changing. However, if the count goes to - * zero, then only attach_task(), which holds both mutexes, can - * increment it again. Because a count of zero means that no tasks - * are currently attached, therefore there is no way a task attached - * to that cpuset can fork (the other way to increment the count). - * So code holding manage_mutex or callback_mutex can safely assume that - * if the count is zero, it will stay zero. Similarly, if a task - * holds manage_mutex or callback_mutex on a cpuset with zero count, it - * knows that the cpuset won't be removed, as cpuset_rmdir() needs - * both of those mutexes. - * * The cpuset_common_file_write handler for operations that modify - * the cpuset hierarchy holds manage_mutex across the entire operation, + * the cpuset hierarchy holds cgroup_mutex across the entire operation, * single threading all such cpuset modifications across the system. * * The cpuset_common_file_read() handlers only hold callback_mutex across * small pieces of code, such as when reading out possibly multi-word * cpumasks and nodemasks. * - * The fork and exit callbacks cpuset_fork() and cpuset_exit(), don't - * (usually) take either mutex. These are the two most performance - * critical pieces of code here. The exception occurs on cpuset_exit(), - * when a task in a notify_on_release cpuset exits. Then manage_mutex - * is taken, and if the cpuset count is zero, a usermode call made - * to /sbin/cpuset_release_agent with the name of the cpuset (path - * relative to the root of cpuset file system) as the argument. - * - * A cpuset can only be deleted if both its 'count' of using tasks - * is zero, and its list of 'children' cpusets is empty. Since all - * tasks in the system use _some_ cpuset, and since there is always at - * least one task in the system (init), therefore, top_cpuset - * always has either children cpusets and/or using tasks. So we don't - * need a special hack to ensure that top_cpuset cannot be deleted. - * - * The above "Tale of Two Semaphores" would be complete, but for: - * - * The task_lock() exception - * - * The need for this exception arises from the action of attach_task(), - * which overwrites one tasks cpuset pointer with another. It does - * so using both mutexes, however there are several performance - * critical places that need to reference task->cpuset without the - * expense of grabbing a system global mutex. Therefore except as - * noted below, when dereferencing or, as in attach_task(), modifying - * a tasks cpuset pointer we use task_lock(), which acts on a spinlock - * (task->alloc_lock) already in the task_struct routinely used for - * such matters. - * - * P.S. One more locking exception. RCU is used to guard the - * update of a tasks cpuset pointer by attach_task() and the - * access of task->cpuset->mems_generation via that pointer in - * the routine cpuset_update_task_memory_state(). + * Accessing a task's cpuset should be done in accordance with the + * guidelines for accessing subsystem state in kernel/cgroup.c */ static DEFINE_MUTEX(callback_mutex); @@ -354,15 +320,14 @@ static void guarantee_online_mems(const struct cpuset *cs, nodemask_t *pmask) * Do not call this routine if in_interrupt(). * * Call without callback_mutex or task_lock() held. May be - * called with or without manage_mutex held. Thanks in part to - * 'the_top_cpuset_hack', the tasks cpuset pointer will never + * called with or without cgroup_mutex held. Thanks in part to + * 'the_top_cpuset_hack', the task's cpuset pointer will never * be NULL. This routine also might acquire callback_mutex and * current->mm->mmap_sem during call. * * Reading current->cpuset->mems_generation doesn't need task_lock * to guard the current->cpuset derefence, because it is guarded - * from concurrent freeing of current->cpuset by attach_task(), - * using RCU. + * from concurrent freeing of current->cpuset using RCU. * * The rcu_dereference() is technically probably not needed, * as I don't actually mind if I see a new cpuset pointer but @@ -424,7 +389,7 @@ void cpuset_update_task_memory_state(void) * * One cpuset is a subset of another if all its allowed CPUs and * Memory Nodes are a subset of the other, and its exclusive flags - * are only set if the other's are set. Call holding manage_mutex. + * are only set if the other's are set. Call holding cgroup_mutex. */ static int is_cpuset_subset(const struct cpuset *p, const struct cpuset *q) @@ -442,7 +407,7 @@ static int is_cpuset_subset(const struct cpuset *p, const struct cpuset *q) * If we replaced the flag and mask values of the current cpuset * (cur) with those values in the trial cpuset (trial), would * our various subset and exclusive rules still be valid? Presumes - * manage_mutex held. + * cgroup_mutex held. * * 'cur' is the address of an actual, in-use cpuset. Operations * such as list traversal that depend on the actual address of the @@ -476,7 +441,10 @@ static int validate_change(const struct cpuset *cur, const struct cpuset *trial) if (!is_cpuset_subset(trial, par)) return -EACCES; - /* If either I or some sibling (!= me) is exclusive, we can't overlap */ + /* + * If either I or some sibling (!= me) is exclusive, we can't + * overlap + */ list_for_each_entry(cont, &par->css.cgroup->children, sibling) { c = cgroup_cs(cont); if ((is_cpu_exclusive(trial) || is_cpu_exclusive(c)) && @@ -732,22 +700,50 @@ static inline int started_after(void *p1, void *p2) return started_after_time(t1, &t2->start_time, t2); } -/* - * Call with manage_mutex held. May take callback_mutex during call. +/** + * cpuset_test_cpumask - test a task's cpus_allowed versus its cpuset's + * @tsk: task to test + * @scan: struct cgroup_scanner contained in its struct cpuset_hotplug_scanner + * + * Call with cgroup_mutex held. May take callback_mutex during call. + * Called for each task in a cgroup by cgroup_scan_tasks(). + * Return nonzero if this tasks's cpus_allowed mask should be changed (in other + * words, if its mask is not equal to its cpuset's mask). + */ +int cpuset_test_cpumask(struct task_struct *tsk, struct cgroup_scanner *scan) +{ + return !cpus_equal(tsk->cpus_allowed, + (cgroup_cs(scan->cg))->cpus_allowed); +} + +/** + * cpuset_change_cpumask - make a task's cpus_allowed the same as its cpuset's + * @tsk: task to test + * @scan: struct cgroup_scanner containing the cgroup of the task + * + * Called by cgroup_scan_tasks() for each task in a cgroup whose + * cpus_allowed mask needs to be changed. + * + * We don't need to re-check for the cgroup/cpuset membership, since we're + * holding cgroup_lock() at this point. */ +void cpuset_change_cpumask(struct task_struct *tsk, struct cgroup_scanner *scan) +{ + set_cpus_allowed(tsk, (cgroup_cs(scan->cg))->cpus_allowed); +} +/** + * update_cpumask - update the cpus_allowed mask of a cpuset and all tasks in it + * @cs: the cpuset to consider + * @buf: buffer of cpu numbers written to this cpuset + */ static int update_cpumask(struct cpuset *cs, char *buf) { struct cpuset trialcs; - int retval, i; - int is_load_balanced; - struct cgroup_iter it; - struct cgroup *cgrp = cs->css.cgroup; - struct task_struct *p, *dropped; - /* Never dereference latest_task, since it's not refcounted */ - struct task_struct *latest_task = NULL; + struct cgroup_scanner scan; struct ptr_heap heap; - struct timespec latest_time = { 0, 0 }; + int retval; + int is_load_balanced; /* top_cpuset.cpus_allowed tracks cpu_online_map; it's read-only */ if (cs == &top_cpuset) @@ -756,7 +752,7 @@ static int update_cpumask(struct cpuset *cs, char *buf) trialcs = *cs; /* - * An empty cpus_allowed is ok iff there are no tasks in the cpuset. + * An empty cpus_allowed is ok only if the cpuset has no tasks. * Since cpulist_parse() fails on an empty mask, we special case * that parsing. The validate_change() call ensures that cpusets * with tasks have cpus. @@ -777,6 +773,7 @@ static int update_cpumask(struct cpuset *cs, char *buf) /* Nothing to do if the cpus didn't change */ if (cpus_equal(cs->cpus_allowed, trialcs.cpus_allowed)) return 0; + retval = heap_init(&heap, PAGE_SIZE, GFP_KERNEL, &started_after); if (retval) return retval; @@ -787,62 +784,19 @@ static int update_cpumask(struct cpuset *cs, char *buf) cs->cpus_allowed = trialcs.cpus_allowed; mutex_unlock(&callback_mutex); - again: /* * Scan tasks in the cpuset, and update the cpumasks of any - * that need an update. Since we can't call set_cpus_allowed() - * while holding tasklist_lock, gather tasks to be processed - * in a heap structure. If the statically-sized heap fills up, - * overflow tasks that started later, and in future iterations - * only consider tasks that started after the latest task in - * the previous pass. This guarantees forward progress and - * that we don't miss any tasks + * that need an update. */ - heap.size = 0; - cgroup_iter_start(cgrp, &it); - while ((p = cgroup_iter_next(cgrp, &it))) { - /* Only affect tasks that don't have the right cpus_allowed */ - if (cpus_equal(p->cpus_allowed, cs->cpus_allowed)) - continue; - /* - * Only process tasks that started after the last task - * we processed - */ - if (!started_after_time(p, &latest_time, latest_task)) - continue; - dropped = heap_insert(&heap, p); - if (dropped == NULL) { - get_task_struct(p); - } else if (dropped != p) { - get_task_struct(p); - put_task_struct(dropped); - } - } - cgroup_iter_end(cgrp, &it); - if (heap.size) { - for (i = 0; i < heap.size; i++) { - struct task_struct *p = heap.ptrs[i]; - if (i == 0) { - latest_time = p->start_time; - latest_task = p; - } - set_cpus_allowed(p, cs->cpus_allowed); - put_task_struct(p); - } - /* - * If we had to process any tasks at all, scan again - * in case some of them were in the middle of forking - * children that didn't notice the new cpumask - * restriction. Not the most efficient way to do it, - * but it avoids having to take callback_mutex in the - * fork path - */ - goto again; - } + scan.cg = cs->css.cgroup; + scan.test_task = cpuset_test_cpumask; + scan.process_task = cpuset_change_cpumask; + scan.heap = &heap; + cgroup_scan_tasks(&scan); heap_free(&heap); + if (is_load_balanced) rebuild_sched_domains(); - return 0; } @@ -854,11 +808,11 @@ static int update_cpumask(struct cpuset *cs, char *buf) * Temporarilly set tasks mems_allowed to target nodes of migration, * so that the migration code can allocate pages on these nodes. * - * Call holding manage_mutex, so our current->cpuset won't change - * during this call, as manage_mutex holds off any attach_task() + * Call holding cgroup_mutex, so current's cpuset won't change + * during this call, as manage_mutex holds off any cpuset_attach() * calls. Therefore we don't need to take task_lock around the * call to guarantee_online_mems(), as we know no one is changing - * our tasks cpuset. + * our task's cpuset. * * Hold callback_mutex around the two modifications of our tasks * mems_allowed to synchronize with cpuset_mems_allowed(). @@ -903,7 +857,7 @@ static void cpuset_migrate_mm(struct mm_struct *mm, const nodemask_t *from, * the cpuset is marked 'memory_migrate', migrate the tasks * pages to the new memory. * - * Call with manage_mutex held. May take callback_mutex during call. + * Call with cgroup_mutex held. May take callback_mutex during call. * Will take tasklist_lock, scan tasklist for tasks in cpuset cs, * lock each such tasks mm->mmap_sem, scan its vma's and rebind * their mempolicies to the cpusets new mems_allowed. @@ -1016,7 +970,7 @@ static int update_nodemask(struct cpuset *cs, char *buf) * tasklist_lock. Forks can happen again now - the mpol_copy() * cpuset_being_rebound check will catch such forks, and rebind * their vma mempolicies too. Because we still hold the global - * cpuset manage_mutex, we know that no other rebind effort will + * cgroup_mutex, we know that no other rebind effort will * be contending for the global variable cpuset_being_rebound. * It's ok if we rebind the same mm twice; mpol_rebind_mm() * is idempotent. Also migrate pages in each mm to new nodes. @@ -1031,7 +985,7 @@ static int update_nodemask(struct cpuset *cs, char *buf) mmput(mm); } - /* We're done rebinding vma's to this cpusets new mems_allowed. */ + /* We're done rebinding vmas to this cpuset's new mems_allowed. */ kfree(mmarray); cpuset_being_rebound = NULL; retval = 0; @@ -1045,7 +999,7 @@ int current_cpuset_is_being_rebound(void) } /* - * Call with manage_mutex held. + * Call with cgroup_mutex held. */ static int update_memory_pressure_enabled(struct cpuset *cs, char *buf) @@ -1066,7 +1020,7 @@ static int update_memory_pressure_enabled(struct cpuset *cs, char *buf) * cs: the cpuset to update * buf: the buffer where we read the 0 or 1 * - * Call with manage_mutex held. + * Call with cgroup_mutex held. */ static int update_flag(cpuset_flagbits_t bit, struct cpuset *cs, char *buf) @@ -1200,6 +1154,7 @@ static int fmeter_getrate(struct fmeter *fmp) return val; } +/* Called by cgroups to determine if a cpuset is usable; cgroup_mutex held */ static int cpuset_can_attach(struct cgroup_subsys *ss, struct cgroup *cont, struct task_struct *tsk) { @@ -1547,7 +1502,8 @@ static int cpuset_populate(struct cgroup_subsys *ss, struct cgroup *cont) * If this becomes a problem for some users who wish to * allow that scenario, then cpuset_post_clone() could be * changed to grant parent->cpus_allowed-sibling_cpus_exclusive - * (and likewise for mems) to the new cgroup. + * (and likewise for mems) to the new cgroup. Called with cgroup_mutex + * held. */ static void cpuset_post_clone(struct cgroup_subsys *ss, struct cgroup *cgroup) @@ -1571,11 +1527,8 @@ static void cpuset_post_clone(struct cgroup_subsys *ss, /* * cpuset_create - create a cpuset - * parent: cpuset that will be parent of the new cpuset. - * name: name of the new cpuset. Will be strcpy'ed. - * mode: mode to set on new inode - * - * Must be called with the mutex on the parent inode held + * ss: cpuset cgroup subsystem + * cont: control group that the new cpuset will be part of */ static struct cgroup_subsys_state *cpuset_create( @@ -1687,53 +1640,140 @@ int __init cpuset_init(void) return 0; } +/** + * cpuset_do_move_task - move a given task to another cpuset + * @tsk: pointer to task_struct the task to move + * @scan: struct cgroup_scanner contained in its struct cpuset_hotplug_scanner + * + * Called by cgroup_scan_tasks() for each task in a cgroup. + * Return nonzero to stop the walk through the tasks. + */ +void cpuset_do_move_task(struct task_struct *tsk, struct cgroup_scanner *scan) +{ + struct cpuset_hotplug_scanner *chsp; + + chsp = container_of(scan, struct cpuset_hotplug_scanner, scan); + cgroup_attach_task(chsp->to, tsk); +} + +/** + * move_member_tasks_to_cpuset - move tasks from one cpuset to another + * @from: cpuset in which the tasks currently reside + * @to: cpuset to which the tasks will be moved + * + * Called with cgroup_mutex held + * callback_mutex must not be held, as cpuset_attach() will take it. + * + * The cgroup_scan_tasks() function will scan all the tasks in a cgroup, + * calling callback functions for each. + */ +static void move_member_tasks_to_cpuset(struct cpuset *from, struct cpuset *to) +{ + struct cpuset_hotplug_scanner scan; + + scan.scan.cg = from->css.cgroup; + scan.scan.test_task = NULL; /* select all tasks in cgroup */ + scan.scan.process_task = cpuset_do_move_task; + scan.scan.heap = NULL; + scan.to = to->css.cgroup; + + if (cgroup_scan_tasks((struct cgroup_scanner *)&scan)) + printk(KERN_ERR "move_member_tasks_to_cpuset: " + "cgroup_scan_tasks failed\n"); +} + /* * If common_cpu_mem_hotplug_unplug(), below, unplugs any CPUs * or memory nodes, we need to walk over the cpuset hierarchy, * removing that CPU or node from all cpusets. If this removes the - * last CPU or node from a cpuset, then the guarantee_online_cpus() - * or guarantee_online_mems() code will use that emptied cpusets - * parent online CPUs or nodes. Cpusets that were already empty of - * CPUs or nodes are left empty. + * last CPU or node from a cpuset, then move the tasks in the empty + * cpuset to its next-highest non-empty parent. * - * This routine is intentionally inefficient in a couple of regards. - * It will check all cpusets in a subtree even if the top cpuset of - * the subtree has no offline CPUs or nodes. It checks both CPUs and - * nodes, even though the caller could have been coded to know that - * only one of CPUs or nodes needed to be checked on a given call. - * This was done to minimize text size rather than cpu cycles. + * Called with cgroup_mutex held + * callback_mutex must not be held, as cpuset_attach() will take it. + */ +static void remove_tasks_in_empty_cpuset(struct cpuset *cs) +{ + struct cpuset *parent; + + /* + * The cgroup's css_sets list is in use if there are tasks + * in the cpuset; the list is empty if there are none; + * the cs->css.refcnt seems always 0. + */ + if (list_empty(&cs->css.cgroup->css_sets)) + return; + + /* + * Find its next-highest non-empty parent, (top cpuset + * has online cpus, so can't be empty). + */ + parent = cs->parent; + while (cpus_empty(parent->cpus_allowed) || + nodes_empty(parent->mems_allowed)) + parent = parent->parent; + + move_member_tasks_to_cpuset(cs, parent); +} + +/* + * Walk the specified cpuset subtree and look for empty cpusets. + * The tasks of such cpuset must be moved to a parent cpuset. + * + * Called with cgroup_mutex held. We take callback_mutex to modify + * cpus_allowed and mems_allowed. * - * Call with both manage_mutex and callback_mutex held. + * This walk processes the tree from top to bottom, completing one layer + * before dropping down to the next. It always processes a node before + * any of its children. * - * Recursive, on depth of cpuset subtree. + * For now, since we lack memory hot unplug, we'll never see a cpuset + * that has tasks along with an empty 'mems'. But if we did see such + * a cpuset, we'd handle it just like we do if its 'cpus' was empty. */ - -static void guarantee_online_cpus_mems_in_subtree(const struct cpuset *cur) +static void scan_for_empty_cpusets(const struct cpuset *root) { + struct cpuset *cp; /* scans cpusets being updated */ + struct cpuset *child; /* scans child cpusets of cp */ + struct list_head queue; struct cgroup *cont; - struct cpuset *c; - /* Each of our child cpusets mems must be online */ - list_for_each_entry(cont, &cur->css.cgroup->children, sibling) { - c = cgroup_cs(cont); - guarantee_online_cpus_mems_in_subtree(c); - if (!cpus_empty(c->cpus_allowed)) - guarantee_online_cpus(c, &c->cpus_allowed); - if (!nodes_empty(c->mems_allowed)) - guarantee_online_mems(c, &c->mems_allowed); + INIT_LIST_HEAD(&queue); + + list_add_tail((struct list_head *)&root->stack_list, &queue); + + while (!list_empty(&queue)) { + cp = container_of(queue.next, struct cpuset, stack_list); + list_del(queue.next); + list_for_each_entry(cont, &cp->css.cgroup->children, sibling) { + child = cgroup_cs(cont); + list_add_tail(&child->stack_list, &queue); + } + cont = cp->css.cgroup; + + /* Continue past cpusets with all cpus, mems online */ + if (cpus_subset(cp->cpus_allowed, cpu_online_map) && + nodes_subset(cp->mems_allowed, node_states[N_HIGH_MEMORY])) + continue; + + /* Remove offline cpus and mems from this cpuset. */ + mutex_lock(&callback_mutex); + cpus_and(cp->cpus_allowed, cp->cpus_allowed, cpu_online_map); + nodes_and(cp->mems_allowed, cp->mems_allowed, + node_states[N_HIGH_MEMORY]); + mutex_unlock(&callback_mutex); + + /* Move tasks from the empty cpuset to a parent */ + if (cpus_empty(cp->cpus_allowed) || + nodes_empty(cp->mems_allowed)) + remove_tasks_in_empty_cpuset(cp); } } /* * The cpus_allowed and mems_allowed nodemasks in the top_cpuset track * cpu_online_map and node_states[N_HIGH_MEMORY]. Force the top cpuset to - * track what's online after any CPU or memory node hotplug or unplug - * event. - * - * To ensure that we don't remove a CPU or node from the top cpuset - * that is currently in use by a child cpuset (which would violate - * the rule that cpusets must be subsets of their parent), we first - * call the recursive routine guarantee_online_cpus_mems_in_subtree(). + * track what's online after any CPU or memory node hotplug or unplug event. * * Since there are two callers of this routine, one for CPU hotplug * events and one for memory node hotplug events, we could have coded @@ -1744,13 +1784,11 @@ static void guarantee_online_cpus_mems_in_subtree(const struct cpuset *cur) static void common_cpu_mem_hotplug_unplug(void) { cgroup_lock(); - mutex_lock(&callback_mutex); - guarantee_online_cpus_mems_in_subtree(&top_cpuset); top_cpuset.cpus_allowed = cpu_online_map; top_cpuset.mems_allowed = node_states[N_HIGH_MEMORY]; + scan_for_empty_cpusets(&top_cpuset); - mutex_unlock(&callback_mutex); cgroup_unlock(); } @@ -1826,7 +1864,7 @@ cpumask_t cpuset_cpus_allowed(struct task_struct *tsk) /** * cpuset_cpus_allowed_locked - return cpus_allowed mask from a tasks cpuset. - * Must be called with callback_mutex held. + * Must be called with callback_mutex held. **/ cpumask_t cpuset_cpus_allowed_locked(struct task_struct *tsk) { @@ -2163,10 +2201,8 @@ void __cpuset_memory_pressure_bump(void) * - Used for /proc/<pid>/cpuset. * - No need to task_lock(tsk) on this tsk->cpuset reference, as it * doesn't really matter if tsk->cpuset changes after we read it, - * and we take manage_mutex, keeping attach_task() from changing it - * anyway. No need to check that tsk->cpuset != NULL, thanks to - * the_top_cpuset_hack in cpuset_exit(), which sets an exiting tasks - * cpuset to top_cpuset. + * and we take cgroup_mutex, keeping cpuset_attach() from changing it + * anyway. */ static int proc_cpuset_show(struct seq_file *m, void *unused_v) { diff --git a/kernel/fork.c b/kernel/fork.c index 3995297567a9..b2ef8e4fad70 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -40,6 +40,7 @@ #include <linux/ptrace.h> #include <linux/mount.h> #include <linux/audit.h> +#include <linux/memcontrol.h> #include <linux/profile.h> #include <linux/rmap.h> #include <linux/acct.h> @@ -340,7 +341,7 @@ __cacheline_aligned_in_smp DEFINE_SPINLOCK(mmlist_lock); #include <linux/init_task.h> -static struct mm_struct * mm_init(struct mm_struct * mm) +static struct mm_struct * mm_init(struct mm_struct * mm, struct task_struct *p) { atomic_set(&mm->mm_users, 1); atomic_set(&mm->mm_count, 1); @@ -357,11 +358,14 @@ static struct mm_struct * mm_init(struct mm_struct * mm) mm->ioctx_list = NULL; mm->free_area_cache = TASK_UNMAPPED_BASE; mm->cached_hole_size = ~0UL; + mm_init_cgroup(mm, p); if (likely(!mm_alloc_pgd(mm))) { mm->def_flags = 0; return mm; } + + mm_free_cgroup(mm); free_mm(mm); return NULL; } @@ -376,7 +380,7 @@ struct mm_struct * mm_alloc(void) mm = allocate_mm(); if (mm) { memset(mm, 0, sizeof(*mm)); - mm = mm_init(mm); + mm = mm_init(mm, current); } return mm; } @@ -390,6 +394,7 @@ void fastcall __mmdrop(struct mm_struct *mm) { BUG_ON(mm == &init_mm); mm_free_pgd(mm); + mm_free_cgroup(mm); destroy_context(mm); free_mm(mm); } @@ -511,7 +516,7 @@ static struct mm_struct *dup_mm(struct task_struct *tsk) mm->token_priority = 0; mm->last_interval = 0; - if (!mm_init(mm)) + if (!mm_init(mm, tsk)) goto fail_nomem; if (init_new_context(tsk, mm)) diff --git a/kernel/kexec.c b/kernel/kexec.c index 9a26eec9eb04..06a0e2775651 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -1361,8 +1361,8 @@ unsigned long __attribute__ ((weak)) paddr_vmcoreinfo_note(void) static int __init crash_save_vmcoreinfo_init(void) { - vmcoreinfo_append_str("OSRELEASE=%s\n", init_uts_ns.name.release); - vmcoreinfo_append_str("PAGESIZE=%ld\n", PAGE_SIZE); + VMCOREINFO_OSRELEASE(init_uts_ns.name.release); + VMCOREINFO_PAGESIZE(PAGE_SIZE); VMCOREINFO_SYMBOL(init_uts_ns); VMCOREINFO_SYMBOL(node_online_map); @@ -1376,15 +1376,15 @@ static int __init crash_save_vmcoreinfo_init(void) #ifdef CONFIG_SPARSEMEM VMCOREINFO_SYMBOL(mem_section); VMCOREINFO_LENGTH(mem_section, NR_SECTION_ROOTS); - VMCOREINFO_SIZE(mem_section); + VMCOREINFO_STRUCT_SIZE(mem_section); VMCOREINFO_OFFSET(mem_section, section_mem_map); #endif - VMCOREINFO_SIZE(page); - VMCOREINFO_SIZE(pglist_data); - VMCOREINFO_SIZE(zone); - VMCOREINFO_SIZE(free_area); - VMCOREINFO_SIZE(list_head); - VMCOREINFO_TYPEDEF_SIZE(nodemask_t); + VMCOREINFO_STRUCT_SIZE(page); + VMCOREINFO_STRUCT_SIZE(pglist_data); + VMCOREINFO_STRUCT_SIZE(zone); + VMCOREINFO_STRUCT_SIZE(free_area); + VMCOREINFO_STRUCT_SIZE(list_head); + VMCOREINFO_SIZE(nodemask_t); VMCOREINFO_OFFSET(page, flags); VMCOREINFO_OFFSET(page, _count); VMCOREINFO_OFFSET(page, mapping); diff --git a/kernel/pid.c b/kernel/pid.c index f815455431bf..3b30bccdfcdc 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -368,6 +368,7 @@ struct task_struct * fastcall pid_task(struct pid *pid, enum pid_type type) } return result; } +EXPORT_SYMBOL(pid_task); /* * Must be called under rcu_read_lock() or with tasklist_lock read-held. diff --git a/kernel/res_counter.c b/kernel/res_counter.c new file mode 100644 index 000000000000..16cbec2d5d60 --- /dev/null +++ b/kernel/res_counter.c @@ -0,0 +1,134 @@ +/* + * resource cgroups + * + * Copyright 2007 OpenVZ SWsoft Inc + * + * Author: Pavel Emelianov <xemul@openvz.org> + * + */ + +#include <linux/types.h> +#include <linux/parser.h> +#include <linux/fs.h> +#include <linux/res_counter.h> +#include <linux/uaccess.h> + +void res_counter_init(struct res_counter *counter) +{ + spin_lock_init(&counter->lock); + counter->limit = (unsigned long long)LLONG_MAX; +} + +int res_counter_charge_locked(struct res_counter *counter, unsigned long val) +{ + if (counter->usage + val > counter->limit) { + counter->failcnt++; + return -ENOMEM; + } + + counter->usage += val; + return 0; +} + +int res_counter_charge(struct res_counter *counter, unsigned long val) +{ + int ret; + unsigned long flags; + + spin_lock_irqsave(&counter->lock, flags); + ret = res_counter_charge_locked(counter, val); + spin_unlock_irqrestore(&counter->lock, flags); + return ret; +} + +void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val) +{ + if (WARN_ON(counter->usage < val)) + val = counter->usage; + + counter->usage -= val; +} + +void res_counter_uncharge(struct res_counter *counter, unsigned long val) +{ + unsigned long flags; + + spin_lock_irqsave(&counter->lock, flags); + res_counter_uncharge_locked(counter, val); + spin_unlock_irqrestore(&counter->lock, flags); +} + + +static inline unsigned long long * +res_counter_member(struct res_counter *counter, int member) +{ + switch (member) { + case RES_USAGE: + return &counter->usage; + case RES_LIMIT: + return &counter->limit; + case RES_FAILCNT: + return &counter->failcnt; + }; + + BUG(); + return NULL; +} + +ssize_t res_counter_read(struct res_counter *counter, int member, + const char __user *userbuf, size_t nbytes, loff_t *pos, + int (*read_strategy)(unsigned long long val, char *st_buf)) +{ + unsigned long long *val; + char buf[64], *s; + + s = buf; + val = res_counter_member(counter, member); + if (read_strategy) + s += read_strategy(*val, s); + else + s += sprintf(s, "%llu\n", *val); + return simple_read_from_buffer((void __user *)userbuf, nbytes, + pos, buf, s - buf); +} + +ssize_t res_counter_write(struct res_counter *counter, int member, + const char __user *userbuf, size_t nbytes, loff_t *pos, + int (*write_strategy)(char *st_buf, unsigned long long *val)) +{ + int ret; + char *buf, *end; + unsigned long flags; + unsigned long long tmp, *val; + + buf = kmalloc(nbytes + 1, GFP_KERNEL); + ret = -ENOMEM; + if (buf == NULL) + goto out; + + buf[nbytes] = '\0'; + ret = -EFAULT; + if (copy_from_user(buf, userbuf, nbytes)) + goto out_free; + + ret = -EINVAL; + + if (write_strategy) { + if (write_strategy(buf, &tmp)) { + goto out_free; + } + } else { + tmp = simple_strtoull(buf, &end, 10); + if (*end != '\0') + goto out_free; + } + spin_lock_irqsave(&counter->lock, flags); + val = res_counter_member(counter, member); + *val = tmp; + spin_unlock_irqrestore(&counter->lock, flags); + ret = nbytes; +out_free: + kfree(buf); +out: + return ret; +} diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 86daaa26d120..8c98d8147d88 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -67,6 +67,7 @@ extern int sysctl_overcommit_memory; extern int sysctl_overcommit_ratio; extern int sysctl_panic_on_oom; extern int sysctl_oom_kill_allocating_task; +extern int sysctl_oom_dump_tasks; extern int max_threads; extern int core_uses_pid; extern int suid_dumpable; @@ -871,6 +872,14 @@ static struct ctl_table vm_table[] = { .proc_handler = &proc_dointvec, }, { + .ctl_name = CTL_UNNUMBERED, + .procname = "oom_dump_tasks", + .data = &sysctl_oom_dump_tasks, + .maxlen = sizeof(sysctl_oom_dump_tasks), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, + { .ctl_name = VM_OVERCOMMIT_RATIO, .procname = "overcommit_ratio", .data = &sysctl_overcommit_ratio, diff --git a/mm/Makefile b/mm/Makefile index 4af5dff37277..9f117bab5322 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -32,4 +32,5 @@ obj-$(CONFIG_FS_XIP) += filemap_xip.o obj-$(CONFIG_MIGRATION) += migrate.o obj-$(CONFIG_SMP) += allocpercpu.o obj-$(CONFIG_QUICKLIST) += quicklist.o +obj-$(CONFIG_CGROUP_MEM_CONT) += memcontrol.o diff --git a/mm/bootmem.c b/mm/bootmem.c index 00a96970b237..f6ff4337b424 100644 --- a/mm/bootmem.c +++ b/mm/bootmem.c @@ -111,11 +111,12 @@ static unsigned long __init init_bootmem_core(pg_data_t *pgdat, * might be used for boot-time allocations - or it might get added * to the free page pool later on. */ -static void __init reserve_bootmem_core(bootmem_data_t *bdata, unsigned long addr, - unsigned long size) +static int __init reserve_bootmem_core(bootmem_data_t *bdata, + unsigned long addr, unsigned long size, int flags) { unsigned long sidx, eidx; unsigned long i; + int ret; /* * round up, partially reserved pages are considered @@ -133,7 +134,20 @@ static void __init reserve_bootmem_core(bootmem_data_t *bdata, unsigned long add #ifdef CONFIG_DEBUG_BOOTMEM printk("hm, page %08lx reserved twice.\n", i*PAGE_SIZE); #endif + if (flags & BOOTMEM_EXCLUSIVE) { + ret = -EBUSY; + goto err; + } } + + return 0; + +err: + /* unreserve memory we accidentally reserved */ + for (i--; i >= sidx; i--) + clear_bit(i, bdata->node_bootmem_map); + + return ret; } static void __init free_bootmem_core(bootmem_data_t *bdata, unsigned long addr, @@ -374,9 +388,9 @@ unsigned long __init init_bootmem_node(pg_data_t *pgdat, unsigned long freepfn, } void __init reserve_bootmem_node(pg_data_t *pgdat, unsigned long physaddr, - unsigned long size) + unsigned long size, int flags) { - reserve_bootmem_core(pgdat->bdata, physaddr, size); + reserve_bootmem_core(pgdat->bdata, physaddr, size, flags); } void __init free_bootmem_node(pg_data_t *pgdat, unsigned long physaddr, @@ -398,9 +412,10 @@ unsigned long __init init_bootmem(unsigned long start, unsigned long pages) } #ifndef CONFIG_HAVE_ARCH_BOOTMEM_NODE -void __init reserve_bootmem(unsigned long addr, unsigned long size) +int __init reserve_bootmem(unsigned long addr, unsigned long size, + int flags) { - reserve_bootmem_core(NODE_DATA(0)->bdata, addr, size); + return reserve_bootmem_core(NODE_DATA(0)->bdata, addr, size, flags); } #endif /* !CONFIG_HAVE_ARCH_BOOTMEM_NODE */ diff --git a/mm/filemap.c b/mm/filemap.c index 81fb9bff0d4f..5357fcc4643b 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -33,6 +33,7 @@ #include <linux/syscalls.h> #include <linux/cpuset.h> #include <linux/hardirq.h> /* for BUG_ON(!in_atomic()) only */ +#include <linux/memcontrol.h> #include "internal.h" /* @@ -118,6 +119,7 @@ void __remove_from_page_cache(struct page *page) { struct address_space *mapping = page->mapping; + mem_cgroup_uncharge_page(page); radix_tree_delete(&mapping->page_tree, page->index); page->mapping = NULL; mapping->nrpages--; @@ -458,8 +460,12 @@ int filemap_write_and_wait_range(struct address_space *mapping, int add_to_page_cache(struct page *page, struct address_space *mapping, pgoff_t offset, gfp_t gfp_mask) { - int error = radix_tree_preload(gfp_mask & ~__GFP_HIGHMEM); + int error = mem_cgroup_cache_charge(page, current->mm, + gfp_mask & ~__GFP_HIGHMEM); + if (error) + goto out; + error = radix_tree_preload(gfp_mask & ~__GFP_HIGHMEM); if (error == 0) { write_lock_irq(&mapping->tree_lock); error = radix_tree_insert(&mapping->page_tree, offset, page); @@ -470,10 +476,14 @@ int add_to_page_cache(struct page *page, struct address_space *mapping, page->index = offset; mapping->nrpages++; __inc_zone_page_state(page, NR_FILE_PAGES); - } + } else + mem_cgroup_uncharge_page(page); + write_unlock_irq(&mapping->tree_lock); radix_tree_preload_end(); - } + } else + mem_cgroup_uncharge_page(page); +out: return error; } EXPORT_SYMBOL(add_to_page_cache); diff --git a/mm/memcontrol.c b/mm/memcontrol.c new file mode 100644 index 000000000000..5c2c702af617 --- /dev/null +++ b/mm/memcontrol.c @@ -0,0 +1,1192 @@ +/* memcontrol.c - Memory Controller + * + * Copyright IBM Corporation, 2007 + * Author Balbir Singh <balbir@linux.vnet.ibm.com> + * + * Copyright 2007 OpenVZ SWsoft Inc + * Author: Pavel Emelianov <xemul@openvz.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/res_counter.h> +#include <linux/memcontrol.h> +#include <linux/cgroup.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/page-flags.h> +#include <linux/backing-dev.h> +#include <linux/bit_spinlock.h> +#include <linux/rcupdate.h> +#include <linux/swap.h> +#include <linux/spinlock.h> +#include <linux/fs.h> +#include <linux/seq_file.h> + +#include <asm/uaccess.h> + +struct cgroup_subsys mem_cgroup_subsys; +static const int MEM_CGROUP_RECLAIM_RETRIES = 5; + +/* + * Statistics for memory cgroup. + */ +enum mem_cgroup_stat_index { + /* + * For MEM_CONTAINER_TYPE_ALL, usage = pagecache + rss. + */ + MEM_CGROUP_STAT_CACHE, /* # of pages charged as cache */ + MEM_CGROUP_STAT_RSS, /* # of pages charged as rss */ + + MEM_CGROUP_STAT_NSTATS, +}; + +struct mem_cgroup_stat_cpu { + s64 count[MEM_CGROUP_STAT_NSTATS]; +} ____cacheline_aligned_in_smp; + +struct mem_cgroup_stat { + struct mem_cgroup_stat_cpu cpustat[NR_CPUS]; +}; + +/* + * For accounting under irq disable, no need for increment preempt count. + */ +static void __mem_cgroup_stat_add_safe(struct mem_cgroup_stat *stat, + enum mem_cgroup_stat_index idx, int val) +{ + int cpu = smp_processor_id(); + stat->cpustat[cpu].count[idx] += val; +} + +static s64 mem_cgroup_read_stat(struct mem_cgroup_stat *stat, + enum mem_cgroup_stat_index idx) +{ + int cpu; + s64 ret = 0; + for_each_possible_cpu(cpu) + ret += stat->cpustat[cpu].count[idx]; + return ret; +} + +/* + * per-zone information in memory controller. + */ + +enum mem_cgroup_zstat_index { + MEM_CGROUP_ZSTAT_ACTIVE, + MEM_CGROUP_ZSTAT_INACTIVE, + + NR_MEM_CGROUP_ZSTAT, +}; + +struct mem_cgroup_per_zone { + /* + * spin_lock to protect the per cgroup LRU + */ + spinlock_t lru_lock; + struct list_head active_list; + struct list_head inactive_list; + unsigned long count[NR_MEM_CGROUP_ZSTAT]; +}; +/* Macro for accessing counter */ +#define MEM_CGROUP_ZSTAT(mz, idx) ((mz)->count[(idx)]) + +struct mem_cgroup_per_node { + struct mem_cgroup_per_zone zoneinfo[MAX_NR_ZONES]; +}; + +struct mem_cgroup_lru_info { + struct mem_cgroup_per_node *nodeinfo[MAX_NUMNODES]; +}; + +/* + * The memory controller data structure. The memory controller controls both + * page cache and RSS per cgroup. We would eventually like to provide + * statistics based on the statistics developed by Rik Van Riel for clock-pro, + * to help the administrator determine what knobs to tune. + * + * TODO: Add a water mark for the memory controller. Reclaim will begin when + * we hit the water mark. May be even add a low water mark, such that + * no reclaim occurs from a cgroup at it's low water mark, this is + * a feature that will be implemented much later in the future. + */ +struct mem_cgroup { + struct cgroup_subsys_state css; + /* + * the counter to account for memory usage + */ + struct res_counter res; + /* + * Per cgroup active and inactive list, similar to the + * per zone LRU lists. + */ + struct mem_cgroup_lru_info info; + + int prev_priority; /* for recording reclaim priority */ + /* + * statistics. + */ + struct mem_cgroup_stat stat; +}; + +/* + * We use the lower bit of the page->page_cgroup pointer as a bit spin + * lock. We need to ensure that page->page_cgroup is atleast two + * byte aligned (based on comments from Nick Piggin) + */ +#define PAGE_CGROUP_LOCK_BIT 0x0 +#define PAGE_CGROUP_LOCK (1 << PAGE_CGROUP_LOCK_BIT) + +/* + * A page_cgroup page is associated with every page descriptor. The + * page_cgroup helps us identify information about the cgroup + */ +struct page_cgroup { + struct list_head lru; /* per cgroup LRU list */ + struct page *page; + struct mem_cgroup *mem_cgroup; + atomic_t ref_cnt; /* Helpful when pages move b/w */ + /* mapped and cached states */ + int flags; +}; +#define PAGE_CGROUP_FLAG_CACHE (0x1) /* charged as cache */ +#define PAGE_CGROUP_FLAG_ACTIVE (0x2) /* page is active in this cgroup */ + +static inline int page_cgroup_nid(struct page_cgroup *pc) +{ + return page_to_nid(pc->page); +} + +static inline enum zone_type page_cgroup_zid(struct page_cgroup *pc) +{ + return page_zonenum(pc->page); +} + +enum { + MEM_CGROUP_TYPE_UNSPEC = 0, + MEM_CGROUP_TYPE_MAPPED, + MEM_CGROUP_TYPE_CACHED, + MEM_CGROUP_TYPE_ALL, + MEM_CGROUP_TYPE_MAX, +}; + +enum charge_type { + MEM_CGROUP_CHARGE_TYPE_CACHE = 0, + MEM_CGROUP_CHARGE_TYPE_MAPPED, +}; + + +/* + * Always modified under lru lock. Then, not necessary to preempt_disable() + */ +static void mem_cgroup_charge_statistics(struct mem_cgroup *mem, int flags, + bool charge) +{ + int val = (charge)? 1 : -1; + struct mem_cgroup_stat *stat = &mem->stat; + VM_BUG_ON(!irqs_disabled()); + + if (flags & PAGE_CGROUP_FLAG_CACHE) + __mem_cgroup_stat_add_safe(stat, + MEM_CGROUP_STAT_CACHE, val); + else + __mem_cgroup_stat_add_safe(stat, MEM_CGROUP_STAT_RSS, val); +} + +static inline struct mem_cgroup_per_zone * +mem_cgroup_zoneinfo(struct mem_cgroup *mem, int nid, int zid) +{ + BUG_ON(!mem->info.nodeinfo[nid]); + return &mem->info.nodeinfo[nid]->zoneinfo[zid]; +} + +static inline struct mem_cgroup_per_zone * +page_cgroup_zoneinfo(struct page_cgroup *pc) +{ + struct mem_cgroup *mem = pc->mem_cgroup; + int nid = page_cgroup_nid(pc); + int zid = page_cgroup_zid(pc); + + return mem_cgroup_zoneinfo(mem, nid, zid); +} + +static unsigned long mem_cgroup_get_all_zonestat(struct mem_cgroup *mem, + enum mem_cgroup_zstat_index idx) +{ + int nid, zid; + struct mem_cgroup_per_zone *mz; + u64 total = 0; + + for_each_online_node(nid) + for (zid = 0; zid < MAX_NR_ZONES; zid++) { + mz = mem_cgroup_zoneinfo(mem, nid, zid); + total += MEM_CGROUP_ZSTAT(mz, idx); + } + return total; +} + +static struct mem_cgroup init_mem_cgroup; + +static inline +struct mem_cgroup *mem_cgroup_from_cont(struct cgroup *cont) +{ + return container_of(cgroup_subsys_state(cont, + mem_cgroup_subsys_id), struct mem_cgroup, + css); +} + +static inline +struct mem_cgroup *mem_cgroup_from_task(struct task_struct *p) +{ + return container_of(task_subsys_state(p, mem_cgroup_subsys_id), + struct mem_cgroup, css); +} + +void mm_init_cgroup(struct mm_struct *mm, struct task_struct *p) +{ + struct mem_cgroup *mem; + + mem = mem_cgroup_from_task(p); + css_get(&mem->css); + mm->mem_cgroup = mem; +} + +void mm_free_cgroup(struct mm_struct *mm) +{ + css_put(&mm->mem_cgroup->css); +} + +static inline int page_cgroup_locked(struct page *page) +{ + return bit_spin_is_locked(PAGE_CGROUP_LOCK_BIT, + &page->page_cgroup); +} + +void page_assign_page_cgroup(struct page *page, struct page_cgroup *pc) +{ + int locked; + + /* + * While resetting the page_cgroup we might not hold the + * page_cgroup lock. free_hot_cold_page() is an example + * of such a scenario + */ + if (pc) + VM_BUG_ON(!page_cgroup_locked(page)); + locked = (page->page_cgroup & PAGE_CGROUP_LOCK); + page->page_cgroup = ((unsigned long)pc | locked); +} + +struct page_cgroup *page_get_page_cgroup(struct page *page) +{ + return (struct page_cgroup *) + (page->page_cgroup & ~PAGE_CGROUP_LOCK); +} + +static void __always_inline lock_page_cgroup(struct page *page) +{ + bit_spin_lock(PAGE_CGROUP_LOCK_BIT, &page->page_cgroup); + VM_BUG_ON(!page_cgroup_locked(page)); +} + +static void __always_inline unlock_page_cgroup(struct page *page) +{ + bit_spin_unlock(PAGE_CGROUP_LOCK_BIT, &page->page_cgroup); +} + +/* + * Tie new page_cgroup to struct page under lock_page_cgroup() + * This can fail if the page has been tied to a page_cgroup. + * If success, returns 0. + */ +static int page_cgroup_assign_new_page_cgroup(struct page *page, + struct page_cgroup *pc) +{ + int ret = 0; + + lock_page_cgroup(page); + if (!page_get_page_cgroup(page)) + page_assign_page_cgroup(page, pc); + else /* A page is tied to other pc. */ + ret = 1; + unlock_page_cgroup(page); + return ret; +} + +/* + * Clear page->page_cgroup member under lock_page_cgroup(). + * If given "pc" value is different from one page->page_cgroup, + * page->cgroup is not cleared. + * Returns a value of page->page_cgroup at lock taken. + * A can can detect failure of clearing by following + * clear_page_cgroup(page, pc) == pc + */ + +static struct page_cgroup *clear_page_cgroup(struct page *page, + struct page_cgroup *pc) +{ + struct page_cgroup *ret; + /* lock and clear */ + lock_page_cgroup(page); + ret = page_get_page_cgroup(page); + if (likely(ret == pc)) + page_assign_page_cgroup(page, NULL); + unlock_page_cgroup(page); + return ret; +} + +static void __mem_cgroup_remove_list(struct page_cgroup *pc) +{ + int from = pc->flags & PAGE_CGROUP_FLAG_ACTIVE; + struct mem_cgroup_per_zone *mz = page_cgroup_zoneinfo(pc); + + if (from) + MEM_CGROUP_ZSTAT(mz, MEM_CGROUP_ZSTAT_ACTIVE) -= 1; + else + MEM_CGROUP_ZSTAT(mz, MEM_CGROUP_ZSTAT_INACTIVE) -= 1; + + mem_cgroup_charge_statistics(pc->mem_cgroup, pc->flags, false); + list_del_init(&pc->lru); +} + +static void __mem_cgroup_add_list(struct page_cgroup *pc) +{ + int to = pc->flags & PAGE_CGROUP_FLAG_ACTIVE; + struct mem_cgroup_per_zone *mz = page_cgroup_zoneinfo(pc); + + if (!to) { + MEM_CGROUP_ZSTAT(mz, MEM_CGROUP_ZSTAT_INACTIVE) += 1; + list_add(&pc->lru, &mz->inactive_list); + } else { + MEM_CGROUP_ZSTAT(mz, MEM_CGROUP_ZSTAT_ACTIVE) += 1; + list_add(&pc->lru, &mz->active_list); + } + mem_cgroup_charge_statistics(pc->mem_cgroup, pc->flags, true); +} + +static void __mem_cgroup_move_lists(struct page_cgroup *pc, bool active) +{ + int from = pc->flags & PAGE_CGROUP_FLAG_ACTIVE; + struct mem_cgroup_per_zone *mz = page_cgroup_zoneinfo(pc); + + if (from) + MEM_CGROUP_ZSTAT(mz, MEM_CGROUP_ZSTAT_ACTIVE) -= 1; + else + MEM_CGROUP_ZSTAT(mz, MEM_CGROUP_ZSTAT_INACTIVE) -= 1; + + if (active) { + MEM_CGROUP_ZSTAT(mz, MEM_CGROUP_ZSTAT_ACTIVE) += 1; + pc->flags |= PAGE_CGROUP_FLAG_ACTIVE; + list_move(&pc->lru, &mz->active_list); + } else { + MEM_CGROUP_ZSTAT(mz, MEM_CGROUP_ZSTAT_INACTIVE) += 1; + pc->flags &= ~PAGE_CGROUP_FLAG_ACTIVE; + list_move(&pc->lru, &mz->inactive_list); + } +} + +int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *mem) +{ + int ret; + + task_lock(task); + ret = task->mm && mm_cgroup(task->mm) == mem; + task_unlock(task); + return ret; +} + +/* + * This routine assumes that the appropriate zone's lru lock is already held + */ +void mem_cgroup_move_lists(struct page_cgroup *pc, bool active) +{ + struct mem_cgroup_per_zone *mz; + unsigned long flags; + + if (!pc) + return; + + mz = page_cgroup_zoneinfo(pc); + spin_lock_irqsave(&mz->lru_lock, flags); + __mem_cgroup_move_lists(pc, active); + spin_unlock_irqrestore(&mz->lru_lock, flags); +} + +/* + * Calculate mapped_ratio under memory controller. This will be used in + * vmscan.c for deteremining we have to reclaim mapped pages. + */ +int mem_cgroup_calc_mapped_ratio(struct mem_cgroup *mem) +{ + long total, rss; + + /* + * usage is recorded in bytes. But, here, we assume the number of + * physical pages can be represented by "long" on any arch. + */ + total = (long) (mem->res.usage >> PAGE_SHIFT) + 1L; + rss = (long)mem_cgroup_read_stat(&mem->stat, MEM_CGROUP_STAT_RSS); + return (int)((rss * 100L) / total); +} +/* + * This function is called from vmscan.c. In page reclaiming loop. balance + * between active and inactive list is calculated. For memory controller + * page reclaiming, we should use using mem_cgroup's imbalance rather than + * zone's global lru imbalance. + */ +long mem_cgroup_reclaim_imbalance(struct mem_cgroup *mem) +{ + unsigned long active, inactive; + /* active and inactive are the number of pages. 'long' is ok.*/ + active = mem_cgroup_get_all_zonestat(mem, MEM_CGROUP_ZSTAT_ACTIVE); + inactive = mem_cgroup_get_all_zonestat(mem, MEM_CGROUP_ZSTAT_INACTIVE); + return (long) (active / (inactive + 1)); +} + +/* + * prev_priority control...this will be used in memory reclaim path. + */ +int mem_cgroup_get_reclaim_priority(struct mem_cgroup *mem) +{ + return mem->prev_priority; +} + +void mem_cgroup_note_reclaim_priority(struct mem_cgroup *mem, int priority) +{ + if (priority < mem->prev_priority) + mem->prev_priority = priority; +} + +void mem_cgroup_record_reclaim_priority(struct mem_cgroup *mem, int priority) +{ + mem->prev_priority = priority; +} + +/* + * Calculate # of pages to be scanned in this priority/zone. + * See also vmscan.c + * + * priority starts from "DEF_PRIORITY" and decremented in each loop. + * (see include/linux/mmzone.h) + */ + +long mem_cgroup_calc_reclaim_active(struct mem_cgroup *mem, + struct zone *zone, int priority) +{ + long nr_active; + int nid = zone->zone_pgdat->node_id; + int zid = zone_idx(zone); + struct mem_cgroup_per_zone *mz = mem_cgroup_zoneinfo(mem, nid, zid); + + nr_active = MEM_CGROUP_ZSTAT(mz, MEM_CGROUP_ZSTAT_ACTIVE); + return (nr_active >> priority); +} + +long mem_cgroup_calc_reclaim_inactive(struct mem_cgroup *mem, + struct zone *zone, int priority) +{ + long nr_inactive; + int nid = zone->zone_pgdat->node_id; + int zid = zone_idx(zone); + struct mem_cgroup_per_zone *mz = mem_cgroup_zoneinfo(mem, nid, zid); + + nr_inactive = MEM_CGROUP_ZSTAT(mz, MEM_CGROUP_ZSTAT_INACTIVE); + + return (nr_inactive >> priority); +} + +unsigned long mem_cgroup_isolate_pages(unsigned long nr_to_scan, + struct list_head *dst, + unsigned long *scanned, int order, + int mode, struct zone *z, + struct mem_cgroup *mem_cont, + int active) +{ + unsigned long nr_taken = 0; + struct page *page; + unsigned long scan; + LIST_HEAD(pc_list); + struct list_head *src; + struct page_cgroup *pc, *tmp; + int nid = z->zone_pgdat->node_id; + int zid = zone_idx(z); + struct mem_cgroup_per_zone *mz; + + mz = mem_cgroup_zoneinfo(mem_cont, nid, zid); + if (active) + src = &mz->active_list; + else + src = &mz->inactive_list; + + + spin_lock(&mz->lru_lock); + scan = 0; + list_for_each_entry_safe_reverse(pc, tmp, src, lru) { + if (scan >= nr_to_scan) + break; + page = pc->page; + VM_BUG_ON(!pc); + + if (unlikely(!PageLRU(page))) + continue; + + if (PageActive(page) && !active) { + __mem_cgroup_move_lists(pc, true); + continue; + } + if (!PageActive(page) && active) { + __mem_cgroup_move_lists(pc, false); + continue; + } + + scan++; + list_move(&pc->lru, &pc_list); + + if (__isolate_lru_page(page, mode) == 0) { + list_move(&page->lru, dst); + nr_taken++; + } + } + + list_splice(&pc_list, src); + spin_unlock(&mz->lru_lock); + + *scanned = scan; + return nr_taken; +} + +/* + * Charge the memory controller for page usage. + * Return + * 0 if the charge was successful + * < 0 if the cgroup is over its limit + */ +static int mem_cgroup_charge_common(struct page *page, struct mm_struct *mm, + gfp_t gfp_mask, enum charge_type ctype) +{ + struct mem_cgroup *mem; + struct page_cgroup *pc; + unsigned long flags; + unsigned long nr_retries = MEM_CGROUP_RECLAIM_RETRIES; + struct mem_cgroup_per_zone *mz; + + /* + * Should page_cgroup's go to their own slab? + * One could optimize the performance of the charging routine + * by saving a bit in the page_flags and using it as a lock + * to see if the cgroup page already has a page_cgroup associated + * with it + */ +retry: + if (page) { + lock_page_cgroup(page); + pc = page_get_page_cgroup(page); + /* + * The page_cgroup exists and + * the page has already been accounted. + */ + if (pc) { + if (unlikely(!atomic_inc_not_zero(&pc->ref_cnt))) { + /* this page is under being uncharged ? */ + unlock_page_cgroup(page); + cpu_relax(); + goto retry; + } else { + unlock_page_cgroup(page); + goto done; + } + } + unlock_page_cgroup(page); + } + + pc = kzalloc(sizeof(struct page_cgroup), gfp_mask); + if (pc == NULL) + goto err; + + /* + * We always charge the cgroup the mm_struct belongs to. + * The mm_struct's mem_cgroup changes on task migration if the + * thread group leader migrates. It's possible that mm is not + * set, if so charge the init_mm (happens for pagecache usage). + */ + if (!mm) + mm = &init_mm; + + rcu_read_lock(); + mem = rcu_dereference(mm->mem_cgroup); + /* + * For every charge from the cgroup, increment reference + * count + */ + css_get(&mem->css); + rcu_read_unlock(); + + /* + * If we created the page_cgroup, we should free it on exceeding + * the cgroup limit. + */ + while (res_counter_charge(&mem->res, PAGE_SIZE)) { + if (!(gfp_mask & __GFP_WAIT)) + goto out; + + if (try_to_free_mem_cgroup_pages(mem, gfp_mask)) + continue; + + /* + * try_to_free_mem_cgroup_pages() might not give us a full + * picture of reclaim. Some pages are reclaimed and might be + * moved to swap cache or just unmapped from the cgroup. + * Check the limit again to see if the reclaim reduced the + * current usage of the cgroup before giving up + */ + if (res_counter_check_under_limit(&mem->res)) + continue; + + if (!nr_retries--) { + mem_cgroup_out_of_memory(mem, gfp_mask); + goto out; + } + congestion_wait(WRITE, HZ/10); + } + + atomic_set(&pc->ref_cnt, 1); + pc->mem_cgroup = mem; + pc->page = page; + pc->flags = PAGE_CGROUP_FLAG_ACTIVE; + if (ctype == MEM_CGROUP_CHARGE_TYPE_CACHE) + pc->flags |= PAGE_CGROUP_FLAG_CACHE; + + if (!page || page_cgroup_assign_new_page_cgroup(page, pc)) { + /* + * Another charge has been added to this page already. + * We take lock_page_cgroup(page) again and read + * page->cgroup, increment refcnt.... just retry is OK. + */ + res_counter_uncharge(&mem->res, PAGE_SIZE); + css_put(&mem->css); + kfree(pc); + if (!page) + goto done; + goto retry; + } + + mz = page_cgroup_zoneinfo(pc); + spin_lock_irqsave(&mz->lru_lock, flags); + /* Update statistics vector */ + __mem_cgroup_add_list(pc); + spin_unlock_irqrestore(&mz->lru_lock, flags); + +done: + return 0; +out: + css_put(&mem->css); + kfree(pc); +err: + return -ENOMEM; +} + +int mem_cgroup_charge(struct page *page, struct mm_struct *mm, + gfp_t gfp_mask) +{ + return mem_cgroup_charge_common(page, mm, gfp_mask, + MEM_CGROUP_CHARGE_TYPE_MAPPED); +} + +/* + * See if the cached pages should be charged at all? + */ +int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm, + gfp_t gfp_mask) +{ + int ret = 0; + if (!mm) + mm = &init_mm; + + ret = mem_cgroup_charge_common(page, mm, gfp_mask, + MEM_CGROUP_CHARGE_TYPE_CACHE); + return ret; +} + +/* + * Uncharging is always a welcome operation, we never complain, simply + * uncharge. This routine should be called with lock_page_cgroup held + */ +void mem_cgroup_uncharge(struct page_cgroup *pc) +{ + struct mem_cgroup *mem; + struct mem_cgroup_per_zone *mz; + struct page *page; + unsigned long flags; + + /* + * Check if our page_cgroup is valid + */ + if (!pc) + return; + + if (atomic_dec_and_test(&pc->ref_cnt)) { + page = pc->page; + mz = page_cgroup_zoneinfo(pc); + /* + * get page->cgroup and clear it under lock. + * force_empty can drop page->cgroup without checking refcnt. + */ + unlock_page_cgroup(page); + if (clear_page_cgroup(page, pc) == pc) { + mem = pc->mem_cgroup; + css_put(&mem->css); + res_counter_uncharge(&mem->res, PAGE_SIZE); + spin_lock_irqsave(&mz->lru_lock, flags); + __mem_cgroup_remove_list(pc); + spin_unlock_irqrestore(&mz->lru_lock, flags); + kfree(pc); + } + lock_page_cgroup(page); + } +} + +void mem_cgroup_uncharge_page(struct page *page) +{ + lock_page_cgroup(page); + mem_cgroup_uncharge(page_get_page_cgroup(page)); + unlock_page_cgroup(page); +} + +/* + * Returns non-zero if a page (under migration) has valid page_cgroup member. + * Refcnt of page_cgroup is incremented. + */ + +int mem_cgroup_prepare_migration(struct page *page) +{ + struct page_cgroup *pc; + int ret = 0; + lock_page_cgroup(page); + pc = page_get_page_cgroup(page); + if (pc && atomic_inc_not_zero(&pc->ref_cnt)) + ret = 1; + unlock_page_cgroup(page); + return ret; +} + +void mem_cgroup_end_migration(struct page *page) +{ + struct page_cgroup *pc; + + lock_page_cgroup(page); + pc = page_get_page_cgroup(page); + mem_cgroup_uncharge(pc); + unlock_page_cgroup(page); +} +/* + * We know both *page* and *newpage* are now not-on-LRU and Pg_locked. + * And no race with uncharge() routines because page_cgroup for *page* + * has extra one reference by mem_cgroup_prepare_migration. + */ + +void mem_cgroup_page_migration(struct page *page, struct page *newpage) +{ + struct page_cgroup *pc; + struct mem_cgroup *mem; + unsigned long flags; + struct mem_cgroup_per_zone *mz; +retry: + pc = page_get_page_cgroup(page); + if (!pc) + return; + mem = pc->mem_cgroup; + mz = page_cgroup_zoneinfo(pc); + if (clear_page_cgroup(page, pc) != pc) + goto retry; + spin_lock_irqsave(&mz->lru_lock, flags); + + __mem_cgroup_remove_list(pc); + spin_unlock_irqrestore(&mz->lru_lock, flags); + + pc->page = newpage; + lock_page_cgroup(newpage); + page_assign_page_cgroup(newpage, pc); + unlock_page_cgroup(newpage); + + mz = page_cgroup_zoneinfo(pc); + spin_lock_irqsave(&mz->lru_lock, flags); + __mem_cgroup_add_list(pc); + spin_unlock_irqrestore(&mz->lru_lock, flags); + return; +} + +/* + * This routine traverse page_cgroup in given list and drop them all. + * This routine ignores page_cgroup->ref_cnt. + * *And* this routine doesn't reclaim page itself, just removes page_cgroup. + */ +#define FORCE_UNCHARGE_BATCH (128) +static void +mem_cgroup_force_empty_list(struct mem_cgroup *mem, + struct mem_cgroup_per_zone *mz, + int active) +{ + struct page_cgroup *pc; + struct page *page; + int count; + unsigned long flags; + struct list_head *list; + + if (active) + list = &mz->active_list; + else + list = &mz->inactive_list; + + if (list_empty(list)) + return; +retry: + count = FORCE_UNCHARGE_BATCH; + spin_lock_irqsave(&mz->lru_lock, flags); + + while (--count && !list_empty(list)) { + pc = list_entry(list->prev, struct page_cgroup, lru); + page = pc->page; + /* Avoid race with charge */ + atomic_set(&pc->ref_cnt, 0); + if (clear_page_cgroup(page, pc) == pc) { + css_put(&mem->css); + res_counter_uncharge(&mem->res, PAGE_SIZE); + __mem_cgroup_remove_list(pc); + kfree(pc); + } else /* being uncharged ? ...do relax */ + break; + } + spin_unlock_irqrestore(&mz->lru_lock, flags); + if (!list_empty(list)) { + cond_resched(); + goto retry; + } + return; +} + +/* + * make mem_cgroup's charge to be 0 if there is no task. + * This enables deleting this mem_cgroup. + */ + +int mem_cgroup_force_empty(struct mem_cgroup *mem) +{ + int ret = -EBUSY; + int node, zid; + css_get(&mem->css); + /* + * page reclaim code (kswapd etc..) will move pages between +` * active_list <-> inactive_list while we don't take a lock. + * So, we have to do loop here until all lists are empty. + */ + while (mem->res.usage > 0) { + if (atomic_read(&mem->css.cgroup->count) > 0) + goto out; + for_each_node_state(node, N_POSSIBLE) + for (zid = 0; zid < MAX_NR_ZONES; zid++) { + struct mem_cgroup_per_zone *mz; + mz = mem_cgroup_zoneinfo(mem, node, zid); + /* drop all page_cgroup in active_list */ + mem_cgroup_force_empty_list(mem, mz, 1); + /* drop all page_cgroup in inactive_list */ + mem_cgroup_force_empty_list(mem, mz, 0); + } + } + ret = 0; +out: + css_put(&mem->css); + return ret; +} + + + +int mem_cgroup_write_strategy(char *buf, unsigned long long *tmp) +{ + *tmp = memparse(buf, &buf); + if (*buf != '\0') + return -EINVAL; + + /* + * Round up the value to the closest page size + */ + *tmp = ((*tmp + PAGE_SIZE - 1) >> PAGE_SHIFT) << PAGE_SHIFT; + return 0; +} + +static ssize_t mem_cgroup_read(struct cgroup *cont, + struct cftype *cft, struct file *file, + char __user *userbuf, size_t nbytes, loff_t *ppos) +{ + return res_counter_read(&mem_cgroup_from_cont(cont)->res, + cft->private, userbuf, nbytes, ppos, + NULL); +} + +static ssize_t mem_cgroup_write(struct cgroup *cont, struct cftype *cft, + struct file *file, const char __user *userbuf, + size_t nbytes, loff_t *ppos) +{ + return res_counter_write(&mem_cgroup_from_cont(cont)->res, + cft->private, userbuf, nbytes, ppos, + mem_cgroup_write_strategy); +} + +static ssize_t mem_force_empty_write(struct cgroup *cont, + struct cftype *cft, struct file *file, + const char __user *userbuf, + size_t nbytes, loff_t *ppos) +{ + struct mem_cgroup *mem = mem_cgroup_from_cont(cont); + int ret; + ret = mem_cgroup_force_empty(mem); + if (!ret) + ret = nbytes; + return ret; +} + +/* + * Note: This should be removed if cgroup supports write-only file. + */ + +static ssize_t mem_force_empty_read(struct cgroup *cont, + struct cftype *cft, + struct file *file, char __user *userbuf, + size_t nbytes, loff_t *ppos) +{ + return -EINVAL; +} + + +static const struct mem_cgroup_stat_desc { + const char *msg; + u64 unit; +} mem_cgroup_stat_desc[] = { + [MEM_CGROUP_STAT_CACHE] = { "cache", PAGE_SIZE, }, + [MEM_CGROUP_STAT_RSS] = { "rss", PAGE_SIZE, }, +}; + +static int mem_control_stat_show(struct seq_file *m, void *arg) +{ + struct cgroup *cont = m->private; + struct mem_cgroup *mem_cont = mem_cgroup_from_cont(cont); + struct mem_cgroup_stat *stat = &mem_cont->stat; + int i; + + for (i = 0; i < ARRAY_SIZE(stat->cpustat[0].count); i++) { + s64 val; + + val = mem_cgroup_read_stat(stat, i); + val *= mem_cgroup_stat_desc[i].unit; + seq_printf(m, "%s %lld\n", mem_cgroup_stat_desc[i].msg, + (long long)val); + } + /* showing # of active pages */ + { + unsigned long active, inactive; + + inactive = mem_cgroup_get_all_zonestat(mem_cont, + MEM_CGROUP_ZSTAT_INACTIVE); + active = mem_cgroup_get_all_zonestat(mem_cont, + MEM_CGROUP_ZSTAT_ACTIVE); + seq_printf(m, "active %ld\n", (active) * PAGE_SIZE); + seq_printf(m, "inactive %ld\n", (inactive) * PAGE_SIZE); + } + return 0; +} + +static const struct file_operations mem_control_stat_file_operations = { + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int mem_control_stat_open(struct inode *unused, struct file *file) +{ + /* XXX __d_cont */ + struct cgroup *cont = file->f_dentry->d_parent->d_fsdata; + + file->f_op = &mem_control_stat_file_operations; + return single_open(file, mem_control_stat_show, cont); +} + + + +static struct cftype mem_cgroup_files[] = { + { + .name = "usage_in_bytes", + .private = RES_USAGE, + .read = mem_cgroup_read, + }, + { + .name = "limit_in_bytes", + .private = RES_LIMIT, + .write = mem_cgroup_write, + .read = mem_cgroup_read, + }, + { + .name = "failcnt", + .private = RES_FAILCNT, + .read = mem_cgroup_read, + }, + { + .name = "force_empty", + .write = mem_force_empty_write, + .read = mem_force_empty_read, + }, + { + .name = "stat", + .open = mem_control_stat_open, + }, +}; + +static int alloc_mem_cgroup_per_zone_info(struct mem_cgroup *mem, int node) +{ + struct mem_cgroup_per_node *pn; + struct mem_cgroup_per_zone *mz; + int zone; + /* + * This routine is called against possible nodes. + * But it's BUG to call kmalloc() against offline node. + * + * TODO: this routine can waste much memory for nodes which will + * never be onlined. It's better to use memory hotplug callback + * function. + */ + if (node_state(node, N_HIGH_MEMORY)) + pn = kmalloc_node(sizeof(*pn), GFP_KERNEL, node); + else + pn = kmalloc(sizeof(*pn), GFP_KERNEL); + if (!pn) + return 1; + + mem->info.nodeinfo[node] = pn; + memset(pn, 0, sizeof(*pn)); + + for (zone = 0; zone < MAX_NR_ZONES; zone++) { + mz = &pn->zoneinfo[zone]; + INIT_LIST_HEAD(&mz->active_list); + INIT_LIST_HEAD(&mz->inactive_list); + spin_lock_init(&mz->lru_lock); + } + return 0; +} + +static void free_mem_cgroup_per_zone_info(struct mem_cgroup *mem, int node) +{ + kfree(mem->info.nodeinfo[node]); +} + + +static struct mem_cgroup init_mem_cgroup; + +static struct cgroup_subsys_state * +mem_cgroup_create(struct cgroup_subsys *ss, struct cgroup *cont) +{ + struct mem_cgroup *mem; + int node; + + if (unlikely((cont->parent) == NULL)) { + mem = &init_mem_cgroup; + init_mm.mem_cgroup = mem; + } else + mem = kzalloc(sizeof(struct mem_cgroup), GFP_KERNEL); + + if (mem == NULL) + return NULL; + + res_counter_init(&mem->res); + + memset(&mem->info, 0, sizeof(mem->info)); + + for_each_node_state(node, N_POSSIBLE) + if (alloc_mem_cgroup_per_zone_info(mem, node)) + goto free_out; + + return &mem->css; +free_out: + for_each_node_state(node, N_POSSIBLE) + free_mem_cgroup_per_zone_info(mem, node); + if (cont->parent != NULL) + kfree(mem); + return NULL; +} + +static void mem_cgroup_pre_destroy(struct cgroup_subsys *ss, + struct cgroup *cont) +{ + struct mem_cgroup *mem = mem_cgroup_from_cont(cont); + mem_cgroup_force_empty(mem); +} + +static void mem_cgroup_destroy(struct cgroup_subsys *ss, + struct cgroup *cont) +{ + int node; + struct mem_cgroup *mem = mem_cgroup_from_cont(cont); + + for_each_node_state(node, N_POSSIBLE) + free_mem_cgroup_per_zone_info(mem, node); + + kfree(mem_cgroup_from_cont(cont)); +} + +static int mem_cgroup_populate(struct cgroup_subsys *ss, + struct cgroup *cont) +{ + return cgroup_add_files(cont, ss, mem_cgroup_files, + ARRAY_SIZE(mem_cgroup_files)); +} + +static void mem_cgroup_move_task(struct cgroup_subsys *ss, + struct cgroup *cont, + struct cgroup *old_cont, + struct task_struct *p) +{ + struct mm_struct *mm; + struct mem_cgroup *mem, *old_mem; + + mm = get_task_mm(p); + if (mm == NULL) + return; + + mem = mem_cgroup_from_cont(cont); + old_mem = mem_cgroup_from_cont(old_cont); + + if (mem == old_mem) + goto out; + + /* + * Only thread group leaders are allowed to migrate, the mm_struct is + * in effect owned by the leader + */ + if (p->tgid != p->pid) + goto out; + + css_get(&mem->css); + rcu_assign_pointer(mm->mem_cgroup, mem); + css_put(&old_mem->css); + +out: + mmput(mm); + return; +} + +struct cgroup_subsys mem_cgroup_subsys = { + .name = "memory", + .subsys_id = mem_cgroup_subsys_id, + .create = mem_cgroup_create, + .pre_destroy = mem_cgroup_pre_destroy, + .destroy = mem_cgroup_destroy, + .populate = mem_cgroup_populate, + .attach = mem_cgroup_move_task, + .early_init = 0, +}; diff --git a/mm/memory.c b/mm/memory.c index 9d073fa0a2d0..153a54b2013c 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -50,6 +50,7 @@ #include <linux/delayacct.h> #include <linux/init.h> #include <linux/writeback.h> +#include <linux/memcontrol.h> #include <asm/pgalloc.h> #include <asm/uaccess.h> @@ -1144,16 +1145,20 @@ static int insert_page(struct mm_struct *mm, unsigned long addr, struct page *pa { int retval; pte_t *pte; - spinlock_t *ptl; + spinlock_t *ptl; + + retval = mem_cgroup_charge(page, mm, GFP_KERNEL); + if (retval) + goto out; retval = -EINVAL; if (PageAnon(page)) - goto out; + goto out_uncharge; retval = -ENOMEM; flush_dcache_page(page); pte = get_locked_pte(mm, addr, &ptl); if (!pte) - goto out; + goto out_uncharge; retval = -EBUSY; if (!pte_none(*pte)) goto out_unlock; @@ -1165,8 +1170,12 @@ static int insert_page(struct mm_struct *mm, unsigned long addr, struct page *pa set_pte_at(mm, addr, pte, mk_pte(page, prot)); retval = 0; + pte_unmap_unlock(pte, ptl); + return retval; out_unlock: pte_unmap_unlock(pte, ptl); +out_uncharge: + mem_cgroup_uncharge_page(page); out: return retval; } @@ -1641,6 +1650,9 @@ gotten: cow_user_page(new_page, old_page, address, vma); __SetPageUptodate(new_page); + if (mem_cgroup_charge(new_page, mm, GFP_KERNEL)) + goto oom_free_new; + /* * Re-check the pte - we dropped the lock */ @@ -1672,7 +1684,9 @@ gotten: /* Free the old page.. */ new_page = old_page; ret |= VM_FAULT_WRITE; - } + } else + mem_cgroup_uncharge_page(new_page); + if (new_page) page_cache_release(new_page); if (old_page) @@ -1696,6 +1710,8 @@ unlock: put_page(dirty_page); } return ret; +oom_free_new: + __free_page(new_page); oom: if (old_page) page_cache_release(old_page); @@ -2036,6 +2052,12 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma, count_vm_event(PGMAJFAULT); } + if (mem_cgroup_charge(page, mm, GFP_KERNEL)) { + delayacct_clear_flag(DELAYACCT_PF_SWAPIN); + ret = VM_FAULT_OOM; + goto out; + } + mark_page_accessed(page); lock_page(page); delayacct_clear_flag(DELAYACCT_PF_SWAPIN); @@ -2073,8 +2095,10 @@ static int do_swap_page(struct mm_struct *mm, struct vm_area_struct *vma, if (write_access) { /* XXX: We could OR the do_wp_page code with this one? */ if (do_wp_page(mm, vma, address, - page_table, pmd, ptl, pte) & VM_FAULT_OOM) + page_table, pmd, ptl, pte) & VM_FAULT_OOM) { + mem_cgroup_uncharge_page(page); ret = VM_FAULT_OOM; + } goto out; } @@ -2085,6 +2109,7 @@ unlock: out: return ret; out_nomap: + mem_cgroup_uncharge_page(page); pte_unmap_unlock(page_table, ptl); unlock_page(page); page_cache_release(page); @@ -2114,6 +2139,9 @@ static int do_anonymous_page(struct mm_struct *mm, struct vm_area_struct *vma, goto oom; __SetPageUptodate(page); + if (mem_cgroup_charge(page, mm, GFP_KERNEL)) + goto oom_free_page; + entry = mk_pte(page, vma->vm_page_prot); entry = maybe_mkwrite(pte_mkdirty(entry), vma); @@ -2131,8 +2159,11 @@ unlock: pte_unmap_unlock(page_table, ptl); return 0; release: + mem_cgroup_uncharge_page(page); page_cache_release(page); goto unlock; +oom_free_page: + __free_page(page); oom: return VM_FAULT_OOM; } @@ -2246,6 +2277,11 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma, } + if (mem_cgroup_charge(page, mm, GFP_KERNEL)) { + ret = VM_FAULT_OOM; + goto out; + } + page_table = pte_offset_map_lock(mm, pmd, address, &ptl); /* @@ -2281,6 +2317,7 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma, /* no need to invalidate: a not-present page won't be cached */ update_mmu_cache(vma, address, entry); } else { + mem_cgroup_uncharge_page(page); if (anon) page_cache_release(page); else diff --git a/mm/migrate.c b/mm/migrate.c index 857a987e3690..a73504ff5ab9 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -29,6 +29,7 @@ #include <linux/mempolicy.h> #include <linux/vmalloc.h> #include <linux/security.h> +#include <linux/memcontrol.h> #include "internal.h" @@ -152,6 +153,11 @@ static void remove_migration_pte(struct vm_area_struct *vma, return; } + if (mem_cgroup_charge(new, mm, GFP_KERNEL)) { + pte_unmap(ptep); + return; + } + ptl = pte_lockptr(mm, pmd); spin_lock(ptl); pte = *ptep; @@ -587,9 +593,10 @@ static int move_to_new_page(struct page *newpage, struct page *page) else rc = fallback_migrate_page(mapping, newpage, page); - if (!rc) + if (!rc) { + mem_cgroup_page_migration(page, newpage); remove_migration_ptes(page, newpage); - else + } else newpage->mapping = NULL; unlock_page(newpage); @@ -608,6 +615,7 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private, int *result = NULL; struct page *newpage = get_new_page(page, private, &result); int rcu_locked = 0; + int charge = 0; if (!newpage) return -ENOMEM; @@ -667,14 +675,19 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private, goto rcu_unlock; } + charge = mem_cgroup_prepare_migration(page); /* Establish migration ptes or remove ptes */ try_to_unmap(page, 1); if (!page_mapped(page)) rc = move_to_new_page(newpage, page); - if (rc) + if (rc) { remove_migration_ptes(page, page); + if (charge) + mem_cgroup_end_migration(page); + } else if (charge) + mem_cgroup_end_migration(newpage); rcu_unlock: if (rcu_locked) rcu_read_unlock(); diff --git a/mm/oom_kill.c b/mm/oom_kill.c index c1850bf991cd..4194b9db0104 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -25,9 +25,11 @@ #include <linux/cpuset.h> #include <linux/module.h> #include <linux/notifier.h> +#include <linux/memcontrol.h> int sysctl_panic_on_oom; int sysctl_oom_kill_allocating_task; +int sysctl_oom_dump_tasks; static DEFINE_SPINLOCK(zone_scan_mutex); /* #define DEBUG */ @@ -50,7 +52,8 @@ static DEFINE_SPINLOCK(zone_scan_mutex); * of least surprise ... (be careful when you change it) */ -unsigned long badness(struct task_struct *p, unsigned long uptime) +unsigned long badness(struct task_struct *p, unsigned long uptime, + struct mem_cgroup *mem) { unsigned long points, cpu_time, run_time, s; struct mm_struct *mm; @@ -193,7 +196,8 @@ static inline enum oom_constraint constrained_alloc(struct zonelist *zonelist, * * (not docbooked, we don't want this one cluttering up the manual) */ -static struct task_struct *select_bad_process(unsigned long *ppoints) +static struct task_struct *select_bad_process(unsigned long *ppoints, + struct mem_cgroup *mem) { struct task_struct *g, *p; struct task_struct *chosen = NULL; @@ -213,6 +217,8 @@ static struct task_struct *select_bad_process(unsigned long *ppoints) /* skip the init task */ if (is_global_init(p)) continue; + if (mem && !task_in_mem_cgroup(p, mem)) + continue; /* * This task already has access to memory reserves and is @@ -247,7 +253,7 @@ static struct task_struct *select_bad_process(unsigned long *ppoints) if (p->oomkilladj == OOM_DISABLE) continue; - points = badness(p, uptime.tv_sec); + points = badness(p, uptime.tv_sec, mem); if (points > *ppoints || !chosen) { chosen = p; *ppoints = points; @@ -258,6 +264,41 @@ static struct task_struct *select_bad_process(unsigned long *ppoints) } /** + * Dumps the current memory state of all system tasks, excluding kernel threads. + * State information includes task's pid, uid, tgid, vm size, rss, cpu, oom_adj + * score, and name. + * + * If the actual is non-NULL, only tasks that are a member of the mem_cgroup are + * shown. + * + * Call with tasklist_lock read-locked. + */ +static void dump_tasks(const struct mem_cgroup *mem) +{ + struct task_struct *g, *p; + + printk(KERN_INFO "[ pid ] uid tgid total_vm rss cpu oom_adj " + "name\n"); + do_each_thread(g, p) { + /* + * total_vm and rss sizes do not exist for tasks with a + * detached mm so there's no need to report them. + */ + if (!p->mm) + continue; + if (mem && !task_in_mem_cgroup(p, mem)) + continue; + + task_lock(p); + printk(KERN_INFO "[%5d] %5d %5d %8lu %8lu %3d %3d %s\n", + p->pid, p->uid, p->tgid, p->mm->total_vm, + get_mm_rss(p->mm), (int)task_cpu(p), p->oomkilladj, + p->comm); + task_unlock(p); + } while_each_thread(g, p); +} + +/** * Send SIGKILL to the selected process irrespective of CAP_SYS_RAW_IO * flag though it's unlikely that we select a process with CAP_SYS_RAW_IO * set. @@ -334,7 +375,8 @@ static int oom_kill_task(struct task_struct *p) } static int oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, - unsigned long points, const char *message) + unsigned long points, struct mem_cgroup *mem, + const char *message) { struct task_struct *c; @@ -344,6 +386,8 @@ static int oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, current->comm, gfp_mask, order, current->oomkilladj); dump_stack(); show_mem(); + if (sysctl_oom_dump_tasks) + dump_tasks(mem); } /* @@ -368,6 +412,31 @@ static int oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, return oom_kill_task(p); } +#ifdef CONFIG_CGROUP_MEM_CONT +void mem_cgroup_out_of_memory(struct mem_cgroup *mem, gfp_t gfp_mask) +{ + unsigned long points = 0; + struct task_struct *p; + + cgroup_lock(); + rcu_read_lock(); +retry: + p = select_bad_process(&points, mem); + if (PTR_ERR(p) == -1UL) + goto out; + + if (!p) + p = current; + + if (oom_kill_process(p, gfp_mask, 0, points, mem, + "Memory cgroup out of memory")) + goto retry; +out: + rcu_read_unlock(); + cgroup_unlock(); +} +#endif + static BLOCKING_NOTIFIER_HEAD(oom_notify_list); int register_oom_notifier(struct notifier_block *nb) @@ -465,7 +534,7 @@ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, int order) switch (constraint) { case CONSTRAINT_MEMORY_POLICY: - oom_kill_process(current, gfp_mask, order, points, + oom_kill_process(current, gfp_mask, order, points, NULL, "No available memory (MPOL_BIND)"); break; @@ -475,7 +544,7 @@ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, int order) /* Fall-through */ case CONSTRAINT_CPUSET: if (sysctl_oom_kill_allocating_task) { - oom_kill_process(current, gfp_mask, order, points, + oom_kill_process(current, gfp_mask, order, points, NULL, "Out of memory (oom_kill_allocating_task)"); break; } @@ -484,7 +553,7 @@ retry: * Rambo mode: Shoot down a process and hope it solves whatever * issues we may have. */ - p = select_bad_process(&points); + p = select_bad_process(&points, NULL); if (PTR_ERR(p) == -1UL) goto out; @@ -495,7 +564,7 @@ retry: panic("Out of memory and no killable processes...\n"); } - if (oom_kill_process(p, gfp_mask, order, points, + if (oom_kill_process(p, gfp_mask, order, points, NULL, "Out of memory")) goto retry; diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 37576b822f06..26a54a17dc9f 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -43,6 +43,7 @@ #include <linux/backing-dev.h> #include <linux/fault-inject.h> #include <linux/page-isolation.h> +#include <linux/memcontrol.h> #include <asm/tlbflush.h> #include <asm/div64.h> @@ -987,6 +988,7 @@ static void free_hot_cold_page(struct page *page, int cold) if (!PageHighMem(page)) debug_check_no_locks_freed(page_address(page), PAGE_SIZE); + VM_BUG_ON(page_get_page_cgroup(page)); arch_free_page(page, 0); kernel_map_pages(page, 1, 0); @@ -2525,6 +2527,7 @@ void __meminit memmap_init_zone(unsigned long size, int nid, unsigned long zone, set_page_links(page, zone, nid, pfn); init_page_count(page); reset_page_mapcount(page); + page_assign_page_cgroup(page, NULL); SetPageReserved(page); /* diff --git a/mm/rmap.c b/mm/rmap.c index 57ad276900c9..a0e92a263d12 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -48,6 +48,7 @@ #include <linux/rcupdate.h> #include <linux/module.h> #include <linux/kallsyms.h> +#include <linux/memcontrol.h> #include <asm/tlbflush.h> @@ -301,7 +302,8 @@ out: return referenced; } -static int page_referenced_anon(struct page *page) +static int page_referenced_anon(struct page *page, + struct mem_cgroup *mem_cont) { unsigned int mapcount; struct anon_vma *anon_vma; @@ -314,6 +316,13 @@ static int page_referenced_anon(struct page *page) mapcount = page_mapcount(page); list_for_each_entry(vma, &anon_vma->head, anon_vma_node) { + /* + * If we are reclaiming on behalf of a cgroup, skip + * counting on behalf of references from different + * cgroups + */ + if (mem_cont && (mm_cgroup(vma->vm_mm) != mem_cont)) + continue; referenced += page_referenced_one(page, vma, &mapcount); if (!mapcount) break; @@ -334,7 +343,8 @@ static int page_referenced_anon(struct page *page) * * This function is only called from page_referenced for object-based pages. */ -static int page_referenced_file(struct page *page) +static int page_referenced_file(struct page *page, + struct mem_cgroup *mem_cont) { unsigned int mapcount; struct address_space *mapping = page->mapping; @@ -367,6 +377,13 @@ static int page_referenced_file(struct page *page) mapcount = page_mapcount(page); vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) { + /* + * If we are reclaiming on behalf of a cgroup, skip + * counting on behalf of references from different + * cgroups + */ + if (mem_cont && (mm_cgroup(vma->vm_mm) != mem_cont)) + continue; if ((vma->vm_flags & (VM_LOCKED|VM_MAYSHARE)) == (VM_LOCKED|VM_MAYSHARE)) { referenced++; @@ -389,7 +406,8 @@ static int page_referenced_file(struct page *page) * Quick test_and_clear_referenced for all mappings to a page, * returns the number of ptes which referenced the page. */ -int page_referenced(struct page *page, int is_locked) +int page_referenced(struct page *page, int is_locked, + struct mem_cgroup *mem_cont) { int referenced = 0; @@ -401,14 +419,15 @@ int page_referenced(struct page *page, int is_locked) if (page_mapped(page) && page->mapping) { if (PageAnon(page)) - referenced += page_referenced_anon(page); + referenced += page_referenced_anon(page, mem_cont); else if (is_locked) - referenced += page_referenced_file(page); + referenced += page_referenced_file(page, mem_cont); else if (TestSetPageLocked(page)) referenced++; else { if (page->mapping) - referenced += page_referenced_file(page); + referenced += + page_referenced_file(page, mem_cont); unlock_page(page); } } @@ -554,8 +573,14 @@ void page_add_anon_rmap(struct page *page, VM_BUG_ON(address < vma->vm_start || address >= vma->vm_end); if (atomic_inc_and_test(&page->_mapcount)) __page_set_anon_rmap(page, vma, address); - else + else { __page_check_anon_rmap(page, vma, address); + /* + * We unconditionally charged during prepare, we uncharge here + * This takes care of balancing the reference counts + */ + mem_cgroup_uncharge_page(page); + } } /* @@ -586,6 +611,12 @@ void page_add_file_rmap(struct page *page) { if (atomic_inc_and_test(&page->_mapcount)) __inc_zone_page_state(page, NR_FILE_MAPPED); + else + /* + * We unconditionally charged during prepare, we uncharge here + * This takes care of balancing the reference counts + */ + mem_cgroup_uncharge_page(page); } #ifdef CONFIG_DEBUG_VM @@ -646,6 +677,8 @@ void page_remove_rmap(struct page *page, struct vm_area_struct *vma) page_clear_dirty(page); set_page_dirty(page); } + mem_cgroup_uncharge_page(page); + __dec_zone_page_state(page, PageAnon(page) ? NR_ANON_PAGES : NR_FILE_MAPPED); } diff --git a/mm/shmem.c b/mm/shmem.c index 0f246c44a574..85bed948fafc 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -912,9 +912,13 @@ found: error = 1; if (!inode) goto out; - error = radix_tree_preload(GFP_KERNEL); + /* Precharge page while we can wait, compensate afterwards */ + error = mem_cgroup_cache_charge(page, current->mm, GFP_KERNEL); if (error) goto out; + error = radix_tree_preload(GFP_KERNEL); + if (error) + goto uncharge; error = 1; spin_lock(&info->lock); @@ -947,6 +951,8 @@ found: shmem_swp_unmap(ptr); spin_unlock(&info->lock); radix_tree_preload_end(); +uncharge: + mem_cgroup_uncharge_page(page); out: unlock_page(page); page_cache_release(page); @@ -1308,6 +1314,13 @@ repeat: spin_unlock(&info->lock); unlock_page(swappage); page_cache_release(swappage); + if (error == -ENOMEM) { + /* allow reclaim from this memory cgroup */ + error = mem_cgroup_cache_charge(NULL, + current->mm, gfp & ~__GFP_HIGHMEM); + if (error) + goto failed; + } goto repeat; } } else if (sgp == SGP_READ && !filepage) { @@ -1353,6 +1366,17 @@ repeat: goto failed; } + /* Precharge page while we can wait, compensate after */ + error = mem_cgroup_cache_charge(filepage, current->mm, + gfp & ~__GFP_HIGHMEM); + if (error) { + page_cache_release(filepage); + shmem_unacct_blocks(info->flags, 1); + shmem_free_blocks(inode, 1); + filepage = NULL; + goto failed; + } + spin_lock(&info->lock); entry = shmem_swp_alloc(info, idx, sgp); if (IS_ERR(entry)) @@ -1364,6 +1388,7 @@ repeat: if (error || swap.val || 0 != add_to_page_cache_lru( filepage, mapping, idx, GFP_NOWAIT)) { spin_unlock(&info->lock); + mem_cgroup_uncharge_page(filepage); page_cache_release(filepage); shmem_unacct_blocks(info->flags, 1); shmem_free_blocks(inode, 1); @@ -1372,6 +1397,7 @@ repeat: goto failed; goto repeat; } + mem_cgroup_uncharge_page(filepage); info->flags |= SHMEM_PAGEIN; } diff --git a/mm/swap.c b/mm/swap.c index 57b7e25a939c..710a20bb9749 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -29,6 +29,7 @@ #include <linux/cpu.h> #include <linux/notifier.h> #include <linux/backing-dev.h> +#include <linux/memcontrol.h> /* How many pages do we try to swap or page in/out together? */ int page_cluster; @@ -175,6 +176,7 @@ void activate_page(struct page *page) SetPageActive(page); add_page_to_active_list(zone, page); __count_vm_event(PGACTIVATE); + mem_cgroup_move_lists(page_get_page_cgroup(page), true); } spin_unlock_irq(&zone->lru_lock); } diff --git a/mm/swapfile.c b/mm/swapfile.c index eade24da9310..02ccab5ad9d9 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -27,6 +27,7 @@ #include <linux/mutex.h> #include <linux/capability.h> #include <linux/syscalls.h> +#include <linux/memcontrol.h> #include <asm/pgtable.h> #include <asm/tlbflush.h> @@ -511,11 +512,16 @@ static int unuse_pte(struct vm_area_struct *vma, pmd_t *pmd, { spinlock_t *ptl; pte_t *pte; - int found = 1; + int ret = 1; + + if (mem_cgroup_charge(page, vma->vm_mm, GFP_KERNEL)) + ret = -ENOMEM; pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl); if (unlikely(!pte_same(*pte, swp_entry_to_pte(entry)))) { - found = 0; + if (ret > 0) + mem_cgroup_uncharge_page(page); + ret = 0; goto out; } @@ -532,7 +538,7 @@ static int unuse_pte(struct vm_area_struct *vma, pmd_t *pmd, activate_page(page); out: pte_unmap_unlock(pte, ptl); - return found; + return ret; } static int unuse_pte_range(struct vm_area_struct *vma, pmd_t *pmd, @@ -541,7 +547,7 @@ static int unuse_pte_range(struct vm_area_struct *vma, pmd_t *pmd, { pte_t swp_pte = swp_entry_to_pte(entry); pte_t *pte; - int found = 0; + int ret = 0; /* * We don't actually need pte lock while scanning for swp_pte: since @@ -560,15 +566,15 @@ static int unuse_pte_range(struct vm_area_struct *vma, pmd_t *pmd, */ if (unlikely(pte_same(*pte, swp_pte))) { pte_unmap(pte); - found = unuse_pte(vma, pmd, addr, entry, page); - if (found) + ret = unuse_pte(vma, pmd, addr, entry, page); + if (ret) goto out; pte = pte_offset_map(pmd, addr); } } while (pte++, addr += PAGE_SIZE, addr != end); pte_unmap(pte - 1); out: - return found; + return ret; } static inline int unuse_pmd_range(struct vm_area_struct *vma, pud_t *pud, @@ -577,14 +583,16 @@ static inline int unuse_pmd_range(struct vm_area_struct *vma, pud_t *pud, { pmd_t *pmd; unsigned long next; + int ret; pmd = pmd_offset(pud, addr); do { next = pmd_addr_end(addr, end); if (pmd_none_or_clear_bad(pmd)) continue; - if (unuse_pte_range(vma, pmd, addr, next, entry, page)) - return 1; + ret = unuse_pte_range(vma, pmd, addr, next, entry, page); + if (ret) + return ret; } while (pmd++, addr = next, addr != end); return 0; } @@ -595,14 +603,16 @@ static inline int unuse_pud_range(struct vm_area_struct *vma, pgd_t *pgd, { pud_t *pud; unsigned long next; + int ret; pud = pud_offset(pgd, addr); do { next = pud_addr_end(addr, end); if (pud_none_or_clear_bad(pud)) continue; - if (unuse_pmd_range(vma, pud, addr, next, entry, page)) - return 1; + ret = unuse_pmd_range(vma, pud, addr, next, entry, page); + if (ret) + return ret; } while (pud++, addr = next, addr != end); return 0; } @@ -612,6 +622,7 @@ static int unuse_vma(struct vm_area_struct *vma, { pgd_t *pgd; unsigned long addr, end, next; + int ret; if (page->mapping) { addr = page_address_in_vma(page, vma); @@ -629,8 +640,9 @@ static int unuse_vma(struct vm_area_struct *vma, next = pgd_addr_end(addr, end); if (pgd_none_or_clear_bad(pgd)) continue; - if (unuse_pud_range(vma, pgd, addr, next, entry, page)) - return 1; + ret = unuse_pud_range(vma, pgd, addr, next, entry, page); + if (ret) + return ret; } while (pgd++, addr = next, addr != end); return 0; } @@ -639,6 +651,7 @@ static int unuse_mm(struct mm_struct *mm, swp_entry_t entry, struct page *page) { struct vm_area_struct *vma; + int ret = 0; if (!down_read_trylock(&mm->mmap_sem)) { /* @@ -651,15 +664,11 @@ static int unuse_mm(struct mm_struct *mm, lock_page(page); } for (vma = mm->mmap; vma; vma = vma->vm_next) { - if (vma->anon_vma && unuse_vma(vma, entry, page)) + if (vma->anon_vma && (ret = unuse_vma(vma, entry, page))) break; } up_read(&mm->mmap_sem); - /* - * Currently unuse_mm cannot fail, but leave error handling - * at call sites for now, since we change it from time to time. - */ - return 0; + return (ret < 0)? ret: 0; } /* diff --git a/mm/vmscan.c b/mm/vmscan.c index e5a9597e3bbc..a26dabd62fed 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -37,6 +37,7 @@ #include <linux/delay.h> #include <linux/kthread.h> #include <linux/freezer.h> +#include <linux/memcontrol.h> #include <asm/tlbflush.h> #include <asm/div64.h> @@ -68,6 +69,22 @@ struct scan_control { int all_unreclaimable; int order; + + /* + * Pages that have (or should have) IO pending. If we run into + * a lot of these, we're better off waiting a little for IO to + * finish rather than scanning more pages in the VM. + */ + int nr_io_pages; + + /* Which cgroup do we reclaim from */ + struct mem_cgroup *mem_cgroup; + + /* Pluggable isolate pages callback */ + unsigned long (*isolate_pages)(unsigned long nr, struct list_head *dst, + unsigned long *scanned, int order, int mode, + struct zone *z, struct mem_cgroup *mem_cont, + int active); }; #define lru_to_page(_head) (list_entry((_head)->prev, struct page, lru)) @@ -109,6 +126,12 @@ long vm_total_pages; /* The total number of pages which the VM controls */ static LIST_HEAD(shrinker_list); static DECLARE_RWSEM(shrinker_rwsem); +#ifdef CONFIG_CGROUP_MEM_CONT +#define scan_global_lru(sc) (!(sc)->mem_cgroup) +#else +#define scan_global_lru(sc) (1) +#endif + /* * Add a shrinker callback to be called from the vm */ @@ -489,11 +512,13 @@ static unsigned long shrink_page_list(struct list_head *page_list, */ if (sync_writeback == PAGEOUT_IO_SYNC && may_enter_fs) wait_on_page_writeback(page); - else + else { + sc->nr_io_pages++; goto keep_locked; + } } - referenced = page_referenced(page, 1); + referenced = page_referenced(page, 1, sc->mem_cgroup); /* In active use or really unfreeable? Activate it. */ if (sc->order <= PAGE_ALLOC_COSTLY_ORDER && referenced && page_mapping_inuse(page)) @@ -529,8 +554,10 @@ static unsigned long shrink_page_list(struct list_head *page_list, if (PageDirty(page)) { if (sc->order <= PAGE_ALLOC_COSTLY_ORDER && referenced) goto keep_locked; - if (!may_enter_fs) + if (!may_enter_fs) { + sc->nr_io_pages++; goto keep_locked; + } if (!sc->may_writepage) goto keep_locked; @@ -541,8 +568,10 @@ static unsigned long shrink_page_list(struct list_head *page_list, case PAGE_ACTIVATE: goto activate_locked; case PAGE_SUCCESS: - if (PageWriteback(page) || PageDirty(page)) + if (PageWriteback(page) || PageDirty(page)) { + sc->nr_io_pages++; goto keep; + } /* * A synchronous write - probably a ramdisk. Go * ahead and try to reclaim the page. @@ -626,7 +655,7 @@ keep: * * returns 0 on success, -ve errno on failure. */ -static int __isolate_lru_page(struct page *page, int mode) +int __isolate_lru_page(struct page *page, int mode) { int ret = -EINVAL; @@ -760,6 +789,21 @@ static unsigned long isolate_lru_pages(unsigned long nr_to_scan, return nr_taken; } +static unsigned long isolate_pages_global(unsigned long nr, + struct list_head *dst, + unsigned long *scanned, int order, + int mode, struct zone *z, + struct mem_cgroup *mem_cont, + int active) +{ + if (active) + return isolate_lru_pages(nr, &z->active_list, dst, + scanned, order, mode); + else + return isolate_lru_pages(nr, &z->inactive_list, dst, + scanned, order, mode); +} + /* * clear_active_flags() is a helper for shrink_active_list(), clearing * any active bits from the pages in the list. @@ -801,18 +845,19 @@ static unsigned long shrink_inactive_list(unsigned long max_scan, unsigned long nr_freed; unsigned long nr_active; - nr_taken = isolate_lru_pages(sc->swap_cluster_max, - &zone->inactive_list, + nr_taken = sc->isolate_pages(sc->swap_cluster_max, &page_list, &nr_scan, sc->order, (sc->order > PAGE_ALLOC_COSTLY_ORDER)? - ISOLATE_BOTH : ISOLATE_INACTIVE); + ISOLATE_BOTH : ISOLATE_INACTIVE, + zone, sc->mem_cgroup, 0); nr_active = clear_active_flags(&page_list); __count_vm_events(PGDEACTIVATE, nr_active); __mod_zone_page_state(zone, NR_ACTIVE, -nr_active); __mod_zone_page_state(zone, NR_INACTIVE, -(nr_taken - nr_active)); - zone->pages_scanned += nr_scan; + if (scan_global_lru(sc)) + zone->pages_scanned += nr_scan; spin_unlock_irq(&zone->lru_lock); nr_scanned += nr_scan; @@ -844,8 +889,9 @@ static unsigned long shrink_inactive_list(unsigned long max_scan, if (current_is_kswapd()) { __count_zone_vm_events(PGSCAN_KSWAPD, zone, nr_scan); __count_vm_events(KSWAPD_STEAL, nr_freed); - } else + } else if (scan_global_lru(sc)) __count_zone_vm_events(PGSCAN_DIRECT, zone, nr_scan); + __count_zone_vm_events(PGSTEAL, zone, nr_freed); if (nr_taken == 0) @@ -899,6 +945,113 @@ static inline int zone_is_near_oom(struct zone *zone) } /* + * Determine we should try to reclaim mapped pages. + * This is called only when sc->mem_cgroup is NULL. + */ +static int calc_reclaim_mapped(struct scan_control *sc, struct zone *zone, + int priority) +{ + long mapped_ratio; + long distress; + long swap_tendency; + long imbalance; + int reclaim_mapped = 0; + int prev_priority; + + if (scan_global_lru(sc) && zone_is_near_oom(zone)) + return 1; + /* + * `distress' is a measure of how much trouble we're having + * reclaiming pages. 0 -> no problems. 100 -> great trouble. + */ + if (scan_global_lru(sc)) + prev_priority = zone->prev_priority; + else + prev_priority = mem_cgroup_get_reclaim_priority(sc->mem_cgroup); + + distress = 100 >> min(prev_priority, priority); + + /* + * The point of this algorithm is to decide when to start + * reclaiming mapped memory instead of just pagecache. Work out + * how much memory + * is mapped. + */ + if (scan_global_lru(sc)) + mapped_ratio = ((global_page_state(NR_FILE_MAPPED) + + global_page_state(NR_ANON_PAGES)) * 100) / + vm_total_pages; + else + mapped_ratio = mem_cgroup_calc_mapped_ratio(sc->mem_cgroup); + + /* + * Now decide how much we really want to unmap some pages. The + * mapped ratio is downgraded - just because there's a lot of + * mapped memory doesn't necessarily mean that page reclaim + * isn't succeeding. + * + * The distress ratio is important - we don't want to start + * going oom. + * + * A 100% value of vm_swappiness overrides this algorithm + * altogether. + */ + swap_tendency = mapped_ratio / 2 + distress + sc->swappiness; + + /* + * If there's huge imbalance between active and inactive + * (think active 100 times larger than inactive) we should + * become more permissive, or the system will take too much + * cpu before it start swapping during memory pressure. + * Distress is about avoiding early-oom, this is about + * making swappiness graceful despite setting it to low + * values. + * + * Avoid div by zero with nr_inactive+1, and max resulting + * value is vm_total_pages. + */ + if (scan_global_lru(sc)) { + imbalance = zone_page_state(zone, NR_ACTIVE); + imbalance /= zone_page_state(zone, NR_INACTIVE) + 1; + } else + imbalance = mem_cgroup_reclaim_imbalance(sc->mem_cgroup); + + /* + * Reduce the effect of imbalance if swappiness is low, + * this means for a swappiness very low, the imbalance + * must be much higher than 100 for this logic to make + * the difference. + * + * Max temporary value is vm_total_pages*100. + */ + imbalance *= (vm_swappiness + 1); + imbalance /= 100; + + /* + * If not much of the ram is mapped, makes the imbalance + * less relevant, it's high priority we refill the inactive + * list with mapped pages only in presence of high ratio of + * mapped pages. + * + * Max temporary value is vm_total_pages*100. + */ + imbalance *= mapped_ratio; + imbalance /= 100; + + /* apply imbalance feedback to swap_tendency */ + swap_tendency += imbalance; + + /* + * Now use this metric to decide whether to start moving mapped + * memory onto the inactive list. + */ + if (swap_tendency >= 100) + reclaim_mapped = 1; + + return reclaim_mapped; +} + +/* * This moves pages from the active list to the inactive list. * * We move them the other way if the page is referenced by one or more @@ -915,6 +1068,8 @@ static inline int zone_is_near_oom(struct zone *zone) * The downside is that we have to touch page->_count against each page. * But we had to alter page->flags anyway. */ + + static void shrink_active_list(unsigned long nr_pages, struct zone *zone, struct scan_control *sc, int priority) { @@ -928,99 +1083,21 @@ static void shrink_active_list(unsigned long nr_pages, struct zone *zone, struct pagevec pvec; int reclaim_mapped = 0; - if (sc->may_swap) { - long mapped_ratio; - long distress; - long swap_tendency; - long imbalance; - - if (zone_is_near_oom(zone)) - goto force_reclaim_mapped; - - /* - * `distress' is a measure of how much trouble we're having - * reclaiming pages. 0 -> no problems. 100 -> great trouble. - */ - distress = 100 >> min(zone->prev_priority, priority); - - /* - * The point of this algorithm is to decide when to start - * reclaiming mapped memory instead of just pagecache. Work out - * how much memory - * is mapped. - */ - mapped_ratio = ((global_page_state(NR_FILE_MAPPED) + - global_page_state(NR_ANON_PAGES)) * 100) / - vm_total_pages; - - /* - * Now decide how much we really want to unmap some pages. The - * mapped ratio is downgraded - just because there's a lot of - * mapped memory doesn't necessarily mean that page reclaim - * isn't succeeding. - * - * The distress ratio is important - we don't want to start - * going oom. - * - * A 100% value of vm_swappiness overrides this algorithm - * altogether. - */ - swap_tendency = mapped_ratio / 2 + distress + sc->swappiness; - - /* - * If there's huge imbalance between active and inactive - * (think active 100 times larger than inactive) we should - * become more permissive, or the system will take too much - * cpu before it start swapping during memory pressure. - * Distress is about avoiding early-oom, this is about - * making swappiness graceful despite setting it to low - * values. - * - * Avoid div by zero with nr_inactive+1, and max resulting - * value is vm_total_pages. - */ - imbalance = zone_page_state(zone, NR_ACTIVE); - imbalance /= zone_page_state(zone, NR_INACTIVE) + 1; - - /* - * Reduce the effect of imbalance if swappiness is low, - * this means for a swappiness very low, the imbalance - * must be much higher than 100 for this logic to make - * the difference. - * - * Max temporary value is vm_total_pages*100. - */ - imbalance *= (vm_swappiness + 1); - imbalance /= 100; - - /* - * If not much of the ram is mapped, makes the imbalance - * less relevant, it's high priority we refill the inactive - * list with mapped pages only in presence of high ratio of - * mapped pages. - * - * Max temporary value is vm_total_pages*100. - */ - imbalance *= mapped_ratio; - imbalance /= 100; - - /* apply imbalance feedback to swap_tendency */ - swap_tendency += imbalance; - - /* - * Now use this metric to decide whether to start moving mapped - * memory onto the inactive list. - */ - if (swap_tendency >= 100) -force_reclaim_mapped: - reclaim_mapped = 1; - } + if (sc->may_swap) + reclaim_mapped = calc_reclaim_mapped(sc, zone, priority); lru_add_drain(); spin_lock_irq(&zone->lru_lock); - pgmoved = isolate_lru_pages(nr_pages, &zone->active_list, - &l_hold, &pgscanned, sc->order, ISOLATE_ACTIVE); - zone->pages_scanned += pgscanned; + pgmoved = sc->isolate_pages(nr_pages, &l_hold, &pgscanned, sc->order, + ISOLATE_ACTIVE, zone, + sc->mem_cgroup, 1); + /* + * zone->pages_scanned is used for detect zone's oom + * mem_cgroup remembers nr_scan by itself. + */ + if (scan_global_lru(sc)) + zone->pages_scanned += pgscanned; + __mod_zone_page_state(zone, NR_ACTIVE, -pgmoved); spin_unlock_irq(&zone->lru_lock); @@ -1031,7 +1108,7 @@ force_reclaim_mapped: if (page_mapped(page)) { if (!reclaim_mapped || (total_swap_pages == 0 && PageAnon(page)) || - page_referenced(page, 0)) { + page_referenced(page, 0, sc->mem_cgroup)) { list_add(&page->lru, &l_active); continue; } @@ -1051,6 +1128,7 @@ force_reclaim_mapped: ClearPageActive(page); list_move(&page->lru, &zone->inactive_list); + mem_cgroup_move_lists(page_get_page_cgroup(page), false); pgmoved++; if (!pagevec_add(&pvec, page)) { __mod_zone_page_state(zone, NR_INACTIVE, pgmoved); @@ -1079,6 +1157,7 @@ force_reclaim_mapped: SetPageLRU(page); VM_BUG_ON(!PageActive(page)); list_move(&page->lru, &zone->active_list); + mem_cgroup_move_lists(page_get_page_cgroup(page), true); pgmoved++; if (!pagevec_add(&pvec, page)) { __mod_zone_page_state(zone, NR_ACTIVE, pgmoved); @@ -1108,25 +1187,39 @@ static unsigned long shrink_zone(int priority, struct zone *zone, unsigned long nr_to_scan; unsigned long nr_reclaimed = 0; - /* - * Add one to `nr_to_scan' just to make sure that the kernel will - * slowly sift through the active list. - */ - zone->nr_scan_active += - (zone_page_state(zone, NR_ACTIVE) >> priority) + 1; - nr_active = zone->nr_scan_active; - if (nr_active >= sc->swap_cluster_max) - zone->nr_scan_active = 0; - else - nr_active = 0; + if (scan_global_lru(sc)) { + /* + * Add one to nr_to_scan just to make sure that the kernel + * will slowly sift through the active list. + */ + zone->nr_scan_active += + (zone_page_state(zone, NR_ACTIVE) >> priority) + 1; + nr_active = zone->nr_scan_active; + zone->nr_scan_inactive += + (zone_page_state(zone, NR_INACTIVE) >> priority) + 1; + nr_inactive = zone->nr_scan_inactive; + if (nr_inactive >= sc->swap_cluster_max) + zone->nr_scan_inactive = 0; + else + nr_inactive = 0; + + if (nr_active >= sc->swap_cluster_max) + zone->nr_scan_active = 0; + else + nr_active = 0; + } else { + /* + * This reclaim occurs not because zone memory shortage but + * because memory controller hits its limit. + * Then, don't modify zone reclaim related data. + */ + nr_active = mem_cgroup_calc_reclaim_active(sc->mem_cgroup, + zone, priority); + + nr_inactive = mem_cgroup_calc_reclaim_inactive(sc->mem_cgroup, + zone, priority); + } - zone->nr_scan_inactive += - (zone_page_state(zone, NR_INACTIVE) >> priority) + 1; - nr_inactive = zone->nr_scan_inactive; - if (nr_inactive >= sc->swap_cluster_max) - zone->nr_scan_inactive = 0; - else - nr_inactive = 0; while (nr_active || nr_inactive) { if (nr_active) { @@ -1171,25 +1264,39 @@ static unsigned long shrink_zones(int priority, struct zone **zones, unsigned long nr_reclaimed = 0; int i; + sc->all_unreclaimable = 1; for (i = 0; zones[i] != NULL; i++) { struct zone *zone = zones[i]; if (!populated_zone(zone)) continue; + /* + * Take care memory controller reclaiming has small influence + * to global LRU. + */ + if (scan_global_lru(sc)) { + if (!cpuset_zone_allowed_hardwall(zone, GFP_KERNEL)) + continue; + note_zone_scanning_priority(zone, priority); - if (!cpuset_zone_allowed_hardwall(zone, GFP_KERNEL)) - continue; - - note_zone_scanning_priority(zone, priority); - - if (zone_is_all_unreclaimable(zone) && priority != DEF_PRIORITY) - continue; /* Let kswapd poll it */ - - sc->all_unreclaimable = 0; + if (zone_is_all_unreclaimable(zone) && + priority != DEF_PRIORITY) + continue; /* Let kswapd poll it */ + sc->all_unreclaimable = 0; + } else { + /* + * Ignore cpuset limitation here. We just want to reduce + * # of used pages by us regardless of memory shortage. + */ + sc->all_unreclaimable = 0; + mem_cgroup_note_reclaim_priority(sc->mem_cgroup, + priority); + } nr_reclaimed += shrink_zone(priority, zone, sc); } + return nr_reclaimed; } @@ -1206,7 +1313,8 @@ static unsigned long shrink_zones(int priority, struct zone **zones, * holds filesystem locks which prevent writeout this might not work, and the * allocation attempt will fail. */ -unsigned long try_to_free_pages(struct zone **zones, int order, gfp_t gfp_mask) +static unsigned long do_try_to_free_pages(struct zone **zones, gfp_t gfp_mask, + struct scan_control *sc) { int priority; int ret = 0; @@ -1215,39 +1323,43 @@ unsigned long try_to_free_pages(struct zone **zones, int order, gfp_t gfp_mask) struct reclaim_state *reclaim_state = current->reclaim_state; unsigned long lru_pages = 0; int i; - struct scan_control sc = { - .gfp_mask = gfp_mask, - .may_writepage = !laptop_mode, - .swap_cluster_max = SWAP_CLUSTER_MAX, - .may_swap = 1, - .swappiness = vm_swappiness, - .order = order, - }; - - count_vm_event(ALLOCSTALL); - for (i = 0; zones[i] != NULL; i++) { - struct zone *zone = zones[i]; + if (scan_global_lru(sc)) + count_vm_event(ALLOCSTALL); + /* + * mem_cgroup will not do shrink_slab. + */ + if (scan_global_lru(sc)) { + for (i = 0; zones[i] != NULL; i++) { + struct zone *zone = zones[i]; - if (!cpuset_zone_allowed_hardwall(zone, GFP_KERNEL)) - continue; + if (!cpuset_zone_allowed_hardwall(zone, GFP_KERNEL)) + continue; - lru_pages += zone_page_state(zone, NR_ACTIVE) - + zone_page_state(zone, NR_INACTIVE); + lru_pages += zone_page_state(zone, NR_ACTIVE) + + zone_page_state(zone, NR_INACTIVE); + } } for (priority = DEF_PRIORITY; priority >= 0; priority--) { - sc.nr_scanned = 0; + sc->nr_scanned = 0; + sc->nr_io_pages = 0; if (!priority) disable_swap_token(); - nr_reclaimed += shrink_zones(priority, zones, &sc); - shrink_slab(sc.nr_scanned, gfp_mask, lru_pages); - if (reclaim_state) { - nr_reclaimed += reclaim_state->reclaimed_slab; - reclaim_state->reclaimed_slab = 0; + nr_reclaimed += shrink_zones(priority, zones, sc); + /* + * Don't shrink slabs when reclaiming memory from + * over limit cgroups + */ + if (scan_global_lru(sc)) { + shrink_slab(sc->nr_scanned, gfp_mask, lru_pages); + if (reclaim_state) { + nr_reclaimed += reclaim_state->reclaimed_slab; + reclaim_state->reclaimed_slab = 0; + } } - total_scanned += sc.nr_scanned; - if (nr_reclaimed >= sc.swap_cluster_max) { + total_scanned += sc->nr_scanned; + if (nr_reclaimed >= sc->swap_cluster_max) { ret = 1; goto out; } @@ -1259,18 +1371,19 @@ unsigned long try_to_free_pages(struct zone **zones, int order, gfp_t gfp_mask) * that's undesirable in laptop mode, where we *want* lumpy * writeout. So in laptop mode, write out the whole world. */ - if (total_scanned > sc.swap_cluster_max + - sc.swap_cluster_max / 2) { + if (total_scanned > sc->swap_cluster_max + + sc->swap_cluster_max / 2) { wakeup_pdflush(laptop_mode ? 0 : total_scanned); - sc.may_writepage = 1; + sc->may_writepage = 1; } /* Take a nap, wait for some writeback to complete */ - if (sc.nr_scanned && priority < DEF_PRIORITY - 2) + if (sc->nr_scanned && priority < DEF_PRIORITY - 2 && + sc->nr_io_pages > sc->swap_cluster_max) congestion_wait(WRITE, HZ/10); } /* top priority shrink_caches still had more to do? don't OOM, then */ - if (!sc.all_unreclaimable) + if (!sc->all_unreclaimable && scan_global_lru(sc)) ret = 1; out: /* @@ -1282,17 +1395,63 @@ out: */ if (priority < 0) priority = 0; - for (i = 0; zones[i] != NULL; i++) { - struct zone *zone = zones[i]; - if (!cpuset_zone_allowed_hardwall(zone, GFP_KERNEL)) - continue; + if (scan_global_lru(sc)) { + for (i = 0; zones[i] != NULL; i++) { + struct zone *zone = zones[i]; + + if (!cpuset_zone_allowed_hardwall(zone, GFP_KERNEL)) + continue; + + zone->prev_priority = priority; + } + } else + mem_cgroup_record_reclaim_priority(sc->mem_cgroup, priority); - zone->prev_priority = priority; - } return ret; } +unsigned long try_to_free_pages(struct zone **zones, int order, gfp_t gfp_mask) +{ + struct scan_control sc = { + .gfp_mask = gfp_mask, + .may_writepage = !laptop_mode, + .swap_cluster_max = SWAP_CLUSTER_MAX, + .may_swap = 1, + .swappiness = vm_swappiness, + .order = order, + .mem_cgroup = NULL, + .isolate_pages = isolate_pages_global, + }; + + return do_try_to_free_pages(zones, gfp_mask, &sc); +} + +#ifdef CONFIG_CGROUP_MEM_CONT + +unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *mem_cont, + gfp_t gfp_mask) +{ + struct scan_control sc = { + .gfp_mask = gfp_mask, + .may_writepage = !laptop_mode, + .may_swap = 1, + .swap_cluster_max = SWAP_CLUSTER_MAX, + .swappiness = vm_swappiness, + .order = 0, + .mem_cgroup = mem_cont, + .isolate_pages = mem_cgroup_isolate_pages, + }; + struct zone **zones; + int target_zone = gfp_zone(GFP_HIGHUSER_MOVABLE); + + zones = NODE_DATA(numa_node_id())->node_zonelists[target_zone].zones; + if (do_try_to_free_pages(zones, sc.gfp_mask, &sc)) + return 1; + return 0; +} +#endif + /* * For kswapd, balance_pgdat() will work across all this node's zones until * they are all at pages_high. @@ -1328,6 +1487,8 @@ static unsigned long balance_pgdat(pg_data_t *pgdat, int order) .swap_cluster_max = SWAP_CLUSTER_MAX, .swappiness = vm_swappiness, .order = order, + .mem_cgroup = NULL, + .isolate_pages = isolate_pages_global, }; /* * temp_priority is used to remember the scanning priority at which @@ -1352,6 +1513,7 @@ loop_again: if (!priority) disable_swap_token(); + sc.nr_io_pages = 0; all_zones_ok = 1; /* @@ -1444,7 +1606,8 @@ loop_again: * OK, kswapd is getting into trouble. Take a nap, then take * another pass across the zones. */ - if (total_scanned && priority < DEF_PRIORITY - 2) + if (total_scanned && priority < DEF_PRIORITY - 2 && + sc.nr_io_pages > sc.swap_cluster_max) congestion_wait(WRITE, HZ/10); /* @@ -1649,6 +1812,7 @@ unsigned long shrink_all_memory(unsigned long nr_pages) .swap_cluster_max = nr_pages, .may_writepage = 1, .swappiness = vm_swappiness, + .isolate_pages = isolate_pages_global, }; current->reclaim_state = &reclaim_state; @@ -1834,6 +1998,7 @@ static int __zone_reclaim(struct zone *zone, gfp_t gfp_mask, unsigned int order) SWAP_CLUSTER_MAX), .gfp_mask = gfp_mask, .swappiness = vm_swappiness, + .isolate_pages = isolate_pages_global, }; unsigned long slab_reclaimable; diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index 5e82f1c0afbb..2d0c29c837f7 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -239,7 +239,7 @@ static struct rxrpc_transport *rxrpc_name_to_transport(struct socket *sock, /* find a remote transport endpoint from the local one */ peer = rxrpc_get_peer(srx, gfp); if (IS_ERR(peer)) - return ERR_PTR(PTR_ERR(peer)); + return ERR_CAST(peer); /* find a transport */ trans = rxrpc_get_transport(rx->local, peer, gfp); @@ -282,7 +282,7 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock, trans = rxrpc_name_to_transport(sock, (struct sockaddr *) srx, sizeof(*srx), 0, gfp); if (IS_ERR(trans)) { - call = ERR_PTR(PTR_ERR(trans)); + call = ERR_CAST(trans); trans = NULL; goto out; } @@ -306,7 +306,7 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock, bundle = rxrpc_get_bundle(rx, trans, key, service_id, gfp); if (IS_ERR(bundle)) { - call = ERR_PTR(PTR_ERR(bundle)); + call = ERR_CAST(bundle); goto out; } diff --git a/scripts/kernel-doc b/scripts/kernel-doc index ec54f12f57b0..6c18a14386a4 100755 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -218,6 +218,7 @@ sub usage { print " [ -function funcname [ -function funcname ...] ]\n"; print " [ -nofunction funcname [ -nofunction funcname ...] ]\n"; print " c source file(s) > outputfile\n"; + print " -v : verbose output, more warnings & other info listed\n"; exit 1; } @@ -1654,7 +1655,7 @@ sub dump_function($$) { $prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || $prototype =~ m/^(\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || $prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || + $prototype =~ m/^(\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || $prototype =~ m/^(\w+\s+\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || $prototype =~ m/^(\w+\s+\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || $prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || @@ -1881,6 +1882,13 @@ sub process_file($) { } else { $declaration_purpose = ""; } + + if (($declaration_purpose eq "") && $verbose) { + print STDERR "Warning(${file}:$.): missing initial short description on line:\n"; + print STDERR $_; + ++$warnings; + } + if ($identifier =~ m/^struct/) { $decl_type = 'struct'; } elsif ($identifier =~ m/^union/) { @@ -1907,7 +1915,7 @@ sub process_file($) { $newsection = $1; $newcontents = $2; - if ($contents ne "") { + if (($contents ne "") && ($contents ne "\n")) { if (!$in_doc_sect && $verbose) { print STDERR "Warning(${file}:$.): contents before sections\n"; ++$warnings; diff --git a/security/keys/key.c b/security/keys/key.c index fdd5ca6d89fc..654d23baf352 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -820,7 +820,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, key = key_alloc(ktype, description, current->fsuid, current->fsgid, current, perm, flags); if (IS_ERR(key)) { - key_ref = ERR_PTR(PTR_ERR(key)); + key_ref = ERR_CAST(key); goto error_3; } diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 2a0eb946fc7e..c886a2bb792a 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -660,7 +660,7 @@ key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id, key = key_lookup(id); if (IS_ERR(key)) { - key_ref = ERR_PTR(PTR_ERR(key)); + key_ref = ERR_CAST(key); goto error; } diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 6381e616c477..5ecc5057fb54 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -389,7 +389,7 @@ struct key *request_key_and_link(struct key_type *type, if (!IS_ERR(key_ref)) { key = key_ref_to_ptr(key_ref); } else if (PTR_ERR(key_ref) != -EAGAIN) { - key = ERR_PTR(PTR_ERR(key_ref)); + key = ERR_CAST(key_ref); } else { /* the search failed, but the keyrings were searchable, so we * should consult userspace if we can */ diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 510f7be73a2d..e42b5252486f 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -261,7 +261,7 @@ struct key *key_get_instantiation_authkey(key_serial_t target_id) current); if (IS_ERR(authkey_ref)) { - authkey = ERR_PTR(PTR_ERR(authkey_ref)); + authkey = ERR_CAST(authkey_ref); goto error; } |