All of lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH v7 00/27] x86, boot, 64bit: Add support for loading ramdisk and bzImage above 4G
From: Borislav Petkov @ 2012-12-25 11:52 UTC (permalink / raw)
  To: H. Peter Anvin
  Cc: Konrad Rzeszutek Wilk, Yinghai Lu, Thomas Gleixner, Ingo Molnar,
	Eric W. Biederman, Andrew Morton, linux-kernel
In-Reply-To: <50D8FBF9.6050509@zytor.com>

On Mon, Dec 24, 2012 at 05:06:01PM -0800, H. Peter Anvin wrote:
> Could this be the ljmpq problem that Borislav reported
> (Intel implemented ljmpq, AMD didn't, and I was tempted by a
> micro-optimization which broke AMD which made it into the patchset)?

It has to be. Just booted Yinghai's -v8 in kvm on an AMD host and it
worked fine.

With the change below it keeps rebooting like I reported earlier. I'd go
out on a limb here and guess that the guest is triple-faulting due to an
unhandled #GP caused by an invalid opcode or similar.

---
diff --git a/arch/x86/kernel/head_64.S b/arch/x86/kernel/head_64.S
index d94f6d68be2a..1842d30c96a2 100644
--- a/arch/x86/kernel/head_64.S
+++ b/arch/x86/kernel/head_64.S
@@ -279,11 +279,8 @@ ENTRY(secondary_startup_64)
         *      REX.W + FF /5 JMP m16:64 Jump far, absolute indirect,
         *              address given in m16:64.
         */
-       movq    initial_code(%rip),%rax
        pushq   $0              # fake return address to stop unwinder
