Linux userland API discussions
 help / color / mirror / Atom feed
* Re: [RFC PATCH net-next v2 0/5] netns: allow to identify peer netns
From: David Ahern @ 2014-09-26 20:56 UTC (permalink / raw)
  To: Eric W. Biederman
  Cc: Andrew Morton, Cong Wang,
	containers-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Andy Lutomirski, Stephen Hemminger, netdev,
	linux-api-u79uwXL29TY76Z2rM5mHXA,
	nicolas.dichtel-pdR9zngts4EAvxtiuMwx3w, David Miller
In-Reply-To: <87tx3uun4q.fsf-JOvCrm2gF+uungPnsOpG7nhyD016LWXt@public.gmane.org>

On 9/26/14, 2:45 PM, Eric W. Biederman wrote:
> Ah.  You are talking about a system with 2k namespaces and 20-50
> services providing services in all 2k namespaces. Something completely
> different than the case of quagga you mentioned earlier.

Not at all. The earlier quagga example was a starting point on the 
bigger topic -- inefficiencies of namespaces as VRFs. In all of the 
products I have worked on there is always more than 1 service running on 
the system.

> But again this has nothing do with the peer netns work.  So if you have
> something practical to contribute please start a new thread.

Sure, I'll start a new thread.

Thanks,
David

^ permalink raw reply

* Re: eBPF verifier thoughts (Re: [PATCH v15 net-next 00/11] eBPF syscall, verifier, testsuite)
From: Alexei Starovoitov @ 2014-09-26 21:25 UTC (permalink / raw)
  To: Andy Lutomirski
  Cc: David Miller, Ingo Molnar, Linus Torvalds, Daniel Borkmann,
	Hannes Frederic Sowa, Chema Gonzalez, Eric Dumazet,
	Peter Zijlstra, Pablo Neira Ayuso, H. Peter Anvin, Andrew Morton,
	Kees Cook, Linux API, Network Development,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <CALCETrXS5KBHm-3Fz031VFPpTHC_BDOLd_zNB6DjCidZa5-x2A-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

On Fri, Sep 26, 2014 at 1:39 PM, Andy Lutomirski <luto-kltTT9wpgjJwATOyAt5JVQ@public.gmane.org> wrote:
>> not quite. there is a distinction between key and value.
>> They both come from map definition and correspond to key_size
>> and value_size, so they have to have two different corresponding
>> _internal_ types 'ptr_to_map_key' and 'ptr_to_map_value'
>> This distinction is needed to properly describe function
>> arguments constraints.
>
> But they're still just pointers to buffers of some size known to the
> verifier, right?  By calling them "pointer to map key" and "pointer to
> map value" you're tying them to map objects in a way that makes little
> sense to me.

'pointer_to_map_key' is internal argument constraint of the
in-kernel helper function. It tells verifier how to check the values
passed into function.
Just pointer + size abstraction is not enough here.
verifier has to know the type of what it's checking.

> So what's "spill part"?  Unless I misunderstood the stack tracking
> code, you're tracking each byte separately.
>
> You're also tracking the type for each stack slot separately for each
> instruction.  That looks like it'll account for the considerable
> majority of total memory usage.

verifier has to track each byte separately, because
malicious program may write a pointer into stack with 8-byte
write, then modify single byte with 1-byte write and then
try to read 8-byte back. Verifier has to catch that and
that's why it's tracking every byte-sized slot independently.

> I don't like the fact that the function proto comes from the
> environment instead of from the program.

that's must have.
in-kernel function argument constraints must come from
kernel. where else?
User program says I want to call function foo() and here
is my code that invokes it. Kernel sees prototype of this
foo() and checks arguments.
There is no point for user space program to also
pass foo() constraints. The only thing kernel can do
with this extra info is to check that it matches what
kernel already knows.

>> nope. breadth-first just doesn't work at all.
>
> Sorry, I didn't actually mean BFS.  I meant to order the search such
> that all incoming control flow edges to an insn are visited before any
> of the outgoing edges are visited.

hmm. I'm not sure how exactly you plan on achieving that.
I don't think we want to see real control/data flow graph
analysis in the kernel the way compilers do things.
It will be tens of thousands lines of code.
The algorithm you see in this verifier is straight forward and
tiny. I guess when time passes by when may get enough
courage to attempt something like this, but
today 'kiss' principle rules.

>> complexity is actually described in the doc.
>> There are several limits. Verifier will be aborted if it walks
>> more then 32k instructions or more then 1k branches.
>> So the very worst case takes micro seconds to reject
>> the program. So I don't see your concern.
>
> That this will randomly fail, then.  For all I know, there are
> existing valid BPF programs with vastly more than 32k "instructions"
> as counted by the verifier.

you need to double check your data :)
classic bpf limit is 4k instructions per program.
We're keeping the same limit for eBPF.
32k limit says that verifier will visit each instruction
no more than 8 times.
if we have a program full of branches, then yes, 32k limit will
be reached and that's exactly what 'state pruning' patch is
addressing! As I already said, I dropped it out of this set
to ease review and to keep patch set size minimal.
You can see it my tree:
https://git.kernel.org/cgit/linux/kernel/git/ast/bpf.git/commit/?h=v14&id=1d9529ae4ce24bc31ca245a156299aa9e59a29f0
I was planning to send it next.
It's small incremental patch on top of existing things.

^ permalink raw reply

* Re: eBPF verifier thoughts (Re: [PATCH v15 net-next 00/11] eBPF syscall, verifier, testsuite)
From: Alexei Starovoitov @ 2014-09-26 21:46 UTC (permalink / raw)
  To: Andy Lutomirski
  Cc: David Miller, Ingo Molnar, Linus Torvalds, Daniel Borkmann,
	Hannes Frederic Sowa, Chema Gonzalez, Eric Dumazet,
	Peter Zijlstra, Pablo Neira Ayuso, H. Peter Anvin, Andrew Morton,
	Kees Cook, Linux API, Network Development,
	linux-kernel@vger.kernel.org
In-Reply-To: <CALCETrVAQ7oRm8zDHDiUurjrMw=+ABucLWjed_P__4j8deNE7A@mail.gmail.com>

On Fri, Sep 26, 2014 at 1:42 PM, Andy Lutomirski <luto@amacapital.net> wrote:
> On Fri, Sep 26, 2014 at 1:09 PM, Alexei Starovoitov <ast@plumgrid.com> wrote:
>> On Fri, Sep 26, 2014 at 12:51 PM, Andy Lutomirski <luto@amacapital.net> wrote:
>>> On Fri, Sep 26, 2014 at 12:34 PM, Andy Lutomirski <luto@amacapital.net> wrote:
>>>
>>> To add one more point:
>>>
>>> With the current verifier design, it's impossible to write a userspace
>>> tool that can take an eBPF program and check it.  The verification is
>>> far too context-dependent for that to be possible.  I won't go so far
>>> as to say that a userspace tool needs to *exist*, but I strongly
>>> object to exposing a verification algorithm that *precludes* writing
>>> such a tool.
>>
>> that's just not true.
>> why is it not possible?
>
> Because the types of referenced objects aren't encoded in the blob
> that a user program loads, unless I'm missing something.

patch #8 'handle pseudo BPF_LD_IMM64 insn' of this set
handles first type == map. Other types will be added in the future.
The same verification can be done in user space.
It's pretty much copy paste for everything from the kernel.
I don't understand yet why you really must do it in in userspace
in addition to doing it in kernel. It's definitely doable.
Instead of asking kernel to create a map, user space
can just remember map attributes (key_size, value_size)
and continues verification in userspace.

> But the eBPF binary doesn't encode this information. In fact, the
> caller of an ebpf syscall may not even have access to this
> information.

I don't follow. What info are you talking about?
are you saying that program only that references maps via fds
is not verifiable unless one knows what this fds refer to?
yeah, but we're talking user space verification here.
user knows what maps it creates with what attributes.
Also we can add a command to this syscall to fetch map
attributes. That would be trivial _incremental_ addition, right?

> I think this is addressable as a smallish change on top of your code.
> Rather than looking up a map when you need to learn its key and value
> size, I think that all you need to do is to look in a program section
> for the key and value size (and the fact that it's a map) and confirm
> that the referenced map *matches* the stored values.

we can add extra info to the program that will encode
program assumptions about maps. Sure. Though I think
it's extra info that kernel doesn't really need, since it can
only check that program assumptions match to what
kernel already knows. Kernel cannot rely on them.
So I'm not sure what this extra check really buys.

Anyway, if you think it's a smallish change, we can do it
incrementally on top of existing stuff, right?
Why this arguing then?
Sounds like you want to help with the development?
This is great! I'm all for it :)

^ permalink raw reply

* Re: eBPF verifier thoughts (Re: [PATCH v15 net-next 00/11] eBPF syscall, verifier, testsuite)
From: Andy Lutomirski @ 2014-09-26 21:47 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: David Miller, Ingo Molnar, Linus Torvalds, Daniel Borkmann,
	Hannes Frederic Sowa, Chema Gonzalez, Eric Dumazet,
	Peter Zijlstra, Pablo Neira Ayuso, H. Peter Anvin, Andrew Morton,
	Kees Cook, Linux API, Network Development,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <CAMEtUux-WFOGABTrWW=BGUstE=Zz6agXTGCGrRJtNXDfRmx-5A-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

On Fri, Sep 26, 2014 at 2:25 PM, Alexei Starovoitov <ast-uqk4Ao+rVK5Wk0Htik3J/w@public.gmane.org> wrote:
> On Fri, Sep 26, 2014 at 1:39 PM, Andy Lutomirski <luto-kltTT9wpgjJwATOyAt5JVQ@public.gmane.org> wrote:
>>> not quite. there is a distinction between key and value.
>>> They both come from map definition and correspond to key_size
>>> and value_size, so they have to have two different corresponding
>>> _internal_ types 'ptr_to_map_key' and 'ptr_to_map_value'
>>> This distinction is needed to properly describe function
>>> arguments constraints.
>>
>> But they're still just pointers to buffers of some size known to the
>> verifier, right?  By calling them "pointer to map key" and "pointer to
>> map value" you're tying them to map objects in a way that makes little
>> sense to me.
>
> 'pointer_to_map_key' is internal argument constraint of the
> in-kernel helper function. It tells verifier how to check the values
> passed into function.
> Just pointer + size abstraction is not enough here.
> verifier has to know the type of what it's checking.

Ignore "pointer_to_map_key" -- that was an error on my part.

I still think that "pointer to map value" should be "pointer to bytes".

>
>> So what's "spill part"?  Unless I misunderstood the stack tracking
>> code, you're tracking each byte separately.
>>
>> You're also tracking the type for each stack slot separately for each
>> instruction.  That looks like it'll account for the considerable
>> majority of total memory usage.
>
> verifier has to track each byte separately, because
> malicious program may write a pointer into stack with 8-byte
> write, then modify single byte with 1-byte write and then
> try to read 8-byte back. Verifier has to catch that and
> that's why it's tracking every byte-sized slot independently.
>

Can't you just disallow the 1-byte write to the stack?

>> I don't like the fact that the function proto comes from the
>> environment instead of from the program.
>
> that's must have.
> in-kernel function argument constraints must come from
> kernel. where else?
> User program says I want to call function foo() and here
> is my code that invokes it. Kernel sees prototype of this
> foo() and checks arguments.
> There is no point for user space program to also
> pass foo() constraints. The only thing kernel can do
> with this extra info is to check that it matches what
> kernel already knows.

User says "I'm calling a function called foo that has this signature".
Kernel checks (a) that the signature is right and (b) that the call is
compliant.

>
>>> nope. breadth-first just doesn't work at all.
>>
>> Sorry, I didn't actually mean BFS.  I meant to order the search such
>> that all incoming control flow edges to an insn are visited before any
>> of the outgoing edges are visited.
>
> hmm. I'm not sure how exactly you plan on achieving that.
> I don't think we want to see real control/data flow graph
> analysis in the kernel the way compilers do things.
> It will be tens of thousands lines of code.
> The algorithm you see in this verifier is straight forward and
> tiny. I guess when time passes by when may get enough
> courage to attempt something like this, but
> today 'kiss' principle rules.

I'll try it in Python.  I bet I can get it to be shorter than the current code.

>
>>> complexity is actually described in the doc.
>>> There are several limits. Verifier will be aborted if it walks
>>> more then 32k instructions or more then 1k branches.
>>> So the very worst case takes micro seconds to reject
>>> the program. So I don't see your concern.
>>
>> That this will randomly fail, then.  For all I know, there are
>> existing valid BPF programs with vastly more than 32k "instructions"
>> as counted by the verifier.
>
> you need to double check your data :)
> classic bpf limit is 4k instructions per program.
> We're keeping the same limit for eBPF.
> 32k limit says that verifier will visit each instruction
> no more than 8 times.
> if we have a program full of branches, then yes, 32k limit will
> be reached and that's exactly what 'state pruning' patch is
> addressing! As I already said, I dropped it out of this set
> to ease review and to keep patch set size minimal.
> You can see it my tree:
> https://git.kernel.org/cgit/linux/kernel/git/ast/bpf.git/commit/?h=v14&id=1d9529ae4ce24bc31ca245a156299aa9e59a29f0
> I was planning to send it next.
> It's small incremental patch on top of existing things.

Yes, but does it work reliably?

--Andy

-- 
Andy Lutomirski
AMA Capital Management, LLC

^ permalink raw reply

* Re: eBPF verifier thoughts (Re: [PATCH v15 net-next 00/11] eBPF syscall, verifier, testsuite)
From: Andy Lutomirski @ 2014-09-26 21:48 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: David Miller, Ingo Molnar, Linus Torvalds, Daniel Borkmann,
	Hannes Frederic Sowa, Chema Gonzalez, Eric Dumazet,
	Peter Zijlstra, Pablo Neira Ayuso, H. Peter Anvin, Andrew Morton,
	Kees Cook, Linux API, Network Development,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <CAMEtUuw3rmQR5v7zrhUR=jYKrhCTp+GpndHDa-9djT3nBnhsaA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

On Fri, Sep 26, 2014 at 2:46 PM, Alexei Starovoitov <ast-uqk4Ao+rVK5Wk0Htik3J/w@public.gmane.org> wrote:
> On Fri, Sep 26, 2014 at 1:42 PM, Andy Lutomirski <luto-kltTT9wpgjJwATOyAt5JVQ@public.gmane.org> wrote:
>> On Fri, Sep 26, 2014 at 1:09 PM, Alexei Starovoitov <ast-uqk4Ao+rVK5Wk0Htik3J/w@public.gmane.org> wrote:
>>> On Fri, Sep 26, 2014 at 12:51 PM, Andy Lutomirski <luto-kltTT9wpgjJwATOyAt5JVQ@public.gmane.org> wrote:
>>>> On Fri, Sep 26, 2014 at 12:34 PM, Andy Lutomirski <luto-kltTT9wpgjJwATOyAt5JVQ@public.gmane.org> wrote:
>>>>
>>>> To add one more point:
>>>>
>>>> With the current verifier design, it's impossible to write a userspace
>>>> tool that can take an eBPF program and check it.  The verification is
>>>> far too context-dependent for that to be possible.  I won't go so far
>>>> as to say that a userspace tool needs to *exist*, but I strongly
>>>> object to exposing a verification algorithm that *precludes* writing
>>>> such a tool.
>>>
>>> that's just not true.
>>> why is it not possible?
>>
>> Because the types of referenced objects aren't encoded in the blob
>> that a user program loads, unless I'm missing something.
>
> patch #8 'handle pseudo BPF_LD_IMM64 insn' of this set
> handles first type == map. Other types will be added in the future.
> The same verification can be done in user space.
> It's pretty much copy paste for everything from the kernel.
> I don't understand yet why you really must do it in in userspace
> in addition to doing it in kernel. It's definitely doable.
> Instead of asking kernel to create a map, user space
> can just remember map attributes (key_size, value_size)
> and continues verification in userspace.
>
>> But the eBPF binary doesn't encode this information. In fact, the
>> caller of an ebpf syscall may not even have access to this
>> information.
>
> I don't follow. What info are you talking about?
> are you saying that program only that references maps via fds
> is not verifiable unless one knows what this fds refer to?
> yeah, but we're talking user space verification here.
> user knows what maps it creates with what attributes.
> Also we can add a command to this syscall to fetch map
> attributes. That would be trivial _incremental_ addition, right?

That would also work, I suppose.

>
>> I think this is addressable as a smallish change on top of your code.
>> Rather than looking up a map when you need to learn its key and value
>> size, I think that all you need to do is to look in a program section
>> for the key and value size (and the fact that it's a map) and confirm
>> that the referenced map *matches* the stored values.
>
> we can add extra info to the program that will encode
> program assumptions about maps. Sure. Though I think
> it's extra info that kernel doesn't really need, since it can
> only check that program assumptions match to what
> kernel already knows. Kernel cannot rely on them.
> So I'm not sure what this extra check really buys.
>
> Anyway, if you think it's a smallish change, we can do it
> incrementally on top of existing stuff, right?
> Why this arguing then?
> Sounds like you want to help with the development?
> This is great! I'm all for it :)



-- 
Andy Lutomirski
AMA Capital Management, LLC

^ permalink raw reply

* Re: eBPF verifier thoughts (Re: [PATCH v15 net-next 00/11] eBPF syscall, verifier, testsuite)
From: Alexei Starovoitov @ 2014-09-26 22:03 UTC (permalink / raw)
  To: Andy Lutomirski
  Cc: David Miller, Ingo Molnar, Linus Torvalds, Daniel Borkmann,
	Hannes Frederic Sowa, Chema Gonzalez, Eric Dumazet,
	Peter Zijlstra, Pablo Neira Ayuso, H. Peter Anvin, Andrew Morton,
	Kees Cook, Linux API, Network Development,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <CALCETrVLtJOYkHwsmwueZ7jsSgrNED564W+m4FmMZJZL-836mg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

On Fri, Sep 26, 2014 at 2:47 PM, Andy Lutomirski <luto-kltTT9wpgjJwATOyAt5JVQ@public.gmane.org> wrote:
>
> Can't you just disallow the 1-byte write to the stack?

of course not.
That would be extremely limiting to users.
Can you actually see yourself living with stack that only
allows 8-byte writes/reads?
The stack usage will increase a lot, since all char/short
stack variables will become 8-byte...

> I'll try it in Python.  I bet I can get it to be shorter than the current code.

Awesome challenge! :)
I'll buy you a drink of your choice if you can achieve that.
Also I'll send you our C programs that we use for
testing to make sure, your python verifier can analyze them.
If it passes, I'll be glad to rip mine out. Seriously.
Deal?

> Yes, but does it work reliably?

I'm not saying current verifier is perfect. It can get better.
So far it was enough to let people code freely on top of it.

^ permalink raw reply

* Re: eBPF verifier thoughts (Re: [PATCH v15 net-next 00/11] eBPF syscall, verifier, testsuite)
From: Andy Lutomirski @ 2014-09-26 22:07 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: David Miller, Ingo Molnar, Linus Torvalds, Daniel Borkmann,
	Hannes Frederic Sowa, Chema Gonzalez, Eric Dumazet,
	Peter Zijlstra, Pablo Neira Ayuso, H. Peter Anvin, Andrew Morton,
	Kees Cook, Linux API, Network Development,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <CAMEtUuxq7oNmDAHZ+1t4Vd-QhW6SV7eoM_juxXGEDJBF3Nfu6A-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

On Fri, Sep 26, 2014 at 3:03 PM, Alexei Starovoitov <ast-uqk4Ao+rVK5Wk0Htik3J/w@public.gmane.org> wrote:
> On Fri, Sep 26, 2014 at 2:47 PM, Andy Lutomirski <luto-kltTT9wpgjJwATOyAt5JVQ@public.gmane.org> wrote:
>>
>> Can't you just disallow the 1-byte write to the stack?
>
> of course not.
> That would be extremely limiting to users.
> Can you actually see yourself living with stack that only
> allows 8-byte writes/reads?
> The stack usage will increase a lot, since all char/short
> stack variables will become 8-byte...

How about requiring that sub-8-byte stack accesses only be to integer slots?

>
>> I'll try it in Python.  I bet I can get it to be shorter than the current code.
>
> Awesome challenge! :)
> I'll buy you a drink of your choice if you can achieve that.
> Also I'll send you our C programs that we use for
> testing to make sure, your python verifier can analyze them.
> If it passes, I'll be glad to rip mine out. Seriously.
> Deal?

Sure.

>
>> Yes, but does it work reliably?
>
> I'm not saying current verifier is perfect. It can get better.
> So far it was enough to let people code freely on top of it.



-- 
Andy Lutomirski
AMA Capital Management, LLC

^ permalink raw reply

* Re: eBPF verifier thoughts (Re: [PATCH v15 net-next 00/11] eBPF syscall, verifier, testsuite)
From: Alexei Starovoitov @ 2014-09-26 22:26 UTC (permalink / raw)
  To: Andy Lutomirski
  Cc: David Miller, Ingo Molnar, Linus Torvalds, Daniel Borkmann,
	Hannes Frederic Sowa, Chema Gonzalez, Eric Dumazet,
	Peter Zijlstra, Pablo Neira Ayuso, H. Peter Anvin, Andrew Morton,
	Kees Cook, Linux API, Network Development,
	linux-kernel@vger.kernel.org
In-Reply-To: <CALCETrWcmZVmRZARM_s9S-zZ4Xm_TcxRrzJWGwL0O9ySmVgr5A@mail.gmail.com>

On Fri, Sep 26, 2014 at 3:07 PM, Andy Lutomirski <luto@amacapital.net> wrote:
> On Fri, Sep 26, 2014 at 3:03 PM, Alexei Starovoitov <ast@plumgrid.com> wrote:
>> On Fri, Sep 26, 2014 at 2:47 PM, Andy Lutomirski <luto@amacapital.net> wrote:
>>>
>>> Can't you just disallow the 1-byte write to the stack?
>>
>> of course not.
>> That would be extremely limiting to users.
>> Can you actually see yourself living with stack that only
>> allows 8-byte writes/reads?
>> The stack usage will increase a lot, since all char/short
>> stack variables will become 8-byte...
>
> How about requiring that sub-8-byte stack accesses only be to integer slots?

you mean to reject the sub-8-byte write early if it's going
into space where pointers were stored?
That will limit stack reuse.
gcc/llvm generate code where the same stack location
is used by different variables during life of the function.
So if I reject the write early, it will break otherwise valid
programs.

^ permalink raw reply

* Re: eBPF verifier thoughts (Re: [PATCH v15 net-next 00/11] eBPF syscall, verifier, testsuite)
From: Andy Lutomirski @ 2014-09-26 22:41 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: David Miller, Ingo Molnar, Linus Torvalds, Daniel Borkmann,
	Hannes Frederic Sowa, Chema Gonzalez, Eric Dumazet,
	Peter Zijlstra, Pablo Neira Ayuso, H. Peter Anvin, Andrew Morton,
	Kees Cook, Linux API, Network Development,
	linux-kernel@vger.kernel.org
In-Reply-To: <CAMEtUuzQF+3uWFWmj5nSdYoJfo0W2F0wkvoyc4pP--EH3O_jYA@mail.gmail.com>

On Fri, Sep 26, 2014 at 3:26 PM, Alexei Starovoitov <ast@plumgrid.com> wrote:
> On Fri, Sep 26, 2014 at 3:07 PM, Andy Lutomirski <luto@amacapital.net> wrote:
>> On Fri, Sep 26, 2014 at 3:03 PM, Alexei Starovoitov <ast@plumgrid.com> wrote:
>>> On Fri, Sep 26, 2014 at 2:47 PM, Andy Lutomirski <luto@amacapital.net> wrote:
>>>>
>>>> Can't you just disallow the 1-byte write to the stack?
>>>
>>> of course not.
>>> That would be extremely limiting to users.
>>> Can you actually see yourself living with stack that only
>>> allows 8-byte writes/reads?
>>> The stack usage will increase a lot, since all char/short
>>> stack variables will become 8-byte...
>>
>> How about requiring that sub-8-byte stack accesses only be to integer slots?
>
> you mean to reject the sub-8-byte write early if it's going
> into space where pointers were stored?
> That will limit stack reuse.
> gcc/llvm generate code where the same stack location
> is used by different variables during life of the function.
> So if I reject the write early, it will break otherwise valid
> programs.

I think that a sub-8-byte write to an integer slot should leave it as
an integer and a sub-8-byte write to a non-integer slot should turn
that slot into an integer (if conversions to integer are permitted) or
be rejected otherwise.  gcc/llvm could emit an 8-byte write first, as
needed, to make this valid.

Alternatively, an integer stack slot could have a bitmask indicating
which bytes are valid.

--Andy

-- 
Andy Lutomirski
AMA Capital Management, LLC

^ permalink raw reply

* Re: eBPF verifier thoughts (Re: [PATCH v15 net-next 00/11] eBPF syscall, verifier, testsuite)
From: Alexei Starovoitov @ 2014-09-26 23:13 UTC (permalink / raw)
  To: Andy Lutomirski
  Cc: David Miller, Ingo Molnar, Linus Torvalds, Daniel Borkmann,
	Hannes Frederic Sowa, Chema Gonzalez, Eric Dumazet,
	Peter Zijlstra, Pablo Neira Ayuso, H. Peter Anvin, Andrew Morton,
	Kees Cook, Linux API, Network Development,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <CALCETrUP=LE2QNzYL8LCukeuWeumOn0bm0eqYscc7GJqq45oYA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

On Fri, Sep 26, 2014 at 3:41 PM, Andy Lutomirski <luto-kltTT9wpgjJwATOyAt5JVQ@public.gmane.org> wrote:
> On Fri, Sep 26, 2014 at 3:26 PM, Alexei Starovoitov <ast-uqk4Ao+rVK5Wk0Htik3J/w@public.gmane.org> wrote:
>> On Fri, Sep 26, 2014 at 3:07 PM, Andy Lutomirski <luto-kltTT9wpgjJwATOyAt5JVQ@public.gmane.org> wrote:
>>> On Fri, Sep 26, 2014 at 3:03 PM, Alexei Starovoitov <ast-uqk4Ao+rVK5Wk0Htik3J/w@public.gmane.org> wrote:
>>>> On Fri, Sep 26, 2014 at 2:47 PM, Andy Lutomirski <luto-kltTT9wpgjJwATOyAt5JVQ@public.gmane.org> wrote:
>>>>>
>>>>> Can't you just disallow the 1-byte write to the stack?
>>>>
>>>> of course not.
>>>> That would be extremely limiting to users.
>>>> Can you actually see yourself living with stack that only
>>>> allows 8-byte writes/reads?
>>>> The stack usage will increase a lot, since all char/short
>>>> stack variables will become 8-byte...
>>>
>>> How about requiring that sub-8-byte stack accesses only be to integer slots?
>>
>> you mean to reject the sub-8-byte write early if it's going
>> into space where pointers were stored?
>> That will limit stack reuse.
>> gcc/llvm generate code where the same stack location
>> is used by different variables during life of the function.
>> So if I reject the write early, it will break otherwise valid
>> programs.
>
> I think that a sub-8-byte write to an integer slot should leave it as
> an integer and a sub-8-byte write to a non-integer slot should turn
> that slot into an integer (if conversions to integer are permitted) or
> be rejected otherwise.  gcc/llvm could emit an 8-byte write first, as
> needed, to make this valid.

I don't think the above will work.

> Alternatively, an integer stack slot could have a bitmask indicating
> which bytes are valid.

but this one might. Let me think about it more.
Note verifier, as it stands, is using 12Kbyte of temporary
memory to track stack with byte granularity.
(it's freed as soon as verifier finishes)
This bitmask optimization, best case, will reduce it to 1.5Kbyte
at the cost of extra complexity. I'll play with this idea to
see whether this trade off is actually worth doing.
Also note that there are indirect stack reads
(see check_func_arg() -> check_stack_boundary())
used to verify that N bytes were initialized in stack
before calling into helper function.
Also in the future I was planning to allow
indirect stack write, so that helper function can
store stuff into program stack. This indirect accesses
are crossing 8-byte boundaries, so would need special
care with bitmask optimization.
We need to carefully weight all pros and cons.
imo this memory usage during verification is not a big deal,
but of course we should not waste it. I'll see what can be done.

In any case, we're talking about incremental improvements
on top of current stuff, right?

^ permalink raw reply

* Re: [PATCH v2] next: openrisc: Fix build
From: Stefan Kristiansson @ 2014-09-27  4:13 UTC (permalink / raw)
  To: Jonas Bonn
  Cc: Stephen Rothwell, linux-api, linux-kernel, Eric Paris, linux-next,
	linux, Guenter Roeck
In-Reply-To: <5425A7B5.10209@southpole.se>

On Fri, Sep 26, 2014 at 07:51:49PM +0200, Jonas Bonn wrote:
> On 09/26/2014 06:05 PM, Guenter Roeck wrote:
> > openrisc:defconfig fails to build in next-20140926 with the following error.
> >
> > In file included from arch/openrisc/kernel/signal.c:31:0:
> > ./arch/openrisc/include/asm/syscall.h: In function 'syscall_get_arch':
> > ./arch/openrisc/include/asm/syscall.h:77:9: error: 'EM_OPENRISC' undeclared
> >
> > Fix by moving EM_OPENRISC to include/uapi/linux/elf-em.h.
> >
> > Fixes: ce5d112827e5 ("ARCH: AUDIT: implement syscall_get_arch for all arches")
> > Cc: Eric Paris <eparis@redhat.com>
> > Cc: Stefan Kristiansson <stefan.kristiansson@saunalahti.fi>
> > Cc: Geert Uytterhoeven <geert@linux-m68k.org>
> > Cc: Stephen Rothwell <sfr@canb.auug.org.au>
> > Signed-off-by: Guenter Roeck <linux@roeck-us.net>
> > ---
> > v2: Only move EM_OPENRISC.
> >
> > Another possible solution for the problem would be to include asm/elf.h
> > in arch/openrisc/kernel/signal.c. I had actually submitted a patch with
> > that fix back in August (maybe that is where I remembered the problem from).
> > Wonder what happened with that patch.
> >
> > Would it make sense to drop EM_OR32 and replace it with EM_OPENRISC where
> > it is used ? binutils seems to suggest that EM_OPENRISC is the "official"
> > definition.
> 
> Do we even use EM_OR32?  Will the kernel build with the old toolchain if
> we drop it?  If yes, drop it altogether... I don't recall the details as
> to why we kept that define around at all.  And really, why bother
> supporting the old toolchain at all... it's been at least two or three
> years since EM_OPENRISC was added, hopefully people have moved on.  If
> users want to upgrade their kernel, they can update the toolchain, too,
> at this point.
> 

EM_OPENRISC was added about ten years ago, and when the OR32 things in binutils
was removed in favour for OR1K earlier this year,
EM_OR1K was added as an alias to EM_OPENRISC.

With that said, I'm putting in a vote for removing EM_OR32 as well.

Stefan

^ permalink raw reply

* Re: [PATCHv8 1/6] fanotify: enable close-on-exec on events' fd when requested in fanotify_init()
From: Heinrich Schuchardt @ 2014-09-27  7:26 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Yann Droneaud, Eric Paris, Richard Guy Briggs, Al Viro,
	linux-kernel, linux-fsdevel, stable, linux-api, Jan Kara,
	Lino Sanfilippo
In-Reply-To: <1411721898.7778.18.camel@localhost.localdomain>

Hello Andrew,

please, add the patch described in
https://lkml.org/lkml/2014/9/24/967
to the MM tree.

I have tested kernel 3.17.0-rc6 with and without the patch and it fixes 
the described error.
As the patch is valid independent of the rest of the patch set, there is 
no reason to wait for the rest to be merged.

You may add
Reviewed by: Heinrich Schuchardt <xypron.glpk@gmx.de>

Best regards

Heinrich Schuchardt




On 26.09.2014 10:58, Yann Droneaud wrote:
> Hi,
>
> Le jeudi 25 septembre 2014 à 22:57 +0200, Heinrich Schuchardt a écrit :
>> On 24.09.2014 15:11, Yann Droneaud wrote:
>>> According to commit 80af258867648 ('fanotify: groups can specify
>>> their f_flags for new fd'), file descriptors created as part of
>>> file access notification events inherit flags from the
>>> event_f_flags argument passed to syscall fanotify_init(2).
>>>
>>> So while it is legal for userspace to call fanotify_init() with
>>> O_CLOEXEC as part of its second argument, O_CLOEXEC is currently
>>> silently ignored.
>>>
>>> Indeed event_f_flags are only given to dentry_open(), which only
>>> seems to care about O_ACCMODE and O_PATH in do_dentry_open(),
>>> O_DIRECT in open_check_o_direct() and O_LARGEFILE in
>>> generic_file_open().
>>
>> I tested on kernel 3.17.0-rc5. I passed O_CLOEXEC in event_f_flags.
>> When I called fcnt(event_metadata->fd, F_GETFD) it did not return
>> FD_CLOEXEC. So I can confirm your observation that O_CLOEXEC is not
>> working as expected.
>>
>> I found this definition
>> #define get_unused_fd() get_unused_fd_flags(0)
>>
>> So essentially when get_unused_fd() is called for a fanotify event
>> O_CLOEXEC is ignored.
>>
>> This is what your patch fixes.
>>

^ permalink raw reply

* Re: [PATCH 8/8] iio: Add ABI documentation for input current readings
From: Jonathan Cameron @ 2014-09-27 10:23 UTC (permalink / raw)
  To: Adam Thomson, Lee Jones, Samuel Ortiz,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, Sebastian Reichel,
	Dmitry Eremin-Solenikov, David Woodhouse,
	linux-pm-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Andrew Morton, Joe Perches,
	linux-api-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	support.opensource-WBD+wuPFNBhBDgjK7y7TUQ
In-Reply-To: <b578cdd53527bb71c271eefadd1ce1bac175b5a7.1411396719.git.Adam.Thomson.Opensource-WBD+wuPFNBhBDgjK7y7TUQ@public.gmane.org>

On 23/09/14 11:53, Adam Thomson wrote:
> Add information on in_current related readings.
> 
> Signed-off-by: Adam Thomson <Adam.Thomson.Opensource-WBD+wuPFNBhBDgjK7y7TUQ@public.gmane.org>
This crossed with a similar patch also adding some docs for current channels.
That just missed this cycle anyway so I'll probably just merge this in as
an extension of that patch when we are happy with the rest of the series.
That patch covers the raw attribute but doesn't add the other entries..
(in the togreg branch of iio.git)

Jonathan
> ---
>  Documentation/ABI/testing/sysfs-bus-iio | 17 +++++++++++++++++
>  1 file changed, 17 insertions(+)
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio
> index d760b02..37cd4d4 100644
> --- a/Documentation/ABI/testing/sysfs-bus-iio
> +++ b/Documentation/ABI/testing/sysfs-bus-iio
> @@ -92,6 +92,18 @@ Description:
>  		is required is a consistent labeling.  Units after application
>  		of scale and offset are millivolts.
> 
> +What:		/sys/bus/iio/devices/iio:deviceX/in_currentY_raw
> +What:		/sys/bus/iio/devices/iio:deviceX/in_currentY_supply_raw
> +KernelVersion:	3.17
> +Contact:	linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> +Description:
> +		Raw (unscaled no bias removal etc.) current measurement from
> +		channel Y. In special cases where the channel does not
> +		correspond to externally available input one of the named
> +		versions may be used. The number must always be specified and
> +		unique to allow association with event codes. Units after
> +		application of scale and offset are milliamps.
> +
>  What:		/sys/bus/iio/devices/iio:deviceX/in_capacitanceY_raw
>  KernelVersion:	3.2
>  Contact:	linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> @@ -227,6 +239,8 @@ What:		/sys/bus/iio/devices/iio:deviceX/in_accel_y_offset
>  What:		/sys/bus/iio/devices/iio:deviceX/in_accel_z_offset
>  What:		/sys/bus/iio/devices/iio:deviceX/in_voltageY_offset
>  What:		/sys/bus/iio/devices/iio:deviceX/in_voltage_offset
> +What:		/sys/bus/iio/devices/iio:deviceX/in_currentY_offset
> +What:		/sys/bus/iio/devices/iio:deviceX/in_current_offset
>  What:		/sys/bus/iio/devices/iio:deviceX/in_tempY_offset
>  What:		/sys/bus/iio/devices/iio:deviceX/in_temp_offset
>  What:		/sys/bus/iio/devices/iio:deviceX/in_pressureY_offset
> @@ -249,8 +263,11 @@ Description:
>  		to the _raw output.
> 
>  What:		/sys/bus/iio/devices/iio:deviceX/in_voltageY_scale
> +What:		/sys/bus/iio/devices/iio:deviceX/in_currentY_scale
>  What:		/sys/bus/iio/devices/iio:deviceX/in_voltageY_supply_scale
> +What:		/sys/bus/iio/devices/iio:deviceX/in_currentY_supply_scale
>  What:		/sys/bus/iio/devices/iio:deviceX/in_voltage_scale
> +What:		/sys/bus/iio/devices/iio:deviceX/in_current_scale
>  What:		/sys/bus/iio/devices/iio:deviceX/out_voltageY_scale
>  What:		/sys/bus/iio/devices/iio:deviceX/out_altvoltageY_scale
>  What:		/sys/bus/iio/devices/iio:deviceX/in_accel_scale
> --
> 1.9.3
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

^ permalink raw reply

* Re: [PATCH 1/8] mfd: Add support for DA9150 combined charger & fuel-gauge device
From: Jonathan Cameron @ 2014-09-27 10:33 UTC (permalink / raw)
  To: Adam Thomson, Lee Jones, Samuel Ortiz,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, Sebastian Reichel,
	Dmitry Eremin-Solenikov, David Woodhouse,
	linux-pm-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Andrew Morton, Joe Perches,
	linux-api-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	support.opensource-WBD+wuPFNBhBDgjK7y7TUQ
In-Reply-To: <8fd576055fd9e4f8c75cba06d6ebb13fea670920.1411396719.git.Adam.Thomson.Opensource-WBD+wuPFNBhBDgjK7y7TUQ@public.gmane.org>

On 23/09/14 11:53, Adam Thomson wrote:
> DA9150 is a combined Charger and Fuel-Gauge IC, with additional
> GPIO and GPADC functionality.
>
> Signed-off-by: Adam Thomson <Adam.Thomson.Opensource-WBD+wuPFNBhBDgjK7y7TUQ@public.gmane.org>
A random suggestion to reduced the massive set of defines... Why
not just compute the shift from the mask where needed?
Just use ffs(MASK).

No idea if anyone else will like the suggestion though ;)
Otherwise looks fine to me.
> ---
>  drivers/mfd/Kconfig                  |   12 +
>  drivers/mfd/Makefile                 |    2 +-
>  drivers/mfd/da9150-core.c            |  413 ++++++++++++
>  include/linux/mfd/da9150/core.h      |   68 ++
>  include/linux/mfd/da9150/registers.h | 1155 ++++++++++++++++++++++++++++++++++
>  5 files changed, 1649 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/mfd/da9150-core.c
>  create mode 100644 include/linux/mfd/da9150/core.h
>  create mode 100644 include/linux/mfd/da9150/registers.h
>
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index de5abf2..e29fc9a 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -183,6 +183,18 @@ config MFD_DA9063
>  	  Additional drivers must be enabled in order to use the functionality
>  	  of the device.
>
> +config MFD_DA9150
> +	tristate "Dialog Semiconductor DA9150 Charger Fuel-Gauge chip"
> +	depends on I2C=y
> +	select MFD_CORE
> +	select REGMAP_I2C
> +	select REGMAP_IRQ
> +	help
> +	  This adds support for the DA9150 integrated charger and fuel-gauge
> +	  chip. This driver provides common support for accessing the device.
> +	  Additional drivers must be enabled in order to use the specific
> +	  features of the device.
> +
>  config MFD_MC13XXX
>  	tristate
>  	depends on (SPI_MASTER || I2C)
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index f001487..f5484b3 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -113,7 +113,7 @@ obj-$(CONFIG_MFD_DA9055)	+= da9055.o
>
>  da9063-objs			:= da9063-core.o da9063-irq.o da9063-i2c.o
>  obj-$(CONFIG_MFD_DA9063)	+= da9063.o
> -
> +obj-$(CONFIG_MFD_DA9150)	+= da9150-core.o
>  obj-$(CONFIG_MFD_MAX14577)	+= max14577.o
>  obj-$(CONFIG_MFD_MAX77686)	+= max77686.o
>  obj-$(CONFIG_MFD_MAX77693)	+= max77693.o
> diff --git a/drivers/mfd/da9150-core.c b/drivers/mfd/da9150-core.c
> new file mode 100644
> index 0000000..1de6db7
> --- /dev/null
> +++ b/drivers/mfd/da9150-core.c
> @@ -0,0 +1,413 @@
> +/*
> + * DA9150 Core MFD Driver
> + *
> + * Copyright (c) 2014 Dialog Semiconductor
> + *
> + * Author: Adam Thomson <Adam.Thomson.Opensource-WBD+wuPFNBhBDgjK7y7TUQ@public.gmane.org>
> + *
> + * This program is free software; you can redistribute  it and/or modify it
> + * under  the terms of  the GNU General  Public License as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/i2c.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +#include <linux/irq.h>
> +#include <linux/interrupt.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/da9150/core.h>
> +#include <linux/mfd/da9150/registers.h>
> +
> +static bool da9150_volatile_reg(struct device *dev, unsigned int reg)
> +{
> +	switch (reg) {
> +	case DA9150_PAGE_CON:
> +	case DA9150_STATUS_A:
> +	case DA9150_STATUS_B:
> +	case DA9150_STATUS_C:
> +	case DA9150_STATUS_D:
> +	case DA9150_STATUS_E:
> +	case DA9150_STATUS_F:
> +	case DA9150_STATUS_G:
> +	case DA9150_STATUS_H:
> +	case DA9150_STATUS_I:
> +	case DA9150_STATUS_J:
> +	case DA9150_STATUS_K:
> +	case DA9150_STATUS_L:
> +	case DA9150_STATUS_N:
> +	case DA9150_FAULT_LOG_A:
> +	case DA9150_FAULT_LOG_B:
> +	case DA9150_EVENT_E:
> +	case DA9150_EVENT_F:
> +	case DA9150_EVENT_G:
> +	case DA9150_EVENT_H:
> +	case DA9150_CONTROL_B:
> +	case DA9150_CONTROL_C:
> +	case DA9150_GPADC_MAN:
> +	case DA9150_GPADC_RES_A:
> +	case DA9150_GPADC_RES_B:
> +	case DA9150_ADETVB_CFG_C:
> +	case DA9150_ADETD_STAT:
> +	case DA9150_ADET_CMPSTAT:
> +	case DA9150_ADET_CTRL_A:
> +	case DA9150_PPR_TCTR_B:
> +	case DA9150_COREBTLD_STAT_A:
> +	case DA9150_CORE_DATA_A:
> +	case DA9150_CORE_DATA_B:
> +	case DA9150_CORE_DATA_C:
> +	case DA9150_CORE_DATA_D:
> +	case DA9150_CORE2WIRE_STAT_A:
> +	case DA9150_FW_CTRL_C:
> +	case DA9150_FG_CTRL_B:
> +	case DA9150_FW_CTRL_B:
> +	case DA9150_GPADC_CMAN:
> +	case DA9150_GPADC_CRES_A:
> +	case DA9150_GPADC_CRES_B:
> +	case DA9150_CC_ICHG_RES_A:
> +	case DA9150_CC_ICHG_RES_B:
> +	case DA9150_CC_IAVG_RES_A:
> +	case DA9150_CC_IAVG_RES_B:
> +	case DA9150_TAUX_CTRL_A:
> +	case DA9150_TAUX_VALUE_H:
> +	case DA9150_TAUX_VALUE_L:
> +	case DA9150_TBAT_RES_A:
> +	case DA9150_TBAT_RES_B:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static const struct regmap_range_cfg da9150_range_cfg[] = {
> +	{
> +		.range_min = DA9150_PAGE_CON,
> +		.range_max = DA9150_TBAT_RES_B,
> +		.selector_reg = DA9150_PAGE_CON,
> +		.selector_mask = DA9150_I2C_PAGE_MASK,
> +		.selector_shift = DA9150_I2C_PAGE_SHIFT,
> +		.window_start = 0,
> +		.window_len = 256,
> +	},
> +};
> +
> +static struct regmap_config da9150_regmap_config = {
> +	.reg_bits = 8,
> +	.val_bits = 8,
> +	.ranges = da9150_range_cfg,
> +	.num_ranges = ARRAY_SIZE(da9150_range_cfg),
> +	.max_register = DA9150_TBAT_RES_B,
> +
> +	.cache_type = REGCACHE_RBTREE,
> +
> +	.volatile_reg = da9150_volatile_reg,
> +};
> +
> +u8 da9150_reg_read(struct da9150 *da9150, u16 reg)
> +{
> +	int val, ret;
> +
> +	ret = regmap_read(da9150->regmap, reg, &val);
> +	if (ret)
> +		dev_err(da9150->dev, "Failed to read from reg 0x%x: %d\n",
> +			reg, ret);
> +
> +	return (u8) val;
> +}
> +EXPORT_SYMBOL_GPL(da9150_reg_read);
> +
> +void da9150_reg_write(struct da9150 *da9150, u16 reg, u8 val)
> +{
> +	int ret;
> +
> +	ret = regmap_write(da9150->regmap, reg, val);
> +	if (ret)
> +		dev_err(da9150->dev, "Failed to write to reg 0x%x: %d\n",
> +			reg, ret);
> +}
> +EXPORT_SYMBOL_GPL(da9150_reg_write);
> +
> +void da9150_set_bits(struct da9150 *da9150, u16 reg, u8 mask, u8 val)
> +{
> +	int ret;
> +
> +	ret = regmap_update_bits(da9150->regmap, reg, mask, val);
> +	if (ret)
> +		dev_err(da9150->dev, "Failed to set bits in reg 0x%x: %d\n",
> +			reg, ret);
> +}
> +EXPORT_SYMBOL_GPL(da9150_set_bits);
> +
> +void da9150_bulk_read(struct da9150 *da9150, u16 reg, int count, u8 *buf)
> +{
> +	int ret;
> +
> +	ret = regmap_bulk_read(da9150->regmap, reg, buf, count);
> +	if (ret)
> +		dev_err(da9150->dev, "Failed to bulk read from reg 0x%x: %d\n",
> +			reg, ret);
> +}
> +EXPORT_SYMBOL_GPL(da9150_bulk_read);
> +
> +void da9150_bulk_write(struct da9150 *da9150, u16 reg, int count, const u8 *buf)
> +{
> +	int ret;
> +
> +	ret = regmap_raw_write(da9150->regmap, reg, buf, count);
> +	if (ret)
> +		dev_err(da9150->dev, "Failed to bulk write to reg 0x%x %d\n",
> +			reg, ret);
> +}
> +EXPORT_SYMBOL_GPL(da9150_bulk_write);
> +
> +static struct regmap_irq da9150_irqs[] = {
> +	[DA9150_IRQ_VBUS] = {
> +		.reg_offset = 0,
> +		.mask = DA9150_E_VBUS_MASK,
> +	},
> +	[DA9150_IRQ_CHG] = {
> +		.reg_offset = 0,
> +		.mask = DA9150_E_CHG_MASK,
> +	},
> +	[DA9150_IRQ_TCLASS] = {
> +		.reg_offset = 0,
> +		.mask = DA9150_E_TCLASS_MASK,
> +	},
> +	[DA9150_IRQ_TJUNC] = {
> +		.reg_offset = 0,
> +		.mask = DA9150_E_TJUNC_MASK,
> +	},
> +	[DA9150_IRQ_VFAULT] = {
> +		.reg_offset = 0,
> +		.mask = DA9150_E_VFAULT_MASK,
> +	},
> +	[DA9150_IRQ_CONF] = {
> +		.reg_offset = 1,
> +		.mask = DA9150_E_CONF_MASK,
> +	},
> +	[DA9150_IRQ_DAT] = {
> +		.reg_offset = 1,
> +		.mask = DA9150_E_DAT_MASK,
> +	},
> +	[DA9150_IRQ_DTYPE] = {
> +		.reg_offset = 1,
> +		.mask = DA9150_E_DTYPE_MASK,
> +	},
> +	[DA9150_IRQ_ID] = {
> +		.reg_offset = 1,
> +		.mask = DA9150_E_ID_MASK,
> +	},
> +	[DA9150_IRQ_ADP] = {
> +		.reg_offset = 1,
> +		.mask = DA9150_E_ADP_MASK,
> +	},
> +	[DA9150_IRQ_SESS_END] = {
> +		.reg_offset = 1,
> +		.mask = DA9150_E_SESS_END_MASK,
> +	},
> +	[DA9150_IRQ_SESS_VLD] = {
> +		.reg_offset = 1,
> +		.mask = DA9150_E_SESS_VLD_MASK,
> +	},
> +	[DA9150_IRQ_FG] = {
> +		.reg_offset = 2,
> +		.mask = DA9150_E_FG_MASK,
> +	},
> +	[DA9150_IRQ_GP] = {
> +		.reg_offset = 2,
> +		.mask = DA9150_E_GP_MASK,
> +	},
> +	[DA9150_IRQ_TBAT] = {
> +		.reg_offset = 2,
> +		.mask = DA9150_E_TBAT_MASK,
> +	},
> +	[DA9150_IRQ_GPIOA] = {
> +		.reg_offset = 2,
> +		.mask = DA9150_E_GPIOA_MASK,
> +	},
> +	[DA9150_IRQ_GPIOB] = {
> +		.reg_offset = 2,
> +		.mask = DA9150_E_GPIOB_MASK,
> +	},
> +	[DA9150_IRQ_GPIOC] = {
> +		.reg_offset = 2,
> +		.mask = DA9150_E_GPIOC_MASK,
> +	},
> +	[DA9150_IRQ_GPIOD] = {
> +		.reg_offset = 2,
> +		.mask = DA9150_E_GPIOD_MASK,
> +	},
> +	[DA9150_IRQ_GPADC] = {
> +		.reg_offset = 2,
> +		.mask = DA9150_E_GPADC_MASK,
> +	},
> +	[DA9150_IRQ_WKUP] = {
> +		.reg_offset = 3,
> +		.mask = DA9150_E_WKUP_MASK,
> +	},
> +};
> +
> +static struct regmap_irq_chip da9150_regmap_irq_chip = {
> +	.name = "da9150_irq",
> +	.status_base = DA9150_EVENT_E,
> +	.mask_base = DA9150_IRQ_MASK_E,
> +	.ack_base = DA9150_EVENT_E,
> +	.num_regs = DA9150_NUM_IRQ_REGS,
> +	.irqs = da9150_irqs,
> +	.num_irqs = ARRAY_SIZE(da9150_irqs),
> +};
> +
> +static struct resource da9150_gpadc_resources[] = {
> +	{
> +		.name = "GPADC",
> +		.start = DA9150_IRQ_GPADC,
> +		.end = DA9150_IRQ_GPADC,
> +		.flags = IORESOURCE_IRQ,
> +	},
> +};
> +
> +static struct resource da9150_charger_resources[] = {
> +	{
> +		.name = "CHG_STATUS",
> +		.start = DA9150_IRQ_CHG,
> +		.end = DA9150_IRQ_CHG,
> +		.flags = IORESOURCE_IRQ,
> +	},
> +	{
> +		.name = "CHG_TJUNC",
> +		.start = DA9150_IRQ_TJUNC,
> +		.end = DA9150_IRQ_TJUNC,
> +		.flags = IORESOURCE_IRQ,
> +	},
> +	{
> +		.name = "CHG_VFAULT",
> +		.start = DA9150_IRQ_VFAULT,
> +		.end = DA9150_IRQ_VFAULT,
> +		.flags = IORESOURCE_IRQ,
> +	},
> +	{
> +		.name = "CHG_VBUS",
> +		.start = DA9150_IRQ_VBUS,
> +		.end = DA9150_IRQ_VBUS,
> +		.flags = IORESOURCE_IRQ,
> +	},
> +};
> +
> +static struct mfd_cell da9150_devs[] = {
> +	{
> +		.name = "da9150-gpadc",
> +		.of_compatible = "dlg,da9150-gpadc",
> +		.resources = da9150_gpadc_resources,
> +		.num_resources = ARRAY_SIZE(da9150_gpadc_resources),
> +	},
> +	{
> +		.name = "da9150-charger",
> +		.of_compatible = "dlg,da9150-charger",
> +		.resources = da9150_charger_resources,
> +		.num_resources = ARRAY_SIZE(da9150_charger_resources),
> +	},
> +};
> +
> +static int da9150_probe(struct i2c_client *client,
> +			const struct i2c_device_id *id)
> +{
> +	struct da9150 *da9150;
> +	struct da9150_pdata *pdata = dev_get_platdata(&client->dev);
> +	int ret;
> +
> +	da9150 = devm_kzalloc(&client->dev, sizeof(*da9150), GFP_KERNEL);
> +	if (!da9150)
> +		return -ENOMEM;
> +
> +	da9150->dev = &client->dev;
> +	da9150->irq = client->irq;
> +	i2c_set_clientdata(client, da9150);
> +
> +	da9150->regmap = devm_regmap_init_i2c(client, &da9150_regmap_config);
> +	if (IS_ERR(da9150->regmap)) {
> +		ret = PTR_ERR(da9150->regmap);
> +		dev_err(da9150->dev, "Failed to allocate register map: %d\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	da9150->irq_base = pdata ? pdata->irq_base : -1;
> +
> +	ret = regmap_add_irq_chip(da9150->regmap, da9150->irq,
> +				  IRQF_TRIGGER_LOW | IRQF_ONESHOT,
> +				  da9150->irq_base, &da9150_regmap_irq_chip,
> +				  &da9150->regmap_irq_data);
> +	if (ret)
> +		return ret;
> +
> +	da9150->irq_base = regmap_irq_chip_get_base(da9150->regmap_irq_data);
> +	enable_irq_wake(da9150->irq);
> +
> +	ret = mfd_add_devices(da9150->dev, -1, da9150_devs,
> +			      ARRAY_SIZE(da9150_devs), NULL,
> +			      da9150->irq_base, NULL);
> +	if (ret) {
> +		dev_err(da9150->dev, "Failed to add child devices: %d\n", ret);
> +		regmap_del_irq_chip(da9150->irq, da9150->regmap_irq_data);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int da9150_remove(struct i2c_client *client)
> +{
> +	struct da9150 *da9150 = i2c_get_clientdata(client);
> +
> +	regmap_del_irq_chip(da9150->irq, da9150->regmap_irq_data);
> +	mfd_remove_devices(da9150->dev);
> +
> +	return 0;
> +}
> +
> +static void da9150_shutdown(struct i2c_client *client)
> +{
> +	struct da9150 *da9150 = i2c_get_clientdata(client);
> +
> +	/* Make sure we have a wakup source for the device */
> +	da9150_set_bits(da9150, DA9150_CONFIG_D,
> +			DA9150_WKUP_PM_EN_MASK,
> +			DA9150_WKUP_PM_EN_MASK);
> +
> +	/* Set device to DISABLED mode */
> +	da9150_set_bits(da9150, DA9150_CONTROL_C,
> +			DA9150_DISABLE_MASK, DA9150_DISABLE_MASK);
> +}
> +
> +static const struct i2c_device_id da9150_i2c_id[] = {
> +	{ "da9150", },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, da9150_i2c_id);
> +
> +static const struct of_device_id da9150_of_match[] = {
> +	{ .compatible = "dlg,da9150", },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, da9150_of_match);
> +
> +static struct i2c_driver da9150_driver = {
> +	.driver	= {
> +		.name	= "da9150",
> +		.of_match_table = of_match_ptr(da9150_of_match),
> +	},
> +	.probe		= da9150_probe,
> +	.remove		= da9150_remove,
> +	.shutdown	= da9150_shutdown,
> +	.id_table	= da9150_i2c_id,
> +};
> +
> +module_i2c_driver(da9150_driver);
> +
> +MODULE_DESCRIPTION("MFD Core Driver for DA9150");
> +MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource-WBD+wuPFNBhBDgjK7y7TUQ@public.gmane.org");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/mfd/da9150/core.h b/include/linux/mfd/da9150/core.h
> new file mode 100644
> index 0000000..76e6689
> --- /dev/null
> +++ b/include/linux/mfd/da9150/core.h
> @@ -0,0 +1,68 @@
> +/*
> + * DA9150 MFD Driver - Core Data
> + *
> + * Copyright (c) 2014 Dialog Semiconductor
> + *
> + * Author: Adam Thomson <Adam.Thomson.Opensource-WBD+wuPFNBhBDgjK7y7TUQ@public.gmane.org>
> + *
> + * This program is free software; you can redistribute  it and/or modify it
> + * under  the terms of  the GNU General  Public License as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + */
> +
> +#ifndef __DA9150_CORE_H
> +#define __DA9150_CORE_H
> +
> +#include <linux/device.h>
> +#include <linux/interrupt.h>
> +#include <linux/regmap.h>
> +
> +/* I2C address paging */
> +#define DA9150_REG_PAGE_SHIFT	8
> +#define DA9150_REG_PAGE_MASK	0xFF
> +
> +/* IRQs */
> +#define DA9150_NUM_IRQ_REGS	4
> +#define DA9150_IRQ_VBUS		0
> +#define DA9150_IRQ_CHG		1
> +#define DA9150_IRQ_TCLASS	2
> +#define DA9150_IRQ_TJUNC	3
> +#define DA9150_IRQ_VFAULT	4
> +#define DA9150_IRQ_CONF		5
> +#define DA9150_IRQ_DAT		6
> +#define DA9150_IRQ_DTYPE	7
> +#define DA9150_IRQ_ID		8
> +#define DA9150_IRQ_ADP		9
> +#define DA9150_IRQ_SESS_END	10
> +#define DA9150_IRQ_SESS_VLD	11
> +#define DA9150_IRQ_FG		12
> +#define DA9150_IRQ_GP		13
> +#define DA9150_IRQ_TBAT		14
> +#define DA9150_IRQ_GPIOA	15
> +#define DA9150_IRQ_GPIOB	16
> +#define DA9150_IRQ_GPIOC	17
> +#define DA9150_IRQ_GPIOD	18
> +#define DA9150_IRQ_GPADC	19
> +#define DA9150_IRQ_WKUP		20
> +
> +struct da9150_pdata {
> +	int irq_base;
> +};
> +
> +struct da9150 {
> +	struct device *dev;
> +	struct regmap *regmap;
> +	struct regmap_irq_chip_data *regmap_irq_data;
> +	int irq;
> +	int irq_base;
> +};
> +
> +/* Device I/O */
> +u8 da9150_reg_read(struct da9150 *da9150, u16 reg);
> +void da9150_reg_write(struct da9150 *da9150, u16 reg, u8 val);
> +void da9150_set_bits(struct da9150 *da9150, u16 reg, u8 mask, u8 val);
> +
> +void da9150_bulk_read(struct da9150 *da9150, u16 reg, int count, u8 *buf);
> +void da9150_bulk_write(struct da9150 *da9150, u16 reg, int count, const u8 *buf);
> +#endif /* __DA9150_CORE_H */
> diff --git a/include/linux/mfd/da9150/registers.h b/include/linux/mfd/da9150/registers.h
> new file mode 100644
> index 0000000..27ca6ee
> --- /dev/null
> +++ b/include/linux/mfd/da9150/registers.h
> @@ -0,0 +1,1155 @@
> +/*
> + * DA9150 MFD Driver - Registers
> + *
> + * Copyright (c) 2014 Dialog Semiconductor
> + *
> + * Author: Adam Thomson <Adam.Thomson.Opensource-WBD+wuPFNBhBDgjK7y7TUQ@public.gmane.org>
> + *
> + * This program is free software; you can redistribute  it and/or modify it
> + * under  the terms of  the GNU General  Public License as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + */
> +
> +#ifndef __DA9150_REGISTERS_H
> +#define __DA9150_REGISTERS_H
> +
> +#include <linux/bitops.h>
> +
> +/* Registers */
> +#define DA9150_PAGE_CON			0x000
> +#define DA9150_STATUS_A			0x068
> +#define DA9150_STATUS_B			0x069
> +#define DA9150_STATUS_C			0x06A
> +#define DA9150_STATUS_D			0x06B
> +#define DA9150_STATUS_E			0x06C
> +#define DA9150_STATUS_F			0x06D
> +#define DA9150_STATUS_G			0x06E
> +#define DA9150_STATUS_H			0x06F
> +#define DA9150_STATUS_I			0x070
> +#define DA9150_STATUS_J			0x071
> +#define DA9150_STATUS_K			0x072
> +#define DA9150_STATUS_L			0x073
> +#define DA9150_STATUS_N			0x074
> +#define DA9150_FAULT_LOG_A		0x076
> +#define DA9150_FAULT_LOG_B		0x077
> +#define DA9150_EVENT_E			0x078
> +#define DA9150_EVENT_F			0x079
> +#define DA9150_EVENT_G			0x07A
> +#define DA9150_EVENT_H			0x07B
> +#define DA9150_IRQ_MASK_E		0x07C
> +#define DA9150_IRQ_MASK_F		0x07D
> +#define DA9150_IRQ_MASK_G		0x07E
> +#define DA9150_IRQ_MASK_H		0x07F
> +#define DA9150_PAGE_CON_1		0x080
> +#define DA9150_CONFIG_A			0x0E0
> +#define DA9150_CONFIG_B			0x0E1
> +#define DA9150_CONFIG_C			0x0E2
> +#define DA9150_CONFIG_D			0x0E3
> +#define DA9150_CONFIG_E			0x0E4
> +#define DA9150_CONTROL_A		0x0E5
> +#define DA9150_CONTROL_B		0x0E6
> +#define DA9150_CONTROL_C		0x0E7
> +#define DA9150_GPIO_A_B			0x0E8
> +#define DA9150_GPIO_C_D			0x0E9
> +#define DA9150_GPIO_MODE_CONT		0x0EA
> +#define DA9150_GPIO_CTRL_B		0x0EB
> +#define DA9150_GPIO_CTRL_A		0x0EC
> +#define DA9150_GPIO_CTRL_C		0x0ED
> +#define DA9150_GPIO_CFG_A		0x0EE
> +#define DA9150_GPIO_CFG_B		0x0EF
> +#define DA9150_GPIO_CFG_C		0x0F0
> +#define DA9150_GPADC_MAN		0x0F2
> +#define DA9150_GPADC_RES_A		0x0F4
> +#define DA9150_GPADC_RES_B		0x0F5
> +#define DA9150_PAGE_CON_2		0x100
> +#define DA9150_OTP_CONT_SHARED		0x101
> +#define DA9150_INTERFACE_SHARED		0x105
> +#define DA9150_CONFIG_A_SHARED		0x106
> +#define DA9150_CONFIG_D_SHARED		0x109
> +#define DA9150_ADETVB_CFG_C		0x150
> +#define DA9150_ADETD_STAT		0x151
> +#define DA9150_ADET_CMPSTAT		0x152
> +#define DA9150_ADET_CTRL_A		0x153
> +#define DA9150_ADETVB_CFG_B		0x154
> +#define DA9150_ADETVB_CFG_A		0x155
> +#define DA9150_ADETAC_CFG_A		0x156
> +#define DA9150_ADDETAC_CFG_B		0x157
> +#define DA9150_ADETAC_CFG_C		0x158
> +#define DA9150_ADETAC_CFG_D		0x159
> +#define DA9150_ADETVB_CFG_D		0x15A
> +#define DA9150_ADETID_CFG_A		0x15B
> +#define DA9150_ADET_RID_PT_CHG_H	0x15C
> +#define DA9150_ADET_RID_PT_CHG_L	0x15D
> +#define DA9150_PPR_TCTR_B		0x160
> +#define DA9150_PPR_BKCTRL_A		0x163
> +#define DA9150_PPR_BKCFG_A		0x164
> +#define DA9150_PPR_BKCFG_B		0x165
> +#define DA9150_PPR_CHGCTRL_A		0x166
> +#define DA9150_PPR_CHGCTRL_B		0x167
> +#define DA9150_PPR_CHGCTRL_C		0x168
> +#define DA9150_PPR_TCTR_A		0x169
> +#define DA9150_PPR_CHGCTRL_D		0x16A
> +#define DA9150_PPR_CHGCTRL_E		0x16B
> +#define DA9150_PPR_CHGCTRL_F		0x16C
> +#define DA9150_PPR_CHGCTRL_G		0x16D
> +#define DA9150_PPR_CHGCTRL_H		0x16E
> +#define DA9150_PPR_CHGCTRL_I		0x16F
> +#define DA9150_PPR_CHGCTRL_J		0x170
> +#define DA9150_PPR_CHGCTRL_K		0x171
> +#define DA9150_PPR_CHGCTRL_L		0x172
> +#define DA9150_PPR_CHGCTRL_M		0x173
> +#define DA9150_PPR_THYST_A		0x174
> +#define DA9150_PPR_THYST_B		0x175
> +#define DA9150_PPR_THYST_C		0x176
> +#define DA9150_PPR_THYST_D		0x177
> +#define DA9150_PPR_THYST_E		0x178
> +#define DA9150_PPR_THYST_F		0x179
> +#define DA9150_PPR_THYST_G		0x17A
> +#define DA9150_PAGE_CON_3		0x180
> +#define DA9150_PAGE_CON_4		0x200
> +#define DA9150_PAGE_CON_5		0x280
> +#define DA9150_PAGE_CON_6		0x300
> +#define DA9150_COREBTLD_STAT_A		0x302
> +#define DA9150_COREBTLD_CTRL_A		0x303
> +#define DA9150_CORE_CONFIG_A		0x304
> +#define DA9150_CORE_CONFIG_C		0x305
> +#define DA9150_CORE_CONFIG_B		0x306
> +#define DA9150_CORE_CFG_DATA_A		0x307
> +#define DA9150_CORE_CFG_DATA_B		0x308
> +#define DA9150_CORE_CMD_A		0x309
> +#define DA9150_CORE_DATA_A		0x30A
> +#define DA9150_CORE_DATA_B		0x30B
> +#define DA9150_CORE_DATA_C		0x30C
> +#define DA9150_CORE_DATA_D		0x30D
> +#define DA9150_CORE2WIRE_STAT_A		0x310
> +#define DA9150_CORE2WIRE_CTRL_A		0x311
> +#define DA9150_FW_CTRL_A		0x312
> +#define DA9150_FW_CTRL_C		0x313
> +#define DA9150_FW_CTRL_D		0x314
> +#define DA9150_FG_CTRL_A		0x315
> +#define DA9150_FG_CTRL_B		0x316
> +#define DA9150_FW_CTRL_E		0x317
> +#define DA9150_FW_CTRL_B		0x318
> +#define DA9150_GPADC_CMAN		0x320
> +#define DA9150_GPADC_CRES_A		0x322
> +#define DA9150_GPADC_CRES_B		0x323
> +#define DA9150_CC_CFG_A			0x328
> +#define DA9150_CC_CFG_B			0x329
> +#define DA9150_CC_ICHG_RES_A		0x32A
> +#define DA9150_CC_ICHG_RES_B		0x32B
> +#define DA9150_CC_IAVG_RES_A		0x32C
> +#define DA9150_CC_IAVG_RES_B		0x32D
> +#define DA9150_TAUX_CTRL_A		0x330
> +#define DA9150_TAUX_RELOAD_H		0x332
> +#define DA9150_TAUX_RELOAD_L		0x333
> +#define DA9150_TAUX_VALUE_H		0x334
> +#define DA9150_TAUX_VALUE_L		0x335
> +#define DA9150_AUX_DATA_0		0x338
> +#define DA9150_AUX_DATA_1		0x339
> +#define DA9150_AUX_DATA_2		0x33A
> +#define DA9150_AUX_DATA_3		0x33B
> +#define DA9150_BIF_CTRL			0x340
> +#define DA9150_TBAT_CTRL_A		0x342
> +#define DA9150_TBAT_CTRL_B		0x343
> +#define DA9150_TBAT_RES_A		0x344
> +#define DA9150_TBAT_RES_B		0x345
> +
> +/* DA9150_PAGE_CON = 0x000 */
> +#define DA9150_PAGE_SHIFT			0
You could drop all the shift values and compute it from the mask...
(just ffs(MASK) should work I think).
Just feels very clunky to have all the repitition we have in these
defines!

> +#define DA9150_PAGE_MASK			(0x3f << 0)
> +#define DA9150_I2C_PAGE_SHIFT			1
> +#define DA9150_I2C_PAGE_MASK			(0x1f << 1)
> +#define DA9150_WRITE_MODE_SHIFT			6
> +#define DA9150_WRITE_MODE_MASK			BIT(6)
> +#define DA9150_REVERT_SHIFT			7
> +#define DA9150_REVERT_MASK			BIT(7)
> +
> +/* DA9150_STATUS_A = 0x068 */
> +#define DA9150_WKUP_STAT_SHIFT			2
> +#define DA9150_WKUP_STAT_MASK			(0x0f << 2)
> +#define DA9150_SLEEP_STAT_SHIFT			6
> +#define DA9150_SLEEP_STAT_MASK			(0x03 << 6)
> +
> +/* DA9150_STATUS_B = 0x069 */
> +#define DA9150_VFAULT_STAT_SHIFT		0
> +#define DA9150_VFAULT_STAT_MASK			BIT(0)
> +#define DA9150_TFAULT_STAT_SHIFT		1
> +#define DA9150_TFAULT_STAT_MASK			BIT(1)
> +
> +/* DA9150_STATUS_C = 0x06A */
> +#define DA9150_VDD33_STAT_SHIFT			0
> +#define DA9150_VDD33_STAT_MASK			BIT(0)
> +#define DA9150_VDD33_SLEEP_SHIFT		1
> +#define DA9150_VDD33_SLEEP_MASK			BIT(1)
> +#define DA9150_LFOSC_STAT_SHIFT			7
> +#define DA9150_LFOSC_STAT_MASK			BIT(7)
> +
> +/* DA9150_STATUS_D = 0x06B */
> +#define DA9150_GPIOA_STAT_SHIFT			0
> +#define DA9150_GPIOA_STAT_MASK			BIT(0)
> +#define DA9150_GPIOB_STAT_SHIFT			1
> +#define DA9150_GPIOB_STAT_MASK			BIT(1)
> +#define DA9150_GPIOC_STAT_SHIFT			2
> +#define DA9150_GPIOC_STAT_MASK			BIT(2)
> +#define DA9150_GPIOD_STAT_SHIFT			3
> +#define DA9150_GPIOD_STAT_MASK			BIT(3)
> +
> +/* DA9150_STATUS_E = 0x06C */
> +#define DA9150_DTYPE_SHIFT			0
> +#define DA9150_DTYPE_MASK			(0x1f << 0)
> +#define DA9150_DTYPE_DT_NIL			(0x00 << 0)
> +#define DA9150_DTYPE_DT_USB_OTG			BIT(0)
> +#define DA9150_DTYPE_DT_USB_STD			(0x02 << 0)
> +#define DA9150_DTYPE_DT_USB_CHG			(0x03 << 0)
> +#define DA9150_DTYPE_DT_ACA_CHG			(0x04 << 0)
> +#define DA9150_DTYPE_DT_ACA_OTG			(0x05 << 0)
> +#define DA9150_DTYPE_DT_ACA_DOC			(0x06 << 0)
> +#define DA9150_DTYPE_DT_DED_CHG			(0x07 << 0)
> +#define DA9150_DTYPE_DT_CR5_CHG			(0x08 << 0)
> +#define DA9150_DTYPE_DT_CR4_CHG			(0x0c << 0)
> +#define DA9150_DTYPE_DT_PT_CHG			(0x11 << 0)
> +#define DA9150_DTYPE_DT_NN_ACC			(0x16 << 0)
> +#define DA9150_DTYPE_DT_NN_CHG			(0x17 << 0)
> +
> +/* DA9150_STATUS_F = 0x06D */
> +#define DA9150_SESS_VLD_SHIFT			0
> +#define DA9150_SESS_VLD_MASK			BIT(0)
> +#define DA9150_ID_ERR_SHIFT			1
> +#define DA9150_ID_ERR_MASK			BIT(1)
> +#define DA9150_PT_CHG_SHIFT			2
> +#define DA9150_PT_CHG_MASK			BIT(2)
> +
> +/* DA9150_STATUS_G = 0x06E */
> +#define DA9150_RID_SHIFT			0
> +#define DA9150_RID_MASK				(0xff << 0)
> +
> +/* DA9150_STATUS_H = 0x06F */
> +#define DA9150_VBUS_STAT_SHIFT			0
> +#define DA9150_VBUS_STAT_MASK			(0x07 << 0)
> +#define DA9150_VBUS_STAT_OFF			(0x00 << 0)
> +#define DA9150_VBUS_STAT_WAIT			BIT(0)
> +#define DA9150_VBUS_STAT_CHG			(0x02 << 0)
> +#define DA9150_VBUS_TRED_SHIFT			3
> +#define DA9150_VBUS_TRED_MASK			BIT(3)
> +#define DA9150_VBUS_DROP_STAT_SHIFT		4
> +#define DA9150_VBUS_DROP_STAT_MASK		(0x0f << 4)
> +
> +/* DA9150_STATUS_I = 0x070 */
> +#define DA9150_VBUS_ISET_STAT_SHIFT		0
> +#define DA9150_VBUS_ISET_STAT_MASK		(0x1f << 0)
> +#define DA9150_VBUS_OT_SHIFT			7
> +#define DA9150_VBUS_OT_MASK			BIT(7)
> +
> +/* DA9150_STATUS_J = 0x071 */
> +#define DA9150_CHG_STAT_SHIFT			0
> +#define DA9150_CHG_STAT_MASK			(0x0f << 0)
> +#define DA9150_CHG_STAT_OFF			(0x00 << 0)
> +#define DA9150_CHG_STAT_SUSP			BIT(0)
> +#define DA9150_CHG_STAT_ACT			(0x02 << 0)
> +#define DA9150_CHG_STAT_PRE			(0x03 << 0)
> +#define DA9150_CHG_STAT_CC			(0x04 << 0)
> +#define DA9150_CHG_STAT_CV			(0x05 << 0)
> +#define DA9150_CHG_STAT_FULL			(0x06 << 0)
> +#define DA9150_CHG_STAT_TEMP			(0x07 << 0)
> +#define DA9150_CHG_STAT_TIME			(0x08 << 0)
> +#define DA9150_CHG_STAT_BAT			(0x09 << 0)
> +#define DA9150_CHG_TEMP_SHIFT			4
> +#define DA9150_CHG_TEMP_MASK			(0x07 << 4)
> +#define DA9150_CHG_TEMP_UNDER			(0x06 << 4)
> +#define DA9150_CHG_TEMP_OVER			(0x07 << 4)
> +#define DA9150_CHG_IEND_STAT_SHIFT		7
> +#define DA9150_CHG_IEND_STAT_MASK		BIT(7)
> +
> +/* DA9150_STATUS_K = 0x072 */
> +#define DA9150_CHG_IAV_H_SHIFT			0
> +#define DA9150_CHG_IAV_H_MASK			(0xff << 0)
> +
> +/* DA9150_STATUS_L = 0x073 */
> +#define DA9150_CHG_IAV_L_SHIFT			5
> +#define DA9150_CHG_IAV_L_MASK			(0x07 << 5)
> +
> +/* DA9150_STATUS_N = 0x074 */
> +#define DA9150_CHG_TIME_SHIFT			1
> +#define DA9150_CHG_TIME_MASK			BIT(1)
> +#define DA9150_CHG_TRED_SHIFT			2
> +#define DA9150_CHG_TRED_MASK			BIT(2)
> +#define DA9150_CHG_TJUNC_CLASS_SHIFT		3
> +#define DA9150_CHG_TJUNC_CLASS_MASK		(0x07 << 3)
> +#define DA9150_CHG_TJUNC_CLASS_6		(0x06 << 3)
> +#define DA9150_EBS_STAT_SHIFT			6
> +#define DA9150_EBS_STAT_MASK			BIT(6)
> +#define DA9150_CHG_BAT_REMOVED_SHIFT		7
> +#define DA9150_CHG_BAT_REMOVED_MASK		BIT(7)
> +
> +/* DA9150_FAULT_LOG_A = 0x076 */
> +#define DA9150_TEMP_FAULT_SHIFT			0
> +#define DA9150_TEMP_FAULT_MASK			BIT(0)
> +#define DA9150_VSYS_FAULT_SHIFT			1
> +#define DA9150_VSYS_FAULT_MASK			BIT(1)
> +#define DA9150_START_FAULT_SHIFT		2
> +#define DA9150_START_FAULT_MASK			BIT(2)
> +#define DA9150_EXT_FAULT_SHIFT			3
> +#define DA9150_EXT_FAULT_MASK			BIT(3)
> +#define DA9150_POR_FAULT_SHIFT			4
> +#define DA9150_POR_FAULT_MASK			BIT(4)
> +
> +/* DA9150_FAULT_LOG_B = 0x077 */
> +#define DA9150_VBUS_FAULT_SHIFT			0
> +#define DA9150_VBUS_FAULT_MASK			BIT(0)
> +#define DA9150_OTG_FAULT_SHIFT			1
> +#define DA9150_OTG_FAULT_MASK			BIT(1)
> +
> +/* DA9150_EVENT_E = 0x078 */
> +#define DA9150_E_VBUS_SHIFT			0
> +#define DA9150_E_VBUS_MASK			BIT(0)
> +#define DA9150_E_CHG_SHIFT			1
> +#define DA9150_E_CHG_MASK			BIT(1)
> +#define DA9150_E_TCLASS_SHIFT			2
> +#define DA9150_E_TCLASS_MASK			BIT(2)
> +#define DA9150_E_TJUNC_SHIFT			3
> +#define DA9150_E_TJUNC_MASK			BIT(3)
> +#define DA9150_E_VFAULT_SHIFT			4
> +#define DA9150_E_VFAULT_MASK			BIT(4)
> +#define DA9150_EVENTS_H_SHIFT			5
> +#define DA9150_EVENTS_H_MASK			BIT(5)
> +#define DA9150_EVENTS_G_SHIFT			6
> +#define DA9150_EVENTS_G_MASK			BIT(6)
> +#define DA9150_EVENTS_F_SHIFT			7
> +#define DA9150_EVENTS_F_MASK			BIT(7)
> +
> +/* DA9150_EVENT_F = 0x079 */
> +#define DA9150_E_CONF_SHIFT			0
> +#define DA9150_E_CONF_MASK			BIT(0)
> +#define DA9150_E_DAT_SHIFT			1
> +#define DA9150_E_DAT_MASK			BIT(1)
> +#define DA9150_E_DTYPE_SHIFT			3
> +#define DA9150_E_DTYPE_MASK			BIT(3)
> +#define DA9150_E_ID_SHIFT			4
> +#define DA9150_E_ID_MASK			BIT(4)
> +#define DA9150_E_ADP_SHIFT			5
> +#define DA9150_E_ADP_MASK			BIT(5)
> +#define DA9150_E_SESS_END_SHIFT			6
> +#define DA9150_E_SESS_END_MASK			BIT(6)
> +#define DA9150_E_SESS_VLD_SHIFT			7
> +#define DA9150_E_SESS_VLD_MASK			BIT(7)
> +
> +/* DA9150_EVENT_G = 0x07A */
> +#define DA9150_E_FG_SHIFT			0
> +#define DA9150_E_FG_MASK			BIT(0)
> +#define DA9150_E_GP_SHIFT			1
> +#define DA9150_E_GP_MASK			BIT(1)
> +#define DA9150_E_TBAT_SHIFT			2
> +#define DA9150_E_TBAT_MASK			BIT(2)
> +#define DA9150_E_GPIOA_SHIFT			3
> +#define DA9150_E_GPIOA_MASK			BIT(3)
> +#define DA9150_E_GPIOB_SHIFT			4
> +#define DA9150_E_GPIOB_MASK			BIT(4)
> +#define DA9150_E_GPIOC_SHIFT			5
> +#define DA9150_E_GPIOC_MASK			BIT(5)
> +#define DA9150_E_GPIOD_SHIFT			6
> +#define DA9150_E_GPIOD_MASK			BIT(6)
> +#define DA9150_E_GPADC_SHIFT			7
> +#define DA9150_E_GPADC_MASK			BIT(7)
> +
> +/* DA9150_EVENT_H = 0x07B */
> +#define DA9150_E_WKUP_SHIFT			0
> +#define DA9150_E_WKUP_MASK			BIT(0)
> +
> +/* DA9150_IRQ_MASK_E = 0x07C */
> +#define DA9150_M_VBUS_SHIFT			0
> +#define DA9150_M_VBUS_MASK			BIT(0)
> +#define DA9150_M_CHG_SHIFT			1
> +#define DA9150_M_CHG_MASK			BIT(1)
> +#define DA9150_M_TJUNC_SHIFT			3
> +#define DA9150_M_TJUNC_MASK			BIT(3)
> +#define DA9150_M_VFAULT_SHIFT			4
> +#define DA9150_M_VFAULT_MASK			BIT(4)
> +
> +/* DA9150_IRQ_MASK_F = 0x07D */
> +#define DA9150_M_CONF_SHIFT			0
> +#define DA9150_M_CONF_MASK			BIT(0)
> +#define DA9150_M_DAT_SHIFT			1
> +#define DA9150_M_DAT_MASK			BIT(1)
> +#define DA9150_M_DTYPE_SHIFT			3
> +#define DA9150_M_DTYPE_MASK			BIT(3)
> +#define DA9150_M_ID_SHIFT			4
> +#define DA9150_M_ID_MASK			BIT(4)
> +#define DA9150_M_ADP_SHIFT			5
> +#define DA9150_M_ADP_MASK			BIT(5)
> +#define DA9150_M_SESS_END_SHIFT			6
> +#define DA9150_M_SESS_END_MASK			BIT(6)
> +#define DA9150_M_SESS_VLD_SHIFT			7
> +#define DA9150_M_SESS_VLD_MASK			BIT(7)
> +
> +/* DA9150_IRQ_MASK_G = 0x07E */
> +#define DA9150_M_FG_SHIFT			0
> +#define DA9150_M_FG_MASK			BIT(0)
> +#define DA9150_M_GP_SHIFT			1
> +#define DA9150_M_GP_MASK			BIT(1)
> +#define DA9150_M_TBAT_SHIFT			2
> +#define DA9150_M_TBAT_MASK			BIT(2)
> +#define DA9150_M_GPIOA_SHIFT			3
> +#define DA9150_M_GPIOA_MASK			BIT(3)
> +#define DA9150_M_GPIOB_SHIFT			4
> +#define DA9150_M_GPIOB_MASK			BIT(4)
> +#define DA9150_M_GPIOC_SHIFT			5
> +#define DA9150_M_GPIOC_MASK			BIT(5)
> +#define DA9150_M_GPIOD_SHIFT			6
> +#define DA9150_M_GPIOD_MASK			BIT(6)
> +#define DA9150_M_GPADC_SHIFT			7
> +#define DA9150_M_GPADC_MASK			BIT(7)
> +
> +/* DA9150_IRQ_MASK_H = 0x07F */
> +#define DA9150_M_WKUP_SHIFT			0
> +#define DA9150_M_WKUP_MASK			BIT(0)
> +
> +/* DA9150_PAGE_CON_1 = 0x080 */
> +#define DA9150_PAGE_SHIFT			0
> +#define DA9150_PAGE_MASK			(0x3f << 0)
> +#define DA9150_WRITE_MODE_SHIFT			6
> +#define DA9150_WRITE_MODE_MASK			BIT(6)
> +#define DA9150_REVERT_SHIFT			7
> +#define DA9150_REVERT_MASK			BIT(7)
> +
> +/* DA9150_CONFIG_A = 0x0E0 */
> +#define DA9150_RESET_DUR_SHIFT			0
> +#define DA9150_RESET_DUR_MASK			(0x03 << 0)
> +#define DA9150_RESET_EXT_SHIFT			2
> +#define DA9150_RESET_EXT_MASK			(0x03 << 2)
> +#define DA9150_START_MAX_SHIFT			4
> +#define DA9150_START_MAX_MASK			(0x03 << 4)
> +#define DA9150_PS_WAIT_EN_SHIFT			6
> +#define DA9150_PS_WAIT_EN_MASK			BIT(6)
> +#define DA9150_PS_DISABLE_DIRECT_SHIFT		7
> +#define DA9150_PS_DISABLE_DIRECT_MASK		BIT(7)
> +
> +/* DA9150_CONFIG_B = 0x0E1 */
> +#define DA9150_VFAULT_ADJ_SHIFT			0
> +#define DA9150_VFAULT_ADJ_MASK			(0x0f << 0)
> +#define DA9150_VFAULT_HYST_SHIFT		4
> +#define DA9150_VFAULT_HYST_MASK			(0x07 << 4)
> +#define DA9150_VFAULT_EN_SHIFT			7
> +#define DA9150_VFAULT_EN_MASK			BIT(7)
> +
> +/* DA9150_CONFIG_C = 0x0E2 */
> +#define DA9150_VSYS_MIN_SHIFT			3
> +#define DA9150_VSYS_MIN_MASK			(0x1f << 3)
> +
> +/* DA9150_CONFIG_D = 0x0E3 */
> +#define DA9150_LFOSC_EXT_SHIFT			0
> +#define DA9150_LFOSC_EXT_MASK			BIT(0)
> +#define DA9150_VDD33_DWN_SHIFT			1
> +#define DA9150_VDD33_DWN_MASK			BIT(1)
> +#define DA9150_WKUP_PM_EN_SHIFT			2
> +#define DA9150_WKUP_PM_EN_MASK			BIT(2)
> +#define DA9150_WKUP_CE_SEL_SHIFT		3
> +#define DA9150_WKUP_CE_SEL_MASK			(0x03 << 3)
> +#define DA9150_WKUP_CLK32K_EN_SHIFT		5
> +#define DA9150_WKUP_CLK32K_EN_MASK		BIT(5)
> +#define DA9150_DISABLE_DEL_SHIFT		7
> +#define DA9150_DISABLE_DEL_MASK			BIT(7)
> +
> +/* DA9150_CONFIG_E = 0x0E4 */
> +#define DA9150_PM_SPKSUP_DIS_SHIFT		0
> +#define DA9150_PM_SPKSUP_DIS_MASK		BIT(0)
> +#define DA9150_PM_MERGE_SHIFT			1
> +#define DA9150_PM_MERGE_MASK			BIT(1)
> +#define DA9150_PM_SR_OFF_SHIFT			2
> +#define DA9150_PM_SR_OFF_MASK			BIT(2)
> +#define DA9150_PM_TIMEOUT_EN_SHIFT		3
> +#define DA9150_PM_TIMEOUT_EN_MASK		BIT(3)
> +#define DA9150_PM_DLY_SEL_SHIFT			4
> +#define DA9150_PM_DLY_SEL_MASK			(0x07 << 4)
> +#define DA9150_PM_OUT_DLY_SEL_SHIFT		7
> +#define DA9150_PM_OUT_DLY_SEL_MASK		BIT(7)
> +
> +/* DA9150_CONTROL_A = 0x0E5 */
> +#define DA9150_VDD33_SL_SHIFT			0
> +#define DA9150_VDD33_SL_MASK			BIT(0)
> +#define DA9150_VDD33_LPM_SHIFT			1
> +#define DA9150_VDD33_LPM_MASK			(0x03 << 1)
> +#define DA9150_VDD33_EN_SHIFT			3
> +#define DA9150_VDD33_EN_MASK			BIT(3)
> +#define DA9150_GPI_LPM_SHIFT			6
> +#define DA9150_GPI_LPM_MASK			BIT(6)
> +#define DA9150_PM_IF_LPM_SHIFT			7
> +#define DA9150_PM_IF_LPM_MASK			BIT(7)
> +
> +/* DA9150_CONTROL_B = 0x0E6 */
> +#define DA9150_LPM_SHIFT			0
> +#define DA9150_LPM_MASK				BIT(0)
> +#define DA9150_RESET_SHIFT			1
> +#define DA9150_RESET_MASK			BIT(1)
> +#define DA9150_RESET_USRCONF_EN_SHIFT		2
> +#define DA9150_RESET_USRCONF_EN_MASK		BIT(2)
> +
> +/* DA9150_CONTROL_C = 0x0E7 */
> +#define DA9150_DISABLE_SHIFT			0
> +#define DA9150_DISABLE_MASK			BIT(0)
> +
> +/* DA9150_GPIO_A_B = 0x0E8 */
> +#define DA9150_GPIOA_PIN_SHIFT			0
> +#define DA9150_GPIOA_PIN_MASK			(0x07 << 0)
> +#define DA9150_GPIOA_PIN_GPI			(0x00 << 0)
> +#define DA9150_GPIOA_PIN_GPO_OD			BIT(0)
> +#define DA9150_GPIOA_TYPE_SHIFT			3
> +#define DA9150_GPIOA_TYPE_MASK			BIT(3)
> +#define DA9150_GPIOB_PIN_SHIFT			4
> +#define DA9150_GPIOB_PIN_MASK			(0x07 << 4)
> +#define DA9150_GPIOB_PIN_GPI			(0x00 << 4)
> +#define DA9150_GPIOB_PIN_GPO_OD			BIT(4)
> +#define DA9150_GPIOB_TYPE_SHIFT			7
> +#define DA9150_GPIOB_TYPE_MASK			BIT(7)
> +
> +/* DA9150_GPIO_C_D = 0x0E9 */
> +#define DA9150_GPIOC_PIN_SHIFT			0
> +#define DA9150_GPIOC_PIN_MASK			(0x07 << 0)
> +#define DA9150_GPIOC_PIN_GPI			(0x00 << 0)
> +#define DA9150_GPIOC_PIN_GPO_OD			BIT(0)
> +#define DA9150_GPIOC_TYPE_SHIFT			3
> +#define DA9150_GPIOC_TYPE_MASK			BIT(3)
> +#define DA9150_GPIOD_PIN_SHIFT			4
> +#define DA9150_GPIOD_PIN_MASK			(0x07 << 4)
> +#define DA9150_GPIOD_PIN_GPI			(0x00 << 4)
> +#define DA9150_GPIOD_PIN_GPO_OD			BIT(4)
> +#define DA9150_GPIOD_TYPE_SHIFT			7
> +#define DA9150_GPIOD_TYPE_MASK			BIT(7)
> +
> +/* DA9150_GPIO_MODE_CONT = 0x0EA */
> +#define DA9150_GPIOA_MODE_SHIFT			0
> +#define DA9150_GPIOA_MODE_MASK			BIT(0)
> +#define DA9150_GPIOB_MODE_SHIFT			1
> +#define DA9150_GPIOB_MODE_MASK			BIT(1)
> +#define DA9150_GPIOC_MODE_SHIFT			2
> +#define DA9150_GPIOC_MODE_MASK			BIT(2)
> +#define DA9150_GPIOD_MODE_SHIFT			3
> +#define DA9150_GPIOD_MODE_MASK			BIT(3)
> +#define DA9150_GPIOA_CONT_SHIFT			4
> +#define DA9150_GPIOA_CONT_MASK			BIT(4)
> +#define DA9150_GPIOB_CONT_SHIFT			5
> +#define DA9150_GPIOB_CONT_MASK			BIT(5)
> +#define DA9150_GPIOC_CONT_SHIFT			6
> +#define DA9150_GPIOC_CONT_MASK			BIT(6)
> +#define DA9150_GPIOD_CONT_SHIFT			7
> +#define DA9150_GPIOD_CONT_MASK			BIT(7)
> +
> +/* DA9150_GPIO_CTRL_B = 0x0EB */
> +#define DA9150_WAKE_PIN_SHIFT			0
> +#define DA9150_WAKE_PIN_MASK			(0x03 << 0)
> +#define DA9150_WAKE_MODE_SHIFT			2
> +#define DA9150_WAKE_MODE_MASK			BIT(2)
> +#define DA9150_WAKE_CONT_SHIFT			3
> +#define DA9150_WAKE_CONT_MASK			BIT(3)
> +#define DA9150_WAKE_DLY_SHIFT			4
> +#define DA9150_WAKE_DLY_MASK			BIT(4)
> +
> +/* DA9150_GPIO_CTRL_A = 0x0EC */
> +#define DA9150_GPIOA_ANAEN_SHIFT		0
> +#define DA9150_GPIOA_ANAEN_MASK			BIT(0)
> +#define DA9150_GPIOB_ANAEN_SHIFT		1
> +#define DA9150_GPIOB_ANAEN_MASK			BIT(1)
> +#define DA9150_GPIOC_ANAEN_SHIFT		2
> +#define DA9150_GPIOC_ANAEN_MASK			BIT(2)
> +#define DA9150_GPIOD_ANAEN_SHIFT		3
> +#define DA9150_GPIOD_ANAEN_MASK			BIT(3)
> +#define DA9150_GPIO_ANAEN			0x01
> +#define DA9150_GPIO_ANAEN_MASK			0x0F
> +#define DA9150_CHGLED_PIN_SHIFT			5
> +#define DA9150_CHGLED_PIN_MASK			(0x07 << 5)
> +
> +/* DA9150_GPIO_CTRL_C = 0x0ED */
> +#define DA9150_CHGBL_DUR_SHIFT			0
> +#define DA9150_CHGBL_DUR_MASK			(0x03 << 0)
> +#define DA9150_CHGBL_DBL_SHIFT			2
> +#define DA9150_CHGBL_DBL_MASK			BIT(2)
> +#define DA9150_CHGBL_FRQ_SHIFT			3
> +#define DA9150_CHGBL_FRQ_MASK			(0x03 << 3)
> +#define DA9150_CHGBL_FLKR_SHIFT			5
> +#define DA9150_CHGBL_FLKR_MASK			BIT(5)
> +
> +/* DA9150_GPIO_CFG_A = 0x0EE */
> +#define DA9150_CE_LPM_DEB_SHIFT			0
> +#define DA9150_CE_LPM_DEB_MASK			(0x07 << 0)
> +
> +/* DA9150_GPIO_CFG_B = 0x0EF */
> +#define DA9150_GPIOA_PUPD_SHIFT			0
> +#define DA9150_GPIOA_PUPD_MASK			BIT(0)
> +#define DA9150_GPIOB_PUPD_SHIFT			1
> +#define DA9150_GPIOB_PUPD_MASK			BIT(1)
> +#define DA9150_GPIOC_PUPD_SHIFT			2
> +#define DA9150_GPIOC_PUPD_MASK			BIT(2)
> +#define DA9150_GPIOD_PUPD_SHIFT			3
> +#define DA9150_GPIOD_PUPD_MASK			BIT(3)
> +#define DA9150_GPIO_PUPD_MASK			(0xF << 0)
> +#define DA9150_GPI_DEB_SHIFT			4
> +#define DA9150_GPI_DEB_MASK			(0x07 << 4)
> +#define DA9150_LPM_EN_SHIFT			7
> +#define DA9150_LPM_EN_MASK			BIT(7)
> +
> +/* DA9150_GPIO_CFG_C = 0x0F0 */
> +#define DA9150_GPI_V_SHIFT			0
> +#define DA9150_GPI_V_MASK			BIT(0)
> +#define DA9150_VDDIO_INT_SHIFT			1
> +#define DA9150_VDDIO_INT_MASK			BIT(1)
> +#define DA9150_FAULT_PIN_SHIFT			3
> +#define DA9150_FAULT_PIN_MASK			(0x07 << 3)
> +#define DA9150_FAULT_TYPE_SHIFT			6
> +#define DA9150_FAULT_TYPE_MASK			BIT(6)
> +#define DA9150_NIRQ_PUPD_SHIFT			7
> +#define DA9150_NIRQ_PUPD_MASK			BIT(7)
> +
> +/* DA9150_GPADC_MAN = 0x0F2 */
> +#define DA9150_GPADC_EN_SHIFT			0
> +#define DA9150_GPADC_EN_MASK			BIT(0)
> +#define DA9150_GPADC_MUX_SHIFT			1
> +#define DA9150_GPADC_MUX_MASK			(0x1f << 1)
> +
> +/* DA9150_GPADC_RES_A = 0x0F4 */
> +#define DA9150_GPADC_RES_H_SHIFT		0
> +#define DA9150_GPADC_RES_H_MASK			(0xff << 0)
> +
> +/* DA9150_GPADC_RES_B = 0x0F5 */
> +#define DA9150_GPADC_RUN_SHIFT			0
> +#define DA9150_GPADC_RUN_MASK			BIT(0)
> +#define DA9150_GPADC_RES_L_SHIFT		6
> +#define DA9150_GPADC_RES_L_MASK			(0x03 << 6)
> +#define DA9150_GPADC_RES_L_BITS			2
> +
> +/* DA9150_PAGE_CON_2 = 0x100 */
> +#define DA9150_PAGE_SHIFT			0
> +#define DA9150_PAGE_MASK			(0x3f << 0)
> +#define DA9150_WRITE_MODE_SHIFT			6
> +#define DA9150_WRITE_MODE_MASK			BIT(6)
> +#define DA9150_REVERT_SHIFT			7
> +#define DA9150_REVERT_MASK			BIT(7)
> +
> +/* DA9150_OTP_CONT_SHARED = 0x101 */
> +#define DA9150_PC_DONE_SHIFT			3
> +#define DA9150_PC_DONE_MASK			BIT(3)
> +
> +/* DA9150_INTERFACE_SHARED = 0x105 */
> +#define DA9150_IF_BASE_ADDR_SHIFT		4
> +#define DA9150_IF_BASE_ADDR_MASK		(0x0f << 4)
> +
> +/* DA9150_CONFIG_A_SHARED = 0x106 */
> +#define DA9150_NIRQ_VDD_SHIFT			1
> +#define DA9150_NIRQ_VDD_MASK			BIT(1)
> +#define DA9150_NIRQ_PIN_SHIFT			2
> +#define DA9150_NIRQ_PIN_MASK			BIT(2)
> +#define DA9150_NIRQ_TYPE_SHIFT			3
> +#define DA9150_NIRQ_TYPE_MASK			BIT(3)
> +#define DA9150_PM_IF_V_SHIFT			4
> +#define DA9150_PM_IF_V_MASK			BIT(4)
> +#define DA9150_PM_IF_FMP_SHIFT			5
> +#define DA9150_PM_IF_FMP_MASK			BIT(5)
> +#define DA9150_PM_IF_HSM_SHIFT			6
> +#define DA9150_PM_IF_HSM_MASK			BIT(6)
> +
> +/* DA9150_CONFIG_D_SHARED = 0x109 */
> +#define DA9150_NIRQ_MODE_SHIFT			1
> +#define DA9150_NIRQ_MODE_MASK			BIT(1)
> +
> +/* DA9150_ADETVB_CFG_C = 0x150 */
> +#define DA9150_TADP_RISE_SHIFT			0
> +#define DA9150_TADP_RISE_MASK			(0xff << 0)
> +
> +/* DA9150_ADETD_STAT = 0x151 */
> +#define DA9150_DCD_STAT_SHIFT			0
> +#define DA9150_DCD_STAT_MASK			BIT(0)
> +#define DA9150_PCD_STAT_SHIFT			1
> +#define DA9150_PCD_STAT_MASK			(0x03 << 1)
> +#define DA9150_SCD_STAT_SHIFT			3
> +#define DA9150_SCD_STAT_MASK			(0x03 << 3)
> +#define DA9150_DP_STAT_SHIFT			5
> +#define DA9150_DP_STAT_MASK			BIT(5)
> +#define DA9150_DM_STAT_SHIFT			6
> +#define DA9150_DM_STAT_MASK			BIT(6)
> +
> +/* DA9150_ADET_CMPSTAT = 0x152 */
> +#define DA9150_DP_COMP_SHIFT			1
> +#define DA9150_DP_COMP_MASK			BIT(1)
> +#define DA9150_DM_COMP_SHIFT			2
> +#define DA9150_DM_COMP_MASK			BIT(2)
> +#define DA9150_ADP_SNS_COMP_SHIFT		3
> +#define DA9150_ADP_SNS_COMP_MASK		BIT(3)
> +#define DA9150_ADP_PRB_COMP_SHIFT		4
> +#define DA9150_ADP_PRB_COMP_MASK		BIT(4)
> +#define DA9150_ID_COMP_SHIFT			5
> +#define DA9150_ID_COMP_MASK			BIT(5)
> +
> +/* DA9150_ADET_CTRL_A = 0x153 */
> +#define DA9150_AID_DAT_SHIFT			0
> +#define DA9150_AID_DAT_MASK			BIT(0)
> +#define DA9150_AID_ID_SHIFT			1
> +#define DA9150_AID_ID_MASK			BIT(1)
> +#define DA9150_AID_TRIG_SHIFT			2
> +#define DA9150_AID_TRIG_MASK			BIT(2)
> +
> +/* DA9150_ADETVB_CFG_B = 0x154 */
> +#define DA9150_VB_MODE_SHIFT			0
> +#define DA9150_VB_MODE_MASK			(0x03 << 0)
> +#define DA9150_VB_MODE_VB_SESS			BIT(0)
> +
> +#define DA9150_TADP_PRB_SHIFT			2
> +#define DA9150_TADP_PRB_MASK			BIT(2)
> +#define DA9150_DAT_RPD_EXT_SHIFT		5
> +#define DA9150_DAT_RPD_EXT_MASK			BIT(5)
> +#define DA9150_CONF_RPD_SHIFT			6
> +#define DA9150_CONF_RPD_MASK			BIT(6)
> +#define DA9150_CONF_SRP_SHIFT			7
> +#define DA9150_CONF_SRP_MASK			BIT(7)
> +
> +/* DA9150_ADETVB_CFG_A = 0x155 */
> +#define DA9150_AID_MODE_SHIFT			0
> +#define DA9150_AID_MODE_MASK			(0x03 << 0)
> +#define DA9150_AID_EXT_POL_SHIFT		2
> +#define DA9150_AID_EXT_POL_MASK			BIT(2)
> +
> +/* DA9150_ADETAC_CFG_A = 0x156 */
> +#define DA9150_ISET_CDP_SHIFT			0
> +#define DA9150_ISET_CDP_MASK			(0x1f << 0)
> +#define DA9150_CONF_DBP_SHIFT			5
> +#define DA9150_CONF_DBP_MASK			BIT(5)
> +
> +/* DA9150_ADDETAC_CFG_B = 0x157 */
> +#define DA9150_ISET_DCHG_SHIFT			0
> +#define DA9150_ISET_DCHG_MASK			(0x1f << 0)
> +#define DA9150_CONF_GPIOA_SHIFT			5
> +#define DA9150_CONF_GPIOA_MASK			BIT(5)
> +#define DA9150_CONF_GPIOB_SHIFT			6
> +#define DA9150_CONF_GPIOB_MASK			BIT(6)
> +#define DA9150_AID_VB_SHIFT			7
> +#define DA9150_AID_VB_MASK			BIT(7)
> +
> +/* DA9150_ADETAC_CFG_C = 0x158 */
> +#define DA9150_ISET_DEF_SHIFT			0
> +#define DA9150_ISET_DEF_MASK			(0x1f << 0)
> +#define DA9150_CONF_MODE_SHIFT			5
> +#define DA9150_CONF_MODE_MASK			(0x03 << 5)
> +#define DA9150_AID_CR_DIS_SHIFT			7
> +#define DA9150_AID_CR_DIS_MASK			BIT(7)
> +
> +/* DA9150_ADETAC_CFG_D = 0x159 */
> +#define DA9150_ISET_UNIT_SHIFT			0
> +#define DA9150_ISET_UNIT_MASK			(0x1f << 0)
> +#define DA9150_AID_UNCLAMP_SHIFT		5
> +#define DA9150_AID_UNCLAMP_MASK			BIT(5)
> +
> +/* DA9150_ADETVB_CFG_D = 0x15A */
> +#define DA9150_ID_MODE_SHIFT			0
> +#define DA9150_ID_MODE_MASK			(0x03 << 0)
> +#define DA9150_DAT_MODE_SHIFT			2
> +#define DA9150_DAT_MODE_MASK			(0x0f << 2)
> +#define DA9150_DAT_SWP_SHIFT			6
> +#define DA9150_DAT_SWP_MASK			BIT(6)
> +#define DA9150_DAT_CLAMP_EXT_SHIFT		7
> +#define DA9150_DAT_CLAMP_EXT_MASK		BIT(7)
> +
> +/* DA9150_ADETID_CFG_A = 0x15B */
> +#define DA9150_TID_POLL_SHIFT			0
> +#define DA9150_TID_POLL_MASK			(0x07 << 0)
> +#define DA9150_RID_CONV_SHIFT			3
> +#define DA9150_RID_CONV_MASK			BIT(3)
> +
> +/* DA9150_ADET_RID_PT_CHG_H = 0x15C */
> +#define DA9150_RID_PT_CHG_H_SHIFT		0
> +#define DA9150_RID_PT_CHG_H_MASK		(0xff << 0)
> +
> +/* DA9150_ADET_RID_PT_CHG_L = 0x15D */
> +#define DA9150_RID_PT_CHG_L_SHIFT		6
> +#define DA9150_RID_PT_CHG_L_MASK		(0x03 << 6)
> +
> +/* DA9150_PPR_TCTR_B = 0x160 */
> +#define DA9150_CHG_TCTR_VAL_SHIFT		0
> +#define DA9150_CHG_TCTR_VAL_MASK		(0xff << 0)
> +
> +/* DA9150_PPR_BKCTRL_A = 0x163 */
> +#define DA9150_VBUS_MODE_SHIFT			0
> +#define DA9150_VBUS_MODE_MASK			(0x03 << 0)
> +#define DA9150_VBUS_MODE_CHG			BIT(0)
> +#define DA9150_VBUS_MODE_OTG			(0x02 << 0)
> +#define DA9150_VBUS_LPM_SHIFT			2
> +#define DA9150_VBUS_LPM_MASK			(0x03 << 2)
> +#define DA9150_VBUS_SUSP_SHIFT			4
> +#define DA9150_VBUS_SUSP_MASK			BIT(4)
> +#define DA9150_VBUS_PWM_SHIFT			5
> +#define DA9150_VBUS_PWM_MASK			BIT(5)
> +#define DA9150_VBUS_ISO_SHIFT			6
> +#define DA9150_VBUS_ISO_MASK			BIT(6)
> +#define DA9150_VBUS_LDO_SHIFT			7
> +#define DA9150_VBUS_LDO_MASK			BIT(7)
> +
> +/* DA9150_PPR_BKCFG_A = 0x164 */
> +#define DA9150_VBUS_ISET_SHIFT			0
> +#define DA9150_VBUS_ISET_MASK			(0x1f << 0)
> +#define DA9150_VBUS_IMAX_SHIFT			5
> +#define DA9150_VBUS_IMAX_MASK			BIT(5)
> +#define DA9150_VBUS_IOTG_SHIFT			6
> +#define DA9150_VBUS_IOTG_MASK			(0x03 << 6)
> +
> +/* DA9150_PPR_BKCFG_B = 0x165 */
> +#define DA9150_VBUS_DROP_SHIFT			0
> +#define DA9150_VBUS_DROP_MASK			(0x0f << 0)
> +#define DA9150_VBUS_FAULT_DIS_SHIFT		6
> +#define DA9150_VBUS_FAULT_DIS_MASK		BIT(6)
> +#define DA9150_OTG_FAULT_DIS_SHIFT		7
> +#define DA9150_OTG_FAULT_DIS_MASK		BIT(7)
> +
> +/* DA9150_PPR_CHGCTRL_A = 0x166 */
> +#define DA9150_CHG_EN_SHIFT			0
> +#define DA9150_CHG_EN_MASK			BIT(0)
> +
> +/* DA9150_PPR_CHGCTRL_B = 0x167 */
> +#define DA9150_CHG_VBAT_SHIFT			0
> +#define DA9150_CHG_VBAT_MASK			(0x1f << 0)
> +#define DA9150_CHG_VDROP_SHIFT			6
> +#define DA9150_CHG_VDROP_MASK			(0x03 << 6)
> +
> +/* DA9150_PPR_CHGCTRL_C = 0x168 */
> +#define DA9150_CHG_VFAULT_SHIFT			0
> +#define DA9150_CHG_VFAULT_MASK			(0x0f << 0)
> +#define DA9150_CHG_IPRE_SHIFT			4
> +#define DA9150_CHG_IPRE_MASK			(0x03 << 4)
> +
> +/* DA9150_PPR_TCTR_A = 0x169 */
> +#define DA9150_CHG_TCTR_SHIFT			0
> +#define DA9150_CHG_TCTR_MASK			(0x07 << 0)
> +#define DA9150_CHG_TCTR_MODE_SHIFT		4
> +#define DA9150_CHG_TCTR_MODE_MASK		BIT(4)
> +
> +/* DA9150_PPR_CHGCTRL_D = 0x16A */
> +#define DA9150_CHG_IBAT_SHIFT			0
> +#define DA9150_CHG_IBAT_MASK			(0xff << 0)
> +
> +/* DA9150_PPR_CHGCTRL_E = 0x16B */
> +#define DA9150_CHG_IEND_SHIFT			0
> +#define DA9150_CHG_IEND_MASK			(0xff << 0)
> +
> +/* DA9150_PPR_CHGCTRL_F = 0x16C */
> +#define DA9150_CHG_VCOLD_SHIFT			0
> +#define DA9150_CHG_VCOLD_MASK			(0x1f << 0)
> +#define DA9150_TBAT_TQA_EN_SHIFT		6
> +#define DA9150_TBAT_TQA_EN_MASK			BIT(6)
> +#define DA9150_TBAT_TDP_EN_SHIFT		7
> +#define DA9150_TBAT_TDP_EN_MASK			BIT(7)
> +
> +/* DA9150_PPR_CHGCTRL_G = 0x16D */
> +#define DA9150_CHG_VWARM_SHIFT			0
> +#define DA9150_CHG_VWARM_MASK			(0x1f << 0)
> +
> +/* DA9150_PPR_CHGCTRL_H = 0x16E */
> +#define DA9150_CHG_VHOT_SHIFT			0
> +#define DA9150_CHG_VHOT_MASK			(0x1f << 0)
> +
> +/* DA9150_PPR_CHGCTRL_I = 0x16F */
> +#define DA9150_CHG_ICOLD_SHIFT			0
> +#define DA9150_CHG_ICOLD_MASK			(0xff << 0)
> +
> +/* DA9150_PPR_CHGCTRL_J = 0x170 */
> +#define DA9150_CHG_IWARM_SHIFT			0
> +#define DA9150_CHG_IWARM_MASK			(0xff << 0)
> +
> +/* DA9150_PPR_CHGCTRL_K = 0x171 */
> +#define DA9150_CHG_IHOT_SHIFT			0
> +#define DA9150_CHG_IHOT_MASK			(0xff << 0)
> +
> +/* DA9150_PPR_CHGCTRL_L = 0x172 */
> +#define DA9150_CHG_IBAT_TRED_SHIFT		0
> +#define DA9150_CHG_IBAT_TRED_MASK		(0xff << 0)
> +
> +/* DA9150_PPR_CHGCTRL_M = 0x173 */
> +#define DA9150_CHG_VFLOAT_SHIFT			0
> +#define DA9150_CHG_VFLOAT_MASK			(0x0f << 0)
> +#define DA9150_CHG_LPM_SHIFT			5
> +#define DA9150_CHG_LPM_MASK			BIT(5)
> +#define DA9150_CHG_NBLO_SHIFT			6
> +#define DA9150_CHG_NBLO_MASK			BIT(6)
> +#define DA9150_EBS_EN_SHIFT			7
> +#define DA9150_EBS_EN_MASK			BIT(7)
> +
> +/* DA9150_PPR_THYST_A = 0x174 */
> +#define DA9150_TBAT_T1_SHIFT			0
> +#define DA9150_TBAT_T1_MASK			(0xff << 0)
> +
> +/* DA9150_PPR_THYST_B = 0x175 */
> +#define DA9150_TBAT_T2_SHIFT			0
> +#define DA9150_TBAT_T2_MASK			(0xff << 0)
> +
> +/* DA9150_PPR_THYST_C = 0x176 */
> +#define DA9150_TBAT_T3_SHIFT			0
> +#define DA9150_TBAT_T3_MASK			(0xff << 0)
> +
> +/* DA9150_PPR_THYST_D = 0x177 */
> +#define DA9150_TBAT_T4_SHIFT			0
> +#define DA9150_TBAT_T4_MASK			(0xff << 0)
> +
> +/* DA9150_PPR_THYST_E = 0x178 */
> +#define DA9150_TBAT_T5_SHIFT			0
> +#define DA9150_TBAT_T5_MASK			(0xff << 0)
> +
> +/* DA9150_PPR_THYST_F = 0x179 */
> +#define DA9150_TBAT_H1_SHIFT			0
> +#define DA9150_TBAT_H1_MASK			(0xff << 0)
> +
> +/* DA9150_PPR_THYST_G = 0x17A */
> +#define DA9150_TBAT_H5_SHIFT			0
> +#define DA9150_TBAT_H5_MASK			(0xff << 0)
> +
> +/* DA9150_PAGE_CON_3 = 0x180 */
> +#define DA9150_PAGE_SHIFT			0
> +#define DA9150_PAGE_MASK			(0x3f << 0)
> +#define DA9150_WRITE_MODE_SHIFT			6
> +#define DA9150_WRITE_MODE_MASK			BIT(6)
> +#define DA9150_REVERT_SHIFT			7
> +#define DA9150_REVERT_MASK			BIT(7)
> +
> +/* DA9150_PAGE_CON_4 = 0x200 */
> +#define DA9150_PAGE_SHIFT			0
> +#define DA9150_PAGE_MASK			(0x3f << 0)
> +#define DA9150_WRITE_MODE_SHIFT			6
> +#define DA9150_WRITE_MODE_MASK			BIT(6)
> +#define DA9150_REVERT_SHIFT			7
> +#define DA9150_REVERT_MASK			BIT(7)
> +
> +/* DA9150_PAGE_CON_5 = 0x280 */
> +#define DA9150_PAGE_SHIFT			0
> +#define DA9150_PAGE_MASK			(0x3f << 0)
> +#define DA9150_WRITE_MODE_SHIFT			6
> +#define DA9150_WRITE_MODE_MASK			BIT(6)
> +#define DA9150_REVERT_SHIFT			7
> +#define DA9150_REVERT_MASK			BIT(7)
> +
> +/* DA9150_PAGE_CON_6 = 0x300 */
> +#define DA9150_PAGE_SHIFT			0
> +#define DA9150_PAGE_MASK			(0x3f << 0)
> +#define DA9150_WRITE_MODE_SHIFT			6
> +#define DA9150_WRITE_MODE_MASK			BIT(6)
> +#define DA9150_REVERT_SHIFT			7
> +#define DA9150_REVERT_MASK			BIT(7)
> +
> +/* DA9150_COREBTLD_STAT_A = 0x302 */
> +#define DA9150_BOOTLD_STAT_SHIFT		0
> +#define DA9150_BOOTLD_STAT_MASK			(0x03 << 0)
> +#define DA9150_CORE_LOCKUP_SHIFT		2
> +#define DA9150_CORE_LOCKUP_MASK			BIT(2)
> +
> +/* DA9150_COREBTLD_CTRL_A = 0x303 */
> +#define DA9150_CORE_RESET_SHIFT			0
> +#define DA9150_CORE_RESET_MASK			BIT(0)
> +#define DA9150_CORE_STOP_SHIFT			1
> +#define DA9150_CORE_STOP_MASK			BIT(1)
> +
> +/* DA9150_CORE_CONFIG_A = 0x304 */
> +#define DA9150_CORE_MEMMUX_SHIFT		0
> +#define DA9150_CORE_MEMMUX_MASK			(0x03 << 0)
> +#define DA9150_WDT_AUTO_START_SHIFT		2
> +#define DA9150_WDT_AUTO_START_MASK		BIT(2)
> +#define DA9150_WDT_AUTO_LOCK_SHIFT		3
> +#define DA9150_WDT_AUTO_LOCK_MASK		BIT(3)
> +#define DA9150_WDT_HLT_NO_CLK_SHIFT		4
> +#define DA9150_WDT_HLT_NO_CLK_MASK		BIT(4)
> +
> +/* DA9150_CORE_CONFIG_C = 0x305 */
> +#define DA9150_CORE_SW_SIZE_SHIFT		0
> +#define DA9150_CORE_SW_SIZE_MASK		(0xff << 0)
> +
> +/* DA9150_CORE_CONFIG_B = 0x306 */
> +#define DA9150_BOOTLD_EN_SHIFT			0
> +#define DA9150_BOOTLD_EN_MASK			BIT(0)
> +#define DA9150_CORE_EN_SHIFT			2
> +#define DA9150_CORE_EN_MASK			BIT(2)
> +#define DA9150_CORE_SW_SRC_SHIFT		3
> +#define DA9150_CORE_SW_SRC_MASK			(0x07 << 3)
> +#define DA9150_DEEP_SLEEP_EN_SHIFT		7
> +#define DA9150_DEEP_SLEEP_EN_MASK		BIT(7)
> +
> +/* DA9150_CORE_CFG_DATA_A = 0x307 */
> +#define DA9150_CORE_CFG_DT_A_SHIFT		0
> +#define DA9150_CORE_CFG_DT_A_MASK		(0xff << 0)
> +
> +/* DA9150_CORE_CFG_DATA_B = 0x308 */
> +#define DA9150_CORE_CFG_DT_B_SHIFT		0
> +#define DA9150_CORE_CFG_DT_B_MASK		(0xff << 0)
> +
> +/* DA9150_CORE_CMD_A = 0x309 */
> +#define DA9150_CORE_CMD_SHIFT			0
> +#define DA9150_CORE_CMD_MASK			(0xff << 0)
> +
> +/* DA9150_CORE_DATA_A = 0x30A */
> +#define DA9150_CORE_DATA_0_SHIFT		0
> +#define DA9150_CORE_DATA_0_MASK			(0xff << 0)
> +
> +/* DA9150_CORE_DATA_B = 0x30B */
> +#define DA9150_CORE_DATA_1_SHIFT		0
> +#define DA9150_CORE_DATA_1_MASK			(0xff << 0)
> +
> +/* DA9150_CORE_DATA_C = 0x30C */
> +#define DA9150_CORE_DATA_2_SHIFT		0
> +#define DA9150_CORE_DATA_2_MASK			(0xff << 0)
> +
> +/* DA9150_CORE_DATA_D = 0x30D */
> +#define DA9150_CORE_DATA_3_SHIFT		0
> +#define DA9150_CORE_DATA_3_MASK			(0xff << 0)
> +
> +/* DA9150_CORE2WIRE_STAT_A = 0x310 */
> +#define DA9150_FW_FWDL_ERR_SHIFT		7
> +#define DA9150_FW_FWDL_ERR_MASK			BIT(7)
> +
> +/* DA9150_CORE2WIRE_CTRL_A = 0x311 */
> +#define DA9150_FW_FWDL_EN_SHIFT			0
> +#define DA9150_FW_FWDL_EN_MASK			BIT(0)
> +#define DA9150_FG_QIF_EN_SHIFT			1
> +#define DA9150_FG_QIF_EN_MASK			BIT(1)
> +#define DA9150_CORE_BASE_ADDR_SHIFT		4
> +#define DA9150_CORE_BASE_ADDR_MASK		(0x0f << 4)
> +
> +/* DA9150_FW_CTRL_A = 0x312 */
> +#define DA9150_FW_SEAL_SHIFT			0
> +#define DA9150_FW_SEAL_MASK			(0xff << 0)
> +
> +/* DA9150_FW_CTRL_C = 0x313 */
> +#define DA9150_FW_FWDL_CRC_SHIFT		0
> +#define DA9150_FW_FWDL_CRC_MASK			(0xff << 0)
> +
> +/* DA9150_FW_CTRL_D = 0x314 */
> +#define DA9150_FW_FWDL_BASE_SHIFT		0
> +#define DA9150_FW_FWDL_BASE_MASK		(0x0f << 0)
> +
> +/* DA9150_FG_CTRL_A = 0x315 */
> +#define DA9150_FG_QIF_CODE_SHIFT		0
> +#define DA9150_FG_QIF_CODE_MASK			(0xff << 0)
> +
> +/* DA9150_FG_CTRL_B = 0x316 */
> +#define DA9150_FG_QIF_VALUE_SHIFT		0
> +#define DA9150_FG_QIF_VALUE_MASK		(0xff << 0)
> +
> +/* DA9150_FW_CTRL_E = 0x317 */
> +#define DA9150_FW_FWDL_SEG_SHIFT		0
> +#define DA9150_FW_FWDL_SEG_MASK			(0xff << 0)
> +
> +/* DA9150_FW_CTRL_B = 0x318 */
> +#define DA9150_FW_FWDL_VALUE_SHIFT		0
> +#define DA9150_FW_FWDL_VALUE_MASK		(0xff << 0)
> +
> +/* DA9150_GPADC_CMAN = 0x320 */
> +#define DA9150_GPADC_CEN_SHIFT			0
> +#define DA9150_GPADC_CEN_MASK			BIT(0)
> +#define DA9150_GPADC_CMUX_SHIFT			1
> +#define DA9150_GPADC_CMUX_MASK			(0x1f << 1)
> +
> +/* DA9150_GPADC_CRES_A = 0x322 */
> +#define DA9150_GPADC_CRES_H_SHIFT		0
> +#define DA9150_GPADC_CRES_H_MASK		(0xff << 0)
> +
> +/* DA9150_GPADC_CRES_B = 0x323 */
> +#define DA9150_GPADC_CRUN_SHIFT			0
> +#define DA9150_GPADC_CRUN_MASK			BIT(0)
> +#define DA9150_GPADC_CRES_L_SHIFT		6
> +#define DA9150_GPADC_CRES_L_MASK		(0x03 << 6)
> +
> +/* DA9150_CC_CFG_A = 0x328 */
> +#define DA9150_CC_EN_SHIFT			0
> +#define DA9150_CC_EN_MASK			BIT(0)
> +#define DA9150_CC_TIMEBASE_SHIFT		1
> +#define DA9150_CC_TIMEBASE_MASK			(0x03 << 1)
> +#define DA9150_CC_CFG_SHIFT			5
> +#define DA9150_CC_CFG_MASK			(0x03 << 5)
> +#define DA9150_CC_ENDLESS_MODE_SHIFT		7
> +#define DA9150_CC_ENDLESS_MODE_MASK		BIT(7)
> +
> +/* DA9150_CC_CFG_B = 0x329 */
> +#define DA9150_CC_OPT_SHIFT			0
> +#define DA9150_CC_OPT_MASK			(0x03 << 0)
> +#define DA9150_CC_PREAMP_SHIFT			2
> +#define DA9150_CC_PREAMP_MASK			(0x03 << 2)
> +
> +/* DA9150_CC_ICHG_RES_A = 0x32A */
> +#define DA9150_CC_ICHG_RES_H_SHIFT		0
> +#define DA9150_CC_ICHG_RES_H_MASK		(0xff << 0)
> +
> +/* DA9150_CC_ICHG_RES_B = 0x32B */
> +#define DA9150_CC_ICHG_RES_L_SHIFT		3
> +#define DA9150_CC_ICHG_RES_L_MASK		(0x1f << 3)
> +
> +/* DA9150_CC_IAVG_RES_A = 0x32C */
> +#define DA9150_CC_IAVG_RES_H_SHIFT		0
> +#define DA9150_CC_IAVG_RES_H_MASK		(0xff << 0)
> +
> +/* DA9150_CC_IAVG_RES_B = 0x32D */
> +#define DA9150_CC_IAVG_RES_L_SHIFT		0
> +#define DA9150_CC_IAVG_RES_L_MASK		(0xff << 0)
> +
> +/* DA9150_TAUX_CTRL_A = 0x330 */
> +#define DA9150_TAUX_EN_SHIFT			0
> +#define DA9150_TAUX_EN_MASK			BIT(0)
> +#define DA9150_TAUX_MOD_SHIFT			1
> +#define DA9150_TAUX_MOD_MASK			BIT(1)
> +#define DA9150_TAUX_UPDATE_SHIFT		2
> +#define DA9150_TAUX_UPDATE_MASK			BIT(2)
> +
> +/* DA9150_TAUX_RELOAD_H = 0x332 */
> +#define DA9150_TAUX_RLD_H_SHIFT			0
> +#define DA9150_TAUX_RLD_H_MASK			(0xff << 0)
> +
> +/* DA9150_TAUX_RELOAD_L = 0x333 */
> +#define DA9150_TAUX_RLD_L_SHIFT			3
> +#define DA9150_TAUX_RLD_L_MASK			(0x1f << 3)
> +
> +/* DA9150_TAUX_VALUE_H = 0x334 */
> +#define DA9150_TAUX_VAL_H_SHIFT			0
> +#define DA9150_TAUX_VAL_H_MASK			(0xff << 0)
> +
> +/* DA9150_TAUX_VALUE_L = 0x335 */
> +#define DA9150_TAUX_VAL_L_SHIFT			3
> +#define DA9150_TAUX_VAL_L_MASK			(0x1f << 3)
> +
> +/* DA9150_AUX_DATA_0 = 0x338 */
> +#define DA9150_AUX_DAT_0_SHIFT			0
> +#define DA9150_AUX_DAT_0_MASK			(0xff << 0)
> +
> +/* DA9150_AUX_DATA_1 = 0x339 */
> +#define DA9150_AUX_DAT_1_SHIFT			0
> +#define DA9150_AUX_DAT_1_MASK			(0xff << 0)
> +
> +/* DA9150_AUX_DATA_2 = 0x33A */
> +#define DA9150_AUX_DAT_2_SHIFT			0
> +#define DA9150_AUX_DAT_2_MASK			(0xff << 0)
> +
> +/* DA9150_AUX_DATA_3 = 0x33B */
> +#define DA9150_AUX_DAT_3_SHIFT			0
> +#define DA9150_AUX_DAT_3_MASK			(0xff << 0)
> +
> +/* DA9150_BIF_CTRL = 0x340 */
> +#define DA9150_BIF_ISRC_EN_SHIFT		0
> +#define DA9150_BIF_ISRC_EN_MASK			BIT(0)
> +
> +/* DA9150_TBAT_CTRL_A = 0x342 */
> +#define DA9150_TBAT_EN_SHIFT			0
> +#define DA9150_TBAT_EN_MASK			BIT(0)
> +#define DA9150_TBAT_SW1_SHIFT			1
> +#define DA9150_TBAT_SW1_MASK			BIT(1)
> +#define DA9150_TBAT_SW2_SHIFT			2
> +#define DA9150_TBAT_SW2_MASK			BIT(2)
> +
> +/* DA9150_TBAT_CTRL_B = 0x343 */
> +#define DA9150_TBAT_SW_FRC_SHIFT		0
> +#define DA9150_TBAT_SW_FRC_MASK			BIT(0)
> +#define DA9150_TBAT_STAT_SW1_SHIFT		1
> +#define DA9150_TBAT_STAT_SW1_MASK		BIT(1)
> +#define DA9150_TBAT_STAT_SW2_SHIFT		2
> +#define DA9150_TBAT_STAT_SW2_MASK		BIT(2)
> +#define DA9150_TBAT_HIGH_CURR_SHIFT		3
> +#define DA9150_TBAT_HIGH_CURR_MASK		BIT(3)
> +
> +/* DA9150_TBAT_RES_A = 0x344 */
> +#define DA9150_TBAT_RES_H_SHIFT			0
> +#define DA9150_TBAT_RES_H_MASK			(0xff << 0)
> +
> +/* DA9150_TBAT_RES_B = 0x345 */
> +#define DA9150_TBAT_RES_DIS_SHIFT		0
> +#define DA9150_TBAT_RES_DIS_MASK		BIT(0)
> +#define DA9150_TBAT_RES_L_SHIFT			6
> +#define DA9150_TBAT_RES_L_MASK			(0x03 << 6)
> +
> +#endif /* __DA9150_REGISTERS_H */
> --
> 1.9.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

^ permalink raw reply

* Re: [PATCH 2/8] mfd: da9150: Add DT binding documentation for core
From: Jonathan Cameron @ 2014-09-27 10:36 UTC (permalink / raw)
  To: Adam Thomson, Lee Jones, Samuel Ortiz, linux-iio,
	Sebastian Reichel, Dmitry Eremin-Solenikov, David Woodhouse,
	linux-pm, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Grant Likely, devicetree, Andrew Morton, Joe Perches,
	linux-api
  Cc: linux-kernel, support.opensource
In-Reply-To: <b71815ff49a1cabe2c7907a96cc1be34f924648e.1411396719.git.Adam.Thomson.Opensource@diasemi.com>

On 23/09/14 11:53, Adam Thomson wrote:
> Signed-off-by: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
Obviously this really wants a review from one of the device tree guys, but I
have a few
bits based on what Mark has recently said in other reviews ;)
> ---
>  Documentation/devicetree/bindings/mfd/da9150.txt | 41 ++++++++++++++++++++++++
>  1 file changed, 41 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mfd/da9150.txt
> 
> diff --git a/Documentation/devicetree/bindings/mfd/da9150.txt b/Documentation/devicetree/bindings/mfd/da9150.txt
> new file mode 100644
> index 0000000..d7de150
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/da9150.txt
> @@ -0,0 +1,41 @@
> +Dialog Semiconductor DA9150 Combined Charger/Fuel-Gauge MFD bindings
> +
> +DA9150 consists of a group of sub-devices (I2C Only):
What does I2C only add to the description?
> +
> +Device			 Description
> +------			 -----------
> +da9150-gpadc		: IIO - GPADC
Given usual aversion to anything driver specific in the device tree description
you probably
just want to describe what they do rather than what subsystem provides the driver.

> +da9150-charger		: Power Supply (Charger)
> +
> +======
> +
> +Required properties:
> +- compatible : Should be "dlg,da9150"
> +- reg: Specifies the I2C slave address
> +- interrupt-parent: Specifies the phandle of the interrupt controller to which
> +  the IRQs from da9150 are delivered to.
> +- interrupts: IRQ line info for da9150 chip.
Cross refer to the standard interrupts doc for these...

> +- interrupt-controller: da9150 has internal IRQs (own IRQ domain).
> +
> +Sub-devices:
> +- da9150-gpadc: See Documentation/devicetree/bindings/iio/adc/da9150-gpadc.txt
> +- da9150-charger: See Documentation/devicetree/bindings/power/da9150-charger.txt
> +
> +
> +Example:
> +
> +	charger_fg: da9150@58 {
> +		compatible = "dlg,da9150";
> +		reg = <0x58>;
> +		interrupt-parent = <&gpio6>;
> +		interrupts = <11 IRQ_TYPE_LEVEL_LOW>;
> +		interrupt-controller;
> +
> +		gpadc: da9150-gpadc {
> +			...
> +		};
> +
> +		da9150-charger {
> +			...
> +		};
> +	};
> --
> 1.9.3
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

^ permalink raw reply

* Re: [PATCH 3/8] iio: Add support for DA9150 GPADC
From: Jonathan Cameron @ 2014-09-27 10:49 UTC (permalink / raw)
  To: Adam Thomson, Lee Jones, Samuel Ortiz, linux-iio,
	Sebastian Reichel, Dmitry Eremin-Solenikov, David Woodhouse,
	linux-pm, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Grant Likely, devicetree, Andrew Morton, Joe Perches,
	linux-api
  Cc: linux-kernel, support.opensource
In-Reply-To: <ee4c56931f326d8ee9c1313c44456bce2dec8b8c.1411396719.git.Adam.Thomson.Opensource@diasemi.com>

On 23/09/14 11:53, Adam Thomson wrote:
> This patch adds support for DA9150 Charger & Fuel-Gauge IC GPADC.
>
> Signed-off-by: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
Very nearly there although I still would prefer all channels to use
(raw + offset) * scale and let userspace or in kernel conversion functions
handle it.

> ---
>  drivers/iio/adc/Kconfig        |   9 +
>  drivers/iio/adc/Makefile       |   1 +
>  drivers/iio/adc/da9150-gpadc.c | 406 +++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 416 insertions(+)
>  create mode 100644 drivers/iio/adc/da9150-gpadc.c
>
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 11b048a..8041347 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -127,6 +127,15 @@ config AT91_ADC
>  	help
>  	  Say yes here to build support for Atmel AT91 ADC.
>
> +config DA9150_GPADC
> +	tristate "Dialog DA9150 GPADC driver support"
> +	depends on MFD_DA9150
> +	help
> +	  Say yes here to build support for Dialog DA9150 GPADC.
> +
> +	  This driver can also be built as a module. If chosen, the module name
> +	  will be da9150-gpadc.
> +
>  config EXYNOS_ADC
>  	tristate "Exynos ADC driver support"
>  	depends on ARCH_EXYNOS || (OF && COMPILE_TEST)
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index ad81b51..48413d2 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -14,6 +14,7 @@ obj-$(CONFIG_AD7793) += ad7793.o
>  obj-$(CONFIG_AD7887) += ad7887.o
>  obj-$(CONFIG_AD799X) += ad799x.o
>  obj-$(CONFIG_AT91_ADC) += at91_adc.o
> +obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
>  obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
>  obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
>  obj-$(CONFIG_MAX1027) += max1027.o
> diff --git a/drivers/iio/adc/da9150-gpadc.c b/drivers/iio/adc/da9150-gpadc.c
> new file mode 100644
> index 0000000..2b83ee0
> --- /dev/null
> +++ b/drivers/iio/adc/da9150-gpadc.c
> @@ -0,0 +1,406 @@
> +/*
> + * DA9150 GPADC Driver
> + *
> + * Copyright (c) 2014 Dialog Semiconductor
> + *
> + * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
> + *
> + * This program is free software; you can redistribute  it and/or modify it
> + * under  the terms of  the GNU General  Public License as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/mutex.h>
> +#include <linux/completion.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/machine.h>
> +#include <linux/iio/driver.h>
> +#include <linux/mfd/da9150/core.h>
> +#include <linux/mfd/da9150/registers.h>
> +
> +/* Channels */
> +enum da9150_gpadc_hw_channel {
> +	DA9150_GPADC_HW_CHAN_GPIOA_2V = 0,
> +	DA9150_GPADC_HW_CHAN_GPIOA_2V_,
> +	DA9150_GPADC_HW_CHAN_GPIOB_2V,
> +	DA9150_GPADC_HW_CHAN_GPIOB_2V_,
> +	DA9150_GPADC_HW_CHAN_GPIOC_2V,
> +	DA9150_GPADC_HW_CHAN_GPIOC_2V_,
> +	DA9150_GPADC_HW_CHAN_GPIOD_2V,
> +	DA9150_GPADC_HW_CHAN_GPIOD_2V_,
> +	DA9150_GPADC_HW_CHAN_IBUS_SENSE,
> +	DA9150_GPADC_HW_CHAN_IBUS_SENSE_,
> +	DA9150_GPADC_HW_CHAN_VBUS_DIV,
> +	DA9150_GPADC_HW_CHAN_VBUS_DIV_,
> +	DA9150_GPADC_HW_CHAN_ID,
> +	DA9150_GPADC_HW_CHAN_ID_,
> +	DA9150_GPADC_HW_CHAN_VSYS,
> +	DA9150_GPADC_HW_CHAN_VSYS_,
> +	DA9150_GPADC_HW_CHAN_GPIOA_6V,
> +	DA9150_GPADC_HW_CHAN_GPIOA_6V_,
> +	DA9150_GPADC_HW_CHAN_GPIOB_6V,
> +	DA9150_GPADC_HW_CHAN_GPIOB_6V_,
> +	DA9150_GPADC_HW_CHAN_GPIOC_6V,
> +	DA9150_GPADC_HW_CHAN_GPIOC_6V_,
> +	DA9150_GPADC_HW_CHAN_GPIOD_6V,
> +	DA9150_GPADC_HW_CHAN_GPIOD_6V_,
> +	DA9150_GPADC_HW_CHAN_VBAT,
> +	DA9150_GPADC_HW_CHAN_VBAT_,
> +	DA9150_GPADC_HW_CHAN_TBAT,
> +	DA9150_GPADC_HW_CHAN_TBAT_,
> +	DA9150_GPADC_HW_CHAN_TJUNC_CORE,
> +	DA9150_GPADC_HW_CHAN_TJUNC_CORE_,
> +	DA9150_GPADC_HW_CHAN_TJUNC_OVP,
> +	DA9150_GPADC_HW_CHAN_TJUNC_OVP_,
> +};
> +
> +enum da9150_gpadc_channel {
> +	DA9150_GPADC_CHAN_GPIOA = 0,
> +	DA9150_GPADC_CHAN_GPIOB,
> +	DA9150_GPADC_CHAN_GPIOC,
> +	DA9150_GPADC_CHAN_GPIOD,
> +	DA9150_GPADC_CHAN_IBUS,
> +	DA9150_GPADC_CHAN_VBUS,
> +	DA9150_GPADC_CHAN_ID,
> +	DA9150_GPADC_CHAN_VSYS,
> +	DA9150_GPADC_CHAN_VBAT,
> +	DA9150_GPADC_CHAN_TBAT,
> +	DA9150_GPADC_CHAN_TJUNC_CORE,
> +	DA9150_GPADC_CHAN_TJUNC_OVP,
> +};
> +
> +/* Private data */
> +struct da9150_gpadc {
> +	struct da9150 *da9150;
> +	struct device *dev;
> +
> +	struct mutex lock;
> +	struct completion complete;
> +};
> +
> +
> +static irqreturn_t da9150_gpadc_irq(int irq, void *data)
> +{
> +
> +	struct da9150_gpadc *gpadc = data;
> +
> +	complete(&gpadc->complete);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int da9150_gpadc_read_adc(struct da9150_gpadc *gpadc, int hw_chan)
> +{
> +	u8 result_regs[2];
> +	int result;
> +
> +	mutex_lock(&gpadc->lock);
> +
> +	/* Set channel & enable measurement */
> +	da9150_reg_write(gpadc->da9150, DA9150_GPADC_MAN,
> +			 (DA9150_GPADC_EN_MASK |
> +			  hw_chan << DA9150_GPADC_MUX_SHIFT));
> +
> +	/* Consume left-over completion from a previous timeout */
> +	try_wait_for_completion(&gpadc->complete);
> +
> +	/* Check for actual completion */
> +	wait_for_completion_timeout(&gpadc->complete, msecs_to_jiffies(5));
> +
> +	/* Read result and status from device */
> +	da9150_bulk_read(gpadc->da9150, DA9150_GPADC_RES_A, 2, result_regs);
> +
> +	mutex_unlock(&gpadc->lock);
> +
> +	/* Check to make sure device really has completed reading */
> +	if (result_regs[1] & DA9150_GPADC_RUN_MASK) {
> +		dev_err(gpadc->dev, "Timeout on channel %d of GPADC\n",
> +			hw_chan);
> +		return -ETIMEDOUT;
> +	}
> +
> +	/* LSBs - 2 bits */
> +	result = (result_regs[1] & DA9150_GPADC_RES_L_MASK) >>
> +		 DA9150_GPADC_RES_L_SHIFT;
> +	/* MSBs - 8 bits */
> +	result |= result_regs[0] << DA9150_GPADC_RES_L_BITS;
> +
> +	return result;
> +}
> +
> +static inline int da9150_gpadc_gpio_6v_voltage_now(int raw_val)
> +{
> +	/* Convert to mV */
> +	return (6 * ((raw_val * 1000) + 500)) / 1024;
These could all be expressed as raw values with offsets
and scales (and that would be preferred).
E.g. This one has offset 500000 and scale 6000/1024 or even
better use IIO_VAL_FRACTIONAL_LOG2 for scale with val1 = 6000
and val2 = (log_2 1024) = 10.

> +}
> +
> +static inline int da9150_gpadc_ibus_current_avg(int raw_val)
> +{
> +	/* Convert to mA */
> +	return (4 * ((raw_val * 1000) + 500)) / 2048;
> +}
> +
> +static inline int da9150_gpadc_vbus_21v_voltage_now(int raw_val)
> +{
> +	/* Convert to mV */
> +	return (21 * ((raw_val * 1000) + 500)) / 1024;
> +}
> +
> +static inline int da9150_gpadc_vsys_6v_voltage_now(int raw_val)
> +{
> +	/* Convert to mV */
> +	return (3 * ((raw_val * 1000) + 500)) / 512;
> +}
> +
> +static int da9150_gpadc_read_processed(struct da9150_gpadc *gpadc, int channel,
> +				       int hw_chan, int *val)
> +{
> +	int raw_val;
> +
> +	raw_val = da9150_gpadc_read_adc(gpadc, hw_chan);
> +	if (raw_val < 0)
> +		return raw_val;
> +
> +	switch (channel) {
> +	case DA9150_GPADC_CHAN_GPIOA:
> +	case DA9150_GPADC_CHAN_GPIOB:
> +	case DA9150_GPADC_CHAN_GPIOC:
> +	case DA9150_GPADC_CHAN_GPIOD:
> +		*val = da9150_gpadc_gpio_6v_voltage_now(raw_val);
> +		break;
> +	case DA9150_GPADC_CHAN_IBUS:
> +		*val = da9150_gpadc_ibus_current_avg(raw_val);
> +		break;
> +	case DA9150_GPADC_CHAN_VBUS:
> +		*val = da9150_gpadc_vbus_21v_voltage_now(raw_val);
> +		break;
> +	case DA9150_GPADC_CHAN_VSYS:
> +		*val = da9150_gpadc_vsys_6v_voltage_now(raw_val);
> +		break;
> +	default:
> +		/* No processing for other channels so return raw value */
> +		*val = raw_val;
> +		break;
> +	}
> +
> +	return IIO_VAL_INT;
> +}
> +
> +static int da9150_gpadc_read_scale(int channel, int *val, int *val2)
> +{
> +	switch (channel) {
> +	case DA9150_GPADC_CHAN_VBAT:
> +		*val = 2932;
> +		*val2 = 1000;
> +		return IIO_VAL_FRACTIONAL;
> +	case DA9150_GPADC_CHAN_TJUNC_CORE:
> +	case DA9150_GPADC_CHAN_TJUNC_OVP:
> +		*val = 1000000;
> +		*val2 = 4420;
> +		return IIO_VAL_FRACTIONAL;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int da9150_gpadc_read_offset(int channel, int *val)
> +{
> +	switch (channel) {
> +	case DA9150_GPADC_CHAN_VBAT:
> +		*val = 1500000 / 2932;
> +		return IIO_VAL_INT;
> +	case DA9150_GPADC_CHAN_TJUNC_CORE:
> +	case DA9150_GPADC_CHAN_TJUNC_OVP:
> +		*val = -144;
> +		return IIO_VAL_INT;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int da9150_gpadc_read_raw(struct iio_dev *indio_dev,
> +				 struct iio_chan_spec const *chan,
> +				 int *val, int *val2, long mask)
> +{
> +	struct da9150_gpadc *gpadc = iio_priv(indio_dev);
> +
> +	if ((chan->channel < DA9150_GPADC_CHAN_GPIOA) ||
> +	    (chan->channel > DA9150_GPADC_CHAN_TJUNC_OVP))
> +		return -EINVAL;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +	case IIO_CHAN_INFO_PROCESSED:
> +		return da9150_gpadc_read_processed(gpadc, chan->channel,
> +						   chan->address, val);
> +	case IIO_CHAN_INFO_SCALE:
> +		return da9150_gpadc_read_scale(chan->channel, val, val2);
> +	case IIO_CHAN_INFO_OFFSET:
> +		return da9150_gpadc_read_offset(chan->channel, val);
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static const struct iio_info da9150_gpadc_info = {
> +	.read_raw = &da9150_gpadc_read_raw,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +#define DA9150_GPADC_CHANNEL(_id, _hw_id, _type, chan_info,	\
> +			     _ext_name) {			\
> +	.type = _type,						\
> +	.indexed = 1,						\
> +	.channel = DA9150_GPADC_CHAN_##_id,			\
> +	.address = DA9150_GPADC_HW_CHAN_##_hw_id,		\
> +	.info_mask_separate = chan_info,			\
> +	.extend_name = _ext_name,				\
> +	.datasheet_name = #_id,					\
> +}
> +
> +#define DA9150_GPADC_CHANNEL_RAW(_id, _hw_id, _type, _ext_name)	\
> +	DA9150_GPADC_CHANNEL(_id, _hw_id, _type,		\
> +			     BIT(IIO_CHAN_INFO_RAW), _ext_name)
> +
> +#define DA9150_GPADC_CHANNEL_SCALED(_id, _hw_id, _type, _ext_name)	\
> +	DA9150_GPADC_CHANNEL(_id, _hw_id, _type,			\
> +			     BIT(IIO_CHAN_INFO_RAW) |			\
> +			     BIT(IIO_CHAN_INFO_SCALE) |			\
> +			     BIT(IIO_CHAN_INFO_OFFSET),			\
> +			     _ext_name)
> +
> +#define DA9150_GPADC_CHANNEL_PROCESSED(_id, _hw_id, _type, _ext_name)	\
> +	DA9150_GPADC_CHANNEL(_id, _hw_id, _type,			\
> +			     BIT(IIO_CHAN_INFO_PROCESSED), _ext_name)
> +
> +/* Supported channels */
> +static const struct iio_chan_spec da9150_gpadc_channels[] = {
> +	DA9150_GPADC_CHANNEL_PROCESSED(GPIOA, GPIOA_6V, IIO_VOLTAGE, "GPIOA"),
> +	DA9150_GPADC_CHANNEL_PROCESSED(GPIOB, GPIOB_6V, IIO_VOLTAGE, "GPIOB"),
> +	DA9150_GPADC_CHANNEL_PROCESSED(GPIOC, GPIOC_6V, IIO_VOLTAGE, "GPIOC"),
> +	DA9150_GPADC_CHANNEL_PROCESSED(GPIOD, GPIOD_6V, IIO_VOLTAGE, "GPIOD"),
> +	DA9150_GPADC_CHANNEL_PROCESSED(IBUS, IBUS_SENSE, IIO_CURRENT, "IBUS"),
> +	DA9150_GPADC_CHANNEL_PROCESSED(VBUS, VBUS_DIV_, IIO_VOLTAGE, "VBUS"),
> +	DA9150_GPADC_CHANNEL_RAW(ID, ID, IIO_VOLTAGE, "ID"),
> +	DA9150_GPADC_CHANNEL_PROCESSED(VSYS, VSYS, IIO_VOLTAGE, "VSYS"),
> +	DA9150_GPADC_CHANNEL_SCALED(VBAT, VBAT, IIO_VOLTAGE, "VBAT"),
> +	DA9150_GPADC_CHANNEL_RAW(TBAT, TBAT, IIO_VOLTAGE, "TBAT"),
> +	DA9150_GPADC_CHANNEL_SCALED(TJUNC_CORE, TJUNC_CORE, IIO_TEMP,
> +				    "TJUNC_CORE"),
> +	DA9150_GPADC_CHANNEL_SCALED(TJUNC_OVP, TJUNC_OVP, IIO_TEMP,
> +				    "TJUNC_OVP"),
> +};
> +
> +/* Default maps used by da9150-charger */
> +static struct iio_map da9150_gpadc_default_maps[] = {
> +	{
> +		.consumer_dev_name = "da9150-charger",
> +		.consumer_channel = "CHAN_IBUS",
> +		.adc_channel_label = "IBUS",
> +	},
> +	{
> +		.consumer_dev_name = "da9150-charger",
> +		.consumer_channel = "CHAN_VBUS",
> +		.adc_channel_label = "VBUS",
> +	},
> +	{
> +		.consumer_dev_name = "da9150-charger",
> +		.consumer_channel = "CHAN_TJUNC",
> +		.adc_channel_label = "TJUNC_CORE",
> +	},
> +	{
> +		.consumer_dev_name = "da9150-charger",
> +		.consumer_channel = "CHAN_VBAT",
> +		.adc_channel_label = "VBAT",
> +	},
> +	{},
> +};
> +
> +static int da9150_gpadc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct da9150 *da9150 = dev_get_drvdata(dev->parent);
> +	struct da9150_gpadc *gpadc;
> +	struct iio_dev *indio_dev;
> +	int irq, ret;
> +
> +	indio_dev = devm_iio_device_alloc(&pdev->dev,
> +					  sizeof(struct da9150_gpadc));
> +	if (!indio_dev) {
> +		dev_err(&pdev->dev, "Failed to allocate IIO device\n");
> +		return -ENOMEM;
> +	}
> +	gpadc = iio_priv(indio_dev);
> +
> +	platform_set_drvdata(pdev, indio_dev);
> +	gpadc->da9150 = da9150;
> +	gpadc->dev = dev;
> +
> +	ret = iio_map_array_register(indio_dev, da9150_gpadc_default_maps);
> +	if (ret) {
> +		dev_err(dev, "Failed to register IIO maps: %d\n", ret);
> +		return ret;
> +	}
I'd suggest doing the devm_request_thread_irq before the iio_map_array
stuff.  This is purely to avoid the order during remove not being
obviously correct as it isn't the reverse of during probe.

> +
> +	mutex_init(&gpadc->lock);
> +	init_completion(&gpadc->complete);
> +
> +	irq = platform_get_irq_byname(pdev, "GPADC");
> +	ret = devm_request_threaded_irq(dev, irq, NULL, da9150_gpadc_irq,
> +					IRQF_ONESHOT, "GPADC", gpadc);
> +	if (ret) {
> +		dev_err(dev, "Failed to request IRQ %d: %d\n", irq, ret);
> +		goto iio_map_unreg;
> +	}
> +
> +	indio_dev->name = dev_name(dev);
> +	indio_dev->dev.parent = dev;
> +	indio_dev->dev.of_node = pdev->dev.of_node;
> +	indio_dev->info = &da9150_gpadc_info;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +	indio_dev->channels = da9150_gpadc_channels;
> +	indio_dev->num_channels = ARRAY_SIZE(da9150_gpadc_channels);
> +
> +	ret = iio_device_register(indio_dev);
> +	if (ret) {
> +		dev_err(dev, "Failed to register IIO device: %d\n", ret);
> +		goto iio_map_unreg;
> +	}
> +
> +	return 0;
> +
> +iio_map_unreg:
> +	iio_map_array_unregister(indio_dev);
> +
> +	return ret;
> +}
> +
> +static int da9150_gpadc_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +
> +	iio_map_array_unregister(indio_dev);
Twice in one day.  I'm definitely thinking we should add a
devm version of iio_map_array_register...

> +	iio_device_unregister(indio_dev);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver da9150_gpadc_driver = {
> +	.driver = {
> +		.name = "da9150-gpadc",
> +	},
> +	.probe = da9150_gpadc_probe,
> +	.remove = da9150_gpadc_remove,
> +};
> +
> +module_platform_driver(da9150_gpadc_driver);
> +
> +MODULE_DESCRIPTION("GPADC Driver for DA9150");
> +MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com");
> +MODULE_LICENSE("GPL");
> --
> 1.9.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

^ permalink raw reply

* Re: [PATCH 4/8] iio: da9150: Add DT binding documentation for GPADC
From: Jonathan Cameron @ 2014-09-27 10:52 UTC (permalink / raw)
  To: Adam Thomson, Lee Jones, Samuel Ortiz,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, Sebastian Reichel,
	Dmitry Eremin-Solenikov, David Woodhouse,
	linux-pm-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Andrew Morton, Joe Perches,
	linux-api-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	support.opensource-WBD+wuPFNBhBDgjK7y7TUQ
In-Reply-To: <6697dbc9711c9c3551aede1b5e4f986caa0d1a44.1411396719.git.Adam.Thomson.Opensource-WBD+wuPFNBhBDgjK7y7TUQ@public.gmane.org>

On 23/09/14 11:53, Adam Thomson wrote:
> Signed-off-by: Adam Thomson <Adam.Thomson.Opensource-WBD+wuPFNBhBDgjK7y7TUQ@public.gmane.org>
Acked-by: Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> ---
>  .../devicetree/bindings/iio/adc/da9150-gpadc.txt         | 16 ++++++++++++++++
>  1 file changed, 16 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/da9150-gpadc.txt
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/da9150-gpadc.txt b/Documentation/devicetree/bindings/iio/adc/da9150-gpadc.txt
> new file mode 100644
> index 0000000..c07228d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/da9150-gpadc.txt
> @@ -0,0 +1,16 @@
> +Dialog Semiconductor DA9150 IIO GPADC bindings
> +
> +Required properties:
> +- compatible: "dlg,da9150-gpadc" for DA9150 IIO GPADC
> +- #io-channel-cells: Should be set to <1>
> +  (See Documentation/devicetree/bindings/iio/iio-bindings.txt for further info)
> +
> +For further information on GPADC channels, see device datasheet.
> +
> +
> +Example:
> +
> +	gpadc: da9150-gpadc {
> +		compatible = "dlg,da9150-gpadc";
> +		#io-channel-cells = <1>;
> +	};
> --
> 1.9.3
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

^ permalink raw reply

* Re: [PATCH 5/8] power: Add support for DA9150 Charger
From: Jonathan Cameron @ 2014-09-27 11:09 UTC (permalink / raw)
  To: Adam Thomson, Lee Jones, Samuel Ortiz,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, Sebastian Reichel,
	Dmitry Eremin-Solenikov, David Woodhouse,
	linux-pm-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Andrew Morton, Joe Perches,
	linux-api-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	support.opensource-WBD+wuPFNBhBDgjK7y7TUQ
In-Reply-To: <340f6a21981f52030dda665fb70ee58328e8c161.1411396719.git.Adam.Thomson.Opensource-WBD+wuPFNBhBDgjK7y7TUQ@public.gmane.org>

On 23/09/14 11:53, Adam Thomson wrote:
> This patch adds support for DA9150 Charger & Fuel-Gauge IC Charger.
>
> Signed-off-by: Adam Thomson <Adam.Thomson.Opensource-WBD+wuPFNBhBDgjK7y7TUQ@public.gmane.org>
The IIO elements of this look fine to me. I'm not failiar enough with
the charger framework to give more than a superficial opinion on that.

I do note that we have a lot of attributes added to the device after
registration which we have spent time elsewhere in the kernel avoiding
because of notification issues...

http://kroah.com/log/blog/2013/06/26/how-to-create-a-sysfs-file-correctly/
(convient summary of this from GregKH)

Still probably not an issue with this driver it self...
> ---
>  drivers/power/Kconfig          |  12 +
>  drivers/power/Makefile         |   1 +
>  drivers/power/da9150-charger.c | 740 +++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 753 insertions(+)
>  create mode 100644 drivers/power/da9150-charger.c
>
> diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
> index 73cfcdf..622884d 100644
> --- a/drivers/power/Kconfig
> +++ b/drivers/power/Kconfig
> @@ -192,6 +192,18 @@ config BATTERY_DA9052
>  	  Say Y here to enable support for batteries charger integrated into
>  	  DA9052 PMIC.
>
> +config CHARGER_DA9150
> +	tristate "Dialog Semiconductor DA9150 Charger support"
> +	depends on MFD_DA9150
> +	depends on DA9150_GPADC
> +	depends on IIO
> +	help
> +	  Say Y here to enable support for charger unit of the DA9150
> +	  Integrated Charger & Fuel-Gauge IC.
> +
> +	  This driver can also be built as a module. If so, the module will be
> +	  called da9150-charger.
> +
>  config BATTERY_MAX17040
>  	tristate "Maxim MAX17040 Fuel Gauge"
>  	depends on I2C
> diff --git a/drivers/power/Makefile b/drivers/power/Makefile
> index dfa8942..0c1896d 100644
> --- a/drivers/power/Makefile
> +++ b/drivers/power/Makefile
> @@ -31,6 +31,7 @@ obj-$(CONFIG_BATTERY_SBS)	+= sbs-battery.o
>  obj-$(CONFIG_BATTERY_BQ27x00)	+= bq27x00_battery.o
>  obj-$(CONFIG_BATTERY_DA9030)	+= da9030_battery.o
>  obj-$(CONFIG_BATTERY_DA9052)	+= da9052-battery.o
> +obj-$(CONFIG_CHARGER_DA9150)	+= da9150-charger.o
>  obj-$(CONFIG_BATTERY_MAX17040)	+= max17040_battery.o
>  obj-$(CONFIG_BATTERY_MAX17042)	+= max17042_battery.o
>  obj-$(CONFIG_BATTERY_Z2)	+= z2_battery.o
> diff --git a/drivers/power/da9150-charger.c b/drivers/power/da9150-charger.c
> new file mode 100644
> index 0000000..38345cf
> --- /dev/null
> +++ b/drivers/power/da9150-charger.c
> @@ -0,0 +1,740 @@
> +/*
> + * DA9150 Charger Driver
> + *
> + * Copyright (c) 2014 Dialog Semiconductor
> + *
> + * Author: Adam Thomson <Adam.Thomson.Opensource-WBD+wuPFNBhBDgjK7y7TUQ@public.gmane.org>
> + *
> + * This program is free software; you can redistribute  it and/or modify it
> + * under  the terms of  the GNU General  Public License as published by the
> + * Free Software Foundation;  either version 2 of the  License, or (at your
> + * option) any later version.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/interrupt.h>
> +#include <linux/power_supply.h>
> +#include <linux/notifier.h>
> +#include <linux/usb/phy.h>
> +#include <linux/iio/consumer.h>
> +#include <linux/sysfs.h>
> +#include <linux/mfd/da9150/core.h>
> +#include <linux/mfd/da9150/registers.h>
> +
> +/* Private data */
> +struct da9150_charger_attr_map {
> +	struct device_attribute attr;
> +	u16 reg;
> +	u8 shift;
> +	u8 mask;
> +};
> +
> +struct da9150_charger {
> +	struct da9150 *da9150;
> +	struct device *dev;
> +
> +	struct power_supply usb;
> +	struct power_supply battery;
> +	struct power_supply *supply_online;
> +
> +	struct usb_phy *usb_phy;
> +	struct notifier_block otg_nb;
> +	struct work_struct otg_work;
> +	unsigned long usb_event;
> +
> +	struct iio_channel *ibus_chan;
> +	struct iio_channel *vbus_chan;
> +	struct iio_channel *tjunc_chan;
> +	struct iio_channel *vbat_chan;
> +};
> +
> +static inline int da9150_charger_supply_online(struct da9150_charger *charger,
> +					       struct power_supply *psy,
> +					       union power_supply_propval *val)
> +{
> +	val->intval = (psy == charger->supply_online) ? 1 : 0;
> +
> +	return 0;
> +}
> +
> +/* Charger Properties */
> +static int da9150_charger_vbus_voltage_now(struct da9150_charger *charger,
> +					   union power_supply_propval *val)
> +{
> +	int v_val, ret;
> +
> +	/* Read processed value - mV units */
> +	ret = iio_read_channel_processed(charger->vbus_chan, &v_val);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Convert voltage to expected uV units */
> +	val->intval = v_val * 1000;
> +
> +	return 0;
> +}
> +
> +static int da9150_charger_ibus_current_avg(struct da9150_charger *charger,
> +					   union power_supply_propval *val)
> +{
> +	int i_val, ret;
> +
> +	/* Read processed value - mA units */
> +	ret = iio_read_channel_processed(charger->ibus_chan, &i_val);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Convert current to expected uA units */
> +	val->intval = i_val * 1000;
> +
> +	return 0;
> +}
> +
> +static int da9150_charger_tjunc_temp(struct da9150_charger *charger,
> +				     union power_supply_propval *val)
> +{
> +	int t_val, ret;
> +
> +	/* Read processed value - 0.001 degrees C units */
> +	ret = iio_read_channel_processed(charger->tjunc_chan, &t_val);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Convert temp to expect 0.1 degrees C units */
> +	val->intval = t_val / 100;
> +
> +	return 0;
> +}
> +
> +static enum power_supply_property da9150_charger_props[] = {
> +	POWER_SUPPLY_PROP_ONLINE,
> +	POWER_SUPPLY_PROP_VOLTAGE_NOW,
> +	POWER_SUPPLY_PROP_CURRENT_AVG,
> +	POWER_SUPPLY_PROP_TEMP,
> +};
> +
> +static int da9150_charger_get_prop(struct power_supply *psy,
> +				   enum power_supply_property psp,
> +				   union power_supply_propval *val)
> +{
> +	struct da9150_charger *charger = dev_get_drvdata(psy->dev->parent);
> +	int ret;
> +
> +	switch (psp) {
> +	case POWER_SUPPLY_PROP_ONLINE:
> +		ret = da9150_charger_supply_online(charger, psy, val);
> +		break;
> +	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
> +		ret = da9150_charger_vbus_voltage_now(charger, val);
> +		break;
> +	case POWER_SUPPLY_PROP_CURRENT_AVG:
> +		ret = da9150_charger_ibus_current_avg(charger, val);
> +		break;
> +	case POWER_SUPPLY_PROP_TEMP:
> +		ret = da9150_charger_tjunc_temp(charger, val);
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +/* Battery Properties */
> +static int da9150_charger_battery_status(struct da9150_charger *charger,
> +					 union power_supply_propval *val)
> +{
> +	u8 reg;
> +
> +	/* Check to see if battery is discharging */
> +	reg = da9150_reg_read(charger->da9150, DA9150_STATUS_H);
> +
> +	if (((reg & DA9150_VBUS_STAT_MASK) == DA9150_VBUS_STAT_OFF) ||
> +	    ((reg & DA9150_VBUS_STAT_MASK) == DA9150_VBUS_STAT_WAIT)) {
> +		val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
> +
> +		return 0;
> +	}
> +
> +	reg = da9150_reg_read(charger->da9150, DA9150_STATUS_J);
> +
> +	/* Now check for other states */
> +	switch (reg & DA9150_CHG_STAT_MASK) {
> +	case DA9150_CHG_STAT_ACT:
> +	case DA9150_CHG_STAT_PRE:
> +	case DA9150_CHG_STAT_CC:
> +	case DA9150_CHG_STAT_CV:
> +		val->intval = POWER_SUPPLY_STATUS_CHARGING;
> +		break;
> +	case DA9150_CHG_STAT_OFF:
> +	case DA9150_CHG_STAT_SUSP:
> +	case DA9150_CHG_STAT_TEMP:
> +	case DA9150_CHG_STAT_TIME:
> +	case DA9150_CHG_STAT_BAT:
> +		val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
> +		break;
> +	case DA9150_CHG_STAT_FULL:
> +		val->intval = POWER_SUPPLY_STATUS_FULL;
> +		break;
> +	default:
> +		val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +static int da9150_charger_battery_health(struct da9150_charger *charger,
> +					 union power_supply_propval *val)
> +{
> +	u8 reg;
> +
> +	reg = da9150_reg_read(charger->da9150, DA9150_STATUS_J);
> +
> +	/* Check if temperature limit reached */
> +	switch (reg & DA9150_CHG_TEMP_MASK) {
> +	case DA9150_CHG_TEMP_UNDER:
> +		val->intval = POWER_SUPPLY_HEALTH_COLD;
> +		return 0;
> +	case DA9150_CHG_TEMP_OVER:
> +		val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
> +		return 0;
> +	default:
> +		break;
> +	}
> +
> +	/* Check for other health states */
> +	switch (reg & DA9150_CHG_STAT_MASK) {
> +	case DA9150_CHG_STAT_ACT:
> +	case DA9150_CHG_STAT_PRE:
> +		val->intval = POWER_SUPPLY_HEALTH_DEAD;
> +		break;
> +	case DA9150_CHG_STAT_TIME:
> +		val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
> +		break;
> +	default:
> +		val->intval = POWER_SUPPLY_HEALTH_GOOD;
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +static int da9150_charger_battery_present(struct da9150_charger *charger,
> +					  union power_supply_propval *val)
> +{
> +	u8 reg;
> +
> +	/* Check if battery present or removed */
> +	reg = da9150_reg_read(charger->da9150, DA9150_STATUS_J);
> +	if ((reg & DA9150_CHG_STAT_MASK) == DA9150_CHG_STAT_BAT)
> +		val->intval = 0;
> +	else
> +		val->intval = 1;
> +
> +	return 0;
> +}
> +
> +static int da9150_charger_battery_charge_type(struct da9150_charger *charger,
> +					      union power_supply_propval *val)
> +{
> +	u8 reg;
> +
> +	reg = da9150_reg_read(charger->da9150, DA9150_STATUS_J);
> +
> +	switch (reg & DA9150_CHG_STAT_MASK) {
> +	case DA9150_CHG_STAT_CC:
> +		val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
> +		break;
> +	case DA9150_CHG_STAT_ACT:
> +	case DA9150_CHG_STAT_PRE:
> +	case DA9150_CHG_STAT_CV:
> +		val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
> +		break;
> +	default:
> +		val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +static int da9150_charger_battery_voltage_min(struct da9150_charger *charger,
> +					      union power_supply_propval *val)
> +{
> +	u8 reg;
> +
> +	reg = da9150_reg_read(charger->da9150, DA9150_PPR_CHGCTRL_C);
> +
> +	/* Value starts at 2500 mV, 50 mV increments, presented in uV */
> +	val->intval = ((reg & DA9150_CHG_VFAULT_MASK) * 50000) + 2500000;
> +
> +	return 0;
> +}
> +
> +static int da9150_charger_battery_voltage_now(struct da9150_charger *charger,
> +					      union power_supply_propval *val)
> +{
> +	int v_val, ret;
> +
> +	/* Read processed value - mV units */
> +	ret = iio_read_channel_processed(charger->vbat_chan, &v_val);
> +	if (ret < 0)
> +		return ret;
> +
> +	val->intval = v_val * 1000;
> +
> +	return 0;
> +}
> +
> +static int da9150_charger_battery_current_max(struct da9150_charger *charger,
> +					      union power_supply_propval *val)
> +{
> +	int reg;
> +
> +	reg = da9150_reg_read(charger->da9150, DA9150_PPR_CHGCTRL_D);
> +
> +	/* 25mA increments */
> +	val->intval = reg * 25000;
> +
> +	return 0;
> +}
> +
> +static int da9150_charger_battery_voltage_max(struct da9150_charger *charger,
> +					      union power_supply_propval *val)
> +{
> +	u8 reg;
> +
> +	reg = da9150_reg_read(charger->da9150, DA9150_PPR_CHGCTRL_B);
> +
> +	/* Value starts at 3650 mV, 25 mV increments, presented in uV */
> +	val->intval = ((reg & DA9150_CHG_VBAT_MASK) * 25000) + 3650000;
> +	return 0;
> +}
> +
> +static enum power_supply_property da9150_charger_bat_props[] = {
> +	POWER_SUPPLY_PROP_STATUS,
> +	POWER_SUPPLY_PROP_ONLINE,
> +	POWER_SUPPLY_PROP_HEALTH,
> +	POWER_SUPPLY_PROP_PRESENT,
> +	POWER_SUPPLY_PROP_CHARGE_TYPE,
> +	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
> +	POWER_SUPPLY_PROP_VOLTAGE_NOW,
> +	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
> +	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
> +};
> +
> +static int da9150_charger_battery_get_prop(struct power_supply *psy,
> +					   enum power_supply_property psp,
> +					   union power_supply_propval *val)
> +{
> +	struct da9150_charger *charger = dev_get_drvdata(psy->dev->parent);
> +	int ret;
> +
> +	switch (psp) {
> +	case POWER_SUPPLY_PROP_STATUS:
> +		ret = da9150_charger_battery_status(charger, val);
> +		break;
> +	case POWER_SUPPLY_PROP_ONLINE:
> +		ret = da9150_charger_supply_online(charger, psy, val);
> +		break;
> +	case POWER_SUPPLY_PROP_HEALTH:
> +		ret = da9150_charger_battery_health(charger, val);
> +		break;
> +	case POWER_SUPPLY_PROP_PRESENT:
> +		ret = da9150_charger_battery_present(charger, val);
> +		break;
> +	case POWER_SUPPLY_PROP_CHARGE_TYPE:
> +		ret = da9150_charger_battery_charge_type(charger, val);
> +		break;
> +	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
> +		ret = da9150_charger_battery_voltage_min(charger, val);
> +		break;
> +	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
> +		ret = da9150_charger_battery_voltage_now(charger, val);
> +		break;
> +	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
> +		ret = da9150_charger_battery_current_max(charger, val);
> +		break;
> +	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
> +		ret = da9150_charger_battery_voltage_max(charger, val);
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +/* Other properties */
> +static ssize_t da9150_charger_attr_show(struct device *dev,
> +					struct device_attribute *attr,
> +					char *buf);
> +
> +#define DA9150_CHARGER_ATTR(_name, _reg, _shift, _mask)		\
> +	{							\
> +		.attr = __ATTR(_name, S_IRUGO,			\
> +			       da9150_charger_attr_show,	\
> +			       NULL),				\
> +		.reg = _reg,					\
> +		.shift = _shift,				\
> +		.mask = _mask,					\
> +	}
> +
> +static struct da9150_charger_attr_map da9150_charger_attrs[] = {
> +	DA9150_CHARGER_ATTR(chg_en, DA9150_PPR_CHGCTRL_A,
> +			    DA9150_CHG_EN_SHIFT, DA9150_CHG_EN_MASK),
> +	DA9150_CHARGER_ATTR(chg_ipre, DA9150_PPR_CHGCTRL_C,
> +			    DA9150_CHG_IPRE_SHIFT, DA9150_CHG_IPRE_MASK),
> +	DA9150_CHARGER_ATTR(chg_iend, DA9150_PPR_CHGCTRL_E,
> +			    DA9150_CHG_IEND_SHIFT, DA9150_CHG_IEND_MASK),
> +	DA9150_CHARGER_ATTR(chg_temp, DA9150_STATUS_J,
> +			    DA9150_CHG_TEMP_SHIFT, DA9150_CHG_TEMP_MASK),
> +	DA9150_CHARGER_ATTR(chg_vdrop, DA9150_PPR_CHGCTRL_B,
> +			    DA9150_CHG_VDROP_SHIFT, DA9150_CHG_VDROP_MASK),
> +	DA9150_CHARGER_ATTR(tbat_upper, DA9150_PPR_THYST_E,
> +			    DA9150_TBAT_T5_SHIFT, DA9150_TBAT_T5_MASK),
> +	DA9150_CHARGER_ATTR(tbat_lower, DA9150_PPR_THYST_A,
> +			    DA9150_TBAT_T1_SHIFT, DA9150_TBAT_T1_MASK),
> +	DA9150_CHARGER_ATTR(vbus_mode, DA9150_PPR_BKCTRL_A,
> +			    DA9150_VBUS_MODE_SHIFT, DA9150_VBUS_MODE_MASK),
> +	DA9150_CHARGER_ATTR(vbus_tred, DA9150_STATUS_H,
> +			    DA9150_VBUS_TRED_SHIFT, DA9150_VBUS_TRED_MASK),
> +	DA9150_CHARGER_ATTR(vbus_drop_stat, DA9150_STATUS_H,
> +			    DA9150_VBUS_DROP_STAT_SHIFT,
> +			    DA9150_VBUS_DROP_STAT_MASK),
> +};
> +
> +static ssize_t da9150_charger_attr_show(struct device *dev,
> +					struct device_attribute *attr,
> +					char *buf)
> +{
> +	struct da9150_charger *charger = dev_get_drvdata(dev);
> +	struct da9150_charger_attr_map *charger_attrs;
> +	int i;
> +
> +	/* Find attribute, and print out register field contents. */
> +	for (i = 0, charger_attrs = da9150_charger_attrs;
> +	     i < ARRAY_SIZE(da9150_charger_attrs);
> +	     ++i, ++charger_attrs) {
> +		if (attr == &charger_attrs->attr) {
> +			u8 val;
> +
> +			val = da9150_reg_read(charger->da9150,
> +					      charger_attrs->reg);
> +			val &= charger_attrs->mask;
> +			val = val >> charger_attrs->shift;
> +
> +			return sprintf(buf, "0x%x\n", val);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static irqreturn_t da9150_charger_chg_irq(int irq, void *data)
> +{
> +	struct da9150_charger *charger = data;
> +
> +	power_supply_changed(&charger->battery);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t da9150_charger_tjunc_irq(int irq, void *data)
> +{
> +	struct da9150_charger *charger = data;
> +
> +	/* Nothing we can really do except report this. */
> +	dev_crit(charger->dev, "TJunc over temperature!!!\n");
> +	power_supply_changed(&charger->usb);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t da9150_charger_vfault_irq(int irq, void *data)
> +{
> +	struct da9150_charger *charger = data;
> +
> +	/* Nothing we can really do except report this. */
> +	dev_crit(charger->dev, "VSYS under voltage!!!\n");
> +	power_supply_changed(&charger->usb);
> +	power_supply_changed(&charger->battery);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t da9150_charger_vbus_irq(int irq, void *data)
> +{
> +	struct da9150_charger *charger = data;
> +	u8 reg;
> +
> +	reg = da9150_reg_read(charger->da9150, DA9150_STATUS_H);
> +
> +	/* Charger plugged in or battery only */
> +	switch (reg & DA9150_VBUS_STAT_MASK) {
> +	case DA9150_VBUS_STAT_OFF:
> +	case DA9150_VBUS_STAT_WAIT:
> +		charger->supply_online = &charger->battery;
> +		break;
> +	case DA9150_VBUS_STAT_CHG:
> +		charger->supply_online = &charger->usb;
> +		break;
> +	default:
> +		dev_warn(charger->dev, "Unknown VBUS state - reg = 0x%x\n",
> +			 reg);
> +		charger->supply_online = NULL;
> +		break;
> +	}
> +
> +	power_supply_changed(&charger->usb);
> +	power_supply_changed(&charger->battery);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static void da9150_charger_otg_work(struct work_struct *data)
> +{
> +	struct da9150_charger *charger =
> +		container_of(data, struct da9150_charger, otg_work);
> +
> +	switch (charger->usb_event) {
> +	case USB_EVENT_ID:
> +		/* Enable OTG Boost */
> +		da9150_set_bits(charger->da9150, DA9150_PPR_BKCTRL_A,
> +				DA9150_VBUS_MODE_MASK, DA9150_VBUS_MODE_OTG);
> +		break;
> +	case USB_EVENT_NONE:
> +		/* Revert to charge mode */
> +		power_supply_changed(&charger->usb);
> +		power_supply_changed(&charger->battery);
> +		da9150_set_bits(charger->da9150, DA9150_PPR_BKCTRL_A,
> +				DA9150_VBUS_MODE_MASK, DA9150_VBUS_MODE_CHG);
> +		break;
> +	}
> +}
> +
> +static int da9150_charger_otg_ncb(struct notifier_block *nb, unsigned long val,
> +				  void *priv)
> +{
> +	struct da9150_charger *charger =
> +		container_of(nb, struct da9150_charger, otg_nb);
> +
> +	dev_dbg(charger->dev, "DA9150 OTG notify %lu\n", val);
> +
> +	charger->usb_event = val;
> +	schedule_work(&charger->otg_work);
> +
> +	return NOTIFY_OK;
> +}
> +
> +static int da9150_charger_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct da9150 *da9150 = dev_get_drvdata(dev->parent);
> +	struct da9150_charger *charger;
> +	struct power_supply *usb, *battery;
> +	u8 reg;
> +	int i, irq, ret;
> +
> +	charger = devm_kzalloc(dev, sizeof(struct da9150_charger), GFP_KERNEL);
> +	if (charger == NULL)
> +		return -ENOMEM;
> +
> +	platform_set_drvdata(pdev, charger);
> +	charger->da9150 = da9150;
> +	charger->dev = dev;
> +
> +	/* Acquire ADC channels */
> +	charger->ibus_chan = iio_channel_get(dev, "CHAN_IBUS");
> +	if (IS_ERR(charger->ibus_chan)) {
> +		ret = PTR_ERR(charger->ibus_chan);
> +		goto ibus_chan_fail;
> +	}
> +
> +	charger->vbus_chan = iio_channel_get(dev, "CHAN_VBUS");
> +	if (IS_ERR(charger->vbus_chan)) {
> +		ret = PTR_ERR(charger->vbus_chan);
> +		goto vbus_chan_fail;
> +	}
> +
> +	charger->tjunc_chan = iio_channel_get(dev, "CHAN_TJUNC");
> +	if (IS_ERR(charger->tjunc_chan)) {
> +		ret = PTR_ERR(charger->tjunc_chan);
> +		goto tjunc_chan_fail;
> +	}
> +
> +	charger->vbat_chan = iio_channel_get(dev, "CHAN_VBAT");
> +	if (IS_ERR(charger->vbat_chan)) {
> +		ret = PTR_ERR(charger->vbat_chan);
> +		goto vbat_chan_fail;
> +	}
> +
> +	/* Register power supplies */
> +	usb = &charger->usb;
> +	battery = &charger->battery;
> +
> +	usb->name = "da9150-usb",
> +	usb->type = POWER_SUPPLY_TYPE_USB;
> +	usb->properties = da9150_charger_props;
> +	usb->num_properties = ARRAY_SIZE(da9150_charger_props);
> +	usb->get_property = da9150_charger_get_prop;
> +	ret = power_supply_register(dev, usb);
> +	if (ret)
> +		goto usb_fail;
> +
> +	battery->name = "da9150-battery";
> +	battery->type = POWER_SUPPLY_TYPE_BATTERY;
> +	battery->properties = da9150_charger_bat_props;
> +	battery->num_properties = ARRAY_SIZE(da9150_charger_bat_props);
> +	battery->get_property = da9150_charger_battery_get_prop;
> +	ret = power_supply_register(dev, battery);
> +	if (ret)
> +		goto battery_fail;
> +
> +	/* Create additional sysfs attributes */
> +	for (i = 0; i < ARRAY_SIZE(da9150_charger_attrs); ++i) {
> +		ret = device_create_file(dev, &da9150_charger_attrs[i].attr);
> +		if (ret)
> +			goto sysfs_fail;
> +	}
> +
> +	/* Get initial online supply */
> +	reg = da9150_reg_read(da9150, DA9150_STATUS_H);
> +
> +	switch (reg & DA9150_VBUS_STAT_MASK) {
> +	case DA9150_VBUS_STAT_OFF:
> +	case DA9150_VBUS_STAT_WAIT:
> +		charger->supply_online = &charger->battery;
> +		break;
> +	case DA9150_VBUS_STAT_CHG:
> +		charger->supply_online = &charger->usb;
> +		break;
> +	default:
> +		dev_warn(dev, "Unknown VBUS state - reg = 0x%x\n", reg);
> +		charger->supply_online = NULL;
> +		break;
> +	}
> +
> +	/* Setup OTG reporting & configuration */
> +	charger->usb_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
> +	if (!IS_ERR_OR_NULL(charger->usb_phy)) {
> +		INIT_WORK(&charger->otg_work, da9150_charger_otg_work);
> +		charger->otg_nb.notifier_call = da9150_charger_otg_ncb;
> +		usb_register_notifier(charger->usb_phy, &charger->otg_nb);
> +	}
> +
> +	/* Register IRQs */
> +	irq = platform_get_irq_byname(pdev, "CHG_STATUS");
> +	ret = devm_request_threaded_irq(dev, irq, NULL, da9150_charger_chg_irq,
> +					IRQF_ONESHOT, "CHG_STATUS", charger);
> +	if (ret)
> +		goto irq_fail;
> +
> +	irq = platform_get_irq_byname(pdev, "CHG_TJUNC");
> +	ret = devm_request_threaded_irq(dev, irq, NULL,
> +					da9150_charger_tjunc_irq,
> +					IRQF_ONESHOT, "CHG_TJUNC", charger);
> +	if (ret)
> +		goto irq_fail;
> +
> +	irq = platform_get_irq_byname(pdev, "CHG_VFAULT");
> +	ret = devm_request_threaded_irq(dev, irq, NULL,
> +					da9150_charger_vfault_irq,
> +					IRQF_ONESHOT, "CHG_VFAULT", charger);
> +	if (ret)
> +		goto irq_fail;
> +
> +	irq = platform_get_irq_byname(pdev, "CHG_VBUS");
> +	ret = devm_request_threaded_irq(dev, irq, NULL, da9150_charger_vbus_irq,
> +					IRQF_ONESHOT, "CHG_VBUS", charger);
> +	if (ret)
> +		goto irq_fail;
> +
> +	return 0;
> +
> +irq_fail:
> +	dev_err(&pdev->dev, "Failed to request IRQ %d: %d\n", irq, ret);
> +	if (!IS_ERR_OR_NULL(charger->usb_phy))
> +		usb_unregister_notifier(charger->usb_phy, &charger->otg_nb);
> +sysfs_fail:
> +	while (--i >= 0)
> +		device_remove_file(&pdev->dev, &da9150_charger_attrs[i].attr);
> +	power_supply_unregister(battery);
> +
> +battery_fail:
> +	power_supply_unregister(usb);
> +
> +usb_fail:
> +	iio_channel_release(charger->vbat_chan);
> +
> +vbat_chan_fail:
> +	iio_channel_release(charger->tjunc_chan);
> +
> +tjunc_chan_fail:
> +	iio_channel_release(charger->vbus_chan);
> +
> +vbus_chan_fail:
> +	iio_channel_release(charger->ibus_chan);
> +
> +ibus_chan_fail:
> +	return ret;
> +}
> +
> +static int da9150_charger_remove(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct da9150_charger *charger = platform_get_drvdata(pdev);
> +	int i, irq;
> +
> +	/* Make sure IRQs are released before unregistering power supplies */
> +	irq = platform_get_irq_byname(pdev, "CHG_VBUS");
> +	devm_free_irq(dev, irq, charger);
> +
> +	irq = platform_get_irq_byname(pdev, "CHG_VFAULT");
> +	devm_free_irq(dev, irq, charger);
> +
> +	irq = platform_get_irq_byname(pdev, "CHG_TJUNC");
> +	devm_free_irq(dev, irq, charger);
> +
> +	irq = platform_get_irq_byname(pdev, "CHG_STATUS");
> +	devm_free_irq(dev, irq, charger);
> +
> +	if (!IS_ERR_OR_NULL(charger->usb_phy))
> +		usb_unregister_notifier(charger->usb_phy, &charger->otg_nb);
> +
> +	power_supply_unregister(&charger->battery);
> +	power_supply_unregister(&charger->usb);
> +
> +	for (i = 0; i < ARRAY_SIZE(da9150_charger_attrs); ++i)
> +		device_remove_file(&pdev->dev, &da9150_charger_attrs[i].attr);
> +
> +	/* Release ADC channels */
> +	iio_channel_release(charger->ibus_chan);
> +	iio_channel_release(charger->vbus_chan);
> +	iio_channel_release(charger->tjunc_chan);
> +	iio_channel_release(charger->vbat_chan);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver da9150_charger_driver = {
> +	.driver = {
> +		.name = "da9150-charger",
> +	},
> +	.probe = da9150_charger_probe,
> +	.remove = da9150_charger_remove,
> +};
> +
> +module_platform_driver(da9150_charger_driver);
> +
> +MODULE_DESCRIPTION("Charger Driver for DA9150");
> +MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource-WBD+wuPFNBhBDgjK7y7TUQ@public.gmane.org");
> +MODULE_LICENSE("GPL");
> --
> 1.9.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

^ permalink raw reply

* Re: [PATCH 2/2] perf: Userspace software event and ioctl
From: Frederic Weisbecker @ 2014-09-27 17:14 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Pawel Moll, Ingo Molnar, Arnaldo Carvalho de Melo,
	Richard Cochran, Steven Rostedt, Peter Zijlstra, Paul Mackerras,
	John Stultz, linux-kernel@vger.kernel.org,
	linux-api@vger.kernel.org
In-Reply-To: <20140925183342.GB6854@gmail.com>

2014-09-25 20:33 GMT+02:00 Ingo Molnar <mingo@kernel.org>:
>
> * Pawel Moll <pawel.moll@arm.com> wrote:
>
>> On Wed, 2014-09-24 at 08:49 +0100, Ingo Molnar wrote:
>> > * Pawel Moll <pawel.moll@arm.com> wrote:
>> >
>> > > On Thu, 2014-09-18 at 15:34 +0100, Pawel Moll wrote:
>> > > > This patch adds a PERF_COUNT_SW_USERSPACE_EVENT type,
>> > > > which can be generated by user with PERF_EVENT_IOC_ENTRY
>> > > > ioctl command, which injects an event of said type into
>> > > > the perf buffer.
>> > >
>> > > It occurred to me last night that currently perf doesn't handle "write"
>> > > syscall at all, while this seems like the most natural way of
>> > > "injecting" userspace events into perf buffer.
>> > >
>> > > An ioctl would still be needed to set a type of the following events,
>> > > something like:
>> > >
>> > >   ioctl(SET_TYPE, 0x42);
>> > >   write(perf_fd, binaryblob, size);
>> > >   ioctl(SET_TYPE, 0);
>> > >   dprintf(perf_fd, "String");
>> > >
>> > > which is fine for use cases when the type doesn't change often,
>> > > but would double the amount of syscalls when every single event
>> > > is of a different type. Perhaps there still should be a
>> > > "generating ioctl" taking both type and data/size in one go?
>> >
>> > Absolutely, there should be a single syscall.
>>
>> Yeah, it's my gut feeling as well. I just wonder if we still want to
>> keep write() handler for operations on perf fds? This seems natural -
>> takes data buffer and its size. The only issue is the type.
>>
>> > I'd even argue it should be a new prctl(): that way we could both
>> > generate user events for specific perf fds, but also into any
>> > currently active context (that allows just generation/injection
>> > of user events). In the latter case we might have no fd to work
>> > off from.
>>
>> When Arnaldo suggested that the "user events" could be used by perf
>> trace, it was exactly my first thought. I just didn't have answer how to
>> present it to the user (an extra syscall didn't seem like a good idea),
>> but prctl seems interesting, something like this?
>>
>>       prctl(PR_TRACE_UEVENT, type, size, data, 0);
>
> Exactly!
>
>> How would we select tasks that can write to a given buffer? Maybe an
>> ioctl() on a perf fd? Something like this?
>>
>>       ioctl(perf_fd, PERF_EVENT_IOC_ENABLE_UEVENT, pid);
>>       ioctl(perf_fd, PERF_EVENT_IOC_DISABLE_UEVENT, pid);
>
> No, I think there's a simpler way: this should be a regular
> perf_attr flag, which defaults to '0' (tasks cannot do this), but
> which can be set to 1 if the profiler explicitly allows such
> event injection.

Maybe we just don't even need any permission at all. Which harm can
that do if this only ever generate events to those interested in the
relevant perf context? It could be a simple tracepoint BTW.

Oh and I really like the fact we don't use a syscall that requires an
fd. The tracee really shouldn't be aware of the tracer.

^ permalink raw reply

* Re: [RESENT PATCH v7 1/3] drm: rockchip: Add basic drm driver
From: Daniel Kurtz @ 2014-09-29  4:01 UTC (permalink / raw)
  To: Mark Yao
  Cc: Heiko Stübner, Boris BREZILLON, David Airlie, Rob Clark,
	Daniel Vetter, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Randy Dunlap, Grant Likely,
	Greg Kroah-Hartman, John Stultz, Rom Lemarchand,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, dri-devel,
	linux-api-u79uwXL29TY76Z2rM5mHXA,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Douglas Anderson
In-Reply-To: <1411728904-25182-1-git-send-email-mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>

Hi Mark,

More review comments inline... (sorry for the delay)


On Fri, Sep 26, 2014 at 6:55 PM, Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org> wrote:
> From: Mark yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>
> This patch adds the basic structure of a DRM Driver for Rockchip Socs.
>
> Signed-off-by: Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> Signed-off-by: Daniel Kurtz <djkurtz-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
> Acked-by: Daniel Vetter <daniel-/w4YWyX8dFk@public.gmane.org>
> Reviewed-by: Rob Clark <robdclark-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> ---
> Changes in v2:
> - use the component framework to defer main drm driver probe
>   until all VOP devices have been probed.
> - use dma-mapping API with ARM_DMA_USE_IOMMU, create dma mapping by
>   master device and each vop device can shared the drm dma mapping.
> - use drm_crtc_init_with_planes and drm_universal_plane_init.
> - remove unnecessary middle layers.
> - add cursor set, move funcs to rockchip drm crtc.
> - use vop reset at first init
> - reference framebuffer when used and unreference when swap out vop
>
> Changes in v3:
> - change "crtc->fb" to "crtc->primary-fb"
> Adviced by Daniel Vetter
> - init cursor plane with universal api, remove unnecessary cursor set,move
>
> Changes in v4:
> Adviced by David Herrmann
> - remove drm_platform_*() usage, use register drm device directly.
> Adviced by Rob Clark
> - remove special mmap ioctl, do userspace mmap with normal mmap() or mmap offset
>
> Changes in v5:
> Adviced by Arnd Bergmann
> - doing DMA start with a 32-bit masks with dma_mask and dma_coherent_mark
> - fix some incorrect dependencies.
> Adviced by Boris BREZILLON
> - fix some mistake and bugs.
> Adviced by Daniel Vetter
> - drop all special ioctl and use generic kms ioctl instead.
> Adviced by Rob Clark
> - use unlocked api for drm_fb_helper_restore_fbdev_mode.
> - remove unused rockchip_gem_prime_import_sg_table.
>
> Changes in v6:
> - set gem buffer pitch 64 bytes align, needed by mali gpu.
> Adviced by Daniel Kurtz
> - fix some mistake, bugs, remove unused define, more better code style etc.
> - use clk_prepare()/unprepare() at probe()/remove() and clk_enable()/disable()
>   at runtime instead of clk_prepare_enable().
> - provide a help function from vop for encoder to do mode config, instead of
>   using drm_diaplay_mode private method.
> - change vop mode_set timing to make it more safely.
>
> Changes in v7:
> - fix memory leakage problem
>
>  drivers/gpu/drm/Kconfig                       |    2 +
>  drivers/gpu/drm/Makefile                      |    1 +
>  drivers/gpu/drm/rockchip/Kconfig              |   17 +
>  drivers/gpu/drm/rockchip/Makefile             |    8 +
>  drivers/gpu/drm/rockchip/rockchip_drm_drv.c   |  516 +++++++++
>  drivers/gpu/drm/rockchip/rockchip_drm_drv.h   |   61 ++
>  drivers/gpu/drm/rockchip/rockchip_drm_fb.c    |  201 ++++
>  drivers/gpu/drm/rockchip/rockchip_drm_fb.h    |   28 +
>  drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c |  230 ++++
>  drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h |   20 +
>  drivers/gpu/drm/rockchip/rockchip_drm_gem.c   |  345 ++++++
>  drivers/gpu/drm/rockchip/rockchip_drm_gem.h   |   55 +
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.c   | 1422 +++++++++++++++++++++++++
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.h   |  196 ++++
>  14 files changed, 3102 insertions(+)
>  create mode 100644 drivers/gpu/drm/rockchip/Kconfig
>  create mode 100644 drivers/gpu/drm/rockchip/Makefile
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_drv.c
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fb.h
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_gem.c
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_gem.h
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index b066bb3..7c4c3c6 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -171,6 +171,8 @@ config DRM_SAVAGE
>
>  source "drivers/gpu/drm/exynos/Kconfig"
>
> +source "drivers/gpu/drm/rockchip/Kconfig"
> +
>  source "drivers/gpu/drm/vmwgfx/Kconfig"
>
>  source "drivers/gpu/drm/gma500/Kconfig"
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 4a55d59..d03387a 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -52,6 +52,7 @@ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
>  obj-$(CONFIG_DRM_VIA)  +=via/
>  obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
>  obj-$(CONFIG_DRM_EXYNOS) +=exynos/
> +obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/
>  obj-$(CONFIG_DRM_GMA500) += gma500/
>  obj-$(CONFIG_DRM_UDL) += udl/
>  obj-$(CONFIG_DRM_AST) += ast/
> diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
> new file mode 100644
> index 0000000..87255f7
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/Kconfig
> @@ -0,0 +1,17 @@
> +config DRM_ROCKCHIP
> +       tristate "DRM Support for Rockchip"
> +       depends on DRM && ROCKCHIP_IOMMU && ARM_DMA_USE_IOMMU && IOMMU_API
> +       select DRM_KMS_HELPER
> +       select DRM_KMS_FB_HELPER
> +       select DRM_PANEL
> +       select FB_CFB_FILLRECT
> +       select FB_CFB_COPYAREA
> +       select FB_CFB_IMAGEBLIT
> +       select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
> +       select VIDEOMODE_HELPERS
> +       help
> +         Choose this option if you have a Rockchip soc chipset.
> +         This driver provides kernel mode setting and buffer
> +         management to userspace. This driver does not provides
> +         2D or 3D acceleration; acceleration is performed by other
> +         IP found on the SoC.
> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> new file mode 100644
> index 0000000..b3a5193
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/Makefile
> @@ -0,0 +1,8 @@
> +#
> +# Makefile for the drm device driver.  This driver provides support for the
> +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
> +
> +rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o rockchip_drm_fbdev.o \
> +               rockchip_drm_gem.o rockchip_drm_vop.o
> +
> +obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> new file mode 100644
> index 0000000..a571acc
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> @@ -0,0 +1,516 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> + *
> + * based on exynos_drm_drv.c
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <asm/dma-iommu.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_fb_helper.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/of_graph.h>
> +#include <linux/component.h>
> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_fb.h"
> +#include "rockchip_drm_fbdev.h"
> +#include "rockchip_drm_gem.h"
> +
> +#define DRIVER_NAME    "rockchip"
> +#define DRIVER_DESC    "RockChip Soc DRM"
> +#define DRIVER_DATE    "20140818"
> +#define DRIVER_MAJOR   1
> +#define DRIVER_MINOR   0
> +
> +/*
> + * Attach a (component) device to the shared drm dma mapping from master drm
> + * device.  This is used by the VOPs to map GEM buffers to a common DMA
> + * mapping.
> + */
> +int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
> +                                  struct device *dev)
> +{
> +       struct dma_iommu_mapping *mapping = drm_dev->dev->archdata.mapping;
> +       int ret;
> +
> +       ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
> +       if (ret)
> +               return ret;
> +
> +       dma_set_max_seg_size(dev, 0xffffffffu);

Perhaps:
 dma_set_max_seg_size(dev, DMA_BIT_MASK(32));

> +
> +       return arm_iommu_attach_device(dev, mapping);
> +}
> +
> +void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
> +                                   struct device *dev)
> +{
> +       arm_iommu_detach_device(drm_dev->dev);
> +}
> +
> +static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags)
> +{
> +       struct rockchip_drm_private *private;
> +       struct dma_iommu_mapping *mapping;
> +       struct device *dev = drm_dev->dev;
> +       int ret;
> +
> +       private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL);
> +       if (!private)
> +               return -ENOMEM;
> +
> +       drm_dev->dev_private = private;
> +
> +       drm_mode_config_init(drm_dev);
> +
> +       rockchip_drm_mode_config_init(drm_dev);
> +
> +       dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms),
> +                                     GFP_KERNEL);
> +       if (!dev->dma_parms) {
> +               ret = -ENOMEM;
> +               goto err_config_cleanup;
> +       }
> +
> +       /* TODO(djkurtz): fetch the mapping start/size from somewhere */
> +       mapping = arm_iommu_create_mapping(&platform_bus_type, 0x10000000,
> +                                          SZ_1G);


Can we make this bigger, and aligned to the requested size?
I find it makes it easier to debug that way.

How about:
    mapping = arm_iommu_create_mapping(&platform_bus_type, 0x80000000, SZ_2G);

In fact, is there any reason why we do not just make this a 4GB mapping starting
at 0x00000000?  Is that even possible?


> +       if (IS_ERR(mapping)) {
> +               ret = PTR_ERR(mapping);
> +               goto err_config_cleanup;
> +       }
> +
> +       ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
> +       if (ret)
> +               goto err_release_mapping;
> +
> +       dma_set_max_seg_size(dev, 0xffffffffu);

see above.

> +
> +       ret = arm_iommu_attach_device(dev, mapping);
> +       if (ret)
> +               goto err_release_mapping;
> +
> +       /* Try to bind all sub drivers. */
> +       ret = component_bind_all(dev, drm_dev);
> +       if (ret)
> +               goto err_detach_device;
> +
> +       /* init kms poll for handling hpd */
> +       drm_kms_helper_poll_init(drm_dev);
> +
> +       /*
> +        * enable drm irq mode.
> +        * - with irq_enabled = true, we can use the vblank feature.
> +        */
> +       drm_dev->irq_enabled = true;
> +
> +       /*
> +        * with vblank_disable_allowed = true, vblank interrupt will be disabled
> +        * by drm timer once a current process gives up ownership of
> +        * vblank event.(after drm_vblank_put function is called)
> +        */
> +       drm_dev->vblank_disable_allowed = true;
> +
> +       ret = drm_vblank_init(drm_dev, ROCKCHIP_MAX_CRTC);
> +       if (ret)
> +               goto err_kms_helper_poll_fini;
> +
> +       rockchip_drm_fbdev_init(drm_dev);
> +
> +       /* force connectors detection */
> +       drm_helper_hpd_irq_event(drm_dev);
> +
> +       return 0;
> +
> +err_kms_helper_poll_fini:
> +       drm_kms_helper_poll_fini(drm_dev);
> +       component_unbind_all(dev, drm_dev);
> +err_detach_device:
> +       arm_iommu_detach_device(dev);
> +err_release_mapping:
> +       arm_iommu_release_mapping(dev->archdata.mapping);
> +err_config_cleanup:
> +       drm_mode_config_cleanup(drm_dev);
> +       drm_dev->dev_private = NULL;
> +       return ret;
> +}
> +
> +static int rockchip_drm_unload(struct drm_device *drm_dev)
> +{
> +       struct device *dev = drm_dev->dev;
> +
> +       drm_kms_helper_poll_fini(drm_dev);
> +       component_unbind_all(dev, drm_dev);
> +       arm_iommu_detach_device(dev);
> +       arm_iommu_release_mapping(dev->archdata.mapping);
> +       drm_mode_config_cleanup(drm_dev);
> +       drm_dev->dev_private = NULL;
> +
> +       return 0;
> +}
> +
> +void rockchip_drm_lastclose(struct drm_device *dev)
> +{
> +       struct rockchip_drm_private *priv = dev->dev_private;
> +
> +       if (priv->fb_helper)
> +               drm_fb_helper_restore_fbdev_mode_unlocked(priv->fb_helper);
> +}
> +
> +static const struct file_operations rockchip_drm_driver_fops = {
> +       .owner = THIS_MODULE,
> +       .open = drm_open,
> +       .mmap = rockchip_drm_gem_mmap,
> +       .poll = drm_poll,
> +       .read = drm_read,
> +       .unlocked_ioctl = drm_ioctl,
> +#ifdef CONFIG_COMPAT
> +       .compat_ioctl = drm_compat_ioctl,
> +#endif
> +       .release = drm_release,
> +};
> +
> +const struct vm_operations_struct rockchip_drm_vm_ops = {
> +       .open = drm_gem_vm_open,
> +       .close = drm_gem_vm_close,
> +};
> +
> +static struct drm_driver rockchip_drm_driver = {
> +       .driver_features        = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
> +       .load                   = rockchip_drm_load,
> +       .unload                 = rockchip_drm_unload,
> +       .lastclose              = rockchip_drm_lastclose,
> +       .get_vblank_counter     = drm_vblank_count,
> +       .enable_vblank          = rockchip_drm_crtc_enable_vblank,
> +       .disable_vblank         = rockchip_drm_crtc_disable_vblank,
> +       .gem_vm_ops             = &rockchip_drm_vm_ops,
> +       .gem_free_object        = rockchip_gem_free_object,
> +       .dumb_create            = rockchip_gem_dumb_create,
> +       .dumb_map_offset        = rockchip_gem_dumb_map_offset,
> +       .dumb_destroy           = drm_gem_dumb_destroy,
> +       .prime_handle_to_fd     = drm_gem_prime_handle_to_fd,
> +       .prime_fd_to_handle     = drm_gem_prime_fd_to_handle,
> +       .gem_prime_import       = drm_gem_prime_import,
> +       .gem_prime_export       = drm_gem_prime_export,
> +       .gem_prime_get_sg_table = rockchip_gem_prime_get_sg_table,
> +       .gem_prime_vmap         = rockchip_gem_prime_vmap,
> +       .gem_prime_vunmap       = rockchip_gem_prime_vunmap,
> +       .gem_prime_mmap         = rockchip_gem_prime_mmap,
> +       .fops                   = &rockchip_drm_driver_fops,
> +       .name   = DRIVER_NAME,
> +       .desc   = DRIVER_DESC,
> +       .date   = DRIVER_DATE,
> +       .major  = DRIVER_MAJOR,
> +       .minor  = DRIVER_MINOR,
> +};
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int rockchip_drm_suspend(struct drm_device *dev, pm_message_t state)
> +{
> +       struct drm_connector *connector;
> +
> +       drm_modeset_lock_all(dev);
> +       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> +               int old_dpms = connector->dpms;
> +
> +               if (connector->funcs->dpms)
> +                       connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF);
> +
> +               /* Set the old mode back to the connector for resume */
> +               connector->dpms = old_dpms;
> +       }
> +       drm_modeset_unlock_all(dev);
> +
> +       return 0;
> +}
> +
> +static int rockchip_drm_resume(struct drm_device *dev)
> +{
> +       struct drm_connector *connector;
> +
> +       drm_modeset_lock_all(dev);
> +       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
> +               if (connector->funcs->dpms)
> +                       connector->funcs->dpms(connector, connector->dpms);
> +       }
> +       drm_modeset_unlock_all(dev);
> +
> +       drm_helper_resume_force_mode(dev);
> +
> +       return 0;
> +}
> +
> +static int rockchip_drm_sys_suspend(struct device *dev)
> +{
> +       struct drm_device *drm_dev = dev_get_drvdata(dev);
> +       pm_message_t message;
> +
> +       if (pm_runtime_suspended(dev))
> +               return 0;
> +
> +       message.event = PM_EVENT_SUSPEND;
> +
> +       return rockchip_drm_suspend(drm_dev, message);
> +}
> +
> +static int rockchip_drm_sys_resume(struct device *dev)
> +{
> +       struct drm_device *drm_dev = dev_get_drvdata(dev);
> +
> +       if (!pm_runtime_suspended(dev))
> +               return 0;
> +
> +       return rockchip_drm_resume(drm_dev);
> +}
> +#endif
> +
> +static const struct dev_pm_ops rockchip_drm_pm_ops = {
> +       SET_SYSTEM_SLEEP_PM_OPS(rockchip_drm_sys_suspend,
> +                               rockchip_drm_sys_resume)
> +};
> +
> +int rockchip_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
> +                         struct device_node *np)
> +{
> +       struct rockchip_drm_private *priv = drm->dev_private;
> +       struct device_node *port;
> +       int pipe;
> +
> +       if (priv->num_pipe >= ROCKCHIP_MAX_CRTC)
> +               return -EINVAL;
> +
> +       port = of_get_child_by_name(np, "port");
> +       if (!port) {
> +               dev_err(drm->dev, "no port node found in %s\n",
> +                       np->full_name);
> +               return -ENXIO;
> +       }
> +       pipe = priv->num_pipe++;
> +       crtc->port = port;
> +
> +       priv->crtc[pipe] = crtc;
> +
> +       return pipe;
> +}
> +
> +void rockchip_drm_remove_crtc(struct drm_device *drm, int pipe)
> +{
> +       struct rockchip_drm_private *priv = drm->dev_private;
> +
> +       priv->num_pipe--;
> +       of_node_put(priv->crtc[pipe]->port);
> +       priv->crtc[pipe] = NULL;
> +}
> +
> +struct drm_crtc *rockchip_drm_find_crtc(struct drm_device *drm, int pipe)
> +{
> +       struct rockchip_drm_private *priv = drm->dev_private;
> +
> +       if (pipe < ROCKCHIP_MAX_CRTC && priv->crtc[pipe])
> +               return priv->crtc[pipe];
> +
> +       return NULL;
> +}
> +
> +/*
> + * @node: device tree node containing encoder input ports
> + * @encoder: drm_encoder
> + */
> +int rockchip_drm_encoder_get_mux_id(struct device_node *node,
> +                                   struct drm_encoder *encoder)
> +{
> +       struct device_node *ep = NULL;
> +       struct drm_crtc *crtc = encoder->crtc;
> +       struct of_endpoint endpoint;
> +       struct device_node *port;
> +       int ret;
> +
> +       if (!node || !crtc)
> +               return -EINVAL;
> +
> +       do {
> +               ep = of_graph_get_next_endpoint(node, ep);
> +               if (!ep)
> +                       break;
> +
> +               port = of_graph_get_remote_port(ep);
> +               of_node_put(port);
> +               if (port == crtc->port) {
> +                       ret = of_graph_parse_endpoint(ep, &endpoint);
> +                       return ret ?: endpoint.id;
> +               }
> +       } while (ep);
> +
> +       return -EINVAL;
> +}
> +
> +static int compare_of(struct device *dev, void *data)
> +{
> +       struct device_node *np = data;
> +
> +       return dev->of_node == np;
> +}
> +
> +static void rockchip_add_endpoints(struct device *dev,
> +                                  struct component_match **match,
> +                                  struct device_node *port)
> +{
> +       struct device_node *ep, *remote;
> +
> +       for_each_child_of_node(port, ep) {
> +               remote = of_graph_get_remote_port_parent(ep);
> +               if (!remote || !of_device_is_available(remote)) {
> +                       of_node_put(remote);
> +                       continue;
> +               } else if (!of_device_is_available(remote->parent)) {
> +                       dev_warn(dev, "parent device of %s is not available\n",
> +                                remote->full_name);
> +                       of_node_put(remote);
> +                       continue;
> +               }
> +
> +               component_match_add(dev, match, compare_of, remote);
> +               of_node_put(remote);
> +       }
> +}
> +
> +static int rockchip_drm_bind(struct device *dev)
> +{
> +       struct drm_device *drm;
> +       int ret;
> +
> +       drm = drm_dev_alloc(&rockchip_drm_driver, dev);
> +       if (!drm)
> +               return -ENOMEM;
> +
> +       ret = drm_dev_set_unique(drm, "%s", dev_name(dev));
> +       if (ret)
> +               goto err_free;
> +
> +       ret = drm_dev_register(drm, 0);
> +       if (ret)
> +               goto err_free;
> +
> +       dev_set_drvdata(dev, drm);
> +
> +       return 0;
> +
> +err_free:
> +       drm_dev_unref(drm);
> +       return ret;
> +}
> +
> +static void rockchip_drm_unbind(struct device *dev)
> +{
> +       struct drm_device *drm = dev_get_drvdata(dev);
> +
> +       drm_dev_unregister(drm);
> +       drm_dev_unref(drm);
> +}
> +
> +static const struct component_master_ops rockchip_drm_ops = {
> +       .bind = rockchip_drm_bind,
> +       .unbind = rockchip_drm_unbind,
> +};
> +
> +static int rockchip_drm_platform_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct component_match *match = NULL;
> +       struct device_node *np = dev->of_node;
> +       struct device_node *port;
> +       int i;
> +       int ret;
> +
> +       if (!np)
> +               return -ENODEV;
> +       /*
> +        * Bind the crtc ports first, so that
> +        * drm_of_find_possible_crtcs called from encoder .bind callbacks
> +        * works as expected.
> +        */
> +       for (i = 0;; i++) {
> +               port = of_parse_phandle(np, "ports", i);
> +               if (!port)
> +                       break;
> +
> +               if (!of_device_is_available(port->parent)) {
> +                       of_node_put(port);
> +                       continue;
> +               }
> +
> +               component_match_add(dev, &match, compare_of, port->parent);
> +               of_node_put(port);
> +       }
> +
> +       if (i == 0) {
> +               dev_err(dev, "missing 'ports' property\n");
> +               return -ENODEV;
> +       }
> +       /*
> +        * For each bound crtc, bind the encoders attached to its
> +        * remote endpoint.
> +        */
> +       for (i = 0;; i++) {
> +               port = of_parse_phandle(np, "ports", i);
> +               if (!port)
> +                       break;
> +
> +               if (!of_device_is_available(port->parent)) {
> +                       of_node_put(port);
> +                       continue;
> +               }
> +
> +               rockchip_add_endpoints(dev, &match, port);
> +               of_node_put(port);
> +       }
> +
> +       ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
> +       if (ret)
> +               return ret;

This is in drm_load() now.

> +
> +       return component_master_add_with_match(dev, &rockchip_drm_ops, match);
> +}
> +
> +static int rockchip_drm_platform_remove(struct platform_device *pdev)
> +{
> +       component_master_del(&pdev->dev, &rockchip_drm_ops);
> +
> +       return 0;
> +}
> +
> +static const struct of_device_id rockchip_drm_dt_ids[] = {
> +       { .compatible = "rockchip,display-subsystem", },
> +       { /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, rockchip_drm_dt_ids);
> +
> +static struct platform_driver rockchip_drm_platform_driver = {
> +       .probe = rockchip_drm_platform_probe,
> +       .remove = rockchip_drm_platform_remove,
> +       .driver = {
> +               .owner = THIS_MODULE,
> +               .name = "rockchip-drm",
> +               .of_match_table = rockchip_drm_dt_ids,
> +               .pm = &rockchip_drm_pm_ops,
> +       },
> +};
> +
> +module_platform_driver(rockchip_drm_platform_driver);
> +
> +MODULE_AUTHOR("Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>");
> +MODULE_DESCRIPTION("ROCKCHIP DRM Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> new file mode 100644
> index 0000000..98094ecf
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> @@ -0,0 +1,61 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> + *
> + * based on exynos_drm_drv.h
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef _ROCKCHIP_DRM_DRV_H
> +#define _ROCKCHIP_DRM_DRV_H
> +
> +#include <linux/module.h>
> +#include <linux/component.h>
> +
> +#define ROCKCHIP_MAX_FB_BUFFER 3
> +#define ROCKCHIP_MAX_CONNECTOR 2
> +#define ROCKCHIP_MAX_CRTC      2
> +
> +struct drm_device;
> +struct drm_connector;
> +
> +/*
> + * Rockchip drm private structure.
> + *
> + * @crtc: array of enabled CRTCs, used to map from "pipe" to drm_crtc.
> + * @num_pipe: number of pipes for this device.
> + */
> +struct rockchip_drm_private {
> +       struct drm_fb_helper *fb_helper;
> +       /*
> +        * created crtc object would be contained at this array and
> +        * this array is used to be aware of which crtc did it request vblank.
> +        */
> +       struct drm_crtc *crtc[ROCKCHIP_MAX_CRTC];
> +
> +       unsigned int num_pipe;
> +};
> +
> +int rockchip_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
> +                         struct device_node *port);
> +void rockchip_drm_remove_crtc(struct drm_device *drm, int pipe);
> +struct drm_crtc *rockchip_drm_find_crtc(struct drm_device *drm, int pipe);
> +int rockchip_drm_encoder_get_mux_id(struct device_node *node,
> +                                   struct drm_encoder *encoder);
> +int rockchip_drm_crtc_mode_config(struct drm_crtc *crtc, int connector_type);
> +int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe);
> +void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int pipe);
> +int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
> +                                  struct device *dev);
> +void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
> +                                   struct device *dev);
> +
> +#endif /* _ROCKCHIP_DRM_DRV_H_ */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> new file mode 100644
> index 0000000..482f7b8
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> @@ -0,0 +1,201 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/kernel.h>
> +#include <drm/drm.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_gem.h"
> +
> +#define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
> +
> +struct rockchip_drm_fb {
> +       struct drm_framebuffer fb;
> +       struct drm_gem_object *obj[ROCKCHIP_MAX_FB_BUFFER];
> +};
> +
> +struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
> +                                              unsigned int plane)
> +{
> +       struct rockchip_drm_fb *rk_fb = to_rockchip_fb(fb);
> +
> +       if (plane >= ROCKCHIP_MAX_FB_BUFFER)
> +               return NULL;
> +
> +       return rk_fb->obj[plane];
> +}
> +
> +static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb)
> +{
> +       struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
> +       struct drm_gem_object *obj;
> +       int i;
> +
> +       for (i = 0; i < ROCKCHIP_MAX_FB_BUFFER; i++) {
> +               obj = rockchip_fb->obj[i];
> +               if (obj)
> +                       drm_gem_object_unreference_unlocked(obj);
> +       }
> +
> +       drm_framebuffer_cleanup(fb);
> +       kfree(rockchip_fb);
> +}
> +
> +static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
> +                                        struct drm_file *file_priv,
> +                                        unsigned int *handle)
> +{
> +       struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
> +
> +       return drm_gem_handle_create(file_priv,
> +                                    rockchip_fb->obj[0], handle);
> +}
> +
> +static struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
> +       .destroy        = rockchip_drm_fb_destroy,
> +       .create_handle  = rockchip_drm_fb_create_handle,
> +};
> +
> +static struct rockchip_drm_fb *
> +rockchip_fb_alloc(struct drm_device *dev, struct drm_mode_fb_cmd2 *mode_cmd,
> +                 struct drm_gem_object **obj, unsigned int num_planes)
> +{
> +       struct rockchip_drm_fb *rockchip_fb;
> +       int ret;
> +       int i;
> +
> +       rockchip_fb = kzalloc(sizeof(*rockchip_fb), GFP_KERNEL);
> +       if (!rockchip_fb)
> +               return ERR_PTR(-ENOMEM);
> +
> +       drm_helper_mode_fill_fb_struct(&rockchip_fb->fb, mode_cmd);
> +
> +       for (i = 0; i < num_planes; i++)
> +               rockchip_fb->obj[i] = obj[i];
> +
> +       ret = drm_framebuffer_init(dev, &rockchip_fb->fb,
> +                                  &rockchip_drm_fb_funcs);
> +       if (ret) {
> +               dev_err(dev->dev, "Failed to initialize framebuffer: %d\n",
> +                       ret);
> +               kfree(rockchip_fb);
> +               return ERR_PTR(ret);
> +       }
> +
> +       return rockchip_fb;
> +}
> +
> +static struct drm_framebuffer *
> +rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
> +                       struct drm_mode_fb_cmd2 *mode_cmd)
> +{
> +       struct rockchip_drm_fb *rockchip_fb;
> +       struct drm_gem_object *objs[ROCKCHIP_MAX_FB_BUFFER];
> +       struct drm_gem_object *obj;
> +       unsigned int hsub;
> +       unsigned int vsub;
> +       int num_planes;
> +       int ret;
> +       int i;
> +
> +       hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format);
> +       vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format);
> +       num_planes = min(drm_format_num_planes(mode_cmd->pixel_format),
> +                        ROCKCHIP_MAX_FB_BUFFER);
> +
> +       for (i = 0; i < num_planes; i++) {
> +               unsigned int width = mode_cmd->width / (i ? hsub : 1);
> +               unsigned int height = mode_cmd->height / (i ? vsub : 1);
> +               unsigned int min_size;
> +
> +               obj = drm_gem_object_lookup(dev, file_priv,
> +                                           mode_cmd->handles[i]);
> +               if (!obj) {
> +                       dev_err(dev->dev, "Failed to lookup GEM object\n");
> +                       ret = -ENXIO;
> +                       goto err_gem_object_unreference;
> +               }
> +
> +               min_size = (height - 1) * mode_cmd->pitches[i] +
> +                       mode_cmd->offsets[i] +
> +                       width * drm_format_plane_cpp(mode_cmd->pixel_format, i);
> +
> +               if (obj->size < min_size) {
> +                       drm_gem_object_unreference_unlocked(obj);
> +                       ret = -EINVAL;
> +                       goto err_gem_object_unreference;
> +               }
> +               objs[i] = obj;
> +       }
> +
> +       rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, objs, i);
> +       if (IS_ERR(rockchip_fb)) {
> +               ret = PTR_ERR(rockchip_fb);
> +               goto err_gem_object_unreference;
> +       }
> +
> +       return &rockchip_fb->fb;
> +
> +err_gem_object_unreference:
> +       for (i--; i >= 0; i--)
> +               drm_gem_object_unreference_unlocked(objs[i]);
> +       return ERR_PTR(ret);
> +}
> +
> +static void rockchip_drm_output_poll_changed(struct drm_device *dev)
> +{
> +       struct rockchip_drm_private *private = dev->dev_private;
> +       struct drm_fb_helper *fb_helper = private->fb_helper;
> +
> +       if (fb_helper)
> +               drm_fb_helper_hotplug_event(fb_helper);
> +}
> +
> +static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
> +       .fb_create = rockchip_user_fb_create,
> +       .output_poll_changed = rockchip_drm_output_poll_changed,
> +};
> +
> +struct drm_framebuffer *
> +rockchip_drm_framebuffer_init(struct drm_device *dev,
> +                             struct drm_mode_fb_cmd2 *mode_cmd,
> +                             struct drm_gem_object *obj)
> +{
> +       struct rockchip_drm_fb *rockchip_fb;
> +
> +       rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, &obj, 1);
> +       if (IS_ERR(rockchip_fb))
> +               return NULL;
> +
> +       return &rockchip_fb->fb;
> +}
> +
> +void rockchip_drm_mode_config_init(struct drm_device *dev)
> +{
> +       dev->mode_config.min_width = 0;
> +       dev->mode_config.min_height = 0;
> +
> +       /*
> +        * set max width and height as default value(4096x4096).
> +        * this value would be used to check framebuffer size limitation
> +        * at drm_mode_addfb().
> +        */
> +       dev->mode_config.max_width = 4096;
> +       dev->mode_config.max_height = 4096;
> +
> +       dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
> +}
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.h b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
> new file mode 100644
> index 0000000..09574d4
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
> @@ -0,0 +1,28 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef _ROCKCHIP_DRM_FB_H
> +#define _ROCKCHIP_DRM_FB_H
> +
> +struct drm_framebuffer *
> +rockchip_drm_framebuffer_init(struct drm_device *dev,
> +                             struct drm_mode_fb_cmd2 *mode_cmd,
> +                             struct drm_gem_object *obj);
> +void rockchip_drm_framebuffer_fini(struct drm_framebuffer *fb);
> +
> +void rockchip_drm_mode_config_init(struct drm_device *dev);
> +
> +struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
> +                                              unsigned int plane);
> +#endif /* _ROCKCHIP_DRM_FB_H */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
> new file mode 100644
> index 0000000..ae563f5
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
> @@ -0,0 +1,230 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <drm/drm.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_gem.h"
> +#include "rockchip_drm_fb.h"
> +
> +#define PREFERRED_BPP          32
> +#define to_rockchip_fbdev(x) container_of(x, struct rockchip_fbdev, helper)
> +
> +struct rockchip_fbdev {
> +       struct drm_fb_helper helper;
> +       struct drm_gem_object *bo;
> +};
> +
> +static int rockchip_fbdev_mmap(struct fb_info *info,
> +                              struct vm_area_struct *vma)
> +{
> +       struct drm_fb_helper *helper = info->par;
> +       struct rockchip_fbdev *fbdev = to_rockchip_fbdev(helper);
> +
> +       return rockchip_gem_mmap(fbdev->bo, vma);
> +}
> +
> +static struct fb_ops rockchip_drm_fbdev_ops = {
> +       .owner          = THIS_MODULE,
> +       .fb_mmap        = rockchip_fbdev_mmap,
> +       .fb_fillrect    = cfb_fillrect,
> +       .fb_copyarea    = cfb_copyarea,
> +       .fb_imageblit   = cfb_imageblit,
> +       .fb_check_var   = drm_fb_helper_check_var,
> +       .fb_set_par     = drm_fb_helper_set_par,
> +       .fb_blank       = drm_fb_helper_blank,
> +       .fb_pan_display = drm_fb_helper_pan_display,
> +       .fb_setcmap     = drm_fb_helper_setcmap,
> +};
> +
> +static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper,
> +                                    struct drm_fb_helper_surface_size *sizes)
> +{
> +       struct rockchip_fbdev *fbdev = to_rockchip_fbdev(helper);
> +       struct drm_mode_fb_cmd2 mode_cmd = { 0 };
> +       struct drm_device *dev = helper->dev;
> +       struct rockchip_gem_object *rk_obj;
> +       struct drm_framebuffer *fb;
> +       unsigned int bytes_per_pixel;
> +       unsigned long offset;
> +       struct fb_info *fbi;
> +       size_t size;
> +       int ret;
> +
> +       bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
> +
> +       mode_cmd.width = sizes->surface_width;
> +       mode_cmd.height = sizes->surface_height;
> +       mode_cmd.pitches[0] = sizes->surface_width * bytes_per_pixel;
> +       mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
> +               sizes->surface_depth);
> +
> +       size = mode_cmd.pitches[0] * mode_cmd.height;
> +
> +       rk_obj = rockchip_gem_create_object(dev, size);
> +       if (IS_ERR(rk_obj))
> +               return -ENOMEM;
> +
> +       fbdev->bo = &rk_obj->base;
> +
> +       fbi = framebuffer_alloc(0, dev->dev);
> +       if (!fbi) {
> +               dev_err(dev->dev, "Failed to allocate framebuffer info.\n");
> +               ret = -ENOMEM;
> +               goto err_rockchip_gem_free_object;
> +       }
> +
> +       helper->fb = rockchip_drm_framebuffer_init(dev, &mode_cmd, fbdev->bo);
> +       if (IS_ERR(helper->fb)) {
> +               dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n");
> +               ret = PTR_ERR(helper->fb);
> +               goto err_framebuffer_release;
> +       }
> +
> +       helper->fbdev = fbi;
> +
> +       fbi->par = helper;
> +       fbi->flags = FBINFO_FLAG_DEFAULT;
> +       fbi->fbops = &rockchip_drm_fbdev_ops;
> +
> +       ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
> +       if (ret) {
> +               dev_err(dev->dev, "Failed to allocate color map.\n");
> +               goto err_drm_framebuffer_unref;
> +       }
> +
> +       fb = helper->fb;
> +       drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
> +       drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
> +
> +       offset = fbi->var.xoffset * bytes_per_pixel;
> +       offset += fbi->var.yoffset * fb->pitches[0];
> +
> +       dev->mode_config.fb_base = 0;
> +       fbi->screen_base = rk_obj->kvaddr + offset;
> +       fbi->screen_size = rk_obj->base.size;
> +       fbi->fix.smem_len = rk_obj->base.size;
> +
> +       DRM_DEBUG_KMS("FB [%dx%d]-%d kvaddr=%p offset=%ld size=%d\n",
> +                     fb->width, fb->height, fb->depth, rk_obj->kvaddr,
> +                     offset, size);
> +       return 0;
> +
> +err_drm_framebuffer_unref:
> +       drm_framebuffer_unreference(helper->fb);
> +err_framebuffer_release:
> +       framebuffer_release(fbi);
> +err_rockchip_gem_free_object:
> +       rockchip_gem_free_object(&rk_obj->base);
> +       return ret;
> +}
> +
> +static struct drm_fb_helper_funcs rockchip_drm_fb_helper_funcs = {
> +       .fb_probe = rockchip_drm_fbdev_create,
> +};
> +
> +int rockchip_drm_fbdev_init(struct drm_device *dev)
> +{
> +       struct rockchip_drm_private *private = dev->dev_private;
> +       struct rockchip_fbdev *fbdev;
> +       struct drm_fb_helper *helper;
> +       unsigned int num_crtc;
> +       int ret;
> +
> +       if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
> +               return -EINVAL;
> +
> +       if (private->fb_helper) {
> +               DRM_ERROR("no allow to reinit fbdev\n");
> +               return -EINVAL;
> +       }
> +
> +       num_crtc = dev->mode_config.num_crtc;
> +
> +       fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);

Can you just embed rockchip_fbdev in struct rockchip_drm_private...
Or, just add its two fields to rockchip_drm_private:

      struct drm_fb_helper fbdev_helper;
      struct drm_gem_object *fbdev_bo;

It would eliminate some of the complexity in _fbdev_init, and especially in
_fbdev_fini.


> +       if (!fbdev)
> +               return -ENOMEM;
> +
> +       fbdev->helper.funcs = &rockchip_drm_fb_helper_funcs;
> +       helper = &fbdev->helper;
> +
> +       ret = drm_fb_helper_init(dev, helper, num_crtc, ROCKCHIP_MAX_CONNECTOR);
> +       if (ret < 0) {
> +               dev_err(dev->dev, "Failed to initialize drm fb helper.\n");
> +               goto err_free;
> +       }
> +
> +       ret = drm_fb_helper_single_add_all_connectors(helper);
> +       if (ret < 0) {
> +               dev_err(dev->dev, "Failed to add connectors.\n");
> +               goto err_drm_fb_helper_fini;
> +       }
> +
> +       /* disable all the possible outputs/crtcs before entering KMS mode */
> +       drm_helper_disable_unused_functions(dev);
> +
> +       ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
> +       if (ret < 0) {
> +               dev_err(dev->dev, "Failed to set initial hw configuration.\n");
> +               goto err_drm_fb_helper_fini;
> +       }
> +
> +       private->fb_helper = helper;
> +
> +       return 0;
> +
> +err_drm_fb_helper_fini:
> +       drm_fb_helper_fini(helper);
> +err_free:
> +       kfree(fbdev);
> +       return ret;
> +}
> +
> +void rockchip_drm_fbdev_fini(struct drm_device *dev)
> +{
> +       struct rockchip_drm_private *private = dev->dev_private;
> +       struct drm_fb_helper *helper;
> +       struct rockchip_fbdev *fbdev;
> +
> +       if (!private || !private->fb_helper)
> +               return;

Can these ever be NULL here?

> +
> +       helper = private->fb_helper;
> +       fbdev = to_rockchip_fbdev(helper);
> +
> +       if (helper->fbdev) {
> +               struct fb_info *info;
> +               int ret;
> +
> +               info = helper->fbdev;
> +               ret = unregister_framebuffer(info);
> +               if (ret < 0)
> +                       DRM_DEBUG_KMS("failed unregister_framebuffer()\n");
> +
> +               if (info->cmap.len)
> +                       fb_dealloc_cmap(&info->cmap);
> +
> +               framebuffer_release(info);
> +       }
> +
> +       if (helper->fb)
> +               drm_framebuffer_unreference(helper->fb);
> +
> +       drm_fb_helper_fini(helper);
> +       kfree(fbdev);
> +       private->fb_helper = NULL;
> +}
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
> new file mode 100644
> index 0000000..5edcf6a
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.h
> @@ -0,0 +1,20 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef _ROCKCHIP_DRM_FBDEV_H
> +#define _ROCKCHIP_DRM_FBDEV_H
> +
> +int rockchip_drm_fbdev_init(struct drm_device *dev);
> +
> +#endif /* _ROCKCHIP_DRM_FBDEV_H */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
> new file mode 100644
> index 0000000..5563dff
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
> @@ -0,0 +1,345 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <drm/drm.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_vma_manager.h>
> +
> +#include <linux/anon_inodes.h>
> +#include <linux/dma-attrs.h>
> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_gem.h"
> +
> +static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj)
> +{
> +       struct drm_gem_object *obj = &rk_obj->base;
> +       struct drm_device *drm = obj->dev;
> +
> +       init_dma_attrs(&rk_obj->dma_attrs);
> +       dma_set_attr(DMA_ATTR_WRITE_COMBINE, &rk_obj->dma_attrs);
> +
> +       /* TODO(djkurtz): Use DMA_ATTR_NO_KERNEL_MAPPING except for fbdev */
> +       rk_obj->kvaddr = dma_alloc_attrs(drm->dev, obj->size,
> +                                        &rk_obj->dma_addr, GFP_KERNEL,
> +                                        &rk_obj->dma_attrs);
> +       if (IS_ERR(rk_obj->kvaddr)) {
> +               int ret = PTR_ERR(rk_obj->kvaddr);
> +
> +               DRM_ERROR("failed to allocate %#x byte dma buffer, %d",
> +                         obj->size, ret);
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static void rockchip_gem_free_buf(struct rockchip_gem_object *rk_obj)
> +{
> +       struct drm_gem_object *obj = &rk_obj->base;
> +       struct drm_device *drm = obj->dev;
> +
> +       dma_free_attrs(drm->dev, obj->size, rk_obj->kvaddr, rk_obj->dma_addr,
> +                      &rk_obj->dma_attrs);
> +}
> +
> +/* drm driver mmap file operations */
> +int rockchip_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
> +{
> +       struct drm_file *priv = filp->private_data;
> +       struct drm_device *dev = priv->minor->dev;
> +       struct drm_gem_object *obj;
> +       struct drm_vma_offset_node *node;
> +       int ret;
> +
> +       if (drm_device_is_unplugged(dev))
> +               return -ENODEV;
> +
> +       mutex_lock(&dev->struct_mutex);
> +
> +       node = drm_vma_offset_exact_lookup(dev->vma_offset_manager,
> +                                          vma->vm_pgoff,
> +                                          vma_pages(vma));
> +       if (!node) {
> +               mutex_unlock(&dev->struct_mutex);
> +               DRM_ERROR("failed to find vma node.\n");
> +               return -EINVAL;
> +       } else if (!drm_vma_node_is_allowed(node, filp)) {
> +               mutex_unlock(&dev->struct_mutex);
> +               return -EACCES;
> +       }
> +
> +       obj = container_of(node, struct drm_gem_object, vma_node);
> +       ret = rockchip_gem_mmap(obj, vma);
> +
> +       mutex_unlock(&dev->struct_mutex);
> +
> +       return ret;
> +}
> +
> +int rockchip_drm_gem_mmap_buffer(struct file *filp,
> +                                struct vm_area_struct *vma)
> +{
> +       struct drm_gem_object *obj = filp->private_data;
> +
> +       return rockchip_gem_mmap(obj, vma);
> +}
> +
> +static const struct file_operations rockchip_drm_gem_fops = {
> +       .mmap = rockchip_drm_gem_mmap_buffer,
> +};
> +
> +struct rockchip_gem_object *
> +       rockchip_gem_create_object(struct drm_device *drm, unsigned int size)
> +{
> +       struct rockchip_gem_object *rk_obj;
> +       struct drm_gem_object *obj;
> +       struct file *filp;
> +       int ret;
> +
> +       size = round_up(size, PAGE_SIZE);
> +
> +       rk_obj = kzalloc(sizeof(*rk_obj), GFP_KERNEL);
> +       if (!rk_obj)
> +               return ERR_PTR(-ENOMEM);
> +
> +       obj = &rk_obj->base;
> +
> +       drm_gem_private_object_init(drm, obj, size);
> +
> +       filp = anon_inode_getfile("rockchip_gem", &rockchip_drm_gem_fops,
> +                                 obj, 0);
> +       if (IS_ERR(filp)) {
> +               DRM_ERROR("failed to create anon file object.\n");
> +               ret = PTR_ERR(filp);
> +               goto err_free_rk_obj;
> +       }
> +       filp->f_mode = FMODE_READ | FMODE_WRITE;
> +       obj->filp = filp;

I think this chunk goes away now, if we do not have custom gem mmap ioctl? an
Along with rockchip_drm_gem_fops and rockchip_drm_gem_mmap_buffer.

> +
> +       ret = drm_gem_create_mmap_offset(obj);
> +       if (ret)
> +               goto err_free_obj;
> +
> +       ret = rockchip_gem_alloc_buf(rk_obj);
> +       if (ret)
> +               goto err_free_mmap_offset;
> +
> +       return rk_obj;
> +
> +err_free_mmap_offset:
> +       drm_gem_free_mmap_offset(obj);
> +err_free_obj:
> +       drm_gem_object_release(obj);
> +err_free_rk_obj:
> +       kfree(rk_obj);
> +       return ERR_PTR(ret);
> +}
> +
> +/*
> + * rockchip_gem_free_object - (struct drm_driver)->gem_free_object callback
> + * function
> + */
> +void rockchip_gem_free_object(struct drm_gem_object *obj)
> +{
> +       struct rockchip_gem_object *rk_obj;
> +
> +       drm_gem_free_mmap_offset(obj);
> +
> +       rk_obj = to_rockchip_obj(obj);
> +
> +       rockchip_gem_free_buf(rk_obj);
> +
> +       drm_gem_object_release(obj);
> +
> +       kfree(rk_obj);
> +}
> +
> +int rockchip_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)

Nit: Can you move this function up just below rockchip_gem_free_buf.
That would put these three dma_*_attrs() wrapper functions in one place.

> +{
> +       struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
> +       struct drm_device *drm = obj->dev;
> +       unsigned long vm_size;
> +
> +       vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
> +       vm_size = vma->vm_end - vma->vm_start;
> +
> +       if (vm_size > obj->size)
> +               return -EINVAL;
> +
> +       return dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr,
> +                            obj->size, &rk_obj->dma_attrs);
> +}
> +
> +/*
> + * rockchip_gem_create_with_handle - allocate an object with the given
> + * size and create a gem handle on it
> + *
> + * returns a struct rockchip_gem_object* on success or ERR_PTR values
> + * on failure.
> + */
> +static struct rockchip_gem_object *
> +rockchip_gem_create_with_handle(struct drm_file *file_priv,
> +                               struct drm_device *drm, unsigned int size,
> +                               unsigned int *handle)
> +{
> +       struct rockchip_gem_object *rk_obj;
> +       struct drm_gem_object *obj;
> +       int ret;
> +
> +       rk_obj = rockchip_gem_create_object(drm, size);
> +       if (IS_ERR(rk_obj))
> +               return NULL;

Should this be:
   return ERR_CAST(rk_obj);

> +
> +       obj = &rk_obj->base;
> +
> +       /*
> +        * allocate a id of idr table where the obj is registered
> +        * and handle has the id what user can see.
> +        */
> +       ret = drm_gem_handle_create(file_priv, obj, handle);
> +       if (ret)
> +               goto err_handle_create;
> +
> +       /* drop reference from allocate - handle holds it now. */
> +       drm_gem_object_unreference_unlocked(obj);
> +
> +       return rk_obj;
> +
> +err_handle_create:
> +       rockchip_gem_free_object(obj);
> +
> +       return ERR_PTR(ret);
> +}
> +
> +int rockchip_gem_dumb_map_offset(struct drm_file *file_priv,
> +                                struct drm_device *dev, uint32_t handle,
> +                                uint64_t *offset)
> +{
> +       struct drm_gem_object *obj;
> +       int ret = 0;

Nit: No = 0;

(I am a fan of letting the compiler tell us if we forget to initialize a
return value in goto/error paths)

> +
> +       mutex_lock(&dev->struct_mutex);
> +
> +       /*
> +        * get offset of memory allocated for drm framebuffer.
> +        * - this callback would be called by user application
> +        * with DRM_IOCTL_MODE_MAP_DUMB command.
> +        */

Just delete this comment that was copied from exynos.

> +
> +       obj = drm_gem_object_lookup(dev, file_priv, handle);
> +       if (!obj) {
> +               DRM_ERROR("failed to lookup gem object.\n");
> +               ret = -EINVAL;
> +               goto unlock;
> +       }
> +
> +       ret = drm_gem_create_mmap_offset(obj);
> +       if (ret)
> +               goto out;
> +
> +       *offset = drm_vma_node_offset_addr(&obj->vma_node);
> +       DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset);

Nit: print all 64 bits of offset

> +
> +out:
> +       drm_gem_object_unreference(obj);
> +unlock:
> +       mutex_unlock(&dev->struct_mutex);
> +       return ret;
> +}
> +
> +/*
> + * rockchip_gem_dumb_create - (struct drm_driver)->dumb_create callback
> + * function
> + *
> + * This aligns the pitch and size arguments to the minimum required. wrap
> + * this into your own function if you need bigger alignment.
> + */
> +int rockchip_gem_dumb_create(struct drm_file *file_priv,
> +                            struct drm_device *dev,
> +                            struct drm_mode_create_dumb *args)
> +{
> +       struct rockchip_gem_object *rk_obj;
> +       int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
> +
> +       /*
> +        * align to 64 bytes since Mali requires it.
> +        */
> +       min_pitch = ((min_pitch + 63) / 64) * 64;

ALIGN(min_pitch, 64);

> +
> +       if (args->pitch < min_pitch)
> +               args->pitch = min_pitch;
> +
> +       if (args->size < args->pitch * args->height)
> +               args->size = args->pitch * args->height;
> +
> +       rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size,
> +                                                &args->handle);
> +
> +       return PTR_ERR_OR_ZERO(rk_obj);
> +}
> +
> +/*
> + * Allocate a sg_table for this GEM object.
> + * Note: Both the table's contents, and the sg_table itself must be freed by
> + *       the caller.
> + * Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error.
> + */
> +struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj)
> +{
> +       struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
> +       struct drm_device *drm = obj->dev;
> +       struct sg_table *sgt = NULL;

Do not init to NULL

> +       int ret;
> +
> +       sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
> +       if (!sgt)
> +               return ERR_PTR(-ENOMEM);
> +
> +       ret = dma_get_sgtable_attrs(drm->dev, sgt, rk_obj->kvaddr,
> +                                   rk_obj->dma_addr, obj->size,
> +                                   &rk_obj->dma_attrs);
> +       if (ret) {
> +               DRM_ERROR("failed to allocate sgt, %d\n", ret);
> +               kfree(sgt);
> +               return ERR_PTR(ret);
> +       }
> +
> +       return sgt;
> +}
> +
> +void *rockchip_gem_prime_vmap(struct drm_gem_object *obj)
> +{
> +       struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
> +
> +       return rk_obj->kvaddr;
> +}
> +
> +void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
> +{
> +       /* Nothing to do */
> +}
> +
> +int rockchip_gem_prime_mmap(struct drm_gem_object *obj,
> +                           struct vm_area_struct *vma)

I don't think this function is correct.  I think we should use dma_mmap_attrs()?
But, you can probably just remove it, and drop support for mmap'ing
exported dma_bufs for now.

> +{
> +       struct drm_device *dev = obj->dev;
> +       int ret;
> +
> +       mutex_lock(&dev->struct_mutex);
> +       ret = drm_gem_mmap_obj(obj, obj->size, vma);
> +       mutex_unlock(&dev->struct_mutex);
> +
> +       return ret;
> +}
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
> new file mode 100644
> index 0000000..e37b921
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
> @@ -0,0 +1,55 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef _ROCKCHIP_DRM_GEM_H
> +#define _ROCKCHIP_DRM_GEM_H
> +
> +#define to_rockchip_obj(x) container_of(x, struct rockchip_gem_object, base)
> +
> +struct rockchip_gem_object {
> +       struct drm_gem_object base;
> +       unsigned int flags;
> +
> +       void *kvaddr;
> +       dma_addr_t dma_addr;
> +       struct dma_attrs dma_attrs;
> +};
> +
> +struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj);
> +struct drm_gem_object *
> +rockchip_gem_prime_import_sg_table(struct drm_device *dev, size_t size,
> +                                  struct sg_table *sgt);
> +void *rockchip_gem_prime_vmap(struct drm_gem_object *obj);
> +void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
> +int rockchip_gem_prime_mmap(struct drm_gem_object *obj,
> +                           struct vm_area_struct *vma);
> +
> +/* drm driver mmap file operations */
> +int rockchip_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
> +
> +/* mmap a gem object to userspace. */
> +int rockchip_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma);
> +
> +struct rockchip_gem_object *
> +       rockchip_gem_create_object(struct drm_device *drm, unsigned int size);
> +
> +void rockchip_gem_free_object(struct drm_gem_object *obj);
> +
> +int rockchip_gem_dumb_create(struct drm_file *file_priv,
> +                            struct drm_device *dev,
> +                            struct drm_mode_create_dumb *args);
> +int rockchip_gem_dumb_map_offset(struct drm_file *file_priv,
> +                                struct drm_device *dev, uint32_t handle,
> +                                uint64_t *offset);
> +#endif /* _ROCKCHIP_DRM_GEM_H */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> new file mode 100644
> index 0000000..fe9d2b0
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> @@ -0,0 +1,1422 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <drm/drm.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include <linux/kernel.h>
> +#include <linux/platform_device.h>
> +#include <linux/clk.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/component.h>
> +
> +#include <linux/reset.h>
> +#include <linux/iommu.h>
> +#include <linux/delay.h>
> +
> +#include <video/of_display_timing.h>
> +#include <video/of_videomode.h>
> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_fbdev.h"
> +#include "rockchip_drm_gem.h"
> +#include "rockchip_drm_fb.h"
> +#include "rockchip_drm_vop.h"
> +
> +#define VOP_DEFAULT_FRAMERATE  60
> +#define VOP_MAX_WIN_SUPPORT    5
> +#define VOP_DEFAULT_PRIMARY    0
> +#define VOP_DEFAULT_CURSOR     1
> +#define VOP_REG(off, _mask, s) \
> +               {.offset = off, \
> +                .mask = _mask, \
> +                .shift = s,}
> +
> +#define __REG_SET(x, off, mask, shift, v) \
> +               vop_mask_write(x, off, (mask) << shift, (v) << shift)
> +
> +#define REG_SET(x, base, reg, v) \
> +               __REG_SET(x, base + reg.offset, reg.mask, reg.shift, v)
> +
> +#define VOP_WIN_SET(x, win, name, v) \
> +               REG_SET(x, win->base, win->phy->name, v)
> +#define VOP_CTRL_SET(x, name, v) \
> +               REG_SET(x, 0, (x)->data->ctrl->name, v)
> +
> +#define VOP_WIN_GET_YRGBADDR(ctx, win) \
> +               vop_readl(ctx, win->base + win->phy->yrgb_mst.offset)
> +
> +#define to_vop_ctx(x) container_of(x, struct vop_context, crtc)
> +#define to_rockchip_plane(x) container_of(x, struct rockchip_plane, base)
> +
> +struct rockchip_plane {
> +       int id;
> +       struct drm_plane base;
> +       const struct vop_win *win;
> +
> +       uint32_t pending_yrgb_mst;
> +       struct drm_framebuffer *front_fb;
> +       struct drm_framebuffer *pending_fb;
> +       bool enabled;
> +};
> +
> +struct vop_context {
> +       struct device *dev;
> +       struct drm_device *drm_dev;
> +       struct drm_crtc crtc;
> +       struct drm_pending_vblank_event *event;
> +       struct vop_driver *drv;
> +       unsigned int dpms;
> +       unsigned int win_mask;
> +       wait_queue_head_t wait_vsync_queue;
> +       atomic_t wait_vsync_event;
> +
> +       struct workqueue_struct *vsync_wq;
> +       struct work_struct vsync_work;
> +
> +       /* mutex vsync_ work */
> +       struct mutex vsync_mutex;
> +       bool vsync_work_pending;
> +
> +       struct vop_driver_data *data;
> +
> +       uint32_t *regsbak;
> +       void __iomem *regs;
> +
> +       /* physical map length of vop register */
> +       uint32_t len;
> +
> +       /* one time only one process allowed to config the register */
> +       spinlock_t reg_lock;
> +       /* lock vop irq reg */
> +       spinlock_t irq_lock;
> +
> +       unsigned int irq;
> +
> +       /* vop AHP clk */
> +       struct clk *hclk;
> +       /* vop dclk */
> +       struct clk *dclk;
> +       /* vop share memory frequency */
> +       struct clk *aclk;
> +
> +       /* vop ahb reset */
> +       struct reset_control *ahb_rst;
> +       /* vop dclk reset */
> +       struct reset_control *dclk_rst;
> +       uint32_t pixclock;

Where is pixclock used?

> +
> +       int pipe;
> +       bool clk_on;
> +};
> +
> +enum vop_data_format {
> +       VOP_FMT_ARGB8888 = 0,
> +       VOP_FMT_RGB888,
> +       VOP_FMT_RGB565,
> +       VOP_FMT_YUV420SP = 4,
> +       VOP_FMT_YUV422SP,
> +       VOP_FMT_YUV444SP,
> +};
> +
> +struct vop_reg_data {
> +       uint32_t offset;
> +       uint32_t value;
> +};
> +
> +struct vop_reg {
> +       uint32_t offset;
> +       uint32_t shift;
> +       uint32_t mask;
> +};
> +
> +struct vop_ctrl {
> +       struct vop_reg standby;
> +       struct vop_reg data_blank;
> +       struct vop_reg gate_en;
> +       struct vop_reg mmu_en;
> +       struct vop_reg rgb_en;
> +       struct vop_reg edp_en;
> +       struct vop_reg hdmi_en;
> +       struct vop_reg mipi_en;
> +       struct vop_reg out_mode;
> +       struct vop_reg dither_down;
> +       struct vop_reg dither_up;
> +       struct vop_reg pin_pol;
> +
> +       struct vop_reg htotal_pw;
> +       struct vop_reg hact_st_end;
> +       struct vop_reg vtotal_pw;
> +       struct vop_reg vact_st_end;
> +       struct vop_reg hpost_st_end;
> +       struct vop_reg vpost_st_end;
> +};
> +
> +struct vop_win_phy {
> +       const uint32_t *data_formats;
> +       uint32_t nformats;
> +
> +       struct vop_reg enable;
> +       struct vop_reg format;
> +       struct vop_reg act_info;
> +       struct vop_reg dsp_info;
> +       struct vop_reg dsp_st;
> +       struct vop_reg yrgb_mst;
> +       struct vop_reg uv_mst;
> +       struct vop_reg yrgb_vir;
> +       struct vop_reg uv_vir;
> +
> +       struct vop_reg dst_alpha_ctl;
> +       struct vop_reg src_alpha_ctl;
> +};
> +
> +struct vop_win {
> +       uint32_t base;
> +       const struct vop_win_phy *phy;
> +};
> +
> +struct vop_driver_data {
> +       const void *init_table;
> +       int table_size;
> +       const struct vop_ctrl *ctrl;
> +       const struct vop_win *win[VOP_MAX_WIN_SUPPORT];
> +};
> +
> +static const uint32_t formats_01[] = {
> +       DRM_FORMAT_XRGB8888,
> +       DRM_FORMAT_ARGB8888,
> +       DRM_FORMAT_RGB888,
> +       DRM_FORMAT_RGB565,
> +       DRM_FORMAT_NV12,
> +       DRM_FORMAT_NV16,
> +       DRM_FORMAT_NV24,
> +};
> +
> +static const uint32_t formats_234[] = {
> +       DRM_FORMAT_XRGB8888,
> +       DRM_FORMAT_ARGB8888,
> +       DRM_FORMAT_RGB888,
> +       DRM_FORMAT_RGB565,
> +};
> +
> +static const struct vop_win_phy win01_data = {
> +       .data_formats = formats_01,
> +       .nformats = ARRAY_SIZE(formats_01),
> +       .enable = VOP_REG(WIN0_CTRL0, 0x1, 0),
> +       .format = VOP_REG(WIN0_CTRL0, 0x7, 1),
> +       .act_info = VOP_REG(WIN0_ACT_INFO, 0x1fff1fff, 0),
> +       .dsp_info = VOP_REG(WIN0_DSP_INFO, 0x1fff1fff, 0),
> +       .dsp_st = VOP_REG(WIN0_DSP_ST, 0x1fff1fff, 0),
> +       .yrgb_mst = VOP_REG(WIN0_YRGB_MST, 0xffffffff, 0),
> +       .uv_mst = VOP_REG(WIN0_CBR_MST, 0xffffffff, 0),
> +       .yrgb_vir = VOP_REG(WIN0_VIR, 0x3fff, 0),
> +       .uv_vir = VOP_REG(WIN0_VIR, 0x3fff, 16),
> +       .src_alpha_ctl = VOP_REG(WIN0_SRC_ALPHA_CTRL, 0xff, 0),
> +       .dst_alpha_ctl = VOP_REG(WIN0_DST_ALPHA_CTRL, 0xff, 0),
> +};
> +
> +static const struct vop_win_phy win23_data = {
> +       .data_formats = formats_234,
> +       .nformats = ARRAY_SIZE(formats_234),
> +       .enable = VOP_REG(WIN2_CTRL0, 0x1, 0),
> +       .format = VOP_REG(WIN2_CTRL0, 0x7, 1),
> +       .dsp_info = VOP_REG(WIN2_DSP_INFO0, 0x0fff0fff, 0),
> +       .dsp_st = VOP_REG(WIN2_DSP_ST0, 0x1fff1fff, 0),
> +       .yrgb_mst = VOP_REG(WIN2_MST0, 0xffffffff, 0),
> +       .yrgb_vir = VOP_REG(WIN2_VIR0_1, 0x1fff, 0),
> +       .src_alpha_ctl = VOP_REG(WIN2_SRC_ALPHA_CTRL, 0xff, 0),
> +       .dst_alpha_ctl = VOP_REG(WIN2_DST_ALPHA_CTRL, 0xff, 0),
> +};
> +
> +static const struct vop_win_phy cursor_data = {
> +       .data_formats = formats_234,
> +       .nformats = ARRAY_SIZE(formats_234),
> +       .enable = VOP_REG(HWC_CTRL0, 0x1, 0),
> +       .format = VOP_REG(HWC_CTRL0, 0x7, 1),
> +       .dsp_st = VOP_REG(HWC_DSP_ST, 0x1fff1fff, 0),
> +       .yrgb_mst = VOP_REG(HWC_MST, 0xffffffff, 0),
> +};
> +
> +static const struct vop_win win0 = {
> +       .base = 0,
> +       .phy = &win01_data,
> +};
> +
> +static const struct vop_win win1 = {
> +       .base = 0x40,
> +       .phy = &win01_data,
> +};
> +
> +static const struct vop_win win2 = {
> +       .base = 0,
> +       .phy = &win23_data,
> +};
> +
> +static const struct vop_win win3 = {
> +       .base = 0x50,
> +       .phy = &win23_data,
> +};
> +
> +static const struct vop_win win_cursor = {
> +       .base = 0,
> +       .phy = &cursor_data,
> +};
> +
> +static const struct vop_ctrl ctrl_data = {
> +       .standby = VOP_REG(SYS_CTRL, 0x1, 22),
> +       .gate_en = VOP_REG(SYS_CTRL, 0x1, 23),
> +       .mmu_en = VOP_REG(SYS_CTRL, 0x1, 20),
> +       .rgb_en = VOP_REG(SYS_CTRL, 0x1, 12),
> +       .hdmi_en = VOP_REG(SYS_CTRL, 0x1, 13),
> +       .edp_en = VOP_REG(SYS_CTRL, 0x1, 14),
> +       .mipi_en = VOP_REG(SYS_CTRL, 0x1, 15),
> +       .dither_down = VOP_REG(DSP_CTRL1, 0xf, 1),
> +       .dither_up = VOP_REG(DSP_CTRL1, 0x1, 6),
> +       .data_blank = VOP_REG(DSP_CTRL0, 0x1, 19),
> +       .out_mode = VOP_REG(DSP_CTRL0, 0xf, 0),
> +       .pin_pol = VOP_REG(DSP_CTRL0, 0xf, 4),
> +       .htotal_pw = VOP_REG(DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
> +       .hact_st_end = VOP_REG(DSP_HACT_ST_END, 0x1fff1fff, 0),
> +       .vtotal_pw = VOP_REG(DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
> +       .vact_st_end = VOP_REG(DSP_VACT_ST_END, 0x1fff1fff, 0),
> +       .hpost_st_end = VOP_REG(POST_DSP_HACT_INFO, 0x1fff1fff, 0),
> +       .vpost_st_end = VOP_REG(POST_DSP_VACT_INFO, 0x1fff1fff, 0),
> +};
> +
> +static const struct vop_reg_data vop_init_reg_table[] = {
> +       {SYS_CTRL, 0x00801000},
> +       {DSP_CTRL0, 0x00000000},
> +       {WIN0_CTRL0, 0x00000080},
> +       {WIN1_CTRL0, 0x00000080},
> +};
> +
> +static const struct vop_driver_data rockchip_rk3288_vop = {
> +       .init_table = vop_init_reg_table,
> +       .table_size = ARRAY_SIZE(vop_init_reg_table),
> +       .ctrl = &ctrl_data,
> +       .win[0] = &win0,
> +       .win[1] = &win1,
> +       .win[2] = &win2,
> +       .win[3] = &win3,
> +       .win[4] = &win_cursor,
> +};
> +
> +static const struct of_device_id vop_driver_dt_match[] = {
> +       { .compatible = "rockchip,rk3288-vop",
> +         .data = (void *)&rockchip_rk3288_vop },
> +       {},
> +};
> +
> +static inline void vop_writel(struct vop_context *ctx,
> +                             uint32_t offset, uint32_t v)
> +{
> +       writel(v, ctx->regs + offset);

TODO: I *think* the following may be a useful optimization:
When doing mode_set or update_plane, use writel_relaxed to write all of the
shadowed mode/window configuration registers, and just use writel() to write
vop_cfg_done.  This will write all the configuration registers without barriers,
but still ensure that all such registers are written before cfg_done.

However, for most other register writes, you probably do want to always use
writel().

> +       ctx->regsbak[offset >> 2] = v;
> +}
> +
> +static inline uint32_t vop_readl(struct vop_context *ctx, uint32_t offset)
> +{
> +       return readl(ctx->regs + offset);
> +}
> +
> +static inline void vop_cfg_done(struct vop_context *ctx)
> +{
> +       writel(0x01, ctx->regs + REG_CFG_DONE);
> +}
> +
> +static inline void vop_mask_write(struct vop_context *ctx,
> +                                 uint32_t offset, uint32_t mask, uint32_t v)
> +{
> +       if (mask) {
> +               uint32_t cached_val = ctx->regsbak[offset >> 2];
> +
> +               cached_val = (cached_val & ~mask) | v;
> +               writel(cached_val, ctx->regs + offset);
> +               ctx->regsbak[offset >> 2] = cached_val;
> +       }
> +}
> +
> +static inline struct vop_driver_data *vop_get_driver_data(struct device *dev)
> +{
> +       const struct of_device_id *of_id =
> +                       of_match_device(vop_driver_dt_match, dev);
> +
> +       return (struct vop_driver_data *)of_id->data;
> +}
> +
> +static enum vop_data_format vop_convert_format(uint32_t format)
> +{
> +       switch (format) {
> +       case DRM_FORMAT_XRGB8888:
> +       case DRM_FORMAT_ARGB8888:
> +               return VOP_FMT_ARGB8888;
> +       case DRM_FORMAT_RGB888:
> +               return VOP_FMT_RGB888;
> +       case DRM_FORMAT_RGB565:
> +               return VOP_FMT_RGB565;
> +       case DRM_FORMAT_NV12:
> +               return VOP_FMT_YUV420SP;
> +       case DRM_FORMAT_NV16:
> +               return VOP_FMT_YUV422SP;
> +       case DRM_FORMAT_NV24:
> +               return VOP_FMT_YUV444SP;
> +       default:
> +               DRM_ERROR("unsupport format[%08x]\n", format);
> +               return -EINVAL;
> +       }
> +}
> +
> +static bool is_alpha_support(uint32_t format)
> +{
> +       switch (format) {
> +       case DRM_FORMAT_ARGB8888:
> +               return true;
> +       default:
> +               return false;
> +       }
> +}
> +
> +/* TODO(djkurtz): move generic 'setup slave rk_iommu' code somewhere common */
> +static int vop_iommu_init(struct vop_context *ctx)
> +{
> +       struct device *dev = ctx->dev;
> +       struct device_node *np = dev->of_node;
> +       struct platform_device *pd;
> +       int count;
> +       int ret;
> +       struct of_phandle_args args;
> +
> +       /* Each VOP must have exactly one iommu node, with no args */
> +       count = of_count_phandle_with_args(np, "iommus", "#iommu-cells");
> +       if (count != 1) {
> +               dev_err(dev, "of_count_phandle_with_args(%s) => %d\n",
> +                       np->full_name, count);
> +               return -EINVAL;
> +       }
> +
> +       ret = of_parse_phandle_with_args(np, "iommus", "#iommu-cells", 0,
> +                                        &args);
> +       if (ret) {
> +               dev_err(dev, "of_parse_phandle_with_args(%s) => %d\n",
> +                       np->full_name, ret);
> +               return ret;
> +       }
> +       if (args.args_count != 0) {
> +               dev_err(dev, "incorrect number of iommu params found for %s (found %d, expected 0)\n",
> +                       args.np->full_name, args.args_count);
> +               return -EINVAL;
> +       }
> +
> +       pd = of_find_device_by_node(args.np);
> +       of_node_put(args.np);
> +       if (!pd) {
> +               dev_err(dev, "iommu %s not found\n", args.np->full_name);
> +               return -EPROBE_DEFER;
> +       }
> +
> +       /* TODO(djkurtz): handle multiple slave iommus for a single master */
> +       dev->archdata.iommu = &pd->dev;
> +
> +       ret = rockchip_drm_dma_attach_device(ctx->drm_dev, dev);
> +       if (ret) {
> +               dev_err(dev, "failed to attach to drm dma mapping, %d\n", ret);
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static void vop_iommu_fini(struct vop_context *ctx)
> +{
> +       rockchip_drm_dma_detach_device(ctx->drm_dev, ctx->dev);
> +}
> +
> +static int vop_clk_enable(struct vop_context *ctx)
> +{
> +       int ret;

if (ctx->clk_on)
  return 0;

> +
> +       if (!ctx->clk_on) {
> +               ret = clk_enable(ctx->hclk);
> +               if (ret < 0) {
> +                       dev_err(ctx->dev, "failed to enable hclk\n");

I find it nice to print the errno, to:

   dev_err(ctx->dev, "failed to enable hclk, %d\n", ret);

> +                       return ret;
> +               }
> +
> +               ret = clk_enable(ctx->dclk);
> +               if (ret < 0) {
> +                       dev_err(ctx->dev, "failed to enable dclk\n");
> +                       goto err_dclk;

goto err_disable_hclk;

> +               }
> +
> +               ret = clk_enable(ctx->aclk);
> +               if (ret < 0) {
> +                       dev_err(ctx->dev, "failed to enable aclk\n");
> +                       goto err_aclk;;

goto err_disable_dclk;

> +               }
> +               ctx->clk_on = true;
> +       }
> +
> +       return ret;

Hm, didn't the compiler complain about ret possibly being returned
undefined here?

> +err_aclk:
> +       clk_disable(ctx->aclk);
> +err_dclk:
> +       clk_disable(ctx->dclk);

err_disable_dclk:
       clk_disable(ctx->dclk);
err_disable_hclk:
       clk_disable(ctx->hclk);

> +       return ret;
> +}
> +
> +static void vop_clk_disable(struct vop_context *ctx)
> +{

if (ctx->clk_on)
  return;

> +       if (ctx->clk_on) {
> +               clk_disable(ctx->aclk);
> +               clk_disable(ctx->dclk);
> +               clk_disable(ctx->hclk);
> +               ctx->clk_on = false;
> +       }
> +}
> +
> +static void vop_power_on(struct vop_context *ctx)
> +{
> +       if (vop_clk_enable(ctx) < 0) {
> +               dev_err(ctx->dev, "failed to enable clks\n");
> +               return;
> +       }
> +
> +       spin_lock(&ctx->reg_lock);
> +
> +       VOP_CTRL_SET(ctx, standby, 0);
> +
> +       spin_unlock(&ctx->reg_lock);
> +}
> +
> +static void vop_power_off(struct vop_context *ctx)
> +{
> +       spin_lock(&ctx->reg_lock);
> +
> +       VOP_CTRL_SET(ctx, standby, 1);
> +
> +       spin_unlock(&ctx->reg_lock);
> +
> +       vop_clk_disable(ctx);
> +}
> +
> +/*
> + * @start:  starting pixel in screen coordinates
> + * @length: length of buffer "row" in pixels
> + * @end:    one pixel past end of buffer in screen coordinates
> + * @last:   width of screen, or one pixel past end of screen
> + * @size:   number of pixels of buffer to display on scree
> + */
> +static int rockchip_plane_get_size(int start, unsigned length, unsigned last)
> +{
> +       int end = start + length;
> +       int size = 0;
> +
> +       if (start <= 0) {
> +               if (end > 0)
> +                       size = min_t(unsigned, end, last);
> +       } else if (start < last) {
> +               size = min_t(unsigned, last - start, length);
> +       }
> +
> +       return size;
> +}
> +
> +static int rockchip_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
> +                                struct drm_framebuffer *fb, int crtc_x,
> +                                int crtc_y, unsigned int crtc_w,
> +                                unsigned int crtc_h, uint32_t src_x,
> +                                uint32_t src_y, uint32_t src_w, uint32_t src_h)
> +{
> +       struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
> +       const struct vop_win *win = rockchip_plane->win;
> +       struct vop_context *ctx = to_vop_ctx(crtc);
> +       struct drm_gem_object *obj;
> +       struct rockchip_gem_object *rk_obj;
> +       unsigned long offset;
> +       unsigned int actual_w;
> +       unsigned int actual_h;
> +       unsigned int dsp_stx;
> +       unsigned int dsp_sty;
> +       unsigned int y_vir_stride;
> +       dma_addr_t yrgb_mst;
> +       enum vop_data_format format;
> +       uint32_t val;
> +       bool is_alpha;
> +
> +       src_w >>= 16;
> +       src_h >>= 16;
> +       src_x >>= 16;
> +       src_y >>= 16;
> +
> +       if (crtc_w != src_w || crtc_h != src_h)
> +               return -EINVAL;

Can we use drm_plane_helper_check_update() here instead of doing our own checks?

> +
> +       actual_w = rockchip_plane_get_size(crtc_x,
> +                                          crtc_w, crtc->mode.hdisplay);
> +       actual_h = rockchip_plane_get_size(crtc_y,
> +                                          crtc_h, crtc->mode.vdisplay);
> +       if (!actual_w || !actual_h)
> +               return -EINVAL;
> +
> +       if (crtc_x < 0) {
> +               if (actual_w)
> +                       src_x -= crtc_x;
> +               crtc_x = 0;
> +       }
> +
> +       if (crtc_y < 0) {
> +               if (actual_h)
> +                       src_y -= crtc_y;
> +               crtc_y = 0;
> +       }
> +
> +       is_alpha = is_alpha_support(fb->pixel_format);
> +       format = vop_convert_format(fb->pixel_format);
> +       if (format < 0)
> +               return format;
> +
> +       obj = rockchip_fb_get_gem_obj(fb, 0);
> +       if (!obj) {
> +               DRM_ERROR("fail to get rockchip gem object from framebuffer\n");
> +               return -EINVAL;
> +       }
> +
> +       rk_obj = to_rockchip_obj(obj);
> +
> +       yrgb_mst = rk_obj->dma_addr;
> +
> +       dsp_stx = crtc_x + crtc->mode.htotal - crtc->mode.hsync_start;
> +       dsp_sty = crtc_y + crtc->mode.vtotal - crtc->mode.vsync_start;
> +
> +       offset = src_x * (fb->bits_per_pixel >> 3);
> +       offset += src_y * fb->pitches[0];
> +
> +       y_vir_stride = fb->pitches[0] / (fb->bits_per_pixel >> 3);
> +
> +       spin_lock(&ctx->reg_lock);
> +
> +       VOP_WIN_SET(ctx, win, format, format);
> +       VOP_WIN_SET(ctx, win, yrgb_vir, y_vir_stride);
> +       yrgb_mst += offset;
> +       VOP_WIN_SET(ctx, win, yrgb_mst, yrgb_mst);
> +       val = ((actual_h - 1) & 0xffff) << 16;
> +       val |= (actual_w - 1) & 0xffff;
> +       VOP_WIN_SET(ctx, win, act_info, val);
> +       VOP_WIN_SET(ctx, win, dsp_info, val);
> +       val = ((dsp_sty - 1) & 0xffff) << 16;
> +       val |= (dsp_stx - 1) & 0xffff;
> +       VOP_WIN_SET(ctx, win, dsp_st, val);
> +

It looks a bit funny to me, but according to the TRM I am looking at:
  act mask is 0x1fff
  dsp_info is 0x0fff
  dsp_st   is 0x1fff

But these would be better as macros:

#define ACT_INFO_VAL(x) ((x - 1) & 0x1fff)
#define DSP_INFO_VAL(x) ((x - 1) & 0x0fff)
#define DSP_ST_VAL(x) ((x - 1) & 0x1fff)

#define ACT_INFO(w, h) ((ACT_INFO_VAL(h) << 16) | (ACT_INFO_VAL(w))
#define DSP_INFO(w, h) ((DSP_INFO_VAL(h) << 16) | (DSP_INFO_VAL(w))
#define DSP_INFO(x, y) ((DSP_ST_VAL(y) << 16) | (DSP_ST_VAL(x))

> +       if (is_alpha) {
> +               VOP_WIN_SET(ctx, win, dst_alpha_ctl,
> +                           DST_FACTOR_M0(ALPHA_SRC_INVERSE));
> +               val = SRC_ALPHA_EN(1) | SRC_COLOR_M0(ALPHA_SRC_PRE_MUL) |
> +                       SRC_ALPHA_M0(ALPHA_STRAIGHT) |
> +                       SRC_BLEND_M0(ALPHA_PER_PIX) |
> +                       SRC_ALPHA_CAL_M0(ALPHA_NO_SATURATION) |
> +                       SRC_FACTOR_M0(ALPHA_ONE);
> +               VOP_WIN_SET(ctx, win, src_alpha_ctl, val);
> +       } else {
> +               VOP_WIN_SET(ctx, win, src_alpha_ctl, SRC_ALPHA_EN(0));
> +       }
> +
> +       VOP_WIN_SET(ctx, win, enable, 1);
> +
> +       spin_unlock(&ctx->reg_lock);
> +
> +       mutex_lock(&ctx->vsync_mutex);
> +
> +       /*
> +        * Because the buffer set to vop take effect at frame start time,
> +        * we need make sure old buffer is not in use before we release
> +        * it.
> +        * reference the framebuffer, and unference it when it swap out of vop.
> +        */
> +       if (fb != rockchip_plane->front_fb) {
> +               drm_framebuffer_reference(fb);
> +               if (rockchip_plane->pending_fb)
> +                       drm_framebuffer_unreference(rockchip_plane->pending_fb);
> +               rockchip_plane->pending_fb = fb;
> +               rockchip_plane->pending_yrgb_mst = yrgb_mst;
> +               ctx->vsync_work_pending = true;
> +       }
> +       rockchip_plane->enabled = true;
> +
> +       mutex_unlock(&ctx->vsync_mutex);
> +
> +       spin_lock(&ctx->reg_lock);
> +       vop_cfg_done(ctx);
> +       spin_unlock(&ctx->reg_lock);
> +
> +       return 0;
> +}
> +
> +#define rockchip_update_plane_helper(plane, crtc, fb, x, y) \
> +               rockchip_update_plane(plane, crtc, fb, 0, 0, \
> +                                     (fb)->width, (fb)->height, \
> +                                     (x) << 16, (y) << 16, \
> +                                     (fb)->width << 16, (fb)->height << 16)

(1) No.  This is using the framebuffer size.  We should be using the
crtc's mode's
dimensions to display the primary plane.
(2)  Also, please make this an inline function named
rockchip_update_primary_plane()

> +
> +static int rockchip_disable_plane(struct drm_plane *plane)
> +{
> +       struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
> +       const struct vop_win *win = rockchip_plane->win;
> +       struct vop_context *ctx;
> +
> +       if (!plane->crtc)
> +               return 0;
> +
> +       ctx = to_vop_ctx(plane->crtc);
> +       spin_lock(&ctx->reg_lock);
> +
> +       VOP_WIN_SET(ctx, win, enable, 0);
> +       vop_cfg_done(ctx);
> +
> +       spin_unlock(&ctx->reg_lock);
> +
> +       mutex_lock(&ctx->vsync_mutex);
> +
> +       /*
> +       * clear the pending framebuffer and set vsync_work_pending true,
> +       * so that the framebuffer will unref at the next vblank.
> +       */
> +       if (rockchip_plane->pending_fb) {
> +               drm_framebuffer_unreference(rockchip_plane->pending_fb);
> +               rockchip_plane->pending_fb = NULL;
> +       }
> +
> +       rockchip_plane->enabled = false;
> +       ctx->vsync_work_pending = true;
> +
> +       mutex_unlock(&ctx->vsync_mutex);
> +
> +       return 0;
> +}
> +
> +static void rockchip_plane_destroy(struct drm_plane *plane)
> +{
> +       struct rockchip_plane *rockchip_plane = to_rockchip_plane(plane);
> +       struct vop_context *ctx = to_vop_ctx(plane->crtc);
> +
> +       rockchip_disable_plane(plane);
> +       drm_plane_cleanup(plane);
> +       ctx->win_mask &= ~(1 << rockchip_plane->id);
> +       kfree(rockchip_plane);
> +}
> +
> +static const struct drm_plane_funcs rockchip_plane_funcs = {
> +       .update_plane = rockchip_update_plane,
> +       .disable_plane = rockchip_disable_plane,
> +       .destroy = rockchip_plane_destroy,
> +};
> +
> +static struct drm_plane *rockchip_plane_init(struct vop_context *ctx,
> +                                            unsigned long possible_crtcs,
> +                                            enum drm_plane_type type,
> +                                            int index)
> +{
> +       struct rockchip_plane *rockchip_plane;
> +       struct vop_driver_data *vop_data = ctx->data;
> +       const struct vop_win *win;
> +       int err;
> +
> +       if (index >= VOP_MAX_WIN_SUPPORT)
> +               return ERR_PTR(-EINVAL);
> +
> +       rockchip_plane = kzalloc(sizeof(*rockchip_plane), GFP_KERNEL);
> +       if (!rockchip_plane)
> +               return ERR_PTR(-ENOMEM);
> +
> +       win = vop_data->win[index];
> +       ctx->win_mask |= (1 << index);
> +       rockchip_plane->id = index;
> +       rockchip_plane->win = win;
> +
> +       err = drm_universal_plane_init(ctx->drm_dev, &rockchip_plane->base,
> +                                      possible_crtcs, &rockchip_plane_funcs,
> +                                      win->phy->data_formats,
> +                                      win->phy->nformats, type);
> +       if (err) {
> +               DRM_ERROR("failed to initialize plane\n");
> +               kfree(rockchip_plane);
> +               return ERR_PTR(err);
> +       }
> +
> +       return &rockchip_plane->base;
> +}
> +
> +int rockchip_drm_crtc_mode_config(struct drm_crtc *crtc, int connector_type)
> +{
> +       struct vop_context *ctx = to_vop_ctx(crtc);
> +
> +       if (ctx->dpms != DRM_MODE_DPMS_ON)
> +               return -EPERM;
> +
> +       switch (connector_type) {
> +       case DRM_MODE_CONNECTOR_LVDS:
> +               VOP_CTRL_SET(ctx, rgb_en, 1);
> +               VOP_CTRL_SET(ctx, out_mode, ROCKCHIP_OUT_MODE_P888);
> +               break;
> +       case DRM_MODE_CONNECTOR_eDP:
> +               VOP_CTRL_SET(ctx, edp_en, 1);
> +               VOP_CTRL_SET(ctx, out_mode, ROCKCHIP_OUT_MODE_AAAA);
> +               break;
> +       case DRM_MODE_CONNECTOR_HDMIA:
> +               VOP_CTRL_SET(ctx, out_mode, ROCKCHIP_OUT_MODE_AAAA);
> +               VOP_CTRL_SET(ctx, hdmi_en, 1);
> +               break;
> +       default:
> +               DRM_ERROR("unsupport connector_type[%d]\n", connector_type);
> +               return -EINVAL;
> +       };
> +
> +       return 0;
> +}
> +
> +int rockchip_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)
> +{
> +       struct vop_context *ctx = to_vop_ctx(rockchip_drm_find_crtc(dev, pipe));
> +       unsigned long flags;
> +
> +       if (ctx->dpms != DRM_MODE_DPMS_ON)
> +               return -EPERM;
> +
> +       spin_lock_irqsave(&ctx->irq_lock, flags);
> +
> +       vop_mask_write(ctx, INTR_CTRL0, LINE_FLAG_INTR_MASK,
> +                      LINE_FLAG_INTR_EN(1));
> +
> +       spin_unlock_irqrestore(&ctx->irq_lock, flags);
> +
> +       return 0;
> +}
> +
> +void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, int pipe)
> +{
> +       struct vop_context *ctx = to_vop_ctx(rockchip_drm_find_crtc(dev, pipe));
> +       unsigned long flags;
> +
> +       if (ctx->dpms != DRM_MODE_DPMS_ON)
> +               return;
> +       spin_lock_irqsave(&ctx->irq_lock, flags);
> +       vop_mask_write(ctx, INTR_CTRL0, LINE_FLAG_INTR_MASK,
> +                      LINE_FLAG_INTR_EN(0));
> +       spin_unlock_irqrestore(&ctx->irq_lock, flags);
> +}
> +
> +static void rockchip_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
> +{
> +       struct vop_context *ctx = to_vop_ctx(crtc);
> +
> +       DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
> +
> +       if (ctx->dpms == mode) {
> +               DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
> +               return;
> +       }
> +       if (mode > DRM_MODE_DPMS_ON) {
> +               /* wait for the completion of page flip. */
> +               if (!wait_event_timeout(ctx->wait_vsync_queue,
> +                                       !atomic_read(&ctx->wait_vsync_event),
> +                                       HZ/20))
> +                       DRM_DEBUG_KMS("vblank wait timed out.\n");
> +               drm_vblank_off(crtc->dev, ctx->pipe);
> +       }
> +
> +       switch (mode) {
> +       case DRM_MODE_DPMS_ON:
> +               vop_power_on(ctx);
> +               break;
> +       case DRM_MODE_DPMS_STANDBY:
> +       case DRM_MODE_DPMS_SUSPEND:
> +       case DRM_MODE_DPMS_OFF:
> +               vop_power_off(ctx);
> +               break;
> +       default:
> +               DRM_DEBUG_KMS("unspecified mode %d\n", mode);
> +               break;
> +       }
> +
> +       ctx->dpms = mode;
> +}
> +
> +static void rockchip_drm_crtc_prepare(struct drm_crtc *crtc)
> +{
> +       rockchip_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
> +}
> +
> +static bool rockchip_drm_crtc_mode_fixup(struct drm_crtc *crtc,
> +                                        const struct drm_display_mode *mode,
> +                                        struct drm_display_mode *adjusted_mode)
> +{
> +       if (adjusted_mode->htotal == 0 || adjusted_mode->vtotal == 0)
> +               return false;
> +
> +       return true;
> +}
> +
> +static int rockchip_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
> +                                          struct drm_framebuffer *old_fb);
> +
> +static int rockchip_drm_crtc_mode_set(struct drm_crtc *crtc,
> +                                     struct drm_display_mode *mode,
> +                                     struct drm_display_mode *adjusted_mode,
> +                                     int x, int y,
> +                                     struct drm_framebuffer *fb)
> +{
> +       struct vop_context *ctx = to_vop_ctx(crtc);
> +       u16 hsync_len = adjusted_mode->hsync_end - adjusted_mode->hsync_start;
> +       u16 hdisplay = adjusted_mode->hdisplay;
> +       u16 htotal = adjusted_mode->htotal;
> +       u16 hact_st = adjusted_mode->htotal - adjusted_mode->hsync_start;
> +       u16 hact_end = hact_st + hdisplay;
> +       u16 vdisplay = adjusted_mode->vdisplay;
> +       u16 vtotal = adjusted_mode->vtotal;
> +       u16 vsync_len = adjusted_mode->vsync_end - adjusted_mode->vsync_start;
> +       u16 vact_st = adjusted_mode->vtotal - adjusted_mode->vsync_start;
> +       u16 vact_end = vact_st + vdisplay;
> +       unsigned long flags;
> +       int ret;
> +       uint32_t val;
> +
> +       clk_disable(ctx->dclk);
> +
> +       ret = rockchip_drm_crtc_mode_set_base(crtc, x, y, fb);
> +       if (ret)
> +               return ret;
> +
> +       val = 0x8;
> +       val |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0;
> +       val |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? (1 << 1) : 0;
> +       VOP_CTRL_SET(ctx, pin_pol, val);
> +
> +       VOP_CTRL_SET(ctx, htotal_pw, (htotal << 16) | hsync_len);
> +       val = hact_st << 16;
> +       val |= hact_end;
> +       VOP_CTRL_SET(ctx, hact_st_end, val);
> +       VOP_CTRL_SET(ctx, hpost_st_end, val);
> +
> +       VOP_CTRL_SET(ctx, vtotal_pw, (vtotal << 16) | vsync_len);
> +       val = vact_st << 16;
> +       val |= vact_end;
> +       VOP_CTRL_SET(ctx, vact_st_end, val);
> +       VOP_CTRL_SET(ctx, vpost_st_end, val);
> +
> +       spin_lock_irqsave(&ctx->irq_lock, flags);
> +
> +       vop_mask_write(ctx, INTR_CTRL0, DSP_LINE_NUM_MASK,
> +                      DSP_LINE_NUM(vact_end));
> +
> +       spin_unlock_irqrestore(&ctx->irq_lock, flags);
> +
> +       /*
> +        * do dclk_reset, let all config take affect, so new clk will safely
> +        * run in correct frame.
> +        */
> +       reset_control_assert(ctx->dclk_rst);
> +       usleep_range(10, 20);
> +       reset_control_deassert(ctx->dclk_rst);
> +
> +       clk_set_rate(ctx->dclk, adjusted_mode->clock * 1000);
> +       clk_enable(ctx->dclk);
> +
> +       return 0;
> +}
> +
> +static int rockchip_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
> +                                          struct drm_framebuffer *old_fb)
> +{
> +       int ret;
> +
> +       ret = rockchip_update_plane_helper(crtc->primary, crtc,
> +                                          crtc->primary->fb, x, y);
> +       if (ret < 0) {
> +               DRM_ERROR("fail to update plane\n");
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static void rockchip_drm_crtc_commit(struct drm_crtc *crtc)
> +{
> +       /* just do dummy now */
> +}
> +
> +static const struct drm_crtc_helper_funcs rockchip_crtc_helper_funcs = {
> +       .dpms = rockchip_drm_crtc_dpms,
> +       .prepare = rockchip_drm_crtc_prepare,
> +       .mode_fixup = rockchip_drm_crtc_mode_fixup,
> +       .mode_set = rockchip_drm_crtc_mode_set,
> +       .mode_set_base = rockchip_drm_crtc_mode_set_base,
> +       .commit = rockchip_drm_crtc_commit,
> +};
> +
> +static int rockchip_drm_crtc_page_flip(struct drm_crtc *crtc,
> +                                      struct drm_framebuffer *fb,
> +                                      struct drm_pending_vblank_event *event,
> +                                      uint32_t page_flip_flags)
> +{
> +       struct drm_device *dev = crtc->dev;
> +       struct vop_context *ctx = to_vop_ctx(crtc);
> +       struct drm_framebuffer *old_fb = crtc->primary->fb;
> +       int ret;
> +
> +       /* when the page flip is requested, crtc's dpms should be on */
> +       if (ctx->dpms > DRM_MODE_DPMS_ON) {
> +               DRM_DEBUG("failed page flip request at dpms[%d].\n", ctx->dpms);
> +               return 0;
> +       }
> +
> +       ret = drm_vblank_get(dev, ctx->pipe);
> +       if (ret) {
> +               DRM_DEBUG("failed to acquire vblank counter\n");
> +               return ret;
> +       }
> +
> +       spin_lock_irq(&dev->event_lock);
> +       if (ctx->event) {
> +               spin_unlock_irq(&dev->event_lock);
> +               DRM_ERROR("already pending flip!\n");
> +               return -EBUSY;
> +       }
> +       ctx->event = event;
> +       atomic_set(&ctx->wait_vsync_event, 1);
> +       spin_unlock_irq(&dev->event_lock);
> +
> +       crtc->primary->fb = fb;
> +
> +       ret = rockchip_update_plane_helper(crtc->primary, crtc, fb,
> +                                          crtc->x, crtc->y);
> +       if (ret) {
> +               crtc->primary->fb = old_fb;
> +
> +               spin_lock_irq(&dev->event_lock);
> +               drm_vblank_put(dev, ctx->pipe);
> +               atomic_set(&ctx->wait_vsync_event, 0);
> +               ctx->event = NULL;
> +               spin_unlock_irq(&dev->event_lock);
> +       }
> +
> +       return ret;
> +}
> +
> +static void rockchip_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe)
> +{
> +       struct rockchip_drm_private *dev_priv = dev->dev_private;
> +       struct drm_crtc *drm_crtc = dev_priv->crtc[pipe];
> +       struct vop_context *ctx;
> +       unsigned long flags;
> +
> +       ctx = to_vop_ctx(drm_crtc);
> +
> +       spin_lock_irqsave(&dev->event_lock, flags);
> +
> +       if (ctx->event) {
> +               drm_send_vblank_event(dev, -1, ctx->event);
> +               drm_vblank_put(dev, pipe);
> +               atomic_set(&ctx->wait_vsync_event, 0);
> +               wake_up(&ctx->wait_vsync_queue);
> +               ctx->event = NULL;
> +       }
> +
> +       spin_unlock_irqrestore(&dev->event_lock, flags);
> +}
> +
> +static void rockchip_drm_crtc_destroy(struct drm_crtc *crtc)
> +{
> +       struct vop_context *ctx = to_vop_ctx(crtc);
> +       struct rockchip_drm_private *private = crtc->dev->dev_private;
> +
> +       private->crtc[ctx->pipe] = NULL;
> +       drm_crtc_cleanup(crtc);
> +}
> +
> +static const struct drm_crtc_funcs rockchip_crtc_funcs = {
> +       .set_config = drm_crtc_helper_set_config,
> +       .page_flip = rockchip_drm_crtc_page_flip,
> +       .destroy = rockchip_drm_crtc_destroy,
> +};
> +
> +static void rockchip_vsync_worker(struct work_struct *work)
> +{
> +       struct vop_context *ctx = container_of(work, struct vop_context,
> +                                              vsync_work);
> +       struct drm_device *drm = ctx->drm_dev;
> +       struct rockchip_drm_private *dev_priv = drm->dev_private;
> +       struct drm_crtc *crtc = dev_priv->crtc[ctx->pipe];
> +       struct rockchip_plane *rockchip_plane;
> +       struct drm_plane *plane;
> +       uint32_t yrgb_mst;
> +
> +       mutex_lock(&ctx->vsync_mutex);
> +
> +       ctx->vsync_work_pending = false;
> +
> +       list_for_each_entry(plane, &drm->mode_config.plane_list, head) {
> +               rockchip_plane = to_rockchip_plane(plane);
> +
> +               if (to_vop_ctx(plane->crtc) != ctx)
> +                       continue;
> +               if (rockchip_plane->enabled && !rockchip_plane->pending_fb)
> +                       continue;
> +               if (!rockchip_plane->enabled && !rockchip_plane->front_fb)
> +                       continue;
> +               /*
> +                * make sure the yrgb_mst take effect, so that
> +                * we can unreference the old framebuffer.
> +                */
> +               yrgb_mst = VOP_WIN_GET_YRGBADDR(ctx, rockchip_plane->win);
> +               if (rockchip_plane->pending_yrgb_mst != yrgb_mst) {
> +                       /*
> +                        * some plane no complete, unref at next vblank
> +                        */
> +                       ctx->vsync_work_pending = true;
> +                       continue;
> +               }
> +
> +               /*
> +                * drm_framebuffer_unreference maybe call iommu unmap,
> +                * and iommu not allow unmap buffer at irq context,
> +                * so we do drm_framebuffer_unreference at queue_work.
> +                */
> +               if (rockchip_plane->front_fb)
> +                       drm_framebuffer_unreference(rockchip_plane->front_fb);
> +
> +               rockchip_plane->front_fb = rockchip_plane->pending_fb;
> +               rockchip_plane->pending_fb = NULL;
> +
> +               /*
> +                * if primary plane flip complete, sending the event to
> +                * userspace
> +                */
> +               if (&rockchip_plane->base == crtc->primary)
> +                       rockchip_drm_crtc_finish_pageflip(ctx->drm_dev,
> +                                                         ctx->pipe);
> +       }
> +
> +       mutex_unlock(&ctx->vsync_mutex);
> +}
> +
> +static irqreturn_t rockchip_vop_isr(int irq, void *data)
> +{
> +       struct vop_context *ctx = data;
> +       uint32_t intr0_reg;
> +       unsigned long flags;
> +
> +       intr0_reg = vop_readl(ctx, INTR_CTRL0);
> +       if (intr0_reg & LINE_FLAG_INTR) {
> +               spin_lock_irqsave(&ctx->irq_lock, flags);
> +               vop_writel(ctx, INTR_CTRL0, intr0_reg | LINE_FLAG_INTR_CLR);
> +               spin_unlock_irqrestore(&ctx->irq_lock, flags);
> +       } else {
> +               return IRQ_NONE;
> +       }
> +
> +       drm_handle_vblank(ctx->drm_dev, ctx->pipe);
> +       if (ctx->vsync_work_pending)
> +               queue_work(ctx->vsync_wq, &ctx->vsync_work);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static int vop_create_crtc(struct vop_context *ctx)
> +{
> +       struct device *dev = ctx->dev;
> +       struct drm_device *drm_dev = ctx->drm_dev;
> +       struct drm_plane *primary, *cursor;
> +       unsigned long possible_crtcs;
> +       struct drm_crtc *crtc;
> +       int ret;
> +       int nr;
> +
> +       ctx->win_mask = 0;
> +       crtc = &ctx->crtc;
> +
> +       ret = rockchip_drm_add_crtc(drm_dev, crtc, dev->of_node);
> +       if (ret < 0)
> +               return ret;
> +       ctx->pipe = ret;
> +
> +       possible_crtcs = (1 << ctx->pipe);
> +
> +       for (nr = 0; nr < VOP_MAX_WIN_SUPPORT; nr++) {
> +               if (nr == VOP_DEFAULT_PRIMARY) {
> +                       primary = rockchip_plane_init(ctx, possible_crtcs,
> +                                                     DRM_PLANE_TYPE_PRIMARY,
> +                                                     VOP_DEFAULT_PRIMARY);
> +                       if (IS_ERR(primary)) {
> +                               DRM_ERROR("fail to init primary plane\n");
> +                               return PTR_ERR(primary);
> +                       }
> +               } else if (nr == VOP_DEFAULT_CURSOR) {
> +                       cursor = rockchip_plane_init(ctx, possible_crtcs,
> +                                                    DRM_PLANE_TYPE_CURSOR,
> +                                                    VOP_DEFAULT_CURSOR);
> +                       if (IS_ERR(cursor)) {
> +                               DRM_ERROR("fail to init cursor plane\n");
> +                               return PTR_ERR(cursor);
> +                       }
> +               } else {
> +                       struct drm_plane *plane;
> +
> +                       plane = rockchip_plane_init(ctx, possible_crtcs,
> +                                                   DRM_PLANE_TYPE_OVERLAY,
> +                                                   nr);
> +                       if (IS_ERR(plane)) {
> +                               DRM_ERROR("fail to init overlay plane\n");
> +                               return PTR_ERR(plane);
> +                       }
> +               }
> +       }
> +
> +       drm_crtc_init_with_planes(drm_dev, crtc, primary, cursor,
> +                                 &rockchip_crtc_funcs);
> +       drm_crtc_helper_add(crtc, &rockchip_crtc_helper_funcs);
> +
> +       return 0;
> +}
> +
> +static int rockchip_vop_initial(struct vop_context *ctx)
> +{
> +       struct vop_driver_data *vop_data = ctx->data;
> +       const struct vop_reg_data *init_table = vop_data->init_table;
> +       struct reset_control *ahb_rst;
> +       int i, ret;
> +
> +       ctx->hclk = devm_clk_get(ctx->dev, "hclk_vop");
> +       if (IS_ERR(ctx->hclk)) {
> +               dev_err(ctx->dev, "failed to get hclk source\n");
> +               return PTR_ERR(ctx->hclk);
> +       }
> +       ctx->aclk = devm_clk_get(ctx->dev, "aclk_vop");
> +       if (IS_ERR(ctx->aclk)) {
> +               dev_err(ctx->dev, "failed to get aclk source\n");
> +               return PTR_ERR(ctx->aclk);
> +       }
> +       ctx->dclk = devm_clk_get(ctx->dev, "dclk_vop");
> +       if (IS_ERR(ctx->dclk)) {
> +               dev_err(ctx->dev, "failed to get dclk source\n");
> +               return PTR_ERR(ctx->dclk);
> +       }
> +
> +       ret = clk_prepare(ctx->hclk);
> +       if (ret < 0) {
> +               dev_err(ctx->dev, "failed to prepare hclk\n");
> +               return ret;
> +       }
> +
> +       ret = clk_prepare(ctx->dclk);
> +       if (ret < 0) {
> +               dev_err(ctx->dev, "failed to prepare dclk\n");
> +               goto err_prepare_hclk;

goto err_unprepare_hclk;

> +       }
> +
> +       ret = clk_prepare(ctx->aclk);
> +       if (ret < 0) {
> +               dev_err(ctx->dev, "failed to prepare aclk\n");
> +               goto err_prepare_dclk;

goto err_unprepare_dclk;

> +       }
> +
> +       ret = vop_clk_enable(ctx);
> +       if (ret < 0)
> +               goto err_prepare_aclk;

goto err_unprepare_aclk;

> +
> +       /*
> +        * do hclk_reset, reset all vop registers.
> +        */
> +       ahb_rst = devm_reset_control_get(ctx->dev, "ahb");
> +       if (IS_ERR(ahb_rst)) {
> +               dev_err(ctx->dev, "failed to get ahb reset\n");
> +               ret = PTR_ERR(ahb_rst);
> +               goto err_diable_clk;
> +       }
> +       reset_control_assert(ahb_rst);
> +       usleep_range(10, 20);
> +       reset_control_deassert(ahb_rst);
> +
> +       memcpy(ctx->regsbak, ctx->regs, ctx->len);
> +
> +       for (i = 0; i < vop_data->table_size; i++)
> +               vop_writel(ctx, init_table[i].offset, init_table[i].value);
> +
> +       for (i = 0; i < VOP_MAX_WIN_SUPPORT; i++)
> +               VOP_WIN_SET(ctx, vop_data->win[i], enable, 0);
> +
> +       vop_cfg_done(ctx);
> +
> +       /*
> +        * do dclk_reset, let all win config take affect, and then we can enable
> +        * iommu safe.
> +        */
> +       ctx->dclk_rst = devm_reset_control_get(ctx->dev, "dclk");
> +       if (IS_ERR(ctx->dclk_rst)) {
> +               dev_err(ctx->dev, "failed to get dclk reset\n");
> +               ret = PTR_ERR(ctx->dclk_rst);
> +               goto err_diable_clk;

goto err_disable_clk;

> +       }
> +       reset_control_assert(ctx->dclk_rst);
> +       usleep_range(10, 20);
> +       reset_control_deassert(ctx->dclk_rst);
> +
> +       ctx->dpms = DRM_MODE_DPMS_ON;

The reset at probe time is probably a good idea.

However, this leaves the VOP enabled and clocking at...  but what will it be
displaying, and what mode will it use?

Perhaps we should delay enabling clocks / dpms until the first mode set /
dpms(DPMS_ON) request?

> +
> +       return 0;
> +
> +err_prepare_aclk:
> +       clk_unprepare(ctx->aclk);
> +err_prepare_dclk:
> +       clk_unprepare(ctx->dclk);
> +err_prepare_hclk:
> +       clk_unprepare(ctx->hclk);
> +err_diable_clk:
> +       vop_clk_disable(ctx);
> +       return ret;

err_disable_clk:
       vop_clk_disable(ctx);
err_unprepare_aclk:
       clk_unprepare(ctx->aclk);
err_unprepare_dclk:
       clk_unprepare(ctx->dclk);
err_unprepare_hclk:
       clk_unprepare(ctx->hclk);
       return ret;

That's it for now!

-djk


> +}
> +
> +static int vop_bind(struct device *dev, struct device *master, void *data)
> +{
> +       struct platform_device *pdev = to_platform_device(dev);
> +       struct vop_driver_data *vop_data = vop_get_driver_data(dev);
> +       struct drm_device *drm_dev = data;
> +       struct vop_context *ctx;
> +       struct resource *res;
> +       int ret;
> +
> +       if (!vop_data)
> +               return -ENODEV;
> +
> +       ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
> +       if (!ctx)
> +               return -ENOMEM;
> +
> +       ctx->dev = dev;
> +       ctx->data = vop_data;
> +       ctx->drm_dev = drm_dev;
> +       dev_set_drvdata(dev, ctx);
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       ctx->len = resource_size(res);
> +       ctx->regs = devm_ioremap_resource(dev, res);
> +       if (IS_ERR(ctx->regs))
> +               return PTR_ERR(ctx->regs);
> +
> +       ctx->regsbak = devm_kzalloc(dev, ctx->len, GFP_KERNEL);
> +       if (!ctx->regsbak)
> +               return -ENOMEM;
> +
> +       ret = rockchip_vop_initial(ctx);
> +       if (ret < 0) {
> +               dev_err(&pdev->dev, "cannot initial vop dev - err %d\n", ret);
> +               return ret;
> +       }
> +
> +       ctx->irq = platform_get_irq(pdev, 0);
> +       if (ctx->irq < 0) {
> +               dev_err(dev, "cannot find irq for vop\n");
> +               return ctx->irq;
> +       }
> +
> +       spin_lock_init(&ctx->reg_lock);
> +       spin_lock_init(&ctx->irq_lock);
> +
> +       init_waitqueue_head(&ctx->wait_vsync_queue);
> +       atomic_set(&ctx->wait_vsync_event, 0);
> +
> +       ret = vop_iommu_init(ctx);
> +       if (ret) {
> +               DRM_ERROR("Failed to setup iommu, %d\n", ret);
> +               return ret;
> +       }
> +
> +       ctx->vsync_wq = create_singlethread_workqueue("vsync");
> +       if (!ctx->vsync_wq) {
> +               dev_err(dev, "failed to create workqueue\n");
> +               return -EINVAL;
> +       }
> +       INIT_WORK(&ctx->vsync_work, rockchip_vsync_worker);
> +
> +       mutex_init(&ctx->vsync_mutex);
> +       pm_runtime_enable(&pdev->dev);
> +
> +       ret = devm_request_irq(dev, ctx->irq, rockchip_vop_isr,
> +                              IRQF_SHARED, dev_name(dev), ctx);
> +       if (ret) {
> +               dev_err(dev, "cannot requeset irq%d - err %d\n", ctx->irq, ret);
> +               return ret;
> +       }
> +
> +       return vop_create_crtc(ctx);
> +}
> +
> +static void vop_unbind(struct device *dev, struct device *master,
> +                      void *data)
> +{
> +       struct drm_device *drm_dev = data;
> +       struct vop_context *ctx = dev_get_drvdata(dev);
> +       struct drm_crtc *crtc = &ctx->crtc;
> +
> +       drm_crtc_cleanup(crtc);
> +       pm_runtime_disable(dev);
> +       rockchip_drm_remove_crtc(drm_dev, ctx->pipe);
> +
> +       vop_iommu_fini(ctx);
> +}
> +
> +static const struct component_ops vop_component_ops = {
> +       .bind = vop_bind,
> +       .unbind = vop_unbind,
> +};
> +
> +static int vop_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct vop_context *ctx;
> +
> +       if (!dev->of_node) {
> +               dev_err(dev, "can't find vop devices\n");
> +               return -ENODEV;
> +       }
> +
> +       platform_set_drvdata(pdev, ctx);
> +
> +       return component_add(dev, &vop_component_ops);
> +}
> +
> +static int vop_remove(struct platform_device *pdev)
> +{
> +       component_del(&pdev->dev, &vop_component_ops);
> +
> +       return 0;
> +}
> +
> +struct platform_driver rockchip_vop_platform_driver = {
> +       .probe = vop_probe,
> +       .remove = vop_remove,
> +       .driver = {
> +               .name = "rockchip-vop",
> +               .owner = THIS_MODULE,
> +               .of_match_table = of_match_ptr(vop_driver_dt_match),
> +       },
> +};
> +
> +module_platform_driver(rockchip_vop_platform_driver);
> +
> +MODULE_AUTHOR("Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>");
> +MODULE_DESCRIPTION("ROCKCHIP VOP Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> new file mode 100644
> index 0000000..d2a04f0
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> @@ -0,0 +1,196 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:Mark Yao <mark.yao-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef _ROCKCHIP_DRM_VOP_H
> +#define _ROCKCHIP_DRM_VOP_H
> +
> +/* register definition */
> +#define REG_CFG_DONE                   0x0000
> +#define VERSION_INFO                   0x0004
> +#define SYS_CTRL                       0x0008
> +#define SYS_CTRL1                      0x000c
> +#define DSP_CTRL0                      0x0010
> +#define DSP_CTRL1                      0x0014
> +#define DSP_BG                         0x0018
> +#define MCU_CTRL                       0x001c
> +#define INTR_CTRL0                     0x0020
> +#define INTR_CTRL1                     0x0024
> +#define WIN0_CTRL0                     0x0030
> +#define WIN0_CTRL1                     0x0034
> +#define WIN0_COLOR_KEY                 0x0038
> +#define WIN0_VIR                       0x003c
> +#define WIN0_YRGB_MST                  0x0040
> +#define WIN0_CBR_MST                   0x0044
> +#define WIN0_ACT_INFO                  0x0048
> +#define WIN0_DSP_INFO                  0x004c
> +#define WIN0_DSP_ST                    0x0050
> +#define WIN0_SCL_FACTOR_YRGB           0x0054
> +#define WIN0_SCL_FACTOR_CBR            0x0058
> +#define WIN0_SCL_OFFSET                        0x005c
> +#define WIN0_SRC_ALPHA_CTRL            0x0060
> +#define WIN0_DST_ALPHA_CTRL            0x0064
> +#define WIN0_FADING_CTRL               0x0068
> +/* win1 register */
> +#define WIN1_CTRL0                     0x0070
> +#define WIN1_CTRL1                     0x0074
> +#define WIN1_COLOR_KEY                 0x0078
> +#define WIN1_VIR                       0x007c
> +#define WIN1_YRGB_MST                  0x0080
> +#define WIN1_CBR_MST                   0x0084
> +#define WIN1_ACT_INFO                  0x0088
> +#define WIN1_DSP_INFO                  0x008c
> +#define WIN1_DSP_ST                    0x0090
> +#define WIN1_SCL_FACTOR_YRGB           0x0094
> +#define WIN1_SCL_FACTOR_CBR            0x0098
> +#define WIN1_SCL_OFFSET                        0x009c
> +#define WIN1_SRC_ALPHA_CTRL            0x00a0
> +#define WIN1_DST_ALPHA_CTRL            0x00a4
> +#define WIN1_FADING_CTRL               0x00a8
> +/* win2 register */
> +#define WIN2_CTRL0                     0x00b0
> +#define WIN2_CTRL1                     0x00b4
> +#define WIN2_VIR0_1                    0x00b8
> +#define WIN2_VIR2_3                    0x00bc
> +#define WIN2_MST0                      0x00c0
> +#define WIN2_DSP_INFO0                 0x00c4
> +#define WIN2_DSP_ST0                   0x00c8
> +#define WIN2_COLOR_KEY                 0x00cc
> +#define WIN2_MST1                      0x00d0
> +#define WIN2_DSP_INFO1                 0x00d4
> +#define WIN2_DSP_ST1                   0x00d8
> +#define WIN2_SRC_ALPHA_CTRL            0x00dc
> +#define WIN2_MST2                      0x00e0
> +#define WIN2_DSP_INFO2                 0x00e4
> +#define WIN2_DSP_ST2                   0x00e8
> +#define WIN2_DST_ALPHA_CTRL            0x00ec
> +#define WIN2_MST3                      0x00f0
> +#define WIN2_DSP_INFO3                 0x00f4
> +#define WIN2_DSP_ST3                   0x00f8
> +#define WIN2_FADING_CTRL               0x00fc
> +/* win3 register */
> +#define WIN3_CTRL0                     0x0100
> +#define WIN3_CTRL1                     0x0104
> +#define WIN3_VIR0_1                    0x0108
> +#define WIN3_VIR2_3                    0x010c
> +#define WIN3_MST0                      0x0110
> +#define WIN3_DSP_INFO0                 0x0114
> +#define WIN3_DSP_ST0                   0x0118
> +#define WIN3_COLOR_KEY                 0x011c
> +#define WIN3_MST1                      0x0120
> +#define WIN3_DSP_INFO1                 0x0124
> +#define WIN3_DSP_ST1                   0x0128
> +#define WIN3_SRC_ALPHA_CTRL            0x012c
> +#define WIN3_MST2                      0x0130
> +#define WIN3_DSP_INFO2                 0x0134
> +#define WIN3_DSP_ST2                   0x0138
> +#define WIN3_DST_ALPHA_CTRL            0x013c
> +#define WIN3_MST3                      0x0140
> +#define WIN3_DSP_INFO3                 0x0144
> +#define WIN3_DSP_ST3                   0x0148
> +#define WIN3_FADING_CTRL               0x014c
> +/* hwc register */
> +#define HWC_CTRL0                      0x0150
> +#define HWC_CTRL1                      0x0154
> +#define HWC_MST                                0x0158
> +#define HWC_DSP_ST                     0x015c
> +#define HWC_SRC_ALPHA_CTRL             0x0160
> +#define HWC_DST_ALPHA_CTRL             0x0164
> +#define HWC_FADING_CTRL                        0x0168
> +/* post process register */
> +#define POST_DSP_HACT_INFO             0x0170
> +#define POST_DSP_VACT_INFO             0x0174
> +#define POST_SCL_FACTOR_YRGB           0x0178
> +#define POST_SCL_CTRL                  0x0180
> +#define POST_DSP_VACT_INFO_F1          0x0184
> +#define DSP_HTOTAL_HS_END              0x0188
> +#define DSP_HACT_ST_END                        0x018c
> +#define DSP_VTOTAL_VS_END              0x0190
> +#define DSP_VACT_ST_END                        0x0194
> +#define DSP_VS_ST_END_F1               0x0198
> +#define DSP_VACT_ST_END_F1             0x019c
> +/* register definition end */
> +
> +/* interrupt define */
> +#define DSP_HOLD_VALID_INTR            (1 << 0)
> +#define FS_INTR                                (1 << 1)
> +#define LINE_FLAG_INTR                 (1 << 2)
> +#define BUS_ERROR_INTR                 (1 << 3)
> +
> +#define DSP_HOLD_VALID_INTR_EN(x)      ((x) << 4)
> +#define FS_INTR_EN(x)                  ((x) << 5)
> +#define LINE_FLAG_INTR_EN(x)           ((x) << 6)
> +#define BUS_ERROR_INTR_EN(x)           ((x) << 7)
> +#define DSP_HOLD_VALID_INTR_MASK       (1 << 4)
> +#define FS_INTR_EN_MASK                        (1 << 5)
> +#define LINE_FLAG_INTR_MASK            (1 << 6)
> +#define BUS_ERROR_INTR_MASK            (1 << 7)
> +
> +#define DSP_HOLD_VALID_INTR_CLR                (1 << 8)
> +#define FS_INTR_EN_CLR                 (1 << 9)
> +#define LINE_FLAG_INTR_CLR             (1 << 10)
> +#define BUS_ERROR_INTR_CLR             (1 << 11)
> +#define DSP_LINE_NUM(x)                        (((x) & 0x1fff) << 12)
> +#define DSP_LINE_NUM_MASK              (0x1fff << 12)
> +
> +/* src alpha ctrl define */
> +#define SRC_FADING_VALUE(x)            (((x) & 0xff) << 24)
> +#define SRC_GLOBAL_ALPHA(x)            (((x) & 0xff) << 16)
> +#define SRC_FACTOR_M0(x)               (((x) & 0x7) << 6)
> +#define SRC_ALPHA_CAL_M0(x)            (((x) & 0x1) << 5)
> +#define SRC_BLEND_M0(x)                        (((x) & 0x3) << 3)
> +#define SRC_ALPHA_M0(x)                        (((x) & 0x1) << 2)
> +#define SRC_COLOR_M0(x)                        (((x) & 0x1) << 1)
> +#define SRC_ALPHA_EN(x)                        (((x) & 0x1) << 0)
> +/* dst alpha ctrl define */
> +#define DST_FACTOR_M0(x)               (((x) & 0x7) << 6)
> +
> +/*
> + * display output interface supported by rockchip lcdc
> + */
> +#define ROCKCHIP_OUT_MODE_P888 0
> +#define ROCKCHIP_OUT_MODE_P666 1
> +#define ROCKCHIP_OUT_MODE_P565 2
> +/* for use special outface */
> +#define ROCKCHIP_OUT_MODE_AAAA 15
> +
> +enum alpha_mode {
> +       ALPHA_STRAIGHT,
> +       ALPHA_INVERSE,
> +};
> +
> +enum global_blend_mode {
> +       ALPHA_GLOBAL,
> +       ALPHA_PER_PIX,
> +       ALPHA_PER_PIX_GLOBAL,
> +};
> +
> +enum alpha_cal_mode {
> +       ALPHA_SATURATION,
> +       ALPHA_NO_SATURATION,
> +};
> +
> +enum color_mode {
> +       ALPHA_SRC_PRE_MUL,
> +       ALPHA_SRC_NO_PRE_MUL,
> +};
> +
> +enum factor_mode {
> +       ALPHA_ZERO,
> +       ALPHA_ONE,
> +       ALPHA_SRC,
> +       ALPHA_SRC_INVERSE,
> +       ALPHA_SRC_GLOBAL,
> +};
> +
> +#endif /* _ROCKCHIP_DRM_VOP_H */
> --
> 1.7.9.5
>
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCHv8.1] fanotify: enable close-on-exec on events' fd when requested in fanotify_init()
From: Yann Droneaud @ 2014-09-29  8:49 UTC (permalink / raw)
  To: Heinrich Schuchardt, Andrew Morton
  Cc: Yann Droneaud, Eric Paris, Richard Guy Briggs, Al Viro,
	linux-kernel, linux-fsdevel, stable, linux-api, Jan Kara,
	Lino Sanfilippo, Valdis Kletnieks, Michael Kerrisk-manpages
In-Reply-To: <542666B2.9080700@gmx.de>

According to commit 80af258867648 ('fanotify: groups can specify
their f_flags for new fd'), file descriptors created as part of
file access notification events inherit flags from the
event_f_flags argument passed to syscall fanotify_init(2).

So while it is legal for userspace to call fanotify_init() with
O_CLOEXEC as part of its second argument, O_CLOEXEC is currently
silently ignored.

Indeed event_f_flags are only given to dentry_open(), which only
seems to care about O_ACCMODE and O_PATH in do_dentry_open(),
O_DIRECT in open_check_o_direct() and O_LARGEFILE in
generic_file_open().

But it seems logical to set close-on-exec flag on the file
descriptor if userspace is allowed to request it with O_CLOEXEC.

In fact, according to some lookup on http://codesearch.debian.net/
and various search engine, there's already some userspace code
requesting it:

- in systemd's readahead[2]:

    fanotify_fd = fanotify_init(FAN_CLOEXEC|FAN_NONBLOCK, O_RDONLY|O_LARGEFILE|O_CLOEXEC|O_NOATIME);

- in clsync[3]:

    #define FANOTIFY_EVFLAGS (O_LARGEFILE|O_RDONLY|O_CLOEXEC)

    int fanotify_d = fanotify_init(FANOTIFY_FLAGS, FANOTIFY_EVFLAGS);

- in examples [4] from "Filesystem monitoring in the Linux
  kernel" article[5] by Aleksander Morgado:

    if ((fanotify_fd = fanotify_init (FAN_CLOEXEC,
                                      O_RDONLY | O_CLOEXEC | O_LARGEFILE)) < 0)

Lookup also returned some wrong usage of the syscall:

- in Gonk HAL from Mozilla Firefox OS sources[6]:

    mFd = fanotify_init(FAN_CLASS_NOTIF, FAN_CLOEXEC);

Adding support for O_CLOEXEC in fanotify_init() won't magically
enable it for Gonk since FAN_CLOEXEC is defined as 0x1, which
is likely equal to O_WRONLY when used in open flag context. In
the other hand, it won't hurt it either.

So this patch replaces call to macro get_unused_fd() by a call
to function get_unused_fd_flags() with event_f_flags value as
argument. This way O_CLOEXEC flag in the second argument of
fanotify_init(2) syscall is interpreted so that close-on-exec
get enabled when requested.

[1] http://blog.ffwll.ch/2013/11/botching-up-ioctls.html
[2] http://cgit.freedesktop.org/systemd/systemd/tree/src/readahead/readahead-collect.c?id=v208#n294
[3] https://github.com/xaionaro/clsync/blob/v0.2.1/sync.c#L1631
    https://github.com/xaionaro/clsync/blob/v0.2.1/configuration.h#L38
[4] http://www.lanedo.com/~aleksander/fanotify/fanotify-example.c
[5] http://www.lanedo.com/2013/filesystem-monitoring-linux-kernel/
[6] http://hg.mozilla.org/mozilla-central/file/325c74addeba/hal/gonk/GonkDiskSpaceWatcher.cpp#l167

Link: http://lkml.kernel.org/r/cover.1394532336.git.ydroneaud@opteya.com
Cc: Heinrich Schuchardt <xypron.glpk@gmx.de>
Cc: Jan Kara <jack@suse.cz>
Cc: Valdis Kletnieks <Valdis.Kletnieks@vt.edu>
Cc: Michael Kerrisk-manpages <mtk.manpages@gmail.com>
Cc: Lino Sanfilippo <LinoSanfilippo@gmx.de>
Cc: Richard Guy Briggs <rgb@redhat.com>
Cc: Eric Paris <eparis@redhat.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: stable@vger.kernel.org
Cc: linux-api@vger.kernel.org
Reviewed by: Heinrich Schuchardt <xypron.glpk@gmx.de>
Signed-off-by: Yann Droneaud <ydroneaud@opteya.com>
---
Hi Andrew and Henrich,

Please find an updated patch with a commit message fixed regarding
the obsolote comments on code which is now updated, thanks to
Heinrich's patch.

Changes from v8:
- fixed commit message
- added Reviewed-by:

Regards.

Yann Droneaud,
OPTEYA

 fs/notify/fanotify/fanotify_user.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index b13992a41bd9..c991616acca9 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -78,7 +78,7 @@ static int create_fd(struct fsnotify_group *group,
 
 	pr_debug("%s: group=%p event=%p\n", __func__, group, event);
 
-	client_fd = get_unused_fd();
+	client_fd = get_unused_fd_flags(group->fanotify_data.f_flags);
 	if (client_fd < 0)
 		return client_fd;
 
-- 
1.9.3

^ permalink raw reply related

* Re: [PATCHv7 10/26] vfio: platform: probe to devices on the platform bus
From: Antonios Motakis @ 2014-09-29 10:21 UTC (permalink / raw)
  To: Alex Williamson
  Cc: KVM devel mailing list, Eric Auger, Marc Zyngier,
	open list:ABI/API, Will Deacon, open list, Linux IOMMU,
	VirtualOpenSystems Technical Team, kvm-arm, Christoffer Dall
In-Reply-To: <1411762736.7360.42.camel-85EaTFmN5p//9pzu0YdTqQ@public.gmane.org>

On Fri, Sep 26, 2014 at 10:18 PM, Alex Williamson
<alex.williamson-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> wrote:
> On Fri, 2014-09-26 at 17:30 +0200, Antonios Motakis wrote:
>> On Wed, Sep 24, 2014 at 1:01 AM, Alex Williamson
>> <alex.williamson-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> wrote:
>> >
>> > On Tue, 2014-09-23 at 16:46 +0200, Antonios Motakis wrote:
>> > > Driver to bind to Linux platform devices, and callbacks to discover their
>> > > resources to be used by the main VFIO PLATFORM code.
>> > >
>> > > Signed-off-by: Antonios Motakis <a.motakis-lrHrjnjw1UfHK3s98zE1ajGjJy/sRE9J@public.gmane.org>
>> > > ---
>> > >  drivers/vfio/platform/vfio_platform.c | 96 +++++++++++++++++++++++++++++++++++
>> > >  include/uapi/linux/vfio.h             |  1 +
>> > >  2 files changed, 97 insertions(+)
>> > >  create mode 100644 drivers/vfio/platform/vfio_platform.c
>> > >
>> > > diff --git a/drivers/vfio/platform/vfio_platform.c b/drivers/vfio/platform/vfio_platform.c
>> > > new file mode 100644
>> > > index 0000000..024c026
>> > > --- /dev/null
>> > > +++ b/drivers/vfio/platform/vfio_platform.c
>> > > @@ -0,0 +1,96 @@
>> > > +/*
>> > > + * Copyright (C) 2013 - Virtual Open Systems
>> > > + * Author: Antonios Motakis <a.motakis-lrHrjnjw1UfHK3s98zE1ajGjJy/sRE9J@public.gmane.org>
>> > > + *
>> > > + * This program is free software; you can redistribute it and/or modify
>> > > + * it under the terms of the GNU General Public License, version 2, as
>> > > + * published by the Free Software Foundation.
>> > > + *
>> > > + * This program is distributed in the hope that it will be useful,
>> > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> > > + * GNU General Public License for more details.
>> > > + */
>> > > +
>> > > +#include <linux/device.h>
>> > > +#include <linux/eventfd.h>
>> > > +#include <linux/interrupt.h>
>> > > +#include <linux/iommu.h>
>> > > +#include <linux/module.h>
>> > > +#include <linux/mutex.h>
>> > > +#include <linux/notifier.h>
>> > > +#include <linux/pm_runtime.h>
>> > > +#include <linux/slab.h>
>> > > +#include <linux/types.h>
>> > > +#include <linux/uaccess.h>
>> > > +#include <linux/vfio.h>
>> > > +#include <linux/io.h>
>> > > +#include <linux/platform_device.h>
>> > > +#include <linux/irq.h>
>> > > +
>> > > +#include "vfio_platform_private.h"
>> > > +
>> > > +#define DRIVER_VERSION  "0.7"
>> > > +#define DRIVER_AUTHOR   "Antonios Motakis <a.motakis-lrHrjnjw1UfHK3s98zE1ajGjJy/sRE9J@public.gmane.org>"
>> > > +#define DRIVER_DESC     "VFIO for platform devices - User Level meta-driver"
>> > > +
>> > > +/* probing devices from the linux platform bus */
>> > > +
>> > > +static struct resource *get_platform_resource(struct vfio_platform_device *vdev,
>> > > +                                             int i)
>> > > +{
>> > > +     struct platform_device *pdev = (struct platform_device *) vdev->opaque;
>> > > +
>> > > +     return platform_get_resource(pdev, IORESOURCE_MEM, i);
>> >
>> > ARM may only support IORESOURCE_MEM, but I don't think platform devices
>> > are limited to MMIO, right?  vfio-platform shouldn't be either.
>> >
>>
>> Indeed. Should we however implement this lacking a target to verify it
>> is working correctly?
>>
>> Leaving it out would mean PIO resources for those devices would not be
>> exposed before an update to VFIO, but we wouldn't have to break
>> backward compatibility I think.
>>
>> Would you prefer to have it implemented regardless?
>
> I think we need to have PIO figured out at least enough to have stubbed
> read/write handlers that could be filled in by someone with test
> hardware.  I'm not sure I fully understand how a user associates a
> region index to a device tree description, whether it's ordering or
> something more complicated, so I'm not sure if simply listing all the
> PIO resources after the MMIO resources is sufficient and compatible.
> Maybe you have some thoughts on that.  Thanks,

You are right. I'm not aware if the Linux calls used are guaranteed to
preserve ordering of PIO/MMIO resources, I will investigate a bit on
that.

>
> Alex
>
>> > > +}
>> > > +
>> > > +static int get_platform_irq(struct vfio_platform_device *vdev, int i)
>> > > +{
>> > > +     struct platform_device *pdev = (struct platform_device *) vdev->opaque;
>> > > +
>> > > +     return platform_get_irq(pdev, i);
>> > > +}
>> > > +
>> > > +
>> > > +static int vfio_platform_probe(struct platform_device *pdev)
>> > > +{
>> > > +     struct vfio_platform_device *vdev;
>> > > +     int ret;
>> > > +
>> > > +     vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
>> > > +     if (!vdev)
>> > > +             return -ENOMEM;
>> > > +
>> > > +     vdev->opaque = (void *) pdev;
>> > > +     vdev->name = pdev->name;
>> > > +     vdev->flags = VFIO_DEVICE_FLAGS_PLATFORM;
>> > > +     vdev->get_resource = get_platform_resource;
>> > > +     vdev->get_irq = get_platform_irq;
>> > > +
>> > > +     ret = vfio_platform_probe_common(vdev, &pdev->dev);
>> > > +     if (ret)
>> > > +             kfree(vdev);
>> > > +
>> > > +     return ret;
>> > > +}
>> > > +
>> > > +static int vfio_platform_remove(struct platform_device *pdev)
>> > > +{
>> > > +     return vfio_platform_remove_common(&pdev->dev);
>> > > +}
>> > > +
>> > > +static struct platform_driver vfio_platform_driver = {
>> > > +     .probe          = vfio_platform_probe,
>> > > +     .remove         = vfio_platform_remove,
>> > > +     .driver = {
>> > > +             .name   = "vfio-platform",
>> > > +             .owner  = THIS_MODULE,
>> > > +     },
>> > > +};
>> > > +
>> > > +module_platform_driver(vfio_platform_driver);
>> > > +
>> > > +MODULE_VERSION(DRIVER_VERSION);
>> > > +MODULE_LICENSE("GPL v2");
>> > > +MODULE_AUTHOR(DRIVER_AUTHOR);
>> > > +MODULE_DESCRIPTION(DRIVER_DESC);
>> > > diff --git a/include/uapi/linux/vfio.h b/include/uapi/linux/vfio.h
>> > > index 30f630c..b022a25 100644
>> > > --- a/include/uapi/linux/vfio.h
>> > > +++ b/include/uapi/linux/vfio.h
>> > > @@ -158,6 +158,7 @@ struct vfio_device_info {
>> > >       __u32   flags;
>> > >  #define VFIO_DEVICE_FLAGS_RESET      (1 << 0)        /* Device supports reset */
>> > >  #define VFIO_DEVICE_FLAGS_PCI        (1 << 1)        /* vfio-pci device */
>> > > +#define VFIO_DEVICE_FLAGS_PLATFORM (1 << 2)  /* vfio-platform device */
>> > >       __u32   num_regions;    /* Max region index + 1 */
>> > >       __u32   num_irqs;       /* Max IRQ index + 1 */
>> > >  };
>> >
>> >
>> >
>>
>>
>>
>
>
>



-- 
Antonios Motakis
Virtual Open Systems

^ permalink raw reply

* Re: [PATCHv8.1] fanotify: enable close-on-exec on events' fd when requested in fanotify_init()
From: Jan Kara @ 2014-09-29 11:50 UTC (permalink / raw)
  To: Yann Droneaud
  Cc: Heinrich Schuchardt, Andrew Morton, Eric Paris,
	Richard Guy Briggs, Al Viro, linux-kernel, linux-fsdevel, stable,
	linux-api, Jan Kara, Lino Sanfilippo, Valdis Kletnieks,
	Michael Kerrisk-manpages
In-Reply-To: <1411980555-10818-1-git-send-email-ydroneaud@opteya.com>

On Mon 29-09-14 10:49:15, Yann Droneaud wrote:
> According to commit 80af258867648 ('fanotify: groups can specify
> their f_flags for new fd'), file descriptors created as part of
> file access notification events inherit flags from the
> event_f_flags argument passed to syscall fanotify_init(2).
> 
> So while it is legal for userspace to call fanotify_init() with
> O_CLOEXEC as part of its second argument, O_CLOEXEC is currently
> silently ignored.
> 
> Indeed event_f_flags are only given to dentry_open(), which only
> seems to care about O_ACCMODE and O_PATH in do_dentry_open(),
> O_DIRECT in open_check_o_direct() and O_LARGEFILE in
> generic_file_open().
> 
> But it seems logical to set close-on-exec flag on the file
> descriptor if userspace is allowed to request it with O_CLOEXEC.
> 
> In fact, according to some lookup on http://codesearch.debian.net/
> and various search engine, there's already some userspace code
> requesting it:
> 
> - in systemd's readahead[2]:
> 
>     fanotify_fd = fanotify_init(FAN_CLOEXEC|FAN_NONBLOCK, O_RDONLY|O_LARGEFILE|O_CLOEXEC|O_NOATIME);
> 
> - in clsync[3]:
> 
>     #define FANOTIFY_EVFLAGS (O_LARGEFILE|O_RDONLY|O_CLOEXEC)
> 
>     int fanotify_d = fanotify_init(FANOTIFY_FLAGS, FANOTIFY_EVFLAGS);
> 
> - in examples [4] from "Filesystem monitoring in the Linux
>   kernel" article[5] by Aleksander Morgado:
> 
>     if ((fanotify_fd = fanotify_init (FAN_CLOEXEC,
>                                       O_RDONLY | O_CLOEXEC | O_LARGEFILE)) < 0)
> 
> Lookup also returned some wrong usage of the syscall:
> 
> - in Gonk HAL from Mozilla Firefox OS sources[6]:
> 
>     mFd = fanotify_init(FAN_CLASS_NOTIF, FAN_CLOEXEC);
> 
> Adding support for O_CLOEXEC in fanotify_init() won't magically
> enable it for Gonk since FAN_CLOEXEC is defined as 0x1, which
> is likely equal to O_WRONLY when used in open flag context. In
> the other hand, it won't hurt it either.
> 
> So this patch replaces call to macro get_unused_fd() by a call
> to function get_unused_fd_flags() with event_f_flags value as
> argument. This way O_CLOEXEC flag in the second argument of
> fanotify_init(2) syscall is interpreted so that close-on-exec
> get enabled when requested.
> 
> [1] http://blog.ffwll.ch/2013/11/botching-up-ioctls.html
> [2] http://cgit.freedesktop.org/systemd/systemd/tree/src/readahead/readahead-collect.c?id=v208#n294
> [3] https://github.com/xaionaro/clsync/blob/v0.2.1/sync.c#L1631
>     https://github.com/xaionaro/clsync/blob/v0.2.1/configuration.h#L38
> [4] http://www.lanedo.com/~aleksander/fanotify/fanotify-example.c
> [5] http://www.lanedo.com/2013/filesystem-monitoring-linux-kernel/
> [6] http://hg.mozilla.org/mozilla-central/file/325c74addeba/hal/gonk/GonkDiskSpaceWatcher.cpp#l167
> 
> Link: http://lkml.kernel.org/r/cover.1394532336.git.ydroneaud@opteya.com
> Cc: Heinrich Schuchardt <xypron.glpk@gmx.de>
> Cc: Jan Kara <jack@suse.cz>
> Cc: Valdis Kletnieks <Valdis.Kletnieks@vt.edu>
> Cc: Michael Kerrisk-manpages <mtk.manpages@gmail.com>
> Cc: Lino Sanfilippo <LinoSanfilippo@gmx.de>
> Cc: Richard Guy Briggs <rgb@redhat.com>
> Cc: Eric Paris <eparis@redhat.com>
> Cc: Al Viro <viro@zeniv.linux.org.uk>
> Cc: Andrew Morton <akpm@linux-foundation.org>
> Cc: stable@vger.kernel.org
> Cc: linux-api@vger.kernel.org
> Reviewed by: Heinrich Schuchardt <xypron.glpk@gmx.de>
> Signed-off-by: Yann Droneaud <ydroneaud@opteya.com>
  The patch looks good. You can add:
Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
> Hi Andrew and Henrich,
> 
> Please find an updated patch with a commit message fixed regarding
> the obsolote comments on code which is now updated, thanks to
> Heinrich's patch.
> 
> Changes from v8:
> - fixed commit message
> - added Reviewed-by:
> 
> Regards.
> 
> Yann Droneaud,
> OPTEYA
> 
>  fs/notify/fanotify/fanotify_user.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
> index b13992a41bd9..c991616acca9 100644
> --- a/fs/notify/fanotify/fanotify_user.c
> +++ b/fs/notify/fanotify/fanotify_user.c
> @@ -78,7 +78,7 @@ static int create_fd(struct fsnotify_group *group,
>  
>  	pr_debug("%s: group=%p event=%p\n", __func__, group, event);
>  
> -	client_fd = get_unused_fd();
> +	client_fd = get_unused_fd_flags(group->fanotify_data.f_flags);
>  	if (client_fd < 0)
>  		return client_fd;
>  
> -- 
> 1.9.3
> 
-- 
Jan Kara <jack@suse.cz>
SUSE Labs, CR

^ permalink raw reply

* Re: [RFC PATCH net-next v2 0/5] netns: allow to identify peer netns
From: Nicolas Dichtel @ 2014-09-29 12:06 UTC (permalink / raw)
  To: Eric W. Biederman, Andy Lutomirski
  Cc: Network Development, Linux Containers,
	linux-kernel@vger.kernel.org, Linux API, David S. Miller,
	Stephen Hemminger, Andrew Morton, Cong Wang
In-Reply-To: <87y4t61a6v.fsf@x220.int.ebiederm.org>

Le 26/09/2014 20:57, Eric W. Biederman a écrit :
> Andy Lutomirski <luto@amacapital.net> writes:
>
>> On Fri, Sep 26, 2014 at 11:10 AM, Eric W. Biederman
>> <ebiederm@xmission.com> wrote:
>>> Nicolas Dichtel <nicolas.dichtel@6wind.com> writes:
>>>
>>>> The goal of this serie is to be able to multicast netlink messages with an
>>>> attribute that identify a peer netns.
>>>> This is needed by the userland to interpret some informations contained in
>>>> netlink messages (like IFLA_LINK value, but also some other attributes in case
>>>> of x-netns netdevice (see also
>>>> http://thread.gmane.org/gmane.linux.network/315933/focus=316064 and
>>>> http://thread.gmane.org/gmane.linux.kernel.containers/28301/focus=4239)).
>>>
>>> I want say that the problem addressed by patch 3/5 of this series is a
>>> fundamentally valid problem.  We have network objects spanning network
>>> namespaces and it would be very nice to be able to talk about them in
>>> netlink, and file descriptors are too local and argubably too heavy
>>> weight for netlink quires and especially for netlink broadcast messages.
>>>
>>> Furthermore the concept of ineternal concept of peernet2id seems valid.
>>>
>>> However what you do not address is a way for CRIU (aka process
>>> migration) to be able to restore these ids after process migration.
>>> Going farther it looks like you are actively breaking process migration
>>> at this time, making this set of patches a no-go.
Ok, I will look more deeply into CRIU.

>>>
>>> When adding a new form of namespace id CRIU patches are just about
>>> as necessary as iproute patches.
Noted.

>>>
>>>> Ids are stored in the parent user namespace. These ids are valid only inside
>>>> this user namespace. The user can retrieve these ids via a new netlink messages,
>>>> but only if peer netns are in the same user namespace.
>>>
>>> That does not describe what you have actually implemented in the
>>> patches.
>>>
>>> I see two ways to go with this.
>>>
>>> - A per network namespace table to that you can store ids for ``peer''
>>>    network namespaces.  The table would need to be populated manually by
>>>    the likes of ip netns add.
>>>
>>>    That flips the order of assignment and makes this idea solid.
I have a preference for this solution, because it allows to have a full
broadcast messages. When you have a lot of network interfaces (> 10k),
it saves a lot of time to avoid another request to get all informations.

>>>
>>>    Unfortunately in the case of a fully referencing mesh of N network
>>>    namespaces such a mesh winds up taking O(N^2) space, which seems
>>>    undesirable.
Memory consumption vs performances ;-)
In fact, when you have a lot of netns, you already should have some memory
available (at least N lo interfaces + N interfaces (veth or a x-netns
interface)). I'm not convinced that this is really an obstacle.

>>>
>>> - Add a netlink attribute that says this network element is in a peer
>>>    network namespace.
>>>
>>>    Add a unicast query message that let's you ask if the remote
>>>    end of a tunnel is in a network namespace specified by file
>>>    descriptor.
>>>
>>> I personally lean towards the second version as it is fundamentally
>>> simpler, and generally scales better, and the visibility controls are
>>> the existing visibility controls.  The only downside is it requires
>>> a query after receiving a netlink broadcast message for the times that
>>> we care.
>>
>> The downside of that approach, and all the similar kcmp stuff, is that
>> it scales poorly for applications using it.  This is probably not the
>> end of the world, but it's not ideal.
>
> Agreed, the efficiency is not ideal and there is plenty of room for
> optimization.  We could certainly adopt some of kcmps ordering
> infrastructure to make it suck less, or even potentially work out how
> to return a file descriptor to the network namespace in question.
>
> The key insight of my second proposal is that we can get out of the
> broadcast message business, and only care about the remote namespace for
> unicast messages.  Putting the work in an infrequently used slow path
> instead of a comparitively common path gives us much more freedom in
> the implementation.
I think it's better to have a full netlink messages, instead a partial one.
There is already a lot of attributes added for each rtnl interface messages to
be sure to describe all parameters of these interfaces.
And if the user don't care about ids (user has not set any id with iproute2),
we can just add the same attribute with id 0 (let's say it's a reserved id) to
indicate that the link part of this interface is in another netns.

The great benefit of your first proposal is that the ids are set by the
userspace and thus it allows a high flexibility.

Would you accept a patch that implements this first solution?

^ permalink raw reply

* Re: [PATCH] usb: gadget: f_rndis: fix usb_interface_descriptor for rndis
From: Heiko Schocher @ 2014-09-29 12:11 UTC (permalink / raw)
  To: Lars Melin
  Cc: linux-usb, Felipe Balbi, Greg Kroah-Hartman, linux-kernel, netdev,
	linux-api, Andrzej Pietrasiewicz, Michal Nazarewicz,
	Kyungmin Park, Dan Carpenter, Macpaul Lin, Meier, Roger
In-Reply-To: <5422D39D.70006@gmail.com>

Hello Lars,

sorry for my late answer ...

Am 24.09.2014 16:22, schrieb Lars Melin:
> On 2014-09-24 20:12, Heiko Schocher wrote:
>> Hello Lars,
>>
>> Am 24.09.2014 14:25, schrieb Lars Melin:
>>> On 2014-09-24 13:48, Heiko Schocher wrote:
>>>> use the values for RNDIS over Ethernet as defined in
>>>> http://www.usb.org/developers/defined_class
>>>> (search for RDNIS):
>>>>
>>>> - baseclass: 0xef (miscellaneous)
>>>> - subclass: 0x04
>>>> - protocol: 0x01
>>>>
>>> That is usb class, it is not the same thing as communication device class.
>>>> --- a/include/uapi/linux/usb/cdc.h
>>>> +++ b/include/uapi/linux/usb/cdc.h
>>>> @@ -12,6 +12,7 @@
>>>> #include <linux/types.h>
>>>> #define USB_CDC_SUBCLASS_ACM 0x02
>>>> +#define USB_CDC_SUBCLASS_RNDIS 0x04
>>> No, no, no.
>>> There is no CDC_SUBCLASS_RNDIS and you can not define one over an already used cdc subclass number, 0x04 is Multi-Channel Control Model
>>
>> Ah, ok, so I have to define this values in a new header file, as there
>> is no current file for the USB_CLASS_MISC defines? Or is there a proper
>> place for them?
>>
>> BTW: where do I find the "cdc subclass number, 0x04 is Multi-Channel
>> Control Model" define?
>>
>> bye,
>> Heiko
>
> You can still find the original specification usbcdc11.pdf on the net if you google for it, it has been pulled from usb.org where you could download it until a few years ago.
> It is old but covers a lot of what you need to know.

Hmm.. maybe I am to dummy for finding this docment...

http://www.usb.org/results?q=usbcdc11.pdf&submit=Search

does not find this document ... could you send me a direct link?

I found with the above search:

http://www.usb.org/developers/defined_class

and this site, exactly describes the values for RNDIS over ethernet,
as my patch changes [1]

> Linux has afaik only the cdc.h definition file, everything else is coded by class/subclass in respectively drivers when needed.

why not in header files? I thought, magical values are not welcome
in source code ...

As for the is_rndis() function case, this function is defined in
2 places:

- drivers/net/usb/cdc_ether.c
- drivers/usb/core/generic.c

Has this a special reason? This seems suboptimal to me ...

> 02/02/ff or e0/01/03 are the most common interface attribute for rndis, both of them together with a data interface with attributes 0a/00/00.

I must admit, I am not a USB nor a RNDIS expert ...

> Please check the whitelisting in drivers/net/usb/rndis_host.c and also blacklistings in other net drivers under the same path, it should give you an idea how to bind an interface to a specific driver by interface attributes and/or usb vid:pid.
> You should be able to do the same for your particular device.

Hmm.. I did not understand you here ... so, one step back:

I got from a customer this patch (in a similiar version) and
he did tests with [3] and saw, that a board which runs linux,
is seen in [3] with the values [2] ... so he changed the
values in drivers/usb/gadget/function/f_rndis.c to the
values [1], which are documented in [4] and with them
the test [3] is happy ... and the file
"Documentation/usb/linux.inf" is not longer needed on the
windows pc!

So he (and at the end I too) thought, that this is the proper
way to make [3] happy ... (maybe [3] is incorrect ? )

Is current ML code correct? And if yes, why?

If the values [2] in current ML linux are correct,
could you say me, where they are documented?

(and sorry for my stupid questions ...)

Thanks!

bye,
Heiko

[1] values which my patch sets for RNDIS over ethernet
- baseclass: 0xef (miscellaneous)
- subclass: 0x04
- protocol: 0x01

[2] currently used values for RNDIS over ethernet
- baseclass: 0x02 (USB_CLASS_COMM)
- subclass: 0x02
- protocol: 0xff

[3] "USB Compliance test suite which runs Windows", see:
http://www.usb.org/developers/tools/usb20_tools/#usb20cv

[4] http://www.usb.org/developers/defined_class
-- 
DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox