All of lore.kernel.org
 help / color / mirror / Atom feed
* Re: Which version of nilfs do you use?
From: Elmer Zhang @ 2012-12-28  2:20 UTC (permalink / raw)
  To: dexen deVries; +Cc: linux-nilfs-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1902997.ayblf9kj7g@coil>

Hi,

There is more than 2TB of data and a large number of databases, tables in our MySQL service. The conventional cold backup methods can not meet our needs. So we are trying to find some other methods.

I copied the MySQL files after shut MySQL done. The same data runs well with ext4 file system on another server, so I can confirm that my original data is no problem.

Elmer Zhang


ÔÚ 2012-12-27£¬18:35£¬dexen deVries <dexen.devries-Re5JQEeQqe8@public.gmane.orgm> дµÀ£º

> Hi Elmer,
> 
> 
> On Thursday 27 of December 2012 17:47:34 you wrote:
>> I am trying to use NILFS2 to make MySQL cold backup, but  the MySQL tables
>> often be crashed. It seems there is some problems with nilfs filesystem.
>> 
>> I have described how I use nilfs in this mail:
>> http://www.mail-archive.com/linux-nilfs-u79uwXL29TY76Z2rM5mHXA@public.gmane.org/msg01481.html
>> 
>> Can I know which version of nilfs do you use, and what do you use nilfs for?
> 
> FWIW,  I have MySQL installation on NILFS2 and it works just fine. The server 
> is only under light load, thou. MySQL version 5.5.27 (and 5.1.50 earlier on); 
> NILFS2 from kernel v3.0 and later, up to v3.5.
> 
> Perhaps your problems have something to do with MySQL recovery of data from 
> the cold backup? In MySQL Reference Manual chapter 7 -- Backup and Recovery 
> covers the subject.
> 
> If I'm reading your other email correctly, it seems you copied MyISAM files 
> without shutting MySQL down first. That's not supposed to be done with non-
> transactional tables, AFAIK, and I'd suspect this as the main trouble source. 
> 
> On the other hand, InnoDB tables should survive being copied from working 
> MySQL, as long as you observe correct order of (table, log files) copying -- 
> not sure which goes first, it's in the docs.
> 
> 
> Cheers,
> -- 
> dexen deVries
> 
> [[[¡ý][¡ú]]]
> 
> 
> Reality is just a convenient measure of complexity.
>                -- Alvy Ray Smith

--
To unsubscribe from this list: send the line "unsubscribe linux-nilfs" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: bugreport Connectivity problems with rtl8192cu
From: Alden Page @ 2012-12-28  2:18 UTC (permalink / raw)
  To: linux-wireless
In-Reply-To: <50DCE1F3.806@lwfinger.net>


Larry Finger <Larry.Finger@...> writes:

> What is this doing here? You need to have either the in-kernel
 driver available, or the vendor driver. This bit shows you
> have both.

Since I was having a lot of trouble with the in-kernel driver,
I tried the vendor driver. I went to some lengths to get it to
compile and install in thenew kernel. Unfortunately, this was 
before I referred to the documentation and read that it supports
x86 only.

Apparently I didn't uninstall the broken vendor driver entirely
as I thought I did. I will try to clean it out. Still, the
problems that I described were present before I installed it.


^ permalink raw reply

* [PATCH] mm: fix null pointer dereference in wait_iff_congested()
From: Zlatko Calusic @ 2012-12-28  2:16 UTC (permalink / raw)
  To: Andrew Morton, Mel Gorman
  Cc: Linus Torvalds, Hugh Dickins, linux-mm, Linux Kernel Mailing List,
	Zhouping Liu, Sedat Dilek
In-Reply-To: <50DB129E.7010000@iskon.hr>

From: Zlatko Calusic <zlatko.calusic@iskon.hr>

The unintended consequence of commit 4ae0a48b is that
wait_iff_congested() can now be called with NULL struct zone*
producing kernel oops like this:

BUG: unable to handle kernel NULL pointer dereference
IP: [<ffffffff811542d9>] wait_iff_congested+0x59/0x140

This trivial patch fixes it.

Reported-by: Zhouping Liu <zliu@redhat.com>
Reported-and-tested-by: Sedat Dilek <sedat.dilek@gmail.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Mel Gorman <mgorman@suse.de>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Zlatko Calusic <zlatko.calusic@iskon.hr>
---
 mm/vmscan.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mm/vmscan.c b/mm/vmscan.c
index 02bcfa3..e55ce55 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -2782,7 +2782,7 @@ loop_again:
 		if (total_scanned && (sc.priority < DEF_PRIORITY - 2)) {
 			if (has_under_min_watermark_zone)
 				count_vm_event(KSWAPD_SKIP_CONGESTION_WAIT);
-			else
+			else if (unbalanced_zone)
 				wait_iff_congested(unbalanced_zone, BLK_RW_ASYNC, HZ/10);
 		}
 
-- 
1.8.1.rc3

-- 
Zlatko

^ permalink raw reply related

* [PATCH] mm: fix null pointer dereference in wait_iff_congested()
From: Zlatko Calusic @ 2012-12-28  2:16 UTC (permalink / raw)
  To: Andrew Morton, Mel Gorman
  Cc: Linus Torvalds, Hugh Dickins, linux-mm, Linux Kernel Mailing List,
	Zhouping Liu, Sedat Dilek
In-Reply-To: <50DB129E.7010000@iskon.hr>

From: Zlatko Calusic <zlatko.calusic@iskon.hr>

The unintended consequence of commit 4ae0a48b is that
wait_iff_congested() can now be called with NULL struct zone*
producing kernel oops like this:

BUG: unable to handle kernel NULL pointer dereference
IP: [<ffffffff811542d9>] wait_iff_congested+0x59/0x140

This trivial patch fixes it.

Reported-by: Zhouping Liu <zliu@redhat.com>
Reported-and-tested-by: Sedat Dilek <sedat.dilek@gmail.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Mel Gorman <mgorman@suse.de>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Zlatko Calusic <zlatko.calusic@iskon.hr>
---
 mm/vmscan.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mm/vmscan.c b/mm/vmscan.c
index 02bcfa3..e55ce55 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -2782,7 +2782,7 @@ loop_again:
 		if (total_scanned && (sc.priority < DEF_PRIORITY - 2)) {
 			if (has_under_min_watermark_zone)
 				count_vm_event(KSWAPD_SKIP_CONGESTION_WAIT);
-			else
+			else if (unbalanced_zone)
 				wait_iff_congested(unbalanced_zone, BLK_RW_ASYNC, HZ/10);
 		}
 
-- 
1.8.1.rc3

-- 
Zlatko

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related

* [QA 12/28] Testing report for `drm-intel-testing` (was: Updated -next)
From: Sun, Yi @ 2012-12-28  2:14 UTC (permalink / raw)
  To: Daniel Vetter, Intel Graphics Development; +Cc: Yang, Guang A