-       pushq   $__KERNEL_CS    # set correct cs
-       pushq   %rax            # target address in negative space
-       lretq
+       rex64 ljmp *initial_code(%rip)
 
 #ifdef CONFIG_HOTPLUG_CPU
 /*

-- 
Regards/Gruss,
    Boris.

Sent from a fat crate under my desk. Formatting is fine.
--

^ permalink raw reply related

* Re: [PATCH 20/29] batman-adv: rename random32() to prandom_u32()
From: Antonio Quartulli @ 2012-12-25 11:30 UTC (permalink / raw)
  To: Akinobu Mita
  Cc: linux-kernel, akpm, Marek Lindner, Simon Wunderlich, b.a.t.m.a.n,
	David S. Miller, netdev
In-Reply-To: <1356315256-6572-21-git-send-email-akinobu.mita@gmail.com>

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

On Mon, Dec 24, 2012 at 11:14:07AM +0900, Akinobu Mita wrote:
> Use more preferable function name which implies using a pseudo-random
> number generator.
> 
> Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
> Cc: Marek Lindner <lindner_marek@yahoo.de>
> Cc: Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
> Cc: Antonio Quartulli <ordex@autistici.org>
> Cc: b.a.t.m.a.n@lists.open-mesh.org
> Cc: "David S. Miller" <davem@davemloft.net>
> Cc: netdev@vger.kernel.org

Acked-by: Antonio Quartulli <ordex@autistici.org>

-- 
Antonio Quartulli

..each of us alone is worth nothing..
Ernesto "Che" Guevara

[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: [PATCH 1/6] uvcvideo: Set error_idx properly for extended controls API failures
From: Hans Verkuil @ 2012-12-25 11:50 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: linux-media, Hans Verkuil
In-Reply-To: <1427386.yhbGQRN2rP@avalon>

On Tue December 25 2012 12:23:00 Laurent Pinchart wrote:
> Hi Hans,
> 
> On Tuesday 25 December 2012 12:15:25 Hans Verkuil wrote:
> > On Mon December 24 2012 13:27:08 Laurent Pinchart wrote:
> > > On Thursday 27 September 2012 17:16:15 Laurent Pinchart wrote:
> > > > When one of the requested controls doesn't exist the error_idx field
> > > > must reflect that situation. For G_EXT_CTRLS and S_EXT_CTRLS, error_idx
> > > > must be set to the control count. For TRY_EXT_CTRLS, it must be set to
> > > > the index of the unexisting control.
> > > > 
> > > > This issue was found by the v4l2-compliance tool.
> > > 
> > > I'm revisiting this patch as it has been reverted in v3.8-rc1.
> > > 
> > > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > > > ---
> > > > 
> > > >  drivers/media/usb/uvc/uvc_ctrl.c |   17 ++++++++++-------
> > > >  drivers/media/usb/uvc/uvc_v4l2.c |   19 ++++++++++++-------
> > > >  2 files changed, 22 insertions(+), 14 deletions(-)
> > > 
> > > [snip]
> > > 
> > > > diff --git a/drivers/media/usb/uvc/uvc_v4l2.c
> > > > b/drivers/media/usb/uvc/uvc_v4l2.c index f00db30..e5817b9 100644
> > > > --- a/drivers/media/usb/uvc/uvc_v4l2.c
> > > > +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> > > > @@ -591,8 +591,10 @@ static long uvc_v4l2_do_ioctl(struct file *file,
> > > 
> > > [snip]
> > > 
> > > > @@ -637,8 +639,9 @@ static long uvc_v4l2_do_ioctl(struct file *file,
> > > > unsigned int cmd, void *arg) ret = uvc_ctrl_get(chain, ctrl);
> > > > 
> > > >  			if (ret < 0) {
> > > >  			
> > > >  				uvc_ctrl_rollback(handle);
> > > > 
> > > > -				ctrls->error_idx = i;
> > > > -				return ret;
> > > > +				ctrls->error_idx = ret == -ENOENT
> > > > +						 ? ctrls->count : i;
> > > > +				return ret == -ENOENT ? -EINVAL : ret;
> > > > 
> > > >  			}
> > > >  		
> > > >  		}
> > > >  		ctrls->error_idx = 0;
> > > > 
> > > > @@ -661,8 +664,10 @@ static long uvc_v4l2_do_ioctl(struct file *file,
> > > > unsigned int cmd, void *arg) ret = uvc_ctrl_set(chain, ctrl);
> > > > 
> > > >  			if (ret < 0) {
> > > >  			
> > > >  				uvc_ctrl_rollback(handle);
> > > > 
> > > > -				ctrls->error_idx = i;
> > > > -				return ret;
> > > > +				ctrls->error_idx = (ret == -ENOENT &&
> > > > +						    cmd == VIDIOC_S_EXT_CTRLS)
> > > > +						 ? ctrls->count : i;
> > > > +				return ret == -ENOENT ? -EINVAL : ret;
> > > > 
> > > >  			}
> > > >  		
> > > >  		}
> > > 
> > > I've reread the V4L2 specification, and the least I can say is that the
> > > text is pretty ambiguous. Let's clarify it.
> > > 
> > > Is there a reason to differentiate between invalid control IDs and other
> > > errors as far as error_idx is concerned ? It would be simpler if error_idx
> > > was set to the index of the first error for get and try operations,
> > > regardless of the error type. What do you think ?
> > 
> > There is a good reason for doing this: the G/S_EXT_CTRLS ioctls have to be
> > as atomic as possible, i.e. it should try hard to prevent leaving the
> > hardware in an inconsistent state because not all controls could be set. It
> > can never be fully atomic since writing multiple registers over usb or i2c
> > can always return errors for one of those writes, but it should certainly
> > check for all the obvious errors first that do not require actually writing
> > to the hardware, such as whether all the controls in the control list
> > actually exist.
> > 
> > And for such errors error_idx should be set to the number of controls to
> > indicate that none of the controls were actually set but that there was a
> > problem with the list of controls itself.
> 
> For S_EXT_CTRLS, sure, but G_EXT_CTRLS doesn't modify the hardware state, so 
> it could get all controls up to the erroneous one.

I have thought about that but I decided against it. One reason is to have get
and set behave the same since both access the hardware. The other reason is
that even getting a control value might change the hardware state, for example
by resetting some internal hardware counter when a register is read (it's rare
but there is hardware like that). Furthermore, reading hardware registers can
be slow so why not do the sanity check first?

Regards,

	Hans

^ permalink raw reply

* Re: [lm-sensors] [PATCH 1/2] lm73: added 'update_interval' attribute
From: Guenter Roeck @ 2012-12-25 11:50 UTC (permalink / raw)
  To: lm-sensors
In-Reply-To: <1356378547-31499-1-git-send-email-kg4ysn@gmail.com>

On Tue, Dec 25, 2012 at 01:05:31AM -0800, Chris Verges wrote:
> On Mon, Dec 24, 2012 at 10:24 PM, Guenter Roeck <linux@roeck-us.net> wrote:
> > *snip*
> 
> Good suggestions.  I'll add them in.  I'm also working on a patch for
> ALERT support.  Might take a couple more days.  Expect a full patch for
> all of it when you get back.
> 
Cool. Just to be sure - remember SubmittingPatches rule #3.

Thanks,
Guenter

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply

* Re: [PATCH 28/29] net/: rename net_random() to prandom_u32()
From: Akinobu Mita @ 2012-12-25 11:47 UTC (permalink / raw)
  To: Neil Horman
  Cc: linux-kernel, akpm, Jesse Gross, Venkat Venkatsubra,
	Vlad Yasevich, Sridhar Samudrala, Steffen Klassert, Herbert Xu,
	David S. Miller, linux-sctp, dev, netdev
In-Reply-To: <20121225002149.GA5235@neilslaptop.think-freely.org>

2012/12/25 Neil Horman <nhorman@tuxdriver.com>:
> On Mon, Dec 24, 2012 at 11:14:15AM +0900, Akinobu Mita wrote:
>> Use more preferable function name which implies using a pseudo-random
>> number generator.
>>
>> Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
>> Cc: Jesse Gross <jesse@nicira.com>
>> Cc: Venkat Venkatsubra <venkat.x.venkatsubra@oracle.com>
>> Cc: Vlad Yasevich <vyasevich@gmail.com>
>> Cc: Sridhar Samudrala <sri@us.ibm.com>
>> Cc: Neil Horman <nhorman@tuxdriver.com>
>> Cc: Steffen Klassert <steffen.klassert@secunet.com>
>> Cc: Herbert Xu <herbert@gondor.apana.org.au>
>> Cc: "David S. Miller" <davem@davemloft.net>
>> Cc: linux-sctp@vger.kernel.org
>> Cc: dev@openvswitch.org
>> Cc: netdev@vger.kernel.org
>> ---
>>  include/net/red.h         | 2 +-
>>  net/802/garp.c            | 2 +-
>>  net/openvswitch/actions.c | 2 +-
>>  net/rds/bind.c            | 2 +-
>>  net/sctp/socket.c         | 2 +-
>>  net/xfrm/xfrm_state.c     | 2 +-
>>  6 files changed, 6 insertions(+), 6 deletions(-)
>>
> I'm largely indifferent to this patch, but I kind of feel like its just churn.
> Whats the real advantage in making this change?  I grant that it clearly
> indicates the type of random number generator we're using at a given call site,
> But for those using net_random, you probably don't care too much about
> the source of your random bits.  If you did really want true random vs.
> pseudo-random data, you need to explicitly use the right call.  You're previous
> patch series did good cleanup on differentiating the different random calls, but
> this just seems like its removing what is otherwise useful indirection.

I overlooked the importance of  net_random() indirection.
Thanks for the feedback. I'll leave all net_random() callers as-is in
the next version.

^ permalink raw reply

* Re: [PATCH 28/29] net/: rename net_random() to prandom_u32()
From: Akinobu Mita @ 2012-12-25 11:47 UTC (permalink / raw)
  To: Neil Horman
  Cc: linux-kernel, akpm, Jesse Gross, Venkat Venkatsubra,
	Vlad Yasevich, Sridhar Samudrala, Steffen Klassert, Herbert Xu,
	David S. Miller, linux-sctp, dev, netdev
In-Reply-To: <20121225002149.GA5235@neilslaptop.think-freely.org>

2012/12/25 Neil Horman <nhorman@tuxdriver.com>:
> On Mon, Dec 24, 2012 at 11:14:15AM +0900, Akinobu Mita wrote:
>> Use more preferable function name which implies using a pseudo-random
>> number generator.
>>
>> Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
>> Cc: Jesse Gross <jesse@nicira.com>
>> Cc: Venkat Venkatsubra <venkat.x.venkatsubra@oracle.com>
>> Cc: Vlad Yasevich <vyasevich@gmail.com>
>> Cc: Sridhar Samudrala <sri@us.ibm.com>
>> Cc: Neil Horman <nhorman@tuxdriver.com>
>> Cc: Steffen Klassert <steffen.klassert@secunet.com>
>> Cc: Herbert Xu <herbert@gondor.apana.org.au>
>> Cc: "David S. Miller" <davem@davemloft.net>
>> Cc: linux-sctp@vger.kernel.org
>> Cc: dev@openvswitch.org
>> Cc: netdev@vger.kernel.org
>> ---
>>  include/net/red.h         | 2 +-
>>  net/802/garp.c            | 2 +-
>>  net/openvswitch/actions.c | 2 +-
>>  net/rds/bind.c            | 2 +-
>>  net/sctp/socket.c         | 2 +-
>>  net/xfrm/xfrm_state.c     | 2 +-
>>  6 files changed, 6 insertions(+), 6 deletions(-)
>>
> I'm largely indifferent to this patch, but I kind of feel like its just churn.
> Whats the real advantage in making this change?  I grant that it clearly
> indicates the type of random number generator we're using at a given call site,
> But for those using net_random, you probably don't care too much about
> the source of your random bits.  If you did really want true random vs.
> pseudo-random data, you need to explicitly use the right call.  You're previous
> patch series did good cleanup on differentiating the different random calls, but
> this just seems like its removing what is otherwise useful indirection.

I overlooked the importance of  net_random() indirection.
Thanks for the feedback. I'll leave all net_random() callers as-is in
the next version.

^ permalink raw reply

* Re: [PATCH 28/29] net/: rename net_random() to prandom_u32()
From: Akinobu Mita @ 2012-12-25 11:47 UTC (permalink / raw)
  To: Neil Horman
  Cc: linux-kernel, akpm, Jesse Gross, Venkat Venkatsubra,
	Vlad Yasevich, Sridhar Samudrala, Steffen Klassert, Herbert Xu,
	David S. Miller, linux-sctp, dev, netdev
In-Reply-To: <20121225002149.GA5235@neilslaptop.think-freely.org>

2012/12/25 Neil Horman <nhorman@tuxdriver.com>:
> On Mon, Dec 24, 2012 at 11:14:15AM +0900, Akinobu Mita wrote:
>> Use more preferable function name which implies using a pseudo-random
>> number generator.
>>
>> Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
>> Cc: Jesse Gross <jesse@nicira.com>
>> Cc: Venkat Venkatsubra <venkat.x.venkatsubra@oracle.com>
>> Cc: Vlad Yasevich <vyasevich@gmail.com>
>> Cc: Sridhar Samudrala <sri@us.ibm.com>
>> Cc: Neil Horman <nhorman@tuxdriver.com>
>> Cc: Steffen Klassert <steffen.klassert@secunet.com>
>> Cc: Herbert Xu <herbert@gondor.apana.org.au>
>> Cc: "David S. Miller" <davem@davemloft.net>
>> Cc: linux-sctp@vger.kernel.org
>> Cc: dev@openvswitch.org
>> Cc: netdev@vger.kernel.org
>> ---
>>  include/net/red.h         | 2 +-
>>  net/802/garp.c            | 2 +-
>>  net/openvswitch/actions.c | 2 +-
>>  net/rds/bind.c            | 2 +-
>>  net/sctp/socket.c         | 2 +-
>>  net/xfrm/xfrm_state.c     | 2 +-
>>  6 files changed, 6 insertions(+), 6 deletions(-)
>>
> I'm largely indifferent to this patch, but I kind of feel like its just churn.
> Whats the real advantage in making this change?  I grant that it clearly
> indicates the type of random number generator we're using at a given call site,
> But for those using net_random, you probably don't care too much about
> the source of your random bits.  If you did really want true random vs.
> pseudo-random data, you need to explicitly use the right call.  You're previous
> patch series did good cleanup on differentiating the different random calls, but
> this just seems like its removing what is otherwise useful indirection.

I overlooked the importance of  net_random() indirection.
Thanks for the feedback. I'll leave all net_random() callers as-is in
the next version.

^ permalink raw reply

* [PATCH] trace-cmd: fix kvm_mmu_prepare_zap_page even name and kvm_mmu_get_page event output in kvm plugin
From: Gleb Natapov @ 2012-12-25 11:46 UTC (permalink / raw)
  To: srostedt; +Cc: kvm

kvm_mmu_zap_page even was renamed to kvm_mmu_prepare_zap_page.
Print out created field for kvm_mmu_get_page event.

Signed-off-by: Gleb Natapov <gleb@redhat.com>
diff --git a/plugin_kvm.c b/plugin_kvm.c
index 55812ef..adc5694 100644
--- a/plugin_kvm.c
+++ b/plugin_kvm.c
@@ -382,7 +382,7 @@ static int kvm_mmu_print_role(struct trace_seq *s, struct pevent_record *record,
 	} else
 		trace_seq_printf(s, "WORD: %08x", role.word);
 
-	pevent_print_num_field(s, " root %u",  event,
+	pevent_print_num_field(s, " root %u ",  event,
 			       "root_count", record, 1);
 
 	if (pevent_get_field_val(s, event, "unsync", record, &val, 1) < 0)
@@ -397,6 +397,11 @@ static int kvm_mmu_get_page_handler(struct trace_seq *s, struct pevent_record *r
 {
 	unsigned long long val;
 
+	if (pevent_get_field_val(s, event, "created", record, &val, 1) < 0)
+		return -1;
+
+	trace_seq_printf(s, "%s ", val ? "new" : "existing");
+
 	if (pevent_get_field_val(s, event, "gfn", record, &val, 1) < 0)
 		return -1;
 
@@ -430,7 +435,7 @@ int PEVENT_PLUGIN_LOADER(struct pevent *pevent)
 	pevent_register_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_unsync_page",
 				      kvm_mmu_print_role, NULL);
 
-	pevent_register_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_zap_page",
+	pevent_register_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_prepare_zap_page",
 				      kvm_mmu_print_role, NULL);
 
 	return 0;
--
			Gleb.

^ permalink raw reply related

* [U-Boot] [PATCH 3/4] fix memory corruption on versatile
From: Albert ARIBAUD @ 2012-12-25 11:37 UTC (permalink / raw)
  To: u-boot
In-Reply-To: <2293859.6svWlyftBz@merom>

Hi Pavel,

On Mon, 24 Dec 2012 15:57:30 +0100, Pavel Herrmann
<morpheus.ibis@gmail.com> wrote:
> Hi,
> 
> On Monday 24 December 2012 14:56:03 Albert ARIBAUD wrote:
> > Hi Pavel,
> > 
> > On Mon, 24 Dec 2012 02:27:53 +0100, Marek Vasut <marex@denx.de> wrote:
> > > Dear Pavel Herrmann,
> > > 
> > > > ARM board.c doesnt respect CONFIG_SYS_GBL_DATA_OFFSET, nor do all boards
> > > > set it, so reorganize the memory a bit to avoid overlaps.
> > > > 
> > > > Signed-off-by: Pavel Herrmann <morpheus.ibis@gmail.com>
> > > 
> > > Ccing Albert
> > > 
> > > > ---
> > > > 
> > > >  include/configs/versatile.h | 2 +-
> > > >  1 file changed, 1 insertion(+), 1 deletion(-)
> > > > 
> > > > diff --git a/include/configs/versatile.h b/include/configs/versatile.h
> > > > index c9aed72..27ffffe 100644
> > > > --- a/include/configs/versatile.h
> > > > +++ b/include/configs/versatile.h
> > > > @@ -35,7 +35,7 @@
> > > > 
> > > >  #define CONFIG_DM_BLOCK
> > > >  #define CONFIG_SYS_EARLY_MALLOC
> > > >  #define CONFIG_SYS_EARLY_HEAP_ADDR	(CONFIG_SYS_INIT_RAM_ADDR + \
> > > > 
> > > > -						CONFIG_SYS_GBL_DATA_OFFSET - \
> > > > +						CONFIG_SYS_INIT_RAM_SIZE - \
> > > > 
> > > >  						CONFIG_SYS_EARLY_HEAP_SIZE - 64)
> > > >  
> > > >  #define CONFIG_SYS_EARLY_HEAP_SIZE	1024
> > > 
> > > Best regards,
> > > Marek Vasut
> > 
> > I can't seem to find this patch in my inbox or in the list archives.
> > 
> > Pavel, can you please repost it?
> > 
> > Amicalement,
> 
> I dont really understand why Marek CCed you, this patch is meant to go in the 
> DM tree, and i dont see why it should be considered for upstream.

Patches should not be meant for a tree in the first place; they should
be meant for the tree where they make most sense. IIUC, this patch
fixes an issue related not to DM per se but to ARM, which is why Marek
CC:ed me (and the list).

> The problem this is trying to solve is that some boards (like the versatile i 
> use, as it has upstream qemu support) do define a suspicious looking 
> CONFIG_SYS_GBL_DATA_OFFSET, but that macro is not used in the board_init_f. 
> instead the GD is placed under the bottom of stack.
> 
> This is perfectly fine when we only have GD and stack in memory, but I would 
> like to have the early heap in there somewhere, ideally between GD and stack. 
> When I set the macros (not knowing that it is actually ignored) to represent 
> this layout, the boot failed because the stack was overlapping the GD, so this 
> patch changed the layout to [stack][GD][heap][end].
> 
> the "optimal" solution would be to have universal (as in that all archs 
> board_init_f()s would respect them, and all board configs would have them) 
> macros for address of GD, early heap and stack independently (with the obvious 
> exception that for stack you have the highest address, while for other you 
> have the lowest), so that we can have the originally intended layout ([stack]
> [heap][GD][end]), or any other layout for that matter.
> 
> This, of course, may not be possible due to various limitations of various 
> archs, so I did not attempt to make any such patch, instead just quick-fix the 
> board i am using to test early DM initialization.
> 
> Hope this clears the confusion

It does; I think I understand the problem you are stating. The only
problem I have is that I am still unable to tell if Marek did quote the
whole patch or not. :)

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?

> Pavel Herrmann

Amicalement,
-- 
Albert.

^ permalink raw reply

* [PATCH] ARM: at91: rm9200: remake the BGA as default version
From: Sergei Shtylyov @ 2012-12-25 11:37 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <50D759B1.1010701@mvista.com>

Hello.

On 23-12-2012 23:21, Sergei Shtylyov wrote:

>> as we are supposed to just have to specify when we use the PQFP version

>> since commit 3e90772f76010c315474bde59eaca7cc4c94d645
>> ARM: at91: fix at91rm9200 soc subtype handling

>> Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
>> Cc: stable at kernel.org

>     The new address is stable at vger.kernel.org. Don't know if the old one works
> still...

    Apparently it doesn't as I got a denial from server.

WBR, Sergei

^ permalink raw reply

* Re: Does init start with any open files?
From: bbi5291 @ 2012-12-25 11:25 UTC (permalink / raw)
  To: Borislav Petkov, bbi5291, linux-kernel
In-Reply-To: <20121225111742.GB7720@liondog.tnic>

Well, that can't be right---I grepped the kernel source for "initctl"
and got no results. Besides, if you're running a shell, then init has
already had time to open and close whatever file descriptors it wants
to. What I want to know is whether init has any open file descriptors,
at, say, the moment its main() starts executing.
________________________________
Brian Bi


On Tue, Dec 25, 2012 at 6:17 AM, Borislav Petkov <bp@alien8.de> wrote:
> On Tue, Dec 25, 2012 at 02:38:09AM -0500, bbi5291 wrote:
>> When the init process is created on system startup, does it have any
>> open file descriptors? If so, where do they point?
>
> $ tree /proc/1/fd
> /proc/1/fd
> └── 10 -> /run/initctl
>
> 0 directories, 1 file
>
> --
> Regards/Gruss,
>     Boris.
>
> Sent from a fat crate under my desk. Formatting is fine.
> --

^ permalink raw reply

* Re: [PATCH 20/29] batman-adv: rename random32() to prandom_u32()
From: Antonio Quartulli @ 2012-12-25 11:30 UTC (permalink / raw)
  To: Akinobu Mita
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
	b.a.t.m.a.n-ZwoEplunGu2X36UT3dwllkB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Simon Wunderlich,
	akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b, Marek Lindner,
	David S. Miller
In-Reply-To: <1356315256-6572-21-git-send-email-akinobu.mita-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

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

On Mon, Dec 24, 2012 at 11:14:07AM +0900, Akinobu Mita wrote:
> Use more preferable function name which implies using a pseudo-random
> number generator.
> 
> Signed-off-by: Akinobu Mita <akinobu.mita-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> Cc: Marek Lindner <lindner_marek-LWAfsSFWpa4@public.gmane.org>
> Cc: Simon Wunderlich <siwu-MaAgPAbsBIVS8oHt8HbXEIQuADTiUCJX@public.gmane.org>
> Cc: Antonio Quartulli <ordex-GaUfNO9RBHfsrOwW+9ziJQ@public.gmane.org>
> Cc: b.a.t.m.a.n-ZwoEplunGu2X36UT3dwllkB+6BGkLq7r@public.gmane.org
> Cc: "David S. Miller" <davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
> Cc: netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org

Acked-by: Antonio Quartulli <ordex-GaUfNO9RBHfsrOwW+9ziJQ@public.gmane.org>

-- 
Antonio Quartulli

..each of us alone is worth nothing..
Ernesto "Che" Guevara

[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: [B.A.T.M.A.N.] [PATCH 20/29] batman-adv: rename random32() to prandom_u32()
From: Antonio Quartulli @ 2012-12-25 11:30 UTC (permalink / raw)
  To: Akinobu Mita
  Cc: netdev, b.a.t.m.a.n, linux-kernel, Simon Wunderlich, akpm,
	Marek Lindner, David S. Miller
In-Reply-To: <1356315256-6572-21-git-send-email-akinobu.mita@gmail.com>

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

On Mon, Dec 24, 2012 at 11:14:07AM +0900, Akinobu Mita wrote:
> Use more preferable function name which implies using a pseudo-random
> number generator.
> 
> Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
> Cc: Marek Lindner <lindner_marek@yahoo.de>
> Cc: Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
> Cc: Antonio Quartulli <ordex@autistici.org>
> Cc: b.a.t.m.a.n@lists.open-mesh.org
> Cc: "David S. Miller" <davem@davemloft.net>
> Cc: netdev@vger.kernel.org

Acked-by: Antonio Quartulli <ordex@autistici.org>

-- 
Antonio Quartulli

..each of us alone is worth nothing..
Ernesto "Che" Guevara

[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: [PATCH 19/29] batman-adv: fix random jitter calculation
From: Antonio Quartulli @ 2012-12-25 11:26 UTC (permalink / raw)
  To: Akinobu Mita
  Cc: linux-kernel, akpm, Marek Lindner, Simon Wunderlich, b.a.t.m.a.n,
	David S. Miller, netdev
In-Reply-To: <1356315256-6572-20-git-send-email-akinobu.mita@gmail.com>

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

On Mon, Dec 24, 2012 at 11:14:06AM +0900, Akinobu Mita wrote:
> batadv_iv_ogm_emit_send_time() attempts to calculates a random integer
> in the range of 'orig_interval +- BATADV_JITTER' by the below lines.
> 
>         msecs = atomic_read(&bat_priv->orig_interval) - BATADV_JITTER;
>         msecs += (random32() % 2 * BATADV_JITTER);
> 
> But it actually gets 'orig_interval' or 'orig_interval - BATADV_JITTER'
> because '%' and '*' have same precedence and associativity is
> left-to-right.
> 
> This adds the parentheses at the appropriate position so that it matches
> original intension.
> 
> Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
> Cc: Marek Lindner <lindner_marek@yahoo.de>
> Cc: Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
> Cc: Antonio Quartulli <ordex@autistici.org>
> Cc: b.a.t.m.a.n@lists.open-mesh.org
> Cc: "David S. Miller" <davem@davemloft.net>
> Cc: netdev@vger.kernel.org
> ---

Acked-by: Antonio Quartulli <ordex@autistici.org>

But I would suggest to apply this change to net, since it is a fix.

Cheers,


-- 
Antonio Quartulli

..each of us alone is worth nothing..
Ernesto "Che" Guevara

[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: [PATCH 19/29] batman-adv: fix random jitter calculation
From: Antonio Quartulli @ 2012-12-25 11:26 UTC (permalink / raw)
  To: Akinobu Mita
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
	b.a.t.m.a.n-ZwoEplunGu2X36UT3dwllkB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Simon Wunderlich,
	akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b, Marek Lindner,
	David S. Miller
In-Reply-To: <1356315256-6572-20-git-send-email-akinobu.mita-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

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

On Mon, Dec 24, 2012 at 11:14:06AM +0900, Akinobu Mita wrote:
> batadv_iv_ogm_emit_send_time() attempts to calculates a random integer
> in the range of 'orig_interval +- BATADV_JITTER' by the below lines.
> 
>         msecs = atomic_read(&bat_priv->orig_interval) - BATADV_JITTER;
>         msecs += (random32() % 2 * BATADV_JITTER);
> 
> But it actually gets 'orig_interval' or 'orig_interval - BATADV_JITTER'
> because '%' and '*' have same precedence and associativity is
> left-to-right.
> 
> This adds the parentheses at the appropriate position so that it matches
> original intension.
> 
> Signed-off-by: Akinobu Mita <akinobu.mita-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> Cc: Marek Lindner <lindner_marek-LWAfsSFWpa4@public.gmane.org>
> Cc: Simon Wunderlich <siwu-MaAgPAbsBIVS8oHt8HbXEIQuADTiUCJX@public.gmane.org>
> Cc: Antonio Quartulli <ordex-GaUfNO9RBHfsrOwW+9ziJQ@public.gmane.org>
> Cc: b.a.t.m.a.n-ZwoEplunGu2X36UT3dwllkB+6BGkLq7r@public.gmane.org
> Cc: "David S. Miller" <davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>
> Cc: netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> ---

Acked-by: Antonio Quartulli <ordex-GaUfNO9RBHfsrOwW+9ziJQ@public.gmane.org>

But I would suggest to apply this change to net, since it is a fix.

Cheers,


-- 
Antonio Quartulli

..each of us alone is worth nothing..
Ernesto "Che" Guevara

[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: [B.A.T.M.A.N.] [PATCH 19/29] batman-adv: fix random jitter calculation
From: Antonio Quartulli @ 2012-12-25 11:26 UTC (permalink / raw)
  To: Akinobu Mita
  Cc: netdev, b.a.t.m.a.n, linux-kernel, Simon Wunderlich, akpm,
	Marek Lindner, David S. Miller
In-Reply-To: <1356315256-6572-20-git-send-email-akinobu.mita@gmail.com>

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

On Mon, Dec 24, 2012 at 11:14:06AM +0900, Akinobu Mita wrote:
> batadv_iv_ogm_emit_send_time() attempts to calculates a random integer
> in the range of 'orig_interval +- BATADV_JITTER' by the below lines.
> 
>         msecs = atomic_read(&bat_priv->orig_interval) - BATADV_JITTER;
>         msecs += (random32() % 2 * BATADV_JITTER);
> 
> But it actually gets 'orig_interval' or 'orig_interval - BATADV_JITTER'
> because '%' and '*' have same precedence and associativity is
> left-to-right.
> 
> This adds the parentheses at the appropriate position so that it matches
> original intension.
> 
> Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com>
> Cc: Marek Lindner <lindner_marek@yahoo.de>
> Cc: Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
> Cc: Antonio Quartulli <ordex@autistici.org>
> Cc: b.a.t.m.a.n@lists.open-mesh.org
> Cc: "David S. Miller" <davem@davemloft.net>
> Cc: netdev@vger.kernel.org
> ---

Acked-by: Antonio Quartulli <ordex@autistici.org>

But I would suggest to apply this change to net, since it is a fix.

Cheers,


-- 
Antonio Quartulli

..each of us alone is worth nothing..
Ernesto "Che" Guevara

[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* [PATCH 2/2] i2c-exynos5: add debugfs support for registers
From: Naveen Krishna Chatradhi @ 2012-12-25 11:25 UTC (permalink / raw)
  Cc: linux-arm-kernel, linux-samsung-soc, devicetree-discuss,
	linux-i2c, naveenkrishna.ch, kgene.kim, grant.likely, w.sang,
	linux-kernel, taeggyun.ko, balbi, thomas.abraham
In-Reply-To: <1356434755-13702-1-git-send-email-ch.naveen@samsung.com>

This patch replaces the dev_vdbg with debugfs function calls
in i2c-exynos5.c driver.

Signed-off-by: Naveen Krishna Chatradhi <ch.naveen@samsung.com>
---
Changes since v1:
The debugfs implementation is a suggestion by Felipe Balbi.
Its not tested, Implemented only for review purpose..

 drivers/i2c/busses/i2c-exynos5.c |  140 +++++++++++++++++++++++++++++---------
 1 file changed, 109 insertions(+), 31 deletions(-)

diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c
index 7614f60..2d2da22 100644
--- a/drivers/i2c/busses/i2c-exynos5.c
+++ b/drivers/i2c/busses/i2c-exynos5.c
@@ -12,6 +12,7 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/debugfs.h>
 
 #include <linux/i2c.h>
 #include <linux/init.h>
@@ -55,6 +56,7 @@ struct exynos5_i2c {
 
 	int			bus_num;
 	int			speed_mode;
+	struct dentry		*debugfs_root;
 };
 
 static const struct of_device_id exynos5_i2c_match[] = {
@@ -63,39 +65,112 @@ static const struct of_device_id exynos5_i2c_match[] = {
 };
 MODULE_DEVICE_TABLE(of, exynos5_i2c_match);
 
-/* TODO: Should go to debugfs */
-static inline void dump_i2c_register(struct exynos5_i2c *i2c)
+static int exynos5_i2c_regs_show(struct seq_file *s, void *data)
 {
-	dev_vdbg(i2c->dev, "Register dump(%d) :\n %x\n %x\n %x\n %x\n"
-		" %x\n %x\n %x\n %x\n %x\n"
-		" %x\n %x\n %x\n %x\n %x\n"
-		" %x\n %x\n %x\n %x\n %x\n"
-		" %x\n %x\n %x\n %x\n %x\n",
-		i2c->suspended,
-		readl(i2c->regs + HSI2C_CTL),
-		readl(i2c->regs + HSI2C_FIFO_CTL),
-		readl(i2c->regs + HSI2C_TRAILIG_CTL),
-		readl(i2c->regs + HSI2C_CLK_CTL),
-		readl(i2c->regs + HSI2C_CLK_SLOT),
-		readl(i2c->regs + HSI2C_INT_ENABLE),
-		readl(i2c->regs + HSI2C_INT_STATUS),
-		readl(i2c->regs + HSI2C_ERR_STATUS),
-		readl(i2c->regs + HSI2C_FIFO_STATUS),
-		readl(i2c->regs + HSI2C_TX_DATA),
-		readl(i2c->regs + HSI2C_RX_DATA),
-		readl(i2c->regs + HSI2C_CONF),
-		readl(i2c->regs + HSI2C_AUTO_CONF),
-		readl(i2c->regs + HSI2C_TIMEOUT),
-		readl(i2c->regs + HSI2C_MANUAL_CMD),
-		readl(i2c->regs + HSI2C_TRANS_STATUS),
-		readl(i2c->regs + HSI2C_TIMING_HS1),
-		readl(i2c->regs + HSI2C_TIMING_HS2),
-		readl(i2c->regs + HSI2C_TIMING_HS3),
-		readl(i2c->regs + HSI2C_TIMING_FS1),
-		readl(i2c->regs + HSI2C_TIMING_FS2),
-		readl(i2c->regs + HSI2C_TIMING_FS3),
-		readl(i2c->regs + HSI2C_TIMING_SLA),
+	struct exynos5_i2c *i2c = (struct exynos5_i2c *)s->private;
+
+	if (i2c->suspended) {
+		seq_printf(s, "i2c suspended, can't read registers\n");
+		return 0;
+	}
+
+	pm_runtime_get_sync(i2c->dev);
+
+	seq_printf(s, "CTL:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_CTL));
+	seq_printf(s, "FIFO_CTL:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_FIFO_CTL));
+	seq_printf(s, "TRAILIG_CTL:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TRAILIG_CTL));
+	seq_printf(s, "CLK_CTL:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_CLK_CTL));
+	seq_printf(s, "CLK_SLOT:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_CLK_SLOT));
+	seq_printf(s, "INT_ENABLE:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_INT_ENABLE));
+	seq_printf(s, "INT_STATUS:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_INT_STATUS));
+	seq_printf(s, "FIFO_STATUS:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_FIFO_STATUS));
+	seq_printf(s, "TX_DATA:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TX_DATA));
+	seq_printf(s, "RX_DATA:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_RX_DATA));
+	seq_printf(s, "CONF:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_CONF));
+	seq_printf(s, "AUTO_CONF:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_AUTO_CONF));
+	seq_printf(s, "TIMEOUT:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TIMEOUT));
+	seq_printf(s, "MANUAL_CMD:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_MANUAL_CMD));
+	seq_printf(s, "TRANS_STATUS:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TRANS_STATUS));
+	seq_printf(s, "TIMING_HS1:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TIMING_HS1));
+	seq_printf(s, "TIMING_HS2:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TIMING_HS2));
+	seq_printf(s, "TIMING_HS3:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TIMING_HS3));
+	seq_printf(s, "TIMING_FS1:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TIMING_FS1));
+	seq_printf(s, "TIMING_FS2:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TIMING_FS2));
+	seq_printf(s, "TIMING_FS3:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TIMING_FS3));
+	seq_printf(s, "TIMING_SLA:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TIMING_SLA));
+	seq_printf(s, "ADDR:\t\t0x%08x\n",
 		readl(i2c->regs + HSI2C_ADDR));
+
+	pm_runtime_mark_last_busy(i2c->dev);
+	pm_runtime_put_autosuspend(i2c->dev);
+
+	return 0;
+}
+
+static int exynos5_i2c_regs_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, exynos5_i2c_regs_show, inode->i_private);
+}
+
+static const struct file_operations exynos5_i2c_regs_fops = {
+	.open           = exynos5_i2c_regs_open,
+	.read           = seq_read,
+	.llseek         = seq_lseek,
+	.release        = single_release,
+};
+
+static void exynos5_i2c_debugfs(struct exynos5_i2c *i2c)
+{
+	if (i2c->debugfs_root)
+		debugfs_create_file("regs", S_IRUSR, i2c->debugfs_root,
+			i2c, &exynos5_i2c_regs_fops);
+}
+
+static void exynos5_i2c_add_debugfs(struct exynos5_i2c *i2c)
+{
+	struct dentry *root;
+
+	root = debugfs_create_dir(dev_name(i2c->dev), NULL);
+	if (IS_ERR(root))
+		/* Don't complain -- debugfs just isn't enabled */
+		return;
+	if (!root)
+		/* Complain -- debugfs is enabled, but it failed to
+		 * create the directory. */
+		goto err_root;
+
+	i2c->debugfs_root = root;
+	return;
+
+ err_root:
+	dev_err(i2c->dev, "failed to initialize debugfs\n");
+}
+
+void exynos5_i2c_remove_debugfs(struct exynos5_i2c *i2c)
+{
+	debugfs_remove_recursive(i2c->debugfs_root);
 }
 
 static inline void exynos5_i2c_stop(struct exynos5_i2c *i2c, int ret)
@@ -553,6 +628,8 @@ static int exynos5_i2c_probe(struct platform_device *pdev)
 	dev_info(&pdev->dev, "%s: Exynos5 HS-I2C adapter\n",
 		dev_name(&i2c->adap.dev));
 
+	exynos5_i2c_add_debugfs(i2c);
+	exynos5_i2c_debugfs(i2c);
 	clk_disable_unprepare(i2c->clk);
 	pm_runtime_mark_last_busy(i2c->dev);
 	pm_runtime_put_autosuspend(i2c->dev);
@@ -586,6 +663,7 @@ static int exynos5_i2c_remove(struct platform_device *pdev)
 
 	iounmap(i2c->regs);
 	platform_set_drvdata(pdev, NULL);
+	exynos5_i2c_remove_debugfs(i2c);
 
 	return 0;
 }
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH 1/2] i2c: exynos5: add High Speed I2C controller driver
From: Naveen Krishna Chatradhi @ 2012-12-25 11:25 UTC (permalink / raw)
  Cc: linux-arm-kernel, linux-samsung-soc, devicetree-discuss,
	linux-i2c, naveenkrishna.ch, kgene.kim, grant.likely, w.sang,
	linux-kernel, taeggyun.ko, balbi, thomas.abraham
In-Reply-To: <1354021236-28596-2-git-send-email-ch.naveen@samsung.com>

Adds support for High Speed I2C driver found in Exynos5 and later
SoCs from Samsung. This driver currently supports Auto mode.

Driver only supports Device Tree method.

Signed-off-by: Taekgyun Ko <taeggyun.ko@samsung.com>
Signed-off-by: Naveen Krishna Chatradhi <ch.naveen@samsung.com>
---
Changes since v1:
Fixed the comments from Felipe Balbi and Thomas Abraham.

 drivers/i2c/busses/Kconfig       |    7 +
 drivers/i2c/busses/Makefile      |    1 +
 drivers/i2c/busses/i2c-exynos5.c |  652 ++++++++++++++++++++++++++++++++++++++
 drivers/i2c/busses/i2c-exynos5.h |  102 ++++++
 4 files changed, 762 insertions(+)
 create mode 100644 drivers/i2c/busses/i2c-exynos5.c
 create mode 100644 drivers/i2c/busses/i2c-exynos5.h

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index bdca511..4caea76 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -618,6 +618,13 @@ config I2C_S3C2410
 	  Say Y here to include support for I2C controller in the
 	  Samsung SoCs.
 
+config I2C_EXYNOS5
+	tristate "Exynos5 high-speed I2C driver"
+	depends on ARCH_EXYNOS5
+	help
+	  Say Y here to include support for High-speed I2C controller in the
+	  Exynos5 based Samsung SoCs.
+
 config I2C_S6000
 	tristate "S6000 I2C support"
 	depends on XTENSA_VARIANT_S6000
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 6181f3f..4b1548c 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_I2C_PUV3)		+= i2c-puv3.o
 obj-$(CONFIG_I2C_PXA)		+= i2c-pxa.o
 obj-$(CONFIG_I2C_PXA_PCI)	+= i2c-pxa-pci.o
 obj-$(CONFIG_I2C_S3C2410)	+= i2c-s3c2410.o
+obj-$(CONFIG_I2C_EXYNOS5)	+= i2c-exynos5.o
 obj-$(CONFIG_I2C_S6000)		+= i2c-s6000.o
 obj-$(CONFIG_I2C_SH7760)	+= i2c-sh7760.o
 obj-$(CONFIG_I2C_SH_MOBILE)	+= i2c-sh_mobile.o
diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c
new file mode 100644
index 0000000..7614f60
--- /dev/null
+++ b/drivers/i2c/busses/i2c-exynos5.c
@@ -0,0 +1,652 @@
+/* linux/drivers/i2c/busses/i2c-exynos5.c
+ *
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ *
+ * High speed I2C controller driver
+ * for Exynos5 and later SoCs from Samsung.
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/of_i2c.h>
+
+#include "i2c-exynos5.h"
+
+#define EXYNOS5_I2C_TIMEOUT (msecs_to_jiffies(1000))
+
+/* timeout for pm runtime autosuspend */
+#define EXYNOS5_I2C_PM_TIMEOUT		1000	/* ms */
+
+struct exynos5_i2c {
+	struct i2c_adapter	adap;
+	unsigned int		suspended:1;
+
+	struct i2c_msg		*msg;
+	unsigned int		msg_idx;
+	struct completion	msg_complete;
+	unsigned int		msg_ptr;
+
+	unsigned int		irq;
+
+	void __iomem		*regs;
+	struct clk		*clk;
+	struct device		*dev;
+	int			gpios[2];
+
+	int			bus_num;
+	int			speed_mode;
+};
+
+static const struct of_device_id exynos5_i2c_match[] = {
+	{ .compatible = "samsung,exynos5-hsi2c" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, exynos5_i2c_match);
+
+/* TODO: Should go to debugfs */
+static inline void dump_i2c_register(struct exynos5_i2c *i2c)
+{
+	dev_vdbg(i2c->dev, "Register dump(%d) :\n %x\n %x\n %x\n %x\n"
+		" %x\n %x\n %x\n %x\n %x\n"
+		" %x\n %x\n %x\n %x\n %x\n"
+		" %x\n %x\n %x\n %x\n %x\n"
+		" %x\n %x\n %x\n %x\n %x\n",
+		i2c->suspended,
+		readl(i2c->regs + HSI2C_CTL),
+		readl(i2c->regs + HSI2C_FIFO_CTL),
+		readl(i2c->regs + HSI2C_TRAILIG_CTL),
+		readl(i2c->regs + HSI2C_CLK_CTL),
+		readl(i2c->regs + HSI2C_CLK_SLOT),
+		readl(i2c->regs + HSI2C_INT_ENABLE),
+		readl(i2c->regs + HSI2C_INT_STATUS),
+		readl(i2c->regs + HSI2C_ERR_STATUS),
+		readl(i2c->regs + HSI2C_FIFO_STATUS),
+		readl(i2c->regs + HSI2C_TX_DATA),
+		readl(i2c->regs + HSI2C_RX_DATA),
+		readl(i2c->regs + HSI2C_CONF),
+		readl(i2c->regs + HSI2C_AUTO_CONF),
+		readl(i2c->regs + HSI2C_TIMEOUT),
+		readl(i2c->regs + HSI2C_MANUAL_CMD),
+		readl(i2c->regs + HSI2C_TRANS_STATUS),
+		readl(i2c->regs + HSI2C_TIMING_HS1),
+		readl(i2c->regs + HSI2C_TIMING_HS2),
+		readl(i2c->regs + HSI2C_TIMING_HS3),
+		readl(i2c->regs + HSI2C_TIMING_FS1),
+		readl(i2c->regs + HSI2C_TIMING_FS2),
+		readl(i2c->regs + HSI2C_TIMING_FS3),
+		readl(i2c->regs + HSI2C_TIMING_SLA),
+		readl(i2c->regs + HSI2C_ADDR));
+}
+
+static inline void exynos5_i2c_stop(struct exynos5_i2c *i2c, int ret)
+{
+	dev_dbg(i2c->dev, "STOP\n");
+
+	i2c->msg_idx++;
+	if (ret)
+		i2c->msg_idx = ret;
+
+	/* Disable interrrupts */
+	writel(0, i2c->regs + HSI2C_INT_ENABLE);
+	complete(&i2c->msg_complete);
+}
+
+static void exynos5_i2c_en_timeout(struct exynos5_i2c *i2c)
+{
+	unsigned long i2c_timeout = readl(i2c->regs + HSI2C_TIMEOUT);
+
+	/* Clear to enable Timeout */
+	i2c_timeout &= ~HSI2C_TIMEOUT_EN;
+	writel(i2c_timeout, i2c->regs + HSI2C_TIMEOUT);
+}
+
+static void exynos5_i2c_master_run(struct exynos5_i2c *i2c)
+{
+	/* Start data transfer in Master mode */
+	u32 i2c_auto_conf = readl(i2c->regs + HSI2C_AUTO_CONF);
+	i2c_auto_conf |= HSI2C_MASTER_RUN;
+	writel(i2c_auto_conf, i2c->regs + HSI2C_AUTO_CONF);
+}
+
+/* exynos5_i2c_set_bus
+ *
+ * get the i2c bus for a master/slave transaction
+ */
+static int exynos5_i2c_set_bus(struct exynos5_i2c *i2c, int master)
+{
+	unsigned long t_status;
+	int timeout = 400;
+
+	while (timeout-- > 0) {
+		t_status = readl(i2c->regs + HSI2C_TRANS_STATUS);
+
+		if (master) {
+			if (!(t_status & HSI2C_MASTER_BUSY))
+				return 0;
+		} else {
+			if (!(t_status & HSI2C_SLAVE_BUSY))
+				return 0;
+		}
+
+		msleep(20);
+	}
+
+	return -ETIMEDOUT;
+}
+
+/* exynos5_i2c_irq
+ *
+ * top level IRQ servicing routine
+ */
+static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id)
+{
+	struct exynos5_i2c *i2c = dev_id;
+	unsigned long t_stat;
+	unsigned char byte;
+
+	t_stat = readl(i2c->regs + HSI2C_TRANS_STATUS);
+
+	if (t_stat & HSI2C_TRANS_ABORT) {
+		/* deal with arbitration loss */
+		dev_err(i2c->dev, "deal with arbitration loss\n");
+		goto out;
+	}
+	if (i2c->msg->flags & I2C_M_RD) {
+		if (t_stat & HSI2C_TRANS_DONE) {
+			dev_dbg(i2c->dev, "Device found.");
+			while ((readl(i2c->regs + HSI2C_FIFO_STATUS) &
+					HSI2C_RX_FIFO_EMPTY) == 0) {
+				byte = readl(i2c->regs + HSI2C_RX_DATA);
+				dev_dbg(i2c->dev, "read rx_data = %x", byte);
+				i2c->msg->buf[i2c->msg_ptr++] = byte;
+			}
+		} else if (t_stat & HSI2C_NO_DEV) {
+			dev_dbg(i2c->dev, "No device found.");
+			exynos5_i2c_stop(i2c, -ENXIO);
+		} else if (t_stat & HSI2C_NO_DEV_ACK &&
+				!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
+			dev_dbg(i2c->dev, "No device Ack.");
+			exynos5_i2c_stop(i2c, -ENXIO);
+		}
+	} else {
+		byte = i2c->msg->buf[i2c->msg_ptr++];
+		dev_dbg(i2c->dev, "write tx_data = %x ", byte);
+		writel(byte, i2c->regs + HSI2C_TX_DATA);
+	}
+
+	if (i2c->msg_ptr >= i2c->msg->len)
+		exynos5_i2c_stop(i2c, 0);
+
+ out:
+	/* Set those bits to clear them */
+	writel(readl(i2c->regs + HSI2C_INT_STATUS),
+				i2c->regs + HSI2C_INT_STATUS);
+
+	return IRQ_HANDLED;
+}
+
+static void exynos5_i2c_message_start(struct exynos5_i2c *i2c,
+				      struct i2c_msg *msgs)
+{
+	unsigned long usi_ctl = HSI2C_FUNC_MODE_I2C | HSI2C_MASTER;
+	unsigned long i2c_auto_conf;
+	unsigned long i2c_addr = ((msgs->addr & 0x7f) << 10);
+	unsigned long usi_int_en = 0;
+
+	exynos5_i2c_en_timeout(i2c);
+
+	if (msgs->flags & I2C_M_RD) {
+		usi_ctl &= ~HSI2C_TXCHON;
+		usi_ctl |= HSI2C_RXCHON;
+
+		i2c_auto_conf |= HSI2C_READ_WRITE;
+
+		usi_int_en |= (HSI2C_INT_RX_ALMOSTFULL_EN |
+			HSI2C_INT_TRAILING_EN);
+	} else {
+		usi_ctl &= ~HSI2C_RXCHON;
+		usi_ctl |= HSI2C_TXCHON;
+
+		i2c_auto_conf &= ~HSI2C_READ_WRITE;
+
+		usi_int_en |= HSI2C_INT_TX_ALMOSTEMPTY_EN;
+	}
+
+	writel(i2c_addr, i2c->regs + HSI2C_ADDR);
+	writel(usi_ctl, i2c->regs + HSI2C_CTL);
+
+	i2c_auto_conf |= i2c->msg->len;
+	i2c_auto_conf |= HSI2C_STOP_AFTER_TRANS;
+	writel(i2c_auto_conf, i2c->regs + HSI2C_AUTO_CONF);
+
+	exynos5_i2c_master_run(i2c);
+
+	/* Enable appropriate interrupts */
+	writel(usi_int_en, i2c->regs + HSI2C_INT_ENABLE);
+}
+
+static int exynos5_i2c_doxfer(struct exynos5_i2c *i2c, struct i2c_msg *msgs)
+{
+	unsigned long timeout;
+
+	if (i2c->suspended) {
+		dev_err(i2c->dev, "HS-I2C is not initialzed.\n");
+		return -EIO;
+	}
+
+	if (exynos5_i2c_set_bus(i2c, 1)) {
+		dev_err(i2c->dev, "cannot get bus, Master busy.\n");
+		return -EAGAIN;
+	}
+
+	i2c->msg = msgs;
+	i2c->msg_ptr = 0;
+	i2c->msg_idx = 0;
+
+	INIT_COMPLETION(i2c->msg_complete);
+
+	exynos5_i2c_message_start(i2c, msgs);
+
+	timeout = wait_for_completion_timeout(&i2c->msg_complete,
+		EXYNOS5_I2C_TIMEOUT);
+
+	if (timeout == 0)
+		dev_dbg(i2c->dev, "timeout\n");
+	else if (i2c->msg_idx != msgs->len)
+		dev_dbg(i2c->dev, "incomplete xfer (%d)\n", i2c->msg_idx);
+
+	return i2c->msg_idx;
+}
+
+static int exynos5_i2c_xfer(struct i2c_adapter *adap,
+			struct i2c_msg *msgs, int num)
+{
+	struct exynos5_i2c *i2c = (struct exynos5_i2c *)adap->algo_data;
+	int retry, i;
+	int ret;
+
+	ret = pm_runtime_get_sync(i2c->dev);
+	if (IS_ERR_VALUE(ret))
+		goto out;
+
+	clk_prepare_enable(i2c->clk);
+
+	for (retry = 0; retry < adap->retries; retry++) {
+		for (i = 0; i < num; i++) {
+			ret = exynos5_i2c_doxfer(i2c, msgs);
+			msgs++;
+
+			if (ret == -EAGAIN)
+				break;
+		}
+		if (i == num) {
+			clk_disable_unprepare(i2c->clk);
+			ret = i2c->msg_idx;
+			goto out;
+		}
+
+		dev_dbg(i2c->dev, "retrying transfer (%d)\n", retry);
+
+		udelay(100);
+	}
+
+	ret = -EREMOTEIO;
+	clk_disable_unprepare(i2c->clk);
+ out:
+	pm_runtime_mark_last_busy(i2c->dev);
+	pm_runtime_put_autosuspend(i2c->dev);
+	return ret;
+}
+
+static u32 exynos5_i2c_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm exynos5_i2c_algorithm = {
+	.master_xfer		= exynos5_i2c_xfer,
+	.functionality		= exynos5_i2c_func,
+};
+
+static int exynos5_i2c_set_timing(struct exynos5_i2c *i2c, int speed_mode)
+{
+	unsigned long i2c_timing_s1;
+	unsigned long i2c_timing_s2;
+	unsigned long i2c_timing_s3;
+	unsigned long i2c_timing_sla;
+	unsigned int op_clk;
+	unsigned int clkin = clk_get_rate(i2c->clk);
+	unsigned int n_clkdiv;
+	unsigned int t_start_su, t_start_hd;
+	unsigned int t_stop_su;
+	unsigned int t_data_su, t_data_hd;
+	unsigned int t_scl_l, t_scl_h;
+	unsigned int t_sr_release;
+	unsigned int t_ftl_cycle;
+	unsigned int i = 0, utemp0 = 0, utemp1 = 0, utemp2 = 0;
+
+	if (speed_mode == HSI2C_HIGH_SPD)
+		op_clk = HSI2C_HS_TX_CLOCK;
+	else
+		op_clk = HSI2C_FS_TX_CLOCK;
+
+	/* FPCLK / FI2C =
+	 * (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) + 8 + 2 * FLT_CYCLE
+	 * uTemp0 = (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2)
+	 * uTemp1 = (TSCLK_L + TSCLK_H + 2)
+	 * uTemp2 = TSCLK_L + TSCLK_H
+	*/
+	t_ftl_cycle = (readl(i2c->regs + HSI2C_CONF) >> 16) & 0x7;
+	utemp0 = (clkin / op_clk) - 8 - 2 * t_ftl_cycle;
+
+	/* CLK_DIV max is 256 */
+	for (i = 0; i < 256; i++) {
+		utemp1 = utemp0 / (i + 1);
+		/* SCLK_L/H max is 255
+		 * so sclk_l + sclk_h has max value of 510
+		 */
+		if (utemp1 < 511) {
+			utemp2 = utemp1 - 2;
+			break;
+		}
+	}
+
+	n_clkdiv = i;
+	t_scl_l = utemp2 / 2;
+	t_scl_h = utemp2 / 2;
+	t_start_su = t_scl_l;
+	t_start_hd = t_scl_l;
+	t_stop_su = t_scl_l;
+	t_data_su = t_scl_l / 2;
+	t_data_hd = t_scl_l / 2;
+	t_sr_release = utemp2;
+
+	i2c_timing_s1 = t_start_su << 24 | t_start_hd << 16 | t_stop_su << 8;
+	i2c_timing_s2 = t_data_su << 24 | t_scl_l << 8 | t_scl_h << 0;
+	i2c_timing_s3 = n_clkdiv << 16 | t_sr_release << 0;
+	i2c_timing_sla = t_data_hd << 0;
+
+	dev_dbg(i2c->dev, "tSTART_SU: %X, tSTART_HD: %X, tSTOP_SU: %X\n",
+		t_start_su, t_start_hd, t_stop_su);
+	dev_dbg(i2c->dev, "tDATA_SU: %X, tSCL_L: %X, tSCL_H: %X\n",
+		t_data_su, t_scl_l, t_scl_h);
+	dev_dbg(i2c->dev, "nClkDiv: %X, tSR_RELEASE: %X\n",
+		n_clkdiv, t_sr_release);
+	dev_dbg(i2c->dev, "tDATA_HD: %X\n", t_data_hd);
+
+	if (speed_mode == HSI2C_HIGH_SPD) {
+		writel(i2c_timing_s1, i2c->regs + HSI2C_TIMING_HS1);
+		writel(i2c_timing_s2, i2c->regs + HSI2C_TIMING_HS2);
+		writel(i2c_timing_s3, i2c->regs + HSI2C_TIMING_HS3);
+	} else {
+		writel(i2c_timing_s1, i2c->regs + HSI2C_TIMING_FS1);
+		writel(i2c_timing_s2, i2c->regs + HSI2C_TIMING_FS2);
+		writel(i2c_timing_s3, i2c->regs + HSI2C_TIMING_FS3);
+	}
+	writel(i2c_timing_sla, i2c->regs + HSI2C_TIMING_SLA);
+
+	return 0;
+}
+
+/**
+ * Parse a list of GPIOs from a node property and request each one
+ *
+ * @param i2c		i2c driver data
+ * @return 0 on success, -ve on error, in which case no GPIOs requested
+ */
+static int exynos5_i2c_parse_dt_gpio(struct exynos5_i2c *i2c)
+{
+	int idx, gpio, ret;
+
+	for (idx = 0; idx < 2; idx++) {
+		gpio = of_get_gpio(i2c->dev->of_node, idx);
+		if (!gpio_is_valid(gpio)) {
+			dev_err(i2c->dev, "invalid gpio[%d]: %d\n", idx, gpio);
+			return -EINVAL;
+		}
+		i2c->gpios[idx] = gpio;
+
+		ret = devm_gpio_request(i2c->dev, gpio, "i2c-bus");
+		if (ret) {
+			dev_err(i2c->dev, "gpio [%d] request failed\n", gpio);
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+static void exynos5_i2c_init(struct exynos5_i2c *i2c)
+{
+	unsigned long usi_trailing_ctl = HSI2C_TRAILING_COUNT;
+	unsigned long i2c_conf = HSI2C_AUTO_MODE;
+	unsigned long usi_fifo_ctl;
+
+	writel(usi_trailing_ctl, i2c->regs + HSI2C_TRAILIG_CTL);
+
+	/* Set default trigger level for TXFIFO and RXFIFO */
+	usi_fifo_ctl = HSI2C_TXFIFO_TRIGGER_LEVEL | HSI2C_RXFIFO_TRIGGER_LEVEL;
+
+	/* Enable RXFIFO and TXFIFO */
+	usi_fifo_ctl = HSI2C_RXFIFO_EN | HSI2C_TXFIFO_EN;
+	writel(usi_fifo_ctl, i2c->regs + HSI2C_FIFO_CTL);
+
+	if (i2c->speed_mode == HSI2C_HIGH_SPD) {
+		exynos5_i2c_set_timing(i2c, HSI2C_HIGH_SPD);
+		/* Configure I2C controller in High speed mode */
+		i2c_conf |= HSI2C_HS_MODE;
+		writel(i2c_conf, i2c->regs + HSI2C_CONF);
+	} else {
+		/* Configure I2C controller in Fast speed mode */
+		exynos5_i2c_set_timing(i2c, HSI2C_FAST_SPD);
+	}
+}
+
+static int exynos5_i2c_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct exynos5_i2c *i2c;
+	int ret;
+
+	if (!np) {
+		dev_err(&pdev->dev, "no device node\n");
+		return -ENOENT;
+	}
+
+	i2c = devm_kzalloc(&pdev->dev, sizeof(struct exynos5_i2c), GFP_KERNEL);
+	if (!i2c) {
+		dev_err(&pdev->dev, "no memory for state\n");
+		return -ENOMEM;
+	}
+
+	i2c->bus_num = -1;
+	/* Mode of operation High/Fast Speed mode */
+	of_property_read_u32(np, "samsung,hs-mode", &i2c->speed_mode);
+
+	strlcpy(i2c->adap.name, "exynos5-hsi2c", sizeof(i2c->adap.name));
+	i2c->adap.owner   = THIS_MODULE;
+	i2c->adap.algo    = &exynos5_i2c_algorithm;
+	i2c->adap.retries = 2;
+	i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+
+	i2c->dev = &pdev->dev;
+	i2c->clk = clk_get(&pdev->dev, "hsi2c");
+	if (IS_ERR(i2c->clk)) {
+		dev_err(&pdev->dev, "cannot get clock\n");
+		ret = -ENOENT;
+		goto err_noclk;
+	}
+
+	dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);
+
+	clk_prepare_enable(i2c->clk);
+
+	i2c->regs = of_iomap(np, 0);
+	if (!i2c->regs) {
+		dev_err(&pdev->dev, "cannot map HS-I2C IO\n");
+		ret = -ENXIO;
+		goto err_clk;
+	}
+
+	/* inititalise the gpio */
+	if (exynos5_i2c_parse_dt_gpio(i2c))
+		return -EINVAL;
+
+	i2c->irq = irq_of_parse_and_map(np, 0);
+	if (i2c->irq) {
+		ret = devm_request_irq(&pdev->dev, i2c->irq, exynos5_i2c_irq,
+				0, dev_name(&pdev->dev), i2c);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "cannot request HS-I2C IRQ %d\n",
+								i2c->irq);
+			goto err_iomap;
+		}
+	}
+
+	/* TODO: Use private lock to avoid race conditions as
+	 * mentioned in pm_runtime.txt
+	 */
+	pm_runtime_enable(i2c->dev);
+	pm_runtime_set_autosuspend_delay(i2c->dev, EXYNOS5_I2C_PM_TIMEOUT);
+	pm_runtime_use_autosuspend(i2c->dev);
+
+	ret = pm_runtime_get_sync(i2c->dev);
+	if (IS_ERR_VALUE(ret))
+		goto err_iomap;
+
+	exynos5_i2c_init(i2c);
+
+	i2c->adap.algo_data = i2c;
+	i2c->adap.dev.parent = &pdev->dev;
+	i2c->adap.nr = i2c->bus_num;
+	i2c->adap.dev.of_node = pdev->dev.of_node;
+
+	ret = i2c_add_numbered_adapter(&i2c->adap);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to add bus to i2c core\n");
+		goto err_pm;
+	}
+
+	init_completion(&i2c->msg_complete);
+	of_i2c_register_devices(&i2c->adap);
+	platform_set_drvdata(pdev, i2c);
+
+	dev_info(&pdev->dev, "%s: Exynos5 HS-I2C adapter\n",
+		dev_name(&i2c->adap.dev));
+
+	clk_disable_unprepare(i2c->clk);
+	pm_runtime_mark_last_busy(i2c->dev);
+	pm_runtime_put_autosuspend(i2c->dev);
+	return 0;
+
+ err_pm:
+	pm_runtime_put(i2c->dev);
+	pm_runtime_disable(&pdev->dev);
+ err_iomap:
+	iounmap(i2c->regs);
+ err_clk:
+	clk_disable_unprepare(i2c->clk);
+ err_noclk:
+	return ret;
+}
+
+static int exynos5_i2c_remove(struct platform_device *pdev)
+{
+	struct exynos5_i2c *i2c = platform_get_drvdata(pdev);
+	int ret;
+
+	ret = pm_runtime_get_sync(&pdev->dev);
+	if (IS_ERR_VALUE(ret))
+		return ret;
+
+	clk_disable_unprepare(i2c->clk);
+	pm_runtime_put(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+
+	i2c_del_adapter(&i2c->adap);
+
+	iounmap(i2c->regs);
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int exynos5_i2c_suspend_noirq(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct exynos5_i2c *i2c = platform_get_drvdata(pdev);
+
+	i2c->suspended = 1;
+
+	return 0;
+}
+
+static int exynos5_i2c_resume_noirq(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct exynos5_i2c *i2c = platform_get_drvdata(pdev);
+
+	clk_prepare_enable(i2c->clk);
+	exynos5_i2c_init(i2c);
+	clk_disable_unprepare(i2c->clk);
+	i2c->suspended = 0;
+
+	return 0;
+}
+
+static const struct dev_pm_ops exynos5_i2c_dev_pm_ops = {
+	.suspend_noirq	= exynos5_i2c_suspend_noirq,
+	.resume_noirq	= exynos5_i2c_resume_noirq,
+};
+
+#define EXYNOS5_DEV_PM_OPS (&exynos5_i2c_dev_pm_ops)
+#else
+#define EXYNOS5_DEV_PM_OPS NULL
+#endif
+
+static struct platform_driver exynos5_i2c_driver = {
+	.probe		= exynos5_i2c_probe,
+	.remove		= exynos5_i2c_remove,
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "exynos5-hsi2c",
+		.pm	= EXYNOS5_DEV_PM_OPS,
+		.of_match_table = exynos5_i2c_match,
+	},
+};
+
+static int __init i2c_adap_exynos5_init(void)
+{
+	return platform_driver_register(&exynos5_i2c_driver);
+}
+subsys_initcall(i2c_adap_exynos5_init);
+
+static void __exit i2c_adap_exynos5_exit(void)
+{
+	platform_driver_unregister(&exynos5_i2c_driver);
+}
+module_exit(i2c_adap_exynos5_exit);
+
+MODULE_DESCRIPTION("Exynos5 HS-I2C Bus driver");
+MODULE_AUTHOR("Taekgyun Ko, <taeggyun.ko@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-exynos5.h b/drivers/i2c/busses/i2c-exynos5.h
new file mode 100644
index 0000000..4adcf88
--- /dev/null
+++ b/drivers/i2c/busses/i2c-exynos5.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ *
+ * Exynos5 series High Speed I2C Controller
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_ARCH_REGS_HS_IIC_H
+#define __ASM_ARCH_REGS_HS_IIC_H
+
+/* Register Map */
+#define HSI2C_CTL				0x00
+#define HSI2C_FIFO_CTL				0x04
+#define HSI2C_TRAILIG_CTL			0x08
+#define HSI2C_CLK_CTL				0x0C
+#define HSI2C_CLK_SLOT				0x10
+#define HSI2C_INT_ENABLE			0x20
+#define HSI2C_INT_STATUS			0x24
+#define HSI2C_ERR_STATUS			0x2C
+#define HSI2C_FIFO_STATUS			0x30
+#define HSI2C_TX_DATA				0x34
+#define HSI2C_RX_DATA				0x38
+#define HSI2C_CONF				0x40
+#define HSI2C_AUTO_CONF				0x44
+#define HSI2C_TIMEOUT				0x48
+#define HSI2C_MANUAL_CMD			0x4C
+#define HSI2C_TRANS_STATUS			0x50
+#define HSI2C_TIMING_HS1			0x54
+#define HSI2C_TIMING_HS2			0x58
+#define HSI2C_TIMING_HS3			0x5C
+#define HSI2C_TIMING_FS1			0x60
+#define HSI2C_TIMING_FS2			0x64
+#define HSI2C_TIMING_FS3			0x68
+#define HSI2C_TIMING_SLA			0x6C
+#define HSI2C_ADDR				0x70
+
+/* I2C_CTL Register */
+#define HSI2C_FUNC_MODE_I2C			(1u << 0)
+#define HSI2C_MASTER				(1u << 3)
+#define HSI2C_RXCHON				(1u << 6)
+#define HSI2C_TXCHON				(1u << 7)
+#define HSI2C_SW_RST				(1u << 31)
+
+/* I2C_FIFO_CTL Register */
+#define HSI2C_RXFIFO_EN				(1u << 0)
+#define HSI2C_TXFIFO_EN				(1u << 1)
+#define HSI2C_TXFIFO_TRIGGER_LEVEL		(0x20 << 16)
+#define HSI2C_RXFIFO_TRIGGER_LEVEL		(0x20 << 4)
+
+/* I2C_TRAILING_CTL Register */
+#define HSI2C_TRAILING_COUNT			(0xf)
+
+/* I2C_INT_EN Register */
+#define HSI2C_INT_TX_ALMOSTEMPTY_EN		(1u << 0)
+#define HSI2C_INT_RX_ALMOSTFULL_EN		(1u << 1)
+#define HSI2C_INT_TRAILING_EN			(1u << 6)
+#define HSI2C_INT_I2C_EN			(1u << 9)
+
+/* I2C_FIFO_STAT Register */
+#define HSI2C_RX_FIFO_EMPTY			(1u << 24)
+#define HSI2C_RX_FIFO_FULL			(1u << 23)
+#define HSI2C_RX_FIFO_LEVEL_MASK		(0x7 << 16)
+#define HSI2C_TX_FIFO_EMPTY			(1u << 8)
+#define HSI2C_TX_FIFO_FULL			(1u << 7)
+#define HSI2C_TX_FIFO_LEVEL_MASK		(0x7 << 7)
+#define HSI2C_FIFO_EMPTY			(0x1000100)
+
+/* I2C_CONF Register */
+#define HSI2C_AUTO_MODE				(1u << 31)
+#define HSI2C_10BIT_ADDR_MODE			(1u << 30)
+#define HSI2C_HS_MODE				(1u << 29)
+
+/* I2C_AUTO_CONF Register */
+#define HSI2C_READ_WRITE			(1u << 16)
+#define HSI2C_STOP_AFTER_TRANS			(1u << 17)
+#define HSI2C_MASTER_RUN			(1u << 31)
+
+/* I2C_TIMEOUT Register */
+#define HSI2C_TIMEOUT_EN			(1u << 31)
+
+/* I2C_TRANS_STATUS register */
+#define HSI2C_MASTER_BUSY			(1u << 17)
+#define HSI2C_SLAVE_BUSY			(1u << 16)
+#define HSI2C_NO_DEV				(1u << 3)
+#define HSI2C_NO_DEV_ACK			(1u << 2)
+#define HSI2C_TRANS_ABORT			(1u << 1)
+#define HSI2C_TRANS_DONE			(1u << 0)
+
+/* Although exynos5 supports max HS-IIC speed of 3.4Mhz,
+ * but currently we are facing booting issues beyond 1Mhz
+ * So limiting HS-IIC bus speed to 1Mhz
+ */
+#define HSI2C_HS_TX_CLOCK	1000000
+#define HSI2C_FS_TX_CLOCK	400000
+
+#define HSI2C_FAST_SPD 0
+#define HSI2C_HIGH_SPD 1
+
+#endif /* __ASM_ARCH_REGS_HS_IIC_H */
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH 2/2] i2c-exynos5: add debugfs support for registers
From: Naveen Krishna Chatradhi @ 2012-12-25 11:25 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1356434755-13702-1-git-send-email-ch.naveen@samsung.com>

This patch replaces the dev_vdbg with debugfs function calls
in i2c-exynos5.c driver.

Signed-off-by: Naveen Krishna Chatradhi <ch.naveen@samsung.com>
---
Changes since v1:
The debugfs implementation is a suggestion by Felipe Balbi.
Its not tested, Implemented only for review purpose..

 drivers/i2c/busses/i2c-exynos5.c |  140 +++++++++++++++++++++++++++++---------
 1 file changed, 109 insertions(+), 31 deletions(-)

diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c
index 7614f60..2d2da22 100644
--- a/drivers/i2c/busses/i2c-exynos5.c
+++ b/drivers/i2c/busses/i2c-exynos5.c
@@ -12,6 +12,7 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/debugfs.h>
 
 #include <linux/i2c.h>
 #include <linux/init.h>
@@ -55,6 +56,7 @@ struct exynos5_i2c {
 
 	int			bus_num;
 	int			speed_mode;
+	struct dentry		*debugfs_root;
 };
 
 static const struct of_device_id exynos5_i2c_match[] = {
@@ -63,39 +65,112 @@ static const struct of_device_id exynos5_i2c_match[] = {
 };
 MODULE_DEVICE_TABLE(of, exynos5_i2c_match);
 
-/* TODO: Should go to debugfs */
-static inline void dump_i2c_register(struct exynos5_i2c *i2c)
+static int exynos5_i2c_regs_show(struct seq_file *s, void *data)
 {
-	dev_vdbg(i2c->dev, "Register dump(%d) :\n %x\n %x\n %x\n %x\n"
-		" %x\n %x\n %x\n %x\n %x\n"
-		" %x\n %x\n %x\n %x\n %x\n"
-		" %x\n %x\n %x\n %x\n %x\n"
-		" %x\n %x\n %x\n %x\n %x\n",
-		i2c->suspended,
-		readl(i2c->regs + HSI2C_CTL),
-		readl(i2c->regs + HSI2C_FIFO_CTL),
-		readl(i2c->regs + HSI2C_TRAILIG_CTL),
-		readl(i2c->regs + HSI2C_CLK_CTL),
-		readl(i2c->regs + HSI2C_CLK_SLOT),
-		readl(i2c->regs + HSI2C_INT_ENABLE),
-		readl(i2c->regs + HSI2C_INT_STATUS),
-		readl(i2c->regs + HSI2C_ERR_STATUS),
-		readl(i2c->regs + HSI2C_FIFO_STATUS),
-		readl(i2c->regs + HSI2C_TX_DATA),
-		readl(i2c->regs + HSI2C_RX_DATA),
-		readl(i2c->regs + HSI2C_CONF),
-		readl(i2c->regs + HSI2C_AUTO_CONF),
-		readl(i2c->regs + HSI2C_TIMEOUT),
-		readl(i2c->regs + HSI2C_MANUAL_CMD),
-		readl(i2c->regs + HSI2C_TRANS_STATUS),
-		readl(i2c->regs + HSI2C_TIMING_HS1),
-		readl(i2c->regs + HSI2C_TIMING_HS2),
-		readl(i2c->regs + HSI2C_TIMING_HS3),
-		readl(i2c->regs + HSI2C_TIMING_FS1),
-		readl(i2c->regs + HSI2C_TIMING_FS2),
-		readl(i2c->regs + HSI2C_TIMING_FS3),
-		readl(i2c->regs + HSI2C_TIMING_SLA),
+	struct exynos5_i2c *i2c = (struct exynos5_i2c *)s->private;
+
+	if (i2c->suspended) {
+		seq_printf(s, "i2c suspended, can't read registers\n");
+		return 0;
+	}
+
+	pm_runtime_get_sync(i2c->dev);
+
+	seq_printf(s, "CTL:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_CTL));
+	seq_printf(s, "FIFO_CTL:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_FIFO_CTL));
+	seq_printf(s, "TRAILIG_CTL:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TRAILIG_CTL));
+	seq_printf(s, "CLK_CTL:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_CLK_CTL));
+	seq_printf(s, "CLK_SLOT:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_CLK_SLOT));
+	seq_printf(s, "INT_ENABLE:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_INT_ENABLE));
+	seq_printf(s, "INT_STATUS:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_INT_STATUS));
+	seq_printf(s, "FIFO_STATUS:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_FIFO_STATUS));
+	seq_printf(s, "TX_DATA:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TX_DATA));
+	seq_printf(s, "RX_DATA:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_RX_DATA));
+	seq_printf(s, "CONF:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_CONF));
+	seq_printf(s, "AUTO_CONF:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_AUTO_CONF));
+	seq_printf(s, "TIMEOUT:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TIMEOUT));
+	seq_printf(s, "MANUAL_CMD:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_MANUAL_CMD));
+	seq_printf(s, "TRANS_STATUS:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TRANS_STATUS));
+	seq_printf(s, "TIMING_HS1:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TIMING_HS1));
+	seq_printf(s, "TIMING_HS2:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TIMING_HS2));
+	seq_printf(s, "TIMING_HS3:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TIMING_HS3));
+	seq_printf(s, "TIMING_FS1:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TIMING_FS1));
+	seq_printf(s, "TIMING_FS2:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TIMING_FS2));
+	seq_printf(s, "TIMING_FS3:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TIMING_FS3));
+	seq_printf(s, "TIMING_SLA:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TIMING_SLA));
+	seq_printf(s, "ADDR:\t\t0x%08x\n",
 		readl(i2c->regs + HSI2C_ADDR));
+
+	pm_runtime_mark_last_busy(i2c->dev);
+	pm_runtime_put_autosuspend(i2c->dev);
+
+	return 0;
+}
+
+static int exynos5_i2c_regs_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, exynos5_i2c_regs_show, inode->i_private);
+}
+
+static const struct file_operations exynos5_i2c_regs_fops = {
+	.open           = exynos5_i2c_regs_open,
+	.read           = seq_read,
+	.llseek         = seq_lseek,
+	.release        = single_release,
+};
+
+static void exynos5_i2c_debugfs(struct exynos5_i2c *i2c)
+{
+	if (i2c->debugfs_root)
+		debugfs_create_file("regs", S_IRUSR, i2c->debugfs_root,
+			i2c, &exynos5_i2c_regs_fops);
+}
+
+static void exynos5_i2c_add_debugfs(struct exynos5_i2c *i2c)
+{
+	struct dentry *root;
+
+	root = debugfs_create_dir(dev_name(i2c->dev), NULL);
+	if (IS_ERR(root))
+		/* Don't complain -- debugfs just isn't enabled */
+		return;
+	if (!root)
+		/* Complain -- debugfs is enabled, but it failed to
+		 * create the directory. */
+		goto err_root;
+
+	i2c->debugfs_root = root;
+	return;
+
+ err_root:
+	dev_err(i2c->dev, "failed to initialize debugfs\n");
+}
+
+void exynos5_i2c_remove_debugfs(struct exynos5_i2c *i2c)
+{
+	debugfs_remove_recursive(i2c->debugfs_root);
 }
 
 static inline void exynos5_i2c_stop(struct exynos5_i2c *i2c, int ret)
@@ -553,6 +628,8 @@ static int exynos5_i2c_probe(struct platform_device *pdev)
 	dev_info(&pdev->dev, "%s: Exynos5 HS-I2C adapter\n",
 		dev_name(&i2c->adap.dev));
 
+	exynos5_i2c_add_debugfs(i2c);
+	exynos5_i2c_debugfs(i2c);
 	clk_disable_unprepare(i2c->clk);
 	pm_runtime_mark_last_busy(i2c->dev);
 	pm_runtime_put_autosuspend(i2c->dev);
@@ -586,6 +663,7 @@ static int exynos5_i2c_remove(struct platform_device *pdev)
 
 	iounmap(i2c->regs);
 	platform_set_drvdata(pdev, NULL);
+	exynos5_i2c_remove_debugfs(i2c);
 
 	return 0;
 }
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 2/2] i2c-exynos5: add debugfs support for registers
From: Naveen Krishna Chatradhi @ 2012-12-25 11:25 UTC (permalink / raw)
  Cc: kgene.kim-Sze3O3UU22JBDgjK7y7TUQ,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	w.sang-bIcnvbaLZ9MEGnE8C9+IrQ, balbi-l0cyMroinI0,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	taeggyun.ko-Sze3O3UU22JBDgjK7y7TUQ,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	naveenkrishna.ch-Re5JQEeQqe8AvxtiuMwx3w,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <1356434755-13702-1-git-send-email-ch.naveen-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>

This patch replaces the dev_vdbg with debugfs function calls
in i2c-exynos5.c driver.

Signed-off-by: Naveen Krishna Chatradhi <ch.naveen-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
---
Changes since v1:
The debugfs implementation is a suggestion by Felipe Balbi.
Its not tested, Implemented only for review purpose..

 drivers/i2c/busses/i2c-exynos5.c |  140 +++++++++++++++++++++++++++++---------
 1 file changed, 109 insertions(+), 31 deletions(-)

diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c
index 7614f60..2d2da22 100644
--- a/drivers/i2c/busses/i2c-exynos5.c
+++ b/drivers/i2c/busses/i2c-exynos5.c
@@ -12,6 +12,7 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/debugfs.h>
 
 #include <linux/i2c.h>
 #include <linux/init.h>
@@ -55,6 +56,7 @@ struct exynos5_i2c {
 
 	int			bus_num;
 	int			speed_mode;
+	struct dentry		*debugfs_root;
 };
 
 static const struct of_device_id exynos5_i2c_match[] = {
@@ -63,39 +65,112 @@ static const struct of_device_id exynos5_i2c_match[] = {
 };
 MODULE_DEVICE_TABLE(of, exynos5_i2c_match);
 
-/* TODO: Should go to debugfs */
-static inline void dump_i2c_register(struct exynos5_i2c *i2c)
+static int exynos5_i2c_regs_show(struct seq_file *s, void *data)
 {
-	dev_vdbg(i2c->dev, "Register dump(%d) :\n %x\n %x\n %x\n %x\n"
-		" %x\n %x\n %x\n %x\n %x\n"
-		" %x\n %x\n %x\n %x\n %x\n"
-		" %x\n %x\n %x\n %x\n %x\n"
-		" %x\n %x\n %x\n %x\n %x\n",
-		i2c->suspended,
-		readl(i2c->regs + HSI2C_CTL),
-		readl(i2c->regs + HSI2C_FIFO_CTL),
-		readl(i2c->regs + HSI2C_TRAILIG_CTL),
-		readl(i2c->regs + HSI2C_CLK_CTL),
-		readl(i2c->regs + HSI2C_CLK_SLOT),
-		readl(i2c->regs + HSI2C_INT_ENABLE),
-		readl(i2c->regs + HSI2C_INT_STATUS),
-		readl(i2c->regs + HSI2C_ERR_STATUS),
-		readl(i2c->regs + HSI2C_FIFO_STATUS),
-		readl(i2c->regs + HSI2C_TX_DATA),
-		readl(i2c->regs + HSI2C_RX_DATA),
-		readl(i2c->regs + HSI2C_CONF),
-		readl(i2c->regs + HSI2C_AUTO_CONF),
-		readl(i2c->regs + HSI2C_TIMEOUT),
-		readl(i2c->regs + HSI2C_MANUAL_CMD),
-		readl(i2c->regs + HSI2C_TRANS_STATUS),
-		readl(i2c->regs + HSI2C_TIMING_HS1),
-		readl(i2c->regs + HSI2C_TIMING_HS2),
-		readl(i2c->regs + HSI2C_TIMING_HS3),
-		readl(i2c->regs + HSI2C_TIMING_FS1),
-		readl(i2c->regs + HSI2C_TIMING_FS2),
-		readl(i2c->regs + HSI2C_TIMING_FS3),
-		readl(i2c->regs + HSI2C_TIMING_SLA),
+	struct exynos5_i2c *i2c = (struct exynos5_i2c *)s->private;
+
+	if (i2c->suspended) {
+		seq_printf(s, "i2c suspended, can't read registers\n");
+		return 0;
+	}
+
+	pm_runtime_get_sync(i2c->dev);
+
+	seq_printf(s, "CTL:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_CTL));
+	seq_printf(s, "FIFO_CTL:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_FIFO_CTL));
+	seq_printf(s, "TRAILIG_CTL:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TRAILIG_CTL));
+	seq_printf(s, "CLK_CTL:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_CLK_CTL));
+	seq_printf(s, "CLK_SLOT:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_CLK_SLOT));
+	seq_printf(s, "INT_ENABLE:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_INT_ENABLE));
+	seq_printf(s, "INT_STATUS:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_INT_STATUS));
+	seq_printf(s, "FIFO_STATUS:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_FIFO_STATUS));
+	seq_printf(s, "TX_DATA:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TX_DATA));
+	seq_printf(s, "RX_DATA:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_RX_DATA));
+	seq_printf(s, "CONF:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_CONF));
+	seq_printf(s, "AUTO_CONF:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_AUTO_CONF));
+	seq_printf(s, "TIMEOUT:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TIMEOUT));
+	seq_printf(s, "MANUAL_CMD:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_MANUAL_CMD));
+	seq_printf(s, "TRANS_STATUS:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TRANS_STATUS));
+	seq_printf(s, "TIMING_HS1:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TIMING_HS1));
+	seq_printf(s, "TIMING_HS2:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TIMING_HS2));
+	seq_printf(s, "TIMING_HS3:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TIMING_HS3));
+	seq_printf(s, "TIMING_FS1:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TIMING_FS1));
+	seq_printf(s, "TIMING_FS2:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TIMING_FS2));
+	seq_printf(s, "TIMING_FS3:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TIMING_FS3));
+	seq_printf(s, "TIMING_SLA:\t\t0x%08x\n",
+		readl(i2c->regs + HSI2C_TIMING_SLA));
+	seq_printf(s, "ADDR:\t\t0x%08x\n",
 		readl(i2c->regs + HSI2C_ADDR));
+
+	pm_runtime_mark_last_busy(i2c->dev);
+	pm_runtime_put_autosuspend(i2c->dev);
+
+	return 0;
+}
+
+static int exynos5_i2c_regs_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, exynos5_i2c_regs_show, inode->i_private);
+}
+
+static const struct file_operations exynos5_i2c_regs_fops = {
+	.open           = exynos5_i2c_regs_open,
+	.read           = seq_read,
+	.llseek         = seq_lseek,
+	.release        = single_release,
+};
+
+static void exynos5_i2c_debugfs(struct exynos5_i2c *i2c)
+{
+	if (i2c->debugfs_root)
+		debugfs_create_file("regs", S_IRUSR, i2c->debugfs_root,
+			i2c, &exynos5_i2c_regs_fops);
+}
+
+static void exynos5_i2c_add_debugfs(struct exynos5_i2c *i2c)
+{
+	struct dentry *root;
+
+	root = debugfs_create_dir(dev_name(i2c->dev), NULL);
+	if (IS_ERR(root))
+		/* Don't complain -- debugfs just isn't enabled */
+		return;
+	if (!root)
+		/* Complain -- debugfs is enabled, but it failed to
+		 * create the directory. */
+		goto err_root;
+
+	i2c->debugfs_root = root;
+	return;
+
+ err_root:
+	dev_err(i2c->dev, "failed to initialize debugfs\n");
+}
+
+void exynos5_i2c_remove_debugfs(struct exynos5_i2c *i2c)
+{
+	debugfs_remove_recursive(i2c->debugfs_root);
 }
 
 static inline void exynos5_i2c_stop(struct exynos5_i2c *i2c, int ret)
@@ -553,6 +628,8 @@ static int exynos5_i2c_probe(struct platform_device *pdev)
 	dev_info(&pdev->dev, "%s: Exynos5 HS-I2C adapter\n",
 		dev_name(&i2c->adap.dev));
 
+	exynos5_i2c_add_debugfs(i2c);
+	exynos5_i2c_debugfs(i2c);
 	clk_disable_unprepare(i2c->clk);
 	pm_runtime_mark_last_busy(i2c->dev);
 	pm_runtime_put_autosuspend(i2c->dev);
@@ -586,6 +663,7 @@ static int exynos5_i2c_remove(struct platform_device *pdev)
 
 	iounmap(i2c->regs);
 	platform_set_drvdata(pdev, NULL);
+	exynos5_i2c_remove_debugfs(i2c);
 
 	return 0;
 }
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 1/2] i2c: exynos5: add High Speed I2C controller driver
From: Naveen Krishna Chatradhi @ 2012-12-25 11:25 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1354021236-28596-2-git-send-email-ch.naveen@samsung.com>

Adds support for High Speed I2C driver found in Exynos5 and later
SoCs from Samsung. This driver currently supports Auto mode.

Driver only supports Device Tree method.

Signed-off-by: Taekgyun Ko <taeggyun.ko@samsung.com>
Signed-off-by: Naveen Krishna Chatradhi <ch.naveen@samsung.com>
---
Changes since v1:
Fixed the comments from Felipe Balbi and Thomas Abraham.

 drivers/i2c/busses/Kconfig       |    7 +
 drivers/i2c/busses/Makefile      |    1 +
 drivers/i2c/busses/i2c-exynos5.c |  652 ++++++++++++++++++++++++++++++++++++++
 drivers/i2c/busses/i2c-exynos5.h |  102 ++++++
 4 files changed, 762 insertions(+)
 create mode 100644 drivers/i2c/busses/i2c-exynos5.c
 create mode 100644 drivers/i2c/busses/i2c-exynos5.h

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index bdca511..4caea76 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -618,6 +618,13 @@ config I2C_S3C2410
 	  Say Y here to include support for I2C controller in the
 	  Samsung SoCs.
 
+config I2C_EXYNOS5
+	tristate "Exynos5 high-speed I2C driver"
+	depends on ARCH_EXYNOS5
+	help
+	  Say Y here to include support for High-speed I2C controller in the
+	  Exynos5 based Samsung SoCs.
+
 config I2C_S6000
 	tristate "S6000 I2C support"
 	depends on XTENSA_VARIANT_S6000
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 6181f3f..4b1548c 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_I2C_PUV3)		+= i2c-puv3.o
 obj-$(CONFIG_I2C_PXA)		+= i2c-pxa.o
 obj-$(CONFIG_I2C_PXA_PCI)	+= i2c-pxa-pci.o
 obj-$(CONFIG_I2C_S3C2410)	+= i2c-s3c2410.o
+obj-$(CONFIG_I2C_EXYNOS5)	+= i2c-exynos5.o
 obj-$(CONFIG_I2C_S6000)		+= i2c-s6000.o
 obj-$(CONFIG_I2C_SH7760)	+= i2c-sh7760.o
 obj-$(CONFIG_I2C_SH_MOBILE)	+= i2c-sh_mobile.o
diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c
new file mode 100644
index 0000000..7614f60
--- /dev/null
+++ b/drivers/i2c/busses/i2c-exynos5.c
@@ -0,0 +1,652 @@
+/* linux/drivers/i2c/busses/i2c-exynos5.c
+ *
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ *
+ * High speed I2C controller driver
+ * for Exynos5 and later SoCs from Samsung.
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/of_i2c.h>
+
+#include "i2c-exynos5.h"
+
+#define EXYNOS5_I2C_TIMEOUT (msecs_to_jiffies(1000))
+
+/* timeout for pm runtime autosuspend */
+#define EXYNOS5_I2C_PM_TIMEOUT		1000	/* ms */
+
+struct exynos5_i2c {
+	struct i2c_adapter	adap;
+	unsigned int		suspended:1;
+
+	struct i2c_msg		*msg;
+	unsigned int		msg_idx;
+	struct completion	msg_complete;
+	unsigned int		msg_ptr;
+
+	unsigned int		irq;
+
+	void __iomem		*regs;
+	struct clk		*clk;
+	struct device		*dev;
+	int			gpios[2];
+
+	int			bus_num;
+	int			speed_mode;
+};
+
+static const struct of_device_id exynos5_i2c_match[] = {
+	{ .compatible = "samsung,exynos5-hsi2c" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, exynos5_i2c_match);
+
+/* TODO: Should go to debugfs */
+static inline void dump_i2c_register(struct exynos5_i2c *i2c)
+{
+	dev_vdbg(i2c->dev, "Register dump(%d) :\n %x\n %x\n %x\n %x\n"
+		" %x\n %x\n %x\n %x\n %x\n"
+		" %x\n %x\n %x\n %x\n %x\n"
+		" %x\n %x\n %x\n %x\n %x\n"
+		" %x\n %x\n %x\n %x\n %x\n",
+		i2c->suspended,
+		readl(i2c->regs + HSI2C_CTL),
+		readl(i2c->regs + HSI2C_FIFO_CTL),
+		readl(i2c->regs + HSI2C_TRAILIG_CTL),
+		readl(i2c->regs + HSI2C_CLK_CTL),
+		readl(i2c->regs + HSI2C_CLK_SLOT),
+		readl(i2c->regs + HSI2C_INT_ENABLE),
+		readl(i2c->regs + HSI2C_INT_STATUS),
+		readl(i2c->regs + HSI2C_ERR_STATUS),
+		readl(i2c->regs + HSI2C_FIFO_STATUS),
+		readl(i2c->regs + HSI2C_TX_DATA),
+		readl(i2c->regs + HSI2C_RX_DATA),
+		readl(i2c->regs + HSI2C_CONF),
+		readl(i2c->regs + HSI2C_AUTO_CONF),
+		readl(i2c->regs + HSI2C_TIMEOUT),
+		readl(i2c->regs + HSI2C_MANUAL_CMD),
+		readl(i2c->regs + HSI2C_TRANS_STATUS),
+		readl(i2c->regs + HSI2C_TIMING_HS1),
+		readl(i2c->regs + HSI2C_TIMING_HS2),
+		readl(i2c->regs + HSI2C_TIMING_HS3),
+		readl(i2c->regs + HSI2C_TIMING_FS1),
+		readl(i2c->regs + HSI2C_TIMING_FS2),
+		readl(i2c->regs + HSI2C_TIMING_FS3),
+		readl(i2c->regs + HSI2C_TIMING_SLA),
+		readl(i2c->regs + HSI2C_ADDR));
+}
+
+static inline void exynos5_i2c_stop(struct exynos5_i2c *i2c, int ret)
+{
+	dev_dbg(i2c->dev, "STOP\n");
+
+	i2c->msg_idx++;
+	if (ret)
+		i2c->msg_idx = ret;
+
+	/* Disable interrrupts */
+	writel(0, i2c->regs + HSI2C_INT_ENABLE);
+	complete(&i2c->msg_complete);
+}
+
+static void exynos5_i2c_en_timeout(struct exynos5_i2c *i2c)
+{
+	unsigned long i2c_timeout = readl(i2c->regs + HSI2C_TIMEOUT);
+
+	/* Clear to enable Timeout */
+	i2c_timeout &= ~HSI2C_TIMEOUT_EN;
+	writel(i2c_timeout, i2c->regs + HSI2C_TIMEOUT);
+}
+
+static void exynos5_i2c_master_run(struct exynos5_i2c *i2c)
+{
+	/* Start data transfer in Master mode */
+	u32 i2c_auto_conf = readl(i2c->regs + HSI2C_AUTO_CONF);
+	i2c_auto_conf |= HSI2C_MASTER_RUN;
+	writel(i2c_auto_conf, i2c->regs + HSI2C_AUTO_CONF);
+}
+
+/* exynos5_i2c_set_bus
+ *
+ * get the i2c bus for a master/slave transaction
+ */
+static int exynos5_i2c_set_bus(struct exynos5_i2c *i2c, int master)
+{
+	unsigned long t_status;
+	int timeout = 400;
+
+	while (timeout-- > 0) {
+		t_status = readl(i2c->regs + HSI2C_TRANS_STATUS);
+
+		if (master) {
+			if (!(t_status & HSI2C_MASTER_BUSY))
+				return 0;
+		} else {
+			if (!(t_status & HSI2C_SLAVE_BUSY))
+				return 0;
+		}
+
+		msleep(20);
+	}
+
+	return -ETIMEDOUT;
+}
+
+/* exynos5_i2c_irq
+ *
+ * top level IRQ servicing routine
+ */
+static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id)
+{
+	struct exynos5_i2c *i2c = dev_id;
+	unsigned long t_stat;
+	unsigned char byte;
+
+	t_stat = readl(i2c->regs + HSI2C_TRANS_STATUS);
+
+	if (t_stat & HSI2C_TRANS_ABORT) {
+		/* deal with arbitration loss */
+		dev_err(i2c->dev, "deal with arbitration loss\n");
+		goto out;
+	}
+	if (i2c->msg->flags & I2C_M_RD) {
+		if (t_stat & HSI2C_TRANS_DONE) {
+			dev_dbg(i2c->dev, "Device found.");
+			while ((readl(i2c->regs + HSI2C_FIFO_STATUS) &
+					HSI2C_RX_FIFO_EMPTY) == 0) {
+				byte = readl(i2c->regs + HSI2C_RX_DATA);
+				dev_dbg(i2c->dev, "read rx_data = %x", byte);
+				i2c->msg->buf[i2c->msg_ptr++] = byte;
+			}
+		} else if (t_stat & HSI2C_NO_DEV) {
+			dev_dbg(i2c->dev, "No device found.");
+			exynos5_i2c_stop(i2c, -ENXIO);
+		} else if (t_stat & HSI2C_NO_DEV_ACK &&
+				!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
+			dev_dbg(i2c->dev, "No device Ack.");
+			exynos5_i2c_stop(i2c, -ENXIO);
+		}
+	} else {
+		byte = i2c->msg->buf[i2c->msg_ptr++];
+		dev_dbg(i2c->dev, "write tx_data = %x ", byte);
+		writel(byte, i2c->regs + HSI2C_TX_DATA);
+	}
+
+	if (i2c->msg_ptr >= i2c->msg->len)
+		exynos5_i2c_stop(i2c, 0);
+
+ out:
+	/* Set those bits to clear them */
+	writel(readl(i2c->regs + HSI2C_INT_STATUS),
+				i2c->regs + HSI2C_INT_STATUS);
+
+	return IRQ_HANDLED;
+}
+
+static void exynos5_i2c_message_start(struct exynos5_i2c *i2c,
+				      struct i2c_msg *msgs)
+{
+	unsigned long usi_ctl = HSI2C_FUNC_MODE_I2C | HSI2C_MASTER;
+	unsigned long i2c_auto_conf;
+	unsigned long i2c_addr = ((msgs->addr & 0x7f) << 10);
+	unsigned long usi_int_en = 0;
+
+	exynos5_i2c_en_timeout(i2c);
+
+	if (msgs->flags & I2C_M_RD) {
+		usi_ctl &= ~HSI2C_TXCHON;
+		usi_ctl |= HSI2C_RXCHON;
+
+		i2c_auto_conf |= HSI2C_READ_WRITE;
+
+		usi_int_en |= (HSI2C_INT_RX_ALMOSTFULL_EN |
+			HSI2C_INT_TRAILING_EN);
+	} else {
+		usi_ctl &= ~HSI2C_RXCHON;
+		usi_ctl |= HSI2C_TXCHON;
+
+		i2c_auto_conf &= ~HSI2C_READ_WRITE;
+
+		usi_int_en |= HSI2C_INT_TX_ALMOSTEMPTY_EN;
+	}
+
+	writel(i2c_addr, i2c->regs + HSI2C_ADDR);
+	writel(usi_ctl, i2c->regs + HSI2C_CTL);
+
+	i2c_auto_conf |= i2c->msg->len;
+	i2c_auto_conf |= HSI2C_STOP_AFTER_TRANS;
+	writel(i2c_auto_conf, i2c->regs + HSI2C_AUTO_CONF);
+
+	exynos5_i2c_master_run(i2c);
+
+	/* Enable appropriate interrupts */
+	writel(usi_int_en, i2c->regs + HSI2C_INT_ENABLE);
+}
+
+static int exynos5_i2c_doxfer(struct exynos5_i2c *i2c, struct i2c_msg *msgs)
+{
+	unsigned long timeout;
+
+	if (i2c->suspended) {
+		dev_err(i2c->dev, "HS-I2C is not initialzed.\n");
+		return -EIO;
+	}
+
+	if (exynos5_i2c_set_bus(i2c, 1)) {
+		dev_err(i2c->dev, "cannot get bus, Master busy.\n");
+		return -EAGAIN;
+	}
+
+	i2c->msg = msgs;
+	i2c->msg_ptr = 0;
+	i2c->msg_idx = 0;
+
+	INIT_COMPLETION(i2c->msg_complete);
+
+	exynos5_i2c_message_start(i2c, msgs);
+
+	timeout = wait_for_completion_timeout(&i2c->msg_complete,
+		EXYNOS5_I2C_TIMEOUT);
+
+	if (timeout == 0)
+		dev_dbg(i2c->dev, "timeout\n");
+	else if (i2c->msg_idx != msgs->len)
+		dev_dbg(i2c->dev, "incomplete xfer (%d)\n", i2c->msg_idx);
+
+	return i2c->msg_idx;
+}
+
+static int exynos5_i2c_xfer(struct i2c_adapter *adap,
+			struct i2c_msg *msgs, int num)
+{
+	struct exynos5_i2c *i2c = (struct exynos5_i2c *)adap->algo_data;
+	int retry, i;
+	int ret;
+
+	ret = pm_runtime_get_sync(i2c->dev);
+	if (IS_ERR_VALUE(ret))
+		goto out;
+
+	clk_prepare_enable(i2c->clk);
+
+	for (retry = 0; retry < adap->retries; retry++) {
+		for (i = 0; i < num; i++) {
+			ret = exynos5_i2c_doxfer(i2c, msgs);
+			msgs++;
+
+			if (ret == -EAGAIN)
+				break;
+		}
+		if (i == num) {
+			clk_disable_unprepare(i2c->clk);
+			ret = i2c->msg_idx;
+			goto out;
+		}
+
+		dev_dbg(i2c->dev, "retrying transfer (%d)\n", retry);
+
+		udelay(100);
+	}
+
+	ret = -EREMOTEIO;
+	clk_disable_unprepare(i2c->clk);
+ out:
+	pm_runtime_mark_last_busy(i2c->dev);
+	pm_runtime_put_autosuspend(i2c->dev);
+	return ret;
+}
+
+static u32 exynos5_i2c_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm exynos5_i2c_algorithm = {
+	.master_xfer		= exynos5_i2c_xfer,
+	.functionality		= exynos5_i2c_func,
+};
+
+static int exynos5_i2c_set_timing(struct exynos5_i2c *i2c, int speed_mode)
+{
+	unsigned long i2c_timing_s1;
+	unsigned long i2c_timing_s2;
+	unsigned long i2c_timing_s3;
+	unsigned long i2c_timing_sla;
+	unsigned int op_clk;
+	unsigned int clkin = clk_get_rate(i2c->clk);
+	unsigned int n_clkdiv;
+	unsigned int t_start_su, t_start_hd;
+	unsigned int t_stop_su;
+	unsigned int t_data_su, t_data_hd;
+	unsigned int t_scl_l, t_scl_h;
+	unsigned int t_sr_release;
+	unsigned int t_ftl_cycle;
+	unsigned int i = 0, utemp0 = 0, utemp1 = 0, utemp2 = 0;
+
+	if (speed_mode == HSI2C_HIGH_SPD)
+		op_clk = HSI2C_HS_TX_CLOCK;
+	else
+		op_clk = HSI2C_FS_TX_CLOCK;
+
+	/* FPCLK / FI2C =
+	 * (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) + 8 + 2 * FLT_CYCLE
+	 * uTemp0 = (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2)
+	 * uTemp1 = (TSCLK_L + TSCLK_H + 2)
+	 * uTemp2 = TSCLK_L + TSCLK_H
+	*/
+	t_ftl_cycle = (readl(i2c->regs + HSI2C_CONF) >> 16) & 0x7;
+	utemp0 = (clkin / op_clk) - 8 - 2 * t_ftl_cycle;
+
+	/* CLK_DIV max is 256 */
+	for (i = 0; i < 256; i++) {
+		utemp1 = utemp0 / (i + 1);
+		/* SCLK_L/H max is 255
+		 * so sclk_l + sclk_h has max value of 510
+		 */
+		if (utemp1 < 511) {
+			utemp2 = utemp1 - 2;
+			break;
+		}
+	}
+
+	n_clkdiv = i;
+	t_scl_l = utemp2 / 2;
+	t_scl_h = utemp2 / 2;
+	t_start_su = t_scl_l;
+	t_start_hd = t_scl_l;
+	t_stop_su = t_scl_l;
+	t_data_su = t_scl_l / 2;
+	t_data_hd = t_scl_l / 2;
+	t_sr_release = utemp2;
+
+	i2c_timing_s1 = t_start_su << 24 | t_start_hd << 16 | t_stop_su << 8;
+	i2c_timing_s2 = t_data_su << 24 | t_scl_l << 8 | t_scl_h << 0;
+	i2c_timing_s3 = n_clkdiv << 16 | t_sr_release << 0;
+	i2c_timing_sla = t_data_hd << 0;
+
+	dev_dbg(i2c->dev, "tSTART_SU: %X, tSTART_HD: %X, tSTOP_SU: %X\n",
+		t_start_su, t_start_hd, t_stop_su);
+	dev_dbg(i2c->dev, "tDATA_SU: %X, tSCL_L: %X, tSCL_H: %X\n",
+		t_data_su, t_scl_l, t_scl_h);
+	dev_dbg(i2c->dev, "nClkDiv: %X, tSR_RELEASE: %X\n",
+		n_clkdiv, t_sr_release);
+	dev_dbg(i2c->dev, "tDATA_HD: %X\n", t_data_hd);
+
+	if (speed_mode == HSI2C_HIGH_SPD) {
+		writel(i2c_timing_s1, i2c->regs + HSI2C_TIMING_HS1);
+		writel(i2c_timing_s2, i2c->regs + HSI2C_TIMING_HS2);
+		writel(i2c_timing_s3, i2c->regs + HSI2C_TIMING_HS3);
+	} else {
+		writel(i2c_timing_s1, i2c->regs + HSI2C_TIMING_FS1);
+		writel(i2c_timing_s2, i2c->regs + HSI2C_TIMING_FS2);
+		writel(i2c_timing_s3, i2c->regs + HSI2C_TIMING_FS3);
+	}
+	writel(i2c_timing_sla, i2c->regs + HSI2C_TIMING_SLA);
+
+	return 0;
+}
+
+/**
+ * Parse a list of GPIOs from a node property and request each one
+ *
+ * @param i2c		i2c driver data
+ * @return 0 on success, -ve on error, in which case no GPIOs requested
+ */
+static int exynos5_i2c_parse_dt_gpio(struct exynos5_i2c *i2c)
+{
+	int idx, gpio, ret;
+
+	for (idx = 0; idx < 2; idx++) {
+		gpio = of_get_gpio(i2c->dev->of_node, idx);
+		if (!gpio_is_valid(gpio)) {
+			dev_err(i2c->dev, "invalid gpio[%d]: %d\n", idx, gpio);
+			return -EINVAL;
+		}
+		i2c->gpios[idx] = gpio;
+
+		ret = devm_gpio_request(i2c->dev, gpio, "i2c-bus");
+		if (ret) {
+			dev_err(i2c->dev, "gpio [%d] request failed\n", gpio);
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+static void exynos5_i2c_init(struct exynos5_i2c *i2c)
+{
+	unsigned long usi_trailing_ctl = HSI2C_TRAILING_COUNT;
+	unsigned long i2c_conf = HSI2C_AUTO_MODE;
+	unsigned long usi_fifo_ctl;
+
+	writel(usi_trailing_ctl, i2c->regs + HSI2C_TRAILIG_CTL);
+
+	/* Set default trigger level for TXFIFO and RXFIFO */
+	usi_fifo_ctl = HSI2C_TXFIFO_TRIGGER_LEVEL | HSI2C_RXFIFO_TRIGGER_LEVEL;
+
+	/* Enable RXFIFO and TXFIFO */
+	usi_fifo_ctl = HSI2C_RXFIFO_EN | HSI2C_TXFIFO_EN;
+	writel(usi_fifo_ctl, i2c->regs + HSI2C_FIFO_CTL);
+
+	if (i2c->speed_mode == HSI2C_HIGH_SPD) {
+		exynos5_i2c_set_timing(i2c, HSI2C_HIGH_SPD);
+		/* Configure I2C controller in High speed mode */
+		i2c_conf |= HSI2C_HS_MODE;
+		writel(i2c_conf, i2c->regs + HSI2C_CONF);
+	} else {
+		/* Configure I2C controller in Fast speed mode */
+		exynos5_i2c_set_timing(i2c, HSI2C_FAST_SPD);
+	}
+}
+
+static int exynos5_i2c_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct exynos5_i2c *i2c;
+	int ret;
+
+	if (!np) {
+		dev_err(&pdev->dev, "no device node\n");
+		return -ENOENT;
+	}
+
+	i2c = devm_kzalloc(&pdev->dev, sizeof(struct exynos5_i2c), GFP_KERNEL);
+	if (!i2c) {
+		dev_err(&pdev->dev, "no memory for state\n");
+		return -ENOMEM;
+	}
+
+	i2c->bus_num = -1;
+	/* Mode of operation High/Fast Speed mode */
+	of_property_read_u32(np, "samsung,hs-mode", &i2c->speed_mode);
+
+	strlcpy(i2c->adap.name, "exynos5-hsi2c", sizeof(i2c->adap.name));
+	i2c->adap.owner   = THIS_MODULE;
+	i2c->adap.algo    = &exynos5_i2c_algorithm;
+	i2c->adap.retries = 2;
+	i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+
+	i2c->dev = &pdev->dev;
+	i2c->clk = clk_get(&pdev->dev, "hsi2c");
+	if (IS_ERR(i2c->clk)) {
+		dev_err(&pdev->dev, "cannot get clock\n");
+		ret = -ENOENT;
+		goto err_noclk;
+	}
+
+	dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);
+
+	clk_prepare_enable(i2c->clk);
+
+	i2c->regs = of_iomap(np, 0);
+	if (!i2c->regs) {
+		dev_err(&pdev->dev, "cannot map HS-I2C IO\n");
+		ret = -ENXIO;
+		goto err_clk;
+	}
+
+	/* inititalise the gpio */
+	if (exynos5_i2c_parse_dt_gpio(i2c))
+		return -EINVAL;
+
+	i2c->irq = irq_of_parse_and_map(np, 0);
+	if (i2c->irq) {
+		ret = devm_request_irq(&pdev->dev, i2c->irq, exynos5_i2c_irq,
+				0, dev_name(&pdev->dev), i2c);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "cannot request HS-I2C IRQ %d\n",
+								i2c->irq);
+			goto err_iomap;
+		}
+	}
+
+	/* TODO: Use private lock to avoid race conditions as
+	 * mentioned in pm_runtime.txt
+	 */
+	pm_runtime_enable(i2c->dev);
+	pm_runtime_set_autosuspend_delay(i2c->dev, EXYNOS5_I2C_PM_TIMEOUT);
+	pm_runtime_use_autosuspend(i2c->dev);
+
+	ret = pm_runtime_get_sync(i2c->dev);
+	if (IS_ERR_VALUE(ret))
+		goto err_iomap;
+
+	exynos5_i2c_init(i2c);
+
+	i2c->adap.algo_data = i2c;
+	i2c->adap.dev.parent = &pdev->dev;
+	i2c->adap.nr = i2c->bus_num;
+	i2c->adap.dev.of_node = pdev->dev.of_node;
+
+	ret = i2c_add_numbered_adapter(&i2c->adap);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to add bus to i2c core\n");
+		goto err_pm;
+	}
+
+	init_completion(&i2c->msg_complete);
+	of_i2c_register_devices(&i2c->adap);
+	platform_set_drvdata(pdev, i2c);
+
+	dev_info(&pdev->dev, "%s: Exynos5 HS-I2C adapter\n",
+		dev_name(&i2c->adap.dev));
+
+	clk_disable_unprepare(i2c->clk);
+	pm_runtime_mark_last_busy(i2c->dev);
+	pm_runtime_put_autosuspend(i2c->dev);
+	return 0;
+
+ err_pm:
+	pm_runtime_put(i2c->dev);
+	pm_runtime_disable(&pdev->dev);
+ err_iomap:
+	iounmap(i2c->regs);
+ err_clk:
+	clk_disable_unprepare(i2c->clk);
+ err_noclk:
+	return ret;
+}
+
+static int exynos5_i2c_remove(struct platform_device *pdev)
+{
+	struct exynos5_i2c *i2c = platform_get_drvdata(pdev);
+	int ret;
+
+	ret = pm_runtime_get_sync(&pdev->dev);
+	if (IS_ERR_VALUE(ret))
+		return ret;
+
+	clk_disable_unprepare(i2c->clk);
+	pm_runtime_put(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+
+	i2c_del_adapter(&i2c->adap);
+
+	iounmap(i2c->regs);
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int exynos5_i2c_suspend_noirq(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct exynos5_i2c *i2c = platform_get_drvdata(pdev);
+
+	i2c->suspended = 1;
+
+	return 0;
+}
+
+static int exynos5_i2c_resume_noirq(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct exynos5_i2c *i2c = platform_get_drvdata(pdev);
+
+	clk_prepare_enable(i2c->clk);
+	exynos5_i2c_init(i2c);
+	clk_disable_unprepare(i2c->clk);
+	i2c->suspended = 0;
+
+	return 0;
+}
+
+static const struct dev_pm_ops exynos5_i2c_dev_pm_ops = {
+	.suspend_noirq	= exynos5_i2c_suspend_noirq,
+	.resume_noirq	= exynos5_i2c_resume_noirq,
+};
+
+#define EXYNOS5_DEV_PM_OPS (&exynos5_i2c_dev_pm_ops)
+#else
+#define EXYNOS5_DEV_PM_OPS NULL
+#endif
+
+static struct platform_driver exynos5_i2c_driver = {
+	.probe		= exynos5_i2c_probe,
+	.remove		= exynos5_i2c_remove,
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "exynos5-hsi2c",
+		.pm	= EXYNOS5_DEV_PM_OPS,
+		.of_match_table = exynos5_i2c_match,
+	},
+};
+
+static int __init i2c_adap_exynos5_init(void)
+{
+	return platform_driver_register(&exynos5_i2c_driver);
+}
+subsys_initcall(i2c_adap_exynos5_init);
+
+static void __exit i2c_adap_exynos5_exit(void)
+{
+	platform_driver_unregister(&exynos5_i2c_driver);
+}
+module_exit(i2c_adap_exynos5_exit);
+
+MODULE_DESCRIPTION("Exynos5 HS-I2C Bus driver");
+MODULE_AUTHOR("Taekgyun Ko, <taeggyun.ko@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-exynos5.h b/drivers/i2c/busses/i2c-exynos5.h
new file mode 100644
index 0000000..4adcf88
--- /dev/null
+++ b/drivers/i2c/busses/i2c-exynos5.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ *
+ * Exynos5 series High Speed I2C Controller
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_ARCH_REGS_HS_IIC_H
+#define __ASM_ARCH_REGS_HS_IIC_H
+
+/* Register Map */
+#define HSI2C_CTL				0x00
+#define HSI2C_FIFO_CTL				0x04
+#define HSI2C_TRAILIG_CTL			0x08
+#define HSI2C_CLK_CTL				0x0C
+#define HSI2C_CLK_SLOT				0x10
+#define HSI2C_INT_ENABLE			0x20
+#define HSI2C_INT_STATUS			0x24
+#define HSI2C_ERR_STATUS			0x2C
+#define HSI2C_FIFO_STATUS			0x30
+#define HSI2C_TX_DATA				0x34
+#define HSI2C_RX_DATA				0x38
+#define HSI2C_CONF				0x40
+#define HSI2C_AUTO_CONF				0x44
+#define HSI2C_TIMEOUT				0x48
+#define HSI2C_MANUAL_CMD			0x4C
+#define HSI2C_TRANS_STATUS			0x50
+#define HSI2C_TIMING_HS1			0x54
+#define HSI2C_TIMING_HS2			0x58
+#define HSI2C_TIMING_HS3			0x5C
+#define HSI2C_TIMING_FS1			0x60
+#define HSI2C_TIMING_FS2			0x64
+#define HSI2C_TIMING_FS3			0x68
+#define HSI2C_TIMING_SLA			0x6C
+#define HSI2C_ADDR				0x70
+
+/* I2C_CTL Register */
+#define HSI2C_FUNC_MODE_I2C			(1u << 0)
+#define HSI2C_MASTER				(1u << 3)
+#define HSI2C_RXCHON				(1u << 6)
+#define HSI2C_TXCHON				(1u << 7)
+#define HSI2C_SW_RST				(1u << 31)
+
+/* I2C_FIFO_CTL Register */
+#define HSI2C_RXFIFO_EN				(1u << 0)
+#define HSI2C_TXFIFO_EN				(1u << 1)
+#define HSI2C_TXFIFO_TRIGGER_LEVEL		(0x20 << 16)
+#define HSI2C_RXFIFO_TRIGGER_LEVEL		(0x20 << 4)
+
+/* I2C_TRAILING_CTL Register */
+#define HSI2C_TRAILING_COUNT			(0xf)
+
+/* I2C_INT_EN Register */
+#define HSI2C_INT_TX_ALMOSTEMPTY_EN		(1u << 0)
+#define HSI2C_INT_RX_ALMOSTFULL_EN		(1u << 1)
+#define HSI2C_INT_TRAILING_EN			(1u << 6)
+#define HSI2C_INT_I2C_EN			(1u << 9)
+
+/* I2C_FIFO_STAT Register */
+#define HSI2C_RX_FIFO_EMPTY			(1u << 24)
+#define HSI2C_RX_FIFO_FULL			(1u << 23)
+#define HSI2C_RX_FIFO_LEVEL_MASK		(0x7 << 16)
+#define HSI2C_TX_FIFO_EMPTY			(1u << 8)
+#define HSI2C_TX_FIFO_FULL			(1u << 7)
+#define HSI2C_TX_FIFO_LEVEL_MASK		(0x7 << 7)
+#define HSI2C_FIFO_EMPTY			(0x1000100)
+
+/* I2C_CONF Register */
+#define HSI2C_AUTO_MODE				(1u << 31)
+#define HSI2C_10BIT_ADDR_MODE			(1u << 30)
+#define HSI2C_HS_MODE				(1u << 29)
+
+/* I2C_AUTO_CONF Register */
+#define HSI2C_READ_WRITE			(1u << 16)
+#define HSI2C_STOP_AFTER_TRANS			(1u << 17)
+#define HSI2C_MASTER_RUN			(1u << 31)
+
+/* I2C_TIMEOUT Register */
+#define HSI2C_TIMEOUT_EN			(1u << 31)
+
+/* I2C_TRANS_STATUS register */
+#define HSI2C_MASTER_BUSY			(1u << 17)
+#define HSI2C_SLAVE_BUSY			(1u << 16)
+#define HSI2C_NO_DEV				(1u << 3)
+#define HSI2C_NO_DEV_ACK			(1u << 2)
+#define HSI2C_TRANS_ABORT			(1u << 1)
+#define HSI2C_TRANS_DONE			(1u << 0)
+
+/* Although exynos5 supports max HS-IIC speed of 3.4Mhz,
+ * but currently we are facing booting issues beyond 1Mhz
+ * So limiting HS-IIC bus speed to 1Mhz
+ */
+#define HSI2C_HS_TX_CLOCK	1000000
+#define HSI2C_FS_TX_CLOCK	400000
+
+#define HSI2C_FAST_SPD 0
+#define HSI2C_HIGH_SPD 1
+
+#endif /* __ASM_ARCH_REGS_HS_IIC_H */
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 1/2] i2c: exynos5: add High Speed I2C controller driver
From: Naveen Krishna Chatradhi @ 2012-12-25 11:25 UTC (permalink / raw)
  Cc: linux-arm-kernel, linux-samsung-soc, devicetree-discuss,
	linux-i2c, naveenkrishna.ch, kgene.kim, grant.likely, w.sang,
	linux-kernel, taeggyun.ko, balbi, thomas.abraham
In-Reply-To: <1354021236-28596-2-git-send-email-ch.naveen@samsung.com>

Adds support for High Speed I2C driver found in Exynos5 and later
SoCs from Samsung. This driver currently supports Auto mode.

Driver only supports Device Tree method.

Signed-off-by: Taekgyun Ko <taeggyun.ko@samsung.com>
Signed-off-by: Naveen Krishna Chatradhi <ch.naveen@samsung.com>
---
Changes since v1:
Fixed the comments from Felipe Balbi and Thomas Abraham.

 drivers/i2c/busses/Kconfig       |    7 +
 drivers/i2c/busses/Makefile      |    1 +
 drivers/i2c/busses/i2c-exynos5.c |  652 ++++++++++++++++++++++++++++++++++++++
 drivers/i2c/busses/i2c-exynos5.h |  102 ++++++
 4 files changed, 762 insertions(+)
 create mode 100644 drivers/i2c/busses/i2c-exynos5.c
 create mode 100644 drivers/i2c/busses/i2c-exynos5.h

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index bdca511..4caea76 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -618,6 +618,13 @@ config I2C_S3C2410
 	  Say Y here to include support for I2C controller in the
 	  Samsung SoCs.
 
+config I2C_EXYNOS5
+	tristate "Exynos5 high-speed I2C driver"
+	depends on ARCH_EXYNOS5
+	help
+	  Say Y here to include support for High-speed I2C controller in the
+	  Exynos5 based Samsung SoCs.
+
 config I2C_S6000
 	tristate "S6000 I2C support"
 	depends on XTENSA_VARIANT_S6000
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 6181f3f..4b1548c 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_I2C_PUV3)		+= i2c-puv3.o
 obj-$(CONFIG_I2C_PXA)		+= i2c-pxa.o
 obj-$(CONFIG_I2C_PXA_PCI)	+= i2c-pxa-pci.o
 obj-$(CONFIG_I2C_S3C2410)	+= i2c-s3c2410.o
+obj-$(CONFIG_I2C_EXYNOS5)	+= i2c-exynos5.o
 obj-$(CONFIG_I2C_S6000)		+= i2c-s6000.o
 obj-$(CONFIG_I2C_SH7760)	+= i2c-sh7760.o
 obj-$(CONFIG_I2C_SH_MOBILE)	+= i2c-sh_mobile.o
diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c
new file mode 100644
index 0000000..7614f60
--- /dev/null
+++ b/drivers/i2c/busses/i2c-exynos5.c
@@ -0,0 +1,652 @@
+/* linux/drivers/i2c/busses/i2c-exynos5.c
+ *
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ *
+ * High speed I2C controller driver
+ * for Exynos5 and later SoCs from Samsung.
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/of_i2c.h>
+
+#include "i2c-exynos5.h"
+
+#define EXYNOS5_I2C_TIMEOUT (msecs_to_jiffies(1000))
+
+/* timeout for pm runtime autosuspend */
+#define EXYNOS5_I2C_PM_TIMEOUT		1000	/* ms */
+
+struct exynos5_i2c {
+	struct i2c_adapter	adap;
+	unsigned int		suspended:1;
+
+	struct i2c_msg		*msg;
+	unsigned int		msg_idx;
+	struct completion	msg_complete;
+	unsigned int		msg_ptr;
+
+	unsigned int		irq;
+
+	void __iomem		*regs;
+	struct clk		*clk;
+	struct device		*dev;
+	int			gpios[2];
+
+	int			bus_num;
+	int			speed_mode;
+};
+
+static const struct of_device_id exynos5_i2c_match[] = {
+	{ .compatible = "samsung,exynos5-hsi2c" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, exynos5_i2c_match);
+
+/* TODO: Should go to debugfs */
+static inline void dump_i2c_register(struct exynos5_i2c *i2c)
+{
+	dev_vdbg(i2c->dev, "Register dump(%d) :\n %x\n %x\n %x\n %x\n"
+		" %x\n %x\n %x\n %x\n %x\n"
+		" %x\n %x\n %x\n %x\n %x\n"
+		" %x\n %x\n %x\n %x\n %x\n"
+		" %x\n %x\n %x\n %x\n %x\n",
+		i2c->suspended,
+		readl(i2c->regs + HSI2C_CTL),
+		readl(i2c->regs + HSI2C_FIFO_CTL),
+		readl(i2c->regs + HSI2C_TRAILIG_CTL),
+		readl(i2c->regs + HSI2C_CLK_CTL),
+		readl(i2c->regs + HSI2C_CLK_SLOT),
+		readl(i2c->regs + HSI2C_INT_ENABLE),
+		readl(i2c->regs + HSI2C_INT_STATUS),
+		readl(i2c->regs + HSI2C_ERR_STATUS),
+		readl(i2c->regs + HSI2C_FIFO_STATUS),
+		readl(i2c->regs + HSI2C_TX_DATA),
+		readl(i2c->regs + HSI2C_RX_DATA),
+		readl(i2c->regs + HSI2C_CONF),
+		readl(i2c->regs + HSI2C_AUTO_CONF),
+		readl(i2c->regs + HSI2C_TIMEOUT),
+		readl(i2c->regs + HSI2C_MANUAL_CMD),
+		readl(i2c->regs + HSI2C_TRANS_STATUS),
+		readl(i2c->regs + HSI2C_TIMING_HS1),
+		readl(i2c->regs + HSI2C_TIMING_HS2),
+		readl(i2c->regs + HSI2C_TIMING_HS3),
+		readl(i2c->regs + HSI2C_TIMING_FS1),
+		readl(i2c->regs + HSI2C_TIMING_FS2),
+		readl(i2c->regs + HSI2C_TIMING_FS3),
+		readl(i2c->regs + HSI2C_TIMING_SLA),
+		readl(i2c->regs + HSI2C_ADDR));
+}
+
+static inline void exynos5_i2c_stop(struct exynos5_i2c *i2c, int ret)
+{
+	dev_dbg(i2c->dev, "STOP\n");
+
+	i2c->msg_idx++;
+	if (ret)
+		i2c->msg_idx = ret;
+
+	/* Disable interrrupts */
+	writel(0, i2c->regs + HSI2C_INT_ENABLE);
+	complete(&i2c->msg_complete);
+}
+
+static void exynos5_i2c_en_timeout(struct exynos5_i2c *i2c)
+{
+	unsigned long i2c_timeout = readl(i2c->regs + HSI2C_TIMEOUT);
+
+	/* Clear to enable Timeout */
+	i2c_timeout &= ~HSI2C_TIMEOUT_EN;
+	writel(i2c_timeout, i2c->regs + HSI2C_TIMEOUT);
+}
+
+static void exynos5_i2c_master_run(struct exynos5_i2c *i2c)
+{
+	/* Start data transfer in Master mode */
+	u32 i2c_auto_conf = readl(i2c->regs + HSI2C_AUTO_CONF);
+	i2c_auto_conf |= HSI2C_MASTER_RUN;
+	writel(i2c_auto_conf, i2c->regs + HSI2C_AUTO_CONF);
+}
+
+/* exynos5_i2c_set_bus
+ *
+ * get the i2c bus for a master/slave transaction
+ */
+static int exynos5_i2c_set_bus(struct exynos5_i2c *i2c, int master)
+{
+	unsigned long t_status;
+	int timeout = 400;
+
+	while (timeout-- > 0) {
+		t_status = readl(i2c->regs + HSI2C_TRANS_STATUS);
+
+		if (master) {
+			if (!(t_status & HSI2C_MASTER_BUSY))
+				return 0;
+		} else {
+			if (!(t_status & HSI2C_SLAVE_BUSY))
+				return 0;
+		}
+
+		msleep(20);
+	}
+
+	return -ETIMEDOUT;
+}
+
+/* exynos5_i2c_irq
+ *
+ * top level IRQ servicing routine
+ */
+static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id)
+{
+	struct exynos5_i2c *i2c = dev_id;
+	unsigned long t_stat;
+	unsigned char byte;
+
+	t_stat = readl(i2c->regs + HSI2C_TRANS_STATUS);
+
+	if (t_stat & HSI2C_TRANS_ABORT) {
+		/* deal with arbitration loss */
+		dev_err(i2c->dev, "deal with arbitration loss\n");
+		goto out;
+	}
+	if (i2c->msg->flags & I2C_M_RD) {
+		if (t_stat & HSI2C_TRANS_DONE) {
+			dev_dbg(i2c->dev, "Device found.");
+			while ((readl(i2c->regs + HSI2C_FIFO_STATUS) &
+					HSI2C_RX_FIFO_EMPTY) == 0) {
+				byte = readl(i2c->regs + HSI2C_RX_DATA);
+				dev_dbg(i2c->dev, "read rx_data = %x", byte);
+				i2c->msg->buf[i2c->msg_ptr++] = byte;
+			}
+		} else if (t_stat & HSI2C_NO_DEV) {
+			dev_dbg(i2c->dev, "No device found.");
+			exynos5_i2c_stop(i2c, -ENXIO);
+		} else if (t_stat & HSI2C_NO_DEV_ACK &&
+				!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
+			dev_dbg(i2c->dev, "No device Ack.");
+			exynos5_i2c_stop(i2c, -ENXIO);
+		}
+	} else {
+		byte = i2c->msg->buf[i2c->msg_ptr++];
+		dev_dbg(i2c->dev, "write tx_data = %x ", byte);
+		writel(byte, i2c->regs + HSI2C_TX_DATA);
+	}
+
+	if (i2c->msg_ptr >= i2c->msg->len)
+		exynos5_i2c_stop(i2c, 0);
+
+ out:
+	/* Set those bits to clear them */
+	writel(readl(i2c->regs + HSI2C_INT_STATUS),
+				i2c->regs + HSI2C_INT_STATUS);
+
+	return IRQ_HANDLED;
+}
+
+static void exynos5_i2c_message_start(struct exynos5_i2c *i2c,
+				      struct i2c_msg *msgs)
+{
+	unsigned long usi_ctl = HSI2C_FUNC_MODE_I2C | HSI2C_MASTER;
+	unsigned long i2c_auto_conf;
+	unsigned long i2c_addr = ((msgs->addr & 0x7f) << 10);
+	unsigned long usi_int_en = 0;
+
+	exynos5_i2c_en_timeout(i2c);
+
+	if (msgs->flags & I2C_M_RD) {
+		usi_ctl &= ~HSI2C_TXCHON;
+		usi_ctl |= HSI2C_RXCHON;
+
+		i2c_auto_conf |= HSI2C_READ_WRITE;
+
+		usi_int_en |= (HSI2C_INT_RX_ALMOSTFULL_EN |
+			HSI2C_INT_TRAILING_EN);
+	} else {
+		usi_ctl &= ~HSI2C_RXCHON;
+		usi_ctl |= HSI2C_TXCHON;
+
+		i2c_auto_conf &= ~HSI2C_READ_WRITE;
+
+		usi_int_en |= HSI2C_INT_TX_ALMOSTEMPTY_EN;
+	}
+
+	writel(i2c_addr, i2c->regs + HSI2C_ADDR);
+	writel(usi_ctl, i2c->regs + HSI2C_CTL);
+
+	i2c_auto_conf |= i2c->msg->len;
+	i2c_auto_conf |= HSI2C_STOP_AFTER_TRANS;
+	writel(i2c_auto_conf, i2c->regs + HSI2C_AUTO_CONF);
+
+	exynos5_i2c_master_run(i2c);
+
+	/* Enable appropriate interrupts */
+	writel(usi_int_en, i2c->regs + HSI2C_INT_ENABLE);
+}
+
+static int exynos5_i2c_doxfer(struct exynos5_i2c *i2c, struct i2c_msg *msgs)
+{
+	unsigned long timeout;
+
+	if (i2c->suspended) {
+		dev_err(i2c->dev, "HS-I2C is not initialzed.\n");
+		return -EIO;
+	}
+
+	if (exynos5_i2c_set_bus(i2c, 1)) {
+		dev_err(i2c->dev, "cannot get bus, Master busy.\n");
+		return -EAGAIN;
+	}
+
+	i2c->msg = msgs;
+	i2c->msg_ptr = 0;
+	i2c->msg_idx = 0;
+
+	INIT_COMPLETION(i2c->msg_complete);
+
+	exynos5_i2c_message_start(i2c, msgs);
+
+	timeout = wait_for_completion_timeout(&i2c->msg_complete,
+		EXYNOS5_I2C_TIMEOUT);
+
+	if (timeout == 0)
+		dev_dbg(i2c->dev, "timeout\n");
+	else if (i2c->msg_idx != msgs->len)
+		dev_dbg(i2c->dev, "incomplete xfer (%d)\n", i2c->msg_idx);
+
+	return i2c->msg_idx;
+}
+
+static int exynos5_i2c_xfer(struct i2c_adapter *adap,
+			struct i2c_msg *msgs, int num)
+{
+	struct exynos5_i2c *i2c = (struct exynos5_i2c *)adap->algo_data;
+	int retry, i;
+	int ret;
+
+	ret = pm_runtime_get_sync(i2c->dev);
+	if (IS_ERR_VALUE(ret))
+		goto out;
+
+	clk_prepare_enable(i2c->clk);
+
+	for (retry = 0; retry < adap->retries; retry++) {
+		for (i = 0; i < num; i++) {
+			ret = exynos5_i2c_doxfer(i2c, msgs);
+			msgs++;
+
+			if (ret == -EAGAIN)
+				break;
+		}
+		if (i == num) {
+			clk_disable_unprepare(i2c->clk);
+			ret = i2c->msg_idx;
+			goto out;
+		}
+
+		dev_dbg(i2c->dev, "retrying transfer (%d)\n", retry);
+
+		udelay(100);
+	}
+
+	ret = -EREMOTEIO;
+	clk_disable_unprepare(i2c->clk);
+ out:
+	pm_runtime_mark_last_busy(i2c->dev);
+	pm_runtime_put_autosuspend(i2c->dev);
+	return ret;
+}
+
+static u32 exynos5_i2c_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm exynos5_i2c_algorithm = {
+	.master_xfer		= exynos5_i2c_xfer,
+	.functionality		= exynos5_i2c_func,
+};
+
+static int exynos5_i2c_set_timing(struct exynos5_i2c *i2c, int speed_mode)
+{
+	unsigned long i2c_timing_s1;
+	unsigned long i2c_timing_s2;
+	unsigned long i2c_timing_s3;
+	unsigned long i2c_timing_sla;
+	unsigned int op_clk;
+	unsigned int clkin = clk_get_rate(i2c->clk);
+	unsigned int n_clkdiv;
+	unsigned int t_start_su, t_start_hd;
+	unsigned int t_stop_su;
+	unsigned int t_data_su, t_data_hd;
+	unsigned int t_scl_l, t_scl_h;
+	unsigned int t_sr_release;
+	unsigned int t_ftl_cycle;
+	unsigned int i = 0, utemp0 = 0, utemp1 = 0, utemp2 = 0;
+
+	if (speed_mode == HSI2C_HIGH_SPD)
+		op_clk = HSI2C_HS_TX_CLOCK;
+	else
+		op_clk = HSI2C_FS_TX_CLOCK;
+
+	/* FPCLK / FI2C =
+	 * (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) + 8 + 2 * FLT_CYCLE
+	 * uTemp0 = (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2)
+	 * uTemp1 = (TSCLK_L + TSCLK_H + 2)
+	 * uTemp2 = TSCLK_L + TSCLK_H
+	*/
+	t_ftl_cycle = (readl(i2c->regs + HSI2C_CONF) >> 16) & 0x7;
+	utemp0 = (clkin / op_clk) - 8 - 2 * t_ftl_cycle;
+
+	/* CLK_DIV max is 256 */
+	for (i = 0; i < 256; i++) {
+		utemp1 = utemp0 / (i + 1);
+		/* SCLK_L/H max is 255
+		 * so sclk_l + sclk_h has max value of 510
+		 */
+		if (utemp1 < 511) {
+			utemp2 = utemp1 - 2;
+			break;
+		}
+	}
+
+	n_clkdiv = i;
+	t_scl_l = utemp2 / 2;
+	t_scl_h = utemp2 / 2;
+	t_start_su = t_scl_l;
+	t_start_hd = t_scl_l;
+	t_stop_su = t_scl_l;
+	t_data_su = t_scl_l / 2;
+	t_data_hd = t_scl_l / 2;
+	t_sr_release = utemp2;
+
+	i2c_timing_s1 = t_start_su << 24 | t_start_hd << 16 | t_stop_su << 8;
+	i2c_timing_s2 = t_data_su << 24 | t_scl_l << 8 | t_scl_h << 0;
+	i2c_timing_s3 = n_clkdiv << 16 | t_sr_release << 0;
+	i2c_timing_sla = t_data_hd << 0;
+
+	dev_dbg(i2c->dev, "tSTART_SU: %X, tSTART_HD: %X, tSTOP_SU: %X\n",
+		t_start_su, t_start_hd, t_stop_su);
+	dev_dbg(i2c->dev, "tDATA_SU: %X, tSCL_L: %X, tSCL_H: %X\n",
+		t_data_su, t_scl_l, t_scl_h);
+	dev_dbg(i2c->dev, "nClkDiv: %X, tSR_RELEASE: %X\n",
+		n_clkdiv, t_sr_release);
+	dev_dbg(i2c->dev, "tDATA_HD: %X\n", t_data_hd);
+
+	if (speed_mode == HSI2C_HIGH_SPD) {
+		writel(i2c_timing_s1, i2c->regs + HSI2C_TIMING_HS1);
+		writel(i2c_timing_s2, i2c->regs + HSI2C_TIMING_HS2);
+		writel(i2c_timing_s3, i2c->regs + HSI2C_TIMING_HS3);
+	} else {
+		writel(i2c_timing_s1, i2c->regs + HSI2C_TIMING_FS1);
+		writel(i2c_timing_s2, i2c->regs + HSI2C_TIMING_FS2);
+		writel(i2c_timing_s3, i2c->regs + HSI2C_TIMING_FS3);
+	}
+	writel(i2c_timing_sla, i2c->regs + HSI2C_TIMING_SLA);
+
+	return 0;
+}
+
+/**
+ * Parse a list of GPIOs from a node property and request each one
+ *
+ * @param i2c		i2c driver data
+ * @return 0 on success, -ve on error, in which case no GPIOs requested
+ */
+static int exynos5_i2c_parse_dt_gpio(struct exynos5_i2c *i2c)
+{
+	int idx, gpio, ret;
+
+	for (idx = 0; idx < 2; idx++) {
+		gpio = of_get_gpio(i2c->dev->of_node, idx);
+		if (!gpio_is_valid(gpio)) {
+			dev_err(i2c->dev, "invalid gpio[%d]: %d\n", idx, gpio);
+			return -EINVAL;
+		}
+		i2c->gpios[idx] = gpio;
+
+		ret = devm_gpio_request(i2c->dev, gpio, "i2c-bus");
+		if (ret) {
+			dev_err(i2c->dev, "gpio [%d] request failed\n", gpio);
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+static void exynos5_i2c_init(struct exynos5_i2c *i2c)
+{
+	unsigned long usi_trailing_ctl = HSI2C_TRAILING_COUNT;
+	unsigned long i2c_conf = HSI2C_AUTO_MODE;
+	unsigned long usi_fifo_ctl;
+
+	writel(usi_trailing_ctl, i2c->regs + HSI2C_TRAILIG_CTL);
+
+	/* Set default trigger level for TXFIFO and RXFIFO */
+	usi_fifo_ctl = HSI2C_TXFIFO_TRIGGER_LEVEL | HSI2C_RXFIFO_TRIGGER_LEVEL;
+
+	/* Enable RXFIFO and TXFIFO */
+	usi_fifo_ctl = HSI2C_RXFIFO_EN | HSI2C_TXFIFO_EN;
+	writel(usi_fifo_ctl, i2c->regs + HSI2C_FIFO_CTL);
+
+	if (i2c->speed_mode == HSI2C_HIGH_SPD) {
+		exynos5_i2c_set_timing(i2c, HSI2C_HIGH_SPD);
+		/* Configure I2C controller in High speed mode */
+		i2c_conf |= HSI2C_HS_MODE;
+		writel(i2c_conf, i2c->regs + HSI2C_CONF);
+	} else {
+		/* Configure I2C controller in Fast speed mode */
+		exynos5_i2c_set_timing(i2c, HSI2C_FAST_SPD);
+	}
+}
+
+static int exynos5_i2c_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct exynos5_i2c *i2c;
+	int ret;
+
+	if (!np) {
+		dev_err(&pdev->dev, "no device node\n");
+		return -ENOENT;
+	}
+
+	i2c = devm_kzalloc(&pdev->dev, sizeof(struct exynos5_i2c), GFP_KERNEL);
+	if (!i2c) {
+		dev_err(&pdev->dev, "no memory for state\n");
+		return -ENOMEM;
+	}
+
+	i2c->bus_num = -1;
+	/* Mode of operation High/Fast Speed mode */
+	of_property_read_u32(np, "samsung,hs-mode", &i2c->speed_mode);
+
+	strlcpy(i2c->adap.name, "exynos5-hsi2c", sizeof(i2c->adap.name));
+	i2c->adap.owner   = THIS_MODULE;
+	i2c->adap.algo    = &exynos5_i2c_algorithm;
+	i2c->adap.retries = 2;
+	i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;
+
+	i2c->dev = &pdev->dev;
+	i2c->clk = clk_get(&pdev->dev, "hsi2c");
+	if (IS_ERR(i2c->clk)) {
+		dev_err(&pdev->dev, "cannot get clock\n");
+		ret = -ENOENT;
+		goto err_noclk;
+	}
+
+	dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);
+
+	clk_prepare_enable(i2c->clk);
+
+	i2c->regs = of_iomap(np, 0);
+	if (!i2c->regs) {
+		dev_err(&pdev->dev, "cannot map HS-I2C IO\n");
+		ret = -ENXIO;
+		goto err_clk;
+	}
+
+	/* inititalise the gpio */
+	if (exynos5_i2c_parse_dt_gpio(i2c))
+		return -EINVAL;
+
+	i2c->irq = irq_of_parse_and_map(np, 0);
+	if (i2c->irq) {
+		ret = devm_request_irq(&pdev->dev, i2c->irq, exynos5_i2c_irq,
+				0, dev_name(&pdev->dev), i2c);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "cannot request HS-I2C IRQ %d\n",
+								i2c->irq);
+			goto err_iomap;
+		}
+	}
+
+	/* TODO: Use private lock to avoid race conditions as
+	 * mentioned in pm_runtime.txt
+	 */
+	pm_runtime_enable(i2c->dev);
+	pm_runtime_set_autosuspend_delay(i2c->dev, EXYNOS5_I2C_PM_TIMEOUT);
+	pm_runtime_use_autosuspend(i2c->dev);
+
+	ret = pm_runtime_get_sync(i2c->dev);
+	if (IS_ERR_VALUE(ret))
+		goto err_iomap;
+
+	exynos5_i2c_init(i2c);
+
+	i2c->adap.algo_data = i2c;
+	i2c->adap.dev.parent = &pdev->dev;
+	i2c->adap.nr = i2c->bus_num;
+	i2c->adap.dev.of_node = pdev->dev.of_node;
+
+	ret = i2c_add_numbered_adapter(&i2c->adap);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to add bus to i2c core\n");
+		goto err_pm;
+	}
+
+	init_completion(&i2c->msg_complete);
+	of_i2c_register_devices(&i2c->adap);
+	platform_set_drvdata(pdev, i2c);
+
+	dev_info(&pdev->dev, "%s: Exynos5 HS-I2C adapter\n",
+		dev_name(&i2c->adap.dev));
+
+	clk_disable_unprepare(i2c->clk);
+	pm_runtime_mark_last_busy(i2c->dev);
+	pm_runtime_put_autosuspend(i2c->dev);
+	return 0;
+
+ err_pm:
+	pm_runtime_put(i2c->dev);
+	pm_runtime_disable(&pdev->dev);
+ err_iomap:
+	iounmap(i2c->regs);
+ err_clk:
+	clk_disable_unprepare(i2c->clk);
+ err_noclk:
+	return ret;
+}
+
+static int exynos5_i2c_remove(struct platform_device *pdev)
+{
+	struct exynos5_i2c *i2c = platform_get_drvdata(pdev);
+	int ret;
+
+	ret = pm_runtime_get_sync(&pdev->dev);
+	if (IS_ERR_VALUE(ret))
+		return ret;
+
+	clk_disable_unprepare(i2c->clk);
+	pm_runtime_put(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+
+	i2c_del_adapter(&i2c->adap);
+
+	iounmap(i2c->regs);
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int exynos5_i2c_suspend_noirq(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct exynos5_i2c *i2c = platform_get_drvdata(pdev);
+
+	i2c->suspended = 1;
+
+	return 0;
+}
+
+static int exynos5_i2c_resume_noirq(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct exynos5_i2c *i2c = platform_get_drvdata(pdev);
+
+	clk_prepare_enable(i2c->clk);
+	exynos5_i2c_init(i2c);
+	clk_disable_unprepare(i2c->clk);
+	i2c->suspended = 0;
+
+	return 0;
+}
+
+static const struct dev_pm_ops exynos5_i2c_dev_pm_ops = {
+	.suspend_noirq	= exynos5_i2c_suspend_noirq,
+	.resume_noirq	= exynos5_i2c_resume_noirq,
+};
+
+#define EXYNOS5_DEV_PM_OPS (&exynos5_i2c_dev_pm_ops)
+#else
+#define EXYNOS5_DEV_PM_OPS NULL
+#endif
+
+static struct platform_driver exynos5_i2c_driver = {
+	.probe		= exynos5_i2c_probe,
+	.remove		= exynos5_i2c_remove,
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "exynos5-hsi2c",
+		.pm	= EXYNOS5_DEV_PM_OPS,
+		.of_match_table = exynos5_i2c_match,
+	},
+};
+
+static int __init i2c_adap_exynos5_init(void)
+{
+	return platform_driver_register(&exynos5_i2c_driver);
+}
+subsys_initcall(i2c_adap_exynos5_init);
+
+static void __exit i2c_adap_exynos5_exit(void)
+{
+	platform_driver_unregister(&exynos5_i2c_driver);
+}
+module_exit(i2c_adap_exynos5_exit);
+
+MODULE_DESCRIPTION("Exynos5 HS-I2C Bus driver");
+MODULE_AUTHOR("Taekgyun Ko, <taeggyun.ko@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-exynos5.h b/drivers/i2c/busses/i2c-exynos5.h
new file mode 100644
index 0000000..4adcf88
--- /dev/null
+++ b/drivers/i2c/busses/i2c-exynos5.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ *
+ * Exynos5 series High Speed I2C Controller
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __ASM_ARCH_REGS_HS_IIC_H
+#define __ASM_ARCH_REGS_HS_IIC_H
+
+/* Register Map */
+#define HSI2C_CTL				0x00
+#define HSI2C_FIFO_CTL				0x04
+#define HSI2C_TRAILIG_CTL			0x08
+#define HSI2C_CLK_CTL				0x0C
+#define HSI2C_CLK_SLOT				0x10
+#define HSI2C_INT_ENABLE			0x20
+#define HSI2C_INT_STATUS			0x24
+#define HSI2C_ERR_STATUS			0x2C
+#define HSI2C_FIFO_STATUS			0x30
+#define HSI2C_TX_DATA				0x34
+#define HSI2C_RX_DATA				0x38
+#define HSI2C_CONF				0x40
+#define HSI2C_AUTO_CONF				0x44
+#define HSI2C_TIMEOUT				0x48
+#define HSI2C_MANUAL_CMD			0x4C
+#define HSI2C_TRANS_STATUS			0x50
+#define HSI2C_TIMING_HS1			0x54
+#define HSI2C_TIMING_HS2			0x58
+#define HSI2C_TIMING_HS3			0x5C
+#define HSI2C_TIMING_FS1			0x60
+#define HSI2C_TIMING_FS2			0x64
+#define HSI2C_TIMING_FS3			0x68
+#define HSI2C_TIMING_SLA			0x6C
+#define HSI2C_ADDR				0x70
+
+/* I2C_CTL Register */
+#define HSI2C_FUNC_MODE_I2C			(1u << 0)
+#define HSI2C_MASTER				(1u << 3)
+#define HSI2C_RXCHON				(1u << 6)
+#define HSI2C_TXCHON				(1u << 7)
+#define HSI2C_SW_RST				(1u << 31)
+
+/* I2C_FIFO_CTL Register */
+#define HSI2C_RXFIFO_EN				(1u << 0)
+#define HSI2C_TXFIFO_EN				(1u << 1)
+#define HSI2C_TXFIFO_TRIGGER_LEVEL		(0x20 << 16)
+#define HSI2C_RXFIFO_TRIGGER_LEVEL		(0x20 << 4)
+
+/* I2C_TRAILING_CTL Register */
+#define HSI2C_TRAILING_COUNT			(0xf)
+
+/* I2C_INT_EN Register */
+#define HSI2C_INT_TX_ALMOSTEMPTY_EN		(1u << 0)
+#define HSI2C_INT_RX_ALMOSTFULL_EN		(1u << 1)
+#define HSI2C_INT_TRAILING_EN			(1u << 6)
+#define HSI2C_INT_I2C_EN			(1u << 9)
+
+/* I2C_FIFO_STAT Register */
+#define HSI2C_RX_FIFO_EMPTY			(1u << 24)
+#define HSI2C_RX_FIFO_FULL			(1u << 23)
+#define HSI2C_RX_FIFO_LEVEL_MASK		(0x7 << 16)
+#define HSI2C_TX_FIFO_EMPTY			(1u << 8)
+#define HSI2C_TX_FIFO_FULL			(1u << 7)
+#define HSI2C_TX_FIFO_LEVEL_MASK		(0x7 << 7)
+#define HSI2C_FIFO_EMPTY			(0x1000100)
+
+/* I2C_CONF Register */
+#define HSI2C_AUTO_MODE				(1u << 31)
+#define HSI2C_10BIT_ADDR_MODE			(1u << 30)
+#define HSI2C_HS_MODE				(1u << 29)
+
+/* I2C_AUTO_CONF Register */
+#define HSI2C_READ_WRITE			(1u << 16)
+#define HSI2C_STOP_AFTER_TRANS			(1u << 17)
+#define HSI2C_MASTER_RUN			(1u << 31)
+
+/* I2C_TIMEOUT Register */
+#define HSI2C_TIMEOUT_EN			(1u << 31)
+
+/* I2C_TRANS_STATUS register */
+#define HSI2C_MASTER_BUSY			(1u << 17)
+#define HSI2C_SLAVE_BUSY			(1u << 16)
+#define HSI2C_NO_DEV				(1u << 3)
+#define HSI2C_NO_DEV_ACK			(1u << 2)
+#define HSI2C_TRANS_ABORT			(1u << 1)
+#define HSI2C_TRANS_DONE			(1u << 0)
+
+/* Although exynos5 supports max HS-IIC speed of 3.4Mhz,
+ * but currently we are facing booting issues beyond 1Mhz
+ * So limiting HS-IIC bus speed to 1Mhz
+ */
+#define HSI2C_HS_TX_CLOCK	1000000
+#define HSI2C_FS_TX_CLOCK	400000
+
+#define HSI2C_FAST_SPD 0
+#define HSI2C_HIGH_SPD 1
+
+#endif /* __ASM_ARCH_REGS_HS_IIC_H */
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH v2] sched: Fix print format for u64
From: Fabio Estevam @ 2012-12-25 11:24 UTC (permalink / raw)
  To: mingo; +Cc: peterz, pjt, linux-kernel, Fabio Estevam

From: Fabio Estevam <fabio.estevam@freescale.com>

commit c566e8e9 (sched: Aggregate total task_group load) introduced the 
following build warning:

kernel/sched/debug.c:225:2: warning: format '%ld' expects argument of type 'long int', but argument 4 has type 'u64' [-Wformat]
kernel/sched/debug.c:225:2: warning: format '%ld' expects argument of type 'long int', but argument 3 has type 'u64' [-Wformat]

Use %llu format to print a u64 value as per Documentation/printk-formats.txt.

Signed-off-by: Fabio Estevam <fabio.estevam@freescale.com>
---
Changes since v1:
- Use llu instead of lld
 kernel/sched/debug.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c
index 2cd3c1b..83ec463 100644
--- a/kernel/sched/debug.c
+++ b/kernel/sched/debug.c
@@ -222,7 +222,7 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq)
 			cfs_rq->runnable_load_avg);
 	SEQ_printf(m, "  .%-30s: %lld\n", "blocked_load_avg",
 			cfs_rq->blocked_load_avg);
-	SEQ_printf(m, "  .%-30s: %ld\n", "tg_load_avg",
+	SEQ_printf(m, "  .%-30s: %llu\n", "tg_load_avg",
 			atomic64_read(&cfs_rq->tg->load_avg));
 	SEQ_printf(m, "  .%-30s: %lld\n", "tg_load_contrib",
 			cfs_rq->tg_load_contrib);
-- 
1.7.9.5


^ permalink raw reply related

* Re: [LTP] [PATCH] pidns30 pidns31: syscall mq_open() failed EFAULT(Bad address)
From: Monson Shao @ 2012-12-25 11:23 UTC (permalink / raw)
  To: Caspar Zhang; +Cc: ltp-list
In-Reply-To: <50D958CA.4010406@casparzhang.com>

On Tue, 2012-12-25 at 15:42 +0800, Caspar Zhang wrote:
> On 12/25/2012 03:25 PM, Monson Shao wrote:
> > 'man 3 mq_open' said mq_open() can take 2 or 4 arguments, but the implement
> > syscall(__NR_mq_open, ...) takes 4, according to [1].
> >
> > Manually tested on kernel-3.6.0 with glibc-2.16 .
> >
> > [1] glibc/sysdeps/unix/sysv/linux/mq_open.c:55
> 
> will this change the result on old kernel/glibc?
> 
> Caspar
I haven't tested it on old kernel/glibc, but 4 arguments should always
be acceptable, since this usage exists in the same file [2].

[2] testcases/kernel/containers/pidns/pidns30.c:255
> 
> >
> > Signed-off-by: Monson Shao <jshao@redhat.com>
> > ---
> >   testcases/kernel/containers/pidns/pidns30.c | 2 +-
> >   testcases/kernel/containers/pidns/pidns31.c | 2 +-
> >   2 个文件被修改,插入 2 行(+),删除 2 行(-)
> >
> > diff --git a/testcases/kernel/containers/pidns/pidns30.c b/testcases/kernel/containers/pidns/pidns30.c
> > index 71e87c1..75c736b 100644
> > --- a/testcases/kernel/containers/pidns/pidns30.c
> > +++ b/testcases/kernel/containers/pidns/pidns30.c
> > @@ -186,7 +186,7 @@ int child_fn(void *arg)
> >   	while (read(father_to_child[0], buf, 1) != 1)
> >   		sleep(1);
> >
> > -	mqd = syscall(__NR_mq_open, mqname, O_RDONLY);
> > +	mqd = syscall(__NR_mq_open, mqname, O_RDONLY, 0, NULL);
> >   	if (mqd == -1) {
> >   		perror("mq_open failed");
> >   		return 1;
> > diff --git a/testcases/kernel/containers/pidns/pidns31.c b/testcases/kernel/containers/pidns/pidns31.c
> > index 2a07489..1498d16 100644
> > --- a/testcases/kernel/containers/pidns/pidns31.c
> > +++ b/testcases/kernel/containers/pidns/pidns31.c
> > @@ -177,7 +177,7 @@ int child_fn(void *arg)
> >   	}
> >   	tst_resm(TINFO, "cinit: my father is ready to receive a message");
> >
> > -	mqd = syscall(__NR_mq_open, mqname, O_WRONLY);
> > +	mqd = syscall(__NR_mq_open, mqname, O_WRONLY, 0, NULL);
> >   	if (mqd == (mqd_t) - 1) {
> >   		tst_resm(TBROK, "cinit: mq_open() failed (%s)",
> >   			 strerror(errno));
> >
> 

-- 
Monson Shao
Kernel QA

#29 @NAY - Beijing, China - GMT+08:00
IRC: monson (#kernel-qe, #eng-china)
Internal:  83 88384
External: +86 10 6260 8384


------------------------------------------------------------------------------
LogMeIn Rescue: Anywhere, Anytime Remote support for IT. Free Trial
Remotely access PCs and mobile devices and provide instant support
Improve your efficiency, and focus on delivering more value-add services
Discover what IT Professionals Know. Rescue delivers
http://p.sf.net/sfu/logmein_12329d2d
_______________________________________________
Ltp-list mailing list
Ltp-list@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/ltp-list

^ permalink raw reply

* Re: [PATCH 1/6] uvcvideo: Set error_idx properly for extended controls API failures
From: Laurent Pinchart @ 2012-12-25 11:23 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil
In-Reply-To: <201212251215.25674.hverkuil@xs4all.nl>

Hi Hans,

On Tuesday 25 December 2012 12:15:25 Hans Verkuil wrote:
> On Mon December 24 2012 13:27:08 Laurent Pinchart wrote:
> > On Thursday 27 September 2012 17:16:15 Laurent Pinchart wrote:
> > > When one of the requested controls doesn't exist the error_idx field
> > > must reflect that situation. For G_EXT_CTRLS and S_EXT_CTRLS, error_idx
> > > must be set to the control count. For TRY_EXT_CTRLS, it must be set to
> > > the index of the unexisting control.
> > > 
> > > This issue was found by the v4l2-compliance tool.
> > 
> > I'm revisiting this patch as it has been reverted in v3.8-rc1.
> > 
> > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > > ---
> > > 
> > >  drivers/media/usb/uvc/uvc_ctrl.c |   17 ++++++++++-------
> > >  drivers/media/usb/uvc/uvc_v4l2.c |   19 ++++++++++++-------
> > >  2 files changed, 22 insertions(+), 14 deletions(-)
> > 
> > [snip]
> > 
> > > diff --git a/drivers/media/usb/uvc/uvc_v4l2.c
> > > b/drivers/media/usb/uvc/uvc_v4l2.c index f00db30..e5817b9 100644
> > > --- a/drivers/media/usb/uvc/uvc_v4l2.c
> > > +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> > > @@ -591,8 +591,10 @@ static long uvc_v4l2_do_ioctl(struct file *file,
> > 
> > [snip]
> > 
> > > @@ -637,8 +639,9 @@ static long uvc_v4l2_do_ioctl(struct file *file,
> > > unsigned int cmd, void *arg) ret = uvc_ctrl_get(chain, ctrl);
> > > 
> > >  			if (ret < 0) {
> > >  			
> > >  				uvc_ctrl_rollback(handle);
> > > 
> > > -				ctrls->error_idx = i;
> > > -				return ret;
> > > +				ctrls->error_idx = ret == -ENOENT
> > > +						 ? ctrls->count : i;
> > > +				return ret == -ENOENT ? -EINVAL : ret;
> > > 
> > >  			}
> > >  		
> > >  		}
> > >  		ctrls->error_idx = 0;
> > > 
> > > @@ -661,8 +664,10 @@ static long uvc_v4l2_do_ioctl(struct file *file,
> > > unsigned int cmd, void *arg) ret = uvc_ctrl_set(chain, ctrl);
> > > 
> > >  			if (ret < 0) {
> > >  			
> > >  				uvc_ctrl_rollback(handle);
> > > 
> > > -				ctrls->error_idx = i;
> > > -				return ret;
> > > +				ctrls->error_idx = (ret == -ENOENT &&
> > > +						    cmd == VIDIOC_S_EXT_CTRLS)
> > > +						 ? ctrls->count : i;
> > > +				return ret == -ENOENT ? -EINVAL : ret;
> > > 
> > >  			}
> > >  		
> > >  		}
> > 
> > I've reread the V4L2 specification, and the least I can say is that the
> > text is pretty ambiguous. Let's clarify it.
> > 
> > Is there a reason to differentiate between invalid control IDs and other
> > errors as far as error_idx is concerned ? It would be simpler if error_idx
> > was set to the index of the first error for get and try operations,
> > regardless of the error type. What do you think ?
> 
> There is a good reason for doing this: the G/S_EXT_CTRLS ioctls have to be
> as atomic as possible, i.e. it should try hard to prevent leaving the
> hardware in an inconsistent state because not all controls could be set. It
> can never be fully atomic since writing multiple registers over usb or i2c
> can always return errors for one of those writes, but it should certainly
> check for all the obvious errors first that do not require actually writing
> to the hardware, such as whether all the controls in the control list
> actually exist.
> 
> And for such errors error_idx should be set to the number of controls to
> indicate that none of the controls were actually set but that there was a
> problem with the list of controls itself.

For S_EXT_CTRLS, sure, but G_EXT_CTRLS doesn't modify the hardware state, so 
it could get all controls up to the erroneous one.

> Since TRY_EXT_CTRLS doesn't touch the hardware at all the error_idx can be
> set to the index of the control that caused the problem.
> 
> The documentation can definitely be improved as the difference between G/S
> and TRY with regards to error_idx is not made explicit.

-- 
Regards,

Laurent Pinchart


^ permalink raw reply


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.