mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-11-18 15:44:02 +08:00
Merge branch 'akpm'
* akpm: (173 commits) genalloc: use bitmap_find_next_zero_area ia64: use bitmap_find_next_zero_area sparc: use bitmap_find_next_zero_area mlx4: use bitmap_find_next_zero_area isp1362-hcd: use bitmap_find_next_zero_area iommu-helper: use bitmap library bitmap: introduce bitmap_set, bitmap_clear, bitmap_find_next_zero_area qnx4: use hweight8 qnx4fs: remove remains of the (defunct) write support resource: constify arg to resource_size() and resource_type() gru: send cross partition interrupts using the gru gru: function to generate chipset IPI values gru: update driver version number gru: improve GRU TLB dropin statistics gru: fix GRU interrupt race at deallocate gru: add hugepage support gru: fix bug in allocation of kernel contexts gru: update GRU structures to match latest hardware spec gru: check for correct GRU chiplet assignment gru: remove stray local_irq_enable ...
This commit is contained in:
commit
9cfc86249f
@ -8,7 +8,7 @@
|
||||
|
||||
DOCBOOKS := z8530book.xml mcabook.xml device-drivers.xml \
|
||||
kernel-hacking.xml kernel-locking.xml deviceiobook.xml \
|
||||
procfs-guide.xml writing_usb_driver.xml networking.xml \
|
||||
writing_usb_driver.xml networking.xml \
|
||||
kernel-api.xml filesystems.xml lsm.xml usb.xml kgdb.xml \
|
||||
gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml \
|
||||
genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \
|
||||
@ -65,7 +65,7 @@ KERNELDOC = $(srctree)/scripts/kernel-doc
|
||||
DOCPROC = $(objtree)/scripts/basic/docproc
|
||||
|
||||
XMLTOFLAGS = -m $(srctree)/Documentation/DocBook/stylesheet.xsl
|
||||
#XMLTOFLAGS += --skip-validation
|
||||
XMLTOFLAGS += --skip-validation
|
||||
|
||||
###
|
||||
# DOCPROC is used for two purposes:
|
||||
@ -101,17 +101,6 @@ endif
|
||||
# Changes in kernel-doc force a rebuild of all documentation
|
||||
$(BOOKS): $(KERNELDOC)
|
||||
|
||||
###
|
||||
# procfs guide uses a .c file as example code.
|
||||
# This requires an explicit dependency
|
||||
C-procfs-example = procfs_example.xml
|
||||
C-procfs-example2 = $(addprefix $(obj)/,$(C-procfs-example))
|
||||
$(obj)/procfs-guide.xml: $(C-procfs-example2)
|
||||
|
||||
# List of programs to build
|
||||
##oops, this is a kernel module::hostprogs-y := procfs_example
|
||||
obj-m += procfs_example.o
|
||||
|
||||
# Tell kbuild to always build the programs
|
||||
always := $(hostprogs-y)
|
||||
|
||||
@ -238,7 +227,7 @@ clean-files := $(DOCBOOKS) \
|
||||
$(patsubst %.xml, %.pdf, $(DOCBOOKS)) \
|
||||
$(patsubst %.xml, %.html, $(DOCBOOKS)) \
|
||||
$(patsubst %.xml, %.9, $(DOCBOOKS)) \
|
||||
$(C-procfs-example) $(index)
|
||||
$(index)
|
||||
|
||||
clean-dirs := $(patsubst %.xml,%,$(DOCBOOKS)) man
|
||||
|
||||
|
@ -1,626 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
|
||||
<!ENTITY procfsexample SYSTEM "procfs_example.xml">
|
||||
]>
|
||||
|
||||
<book id="LKProcfsGuide">
|
||||
<bookinfo>
|
||||
<title>Linux Kernel Procfs Guide</title>
|
||||
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Erik</firstname>
|
||||
<othername>(J.A.K.)</othername>
|
||||
<surname>Mouw</surname>
|
||||
<affiliation>
|
||||
<address>
|
||||
<email>mouw@nl.linux.org</email>
|
||||
</address>
|
||||
</affiliation>
|
||||
</author>
|
||||
<othercredit>
|
||||
<contrib>
|
||||
This software and documentation were written while working on the
|
||||
LART computing board
|
||||
(<ulink url="http://www.lartmaker.nl/">http://www.lartmaker.nl/</ulink>),
|
||||
which was sponsored by the Delt University of Technology projects
|
||||
Mobile Multi-media Communications and Ubiquitous Communications.
|
||||
</contrib>
|
||||
</othercredit>
|
||||
</authorgroup>
|
||||
|
||||
<revhistory>
|
||||
<revision>
|
||||
<revnumber>1.0</revnumber>
|
||||
<date>May 30, 2001</date>
|
||||
<revremark>Initial revision posted to linux-kernel</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>1.1</revnumber>
|
||||
<date>June 3, 2001</date>
|
||||
<revremark>Revised after comments from linux-kernel</revremark>
|
||||
</revision>
|
||||
</revhistory>
|
||||
|
||||
<copyright>
|
||||
<year>2001</year>
|
||||
<holder>Erik Mouw</holder>
|
||||
</copyright>
|
||||
|
||||
|
||||
<legalnotice>
|
||||
<para>
|
||||
This documentation 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.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This documentation 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.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
You should have received a copy of the GNU General Public
|
||||
License along with this program; if not, write to the Free
|
||||
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||||
MA 02111-1307 USA
|
||||
</para>
|
||||
|
||||
<para>
|
||||
For more details see the file COPYING in the source
|
||||
distribution of Linux.
|
||||
</para>
|
||||
</legalnotice>
|
||||
</bookinfo>
|
||||
|
||||
|
||||
|
||||
|
||||
<toc>
|
||||
</toc>
|
||||
|
||||
|
||||
|
||||
|
||||
<preface id="Preface">
|
||||
<title>Preface</title>
|
||||
|
||||
<para>
|
||||
This guide describes the use of the procfs file system from
|
||||
within the Linux kernel. The idea to write this guide came up on
|
||||
the #kernelnewbies IRC channel (see <ulink
|
||||
url="http://www.kernelnewbies.org/">http://www.kernelnewbies.org/</ulink>),
|
||||
when Jeff Garzik explained the use of procfs and forwarded me a
|
||||
message Alexander Viro wrote to the linux-kernel mailing list. I
|
||||
agreed to write it up nicely, so here it is.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
I'd like to thank Jeff Garzik
|
||||
<email>jgarzik@pobox.com</email> and Alexander Viro
|
||||
<email>viro@parcelfarce.linux.theplanet.co.uk</email> for their input,
|
||||
Tim Waugh <email>twaugh@redhat.com</email> for his <ulink
|
||||
url="http://people.redhat.com/twaugh/docbook/selfdocbook/">Selfdocbook</ulink>,
|
||||
and Marc Joosen <email>marcj@historia.et.tudelft.nl</email> for
|
||||
proofreading.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Erik
|
||||
</para>
|
||||
</preface>
|
||||
|
||||
|
||||
|
||||
|
||||
<chapter id="intro">
|
||||
<title>Introduction</title>
|
||||
|
||||
<para>
|
||||
The <filename class="directory">/proc</filename> file system
|
||||
(procfs) is a special file system in the linux kernel. It's a
|
||||
virtual file system: it is not associated with a block device
|
||||
but exists only in memory. The files in the procfs are there to
|
||||
allow userland programs access to certain information from the
|
||||
kernel (like process information in <filename
|
||||
class="directory">/proc/[0-9]+/</filename>), but also for debug
|
||||
purposes (like <filename>/proc/ksyms</filename>).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This guide describes the use of the procfs file system from
|
||||
within the Linux kernel. It starts by introducing all relevant
|
||||
functions to manage the files within the file system. After that
|
||||
it shows how to communicate with userland, and some tips and
|
||||
tricks will be pointed out. Finally a complete example will be
|
||||
shown.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Note that the files in <filename
|
||||
class="directory">/proc/sys</filename> are sysctl files: they
|
||||
don't belong to procfs and are governed by a completely
|
||||
different API described in the Kernel API book.
|
||||
</para>
|
||||
</chapter>
|
||||
|
||||
|
||||
|
||||
|
||||
<chapter id="managing">
|
||||
<title>Managing procfs entries</title>
|
||||
|
||||
<para>
|
||||
This chapter describes the functions that various kernel
|
||||
components use to populate the procfs with files, symlinks,
|
||||
device nodes, and directories.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
A minor note before we start: if you want to use any of the
|
||||
procfs functions, be sure to include the correct header file!
|
||||
This should be one of the first lines in your code:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
#include <linux/proc_fs.h>
|
||||
</programlisting>
|
||||
|
||||
|
||||
|
||||
|
||||
<sect1 id="regularfile">
|
||||
<title>Creating a regular file</title>
|
||||
|
||||
<funcsynopsis>
|
||||
<funcprototype>
|
||||
<funcdef>struct proc_dir_entry* <function>create_proc_entry</function></funcdef>
|
||||
<paramdef>const char* <parameter>name</parameter></paramdef>
|
||||
<paramdef>mode_t <parameter>mode</parameter></paramdef>
|
||||
<paramdef>struct proc_dir_entry* <parameter>parent</parameter></paramdef>
|
||||
</funcprototype>
|
||||
</funcsynopsis>
|
||||
|
||||
<para>
|
||||
This function creates a regular file with the name
|
||||
<parameter>name</parameter>, file mode
|
||||
<parameter>mode</parameter> in the directory
|
||||
<parameter>parent</parameter>. To create a file in the root of
|
||||
the procfs, use <constant>NULL</constant> as
|
||||
<parameter>parent</parameter> parameter. When successful, the
|
||||
function will return a pointer to the freshly created
|
||||
<structname>struct proc_dir_entry</structname>; otherwise it
|
||||
will return <constant>NULL</constant>. <xref
|
||||
linkend="userland"/> describes how to do something useful with
|
||||
regular files.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Note that it is specifically supported that you can pass a
|
||||
path that spans multiple directories. For example
|
||||
<function>create_proc_entry</function>(<parameter>"drivers/via0/info"</parameter>)
|
||||
will create the <filename class="directory">via0</filename>
|
||||
directory if necessary, with standard
|
||||
<constant>0755</constant> permissions.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you only want to be able to read the file, the function
|
||||
<function>create_proc_read_entry</function> described in <xref
|
||||
linkend="convenience"/> may be used to create and initialise
|
||||
the procfs entry in one single call.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
|
||||
|
||||
|
||||
<sect1 id="Creating_a_symlink">
|
||||
<title>Creating a symlink</title>
|
||||
|
||||
<funcsynopsis>
|
||||
<funcprototype>
|
||||
<funcdef>struct proc_dir_entry*
|
||||
<function>proc_symlink</function></funcdef> <paramdef>const
|
||||
char* <parameter>name</parameter></paramdef>
|
||||
<paramdef>struct proc_dir_entry*
|
||||
<parameter>parent</parameter></paramdef> <paramdef>const
|
||||
char* <parameter>dest</parameter></paramdef>
|
||||
</funcprototype>
|
||||
</funcsynopsis>
|
||||
|
||||
<para>
|
||||
This creates a symlink in the procfs directory
|
||||
<parameter>parent</parameter> that points from
|
||||
<parameter>name</parameter> to
|
||||
<parameter>dest</parameter>. This translates in userland to
|
||||
<literal>ln -s</literal> <parameter>dest</parameter>
|
||||
<parameter>name</parameter>.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="Creating_a_directory">
|
||||
<title>Creating a directory</title>
|
||||
|
||||
<funcsynopsis>
|
||||
<funcprototype>
|
||||
<funcdef>struct proc_dir_entry* <function>proc_mkdir</function></funcdef>
|
||||
<paramdef>const char* <parameter>name</parameter></paramdef>
|
||||
<paramdef>struct proc_dir_entry* <parameter>parent</parameter></paramdef>
|
||||
</funcprototype>
|
||||
</funcsynopsis>
|
||||
|
||||
<para>
|
||||
Create a directory <parameter>name</parameter> in the procfs
|
||||
directory <parameter>parent</parameter>.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
|
||||
|
||||
|
||||
<sect1 id="Removing_an_entry">
|
||||
<title>Removing an entry</title>
|
||||
|
||||
<funcsynopsis>
|
||||
<funcprototype>
|
||||
<funcdef>void <function>remove_proc_entry</function></funcdef>
|
||||
<paramdef>const char* <parameter>name</parameter></paramdef>
|
||||
<paramdef>struct proc_dir_entry* <parameter>parent</parameter></paramdef>
|
||||
</funcprototype>
|
||||
</funcsynopsis>
|
||||
|
||||
<para>
|
||||
Removes the entry <parameter>name</parameter> in the directory
|
||||
<parameter>parent</parameter> from the procfs. Entries are
|
||||
removed by their <emphasis>name</emphasis>, not by the
|
||||
<structname>struct proc_dir_entry</structname> returned by the
|
||||
various create functions. Note that this function doesn't
|
||||
recursively remove entries.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Be sure to free the <structfield>data</structfield> entry from
|
||||
the <structname>struct proc_dir_entry</structname> before
|
||||
<function>remove_proc_entry</function> is called (that is: if
|
||||
there was some <structfield>data</structfield> allocated, of
|
||||
course). See <xref linkend="usingdata"/> for more information
|
||||
on using the <structfield>data</structfield> entry.
|
||||
</para>
|
||||
</sect1>
|
||||
</chapter>
|
||||
|
||||
|
||||
|
||||
|
||||
<chapter id="userland">
|
||||
<title>Communicating with userland</title>
|
||||
|
||||
<para>
|
||||
Instead of reading (or writing) information directly from
|
||||
kernel memory, procfs works with <emphasis>call back
|
||||
functions</emphasis> for files: functions that are called when
|
||||
a specific file is being read or written. Such functions have
|
||||
to be initialised after the procfs file is created by setting
|
||||
the <structfield>read_proc</structfield> and/or
|
||||
<structfield>write_proc</structfield> fields in the
|
||||
<structname>struct proc_dir_entry*</structname> that the
|
||||
function <function>create_proc_entry</function> returned:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
struct proc_dir_entry* entry;
|
||||
|
||||
entry->read_proc = read_proc_foo;
|
||||
entry->write_proc = write_proc_foo;
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
If you only want to use a the
|
||||
<structfield>read_proc</structfield>, the function
|
||||
<function>create_proc_read_entry</function> described in <xref
|
||||
linkend="convenience"/> may be used to create and initialise the
|
||||
procfs entry in one single call.
|
||||
</para>
|
||||
|
||||
|
||||
|
||||
<sect1 id="Reading_data">
|
||||
<title>Reading data</title>
|
||||
|
||||
<para>
|
||||
The read function is a call back function that allows userland
|
||||
processes to read data from the kernel. The read function
|
||||
should have the following format:
|
||||
</para>
|
||||
|
||||
<funcsynopsis>
|
||||
<funcprototype>
|
||||
<funcdef>int <function>read_func</function></funcdef>
|
||||
<paramdef>char* <parameter>buffer</parameter></paramdef>
|
||||
<paramdef>char** <parameter>start</parameter></paramdef>
|
||||
<paramdef>off_t <parameter>off</parameter></paramdef>
|
||||
<paramdef>int <parameter>count</parameter></paramdef>
|
||||
<paramdef>int* <parameter>peof</parameter></paramdef>
|
||||
<paramdef>void* <parameter>data</parameter></paramdef>
|
||||
</funcprototype>
|
||||
</funcsynopsis>
|
||||
|
||||
<para>
|
||||
The read function should write its information into the
|
||||
<parameter>buffer</parameter>, which will be exactly
|
||||
<literal>PAGE_SIZE</literal> bytes long.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The parameter
|
||||
<parameter>peof</parameter> should be used to signal that the
|
||||
end of the file has been reached by writing
|
||||
<literal>1</literal> to the memory location
|
||||
<parameter>peof</parameter> points to.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The <parameter>data</parameter>
|
||||
parameter can be used to create a single call back function for
|
||||
several files, see <xref linkend="usingdata"/>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The rest of the parameters and the return value are described
|
||||
by a comment in <filename>fs/proc/generic.c</filename> as follows:
|
||||
</para>
|
||||
|
||||
<blockquote>
|
||||
<para>
|
||||
You have three ways to return data:
|
||||
</para>
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
Leave <literal>*start = NULL</literal>. (This is the default.)
|
||||
Put the data of the requested offset at that
|
||||
offset within the buffer. Return the number (<literal>n</literal>)
|
||||
of bytes there are from the beginning of the
|
||||
buffer up to the last byte of data. If the
|
||||
number of supplied bytes (<literal>= n - offset</literal>) is
|
||||
greater than zero and you didn't signal eof
|
||||
and the reader is prepared to take more data
|
||||
you will be called again with the requested
|
||||
offset advanced by the number of bytes
|
||||
absorbed. This interface is useful for files
|
||||
no larger than the buffer.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Set <literal>*start</literal> to an unsigned long value less than
|
||||
the buffer address but greater than zero.
|
||||
Put the data of the requested offset at the
|
||||
beginning of the buffer. Return the number of
|
||||
bytes of data placed there. If this number is
|
||||
greater than zero and you didn't signal eof
|
||||
and the reader is prepared to take more data
|
||||
you will be called again with the requested
|
||||
offset advanced by <literal>*start</literal>. This interface is
|
||||
useful when you have a large file consisting
|
||||
of a series of blocks which you want to count
|
||||
and return as wholes.
|
||||
(Hack by Paul.Russell@rustcorp.com.au)
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
Set <literal>*start</literal> to an address within the buffer.
|
||||
Put the data of the requested offset at <literal>*start</literal>.
|
||||
Return the number of bytes of data placed there.
|
||||
If this number is greater than zero and you
|
||||
didn't signal eof and the reader is prepared to
|
||||
take more data you will be called again with the
|
||||
requested offset advanced by the number of bytes
|
||||
absorbed.
|
||||
</para>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
</blockquote>
|
||||
|
||||
<para>
|
||||
<xref linkend="example"/> shows how to use a read call back
|
||||
function.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
|
||||
|
||||
|
||||
<sect1 id="Writing_data">
|
||||
<title>Writing data</title>
|
||||
|
||||
<para>
|
||||
The write call back function allows a userland process to write
|
||||
data to the kernel, so it has some kind of control over the
|
||||
kernel. The write function should have the following format:
|
||||
</para>
|
||||
|
||||
<funcsynopsis>
|
||||
<funcprototype>
|
||||
<funcdef>int <function>write_func</function></funcdef>
|
||||
<paramdef>struct file* <parameter>file</parameter></paramdef>
|
||||
<paramdef>const char* <parameter>buffer</parameter></paramdef>
|
||||
<paramdef>unsigned long <parameter>count</parameter></paramdef>
|
||||
<paramdef>void* <parameter>data</parameter></paramdef>
|
||||
</funcprototype>
|
||||
</funcsynopsis>
|
||||
|
||||
<para>
|
||||
The write function should read <parameter>count</parameter>
|
||||
bytes at maximum from the <parameter>buffer</parameter>. Note
|
||||
that the <parameter>buffer</parameter> doesn't live in the
|
||||
kernel's memory space, so it should first be copied to kernel
|
||||
space with <function>copy_from_user</function>. The
|
||||
<parameter>file</parameter> parameter is usually
|
||||
ignored. <xref linkend="usingdata"/> shows how to use the
|
||||
<parameter>data</parameter> parameter.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Again, <xref linkend="example"/> shows how to use this call back
|
||||
function.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
|
||||
|
||||
|
||||
<sect1 id="usingdata">
|
||||
<title>A single call back for many files</title>
|
||||
|
||||
<para>
|
||||
When a large number of almost identical files is used, it's
|
||||
quite inconvenient to use a separate call back function for
|
||||
each file. A better approach is to have a single call back
|
||||
function that distinguishes between the files by using the
|
||||
<structfield>data</structfield> field in <structname>struct
|
||||
proc_dir_entry</structname>. First of all, the
|
||||
<structfield>data</structfield> field has to be initialised:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
struct proc_dir_entry* entry;
|
||||
struct my_file_data *file_data;
|
||||
|
||||
file_data = kmalloc(sizeof(struct my_file_data), GFP_KERNEL);
|
||||
entry->data = file_data;
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
The <structfield>data</structfield> field is a <type>void
|
||||
*</type>, so it can be initialised with anything.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Now that the <structfield>data</structfield> field is set, the
|
||||
<function>read_proc</function> and
|
||||
<function>write_proc</function> can use it to distinguish
|
||||
between files because they get it passed into their
|
||||
<parameter>data</parameter> parameter:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
int foo_read_func(char *page, char **start, off_t off,
|
||||
int count, int *eof, void *data)
|
||||
{
|
||||
int len;
|
||||
|
||||
if(data == file_data) {
|
||||
/* special case for this file */
|
||||
} else {
|
||||
/* normal processing */
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
<para>
|
||||
Be sure to free the <structfield>data</structfield> data field
|
||||
when removing the procfs entry.
|
||||
</para>
|
||||
</sect1>
|
||||
</chapter>
|
||||
|
||||
|
||||
|
||||
|
||||
<chapter id="tips">
|
||||
<title>Tips and tricks</title>
|
||||
|
||||
|
||||
|
||||
|
||||
<sect1 id="convenience">
|
||||
<title>Convenience functions</title>
|
||||
|
||||
<funcsynopsis>
|
||||
<funcprototype>
|
||||
<funcdef>struct proc_dir_entry* <function>create_proc_read_entry</function></funcdef>
|
||||
<paramdef>const char* <parameter>name</parameter></paramdef>
|
||||
<paramdef>mode_t <parameter>mode</parameter></paramdef>
|
||||
<paramdef>struct proc_dir_entry* <parameter>parent</parameter></paramdef>
|
||||
<paramdef>read_proc_t* <parameter>read_proc</parameter></paramdef>
|
||||
<paramdef>void* <parameter>data</parameter></paramdef>
|
||||
</funcprototype>
|
||||
</funcsynopsis>
|
||||
|
||||
<para>
|
||||
This function creates a regular file in exactly the same way
|
||||
as <function>create_proc_entry</function> from <xref
|
||||
linkend="regularfile"/> does, but also allows to set the read
|
||||
function <parameter>read_proc</parameter> in one call. This
|
||||
function can set the <parameter>data</parameter> as well, like
|
||||
explained in <xref linkend="usingdata"/>.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
|
||||
|
||||
<sect1 id="Modules">
|
||||
<title>Modules</title>
|
||||
|
||||
<para>
|
||||
If procfs is being used from within a module, be sure to set
|
||||
the <structfield>owner</structfield> field in the
|
||||
<structname>struct proc_dir_entry</structname> to
|
||||
<constant>THIS_MODULE</constant>.
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
struct proc_dir_entry* entry;
|
||||
|
||||
entry->owner = THIS_MODULE;
|
||||
</programlisting>
|
||||
</sect1>
|
||||
|
||||
|
||||
|
||||
|
||||
<sect1 id="Mode_and_ownership">
|
||||
<title>Mode and ownership</title>
|
||||
|
||||
<para>
|
||||
Sometimes it is useful to change the mode and/or ownership of
|
||||
a procfs entry. Here is an example that shows how to achieve
|
||||
that:
|
||||
</para>
|
||||
|
||||
<programlisting>
|
||||
struct proc_dir_entry* entry;
|
||||
|
||||
entry->mode = S_IWUSR |S_IRUSR | S_IRGRP | S_IROTH;
|
||||
entry->uid = 0;
|
||||
entry->gid = 100;
|
||||
</programlisting>
|
||||
|
||||
</sect1>
|
||||
</chapter>
|
||||
|
||||
|
||||
|
||||
|
||||
<chapter id="example">
|
||||
<title>Example</title>
|
||||
|
||||
<!-- be careful with the example code: it shouldn't be wider than
|
||||
approx. 60 columns, or otherwise it won't fit properly on a page
|
||||
-->
|
||||
|
||||
&procfsexample;
|
||||
|
||||
</chapter>
|
||||
</book>
|
@ -1,201 +0,0 @@
|
||||
/*
|
||||
* procfs_example.c: an example proc interface
|
||||
*
|
||||
* Copyright (C) 2001, Erik Mouw (mouw@nl.linux.org)
|
||||
*
|
||||
* This file accompanies the procfs-guide in the Linux kernel
|
||||
* source. Its main use is to demonstrate the concepts and
|
||||
* functions described in the guide.
|
||||
*
|
||||
* This software has been developed while working on the LART
|
||||
* computing board (http://www.lartmaker.nl), which was sponsored
|
||||
* by the Delt University of Technology projects Mobile Multi-media
|
||||
* Communications and Ubiquitous Communications.
|
||||
*
|
||||
* This program is free software; you can redistribute
|
||||
* it and/or modify it under the terms of the GNU General
|
||||
* Public License as published by the Free Software
|
||||
* Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be
|
||||
* useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place,
|
||||
* Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
|
||||
#define MODULE_VERS "1.0"
|
||||
#define MODULE_NAME "procfs_example"
|
||||
|
||||
#define FOOBAR_LEN 8
|
||||
|
||||
struct fb_data_t {
|
||||
char name[FOOBAR_LEN + 1];
|
||||
char value[FOOBAR_LEN + 1];
|
||||
};
|
||||
|
||||
|
||||
static struct proc_dir_entry *example_dir, *foo_file,
|
||||
*bar_file, *jiffies_file, *symlink;
|
||||
|
||||
|
||||
struct fb_data_t foo_data, bar_data;
|
||||
|
||||
|
||||
static int proc_read_jiffies(char *page, char **start,
|
||||
off_t off, int count,
|
||||
int *eof, void *data)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = sprintf(page, "jiffies = %ld\n",
|
||||
jiffies);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
static int proc_read_foobar(char *page, char **start,
|
||||
off_t off, int count,
|
||||
int *eof, void *data)
|
||||
{
|
||||
int len;
|
||||
struct fb_data_t *fb_data = (struct fb_data_t *)data;
|
||||
|
||||
/* DON'T DO THAT - buffer overruns are bad */
|
||||
len = sprintf(page, "%s = '%s'\n",
|
||||
fb_data->name, fb_data->value);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
static int proc_write_foobar(struct file *file,
|
||||
const char *buffer,
|
||||
unsigned long count,
|
||||
void *data)
|
||||
{
|
||||
int len;
|
||||
struct fb_data_t *fb_data = (struct fb_data_t *)data;
|
||||
|
||||
if(count > FOOBAR_LEN)
|
||||
len = FOOBAR_LEN;
|
||||
else
|
||||
len = count;
|
||||
|
||||
if(copy_from_user(fb_data->value, buffer, len))
|
||||
return -EFAULT;
|
||||
|
||||
fb_data->value[len] = '\0';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
static int __init init_procfs_example(void)
|
||||
{
|
||||
int rv = 0;
|
||||
|
||||
/* create directory */
|
||||
example_dir = proc_mkdir(MODULE_NAME, NULL);
|
||||
if(example_dir == NULL) {
|
||||
rv = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
/* create jiffies using convenience function */
|
||||
jiffies_file = create_proc_read_entry("jiffies",
|
||||
0444, example_dir,
|
||||
proc_read_jiffies,
|
||||
NULL);
|
||||
if(jiffies_file == NULL) {
|
||||
rv = -ENOMEM;
|
||||
goto no_jiffies;
|
||||
}
|
||||
|
||||
/* create foo and bar files using same callback
|
||||
* functions
|
||||
*/
|
||||
foo_file = create_proc_entry("foo", 0644, example_dir);
|
||||
if(foo_file == NULL) {
|
||||
rv = -ENOMEM;
|
||||
goto no_foo;
|
||||
}
|
||||
|
||||
strcpy(foo_data.name, "foo");
|
||||
strcpy(foo_data.value, "foo");
|
||||
foo_file->data = &foo_data;
|
||||
foo_file->read_proc = proc_read_foobar;
|
||||
foo_file->write_proc = proc_write_foobar;
|
||||
|
||||
bar_file = create_proc_entry("bar", 0644, example_dir);
|
||||
if(bar_file == NULL) {
|
||||
rv = -ENOMEM;
|
||||
goto no_bar;
|
||||
}
|
||||
|
||||
strcpy(bar_data.name, "bar");
|
||||
strcpy(bar_data.value, "bar");
|
||||
bar_file->data = &bar_data;
|
||||
bar_file->read_proc = proc_read_foobar;
|
||||
bar_file->write_proc = proc_write_foobar;
|
||||
|
||||
/* create symlink */
|
||||
symlink = proc_symlink("jiffies_too", example_dir,
|
||||
"jiffies");
|
||||
if(symlink == NULL) {
|
||||
rv = -ENOMEM;
|
||||
goto no_symlink;
|
||||
}
|
||||
|
||||
/* everything OK */
|
||||
printk(KERN_INFO "%s %s initialised\n",
|
||||
MODULE_NAME, MODULE_VERS);
|
||||
return 0;
|
||||
|
||||
no_symlink:
|
||||
remove_proc_entry("bar", example_dir);
|
||||
no_bar:
|
||||
remove_proc_entry("foo", example_dir);
|
||||
no_foo:
|
||||
remove_proc_entry("jiffies", example_dir);
|
||||
no_jiffies:
|
||||
remove_proc_entry(MODULE_NAME, NULL);
|
||||
out:
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
static void __exit cleanup_procfs_example(void)
|
||||
{
|
||||
remove_proc_entry("jiffies_too", example_dir);
|
||||
remove_proc_entry("bar", example_dir);
|
||||
remove_proc_entry("foo", example_dir);
|
||||
remove_proc_entry("jiffies", example_dir);
|
||||
remove_proc_entry(MODULE_NAME, NULL);
|
||||
|
||||
printk(KERN_INFO "%s %s removed\n",
|
||||
MODULE_NAME, MODULE_VERS);
|
||||
}
|
||||
|
||||
|
||||
module_init(init_procfs_example);
|
||||
module_exit(cleanup_procfs_example);
|
||||
|
||||
MODULE_AUTHOR("Erik Mouw");
|
||||
MODULE_DESCRIPTION("procfs examples");
|
||||
MODULE_LICENSE("GPL");
|
@ -15,7 +15,7 @@ kernel patches.
|
||||
2: Passes allnoconfig, allmodconfig
|
||||
|
||||
3: Builds on multiple CPU architectures by using local cross-compile tools
|
||||
or something like PLM at OSDL.
|
||||
or some other build farm.
|
||||
|
||||
4: ppc64 is a good architecture for cross-compilation checking because it
|
||||
tends to use `unsigned long' for 64-bit quantities.
|
||||
@ -88,3 +88,6 @@ kernel patches.
|
||||
|
||||
24: All memory barriers {e.g., barrier(), rmb(), wmb()} need a comment in the
|
||||
source code that explains the logic of what they are doing and why.
|
||||
|
||||
25: If any ioctl's are added by the patch, then also update
|
||||
Documentation/ioctl/ioctl-number.txt.
|
||||
|
@ -7,7 +7,7 @@
|
||||
VIA UniChrome Family(CLE266, PM800 / CN400 / CN300,
|
||||
P4M800CE / P4M800Pro / CN700 / VN800,
|
||||
CX700 / VX700, K8M890, P4M890,
|
||||
CN896 / P4M900, VX800)
|
||||
CN896 / P4M900, VX800, VX855)
|
||||
|
||||
[Driver features]
|
||||
------------------------
|
||||
@ -154,13 +154,6 @@
|
||||
0 : No Dual Edge Panel (default)
|
||||
1 : Dual Edge Panel
|
||||
|
||||
viafb_video_dev:
|
||||
This option is used to specify video output devices(CRT, DVI, LCD) for
|
||||
duoview case.
|
||||
For example:
|
||||
To output video on DVI, we should use:
|
||||
modprobe viafb viafb_video_dev=DVI...
|
||||
|
||||
viafb_lcd_port:
|
||||
This option is used to specify LCD output port,
|
||||
available values are "DVP0" "DVP1" "DFP_HIGHLOW" "DFP_HIGH" "DFP_LOW".
|
||||
@ -181,9 +174,6 @@ Notes:
|
||||
and bpp, need to call VIAFB specified ioctl interface VIAFB_SET_DEVICE
|
||||
instead of calling common ioctl function FBIOPUT_VSCREENINFO since
|
||||
viafb doesn't support multi-head well, or it will cause screen crush.
|
||||
4. VX800 2D accelerator hasn't been supported in this driver yet. When
|
||||
using driver on VX800, the driver will disable the acceleration
|
||||
function as default.
|
||||
|
||||
|
||||
[Configure viafb with "fbset" tool]
|
||||
|
@ -248,9 +248,7 @@ code, that is done in the initialization code in the usual way:
|
||||
{
|
||||
struct proc_dir_entry *entry;
|
||||
|
||||
entry = create_proc_entry("sequence", 0, NULL);
|
||||
if (entry)
|
||||
entry->proc_fops = &ct_file_ops;
|
||||
proc_create("sequence", 0, NULL, &ct_file_ops);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -531,6 +531,13 @@ and have the following read/write attributes:
|
||||
This file exists only if the pin can be configured as an
|
||||
interrupt generating input pin.
|
||||
|
||||
"active_low" ... reads as either 0 (false) or 1 (true). Write
|
||||
any nonzero value to invert the value attribute both
|
||||
for reading and writing. Existing and subsequent
|
||||
poll(2) support configuration via the edge attribute
|
||||
for "rising" and "falling" edges will follow this
|
||||
setting.
|
||||
|
||||
GPIO controllers have paths like /sys/class/gpio/gpiochip42/ (for the
|
||||
controller implementing GPIOs starting at #42) and have the following
|
||||
read-only attributes:
|
||||
@ -566,6 +573,8 @@ requested using gpio_request():
|
||||
int gpio_export_link(struct device *dev, const char *name,
|
||||
unsigned gpio)
|
||||
|
||||
/* change the polarity of a GPIO node in sysfs */
|
||||
int gpio_sysfs_set_active_low(unsigned gpio, int value);
|
||||
|
||||
After a kernel driver requests a GPIO, it may only be made available in
|
||||
the sysfs interface by gpio_export(). The driver can control whether the
|
||||
@ -580,3 +589,9 @@ After the GPIO has been exported, gpio_export_link() allows creating
|
||||
symlinks from elsewhere in sysfs to the GPIO sysfs node. Drivers can
|
||||
use this to provide the interface under their own device in sysfs with
|
||||
a descriptive name.
|
||||
|
||||
Drivers can use gpio_sysfs_set_active_low() to hide GPIO line polarity
|
||||
differences between boards from user space. This only affects the
|
||||
sysfs interface. Polarity change can be done both before and after
|
||||
gpio_export(), and previously enabled poll(2) support for either
|
||||
rising or falling edge will be reconfigured to follow this setting.
|
||||
|
@ -2729,6 +2729,11 @@ and is between 256 and 4096 characters. It is defined in the file
|
||||
vmpoff= [KNL,S390] Perform z/VM CP command after power off.
|
||||
Format: <command>
|
||||
|
||||
vt.cur_default= [VT] Default cursor shape.
|
||||
Format: 0xCCBBAA, where AA, BB, and CC are the same as
|
||||
the parameters of the <Esc>[?A;B;Cc escape sequence;
|
||||
see VGA-softcursor.txt. Default: 2 = underline.
|
||||
|
||||
vt.default_blu= [VT]
|
||||
Format: <blue0>,<blue1>,<blue2>,...,<blue15>
|
||||
Change the default blue palette of the console.
|
||||
|
@ -81,7 +81,6 @@ typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG];
|
||||
#define ELF_DATA ELFDATA2LSB
|
||||
#define ELF_ARCH EM_ALPHA
|
||||
|
||||
#define USE_ELF_CORE_DUMP
|
||||
#define ELF_EXEC_PAGESIZE 8192
|
||||
|
||||
/* This is the location that an ET_DYN program is loaded if exec'ed. Typical
|
||||
|
@ -101,7 +101,6 @@ extern int arm_elf_read_implies_exec(const struct elf32_hdr *, int);
|
||||
int dump_task_regs(struct task_struct *t, elf_gregset_t *elfregs);
|
||||
#define ELF_CORE_COPY_TASK_REGS dump_task_regs
|
||||
|
||||
#define USE_ELF_CORE_DUMP
|
||||
#define ELF_EXEC_PAGESIZE 4096
|
||||
|
||||
/* This is the location that an ET_DYN program is loaded if exec'ed. Typical
|
||||
|
@ -339,6 +339,15 @@ static struct davinci_mmc_config da850_mmc_config = {
|
||||
.version = MMC_CTLR_VERSION_2,
|
||||
};
|
||||
|
||||
static void da850_panel_power_ctrl(int val)
|
||||
{
|
||||
/* lcd backlight */
|
||||
gpio_set_value(DA850_LCD_BL_PIN, val);
|
||||
|
||||
/* lcd power */
|
||||
gpio_set_value(DA850_LCD_PWR_PIN, val);
|
||||
}
|
||||
|
||||
static int da850_lcd_hw_init(void)
|
||||
{
|
||||
int status;
|
||||
@ -356,17 +365,11 @@ static int da850_lcd_hw_init(void)
|
||||
gpio_direction_output(DA850_LCD_BL_PIN, 0);
|
||||
gpio_direction_output(DA850_LCD_PWR_PIN, 0);
|
||||
|
||||
/* disable lcd backlight */
|
||||
gpio_set_value(DA850_LCD_BL_PIN, 0);
|
||||
/* Switch off panel power and backlight */
|
||||
da850_panel_power_ctrl(0);
|
||||
|
||||
/* disable lcd power */
|
||||
gpio_set_value(DA850_LCD_PWR_PIN, 0);
|
||||
|
||||
/* enable lcd power */
|
||||
gpio_set_value(DA850_LCD_PWR_PIN, 1);
|
||||
|
||||
/* enable lcd backlight */
|
||||
gpio_set_value(DA850_LCD_BL_PIN, 1);
|
||||
/* Switch on panel power and backlight */
|
||||
da850_panel_power_ctrl(1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -674,6 +677,7 @@ static __init void da850_evm_init(void)
|
||||
pr_warning("da850_evm_init: lcd initialization failed: %d\n",
|
||||
ret);
|
||||
|
||||
sharp_lk043t1dg01_pdata.panel_power_ctrl = da850_panel_power_ctrl,
|
||||
ret = da8xx_register_lcdc(&sharp_lk043t1dg01_pdata);
|
||||
if (ret)
|
||||
pr_warning("da850_evm_init: lcdc registration failed: %d\n",
|
||||
|
@ -77,7 +77,6 @@ typedef struct user_fpu_struct elf_fpregset_t;
|
||||
#endif
|
||||
#define ELF_ARCH EM_AVR32
|
||||
|
||||
#define USE_ELF_CORE_DUMP
|
||||
#define ELF_EXEC_PAGESIZE 4096
|
||||
|
||||
/* This is the location that an ET_DYN program is loaded if exec'ed. Typical
|
||||
|
28
arch/blackfin/include/asm/bfin-lq035q1.h
Normal file
28
arch/blackfin/include/asm/bfin-lq035q1.h
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Blackfin LCD Framebuffer driver SHARP LQ035Q1DH02
|
||||
*
|
||||
* Copyright 2008-2009 Analog Devices Inc.
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#ifndef BFIN_LQ035Q1_H
|
||||
#define BFIN_LQ035Q1_H
|
||||
|
||||
#define LQ035_RL (0 << 8) /* Right -> Left Scan */
|
||||
#define LQ035_LR (1 << 8) /* Left -> Right Scan */
|
||||
#define LQ035_TB (1 << 9) /* Top -> Botton Scan */
|
||||
#define LQ035_BT (0 << 9) /* Botton -> Top Scan */
|
||||
#define LQ035_BGR (1 << 11) /* Use BGR format */
|
||||
#define LQ035_RGB (0 << 11) /* Use RGB format */
|
||||
#define LQ035_NORM (1 << 13) /* Reversal */
|
||||
#define LQ035_REV (0 << 13) /* Reversal */
|
||||
|
||||
struct bfin_lq035q1fb_disp_info {
|
||||
|
||||
unsigned mode;
|
||||
/* GPIOs */
|
||||
int use_bl;
|
||||
unsigned gpio_bl;
|
||||
};
|
||||
|
||||
#endif /* BFIN_LQ035Q1_H */
|
@ -55,7 +55,6 @@ do { \
|
||||
_regs->p2 = _dynamic_addr; \
|
||||
} while(0)
|
||||
|
||||
#define USE_ELF_CORE_DUMP
|
||||
#define ELF_FDPIC_CORE_EFLAGS EF_BFIN_FDPIC
|
||||
#define ELF_EXEC_PAGESIZE 4096
|
||||
|
||||
|
@ -64,8 +64,6 @@ typedef unsigned long elf_fpregset_t;
|
||||
#define EF_CRIS_VARIANT_COMMON_V10_V32 0x00000004
|
||||
/* End of excerpt from {binutils}/include/elf/cris.h. */
|
||||
|
||||
#define USE_ELF_CORE_DUMP
|
||||
|
||||
#define ELF_EXEC_PAGESIZE 8192
|
||||
|
||||
/* This is the location that an ET_DYN program is loaded if exec'ed. Typical
|
||||
|
@ -115,7 +115,6 @@ do { \
|
||||
__kernel_frame0_ptr->gr29 = 0; \
|
||||
} while(0)
|
||||
|
||||
#define USE_ELF_CORE_DUMP
|
||||
#define CORE_DUMP_USE_REGSET
|
||||
#define ELF_FDPIC_CORE_EFLAGS EF_FRV_FDPIC
|
||||
#define ELF_EXEC_PAGESIZE 16384
|
||||
|
@ -34,7 +34,6 @@ typedef unsigned long elf_fpregset_t;
|
||||
|
||||
#define ELF_PLAT_INIT(_r) _r->er1 = 0
|
||||
|
||||
#define USE_ELF_CORE_DUMP
|
||||
#define ELF_EXEC_PAGESIZE 4096
|
||||
|
||||
/* This is the location that an ET_DYN program is loaded if exec'ed. Typical
|
||||
|
@ -11,8 +11,6 @@
|
||||
#include <asm/intrinsics.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#define USE_ELF_CORE_DUMP 1
|
||||
|
||||
/* Override elfcore.h */
|
||||
#define _LINUX_ELFCORE_H 1
|
||||
typedef unsigned int elf_greg_t;
|
||||
|
@ -73,7 +73,7 @@ static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
|
||||
if (!dev->dma_mask)
|
||||
return 0;
|
||||
|
||||
return addr + size <= *dev->dma_mask;
|
||||
return addr + size - 1 <= *dev->dma_mask;
|
||||
}
|
||||
|
||||
static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr)
|
||||
|
@ -25,7 +25,6 @@
|
||||
#define ELF_DATA ELFDATA2LSB
|
||||
#define ELF_ARCH EM_IA_64
|
||||
|
||||
#define USE_ELF_CORE_DUMP
|
||||
#define CORE_DUMP_USE_REGSET
|
||||
|
||||
/* Least-significant four bits of ELF header's e_flags are OS-specific. The bits are
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <asm/sn/sn_sal.h>
|
||||
#include <asm/sn/addrs.h>
|
||||
#include <asm/sn/io.h>
|
||||
@ -369,7 +370,7 @@ tioca_dma_d48(struct pci_dev *pdev, u64 paddr)
|
||||
static dma_addr_t
|
||||
tioca_dma_mapped(struct pci_dev *pdev, unsigned long paddr, size_t req_size)
|
||||
{
|
||||
int i, ps, ps_shift, entry, entries, mapsize, last_entry;
|
||||
int ps, ps_shift, entry, entries, mapsize;
|
||||
u64 xio_addr, end_xio_addr;
|
||||
struct tioca_common *tioca_common;
|
||||
struct tioca_kernel *tioca_kern;
|
||||
@ -410,23 +411,13 @@ tioca_dma_mapped(struct pci_dev *pdev, unsigned long paddr, size_t req_size)
|
||||
map = tioca_kern->ca_pcigart_pagemap;
|
||||
mapsize = tioca_kern->ca_pcigart_entries;
|
||||
|
||||
entry = find_first_zero_bit(map, mapsize);
|
||||
while (entry < mapsize) {
|
||||
last_entry = find_next_bit(map, mapsize, entry);
|
||||
|
||||
if (last_entry - entry >= entries)
|
||||
break;
|
||||
|
||||
entry = find_next_zero_bit(map, mapsize, last_entry);
|
||||
}
|
||||
|
||||
if (entry > mapsize) {
|
||||
entry = bitmap_find_next_zero_area(map, mapsize, 0, entries, 0);
|
||||
if (entry >= mapsize) {
|
||||
kfree(ca_dmamap);
|
||||
goto map_return;
|
||||
}
|
||||
|
||||
for (i = 0; i < entries; i++)
|
||||
set_bit(entry + i, map);
|
||||
bitmap_set(map, entry, entries);
|
||||
|
||||
bus_addr = tioca_kern->ca_pciap_base + (entry * ps);
|
||||
|
||||
|
@ -102,7 +102,6 @@ typedef elf_fpreg_t elf_fpregset_t;
|
||||
*/
|
||||
#define ELF_PLAT_INIT(_r, load_addr) (_r)->r0 = 0
|
||||
|
||||
#define USE_ELF_CORE_DUMP
|
||||
#define ELF_EXEC_PAGESIZE PAGE_SIZE
|
||||
|
||||
/*
|
||||
|
@ -59,7 +59,6 @@ typedef struct user_m68kfp_struct elf_fpregset_t;
|
||||
is actually used on ASV. */
|
||||
#define ELF_PLAT_INIT(_r, load_addr) _r->a1 = 0
|
||||
|
||||
#define USE_ELF_CORE_DUMP
|
||||
#ifndef CONFIG_SUN3
|
||||
#define ELF_EXEC_PAGESIZE 4096
|
||||
#else
|
||||
|
@ -77,7 +77,6 @@ typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG];
|
||||
#define ELF_DATA ELFDATA2MSB
|
||||
#endif
|
||||
|
||||
#define USE_ELF_CORE_DUMP
|
||||
#define ELF_EXEC_PAGESIZE 4096
|
||||
|
||||
|
||||
|
@ -326,7 +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)
|
||||
|
||||
#define USE_ELF_CORE_DUMP
|
||||
#define ELF_EXEC_PAGESIZE PAGE_SIZE
|
||||
|
||||
/* This yields a mask that user programs can use to figure out what
|
||||
|
@ -77,7 +77,6 @@ do { \
|
||||
_ur->a1 = 0; _ur->a0 = 0; _ur->d1 = 0; _ur->d0 = 0; \
|
||||
} while (0)
|
||||
|
||||
#define USE_ELF_CORE_DUMP
|
||||
#define CORE_DUMP_USE_REGSET
|
||||
#define ELF_EXEC_PAGESIZE 4096
|
||||
|
||||
|
@ -328,7 +328,6 @@ struct pt_regs; /* forward declaration... */
|
||||
such function. */
|
||||
#define ELF_PLAT_INIT(_r, load_addr) _r->gr[23] = 0
|
||||
|
||||
#define USE_ELF_CORE_DUMP
|
||||
#define ELF_EXEC_PAGESIZE 4096
|
||||
|
||||
/* This is the location that an ET_DYN program is loaded if exec'ed. Typical
|
||||
|
@ -197,7 +197,7 @@ static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
|
||||
if (!dev->dma_mask)
|
||||
return 0;
|
||||
|
||||
return addr + size <= *dev->dma_mask;
|
||||
return addr + size - 1 <= *dev->dma_mask;
|
||||
}
|
||||
|
||||
static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr)
|
||||
|
@ -170,7 +170,6 @@ typedef elf_fpreg_t elf_vsrreghalf_t32[ELF_NVSRHALFREG];
|
||||
#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
|
||||
|
||||
|
@ -140,6 +140,8 @@ extern void user_enable_single_step(struct task_struct *);
|
||||
extern void user_enable_block_step(struct task_struct *);
|
||||
extern void user_disable_single_step(struct task_struct *);
|
||||
|
||||
#define ARCH_HAS_USER_SINGLE_STEP_INFO
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
@ -30,7 +30,7 @@
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/iommu-helper.h>
|
||||
#include <linux/crash_dump.h>
|
||||
#include <asm/io.h>
|
||||
@ -251,7 +251,7 @@ static void __iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr,
|
||||
}
|
||||
|
||||
ppc_md.tce_free(tbl, entry, npages);
|
||||
iommu_area_free(tbl->it_map, free_entry, npages);
|
||||
bitmap_clear(tbl->it_map, free_entry, npages);
|
||||
}
|
||||
|
||||
static void iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr,
|
||||
|
@ -174,6 +174,15 @@ int die(const char *str, struct pt_regs *regs, long err)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void user_single_step_siginfo(struct task_struct *tsk,
|
||||
struct pt_regs *regs, siginfo_t *info)
|
||||
{
|
||||
memset(info, 0, sizeof(*info));
|
||||
info->si_signo = SIGTRAP;
|
||||
info->si_code = TRAP_TRACE;
|
||||
info->si_addr = (void __user *)regs->nip;
|
||||
}
|
||||
|
||||
void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
|
||||
{
|
||||
siginfo_t info;
|
||||
|
@ -155,7 +155,6 @@ extern unsigned int vdso_enabled;
|
||||
} while (0)
|
||||
|
||||
#define CORE_DUMP_USE_REGSET
|
||||
#define USE_ELF_CORE_DUMP
|
||||
#define ELF_EXEC_PAGESIZE 4096
|
||||
|
||||
/* This is the location that an ET_DYN program is loaded if exec'ed. Typical
|
||||
|
@ -61,7 +61,6 @@ struct task_struct;
|
||||
struct pt_regs;
|
||||
|
||||
#define CORE_DUMP_USE_REGSET
|
||||
#define USE_ELF_CORE_DUMP
|
||||
#define ELF_EXEC_PAGESIZE PAGE_SIZE
|
||||
|
||||
/* This yields a mask that user programs can use to figure out what
|
||||
|
@ -114,7 +114,6 @@ typedef struct user_fpu_struct elf_fpregset_t;
|
||||
*/
|
||||
#define CORE_DUMP_USE_REGSET
|
||||
|
||||
#define USE_ELF_CORE_DUMP
|
||||
#define ELF_FDPIC_CORE_EFLAGS EF_SH_FDPIC
|
||||
#define ELF_EXEC_PAGESIZE PAGE_SIZE
|
||||
|
||||
|
@ -104,8 +104,6 @@ typedef struct {
|
||||
#define ELF_CLASS ELFCLASS32
|
||||
#define ELF_DATA ELFDATA2MSB
|
||||
|
||||
#define USE_ELF_CORE_DUMP
|
||||
|
||||
#define ELF_EXEC_PAGESIZE 4096
|
||||
|
||||
|
||||
|
@ -152,7 +152,6 @@ typedef struct {
|
||||
(x)->e_machine == EM_SPARC32PLUS)
|
||||
#define compat_start_thread start_thread32
|
||||
|
||||
#define USE_ELF_CORE_DUMP
|
||||
#define ELF_EXEC_PAGESIZE PAGE_SIZE
|
||||
|
||||
/* This is the location that an ET_DYN program is loaded if exec'ed. Typical
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/iommu-helper.h>
|
||||
#include <linux/bitmap.h>
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
#include <linux/pci.h>
|
||||
@ -169,7 +170,7 @@ void iommu_range_free(struct iommu *iommu, dma_addr_t dma_addr, unsigned long np
|
||||
|
||||
entry = (dma_addr - iommu->page_table_map_base) >> IO_PAGE_SHIFT;
|
||||
|
||||
iommu_area_free(arena->map, entry, npages);
|
||||
bitmap_clear(arena->map, entry, npages);
|
||||
}
|
||||
|
||||
int iommu_table_init(struct iommu *iommu, int tsbsize,
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/bitmap.h>
|
||||
|
||||
#include <asm/hypervisor.h>
|
||||
#include <asm/iommu.h>
|
||||
@ -1875,7 +1876,7 @@ EXPORT_SYMBOL(ldc_read);
|
||||
static long arena_alloc(struct ldc_iommu *iommu, unsigned long npages)
|
||||
{
|
||||
struct iommu_arena *arena = &iommu->arena;
|
||||
unsigned long n, i, start, end, limit;
|
||||
unsigned long n, start, end, limit;
|
||||
int pass;
|
||||
|
||||
limit = arena->limit;
|
||||
@ -1883,7 +1884,7 @@ static long arena_alloc(struct ldc_iommu *iommu, unsigned long npages)
|
||||
pass = 0;
|
||||
|
||||
again:
|
||||
n = find_next_zero_bit(arena->map, limit, start);
|
||||
n = bitmap_find_next_zero_area(arena->map, limit, start, npages, 0);
|
||||
end = n + npages;
|
||||
if (unlikely(end >= limit)) {
|
||||
if (likely(pass < 1)) {
|
||||
@ -1896,16 +1897,7 @@ again:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = n; i < end; i++) {
|
||||
if (test_bit(i, arena->map)) {
|
||||
start = i + 1;
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = n; i < end; i++)
|
||||
__set_bit(i, arena->map);
|
||||
bitmap_set(arena->map, n, npages);
|
||||
|
||||
arena->hint = end;
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/bitmap.h>
|
||||
|
||||
#include <asm/sections.h>
|
||||
#include <asm/page.h>
|
||||
@ -1021,20 +1022,12 @@ static char *sun4c_lockarea(char *vaddr, unsigned long size)
|
||||
npages = (((unsigned long)vaddr & ~PAGE_MASK) +
|
||||
size + (PAGE_SIZE-1)) >> PAGE_SHIFT;
|
||||
|
||||
scan = 0;
|
||||
local_irq_save(flags);
|
||||
for (;;) {
|
||||
scan = find_next_zero_bit(sun4c_iobuffer_map,
|
||||
iobuffer_map_size, scan);
|
||||
if ((base = scan) + npages > iobuffer_map_size) goto abend;
|
||||
for (;;) {
|
||||
if (scan >= base + npages) goto found;
|
||||
if (test_bit(scan, sun4c_iobuffer_map)) break;
|
||||
scan++;
|
||||
}
|
||||
}
|
||||
base = bitmap_find_next_zero_area(sun4c_iobuffer_map, iobuffer_map_size,
|
||||
0, npages, 0);
|
||||
if (base >= iobuffer_map_size)
|
||||
goto abend;
|
||||
|
||||
found:
|
||||
high = ((base + npages) << PAGE_SHIFT) + sun4c_iobuffer_start;
|
||||
high = SUN4C_REAL_PGDIR_ALIGN(high);
|
||||
while (high > sun4c_iobuffer_high) {
|
||||
|
@ -48,7 +48,6 @@ typedef struct user_i387_struct elf_fpregset_t;
|
||||
PT_REGS_EAX(regs) = 0; \
|
||||
} while (0)
|
||||
|
||||
#define USE_ELF_CORE_DUMP
|
||||
#define ELF_EXEC_PAGESIZE 4096
|
||||
|
||||
#define ELF_ET_DYN_BASE (2 * TASK_SIZE / 3)
|
||||
|
@ -17,8 +17,6 @@ extern long elf_aux_hwcap;
|
||||
#define ELF_CLASS ELFCLASS32
|
||||
#endif
|
||||
|
||||
#define USE_ELF_CORE_DUMP
|
||||
|
||||
#define R_386_NONE 0
|
||||
#define R_386_32 1
|
||||
#define R_386_PC32 2
|
||||
|
@ -104,7 +104,6 @@ extern int elf_core_copy_fpregs(struct task_struct *t, elf_fpregset_t *fpu);
|
||||
clear_thread_flag(TIF_IA32);
|
||||
#endif
|
||||
|
||||
#define USE_ELF_CORE_DUMP
|
||||
#define ELF_EXEC_PAGESIZE 4096
|
||||
|
||||
#define ELF_ET_DYN_BASE (2 * TASK_SIZE / 3)
|
||||
|
@ -67,7 +67,7 @@ static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
|
||||
if (!dev->dma_mask)
|
||||
return 0;
|
||||
|
||||
return addr + size <= *dev->dma_mask;
|
||||
return addr + size - 1 <= *dev->dma_mask;
|
||||
}
|
||||
|
||||
static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr)
|
||||
|
@ -239,7 +239,6 @@ extern int force_personality32;
|
||||
#endif /* !CONFIG_X86_32 */
|
||||
|
||||
#define CORE_DUMP_USE_REGSET
|
||||
#define USE_ELF_CORE_DUMP
|
||||
#define ELF_EXEC_PAGESIZE 4096
|
||||
|
||||
/* This is the location that an ET_DYN program is loaded if exec'ed. Typical
|
||||
|
@ -292,6 +292,8 @@ extern void user_enable_block_step(struct task_struct *);
|
||||
#define arch_has_block_step() (boot_cpu_data.x86 >= 6)
|
||||
#endif
|
||||
|
||||
#define ARCH_HAS_USER_SINGLE_STEP_INFO
|
||||
|
||||
struct user_desc;
|
||||
extern int do_get_thread_area(struct task_struct *p, int idx,
|
||||
struct user_desc __user *info);
|
||||
|
@ -76,15 +76,6 @@ union partition_info_u {
|
||||
};
|
||||
};
|
||||
|
||||
union uv_watchlist_u {
|
||||
u64 val;
|
||||
struct {
|
||||
u64 blade : 16,
|
||||
size : 32,
|
||||
filler : 16;
|
||||
};
|
||||
};
|
||||
|
||||
enum uv_memprotect {
|
||||
UV_MEMPROT_RESTRICT_ACCESS,
|
||||
UV_MEMPROT_ALLOW_AMO,
|
||||
@ -100,7 +91,7 @@ extern s64 uv_bios_call_reentrant(enum uv_bios_cmd, u64, u64, u64, u64, u64);
|
||||
|
||||
extern s64 uv_bios_get_sn_info(int, int *, long *, long *, long *);
|
||||
extern s64 uv_bios_freq_base(u64, u64 *);
|
||||
extern int uv_bios_mq_watchlist_alloc(int, unsigned long, unsigned int,
|
||||
extern int uv_bios_mq_watchlist_alloc(unsigned long, unsigned int,
|
||||
unsigned long *);
|
||||
extern int uv_bios_mq_watchlist_free(int, int);
|
||||
extern s64 uv_bios_change_memprotect(u64, u64, enum uv_memprotect);
|
||||
|
@ -172,6 +172,8 @@ DECLARE_PER_CPU(struct uv_hub_info_s, __uv_hub_info);
|
||||
#define UV_LOCAL_MMR_SIZE (64UL * 1024 * 1024)
|
||||
#define UV_GLOBAL_MMR32_SIZE (64UL * 1024 * 1024)
|
||||
|
||||
#define UV_GLOBAL_GRU_MMR_BASE 0x4000000
|
||||
|
||||
#define UV_GLOBAL_MMR32_PNODE_SHIFT 15
|
||||
#define UV_GLOBAL_MMR64_PNODE_SHIFT 26
|
||||
|
||||
@ -232,6 +234,26 @@ static inline unsigned long uv_gpa(void *v)
|
||||
return uv_soc_phys_ram_to_gpa(__pa(v));
|
||||
}
|
||||
|
||||
/* Top two bits indicate the requested address is in MMR space. */
|
||||
static inline int
|
||||
uv_gpa_in_mmr_space(unsigned long gpa)
|
||||
{
|
||||
return (gpa >> 62) == 0x3UL;
|
||||
}
|
||||
|
||||
/* UV global physical address --> socket phys RAM */
|
||||
static inline unsigned long uv_gpa_to_soc_phys_ram(unsigned long gpa)
|
||||
{
|
||||
unsigned long paddr = gpa & uv_hub_info->gpa_mask;
|
||||
unsigned long remap_base = uv_hub_info->lowmem_remap_base;
|
||||
unsigned long remap_top = uv_hub_info->lowmem_remap_top;
|
||||
|
||||
if (paddr >= remap_base && paddr < remap_base + remap_top)
|
||||
paddr -= remap_base;
|
||||
return paddr;
|
||||
}
|
||||
|
||||
|
||||
/* gnode -> pnode */
|
||||
static inline unsigned long uv_gpa_to_gnode(unsigned long gpa)
|
||||
{
|
||||
@ -307,6 +329,15 @@ static inline unsigned long uv_read_global_mmr64(int pnode,
|
||||
return readq(uv_global_mmr64_address(pnode, offset));
|
||||
}
|
||||
|
||||
/*
|
||||
* Global MMR space addresses when referenced by the GRU. (GRU does
|
||||
* NOT use socket addressing).
|
||||
*/
|
||||
static inline unsigned long uv_global_gru_mmr_address(int pnode, unsigned long offset)
|
||||
{
|
||||
return UV_GLOBAL_GRU_MMR_BASE | offset | (pnode << uv_hub_info->m_val);
|
||||
}
|
||||
|
||||
/*
|
||||
* Access hub local MMRs. Faster than using global space but only local MMRs
|
||||
* are accessible.
|
||||
@ -434,6 +465,14 @@ static inline void uv_set_cpu_scir_bits(int cpu, unsigned char value)
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned long uv_hub_ipi_value(int apicid, int vector, int mode)
|
||||
{
|
||||
return (1UL << UVH_IPI_INT_SEND_SHFT) |
|
||||
((apicid) << UVH_IPI_INT_APIC_ID_SHFT) |
|
||||
(mode << UVH_IPI_INT_DELIVERY_MODE_SHFT) |
|
||||
(vector << UVH_IPI_INT_VECTOR_SHFT);
|
||||
}
|
||||
|
||||
static inline void uv_hub_send_ipi(int pnode, int apicid, int vector)
|
||||
{
|
||||
unsigned long val;
|
||||
@ -442,10 +481,7 @@ static inline void uv_hub_send_ipi(int pnode, int apicid, int vector)
|
||||
if (vector == NMI_VECTOR)
|
||||
dmode = dest_NMI;
|
||||
|
||||
val = (1UL << UVH_IPI_INT_SEND_SHFT) |
|
||||
((apicid) << UVH_IPI_INT_APIC_ID_SHFT) |
|
||||
(dmode << UVH_IPI_INT_DELIVERY_MODE_SHFT) |
|
||||
(vector << UVH_IPI_INT_VECTOR_SHFT);
|
||||
val = uv_hub_ipi_value(apicid, vector, dmode);
|
||||
uv_write_global_mmr64(pnode, UVH_IPI_INT, val);
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
@ -1162,7 +1162,7 @@ static void dma_ops_free_addresses(struct dma_ops_domain *dom,
|
||||
|
||||
address = (address % APERTURE_RANGE_SIZE) >> PAGE_SHIFT;
|
||||
|
||||
iommu_area_free(range->bitmap, address, pages);
|
||||
bitmap_clear(range->bitmap, address, pages);
|
||||
|
||||
}
|
||||
|
||||
|
@ -101,21 +101,17 @@ s64 uv_bios_get_sn_info(int fc, int *uvtype, long *partid, long *coher,
|
||||
}
|
||||
|
||||
int
|
||||
uv_bios_mq_watchlist_alloc(int blade, unsigned long addr, unsigned int mq_size,
|
||||
uv_bios_mq_watchlist_alloc(unsigned long addr, unsigned int mq_size,
|
||||
unsigned long *intr_mmr_offset)
|
||||
{
|
||||
union uv_watchlist_u size_blade;
|
||||
u64 watchlist;
|
||||
s64 ret;
|
||||
|
||||
size_blade.size = mq_size;
|
||||
size_blade.blade = blade;
|
||||
|
||||
/*
|
||||
* bios returns watchlist number or negative error number.
|
||||
*/
|
||||
ret = (int)uv_bios_call_irqsave(UV_BIOS_WATCHLIST_ALLOC, addr,
|
||||
size_blade.val, (u64)intr_mmr_offset,
|
||||
mq_size, (u64)intr_mmr_offset,
|
||||
(u64)&watchlist, 0);
|
||||
if (ret < BIOS_STATUS_SUCCESS)
|
||||
return ret;
|
||||
|
@ -31,7 +31,7 @@
|
||||
#include <linux/string.h>
|
||||
#include <linux/crash_dump.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/pci_ids.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
@ -212,7 +212,7 @@ static void iommu_range_reserve(struct iommu_table *tbl,
|
||||
|
||||
spin_lock_irqsave(&tbl->it_lock, flags);
|
||||
|
||||
iommu_area_reserve(tbl->it_map, index, npages);
|
||||
bitmap_set(tbl->it_map, index, npages);
|
||||
|
||||
spin_unlock_irqrestore(&tbl->it_lock, flags);
|
||||
}
|
||||
@ -303,7 +303,7 @@ static void iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr,
|
||||
|
||||
spin_lock_irqsave(&tbl->it_lock, flags);
|
||||
|
||||
iommu_area_free(tbl->it_map, entry, npages);
|
||||
bitmap_clear(tbl->it_map, entry, npages);
|
||||
|
||||
spin_unlock_irqrestore(&tbl->it_lock, flags);
|
||||
}
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/topology.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/kdebug.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/iommu-helper.h>
|
||||
@ -126,7 +126,7 @@ static void free_iommu(unsigned long offset, int size)
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&iommu_bitmap_lock, flags);
|
||||
iommu_area_free(iommu_gart_bitmap, offset, size);
|
||||
bitmap_clear(iommu_gart_bitmap, offset, size);
|
||||
if (offset >= next_bit)
|
||||
next_bit = offset + size;
|
||||
spin_unlock_irqrestore(&iommu_bitmap_lock, flags);
|
||||
@ -792,7 +792,7 @@ int __init gart_iommu_init(void)
|
||||
* Out of IOMMU space handling.
|
||||
* Reserve some invalid pages at the beginning of the GART.
|
||||
*/
|
||||
iommu_area_reserve(iommu_gart_bitmap, 0, EMERGENCY_PAGES);
|
||||
bitmap_set(iommu_gart_bitmap, 0, EMERGENCY_PAGES);
|
||||
|
||||
pr_info("PCI-DMA: Reserving %luMB of IOMMU area in the AGP aperture\n",
|
||||
iommu_size >> 20);
|
||||
|
@ -1676,21 +1676,33 @@ const struct user_regset_view *task_user_regset_view(struct task_struct *task)
|
||||
#endif
|
||||
}
|
||||
|
||||
static void fill_sigtrap_info(struct task_struct *tsk,
|
||||
struct pt_regs *regs,
|
||||
int error_code, int si_code,
|
||||
struct siginfo *info)
|
||||
{
|
||||
tsk->thread.trap_no = 1;
|
||||
tsk->thread.error_code = error_code;
|
||||
|
||||
memset(info, 0, sizeof(*info));
|
||||
info->si_signo = SIGTRAP;
|
||||
info->si_code = si_code;
|
||||
info->si_addr = user_mode_vm(regs) ? (void __user *)regs->ip : NULL;
|
||||
}
|
||||
|
||||
void user_single_step_siginfo(struct task_struct *tsk,
|
||||
struct pt_regs *regs,
|
||||
struct siginfo *info)
|
||||
{
|
||||
fill_sigtrap_info(tsk, regs, 0, TRAP_BRKPT, info);
|
||||
}
|
||||
|
||||
void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs,
|
||||
int error_code, int si_code)
|
||||
{
|
||||
struct siginfo info;
|
||||
|
||||
tsk->thread.trap_no = 1;
|
||||
tsk->thread.error_code = error_code;
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.si_signo = SIGTRAP;
|
||||
info.si_code = si_code;
|
||||
|
||||
/* User-mode ip? */
|
||||
info.si_addr = user_mode_vm(regs) ? (void __user *) regs->ip : NULL;
|
||||
|
||||
fill_sigtrap_info(tsk, regs, error_code, si_code, &info);
|
||||
/* Send us the fake SIGTRAP */
|
||||
force_sig_info(SIGTRAP, &info, tsk);
|
||||
}
|
||||
@ -1755,29 +1767,22 @@ asmregparm long syscall_trace_enter(struct pt_regs *regs)
|
||||
|
||||
asmregparm void syscall_trace_leave(struct pt_regs *regs)
|
||||
{
|
||||
bool step;
|
||||
|
||||
if (unlikely(current->audit_context))
|
||||
audit_syscall_exit(AUDITSC_RESULT(regs->ax), regs->ax);
|
||||
|
||||
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
|
||||
trace_sys_exit(regs, regs->ax);
|
||||
|
||||
if (test_thread_flag(TIF_SYSCALL_TRACE))
|
||||
tracehook_report_syscall_exit(regs, 0);
|
||||
|
||||
/*
|
||||
* If TIF_SYSCALL_EMU is set, we only get here because of
|
||||
* TIF_SINGLESTEP (i.e. this is PTRACE_SYSEMU_SINGLESTEP).
|
||||
* We already reported this syscall instruction in
|
||||
* syscall_trace_enter(), so don't do any more now.
|
||||
* syscall_trace_enter().
|
||||
*/
|
||||
if (unlikely(test_thread_flag(TIF_SYSCALL_EMU)))
|
||||
return;
|
||||
|
||||
/*
|
||||
* If we are single-stepping, synthesize a trap to follow the
|
||||
* system call instruction.
|
||||
*/
|
||||
if (test_thread_flag(TIF_SINGLESTEP) &&
|
||||
tracehook_consider_fatal_signal(current, SIGTRAP))
|
||||
send_sigtrap(current, regs, 0, TRAP_BRKPT);
|
||||
step = unlikely(test_thread_flag(TIF_SINGLESTEP)) &&
|
||||
!test_thread_flag(TIF_SYSCALL_EMU);
|
||||
if (step || test_thread_flag(TIF_SYSCALL_TRACE))
|
||||
tracehook_report_syscall_exit(regs, step);
|
||||
}
|
||||
|
@ -123,7 +123,6 @@ extern void xtensa_elf_core_copy_regs (xtensa_gregset_t *, struct pt_regs *);
|
||||
#define ELF_CLASS ELFCLASS32
|
||||
#define ELF_ARCH EM_XTENSA
|
||||
|
||||
#define USE_ELF_CORE_DUMP
|
||||
#define ELF_EXEC_PAGESIZE PAGE_SIZE
|
||||
|
||||
/*
|
||||
|
@ -285,6 +285,7 @@ static const struct file_operations efi_rtc_fops = {
|
||||
.unlocked_ioctl = efi_rtc_ioctl,
|
||||
.open = efi_rtc_open,
|
||||
.release = efi_rtc_close,
|
||||
.llseek = no_llseek,
|
||||
};
|
||||
|
||||
static struct miscdevice efi_rtc_dev= {
|
||||
|
@ -370,7 +370,7 @@ static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time)
|
||||
return SI_SM_IDLE;
|
||||
|
||||
case KCS_START_OP:
|
||||
if (state != KCS_IDLE) {
|
||||
if (state != KCS_IDLE_STATE) {
|
||||
start_error_recovery(kcs,
|
||||
"State machine not idle at start");
|
||||
break;
|
||||
|
@ -339,7 +339,7 @@ static struct sysrq_key_op sysrq_term_op = {
|
||||
|
||||
static void moom_callback(struct work_struct *ignored)
|
||||
{
|
||||
out_of_memory(node_zonelist(0, GFP_KERNEL), GFP_KERNEL, 0);
|
||||
out_of_memory(node_zonelist(0, GFP_KERNEL), GFP_KERNEL, 0, NULL);
|
||||
}
|
||||
|
||||
static DECLARE_WORK(moom_work, moom_callback);
|
||||
|
@ -164,6 +164,9 @@ module_param(default_utf8, int, S_IRUGO | S_IWUSR);
|
||||
int global_cursor_default = -1;
|
||||
module_param(global_cursor_default, int, S_IRUGO | S_IWUSR);
|
||||
|
||||
static int cur_default = CUR_DEFAULT;
|
||||
module_param(cur_default, int, S_IRUGO | S_IWUSR);
|
||||
|
||||
/*
|
||||
* ignore_poke: don't unblank the screen when things are typed. This is
|
||||
* mainly for the privacy of braille terminal users.
|
||||
@ -1636,7 +1639,7 @@ static void reset_terminal(struct vc_data *vc, int do_clear)
|
||||
/* do not do set_leds here because this causes an endless tasklet loop
|
||||
when the keyboard hasn't been initialized yet */
|
||||
|
||||
vc->vc_cursor_type = CUR_DEFAULT;
|
||||
vc->vc_cursor_type = cur_default;
|
||||
vc->vc_complement_mask = vc->vc_s_complement_mask;
|
||||
|
||||
default_attr(vc);
|
||||
@ -1838,7 +1841,7 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
|
||||
if (vc->vc_par[0])
|
||||
vc->vc_cursor_type = vc->vc_par[0] | (vc->vc_par[1] << 8) | (vc->vc_par[2] << 16);
|
||||
else
|
||||
vc->vc_cursor_type = CUR_DEFAULT;
|
||||
vc->vc_cursor_type = cur_default;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
@ -9,6 +9,11 @@
|
||||
* Intel 5100X Chipset Memory Controller Hub (MCH) - Datasheet
|
||||
* http://download.intel.com/design/chipsets/datashts/318378.pdf
|
||||
*
|
||||
* The intel 5100 has two independent channels. EDAC core currently
|
||||
* can not reflect this configuration so instead the chip-select
|
||||
* rows for each respective channel are layed out one after another,
|
||||
* the first half belonging to channel 0, the second half belonging
|
||||
* to channel 1.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
@ -25,6 +30,8 @@
|
||||
|
||||
/* device 16, func 1 */
|
||||
#define I5100_MC 0x40 /* Memory Control Register */
|
||||
#define I5100_MC_SCRBEN_MASK (1 << 7)
|
||||
#define I5100_MC_SCRBDONE_MASK (1 << 4)
|
||||
#define I5100_MS 0x44 /* Memory Status Register */
|
||||
#define I5100_SPDDATA 0x48 /* Serial Presence Detect Status Reg */
|
||||
#define I5100_SPDCMD 0x4c /* Serial Presence Detect Command Reg */
|
||||
@ -72,11 +79,21 @@
|
||||
|
||||
/* bit field accessors */
|
||||
|
||||
static inline u32 i5100_mc_scrben(u32 mc)
|
||||
{
|
||||
return mc >> 7 & 1;
|
||||
}
|
||||
|
||||
static inline u32 i5100_mc_errdeten(u32 mc)
|
||||
{
|
||||
return mc >> 5 & 1;
|
||||
}
|
||||
|
||||
static inline u32 i5100_mc_scrbdone(u32 mc)
|
||||
{
|
||||
return mc >> 4 & 1;
|
||||
}
|
||||
|
||||
static inline u16 i5100_spddata_rdo(u16 a)
|
||||
{
|
||||
return a >> 15 & 1;
|
||||
@ -265,42 +282,43 @@ static inline u32 i5100_recmemb_ras(u32 a)
|
||||
}
|
||||
|
||||
/* some generic limits */
|
||||
#define I5100_MAX_RANKS_PER_CTLR 6
|
||||
#define I5100_MAX_CTLRS 2
|
||||
#define I5100_MAX_RANKS_PER_CHAN 6
|
||||
#define I5100_CHANNELS 2
|
||||
#define I5100_MAX_RANKS_PER_DIMM 4
|
||||
#define I5100_DIMM_ADDR_LINES (6 - 3) /* 64 bits / 8 bits per byte */
|
||||
#define I5100_MAX_DIMM_SLOTS_PER_CTLR 4
|
||||
#define I5100_MAX_DIMM_SLOTS_PER_CHAN 4
|
||||
#define I5100_MAX_RANK_INTERLEAVE 4
|
||||
#define I5100_MAX_DMIRS 5
|
||||
#define I5100_SCRUB_REFRESH_RATE (5 * 60 * HZ)
|
||||
|
||||
struct i5100_priv {
|
||||
/* ranks on each dimm -- 0 maps to not present -- obtained via SPD */
|
||||
int dimm_numrank[I5100_MAX_CTLRS][I5100_MAX_DIMM_SLOTS_PER_CTLR];
|
||||
int dimm_numrank[I5100_CHANNELS][I5100_MAX_DIMM_SLOTS_PER_CHAN];
|
||||
|
||||
/*
|
||||
* mainboard chip select map -- maps i5100 chip selects to
|
||||
* DIMM slot chip selects. In the case of only 4 ranks per
|
||||
* controller, the mapping is fairly obvious but not unique.
|
||||
* we map -1 -> NC and assume both controllers use the same
|
||||
* channel, the mapping is fairly obvious but not unique.
|
||||
* we map -1 -> NC and assume both channels use the same
|
||||
* map...
|
||||
*
|
||||
*/
|
||||
int dimm_csmap[I5100_MAX_DIMM_SLOTS_PER_CTLR][I5100_MAX_RANKS_PER_DIMM];
|
||||
int dimm_csmap[I5100_MAX_DIMM_SLOTS_PER_CHAN][I5100_MAX_RANKS_PER_DIMM];
|
||||
|
||||
/* memory interleave range */
|
||||
struct {
|
||||
u64 limit;
|
||||
unsigned way[2];
|
||||
} mir[I5100_MAX_CTLRS];
|
||||
} mir[I5100_CHANNELS];
|
||||
|
||||
/* adjusted memory interleave range register */
|
||||
unsigned amir[I5100_MAX_CTLRS];
|
||||
unsigned amir[I5100_CHANNELS];
|
||||
|
||||
/* dimm interleave range */
|
||||
struct {
|
||||
unsigned rank[I5100_MAX_RANK_INTERLEAVE];
|
||||
u64 limit;
|
||||
} dmir[I5100_MAX_CTLRS][I5100_MAX_DMIRS];
|
||||
} dmir[I5100_CHANNELS][I5100_MAX_DMIRS];
|
||||
|
||||
/* memory technology registers... */
|
||||
struct {
|
||||
@ -310,30 +328,33 @@ struct i5100_priv {
|
||||
unsigned numbank; /* 2 or 3 lines */
|
||||
unsigned numrow; /* 13 .. 16 lines */
|
||||
unsigned numcol; /* 11 .. 12 lines */
|
||||
} mtr[I5100_MAX_CTLRS][I5100_MAX_RANKS_PER_CTLR];
|
||||
} mtr[I5100_CHANNELS][I5100_MAX_RANKS_PER_CHAN];
|
||||
|
||||
u64 tolm; /* top of low memory in bytes */
|
||||
unsigned ranksperctlr; /* number of ranks per controller */
|
||||
unsigned ranksperchan; /* number of ranks per channel */
|
||||
|
||||
struct pci_dev *mc; /* device 16 func 1 */
|
||||
struct pci_dev *ch0mm; /* device 21 func 0 */
|
||||
struct pci_dev *ch1mm; /* device 22 func 0 */
|
||||
|
||||
struct delayed_work i5100_scrubbing;
|
||||
int scrub_enable;
|
||||
};
|
||||
|
||||
/* map a rank/ctlr to a slot number on the mainboard */
|
||||
/* map a rank/chan to a slot number on the mainboard */
|
||||
static int i5100_rank_to_slot(const struct mem_ctl_info *mci,
|
||||
int ctlr, int rank)
|
||||
int chan, int rank)
|
||||
{
|
||||
const struct i5100_priv *priv = mci->pvt_info;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CTLR; i++) {
|
||||
for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CHAN; i++) {
|
||||
int j;
|
||||
const int numrank = priv->dimm_numrank[ctlr][i];
|
||||
const int numrank = priv->dimm_numrank[chan][i];
|
||||
|
||||
for (j = 0; j < numrank; j++)
|
||||
if (priv->dimm_csmap[i][j] == rank)
|
||||
return i * 2 + ctlr;
|
||||
return i * 2 + chan;
|
||||
}
|
||||
|
||||
return -1;
|
||||
@ -374,32 +395,32 @@ static const char *i5100_err_msg(unsigned err)
|
||||
return "none";
|
||||
}
|
||||
|
||||
/* convert csrow index into a rank (per controller -- 0..5) */
|
||||
/* convert csrow index into a rank (per channel -- 0..5) */
|
||||
static int i5100_csrow_to_rank(const struct mem_ctl_info *mci, int csrow)
|
||||
{
|
||||
const struct i5100_priv *priv = mci->pvt_info;
|
||||
|
||||
return csrow % priv->ranksperctlr;
|
||||
return csrow % priv->ranksperchan;
|
||||
}
|
||||
|
||||
/* convert csrow index into a controller (0..1) */
|
||||
static int i5100_csrow_to_cntlr(const struct mem_ctl_info *mci, int csrow)
|
||||
/* convert csrow index into a channel (0..1) */
|
||||
static int i5100_csrow_to_chan(const struct mem_ctl_info *mci, int csrow)
|
||||
{
|
||||
const struct i5100_priv *priv = mci->pvt_info;
|
||||
|
||||
return csrow / priv->ranksperctlr;
|
||||
return csrow / priv->ranksperchan;
|
||||
}
|
||||
|
||||
static unsigned i5100_rank_to_csrow(const struct mem_ctl_info *mci,
|
||||
int ctlr, int rank)
|
||||
int chan, int rank)
|
||||
{
|
||||
const struct i5100_priv *priv = mci->pvt_info;
|
||||
|
||||
return ctlr * priv->ranksperctlr + rank;
|
||||
return chan * priv->ranksperchan + rank;
|
||||
}
|
||||
|
||||
static void i5100_handle_ce(struct mem_ctl_info *mci,
|
||||
int ctlr,
|
||||
int chan,
|
||||
unsigned bank,
|
||||
unsigned rank,
|
||||
unsigned long syndrome,
|
||||
@ -407,12 +428,12 @@ static void i5100_handle_ce(struct mem_ctl_info *mci,
|
||||
unsigned ras,
|
||||
const char *msg)
|
||||
{
|
||||
const int csrow = i5100_rank_to_csrow(mci, ctlr, rank);
|
||||
const int csrow = i5100_rank_to_csrow(mci, chan, rank);
|
||||
|
||||
printk(KERN_ERR
|
||||
"CE ctlr %d, bank %u, rank %u, syndrome 0x%lx, "
|
||||
"CE chan %d, bank %u, rank %u, syndrome 0x%lx, "
|
||||
"cas %u, ras %u, csrow %u, label \"%s\": %s\n",
|
||||
ctlr, bank, rank, syndrome, cas, ras,
|
||||
chan, bank, rank, syndrome, cas, ras,
|
||||
csrow, mci->csrows[csrow].channels[0].label, msg);
|
||||
|
||||
mci->ce_count++;
|
||||
@ -421,7 +442,7 @@ static void i5100_handle_ce(struct mem_ctl_info *mci,
|
||||
}
|
||||
|
||||
static void i5100_handle_ue(struct mem_ctl_info *mci,
|
||||
int ctlr,
|
||||
int chan,
|
||||
unsigned bank,
|
||||
unsigned rank,
|
||||
unsigned long syndrome,
|
||||
@ -429,23 +450,23 @@ static void i5100_handle_ue(struct mem_ctl_info *mci,
|
||||
unsigned ras,
|
||||
const char *msg)
|
||||
{
|
||||
const int csrow = i5100_rank_to_csrow(mci, ctlr, rank);
|
||||
const int csrow = i5100_rank_to_csrow(mci, chan, rank);
|
||||
|
||||
printk(KERN_ERR
|
||||
"UE ctlr %d, bank %u, rank %u, syndrome 0x%lx, "
|
||||
"UE chan %d, bank %u, rank %u, syndrome 0x%lx, "
|
||||
"cas %u, ras %u, csrow %u, label \"%s\": %s\n",
|
||||
ctlr, bank, rank, syndrome, cas, ras,
|
||||
chan, bank, rank, syndrome, cas, ras,
|
||||
csrow, mci->csrows[csrow].channels[0].label, msg);
|
||||
|
||||
mci->ue_count++;
|
||||
mci->csrows[csrow].ue_count++;
|
||||
}
|
||||
|
||||
static void i5100_read_log(struct mem_ctl_info *mci, int ctlr,
|
||||
static void i5100_read_log(struct mem_ctl_info *mci, int chan,
|
||||
u32 ferr, u32 nerr)
|
||||
{
|
||||
struct i5100_priv *priv = mci->pvt_info;
|
||||
struct pci_dev *pdev = (ctlr) ? priv->ch1mm : priv->ch0mm;
|
||||
struct pci_dev *pdev = (chan) ? priv->ch1mm : priv->ch0mm;
|
||||
u32 dw;
|
||||
u32 dw2;
|
||||
unsigned syndrome = 0;
|
||||
@ -484,7 +505,7 @@ static void i5100_read_log(struct mem_ctl_info *mci, int ctlr,
|
||||
else
|
||||
msg = i5100_err_msg(nerr);
|
||||
|
||||
i5100_handle_ce(mci, ctlr, bank, rank, syndrome, cas, ras, msg);
|
||||
i5100_handle_ce(mci, chan, bank, rank, syndrome, cas, ras, msg);
|
||||
}
|
||||
|
||||
if (i5100_validlog_nrecmemvalid(dw)) {
|
||||
@ -506,7 +527,7 @@ static void i5100_read_log(struct mem_ctl_info *mci, int ctlr,
|
||||
else
|
||||
msg = i5100_err_msg(nerr);
|
||||
|
||||
i5100_handle_ue(mci, ctlr, bank, rank, syndrome, cas, ras, msg);
|
||||
i5100_handle_ue(mci, chan, bank, rank, syndrome, cas, ras, msg);
|
||||
}
|
||||
|
||||
pci_write_config_dword(pdev, I5100_VALIDLOG, dw);
|
||||
@ -534,6 +555,80 @@ static void i5100_check_error(struct mem_ctl_info *mci)
|
||||
}
|
||||
}
|
||||
|
||||
/* The i5100 chipset will scrub the entire memory once, then
|
||||
* set a done bit. Continuous scrubbing is achieved by enqueing
|
||||
* delayed work to a workqueue, checking every few minutes if
|
||||
* the scrubbing has completed and if so reinitiating it.
|
||||
*/
|
||||
|
||||
static void i5100_refresh_scrubbing(struct work_struct *work)
|
||||
{
|
||||
struct delayed_work *i5100_scrubbing = container_of(work,
|
||||
struct delayed_work,
|
||||
work);
|
||||
struct i5100_priv *priv = container_of(i5100_scrubbing,
|
||||
struct i5100_priv,
|
||||
i5100_scrubbing);
|
||||
u32 dw;
|
||||
|
||||
pci_read_config_dword(priv->mc, I5100_MC, &dw);
|
||||
|
||||
if (priv->scrub_enable) {
|
||||
|
||||
pci_read_config_dword(priv->mc, I5100_MC, &dw);
|
||||
|
||||
if (i5100_mc_scrbdone(dw)) {
|
||||
dw |= I5100_MC_SCRBEN_MASK;
|
||||
pci_write_config_dword(priv->mc, I5100_MC, dw);
|
||||
pci_read_config_dword(priv->mc, I5100_MC, &dw);
|
||||
}
|
||||
|
||||
schedule_delayed_work(&(priv->i5100_scrubbing),
|
||||
I5100_SCRUB_REFRESH_RATE);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* The bandwidth is based on experimentation, feel free to refine it.
|
||||
*/
|
||||
static int i5100_set_scrub_rate(struct mem_ctl_info *mci,
|
||||
u32 *bandwidth)
|
||||
{
|
||||
struct i5100_priv *priv = mci->pvt_info;
|
||||
u32 dw;
|
||||
|
||||
pci_read_config_dword(priv->mc, I5100_MC, &dw);
|
||||
if (*bandwidth) {
|
||||
priv->scrub_enable = 1;
|
||||
dw |= I5100_MC_SCRBEN_MASK;
|
||||
schedule_delayed_work(&(priv->i5100_scrubbing),
|
||||
I5100_SCRUB_REFRESH_RATE);
|
||||
} else {
|
||||
priv->scrub_enable = 0;
|
||||
dw &= ~I5100_MC_SCRBEN_MASK;
|
||||
cancel_delayed_work(&(priv->i5100_scrubbing));
|
||||
}
|
||||
pci_write_config_dword(priv->mc, I5100_MC, dw);
|
||||
|
||||
pci_read_config_dword(priv->mc, I5100_MC, &dw);
|
||||
|
||||
*bandwidth = 5900000 * i5100_mc_scrben(dw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i5100_get_scrub_rate(struct mem_ctl_info *mci,
|
||||
u32 *bandwidth)
|
||||
{
|
||||
struct i5100_priv *priv = mci->pvt_info;
|
||||
u32 dw;
|
||||
|
||||
pci_read_config_dword(priv->mc, I5100_MC, &dw);
|
||||
|
||||
*bandwidth = 5900000 * i5100_mc_scrben(dw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pci_dev *pci_get_device_func(unsigned vendor,
|
||||
unsigned device,
|
||||
unsigned func)
|
||||
@ -557,19 +652,19 @@ static unsigned long __devinit i5100_npages(struct mem_ctl_info *mci,
|
||||
int csrow)
|
||||
{
|
||||
struct i5100_priv *priv = mci->pvt_info;
|
||||
const unsigned ctlr_rank = i5100_csrow_to_rank(mci, csrow);
|
||||
const unsigned ctlr = i5100_csrow_to_cntlr(mci, csrow);
|
||||
const unsigned chan_rank = i5100_csrow_to_rank(mci, csrow);
|
||||
const unsigned chan = i5100_csrow_to_chan(mci, csrow);
|
||||
unsigned addr_lines;
|
||||
|
||||
/* dimm present? */
|
||||
if (!priv->mtr[ctlr][ctlr_rank].present)
|
||||
if (!priv->mtr[chan][chan_rank].present)
|
||||
return 0ULL;
|
||||
|
||||
addr_lines =
|
||||
I5100_DIMM_ADDR_LINES +
|
||||
priv->mtr[ctlr][ctlr_rank].numcol +
|
||||
priv->mtr[ctlr][ctlr_rank].numrow +
|
||||
priv->mtr[ctlr][ctlr_rank].numbank;
|
||||
priv->mtr[chan][chan_rank].numcol +
|
||||
priv->mtr[chan][chan_rank].numrow +
|
||||
priv->mtr[chan][chan_rank].numbank;
|
||||
|
||||
return (unsigned long)
|
||||
((unsigned long long) (1ULL << addr_lines) / PAGE_SIZE);
|
||||
@ -581,11 +676,11 @@ static void __devinit i5100_init_mtr(struct mem_ctl_info *mci)
|
||||
struct pci_dev *mms[2] = { priv->ch0mm, priv->ch1mm };
|
||||
int i;
|
||||
|
||||
for (i = 0; i < I5100_MAX_CTLRS; i++) {
|
||||
for (i = 0; i < I5100_CHANNELS; i++) {
|
||||
int j;
|
||||
struct pci_dev *pdev = mms[i];
|
||||
|
||||
for (j = 0; j < I5100_MAX_RANKS_PER_CTLR; j++) {
|
||||
for (j = 0; j < I5100_MAX_RANKS_PER_CHAN; j++) {
|
||||
const unsigned addr =
|
||||
(j < 4) ? I5100_MTR_0 + j * 2 :
|
||||
I5100_MTR_4 + (j - 4) * 2;
|
||||
@ -644,7 +739,6 @@ static int i5100_read_spd_byte(const struct mem_ctl_info *mci,
|
||||
* fill dimm chip select map
|
||||
*
|
||||
* FIXME:
|
||||
* o only valid for 4 ranks per controller
|
||||
* o not the only way to may chip selects to dimm slots
|
||||
* o investigate if there is some way to obtain this map from the bios
|
||||
*/
|
||||
@ -653,9 +747,7 @@ static void __devinit i5100_init_dimm_csmap(struct mem_ctl_info *mci)
|
||||
struct i5100_priv *priv = mci->pvt_info;
|
||||
int i;
|
||||
|
||||
WARN_ON(priv->ranksperctlr != 4);
|
||||
|
||||
for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CTLR; i++) {
|
||||
for (i = 0; i < I5100_MAX_DIMM_SLOTS_PER_CHAN; i++) {
|
||||
int j;
|
||||
|
||||
for (j = 0; j < I5100_MAX_RANKS_PER_DIMM; j++)
|
||||
@ -663,12 +755,21 @@ static void __devinit i5100_init_dimm_csmap(struct mem_ctl_info *mci)
|
||||
}
|
||||
|
||||
/* only 2 chip selects per slot... */
|
||||
priv->dimm_csmap[0][0] = 0;
|
||||
priv->dimm_csmap[0][1] = 3;
|
||||
priv->dimm_csmap[1][0] = 1;
|
||||
priv->dimm_csmap[1][1] = 2;
|
||||
priv->dimm_csmap[2][0] = 2;
|
||||
priv->dimm_csmap[3][0] = 3;
|
||||
if (priv->ranksperchan == 4) {
|
||||
priv->dimm_csmap[0][0] = 0;
|
||||
priv->dimm_csmap[0][1] = 3;
|
||||
priv->dimm_csmap[1][0] = 1;
|
||||
priv->dimm_csmap[1][1] = 2;
|
||||
priv->dimm_csmap[2][0] = 2;
|
||||
priv->dimm_csmap[3][0] = 3;
|
||||
} else {
|
||||
priv->dimm_csmap[0][0] = 0;
|
||||
priv->dimm_csmap[0][1] = 1;
|
||||
priv->dimm_csmap[1][0] = 2;
|
||||
priv->dimm_csmap[1][1] = 3;
|
||||
priv->dimm_csmap[2][0] = 4;
|
||||
priv->dimm_csmap[2][1] = 5;
|
||||
}
|
||||
}
|
||||
|
||||
static void __devinit i5100_init_dimm_layout(struct pci_dev *pdev,
|
||||
@ -677,10 +778,10 @@ static void __devinit i5100_init_dimm_layout(struct pci_dev *pdev,
|
||||
struct i5100_priv *priv = mci->pvt_info;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < I5100_MAX_CTLRS; i++) {
|
||||
for (i = 0; i < I5100_CHANNELS; i++) {
|
||||
int j;
|
||||
|
||||
for (j = 0; j < I5100_MAX_DIMM_SLOTS_PER_CTLR; j++) {
|
||||
for (j = 0; j < I5100_MAX_DIMM_SLOTS_PER_CHAN; j++) {
|
||||
u8 rank;
|
||||
|
||||
if (i5100_read_spd_byte(mci, i, j, 5, &rank) < 0)
|
||||
@ -720,7 +821,7 @@ static void __devinit i5100_init_interleaving(struct pci_dev *pdev,
|
||||
pci_read_config_word(pdev, I5100_AMIR_1, &w);
|
||||
priv->amir[1] = w;
|
||||
|
||||
for (i = 0; i < I5100_MAX_CTLRS; i++) {
|
||||
for (i = 0; i < I5100_CHANNELS; i++) {
|
||||
int j;
|
||||
|
||||
for (j = 0; j < 5; j++) {
|
||||
@ -747,7 +848,7 @@ static void __devinit i5100_init_csrows(struct mem_ctl_info *mci)
|
||||
|
||||
for (i = 0; i < mci->nr_csrows; i++) {
|
||||
const unsigned long npages = i5100_npages(mci, i);
|
||||
const unsigned cntlr = i5100_csrow_to_cntlr(mci, i);
|
||||
const unsigned chan = i5100_csrow_to_chan(mci, i);
|
||||
const unsigned rank = i5100_csrow_to_rank(mci, i);
|
||||
|
||||
if (!npages)
|
||||
@ -765,7 +866,7 @@ static void __devinit i5100_init_csrows(struct mem_ctl_info *mci)
|
||||
mci->csrows[i].grain = 32;
|
||||
mci->csrows[i].csrow_idx = i;
|
||||
mci->csrows[i].dtype =
|
||||
(priv->mtr[cntlr][rank].width == 4) ? DEV_X4 : DEV_X8;
|
||||
(priv->mtr[chan][rank].width == 4) ? DEV_X4 : DEV_X8;
|
||||
mci->csrows[i].ue_count = 0;
|
||||
mci->csrows[i].ce_count = 0;
|
||||
mci->csrows[i].mtype = MEM_RDDR2;
|
||||
@ -777,7 +878,7 @@ static void __devinit i5100_init_csrows(struct mem_ctl_info *mci)
|
||||
mci->csrows[i].channels[0].csrow = mci->csrows + i;
|
||||
snprintf(mci->csrows[i].channels[0].label,
|
||||
sizeof(mci->csrows[i].channels[0].label),
|
||||
"DIMM%u", i5100_rank_to_slot(mci, cntlr, rank));
|
||||
"DIMM%u", i5100_rank_to_slot(mci, chan, rank));
|
||||
|
||||
total_pages += npages;
|
||||
}
|
||||
@ -815,13 +916,6 @@ static int __devinit i5100_init_one(struct pci_dev *pdev,
|
||||
pci_read_config_dword(pdev, I5100_MS, &dw);
|
||||
ranksperch = !!(dw & (1 << 8)) * 2 + 4;
|
||||
|
||||
if (ranksperch != 4) {
|
||||
/* FIXME: get 6 ranks / controller to work - need hw... */
|
||||
printk(KERN_INFO "i5100_edac: unsupported configuration.\n");
|
||||
ret = -ENODEV;
|
||||
goto bail_pdev;
|
||||
}
|
||||
|
||||
/* enable error reporting... */
|
||||
pci_read_config_dword(pdev, I5100_EMASK_MEM, &dw);
|
||||
dw &= ~I5100_FERR_NF_MEM_ANY_MASK;
|
||||
@ -864,11 +958,21 @@ static int __devinit i5100_init_one(struct pci_dev *pdev,
|
||||
mci->dev = &pdev->dev;
|
||||
|
||||
priv = mci->pvt_info;
|
||||
priv->ranksperctlr = ranksperch;
|
||||
priv->ranksperchan = ranksperch;
|
||||
priv->mc = pdev;
|
||||
priv->ch0mm = ch0mm;
|
||||
priv->ch1mm = ch1mm;
|
||||
|
||||
INIT_DELAYED_WORK(&(priv->i5100_scrubbing), i5100_refresh_scrubbing);
|
||||
|
||||
/* If scrubbing was already enabled by the bios, start maintaining it */
|
||||
pci_read_config_dword(pdev, I5100_MC, &dw);
|
||||
if (i5100_mc_scrben(dw)) {
|
||||
priv->scrub_enable = 1;
|
||||
schedule_delayed_work(&(priv->i5100_scrubbing),
|
||||
I5100_SCRUB_REFRESH_RATE);
|
||||
}
|
||||
|
||||
i5100_init_dimm_layout(pdev, mci);
|
||||
i5100_init_interleaving(pdev, mci);
|
||||
|
||||
@ -882,6 +986,8 @@ static int __devinit i5100_init_one(struct pci_dev *pdev,
|
||||
mci->ctl_page_to_phys = NULL;
|
||||
|
||||
mci->edac_check = i5100_check_error;
|
||||
mci->set_sdram_scrub_rate = i5100_set_scrub_rate;
|
||||
mci->get_sdram_scrub_rate = i5100_get_scrub_rate;
|
||||
|
||||
i5100_init_csrows(mci);
|
||||
|
||||
@ -897,12 +1003,14 @@ static int __devinit i5100_init_one(struct pci_dev *pdev,
|
||||
|
||||
if (edac_mc_add_mc(mci)) {
|
||||
ret = -ENODEV;
|
||||
goto bail_mc;
|
||||
goto bail_scrub;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
bail_mc:
|
||||
bail_scrub:
|
||||
priv->scrub_enable = 0;
|
||||
cancel_delayed_work_sync(&(priv->i5100_scrubbing));
|
||||
edac_mc_free(mci);
|
||||
|
||||
bail_disable_ch1:
|
||||
@ -935,6 +1043,10 @@ static void __devexit i5100_remove_one(struct pci_dev *pdev)
|
||||
return;
|
||||
|
||||
priv = mci->pvt_info;
|
||||
|
||||
priv->scrub_enable = 0;
|
||||
cancel_delayed_work_sync(&(priv->i5100_scrubbing));
|
||||
|
||||
pci_disable_device(pdev);
|
||||
pci_disable_device(priv->ch0mm);
|
||||
pci_disable_device(priv->ch1mm);
|
||||
|
@ -206,6 +206,12 @@ config GPIO_LANGWELL
|
||||
help
|
||||
Say Y here to support Intel Moorestown platform GPIO.
|
||||
|
||||
config GPIO_TIMBERDALE
|
||||
bool "Support for timberdale GPIO IP"
|
||||
depends on MFD_TIMBERDALE && GPIOLIB && HAS_IOMEM
|
||||
---help---
|
||||
Add support for the GPIO IP in the timberdale FPGA.
|
||||
|
||||
comment "SPI GPIO expanders:"
|
||||
|
||||
config GPIO_MAX7301
|
||||
|
@ -13,6 +13,7 @@ obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o
|
||||
obj-$(CONFIG_GPIO_PCA953X) += pca953x.o
|
||||
obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o
|
||||
obj-$(CONFIG_GPIO_PL061) += pl061.o
|
||||
obj-$(CONFIG_GPIO_TIMBERDALE) += timbgpio.o
|
||||
obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o
|
||||
obj-$(CONFIG_GPIO_UCB1400) += ucb1400_gpio.o
|
||||
obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o
|
||||
|
@ -53,6 +53,7 @@ struct gpio_desc {
|
||||
#define FLAG_SYSFS 4 /* exported via /sys/class/gpio/control */
|
||||
#define FLAG_TRIG_FALL 5 /* trigger on falling edge */
|
||||
#define FLAG_TRIG_RISE 6 /* trigger on rising edge */
|
||||
#define FLAG_ACTIVE_LOW 7 /* sysfs value has active low */
|
||||
|
||||
#define PDESC_ID_SHIFT 16 /* add new flags before this one */
|
||||
|
||||
@ -210,6 +211,11 @@ static DEFINE_MUTEX(sysfs_lock);
|
||||
* * configures behavior of poll(2) on /value
|
||||
* * available only if pin can generate IRQs on input
|
||||
* * is read/write as "none", "falling", "rising", or "both"
|
||||
* /active_low
|
||||
* * configures polarity of /value
|
||||
* * is read/write as zero/nonzero
|
||||
* * also affects existing and subsequent "falling" and "rising"
|
||||
* /edge configuration
|
||||
*/
|
||||
|
||||
static ssize_t gpio_direction_show(struct device *dev,
|
||||
@ -255,7 +261,7 @@ static ssize_t gpio_direction_store(struct device *dev,
|
||||
return status ? : size;
|
||||
}
|
||||
|
||||
static const DEVICE_ATTR(direction, 0644,
|
||||
static /* const */ DEVICE_ATTR(direction, 0644,
|
||||
gpio_direction_show, gpio_direction_store);
|
||||
|
||||
static ssize_t gpio_value_show(struct device *dev,
|
||||
@ -267,10 +273,17 @@ static ssize_t gpio_value_show(struct device *dev,
|
||||
|
||||
mutex_lock(&sysfs_lock);
|
||||
|
||||
if (!test_bit(FLAG_EXPORT, &desc->flags))
|
||||
if (!test_bit(FLAG_EXPORT, &desc->flags)) {
|
||||
status = -EIO;
|
||||
else
|
||||
status = sprintf(buf, "%d\n", !!gpio_get_value_cansleep(gpio));
|
||||
} else {
|
||||
int value;
|
||||
|
||||
value = !!gpio_get_value_cansleep(gpio);
|
||||
if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
|
||||
value = !value;
|
||||
|
||||
status = sprintf(buf, "%d\n", value);
|
||||
}
|
||||
|
||||
mutex_unlock(&sysfs_lock);
|
||||
return status;
|
||||
@ -294,6 +307,8 @@ static ssize_t gpio_value_store(struct device *dev,
|
||||
|
||||
status = strict_strtol(buf, 0, &value);
|
||||
if (status == 0) {
|
||||
if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
|
||||
value = !value;
|
||||
gpio_set_value_cansleep(gpio, value != 0);
|
||||
status = size;
|
||||
}
|
||||
@ -303,7 +318,7 @@ static ssize_t gpio_value_store(struct device *dev,
|
||||
return status;
|
||||
}
|
||||
|
||||
static /*const*/ DEVICE_ATTR(value, 0644,
|
||||
static const DEVICE_ATTR(value, 0644,
|
||||
gpio_value_show, gpio_value_store);
|
||||
|
||||
static irqreturn_t gpio_sysfs_irq(int irq, void *priv)
|
||||
@ -352,9 +367,11 @@ static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev,
|
||||
|
||||
irq_flags = IRQF_SHARED;
|
||||
if (test_bit(FLAG_TRIG_FALL, &gpio_flags))
|
||||
irq_flags |= IRQF_TRIGGER_FALLING;
|
||||
irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
|
||||
IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
|
||||
if (test_bit(FLAG_TRIG_RISE, &gpio_flags))
|
||||
irq_flags |= IRQF_TRIGGER_RISING;
|
||||
irq_flags |= test_bit(FLAG_ACTIVE_LOW, &desc->flags) ?
|
||||
IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
|
||||
|
||||
if (!pdesc) {
|
||||
pdesc = kmalloc(sizeof(*pdesc), GFP_KERNEL);
|
||||
@ -475,9 +492,79 @@ found:
|
||||
|
||||
static DEVICE_ATTR(edge, 0644, gpio_edge_show, gpio_edge_store);
|
||||
|
||||
static int sysfs_set_active_low(struct gpio_desc *desc, struct device *dev,
|
||||
int value)
|
||||
{
|
||||
int status = 0;
|
||||
|
||||
if (!!test_bit(FLAG_ACTIVE_LOW, &desc->flags) == !!value)
|
||||
return 0;
|
||||
|
||||
if (value)
|
||||
set_bit(FLAG_ACTIVE_LOW, &desc->flags);
|
||||
else
|
||||
clear_bit(FLAG_ACTIVE_LOW, &desc->flags);
|
||||
|
||||
/* reconfigure poll(2) support if enabled on one edge only */
|
||||
if (dev != NULL && (!!test_bit(FLAG_TRIG_RISE, &desc->flags) ^
|
||||
!!test_bit(FLAG_TRIG_FALL, &desc->flags))) {
|
||||
unsigned long trigger_flags = desc->flags & GPIO_TRIGGER_MASK;
|
||||
|
||||
gpio_setup_irq(desc, dev, 0);
|
||||
status = gpio_setup_irq(desc, dev, trigger_flags);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static ssize_t gpio_active_low_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
const struct gpio_desc *desc = dev_get_drvdata(dev);
|
||||
ssize_t status;
|
||||
|
||||
mutex_lock(&sysfs_lock);
|
||||
|
||||
if (!test_bit(FLAG_EXPORT, &desc->flags))
|
||||
status = -EIO;
|
||||
else
|
||||
status = sprintf(buf, "%d\n",
|
||||
!!test_bit(FLAG_ACTIVE_LOW, &desc->flags));
|
||||
|
||||
mutex_unlock(&sysfs_lock);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static ssize_t gpio_active_low_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct gpio_desc *desc = dev_get_drvdata(dev);
|
||||
ssize_t status;
|
||||
|
||||
mutex_lock(&sysfs_lock);
|
||||
|
||||
if (!test_bit(FLAG_EXPORT, &desc->flags)) {
|
||||
status = -EIO;
|
||||
} else {
|
||||
long value;
|
||||
|
||||
status = strict_strtol(buf, 0, &value);
|
||||
if (status == 0)
|
||||
status = sysfs_set_active_low(desc, dev, value != 0);
|
||||
}
|
||||
|
||||
mutex_unlock(&sysfs_lock);
|
||||
|
||||
return status ? : size;
|
||||
}
|
||||
|
||||
static const DEVICE_ATTR(active_low, 0644,
|
||||
gpio_active_low_show, gpio_active_low_store);
|
||||
|
||||
static const struct attribute *gpio_attrs[] = {
|
||||
&dev_attr_direction.attr,
|
||||
&dev_attr_value.attr,
|
||||
&dev_attr_active_low.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
@ -662,12 +749,12 @@ int gpio_export(unsigned gpio, bool direction_may_change)
|
||||
dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0),
|
||||
desc, ioname ? ioname : "gpio%d", gpio);
|
||||
if (!IS_ERR(dev)) {
|
||||
if (direction_may_change)
|
||||
status = sysfs_create_group(&dev->kobj,
|
||||
status = sysfs_create_group(&dev->kobj,
|
||||
&gpio_attr_group);
|
||||
else
|
||||
|
||||
if (!status && direction_may_change)
|
||||
status = device_create_file(dev,
|
||||
&dev_attr_value);
|
||||
&dev_attr_direction);
|
||||
|
||||
if (!status && gpio_to_irq(gpio) >= 0
|
||||
&& (direction_may_change
|
||||
@ -744,6 +831,55 @@ done:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpio_export_link);
|
||||
|
||||
|
||||
/**
|
||||
* gpio_sysfs_set_active_low - set the polarity of gpio sysfs value
|
||||
* @gpio: gpio to change
|
||||
* @value: non-zero to use active low, i.e. inverted values
|
||||
*
|
||||
* Set the polarity of /sys/class/gpio/gpioN/value sysfs attribute.
|
||||
* The GPIO does not have to be exported yet. If poll(2) support has
|
||||
* been enabled for either rising or falling edge, it will be
|
||||
* reconfigured to follow the new polarity.
|
||||
*
|
||||
* Returns zero on success, else an error.
|
||||
*/
|
||||
int gpio_sysfs_set_active_low(unsigned gpio, int value)
|
||||
{
|
||||
struct gpio_desc *desc;
|
||||
struct device *dev = NULL;
|
||||
int status = -EINVAL;
|
||||
|
||||
if (!gpio_is_valid(gpio))
|
||||
goto done;
|
||||
|
||||
mutex_lock(&sysfs_lock);
|
||||
|
||||
desc = &gpio_desc[gpio];
|
||||
|
||||
if (test_bit(FLAG_EXPORT, &desc->flags)) {
|
||||
struct device *dev;
|
||||
|
||||
dev = class_find_device(&gpio_class, NULL, desc, match_export);
|
||||
if (dev == NULL) {
|
||||
status = -ENODEV;
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
|
||||
status = sysfs_set_active_low(desc, dev, value);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&sysfs_lock);
|
||||
|
||||
done:
|
||||
if (status)
|
||||
pr_debug("%s: gpio%d status %d\n", __func__, gpio, status);
|
||||
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpio_sysfs_set_active_low);
|
||||
|
||||
/**
|
||||
* gpio_unexport - reverse effect of gpio_export()
|
||||
* @gpio: gpio to make unavailable
|
||||
@ -1094,6 +1230,7 @@ void gpio_free(unsigned gpio)
|
||||
}
|
||||
desc_set_label(desc, NULL);
|
||||
module_put(desc->chip->owner);
|
||||
clear_bit(FLAG_ACTIVE_LOW, &desc->flags);
|
||||
clear_bit(FLAG_REQUESTED, &desc->flags);
|
||||
} else
|
||||
WARN_ON(extra_checks);
|
||||
|
@ -123,7 +123,7 @@ static int lnw_irq_type(unsigned irq, unsigned type)
|
||||
void __iomem *grer = (void __iomem *)(&lnw->reg_base->GRER[reg]);
|
||||
void __iomem *gfer = (void __iomem *)(&lnw->reg_base->GFER[reg]);
|
||||
|
||||
if (gpio < 0 || gpio > lnw->chip.ngpio)
|
||||
if (gpio >= lnw->chip.ngpio)
|
||||
return -EINVAL;
|
||||
spin_lock_irqsave(&lnw->lock, flags);
|
||||
if (type & IRQ_TYPE_EDGE_RISING)
|
||||
|
342
drivers/gpio/timbgpio.c
Normal file
342
drivers/gpio/timbgpio.c
Normal file
@ -0,0 +1,342 @@
|
||||
/*
|
||||
* timbgpio.c timberdale FPGA GPIO driver
|
||||
* Copyright (c) 2009 Intel Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/* Supports:
|
||||
* Timberdale FPGA GPIO
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/timb_gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#define DRIVER_NAME "timb-gpio"
|
||||
|
||||
#define TGPIOVAL 0x00
|
||||
#define TGPIODIR 0x04
|
||||
#define TGPIO_IER 0x08
|
||||
#define TGPIO_ISR 0x0c
|
||||
#define TGPIO_IPR 0x10
|
||||
#define TGPIO_ICR 0x14
|
||||
#define TGPIO_FLR 0x18
|
||||
#define TGPIO_LVR 0x1c
|
||||
|
||||
struct timbgpio {
|
||||
void __iomem *membase;
|
||||
spinlock_t lock; /* mutual exclusion */
|
||||
struct gpio_chip gpio;
|
||||
int irq_base;
|
||||
};
|
||||
|
||||
static int timbgpio_update_bit(struct gpio_chip *gpio, unsigned index,
|
||||
unsigned offset, bool enabled)
|
||||
{
|
||||
struct timbgpio *tgpio = container_of(gpio, struct timbgpio, gpio);
|
||||
u32 reg;
|
||||
|
||||
spin_lock(&tgpio->lock);
|
||||
reg = ioread32(tgpio->membase + offset);
|
||||
|
||||
if (enabled)
|
||||
reg |= (1 << index);
|
||||
else
|
||||
reg &= ~(1 << index);
|
||||
|
||||
iowrite32(reg, tgpio->membase + offset);
|
||||
spin_unlock(&tgpio->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int timbgpio_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
|
||||
{
|
||||
return timbgpio_update_bit(gpio, nr, TGPIODIR, true);
|
||||
}
|
||||
|
||||
static int timbgpio_gpio_get(struct gpio_chip *gpio, unsigned nr)
|
||||
{
|
||||
struct timbgpio *tgpio = container_of(gpio, struct timbgpio, gpio);
|
||||
u32 value;
|
||||
|
||||
value = ioread32(tgpio->membase + TGPIOVAL);
|
||||
return (value & (1 << nr)) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int timbgpio_gpio_direction_output(struct gpio_chip *gpio,
|
||||
unsigned nr, int val)
|
||||
{
|
||||
return timbgpio_update_bit(gpio, nr, TGPIODIR, false);
|
||||
}
|
||||
|
||||
static void timbgpio_gpio_set(struct gpio_chip *gpio,
|
||||
unsigned nr, int val)
|
||||
{
|
||||
timbgpio_update_bit(gpio, nr, TGPIOVAL, val != 0);
|
||||
}
|
||||
|
||||
static int timbgpio_to_irq(struct gpio_chip *gpio, unsigned offset)
|
||||
{
|
||||
struct timbgpio *tgpio = container_of(gpio, struct timbgpio, gpio);
|
||||
|
||||
if (tgpio->irq_base <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
return tgpio->irq_base + offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* GPIO IRQ
|
||||
*/
|
||||
static void timbgpio_irq_disable(unsigned irq)
|
||||
{
|
||||
struct timbgpio *tgpio = get_irq_chip_data(irq);
|
||||
int offset = irq - tgpio->irq_base;
|
||||
|
||||
timbgpio_update_bit(&tgpio->gpio, offset, TGPIO_IER, 0);
|
||||
}
|
||||
|
||||
static void timbgpio_irq_enable(unsigned irq)
|
||||
{
|
||||
struct timbgpio *tgpio = get_irq_chip_data(irq);
|
||||
int offset = irq - tgpio->irq_base;
|
||||
|
||||
timbgpio_update_bit(&tgpio->gpio, offset, TGPIO_IER, 1);
|
||||
}
|
||||
|
||||
static int timbgpio_irq_type(unsigned irq, unsigned trigger)
|
||||
{
|
||||
struct timbgpio *tgpio = get_irq_chip_data(irq);
|
||||
int offset = irq - tgpio->irq_base;
|
||||
unsigned long flags;
|
||||
u32 lvr, flr;
|
||||
|
||||
if (offset < 0 || offset > tgpio->gpio.ngpio)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&tgpio->lock, flags);
|
||||
|
||||
lvr = ioread32(tgpio->membase + TGPIO_LVR);
|
||||
flr = ioread32(tgpio->membase + TGPIO_FLR);
|
||||
|
||||
if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
|
||||
flr &= ~(1 << offset);
|
||||
if (trigger & IRQ_TYPE_LEVEL_HIGH)
|
||||
lvr |= 1 << offset;
|
||||
else
|
||||
lvr &= ~(1 << offset);
|
||||
}
|
||||
|
||||
if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
|
||||
return -EINVAL;
|
||||
else {
|
||||
flr |= 1 << offset;
|
||||
/* opposite compared to the datasheet, but it mirrors the
|
||||
* reality
|
||||
*/
|
||||
if (trigger & IRQ_TYPE_EDGE_FALLING)
|
||||
lvr |= 1 << offset;
|
||||
else
|
||||
lvr &= ~(1 << offset);
|
||||
}
|
||||
|
||||
iowrite32(lvr, tgpio->membase + TGPIO_LVR);
|
||||
iowrite32(flr, tgpio->membase + TGPIO_FLR);
|
||||
iowrite32(1 << offset, tgpio->membase + TGPIO_ICR);
|
||||
spin_unlock_irqrestore(&tgpio->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void timbgpio_irq(unsigned int irq, struct irq_desc *desc)
|
||||
{
|
||||
struct timbgpio *tgpio = get_irq_data(irq);
|
||||
unsigned long ipr;
|
||||
int offset;
|
||||
|
||||
desc->chip->ack(irq);
|
||||
ipr = ioread32(tgpio->membase + TGPIO_IPR);
|
||||
iowrite32(ipr, tgpio->membase + TGPIO_ICR);
|
||||
|
||||
for_each_bit(offset, &ipr, tgpio->gpio.ngpio)
|
||||
generic_handle_irq(timbgpio_to_irq(&tgpio->gpio, offset));
|
||||
}
|
||||
|
||||
static struct irq_chip timbgpio_irqchip = {
|
||||
.name = "GPIO",
|
||||
.enable = timbgpio_irq_enable,
|
||||
.disable = timbgpio_irq_disable,
|
||||
.set_type = timbgpio_irq_type,
|
||||
};
|
||||
|
||||
static int __devinit timbgpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
int err, i;
|
||||
struct gpio_chip *gc;
|
||||
struct timbgpio *tgpio;
|
||||
struct resource *iomem;
|
||||
struct timbgpio_platform_data *pdata = pdev->dev.platform_data;
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
|
||||
if (!pdata || pdata->nr_pins > 32) {
|
||||
err = -EINVAL;
|
||||
goto err_mem;
|
||||
}
|
||||
|
||||
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!iomem) {
|
||||
err = -EINVAL;
|
||||
goto err_mem;
|
||||
}
|
||||
|
||||
tgpio = kzalloc(sizeof(*tgpio), GFP_KERNEL);
|
||||
if (!tgpio) {
|
||||
err = -EINVAL;
|
||||
goto err_mem;
|
||||
}
|
||||
tgpio->irq_base = pdata->irq_base;
|
||||
|
||||
spin_lock_init(&tgpio->lock);
|
||||
|
||||
if (!request_mem_region(iomem->start, resource_size(iomem),
|
||||
DRIVER_NAME)) {
|
||||
err = -EBUSY;
|
||||
goto err_request;
|
||||
}
|
||||
|
||||
tgpio->membase = ioremap(iomem->start, resource_size(iomem));
|
||||
if (!tgpio->membase) {
|
||||
err = -ENOMEM;
|
||||
goto err_ioremap;
|
||||
}
|
||||
|
||||
gc = &tgpio->gpio;
|
||||
|
||||
gc->label = dev_name(&pdev->dev);
|
||||
gc->owner = THIS_MODULE;
|
||||
gc->dev = &pdev->dev;
|
||||
gc->direction_input = timbgpio_gpio_direction_input;
|
||||
gc->get = timbgpio_gpio_get;
|
||||
gc->direction_output = timbgpio_gpio_direction_output;
|
||||
gc->set = timbgpio_gpio_set;
|
||||
gc->to_irq = (irq >= 0 && tgpio->irq_base > 0) ? timbgpio_to_irq : NULL;
|
||||
gc->dbg_show = NULL;
|
||||
gc->base = pdata->gpio_base;
|
||||
gc->ngpio = pdata->nr_pins;
|
||||
gc->can_sleep = 0;
|
||||
|
||||
err = gpiochip_add(gc);
|
||||
if (err)
|
||||
goto err_chipadd;
|
||||
|
||||
platform_set_drvdata(pdev, tgpio);
|
||||
|
||||
/* make sure to disable interrupts */
|
||||
iowrite32(0x0, tgpio->membase + TGPIO_IER);
|
||||
|
||||
if (irq < 0 || tgpio->irq_base <= 0)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < pdata->nr_pins; i++) {
|
||||
set_irq_chip_and_handler_name(tgpio->irq_base + i,
|
||||
&timbgpio_irqchip, handle_simple_irq, "mux");
|
||||
set_irq_chip_data(tgpio->irq_base + i, tgpio);
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(tgpio->irq_base + i, IRQF_VALID | IRQF_PROBE);
|
||||
#endif
|
||||
}
|
||||
|
||||
set_irq_data(irq, tgpio);
|
||||
set_irq_chained_handler(irq, timbgpio_irq);
|
||||
|
||||
return 0;
|
||||
|
||||
err_chipadd:
|
||||
iounmap(tgpio->membase);
|
||||
err_ioremap:
|
||||
release_mem_region(iomem->start, resource_size(iomem));
|
||||
err_request:
|
||||
kfree(tgpio);
|
||||
err_mem:
|
||||
printk(KERN_ERR DRIVER_NAME": Failed to register GPIOs: %d\n", err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devexit timbgpio_remove(struct platform_device *pdev)
|
||||
{
|
||||
int err;
|
||||
struct timbgpio_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct timbgpio *tgpio = platform_get_drvdata(pdev);
|
||||
struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
|
||||
if (irq >= 0 && tgpio->irq_base > 0) {
|
||||
int i;
|
||||
for (i = 0; i < pdata->nr_pins; i++) {
|
||||
set_irq_chip(tgpio->irq_base + i, NULL);
|
||||
set_irq_chip_data(tgpio->irq_base + i, NULL);
|
||||
}
|
||||
|
||||
set_irq_handler(irq, NULL);
|
||||
set_irq_data(irq, NULL);
|
||||
}
|
||||
|
||||
err = gpiochip_remove(&tgpio->gpio);
|
||||
if (err)
|
||||
printk(KERN_ERR DRIVER_NAME": failed to remove gpio_chip\n");
|
||||
|
||||
iounmap(tgpio->membase);
|
||||
release_mem_region(iomem->start, resource_size(iomem));
|
||||
kfree(tgpio);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver timbgpio_platform_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = timbgpio_probe,
|
||||
.remove = timbgpio_remove,
|
||||
};
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
static int __init timbgpio_init(void)
|
||||
{
|
||||
return platform_driver_register(&timbgpio_platform_driver);
|
||||
}
|
||||
|
||||
static void __exit timbgpio_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&timbgpio_platform_driver);
|
||||
}
|
||||
|
||||
module_init(timbgpio_init);
|
||||
module_exit(timbgpio_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Timberdale GPIO driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Mocean Laboratories");
|
||||
MODULE_ALIAS("platform:"DRIVER_NAME);
|
||||
|
@ -53,6 +53,17 @@ struct gru_chiplet_info {
|
||||
int free_user_cbr;
|
||||
};
|
||||
|
||||
/*
|
||||
* Statictics kept for each context.
|
||||
*/
|
||||
struct gru_gseg_statistics {
|
||||
unsigned long fmm_tlbmiss;
|
||||
unsigned long upm_tlbmiss;
|
||||
unsigned long tlbdropin;
|
||||
unsigned long context_stolen;
|
||||
unsigned long reserved[10];
|
||||
};
|
||||
|
||||
/* Flags for GRU options on the gru_create_context() call */
|
||||
/* Select one of the follow 4 options to specify how TLB misses are handled */
|
||||
#define GRU_OPT_MISS_DEFAULT 0x0000 /* Use default mode */
|
||||
|
@ -34,17 +34,17 @@ extern void gru_wait_abort_proc(void *cb);
|
||||
#include <asm/intrinsics.h>
|
||||
#define __flush_cache(p) ia64_fc((unsigned long)p)
|
||||
/* Use volatile on IA64 to ensure ordering via st4.rel */
|
||||
#define gru_ordered_store_int(p, v) \
|
||||
#define gru_ordered_store_ulong(p, v) \
|
||||
do { \
|
||||
barrier(); \
|
||||
*((volatile int *)(p)) = v; /* force st.rel */ \
|
||||
*((volatile unsigned long *)(p)) = v; /* force st.rel */ \
|
||||
} while (0)
|
||||
#elif defined(CONFIG_X86_64)
|
||||
#define __flush_cache(p) clflush(p)
|
||||
#define gru_ordered_store_int(p, v) \
|
||||
#define gru_ordered_store_ulong(p, v) \
|
||||
do { \
|
||||
barrier(); \
|
||||
*(int *)p = v; \
|
||||
*(unsigned long *)p = v; \
|
||||
} while (0)
|
||||
#else
|
||||
#error "Unsupported architecture"
|
||||
@ -129,8 +129,13 @@ struct gru_instruction_bits {
|
||||
*/
|
||||
struct gru_instruction {
|
||||
/* DW 0 */
|
||||
unsigned int op32; /* icmd,xtype,iaa0,ima,opc */
|
||||
unsigned int tri0;
|
||||
union {
|
||||
unsigned long op64; /* icmd,xtype,iaa0,ima,opc,tri0 */
|
||||
struct {
|
||||
unsigned int op32;
|
||||
unsigned int tri0;
|
||||
};
|
||||
};
|
||||
unsigned long tri1_bufsize; /* DW 1 */
|
||||
unsigned long baddr0; /* DW 2 */
|
||||
unsigned long nelem; /* DW 3 */
|
||||
@ -140,7 +145,7 @@ struct gru_instruction {
|
||||
unsigned long avalue; /* DW 7 */
|
||||
};
|
||||
|
||||
/* Some shifts and masks for the low 32 bits of a GRU command */
|
||||
/* Some shifts and masks for the low 64 bits of a GRU command */
|
||||
#define GRU_CB_ICMD_SHFT 0
|
||||
#define GRU_CB_ICMD_MASK 0x1
|
||||
#define GRU_CB_XTYPE_SHFT 8
|
||||
@ -155,6 +160,10 @@ struct gru_instruction {
|
||||
#define GRU_CB_OPC_MASK 0xff
|
||||
#define GRU_CB_EXOPC_SHFT 24
|
||||
#define GRU_CB_EXOPC_MASK 0xff
|
||||
#define GRU_IDEF2_SHFT 32
|
||||
#define GRU_IDEF2_MASK 0x3ffff
|
||||
#define GRU_ISTATUS_SHFT 56
|
||||
#define GRU_ISTATUS_MASK 0x3
|
||||
|
||||
/* GRU instruction opcodes (opc field) */
|
||||
#define OP_NOP 0x00
|
||||
@ -256,6 +265,7 @@ struct gru_instruction {
|
||||
#define CBE_CAUSE_PROTOCOL_STATE_DATA_ERROR (1 << 16)
|
||||
#define CBE_CAUSE_RA_RESPONSE_DATA_ERROR (1 << 17)
|
||||
#define CBE_CAUSE_HA_RESPONSE_DATA_ERROR (1 << 18)
|
||||
#define CBE_CAUSE_FORCED_ERROR (1 << 19)
|
||||
|
||||
/* CBE cbrexecstatus bits */
|
||||
#define CBR_EXS_ABORT_OCC_BIT 0
|
||||
@ -264,13 +274,15 @@ struct gru_instruction {
|
||||
#define CBR_EXS_QUEUED_BIT 3
|
||||
#define CBR_EXS_TLB_INVAL_BIT 4
|
||||
#define CBR_EXS_EXCEPTION_BIT 5
|
||||
#define CBR_EXS_CB_INT_PENDING_BIT 6
|
||||
|
||||
#define CBR_EXS_ABORT_OCC (1 << CBR_EXS_ABORT_OCC_BIT)
|
||||
#define CBR_EXS_INT_OCC (1 << CBR_EXS_INT_OCC_BIT)
|
||||
#define CBR_EXS_PENDING (1 << CBR_EXS_PENDING_BIT)
|
||||
#define CBR_EXS_QUEUED (1 << CBR_EXS_QUEUED_BIT)
|
||||
#define CBR_TLB_INVAL (1 << CBR_EXS_TLB_INVAL_BIT)
|
||||
#define CBR_EXS_TLB_INVAL (1 << CBR_EXS_TLB_INVAL_BIT)
|
||||
#define CBR_EXS_EXCEPTION (1 << CBR_EXS_EXCEPTION_BIT)
|
||||
#define CBR_EXS_CB_INT_PENDING (1 << CBR_EXS_CB_INT_PENDING_BIT)
|
||||
|
||||
/*
|
||||
* Exceptions are retried for the following cases. If any OTHER bits are set
|
||||
@ -296,12 +308,14 @@ union gru_mesqhead {
|
||||
|
||||
|
||||
/* Generate the low word of a GRU instruction */
|
||||
static inline unsigned int
|
||||
__opword(unsigned char opcode, unsigned char exopc, unsigned char xtype,
|
||||
static inline unsigned long
|
||||
__opdword(unsigned char opcode, unsigned char exopc, unsigned char xtype,
|
||||
unsigned char iaa0, unsigned char iaa1,
|
||||
unsigned char ima)
|
||||
unsigned long idef2, unsigned char ima)
|
||||
{
|
||||
return (1 << GRU_CB_ICMD_SHFT) |
|
||||
((unsigned long)CBS_ACTIVE << GRU_ISTATUS_SHFT) |
|
||||
(idef2<< GRU_IDEF2_SHFT) |
|
||||
(iaa0 << GRU_CB_IAA0_SHFT) |
|
||||
(iaa1 << GRU_CB_IAA1_SHFT) |
|
||||
(ima << GRU_CB_IMA_SHFT) |
|
||||
@ -319,12 +333,13 @@ static inline void gru_flush_cache(void *p)
|
||||
}
|
||||
|
||||
/*
|
||||
* Store the lower 32 bits of the command including the "start" bit. Then
|
||||
* Store the lower 64 bits of the command including the "start" bit. Then
|
||||
* start the instruction executing.
|
||||
*/
|
||||
static inline void gru_start_instruction(struct gru_instruction *ins, int op32)
|
||||
static inline void gru_start_instruction(struct gru_instruction *ins, unsigned long op64)
|
||||
{
|
||||
gru_ordered_store_int(ins, op32);
|
||||
gru_ordered_store_ulong(ins, op64);
|
||||
mb();
|
||||
gru_flush_cache(ins);
|
||||
}
|
||||
|
||||
@ -340,6 +355,30 @@ static inline void gru_start_instruction(struct gru_instruction *ins, int op32)
|
||||
* - nelem and stride are in elements
|
||||
* - tri0/tri1 is in bytes for the beginning of the data segment.
|
||||
*/
|
||||
static inline void gru_vload_phys(void *cb, unsigned long gpa,
|
||||
unsigned int tri0, int iaa, unsigned long hints)
|
||||
{
|
||||
struct gru_instruction *ins = (struct gru_instruction *)cb;
|
||||
|
||||
ins->baddr0 = (long)gpa | ((unsigned long)iaa << 62);
|
||||
ins->nelem = 1;
|
||||
ins->op1_stride = 1;
|
||||
gru_start_instruction(ins, __opdword(OP_VLOAD, 0, XTYPE_DW, iaa, 0,
|
||||
(unsigned long)tri0, CB_IMA(hints)));
|
||||
}
|
||||
|
||||
static inline void gru_vstore_phys(void *cb, unsigned long gpa,
|
||||
unsigned int tri0, int iaa, unsigned long hints)
|
||||
{
|
||||
struct gru_instruction *ins = (struct gru_instruction *)cb;
|
||||
|
||||
ins->baddr0 = (long)gpa | ((unsigned long)iaa << 62);
|
||||
ins->nelem = 1;
|
||||
ins->op1_stride = 1;
|
||||
gru_start_instruction(ins, __opdword(OP_VSTORE, 0, XTYPE_DW, iaa, 0,
|
||||
(unsigned long)tri0, CB_IMA(hints)));
|
||||
}
|
||||
|
||||
static inline void gru_vload(void *cb, unsigned long mem_addr,
|
||||
unsigned int tri0, unsigned char xtype, unsigned long nelem,
|
||||
unsigned long stride, unsigned long hints)
|
||||
@ -348,10 +387,9 @@ static inline void gru_vload(void *cb, unsigned long mem_addr,
|
||||
|
||||
ins->baddr0 = (long)mem_addr;
|
||||
ins->nelem = nelem;
|
||||
ins->tri0 = tri0;
|
||||
ins->op1_stride = stride;
|
||||
gru_start_instruction(ins, __opword(OP_VLOAD, 0, xtype, IAA_RAM, 0,
|
||||
CB_IMA(hints)));
|
||||
gru_start_instruction(ins, __opdword(OP_VLOAD, 0, xtype, IAA_RAM, 0,
|
||||
(unsigned long)tri0, CB_IMA(hints)));
|
||||
}
|
||||
|
||||
static inline void gru_vstore(void *cb, unsigned long mem_addr,
|
||||
@ -362,10 +400,9 @@ static inline void gru_vstore(void *cb, unsigned long mem_addr,
|
||||
|
||||
ins->baddr0 = (long)mem_addr;
|
||||
ins->nelem = nelem;
|
||||
ins->tri0 = tri0;
|
||||
ins->op1_stride = stride;
|
||||
gru_start_instruction(ins, __opword(OP_VSTORE, 0, xtype, IAA_RAM, 0,
|
||||
CB_IMA(hints)));
|
||||
gru_start_instruction(ins, __opdword(OP_VSTORE, 0, xtype, IAA_RAM, 0,
|
||||
tri0, CB_IMA(hints)));
|
||||
}
|
||||
|
||||
static inline void gru_ivload(void *cb, unsigned long mem_addr,
|
||||
@ -376,10 +413,9 @@ static inline void gru_ivload(void *cb, unsigned long mem_addr,
|
||||
|
||||
ins->baddr0 = (long)mem_addr;
|
||||
ins->nelem = nelem;
|
||||
ins->tri0 = tri0;
|
||||
ins->tri1_bufsize = tri1;
|
||||
gru_start_instruction(ins, __opword(OP_IVLOAD, 0, xtype, IAA_RAM, 0,
|
||||
CB_IMA(hints)));
|
||||
gru_start_instruction(ins, __opdword(OP_IVLOAD, 0, xtype, IAA_RAM, 0,
|
||||
tri0, CB_IMA(hints)));
|
||||
}
|
||||
|
||||
static inline void gru_ivstore(void *cb, unsigned long mem_addr,
|
||||
@ -390,10 +426,9 @@ static inline void gru_ivstore(void *cb, unsigned long mem_addr,
|
||||
|
||||
ins->baddr0 = (long)mem_addr;
|
||||
ins->nelem = nelem;
|
||||
ins->tri0 = tri0;
|
||||
ins->tri1_bufsize = tri1;
|
||||
gru_start_instruction(ins, __opword(OP_IVSTORE, 0, xtype, IAA_RAM, 0,
|
||||
CB_IMA(hints)));
|
||||
gru_start_instruction(ins, __opdword(OP_IVSTORE, 0, xtype, IAA_RAM, 0,
|
||||
tri0, CB_IMA(hints)));
|
||||
}
|
||||
|
||||
static inline void gru_vset(void *cb, unsigned long mem_addr,
|
||||
@ -406,8 +441,8 @@ static inline void gru_vset(void *cb, unsigned long mem_addr,
|
||||
ins->op2_value_baddr1 = value;
|
||||
ins->nelem = nelem;
|
||||
ins->op1_stride = stride;
|
||||
gru_start_instruction(ins, __opword(OP_VSET, 0, xtype, IAA_RAM, 0,
|
||||
CB_IMA(hints)));
|
||||
gru_start_instruction(ins, __opdword(OP_VSET, 0, xtype, IAA_RAM, 0,
|
||||
0, CB_IMA(hints)));
|
||||
}
|
||||
|
||||
static inline void gru_ivset(void *cb, unsigned long mem_addr,
|
||||
@ -420,8 +455,8 @@ static inline void gru_ivset(void *cb, unsigned long mem_addr,
|
||||
ins->op2_value_baddr1 = value;
|
||||
ins->nelem = nelem;
|
||||
ins->tri1_bufsize = tri1;
|
||||
gru_start_instruction(ins, __opword(OP_IVSET, 0, xtype, IAA_RAM, 0,
|
||||
CB_IMA(hints)));
|
||||
gru_start_instruction(ins, __opdword(OP_IVSET, 0, xtype, IAA_RAM, 0,
|
||||
0, CB_IMA(hints)));
|
||||
}
|
||||
|
||||
static inline void gru_vflush(void *cb, unsigned long mem_addr,
|
||||
@ -433,15 +468,15 @@ static inline void gru_vflush(void *cb, unsigned long mem_addr,
|
||||
ins->baddr0 = (long)mem_addr;
|
||||
ins->op1_stride = stride;
|
||||
ins->nelem = nelem;
|
||||
gru_start_instruction(ins, __opword(OP_VFLUSH, 0, xtype, IAA_RAM, 0,
|
||||
CB_IMA(hints)));
|
||||
gru_start_instruction(ins, __opdword(OP_VFLUSH, 0, xtype, IAA_RAM, 0,
|
||||
0, CB_IMA(hints)));
|
||||
}
|
||||
|
||||
static inline void gru_nop(void *cb, int hints)
|
||||
{
|
||||
struct gru_instruction *ins = (void *)cb;
|
||||
|
||||
gru_start_instruction(ins, __opword(OP_NOP, 0, 0, 0, 0, CB_IMA(hints)));
|
||||
gru_start_instruction(ins, __opdword(OP_NOP, 0, 0, 0, 0, 0, CB_IMA(hints)));
|
||||
}
|
||||
|
||||
|
||||
@ -455,10 +490,9 @@ static inline void gru_bcopy(void *cb, const unsigned long src,
|
||||
ins->baddr0 = (long)src;
|
||||
ins->op2_value_baddr1 = (long)dest;
|
||||
ins->nelem = nelem;
|
||||
ins->tri0 = tri0;
|
||||
ins->tri1_bufsize = bufsize;
|
||||
gru_start_instruction(ins, __opword(OP_BCOPY, 0, xtype, IAA_RAM,
|
||||
IAA_RAM, CB_IMA(hints)));
|
||||
gru_start_instruction(ins, __opdword(OP_BCOPY, 0, xtype, IAA_RAM,
|
||||
IAA_RAM, tri0, CB_IMA(hints)));
|
||||
}
|
||||
|
||||
static inline void gru_bstore(void *cb, const unsigned long src,
|
||||
@ -470,9 +504,8 @@ static inline void gru_bstore(void *cb, const unsigned long src,
|
||||
ins->baddr0 = (long)src;
|
||||
ins->op2_value_baddr1 = (long)dest;
|
||||
ins->nelem = nelem;
|
||||
ins->tri0 = tri0;
|
||||
gru_start_instruction(ins, __opword(OP_BSTORE, 0, xtype, 0, IAA_RAM,
|
||||
CB_IMA(hints)));
|
||||
gru_start_instruction(ins, __opdword(OP_BSTORE, 0, xtype, 0, IAA_RAM,
|
||||
tri0, CB_IMA(hints)));
|
||||
}
|
||||
|
||||
static inline void gru_gamir(void *cb, int exopc, unsigned long src,
|
||||
@ -481,8 +514,8 @@ static inline void gru_gamir(void *cb, int exopc, unsigned long src,
|
||||
struct gru_instruction *ins = (void *)cb;
|
||||
|
||||
ins->baddr0 = (long)src;
|
||||
gru_start_instruction(ins, __opword(OP_GAMIR, exopc, xtype, IAA_RAM, 0,
|
||||
CB_IMA(hints)));
|
||||
gru_start_instruction(ins, __opdword(OP_GAMIR, exopc, xtype, IAA_RAM, 0,
|
||||
0, CB_IMA(hints)));
|
||||
}
|
||||
|
||||
static inline void gru_gamirr(void *cb, int exopc, unsigned long src,
|
||||
@ -491,8 +524,8 @@ static inline void gru_gamirr(void *cb, int exopc, unsigned long src,
|
||||
struct gru_instruction *ins = (void *)cb;
|
||||
|
||||
ins->baddr0 = (long)src;
|
||||
gru_start_instruction(ins, __opword(OP_GAMIRR, exopc, xtype, IAA_RAM, 0,
|
||||
CB_IMA(hints)));
|
||||
gru_start_instruction(ins, __opdword(OP_GAMIRR, exopc, xtype, IAA_RAM, 0,
|
||||
0, CB_IMA(hints)));
|
||||
}
|
||||
|
||||
static inline void gru_gamer(void *cb, int exopc, unsigned long src,
|
||||
@ -505,8 +538,8 @@ static inline void gru_gamer(void *cb, int exopc, unsigned long src,
|
||||
ins->baddr0 = (long)src;
|
||||
ins->op1_stride = operand1;
|
||||
ins->op2_value_baddr1 = operand2;
|
||||
gru_start_instruction(ins, __opword(OP_GAMER, exopc, xtype, IAA_RAM, 0,
|
||||
CB_IMA(hints)));
|
||||
gru_start_instruction(ins, __opdword(OP_GAMER, exopc, xtype, IAA_RAM, 0,
|
||||
0, CB_IMA(hints)));
|
||||
}
|
||||
|
||||
static inline void gru_gamerr(void *cb, int exopc, unsigned long src,
|
||||
@ -518,8 +551,8 @@ static inline void gru_gamerr(void *cb, int exopc, unsigned long src,
|
||||
ins->baddr0 = (long)src;
|
||||
ins->op1_stride = operand1;
|
||||
ins->op2_value_baddr1 = operand2;
|
||||
gru_start_instruction(ins, __opword(OP_GAMERR, exopc, xtype, IAA_RAM, 0,
|
||||
CB_IMA(hints)));
|
||||
gru_start_instruction(ins, __opdword(OP_GAMERR, exopc, xtype, IAA_RAM, 0,
|
||||
0, CB_IMA(hints)));
|
||||
}
|
||||
|
||||
static inline void gru_gamxr(void *cb, unsigned long src,
|
||||
@ -529,8 +562,8 @@ static inline void gru_gamxr(void *cb, unsigned long src,
|
||||
|
||||
ins->baddr0 = (long)src;
|
||||
ins->nelem = 4;
|
||||
gru_start_instruction(ins, __opword(OP_GAMXR, EOP_XR_CSWAP, XTYPE_DW,
|
||||
IAA_RAM, 0, CB_IMA(hints)));
|
||||
gru_start_instruction(ins, __opdword(OP_GAMXR, EOP_XR_CSWAP, XTYPE_DW,
|
||||
IAA_RAM, 0, 0, CB_IMA(hints)));
|
||||
}
|
||||
|
||||
static inline void gru_mesq(void *cb, unsigned long queue,
|
||||
@ -541,9 +574,8 @@ static inline void gru_mesq(void *cb, unsigned long queue,
|
||||
|
||||
ins->baddr0 = (long)queue;
|
||||
ins->nelem = nelem;
|
||||
ins->tri0 = tri0;
|
||||
gru_start_instruction(ins, __opword(OP_MESQ, 0, XTYPE_CL, IAA_RAM, 0,
|
||||
CB_IMA(hints)));
|
||||
gru_start_instruction(ins, __opdword(OP_MESQ, 0, XTYPE_CL, IAA_RAM, 0,
|
||||
tri0, CB_IMA(hints)));
|
||||
}
|
||||
|
||||
static inline unsigned long gru_get_amo_value(void *cb)
|
||||
@ -662,6 +694,14 @@ static inline void gru_wait_abort(void *cb)
|
||||
gru_wait_abort_proc(cb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a pointer to the start of a gseg
|
||||
* p - Any valid pointer within the gseg
|
||||
*/
|
||||
static inline void *gru_get_gseg_pointer (void *p)
|
||||
{
|
||||
return (void *)((unsigned long)p & ~(GRU_GSEG_PAGESIZE - 1));
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a pointer to a control block
|
||||
|
@ -40,6 +40,12 @@
|
||||
#include "gru_instructions.h"
|
||||
#include <asm/uv/uv_hub.h>
|
||||
|
||||
/* Return codes for vtop functions */
|
||||
#define VTOP_SUCCESS 0
|
||||
#define VTOP_INVALID -1
|
||||
#define VTOP_RETRY -2
|
||||
|
||||
|
||||
/*
|
||||
* Test if a physical address is a valid GRU GSEG address
|
||||
*/
|
||||
@ -90,19 +96,22 @@ static struct gru_thread_state *gru_alloc_locked_gts(unsigned long vaddr)
|
||||
{
|
||||
struct mm_struct *mm = current->mm;
|
||||
struct vm_area_struct *vma;
|
||||
struct gru_thread_state *gts = NULL;
|
||||
struct gru_thread_state *gts = ERR_PTR(-EINVAL);
|
||||
|
||||
down_write(&mm->mmap_sem);
|
||||
vma = gru_find_vma(vaddr);
|
||||
if (vma)
|
||||
gts = gru_alloc_thread_state(vma, TSID(vaddr, vma));
|
||||
if (gts) {
|
||||
mutex_lock(>s->ts_ctxlock);
|
||||
downgrade_write(&mm->mmap_sem);
|
||||
} else {
|
||||
up_write(&mm->mmap_sem);
|
||||
}
|
||||
if (!vma)
|
||||
goto err;
|
||||
|
||||
gts = gru_alloc_thread_state(vma, TSID(vaddr, vma));
|
||||
if (IS_ERR(gts))
|
||||
goto err;
|
||||
mutex_lock(>s->ts_ctxlock);
|
||||
downgrade_write(&mm->mmap_sem);
|
||||
return gts;
|
||||
|
||||
err:
|
||||
up_write(&mm->mmap_sem);
|
||||
return gts;
|
||||
}
|
||||
|
||||
@ -122,38 +131,14 @@ static void gru_unlock_gts(struct gru_thread_state *gts)
|
||||
* is necessary to prevent the user from seeing a stale cb.istatus that will
|
||||
* change as soon as the TFH restart is complete. Races may cause an
|
||||
* occasional failure to clear the cb.istatus, but that is ok.
|
||||
*
|
||||
* If the cb address is not valid (should not happen, but...), nothing
|
||||
* bad will happen.. The get_user()/put_user() will fail but there
|
||||
* are no bad side-effects.
|
||||
*/
|
||||
static void gru_cb_set_istatus_active(unsigned long __user *cb)
|
||||
static void gru_cb_set_istatus_active(struct gru_instruction_bits *cbk)
|
||||
{
|
||||
union {
|
||||
struct gru_instruction_bits bits;
|
||||
unsigned long dw;
|
||||
} u;
|
||||
|
||||
if (cb) {
|
||||
get_user(u.dw, cb);
|
||||
u.bits.istatus = CBS_ACTIVE;
|
||||
put_user(u.dw, cb);
|
||||
if (cbk) {
|
||||
cbk->istatus = CBS_ACTIVE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a interrupt IRQ to a pointer to the GRU GTS that caused the
|
||||
* interrupt. Interrupts are always sent to a cpu on the blade that contains the
|
||||
* GRU (except for headless blades which are not currently supported). A blade
|
||||
* has N grus; a block of N consecutive IRQs is assigned to the GRUs. The IRQ
|
||||
* number uniquely identifies the GRU chiplet on the local blade that caused the
|
||||
* interrupt. Always called in interrupt context.
|
||||
*/
|
||||
static inline struct gru_state *irq_to_gru(int irq)
|
||||
{
|
||||
return &gru_base[uv_numa_blade_id()]->bs_grus[irq - IRQ_GRU];
|
||||
}
|
||||
|
||||
/*
|
||||
* Read & clear a TFM
|
||||
*
|
||||
@ -207,10 +192,11 @@ static int non_atomic_pte_lookup(struct vm_area_struct *vma,
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
/* ZZZ Need to handle HUGE pages */
|
||||
if (is_vm_hugetlb_page(vma))
|
||||
return -EFAULT;
|
||||
#ifdef CONFIG_HUGETLB_PAGE
|
||||
*pageshift = is_vm_hugetlb_page(vma) ? HPAGE_SHIFT : PAGE_SHIFT;
|
||||
#else
|
||||
*pageshift = PAGE_SHIFT;
|
||||
#endif
|
||||
if (get_user_pages
|
||||
(current, current->mm, vaddr, 1, write, 0, &page, NULL) <= 0)
|
||||
return -EFAULT;
|
||||
@ -268,7 +254,6 @@ static int atomic_pte_lookup(struct vm_area_struct *vma, unsigned long vaddr,
|
||||
return 0;
|
||||
|
||||
err:
|
||||
local_irq_enable();
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -301,15 +286,70 @@ static int gru_vtop(struct gru_thread_state *gts, unsigned long vaddr,
|
||||
paddr = paddr & ~((1UL << ps) - 1);
|
||||
*gpa = uv_soc_phys_ram_to_gpa(paddr);
|
||||
*pageshift = ps;
|
||||
return 0;
|
||||
return VTOP_SUCCESS;
|
||||
|
||||
inval:
|
||||
return -1;
|
||||
return VTOP_INVALID;
|
||||
upm:
|
||||
return -2;
|
||||
return VTOP_RETRY;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Flush a CBE from cache. The CBE is clean in the cache. Dirty the
|
||||
* CBE cacheline so that the line will be written back to home agent.
|
||||
* Otherwise the line may be silently dropped. This has no impact
|
||||
* except on performance.
|
||||
*/
|
||||
static void gru_flush_cache_cbe(struct gru_control_block_extended *cbe)
|
||||
{
|
||||
if (unlikely(cbe)) {
|
||||
cbe->cbrexecstatus = 0; /* make CL dirty */
|
||||
gru_flush_cache(cbe);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Preload the TLB with entries that may be required. Currently, preloading
|
||||
* is implemented only for BCOPY. Preload <tlb_preload_count> pages OR to
|
||||
* the end of the bcopy tranfer, whichever is smaller.
|
||||
*/
|
||||
static void gru_preload_tlb(struct gru_state *gru,
|
||||
struct gru_thread_state *gts, int atomic,
|
||||
unsigned long fault_vaddr, int asid, int write,
|
||||
unsigned char tlb_preload_count,
|
||||
struct gru_tlb_fault_handle *tfh,
|
||||
struct gru_control_block_extended *cbe)
|
||||
{
|
||||
unsigned long vaddr = 0, gpa;
|
||||
int ret, pageshift;
|
||||
|
||||
if (cbe->opccpy != OP_BCOPY)
|
||||
return;
|
||||
|
||||
if (fault_vaddr == cbe->cbe_baddr0)
|
||||
vaddr = fault_vaddr + GRU_CACHE_LINE_BYTES * cbe->cbe_src_cl - 1;
|
||||
else if (fault_vaddr == cbe->cbe_baddr1)
|
||||
vaddr = fault_vaddr + (1 << cbe->xtypecpy) * cbe->cbe_nelemcur - 1;
|
||||
|
||||
fault_vaddr &= PAGE_MASK;
|
||||
vaddr &= PAGE_MASK;
|
||||
vaddr = min(vaddr, fault_vaddr + tlb_preload_count * PAGE_SIZE);
|
||||
|
||||
while (vaddr > fault_vaddr) {
|
||||
ret = gru_vtop(gts, vaddr, write, atomic, &gpa, &pageshift);
|
||||
if (ret || tfh_write_only(tfh, gpa, GAA_RAM, vaddr, asid, write,
|
||||
GRU_PAGESIZE(pageshift)))
|
||||
return;
|
||||
gru_dbg(grudev,
|
||||
"%s: gid %d, gts 0x%p, tfh 0x%p, vaddr 0x%lx, asid 0x%x, rw %d, ps %d, gpa 0x%lx\n",
|
||||
atomic ? "atomic" : "non-atomic", gru->gs_gid, gts, tfh,
|
||||
vaddr, asid, write, pageshift, gpa);
|
||||
vaddr -= PAGE_SIZE;
|
||||
STAT(tlb_preload_page);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Drop a TLB entry into the GRU. The fault is described by info in an TFH.
|
||||
* Input:
|
||||
@ -320,11 +360,14 @@ upm:
|
||||
* < 0 = error code
|
||||
*
|
||||
*/
|
||||
static int gru_try_dropin(struct gru_thread_state *gts,
|
||||
static int gru_try_dropin(struct gru_state *gru,
|
||||
struct gru_thread_state *gts,
|
||||
struct gru_tlb_fault_handle *tfh,
|
||||
unsigned long __user *cb)
|
||||
struct gru_instruction_bits *cbk)
|
||||
{
|
||||
int pageshift = 0, asid, write, ret, atomic = !cb;
|
||||
struct gru_control_block_extended *cbe = NULL;
|
||||
unsigned char tlb_preload_count = gts->ts_tlb_preload_count;
|
||||
int pageshift = 0, asid, write, ret, atomic = !cbk, indexway;
|
||||
unsigned long gpa = 0, vaddr = 0;
|
||||
|
||||
/*
|
||||
@ -334,6 +377,14 @@ static int gru_try_dropin(struct gru_thread_state *gts,
|
||||
* the dropin is ignored. This eliminates the need for additional locks.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Prefetch the CBE if doing TLB preloading
|
||||
*/
|
||||
if (unlikely(tlb_preload_count)) {
|
||||
cbe = gru_tfh_to_cbe(tfh);
|
||||
prefetchw(cbe);
|
||||
}
|
||||
|
||||
/*
|
||||
* Error if TFH state is IDLE or FMM mode & the user issuing a UPM call.
|
||||
* Might be a hardware race OR a stupid user. Ignore FMM because FMM
|
||||
@ -341,18 +392,20 @@ static int gru_try_dropin(struct gru_thread_state *gts,
|
||||
*/
|
||||
if (tfh->status != TFHSTATUS_EXCEPTION) {
|
||||
gru_flush_cache(tfh);
|
||||
sync_core();
|
||||
if (tfh->status != TFHSTATUS_EXCEPTION)
|
||||
goto failnoexception;
|
||||
STAT(tfh_stale_on_fault);
|
||||
}
|
||||
if (tfh->state == TFHSTATE_IDLE)
|
||||
goto failidle;
|
||||
if (tfh->state == TFHSTATE_MISS_FMM && cb)
|
||||
if (tfh->state == TFHSTATE_MISS_FMM && cbk)
|
||||
goto failfmm;
|
||||
|
||||
write = (tfh->cause & TFHCAUSE_TLB_MOD) != 0;
|
||||
vaddr = tfh->missvaddr;
|
||||
asid = tfh->missasid;
|
||||
indexway = tfh->indexway;
|
||||
if (asid == 0)
|
||||
goto failnoasid;
|
||||
|
||||
@ -366,41 +419,51 @@ static int gru_try_dropin(struct gru_thread_state *gts,
|
||||
goto failactive;
|
||||
|
||||
ret = gru_vtop(gts, vaddr, write, atomic, &gpa, &pageshift);
|
||||
if (ret == -1)
|
||||
if (ret == VTOP_INVALID)
|
||||
goto failinval;
|
||||
if (ret == -2)
|
||||
if (ret == VTOP_RETRY)
|
||||
goto failupm;
|
||||
|
||||
if (!(gts->ts_sizeavail & GRU_SIZEAVAIL(pageshift))) {
|
||||
gts->ts_sizeavail |= GRU_SIZEAVAIL(pageshift);
|
||||
if (atomic || !gru_update_cch(gts, 0)) {
|
||||
if (atomic || !gru_update_cch(gts)) {
|
||||
gts->ts_force_cch_reload = 1;
|
||||
goto failupm;
|
||||
}
|
||||
}
|
||||
gru_cb_set_istatus_active(cb);
|
||||
|
||||
if (unlikely(cbe) && pageshift == PAGE_SHIFT) {
|
||||
gru_preload_tlb(gru, gts, atomic, vaddr, asid, write, tlb_preload_count, tfh, cbe);
|
||||
gru_flush_cache_cbe(cbe);
|
||||
}
|
||||
|
||||
gru_cb_set_istatus_active(cbk);
|
||||
gts->ustats.tlbdropin++;
|
||||
tfh_write_restart(tfh, gpa, GAA_RAM, vaddr, asid, write,
|
||||
GRU_PAGESIZE(pageshift));
|
||||
STAT(tlb_dropin);
|
||||
gru_dbg(grudev,
|
||||
"%s: tfh 0x%p, vaddr 0x%lx, asid 0x%x, ps %d, gpa 0x%lx\n",
|
||||
ret ? "non-atomic" : "atomic", tfh, vaddr, asid,
|
||||
pageshift, gpa);
|
||||
"%s: gid %d, gts 0x%p, tfh 0x%p, vaddr 0x%lx, asid 0x%x, indexway 0x%x,"
|
||||
" rw %d, ps %d, gpa 0x%lx\n",
|
||||
atomic ? "atomic" : "non-atomic", gru->gs_gid, gts, tfh, vaddr, asid,
|
||||
indexway, write, pageshift, gpa);
|
||||
STAT(tlb_dropin);
|
||||
return 0;
|
||||
|
||||
failnoasid:
|
||||
/* No asid (delayed unload). */
|
||||
STAT(tlb_dropin_fail_no_asid);
|
||||
gru_dbg(grudev, "FAILED no_asid tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr);
|
||||
if (!cb)
|
||||
if (!cbk)
|
||||
tfh_user_polling_mode(tfh);
|
||||
else
|
||||
gru_flush_cache(tfh);
|
||||
gru_flush_cache_cbe(cbe);
|
||||
return -EAGAIN;
|
||||
|
||||
failupm:
|
||||
/* Atomic failure switch CBR to UPM */
|
||||
tfh_user_polling_mode(tfh);
|
||||
gru_flush_cache_cbe(cbe);
|
||||
STAT(tlb_dropin_fail_upm);
|
||||
gru_dbg(grudev, "FAILED upm tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr);
|
||||
return 1;
|
||||
@ -408,6 +471,7 @@ failupm:
|
||||
failfmm:
|
||||
/* FMM state on UPM call */
|
||||
gru_flush_cache(tfh);
|
||||
gru_flush_cache_cbe(cbe);
|
||||
STAT(tlb_dropin_fail_fmm);
|
||||
gru_dbg(grudev, "FAILED fmm tfh: 0x%p, state %d\n", tfh, tfh->state);
|
||||
return 0;
|
||||
@ -415,17 +479,20 @@ failfmm:
|
||||
failnoexception:
|
||||
/* TFH status did not show exception pending */
|
||||
gru_flush_cache(tfh);
|
||||
if (cb)
|
||||
gru_flush_cache(cb);
|
||||
gru_flush_cache_cbe(cbe);
|
||||
if (cbk)
|
||||
gru_flush_cache(cbk);
|
||||
STAT(tlb_dropin_fail_no_exception);
|
||||
gru_dbg(grudev, "FAILED non-exception tfh: 0x%p, status %d, state %d\n", tfh, tfh->status, tfh->state);
|
||||
gru_dbg(grudev, "FAILED non-exception tfh: 0x%p, status %d, state %d\n",
|
||||
tfh, tfh->status, tfh->state);
|
||||
return 0;
|
||||
|
||||
failidle:
|
||||
/* TFH state was idle - no miss pending */
|
||||
gru_flush_cache(tfh);
|
||||
if (cb)
|
||||
gru_flush_cache(cb);
|
||||
gru_flush_cache_cbe(cbe);
|
||||
if (cbk)
|
||||
gru_flush_cache(cbk);
|
||||
STAT(tlb_dropin_fail_idle);
|
||||
gru_dbg(grudev, "FAILED idle tfh: 0x%p, state %d\n", tfh, tfh->state);
|
||||
return 0;
|
||||
@ -433,16 +500,18 @@ failidle:
|
||||
failinval:
|
||||
/* All errors (atomic & non-atomic) switch CBR to EXCEPTION state */
|
||||
tfh_exception(tfh);
|
||||
gru_flush_cache_cbe(cbe);
|
||||
STAT(tlb_dropin_fail_invalid);
|
||||
gru_dbg(grudev, "FAILED inval tfh: 0x%p, vaddr 0x%lx\n", tfh, vaddr);
|
||||
return -EFAULT;
|
||||
|
||||
failactive:
|
||||
/* Range invalidate active. Switch to UPM iff atomic */
|
||||
if (!cb)
|
||||
if (!cbk)
|
||||
tfh_user_polling_mode(tfh);
|
||||
else
|
||||
gru_flush_cache(tfh);
|
||||
gru_flush_cache_cbe(cbe);
|
||||
STAT(tlb_dropin_fail_range_active);
|
||||
gru_dbg(grudev, "FAILED range active: tfh 0x%p, vaddr 0x%lx\n",
|
||||
tfh, vaddr);
|
||||
@ -455,31 +524,41 @@ failactive:
|
||||
* Note that this is the interrupt handler that is registered with linux
|
||||
* interrupt handlers.
|
||||
*/
|
||||
irqreturn_t gru_intr(int irq, void *dev_id)
|
||||
static irqreturn_t gru_intr(int chiplet, int blade)
|
||||
{
|
||||
struct gru_state *gru;
|
||||
struct gru_tlb_fault_map imap, dmap;
|
||||
struct gru_thread_state *gts;
|
||||
struct gru_tlb_fault_handle *tfh = NULL;
|
||||
struct completion *cmp;
|
||||
int cbrnum, ctxnum;
|
||||
|
||||
STAT(intr);
|
||||
|
||||
gru = irq_to_gru(irq);
|
||||
gru = &gru_base[blade]->bs_grus[chiplet];
|
||||
if (!gru) {
|
||||
dev_err(grudev, "GRU: invalid interrupt: cpu %d, irq %d\n",
|
||||
raw_smp_processor_id(), irq);
|
||||
dev_err(grudev, "GRU: invalid interrupt: cpu %d, chiplet %d\n",
|
||||
raw_smp_processor_id(), chiplet);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
get_clear_fault_map(gru, &imap, &dmap);
|
||||
gru_dbg(grudev,
|
||||
"cpu %d, chiplet %d, gid %d, imap %016lx %016lx, dmap %016lx %016lx\n",
|
||||
smp_processor_id(), chiplet, gru->gs_gid,
|
||||
imap.fault_bits[0], imap.fault_bits[1],
|
||||
dmap.fault_bits[0], dmap.fault_bits[1]);
|
||||
|
||||
for_each_cbr_in_tfm(cbrnum, dmap.fault_bits) {
|
||||
complete(gru->gs_blade->bs_async_wq);
|
||||
STAT(intr_cbr);
|
||||
cmp = gru->gs_blade->bs_async_wq;
|
||||
if (cmp)
|
||||
complete(cmp);
|
||||
gru_dbg(grudev, "gid %d, cbr_done %d, done %d\n",
|
||||
gru->gs_gid, cbrnum, gru->gs_blade->bs_async_wq->done);
|
||||
gru->gs_gid, cbrnum, cmp ? cmp->done : -1);
|
||||
}
|
||||
|
||||
for_each_cbr_in_tfm(cbrnum, imap.fault_bits) {
|
||||
STAT(intr_tfh);
|
||||
tfh = get_tfh_by_index(gru, cbrnum);
|
||||
prefetchw(tfh); /* Helps on hdw, required for emulator */
|
||||
|
||||
@ -492,14 +571,20 @@ irqreturn_t gru_intr(int irq, void *dev_id)
|
||||
ctxnum = tfh->ctxnum;
|
||||
gts = gru->gs_gts[ctxnum];
|
||||
|
||||
/* Spurious interrupts can cause this. Ignore. */
|
||||
if (!gts) {
|
||||
STAT(intr_spurious);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is running in interrupt context. Trylock the mmap_sem.
|
||||
* If it fails, retry the fault in user context.
|
||||
*/
|
||||
gts->ustats.fmm_tlbmiss++;
|
||||
if (!gts->ts_force_cch_reload &&
|
||||
down_read_trylock(>s->ts_mm->mmap_sem)) {
|
||||
gts->ustats.fmm_tlbdropin++;
|
||||
gru_try_dropin(gts, tfh, NULL);
|
||||
gru_try_dropin(gru, gts, tfh, NULL);
|
||||
up_read(>s->ts_mm->mmap_sem);
|
||||
} else {
|
||||
tfh_user_polling_mode(tfh);
|
||||
@ -509,20 +594,43 @@ irqreturn_t gru_intr(int irq, void *dev_id)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
irqreturn_t gru0_intr(int irq, void *dev_id)
|
||||
{
|
||||
return gru_intr(0, uv_numa_blade_id());
|
||||
}
|
||||
|
||||
irqreturn_t gru1_intr(int irq, void *dev_id)
|
||||
{
|
||||
return gru_intr(1, uv_numa_blade_id());
|
||||
}
|
||||
|
||||
irqreturn_t gru_intr_mblade(int irq, void *dev_id)
|
||||
{
|
||||
int blade;
|
||||
|
||||
for_each_possible_blade(blade) {
|
||||
if (uv_blade_nr_possible_cpus(blade))
|
||||
continue;
|
||||
gru_intr(0, blade);
|
||||
gru_intr(1, blade);
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
static int gru_user_dropin(struct gru_thread_state *gts,
|
||||
struct gru_tlb_fault_handle *tfh,
|
||||
unsigned long __user *cb)
|
||||
void *cb)
|
||||
{
|
||||
struct gru_mm_struct *gms = gts->ts_gms;
|
||||
int ret;
|
||||
|
||||
gts->ustats.upm_tlbdropin++;
|
||||
gts->ustats.upm_tlbmiss++;
|
||||
while (1) {
|
||||
wait_event(gms->ms_wait_queue,
|
||||
atomic_read(&gms->ms_range_active) == 0);
|
||||
prefetchw(tfh); /* Helps on hdw, required for emulator */
|
||||
ret = gru_try_dropin(gts, tfh, cb);
|
||||
ret = gru_try_dropin(gts->ts_gru, gts, tfh, cb);
|
||||
if (ret <= 0)
|
||||
return ret;
|
||||
STAT(call_os_wait_queue);
|
||||
@ -538,52 +646,41 @@ int gru_handle_user_call_os(unsigned long cb)
|
||||
{
|
||||
struct gru_tlb_fault_handle *tfh;
|
||||
struct gru_thread_state *gts;
|
||||
unsigned long __user *cbp;
|
||||
void *cbk;
|
||||
int ucbnum, cbrnum, ret = -EINVAL;
|
||||
|
||||
STAT(call_os);
|
||||
gru_dbg(grudev, "address 0x%lx\n", cb);
|
||||
|
||||
/* sanity check the cb pointer */
|
||||
ucbnum = get_cb_number((void *)cb);
|
||||
if ((cb & (GRU_HANDLE_STRIDE - 1)) || ucbnum >= GRU_NUM_CB)
|
||||
return -EINVAL;
|
||||
cbp = (unsigned long *)cb;
|
||||
|
||||
gts = gru_find_lock_gts(cb);
|
||||
if (!gts)
|
||||
return -EINVAL;
|
||||
gru_dbg(grudev, "address 0x%lx, gid %d, gts 0x%p\n", cb, gts->ts_gru ? gts->ts_gru->gs_gid : -1, gts);
|
||||
|
||||
if (ucbnum >= gts->ts_cbr_au_count * GRU_CBR_AU_SIZE)
|
||||
goto exit;
|
||||
|
||||
/*
|
||||
* If force_unload is set, the UPM TLB fault is phony. The task
|
||||
* has migrated to another node and the GSEG must be moved. Just
|
||||
* unload the context. The task will page fault and assign a new
|
||||
* context.
|
||||
*/
|
||||
if (gts->ts_tgid_owner == current->tgid && gts->ts_blade >= 0 &&
|
||||
gts->ts_blade != uv_numa_blade_id()) {
|
||||
STAT(call_os_offnode_reference);
|
||||
gts->ts_force_unload = 1;
|
||||
}
|
||||
gru_check_context_placement(gts);
|
||||
|
||||
/*
|
||||
* CCH may contain stale data if ts_force_cch_reload is set.
|
||||
*/
|
||||
if (gts->ts_gru && gts->ts_force_cch_reload) {
|
||||
gts->ts_force_cch_reload = 0;
|
||||
gru_update_cch(gts, 0);
|
||||
gru_update_cch(gts);
|
||||
}
|
||||
|
||||
ret = -EAGAIN;
|
||||
cbrnum = thread_cbr_number(gts, ucbnum);
|
||||
if (gts->ts_force_unload) {
|
||||
gru_unload_context(gts, 1);
|
||||
} else if (gts->ts_gru) {
|
||||
if (gts->ts_gru) {
|
||||
tfh = get_tfh_by_index(gts->ts_gru, cbrnum);
|
||||
ret = gru_user_dropin(gts, tfh, cbp);
|
||||
cbk = get_gseg_base_address_cb(gts->ts_gru->gs_gru_base_vaddr,
|
||||
gts->ts_ctxnum, ucbnum);
|
||||
ret = gru_user_dropin(gts, tfh, cbk);
|
||||
}
|
||||
exit:
|
||||
gru_unlock_gts(gts);
|
||||
@ -605,11 +702,11 @@ int gru_get_exception_detail(unsigned long arg)
|
||||
if (copy_from_user(&excdet, (void __user *)arg, sizeof(excdet)))
|
||||
return -EFAULT;
|
||||
|
||||
gru_dbg(grudev, "address 0x%lx\n", excdet.cb);
|
||||
gts = gru_find_lock_gts(excdet.cb);
|
||||
if (!gts)
|
||||
return -EINVAL;
|
||||
|
||||
gru_dbg(grudev, "address 0x%lx, gid %d, gts 0x%p\n", excdet.cb, gts->ts_gru ? gts->ts_gru->gs_gid : -1, gts);
|
||||
ucbnum = get_cb_number((void *)excdet.cb);
|
||||
if (ucbnum >= gts->ts_cbr_au_count * GRU_CBR_AU_SIZE) {
|
||||
ret = -EINVAL;
|
||||
@ -617,6 +714,7 @@ int gru_get_exception_detail(unsigned long arg)
|
||||
cbrnum = thread_cbr_number(gts, ucbnum);
|
||||
cbe = get_cbe_by_index(gts->ts_gru, cbrnum);
|
||||
gru_flush_cache(cbe); /* CBE not coherent */
|
||||
sync_core(); /* make sure we are have current data */
|
||||
excdet.opc = cbe->opccpy;
|
||||
excdet.exopc = cbe->exopccpy;
|
||||
excdet.ecause = cbe->ecause;
|
||||
@ -624,7 +722,7 @@ int gru_get_exception_detail(unsigned long arg)
|
||||
excdet.exceptdet1 = cbe->idef3upd;
|
||||
excdet.cbrstate = cbe->cbrstate;
|
||||
excdet.cbrexecstatus = cbe->cbrexecstatus;
|
||||
gru_flush_cache(cbe);
|
||||
gru_flush_cache_cbe(cbe);
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = -EAGAIN;
|
||||
@ -733,6 +831,11 @@ long gru_get_gseg_statistics(unsigned long arg)
|
||||
if (copy_from_user(&req, (void __user *)arg, sizeof(req)))
|
||||
return -EFAULT;
|
||||
|
||||
/*
|
||||
* The library creates arrays of contexts for threaded programs.
|
||||
* If no gts exists in the array, the context has never been used & all
|
||||
* statistics are implicitly 0.
|
||||
*/
|
||||
gts = gru_find_lock_gts(req.gseg);
|
||||
if (gts) {
|
||||
memcpy(&req.stats, >s->ustats, sizeof(gts->ustats));
|
||||
@ -762,11 +865,25 @@ int gru_set_context_option(unsigned long arg)
|
||||
return -EFAULT;
|
||||
gru_dbg(grudev, "op %d, gseg 0x%lx, value1 0x%lx\n", req.op, req.gseg, req.val1);
|
||||
|
||||
gts = gru_alloc_locked_gts(req.gseg);
|
||||
if (!gts)
|
||||
return -EINVAL;
|
||||
gts = gru_find_lock_gts(req.gseg);
|
||||
if (!gts) {
|
||||
gts = gru_alloc_locked_gts(req.gseg);
|
||||
if (IS_ERR(gts))
|
||||
return PTR_ERR(gts);
|
||||
}
|
||||
|
||||
switch (req.op) {
|
||||
case sco_blade_chiplet:
|
||||
/* Select blade/chiplet for GRU context */
|
||||
if (req.val1 < -1 || req.val1 >= GRU_MAX_BLADES || !gru_base[req.val1] ||
|
||||
req.val0 < -1 || req.val0 >= GRU_CHIPLETS_PER_HUB) {
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
gts->ts_user_blade_id = req.val1;
|
||||
gts->ts_user_chiplet_id = req.val0;
|
||||
gru_check_context_placement(gts);
|
||||
}
|
||||
break;
|
||||
case sco_gseg_owner:
|
||||
/* Register the current task as the GSEG owner */
|
||||
gts->ts_tgid_owner = current->tgid;
|
||||
|
@ -35,6 +35,9 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/uaccess.h>
|
||||
#ifdef CONFIG_X86_64
|
||||
#include <asm/uv/uv_irq.h>
|
||||
#endif
|
||||
#include <asm/uv/uv.h>
|
||||
#include "gru.h"
|
||||
#include "grulib.h"
|
||||
@ -130,7 +133,6 @@ static int gru_create_new_context(unsigned long arg)
|
||||
struct gru_vma_data *vdata;
|
||||
int ret = -EINVAL;
|
||||
|
||||
|
||||
if (copy_from_user(&req, (void __user *)arg, sizeof(req)))
|
||||
return -EFAULT;
|
||||
|
||||
@ -150,6 +152,7 @@ static int gru_create_new_context(unsigned long arg)
|
||||
vdata->vd_dsr_au_count =
|
||||
GRU_DS_BYTES_TO_AU(req.data_segment_bytes);
|
||||
vdata->vd_cbr_au_count = GRU_CB_COUNT_TO_AU(req.control_blocks);
|
||||
vdata->vd_tlb_preload_count = req.tlb_preload_count;
|
||||
ret = 0;
|
||||
}
|
||||
up_write(¤t->mm->mmap_sem);
|
||||
@ -190,7 +193,7 @@ static long gru_file_unlocked_ioctl(struct file *file, unsigned int req,
|
||||
{
|
||||
int err = -EBADRQC;
|
||||
|
||||
gru_dbg(grudev, "file %p\n", file);
|
||||
gru_dbg(grudev, "file %p, req 0x%x, 0x%lx\n", file, req, arg);
|
||||
|
||||
switch (req) {
|
||||
case GRU_CREATE_CONTEXT:
|
||||
@ -232,23 +235,24 @@ static long gru_file_unlocked_ioctl(struct file *file, unsigned int req,
|
||||
* system.
|
||||
*/
|
||||
static void gru_init_chiplet(struct gru_state *gru, unsigned long paddr,
|
||||
void *vaddr, int nid, int bid, int grunum)
|
||||
void *vaddr, int blade_id, int chiplet_id)
|
||||
{
|
||||
spin_lock_init(&gru->gs_lock);
|
||||
spin_lock_init(&gru->gs_asid_lock);
|
||||
gru->gs_gru_base_paddr = paddr;
|
||||
gru->gs_gru_base_vaddr = vaddr;
|
||||
gru->gs_gid = bid * GRU_CHIPLETS_PER_BLADE + grunum;
|
||||
gru->gs_blade = gru_base[bid];
|
||||
gru->gs_blade_id = bid;
|
||||
gru->gs_gid = blade_id * GRU_CHIPLETS_PER_BLADE + chiplet_id;
|
||||
gru->gs_blade = gru_base[blade_id];
|
||||
gru->gs_blade_id = blade_id;
|
||||
gru->gs_chiplet_id = chiplet_id;
|
||||
gru->gs_cbr_map = (GRU_CBR_AU == 64) ? ~0 : (1UL << GRU_CBR_AU) - 1;
|
||||
gru->gs_dsr_map = (1UL << GRU_DSR_AU) - 1;
|
||||
gru->gs_asid_limit = MAX_ASID;
|
||||
gru_tgh_flush_init(gru);
|
||||
if (gru->gs_gid >= gru_max_gids)
|
||||
gru_max_gids = gru->gs_gid + 1;
|
||||
gru_dbg(grudev, "bid %d, nid %d, gid %d, vaddr %p (0x%lx)\n",
|
||||
bid, nid, gru->gs_gid, gru->gs_gru_base_vaddr,
|
||||
gru_dbg(grudev, "bid %d, gid %d, vaddr %p (0x%lx)\n",
|
||||
blade_id, gru->gs_gid, gru->gs_gru_base_vaddr,
|
||||
gru->gs_gru_base_paddr);
|
||||
}
|
||||
|
||||
@ -264,12 +268,10 @@ static int gru_init_tables(unsigned long gru_base_paddr, void *gru_base_vaddr)
|
||||
|
||||
max_user_cbrs = GRU_NUM_CB;
|
||||
max_user_dsr_bytes = GRU_NUM_DSR_BYTES;
|
||||
for_each_online_node(nid) {
|
||||
bid = uv_node_to_blade_id(nid);
|
||||
pnode = uv_node_to_pnode(nid);
|
||||
if (bid < 0 || gru_base[bid])
|
||||
continue;
|
||||
page = alloc_pages_exact_node(nid, GFP_KERNEL, order);
|
||||
for_each_possible_blade(bid) {
|
||||
pnode = uv_blade_to_pnode(bid);
|
||||
nid = uv_blade_to_memory_nid(bid);/* -1 if no memory on blade */
|
||||
page = alloc_pages_node(nid, GFP_KERNEL, order);
|
||||
if (!page)
|
||||
goto fail;
|
||||
gru_base[bid] = page_address(page);
|
||||
@ -285,7 +287,7 @@ static int gru_init_tables(unsigned long gru_base_paddr, void *gru_base_vaddr)
|
||||
chip++, gru++) {
|
||||
paddr = gru_chiplet_paddr(gru_base_paddr, pnode, chip);
|
||||
vaddr = gru_chiplet_vaddr(gru_base_vaddr, pnode, chip);
|
||||
gru_init_chiplet(gru, paddr, vaddr, nid, bid, chip);
|
||||
gru_init_chiplet(gru, paddr, vaddr, bid, chip);
|
||||
n = hweight64(gru->gs_cbr_map) * GRU_CBR_AU_SIZE;
|
||||
cbrs = max(cbrs, n);
|
||||
n = hweight64(gru->gs_dsr_map) * GRU_DSR_AU_BYTES;
|
||||
@ -298,39 +300,215 @@ static int gru_init_tables(unsigned long gru_base_paddr, void *gru_base_vaddr)
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
for (nid--; nid >= 0; nid--)
|
||||
free_pages((unsigned long)gru_base[nid], order);
|
||||
for (bid--; bid >= 0; bid--)
|
||||
free_pages((unsigned long)gru_base[bid], order);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void gru_free_tables(void)
|
||||
{
|
||||
int bid;
|
||||
int order = get_order(sizeof(struct gru_state) *
|
||||
GRU_CHIPLETS_PER_BLADE);
|
||||
|
||||
for (bid = 0; bid < GRU_MAX_BLADES; bid++)
|
||||
free_pages((unsigned long)gru_base[bid], order);
|
||||
}
|
||||
|
||||
static unsigned long gru_chiplet_cpu_to_mmr(int chiplet, int cpu, int *corep)
|
||||
{
|
||||
unsigned long mmr = 0;
|
||||
int core;
|
||||
|
||||
/*
|
||||
* We target the cores of a blade and not the hyperthreads themselves.
|
||||
* There is a max of 8 cores per socket and 2 sockets per blade,
|
||||
* making for a max total of 16 cores (i.e., 16 CPUs without
|
||||
* hyperthreading and 32 CPUs with hyperthreading).
|
||||
*/
|
||||
core = uv_cpu_core_number(cpu) + UV_MAX_INT_CORES * uv_cpu_socket_number(cpu);
|
||||
if (core >= GRU_NUM_TFM || uv_cpu_ht_number(cpu))
|
||||
return 0;
|
||||
|
||||
if (chiplet == 0) {
|
||||
mmr = UVH_GR0_TLB_INT0_CONFIG +
|
||||
core * (UVH_GR0_TLB_INT1_CONFIG - UVH_GR0_TLB_INT0_CONFIG);
|
||||
} else if (chiplet == 1) {
|
||||
mmr = UVH_GR1_TLB_INT0_CONFIG +
|
||||
core * (UVH_GR1_TLB_INT1_CONFIG - UVH_GR1_TLB_INT0_CONFIG);
|
||||
} else {
|
||||
BUG();
|
||||
}
|
||||
|
||||
*corep = core;
|
||||
return mmr;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IA64
|
||||
|
||||
static int get_base_irq(void)
|
||||
static int gru_irq_count[GRU_CHIPLETS_PER_BLADE];
|
||||
|
||||
static void gru_noop(unsigned int irq)
|
||||
{
|
||||
return IRQ_GRU;
|
||||
}
|
||||
|
||||
static struct irq_chip gru_chip[GRU_CHIPLETS_PER_BLADE] = {
|
||||
[0 ... GRU_CHIPLETS_PER_BLADE - 1] {
|
||||
.mask = gru_noop,
|
||||
.unmask = gru_noop,
|
||||
.ack = gru_noop
|
||||
}
|
||||
};
|
||||
|
||||
static int gru_chiplet_setup_tlb_irq(int chiplet, char *irq_name,
|
||||
irq_handler_t irq_handler, int cpu, int blade)
|
||||
{
|
||||
unsigned long mmr;
|
||||
int irq = IRQ_GRU + chiplet;
|
||||
int ret, core;
|
||||
|
||||
mmr = gru_chiplet_cpu_to_mmr(chiplet, cpu, &core);
|
||||
if (mmr == 0)
|
||||
return 0;
|
||||
|
||||
if (gru_irq_count[chiplet] == 0) {
|
||||
gru_chip[chiplet].name = irq_name;
|
||||
ret = set_irq_chip(irq, &gru_chip[chiplet]);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "%s: set_irq_chip failed, errno=%d\n",
|
||||
GRU_DRIVER_ID_STR, -ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = request_irq(irq, irq_handler, 0, irq_name, NULL);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "%s: request_irq failed, errno=%d\n",
|
||||
GRU_DRIVER_ID_STR, -ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
gru_irq_count[chiplet]++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gru_chiplet_teardown_tlb_irq(int chiplet, int cpu, int blade)
|
||||
{
|
||||
unsigned long mmr;
|
||||
int core, irq = IRQ_GRU + chiplet;
|
||||
|
||||
if (gru_irq_count[chiplet] == 0)
|
||||
return;
|
||||
|
||||
mmr = gru_chiplet_cpu_to_mmr(chiplet, cpu, &core);
|
||||
if (mmr == 0)
|
||||
return;
|
||||
|
||||
if (--gru_irq_count[chiplet] == 0)
|
||||
free_irq(irq, NULL);
|
||||
}
|
||||
|
||||
#elif defined CONFIG_X86_64
|
||||
|
||||
static void noop(unsigned int irq)
|
||||
static int gru_chiplet_setup_tlb_irq(int chiplet, char *irq_name,
|
||||
irq_handler_t irq_handler, int cpu, int blade)
|
||||
{
|
||||
unsigned long mmr;
|
||||
int irq, core;
|
||||
int ret;
|
||||
|
||||
mmr = gru_chiplet_cpu_to_mmr(chiplet, cpu, &core);
|
||||
if (mmr == 0)
|
||||
return 0;
|
||||
|
||||
irq = uv_setup_irq(irq_name, cpu, blade, mmr, UV_AFFINITY_CPU);
|
||||
if (irq < 0) {
|
||||
printk(KERN_ERR "%s: uv_setup_irq failed, errno=%d\n",
|
||||
GRU_DRIVER_ID_STR, -irq);
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = request_irq(irq, irq_handler, 0, irq_name, NULL);
|
||||
if (ret) {
|
||||
uv_teardown_irq(irq);
|
||||
printk(KERN_ERR "%s: request_irq failed, errno=%d\n",
|
||||
GRU_DRIVER_ID_STR, -ret);
|
||||
return ret;
|
||||
}
|
||||
gru_base[blade]->bs_grus[chiplet].gs_irq[core] = irq;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_chip gru_chip = {
|
||||
.name = "gru",
|
||||
.mask = noop,
|
||||
.unmask = noop,
|
||||
.ack = noop,
|
||||
};
|
||||
|
||||
static int get_base_irq(void)
|
||||
static void gru_chiplet_teardown_tlb_irq(int chiplet, int cpu, int blade)
|
||||
{
|
||||
set_irq_chip(IRQ_GRU, &gru_chip);
|
||||
set_irq_chip(IRQ_GRU + 1, &gru_chip);
|
||||
return IRQ_GRU;
|
||||
int irq, core;
|
||||
unsigned long mmr;
|
||||
|
||||
mmr = gru_chiplet_cpu_to_mmr(chiplet, cpu, &core);
|
||||
if (mmr) {
|
||||
irq = gru_base[blade]->bs_grus[chiplet].gs_irq[core];
|
||||
if (irq) {
|
||||
free_irq(irq, NULL);
|
||||
uv_teardown_irq(irq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void gru_teardown_tlb_irqs(void)
|
||||
{
|
||||
int blade;
|
||||
int cpu;
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
blade = uv_cpu_to_blade_id(cpu);
|
||||
gru_chiplet_teardown_tlb_irq(0, cpu, blade);
|
||||
gru_chiplet_teardown_tlb_irq(1, cpu, blade);
|
||||
}
|
||||
for_each_possible_blade(blade) {
|
||||
if (uv_blade_nr_possible_cpus(blade))
|
||||
continue;
|
||||
gru_chiplet_teardown_tlb_irq(0, 0, blade);
|
||||
gru_chiplet_teardown_tlb_irq(1, 0, blade);
|
||||
}
|
||||
}
|
||||
|
||||
static int gru_setup_tlb_irqs(void)
|
||||
{
|
||||
int blade;
|
||||
int cpu;
|
||||
int ret;
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
blade = uv_cpu_to_blade_id(cpu);
|
||||
ret = gru_chiplet_setup_tlb_irq(0, "GRU0_TLB", gru0_intr, cpu, blade);
|
||||
if (ret != 0)
|
||||
goto exit1;
|
||||
|
||||
ret = gru_chiplet_setup_tlb_irq(1, "GRU1_TLB", gru1_intr, cpu, blade);
|
||||
if (ret != 0)
|
||||
goto exit1;
|
||||
}
|
||||
for_each_possible_blade(blade) {
|
||||
if (uv_blade_nr_possible_cpus(blade))
|
||||
continue;
|
||||
ret = gru_chiplet_setup_tlb_irq(0, "GRU0_TLB", gru_intr_mblade, 0, blade);
|
||||
if (ret != 0)
|
||||
goto exit1;
|
||||
|
||||
ret = gru_chiplet_setup_tlb_irq(1, "GRU1_TLB", gru_intr_mblade, 0, blade);
|
||||
if (ret != 0)
|
||||
goto exit1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit1:
|
||||
gru_teardown_tlb_irqs();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* gru_init
|
||||
*
|
||||
@ -338,8 +516,7 @@ static int get_base_irq(void)
|
||||
*/
|
||||
static int __init gru_init(void)
|
||||
{
|
||||
int ret, irq, chip;
|
||||
char id[10];
|
||||
int ret;
|
||||
|
||||
if (!is_uv_system())
|
||||
return 0;
|
||||
@ -354,41 +531,29 @@ static int __init gru_init(void)
|
||||
gru_end_paddr = gru_start_paddr + GRU_MAX_BLADES * GRU_SIZE;
|
||||
printk(KERN_INFO "GRU space: 0x%lx - 0x%lx\n",
|
||||
gru_start_paddr, gru_end_paddr);
|
||||
irq = get_base_irq();
|
||||
for (chip = 0; chip < GRU_CHIPLETS_PER_BLADE; chip++) {
|
||||
ret = request_irq(irq + chip, gru_intr, 0, id, NULL);
|
||||
/* TODO: fix irq handling on x86. For now ignore failure because
|
||||
* interrupts are not required & not yet fully supported */
|
||||
if (ret) {
|
||||
printk(KERN_WARNING
|
||||
"!!!WARNING: GRU ignoring request failure!!!\n");
|
||||
ret = 0;
|
||||
}
|
||||
if (ret) {
|
||||
printk(KERN_ERR "%s: request_irq failed\n",
|
||||
GRU_DRIVER_ID_STR);
|
||||
goto exit1;
|
||||
}
|
||||
}
|
||||
|
||||
ret = misc_register(&gru_miscdev);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "%s: misc_register failed\n",
|
||||
GRU_DRIVER_ID_STR);
|
||||
goto exit1;
|
||||
goto exit0;
|
||||
}
|
||||
|
||||
ret = gru_proc_init();
|
||||
if (ret) {
|
||||
printk(KERN_ERR "%s: proc init failed\n", GRU_DRIVER_ID_STR);
|
||||
goto exit2;
|
||||
goto exit1;
|
||||
}
|
||||
|
||||
ret = gru_init_tables(gru_start_paddr, gru_start_vaddr);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "%s: init tables failed\n", GRU_DRIVER_ID_STR);
|
||||
goto exit3;
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
ret = gru_setup_tlb_irqs();
|
||||
if (ret != 0)
|
||||
goto exit3;
|
||||
|
||||
gru_kservices_init();
|
||||
|
||||
printk(KERN_INFO "%s: v%s\n", GRU_DRIVER_ID_STR,
|
||||
@ -396,31 +561,24 @@ static int __init gru_init(void)
|
||||
return 0;
|
||||
|
||||
exit3:
|
||||
gru_proc_exit();
|
||||
gru_free_tables();
|
||||
exit2:
|
||||
misc_deregister(&gru_miscdev);
|
||||
gru_proc_exit();
|
||||
exit1:
|
||||
for (--chip; chip >= 0; chip--)
|
||||
free_irq(irq + chip, NULL);
|
||||
misc_deregister(&gru_miscdev);
|
||||
exit0:
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static void __exit gru_exit(void)
|
||||
{
|
||||
int i, bid;
|
||||
int order = get_order(sizeof(struct gru_state) *
|
||||
GRU_CHIPLETS_PER_BLADE);
|
||||
|
||||
if (!is_uv_system())
|
||||
return;
|
||||
|
||||
for (i = 0; i < GRU_CHIPLETS_PER_BLADE; i++)
|
||||
free_irq(IRQ_GRU + i, NULL);
|
||||
gru_teardown_tlb_irqs();
|
||||
gru_kservices_exit();
|
||||
for (bid = 0; bid < GRU_MAX_BLADES; bid++)
|
||||
free_pages((unsigned long)gru_base[bid], order);
|
||||
|
||||
gru_free_tables();
|
||||
misc_deregister(&gru_miscdev);
|
||||
gru_proc_exit();
|
||||
}
|
||||
|
@ -27,9 +27,11 @@
|
||||
#ifdef CONFIG_IA64
|
||||
#include <asm/processor.h>
|
||||
#define GRU_OPERATION_TIMEOUT (((cycles_t) local_cpu_data->itc_freq)*10)
|
||||
#define CLKS2NSEC(c) ((c) *1000000000 / local_cpu_data->itc_freq)
|
||||
#else
|
||||
#include <asm/tsc.h>
|
||||
#define GRU_OPERATION_TIMEOUT ((cycles_t) tsc_khz*10*1000)
|
||||
#define CLKS2NSEC(c) ((c) * 1000000 / tsc_khz)
|
||||
#endif
|
||||
|
||||
/* Extract the status field from a kernel handle */
|
||||
@ -39,21 +41,39 @@ struct mcs_op_statistic mcs_op_statistics[mcsop_last];
|
||||
|
||||
static void update_mcs_stats(enum mcs_op op, unsigned long clks)
|
||||
{
|
||||
unsigned long nsec;
|
||||
|
||||
nsec = CLKS2NSEC(clks);
|
||||
atomic_long_inc(&mcs_op_statistics[op].count);
|
||||
atomic_long_add(clks, &mcs_op_statistics[op].total);
|
||||
if (mcs_op_statistics[op].max < clks)
|
||||
mcs_op_statistics[op].max = clks;
|
||||
atomic_long_add(nsec, &mcs_op_statistics[op].total);
|
||||
if (mcs_op_statistics[op].max < nsec)
|
||||
mcs_op_statistics[op].max = nsec;
|
||||
}
|
||||
|
||||
static void start_instruction(void *h)
|
||||
{
|
||||
unsigned long *w0 = h;
|
||||
|
||||
wmb(); /* setting CMD bit must be last */
|
||||
*w0 = *w0 | 1;
|
||||
wmb(); /* setting CMD/STATUS bits must be last */
|
||||
*w0 = *w0 | 0x20001;
|
||||
gru_flush_cache(h);
|
||||
}
|
||||
|
||||
static void report_instruction_timeout(void *h)
|
||||
{
|
||||
unsigned long goff = GSEGPOFF((unsigned long)h);
|
||||
char *id = "???";
|
||||
|
||||
if (TYPE_IS(CCH, goff))
|
||||
id = "CCH";
|
||||
else if (TYPE_IS(TGH, goff))
|
||||
id = "TGH";
|
||||
else if (TYPE_IS(TFH, goff))
|
||||
id = "TFH";
|
||||
|
||||
panic(KERN_ALERT "GRU %p (%s) is malfunctioning\n", h, id);
|
||||
}
|
||||
|
||||
static int wait_instruction_complete(void *h, enum mcs_op opc)
|
||||
{
|
||||
int status;
|
||||
@ -64,9 +84,10 @@ static int wait_instruction_complete(void *h, enum mcs_op opc)
|
||||
status = GET_MSEG_HANDLE_STATUS(h);
|
||||
if (status != CCHSTATUS_ACTIVE)
|
||||
break;
|
||||
if (GRU_OPERATION_TIMEOUT < (get_cycles() - start_time))
|
||||
panic("GRU %p is malfunctioning: start %ld, end %ld\n",
|
||||
h, start_time, (unsigned long)get_cycles());
|
||||
if (GRU_OPERATION_TIMEOUT < (get_cycles() - start_time)) {
|
||||
report_instruction_timeout(h);
|
||||
start_time = get_cycles();
|
||||
}
|
||||
}
|
||||
if (gru_options & OPT_STATS)
|
||||
update_mcs_stats(opc, get_cycles() - start_time);
|
||||
@ -75,9 +96,18 @@ static int wait_instruction_complete(void *h, enum mcs_op opc)
|
||||
|
||||
int cch_allocate(struct gru_context_configuration_handle *cch)
|
||||
{
|
||||
int ret;
|
||||
|
||||
cch->opc = CCHOP_ALLOCATE;
|
||||
start_instruction(cch);
|
||||
return wait_instruction_complete(cch, cchop_allocate);
|
||||
ret = wait_instruction_complete(cch, cchop_allocate);
|
||||
|
||||
/*
|
||||
* Stop speculation into the GSEG being mapped by the previous ALLOCATE.
|
||||
* The GSEG memory does not exist until the ALLOCATE completes.
|
||||
*/
|
||||
sync_core();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cch_start(struct gru_context_configuration_handle *cch)
|
||||
@ -96,9 +126,18 @@ int cch_interrupt(struct gru_context_configuration_handle *cch)
|
||||
|
||||
int cch_deallocate(struct gru_context_configuration_handle *cch)
|
||||
{
|
||||
int ret;
|
||||
|
||||
cch->opc = CCHOP_DEALLOCATE;
|
||||
start_instruction(cch);
|
||||
return wait_instruction_complete(cch, cchop_deallocate);
|
||||
ret = wait_instruction_complete(cch, cchop_deallocate);
|
||||
|
||||
/*
|
||||
* Stop speculation into the GSEG being unmapped by the previous
|
||||
* DEALLOCATE.
|
||||
*/
|
||||
sync_core();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cch_interrupt_sync(struct gru_context_configuration_handle
|
||||
@ -126,17 +165,20 @@ int tgh_invalidate(struct gru_tlb_global_handle *tgh,
|
||||
return wait_instruction_complete(tgh, tghop_invalidate);
|
||||
}
|
||||
|
||||
void tfh_write_only(struct gru_tlb_fault_handle *tfh,
|
||||
unsigned long pfn, unsigned long vaddr,
|
||||
int asid, int dirty, int pagesize)
|
||||
int tfh_write_only(struct gru_tlb_fault_handle *tfh,
|
||||
unsigned long paddr, int gaa,
|
||||
unsigned long vaddr, int asid, int dirty,
|
||||
int pagesize)
|
||||
{
|
||||
tfh->fillasid = asid;
|
||||
tfh->fillvaddr = vaddr;
|
||||
tfh->pfn = pfn;
|
||||
tfh->pfn = paddr >> GRU_PADDR_SHIFT;
|
||||
tfh->gaa = gaa;
|
||||
tfh->dirty = dirty;
|
||||
tfh->pagesize = pagesize;
|
||||
tfh->opc = TFHOP_WRITE_ONLY;
|
||||
start_instruction(tfh);
|
||||
return wait_instruction_complete(tfh, tfhop_write_only);
|
||||
}
|
||||
|
||||
void tfh_write_restart(struct gru_tlb_fault_handle *tfh,
|
||||
|
@ -91,6 +91,12 @@
|
||||
/* Convert an arbitrary handle address to the beginning of the GRU segment */
|
||||
#define GRUBASE(h) ((void *)((unsigned long)(h) & ~(GRU_SIZE - 1)))
|
||||
|
||||
/* Test a valid handle address to determine the type */
|
||||
#define TYPE_IS(hn, h) ((h) >= GRU_##hn##_BASE && (h) < \
|
||||
GRU_##hn##_BASE + GRU_NUM_##hn * GRU_HANDLE_STRIDE && \
|
||||
(((h) & (GRU_HANDLE_STRIDE - 1)) == 0))
|
||||
|
||||
|
||||
/* General addressing macros. */
|
||||
static inline void *get_gseg_base_address(void *base, int ctxnum)
|
||||
{
|
||||
@ -158,6 +164,16 @@ static inline void *gru_chiplet_vaddr(void *vaddr, int pnode, int chiplet)
|
||||
return vaddr + GRU_SIZE * (2 * pnode + chiplet);
|
||||
}
|
||||
|
||||
static inline struct gru_control_block_extended *gru_tfh_to_cbe(
|
||||
struct gru_tlb_fault_handle *tfh)
|
||||
{
|
||||
unsigned long cbe;
|
||||
|
||||
cbe = (unsigned long)tfh - GRU_TFH_BASE + GRU_CBE_BASE;
|
||||
return (struct gru_control_block_extended*)cbe;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
@ -236,6 +252,17 @@ enum gru_tgh_state {
|
||||
TGHSTATE_RESTART_CTX,
|
||||
};
|
||||
|
||||
enum gru_tgh_cause {
|
||||
TGHCAUSE_RR_ECC,
|
||||
TGHCAUSE_TLB_ECC,
|
||||
TGHCAUSE_LRU_ECC,
|
||||
TGHCAUSE_PS_ECC,
|
||||
TGHCAUSE_MUL_ERR,
|
||||
TGHCAUSE_DATA_ERR,
|
||||
TGHCAUSE_SW_FORCE
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* TFH - TLB Global Handle
|
||||
* Used for TLB dropins into the GRU TLB.
|
||||
@ -440,6 +467,12 @@ struct gru_control_block_extended {
|
||||
unsigned int cbrexecstatus:8;
|
||||
};
|
||||
|
||||
/* CBE fields for active BCOPY instructions */
|
||||
#define cbe_baddr0 idef1upd
|
||||
#define cbe_baddr1 idef3upd
|
||||
#define cbe_src_cl idef6cpy
|
||||
#define cbe_nelemcur idef5upd
|
||||
|
||||
enum gru_cbr_state {
|
||||
CBRSTATE_INACTIVE,
|
||||
CBRSTATE_IDLE,
|
||||
@ -487,8 +520,8 @@ int cch_interrupt_sync(struct gru_context_configuration_handle *cch);
|
||||
int tgh_invalidate(struct gru_tlb_global_handle *tgh, unsigned long vaddr,
|
||||
unsigned long vaddrmask, int asid, int pagesize, int global, int n,
|
||||
unsigned short ctxbitmap);
|
||||
void tfh_write_only(struct gru_tlb_fault_handle *tfh, unsigned long pfn,
|
||||
unsigned long vaddr, int asid, int dirty, int pagesize);
|
||||
int tfh_write_only(struct gru_tlb_fault_handle *tfh, unsigned long paddr,
|
||||
int gaa, unsigned long vaddr, int asid, int dirty, int pagesize);
|
||||
void tfh_write_restart(struct gru_tlb_fault_handle *tfh, unsigned long paddr,
|
||||
int gaa, unsigned long vaddr, int asid, int dirty, int pagesize);
|
||||
void tfh_restart(struct gru_tlb_fault_handle *tfh);
|
||||
|
@ -44,7 +44,8 @@ static int gru_user_copy_handle(void __user **dp, void *s)
|
||||
|
||||
static int gru_dump_context_data(void *grubase,
|
||||
struct gru_context_configuration_handle *cch,
|
||||
void __user *ubuf, int ctxnum, int dsrcnt)
|
||||
void __user *ubuf, int ctxnum, int dsrcnt,
|
||||
int flush_cbrs)
|
||||
{
|
||||
void *cb, *cbe, *tfh, *gseg;
|
||||
int i, scr;
|
||||
@ -55,6 +56,8 @@ static int gru_dump_context_data(void *grubase,
|
||||
tfh = grubase + GRU_TFH_BASE;
|
||||
|
||||
for_each_cbr_in_allocation_map(i, &cch->cbr_allocation_map, scr) {
|
||||
if (flush_cbrs)
|
||||
gru_flush_cache(cb);
|
||||
if (gru_user_copy_handle(&ubuf, cb))
|
||||
goto fail;
|
||||
if (gru_user_copy_handle(&ubuf, tfh + i * GRU_HANDLE_STRIDE))
|
||||
@ -115,7 +118,7 @@ fail:
|
||||
|
||||
static int gru_dump_context(struct gru_state *gru, int ctxnum,
|
||||
void __user *ubuf, void __user *ubufend, char data_opt,
|
||||
char lock_cch)
|
||||
char lock_cch, char flush_cbrs)
|
||||
{
|
||||
struct gru_dump_context_header hdr;
|
||||
struct gru_dump_context_header __user *uhdr = ubuf;
|
||||
@ -159,8 +162,7 @@ static int gru_dump_context(struct gru_state *gru, int ctxnum,
|
||||
ret = -EFBIG;
|
||||
else
|
||||
ret = gru_dump_context_data(grubase, cch, ubuf, ctxnum,
|
||||
dsrcnt);
|
||||
|
||||
dsrcnt, flush_cbrs);
|
||||
}
|
||||
if (cch_locked)
|
||||
unlock_cch_handle(cch);
|
||||
@ -215,7 +217,8 @@ int gru_dump_chiplet_request(unsigned long arg)
|
||||
for (ctxnum = 0; ctxnum < GRU_NUM_CCH; ctxnum++) {
|
||||
if (req.ctxnum == ctxnum || req.ctxnum < 0) {
|
||||
ret = gru_dump_context(gru, ctxnum, ubuf, ubufend,
|
||||
req.data_opt, req.lock_cch);
|
||||
req.data_opt, req.lock_cch,
|
||||
req.flush_cbrs);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
ubuf += ret;
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/delay.h>
|
||||
#include <asm/io_apic.h>
|
||||
#include "gru.h"
|
||||
#include "grulib.h"
|
||||
#include "grutables.h"
|
||||
@ -97,9 +98,6 @@
|
||||
#define ASYNC_HAN_TO_BID(h) ((h) - 1)
|
||||
#define ASYNC_BID_TO_HAN(b) ((b) + 1)
|
||||
#define ASYNC_HAN_TO_BS(h) gru_base[ASYNC_HAN_TO_BID(h)]
|
||||
#define KCB_TO_GID(cb) ((cb - gru_start_vaddr) / \
|
||||
(GRU_SIZE * GRU_CHIPLETS_PER_BLADE))
|
||||
#define KCB_TO_BS(cb) gru_base[KCB_TO_GID(cb)]
|
||||
|
||||
#define GRU_NUM_KERNEL_CBR 1
|
||||
#define GRU_NUM_KERNEL_DSR_BYTES 256
|
||||
@ -160,8 +158,10 @@ static void gru_load_kernel_context(struct gru_blade_state *bs, int blade_id)
|
||||
up_read(&bs->bs_kgts_sema);
|
||||
down_write(&bs->bs_kgts_sema);
|
||||
|
||||
if (!bs->bs_kgts)
|
||||
bs->bs_kgts = gru_alloc_gts(NULL, 0, 0, 0, 0);
|
||||
if (!bs->bs_kgts) {
|
||||
bs->bs_kgts = gru_alloc_gts(NULL, 0, 0, 0, 0, 0);
|
||||
bs->bs_kgts->ts_user_blade_id = blade_id;
|
||||
}
|
||||
kgts = bs->bs_kgts;
|
||||
|
||||
if (!kgts->ts_gru) {
|
||||
@ -172,9 +172,9 @@ static void gru_load_kernel_context(struct gru_blade_state *bs, int blade_id)
|
||||
kgts->ts_dsr_au_count = GRU_DS_BYTES_TO_AU(
|
||||
GRU_NUM_KERNEL_DSR_BYTES * ncpus +
|
||||
bs->bs_async_dsr_bytes);
|
||||
while (!gru_assign_gru_context(kgts, blade_id)) {
|
||||
while (!gru_assign_gru_context(kgts)) {
|
||||
msleep(1);
|
||||
gru_steal_context(kgts, blade_id);
|
||||
gru_steal_context(kgts);
|
||||
}
|
||||
gru_load_context(kgts);
|
||||
gru = bs->bs_kgts->ts_gru;
|
||||
@ -200,13 +200,15 @@ static int gru_free_kernel_contexts(void)
|
||||
bs = gru_base[bid];
|
||||
if (!bs)
|
||||
continue;
|
||||
|
||||
/* Ignore busy contexts. Don't want to block here. */
|
||||
if (down_write_trylock(&bs->bs_kgts_sema)) {
|
||||
kgts = bs->bs_kgts;
|
||||
if (kgts && kgts->ts_gru)
|
||||
gru_unload_context(kgts, 0);
|
||||
kfree(kgts);
|
||||
bs->bs_kgts = NULL;
|
||||
up_write(&bs->bs_kgts_sema);
|
||||
kfree(kgts);
|
||||
} else {
|
||||
ret++;
|
||||
}
|
||||
@ -220,13 +222,21 @@ static int gru_free_kernel_contexts(void)
|
||||
static struct gru_blade_state *gru_lock_kernel_context(int blade_id)
|
||||
{
|
||||
struct gru_blade_state *bs;
|
||||
int bid;
|
||||
|
||||
STAT(lock_kernel_context);
|
||||
bs = gru_base[blade_id];
|
||||
again:
|
||||
bid = blade_id < 0 ? uv_numa_blade_id() : blade_id;
|
||||
bs = gru_base[bid];
|
||||
|
||||
/* Handle the case where migration occured while waiting for the sema */
|
||||
down_read(&bs->bs_kgts_sema);
|
||||
if (blade_id < 0 && bid != uv_numa_blade_id()) {
|
||||
up_read(&bs->bs_kgts_sema);
|
||||
goto again;
|
||||
}
|
||||
if (!bs->bs_kgts || !bs->bs_kgts->ts_gru)
|
||||
gru_load_kernel_context(bs, blade_id);
|
||||
gru_load_kernel_context(bs, bid);
|
||||
return bs;
|
||||
|
||||
}
|
||||
@ -255,7 +265,7 @@ static int gru_get_cpu_resources(int dsr_bytes, void **cb, void **dsr)
|
||||
|
||||
BUG_ON(dsr_bytes > GRU_NUM_KERNEL_DSR_BYTES);
|
||||
preempt_disable();
|
||||
bs = gru_lock_kernel_context(uv_numa_blade_id());
|
||||
bs = gru_lock_kernel_context(-1);
|
||||
lcpu = uv_blade_processor_id();
|
||||
*cb = bs->kernel_cb + lcpu * GRU_HANDLE_STRIDE;
|
||||
*dsr = bs->kernel_dsr + lcpu * GRU_NUM_KERNEL_DSR_BYTES;
|
||||
@ -384,13 +394,31 @@ int gru_get_cb_exception_detail(void *cb,
|
||||
struct control_block_extended_exc_detail *excdet)
|
||||
{
|
||||
struct gru_control_block_extended *cbe;
|
||||
struct gru_blade_state *bs;
|
||||
int cbrnum;
|
||||
struct gru_thread_state *kgts = NULL;
|
||||
unsigned long off;
|
||||
int cbrnum, bid;
|
||||
|
||||
bs = KCB_TO_BS(cb);
|
||||
cbrnum = thread_cbr_number(bs->bs_kgts, get_cb_number(cb));
|
||||
/*
|
||||
* Locate kgts for cb. This algorithm is SLOW but
|
||||
* this function is rarely called (ie., almost never).
|
||||
* Performance does not matter.
|
||||
*/
|
||||
for_each_possible_blade(bid) {
|
||||
if (!gru_base[bid])
|
||||
break;
|
||||
kgts = gru_base[bid]->bs_kgts;
|
||||
if (!kgts || !kgts->ts_gru)
|
||||
continue;
|
||||
off = cb - kgts->ts_gru->gs_gru_base_vaddr;
|
||||
if (off < GRU_SIZE)
|
||||
break;
|
||||
kgts = NULL;
|
||||
}
|
||||
BUG_ON(!kgts);
|
||||
cbrnum = thread_cbr_number(kgts, get_cb_number(cb));
|
||||
cbe = get_cbe(GRUBASE(cb), cbrnum);
|
||||
gru_flush_cache(cbe); /* CBE not coherent */
|
||||
sync_core();
|
||||
excdet->opc = cbe->opccpy;
|
||||
excdet->exopc = cbe->exopccpy;
|
||||
excdet->ecause = cbe->ecause;
|
||||
@ -409,8 +437,8 @@ char *gru_get_cb_exception_detail_str(int ret, void *cb,
|
||||
if (ret > 0 && gen->istatus == CBS_EXCEPTION) {
|
||||
gru_get_cb_exception_detail(cb, &excdet);
|
||||
snprintf(buf, size,
|
||||
"GRU exception: cb %p, opc %d, exopc %d, ecause 0x%x,"
|
||||
"excdet0 0x%lx, excdet1 0x%x",
|
||||
"GRU:%d exception: cb %p, opc %d, exopc %d, ecause 0x%x,"
|
||||
"excdet0 0x%lx, excdet1 0x%x", smp_processor_id(),
|
||||
gen, excdet.opc, excdet.exopc, excdet.ecause,
|
||||
excdet.exceptdet0, excdet.exceptdet1);
|
||||
} else {
|
||||
@ -457,9 +485,10 @@ int gru_check_status_proc(void *cb)
|
||||
int ret;
|
||||
|
||||
ret = gen->istatus;
|
||||
if (ret != CBS_EXCEPTION)
|
||||
return ret;
|
||||
return gru_retry_exception(cb);
|
||||
if (ret == CBS_EXCEPTION)
|
||||
ret = gru_retry_exception(cb);
|
||||
rmb();
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
@ -471,7 +500,7 @@ int gru_wait_proc(void *cb)
|
||||
ret = gru_wait_idle_or_exception(gen);
|
||||
if (ret == CBS_EXCEPTION)
|
||||
ret = gru_retry_exception(cb);
|
||||
|
||||
rmb();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -538,7 +567,7 @@ int gru_create_message_queue(struct gru_message_queue_desc *mqd,
|
||||
mqd->mq = mq;
|
||||
mqd->mq_gpa = uv_gpa(mq);
|
||||
mqd->qlines = qlines;
|
||||
mqd->interrupt_pnode = UV_NASID_TO_PNODE(nasid);
|
||||
mqd->interrupt_pnode = nasid >> 1;
|
||||
mqd->interrupt_vector = vector;
|
||||
mqd->interrupt_apicid = apicid;
|
||||
return 0;
|
||||
@ -598,6 +627,8 @@ static int send_noop_message(void *cb, struct gru_message_queue_desc *mqd,
|
||||
ret = MQE_UNEXPECTED_CB_ERR;
|
||||
break;
|
||||
case CBSS_PAGE_OVERFLOW:
|
||||
STAT(mesq_noop_page_overflow);
|
||||
/* fallthru */
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
@ -672,18 +703,6 @@ cberr:
|
||||
return MQE_UNEXPECTED_CB_ERR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a cross-partition interrupt to the SSI that contains the target
|
||||
* message queue. Normally, the interrupt is automatically delivered by hardware
|
||||
* but some error conditions require explicit delivery.
|
||||
*/
|
||||
static void send_message_queue_interrupt(struct gru_message_queue_desc *mqd)
|
||||
{
|
||||
if (mqd->interrupt_vector)
|
||||
uv_hub_send_ipi(mqd->interrupt_pnode, mqd->interrupt_apicid,
|
||||
mqd->interrupt_vector);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle a PUT failure. Note: if message was a 2-line message, one of the
|
||||
* lines might have successfully have been written. Before sending the
|
||||
@ -693,7 +712,8 @@ static void send_message_queue_interrupt(struct gru_message_queue_desc *mqd)
|
||||
static int send_message_put_nacked(void *cb, struct gru_message_queue_desc *mqd,
|
||||
void *mesg, int lines)
|
||||
{
|
||||
unsigned long m;
|
||||
unsigned long m, *val = mesg, gpa, save;
|
||||
int ret;
|
||||
|
||||
m = mqd->mq_gpa + (gru_get_amo_value_head(cb) << 6);
|
||||
if (lines == 2) {
|
||||
@ -704,7 +724,26 @@ static int send_message_put_nacked(void *cb, struct gru_message_queue_desc *mqd,
|
||||
gru_vstore(cb, m, gru_get_tri(mesg), XTYPE_CL, lines, 1, IMA);
|
||||
if (gru_wait(cb) != CBS_IDLE)
|
||||
return MQE_UNEXPECTED_CB_ERR;
|
||||
send_message_queue_interrupt(mqd);
|
||||
|
||||
if (!mqd->interrupt_vector)
|
||||
return MQE_OK;
|
||||
|
||||
/*
|
||||
* Send a cross-partition interrupt to the SSI that contains the target
|
||||
* message queue. Normally, the interrupt is automatically delivered by
|
||||
* hardware but some error conditions require explicit delivery.
|
||||
* Use the GRU to deliver the interrupt. Otherwise partition failures
|
||||
* could cause unrecovered errors.
|
||||
*/
|
||||
gpa = uv_global_gru_mmr_address(mqd->interrupt_pnode, UVH_IPI_INT);
|
||||
save = *val;
|
||||
*val = uv_hub_ipi_value(mqd->interrupt_apicid, mqd->interrupt_vector,
|
||||
dest_Fixed);
|
||||
gru_vstore_phys(cb, gpa, gru_get_tri(mesg), IAA_REGISTER, IMA);
|
||||
ret = gru_wait(cb);
|
||||
*val = save;
|
||||
if (ret != CBS_IDLE)
|
||||
return MQE_UNEXPECTED_CB_ERR;
|
||||
return MQE_OK;
|
||||
}
|
||||
|
||||
@ -739,6 +778,9 @@ static int send_message_failure(void *cb, struct gru_message_queue_desc *mqd,
|
||||
STAT(mesq_send_put_nacked);
|
||||
ret = send_message_put_nacked(cb, mqd, mesg, lines);
|
||||
break;
|
||||
case CBSS_PAGE_OVERFLOW:
|
||||
STAT(mesq_page_overflow);
|
||||
/* fallthru */
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
@ -831,7 +873,6 @@ void *gru_get_next_message(struct gru_message_queue_desc *mqd)
|
||||
int present = mhdr->present;
|
||||
|
||||
/* skip NOOP messages */
|
||||
STAT(mesq_receive);
|
||||
while (present == MQS_NOOP) {
|
||||
gru_free_message(mqd, mhdr);
|
||||
mhdr = mq->next;
|
||||
@ -851,12 +892,36 @@ void *gru_get_next_message(struct gru_message_queue_desc *mqd)
|
||||
if (mhdr->lines == 2)
|
||||
restore_present2(mhdr, mhdr->present2);
|
||||
|
||||
STAT(mesq_receive);
|
||||
return mhdr;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gru_get_next_message);
|
||||
|
||||
/* ---------------------- GRU DATA COPY FUNCTIONS ---------------------------*/
|
||||
|
||||
/*
|
||||
* Load a DW from a global GPA. The GPA can be a memory or MMR address.
|
||||
*/
|
||||
int gru_read_gpa(unsigned long *value, unsigned long gpa)
|
||||
{
|
||||
void *cb;
|
||||
void *dsr;
|
||||
int ret, iaa;
|
||||
|
||||
STAT(read_gpa);
|
||||
if (gru_get_cpu_resources(GRU_NUM_KERNEL_DSR_BYTES, &cb, &dsr))
|
||||
return MQE_BUG_NO_RESOURCES;
|
||||
iaa = gpa >> 62;
|
||||
gru_vload_phys(cb, gpa, gru_get_tri(dsr), iaa, IMA);
|
||||
ret = gru_wait(cb);
|
||||
if (ret == CBS_IDLE)
|
||||
*value = *(unsigned long *)dsr;
|
||||
gru_free_cpu_resources(cb, dsr);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gru_read_gpa);
|
||||
|
||||
|
||||
/*
|
||||
* Copy a block of data using the GRU resources
|
||||
*/
|
||||
@ -898,24 +963,24 @@ static int quicktest0(unsigned long arg)
|
||||
|
||||
gru_vload(cb, uv_gpa(&word0), gru_get_tri(dsr), XTYPE_DW, 1, 1, IMA);
|
||||
if (gru_wait(cb) != CBS_IDLE) {
|
||||
printk(KERN_DEBUG "GRU quicktest0: CBR failure 1\n");
|
||||
printk(KERN_DEBUG "GRU:%d quicktest0: CBR failure 1\n", smp_processor_id());
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (*p != MAGIC) {
|
||||
printk(KERN_DEBUG "GRU: quicktest0 bad magic 0x%lx\n", *p);
|
||||
printk(KERN_DEBUG "GRU:%d quicktest0 bad magic 0x%lx\n", smp_processor_id(), *p);
|
||||
goto done;
|
||||
}
|
||||
gru_vstore(cb, uv_gpa(&word1), gru_get_tri(dsr), XTYPE_DW, 1, 1, IMA);
|
||||
if (gru_wait(cb) != CBS_IDLE) {
|
||||
printk(KERN_DEBUG "GRU quicktest0: CBR failure 2\n");
|
||||
printk(KERN_DEBUG "GRU:%d quicktest0: CBR failure 2\n", smp_processor_id());
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (word0 != word1 || word1 != MAGIC) {
|
||||
printk(KERN_DEBUG
|
||||
"GRU quicktest0 err: found 0x%lx, expected 0x%lx\n",
|
||||
word1, MAGIC);
|
||||
"GRU:%d quicktest0 err: found 0x%lx, expected 0x%lx\n",
|
||||
smp_processor_id(), word1, MAGIC);
|
||||
goto done;
|
||||
}
|
||||
ret = 0;
|
||||
@ -952,8 +1017,11 @@ static int quicktest1(unsigned long arg)
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
if (ret != MQE_QUEUE_FULL || i != 4)
|
||||
if (ret != MQE_QUEUE_FULL || i != 4) {
|
||||
printk(KERN_DEBUG "GRU:%d quicktest1: unexpect status %d, i %d\n",
|
||||
smp_processor_id(), ret, i);
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
m = gru_get_next_message(&mqd);
|
||||
@ -961,7 +1029,12 @@ static int quicktest1(unsigned long arg)
|
||||
break;
|
||||
gru_free_message(&mqd, m);
|
||||
}
|
||||
ret = (i == 4) ? 0 : -EIO;
|
||||
if (i != 4) {
|
||||
printk(KERN_DEBUG "GRU:%d quicktest2: bad message, i %d, m %p, m8 %d\n",
|
||||
smp_processor_id(), i, m, m ? m[8] : -1);
|
||||
goto done;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
done:
|
||||
kfree(p);
|
||||
@ -977,6 +1050,7 @@ static int quicktest2(unsigned long arg)
|
||||
int ret = 0;
|
||||
unsigned long *buf;
|
||||
void *cb0, *cb;
|
||||
struct gru_control_block_status *gen;
|
||||
int i, k, istatus, bytes;
|
||||
|
||||
bytes = numcb * 4 * 8;
|
||||
@ -996,20 +1070,30 @@ static int quicktest2(unsigned long arg)
|
||||
XTYPE_DW, 4, 1, IMA_INTERRUPT);
|
||||
|
||||
ret = 0;
|
||||
for (k = 0; k < numcb; k++) {
|
||||
k = numcb;
|
||||
do {
|
||||
gru_wait_async_cbr(han);
|
||||
for (i = 0; i < numcb; i++) {
|
||||
cb = cb0 + i * GRU_HANDLE_STRIDE;
|
||||
istatus = gru_check_status(cb);
|
||||
if (istatus == CBS_ACTIVE)
|
||||
continue;
|
||||
if (istatus == CBS_EXCEPTION)
|
||||
ret = -EFAULT;
|
||||
else if (buf[i] || buf[i + 1] || buf[i + 2] ||
|
||||
buf[i + 3])
|
||||
ret = -EIO;
|
||||
if (istatus != CBS_ACTIVE && istatus != CBS_CALL_OS)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == numcb)
|
||||
continue;
|
||||
if (istatus != CBS_IDLE) {
|
||||
printk(KERN_DEBUG "GRU:%d quicktest2: cb %d, exception\n", smp_processor_id(), i);
|
||||
ret = -EFAULT;
|
||||
} else if (buf[4 * i] || buf[4 * i + 1] || buf[4 * i + 2] ||
|
||||
buf[4 * i + 3]) {
|
||||
printk(KERN_DEBUG "GRU:%d quicktest2:cb %d, buf 0x%lx, 0x%lx, 0x%lx, 0x%lx\n",
|
||||
smp_processor_id(), i, buf[4 * i], buf[4 * i + 1], buf[4 * i + 2], buf[4 * i + 3]);
|
||||
ret = -EIO;
|
||||
}
|
||||
k--;
|
||||
gen = cb;
|
||||
gen->istatus = CBS_CALL_OS; /* don't handle this CBR again */
|
||||
} while (k);
|
||||
BUG_ON(cmp.done);
|
||||
|
||||
gru_unlock_async_resource(han);
|
||||
@ -1019,6 +1103,22 @@ done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define BUFSIZE 200
|
||||
static int quicktest3(unsigned long arg)
|
||||
{
|
||||
char buf1[BUFSIZE], buf2[BUFSIZE];
|
||||
int ret = 0;
|
||||
|
||||
memset(buf2, 0, sizeof(buf2));
|
||||
memset(buf1, get_cycles() & 255, sizeof(buf1));
|
||||
gru_copy_gpa(uv_gpa(buf2), uv_gpa(buf1), BUFSIZE);
|
||||
if (memcmp(buf1, buf2, BUFSIZE)) {
|
||||
printk(KERN_DEBUG "GRU:%d quicktest3 error\n", smp_processor_id());
|
||||
ret = -EIO;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Debugging only. User hook for various kernel tests
|
||||
* of driver & gru.
|
||||
@ -1037,6 +1137,9 @@ int gru_ktest(unsigned long arg)
|
||||
case 2:
|
||||
ret = quicktest2(arg);
|
||||
break;
|
||||
case 3:
|
||||
ret = quicktest3(arg);
|
||||
break;
|
||||
case 99:
|
||||
ret = gru_free_kernel_contexts();
|
||||
break;
|
||||
|
@ -130,6 +130,20 @@ extern void gru_free_message(struct gru_message_queue_desc *mqd,
|
||||
extern void *gru_get_next_message(struct gru_message_queue_desc *mqd);
|
||||
|
||||
|
||||
/*
|
||||
* Read a GRU global GPA. Source can be located in a remote partition.
|
||||
*
|
||||
* Input:
|
||||
* value memory address where MMR value is returned
|
||||
* gpa source numalink physical address of GPA
|
||||
*
|
||||
* Output:
|
||||
* 0 OK
|
||||
* >0 error
|
||||
*/
|
||||
int gru_read_gpa(unsigned long *value, unsigned long gpa);
|
||||
|
||||
|
||||
/*
|
||||
* Copy data using the GRU. Source or destination can be located in a remote
|
||||
* partition.
|
||||
|
@ -63,18 +63,9 @@
|
||||
#define THREAD_POINTER(p, th) (p + GRU_GSEG_PAGESIZE * (th))
|
||||
#define GSEG_START(cb) ((void *)((unsigned long)(cb) & ~(GRU_GSEG_PAGESIZE - 1)))
|
||||
|
||||
/*
|
||||
* Statictics kept on a per-GTS basis.
|
||||
*/
|
||||
struct gts_statistics {
|
||||
unsigned long fmm_tlbdropin;
|
||||
unsigned long upm_tlbdropin;
|
||||
unsigned long context_stolen;
|
||||
};
|
||||
|
||||
struct gru_get_gseg_statistics_req {
|
||||
unsigned long gseg;
|
||||
struct gts_statistics stats;
|
||||
unsigned long gseg;
|
||||
struct gru_gseg_statistics stats;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -86,6 +77,7 @@ struct gru_create_context_req {
|
||||
unsigned int control_blocks;
|
||||
unsigned int maximum_thread_count;
|
||||
unsigned int options;
|
||||
unsigned char tlb_preload_count;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -98,11 +90,12 @@ struct gru_unload_context_req {
|
||||
/*
|
||||
* Structure used to set context options
|
||||
*/
|
||||
enum {sco_gseg_owner, sco_cch_req_slice};
|
||||
enum {sco_gseg_owner, sco_cch_req_slice, sco_blade_chiplet};
|
||||
struct gru_set_context_option_req {
|
||||
unsigned long gseg;
|
||||
int op;
|
||||
unsigned long val1;
|
||||
int val0;
|
||||
long val1;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -124,6 +117,8 @@ struct gru_dump_chiplet_state_req {
|
||||
int ctxnum;
|
||||
char data_opt;
|
||||
char lock_cch;
|
||||
char flush_cbrs;
|
||||
char fill[10];
|
||||
pid_t pid;
|
||||
void *buf;
|
||||
size_t buflen;
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/err.h>
|
||||
#include <asm/uv/uv_hub.h>
|
||||
#include "gru.h"
|
||||
#include "grutables.h"
|
||||
@ -48,12 +49,20 @@ struct device *grudev = &gru_device;
|
||||
/*
|
||||
* Select a gru fault map to be used by the current cpu. Note that
|
||||
* multiple cpus may be using the same map.
|
||||
* ZZZ should "shift" be used?? Depends on HT cpu numbering
|
||||
* ZZZ should be inline but did not work on emulator
|
||||
*/
|
||||
int gru_cpu_fault_map_id(void)
|
||||
{
|
||||
#ifdef CONFIG_IA64
|
||||
return uv_blade_processor_id() % GRU_NUM_TFM;
|
||||
#else
|
||||
int cpu = smp_processor_id();
|
||||
int id, core;
|
||||
|
||||
core = uv_cpu_core_number(cpu);
|
||||
id = core + UV_MAX_INT_CORES * uv_cpu_socket_number(cpu);
|
||||
return id;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*--------- ASID Management -------------------------------------------
|
||||
@ -286,7 +295,8 @@ static void gru_unload_mm_tracker(struct gru_state *gru,
|
||||
void gts_drop(struct gru_thread_state *gts)
|
||||
{
|
||||
if (gts && atomic_dec_return(>s->ts_refcnt) == 0) {
|
||||
gru_drop_mmu_notifier(gts->ts_gms);
|
||||
if (gts->ts_gms)
|
||||
gru_drop_mmu_notifier(gts->ts_gms);
|
||||
kfree(gts);
|
||||
STAT(gts_free);
|
||||
}
|
||||
@ -310,16 +320,18 @@ static struct gru_thread_state *gru_find_current_gts_nolock(struct gru_vma_data
|
||||
* Allocate a thread state structure.
|
||||
*/
|
||||
struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma,
|
||||
int cbr_au_count, int dsr_au_count, int options, int tsid)
|
||||
int cbr_au_count, int dsr_au_count,
|
||||
unsigned char tlb_preload_count, int options, int tsid)
|
||||
{
|
||||
struct gru_thread_state *gts;
|
||||
struct gru_mm_struct *gms;
|
||||
int bytes;
|
||||
|
||||
bytes = DSR_BYTES(dsr_au_count) + CBR_BYTES(cbr_au_count);
|
||||
bytes += sizeof(struct gru_thread_state);
|
||||
gts = kmalloc(bytes, GFP_KERNEL);
|
||||
if (!gts)
|
||||
return NULL;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
STAT(gts_alloc);
|
||||
memset(gts, 0, sizeof(struct gru_thread_state)); /* zero out header */
|
||||
@ -327,7 +339,10 @@ struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma,
|
||||
mutex_init(>s->ts_ctxlock);
|
||||
gts->ts_cbr_au_count = cbr_au_count;
|
||||
gts->ts_dsr_au_count = dsr_au_count;
|
||||
gts->ts_tlb_preload_count = tlb_preload_count;
|
||||
gts->ts_user_options = options;
|
||||
gts->ts_user_blade_id = -1;
|
||||
gts->ts_user_chiplet_id = -1;
|
||||
gts->ts_tsid = tsid;
|
||||
gts->ts_ctxnum = NULLCTX;
|
||||
gts->ts_tlb_int_select = -1;
|
||||
@ -336,9 +351,10 @@ struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma,
|
||||
if (vma) {
|
||||
gts->ts_mm = current->mm;
|
||||
gts->ts_vma = vma;
|
||||
gts->ts_gms = gru_register_mmu_notifier();
|
||||
if (!gts->ts_gms)
|
||||
gms = gru_register_mmu_notifier();
|
||||
if (IS_ERR(gms))
|
||||
goto err;
|
||||
gts->ts_gms = gms;
|
||||
}
|
||||
|
||||
gru_dbg(grudev, "alloc gts %p\n", gts);
|
||||
@ -346,7 +362,7 @@ struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma,
|
||||
|
||||
err:
|
||||
gts_drop(gts);
|
||||
return NULL;
|
||||
return ERR_CAST(gms);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -360,6 +376,7 @@ struct gru_vma_data *gru_alloc_vma_data(struct vm_area_struct *vma, int tsid)
|
||||
if (!vdata)
|
||||
return NULL;
|
||||
|
||||
STAT(vdata_alloc);
|
||||
INIT_LIST_HEAD(&vdata->vd_head);
|
||||
spin_lock_init(&vdata->vd_lock);
|
||||
gru_dbg(grudev, "alloc vdata %p\n", vdata);
|
||||
@ -392,10 +409,12 @@ struct gru_thread_state *gru_alloc_thread_state(struct vm_area_struct *vma,
|
||||
struct gru_vma_data *vdata = vma->vm_private_data;
|
||||
struct gru_thread_state *gts, *ngts;
|
||||
|
||||
gts = gru_alloc_gts(vma, vdata->vd_cbr_au_count, vdata->vd_dsr_au_count,
|
||||
gts = gru_alloc_gts(vma, vdata->vd_cbr_au_count,
|
||||
vdata->vd_dsr_au_count,
|
||||
vdata->vd_tlb_preload_count,
|
||||
vdata->vd_user_options, tsid);
|
||||
if (!gts)
|
||||
return NULL;
|
||||
if (IS_ERR(gts))
|
||||
return gts;
|
||||
|
||||
spin_lock(&vdata->vd_lock);
|
||||
ngts = gru_find_current_gts_nolock(vdata, tsid);
|
||||
@ -493,6 +512,9 @@ static void gru_load_context_data(void *save, void *grubase, int ctxnum,
|
||||
memset(cbe + i * GRU_HANDLE_STRIDE, 0,
|
||||
GRU_CACHE_LINE_BYTES);
|
||||
}
|
||||
/* Flush CBE to hide race in context restart */
|
||||
mb();
|
||||
gru_flush_cache(cbe + i * GRU_HANDLE_STRIDE);
|
||||
cb += GRU_HANDLE_STRIDE;
|
||||
}
|
||||
|
||||
@ -513,6 +535,12 @@ static void gru_unload_context_data(void *save, void *grubase, int ctxnum,
|
||||
cb = gseg + GRU_CB_BASE;
|
||||
cbe = grubase + GRU_CBE_BASE;
|
||||
length = hweight64(dsrmap) * GRU_DSR_AU_BYTES;
|
||||
|
||||
/* CBEs may not be coherent. Flush them from cache */
|
||||
for_each_cbr_in_allocation_map(i, &cbrmap, scr)
|
||||
gru_flush_cache(cbe + i * GRU_HANDLE_STRIDE);
|
||||
mb(); /* Let the CL flush complete */
|
||||
|
||||
gru_prefetch_context(gseg, cb, cbe, cbrmap, length);
|
||||
|
||||
for_each_cbr_in_allocation_map(i, &cbrmap, scr) {
|
||||
@ -533,7 +561,8 @@ void gru_unload_context(struct gru_thread_state *gts, int savestate)
|
||||
zap_vma_ptes(gts->ts_vma, UGRUADDR(gts), GRU_GSEG_PAGESIZE);
|
||||
cch = get_cch(gru->gs_gru_base_vaddr, ctxnum);
|
||||
|
||||
gru_dbg(grudev, "gts %p\n", gts);
|
||||
gru_dbg(grudev, "gts %p, cbrmap 0x%lx, dsrmap 0x%lx\n",
|
||||
gts, gts->ts_cbr_map, gts->ts_dsr_map);
|
||||
lock_cch_handle(cch);
|
||||
if (cch_interrupt_sync(cch))
|
||||
BUG();
|
||||
@ -549,7 +578,6 @@ void gru_unload_context(struct gru_thread_state *gts, int savestate)
|
||||
|
||||
if (cch_deallocate(cch))
|
||||
BUG();
|
||||
gts->ts_force_unload = 0; /* ts_force_unload locked by CCH lock */
|
||||
unlock_cch_handle(cch);
|
||||
|
||||
gru_free_gru_context(gts);
|
||||
@ -565,9 +593,7 @@ void gru_load_context(struct gru_thread_state *gts)
|
||||
struct gru_context_configuration_handle *cch;
|
||||
int i, err, asid, ctxnum = gts->ts_ctxnum;
|
||||
|
||||
gru_dbg(grudev, "gts %p\n", gts);
|
||||
cch = get_cch(gru->gs_gru_base_vaddr, ctxnum);
|
||||
|
||||
lock_cch_handle(cch);
|
||||
cch->tfm_fault_bit_enable =
|
||||
(gts->ts_user_options == GRU_OPT_MISS_FMM_POLL
|
||||
@ -591,6 +617,7 @@ void gru_load_context(struct gru_thread_state *gts)
|
||||
cch->unmap_enable = 1;
|
||||
cch->tfm_done_bit_enable = 1;
|
||||
cch->cb_int_enable = 1;
|
||||
cch->tlb_int_select = 0; /* For now, ints go to cpu 0 */
|
||||
} else {
|
||||
cch->unmap_enable = 0;
|
||||
cch->tfm_done_bit_enable = 0;
|
||||
@ -616,17 +643,18 @@ void gru_load_context(struct gru_thread_state *gts)
|
||||
if (cch_start(cch))
|
||||
BUG();
|
||||
unlock_cch_handle(cch);
|
||||
|
||||
gru_dbg(grudev, "gid %d, gts %p, cbrmap 0x%lx, dsrmap 0x%lx, tie %d, tis %d\n",
|
||||
gts->ts_gru->gs_gid, gts, gts->ts_cbr_map, gts->ts_dsr_map,
|
||||
(gts->ts_user_options == GRU_OPT_MISS_FMM_INTR), gts->ts_tlb_int_select);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update fields in an active CCH:
|
||||
* - retarget interrupts on local blade
|
||||
* - update sizeavail mask
|
||||
* - force a delayed context unload by clearing the CCH asids. This
|
||||
* forces TLB misses for new GRU instructions. The context is unloaded
|
||||
* when the next TLB miss occurs.
|
||||
*/
|
||||
int gru_update_cch(struct gru_thread_state *gts, int force_unload)
|
||||
int gru_update_cch(struct gru_thread_state *gts)
|
||||
{
|
||||
struct gru_context_configuration_handle *cch;
|
||||
struct gru_state *gru = gts->ts_gru;
|
||||
@ -640,21 +668,13 @@ int gru_update_cch(struct gru_thread_state *gts, int force_unload)
|
||||
goto exit;
|
||||
if (cch_interrupt(cch))
|
||||
BUG();
|
||||
if (!force_unload) {
|
||||
for (i = 0; i < 8; i++)
|
||||
cch->sizeavail[i] = gts->ts_sizeavail;
|
||||
gts->ts_tlb_int_select = gru_cpu_fault_map_id();
|
||||
cch->tlb_int_select = gru_cpu_fault_map_id();
|
||||
cch->tfm_fault_bit_enable =
|
||||
(gts->ts_user_options == GRU_OPT_MISS_FMM_POLL
|
||||
|| gts->ts_user_options == GRU_OPT_MISS_FMM_INTR);
|
||||
} else {
|
||||
for (i = 0; i < 8; i++)
|
||||
cch->asid[i] = 0;
|
||||
cch->tfm_fault_bit_enable = 0;
|
||||
cch->tlb_int_enable = 0;
|
||||
gts->ts_force_unload = 1;
|
||||
}
|
||||
for (i = 0; i < 8; i++)
|
||||
cch->sizeavail[i] = gts->ts_sizeavail;
|
||||
gts->ts_tlb_int_select = gru_cpu_fault_map_id();
|
||||
cch->tlb_int_select = gru_cpu_fault_map_id();
|
||||
cch->tfm_fault_bit_enable =
|
||||
(gts->ts_user_options == GRU_OPT_MISS_FMM_POLL
|
||||
|| gts->ts_user_options == GRU_OPT_MISS_FMM_INTR);
|
||||
if (cch_start(cch))
|
||||
BUG();
|
||||
ret = 1;
|
||||
@ -679,7 +699,54 @@ static int gru_retarget_intr(struct gru_thread_state *gts)
|
||||
|
||||
gru_dbg(grudev, "retarget from %d to %d\n", gts->ts_tlb_int_select,
|
||||
gru_cpu_fault_map_id());
|
||||
return gru_update_cch(gts, 0);
|
||||
return gru_update_cch(gts);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if a GRU context is allowed to use a specific chiplet. By default
|
||||
* a context is assigned to any blade-local chiplet. However, users can
|
||||
* override this.
|
||||
* Returns 1 if assignment allowed, 0 otherwise
|
||||
*/
|
||||
static int gru_check_chiplet_assignment(struct gru_state *gru,
|
||||
struct gru_thread_state *gts)
|
||||
{
|
||||
int blade_id;
|
||||
int chiplet_id;
|
||||
|
||||
blade_id = gts->ts_user_blade_id;
|
||||
if (blade_id < 0)
|
||||
blade_id = uv_numa_blade_id();
|
||||
|
||||
chiplet_id = gts->ts_user_chiplet_id;
|
||||
return gru->gs_blade_id == blade_id &&
|
||||
(chiplet_id < 0 || chiplet_id == gru->gs_chiplet_id);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unload the gru context if it is not assigned to the correct blade or
|
||||
* chiplet. Misassignment can occur if the process migrates to a different
|
||||
* blade or if the user changes the selected blade/chiplet.
|
||||
*/
|
||||
void gru_check_context_placement(struct gru_thread_state *gts)
|
||||
{
|
||||
struct gru_state *gru;
|
||||
|
||||
/*
|
||||
* If the current task is the context owner, verify that the
|
||||
* context is correctly placed. This test is skipped for non-owner
|
||||
* references. Pthread apps use non-owner references to the CBRs.
|
||||
*/
|
||||
gru = gts->ts_gru;
|
||||
if (!gru || gts->ts_tgid_owner != current->tgid)
|
||||
return;
|
||||
|
||||
if (!gru_check_chiplet_assignment(gru, gts)) {
|
||||
STAT(check_context_unload);
|
||||
gru_unload_context(gts, 1);
|
||||
} else if (gru_retarget_intr(gts)) {
|
||||
STAT(check_context_retarget_intr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -712,13 +779,17 @@ static void gts_stolen(struct gru_thread_state *gts,
|
||||
}
|
||||
}
|
||||
|
||||
void gru_steal_context(struct gru_thread_state *gts, int blade_id)
|
||||
void gru_steal_context(struct gru_thread_state *gts)
|
||||
{
|
||||
struct gru_blade_state *blade;
|
||||
struct gru_state *gru, *gru0;
|
||||
struct gru_thread_state *ngts = NULL;
|
||||
int ctxnum, ctxnum0, flag = 0, cbr, dsr;
|
||||
int blade_id;
|
||||
|
||||
blade_id = gts->ts_user_blade_id;
|
||||
if (blade_id < 0)
|
||||
blade_id = uv_numa_blade_id();
|
||||
cbr = gts->ts_cbr_au_count;
|
||||
dsr = gts->ts_dsr_au_count;
|
||||
|
||||
@ -729,35 +800,39 @@ void gru_steal_context(struct gru_thread_state *gts, int blade_id)
|
||||
gru = blade->bs_lru_gru;
|
||||
if (ctxnum == 0)
|
||||
gru = next_gru(blade, gru);
|
||||
blade->bs_lru_gru = gru;
|
||||
blade->bs_lru_ctxnum = ctxnum;
|
||||
ctxnum0 = ctxnum;
|
||||
gru0 = gru;
|
||||
while (1) {
|
||||
if (check_gru_resources(gru, cbr, dsr, GRU_NUM_CCH))
|
||||
break;
|
||||
spin_lock(&gru->gs_lock);
|
||||
for (; ctxnum < GRU_NUM_CCH; ctxnum++) {
|
||||
if (flag && gru == gru0 && ctxnum == ctxnum0)
|
||||
if (gru_check_chiplet_assignment(gru, gts)) {
|
||||
if (check_gru_resources(gru, cbr, dsr, GRU_NUM_CCH))
|
||||
break;
|
||||
ngts = gru->gs_gts[ctxnum];
|
||||
/*
|
||||
* We are grabbing locks out of order, so trylock is
|
||||
* needed. GTSs are usually not locked, so the odds of
|
||||
* success are high. If trylock fails, try to steal a
|
||||
* different GSEG.
|
||||
*/
|
||||
if (ngts && is_gts_stealable(ngts, blade))
|
||||
spin_lock(&gru->gs_lock);
|
||||
for (; ctxnum < GRU_NUM_CCH; ctxnum++) {
|
||||
if (flag && gru == gru0 && ctxnum == ctxnum0)
|
||||
break;
|
||||
ngts = gru->gs_gts[ctxnum];
|
||||
/*
|
||||
* We are grabbing locks out of order, so trylock is
|
||||
* needed. GTSs are usually not locked, so the odds of
|
||||
* success are high. If trylock fails, try to steal a
|
||||
* different GSEG.
|
||||
*/
|
||||
if (ngts && is_gts_stealable(ngts, blade))
|
||||
break;
|
||||
ngts = NULL;
|
||||
}
|
||||
spin_unlock(&gru->gs_lock);
|
||||
if (ngts || (flag && gru == gru0 && ctxnum == ctxnum0))
|
||||
break;
|
||||
ngts = NULL;
|
||||
flag = 1;
|
||||
}
|
||||
spin_unlock(&gru->gs_lock);
|
||||
if (ngts || (flag && gru == gru0 && ctxnum == ctxnum0))
|
||||
if (flag && gru == gru0)
|
||||
break;
|
||||
flag = 1;
|
||||
ctxnum = 0;
|
||||
gru = next_gru(blade, gru);
|
||||
}
|
||||
blade->bs_lru_gru = gru;
|
||||
blade->bs_lru_ctxnum = ctxnum;
|
||||
spin_unlock(&blade->bs_lock);
|
||||
|
||||
if (ngts) {
|
||||
@ -775,20 +850,35 @@ void gru_steal_context(struct gru_thread_state *gts, int blade_id)
|
||||
hweight64(gru->gs_dsr_map));
|
||||
}
|
||||
|
||||
/*
|
||||
* Assign a gru context.
|
||||
*/
|
||||
static int gru_assign_context_number(struct gru_state *gru)
|
||||
{
|
||||
int ctxnum;
|
||||
|
||||
ctxnum = find_first_zero_bit(&gru->gs_context_map, GRU_NUM_CCH);
|
||||
__set_bit(ctxnum, &gru->gs_context_map);
|
||||
return ctxnum;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan the GRUs on the local blade & assign a GRU context.
|
||||
*/
|
||||
struct gru_state *gru_assign_gru_context(struct gru_thread_state *gts,
|
||||
int blade)
|
||||
struct gru_state *gru_assign_gru_context(struct gru_thread_state *gts)
|
||||
{
|
||||
struct gru_state *gru, *grux;
|
||||
int i, max_active_contexts;
|
||||
int blade_id = gts->ts_user_blade_id;
|
||||
|
||||
|
||||
if (blade_id < 0)
|
||||
blade_id = uv_numa_blade_id();
|
||||
again:
|
||||
gru = NULL;
|
||||
max_active_contexts = GRU_NUM_CCH;
|
||||
for_each_gru_on_blade(grux, blade, i) {
|
||||
for_each_gru_on_blade(grux, blade_id, i) {
|
||||
if (!gru_check_chiplet_assignment(grux, gts))
|
||||
continue;
|
||||
if (check_gru_resources(grux, gts->ts_cbr_au_count,
|
||||
gts->ts_dsr_au_count,
|
||||
max_active_contexts)) {
|
||||
@ -809,12 +899,9 @@ again:
|
||||
reserve_gru_resources(gru, gts);
|
||||
gts->ts_gru = gru;
|
||||
gts->ts_blade = gru->gs_blade_id;
|
||||
gts->ts_ctxnum =
|
||||
find_first_zero_bit(&gru->gs_context_map, GRU_NUM_CCH);
|
||||
BUG_ON(gts->ts_ctxnum == GRU_NUM_CCH);
|
||||
gts->ts_ctxnum = gru_assign_context_number(gru);
|
||||
atomic_inc(>s->ts_refcnt);
|
||||
gru->gs_gts[gts->ts_ctxnum] = gts;
|
||||
__set_bit(gts->ts_ctxnum, &gru->gs_context_map);
|
||||
spin_unlock(&gru->gs_lock);
|
||||
|
||||
STAT(assign_context);
|
||||
@ -842,7 +929,6 @@ int gru_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
|
||||
{
|
||||
struct gru_thread_state *gts;
|
||||
unsigned long paddr, vaddr;
|
||||
int blade_id;
|
||||
|
||||
vaddr = (unsigned long)vmf->virtual_address;
|
||||
gru_dbg(grudev, "vma %p, vaddr 0x%lx (0x%lx)\n",
|
||||
@ -857,28 +943,18 @@ int gru_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
|
||||
again:
|
||||
mutex_lock(>s->ts_ctxlock);
|
||||
preempt_disable();
|
||||
blade_id = uv_numa_blade_id();
|
||||
|
||||
if (gts->ts_gru) {
|
||||
if (gts->ts_gru->gs_blade_id != blade_id) {
|
||||
STAT(migrated_nopfn_unload);
|
||||
gru_unload_context(gts, 1);
|
||||
} else {
|
||||
if (gru_retarget_intr(gts))
|
||||
STAT(migrated_nopfn_retarget);
|
||||
}
|
||||
}
|
||||
gru_check_context_placement(gts);
|
||||
|
||||
if (!gts->ts_gru) {
|
||||
STAT(load_user_context);
|
||||
if (!gru_assign_gru_context(gts, blade_id)) {
|
||||
if (!gru_assign_gru_context(gts)) {
|
||||
preempt_enable();
|
||||
mutex_unlock(>s->ts_ctxlock);
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule_timeout(GRU_ASSIGN_DELAY); /* true hack ZZZ */
|
||||
blade_id = uv_numa_blade_id();
|
||||
if (gts->ts_steal_jiffies + GRU_STEAL_DELAY < jiffies)
|
||||
gru_steal_context(gts, blade_id);
|
||||
gru_steal_context(gts);
|
||||
goto again;
|
||||
}
|
||||
gru_load_context(gts);
|
||||
|
@ -36,8 +36,7 @@ static void printstat_val(struct seq_file *s, atomic_long_t *v, char *id)
|
||||
{
|
||||
unsigned long val = atomic_long_read(v);
|
||||
|
||||
if (val)
|
||||
seq_printf(s, "%16lu %s\n", val, id);
|
||||
seq_printf(s, "%16lu %s\n", val, id);
|
||||
}
|
||||
|
||||
static int statistics_show(struct seq_file *s, void *p)
|
||||
@ -46,7 +45,8 @@ static int statistics_show(struct seq_file *s, void *p)
|
||||
printstat(s, vdata_free);
|
||||
printstat(s, gts_alloc);
|
||||
printstat(s, gts_free);
|
||||
printstat(s, vdata_double_alloc);
|
||||
printstat(s, gms_alloc);
|
||||
printstat(s, gms_free);
|
||||
printstat(s, gts_double_allocate);
|
||||
printstat(s, assign_context);
|
||||
printstat(s, assign_context_failed);
|
||||
@ -59,28 +59,25 @@ static int statistics_show(struct seq_file *s, void *p)
|
||||
printstat(s, steal_kernel_context);
|
||||
printstat(s, steal_context_failed);
|
||||
printstat(s, nopfn);
|
||||
printstat(s, break_cow);
|
||||
printstat(s, asid_new);
|
||||
printstat(s, asid_next);
|
||||
printstat(s, asid_wrap);
|
||||
printstat(s, asid_reuse);
|
||||
printstat(s, intr);
|
||||
printstat(s, intr_cbr);
|
||||
printstat(s, intr_tfh);
|
||||
printstat(s, intr_spurious);
|
||||
printstat(s, intr_mm_lock_failed);
|
||||
printstat(s, call_os);
|
||||
printstat(s, call_os_offnode_reference);
|
||||
printstat(s, call_os_check_for_bug);
|
||||
printstat(s, call_os_wait_queue);
|
||||
printstat(s, user_flush_tlb);
|
||||
printstat(s, user_unload_context);
|
||||
printstat(s, user_exception);
|
||||
printstat(s, set_context_option);
|
||||
printstat(s, migrate_check);
|
||||
printstat(s, migrated_retarget);
|
||||
printstat(s, migrated_unload);
|
||||
printstat(s, migrated_unload_delay);
|
||||
printstat(s, migrated_nopfn_retarget);
|
||||
printstat(s, migrated_nopfn_unload);
|
||||
printstat(s, check_context_retarget_intr);
|
||||
printstat(s, check_context_unload);
|
||||
printstat(s, tlb_dropin);
|
||||
printstat(s, tlb_preload_page);
|
||||
printstat(s, tlb_dropin_fail_no_asid);
|
||||
printstat(s, tlb_dropin_fail_upm);
|
||||
printstat(s, tlb_dropin_fail_invalid);
|
||||
@ -88,16 +85,15 @@ static int statistics_show(struct seq_file *s, void *p)
|
||||
printstat(s, tlb_dropin_fail_idle);
|
||||
printstat(s, tlb_dropin_fail_fmm);
|
||||
printstat(s, tlb_dropin_fail_no_exception);
|
||||
printstat(s, tlb_dropin_fail_no_exception_war);
|
||||
printstat(s, tfh_stale_on_fault);
|
||||
printstat(s, mmu_invalidate_range);
|
||||
printstat(s, mmu_invalidate_page);
|
||||
printstat(s, mmu_clear_flush_young);
|
||||
printstat(s, flush_tlb);
|
||||
printstat(s, flush_tlb_gru);
|
||||
printstat(s, flush_tlb_gru_tgh);
|
||||
printstat(s, flush_tlb_gru_zero_asid);
|
||||
printstat(s, copy_gpa);
|
||||
printstat(s, read_gpa);
|
||||
printstat(s, mesq_receive);
|
||||
printstat(s, mesq_receive_none);
|
||||
printstat(s, mesq_send);
|
||||
@ -108,7 +104,6 @@ static int statistics_show(struct seq_file *s, void *p)
|
||||
printstat(s, mesq_send_qlimit_reached);
|
||||
printstat(s, mesq_send_amo_nacked);
|
||||
printstat(s, mesq_send_put_nacked);
|
||||
printstat(s, mesq_qf_not_full);
|
||||
printstat(s, mesq_qf_locked);
|
||||
printstat(s, mesq_qf_noop_not_full);
|
||||
printstat(s, mesq_qf_switch_head_failed);
|
||||
@ -118,6 +113,7 @@ static int statistics_show(struct seq_file *s, void *p)
|
||||
printstat(s, mesq_noop_qlimit_reached);
|
||||
printstat(s, mesq_noop_amo_nacked);
|
||||
printstat(s, mesq_noop_put_nacked);
|
||||
printstat(s, mesq_noop_page_overflow);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -133,8 +129,10 @@ static int mcs_statistics_show(struct seq_file *s, void *p)
|
||||
int op;
|
||||
unsigned long total, count, max;
|
||||
static char *id[] = {"cch_allocate", "cch_start", "cch_interrupt",
|
||||
"cch_interrupt_sync", "cch_deallocate", "tgh_invalidate"};
|
||||
"cch_interrupt_sync", "cch_deallocate", "tfh_write_only",
|
||||
"tfh_write_restart", "tgh_invalidate"};
|
||||
|
||||
seq_printf(s, "%-20s%12s%12s%12s\n", "#id", "count", "aver-clks", "max-clks");
|
||||
for (op = 0; op < mcsop_last; op++) {
|
||||
count = atomic_long_read(&mcs_op_statistics[op].count);
|
||||
total = atomic_long_read(&mcs_op_statistics[op].total);
|
||||
@ -154,6 +152,7 @@ static ssize_t mcs_statistics_write(struct file *file,
|
||||
|
||||
static int options_show(struct seq_file *s, void *p)
|
||||
{
|
||||
seq_printf(s, "#bitmask: 1=trace, 2=statistics\n");
|
||||
seq_printf(s, "0x%lx\n", gru_options);
|
||||
return 0;
|
||||
}
|
||||
@ -183,16 +182,17 @@ static int cch_seq_show(struct seq_file *file, void *data)
|
||||
const char *mode[] = { "??", "UPM", "INTR", "OS_POLL" };
|
||||
|
||||
if (gid == 0)
|
||||
seq_printf(file, "#%5s%5s%6s%9s%6s%8s%8s\n", "gid", "bid",
|
||||
"ctx#", "pid", "cbrs", "dsbytes", "mode");
|
||||
seq_printf(file, "#%5s%5s%6s%7s%9s%6s%8s%8s\n", "gid", "bid",
|
||||
"ctx#", "asid", "pid", "cbrs", "dsbytes", "mode");
|
||||
if (gru)
|
||||
for (i = 0; i < GRU_NUM_CCH; i++) {
|
||||
ts = gru->gs_gts[i];
|
||||
if (!ts)
|
||||
continue;
|
||||
seq_printf(file, " %5d%5d%6d%9d%6d%8d%8s\n",
|
||||
seq_printf(file, " %5d%5d%6d%7d%9d%6d%8d%8s\n",
|
||||
gru->gs_gid, gru->gs_blade_id, i,
|
||||
ts->ts_tgid_owner,
|
||||
is_kernel_context(ts) ? 0 : ts->ts_gms->ms_asids[gid].mt_asid,
|
||||
is_kernel_context(ts) ? 0 : ts->ts_tgid_owner,
|
||||
ts->ts_cbr_au_count * GRU_CBR_AU_SIZE,
|
||||
ts->ts_cbr_au_count * GRU_DSR_AU_BYTES,
|
||||
mode[ts->ts_user_options &
|
||||
@ -355,7 +355,7 @@ static void delete_proc_files(void)
|
||||
for (p = proc_files; p->name; p++)
|
||||
if (p->entry)
|
||||
remove_proc_entry(p->name, proc_gru);
|
||||
remove_proc_entry("gru", NULL);
|
||||
remove_proc_entry("gru", proc_gru->parent);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,7 +161,7 @@ extern unsigned int gru_max_gids;
|
||||
#define GRU_MAX_GRUS (GRU_MAX_BLADES * GRU_CHIPLETS_PER_BLADE)
|
||||
|
||||
#define GRU_DRIVER_ID_STR "SGI GRU Device Driver"
|
||||
#define GRU_DRIVER_VERSION_STR "0.80"
|
||||
#define GRU_DRIVER_VERSION_STR "0.85"
|
||||
|
||||
/*
|
||||
* GRU statistics.
|
||||
@ -171,7 +171,8 @@ struct gru_stats_s {
|
||||
atomic_long_t vdata_free;
|
||||
atomic_long_t gts_alloc;
|
||||
atomic_long_t gts_free;
|
||||
atomic_long_t vdata_double_alloc;
|
||||
atomic_long_t gms_alloc;
|
||||
atomic_long_t gms_free;
|
||||
atomic_long_t gts_double_allocate;
|
||||
atomic_long_t assign_context;
|
||||
atomic_long_t assign_context_failed;
|
||||
@ -184,28 +185,25 @@ struct gru_stats_s {
|
||||
atomic_long_t steal_kernel_context;
|
||||
atomic_long_t steal_context_failed;
|
||||
atomic_long_t nopfn;
|
||||
atomic_long_t break_cow;
|
||||
atomic_long_t asid_new;
|
||||
atomic_long_t asid_next;
|
||||
atomic_long_t asid_wrap;
|
||||
atomic_long_t asid_reuse;
|
||||
atomic_long_t intr;
|
||||
atomic_long_t intr_cbr;
|
||||
atomic_long_t intr_tfh;
|
||||
atomic_long_t intr_spurious;
|
||||
atomic_long_t intr_mm_lock_failed;
|
||||
atomic_long_t call_os;
|
||||
atomic_long_t call_os_offnode_reference;
|
||||
atomic_long_t call_os_check_for_bug;
|
||||
atomic_long_t call_os_wait_queue;
|
||||
atomic_long_t user_flush_tlb;
|
||||
atomic_long_t user_unload_context;
|
||||
atomic_long_t user_exception;
|
||||
atomic_long_t set_context_option;
|
||||
atomic_long_t migrate_check;
|
||||
atomic_long_t migrated_retarget;
|
||||
atomic_long_t migrated_unload;
|
||||
atomic_long_t migrated_unload_delay;
|
||||
atomic_long_t migrated_nopfn_retarget;
|
||||
atomic_long_t migrated_nopfn_unload;
|
||||
atomic_long_t check_context_retarget_intr;
|
||||
atomic_long_t check_context_unload;
|
||||
atomic_long_t tlb_dropin;
|
||||
atomic_long_t tlb_preload_page;
|
||||
atomic_long_t tlb_dropin_fail_no_asid;
|
||||
atomic_long_t tlb_dropin_fail_upm;
|
||||
atomic_long_t tlb_dropin_fail_invalid;
|
||||
@ -213,17 +211,16 @@ struct gru_stats_s {
|
||||
atomic_long_t tlb_dropin_fail_idle;
|
||||
atomic_long_t tlb_dropin_fail_fmm;
|
||||
atomic_long_t tlb_dropin_fail_no_exception;
|
||||
atomic_long_t tlb_dropin_fail_no_exception_war;
|
||||
atomic_long_t tfh_stale_on_fault;
|
||||
atomic_long_t mmu_invalidate_range;
|
||||
atomic_long_t mmu_invalidate_page;
|
||||
atomic_long_t mmu_clear_flush_young;
|
||||
atomic_long_t flush_tlb;
|
||||
atomic_long_t flush_tlb_gru;
|
||||
atomic_long_t flush_tlb_gru_tgh;
|
||||
atomic_long_t flush_tlb_gru_zero_asid;
|
||||
|
||||
atomic_long_t copy_gpa;
|
||||
atomic_long_t read_gpa;
|
||||
|
||||
atomic_long_t mesq_receive;
|
||||
atomic_long_t mesq_receive_none;
|
||||
@ -235,7 +232,7 @@ struct gru_stats_s {
|
||||
atomic_long_t mesq_send_qlimit_reached;
|
||||
atomic_long_t mesq_send_amo_nacked;
|
||||
atomic_long_t mesq_send_put_nacked;
|
||||
atomic_long_t mesq_qf_not_full;
|
||||
atomic_long_t mesq_page_overflow;
|
||||
atomic_long_t mesq_qf_locked;
|
||||
atomic_long_t mesq_qf_noop_not_full;
|
||||
atomic_long_t mesq_qf_switch_head_failed;
|
||||
@ -245,11 +242,13 @@ struct gru_stats_s {
|
||||
atomic_long_t mesq_noop_qlimit_reached;
|
||||
atomic_long_t mesq_noop_amo_nacked;
|
||||
atomic_long_t mesq_noop_put_nacked;
|
||||
atomic_long_t mesq_noop_page_overflow;
|
||||
|
||||
};
|
||||
|
||||
enum mcs_op {cchop_allocate, cchop_start, cchop_interrupt, cchop_interrupt_sync,
|
||||
cchop_deallocate, tghop_invalidate, mcsop_last};
|
||||
cchop_deallocate, tfhop_write_only, tfhop_write_restart,
|
||||
tghop_invalidate, mcsop_last};
|
||||
|
||||
struct mcs_op_statistic {
|
||||
atomic_long_t count;
|
||||
@ -259,8 +258,8 @@ struct mcs_op_statistic {
|
||||
|
||||
extern struct mcs_op_statistic mcs_op_statistics[mcsop_last];
|
||||
|
||||
#define OPT_DPRINT 1
|
||||
#define OPT_STATS 2
|
||||
#define OPT_DPRINT 1
|
||||
#define OPT_STATS 2
|
||||
|
||||
|
||||
#define IRQ_GRU 110 /* Starting IRQ number for interrupts */
|
||||
@ -283,7 +282,7 @@ extern struct mcs_op_statistic mcs_op_statistics[mcsop_last];
|
||||
#define gru_dbg(dev, fmt, x...) \
|
||||
do { \
|
||||
if (gru_options & OPT_DPRINT) \
|
||||
dev_dbg(dev, "%s: " fmt, __func__, x); \
|
||||
printk(KERN_DEBUG "GRU:%d %s: " fmt, smp_processor_id(), __func__, x);\
|
||||
} while (0)
|
||||
#else
|
||||
#define gru_dbg(x...)
|
||||
@ -297,13 +296,7 @@ extern struct mcs_op_statistic mcs_op_statistics[mcsop_last];
|
||||
#define ASID_INC 8 /* number of regions */
|
||||
|
||||
/* Generate a GRU asid value from a GRU base asid & a virtual address. */
|
||||
#if defined CONFIG_IA64
|
||||
#define VADDR_HI_BIT 64
|
||||
#elif defined CONFIG_X86_64
|
||||
#define VADDR_HI_BIT 48
|
||||
#else
|
||||
#error "Unsupported architecture"
|
||||
#endif
|
||||
#define GRUREGION(addr) ((addr) >> (VADDR_HI_BIT - 3) & 3)
|
||||
#define GRUASID(asid, addr) ((asid) + GRUREGION(addr))
|
||||
|
||||
@ -345,6 +338,7 @@ struct gru_vma_data {
|
||||
long vd_user_options;/* misc user option flags */
|
||||
int vd_cbr_au_count;
|
||||
int vd_dsr_au_count;
|
||||
unsigned char vd_tlb_preload_count;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -360,6 +354,7 @@ struct gru_thread_state {
|
||||
struct gru_state *ts_gru; /* GRU where the context is
|
||||
loaded */
|
||||
struct gru_mm_struct *ts_gms; /* asid & ioproc struct */
|
||||
unsigned char ts_tlb_preload_count; /* TLB preload pages */
|
||||
unsigned long ts_cbr_map; /* map of allocated CBRs */
|
||||
unsigned long ts_dsr_map; /* map of allocated DATA
|
||||
resources */
|
||||
@ -368,6 +363,8 @@ struct gru_thread_state {
|
||||
long ts_user_options;/* misc user option flags */
|
||||
pid_t ts_tgid_owner; /* task that is using the
|
||||
context - for migration */
|
||||
short ts_user_blade_id;/* user selected blade */
|
||||
char ts_user_chiplet_id;/* user selected chiplet */
|
||||
unsigned short ts_sizeavail; /* Pagesizes in use */
|
||||
int ts_tsid; /* thread that owns the
|
||||
structure */
|
||||
@ -384,13 +381,11 @@ struct gru_thread_state {
|
||||
char ts_blade; /* If >= 0, migrate context if
|
||||
ref from diferent blade */
|
||||
char ts_force_cch_reload;
|
||||
char ts_force_unload;/* force context to be unloaded
|
||||
after migration */
|
||||
char ts_cbr_idx[GRU_CBR_AU];/* CBR numbers of each
|
||||
allocated CB */
|
||||
int ts_data_valid; /* Indicates if ts_gdata has
|
||||
valid data */
|
||||
struct gts_statistics ustats; /* User statistics */
|
||||
struct gru_gseg_statistics ustats; /* User statistics */
|
||||
unsigned long ts_gdata[0]; /* save area for GRU data (CB,
|
||||
DS, CBE) */
|
||||
};
|
||||
@ -422,6 +417,7 @@ struct gru_state {
|
||||
gru segments (64) */
|
||||
unsigned short gs_gid; /* unique GRU number */
|
||||
unsigned short gs_blade_id; /* blade of GRU */
|
||||
unsigned char gs_chiplet_id; /* blade chiplet of GRU */
|
||||
unsigned char gs_tgh_local_shift; /* used to pick TGH for
|
||||
local flush */
|
||||
unsigned char gs_tgh_first_remote; /* starting TGH# for
|
||||
@ -453,6 +449,7 @@ struct gru_state {
|
||||
in use */
|
||||
struct gru_thread_state *gs_gts[GRU_NUM_CCH]; /* GTS currently using
|
||||
the context */
|
||||
int gs_irq[GRU_NUM_TFM]; /* Interrupt irqs */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -619,6 +616,15 @@ static inline int is_kernel_context(struct gru_thread_state *gts)
|
||||
return !gts->ts_mm;
|
||||
}
|
||||
|
||||
/*
|
||||
* The following are for Nehelem-EX. A more general scheme is needed for
|
||||
* future processors.
|
||||
*/
|
||||
#define UV_MAX_INT_CORES 8
|
||||
#define uv_cpu_socket_number(p) ((cpu_physical_id(p) >> 5) & 1)
|
||||
#define uv_cpu_ht_number(p) (cpu_physical_id(p) & 1)
|
||||
#define uv_cpu_core_number(p) (((cpu_physical_id(p) >> 2) & 4) | \
|
||||
((cpu_physical_id(p) >> 1) & 3))
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Function prototypes & externs
|
||||
*/
|
||||
@ -633,24 +639,26 @@ extern struct gru_thread_state *gru_find_thread_state(struct vm_area_struct
|
||||
*vma, int tsid);
|
||||
extern struct gru_thread_state *gru_alloc_thread_state(struct vm_area_struct
|
||||
*vma, int tsid);
|
||||
extern struct gru_state *gru_assign_gru_context(struct gru_thread_state *gts,
|
||||
int blade);
|
||||
extern struct gru_state *gru_assign_gru_context(struct gru_thread_state *gts);
|
||||
extern void gru_load_context(struct gru_thread_state *gts);
|
||||
extern void gru_steal_context(struct gru_thread_state *gts, int blade_id);
|
||||
extern void gru_steal_context(struct gru_thread_state *gts);
|
||||
extern void gru_unload_context(struct gru_thread_state *gts, int savestate);
|
||||
extern int gru_update_cch(struct gru_thread_state *gts, int force_unload);
|
||||
extern int gru_update_cch(struct gru_thread_state *gts);
|
||||
extern void gts_drop(struct gru_thread_state *gts);
|
||||
extern void gru_tgh_flush_init(struct gru_state *gru);
|
||||
extern int gru_kservices_init(void);
|
||||
extern void gru_kservices_exit(void);
|
||||
extern irqreturn_t gru0_intr(int irq, void *dev_id);
|
||||
extern irqreturn_t gru1_intr(int irq, void *dev_id);
|
||||
extern irqreturn_t gru_intr_mblade(int irq, void *dev_id);
|
||||
extern int gru_dump_chiplet_request(unsigned long arg);
|
||||
extern long gru_get_gseg_statistics(unsigned long arg);
|
||||
extern irqreturn_t gru_intr(int irq, void *dev_id);
|
||||
extern int gru_handle_user_call_os(unsigned long address);
|
||||
extern int gru_user_flush_tlb(unsigned long arg);
|
||||
extern int gru_user_unload_context(unsigned long arg);
|
||||
extern int gru_get_exception_detail(unsigned long arg);
|
||||
extern int gru_set_context_option(unsigned long address);
|
||||
extern void gru_check_context_placement(struct gru_thread_state *gts);
|
||||
extern int gru_cpu_fault_map_id(void);
|
||||
extern struct vm_area_struct *gru_find_vma(unsigned long vaddr);
|
||||
extern void gru_flush_all_tlb(struct gru_state *gru);
|
||||
@ -658,7 +666,8 @@ extern int gru_proc_init(void);
|
||||
extern void gru_proc_exit(void);
|
||||
|
||||
extern struct gru_thread_state *gru_alloc_gts(struct vm_area_struct *vma,
|
||||
int cbr_au_count, int dsr_au_count, int options, int tsid);
|
||||
int cbr_au_count, int dsr_au_count,
|
||||
unsigned char tlb_preload_count, int options, int tsid);
|
||||
extern unsigned long gru_reserve_cb_resources(struct gru_state *gru,
|
||||
int cbr_au_count, char *cbmap);
|
||||
extern unsigned long gru_reserve_ds_resources(struct gru_state *gru,
|
||||
|
@ -184,8 +184,8 @@ void gru_flush_tlb_range(struct gru_mm_struct *gms, unsigned long start,
|
||||
STAT(flush_tlb_gru_tgh);
|
||||
asid = GRUASID(asid, start);
|
||||
gru_dbg(grudev,
|
||||
" FLUSH gruid %d, asid 0x%x, num %ld, cbmap 0x%x\n",
|
||||
gid, asid, num, asids->mt_ctxbitmap);
|
||||
" FLUSH gruid %d, asid 0x%x, vaddr 0x%lx, vamask 0x%x, num %ld, cbmap 0x%x\n",
|
||||
gid, asid, start, grupagesize, num, asids->mt_ctxbitmap);
|
||||
tgh = get_lock_tgh_handle(gru);
|
||||
tgh_invalidate(tgh, start, ~0, asid, grupagesize, 0,
|
||||
num - 1, asids->mt_ctxbitmap);
|
||||
@ -299,6 +299,7 @@ struct gru_mm_struct *gru_register_mmu_notifier(void)
|
||||
{
|
||||
struct gru_mm_struct *gms;
|
||||
struct mmu_notifier *mn;
|
||||
int err;
|
||||
|
||||
mn = mmu_find_ops(current->mm, &gru_mmuops);
|
||||
if (mn) {
|
||||
@ -307,16 +308,22 @@ struct gru_mm_struct *gru_register_mmu_notifier(void)
|
||||
} else {
|
||||
gms = kzalloc(sizeof(*gms), GFP_KERNEL);
|
||||
if (gms) {
|
||||
STAT(gms_alloc);
|
||||
spin_lock_init(&gms->ms_asid_lock);
|
||||
gms->ms_notifier.ops = &gru_mmuops;
|
||||
atomic_set(&gms->ms_refcnt, 1);
|
||||
init_waitqueue_head(&gms->ms_wait_queue);
|
||||
__mmu_notifier_register(&gms->ms_notifier, current->mm);
|
||||
err = __mmu_notifier_register(&gms->ms_notifier, current->mm);
|
||||
if (err)
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
gru_dbg(grudev, "gms %p, refcnt %d\n", gms,
|
||||
atomic_read(&gms->ms_refcnt));
|
||||
return gms;
|
||||
error:
|
||||
kfree(gms);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
void gru_drop_mmu_notifier(struct gru_mm_struct *gms)
|
||||
@ -327,6 +334,7 @@ void gru_drop_mmu_notifier(struct gru_mm_struct *gms)
|
||||
if (!gms->ms_released)
|
||||
mmu_notifier_unregister(&gms->ms_notifier, current->mm);
|
||||
kfree(gms);
|
||||
STAT(gms_free);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -339,6 +339,7 @@ extern short xp_partition_id;
|
||||
extern u8 xp_region_size;
|
||||
|
||||
extern unsigned long (*xp_pa) (void *);
|
||||
extern unsigned long (*xp_socket_pa) (unsigned long);
|
||||
extern enum xp_retval (*xp_remote_memcpy) (unsigned long, const unsigned long,
|
||||
size_t);
|
||||
extern int (*xp_cpu_to_nasid) (int);
|
||||
|
@ -44,6 +44,9 @@ EXPORT_SYMBOL_GPL(xp_region_size);
|
||||
unsigned long (*xp_pa) (void *addr);
|
||||
EXPORT_SYMBOL_GPL(xp_pa);
|
||||
|
||||
unsigned long (*xp_socket_pa) (unsigned long gpa);
|
||||
EXPORT_SYMBOL_GPL(xp_socket_pa);
|
||||
|
||||
enum xp_retval (*xp_remote_memcpy) (unsigned long dst_gpa,
|
||||
const unsigned long src_gpa, size_t len);
|
||||
EXPORT_SYMBOL_GPL(xp_remote_memcpy);
|
||||
|
@ -83,6 +83,15 @@ xp_pa_sn2(void *addr)
|
||||
return __pa(addr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a global physical to a socket physical address.
|
||||
*/
|
||||
static unsigned long
|
||||
xp_socket_pa_sn2(unsigned long gpa)
|
||||
{
|
||||
return gpa;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wrapper for bte_copy().
|
||||
*
|
||||
@ -162,6 +171,7 @@ xp_init_sn2(void)
|
||||
xp_region_size = sn_region_size;
|
||||
|
||||
xp_pa = xp_pa_sn2;
|
||||
xp_socket_pa = xp_socket_pa_sn2;
|
||||
xp_remote_memcpy = xp_remote_memcpy_sn2;
|
||||
xp_cpu_to_nasid = xp_cpu_to_nasid_sn2;
|
||||
xp_expand_memprotect = xp_expand_memprotect_sn2;
|
||||
|
@ -32,12 +32,44 @@ xp_pa_uv(void *addr)
|
||||
return uv_gpa(addr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a global physical to socket physical address.
|
||||
*/
|
||||
static unsigned long
|
||||
xp_socket_pa_uv(unsigned long gpa)
|
||||
{
|
||||
return uv_gpa_to_soc_phys_ram(gpa);
|
||||
}
|
||||
|
||||
static enum xp_retval
|
||||
xp_remote_mmr_read(unsigned long dst_gpa, const unsigned long src_gpa,
|
||||
size_t len)
|
||||
{
|
||||
int ret;
|
||||
unsigned long *dst_va = __va(uv_gpa_to_soc_phys_ram(dst_gpa));
|
||||
|
||||
BUG_ON(!uv_gpa_in_mmr_space(src_gpa));
|
||||
BUG_ON(len != 8);
|
||||
|
||||
ret = gru_read_gpa(dst_va, src_gpa);
|
||||
if (ret == 0)
|
||||
return xpSuccess;
|
||||
|
||||
dev_err(xp, "gru_read_gpa() failed, dst_gpa=0x%016lx src_gpa=0x%016lx "
|
||||
"len=%ld\n", dst_gpa, src_gpa, len);
|
||||
return xpGruCopyError;
|
||||
}
|
||||
|
||||
|
||||
static enum xp_retval
|
||||
xp_remote_memcpy_uv(unsigned long dst_gpa, const unsigned long src_gpa,
|
||||
size_t len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (uv_gpa_in_mmr_space(src_gpa))
|
||||
return xp_remote_mmr_read(dst_gpa, src_gpa, len);
|
||||
|
||||
ret = gru_copy_gpa(dst_gpa, src_gpa, len);
|
||||
if (ret == 0)
|
||||
return xpSuccess;
|
||||
@ -123,6 +155,7 @@ xp_init_uv(void)
|
||||
xp_region_size = sn_region_size;
|
||||
|
||||
xp_pa = xp_pa_uv;
|
||||
xp_socket_pa = xp_socket_pa_uv;
|
||||
xp_remote_memcpy = xp_remote_memcpy_uv;
|
||||
xp_cpu_to_nasid = xp_cpu_to_nasid_uv;
|
||||
xp_expand_memprotect = xp_expand_memprotect_uv;
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/hardirq.h>
|
||||
#include "xpc.h"
|
||||
#include <asm/uv/uv_hub.h>
|
||||
|
||||
/* XPC is exiting flag */
|
||||
int xpc_exiting;
|
||||
@ -92,8 +93,12 @@ xpc_get_rsvd_page_pa(int nasid)
|
||||
break;
|
||||
|
||||
/* !!! L1_CACHE_ALIGN() is only a sn2-bte_copy requirement */
|
||||
if (L1_CACHE_ALIGN(len) > buf_len) {
|
||||
kfree(buf_base);
|
||||
if (is_shub())
|
||||
len = L1_CACHE_ALIGN(len);
|
||||
|
||||
if (len > buf_len) {
|
||||
if (buf_base != NULL)
|
||||
kfree(buf_base);
|
||||
buf_len = L1_CACHE_ALIGN(len);
|
||||
buf = xpc_kmalloc_cacheline_aligned(buf_len, GFP_KERNEL,
|
||||
&buf_base);
|
||||
@ -105,7 +110,7 @@ xpc_get_rsvd_page_pa(int nasid)
|
||||
}
|
||||
}
|
||||
|
||||
ret = xp_remote_memcpy(xp_pa(buf), rp_pa, buf_len);
|
||||
ret = xp_remote_memcpy(xp_pa(buf), rp_pa, len);
|
||||
if (ret != xpSuccess) {
|
||||
dev_dbg(xpc_part, "xp_remote_memcpy failed %d\n", ret);
|
||||
break;
|
||||
@ -143,7 +148,7 @@ xpc_setup_rsvd_page(void)
|
||||
dev_err(xpc_part, "SAL failed to locate the reserved page\n");
|
||||
return -ESRCH;
|
||||
}
|
||||
rp = (struct xpc_rsvd_page *)__va(rp_pa);
|
||||
rp = (struct xpc_rsvd_page *)__va(xp_socket_pa(rp_pa));
|
||||
|
||||
if (rp->SAL_version < 3) {
|
||||
/* SAL_versions < 3 had a SAL_partid defined as a u8 */
|
||||
|
@ -157,22 +157,24 @@ xpc_gru_mq_watchlist_alloc_uv(struct xpc_gru_mq_uv *mq)
|
||||
{
|
||||
int ret;
|
||||
|
||||
#if defined CONFIG_X86_64
|
||||
ret = uv_bios_mq_watchlist_alloc(mq->mmr_blade, uv_gpa(mq->address),
|
||||
mq->order, &mq->mmr_offset);
|
||||
if (ret < 0) {
|
||||
dev_err(xpc_part, "uv_bios_mq_watchlist_alloc() failed, "
|
||||
"ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV
|
||||
ret = sn_mq_watchlist_alloc(mq->mmr_blade, (void *)uv_gpa(mq->address),
|
||||
#if defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV
|
||||
int mmr_pnode = uv_blade_to_pnode(mq->mmr_blade);
|
||||
|
||||
ret = sn_mq_watchlist_alloc(mmr_pnode, (void *)uv_gpa(mq->address),
|
||||
mq->order, &mq->mmr_offset);
|
||||
if (ret < 0) {
|
||||
dev_err(xpc_part, "sn_mq_watchlist_alloc() failed, ret=%d\n",
|
||||
ret);
|
||||
return -EBUSY;
|
||||
}
|
||||
#elif defined CONFIG_X86_64
|
||||
ret = uv_bios_mq_watchlist_alloc(uv_gpa(mq->address),
|
||||
mq->order, &mq->mmr_offset);
|
||||
if (ret < 0) {
|
||||
dev_err(xpc_part, "uv_bios_mq_watchlist_alloc() failed, "
|
||||
"ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
#error not a supported configuration
|
||||
#endif
|
||||
@ -185,12 +187,13 @@ static void
|
||||
xpc_gru_mq_watchlist_free_uv(struct xpc_gru_mq_uv *mq)
|
||||
{
|
||||
int ret;
|
||||
int mmr_pnode = uv_blade_to_pnode(mq->mmr_blade);
|
||||
|
||||
#if defined CONFIG_X86_64
|
||||
ret = uv_bios_mq_watchlist_free(mq->mmr_blade, mq->watchlist_num);
|
||||
ret = uv_bios_mq_watchlist_free(mmr_pnode, mq->watchlist_num);
|
||||
BUG_ON(ret != BIOS_STATUS_SUCCESS);
|
||||
#elif defined CONFIG_IA64_GENERIC || defined CONFIG_IA64_SGI_UV
|
||||
ret = sn_mq_watchlist_free(mq->mmr_blade, mq->watchlist_num);
|
||||
ret = sn_mq_watchlist_free(mmr_pnode, mq->watchlist_num);
|
||||
BUG_ON(ret != SALRET_OK);
|
||||
#else
|
||||
#error not a supported configuration
|
||||
@ -204,6 +207,7 @@ xpc_create_gru_mq_uv(unsigned int mq_size, int cpu, char *irq_name,
|
||||
enum xp_retval xp_ret;
|
||||
int ret;
|
||||
int nid;
|
||||
int nasid;
|
||||
int pg_order;
|
||||
struct page *page;
|
||||
struct xpc_gru_mq_uv *mq;
|
||||
@ -259,9 +263,11 @@ xpc_create_gru_mq_uv(unsigned int mq_size, int cpu, char *irq_name,
|
||||
goto out_5;
|
||||
}
|
||||
|
||||
nasid = UV_PNODE_TO_NASID(uv_cpu_to_pnode(cpu));
|
||||
|
||||
mmr_value = (struct uv_IO_APIC_route_entry *)&mq->mmr_value;
|
||||
ret = gru_create_message_queue(mq->gru_mq_desc, mq->address, mq_size,
|
||||
nid, mmr_value->vector, mmr_value->dest);
|
||||
nasid, mmr_value->vector, mmr_value->dest);
|
||||
if (ret != 0) {
|
||||
dev_err(xpc_part, "gru_create_message_queue() returned "
|
||||
"error=%d\n", ret);
|
||||
@ -946,11 +952,13 @@ xpc_get_fifo_entry_uv(struct xpc_fifo_head_uv *head)
|
||||
head->first = first->next;
|
||||
if (head->first == NULL)
|
||||
head->last = NULL;
|
||||
|
||||
head->n_entries--;
|
||||
BUG_ON(head->n_entries < 0);
|
||||
|
||||
first->next = NULL;
|
||||
}
|
||||
head->n_entries--;
|
||||
BUG_ON(head->n_entries < 0);
|
||||
spin_unlock_irqrestore(&head->lock, irq_flags);
|
||||
first->next = NULL;
|
||||
return first;
|
||||
}
|
||||
|
||||
@ -1019,7 +1027,8 @@ xpc_make_first_contact_uv(struct xpc_partition *part)
|
||||
xpc_send_activate_IRQ_part_uv(part, &msg, sizeof(msg),
|
||||
XPC_ACTIVATE_MQ_MSG_SYNC_ACT_STATE_UV);
|
||||
|
||||
while (part->sn.uv.remote_act_state != XPC_P_AS_ACTIVATING) {
|
||||
while (!((part->sn.uv.remote_act_state == XPC_P_AS_ACTIVATING) ||
|
||||
(part->sn.uv.remote_act_state == XPC_P_AS_ACTIVE))) {
|
||||
|
||||
dev_dbg(xpc_part, "waiting to make first contact with "
|
||||
"partition %d\n", XPC_PARTID(part));
|
||||
@ -1422,7 +1431,6 @@ xpc_handle_notify_mq_msg_uv(struct xpc_partition *part,
|
||||
msg_slot = ch_uv->recv_msg_slots +
|
||||
(msg->hdr.msg_slot_number % ch->remote_nentries) * ch->entry_size;
|
||||
|
||||
BUG_ON(msg->hdr.msg_slot_number != msg_slot->hdr.msg_slot_number);
|
||||
BUG_ON(msg_slot->hdr.size != 0);
|
||||
|
||||
memcpy(msg_slot, msg, msg->hdr.size);
|
||||
@ -1646,8 +1654,6 @@ xpc_received_payload_uv(struct xpc_channel *ch, void *payload)
|
||||
sizeof(struct xpc_notify_mq_msghdr_uv));
|
||||
if (ret != xpSuccess)
|
||||
XPC_DEACTIVATE_PARTITION(&xpc_partitions[ch->partid], ret);
|
||||
|
||||
msg->hdr.msg_slot_number += ch->remote_nentries;
|
||||
}
|
||||
|
||||
static struct xpc_arch_operations xpc_arch_ops_uv = {
|
||||
|
@ -72,35 +72,6 @@ void mlx4_bitmap_free(struct mlx4_bitmap *bitmap, u32 obj)
|
||||
mlx4_bitmap_free_range(bitmap, obj, 1);
|
||||
}
|
||||
|
||||
static unsigned long find_aligned_range(unsigned long *bitmap,
|
||||
u32 start, u32 nbits,
|
||||
int len, int align)
|
||||
{
|
||||
unsigned long end, i;
|
||||
|
||||
again:
|
||||
start = ALIGN(start, align);
|
||||
|
||||
while ((start < nbits) && test_bit(start, bitmap))
|
||||
start += align;
|
||||
|
||||
if (start >= nbits)
|
||||
return -1;
|
||||
|
||||
end = start+len;
|
||||
if (end > nbits)
|
||||
return -1;
|
||||
|
||||
for (i = start + 1; i < end; i++) {
|
||||
if (test_bit(i, bitmap)) {
|
||||
start = i + 1;
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
u32 mlx4_bitmap_alloc_range(struct mlx4_bitmap *bitmap, int cnt, int align)
|
||||
{
|
||||
u32 obj, i;
|
||||
@ -110,13 +81,13 @@ u32 mlx4_bitmap_alloc_range(struct mlx4_bitmap *bitmap, int cnt, int align)
|
||||
|
||||
spin_lock(&bitmap->lock);
|
||||
|
||||
obj = find_aligned_range(bitmap->table, bitmap->last,
|
||||
bitmap->max, cnt, align);
|
||||
obj = bitmap_find_next_zero_area(bitmap->table, bitmap->max,
|
||||
bitmap->last, cnt, align - 1);
|
||||
if (obj >= bitmap->max) {
|
||||
bitmap->top = (bitmap->top + bitmap->max + bitmap->reserved_top)
|
||||
& bitmap->mask;
|
||||
obj = find_aligned_range(bitmap->table, 0, bitmap->max,
|
||||
cnt, align);
|
||||
obj = bitmap_find_next_zero_area(bitmap->table, bitmap->max,
|
||||
0, cnt, align - 1);
|
||||
}
|
||||
|
||||
if (obj < bitmap->max) {
|
||||
|
@ -3403,7 +3403,7 @@ static int __init parport_parse_param(const char *s, int *val,
|
||||
*val = automatic;
|
||||
else if (!strncmp(s, "none", 4))
|
||||
*val = none;
|
||||
else if (nofifo && !strncmp(s, "nofifo", 4))
|
||||
else if (nofifo && !strncmp(s, "nofifo", 6))
|
||||
*val = nofifo;
|
||||
else {
|
||||
char *ep;
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/pnp.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
@ -33,42 +34,65 @@
|
||||
static struct proc_dir_entry *proc_pnp = NULL;
|
||||
static struct proc_dir_entry *proc_pnp_boot = NULL;
|
||||
|
||||
static int proc_read_pnpconfig(char *buf, char **start, off_t pos,
|
||||
int count, int *eof, void *data)
|
||||
static int pnpconfig_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct pnp_isa_config_struc pnps;
|
||||
|
||||
if (pnp_bios_isapnp_config(&pnps))
|
||||
return -EIO;
|
||||
return snprintf(buf, count,
|
||||
"structure_revision %d\n"
|
||||
"number_of_CSNs %d\n"
|
||||
"ISA_read_data_port 0x%x\n",
|
||||
pnps.revision, pnps.no_csns, pnps.isa_rd_data_port);
|
||||
seq_printf(m, "structure_revision %d\n"
|
||||
"number_of_CSNs %d\n"
|
||||
"ISA_read_data_port 0x%x\n",
|
||||
pnps.revision, pnps.no_csns, pnps.isa_rd_data_port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int proc_read_escdinfo(char *buf, char **start, off_t pos,
|
||||
int count, int *eof, void *data)
|
||||
static int pnpconfig_proc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, pnpconfig_proc_show, NULL);
|
||||
}
|
||||
|
||||
static const struct file_operations pnpconfig_proc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = pnpconfig_proc_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int escd_info_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct escd_info_struc escd;
|
||||
|
||||
if (pnp_bios_escd_info(&escd))
|
||||
return -EIO;
|
||||
return snprintf(buf, count,
|
||||
"min_ESCD_write_size %d\n"
|
||||
seq_printf(m, "min_ESCD_write_size %d\n"
|
||||
"ESCD_size %d\n"
|
||||
"NVRAM_base 0x%x\n",
|
||||
escd.min_escd_write_size,
|
||||
escd.escd_size, escd.nv_storage_base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int escd_info_proc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, escd_info_proc_show, NULL);
|
||||
}
|
||||
|
||||
static const struct file_operations escd_info_proc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = escd_info_proc_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
#define MAX_SANE_ESCD_SIZE (32*1024)
|
||||
static int proc_read_escd(char *buf, char **start, off_t pos,
|
||||
int count, int *eof, void *data)
|
||||
static int escd_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct escd_info_struc escd;
|
||||
char *tmpbuf;
|
||||
int escd_size, escd_left_to_read, n;
|
||||
int escd_size;
|
||||
|
||||
if (pnp_bios_escd_info(&escd))
|
||||
return -EIO;
|
||||
@ -76,7 +100,7 @@ static int proc_read_escd(char *buf, char **start, off_t pos,
|
||||
/* sanity check */
|
||||
if (escd.escd_size > MAX_SANE_ESCD_SIZE) {
|
||||
printk(KERN_ERR
|
||||
"PnPBIOS: proc_read_escd: ESCD size reported by BIOS escd_info call is too great\n");
|
||||
"PnPBIOS: %s: ESCD size reported by BIOS escd_info call is too great\n", __func__);
|
||||
return -EFBIG;
|
||||
}
|
||||
|
||||
@ -94,56 +118,75 @@ static int proc_read_escd(char *buf, char **start, off_t pos,
|
||||
|
||||
/* sanity check */
|
||||
if (escd_size > MAX_SANE_ESCD_SIZE) {
|
||||
printk(KERN_ERR "PnPBIOS: proc_read_escd: ESCD size reported by"
|
||||
" BIOS read_escd call is too great\n");
|
||||
printk(KERN_ERR "PnPBIOS: %s: ESCD size reported by"
|
||||
" BIOS read_escd call is too great\n", __func__);
|
||||
kfree(tmpbuf);
|
||||
return -EFBIG;
|
||||
}
|
||||
|
||||
escd_left_to_read = escd_size - pos;
|
||||
if (escd_left_to_read < 0)
|
||||
escd_left_to_read = 0;
|
||||
if (escd_left_to_read == 0)
|
||||
*eof = 1;
|
||||
n = min(count, escd_left_to_read);
|
||||
memcpy(buf, tmpbuf + pos, n);
|
||||
seq_write(m, tmpbuf, escd_size);
|
||||
kfree(tmpbuf);
|
||||
*start = buf;
|
||||
return n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int proc_read_legacyres(char *buf, char **start, off_t pos,
|
||||
int count, int *eof, void *data)
|
||||
static int escd_proc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
/* Assume that the following won't overflow the buffer */
|
||||
if (pnp_bios_get_stat_res(buf))
|
||||
return -EIO;
|
||||
|
||||
return count; // FIXME: Return actual length
|
||||
return single_open(file, escd_proc_show, NULL);
|
||||
}
|
||||
|
||||
static int proc_read_devices(char *buf, char **start, off_t pos,
|
||||
int count, int *eof, void *data)
|
||||
static const struct file_operations escd_proc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = escd_proc_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int pnp_legacyres_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
void *buf;
|
||||
|
||||
buf = kmalloc(65536, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
if (pnp_bios_get_stat_res(buf)) {
|
||||
kfree(buf);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
seq_write(m, buf, 65536);
|
||||
kfree(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pnp_legacyres_proc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, pnp_legacyres_proc_show, NULL);
|
||||
}
|
||||
|
||||
static const struct file_operations pnp_legacyres_proc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = pnp_legacyres_proc_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int pnp_devices_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct pnp_bios_node *node;
|
||||
u8 nodenum;
|
||||
char *p = buf;
|
||||
|
||||
if (pos >= 0xff)
|
||||
return 0;
|
||||
|
||||
node = kzalloc(node_info.max_node_size, GFP_KERNEL);
|
||||
if (!node)
|
||||
return -ENOMEM;
|
||||
|
||||
for (nodenum = pos; nodenum < 0xff;) {
|
||||
for (nodenum = 0; nodenum < 0xff;) {
|
||||
u8 thisnodenum = nodenum;
|
||||
/* 26 = the number of characters per line sprintf'ed */
|
||||
if ((p - buf + 26) > count)
|
||||
break;
|
||||
|
||||
if (pnp_bios_get_dev_node(&nodenum, PNPMODE_DYNAMIC, node))
|
||||
break;
|
||||
p += sprintf(p, "%02x\t%08x\t%02x:%02x:%02x\t%04x\n",
|
||||
seq_printf(m, "%02x\t%08x\t%02x:%02x:%02x\t%04x\n",
|
||||
node->handle, node->eisa_id,
|
||||
node->type_code[0], node->type_code[1],
|
||||
node->type_code[2], node->flags);
|
||||
@ -153,20 +196,29 @@ static int proc_read_devices(char *buf, char **start, off_t pos,
|
||||
"PnPBIOS: proc_read_devices:",
|
||||
(unsigned int)nodenum,
|
||||
(unsigned int)thisnodenum);
|
||||
*eof = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
kfree(node);
|
||||
if (nodenum == 0xff)
|
||||
*eof = 1;
|
||||
*start = (char *)((off_t) nodenum - pos);
|
||||
return p - buf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int proc_read_node(char *buf, char **start, off_t pos,
|
||||
int count, int *eof, void *data)
|
||||
static int pnp_devices_proc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, pnp_devices_proc_show, NULL);
|
||||
}
|
||||
|
||||
static const struct file_operations pnp_devices_proc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = pnp_devices_proc_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int pnpbios_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
void *data = m->private;
|
||||
struct pnp_bios_node *node;
|
||||
int boot = (long)data >> 8;
|
||||
u8 nodenum = (long)data;
|
||||
@ -180,14 +232,20 @@ static int proc_read_node(char *buf, char **start, off_t pos,
|
||||
return -EIO;
|
||||
}
|
||||
len = node->size - sizeof(struct pnp_bios_node);
|
||||
memcpy(buf, node->data, len);
|
||||
seq_write(m, node->data, len);
|
||||
kfree(node);
|
||||
return len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int proc_write_node(struct file *file, const char __user * buf,
|
||||
unsigned long count, void *data)
|
||||
static int pnpbios_proc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, pnpbios_proc_show, PDE(inode)->data);
|
||||
}
|
||||
|
||||
static ssize_t pnpbios_proc_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *pos)
|
||||
{
|
||||
void *data = PDE(file->f_path.dentry->d_inode)->data;
|
||||
struct pnp_bios_node *node;
|
||||
int boot = (long)data >> 8;
|
||||
u8 nodenum = (long)data;
|
||||
@ -218,34 +276,33 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations pnpbios_proc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = pnpbios_proc_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
.write = pnpbios_proc_write,
|
||||
};
|
||||
|
||||
int pnpbios_interface_attach_device(struct pnp_bios_node *node)
|
||||
{
|
||||
char name[3];
|
||||
struct proc_dir_entry *ent;
|
||||
|
||||
sprintf(name, "%02x", node->handle);
|
||||
|
||||
if (!proc_pnp)
|
||||
return -EIO;
|
||||
if (!pnpbios_dont_use_current_config) {
|
||||
ent = create_proc_entry(name, 0, proc_pnp);
|
||||
if (ent) {
|
||||
ent->read_proc = proc_read_node;
|
||||
ent->write_proc = proc_write_node;
|
||||
ent->data = (void *)(long)(node->handle);
|
||||
}
|
||||
proc_create_data(name, 0644, proc_pnp, &pnpbios_proc_fops,
|
||||
(void *)(long)(node->handle));
|
||||
}
|
||||
|
||||
if (!proc_pnp_boot)
|
||||
return -EIO;
|
||||
ent = create_proc_entry(name, 0, proc_pnp_boot);
|
||||
if (ent) {
|
||||
ent->read_proc = proc_read_node;
|
||||
ent->write_proc = proc_write_node;
|
||||
ent->data = (void *)(long)(node->handle + 0x100);
|
||||
if (proc_create_data(name, 0644, proc_pnp_boot, &pnpbios_proc_fops,
|
||||
(void *)(long)(node->handle + 0x100)))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@ -262,14 +319,11 @@ int __init pnpbios_proc_init(void)
|
||||
proc_pnp_boot = proc_mkdir("boot", proc_pnp);
|
||||
if (!proc_pnp_boot)
|
||||
return -EIO;
|
||||
create_proc_read_entry("devices", 0, proc_pnp, proc_read_devices, NULL);
|
||||
create_proc_read_entry("configuration_info", 0, proc_pnp,
|
||||
proc_read_pnpconfig, NULL);
|
||||
create_proc_read_entry("escd_info", 0, proc_pnp, proc_read_escdinfo,
|
||||
NULL);
|
||||
create_proc_read_entry("escd", S_IRUSR, proc_pnp, proc_read_escd, NULL);
|
||||
create_proc_read_entry("legacy_device_resources", 0, proc_pnp,
|
||||
proc_read_legacyres, NULL);
|
||||
proc_create("devices", 0, proc_pnp, &pnp_devices_proc_fops);
|
||||
proc_create("configuration_info", 0, proc_pnp, &pnpconfig_proc_fops);
|
||||
proc_create("escd_info", 0, proc_pnp, &escd_info_proc_fops);
|
||||
proc_create("escd", S_IRUSR, proc_pnp, &escd_proc_fops);
|
||||
proc_create("legacy_device_resources", 0, proc_pnp, &pnp_legacyres_proc_fops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -242,6 +242,15 @@ config RTC_DRV_M41T80_WDT
|
||||
If you say Y here you will get support for the
|
||||
watchdog timer in the ST M41T60 and M41T80 RTC chips series.
|
||||
|
||||
config RTC_DRV_BQ32K
|
||||
tristate "TI BQ32000"
|
||||
help
|
||||
If you say Y here you will get support for the TI
|
||||
BQ32000 I2C RTC chip.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-bq32k.
|
||||
|
||||
config RTC_DRV_DM355EVM
|
||||
tristate "TI DaVinci DM355 EVM RTC"
|
||||
depends on MFD_DM355EVM_MSP
|
||||
@ -592,15 +601,22 @@ config RTC_DRV_AB3100
|
||||
Select this to enable the ST-Ericsson AB3100 Mixed Signal IC RTC
|
||||
support. This chip contains a battery- and capacitor-backed RTC.
|
||||
|
||||
config RTC_DRV_NUC900
|
||||
tristate "NUC910/NUC920 RTC driver"
|
||||
depends on RTC_CLASS && ARCH_W90X900
|
||||
help
|
||||
If you say yes here you get support for the RTC subsystem of the
|
||||
NUC910/NUC920 used in embedded systems.
|
||||
|
||||
comment "on-CPU RTC drivers"
|
||||
|
||||
config RTC_DRV_OMAP
|
||||
tristate "TI OMAP1"
|
||||
depends on ARCH_OMAP15XX || ARCH_OMAP16XX || ARCH_OMAP730
|
||||
depends on ARCH_OMAP15XX || ARCH_OMAP16XX || ARCH_OMAP730 || ARCH_DAVINCI_DA8XX
|
||||
help
|
||||
Say "yes" here to support the real time clock on TI OMAP1 chips.
|
||||
This driver can also be built as a module called rtc-omap.
|
||||
Say "yes" here to support the real time clock on TI OMAP1 and
|
||||
DA8xx/OMAP-L13x chips. This driver can also be built as a
|
||||
module called rtc-omap.
|
||||
|
||||
config RTC_DRV_S3C
|
||||
tristate "Samsung S3C series SoC RTC"
|
||||
@ -846,4 +862,10 @@ config RTC_DRV_PCAP
|
||||
If you say Y here you will get support for the RTC found on
|
||||
the PCAP2 ASIC used on some Motorola phones.
|
||||
|
||||
config RTC_DRV_MC13783
|
||||
depends on MFD_MC13783
|
||||
tristate "Freescale MC13783 RTC"
|
||||
help
|
||||
This enables support for the Freescale MC13783 PMIC RTC
|
||||
|
||||
endif # RTC_CLASS
|
||||
|
@ -23,6 +23,7 @@ obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o
|
||||
obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o
|
||||
obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o
|
||||
obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o
|
||||
obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o
|
||||
obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o
|
||||
obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
|
||||
obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o
|
||||
@ -52,8 +53,10 @@ obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o
|
||||
obj-$(CONFIG_RTC_MXC) += rtc-mxc.o
|
||||
obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o
|
||||
obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
|
||||
obj-$(CONFIG_RTC_DRV_MC13783) += rtc-mc13783.o
|
||||
obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o
|
||||
obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o
|
||||
obj-$(CONFIG_RTC_DRV_NUC900) += rtc-nuc900.o
|
||||
obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
|
||||
obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o
|
||||
obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o
|
||||
|
@ -256,6 +256,8 @@ static int __init at32_rtc_probe(struct platform_device *pdev)
|
||||
goto out_iounmap;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, rtc);
|
||||
|
||||
rtc->rtc = rtc_device_register(pdev->name, &pdev->dev,
|
||||
&at32_rtc_ops, THIS_MODULE);
|
||||
if (IS_ERR(rtc->rtc)) {
|
||||
@ -264,7 +266,6 @@ static int __init at32_rtc_probe(struct platform_device *pdev)
|
||||
goto out_free_irq;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, rtc);
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
|
||||
dev_info(&pdev->dev, "Atmel RTC for AT32AP700x at %08lx irq %ld\n",
|
||||
@ -273,6 +274,7 @@ static int __init at32_rtc_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
|
||||
out_free_irq:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
free_irq(irq, rtc);
|
||||
out_iounmap:
|
||||
iounmap(rtc->regs);
|
||||
|
204
drivers/rtc/rtc-bq32k.c
Normal file
204
drivers/rtc/rtc-bq32k.c
Normal file
@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Driver for TI BQ32000 RTC.
|
||||
*
|
||||
* Copyright (C) 2009 Semihalf.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/bcd.h>
|
||||
|
||||
#define BQ32K_SECONDS 0x00 /* Seconds register address */
|
||||
#define BQ32K_SECONDS_MASK 0x7F /* Mask over seconds value */
|
||||
#define BQ32K_STOP 0x80 /* Oscillator Stop flat */
|
||||
|
||||
#define BQ32K_MINUTES 0x01 /* Minutes register address */
|
||||
#define BQ32K_MINUTES_MASK 0x7F /* Mask over minutes value */
|
||||
#define BQ32K_OF 0x80 /* Oscillator Failure flag */
|
||||
|
||||
#define BQ32K_HOURS_MASK 0x3F /* Mask over hours value */
|
||||
#define BQ32K_CENT 0x40 /* Century flag */
|
||||
#define BQ32K_CENT_EN 0x80 /* Century flag enable bit */
|
||||
|
||||
struct bq32k_regs {
|
||||
uint8_t seconds;
|
||||
uint8_t minutes;
|
||||
uint8_t cent_hours;
|
||||
uint8_t day;
|
||||
uint8_t date;
|
||||
uint8_t month;
|
||||
uint8_t years;
|
||||
};
|
||||
|
||||
static struct i2c_driver bq32k_driver;
|
||||
|
||||
static int bq32k_read(struct device *dev, void *data, uint8_t off, uint8_t len)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = 0,
|
||||
.len = 1,
|
||||
.buf = &off,
|
||||
}, {
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = len,
|
||||
.buf = data,
|
||||
}
|
||||
};
|
||||
|
||||
if (i2c_transfer(client->adapter, msgs, 2) == 2)
|
||||
return 0;
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int bq32k_write(struct device *dev, void *data, uint8_t off, uint8_t len)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
uint8_t buffer[len + 1];
|
||||
|
||||
buffer[0] = off;
|
||||
memcpy(&buffer[1], data, len);
|
||||
|
||||
if (i2c_master_send(client, buffer, len + 1) == len + 1)
|
||||
return 0;
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int bq32k_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct bq32k_regs regs;
|
||||
int error;
|
||||
|
||||
error = bq32k_read(dev, ®s, 0, sizeof(regs));
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
tm->tm_sec = bcd2bin(regs.seconds & BQ32K_SECONDS_MASK);
|
||||
tm->tm_min = bcd2bin(regs.minutes & BQ32K_SECONDS_MASK);
|
||||
tm->tm_hour = bcd2bin(regs.cent_hours & BQ32K_HOURS_MASK);
|
||||
tm->tm_mday = bcd2bin(regs.date);
|
||||
tm->tm_wday = bcd2bin(regs.day) - 1;
|
||||
tm->tm_mon = bcd2bin(regs.month) - 1;
|
||||
tm->tm_year = bcd2bin(regs.years) +
|
||||
((regs.cent_hours & BQ32K_CENT) ? 100 : 0);
|
||||
|
||||
return rtc_valid_tm(tm);
|
||||
}
|
||||
|
||||
static int bq32k_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct bq32k_regs regs;
|
||||
|
||||
regs.seconds = bin2bcd(tm->tm_sec);
|
||||
regs.minutes = bin2bcd(tm->tm_min);
|
||||
regs.cent_hours = bin2bcd(tm->tm_hour) | BQ32K_CENT_EN;
|
||||
regs.day = bin2bcd(tm->tm_wday + 1);
|
||||
regs.date = bin2bcd(tm->tm_mday);
|
||||
regs.month = bin2bcd(tm->tm_mon + 1);
|
||||
|
||||
if (tm->tm_year >= 100) {
|
||||
regs.cent_hours |= BQ32K_CENT;
|
||||
regs.years = bin2bcd(tm->tm_year - 100);
|
||||
} else
|
||||
regs.years = bin2bcd(tm->tm_year);
|
||||
|
||||
return bq32k_write(dev, ®s, 0, sizeof(regs));
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops bq32k_rtc_ops = {
|
||||
.read_time = bq32k_rtc_read_time,
|
||||
.set_time = bq32k_rtc_set_time,
|
||||
};
|
||||
|
||||
static int bq32k_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct rtc_device *rtc;
|
||||
uint8_t reg;
|
||||
int error;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||
return -ENODEV;
|
||||
|
||||
/* Check Oscillator Stop flag */
|
||||
error = bq32k_read(dev, ®, BQ32K_SECONDS, 1);
|
||||
if (!error && (reg & BQ32K_STOP)) {
|
||||
dev_warn(dev, "Oscillator was halted. Restarting...\n");
|
||||
reg &= ~BQ32K_STOP;
|
||||
error = bq32k_write(dev, ®, BQ32K_SECONDS, 1);
|
||||
}
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* Check Oscillator Failure flag */
|
||||
error = bq32k_read(dev, ®, BQ32K_MINUTES, 1);
|
||||
if (!error && (reg & BQ32K_OF)) {
|
||||
dev_warn(dev, "Oscillator Failure. Check RTC battery.\n");
|
||||
reg &= ~BQ32K_OF;
|
||||
error = bq32k_write(dev, ®, BQ32K_MINUTES, 1);
|
||||
}
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
rtc = rtc_device_register(bq32k_driver.driver.name, &client->dev,
|
||||
&bq32k_rtc_ops, THIS_MODULE);
|
||||
if (IS_ERR(rtc))
|
||||
return PTR_ERR(rtc);
|
||||
|
||||
i2c_set_clientdata(client, rtc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devexit bq32k_remove(struct i2c_client *client)
|
||||
{
|
||||
struct rtc_device *rtc = i2c_get_clientdata(client);
|
||||
|
||||
rtc_device_unregister(rtc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id bq32k_id[] = {
|
||||
{ "bq32000", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, bq32k_id);
|
||||
|
||||
static struct i2c_driver bq32k_driver = {
|
||||
.driver = {
|
||||
.name = "bq32k",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = bq32k_probe,
|
||||
.remove = __devexit_p(bq32k_remove),
|
||||
.id_table = bq32k_id,
|
||||
};
|
||||
|
||||
static __init int bq32k_init(void)
|
||||
{
|
||||
return i2c_add_driver(&bq32k_driver);
|
||||
}
|
||||
module_init(bq32k_init);
|
||||
|
||||
static __exit void bq32k_exit(void)
|
||||
{
|
||||
i2c_del_driver(&bq32k_driver);
|
||||
}
|
||||
module_exit(bq32k_exit);
|
||||
|
||||
MODULE_AUTHOR("Semihalf, Piotr Ziecik <kosmo@semihalf.com>");
|
||||
MODULE_DESCRIPTION("TI BQ32000 I2C RTC driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -169,6 +169,8 @@ static int __devinit bq4802_probe(struct platform_device *pdev)
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, p);
|
||||
|
||||
p->rtc = rtc_device_register("bq4802", &pdev->dev,
|
||||
&bq4802_ops, THIS_MODULE);
|
||||
if (IS_ERR(p->rtc)) {
|
||||
@ -176,7 +178,6 @@ static int __devinit bq4802_probe(struct platform_device *pdev)
|
||||
goto out_iounmap;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, p);
|
||||
err = 0;
|
||||
out:
|
||||
return err;
|
||||
|
@ -420,49 +420,43 @@ static int cmos_irq_set_state(struct device *dev, int enabled)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_RTC_INTF_DEV) || defined(CONFIG_RTC_INTF_DEV_MODULE)
|
||||
|
||||
static int
|
||||
cmos_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
|
||||
static int cmos_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
struct cmos_rtc *cmos = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
|
||||
switch (cmd) {
|
||||
case RTC_AIE_OFF:
|
||||
case RTC_AIE_ON:
|
||||
case RTC_UIE_OFF:
|
||||
case RTC_UIE_ON:
|
||||
if (!is_valid_irq(cmos->irq))
|
||||
return -EINVAL;
|
||||
break;
|
||||
/* PIE ON/OFF is handled by cmos_irq_set_state() */
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
if (!is_valid_irq(cmos->irq))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&rtc_lock, flags);
|
||||
switch (cmd) {
|
||||
case RTC_AIE_OFF: /* alarm off */
|
||||
cmos_irq_disable(cmos, RTC_AIE);
|
||||
break;
|
||||
case RTC_AIE_ON: /* alarm on */
|
||||
|
||||
if (enabled)
|
||||
cmos_irq_enable(cmos, RTC_AIE);
|
||||
break;
|
||||
case RTC_UIE_OFF: /* update off */
|
||||
cmos_irq_disable(cmos, RTC_UIE);
|
||||
break;
|
||||
case RTC_UIE_ON: /* update on */
|
||||
cmos_irq_enable(cmos, RTC_UIE);
|
||||
break;
|
||||
}
|
||||
else
|
||||
cmos_irq_disable(cmos, RTC_AIE);
|
||||
|
||||
spin_unlock_irqrestore(&rtc_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
#define cmos_rtc_ioctl NULL
|
||||
#endif
|
||||
static int cmos_update_irq_enable(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
struct cmos_rtc *cmos = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
|
||||
if (!is_valid_irq(cmos->irq))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&rtc_lock, flags);
|
||||
|
||||
if (enabled)
|
||||
cmos_irq_enable(cmos, RTC_UIE);
|
||||
else
|
||||
cmos_irq_disable(cmos, RTC_UIE);
|
||||
|
||||
spin_unlock_irqrestore(&rtc_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_RTC_INTF_PROC) || defined(CONFIG_RTC_INTF_PROC_MODULE)
|
||||
|
||||
@ -503,14 +497,15 @@ static int cmos_procfs(struct device *dev, struct seq_file *seq)
|
||||
#endif
|
||||
|
||||
static const struct rtc_class_ops cmos_rtc_ops = {
|
||||
.ioctl = cmos_rtc_ioctl,
|
||||
.read_time = cmos_read_time,
|
||||
.set_time = cmos_set_time,
|
||||
.read_alarm = cmos_read_alarm,
|
||||
.set_alarm = cmos_set_alarm,
|
||||
.proc = cmos_procfs,
|
||||
.irq_set_freq = cmos_irq_set_freq,
|
||||
.irq_set_state = cmos_irq_set_state,
|
||||
.read_time = cmos_read_time,
|
||||
.set_time = cmos_set_time,
|
||||
.read_alarm = cmos_read_alarm,
|
||||
.set_alarm = cmos_set_alarm,
|
||||
.proc = cmos_procfs,
|
||||
.irq_set_freq = cmos_irq_set_freq,
|
||||
.irq_set_state = cmos_irq_set_state,
|
||||
.alarm_irq_enable = cmos_alarm_irq_enable,
|
||||
.update_irq_enable = cmos_update_irq_enable,
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
@ -871,8 +866,9 @@ static int cmos_suspend(struct device *dev, pm_message_t mesg)
|
||||
mask = RTC_IRQMASK;
|
||||
tmp &= ~mask;
|
||||
CMOS_WRITE(tmp, RTC_CONTROL);
|
||||
hpet_mask_rtc_irq_bit(mask);
|
||||
|
||||
/* shut down hpet emulation - we don't need it for alarm */
|
||||
hpet_mask_rtc_irq_bit(RTC_PIE|RTC_AIE|RTC_UIE);
|
||||
cmos_checkintr(cmos, tmp);
|
||||
}
|
||||
spin_unlock_irq(&rtc_lock);
|
||||
|
@ -143,7 +143,6 @@ static int ds1302_rtc_ioctl(struct device *dev, unsigned int cmd,
|
||||
#ifdef RTC_SET_CHARGE
|
||||
case RTC_SET_CHARGE:
|
||||
{
|
||||
struct ds1302_rtc *rtc = dev_get_drvdata(dev);
|
||||
int tcs_val;
|
||||
|
||||
if (copy_from_user(&tcs_val, (int __user *)arg, sizeof(int)))
|
||||
|
@ -617,7 +617,6 @@ static struct bin_attribute nvram = {
|
||||
static int __devinit ds1305_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ds1305 *ds1305;
|
||||
struct rtc_device *rtc;
|
||||
int status;
|
||||
u8 addr, value;
|
||||
struct ds1305_platform_data *pdata = spi->dev.platform_data;
|
||||
@ -756,14 +755,13 @@ static int __devinit ds1305_probe(struct spi_device *spi)
|
||||
dev_dbg(&spi->dev, "AM/PM\n");
|
||||
|
||||
/* register RTC ... from here on, ds1305->ctrl needs locking */
|
||||
rtc = rtc_device_register("ds1305", &spi->dev,
|
||||
ds1305->rtc = rtc_device_register("ds1305", &spi->dev,
|
||||
&ds1305_ops, THIS_MODULE);
|
||||
if (IS_ERR(rtc)) {
|
||||
status = PTR_ERR(rtc);
|
||||
if (IS_ERR(ds1305->rtc)) {
|
||||
status = PTR_ERR(ds1305->rtc);
|
||||
dev_dbg(&spi->dev, "register rtc --> %d\n", status);
|
||||
goto fail0;
|
||||
}
|
||||
ds1305->rtc = rtc;
|
||||
|
||||
/* Maybe set up alarm IRQ; be ready to handle it triggering right
|
||||
* away. NOTE that we don't share this. The signal is active low,
|
||||
@ -774,7 +772,7 @@ static int __devinit ds1305_probe(struct spi_device *spi)
|
||||
if (spi->irq) {
|
||||
INIT_WORK(&ds1305->work, ds1305_work);
|
||||
status = request_irq(spi->irq, ds1305_irq,
|
||||
0, dev_name(&rtc->dev), ds1305);
|
||||
0, dev_name(&ds1305->rtc->dev), ds1305);
|
||||
if (status < 0) {
|
||||
dev_dbg(&spi->dev, "request_irq %d --> %d\n",
|
||||
spi->irq, status);
|
||||
@ -794,7 +792,7 @@ static int __devinit ds1305_probe(struct spi_device *spi)
|
||||
fail2:
|
||||
free_irq(spi->irq, ds1305);
|
||||
fail1:
|
||||
rtc_device_unregister(rtc);
|
||||
rtc_device_unregister(ds1305->rtc);
|
||||
fail0:
|
||||
kfree(ds1305);
|
||||
return status;
|
||||
@ -802,7 +800,7 @@ fail0:
|
||||
|
||||
static int __devexit ds1305_remove(struct spi_device *spi)
|
||||
{
|
||||
struct ds1305 *ds1305 = spi_get_drvdata(spi);
|
||||
struct ds1305 *ds1305 = spi_get_drvdata(spi);
|
||||
|
||||
sysfs_remove_bin_file(&spi->dev.kobj, &nvram);
|
||||
|
||||
|
@ -874,7 +874,7 @@ read_rtc:
|
||||
}
|
||||
|
||||
if (want_irq) {
|
||||
err = request_irq(client->irq, ds1307_irq, 0,
|
||||
err = request_irq(client->irq, ds1307_irq, IRQF_SHARED,
|
||||
ds1307->rtc->name, client);
|
||||
if (err) {
|
||||
dev_err(&client->dev,
|
||||
|
@ -87,7 +87,6 @@ enum ds1511reg {
|
||||
struct rtc_plat_data {
|
||||
struct rtc_device *rtc;
|
||||
void __iomem *ioaddr; /* virtual base address */
|
||||
unsigned long baseaddr; /* physical base address */
|
||||
int size; /* amount of memory mapped */
|
||||
int irq;
|
||||
unsigned int irqen;
|
||||
@ -95,6 +94,7 @@ struct rtc_plat_data {
|
||||
int alrm_min;
|
||||
int alrm_hour;
|
||||
int alrm_mday;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static DEFINE_SPINLOCK(ds1511_lock);
|
||||
@ -302,7 +302,7 @@ ds1511_rtc_update_alarm(struct rtc_plat_data *pdata)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&pdata->rtc->irq_lock, flags);
|
||||
spin_lock_irqsave(&pdata->lock, flags);
|
||||
rtc_write(pdata->alrm_mday < 0 || (pdata->irqen & RTC_UF) ?
|
||||
0x80 : bin2bcd(pdata->alrm_mday) & 0x3f,
|
||||
RTC_ALARM_DATE);
|
||||
@ -317,7 +317,7 @@ ds1511_rtc_update_alarm(struct rtc_plat_data *pdata)
|
||||
RTC_ALARM_SEC);
|
||||
rtc_write(rtc_read(RTC_CMD) | (pdata->irqen ? RTC_TIE : 0), RTC_CMD);
|
||||
rtc_read(RTC_CMD1); /* clear interrupts */
|
||||
spin_unlock_irqrestore(&pdata->rtc->irq_lock, flags);
|
||||
spin_unlock_irqrestore(&pdata->lock, flags);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -362,61 +362,63 @@ ds1511_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct platform_device *pdev = dev_id;
|
||||
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
|
||||
unsigned long events = RTC_IRQF;
|
||||
unsigned long events = 0;
|
||||
|
||||
spin_lock(&pdata->lock);
|
||||
/*
|
||||
* read and clear interrupt
|
||||
*/
|
||||
if (!(rtc_read(RTC_CMD1) & DS1511_IRQF)) {
|
||||
return IRQ_NONE;
|
||||
if (rtc_read(RTC_CMD1) & DS1511_IRQF) {
|
||||
events = RTC_IRQF;
|
||||
if (rtc_read(RTC_ALARM_SEC) & 0x80)
|
||||
events |= RTC_UF;
|
||||
else
|
||||
events |= RTC_AF;
|
||||
if (likely(pdata->rtc))
|
||||
rtc_update_irq(pdata->rtc, 1, events);
|
||||
}
|
||||
if (rtc_read(RTC_ALARM_SEC) & 0x80) {
|
||||
events |= RTC_UF;
|
||||
} else {
|
||||
events |= RTC_AF;
|
||||
}
|
||||
rtc_update_irq(pdata->rtc, 1, events);
|
||||
return IRQ_HANDLED;
|
||||
spin_unlock(&pdata->lock);
|
||||
return events ? IRQ_HANDLED : IRQ_NONE;
|
||||
}
|
||||
|
||||
static int
|
||||
ds1511_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
|
||||
static int ds1511_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
|
||||
|
||||
if (pdata->irq <= 0) {
|
||||
return -ENOIOCTLCMD; /* fall back into rtc-dev's emulation */
|
||||
}
|
||||
switch (cmd) {
|
||||
case RTC_AIE_OFF:
|
||||
pdata->irqen &= ~RTC_AF;
|
||||
ds1511_rtc_update_alarm(pdata);
|
||||
break;
|
||||
case RTC_AIE_ON:
|
||||
if (pdata->irq <= 0)
|
||||
return -EINVAL;
|
||||
if (enabled)
|
||||
pdata->irqen |= RTC_AF;
|
||||
ds1511_rtc_update_alarm(pdata);
|
||||
break;
|
||||
case RTC_UIE_OFF:
|
||||
pdata->irqen &= ~RTC_UF;
|
||||
ds1511_rtc_update_alarm(pdata);
|
||||
break;
|
||||
case RTC_UIE_ON:
|
||||
else
|
||||
pdata->irqen &= ~RTC_AF;
|
||||
ds1511_rtc_update_alarm(pdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ds1511_rtc_update_irq_enable(struct device *dev,
|
||||
unsigned int enabled)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
|
||||
|
||||
if (pdata->irq <= 0)
|
||||
return -EINVAL;
|
||||
if (enabled)
|
||||
pdata->irqen |= RTC_UF;
|
||||
ds1511_rtc_update_alarm(pdata);
|
||||
break;
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
else
|
||||
pdata->irqen &= ~RTC_UF;
|
||||
ds1511_rtc_update_alarm(pdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops ds1511_rtc_ops = {
|
||||
.read_time = ds1511_rtc_read_time,
|
||||
.set_time = ds1511_rtc_set_time,
|
||||
.read_alarm = ds1511_rtc_read_alarm,
|
||||
.set_alarm = ds1511_rtc_set_alarm,
|
||||
.ioctl = ds1511_rtc_ioctl,
|
||||
.read_time = ds1511_rtc_read_time,
|
||||
.set_time = ds1511_rtc_set_time,
|
||||
.read_alarm = ds1511_rtc_read_alarm,
|
||||
.set_alarm = ds1511_rtc_set_alarm,
|
||||
.alarm_irq_enable = ds1511_rtc_alarm_irq_enable,
|
||||
.update_irq_enable = ds1511_rtc_update_irq_enable,
|
||||
};
|
||||
|
||||
static ssize_t
|
||||
@ -492,29 +494,23 @@ ds1511_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rtc_device *rtc;
|
||||
struct resource *res;
|
||||
struct rtc_plat_data *pdata = NULL;
|
||||
struct rtc_plat_data *pdata;
|
||||
int ret = 0;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
return -ENODEV;
|
||||
}
|
||||
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata) {
|
||||
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return -ENOMEM;
|
||||
}
|
||||
pdata->size = res->end - res->start + 1;
|
||||
if (!request_mem_region(res->start, pdata->size, pdev->name)) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
pdata->baseaddr = res->start;
|
||||
pdata->size = pdata->size;
|
||||
ds1511_base = ioremap(pdata->baseaddr, pdata->size);
|
||||
if (!ds1511_base) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (!devm_request_mem_region(&pdev->dev, res->start, pdata->size,
|
||||
pdev->name))
|
||||
return -EBUSY;
|
||||
ds1511_base = devm_ioremap(&pdev->dev, res->start, pdata->size);
|
||||
if (!ds1511_base)
|
||||
return -ENOMEM;
|
||||
pdata->ioaddr = ds1511_base;
|
||||
pdata->irq = platform_get_irq(pdev, 0);
|
||||
|
||||
@ -540,13 +536,15 @@ ds1511_rtc_probe(struct platform_device *pdev)
|
||||
dev_warn(&pdev->dev, "voltage-low detected.\n");
|
||||
}
|
||||
|
||||
spin_lock_init(&pdata->lock);
|
||||
platform_set_drvdata(pdev, pdata);
|
||||
/*
|
||||
* if the platform has an interrupt in mind for this device,
|
||||
* then by all means, set it
|
||||
*/
|
||||
if (pdata->irq > 0) {
|
||||
rtc_read(RTC_CMD1);
|
||||
if (request_irq(pdata->irq, ds1511_interrupt,
|
||||
if (devm_request_irq(&pdev->dev, pdata->irq, ds1511_interrupt,
|
||||
IRQF_DISABLED | IRQF_SHARED, pdev->name, pdev) < 0) {
|
||||
|
||||
dev_warn(&pdev->dev, "interrupt not available.\n");
|
||||
@ -556,33 +554,13 @@ ds1511_rtc_probe(struct platform_device *pdev)
|
||||
|
||||
rtc = rtc_device_register(pdev->name, &pdev->dev, &ds1511_rtc_ops,
|
||||
THIS_MODULE);
|
||||
if (IS_ERR(rtc)) {
|
||||
ret = PTR_ERR(rtc);
|
||||
goto out;
|
||||
}
|
||||
if (IS_ERR(rtc))
|
||||
return PTR_ERR(rtc);
|
||||
pdata->rtc = rtc;
|
||||
platform_set_drvdata(pdev, pdata);
|
||||
ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1511_nvram_attr);
|
||||
if (ret) {
|
||||
goto out;
|
||||
}
|
||||
return 0;
|
||||
out:
|
||||
if (pdata->rtc) {
|
||||
rtc_device_unregister(pdata->rtc);
|
||||
}
|
||||
if (pdata->irq > 0) {
|
||||
free_irq(pdata->irq, pdev);
|
||||
}
|
||||
if (ds1511_base) {
|
||||
iounmap(ds1511_base);
|
||||
ds1511_base = NULL;
|
||||
}
|
||||
if (pdata->baseaddr) {
|
||||
release_mem_region(pdata->baseaddr, pdata->size);
|
||||
}
|
||||
|
||||
kfree(pdata);
|
||||
ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1511_nvram_attr);
|
||||
if (ret)
|
||||
rtc_device_unregister(pdata->rtc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -593,19 +571,13 @@ ds1511_rtc_remove(struct platform_device *pdev)
|
||||
|
||||
sysfs_remove_bin_file(&pdev->dev.kobj, &ds1511_nvram_attr);
|
||||
rtc_device_unregister(pdata->rtc);
|
||||
pdata->rtc = NULL;
|
||||
if (pdata->irq > 0) {
|
||||
/*
|
||||
* disable the alarm interrupt
|
||||
*/
|
||||
rtc_write(rtc_read(RTC_CMD) & ~RTC_TIE, RTC_CMD);
|
||||
rtc_read(RTC_CMD1);
|
||||
free_irq(pdata->irq, pdev);
|
||||
}
|
||||
iounmap(pdata->ioaddr);
|
||||
ds1511_base = NULL;
|
||||
release_mem_region(pdata->baseaddr, pdata->size);
|
||||
kfree(pdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#define DRV_VERSION "0.2"
|
||||
#define DRV_VERSION "0.3"
|
||||
|
||||
#define RTC_REG_SIZE 0x2000
|
||||
#define RTC_OFFSET 0x1ff0
|
||||
@ -61,7 +61,6 @@
|
||||
struct rtc_plat_data {
|
||||
struct rtc_device *rtc;
|
||||
void __iomem *ioaddr;
|
||||
resource_size_t baseaddr;
|
||||
unsigned long last_jiffies;
|
||||
int irq;
|
||||
unsigned int irqen;
|
||||
@ -69,6 +68,7 @@ struct rtc_plat_data {
|
||||
int alrm_min;
|
||||
int alrm_hour;
|
||||
int alrm_mday;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static int ds1553_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
@ -139,7 +139,7 @@ static void ds1553_rtc_update_alarm(struct rtc_plat_data *pdata)
|
||||
void __iomem *ioaddr = pdata->ioaddr;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&pdata->rtc->irq_lock, flags);
|
||||
spin_lock_irqsave(&pdata->lock, flags);
|
||||
writeb(pdata->alrm_mday < 0 || (pdata->irqen & RTC_UF) ?
|
||||
0x80 : bin2bcd(pdata->alrm_mday),
|
||||
ioaddr + RTC_DATE_ALARM);
|
||||
@ -154,7 +154,7 @@ static void ds1553_rtc_update_alarm(struct rtc_plat_data *pdata)
|
||||
ioaddr + RTC_SECONDS_ALARM);
|
||||
writeb(pdata->irqen ? RTC_INTS_AE : 0, ioaddr + RTC_INTERRUPTS);
|
||||
readb(ioaddr + RTC_FLAGS); /* clear interrupts */
|
||||
spin_unlock_irqrestore(&pdata->rtc->irq_lock, flags);
|
||||
spin_unlock_irqrestore(&pdata->lock, flags);
|
||||
}
|
||||
|
||||
static int ds1553_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
@ -194,64 +194,69 @@ static irqreturn_t ds1553_rtc_interrupt(int irq, void *dev_id)
|
||||
struct platform_device *pdev = dev_id;
|
||||
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
|
||||
void __iomem *ioaddr = pdata->ioaddr;
|
||||
unsigned long events = RTC_IRQF;
|
||||
unsigned long events = 0;
|
||||
|
||||
spin_lock(&pdata->lock);
|
||||
/* read and clear interrupt */
|
||||
if (!(readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_AF))
|
||||
return IRQ_NONE;
|
||||
if (readb(ioaddr + RTC_SECONDS_ALARM) & 0x80)
|
||||
events |= RTC_UF;
|
||||
else
|
||||
events |= RTC_AF;
|
||||
rtc_update_irq(pdata->rtc, 1, events);
|
||||
return IRQ_HANDLED;
|
||||
if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_AF) {
|
||||
events = RTC_IRQF;
|
||||
if (readb(ioaddr + RTC_SECONDS_ALARM) & 0x80)
|
||||
events |= RTC_UF;
|
||||
else
|
||||
events |= RTC_AF;
|
||||
if (likely(pdata->rtc))
|
||||
rtc_update_irq(pdata->rtc, 1, events);
|
||||
}
|
||||
spin_unlock(&pdata->lock);
|
||||
return events ? IRQ_HANDLED : IRQ_NONE;
|
||||
}
|
||||
|
||||
static int ds1553_rtc_ioctl(struct device *dev, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
static int ds1553_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
|
||||
|
||||
if (pdata->irq <= 0)
|
||||
return -ENOIOCTLCMD; /* fall back into rtc-dev's emulation */
|
||||
switch (cmd) {
|
||||
case RTC_AIE_OFF:
|
||||
pdata->irqen &= ~RTC_AF;
|
||||
ds1553_rtc_update_alarm(pdata);
|
||||
break;
|
||||
case RTC_AIE_ON:
|
||||
return -EINVAL;
|
||||
if (enabled)
|
||||
pdata->irqen |= RTC_AF;
|
||||
ds1553_rtc_update_alarm(pdata);
|
||||
break;
|
||||
case RTC_UIE_OFF:
|
||||
pdata->irqen &= ~RTC_UF;
|
||||
ds1553_rtc_update_alarm(pdata);
|
||||
break;
|
||||
case RTC_UIE_ON:
|
||||
else
|
||||
pdata->irqen &= ~RTC_AF;
|
||||
ds1553_rtc_update_alarm(pdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ds1553_rtc_update_irq_enable(struct device *dev,
|
||||
unsigned int enabled)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
|
||||
|
||||
if (pdata->irq <= 0)
|
||||
return -EINVAL;
|
||||
if (enabled)
|
||||
pdata->irqen |= RTC_UF;
|
||||
ds1553_rtc_update_alarm(pdata);
|
||||
break;
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
else
|
||||
pdata->irqen &= ~RTC_UF;
|
||||
ds1553_rtc_update_alarm(pdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops ds1553_rtc_ops = {
|
||||
.read_time = ds1553_rtc_read_time,
|
||||
.set_time = ds1553_rtc_set_time,
|
||||
.read_alarm = ds1553_rtc_read_alarm,
|
||||
.set_alarm = ds1553_rtc_set_alarm,
|
||||
.ioctl = ds1553_rtc_ioctl,
|
||||
.read_time = ds1553_rtc_read_time,
|
||||
.set_time = ds1553_rtc_set_time,
|
||||
.read_alarm = ds1553_rtc_read_alarm,
|
||||
.set_alarm = ds1553_rtc_set_alarm,
|
||||
.alarm_irq_enable = ds1553_rtc_alarm_irq_enable,
|
||||
.update_irq_enable = ds1553_rtc_update_irq_enable,
|
||||
};
|
||||
|
||||
static ssize_t ds1553_nvram_read(struct kobject *kobj,
|
||||
struct bin_attribute *bin_attr,
|
||||
char *buf, loff_t pos, size_t size)
|
||||
{
|
||||
struct platform_device *pdev =
|
||||
to_platform_device(container_of(kobj, struct device, kobj));
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
|
||||
void __iomem *ioaddr = pdata->ioaddr;
|
||||
ssize_t count;
|
||||
@ -265,8 +270,8 @@ static ssize_t ds1553_nvram_write(struct kobject *kobj,
|
||||
struct bin_attribute *bin_attr,
|
||||
char *buf, loff_t pos, size_t size)
|
||||
{
|
||||
struct platform_device *pdev =
|
||||
to_platform_device(container_of(kobj, struct device, kobj));
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
|
||||
void __iomem *ioaddr = pdata->ioaddr;
|
||||
ssize_t count;
|
||||
@ -291,26 +296,23 @@ static int __devinit ds1553_rtc_probe(struct platform_device *pdev)
|
||||
struct rtc_device *rtc;
|
||||
struct resource *res;
|
||||
unsigned int cen, sec;
|
||||
struct rtc_plat_data *pdata = NULL;
|
||||
void __iomem *ioaddr = NULL;
|
||||
struct rtc_plat_data *pdata;
|
||||
void __iomem *ioaddr;
|
||||
int ret = 0;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
|
||||
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return -ENOMEM;
|
||||
if (!request_mem_region(res->start, RTC_REG_SIZE, pdev->name)) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
pdata->baseaddr = res->start;
|
||||
ioaddr = ioremap(pdata->baseaddr, RTC_REG_SIZE);
|
||||
if (!ioaddr) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (!devm_request_mem_region(&pdev->dev, res->start, RTC_REG_SIZE,
|
||||
pdev->name))
|
||||
return -EBUSY;
|
||||
|
||||
ioaddr = devm_ioremap(&pdev->dev, res->start, RTC_REG_SIZE);
|
||||
if (!ioaddr)
|
||||
return -ENOMEM;
|
||||
pdata->ioaddr = ioaddr;
|
||||
pdata->irq = platform_get_irq(pdev, 0);
|
||||
|
||||
@ -326,9 +328,13 @@ static int __devinit ds1553_rtc_probe(struct platform_device *pdev)
|
||||
if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_BLF)
|
||||
dev_warn(&pdev->dev, "voltage-low detected.\n");
|
||||
|
||||
spin_lock_init(&pdata->lock);
|
||||
pdata->last_jiffies = jiffies;
|
||||
platform_set_drvdata(pdev, pdata);
|
||||
if (pdata->irq > 0) {
|
||||
writeb(0, ioaddr + RTC_INTERRUPTS);
|
||||
if (request_irq(pdata->irq, ds1553_rtc_interrupt,
|
||||
if (devm_request_irq(&pdev->dev, pdata->irq,
|
||||
ds1553_rtc_interrupt,
|
||||
IRQF_DISABLED, pdev->name, pdev) < 0) {
|
||||
dev_warn(&pdev->dev, "interrupt not available.\n");
|
||||
pdata->irq = 0;
|
||||
@ -337,27 +343,13 @@ static int __devinit ds1553_rtc_probe(struct platform_device *pdev)
|
||||
|
||||
rtc = rtc_device_register(pdev->name, &pdev->dev,
|
||||
&ds1553_rtc_ops, THIS_MODULE);
|
||||
if (IS_ERR(rtc)) {
|
||||
ret = PTR_ERR(rtc);
|
||||
goto out;
|
||||
}
|
||||
if (IS_ERR(rtc))
|
||||
return PTR_ERR(rtc);
|
||||
pdata->rtc = rtc;
|
||||
pdata->last_jiffies = jiffies;
|
||||
platform_set_drvdata(pdev, pdata);
|
||||
|
||||
ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr);
|
||||
if (ret)
|
||||
goto out;
|
||||
return 0;
|
||||
out:
|
||||
if (pdata->rtc)
|
||||
rtc_device_unregister(pdata->rtc);
|
||||
if (pdata->irq > 0)
|
||||
free_irq(pdata->irq, pdev);
|
||||
if (ioaddr)
|
||||
iounmap(ioaddr);
|
||||
if (pdata->baseaddr)
|
||||
release_mem_region(pdata->baseaddr, RTC_REG_SIZE);
|
||||
kfree(pdata);
|
||||
rtc_device_unregister(rtc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -367,13 +359,8 @@ static int __devexit ds1553_rtc_remove(struct platform_device *pdev)
|
||||
|
||||
sysfs_remove_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr);
|
||||
rtc_device_unregister(pdata->rtc);
|
||||
if (pdata->irq > 0) {
|
||||
if (pdata->irq > 0)
|
||||
writeb(0, pdata->ioaddr + RTC_INTERRUPTS);
|
||||
free_irq(pdata->irq, pdev);
|
||||
}
|
||||
iounmap(pdata->ioaddr);
|
||||
release_mem_region(pdata->baseaddr, RTC_REG_SIZE);
|
||||
kfree(pdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#define DRV_VERSION "0.3"
|
||||
#define DRV_VERSION "0.4"
|
||||
|
||||
#define RTC_SIZE 8
|
||||
|
||||
@ -55,7 +55,6 @@ struct rtc_plat_data {
|
||||
void __iomem *ioaddr_rtc;
|
||||
size_t size_nvram;
|
||||
size_t size;
|
||||
resource_size_t baseaddr;
|
||||
unsigned long last_jiffies;
|
||||
struct bin_attribute nvram_attr;
|
||||
};
|
||||
@ -132,8 +131,8 @@ static ssize_t ds1742_nvram_read(struct kobject *kobj,
|
||||
struct bin_attribute *bin_attr,
|
||||
char *buf, loff_t pos, size_t size)
|
||||
{
|
||||
struct platform_device *pdev =
|
||||
to_platform_device(container_of(kobj, struct device, kobj));
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
|
||||
void __iomem *ioaddr = pdata->ioaddr_nvram;
|
||||
ssize_t count;
|
||||
@ -147,8 +146,8 @@ static ssize_t ds1742_nvram_write(struct kobject *kobj,
|
||||
struct bin_attribute *bin_attr,
|
||||
char *buf, loff_t pos, size_t size)
|
||||
{
|
||||
struct platform_device *pdev =
|
||||
to_platform_device(container_of(kobj, struct device, kobj));
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
|
||||
void __iomem *ioaddr = pdata->ioaddr_nvram;
|
||||
ssize_t count;
|
||||
@ -163,27 +162,24 @@ static int __devinit ds1742_rtc_probe(struct platform_device *pdev)
|
||||
struct rtc_device *rtc;
|
||||
struct resource *res;
|
||||
unsigned int cen, sec;
|
||||
struct rtc_plat_data *pdata = NULL;
|
||||
void __iomem *ioaddr = NULL;
|
||||
struct rtc_plat_data *pdata;
|
||||
void __iomem *ioaddr;
|
||||
int ret = 0;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
|
||||
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return -ENOMEM;
|
||||
pdata->size = res->end - res->start + 1;
|
||||
if (!request_mem_region(res->start, pdata->size, pdev->name)) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
pdata->baseaddr = res->start;
|
||||
ioaddr = ioremap(pdata->baseaddr, pdata->size);
|
||||
if (!ioaddr) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (!devm_request_mem_region(&pdev->dev, res->start, pdata->size,
|
||||
pdev->name))
|
||||
return -EBUSY;
|
||||
ioaddr = devm_ioremap(&pdev->dev, res->start, pdata->size);
|
||||
if (!ioaddr)
|
||||
return -ENOMEM;
|
||||
|
||||
pdata->ioaddr_nvram = ioaddr;
|
||||
pdata->size_nvram = pdata->size - RTC_SIZE;
|
||||
pdata->ioaddr_rtc = ioaddr + pdata->size_nvram;
|
||||
@ -207,31 +203,19 @@ static int __devinit ds1742_rtc_probe(struct platform_device *pdev)
|
||||
if (!(readb(ioaddr + RTC_DAY) & RTC_BATT_FLAG))
|
||||
dev_warn(&pdev->dev, "voltage-low detected.\n");
|
||||
|
||||
rtc = rtc_device_register(pdev->name, &pdev->dev,
|
||||
&ds1742_rtc_ops, THIS_MODULE);
|
||||
if (IS_ERR(rtc)) {
|
||||
ret = PTR_ERR(rtc);
|
||||
goto out;
|
||||
}
|
||||
pdata->rtc = rtc;
|
||||
pdata->last_jiffies = jiffies;
|
||||
platform_set_drvdata(pdev, pdata);
|
||||
rtc = rtc_device_register(pdev->name, &pdev->dev,
|
||||
&ds1742_rtc_ops, THIS_MODULE);
|
||||
if (IS_ERR(rtc))
|
||||
return PTR_ERR(rtc);
|
||||
pdata->rtc = rtc;
|
||||
|
||||
ret = sysfs_create_bin_file(&pdev->dev.kobj, &pdata->nvram_attr);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "creating nvram file in sysfs failed\n");
|
||||
goto out;
|
||||
rtc_device_unregister(rtc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
out:
|
||||
if (pdata->rtc)
|
||||
rtc_device_unregister(pdata->rtc);
|
||||
if (pdata->ioaddr_nvram)
|
||||
iounmap(pdata->ioaddr_nvram);
|
||||
if (pdata->baseaddr)
|
||||
release_mem_region(pdata->baseaddr, pdata->size);
|
||||
kfree(pdata);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -241,9 +225,6 @@ static int __devexit ds1742_rtc_remove(struct platform_device *pdev)
|
||||
|
||||
sysfs_remove_bin_file(&pdev->dev.kobj, &pdata->nvram_attr);
|
||||
rtc_device_unregister(pdata->rtc);
|
||||
iounmap(pdata->ioaddr_nvram);
|
||||
release_mem_region(pdata->baseaddr, pdata->size);
|
||||
kfree(pdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -142,7 +142,6 @@ static const struct rtc_class_ops m48t35_ops = {
|
||||
|
||||
static int __devinit m48t35_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rtc_device *rtc;
|
||||
struct resource *res;
|
||||
struct m48t35_priv *priv;
|
||||
int ret = 0;
|
||||
@ -171,20 +170,21 @@ static int __devinit m48t35_probe(struct platform_device *pdev)
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
spin_lock_init(&priv->lock);
|
||||
rtc = rtc_device_register("m48t35", &pdev->dev,
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
priv->rtc = rtc_device_register("m48t35", &pdev->dev,
|
||||
&m48t35_ops, THIS_MODULE);
|
||||
if (IS_ERR(rtc)) {
|
||||
ret = PTR_ERR(rtc);
|
||||
if (IS_ERR(priv->rtc)) {
|
||||
ret = PTR_ERR(priv->rtc);
|
||||
goto out;
|
||||
}
|
||||
priv->rtc = rtc;
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
if (priv->rtc)
|
||||
rtc_device_unregister(priv->rtc);
|
||||
if (priv->reg)
|
||||
iounmap(priv->reg);
|
||||
if (priv->baseaddr)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user