[-- Attachment #1.1: Type: text/plain, Size: 2658 bytes --]

Summary

We finished a new round of kernel testing. Generally, in this circle, 7 new bug is filed, 8 bugs are still opened, 7 bugs are closed.



Test Environment

Kernel: (drm-intel-testing)5c43656cdac8062485ba54625bdc2edfc34ef0cf

Some additional commit info:

Merge: c0c36b9 da494d7

Author: Daniel Vetter <daniel.vetter@ffwll.ch<mailto:daniel.vetter@ffwll.ch>>

Date:   Fri Dec 21 01:54:51 2012 +0100



Hardware

We covered the platform: Haswell, IvyBridge, SandyBridge, IronLake, G45 and Pineview



Findings

New Bug:

Bug 58497<https://bugs.freedesktop.org/show_bug.cgi?id=58497> - [IVB HSW] I-G-T/testdisplay, there's error in dregs

Bug 58555<https://bugs.freedesktop.org/show_bug.cgi?id=58555> - [ILK,Pineview,G45,SNB,HSW]I-G-T/kms_flip fail

Bug 58695<https://bugs.freedesktop.org/show_bug.cgi?id=58695> - I-G-T/prime_udl fail

Bug 58701<https://bugs.freedesktop.org/show_bug.cgi?id=58701> - [SNB]I-G-T/testdisplay && 1920x1080i showing not full

Bug 58790<https://bugs.freedesktop.org/show_bug.cgi?id=58790> - [ILK DP] 1920x1080i error showing

Bug 58791<https://bugs.freedesktop.org/show_bug.cgi?id=58791> - [ILK] Some modes can't light up on DP display

Bug 58799 - [HSW B0 desktop] S4 fail to resume



Opened Bugs:

Bug 36997 - [G45 SDVO] TV can't display -- SDVO xfer fails due to Pending.

Bug 42194 - [IVB/SNB] coldplug new monitors for fbcon on lastclose()

Bug 45729 - [bisected regression] Incorrect Mode Timing on DP Display, with 3.3-rc (due to interlaced CEA modes)

Bug 51975 - [IVB]can't find the HDMI audio device

Bug 54111 - [IVB]I-G-T/module_reload fail with *ERROR* "Memory manager not clean. Delaying takedown"

Bug 57441<https://bugs.freedesktop.org/show_bug.cgi?id=57441> - [HSW]I-G-T/sysfs_l3_parity fail

Bug 57593<https://bugs.freedesktop.org/show_bug.cgi?id=57593> - [HSW desktop] screen shift right for some VGA modes

Bug 57638<https://bugs.freedesktop.org/show_bug.cgi?id=57638> - [SNB,IVB,HSW]I-G-T/gem_non_secure_batch fail with *ERROR* Hangcheck timer elapsed... GPU hung


Closed Bug:

Bug 52424 - [Bisected SNB Regression] glxgears causes GPU hung

Bug 54911<https://bugs.freedesktop.org/show_bug.cgi?id=54911> - [IVB] EDID error on phantom VGA

Bug 56568<https://bugs.freedesktop.org/show_bug.cgi?id=56568> - [IVB] 3-pipe (VGA + 2 DVI) fail to work

Bug 57934 - [IVB, SNB, HSW]I-G-T/gem_cacheing && gem_cpu_concurrent_blit failed

Bug 58111 - [SNB regression]Mimi-HDMI screen mess

Bug 58113 - [SNB]I-G-T/gem_render_tiled_blits failed

Bug 58279 - [HSW B0 desktop] Resume from S3, VGA white screen



Thanks
    --Sun, Yi


[-- Attachment #1.2: Type: text/html, Size: 9763 bytes --]

[-- Attachment #2: Type: text/plain, Size: 159 bytes --]

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply

* [PATCH v4 2/7] usb: chipidea: add otg file
From: Peter Chen @ 2012-12-28  2:14 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20121227104758.GV26326@pengutronix.de>

On Thu, Dec 27, 2012 at 11:47:58AM +0100, Sascha Hauer wrote:
> On Thu, Dec 27, 2012 at 02:59:58PM +0800, Peter Chen wrote:
> >  struct ci13xxx {
> >  	struct device			*dev;
> > @@ -164,6 +165,7 @@ struct ci13xxx {
> >  	bool				global_phy;
> >  	struct usb_phy			*transceiver;
> >  	struct usb_hcd			*hcd;
> > +	struct usb_otg      otg;
> 
> I'd really like to know how this is going to proceed.
> 
> The ULPI driver currently allocates a struct usb_otg and sets
> otg->set_host and otg->set_vbus. Since now the chipidea driver uses its
> own struct usb_otg, this will not work with ULPI support.
We discussed before that the otg is not related to phy.
The struct usb_otg is better allocated at the otg driver, not phy driver.

I have a look at other phy drivers(drivers/usb/phy), now, there
is no one to create struct usb_otg at their phy drivers. To solve this
problem, we may need to modify ulpi phy driver.

To integrate ULPI phy, we may need to do:
- Make ulpi as a platform driver
- Override otg.set_host, and otg.set_vbus with ulpi's at
otg_ulpi_create or what else.
- Call otg_ulpi_create at the end of ci13xxx_imx_probe (before
pm_runtime_no_callbacks) if the phy is ulpi.

> 
> I see some ways out of this:
> 
> - Use a pointer above instead of an own struct usb_otg in the ci driver.
> - move the set_host and set_vbus callbacks to struct usb_phy.
> 
> I'd like to have this solved before we merge this patch.
> 
> Sascha
> 
> 
> -- 
> Pengutronix e.K.                           |                             |
> Industrial Linux Solutions                 | http://www.pengutronix.de/  |
> Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
> Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
> 

-- 

Best Regards,
Peter Chen

^ permalink raw reply

* [PATCH v2 0/4] ARM: pxa: palmtreo: a few treo fixes
From: Haojian Zhuang @ 2012-12-28  2:02 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20121228001840.GC22054@crashnator.suse.cz>

On 28 December 2012 08:18, Tomas Cech <tcech@suse.cz> wrote:
> Hi,
>
>
> On Thu, Dec 27, 2012 at 10:02:16AM -0800, Mike Dunn wrote:
>>
>> Some miscellaneous fixes to the treo code.
>>
>> Change log:
>> v2:
>>  patch 1: gpio initialization separated out from lcd initialization, per
>>            Marek's suggestion (lcd still initializes correctly this way).
>>  patch 2: Removed #ifdef CONFIG_MACH_TREO680 from around defines in
>>            both palmtreo.h and leds-gpio device data in palmtreo.c, per
>> Marek's
>>            suggestion, and let compiler optimize out unused data
>> (verified).
>>  patch 3: Use IS_EMABLED macro, per Sergei's suggestion
>>  patch 4: new patch for v2: use IS_ENABLED elsewhere in file
>>
>> Mike Dunn (4):
>>  ARM: palmtreo: fix lcd initilialization on treo680
>>  ARM: palmtreo: fix #ifdefs for leds-gpio device
>>  ARM: palmtreo: add docg4 device initialization
>>  ARM: palmtreo: replace #if defined with IF_ENABLED
>>
>> arch/arm/mach-pxa/include/mach/palmtreo.h |    5 +-
>> arch/arm/mach-pxa/palmtreo.c              |  101
>> ++++++++++++++++++++++++++---
>> 2 files changed, 94 insertions(+), 12 deletions(-)
>>
>> --
>> 1.7.8.6
>>
>
> for all the patches
>
> Acked-by: Tomas Cech <sleep_walker@suse.cz>
>
> Thanks for these fixes!
>
> Best regards,
>
> Tomas Cech

Applied all.

^ permalink raw reply

* Re: [PATCH 3/3] libceph: WARN, don't BUG on unexpected connection states
From: Alex Elder @ 2012-12-28  2:01 UTC (permalink / raw)
  To: Sage Weil; +Cc: ceph-devel
In-Reply-To: <alpine.DEB.2.00.1212271655190.22449@cobra.newdream.net>

On 12/27/2012 06:57 PM, Sage Weil wrote:
> I agree that we should do BUG -> WARN on con->state everywhere.
> 
> But I don't think we should drop any of them, yet.  For Ugis's particular 
> crash, it was fail_protocol()'s fault... see my other patch.  The rest of 
> the time, a socket close should be caught at the top of con_work().

I looked at it again, and I think you're right.  I'd rather keep
the constraints in anyway.  So I will remove that last hunk from
this patch.

> For any other cases where we see con->state changing when we don't expect 
> it to, let's look at them on a case-by-case basis and address them in 
> separate patches?

Good plan.  With WARN_ON() rather than BUG_ON() if we find something
we got wrong it won't be a serious problem.

					-Alex

> sage
> 
> 
> On Thu, 27 Dec 2012, Alex Elder wrote:
> 
>> A number of assertions in the ceph messenger are implemented with
>> BUG_ON(), killing the system if connection's state doesn't match
>> what's expected.  At this point our state model is (evidently) not
>> well understood enough for these assertions to trigger a BUG().
>> Convert all BUG_ON(con->state...) calls to be WARN_ON(con->state...)
>> so we learn about these issues without killing the machine.
>>
>> We now recognize that a connection fault can occur due to a socket
>> closure at any time, regardless of the state of the connection.  So
>> there is really nothing we can assert about the state of the
>> connection at that point so eliminate that assertion.
>>
>> Reported-by: Ugis <ugis22@gmail.com>
>> Tested-by: Ugis <ugis22@gmail.com>
>> Signed-off-by: Alex Elder <elder@inktank.com>
>> ---
>>  net/ceph/messenger.c |   13 ++++---------
>>  1 file changed, 4 insertions(+), 9 deletions(-)
>>
>> diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c
>> index 4d111fd..075b9fd 100644
>> --- a/net/ceph/messenger.c
>> +++ b/net/ceph/messenger.c
>> @@ -561,7 +561,7 @@ void ceph_con_open(struct ceph_connection *con,
>>  	mutex_lock(&con->mutex);
>>  	dout("con_open %p %s\n", con, ceph_pr_addr(&addr->in_addr));
>>
>> -	BUG_ON(con->state != CON_STATE_CLOSED);
>> +	WARN_ON(con->state != CON_STATE_CLOSED);
>>  	con->state = CON_STATE_PREOPEN;
>>
>>  	con->peer_name.type = (__u8) entity_type;
>> @@ -1509,7 +1509,7 @@ static int process_banner(struct ceph_connection *con)
>>  static void fail_protocol(struct ceph_connection *con)
>>  {
>>  	reset_connection(con);
>> -	BUG_ON(con->state != CON_STATE_NEGOTIATING);
>> +	WARN_ON(con->state != CON_STATE_NEGOTIATING);
>>  	con->state = CON_STATE_CLOSED;
>>  }
>>
>> @@ -1635,7 +1635,7 @@ static int process_connect(struct ceph_connection
>> *con)
>>  			return -1;
>>  		}
>>
>> -		BUG_ON(con->state != CON_STATE_NEGOTIATING);
>> +		WARN_ON(con->state != CON_STATE_NEGOTIATING);
>>  		con->state = CON_STATE_OPEN;
>>
>>  		con->peer_global_seq = le32_to_cpu(con->in_reply.global_seq);
>> @@ -2132,7 +2132,6 @@ more:
>>  		if (ret < 0)
>>  			goto out;
>>
>> -		BUG_ON(con->state != CON_STATE_CONNECTING);
>>  		con->state = CON_STATE_NEGOTIATING;
>>
>>  		/*
>> @@ -2160,7 +2159,7 @@ more:
>>  		goto more;
>>  	}
>>
>> -	BUG_ON(con->state != CON_STATE_OPEN);
>> +	WARN_ON(con->state != CON_STATE_OPEN);
>>
>>  	if (con->in_base_pos < 0) {
>>  		/*
>> @@ -2382,10 +2381,6 @@ static void ceph_fault(struct ceph_connection *con)
>>  	dout("fault %p state %lu to peer %s\n",
>>  	     con, con->state, ceph_pr_addr(&con->peer_addr.in_addr));
>>
>> -	BUG_ON(con->state != CON_STATE_CONNECTING &&
>> -	       con->state != CON_STATE_NEGOTIATING &&
>> -	       con->state != CON_STATE_OPEN);
>> -
>>  	con_close_socket(con);
>>
>>  	if (test_bit(CON_FLAG_LOSSYTX, &con->flags)) {
>> -- 
>> 1.7.9.5
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe ceph-devel" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>
>>


^ permalink raw reply

* Re: [GIT PULL] f2fs fixes for 3.8-rc2
From: Sedat Dilek @ 2012-12-28  2:00 UTC (permalink / raw)
  To: jaegeuk.kim
  Cc: Linus Torvalds, Linux Kernel, Mailing List,
	Linux FS DEV, Mailing List, Linux F2FS DEV, Mailing List
In-Reply-To: <1356659666.9202.4.camel@kjgkr>

Looks like you didn't S-o-b-ed all patches in your tree (see Leon's or
Geert's patch).

- Sedat -

On Fri, Dec 28, 2012 at 2:54 AM, Jaegeuk Kim <jaegeuk.kim@samsung.com> wrote:
> Hi Linus,
>
> Please pull the following patches for bug fixes on the f2fs file system.
> Thank you very much.
>
> --
> Jaegeuk Kim
> Samsung
>
> The following changes since commit
> 637704cbc95c02d18741b4a6e7a5d2397f8b28ce:
>
>   Merge branch 'i2c-embedded/for-next' of
> git://git.pengutronix.de/git/wsa/linux (2012-12-23 09:48:33 -0800)
>
> are available in the git repository at:
>
>
>   git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs.git
> tags/for-linus
>
> for you to fetch changes up to 782c92d129775840cf9c1ad334f0722df9c76dc0:
>
>   f2fs: unify string length declarations and usage (2012-12-28 09:57:55
> +0900)
>
> ----------------------------------------------------------------
> f2fs: bug fixes for 3.8-rc2
>
> This patch-set includes two major bug fixes:
> - incorrect IUsed provided by *df -i*, and
> - lookup failure of parent inodes in corner cases.
>
> [Other Bug Fixes]
> - Fix error handling routines
> - Trigger recovery process correctly
> - Resolve build failures due to missing header files
>
> [Etc]
> - Add a MAINTAINERS entry for f2fs
> - Fix and clean up variables, functions, and equations
> - Avoid warnings during compilation
>
> ----------------------------------------------------------------
> Geert Uytterhoeven (1):
>       f2fs: add missing #include <linux/prefetch.h>
>
> Jaegeuk Kim (11):
>       f2fs: remove set_page_dirty for atomic f2fs_end_io_write
>       f2fs: fix wrong calculation on f_files in statfs
>       f2fs: fix handling errors got by f2fs_write_inode
>       f2fs: should recover orphan and fsync data
>       f2fs: do f2fs_balance_fs in front of dir operations
>       f2fs: invalidate the node page if allocation is failed
>       f2fs: return a default value for non-void function
>       f2fs: add MAINTAINERS entry
>       f2fs: fix equation of has_not_enough_free_secs()
>       f2fs: clean up the start_bidx_of_node function
>       f2fs: clean up unused variables and return values
>
> Leon Romanovsky (1):
>       f2fs: unify string length declarations and usage
>
> Namjae Jeon (5):
>       f2fs: fix up f2fs_get_parent issue to retrieve correct parent
> inode number
>       f2fs: handle error from f2fs_iget_nowait
>       f2fs: remove unneeded initialization of nr_dirty in
> dirty_seglist_info
>       f2fs: fix fsync_inode list addition logic and avoid invalid access
> to memory
>       f2fs: remove unneeded variable from f2fs_sync_fs
>
>  MAINTAINERS        | 10 ++++++++++
>  fs/f2fs/data.c     |  1 +
>  fs/f2fs/dir.c      | 16 +++++++++-------
>  fs/f2fs/f2fs.h     |  2 +-
>  fs/f2fs/file.c     | 10 ++++++----
>  fs/f2fs/gc.c       | 34 +++++++++++-----------------------
>  fs/f2fs/hash.c     | 18 +++++++++++-------
>  fs/f2fs/inode.c    |  1 +
>  fs/f2fs/namei.c    | 34 +++++++++++++++++-----------------
>  fs/f2fs/node.c     | 37 ++++++++++++++-----------------------
>  fs/f2fs/recovery.c | 10 +++++++---
>  fs/f2fs/segment.c  | 46 ++++++----------------------------------------
>  fs/f2fs/segment.h  | 15 ++++++++++++++-
>  fs/f2fs/super.c    | 15 ++++++---------
>  fs/f2fs/xattr.c    |  5 +++--
>  15 files changed, 117 insertions(+), 137 deletions(-)
>

^ permalink raw reply

* [U-Boot] powerpc/mpc8315erdb : Integration of customized source code to u-boot
From: Krishna Mohan Chagaleti @ 2012-12-28  1:58 UTC (permalink / raw)
  To: u-boot
In-Reply-To: <20121227102554.C75BD200F7A@gemini.denx.de>

Hi Wolfgang Denk,

Thank you very much for your reply.
First of all, sorry for the confusion created in my previous email.

Before describing the actual problem, I would like to share the board and uboot details as below.

Board: Freescale
CPU: e300c3, MPC8315E, Rev: 1.2 at 400 MH3 MHz
I2C:y
DRAFLASH: 8 MB
NAND: 32 MiB

U-boot version: U-Boot 2009.03-rc2 (Dec 27 2012 - 13:41:26) MPC83XX (this is as per "version" command on my u-boot)

Coming to the problem details, I have written few customized applications to test RAM, Nand & Nor flash of my freescale board.
I compiled the applications using powerpc libraries (powerpc-e300c3-linux-gnu) and created ELF executable files.
I converted the ELF executables to binary format using the command "powerpc-e300c3-linux-gnu-objcopy".
When i tftp the binary files and run on u-boot giving the entry-point address(using "go" command), the u-boot is getting reset.
So, i want to know how can i overcome this situation and execute my binary file successfully. This is the first part as per your email.
The above procedure is executed with the reference: http://www.denx.de/wiki/view/DULG/MyStandaloneProgramDoesNotWork

The second part is about integrating my applications as part of u-boot examples/apis permanenetly so that i can run them whenever i wish to.
For example, i see the command "echo" and can run it on u-boot prompt anytime. Can i also embed my application and make it as part of commands.
The whole idea is to make my binaries part of u-boot when it gets loaded.

Sorry for a lengthy email. Please let me know if you still require any further details.

Thank you.

Warm Regards,
-Krishna


________________________________________
From: Wolfgang Denk [wd at denx.de]
Sent: Thursday, December 27, 2012 6:26 PM
To: Krishna Mohan Chagaleti
Cc: u-boot at lists.denx.de; scottwood at freescale.com
Subject: Re: [U-Boot] powerpc/mpc8315erdb : Integration of customized source code to u-boot

Dear Krishna Mohan Chagaleti,

In message <C1C8A20BBB404A44975107148C578C459B6667@MBX01-TPM.mimos.local> you wrote:
>
> I have been trying to test and integrate my customized binary on u-boot.
>
> I tried the procedure of executing the example demo application
> (hello_world.bin) by downloading binary using tftp and executing it.
> But, it didn't help though.

What exactly was the problem? Which exact commands did you run, and
what was the results / error messages / output?

On which exact board are you working, and which exact version of
U-Boot are you using.

> I wanted to know if there is any procedure to integrate my customized
> applications to u-boot environment and run as commands once the
> u-boot is loaded.

Please explan what you really want to do. The first part sounds as if
you wanted to run some standalone applications written by yourself.
The second part ("run as commands") sounds as if you wanted to extend
the functionality of U-Boot itself. Please note that these are
different things.

> Please let me know if you require more details.

Yes, we need a lot more details, see above. [It may help to read
http://www.catb.org/~esr/faqs/smart-questions.html , too.]

Best regards,

Wolfgang Denk

--
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd at denx.de
A rolling stone gathers momentum.

^ permalink raw reply

* [GIT PULL] f2fs fixes for 3.8-rc2
From: Jaegeuk Kim @ 2012-12-28  1:54 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Linux Kernel, Mailing List, Linux FS DEV, Mailing List,
	Linux F2FS DEV, Mailing List

[-- Attachment #1: Type: text/plain, Size: 3088 bytes --]

Hi Linus,

Please pull the following patches for bug fixes on the f2fs file system.
Thank you very much.

-- 
Jaegeuk Kim
Samsung

The following changes since commit
637704cbc95c02d18741b4a6e7a5d2397f8b28ce:

  Merge branch 'i2c-embedded/for-next' of
git://git.pengutronix.de/git/wsa/linux (2012-12-23 09:48:33 -0800)

are available in the git repository at:


  git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs.git
tags/for-linus

for you to fetch changes up to 782c92d129775840cf9c1ad334f0722df9c76dc0:

  f2fs: unify string length declarations and usage (2012-12-28 09:57:55
+0900)

----------------------------------------------------------------
f2fs: bug fixes for 3.8-rc2

This patch-set includes two major bug fixes:
- incorrect IUsed provided by *df -i*, and
- lookup failure of parent inodes in corner cases.

[Other Bug Fixes]
- Fix error handling routines
- Trigger recovery process correctly
- Resolve build failures due to missing header files

[Etc]
- Add a MAINTAINERS entry for f2fs
- Fix and clean up variables, functions, and equations
- Avoid warnings during compilation

----------------------------------------------------------------
Geert Uytterhoeven (1):
      f2fs: add missing #include <linux/prefetch.h>

Jaegeuk Kim (11):
      f2fs: remove set_page_dirty for atomic f2fs_end_io_write
      f2fs: fix wrong calculation on f_files in statfs
      f2fs: fix handling errors got by f2fs_write_inode
      f2fs: should recover orphan and fsync data
      f2fs: do f2fs_balance_fs in front of dir operations
      f2fs: invalidate the node page if allocation is failed
      f2fs: return a default value for non-void function
      f2fs: add MAINTAINERS entry
      f2fs: fix equation of has_not_enough_free_secs()
      f2fs: clean up the start_bidx_of_node function
      f2fs: clean up unused variables and return values

Leon Romanovsky (1):
      f2fs: unify string length declarations and usage

Namjae Jeon (5):
      f2fs: fix up f2fs_get_parent issue to retrieve correct parent
inode number
      f2fs: handle error from f2fs_iget_nowait
      f2fs: remove unneeded initialization of nr_dirty in
dirty_seglist_info
      f2fs: fix fsync_inode list addition logic and avoid invalid access
to memory
      f2fs: remove unneeded variable from f2fs_sync_fs

 MAINTAINERS        | 10 ++++++++++
 fs/f2fs/data.c     |  1 +
 fs/f2fs/dir.c      | 16 +++++++++-------
 fs/f2fs/f2fs.h     |  2 +-
 fs/f2fs/file.c     | 10 ++++++----
 fs/f2fs/gc.c       | 34 +++++++++++-----------------------
 fs/f2fs/hash.c     | 18 +++++++++++-------
 fs/f2fs/inode.c    |  1 +
 fs/f2fs/namei.c    | 34 +++++++++++++++++-----------------
 fs/f2fs/node.c     | 37 ++++++++++++++-----------------------
 fs/f2fs/recovery.c | 10 +++++++---
 fs/f2fs/segment.c  | 46 ++++++----------------------------------------
 fs/f2fs/segment.h  | 15 ++++++++++++++-
 fs/f2fs/super.c    | 15 ++++++---------
 fs/f2fs/xattr.c    |  5 +++--
 15 files changed, 117 insertions(+), 137 deletions(-)


[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: Linux 3.3+ and memory cgroup kernel panics
From: Kamezawa Hiroyuki @ 2012-12-28  1:50 UTC (permalink / raw)
  To: David Strauss; +Cc: cgroups-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <CAKz8sYXcY9kP=QPVAWdP4a-6Nuq-04yDJNuvBojDTfKbvj=x9A-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

(2012/12/22 11:44), David Strauss wrote:
> The kernel seemed to replace the cgroups memory "charging" mechanism
> in 3.3 with a more efficient implementation [1], but we think it may
> be broken under Xen virtualization and load. We do not see any issue
> in Linux 3.2 and earlier.
>
> We have documented panics for Fedora kernels 3.3.4-5.fc17.x86_64,
> 3.3.5-2.fc16.x86_64, and 3.6.10-2.fc16.x86_64 but *not* on Fedora
> kernels 3.1.0-7.fc16.x86_64 or 3.2.6-3.fc16.x86_64.
>
> Many of our services use MemoryLimit= and similar systemd options that
> create a memory cgroup for the service. This correlates with kernel
> panics under the following call path (full listing here [2]):
>
> [20488075.457394]  [<ffffffff811825e7>] ? mem_cgroup_charge_statistics+0x17/0x60
> [20488075.457403]  [<ffffffff81184ade>] __mem_cgroup_uncharge_common+0xfe/0x330
> [20488075.457410]  [<ffffffff8100632d>] ? xen_pte_val+0x1d/0x40
> [20488075.457417]  [<ffffffff81188457>] mem_cgroup_uncharge_page+0x37/0x40
> [20488075.457424]  [<ffffffff8115e6d1>] page_remove_rmap+0xb1/0x140
>
> It culminates in this failure:
>
> [20488075.457183] kernel BUG at arch/x86/mm/fault.c:396!
> [20488075.457189] invalid opcode: 0000 [#1] SMP
>
> There are also reports of similar failures [3] unrelated to systemd
> use and on non-Fedora kernels.
>
> It appears to be an issue with re-attributing the charge for a page to
> a different cgroup. Any ideas why we would be seeing this with Linux
> 3.3+? I can generally reproduce the issue (often minutes after
> booting) on any heavily loaded machine in order to collect any
> additional data to help troubleshooting.
>
> [1] https://lwn.net/Articles/443241/
> [2] https://gist.github.com/raw/70afc901a73e427a0a71
> [3] https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1073238/comments/6
>

This is 1st time for me to see this kind of backlog...EIP is finally BUG_ON()
in vmalloc_fault(). The fault address was in VMALLOC range.
VMALLOC_START < address < VMALLOC_END.

Maybe it's percpu area by memcg->stat which is backed by vmalloc area.

Hmm....no troubles on native host ?

Thanks,
-Kame









^ permalink raw reply

* Re: [PATCH V3 7/8] memcg: disable memcg page stat accounting code when not in use
From: Kamezawa Hiroyuki @ 2012-12-28  1:45 UTC (permalink / raw)
  To: Sha Zhengju
  Cc: linux-kernel, cgroups, linux-mm, mhocko, akpm, gthelen,
	fengguang.wu, glommer, Sha Zhengju
In-Reply-To: <1356456477-14780-1-git-send-email-handai.szj@taobao.com>

(2012/12/26 2:27), Sha Zhengju wrote:
> From: Sha Zhengju <handai.szj@taobao.com>
> 
> It's inspired by a similar optimization from Glauber Costa
> (memcg: make it suck faster; https://lkml.org/lkml/2012/9/25/154).
> Here we use jump label to patch the memcg page stat accounting code
> in or out when not used. when the first non-root memcg comes to
> life the code is patching in otherwise it is out.
> 
> Signed-off-by: Sha Zhengju <handai.szj@taobao.com>
> ---
>   include/linux/memcontrol.h |    9 +++++++++
>   mm/memcontrol.c            |    8 ++++++++
>   2 files changed, 17 insertions(+)
> 
> diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
> index 1d22b81..3c4430c 100644
> --- a/include/linux/memcontrol.h
> +++ b/include/linux/memcontrol.h
> @@ -56,6 +56,9 @@ struct mem_cgroup_reclaim_cookie {
>   };
>   
>   #ifdef CONFIG_MEMCG
> +
> +extern struct static_key memcg_in_use_key;
> +
>   /*
>    * All "charge" functions with gfp_mask should use GFP_KERNEL or
>    * (gfp_mask & GFP_RECLAIM_MASK). In current implementatin, memcg doesn't
> @@ -158,6 +161,9 @@ extern atomic_t memcg_moving;
>   static inline void mem_cgroup_begin_update_page_stat(struct page *page,
>   					bool *locked, unsigned long *flags)
>   {
> +	if (!static_key_false(&memcg_in_use_key))
> +		return;
> +
>   	if (mem_cgroup_disabled())
>   		return;
>   	rcu_read_lock();
> @@ -171,6 +177,9 @@ void __mem_cgroup_end_update_page_stat(struct page *page,
>   static inline void mem_cgroup_end_update_page_stat(struct page *page,
>   					bool *locked, unsigned long *flags)
>   {
> +	if (!static_key_false(&memcg_in_use_key))
> +		return;
> +
>   	if (mem_cgroup_disabled())
>   		return;
>   	if (*locked)
> diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> index 0cb5187..a2f73d7 100644
> --- a/mm/memcontrol.c
> +++ b/mm/memcontrol.c
> @@ -531,6 +531,8 @@ enum res_type {
>   #define MEM_CGROUP_RECLAIM_SHRINK_BIT	0x1
>   #define MEM_CGROUP_RECLAIM_SHRINK	(1 << MEM_CGROUP_RECLAIM_SHRINK_BIT)
>   
> +struct static_key memcg_in_use_key;
> +
>   static void mem_cgroup_get(struct mem_cgroup *memcg);
>   static void mem_cgroup_put(struct mem_cgroup *memcg);
>   
> @@ -2226,6 +2228,9 @@ void mem_cgroup_update_page_stat(struct page *page,
>   	struct page_cgroup *pc = lookup_page_cgroup(page);
>   	unsigned long uninitialized_var(flags);
>   
> +	if (!static_key_false(&memcg_in_use_key))
> +		return;
> +
>   	if (mem_cgroup_disabled())
>   		return;
>   
> @@ -6340,6 +6345,8 @@ mem_cgroup_css_alloc(struct cgroup *cont)
>   		parent = mem_cgroup_from_cont(cont->parent);
>   		memcg->use_hierarchy = parent->use_hierarchy;
>   		memcg->oom_kill_disable = parent->oom_kill_disable;
> +
> +		static_key_slow_inc(&memcg_in_use_key);
>   	}
>   
>   	if (parent && parent->use_hierarchy) {
> @@ -6407,6 +6414,7 @@ static void mem_cgroup_css_free(struct cgroup *cont)
>   	kmem_cgroup_destroy(memcg);
>   
>   	memcg_dangling_add(memcg);
> +	static_key_slow_dec(&memcg_in_use_key);

Hmm. I think this part should be folded into disarm_static_keys().

Thanks,
-Kame





^ permalink raw reply

* Re: [PATCH V3 7/8] memcg: disable memcg page stat accounting code when not in use
From: Kamezawa Hiroyuki @ 2012-12-28  1:45 UTC (permalink / raw)
  To: Sha Zhengju
  Cc: linux-kernel, cgroups, linux-mm, mhocko, akpm, gthelen,
	fengguang.wu, glommer, Sha Zhengju
In-Reply-To: <1356456477-14780-1-git-send-email-handai.szj@taobao.com>

(2012/12/26 2:27), Sha Zhengju wrote:
> From: Sha Zhengju <handai.szj@taobao.com>
> 
> It's inspired by a similar optimization from Glauber Costa
> (memcg: make it suck faster; https://lkml.org/lkml/2012/9/25/154).
> Here we use jump label to patch the memcg page stat accounting code
> in or out when not used. when the first non-root memcg comes to
> life the code is patching in otherwise it is out.
> 
> Signed-off-by: Sha Zhengju <handai.szj@taobao.com>
> ---
>   include/linux/memcontrol.h |    9 +++++++++
>   mm/memcontrol.c            |    8 ++++++++
>   2 files changed, 17 insertions(+)
> 
> diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
> index 1d22b81..3c4430c 100644
> --- a/include/linux/memcontrol.h
> +++ b/include/linux/memcontrol.h
> @@ -56,6 +56,9 @@ struct mem_cgroup_reclaim_cookie {
>   };
>   
>   #ifdef CONFIG_MEMCG
> +
> +extern struct static_key memcg_in_use_key;
> +
>   /*
>    * All "charge" functions with gfp_mask should use GFP_KERNEL or
>    * (gfp_mask & GFP_RECLAIM_MASK). In current implementatin, memcg doesn't
> @@ -158,6 +161,9 @@ extern atomic_t memcg_moving;
>   static inline void mem_cgroup_begin_update_page_stat(struct page *page,
>   					bool *locked, unsigned long *flags)
>   {
> +	if (!static_key_false(&memcg_in_use_key))
> +		return;
> +
>   	if (mem_cgroup_disabled())
>   		return;
>   	rcu_read_lock();
> @@ -171,6 +177,9 @@ void __mem_cgroup_end_update_page_stat(struct page *page,
>   static inline void mem_cgroup_end_update_page_stat(struct page *page,
>   					bool *locked, unsigned long *flags)
>   {
> +	if (!static_key_false(&memcg_in_use_key))
> +		return;
> +
>   	if (mem_cgroup_disabled())
>   		return;
>   	if (*locked)
> diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> index 0cb5187..a2f73d7 100644
> --- a/mm/memcontrol.c
> +++ b/mm/memcontrol.c
> @@ -531,6 +531,8 @@ enum res_type {
>   #define MEM_CGROUP_RECLAIM_SHRINK_BIT	0x1
>   #define MEM_CGROUP_RECLAIM_SHRINK	(1 << MEM_CGROUP_RECLAIM_SHRINK_BIT)
>   
> +struct static_key memcg_in_use_key;
> +
>   static void mem_cgroup_get(struct mem_cgroup *memcg);
>   static void mem_cgroup_put(struct mem_cgroup *memcg);
>   
> @@ -2226,6 +2228,9 @@ void mem_cgroup_update_page_stat(struct page *page,
>   	struct page_cgroup *pc = lookup_page_cgroup(page);
>   	unsigned long uninitialized_var(flags);
>   
> +	if (!static_key_false(&memcg_in_use_key))
> +		return;
> +
>   	if (mem_cgroup_disabled())
>   		return;
>   
> @@ -6340,6 +6345,8 @@ mem_cgroup_css_alloc(struct cgroup *cont)
>   		parent = mem_cgroup_from_cont(cont->parent);
>   		memcg->use_hierarchy = parent->use_hierarchy;
>   		memcg->oom_kill_disable = parent->oom_kill_disable;
> +
> +		static_key_slow_inc(&memcg_in_use_key);
>   	}
>   
>   	if (parent && parent->use_hierarchy) {
> @@ -6407,6 +6414,7 @@ static void mem_cgroup_css_free(struct cgroup *cont)
>   	kmem_cgroup_destroy(memcg);
>   
>   	memcg_dangling_add(memcg);
> +	static_key_slow_dec(&memcg_in_use_key);

Hmm. I think this part should be folded into disarm_static_keys().

Thanks,
-Kame




--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply

* ppoll() stuck on POLLIN while TCP peer is sending
From: Eric Wong @ 2012-12-28  1:45 UTC (permalink / raw)
  To: netdev, linux-kernel
  Cc: Andreas Voellmy, viro, linux-fsdevel, Junchang(Jason) Wang

I'm finding ppoll() unexpectedly stuck when waiting for POLLIN on a
local TCP socket.  The isolated code below can reproduces the issue
after many minutes (<1 hour).  It might be easier to reproduce on
a busy system while disk I/O is happening.

This may also be related to an epoll-related issue reported
by Andreas Voellmy:
http://thread.gmane.org/gmane.linux.kernel/1408782/

My example involves a 3 thread data flow between two pairs
of (4) sockets:

	 send_loop ->   recv_loop(recv_send)   -> recv_loop(recv_only)
	 pair_a[1] -> (pair_a[0] -> pair_b[1]) -> pair_b[0]

At least 3.7 and 3.7.1 are affected.

I have tcp_low_latency=1 set, I will try 0 later

The last progress message I got was after receiving 2942052597760
bytes on fd=7 (out of 64-bit ULONG_MAX / 2)

strace:

3644  sendto(4, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 16384, 0, NULL, 0 <unfinished ...>
3643  sendto(6, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 16384, 0, NULL, 0 <unfinished ...>
3642  ppoll([{fd=7, events=POLLIN}], 1, NULL, NULL, 8 <unfinished ...>
3641  futex(0x7f23ed8129d0, FUTEX_WAIT, 3644, NULL <unfinished ...>

The first and last lines of the strace are expected:

+ 3644	sendto(4) is blocked because 3643 is blocked on sendto(fd=6)
  and not able to call recv().
+ 3641 is the main thread calling pthread_join

What is unexpected is the tid=3643 and tid=3642 interaction.  As confirmed
by lsof below, fd=6 is sending to wake up fd=7, but ppoll(fd=7) seems
to not be waking up.

lsof:
toosleepy 3641   ew    4u  IPv4  12405      0t0     TCP localhost:55904->localhost:33249 (ESTABLISHED)
toosleepy 3641   ew    5u  IPv4  12406      0t0     TCP localhost:33249->localhost:55904 (ESTABLISHED)
toosleepy 3641   ew    6u  IPv4  12408      0t0     TCP localhost:48777->localhost:33348 (ESTABLISHED)
toosleepy 3641   ew    7u  IPv4  12409      0t0     TCP localhost:33348->localhost:48777 (ESTABLISHED)

System info: Linux 3.7.1 x86_64 SMP PREEMPT
AMD Phenom(tm) II X4 945 Processor (4 cores)
Nothing interesting in dmesg, iptables rules are empty.

I have not yet been able to reproduce the issue using UNIX sockets,
only TCP, but you can run:

  ./toosleepy unix

...to test with UNIX sockets intead of TCP.

The following code is also available via git://bogomips.org/toosleepy
gcc -o toosleepy -O2 -Wall -lpthread toosleepy.c
-------------------------------- 8< ------------------------------------
#define _GNU_SOURCE
#include <poll.h>
#include <sys/ioctl.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <assert.h>
#include <limits.h>

struct receiver {
	int rfd;
	int sfd;
};

/* blocking sender */
static void * send_loop(void *fdp)
{
	int fd = *(int *)fdp;
	char buf[16384];
	ssize_t s;
	size_t sent = 0;
	size_t max = (size_t)ULONG_MAX / 2;

	while (sent < max) {
		s = send(fd, buf, sizeof(buf), 0);
		if (s > 0)
			sent += s;
		if (s == -1)
			assert(errno == EINTR);
	}
	dprintf(2, "%d done sending: %zu\n", fd, sent);
	close(fd);
	return NULL;
}

/* non-blocking receiver, using ppoll */
static void * recv_loop(void *p)
{
	const struct receiver *rcvr = p;
	char buf[16384];
	nfds_t nfds = 1;
	struct pollfd fds;
	int rc;
	ssize_t r, s;
	size_t received = 0;
	size_t sent = 0;

	for (;;) {
		r = recv(rcvr->rfd, buf, sizeof(buf), 0);
		if (r == 0) {
			break;
		} else if (r == -1) {
			assert(errno == EAGAIN);

			fds.fd = rcvr->rfd;
			fds.events = POLLIN;
			errno = 0;
			rc = ppoll(&fds, nfds, NULL, NULL);
			assert(rc == 1);
		} else {
			assert(r > 0);
			received += r;
			if (rcvr->sfd >= 0) {
				s = send(rcvr->sfd, buf, sizeof(buf), 0);
				if (s > 0)
					sent += s;
				if (s == -1)
					assert(errno == EINTR);
			} else {
				/* just burn some cycles */
				write(-1, buf, sizeof(buf));
			}
		}
		if ((received % (sizeof(buf) * sizeof(buf) * 16) == 0))
			dprintf(2, " %d progress: %zu\n",
			        rcvr->rfd, received);
	}
	dprintf(2, "%d got: %zu\n", rcvr->rfd, received);
	if (rcvr->sfd >= 0) {
		dprintf(2, "%d sent: %zu\n", rcvr->sfd, sent);
		close(rcvr->sfd);
	}

	return NULL;
}

static void tcp_socketpair(int sv[2], int accept_flags)
{
	struct sockaddr_in addr;
	socklen_t addrlen = sizeof(addr);
	int l = socket(PF_INET, SOCK_STREAM, 0);
	int c = socket(PF_INET, SOCK_STREAM, 0);
	int a;

	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = INADDR_ANY;
	addr.sin_port = 0;
	assert(0 == bind(l, (struct sockaddr*)&addr, addrlen));
	assert(0 == listen(l, 1024));
	assert(0 == getsockname(l, (struct sockaddr *)&addr, &addrlen));
	assert(0 == connect(c, (struct sockaddr *)&addr, addrlen));
	a = accept4(l, NULL, NULL, accept_flags);
	assert(a >= 0);
	close(l);
	sv[0] = a;
	sv[1] = c;
}

int main(int argc, char *argv[])
{
	int pair_a[2];
	int pair_b[2];
	pthread_t s, rs, r;
	struct receiver recv_only;
	struct receiver recv_send;

	if (argc == 2 && strcmp(argv[1], "unix") == 0) {
		int val;
		assert(0 == socketpair(AF_UNIX, SOCK_STREAM, 0, pair_a));
		assert(0 == socketpair(AF_UNIX, SOCK_STREAM, 0, pair_b));
		/* only make the receiver non-blocking */
		val = 1;
		assert(0 == ioctl(pair_a[0], FIONBIO, &val));
		val = 1;
		assert(0 == ioctl(pair_b[0], FIONBIO, &val));
	} else {
		tcp_socketpair(pair_a, SOCK_NONBLOCK);
		tcp_socketpair(pair_b, SOCK_NONBLOCK);
	}

	recv_send.rfd = pair_a[0];
	recv_send.sfd = pair_b[1];
	recv_only.rfd = pair_b[0];
	recv_only.sfd = -1;

	/*
	 * data flow:
	 * send_loop ->   recv_loop(recv_send)   -> recv_loop(recv_only)
	 * pair_a[1] -> (pair_a[0] -> pair_b[1]) -> pair_b[0]
	 */
	assert(0 == pthread_create(&r, NULL, recv_loop, &recv_only));
	assert(0 == pthread_create(&rs, NULL, recv_loop, &recv_send));
	assert(0 == pthread_create(&s, NULL, send_loop, &pair_a[1]));
	assert(0 == pthread_join(s, NULL));
	assert(0 == pthread_join(rs, NULL));
	assert(0 == pthread_join(r, NULL));

	return 0;
}
-------------------------------- 8< ------------------------------------
Any help/suggestions/test patches would be greatly appreciated.
Thanks for reading!

-- 
Eric Wong

^ permalink raw reply

* [U-Boot] [PATCH 3/4] fix memory corruption on versatile
From: Pavel Herrmann @ 2012-12-28  1:44 UTC (permalink / raw)
  To: u-boot
In-Reply-To: <20121227095907.453ca2bf@lilith>

Hi

On Thursday 27 of December 2012 09:59:07 Albert ARIBAUD wrote:
> Hi Pavel,
> 
> > Technically, the patch was quoted whole, but it goes on top of another,
> > that added the DM and early heap macros to the board config, so its of
> > no use as it is.
> 
> Ok, so IIUC this patch is not required for fixing anything in the
> current mainline u-boot, and would not break anything in mainline
> u-boot either, but is required to arrange pre-relocation memory mapping
> for current DM code to work.

Essentially yes, except it would not apply without the first patch. if you 
applied both to mainline, then nothing would hapen (some space would be 
allocated for the early heap, that would never get used), as you dont have any 
DM code there, so it would be just a useless defined macro.

> > > Based on the assumption that this patch is complete as quoted, and on
> > > your comments above, my comment would be (to both Marek and you) why do
> > > you nead a heap during flash-based inits?
> > 
> > We want the DM to replace most of the board-specific code (by providing a
> > "driver" that is configured by board-specific values, essentialy
> > encapsulating common init code independently on actual board and memory
> > layout), so that the board-specific init only loads required drivers with
> > correct parameters, instead of directly initializing the hardware (there
> > was a thought of having a RAM driver, that would init the main memory
> > when loaded, and provided relcation as a method/service, not sure if we
> > want to go this far at the moment).
> > for this we need the DM to run in early (flash-based) phase. The DM itself
> > cannot work without a heap, so we need one there.
> 
> Well, for lack of understanding of DM (I know DM documentation is out
> there, just haven't had time to look it up so far) I cannot tell
> whether it cannot work without a heap or whether it could work without
> it but hasn't been designed that way.
> 
> This leads to two questions:
> 
> 1) Why does the DM need a heap in the first place? When you look at the
> DM requirements (as I understand them), they basically include a full C
> runtime environment, which is precisely what we do *not* provide in the
> first stage of U-Boot, because this first stage is *meant* to set the C
> runtime environment up.

Yes, there are 2 problems (or maybe more) with C in the flash phase - you dont 
have a heap, and everything except your stack is read-only.
the early heap solves the first issue, and the DM is aimed at solving the 
second problem, as we avoid using any global data (well, except for the root 
of DM driver tree, which is in GD), and have everything in per-driver-instance 
data structures, which are allocated on the heap

> 2) Assuming these requirements can be met in a viable way, is this heap
> supposed to survive through relocation? And if it is, then how will it,
> and most of all, how will references to it, remain consistent without
> an ugly manual relocation fixup process?

the heap is supposed to survive relocation, but only for a little while.
what we do is taht after setting up normal heap (and dlmalloc, instead of the 
simple malloc we have in the early heap), we order each driver to relocate to 
a new address. basically we have a "ugly manual relocation fixup process", that 
is made manageable by dividing it between the individual drivers (as only the 
driver knows what private data it created during initialization). we have an 
infrastructure in place that walks the DM driver tree and calls these 
relocation hooks in a sane order. after this is done, we forget about the 
early heap (enable caches if it was in locked cache or whatever).

Pavel Herrmann

^ permalink raw reply

* Re: problems with dracut mounting subvols
From: Chris Murphy @ 2012-12-28  1:44 UTC (permalink / raw)
  To: linux-btrfs Mailing list
In-Reply-To: <50DCF245.20705@parallels.com>


On Dec 27, 2012, at 6:13 PM, dima <dolenin@parallels.com> wrote:
> 
> 
>> /dev/disk/by-uuid/64383cfe-c31d-4d25-97c4-4e6b7e788b26 /sysroot/usr btrfs subvol=usr,subvol=root,ro 1 2
> 
> I'd say that the problem is definitely with this line having two subvolumes listed.
> Maybe you should boot from a live CD, mount your subvolid=1 and check out what subvolumes you really have in there. And then re-write fstab manually.

When I mount subvolid=5 there are subvols root, boot, usr, var, home. And the /etc/fstab in root is:

#
# /etc/fstab
# Created by anaconda on Thu Dec 27 15:21:03 2012
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
UUID=64383cfe-c31d-4d25-97c4-4e6b7e788b26 /                       btrfs   subvol=root     1 1
UUID=64383cfe-c31d-4d25-97c4-4e6b7e788b26 /boot                   btrfs   subvol=boot     1 2
UUID=64383cfe-c31d-4d25-97c4-4e6b7e788b26 /home                   btrfs   subvol=home     1 2
UUID=64383cfe-c31d-4d25-97c4-4e6b7e788b26 /usr                    btrfs   subvol=usr      1 2
UUID=64383cfe-c31d-4d25-97c4-4e6b7e788b26 /var                    btrfs   subvol=var      1 2


It's correct.

Seems dracut is creating the wrong fstab for itself, but then also can't mount it correctly manually from a shell command. When booted in rescue mode to a full system, not just a dracut shell, I can mount -o subvol=root to one mount point, and also mount -o subvol=usr to another mountpoint (within or outside the former mount point) without error.


> If you have /sysroot subvolume, you can create /sysroot/usr as a child subvolume and you won't even need then to specify /sysroot/usr in your fstab because it will be mounted automatically when the parent subvolume gets mounted.

It's a useful work around. I'd like to narrow down if the real problem is dracut and if it should eventually be fixed. Seems both need fixing.


Chris Murphy

^ permalink raw reply

* [Bug 46557] nouveau: NV4E acceleration corruption when DMA above 31-bit (2 Gig barrier)
From: bugzilla-daemon-CC+yJ3UmIYqDUpFQwHEjaQ @ 2012-12-28  1:33 UTC (permalink / raw)
  To: nouveau-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW
In-Reply-To: <bug-46557-8800-V0hAGp6uBxMKqLRl/0Ahz6D7qz1kEfGD2LY78lusg7I@public.gmane.org/>


[-- Attachment #1.1: Type: text/plain, Size: 798 bytes --]

https://bugs.freedesktop.org/show_bug.cgi?id=46557

--- Comment #29 from Marcin Slusarz <marcin.slusarz-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> ---
Created attachment 72201
  --> https://bugs.freedesktop.org/attachment.cgi?id=72201&action=edit
limit vm size to 31 bits (nv04-nv40,nv45)

Ok, original Salah's issue seems to be fixed. Xorg crashes and CACHE_ERRORs
look like separate bugs - please open new bug reports for them (note that for
CACHE_ERRORs I advise running nouveau git kernel with
http://lists.freedesktop.org/archives/nouveau/2012-December/011780.html).

Raphaël Droz's: you have nv34, so changing something in *nv44.c* obviously
won't fix anything for you... Does the above patch help?

-- 
You are receiving this mail because:
You are the assignee for the bug.

[-- Attachment #1.2: Type: text/html, Size: 1963 bytes --]

[-- Attachment #2: Type: text/plain, Size: 181 bytes --]

_______________________________________________
Nouveau mailing list
Nouveau-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org
http://lists.freedesktop.org/mailman/listinfo/nouveau

^ permalink raw reply

* [PATCH v7 2/2] Documentation: add sysfs ABI documentation for ksm
From: Petr Holasek @ 2012-12-28  1:32 UTC (permalink / raw)
  To: Hugh Dickins
  Cc: Andrea Arcangeli, Andrew Morton, Izik Eidus, Rik van Riel,
	David Rientjes, Sasha Levin, linux-kernel, linux-mm, Anton Arapov,
	Petr Holasek
In-Reply-To: <1356658337-12540-1-git-send-email-pholasek@redhat.com>

This patch adds sysfs documentation for Kernel Samepage Merging (KSM)
including new merge_across_nodes knob.

Signed-off-by: Petr Holasek <pholasek@redhat.com>
---
 Documentation/ABI/testing/sysfs-kernel-mm-ksm | 51 +++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-kernel-mm-ksm

diff --git a/Documentation/ABI/testing/sysfs-kernel-mm-ksm b/Documentation/ABI/testing/sysfs-kernel-mm-ksm
new file mode 100644
index 0000000..44384ae
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-kernel-mm-ksm
@@ -0,0 +1,51 @@
+What:		/sys/kernel/mm/ksm
+Date:		September 2009
+Contact:	Linux memory management mailing list <linux-mm@kvack.org>
+Description:	/sys/kernel/mm/ksm contains interface of Kernel Samepage
+		Merging (KSM)
+
+What:		/sys/kernel/mm/ksm/full_scans
+What:		/sys/kernel/mm/ksm/pages_shared
+What:		/sys/kernel/mm/ksm/pages_sharing
+What:		/sys/kernel/mm/ksm/pages_to_scan
+What:		/sys/kernel/mm/ksm/pages_unshared
+What:		/sys/kernel/mm/ksm/pages_volatile
+What:		/sys/kernel/mm/ksm/run
+What:		/sys/kernel/mm/ksm/sleep_millisecs
+Date:		September 2009
+Contact:	Linux memory management mailing list <linux-mm@kvack.org>
+Description:	Kernel Samepage Merging daemon sysfs interface
+
+		full_scans: how many times all mergeable areas have been
+		scanned.
+
+		pages_shared: how many shared pages are being used.
+
+		pages_sharing: how many more sites are sharing them i.e. how
+		much saved.
+
+		pages_to_scan: how many present pages to scan before ksmd goes
+		to sleep.
+
+		pages_unshared: how many pages unique but repeatedly checked
+		for merging.
+
+		pages_volatile: how many pages changing too fast to be placed
+		in a tree.
+
+		run: write 0 to disable ksm, read 0 while ksm is disabled.
+			write 1 to run ksm, read 1 while ksm is running.
+			write 2 to disable ksm and unmerge all its pages.
+
+		sleep_millisecs: how many milliseconds ksm should sleep between
+		scans.
+
+		See Documentation/vm/ksm.txt for more information.
+
+What:		/sys/kernel/mm/ksm/merge_across_nodes
+Date:		December 2012
+Contact:	Linux memory management mailing list <linux-mm@kvack.org>
+Description:	Control merging pages across different NUMA nodes.
+
+		When it is set to 0 only pages from the same node are merged,
+		otherwise pages from all nodes can be merged together (default).
-- 
1.7.11.7


^ permalink raw reply related

* [PATCH v7 1/2] KSM: numa awareness sysfs knob
From: Petr Holasek @ 2012-12-28  1:32 UTC (permalink / raw)
  To: Hugh Dickins
  Cc: Andrea Arcangeli, Andrew Morton, Izik Eidus, Rik van Riel,
	David Rientjes, Sasha Levin, linux-kernel, linux-mm, Anton Arapov,
	Petr Holasek
In-Reply-To: <20121224050817.GA25749@kroah.com>

Introduces new sysfs boolean knob /sys/kernel/mm/ksm/merge_across_nodes
which control merging pages across different numa nodes.
When it is set to zero only pages from the same node are merged,
otherwise pages from all nodes can be merged together (default behavior).

Typical use-case could be a lot of KVM guests on NUMA machine
and cpus from more distant nodes would have significant increase
of access latency to the merged ksm page. Sysfs knob was choosen
for higher variability when some users still prefers higher amount
of saved physical memory regardless of access latency.

Every numa node has its own stable & unstable trees because of faster
searching and inserting. Changing of merge_across_nodes value is possible
only when there are not any ksm shared pages in system.

I've tested this patch on numa machines with 2, 4 and 8 nodes and
measured speed of memory access inside of KVM guests with memory pinned
to one of nodes with this benchmark:

http://pholasek.fedorapeople.org/alloc_pg.c

Population standard deviations of access times in percentage of average
were following:

merge_across_nodes=1
2 nodes 1.4%
4 nodes 1.6%
8 nodes	1.7%

merge_across_nodes=0
2 nodes	1%
4 nodes	0.32%
8 nodes	0.018%

RFC: https://lkml.org/lkml/2011/11/30/91
v1: https://lkml.org/lkml/2012/1/23/46
v2: https://lkml.org/lkml/2012/6/29/105
v3: https://lkml.org/lkml/2012/9/14/550
v4: https://lkml.org/lkml/2012/9/23/137
v5: https://lkml.org/lkml/2012/12/10/540
v6: https://lkml.org/lkml/2012/12/23/154

Changelog:

v2: Andrew's objections were reflected:
	- value of merge_nodes can't be changed while there are some ksm
	pages in system
	- merge_nodes sysfs entry appearance depends on CONFIG_NUMA
	- more verbose documentation
	- added some performance testing results

v3:	- more verbose documentation
	- fixed race in merge_nodes store function
	- introduced share_all debugging knob proposed by Andrew
	- minor cleanups

v4:	- merge_nodes was renamed to merge_across_nodes
	- share_all debug knob was dropped
	- get_kpfn_nid helper
	- fixed page migration behaviour

v5:	- unstable node's nid presence depends on CONFIG_NUMA
	- fixed oops appearing when stable nodes were removed from tree
	- roots of stable trees are initialized properly
	- fixed unstable page migration issue

v6:	- fixed oops caused by stable_nodes appended to wrong tree
	- KSM_RUN_MERGE test removed

v7:	- added sysfs ABI documentation for KSM

Signed-off-by: Petr Holasek <pholasek@redhat.com>
Signed-off-by: Hugh Dickins <hughd@google.com>
Acked-by: Rik van Riel <riel@redhat.com>
---
 Documentation/vm/ksm.txt |   7 +++
 mm/ksm.c                 | 151 +++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 139 insertions(+), 19 deletions(-)

diff --git a/Documentation/vm/ksm.txt b/Documentation/vm/ksm.txt
index b392e49..25cc89b 100644
--- a/Documentation/vm/ksm.txt
+++ b/Documentation/vm/ksm.txt
@@ -58,6 +58,13 @@ sleep_millisecs  - how many milliseconds ksmd should sleep before next scan
                    e.g. "echo 20 > /sys/kernel/mm/ksm/sleep_millisecs"
                    Default: 20 (chosen for demonstration purposes)
 
+merge_across_nodes - specifies if pages from different numa nodes can be merged.
+                   When set to 0, ksm merges only pages which physically
+                   reside in the memory area of same NUMA node. It brings
+                   lower latency to access to shared page. Value can be
+                   changed only when there is no ksm shared pages in system.
+                   Default: 1
+
 run              - set 0 to stop ksmd from running but keep merged pages,
                    set 1 to run ksmd e.g. "echo 1 > /sys/kernel/mm/ksm/run",
                    set 2 to stop ksmd and unmerge all pages currently merged,
diff --git a/mm/ksm.c b/mm/ksm.c
index 5157385..d1e1041 100644
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -36,6 +36,7 @@
 #include <linux/hash.h>
 #include <linux/freezer.h>
 #include <linux/oom.h>
+#include <linux/numa.h>
 
 #include <asm/tlbflush.h>
 #include "internal.h"
@@ -139,6 +140,9 @@ struct rmap_item {
 	struct mm_struct *mm;
 	unsigned long address;		/* + low bits used for flags below */
 	unsigned int oldchecksum;	/* when unstable */
+#ifdef CONFIG_NUMA
+	unsigned int nid;
+#endif
 	union {
 		struct rb_node node;	/* when node of unstable tree */
 		struct {		/* when listed from stable tree */
@@ -153,8 +157,8 @@ struct rmap_item {
 #define STABLE_FLAG	0x200	/* is listed from the stable tree */
 
 /* The stable and unstable tree heads */
-static struct rb_root root_stable_tree = RB_ROOT;
-static struct rb_root root_unstable_tree = RB_ROOT;
+static struct rb_root root_unstable_tree[MAX_NUMNODES];
+static struct rb_root root_stable_tree[MAX_NUMNODES];
 
 #define MM_SLOTS_HASH_SHIFT 10
 #define MM_SLOTS_HASH_HEADS (1 << MM_SLOTS_HASH_SHIFT)
@@ -189,6 +193,9 @@ static unsigned int ksm_thread_pages_to_scan = 100;
 /* Milliseconds ksmd should sleep between batches */
 static unsigned int ksm_thread_sleep_millisecs = 20;
 
+/* Zeroed when merging across nodes is not allowed */
+static unsigned int ksm_merge_across_nodes = 1;
+
 #define KSM_RUN_STOP	0
 #define KSM_RUN_MERGE	1
 #define KSM_RUN_UNMERGE	2
@@ -447,10 +454,25 @@ out:		page = NULL;
 	return page;
 }
 
+/*
+ * This helper is used for getting right index into array of tree roots.
+ * When merge_across_nodes knob is set to 1, there are only two rb-trees for
+ * stable and unstable pages from all nodes with roots in index 0. Otherwise,
+ * every node has its own stable and unstable tree.
+ */
+static inline int get_kpfn_nid(unsigned long kpfn)
+{
+	if (ksm_merge_across_nodes)
+		return 0;
+	else
+		return pfn_to_nid(kpfn);
+}
+
 static void remove_node_from_stable_tree(struct stable_node *stable_node)
 {
 	struct rmap_item *rmap_item;
 	struct hlist_node *hlist;
+	int nid;
 
 	hlist_for_each_entry(rmap_item, hlist, &stable_node->hlist, hlist) {
 		if (rmap_item->hlist.next)
@@ -462,7 +484,9 @@ static void remove_node_from_stable_tree(struct stable_node *stable_node)
 		cond_resched();
 	}
 
-	rb_erase(&stable_node->node, &root_stable_tree);
+	nid = get_kpfn_nid(stable_node->kpfn);
+
+	rb_erase(&stable_node->node, &root_stable_tree[nid]);
 	free_stable_node(stable_node);
 }
 
@@ -560,7 +584,12 @@ static void remove_rmap_item_from_tree(struct rmap_item *rmap_item)
 		age = (unsigned char)(ksm_scan.seqnr - rmap_item->address);
 		BUG_ON(age > 1);
 		if (!age)
-			rb_erase(&rmap_item->node, &root_unstable_tree);
+#ifdef CONFIG_NUMA
+			rb_erase(&rmap_item->node,
+					&root_unstable_tree[rmap_item->nid]);
+#else
+			rb_erase(&rmap_item->node, &root_unstable_tree[0]);
+#endif
 
 		ksm_pages_unshared--;
 		rmap_item->address &= PAGE_MASK;
@@ -996,8 +1025,9 @@ static struct page *try_to_merge_two_pages(struct rmap_item *rmap_item,
  */
 static struct page *stable_tree_search(struct page *page)
 {
-	struct rb_node *node = root_stable_tree.rb_node;
+	struct rb_node *node;
 	struct stable_node *stable_node;
+	int nid;
 
 	stable_node = page_stable_node(page);
 	if (stable_node) {			/* ksm page forked */
@@ -1005,6 +1035,9 @@ static struct page *stable_tree_search(struct page *page)
 		return page;
 	}
 
+	nid = get_kpfn_nid(page_to_pfn(page));
+	node = root_stable_tree[nid].rb_node;
+
 	while (node) {
 		struct page *tree_page;
 		int ret;
@@ -1039,10 +1072,16 @@ static struct page *stable_tree_search(struct page *page)
  */
 static struct stable_node *stable_tree_insert(struct page *kpage)
 {
-	struct rb_node **new = &root_stable_tree.rb_node;
+	int nid;
+	unsigned long kpfn;
+	struct rb_node **new;
 	struct rb_node *parent = NULL;
 	struct stable_node *stable_node;
 
+	kpfn = page_to_pfn(kpage);
+	nid = get_kpfn_nid(kpfn);
+	new = &root_stable_tree[nid].rb_node;
+
 	while (*new) {
 		struct page *tree_page;
 		int ret;
@@ -1076,11 +1115,11 @@ static struct stable_node *stable_tree_insert(struct page *kpage)
 		return NULL;
 
 	rb_link_node(&stable_node->node, parent, new);
-	rb_insert_color(&stable_node->node, &root_stable_tree);
+	rb_insert_color(&stable_node->node, &root_stable_tree[nid]);
 
 	INIT_HLIST_HEAD(&stable_node->hlist);
 
-	stable_node->kpfn = page_to_pfn(kpage);
+	stable_node->kpfn = kpfn;
 	set_page_stable_node(kpage, stable_node);
 
 	return stable_node;
@@ -1104,10 +1143,15 @@ static
 struct rmap_item *unstable_tree_search_insert(struct rmap_item *rmap_item,
 					      struct page *page,
 					      struct page **tree_pagep)
-
 {
-	struct rb_node **new = &root_unstable_tree.rb_node;
+	struct rb_node **new;
+	struct rb_root *root;
 	struct rb_node *parent = NULL;
+	int nid;
+
+	nid = get_kpfn_nid(page_to_pfn(page));
+	root = &root_unstable_tree[nid];
+	new = &root->rb_node;
 
 	while (*new) {
 		struct rmap_item *tree_rmap_item;
@@ -1128,6 +1172,18 @@ struct rmap_item *unstable_tree_search_insert(struct rmap_item *rmap_item,
 			return NULL;
 		}
 
+		/*
+		 * If tree_page has been migrated to another NUMA node, it
+		 * will be flushed out and put into the right unstable tree
+		 * next time: only merge with it if merge_across_nodes.
+		 * Just notice, we don't have similar problem for PageKsm
+		 * because their migration is disabled now. (62b61f611e)
+		 */
+		if (!ksm_merge_across_nodes && page_to_nid(tree_page) != nid) {
+			put_page(tree_page);
+			return NULL;
+		}
+
 		ret = memcmp_pages(page, tree_page);
 
 		parent = *new;
@@ -1145,8 +1201,11 @@ struct rmap_item *unstable_tree_search_insert(struct rmap_item *rmap_item,
 
 	rmap_item->address |= UNSTABLE_FLAG;
 	rmap_item->address |= (ksm_scan.seqnr & SEQNR_MASK);
+#ifdef CONFIG_NUMA
+	rmap_item->nid = nid;
+#endif
 	rb_link_node(&rmap_item->node, parent, new);
-	rb_insert_color(&rmap_item->node, &root_unstable_tree);
+	rb_insert_color(&rmap_item->node, root);
 
 	ksm_pages_unshared++;
 	return NULL;
@@ -1160,6 +1219,13 @@ struct rmap_item *unstable_tree_search_insert(struct rmap_item *rmap_item,
 static void stable_tree_append(struct rmap_item *rmap_item,
 			       struct stable_node *stable_node)
 {
+#ifdef CONFIG_NUMA
+	/*
+	 * Usually rmap_item->nid is already set correctly,
+	 * but it may be wrong after switching merge_across_nodes.
+	 */
+	rmap_item->nid = get_kpfn_nid(stable_node->kpfn);
+#endif
 	rmap_item->head = stable_node;
 	rmap_item->address |= STABLE_FLAG;
 	hlist_add_head(&rmap_item->hlist, &stable_node->hlist);
@@ -1289,6 +1355,7 @@ static struct rmap_item *scan_get_next_rmap_item(struct page **page)
 	struct mm_slot *slot;
 	struct vm_area_struct *vma;
 	struct rmap_item *rmap_item;
+	int nid;
 
 	if (list_empty(&ksm_mm_head.mm_list))
 		return NULL;
@@ -1307,7 +1374,8 @@ static struct rmap_item *scan_get_next_rmap_item(struct page **page)
 		 */
 		lru_add_drain_all();
 
-		root_unstable_tree = RB_ROOT;
+		for (nid = 0; nid < nr_node_ids; nid++)
+			root_unstable_tree[nid] = RB_ROOT;
 
 		spin_lock(&ksm_mmlist_lock);
 		slot = list_entry(slot->mm_list.next, struct mm_slot, mm_list);
@@ -1782,15 +1850,19 @@ static struct stable_node *ksm_check_stable_tree(unsigned long start_pfn,
 						 unsigned long end_pfn)
 {
 	struct rb_node *node;
+	int nid;
 
-	for (node = rb_first(&root_stable_tree); node; node = rb_next(node)) {
-		struct stable_node *stable_node;
+	for (nid = 0; nid < nr_node_ids; nid++)
+		for (node = rb_first(&root_stable_tree[nid]); node;
+				node = rb_next(node)) {
+			struct stable_node *stable_node;
+
+			stable_node = rb_entry(node, struct stable_node, node);
+			if (stable_node->kpfn >= start_pfn &&
+			    stable_node->kpfn < end_pfn)
+				return stable_node;
+		}
 
-		stable_node = rb_entry(node, struct stable_node, node);
-		if (stable_node->kpfn >= start_pfn &&
-		    stable_node->kpfn < end_pfn)
-			return stable_node;
-	}
 	return NULL;
 }
 
@@ -1937,6 +2009,40 @@ static ssize_t run_store(struct kobject *kobj, struct kobj_attribute *attr,
 }
 KSM_ATTR(run);
 
+#ifdef CONFIG_NUMA
+static ssize_t merge_across_nodes_show(struct kobject *kobj,
+				struct kobj_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%u\n", ksm_merge_across_nodes);
+}
+
+static ssize_t merge_across_nodes_store(struct kobject *kobj,
+				   struct kobj_attribute *attr,
+				   const char *buf, size_t count)
+{
+	int err;
+	unsigned long knob;
+
+	err = kstrtoul(buf, 10, &knob);
+	if (err)
+		return err;
+	if (knob > 1)
+		return -EINVAL;
+
+	mutex_lock(&ksm_thread_mutex);
+	if (ksm_merge_across_nodes != knob) {
+		if (ksm_pages_shared)
+			err = -EBUSY;
+		else
+			ksm_merge_across_nodes = knob;
+	}
+	mutex_unlock(&ksm_thread_mutex);
+
+	return err ? err : count;
+}
+KSM_ATTR(merge_across_nodes);
+#endif
+
 static ssize_t pages_shared_show(struct kobject *kobj,
 				 struct kobj_attribute *attr, char *buf)
 {
@@ -1991,6 +2097,9 @@ static struct attribute *ksm_attrs[] = {
 	&pages_unshared_attr.attr,
 	&pages_volatile_attr.attr,
 	&full_scans_attr.attr,
+#ifdef CONFIG_NUMA
+	&merge_across_nodes_attr.attr,
+#endif
 	NULL,
 };
 
@@ -2004,11 +2113,15 @@ static int __init ksm_init(void)
 {
 	struct task_struct *ksm_thread;
 	int err;
+	int nid;
 
 	err = ksm_slab_init();
 	if (err)
 		goto out;
 
+	for (nid = 0; nid < nr_node_ids; nid++)
+		root_stable_tree[nid] = RB_ROOT;
+
 	ksm_thread = kthread_run(ksm_scan_thread, NULL, "ksmd");
 	if (IS_ERR(ksm_thread)) {
 		printk(KERN_ERR "ksm: creating kthread failed\n");
-- 
1.7.11.7


^ permalink raw reply related

* [PATCH v7 2/2] Documentation: add sysfs ABI documentation for ksm
From: Petr Holasek @ 2012-12-28  1:32 UTC (permalink / raw)
  To: Hugh Dickins
  Cc: Andrea Arcangeli, Andrew Morton, Izik Eidus, Rik van Riel,
	David Rientjes, Sasha Levin, linux-kernel, linux-mm, Anton Arapov,
	Petr Holasek
In-Reply-To: <1356658337-12540-1-git-send-email-pholasek@redhat.com>

This patch adds sysfs documentation for Kernel Samepage Merging (KSM)
including new merge_across_nodes knob.

Signed-off-by: Petr Holasek <pholasek@redhat.com>
---
 Documentation/ABI/testing/sysfs-kernel-mm-ksm | 51 +++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-kernel-mm-ksm

diff --git a/Documentation/ABI/testing/sysfs-kernel-mm-ksm b/Documentation/ABI/testing/sysfs-kernel-mm-ksm
new file mode 100644
index 0000000..44384ae
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-kernel-mm-ksm
@@ -0,0 +1,51 @@
+What:		/sys/kernel/mm/ksm
+Date:		September 2009
+Contact:	Linux memory management mailing list <linux-mm@kvack.org>
+Description:	/sys/kernel/mm/ksm contains interface of Kernel Samepage
+		Merging (KSM)
+
+What:		/sys/kernel/mm/ksm/full_scans
+What:		/sys/kernel/mm/ksm/pages_shared
+What:		/sys/kernel/mm/ksm/pages_sharing
+What:		/sys/kernel/mm/ksm/pages_to_scan
+What:		/sys/kernel/mm/ksm/pages_unshared
+What:		/sys/kernel/mm/ksm/pages_volatile
+What:		/sys/kernel/mm/ksm/run
+What:		/sys/kernel/mm/ksm/sleep_millisecs
+Date:		September 2009
+Contact:	Linux memory management mailing list <linux-mm@kvack.org>
+Description:	Kernel Samepage Merging daemon sysfs interface
+
+		full_scans: how many times all mergeable areas have been
+		scanned.
+
+		pages_shared: how many shared pages are being used.
+
+		pages_sharing: how many more sites are sharing them i.e. how
+		much saved.
+
+		pages_to_scan: how many present pages to scan before ksmd goes
+		to sleep.
+
+		pages_unshared: how many pages unique but repeatedly checked
+		for merging.
+
+		pages_volatile: how many pages changing too fast to be placed
+		in a tree.
+
+		run: write 0 to disable ksm, read 0 while ksm is disabled.
+			write 1 to run ksm, read 1 while ksm is running.
+			write 2 to disable ksm and unmerge all its pages.
+
+		sleep_millisecs: how many milliseconds ksm should sleep between
+		scans.
+
+		See Documentation/vm/ksm.txt for more information.
+
+What:		/sys/kernel/mm/ksm/merge_across_nodes
+Date:		December 2012
+Contact:	Linux memory management mailing list <linux-mm@kvack.org>
+Description:	Control merging pages across different NUMA nodes.
+
+		When it is set to 0 only pages from the same node are merged,
+		otherwise pages from all nodes can be merged together (default).
-- 
1.7.11.7

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related

* [PATCH v7 1/2] KSM: numa awareness sysfs knob
From: Petr Holasek @ 2012-12-28  1:32 UTC (permalink / raw)
  To: Hugh Dickins
  Cc: Andrea Arcangeli, Andrew Morton, Izik Eidus, Rik van Riel,
	David Rientjes, Sasha Levin, linux-kernel, linux-mm, Anton Arapov,
	Petr Holasek
In-Reply-To: <20121224050817.GA25749@kroah.com>

Introduces new sysfs boolean knob /sys/kernel/mm/ksm/merge_across_nodes
which control merging pages across different numa nodes.
When it is set to zero only pages from the same node are merged,
otherwise pages from all nodes can be merged together (default behavior).

Typical use-case could be a lot of KVM guests on NUMA machine
and cpus from more distant nodes would have significant increase
of access latency to the merged ksm page. Sysfs knob was choosen
for higher variability when some users still prefers higher amount
of saved physical memory regardless of access latency.

Every numa node has its own stable & unstable trees because of faster
searching and inserting. Changing of merge_across_nodes value is possible
only when there are not any ksm shared pages in system.

I've tested this patch on numa machines with 2, 4 and 8 nodes and
measured speed of memory access inside of KVM guests with memory pinned
to one of nodes with this benchmark:

http://pholasek.fedorapeople.org/alloc_pg.c

Population standard deviations of access times in percentage of average
were following:

merge_across_nodes=1
2 nodes 1.4%
4 nodes 1.6%
8 nodes	1.7%

merge_across_nodes=0
2 nodes	1%
4 nodes	0.32%
8 nodes	0.018%

RFC: https://lkml.org/lkml/2011/11/30/91
v1: https://lkml.org/lkml/2012/1/23/46
v2: https://lkml.org/lkml/2012/6/29/105
v3: https://lkml.org/lkml/2012/9/14/550
v4: https://lkml.org/lkml/2012/9/23/137
v5: https://lkml.org/lkml/2012/12/10/540
v6: https://lkml.org/lkml/2012/12/23/154

Changelog:

v2: Andrew's objections were reflected:
	- value of merge_nodes can't be changed while there are some ksm
	pages in system
	- merge_nodes sysfs entry appearance depends on CONFIG_NUMA
	- more verbose documentation
	- added some performance testing results

v3:	- more verbose documentation
	- fixed race in merge_nodes store function
	- introduced share_all debugging knob proposed by Andrew
	- minor cleanups

v4:	- merge_nodes was renamed to merge_across_nodes
	- share_all debug knob was dropped
	- get_kpfn_nid helper
	- fixed page migration behaviour

v5:	- unstable node's nid presence depends on CONFIG_NUMA
	- fixed oops appearing when stable nodes were removed from tree
	- roots of stable trees are initialized properly
	- fixed unstable page migration issue

v6:	- fixed oops caused by stable_nodes appended to wrong tree
	- KSM_RUN_MERGE test removed

v7:	- added sysfs ABI documentation for KSM

Signed-off-by: Petr Holasek <pholasek@redhat.com>
Signed-off-by: Hugh Dickins <hughd@google.com>
Acked-by: Rik van Riel <riel@redhat.com>
---
 Documentation/vm/ksm.txt |   7 +++
 mm/ksm.c                 | 151 +++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 139 insertions(+), 19 deletions(-)

diff --git a/Documentation/vm/ksm.txt b/Documentation/vm/ksm.txt
index b392e49..25cc89b 100644
--- a/Documentation/vm/ksm.txt
+++ b/Documentation/vm/ksm.txt
@@ -58,6 +58,13 @@ sleep_millisecs  - how many milliseconds ksmd should sleep before next scan
                    e.g. "echo 20 > /sys/kernel/mm/ksm/sleep_millisecs"
                    Default: 20 (chosen for demonstration purposes)
 
+merge_across_nodes - specifies if pages from different numa nodes can be merged.
+                   When set to 0, ksm merges only pages which physically
+                   reside in the memory area of same NUMA node. It brings
+                   lower latency to access to shared page. Value can be
+                   changed only when there is no ksm shared pages in system.
+                   Default: 1
+
 run              - set 0 to stop ksmd from running but keep merged pages,
                    set 1 to run ksmd e.g. "echo 1 > /sys/kernel/mm/ksm/run",
                    set 2 to stop ksmd and unmerge all pages currently merged,
diff --git a/mm/ksm.c b/mm/ksm.c
index 5157385..d1e1041 100644
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -36,6 +36,7 @@
 #include <linux/hash.h>
 #include <linux/freezer.h>
 #include <linux/oom.h>
+#include <linux/numa.h>
 
 #include <asm/tlbflush.h>
 #include "internal.h"
@@ -139,6 +140,9 @@ struct rmap_item {
 	struct mm_struct *mm;
 	unsigned long address;		/* + low bits used for flags below */
 	unsigned int oldchecksum;	/* when unstable */
+#ifdef CONFIG_NUMA
+	unsigned int nid;
+#endif
 	union {
 		struct rb_node node;	/* when node of unstable tree */
 		struct {		/* when listed from stable tree */
@@ -153,8 +157,8 @@ struct rmap_item {
 #define STABLE_FLAG	0x200	/* is listed from the stable tree */
 
 /* The stable and unstable tree heads */
-static struct rb_root root_stable_tree = RB_ROOT;
-static struct rb_root root_unstable_tree = RB_ROOT;
+static struct rb_root root_unstable_tree[MAX_NUMNODES];
+static struct rb_root root_stable_tree[MAX_NUMNODES];
 
 #define MM_SLOTS_HASH_SHIFT 10
 #define MM_SLOTS_HASH_HEADS (1 << MM_SLOTS_HASH_SHIFT)
@@ -189,6 +193,9 @@ static unsigned int ksm_thread_pages_to_scan = 100;
 /* Milliseconds ksmd should sleep between batches */
 static unsigned int ksm_thread_sleep_millisecs = 20;
 
+/* Zeroed when merging across nodes is not allowed */
+static unsigned int ksm_merge_across_nodes = 1;
+
 #define KSM_RUN_STOP	0
 #define KSM_RUN_MERGE	1
 #define KSM_RUN_UNMERGE	2
@@ -447,10 +454,25 @@ out:		page = NULL;
 	return page;
 }
 
+/*
+ * This helper is used for getting right index into array of tree roots.
+ * When merge_across_nodes knob is set to 1, there are only two rb-trees for
+ * stable and unstable pages from all nodes with roots in index 0. Otherwise,
+ * every node has its own stable and unstable tree.
+ */
+static inline int get_kpfn_nid(unsigned long kpfn)
+{
+	if (ksm_merge_across_nodes)
+		return 0;
+	else
+		return pfn_to_nid(kpfn);
+}
+
 static void remove_node_from_stable_tree(struct stable_node *stable_node)
 {
 	struct rmap_item *rmap_item;
 	struct hlist_node *hlist;
+	int nid;
 
 	hlist_for_each_entry(rmap_item, hlist, &stable_node->hlist, hlist) {
 		if (rmap_item->hlist.next)
@@ -462,7 +484,9 @@ static void remove_node_from_stable_tree(struct stable_node *stable_node)
 		cond_resched();
 	}
 
-	rb_erase(&stable_node->node, &root_stable_tree);
+	nid = get_kpfn_nid(stable_node->kpfn);
+
+	rb_erase(&stable_node->node, &root_stable_tree[nid]);
 	free_stable_node(stable_node);
 }
 
@@ -560,7 +584,12 @@ static void remove_rmap_item_from_tree(struct rmap_item *rmap_item)
 		age = (unsigned char)(ksm_scan.seqnr - rmap_item->address);
 		BUG_ON(age > 1);
 		if (!age)
-			rb_erase(&rmap_item->node, &root_unstable_tree);
+#ifdef CONFIG_NUMA
+			rb_erase(&rmap_item->node,
+					&root_unstable_tree[rmap_item->nid]);
+#else
+			rb_erase(&rmap_item->node, &root_unstable_tree[0]);
+#endif
 
 		ksm_pages_unshared--;
 		rmap_item->address &= PAGE_MASK;
@@ -996,8 +1025,9 @@ static struct page *try_to_merge_two_pages(struct rmap_item *rmap_item,
  */
 static struct page *stable_tree_search(struct page *page)
 {
-	struct rb_node *node = root_stable_tree.rb_node;
+	struct rb_node *node;
 	struct stable_node *stable_node;
+	int nid;
 
 	stable_node = page_stable_node(page);
 	if (stable_node) {			/* ksm page forked */
@@ -1005,6 +1035,9 @@ static struct page *stable_tree_search(struct page *page)
 		return page;
 	}
 
+	nid = get_kpfn_nid(page_to_pfn(page));
+	node = root_stable_tree[nid].rb_node;
+
 	while (node) {
 		struct page *tree_page;
 		int ret;
@@ -1039,10 +1072,16 @@ static struct page *stable_tree_search(struct page *page)
  */
 static struct stable_node *stable_tree_insert(struct page *kpage)
 {
-	struct rb_node **new = &root_stable_tree.rb_node;
+	int nid;
+	unsigned long kpfn;
+	struct rb_node **new;
 	struct rb_node *parent = NULL;
 	struct stable_node *stable_node;
 
+	kpfn = page_to_pfn(kpage);
+	nid = get_kpfn_nid(kpfn);
+	new = &root_stable_tree[nid].rb_node;
+
 	while (*new) {
 		struct page *tree_page;
 		int ret;
@@ -1076,11 +1115,11 @@ static struct stable_node *stable_tree_insert(struct page *kpage)
 		return NULL;
 
 	rb_link_node(&stable_node->node, parent, new);
-	rb_insert_color(&stable_node->node, &root_stable_tree);
+	rb_insert_color(&stable_node->node, &root_stable_tree[nid]);
 
 	INIT_HLIST_HEAD(&stable_node->hlist);
 
-	stable_node->kpfn = page_to_pfn(kpage);
+	stable_node->kpfn = kpfn;
 	set_page_stable_node(kpage, stable_node);
 
 	return stable_node;
@@ -1104,10 +1143,15 @@ static
 struct rmap_item *unstable_tree_search_insert(struct rmap_item *rmap_item,
 					      struct page *page,
 					      struct page **tree_pagep)
-
 {
-	struct rb_node **new = &root_unstable_tree.rb_node;
+	struct rb_node **new;
+	struct rb_root *root;
 	struct rb_node *parent = NULL;
+	int nid;
+
+	nid = get_kpfn_nid(page_to_pfn(page));
+	root = &root_unstable_tree[nid];
+	new = &root->rb_node;
 
 	while (*new) {
 		struct rmap_item *tree_rmap_item;
@@ -1128,6 +1172,18 @@ struct rmap_item *unstable_tree_search_insert(struct rmap_item *rmap_item,
 			return NULL;
 		}
 
+		/*
+		 * If tree_page has been migrated to another NUMA node, it
+		 * will be flushed out and put into the right unstable tree
+		 * next time: only merge with it if merge_across_nodes.
+		 * Just notice, we don't have similar problem for PageKsm
+		 * because their migration is disabled now. (62b61f611e)
+		 */
+		if (!ksm_merge_across_nodes && page_to_nid(tree_page) != nid) {
+			put_page(tree_page);
+			return NULL;
+		}
+
 		ret = memcmp_pages(page, tree_page);
 
 		parent = *new;
@@ -1145,8 +1201,11 @@ struct rmap_item *unstable_tree_search_insert(struct rmap_item *rmap_item,
 
 	rmap_item->address |= UNSTABLE_FLAG;
 	rmap_item->address |= (ksm_scan.seqnr & SEQNR_MASK);
+#ifdef CONFIG_NUMA
+	rmap_item->nid = nid;
+#endif
 	rb_link_node(&rmap_item->node, parent, new);
-	rb_insert_color(&rmap_item->node, &root_unstable_tree);
+	rb_insert_color(&rmap_item->node, root);
 
 	ksm_pages_unshared++;
 	return NULL;
@@ -1160,6 +1219,13 @@ struct rmap_item *unstable_tree_search_insert(struct rmap_item *rmap_item,
 static void stable_tree_append(struct rmap_item *rmap_item,
 			       struct stable_node *stable_node)
 {
+#ifdef CONFIG_NUMA
+	/*
+	 * Usually rmap_item->nid is already set correctly,
+	 * but it may be wrong after switching merge_across_nodes.
+	 */
+	rmap_item->nid = get_kpfn_nid(stable_node->kpfn);
+#endif
 	rmap_item->head = stable_node;
 	rmap_item->address |= STABLE_FLAG;
 	hlist_add_head(&rmap_item->hlist, &stable_node->hlist);
@@ -1289,6 +1355,7 @@ static struct rmap_item *scan_get_next_rmap_item(struct page **page)
 	struct mm_slot *slot;
 	struct vm_area_struct *vma;
 	struct rmap_item *rmap_item;
+	int nid;
 
 	if (list_empty(&ksm_mm_head.mm_list))
 		return NULL;
@@ -1307,7 +1374,8 @@ static struct rmap_item *scan_get_next_rmap_item(struct page **page)
 		 */
 		lru_add_drain_all();
 
-		root_unstable_tree = RB_ROOT;
+		for (nid = 0; nid < nr_node_ids; nid++)
+			root_unstable_tree[nid] = RB_ROOT;
 
 		spin_lock(&ksm_mmlist_lock);
 		slot = list_entry(slot->mm_list.next, struct mm_slot, mm_list);
@@ -1782,15 +1850,19 @@ static struct stable_node *ksm_check_stable_tree(unsigned long start_pfn,
 						 unsigned long end_pfn)
 {
 	struct rb_node *node;
+	int nid;
 
-	for (node = rb_first(&root_stable_tree); node; node = rb_next(node)) {
-		struct stable_node *stable_node;
+	for (nid = 0; nid < nr_node_ids; nid++)
+		for (node = rb_first(&root_stable_tree[nid]); node;
+				node = rb_next(node)) {
+			struct stable_node *stable_node;
+
+			stable_node = rb_entry(node, struct stable_node, node);
+			if (stable_node->kpfn >= start_pfn &&
+			    stable_node->kpfn < end_pfn)
+				return stable_node;
+		}
 
-		stable_node = rb_entry(node, struct stable_node, node);
-		if (stable_node->kpfn >= start_pfn &&
-		    stable_node->kpfn < end_pfn)
-			return stable_node;
-	}
 	return NULL;
 }
 
@@ -1937,6 +2009,40 @@ static ssize_t run_store(struct kobject *kobj, struct kobj_attribute *attr,
 }
 KSM_ATTR(run);
 
+#ifdef CONFIG_NUMA
+static ssize_t merge_across_nodes_show(struct kobject *kobj,
+				struct kobj_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%u\n", ksm_merge_across_nodes);
+}
+
+static ssize_t merge_across_nodes_store(struct kobject *kobj,
+				   struct kobj_attribute *attr,
+				   const char *buf, size_t count)
+{
+	int err;
+	unsigned long knob;
+
+	err = kstrtoul(buf, 10, &knob);
+	if (err)
+		return err;
+	if (knob > 1)
+		return -EINVAL;
+
+	mutex_lock(&ksm_thread_mutex);
+	if (ksm_merge_across_nodes != knob) {
+		if (ksm_pages_shared)
+			err = -EBUSY;
+		else
+			ksm_merge_across_nodes = knob;
+	}
+	mutex_unlock(&ksm_thread_mutex);
+
+	return err ? err : count;
+}
+KSM_ATTR(merge_across_nodes);
+#endif
+
 static ssize_t pages_shared_show(struct kobject *kobj,
 				 struct kobj_attribute *attr, char *buf)
 {
@@ -1991,6 +2097,9 @@ static struct attribute *ksm_attrs[] = {
 	&pages_unshared_attr.attr,
 	&pages_volatile_attr.attr,
 	&full_scans_attr.attr,
+#ifdef CONFIG_NUMA
+	&merge_across_nodes_attr.attr,
+#endif
 	NULL,
 };
 
@@ -2004,11 +2113,15 @@ static int __init ksm_init(void)
 {
 	struct task_struct *ksm_thread;
 	int err;
+	int nid;
 
 	err = ksm_slab_init();
 	if (err)
 		goto out;
 
+	for (nid = 0; nid < nr_node_ids; nid++)
+		root_stable_tree[nid] = RB_ROOT;
+
 	ksm_thread = kthread_run(ksm_scan_thread, NULL, "ksmd");
 	if (IS_ERR(ksm_thread)) {
 		printk(KERN_ERR "ksm: creating kthread failed\n");
-- 
1.7.11.7

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related

* [PATCH] i2c: vt8500: Add support for Wondermedia I2C master-mode
From: Tony Prisk @ 2012-12-28  1:29 UTC (permalink / raw)
  To: Ben Dooks, Wolfram Sang
  Cc: linux-arm-kernel, linux-kernel, linux-i2c, Tony Prisk

This patch adds support for the I2C controller found on Wondermedia
SoCs.

Due to the lack of pinmux support, GPIO pin alternate functions are
configured by machine's compatible property, as are pullups.

Signed-off-by: Tony Prisk <linux@prisktech.co.nz>
---
 .../devicetree/bindings/i2c/i2c-vt8500.txt         |   24 +
 drivers/i2c/busses/Kconfig                         |    7 +
 drivers/i2c/busses/Makefile                        |    1 +
 drivers/i2c/busses/i2c-wmt.c                       |  636 ++++++++++++++++++++
 4 files changed, 668 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/i2c-vt8500.txt
 create mode 100644 drivers/i2c/busses/i2c-wmt.c

diff --git a/Documentation/devicetree/bindings/i2c/i2c-vt8500.txt b/Documentation/devicetree/bindings/i2c/i2c-vt8500.txt
new file mode 100644
index 0000000..94a425e
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-vt8500.txt
@@ -0,0 +1,24 @@
+* Wondermedia I2C Controller
+
+Required properties :
+
+ - compatible : should be "wm,wm8505-i2c"
+ - reg : Offset and length of the register set for the device
+ - interrupts : <IRQ> where IRQ is the interrupt number
+ - clocks : phandle to the I2C clock source
+
+Optional properties :
+
+ - clock-frequency : desired I2C bus clock frequency in Hz.
+	Valid values are 100000 and 400000.
+	Default to 100000 if not specified, or invalid value.
+
+Example :
+
+	i2c_0: i2c@d8280000 {
+		compatible = "wm,wm8505-i2c";
+		reg = <0xd8280000 0x1000>;
+		interrupts = <19>;
+		clocks = <&clki2c0>;
+		clock-frequency = <400000>;
+	};
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index bdca511..41270fa 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -700,6 +700,13 @@ config I2C_VERSATILE
 	  This driver can also be built as a module.  If so, the module
 	  will be called i2c-versatile.
 
+config I2C_WMT
+	tristate "Wondermedia I2C Controller support"
+	depends on ARCH_VT8500
+	help
+	  If you say yes to this option, support will be included for the
+	  I2C controllers found in Wondermedia SoCs.
+
 config I2C_OCTEON
 	tristate "Cavium OCTEON I2C bus support"
 	depends on CPU_CAVIUM_OCTEON
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 6181f3f..dce6299 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -69,6 +69,7 @@ obj-$(CONFIG_I2C_SIRF)		+= i2c-sirf.o
 obj-$(CONFIG_I2C_STU300)	+= i2c-stu300.o
 obj-$(CONFIG_I2C_TEGRA)		+= i2c-tegra.o
 obj-$(CONFIG_I2C_VERSATILE)	+= i2c-versatile.o
+obj-$(CONFIG_I2C_WMT)		+= i2c-wmt.o
 obj-$(CONFIG_I2C_OCTEON)	+= i2c-octeon.o
 obj-$(CONFIG_I2C_XILINX)	+= i2c-xiic.o
 obj-$(CONFIG_I2C_XLR)		+= i2c-xlr.o
diff --git a/drivers/i2c/busses/i2c-wmt.c b/drivers/i2c/busses/i2c-wmt.c
new file mode 100644
index 0000000..07d86e3
--- /dev/null
+++ b/drivers/i2c/busses/i2c-wmt.c
@@ -0,0 +1,636 @@
+/*
+ *  Wondermedia I2C Master Mode Driver
+ *
+ *  Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
+ *
+ *  Derived from GPL licensed source:
+ *  - Copyright (C) 2008 WonderMedia Technologies, Inc.
+ *
+ *  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/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_i2c.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+
+#define REG_CR		0x00
+#define REG_TCR		0x02
+#define REG_CSR		0x04
+#define REG_ISR		0x06
+#define REG_IMR		0x08
+#define REG_CDR		0x0A
+#define REG_TR		0x0C
+#define REG_MCR		0x0E
+#define REG_SLAVE_CR	0x10
+#define REG_SLAVE_SR	0x12
+#define REG_SLAVE_ISR	0x14
+#define REG_SLAVE_IMR	0x16
+#define REG_SLAVE_DR	0x18
+#define REG_SLAVE_TR	0x1A
+
+/* REG_CR Bit fields */
+#define CR_TX_NEXT_ACK		0x0000
+#define CR_ENABLE		0x0001
+#define CR_TX_NEXT_NO_ACK	0x0002
+#define CR_TX_END		0x0004
+#define CR_CPU_RDY		0x0008
+#define SLAV_MODE_SEL		0x8000
+
+/* REG_TCR Bit fields */
+#define TCR_STANDARD_MODE	0x0000
+#define TCR_MASTER_WRITE	0x0000
+#define TCR_HS_MODE		0x2000
+#define TCR_MASTER_READ		0x4000
+#define TCR_FAST_MODE		0x8000
+#define TCR_SLAVE_ADDR_MASK	0x007F
+
+/* REG_ISR Bit fields */
+#define ISR_NACK_ADDR		0x0001
+#define ISR_BYTE_END		0x0002
+#define ISR_SCL_TIMEOUT		0x0004
+#define ISR_WRITE_ALL		0x0007
+
+/* REG_IMR Bit fields */
+#define IMR_ENABLE_ALL		0x0007
+
+/* REG_CSR Bit fields */
+#define CSR_RCV_NOT_ACK		0x0001
+#define CSR_RCV_ACK_MASK	0x0001
+#define CSR_READY_MASK		0x0002
+
+#define I2C_MODE_STANDARD	0
+#define I2C_MODE_FAST		1
+
+
+
+struct wmt_i2c_dev {
+	struct i2c_adapter	adapter;
+	struct completion	complete;
+	struct device		*dev;
+	void __iomem		*base;
+	struct clk		*clk;
+	int			mode;
+	int			irq;
+	u16			cmd_status;
+};
+
+static int wmt_i2c_wait_bus_not_busy(struct wmt_i2c_dev *i2c_dev)
+{
+	u16 val;
+	int i;
+	int ret = 0;
+
+	for (i = 0; i < 10000000; i++) {
+		val = readw(i2c_dev->base + REG_CSR);
+		if (val & CSR_READY_MASK)
+			break;
+
+		udelay(1);
+	}
+	if (i >= 9999999)
+		ret = -EBUSY;
+
+	return ret;
+}
+
+static int wmt_check_status(struct wmt_i2c_dev *i2c_dev)
+{
+	int ret = 0;
+
+	if (i2c_dev->cmd_status & ISR_NACK_ADDR)
+		ret = -EIO;
+
+	if (i2c_dev->cmd_status & ISR_SCL_TIMEOUT)
+		ret = -ETIMEDOUT;
+
+	return ret;
+}
+
+static int wmt_i2c_write(struct i2c_adapter *adap, struct i2c_msg *pmsg,
+						   int restart, int last)
+{
+	struct wmt_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+	u16 val;
+	u16 tcr_val;
+	int ret;
+	int wait_result;
+	u32 xfer_len = 0;
+
+	if (pmsg->len < 0)
+		return -EINVAL;
+
+	if (restart == 0) {
+		ret = wmt_i2c_wait_bus_not_busy(i2c_dev);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (pmsg->len == 0)
+		writew(0, i2c_dev->base + REG_CDR);
+	else
+		writew(pmsg->buf[0] & 0xFF, i2c_dev->base + REG_CDR);
+
+	if (restart == 0) {
+		val = readw(i2c_dev->base + REG_CR);
+		val &= ~CR_TX_END;
+		writew(val, i2c_dev->base + REG_CR);
+
+		val = readw(i2c_dev->base + REG_CR);
+		val |= CR_CPU_RDY;
+		writew(val, i2c_dev->base + REG_CR);
+	}
+
+	init_completion(&i2c_dev->complete);
+
+	if (i2c_dev->mode == I2C_MODE_STANDARD)
+		tcr_val = TCR_STANDARD_MODE;
+	else
+		tcr_val = TCR_FAST_MODE;
+
+	tcr_val |= (TCR_MASTER_WRITE | (pmsg->addr & TCR_SLAVE_ADDR_MASK));
+
+	writew(tcr_val, i2c_dev->base + REG_TCR);
+
+	if (restart == 1) {
+		val = readw(i2c_dev->base + REG_CR);
+		val |= CR_CPU_RDY;
+		writew(val, i2c_dev->base + REG_CR);
+	}
+
+	ret = 0;
+
+	for (;;) {
+		wait_result = wait_for_completion_interruptible_timeout(
+				&i2c_dev->complete, 500 * HZ / 1000);
+
+		if (wait_result == 0) {
+			dev_dbg(i2c_dev->dev, "wait timeout (tx)\n");
+			ret = -ETIMEDOUT;
+			break;
+		}
+
+		ret = wmt_check_status(i2c_dev);
+		if (ret)
+			break;
+
+		xfer_len++;
+
+		val = readw(i2c_dev->base + REG_CSR);
+		if ((val & CSR_RCV_ACK_MASK) == CSR_RCV_NOT_ACK) {
+			dev_dbg(i2c_dev->dev, "write RCV NACK error\n");
+			ret = -EIO;
+			break;
+		}
+
+		if (pmsg->len == 0) {
+			val = CR_TX_END | CR_CPU_RDY | CR_ENABLE;
+			writew(val, i2c_dev->base + REG_CR);
+			break;
+		}
+
+		if (pmsg->len > xfer_len) {
+			writew(pmsg->buf[xfer_len] & 0xFF, i2c_dev->base +
+								REG_CDR);
+			writew(CR_CPU_RDY | CR_ENABLE, i2c_dev->base + REG_CR);
+		} else if (pmsg->len == xfer_len) {
+			if (last != 1)
+				writew(CR_ENABLE, i2c_dev->base + REG_CR);
+			break;
+		} else {
+			dev_dbg(i2c_dev->dev, "unknown error (tx)\n");
+			ret = -EIO;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int wmt_i2c_read(struct i2c_adapter *adap, struct i2c_msg *pmsg,
+						  int restart, int last)
+{
+	struct wmt_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+	u16 val;
+	u16 tcr_val;
+	int ret;
+	int wait_result;
+	u32 xfer_len = 0;
+
+	if (pmsg->len <= 0)
+		return -EINVAL;
+
+	if (restart == 0) {
+		ret = wmt_i2c_wait_bus_not_busy(i2c_dev);
+		if (ret < 0)
+			return ret;
+	}
+
+	val = readw(i2c_dev->base + REG_CR);
+	val &= ~CR_TX_END;
+	writew(val, i2c_dev->base + REG_CR);
+
+	val = readw(i2c_dev->base + REG_CR);
+	val &= ~CR_TX_NEXT_NO_ACK;
+	writew(val, i2c_dev->base + REG_CR);
+
+	if (restart == 0) {
+		val = readw(i2c_dev->base + REG_CR);
+		val |= CR_CPU_RDY;
+		writew(val, i2c_dev->base + REG_CR);
+	}
+
+	if (pmsg->len == 1) {
+		val = readw(i2c_dev->base + REG_CR);
+		val |= CR_TX_NEXT_NO_ACK;
+		writew(val, i2c_dev->base + REG_CR);
+	}
+
+	init_completion(&i2c_dev->complete);
+
+	if (i2c_dev->mode == I2C_MODE_STANDARD)
+		tcr_val = TCR_STANDARD_MODE;
+	else
+		tcr_val = TCR_FAST_MODE;
+
+	tcr_val |= TCR_MASTER_READ | (pmsg->addr & TCR_SLAVE_ADDR_MASK);
+
+	writew(tcr_val, i2c_dev->base + REG_TCR);
+
+	if (restart == 1) {
+		val = readw(i2c_dev->base + REG_CR);
+		val |= CR_CPU_RDY;
+		writew(val, i2c_dev->base + REG_CR);
+	}
+
+	ret = 0;
+
+	for (;;) {
+		wait_result = wait_for_completion_interruptible_timeout(
+				&i2c_dev->complete, 500 * HZ / 1000);
+
+		if (wait_result == 0) {
+			dev_dbg(i2c_dev->dev, "wait timeout (tx)\n");
+			ret = -ETIMEDOUT;
+			break;
+		}
+
+		ret = wmt_check_status(i2c_dev);
+		if (ret)
+			break;
+
+		pmsg->buf[xfer_len] = readw(i2c_dev->base + REG_CDR) >> 8;
+		xfer_len++;
+
+		if (pmsg->len > xfer_len) {
+			if (pmsg->len - 1 == xfer_len) {
+				val = readw(i2c_dev->base + REG_CR);
+				val |= (CR_TX_NEXT_NO_ACK | CR_CPU_RDY);
+				writew(val, i2c_dev->base + REG_CR);
+			} else {
+				val = readw(i2c_dev->base + REG_CR);
+				val |= CR_CPU_RDY;
+				writew(val, i2c_dev->base + REG_CR);
+			}
+		} else if (pmsg->len == xfer_len) {
+			break;
+		} else {
+			ret = -EIO;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int wmt_i2c_xfer(struct i2c_adapter *adap,
+			struct i2c_msg msgs[],
+			int num)
+{
+	struct i2c_msg *pmsg;
+	int i;
+	int ret = 0;
+	int is_last;
+	int restart;
+
+	for (i = 0; ret >= 0 && i < num; i++) {
+		is_last = ((i + 1) == num);
+		restart = (i != 0);
+
+		pmsg = &msgs[i];
+		if (pmsg->flags & I2C_M_NOSTART)
+			restart = 1;
+		if (pmsg->flags & I2C_M_RD)
+			ret = wmt_i2c_read(adap, pmsg, restart, is_last);
+		else
+			ret = wmt_i2c_write(adap, pmsg, restart, is_last);
+	}
+
+	if (ret < 0)
+		return ret;
+	else
+		return i;
+}
+
+static u32 wmt_i2c_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm wmt_i2c_algo = {
+	.master_xfer	= wmt_i2c_xfer,
+	.functionality	= wmt_i2c_func,
+};
+
+static irqreturn_t wmt_i2c_isr(int irq, void *data)
+{
+	struct wmt_i2c_dev *i2c_dev = data;
+
+	/* save the status and write-clear it */
+	i2c_dev->cmd_status = readw(i2c_dev->base + REG_ISR);
+	writew(i2c_dev->cmd_status, i2c_dev->base + REG_ISR);
+
+	complete(&i2c_dev->complete);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Due to a lack of pinmux functionality we need to manually configure
+ * the GPIO pullup. Once pinmux is implemented, this function should be
+ * removed. Pullup's are not available on vt8500 or wm8505 so we skip.
+ */
+static int wmt_i2c_setup_gpio(struct wmt_i2c_dev *i2c_dev)
+{
+	struct device_node *np;
+	void __iomem *gpio_base;
+	int nr = i2c_dev->adapter.nr;
+	u8 val;
+	u8 mask;
+
+	/* VT8500 - has external pullups so no configuration required. */
+	if (of_machine_is_compatible("via,vt8500"))
+		return 0;
+
+	/*
+	 * WM8505 - has external pullups but we need to config the alt func
+	 * WM8650 - has programmable pullups + alt func
+	 * WM8850 - has programmable pullups + alt func
+	 */
+	if (of_machine_is_compatible("wm,wm8505")) {
+		np = of_find_compatible_node(NULL, NULL, "wm,wm8505-gpio");
+		if (!np) {
+			dev_err(i2c_dev->dev, "GPIO required for WM8505 i2c\n");
+			return -EINVAL;
+		}
+	} else if (of_machine_is_compatible("wm,wm8650") ||
+		   of_machine_is_compatible("wm,wm8850")) {
+		np = of_find_compatible_node(NULL, NULL, "wm,wm8650-gpio");
+		if (!np) {
+			dev_err(i2c_dev->dev, "GPIO required for WM8650 i2c\n");
+			return -EINVAL;
+		}
+	} else {
+		/* If we don't know what machine, assume its configured */
+		dev_warn(i2c_dev->dev, "unrecognised machine\n");
+		return 0;
+	}
+
+	gpio_base = of_iomap(np, 0);
+	if (!gpio_base) {
+		dev_err(i2c_dev->dev, "failed to map gpio memory\n");
+		return -ENOMEM;
+	}
+
+	if (of_machine_is_compatible("wm,wm8505")) {
+		if (nr == 0) {
+			/* Set gpio pins to alt function */
+			val = readb(gpio_base + 0x500);
+			writeb(val & 0xFC, gpio_base + 0x500);
+		}
+	} else if (of_machine_is_compatible("wm,wm8650")) {
+		if (nr == 0) {
+			/* set gpio pins to alt function */
+			val = readb(gpio_base + 0x55);
+			val &= ~(BIT(0) | BIT(1));
+			writeb(val, gpio_base + 0x55);
+
+			/* enable pull */
+			val = readb(gpio_base + 0x495);
+			val |= (BIT(0) | BIT(1));
+			writeb(val, gpio_base + 0x495);
+
+			/* set pulled-up */
+			val = readb(gpio_base + 0x4D5);
+			val |= (BIT(0) | BIT(1));
+			writeb(val, gpio_base + 0x4D5);
+		} else if (nr == 1) {
+			/* set gpio pins to alt function */
+			val = readb(gpio_base + 0x49);
+			val &= ~(BIT(4) | BIT(5));
+			writeb(val, gpio_base + 0x49);
+
+			/* enable pull */
+			val = readb(gpio_base + 0x489);
+			val |= (BIT(4) | BIT(5));
+			writeb(val, gpio_base + 0x489);
+
+			/* set pulled-up */
+			val = readb(gpio_base + 0x4C9);
+			val |= (BIT(4) | BIT(5));
+			writeb(val, gpio_base + 0x4C9);
+		}
+	} else if (of_machine_is_compatible("wm,wm8850")) {
+		if ((nr >= 0) && (nr <= 2)) {
+			mask = 0x03 << (nr << 1);
+	
+			/* set gpio pins to alt function */
+			val = readb(gpio_base + 0x55);
+			val &= ~mask;
+			writeb(val, gpio_base + 0x55);
+
+			/* enable pull */
+			val = readb(gpio_base + 0x495);
+			val |= mask;
+			writeb(val, gpio_base + 0x495);
+
+			/* set pulled-up */
+			val = readb(gpio_base + 0x4D5);
+			val |= mask;
+			writeb(val, gpio_base + 0x4D5);
+		}
+	}
+
+
+	iounmap(gpio_base);
+
+	return 0;
+}
+
+static int wmt_i2c_reset_hardware(struct wmt_i2c_dev *i2c_dev)
+{
+	int err;
+
+	err = clk_prepare_enable(i2c_dev->clk);
+	if (err) {
+		dev_err(i2c_dev->dev, "failed to enable clock\n");
+		return err;
+	}
+
+	err = clk_set_rate(i2c_dev->clk, 20000000);
+	if (err) {
+		dev_err(i2c_dev->dev, "failed to set clock = 20Mhz\n");
+		return err;
+	}
+
+	err = wmt_i2c_setup_gpio(i2c_dev);
+	if (err)
+		return err;
+
+	writew(0, i2c_dev->base + REG_CR);
+	writew(12, i2c_dev->base + REG_MCR);
+	writew(ISR_WRITE_ALL, i2c_dev->base + REG_ISR);
+	writew(IMR_ENABLE_ALL, i2c_dev->base + REG_IMR);
+	writew(CR_ENABLE, i2c_dev->base + REG_CR);
+	readw(i2c_dev->base + REG_CSR);		/* read clear */
+	writew(ISR_WRITE_ALL, i2c_dev->base + REG_ISR);
+
+	if (i2c_dev->mode == I2C_MODE_STANDARD)
+		writew(0x8064, i2c_dev->base + REG_TR);
+	else
+		writew(0x8019, i2c_dev->base + REG_TR);
+
+	return 0;
+}
+
+static int wmt_i2c_probe(struct platform_device *pdev)
+{
+	struct device_node	*np = pdev->dev.of_node;
+	struct wmt_i2c_dev	*i2c_dev;
+	struct i2c_adapter	*adap;
+	struct resource		*res;
+	int err;
+	u32 clk_rate;
+
+	if (!np) {
+		dev_err(&pdev->dev, "device node not found\n");
+		return -ENODEV;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "no memory resource defined\n");
+		return -ENODEV;
+	}
+
+	i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
+	if (!i2c_dev) {
+		dev_err(&pdev->dev, "device memory allocation failed\n");
+		return -ENOMEM;
+	}
+
+	i2c_dev->base = devm_request_and_ioremap(&pdev->dev, res);
+	if (!i2c_dev->base) {
+		dev_err(&pdev->dev, "memory region unavailable\n");
+		return -ENOMEM;
+	}
+
+	i2c_dev->irq = irq_of_parse_and_map(np, 0);
+	if (!i2c_dev->irq) {
+		dev_err(&pdev->dev, "irq missing or invalid\n");
+		return -EINVAL;
+	}
+
+	i2c_dev->clk = of_clk_get(np, 0);
+	if (IS_ERR(i2c_dev->clk)) {
+		dev_err(&pdev->dev, "unable to request clock\n");
+		return PTR_ERR(i2c_dev->clk);
+	}
+
+	i2c_dev->mode = I2C_MODE_STANDARD;
+	err = of_property_read_u32(np, "clock-frequency", &clk_rate);
+	if ((!err) && (clk_rate == 400000))
+		i2c_dev->mode = I2C_MODE_FAST;
+
+	i2c_dev->dev = &pdev->dev;
+
+	err = devm_request_irq(&pdev->dev, i2c_dev->irq, wmt_i2c_isr, 0,
+							"i2c", i2c_dev);
+	if (err) {
+		dev_err(&pdev->dev, "failed to request irq %i\n", i2c_dev->irq);
+		return err;
+	}
+
+	adap = &i2c_dev->adapter;
+	i2c_set_adapdata(adap, i2c_dev);
+	strlcpy(adap->name, "WMT I2C adapter", sizeof(adap->name));
+	adap->owner		= THIS_MODULE;
+	adap->class		= I2C_CLASS_HWMON;
+	adap->algo		= &wmt_i2c_algo;
+	adap->dev.parent	= &pdev->dev;
+	adap->dev.of_node	= pdev->dev.of_node;
+	adap->nr		= of_alias_get_id(pdev->dev.of_node, "i2c");
+
+	err = wmt_i2c_reset_hardware(i2c_dev);
+	if (err) {
+		dev_err(&pdev->dev, "error initializing hardware\n");
+		return err;
+	}
+
+	if (adap->nr < 0)
+		err = i2c_add_adapter(adap);
+	else
+		err = i2c_add_numbered_adapter(adap);
+
+	if (err) {
+		dev_err(&pdev->dev, "failed to add adapter\n");
+		return err;
+	}
+
+	platform_set_drvdata(pdev, i2c_dev);
+
+	of_i2c_register_devices(adap);
+
+	return 0;
+}
+
+static int wmt_i2c_remove(struct platform_device *pdev)
+{
+	struct wmt_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+
+	i2c_del_adapter(&i2c_dev->adapter);
+
+	return 0;
+}
+
+static struct of_device_id wmt_i2c_dt_ids[] = {
+	{ .compatible = "wm,wm8505-i2c" },
+	{ /* Sentinel */ },
+};
+
+static struct platform_driver wmt_i2c_driver = {
+	.probe		= wmt_i2c_probe,
+	.remove		= wmt_i2c_remove,
+	.driver		= {
+		.name	= "wmt-i2c",
+		.owner	= THIS_MODULE,
+		.of_match_table = wmt_i2c_dt_ids,
+	},
+};
+
+module_platform_driver(wmt_i2c_driver);
+
+MODULE_DESCRIPTION("Wondermedia I2C master-mode bus adapter");
+MODULE_AUTHOR("Tony Prisk <linux@prisktech.co.nz>");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, wmt_i2c_dt_ids);
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH] i2c: vt8500: Add support for Wondermedia I2C master-mode
From: Tony Prisk @ 2012-12-28  1:29 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds support for the I2C controller found on Wondermedia
SoCs.

Due to the lack of pinmux support, GPIO pin alternate functions are
configured by machine's compatible property, as are pullups.

Signed-off-by: Tony Prisk <linux@prisktech.co.nz>
---
 .../devicetree/bindings/i2c/i2c-vt8500.txt         |   24 +
 drivers/i2c/busses/Kconfig                         |    7 +
 drivers/i2c/busses/Makefile                        |    1 +
 drivers/i2c/busses/i2c-wmt.c                       |  636 ++++++++++++++++++++
 4 files changed, 668 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/i2c-vt8500.txt
 create mode 100644 drivers/i2c/busses/i2c-wmt.c

diff --git a/Documentation/devicetree/bindings/i2c/i2c-vt8500.txt b/Documentation/devicetree/bindings/i2c/i2c-vt8500.txt
new file mode 100644
index 0000000..94a425e
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-vt8500.txt
@@ -0,0 +1,24 @@
+* Wondermedia I2C Controller
+
+Required properties :
+
+ - compatible : should be "wm,wm8505-i2c"
+ - reg : Offset and length of the register set for the device
+ - interrupts : <IRQ> where IRQ is the interrupt number
+ - clocks : phandle to the I2C clock source
+
+Optional properties :
+
+ - clock-frequency : desired I2C bus clock frequency in Hz.
+	Valid values are 100000 and 400000.
+	Default to 100000 if not specified, or invalid value.
+
+Example :
+
+	i2c_0: i2c at d8280000 {
+		compatible = "wm,wm8505-i2c";
+		reg = <0xd8280000 0x1000>;
+		interrupts = <19>;
+		clocks = <&clki2c0>;
+		clock-frequency = <400000>;
+	};
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index bdca511..41270fa 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -700,6 +700,13 @@ config I2C_VERSATILE
 	  This driver can also be built as a module.  If so, the module
 	  will be called i2c-versatile.
 
+config I2C_WMT
+	tristate "Wondermedia I2C Controller support"
+	depends on ARCH_VT8500
+	help
+	  If you say yes to this option, support will be included for the
+	  I2C controllers found in Wondermedia SoCs.
+
 config I2C_OCTEON
 	tristate "Cavium OCTEON I2C bus support"
 	depends on CPU_CAVIUM_OCTEON
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 6181f3f..dce6299 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -69,6 +69,7 @@ obj-$(CONFIG_I2C_SIRF)		+= i2c-sirf.o
 obj-$(CONFIG_I2C_STU300)	+= i2c-stu300.o
 obj-$(CONFIG_I2C_TEGRA)		+= i2c-tegra.o
 obj-$(CONFIG_I2C_VERSATILE)	+= i2c-versatile.o
+obj-$(CONFIG_I2C_WMT)		+= i2c-wmt.o
 obj-$(CONFIG_I2C_OCTEON)	+= i2c-octeon.o
 obj-$(CONFIG_I2C_XILINX)	+= i2c-xiic.o
 obj-$(CONFIG_I2C_XLR)		+= i2c-xlr.o
diff --git a/drivers/i2c/busses/i2c-wmt.c b/drivers/i2c/busses/i2c-wmt.c
new file mode 100644
index 0000000..07d86e3
--- /dev/null
+++ b/drivers/i2c/busses/i2c-wmt.c
@@ -0,0 +1,636 @@
+/*
+ *  Wondermedia I2C Master Mode Driver
+ *
+ *  Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
+ *
+ *  Derived from GPL licensed source:
+ *  - Copyright (C) 2008 WonderMedia Technologies, Inc.
+ *
+ *  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/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_i2c.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+
+#define REG_CR		0x00
+#define REG_TCR		0x02
+#define REG_CSR		0x04
+#define REG_ISR		0x06
+#define REG_IMR		0x08
+#define REG_CDR		0x0A
+#define REG_TR		0x0C
+#define REG_MCR		0x0E
+#define REG_SLAVE_CR	0x10
+#define REG_SLAVE_SR	0x12
+#define REG_SLAVE_ISR	0x14
+#define REG_SLAVE_IMR	0x16
+#define REG_SLAVE_DR	0x18
+#define REG_SLAVE_TR	0x1A
+
+/* REG_CR Bit fields */
+#define CR_TX_NEXT_ACK		0x0000
+#define CR_ENABLE		0x0001
+#define CR_TX_NEXT_NO_ACK	0x0002
+#define CR_TX_END		0x0004
+#define CR_CPU_RDY		0x0008
+#define SLAV_MODE_SEL		0x8000
+
+/* REG_TCR Bit fields */
+#define TCR_STANDARD_MODE	0x0000
+#define TCR_MASTER_WRITE	0x0000
+#define TCR_HS_MODE		0x2000
+#define TCR_MASTER_READ		0x4000
+#define TCR_FAST_MODE		0x8000
+#define TCR_SLAVE_ADDR_MASK	0x007F
+
+/* REG_ISR Bit fields */
+#define ISR_NACK_ADDR		0x0001
+#define ISR_BYTE_END		0x0002
+#define ISR_SCL_TIMEOUT		0x0004
+#define ISR_WRITE_ALL		0x0007
+
+/* REG_IMR Bit fields */
+#define IMR_ENABLE_ALL		0x0007
+
+/* REG_CSR Bit fields */
+#define CSR_RCV_NOT_ACK		0x0001
+#define CSR_RCV_ACK_MASK	0x0001
+#define CSR_READY_MASK		0x0002
+
+#define I2C_MODE_STANDARD	0
+#define I2C_MODE_FAST		1
+
+
+
+struct wmt_i2c_dev {
+	struct i2c_adapter	adapter;
+	struct completion	complete;
+	struct device		*dev;
+	void __iomem		*base;
+	struct clk		*clk;
+	int			mode;
+	int			irq;
+	u16			cmd_status;
+};
+
+static int wmt_i2c_wait_bus_not_busy(struct wmt_i2c_dev *i2c_dev)
+{
+	u16 val;
+	int i;
+	int ret = 0;
+
+	for (i = 0; i < 10000000; i++) {
+		val = readw(i2c_dev->base + REG_CSR);
+		if (val & CSR_READY_MASK)
+			break;
+
+		udelay(1);
+	}
+	if (i >= 9999999)
+		ret = -EBUSY;
+
+	return ret;
+}
+
+static int wmt_check_status(struct wmt_i2c_dev *i2c_dev)
+{
+	int ret = 0;
+
+	if (i2c_dev->cmd_status & ISR_NACK_ADDR)
+		ret = -EIO;
+
+	if (i2c_dev->cmd_status & ISR_SCL_TIMEOUT)
+		ret = -ETIMEDOUT;
+
+	return ret;
+}
+
+static int wmt_i2c_write(struct i2c_adapter *adap, struct i2c_msg *pmsg,
+						   int restart, int last)
+{
+	struct wmt_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+	u16 val;
+	u16 tcr_val;
+	int ret;
+	int wait_result;
+	u32 xfer_len = 0;
+
+	if (pmsg->len < 0)
+		return -EINVAL;
+
+	if (restart == 0) {
+		ret = wmt_i2c_wait_bus_not_busy(i2c_dev);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (pmsg->len == 0)
+		writew(0, i2c_dev->base + REG_CDR);
+	else
+		writew(pmsg->buf[0] & 0xFF, i2c_dev->base + REG_CDR);
+
+	if (restart == 0) {
+		val = readw(i2c_dev->base + REG_CR);
+		val &= ~CR_TX_END;
+		writew(val, i2c_dev->base + REG_CR);
+
+		val = readw(i2c_dev->base + REG_CR);
+		val |= CR_CPU_RDY;
+		writew(val, i2c_dev->base + REG_CR);
+	}
+
+	init_completion(&i2c_dev->complete);
+
+	if (i2c_dev->mode == I2C_MODE_STANDARD)
+		tcr_val = TCR_STANDARD_MODE;
+	else
+		tcr_val = TCR_FAST_MODE;
+
+	tcr_val |= (TCR_MASTER_WRITE | (pmsg->addr & TCR_SLAVE_ADDR_MASK));
+
+	writew(tcr_val, i2c_dev->base + REG_TCR);
+
+	if (restart == 1) {
+		val = readw(i2c_dev->base + REG_CR);
+		val |= CR_CPU_RDY;
+		writew(val, i2c_dev->base + REG_CR);
+	}
+
+	ret = 0;
+
+	for (;;) {
+		wait_result = wait_for_completion_interruptible_timeout(
+				&i2c_dev->complete, 500 * HZ / 1000);
+
+		if (wait_result == 0) {
+			dev_dbg(i2c_dev->dev, "wait timeout (tx)\n");
+			ret = -ETIMEDOUT;
+			break;
+		}
+
+		ret = wmt_check_status(i2c_dev);
+		if (ret)
+			break;
+
+		xfer_len++;
+
+		val = readw(i2c_dev->base + REG_CSR);
+		if ((val & CSR_RCV_ACK_MASK) == CSR_RCV_NOT_ACK) {
+			dev_dbg(i2c_dev->dev, "write RCV NACK error\n");
+			ret = -EIO;
+			break;
+		}
+
+		if (pmsg->len == 0) {
+			val = CR_TX_END | CR_CPU_RDY | CR_ENABLE;
+			writew(val, i2c_dev->base + REG_CR);
+			break;
+		}
+
+		if (pmsg->len > xfer_len) {
+			writew(pmsg->buf[xfer_len] & 0xFF, i2c_dev->base +
+								REG_CDR);
+			writew(CR_CPU_RDY | CR_ENABLE, i2c_dev->base + REG_CR);
+		} else if (pmsg->len == xfer_len) {
+			if (last != 1)
+				writew(CR_ENABLE, i2c_dev->base + REG_CR);
+			break;
+		} else {
+			dev_dbg(i2c_dev->dev, "unknown error (tx)\n");
+			ret = -EIO;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int wmt_i2c_read(struct i2c_adapter *adap, struct i2c_msg *pmsg,
+						  int restart, int last)
+{
+	struct wmt_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+	u16 val;
+	u16 tcr_val;
+	int ret;
+	int wait_result;
+	u32 xfer_len = 0;
+
+	if (pmsg->len <= 0)
+		return -EINVAL;
+
+	if (restart == 0) {
+		ret = wmt_i2c_wait_bus_not_busy(i2c_dev);
+		if (ret < 0)
+			return ret;
+	}
+
+	val = readw(i2c_dev->base + REG_CR);
+	val &= ~CR_TX_END;
+	writew(val, i2c_dev->base + REG_CR);
+
+	val = readw(i2c_dev->base + REG_CR);
+	val &= ~CR_TX_NEXT_NO_ACK;
+	writew(val, i2c_dev->base + REG_CR);
+
+	if (restart == 0) {
+		val = readw(i2c_dev->base + REG_CR);
+		val |= CR_CPU_RDY;
+		writew(val, i2c_dev->base + REG_CR);
+	}
+
+	if (pmsg->len == 1) {
+		val = readw(i2c_dev->base + REG_CR);
+		val |= CR_TX_NEXT_NO_ACK;
+		writew(val, i2c_dev->base + REG_CR);
+	}
+
+	init_completion(&i2c_dev->complete);
+
+	if (i2c_dev->mode == I2C_MODE_STANDARD)
+		tcr_val = TCR_STANDARD_MODE;
+	else
+		tcr_val = TCR_FAST_MODE;
+
+	tcr_val |= TCR_MASTER_READ | (pmsg->addr & TCR_SLAVE_ADDR_MASK);
+
+	writew(tcr_val, i2c_dev->base + REG_TCR);
+
+	if (restart == 1) {
+		val = readw(i2c_dev->base + REG_CR);
+		val |= CR_CPU_RDY;
+		writew(val, i2c_dev->base + REG_CR);
+	}
+
+	ret = 0;
+
+	for (;;) {
+		wait_result = wait_for_completion_interruptible_timeout(
+				&i2c_dev->complete, 500 * HZ / 1000);
+
+		if (wait_result == 0) {
+			dev_dbg(i2c_dev->dev, "wait timeout (tx)\n");
+			ret = -ETIMEDOUT;
+			break;
+		}
+
+		ret = wmt_check_status(i2c_dev);
+		if (ret)
+			break;
+
+		pmsg->buf[xfer_len] = readw(i2c_dev->base + REG_CDR) >> 8;
+		xfer_len++;
+
+		if (pmsg->len > xfer_len) {
+			if (pmsg->len - 1 == xfer_len) {
+				val = readw(i2c_dev->base + REG_CR);
+				val |= (CR_TX_NEXT_NO_ACK | CR_CPU_RDY);
+				writew(val, i2c_dev->base + REG_CR);
+			} else {
+				val = readw(i2c_dev->base + REG_CR);
+				val |= CR_CPU_RDY;
+				writew(val, i2c_dev->base + REG_CR);
+			}
+		} else if (pmsg->len == xfer_len) {
+			break;
+		} else {
+			ret = -EIO;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int wmt_i2c_xfer(struct i2c_adapter *adap,
+			struct i2c_msg msgs[],
+			int num)
+{
+	struct i2c_msg *pmsg;
+	int i;
+	int ret = 0;
+	int is_last;
+	int restart;
+
+	for (i = 0; ret >= 0 && i < num; i++) {
+		is_last = ((i + 1) == num);
+		restart = (i != 0);
+
+		pmsg = &msgs[i];
+		if (pmsg->flags & I2C_M_NOSTART)
+			restart = 1;
+		if (pmsg->flags & I2C_M_RD)
+			ret = wmt_i2c_read(adap, pmsg, restart, is_last);
+		else
+			ret = wmt_i2c_write(adap, pmsg, restart, is_last);
+	}
+
+	if (ret < 0)
+		return ret;
+	else
+		return i;
+}
+
+static u32 wmt_i2c_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm wmt_i2c_algo = {
+	.master_xfer	= wmt_i2c_xfer,
+	.functionality	= wmt_i2c_func,
+};
+
+static irqreturn_t wmt_i2c_isr(int irq, void *data)
+{
+	struct wmt_i2c_dev *i2c_dev = data;
+
+	/* save the status and write-clear it */
+	i2c_dev->cmd_status = readw(i2c_dev->base + REG_ISR);
+	writew(i2c_dev->cmd_status, i2c_dev->base + REG_ISR);
+
+	complete(&i2c_dev->complete);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Due to a lack of pinmux functionality we need to manually configure
+ * the GPIO pullup. Once pinmux is implemented, this function should be
+ * removed. Pullup's are not available on vt8500 or wm8505 so we skip.
+ */
+static int wmt_i2c_setup_gpio(struct wmt_i2c_dev *i2c_dev)
+{
+	struct device_node *np;
+	void __iomem *gpio_base;
+	int nr = i2c_dev->adapter.nr;
+	u8 val;
+	u8 mask;
+
+	/* VT8500 - has external pullups so no configuration required. */
+	if (of_machine_is_compatible("via,vt8500"))
+		return 0;
+
+	/*
+	 * WM8505 - has external pullups but we need to config the alt func
+	 * WM8650 - has programmable pullups + alt func
+	 * WM8850 - has programmable pullups + alt func
+	 */
+	if (of_machine_is_compatible("wm,wm8505")) {
+		np = of_find_compatible_node(NULL, NULL, "wm,wm8505-gpio");
+		if (!np) {
+			dev_err(i2c_dev->dev, "GPIO required for WM8505 i2c\n");
+			return -EINVAL;
+		}
+	} else if (of_machine_is_compatible("wm,wm8650") ||
+		   of_machine_is_compatible("wm,wm8850")) {
+		np = of_find_compatible_node(NULL, NULL, "wm,wm8650-gpio");
+		if (!np) {
+			dev_err(i2c_dev->dev, "GPIO required for WM8650 i2c\n");
+			return -EINVAL;
+		}
+	} else {
+		/* If we don't know what machine, assume its configured */
+		dev_warn(i2c_dev->dev, "unrecognised machine\n");
+		return 0;
+	}
+
+	gpio_base = of_iomap(np, 0);
+	if (!gpio_base) {
+		dev_err(i2c_dev->dev, "failed to map gpio memory\n");
+		return -ENOMEM;
+	}
+
+	if (of_machine_is_compatible("wm,wm8505")) {
+		if (nr == 0) {
+			/* Set gpio pins to alt function */
+			val = readb(gpio_base + 0x500);
+			writeb(val & 0xFC, gpio_base + 0x500);
+		}
+	} else if (of_machine_is_compatible("wm,wm8650")) {
+		if (nr == 0) {
+			/* set gpio pins to alt function */
+			val = readb(gpio_base + 0x55);
+			val &= ~(BIT(0) | BIT(1));
+			writeb(val, gpio_base + 0x55);
+
+			/* enable pull */
+			val = readb(gpio_base + 0x495);
+			val |= (BIT(0) | BIT(1));
+			writeb(val, gpio_base + 0x495);
+
+			/* set pulled-up */
+			val = readb(gpio_base + 0x4D5);
+			val |= (BIT(0) | BIT(1));
+			writeb(val, gpio_base + 0x4D5);
+		} else if (nr == 1) {
+			/* set gpio pins to alt function */
+			val = readb(gpio_base + 0x49);
+			val &= ~(BIT(4) | BIT(5));
+			writeb(val, gpio_base + 0x49);
+
+			/* enable pull */
+			val = readb(gpio_base + 0x489);
+			val |= (BIT(4) | BIT(5));
+			writeb(val, gpio_base + 0x489);
+
+			/* set pulled-up */
+			val = readb(gpio_base + 0x4C9);
+			val |= (BIT(4) | BIT(5));
+			writeb(val, gpio_base + 0x4C9);
+		}
+	} else if (of_machine_is_compatible("wm,wm8850")) {
+		if ((nr >= 0) && (nr <= 2)) {
+			mask = 0x03 << (nr << 1);
+	
+			/* set gpio pins to alt function */
+			val = readb(gpio_base + 0x55);
+			val &= ~mask;
+			writeb(val, gpio_base + 0x55);
+
+			/* enable pull */
+			val = readb(gpio_base + 0x495);
+			val |= mask;
+			writeb(val, gpio_base + 0x495);
+
+			/* set pulled-up */
+			val = readb(gpio_base + 0x4D5);
+			val |= mask;
+			writeb(val, gpio_base + 0x4D5);
+		}
+	}
+
+
+	iounmap(gpio_base);
+
+	return 0;
+}
+
+static int wmt_i2c_reset_hardware(struct wmt_i2c_dev *i2c_dev)
+{
+	int err;
+
+	err = clk_prepare_enable(i2c_dev->clk);
+	if (err) {
+		dev_err(i2c_dev->dev, "failed to enable clock\n");
+		return err;
+	}
+
+	err = clk_set_rate(i2c_dev->clk, 20000000);
+	if (err) {
+		dev_err(i2c_dev->dev, "failed to set clock = 20Mhz\n");
+		return err;
+	}
+
+	err = wmt_i2c_setup_gpio(i2c_dev);
+	if (err)
+		return err;
+
+	writew(0, i2c_dev->base + REG_CR);
+	writew(12, i2c_dev->base + REG_MCR);
+	writew(ISR_WRITE_ALL, i2c_dev->base + REG_ISR);
+	writew(IMR_ENABLE_ALL, i2c_dev->base + REG_IMR);
+	writew(CR_ENABLE, i2c_dev->base + REG_CR);
+	readw(i2c_dev->base + REG_CSR);		/* read clear */
+	writew(ISR_WRITE_ALL, i2c_dev->base + REG_ISR);
+
+	if (i2c_dev->mode == I2C_MODE_STANDARD)
+		writew(0x8064, i2c_dev->base + REG_TR);
+	else
+		writew(0x8019, i2c_dev->base + REG_TR);
+
+	return 0;
+}
+
+static int wmt_i2c_probe(struct platform_device *pdev)
+{
+	struct device_node	*np = pdev->dev.of_node;
+	struct wmt_i2c_dev	*i2c_dev;
+	struct i2c_adapter	*adap;
+	struct resource		*res;
+	int err;
+	u32 clk_rate;
+
+	if (!np) {
+		dev_err(&pdev->dev, "device node not found\n");
+		return -ENODEV;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "no memory resource defined\n");
+		return -ENODEV;
+	}
+
+	i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
+	if (!i2c_dev) {
+		dev_err(&pdev->dev, "device memory allocation failed\n");
+		return -ENOMEM;
+	}
+
+	i2c_dev->base = devm_request_and_ioremap(&pdev->dev, res);
+	if (!i2c_dev->base) {
+		dev_err(&pdev->dev, "memory region unavailable\n");
+		return -ENOMEM;
+	}
+
+	i2c_dev->irq = irq_of_parse_and_map(np, 0);
+	if (!i2c_dev->irq) {
+		dev_err(&pdev->dev, "irq missing or invalid\n");
+		return -EINVAL;
+	}
+
+	i2c_dev->clk = of_clk_get(np, 0);
+	if (IS_ERR(i2c_dev->clk)) {
+		dev_err(&pdev->dev, "unable to request clock\n");
+		return PTR_ERR(i2c_dev->clk);
+	}
+
+	i2c_dev->mode = I2C_MODE_STANDARD;
+	err = of_property_read_u32(np, "clock-frequency", &clk_rate);
+	if ((!err) && (clk_rate == 400000))
+		i2c_dev->mode = I2C_MODE_FAST;
+
+	i2c_dev->dev = &pdev->dev;
+
+	err = devm_request_irq(&pdev->dev, i2c_dev->irq, wmt_i2c_isr, 0,
+							"i2c", i2c_dev);
+	if (err) {
+		dev_err(&pdev->dev, "failed to request irq %i\n", i2c_dev->irq);
+		return err;
+	}
+
+	adap = &i2c_dev->adapter;
+	i2c_set_adapdata(adap, i2c_dev);
+	strlcpy(adap->name, "WMT I2C adapter", sizeof(adap->name));
+	adap->owner		= THIS_MODULE;
+	adap->class		= I2C_CLASS_HWMON;
+	adap->algo		= &wmt_i2c_algo;
+	adap->dev.parent	= &pdev->dev;
+	adap->dev.of_node	= pdev->dev.of_node;
+	adap->nr		= of_alias_get_id(pdev->dev.of_node, "i2c");
+
+	err = wmt_i2c_reset_hardware(i2c_dev);
+	if (err) {
+		dev_err(&pdev->dev, "error initializing hardware\n");
+		return err;
+	}
+
+	if (adap->nr < 0)
+		err = i2c_add_adapter(adap);
+	else
+		err = i2c_add_numbered_adapter(adap);
+
+	if (err) {
+		dev_err(&pdev->dev, "failed to add adapter\n");
+		return err;
+	}
+
+	platform_set_drvdata(pdev, i2c_dev);
+
+	of_i2c_register_devices(adap);
+
+	return 0;
+}
+
+static int wmt_i2c_remove(struct platform_device *pdev)
+{
+	struct wmt_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+
+	i2c_del_adapter(&i2c_dev->adapter);
+
+	return 0;
+}
+
+static struct of_device_id wmt_i2c_dt_ids[] = {
+	{ .compatible = "wm,wm8505-i2c" },
+	{ /* Sentinel */ },
+};
+
+static struct platform_driver wmt_i2c_driver = {
+	.probe		= wmt_i2c_probe,
+	.remove		= wmt_i2c_remove,
+	.driver		= {
+		.name	= "wmt-i2c",
+		.owner	= THIS_MODULE,
+		.of_match_table = wmt_i2c_dt_ids,
+	},
+};
+
+module_platform_driver(wmt_i2c_driver);
+
+MODULE_DESCRIPTION("Wondermedia I2C master-mode bus adapter");
+MODULE_AUTHOR("Tony Prisk <linux@prisktech.co.nz>");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, wmt_i2c_dt_ids);
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH] i2c: vt8500: Add support for Wondermedia I2C master-mode
From: Tony Prisk @ 2012-12-28  1:29 UTC (permalink / raw)
  To: Ben Dooks, Wolfram Sang
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA, Tony Prisk

This patch adds support for the I2C controller found on Wondermedia
SoCs.

Due to the lack of pinmux support, GPIO pin alternate functions are
configured by machine's compatible property, as are pullups.

Signed-off-by: Tony Prisk <linux-ci5G2KO2hbZ+pU9mqzGVBQ@public.gmane.org>
---
 .../devicetree/bindings/i2c/i2c-vt8500.txt         |   24 +
 drivers/i2c/busses/Kconfig                         |    7 +
 drivers/i2c/busses/Makefile                        |    1 +
 drivers/i2c/busses/i2c-wmt.c                       |  636 ++++++++++++++++++++
 4 files changed, 668 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/i2c/i2c-vt8500.txt
 create mode 100644 drivers/i2c/busses/i2c-wmt.c

diff --git a/Documentation/devicetree/bindings/i2c/i2c-vt8500.txt b/Documentation/devicetree/bindings/i2c/i2c-vt8500.txt
new file mode 100644
index 0000000..94a425e
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/i2c-vt8500.txt
@@ -0,0 +1,24 @@
+* Wondermedia I2C Controller
+
+Required properties :
+
+ - compatible : should be "wm,wm8505-i2c"
+ - reg : Offset and length of the register set for the device
+ - interrupts : <IRQ> where IRQ is the interrupt number
+ - clocks : phandle to the I2C clock source
+
+Optional properties :
+
+ - clock-frequency : desired I2C bus clock frequency in Hz.
+	Valid values are 100000 and 400000.
+	Default to 100000 if not specified, or invalid value.
+
+Example :
+
+	i2c_0: i2c@d8280000 {
+		compatible = "wm,wm8505-i2c";
+		reg = <0xd8280000 0x1000>;
+		interrupts = <19>;
+		clocks = <&clki2c0>;
+		clock-frequency = <400000>;
+	};
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index bdca511..41270fa 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -700,6 +700,13 @@ config I2C_VERSATILE
 	  This driver can also be built as a module.  If so, the module
 	  will be called i2c-versatile.
 
+config I2C_WMT
+	tristate "Wondermedia I2C Controller support"
+	depends on ARCH_VT8500
+	help
+	  If you say yes to this option, support will be included for the
+	  I2C controllers found in Wondermedia SoCs.
+
 config I2C_OCTEON
 	tristate "Cavium OCTEON I2C bus support"
 	depends on CPU_CAVIUM_OCTEON
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 6181f3f..dce6299 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -69,6 +69,7 @@ obj-$(CONFIG_I2C_SIRF)		+= i2c-sirf.o
 obj-$(CONFIG_I2C_STU300)	+= i2c-stu300.o
 obj-$(CONFIG_I2C_TEGRA)		+= i2c-tegra.o
 obj-$(CONFIG_I2C_VERSATILE)	+= i2c-versatile.o
+obj-$(CONFIG_I2C_WMT)		+= i2c-wmt.o
 obj-$(CONFIG_I2C_OCTEON)	+= i2c-octeon.o
 obj-$(CONFIG_I2C_XILINX)	+= i2c-xiic.o
 obj-$(CONFIG_I2C_XLR)		+= i2c-xlr.o
diff --git a/drivers/i2c/busses/i2c-wmt.c b/drivers/i2c/busses/i2c-wmt.c
new file mode 100644
index 0000000..07d86e3
--- /dev/null
+++ b/drivers/i2c/busses/i2c-wmt.c
@@ -0,0 +1,636 @@
+/*
+ *  Wondermedia I2C Master Mode Driver
+ *
+ *  Copyright (C) 2012 Tony Prisk <linux-ci5G2KO2hbZ+pU9mqzGVBQ@public.gmane.org>
+ *
+ *  Derived from GPL licensed source:
+ *  - Copyright (C) 2008 WonderMedia Technologies, Inc.
+ *
+ *  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/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_i2c.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+
+#define REG_CR		0x00
+#define REG_TCR		0x02
+#define REG_CSR		0x04
+#define REG_ISR		0x06
+#define REG_IMR		0x08
+#define REG_CDR		0x0A
+#define REG_TR		0x0C
+#define REG_MCR		0x0E
+#define REG_SLAVE_CR	0x10
+#define REG_SLAVE_SR	0x12
+#define REG_SLAVE_ISR	0x14
+#define REG_SLAVE_IMR	0x16
+#define REG_SLAVE_DR	0x18
+#define REG_SLAVE_TR	0x1A
+
+/* REG_CR Bit fields */
+#define CR_TX_NEXT_ACK		0x0000
+#define CR_ENABLE		0x0001
+#define CR_TX_NEXT_NO_ACK	0x0002
+#define CR_TX_END		0x0004
+#define CR_CPU_RDY		0x0008
+#define SLAV_MODE_SEL		0x8000
+
+/* REG_TCR Bit fields */
+#define TCR_STANDARD_MODE	0x0000
+#define TCR_MASTER_WRITE	0x0000
+#define TCR_HS_MODE		0x2000
+#define TCR_MASTER_READ		0x4000
+#define TCR_FAST_MODE		0x8000
+#define TCR_SLAVE_ADDR_MASK	0x007F
+
+/* REG_ISR Bit fields */
+#define ISR_NACK_ADDR		0x0001
+#define ISR_BYTE_END		0x0002
+#define ISR_SCL_TIMEOUT		0x0004
+#define ISR_WRITE_ALL		0x0007
+
+/* REG_IMR Bit fields */
+#define IMR_ENABLE_ALL		0x0007
+
+/* REG_CSR Bit fields */
+#define CSR_RCV_NOT_ACK		0x0001
+#define CSR_RCV_ACK_MASK	0x0001
+#define CSR_READY_MASK		0x0002
+
+#define I2C_MODE_STANDARD	0
+#define I2C_MODE_FAST		1
+
+
+
+struct wmt_i2c_dev {
+	struct i2c_adapter	adapter;
+	struct completion	complete;
+	struct device		*dev;
+	void __iomem		*base;
+	struct clk		*clk;
+	int			mode;
+	int			irq;
+	u16			cmd_status;
+};
+
+static int wmt_i2c_wait_bus_not_busy(struct wmt_i2c_dev *i2c_dev)
+{
+	u16 val;
+	int i;
+	int ret = 0;
+
+	for (i = 0; i < 10000000; i++) {
+		val = readw(i2c_dev->base + REG_CSR);
+		if (val & CSR_READY_MASK)
+			break;
+
+		udelay(1);
+	}
+	if (i >= 9999999)
+		ret = -EBUSY;
+
+	return ret;
+}
+
+static int wmt_check_status(struct wmt_i2c_dev *i2c_dev)
+{
+	int ret = 0;
+
+	if (i2c_dev->cmd_status & ISR_NACK_ADDR)
+		ret = -EIO;
+
+	if (i2c_dev->cmd_status & ISR_SCL_TIMEOUT)
+		ret = -ETIMEDOUT;
+
+	return ret;
+}
+
+static int wmt_i2c_write(struct i2c_adapter *adap, struct i2c_msg *pmsg,
+						   int restart, int last)
+{
+	struct wmt_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+	u16 val;
+	u16 tcr_val;
+	int ret;
+	int wait_result;
+	u32 xfer_len = 0;
+
+	if (pmsg->len < 0)
+		return -EINVAL;
+
+	if (restart == 0) {
+		ret = wmt_i2c_wait_bus_not_busy(i2c_dev);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (pmsg->len == 0)
+		writew(0, i2c_dev->base + REG_CDR);
+	else
+		writew(pmsg->buf[0] & 0xFF, i2c_dev->base + REG_CDR);
+
+	if (restart == 0) {
+		val = readw(i2c_dev->base + REG_CR);
+		val &= ~CR_TX_END;
+		writew(val, i2c_dev->base + REG_CR);
+
+		val = readw(i2c_dev->base + REG_CR);
+		val |= CR_CPU_RDY;
+		writew(val, i2c_dev->base + REG_CR);
+	}
+
+	init_completion(&i2c_dev->complete);
+
+	if (i2c_dev->mode == I2C_MODE_STANDARD)
+		tcr_val = TCR_STANDARD_MODE;
+	else
+		tcr_val = TCR_FAST_MODE;
+
+	tcr_val |= (TCR_MASTER_WRITE | (pmsg->addr & TCR_SLAVE_ADDR_MASK));
+
+	writew(tcr_val, i2c_dev->base + REG_TCR);
+
+	if (restart == 1) {
+		val = readw(i2c_dev->base + REG_CR);
+		val |= CR_CPU_RDY;
+		writew(val, i2c_dev->base + REG_CR);
+	}
+
+	ret = 0;
+
+	for (;;) {
+		wait_result = wait_for_completion_interruptible_timeout(
+				&i2c_dev->complete, 500 * HZ / 1000);
+
+		if (wait_result == 0) {
+			dev_dbg(i2c_dev->dev, "wait timeout (tx)\n");
+			ret = -ETIMEDOUT;
+			break;
+		}
+
+		ret = wmt_check_status(i2c_dev);
+		if (ret)
+			break;
+
+		xfer_len++;
+
+		val = readw(i2c_dev->base + REG_CSR);
+		if ((val & CSR_RCV_ACK_MASK) == CSR_RCV_NOT_ACK) {
+			dev_dbg(i2c_dev->dev, "write RCV NACK error\n");
+			ret = -EIO;
+			break;
+		}
+
+		if (pmsg->len == 0) {
+			val = CR_TX_END | CR_CPU_RDY | CR_ENABLE;
+			writew(val, i2c_dev->base + REG_CR);
+			break;
+		}
+
+		if (pmsg->len > xfer_len) {
+			writew(pmsg->buf[xfer_len] & 0xFF, i2c_dev->base +
+								REG_CDR);
+			writew(CR_CPU_RDY | CR_ENABLE, i2c_dev->base + REG_CR);
+		} else if (pmsg->len == xfer_len) {
+			if (last != 1)
+				writew(CR_ENABLE, i2c_dev->base + REG_CR);
+			break;
+		} else {
+			dev_dbg(i2c_dev->dev, "unknown error (tx)\n");
+			ret = -EIO;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int wmt_i2c_read(struct i2c_adapter *adap, struct i2c_msg *pmsg,
+						  int restart, int last)
+{
+	struct wmt_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+	u16 val;
+	u16 tcr_val;
+	int ret;
+	int wait_result;
+	u32 xfer_len = 0;
+
+	if (pmsg->len <= 0)
+		return -EINVAL;
+
+	if (restart == 0) {
+		ret = wmt_i2c_wait_bus_not_busy(i2c_dev);
+		if (ret < 0)
+			return ret;
+	}
+
+	val = readw(i2c_dev->base + REG_CR);
+	val &= ~CR_TX_END;
+	writew(val, i2c_dev->base + REG_CR);
+
+	val = readw(i2c_dev->base + REG_CR);
+	val &= ~CR_TX_NEXT_NO_ACK;
+	writew(val, i2c_dev->base + REG_CR);
+
+	if (restart == 0) {
+		val = readw(i2c_dev->base + REG_CR);
+		val |= CR_CPU_RDY;
+		writew(val, i2c_dev->base + REG_CR);
+	}
+
+	if (pmsg->len == 1) {
+		val = readw(i2c_dev->base + REG_CR);
+		val |= CR_TX_NEXT_NO_ACK;
+		writew(val, i2c_dev->base + REG_CR);
+	}
+
+	init_completion(&i2c_dev->complete);
+
+	if (i2c_dev->mode == I2C_MODE_STANDARD)
+		tcr_val = TCR_STANDARD_MODE;
+	else
+		tcr_val = TCR_FAST_MODE;
+
+	tcr_val |= TCR_MASTER_READ | (pmsg->addr & TCR_SLAVE_ADDR_MASK);
+
+	writew(tcr_val, i2c_dev->base + REG_TCR);
+
+	if (restart == 1) {
+		val = readw(i2c_dev->base + REG_CR);
+		val |= CR_CPU_RDY;
+		writew(val, i2c_dev->base + REG_CR);
+	}
+
+	ret = 0;
+
+	for (;;) {
+		wait_result = wait_for_completion_interruptible_timeout(
+				&i2c_dev->complete, 500 * HZ / 1000);
+
+		if (wait_result == 0) {
+			dev_dbg(i2c_dev->dev, "wait timeout (tx)\n");
+			ret = -ETIMEDOUT;
+			break;
+		}
+
+		ret = wmt_check_status(i2c_dev);
+		if (ret)
+			break;
+
+		pmsg->buf[xfer_len] = readw(i2c_dev->base + REG_CDR) >> 8;
+		xfer_len++;
+
+		if (pmsg->len > xfer_len) {
+			if (pmsg->len - 1 == xfer_len) {
+				val = readw(i2c_dev->base + REG_CR);
+				val |= (CR_TX_NEXT_NO_ACK | CR_CPU_RDY);
+				writew(val, i2c_dev->base + REG_CR);
+			} else {
+				val = readw(i2c_dev->base + REG_CR);
+				val |= CR_CPU_RDY;
+				writew(val, i2c_dev->base + REG_CR);
+			}
+		} else if (pmsg->len == xfer_len) {
+			break;
+		} else {
+			ret = -EIO;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int wmt_i2c_xfer(struct i2c_adapter *adap,
+			struct i2c_msg msgs[],
+			int num)
+{
+	struct i2c_msg *pmsg;
+	int i;
+	int ret = 0;
+	int is_last;
+	int restart;
+
+	for (i = 0; ret >= 0 && i < num; i++) {
+		is_last = ((i + 1) == num);
+		restart = (i != 0);
+
+		pmsg = &msgs[i];
+		if (pmsg->flags & I2C_M_NOSTART)
+			restart = 1;
+		if (pmsg->flags & I2C_M_RD)
+			ret = wmt_i2c_read(adap, pmsg, restart, is_last);
+		else
+			ret = wmt_i2c_write(adap, pmsg, restart, is_last);
+	}
+
+	if (ret < 0)
+		return ret;
+	else
+		return i;
+}
+
+static u32 wmt_i2c_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm wmt_i2c_algo = {
+	.master_xfer	= wmt_i2c_xfer,
+	.functionality	= wmt_i2c_func,
+};
+
+static irqreturn_t wmt_i2c_isr(int irq, void *data)
+{
+	struct wmt_i2c_dev *i2c_dev = data;
+
+	/* save the status and write-clear it */
+	i2c_dev->cmd_status = readw(i2c_dev->base + REG_ISR);
+	writew(i2c_dev->cmd_status, i2c_dev->base + REG_ISR);
+
+	complete(&i2c_dev->complete);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Due to a lack of pinmux functionality we need to manually configure
+ * the GPIO pullup. Once pinmux is implemented, this function should be
+ * removed. Pullup's are not available on vt8500 or wm8505 so we skip.
+ */
+static int wmt_i2c_setup_gpio(struct wmt_i2c_dev *i2c_dev)
+{
+	struct device_node *np;
+	void __iomem *gpio_base;
+	int nr = i2c_dev->adapter.nr;
+	u8 val;
+	u8 mask;
+
+	/* VT8500 - has external pullups so no configuration required. */
+	if (of_machine_is_compatible("via,vt8500"))
+		return 0;
+
+	/*
+	 * WM8505 - has external pullups but we need to config the alt func
+	 * WM8650 - has programmable pullups + alt func
+	 * WM8850 - has programmable pullups + alt func
+	 */
+	if (of_machine_is_compatible("wm,wm8505")) {
+		np = of_find_compatible_node(NULL, NULL, "wm,wm8505-gpio");
+		if (!np) {
+			dev_err(i2c_dev->dev, "GPIO required for WM8505 i2c\n");
+			return -EINVAL;
+		}
+	} else if (of_machine_is_compatible("wm,wm8650") ||
+		   of_machine_is_compatible("wm,wm8850")) {
+		np = of_find_compatible_node(NULL, NULL, "wm,wm8650-gpio");
+		if (!np) {
+			dev_err(i2c_dev->dev, "GPIO required for WM8650 i2c\n");
+			return -EINVAL;
+		}
+	} else {
+		/* If we don't know what machine, assume its configured */
+		dev_warn(i2c_dev->dev, "unrecognised machine\n");
+		return 0;
+	}
+
+	gpio_base = of_iomap(np, 0);
+	if (!gpio_base) {
+		dev_err(i2c_dev->dev, "failed to map gpio memory\n");
+		return -ENOMEM;
+	}
+
+	if (of_machine_is_compatible("wm,wm8505")) {
+		if (nr == 0) {
+			/* Set gpio pins to alt function */
+			val = readb(gpio_base + 0x500);
+			writeb(val & 0xFC, gpio_base + 0x500);
+		}
+	} else if (of_machine_is_compatible("wm,wm8650")) {
+		if (nr == 0) {
+			/* set gpio pins to alt function */
+			val = readb(gpio_base + 0x55);
+			val &= ~(BIT(0) | BIT(1));
+			writeb(val, gpio_base + 0x55);
+
+			/* enable pull */
+			val = readb(gpio_base + 0x495);
+			val |= (BIT(0) | BIT(1));
+			writeb(val, gpio_base + 0x495);
+
+			/* set pulled-up */
+			val = readb(gpio_base + 0x4D5);
+			val |= (BIT(0) | BIT(1));
+			writeb(val, gpio_base + 0x4D5);
+		} else if (nr == 1) {
+			/* set gpio pins to alt function */
+			val = readb(gpio_base + 0x49);
+			val &= ~(BIT(4) | BIT(5));
+			writeb(val, gpio_base + 0x49);
+
+			/* enable pull */
+			val = readb(gpio_base + 0x489);
+			val |= (BIT(4) | BIT(5));
+			writeb(val, gpio_base + 0x489);
+
+			/* set pulled-up */
+			val = readb(gpio_base + 0x4C9);
+			val |= (BIT(4) | BIT(5));
+			writeb(val, gpio_base + 0x4C9);
+		}
+	} else if (of_machine_is_compatible("wm,wm8850")) {
+		if ((nr >= 0) && (nr <= 2)) {
+			mask = 0x03 << (nr << 1);
+	
+			/* set gpio pins to alt function */
+			val = readb(gpio_base + 0x55);
+			val &= ~mask;
+			writeb(val, gpio_base + 0x55);
+
+			/* enable pull */
+			val = readb(gpio_base + 0x495);
+			val |= mask;
+			writeb(val, gpio_base + 0x495);
+
+			/* set pulled-up */
+			val = readb(gpio_base + 0x4D5);
+			val |= mask;
+			writeb(val, gpio_base + 0x4D5);
+		}
+	}
+
+
+	iounmap(gpio_base);
+
+	return 0;
+}
+
+static int wmt_i2c_reset_hardware(struct wmt_i2c_dev *i2c_dev)
+{
+	int err;
+
+	err = clk_prepare_enable(i2c_dev->clk);
+	if (err) {
+		dev_err(i2c_dev->dev, "failed to enable clock\n");
+		return err;
+	}
+
+	err = clk_set_rate(i2c_dev->clk, 20000000);
+	if (err) {
+		dev_err(i2c_dev->dev, "failed to set clock = 20Mhz\n");
+		return err;
+	}
+
+	err = wmt_i2c_setup_gpio(i2c_dev);
+	if (err)
+		return err;
+
+	writew(0, i2c_dev->base + REG_CR);
+	writew(12, i2c_dev->base + REG_MCR);
+	writew(ISR_WRITE_ALL, i2c_dev->base + REG_ISR);
+	writew(IMR_ENABLE_ALL, i2c_dev->base + REG_IMR);
+	writew(CR_ENABLE, i2c_dev->base + REG_CR);
+	readw(i2c_dev->base + REG_CSR);		/* read clear */
+	writew(ISR_WRITE_ALL, i2c_dev->base + REG_ISR);
+
+	if (i2c_dev->mode == I2C_MODE_STANDARD)
+		writew(0x8064, i2c_dev->base + REG_TR);
+	else
+		writew(0x8019, i2c_dev->base + REG_TR);
+
+	return 0;
+}
+
+static int wmt_i2c_probe(struct platform_device *pdev)
+{
+	struct device_node	*np = pdev->dev.of_node;
+	struct wmt_i2c_dev	*i2c_dev;
+	struct i2c_adapter	*adap;
+	struct resource		*res;
+	int err;
+	u32 clk_rate;
+
+	if (!np) {
+		dev_err(&pdev->dev, "device node not found\n");
+		return -ENODEV;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "no memory resource defined\n");
+		return -ENODEV;
+	}
+
+	i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
+	if (!i2c_dev) {
+		dev_err(&pdev->dev, "device memory allocation failed\n");
+		return -ENOMEM;
+	}
+
+	i2c_dev->base = devm_request_and_ioremap(&pdev->dev, res);
+	if (!i2c_dev->base) {
+		dev_err(&pdev->dev, "memory region unavailable\n");
+		return -ENOMEM;
+	}
+
+	i2c_dev->irq = irq_of_parse_and_map(np, 0);
+	if (!i2c_dev->irq) {
+		dev_err(&pdev->dev, "irq missing or invalid\n");
+		return -EINVAL;
+	}
+
+	i2c_dev->clk = of_clk_get(np, 0);
+	if (IS_ERR(i2c_dev->clk)) {
+		dev_err(&pdev->dev, "unable to request clock\n");
+		return PTR_ERR(i2c_dev->clk);
+	}
+
+	i2c_dev->mode = I2C_MODE_STANDARD;
+	err = of_property_read_u32(np, "clock-frequency", &clk_rate);
+	if ((!err) && (clk_rate == 400000))
+		i2c_dev->mode = I2C_MODE_FAST;
+
+	i2c_dev->dev = &pdev->dev;
+
+	err = devm_request_irq(&pdev->dev, i2c_dev->irq, wmt_i2c_isr, 0,
+							"i2c", i2c_dev);
+	if (err) {
+		dev_err(&pdev->dev, "failed to request irq %i\n", i2c_dev->irq);
+		return err;
+	}
+
+	adap = &i2c_dev->adapter;
+	i2c_set_adapdata(adap, i2c_dev);
+	strlcpy(adap->name, "WMT I2C adapter", sizeof(adap->name));
+	adap->owner		= THIS_MODULE;
+	adap->class		= I2C_CLASS_HWMON;
+	adap->algo		= &wmt_i2c_algo;
+	adap->dev.parent	= &pdev->dev;
+	adap->dev.of_node	= pdev->dev.of_node;
+	adap->nr		= of_alias_get_id(pdev->dev.of_node, "i2c");
+
+	err = wmt_i2c_reset_hardware(i2c_dev);
+	if (err) {
+		dev_err(&pdev->dev, "error initializing hardware\n");
+		return err;
+	}
+
+	if (adap->nr < 0)
+		err = i2c_add_adapter(adap);
+	else
+		err = i2c_add_numbered_adapter(adap);
+
+	if (err) {
+		dev_err(&pdev->dev, "failed to add adapter\n");
+		return err;
+	}
+
+	platform_set_drvdata(pdev, i2c_dev);
+
+	of_i2c_register_devices(adap);
+
+	return 0;
+}
+
+static int wmt_i2c_remove(struct platform_device *pdev)
+{
+	struct wmt_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+
+	i2c_del_adapter(&i2c_dev->adapter);
+
+	return 0;
+}
+
+static struct of_device_id wmt_i2c_dt_ids[] = {
+	{ .compatible = "wm,wm8505-i2c" },
+	{ /* Sentinel */ },
+};
+
+static struct platform_driver wmt_i2c_driver = {
+	.probe		= wmt_i2c_probe,
+	.remove		= wmt_i2c_remove,
+	.driver		= {
+		.name	= "wmt-i2c",
+		.owner	= THIS_MODULE,
+		.of_match_table = wmt_i2c_dt_ids,
+	},
+};
+
+module_platform_driver(wmt_i2c_driver);
+
+MODULE_DESCRIPTION("Wondermedia I2C master-mode bus adapter");
+MODULE_AUTHOR("Tony Prisk <linux-ci5G2KO2hbZ+pU9mqzGVBQ@public.gmane.org>");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, wmt_i2c_dt_ids);
-- 
1.7.9.5

^ permalink raw reply related


This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.