diff mbox

[PATCHv1] rtc: bcm-iproc: Add support for Broadcom iproc rtc

Message ID 1418757750-3628-1-git-send-email-arun.ramamurthy@broadcom.com
State Needs Review / ACK, archived
Headers show

Checks

Context Check Description
robh/checkpatch warning total: 1 errors, 0 warnings, 0 lines checked
robh/patch-applied success

Commit Message

Arun Ramamurthy Dec. 16, 2014, 7:22 p.m. UTC
From: Arun Ramamurthy <arunrama@broadcom.com>

Adding support for the RTC module used in Broadcom's
iproc architecture

Changes in v1:
	Resending patch to larger distribution

Signed-off-by: Arun Ramamurthy <arunrama@broadcom.com>
Reviewed-by: Ray Jui <rjui@broadcom.com>
Reviewed-by: Scott Branden <sbranden@broadcom.com>
Tested-by: Scott Branden <sbranden@broadcom.com>
---
 .../devicetree/bindings/rtc/brcm,iproc-rtc.txt     |  26 +
 drivers/rtc/Kconfig                                |  11 +
 drivers/rtc/Makefile                               |   1 +
 drivers/rtc/rtc-bcm-iproc.c                        | 613 +++++++++++++++++++++
 4 files changed, 651 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/rtc/brcm,iproc-rtc.txt
 create mode 100644 drivers/rtc/rtc-bcm-iproc.c

Comments

Arnd Bergmann Dec. 16, 2014, 7:42 p.m. UTC | #1
On Tuesday 16 December 2014 11:22:30 arun.ramamurthy@broadcom.com wrote:
> +       rtc: iproc_rtc@0x03026000 {
> +               compatible = "brcm,iproc-rtc";
> +               reg =   spru_bbl:               <0x03026000 0xC>,
> +                       crmu_pwr_good_status:   <0x0301C02C 0x14>,
> +                       crmu_bbl_auth:          <0x03024C74 0x8>;
> +               interrupts = spru_rtc_periodic: <GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>,
> +                            spru_alarm:        <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>;

The reg properties look really random, could it be that the registers
are really part of some other device that contains multiple functions?

Also, what do you use the labels for?

	Arnd
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Arun Ramamurthy Dec. 16, 2014, 8:05 p.m. UTC | #2
On 14-12-16 11:42 AM, Arnd Bergmann wrote:
> On Tuesday 16 December 2014 11:22:30 arun.ramamurthy@broadcom.com wrote:
>> +       rtc: iproc_rtc@0x03026000 {
>> +               compatible = "brcm,iproc-rtc";
>> +               reg =   spru_bbl:               <0x03026000 0xC>,
>> +                       crmu_pwr_good_status:   <0x0301C02C 0x14>,
>> +                       crmu_bbl_auth:          <0x03024C74 0x8>;
>> +               interrupts = spru_rtc_periodic: <GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>,
>> +                            spru_alarm:        <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>;
>
> The reg properties look really random, could it be that the registers
> are really part of some other device that contains multiple functions?
>
This RTC block is on a battery backed logic island and is accessed 
indirectly using the spru_bbl registers. The CRMU registers are required 
to read the power status and write to some authentication registers. 
Without writing to these authentication
registers, we cannot access any of the spru_bbl registers. In conclusion
I don't think the CRMU register accesses can be considered as another 
device access. Do you agree Arnd?

> Also, what do you use the labels for?
>
The labels are purely to improve readability of the device tree entry
> 	Arnd
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Arnd Bergmann Dec. 16, 2014, 8:19 p.m. UTC | #3
On Tuesday 16 December 2014 12:05:08 Arun Ramamurthy wrote:
> On 14-12-16 11:42 AM, Arnd Bergmann wrote:
> > On Tuesday 16 December 2014 11:22:30 arun.ramamurthy@broadcom.com wrote:
> >> +       rtc: iproc_rtc@0x03026000 {
> >> +               compatible = "brcm,iproc-rtc";
> >> +               reg =   spru_bbl:               <0x03026000 0xC>,
> >> +                       crmu_pwr_good_status:   <0x0301C02C 0x14>,
> >> +                       crmu_bbl_auth:          <0x03024C74 0x8>;
> >> +               interrupts = spru_rtc_periodic: <GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>,
> >> +                            spru_alarm:        <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>;
> >
> > The reg properties look really random, could it be that the registers
> > are really part of some other device that contains multiple functions?
> >
> This RTC block is on a battery backed logic island and is accessed 
> indirectly using the spru_bbl registers. The CRMU registers are required 
> to read the power status and write to some authentication registers. 
> Without writing to these authentication
> registers, we cannot access any of the spru_bbl registers. In conclusion
> I don't think the CRMU register accesses can be considered as another 
> device access. Do you agree Arnd?

It sounds like CRMU is some other unit aside from the RTC. Could this
be something like a generic system controller? I think it should
either have its own driver or use the syscon logic if that is what
this is.

> > Also, what do you use the labels for?
> >
> The labels are purely to improve readability of the device tree entry

Please remove them then, they don't help at all.

	Arnd
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Ray Jui Dec. 16, 2014, 8:27 p.m. UTC | #4
On 12/16/2014 12:19 PM, Arnd Bergmann wrote:
> On Tuesday 16 December 2014 12:05:08 Arun Ramamurthy wrote:
>> On 14-12-16 11:42 AM, Arnd Bergmann wrote:
>>> On Tuesday 16 December 2014 11:22:30 arun.ramamurthy@broadcom.com wrote:
>>>> +       rtc: iproc_rtc@0x03026000 {
>>>> +               compatible = "brcm,iproc-rtc";
>>>> +               reg =   spru_bbl:               <0x03026000 0xC>,
>>>> +                       crmu_pwr_good_status:   <0x0301C02C 0x14>,
>>>> +                       crmu_bbl_auth:          <0x03024C74 0x8>;
>>>> +               interrupts = spru_rtc_periodic: <GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>,
>>>> +                            spru_alarm:        <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>;
>>>
>>> The reg properties look really random, could it be that the registers
>>> are really part of some other device that contains multiple functions?
>>>
>> This RTC block is on a battery backed logic island and is accessed
>> indirectly using the spru_bbl registers. The CRMU registers are required
>> to read the power status and write to some authentication registers.
>> Without writing to these authentication
>> registers, we cannot access any of the spru_bbl registers. In conclusion
>> I don't think the CRMU register accesses can be considered as another
>> device access. Do you agree Arnd?
>
> It sounds like CRMU is some other unit aside from the RTC. Could this
> be something like a generic system controller? I think it should
> either have its own driver or use the syscon logic if that is what
> this is.
>
Giving that CRMU has scattered, miscellaneous control logic for multiple 
different peripherals, it probably makes more sense to use the syscon 
logic here.

>>> Also, what do you use the labels for?
>>>
>> The labels are purely to improve readability of the device tree entry
>
> Please remove them then, they don't help at all.
>
> 	Arnd
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Arun Ramamurthy Dec. 16, 2014, 9:54 p.m. UTC | #5
On 14-12-16 12:27 PM, Ray Jui wrote:
>
>
> On 12/16/2014 12:19 PM, Arnd Bergmann wrote:
>> On Tuesday 16 December 2014 12:05:08 Arun Ramamurthy wrote:
>>> On 14-12-16 11:42 AM, Arnd Bergmann wrote:
>>>> On Tuesday 16 December 2014 11:22:30 arun.ramamurthy@broadcom.com
>>>> wrote:
>>>>> +       rtc: iproc_rtc@0x03026000 {
>>>>> +               compatible = "brcm,iproc-rtc";
>>>>> +               reg =   spru_bbl:               <0x03026000 0xC>,
>>>>> +                       crmu_pwr_good_status:   <0x0301C02C 0x14>,
>>>>> +                       crmu_bbl_auth:          <0x03024C74 0x8>;
>>>>> +               interrupts = spru_rtc_periodic: <GIC_SPI 142
>>>>> IRQ_TYPE_LEVEL_HIGH>,
>>>>> +                            spru_alarm:        <GIC_SPI 133
>>>>> IRQ_TYPE_LEVEL_HIGH>;
>>>>
>>>> The reg properties look really random, could it be that the registers
>>>> are really part of some other device that contains multiple functions?
>>>>
>>> This RTC block is on a battery backed logic island and is accessed
>>> indirectly using the spru_bbl registers. The CRMU registers are required
>>> to read the power status and write to some authentication registers.
>>> Without writing to these authentication
>>> registers, we cannot access any of the spru_bbl registers. In conclusion
>>> I don't think the CRMU register accesses can be considered as another
>>> device access. Do you agree Arnd?
>>
>> It sounds like CRMU is some other unit aside from the RTC. Could this
>> be something like a generic system controller? I think it should
>> either have its own driver or use the syscon logic if that is what
>> this is.
>>
> Giving that CRMU has scattered, miscellaneous control logic for multiple
> different peripherals, it probably makes more sense to use the syscon
> logic here.
>
Arnd, thanks for the feedback. If I was to write a separate driver for 
the CRMU, I would have to export certain functions and create an api 
that only this RTC driver would use. I am not sure that is efficient or 
required. What is your opinion?
Would it be better if I use the syson api in my current driver and move 
the CRMU registers to separate syscon device tree entry?

>>>> Also, what do you use the labels for?
>>>>
>>> The labels are purely to improve readability of the device tree entry
>>
>> Please remove them then, they don't help at all.
>>
Sure, I will remove the labels
>>     Arnd
>>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Arnd Bergmann Dec. 17, 2014, 2:31 p.m. UTC | #6
On Tuesday 16 December 2014 13:54:04 Arun Ramamurthy wrote:
> On 14-12-16 12:27 PM, Ray Jui wrote:
> > On 12/16/2014 12:19 PM, Arnd Bergmann wrote:
> >>
> >> It sounds like CRMU is some other unit aside from the RTC. Could this
> >> be something like a generic system controller? I think it should
> >> either have its own driver or use the syscon logic if that is what
> >> this is.
> >>
> > Giving that CRMU has scattered, miscellaneous control logic for multiple
> > different peripherals, it probably makes more sense to use the syscon
> > logic here.
> >
> Arnd, thanks for the feedback. If I was to write a separate driver for 
> the CRMU, I would have to export certain functions and create an api 
> that only this RTC driver would use. I am not sure that is efficient or 
> required. What is your opinion?
> Would it be better if I use the syson api in my current driver and move 
> the CRMU registers to separate syscon device tree entry?
> 

This is something that's normally up to the platform maintainers, depending
on what works best for a given SoC. If you have a control block that
wants to export the same high-level API for multiple drivers, that's
fine, but if literally every register does something different, a syscon
driver works best.

It's also possible that some of the functions of the CRMU already have
abstractions, like system-reset, device-reset, regulator or clock support.
In that case, you can still use syscon but have the more other drivers
use that for accessing the registers.

	Arnd
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Arun Ramamurthy Feb. 12, 2015, 10:17 p.m. UTC | #7
Hi Arnd

My apologies for the late reply, I was moved to other work items. I 
wanted to get more clarification on the syscon issue so that I can 
submit the next patch set. If I understand correctly, you would like
me to move the CRMU logic to a new driver under mfd/ and use the syscon
api calls in my rtc driver? Thanks

Arun

On 14-12-17 06:31 AM, Arnd Bergmann wrote:
> On Tuesday 16 December 2014 13:54:04 Arun Ramamurthy wrote:
>> On 14-12-16 12:27 PM, Ray Jui wrote:
>>> On 12/16/2014 12:19 PM, Arnd Bergmann wrote:
>>>>
>>>> It sounds like CRMU is some other unit aside from the RTC. Could this
>>>> be something like a generic system controller? I think it should
>>>> either have its own driver or use the syscon logic if that is what
>>>> this is.
>>>>
>>> Giving that CRMU has scattered, miscellaneous control logic for multiple
>>> different peripherals, it probably makes more sense to use the syscon
>>> logic here.
>>>
>> Arnd, thanks for the feedback. If I was to write a separate driver for
>> the CRMU, I would have to export certain functions and create an api
>> that only this RTC driver would use. I am not sure that is efficient or
>> required. What is your opinion?
>> Would it be better if I use the syson api in my current driver and move
>> the CRMU registers to separate syscon device tree entry?
>>
>
> This is something that's normally up to the platform maintainers, depending
> on what works best for a given SoC. If you have a control block that
> wants to export the same high-level API for multiple drivers, that's
> fine, but if literally every register does something different, a syscon
> driver works best.
>
> It's also possible that some of the functions of the CRMU already have
> abstractions, like system-reset, device-reset, regulator or clock support.
> In that case, you can still use syscon but have the more other drivers
> use that for accessing the registers.
>
> 	Arnd
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Arnd Bergmann March 4, 2015, 10:21 p.m. UTC | #8
On Thursday 12 February 2015 14:17:41 Arun Ramamurthy wrote:
> Hi Arnd
> 
> My apologies for the late reply, I was moved to other work items. I 
> wanted to get more clarification on the syscon issue so that I can 
> submit the next patch set. If I understand correctly, you would like
> me to move the CRMU logic to a new driver under mfd/ and use the syscon
> api calls in my rtc driver? Thanks

It depends a lot on what's in there, I can best advise you if you
have some form of register list.

A common approach would be to not have a driver for the crmu at all,
but just mark it as syscon, and have the other drivers either reference
the syscon node through a phandle, or create them as childrem of
the syscon node. The latter case makes most sense if all uses of
the crmu have no other MMIO registers.


> On 14-12-17 06:31 AM, Arnd Bergmann wrote:
> > On Tuesday 16 December 2014 13:54:04 Arun Ramamurthy wrote:
> >> On 14-12-16 12:27 PM, Ray Jui wrote:
> >>> On 12/16/2014 12:19 PM, Arnd Bergmann wrote:
> >>>>
> >>>> It sounds like CRMU is some other unit aside from the RTC. Could this
> >>>> be something like a generic system controller? I think it should
> >>>> either have its own driver or use the syscon logic if that is what
> >>>> this is.
> >>>>
> >>> Giving that CRMU has scattered, miscellaneous control logic for multiple
> >>> different peripherals, it probably makes more sense to use the syscon
> >>> logic here.
> >>>
> >> Arnd, thanks for the feedback. If I was to write a separate driver for
> >> the CRMU, I would have to export certain functions and create an api
> >> that only this RTC driver would use. I am not sure that is efficient or
> >> required. What is your opinion?
> >> Would it be better if I use the syson api in my current driver and move
> >> the CRMU registers to separate syscon device tree entry?
> >>
> >
> > This is something that's normally up to the platform maintainers, depending
> > on what works best for a given SoC. If you have a control block that
> > wants to export the same high-level API for multiple drivers, that's
> > fine, but if literally every register does something different, a syscon
> > driver works best.
> >
> > It's also possible that some of the functions of the CRMU already have
> > abstractions, like system-reset, device-reset, regulator or clock support.
> > In that case, you can still use syscon but have the more other drivers
> > use that for accessing the registers.
> >
> > 	Arnd
> >
> --
> To unsubscribe from this list: send the line "unsubscribe devicetree" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Arun Ramamurthy March 4, 2015, 10:40 p.m. UTC | #9
On 15-03-04 02:21 PM, Arnd Bergmann wrote:
> On Thursday 12 February 2015 14:17:41 Arun Ramamurthy wrote:
>> Hi Arnd
>>
>> My apologies for the late reply, I was moved to other work items. I
>> wanted to get more clarification on the syscon issue so that I can
>> submit the next patch set. If I understand correctly, you would like
>> me to move the CRMU logic to a new driver under mfd/ and use the syscon
>> api calls in my rtc driver? Thanks
>
> It depends a lot on what's in there, I can best advise you if you
> have some form of register list.
>
> A common approach would be to not have a driver for the crmu at all,
> but just mark it as syscon, and have the other drivers either reference
> the syscon node through a phandle, or create them as childrem of
> the syscon node. The latter case makes most sense if all uses of
> the crmu have no other MMIO registers.
>

Thank you Arnd, I am going to follow the approach of adding a child node 
to the syscon node. Several other driver use other registers in the CRMU 
so I think the child node approach makes the most sense.
>
>> On 14-12-17 06:31 AM, Arnd Bergmann wrote:
>>> On Tuesday 16 December 2014 13:54:04 Arun Ramamurthy wrote:
>>>> On 14-12-16 12:27 PM, Ray Jui wrote:
>>>>> On 12/16/2014 12:19 PM, Arnd Bergmann wrote:
>>>>>>
>>>>>> It sounds like CRMU is some other unit aside from the RTC. Could this
>>>>>> be something like a generic system controller? I think it should
>>>>>> either have its own driver or use the syscon logic if that is what
>>>>>> this is.
>>>>>>
>>>>> Giving that CRMU has scattered, miscellaneous control logic for multiple
>>>>> different peripherals, it probably makes more sense to use the syscon
>>>>> logic here.
>>>>>
>>>> Arnd, thanks for the feedback. If I was to write a separate driver for
>>>> the CRMU, I would have to export certain functions and create an api
>>>> that only this RTC driver would use. I am not sure that is efficient or
>>>> required. What is your opinion?
>>>> Would it be better if I use the syson api in my current driver and move
>>>> the CRMU registers to separate syscon device tree entry?
>>>>
>>>
>>> This is something that's normally up to the platform maintainers, depending
>>> on what works best for a given SoC. If you have a control block that
>>> wants to export the same high-level API for multiple drivers, that's
>>> fine, but if literally every register does something different, a syscon
>>> driver works best.
>>>
>>> It's also possible that some of the functions of the CRMU already have
>>> abstractions, like system-reset, device-reset, regulator or clock support.
>>> In that case, you can still use syscon but have the more other drivers
>>> use that for accessing the registers.
>>>
>>> 	Arnd
>>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe devicetree" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Arnd Bergmann March 4, 2015, 10:50 p.m. UTC | #10
On Wednesday 04 March 2015 14:40:13 Arun Ramamurthy wrote:
> On 15-03-04 02:21 PM, Arnd Bergmann wrote:
> > On Thursday 12 February 2015 14:17:41 Arun Ramamurthy wrote:
> >> Hi Arnd
> >>
> >> My apologies for the late reply, I was moved to other work items. I
> >> wanted to get more clarification on the syscon issue so that I can
> >> submit the next patch set. If I understand correctly, you would like
> >> me to move the CRMU logic to a new driver under mfd/ and use the syscon
> >> api calls in my rtc driver? Thanks
> >
> > It depends a lot on what's in there, I can best advise you if you
> > have some form of register list.
> >
> > A common approach would be to not have a driver for the crmu at all,
> > but just mark it as syscon, and have the other drivers either reference
> > the syscon node through a phandle, or create them as childrem of
> > the syscon node. The latter case makes most sense if all uses of
> > the crmu have no other MMIO registers.
> >
> 
> Thank you Arnd, I am going to follow the approach of adding a child node 
> to the syscon node. Several other driver use other registers in the CRMU 
> so I think the child node approach makes the most sense.

Just to be sure we have the same understanding: of those other drivers,
do you think that they would use only CRMU registers, or could there
be drivers that have both CRMU as well as other MMIO registers?

	Arnd
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Arun Ramamurthy March 4, 2015, 10:53 p.m. UTC | #11
On 15-03-04 02:50 PM, Arnd Bergmann wrote:
> On Wednesday 04 March 2015 14:40:13 Arun Ramamurthy wrote:
>> On 15-03-04 02:21 PM, Arnd Bergmann wrote:
>>> On Thursday 12 February 2015 14:17:41 Arun Ramamurthy wrote:
>>>> Hi Arnd
>>>>
>>>> My apologies for the late reply, I was moved to other work items. I
>>>> wanted to get more clarification on the syscon issue so that I can
>>>> submit the next patch set. If I understand correctly, you would like
>>>> me to move the CRMU logic to a new driver under mfd/ and use the syscon
>>>> api calls in my rtc driver? Thanks
>>>
>>> It depends a lot on what's in there, I can best advise you if you
>>> have some form of register list.
>>>
>>> A common approach would be to not have a driver for the crmu at all,
>>> but just mark it as syscon, and have the other drivers either reference
>>> the syscon node through a phandle, or create them as childrem of
>>> the syscon node. The latter case makes most sense if all uses of
>>> the crmu have no other MMIO registers.
>>>
>>
>> Thank you Arnd, I am going to follow the approach of adding a child node
>> to the syscon node. Several other driver use other registers in the CRMU
>> so I think the child node approach makes the most sense.
>
> Just to be sure we have the same understanding: of those other drivers,
> do you think that they would use only CRMU registers, or could there
> be drivers that have both CRMU as well as other MMIO registers?
>
The other drivers have both CRMU and other MMIO registers. So I thought 
they could also switch to using the syscon child nodes.
> 	Arnd
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Arnd Bergmann March 4, 2015, 10:58 p.m. UTC | #12
On Wednesday 04 March 2015 14:53:40 Arun Ramamurthy wrote:
> On 15-03-04 02:50 PM, Arnd Bergmann wrote:
> > On Wednesday 04 March 2015 14:40:13 Arun Ramamurthy wrote:
> >> On 15-03-04 02:21 PM, Arnd Bergmann wrote:
> >>> On Thursday 12 February 2015 14:17:41 Arun Ramamurthy wrote:
> >>>> Hi Arnd
> >>>>
> >>>> My apologies for the late reply, I was moved to other work items. I
> >>>> wanted to get more clarification on the syscon issue so that I can
> >>>> submit the next patch set. If I understand correctly, you would like
> >>>> me to move the CRMU logic to a new driver under mfd/ and use the syscon
> >>>> api calls in my rtc driver? Thanks
> >>>
> >>> It depends a lot on what's in there, I can best advise you if you
> >>> have some form of register list.
> >>>
> >>> A common approach would be to not have a driver for the crmu at all,
> >>> but just mark it as syscon, and have the other drivers either reference
> >>> the syscon node through a phandle, or create them as childrem of
> >>> the syscon node. The latter case makes most sense if all uses of
> >>> the crmu have no other MMIO registers.
> >>>
> >>
> >> Thank you Arnd, I am going to follow the approach of adding a child node
> >> to the syscon node. Several other driver use other registers in the CRMU
> >> so I think the child node approach makes the most sense.
> >
> > Just to be sure we have the same understanding: of those other drivers,
> > do you think that they would use only CRMU registers, or could there
> > be drivers that have both CRMU as well as other MMIO registers?
> >
> The other drivers have both CRMU and other MMIO registers. So I thought 
> they could also switch to using the syscon child nodes.
> 

No, in this case, better not use child nodes at all. When other platforms
use child nodes of a syscon, the common case is that they use the 'reg'
property to refer to syscon registers, which are in a different address
space from other MMIO, and you can't easily mix the two.

	Arnd
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Arun Ramamurthy March 11, 2015, 8 p.m. UTC | #13
On 15-03-04 02:58 PM, Arnd Bergmann wrote:
> On Wednesday 04 March 2015 14:53:40 Arun Ramamurthy wrote:
>> On 15-03-04 02:50 PM, Arnd Bergmann wrote:
>>> On Wednesday 04 March 2015 14:40:13 Arun Ramamurthy wrote:
>>>> On 15-03-04 02:21 PM, Arnd Bergmann wrote:
>>>>> On Thursday 12 February 2015 14:17:41 Arun Ramamurthy wrote:
>>>>>> Hi Arnd
>>>>>>
>>>>>> My apologies for the late reply, I was moved to other work items. I
>>>>>> wanted to get more clarification on the syscon issue so that I can
>>>>>> submit the next patch set. If I understand correctly, you would like
>>>>>> me to move the CRMU logic to a new driver under mfd/ and use the syscon
>>>>>> api calls in my rtc driver? Thanks
>>>>>
>>>>> It depends a lot on what's in there, I can best advise you if you
>>>>> have some form of register list.
>>>>>
>>>>> A common approach would be to not have a driver for the crmu at all,
>>>>> but just mark it as syscon, and have the other drivers either reference
>>>>> the syscon node through a phandle, or create them as childrem of
>>>>> the syscon node. The latter case makes most sense if all uses of
>>>>> the crmu have no other MMIO registers.
>>>>>
>>>>
>>>> Thank you Arnd, I am going to follow the approach of adding a child node
>>>> to the syscon node. Several other driver use other registers in the CRMU
>>>> so I think the child node approach makes the most sense.
>>>
>>> Just to be sure we have the same understanding: of those other drivers,
>>> do you think that they would use only CRMU registers, or could there
>>> be drivers that have both CRMU as well as other MMIO registers?
>>>
>> The other drivers have both CRMU and other MMIO registers. So I thought
>> they could also switch to using the syscon child nodes.
>>
>
> No, in this case, better not use child nodes at all. When other platforms
> use child nodes of a syscon, the common case is that they use the 'reg'
> property to refer to syscon registers, which are in a different address
> space from other MMIO, and you can't easily mix the two.
>

Arnd, this is the device tree entry that I would end up with and I plan 
to use syscon_regmap_lookup_by_phandle in the rtc driver. Does this look 
acceptable?

rtc: iproc_rtc@0x03026000 {
		compatible = "brcm,iproc-rtc";
		reg =	<0x03026000 0xC>,
		iso_cell_syscon = <&crmu_iso_cell_control>;
		bbl_auth_syscon = <&crmu_bbl_auth>
		status = "okay";

crmu_iso_cell_control:crmu@0x0301C02C {
		compatible = "syscon";
		reg = <0x0301C038 0x8>
}

crmu_bbl_auth:crmu@0x03024C74 {
		compatible = "syscon";
		reg = <0x03024C74 0x8>;
}

Thanks
> 	Arnd
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Arnd Bergmann March 11, 2015, 8:31 p.m. UTC | #14
On Wednesday 11 March 2015 13:00:22 Arun Ramamurthy wrote:
> 
> Arnd, this is the device tree entry that I would end up with and I plan 
> to use syscon_regmap_lookup_by_phandle in the rtc driver. Does this look 
> acceptable?
> 
> rtc: iproc_rtc@0x03026000 {
>                 compatible = "brcm,iproc-rtc";
>                 reg =   <0x03026000 0xC>,
>                 iso_cell_syscon = <&crmu_iso_cell_control>;
>                 bbl_auth_syscon = <&crmu_bbl_auth>
>                 status = "okay";
> 
> crmu_iso_cell_control:crmu@0x0301C02C {
>                 compatible = "syscon";
>                 reg = <0x0301C038 0x8>
> }
> 
> crmu_bbl_auth:crmu@0x03024C74 {
>                 compatible = "syscon";
>                 reg = <0x03024C74 0x8>;
> }

This doesn't look right, sorry:

A syscon device is defined as a collection of registers that
have no logical grouping within them but that can be seen
as a single device. What you have here instead are two syscon
nodes that each have only a single 8-byte register.

What are the other registers surrounding those? I would expect
something like

	crmu0: syscon@03010000 {
                 compatible = "syscon";
                 reg = <0x03010000 0x10000>;
	};

and then use an offset into the syscon from the rtc node, like

	iproc_rtc: rtc@03026000 {
		compatible = "brcm,iproc-rtc";
		reg = <0x03026000 0x1000>;
		iso_cell_syscon = <&crmu0 0xc038>;
		bbl_auth_syscon = <&crmu1 0x4c74>;
	};

Note also that you got most of the naming wrong:

- node names should be generic strings like "rtc", "syscon", "pci" etc.
  The specific strings are defined in ePAPR.
- unit addresses should match the first 'reg' property and
  not start with '0x'.
- it seems strange that the rtc has only 12 bytes of registers,
  though that may actually be correct.

	Arnd
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/rtc/brcm,iproc-rtc.txt b/Documentation/devicetree/bindings/rtc/brcm,iproc-rtc.txt
new file mode 100644
index 0000000..f9b354b
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/brcm,iproc-rtc.txt
@@ -0,0 +1,26 @@ 
+Broadcom IPROC Real Time Clock
+
+Required Properties:
+- compatible: should be "brcm,iproc-rtc"
+
+- reg :  Requires three separate register sets.
+	spru_bbl: Base address of SPRU_BBL_WDATA used for
+		  indirect access to RTC registers
+	crmu_pwr_good_status: Base address of CRMU_PWR_GOOD_STATUS register,
+			      used to check power status of BBL
+			      (battery backed logic)
+	crum_bbl_auth: Base address of CRMU_BBL_AUTH_CODE register.
+
+- interrupts: RTC Periodic interrupt and RTC Alarm interrupt.
+
+
+Example:
+	rtc: iproc_rtc@0x03026000 {
+		compatible = "brcm,iproc-rtc";
+		reg =	spru_bbl:		<0x03026000 0xC>,
+			crmu_pwr_good_status:	<0x0301C02C 0x14>,
+			crmu_bbl_auth:		<0x03024C74 0x8>;
+		interrupts = spru_rtc_periodic: <GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>,
+			     spru_alarm:	<GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>;
+		status = "disabled";
+	};
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 4511ddc..0a3fd03 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1426,6 +1426,17 @@  config RTC_DRV_XGENE
 	  This driver can also be built as a module, if so, the module
 	  will be called "rtc-xgene".
 
+config RTC_DRV_IPROC
+	tristate "IPROC RTC support"
+	depends on ARCH_BCM
+	default ARCH_BCM_IPROC
+	help
+	  If you say yes here you get support for Broadcom IPROC RTC subsystem.
+	  If unsure, say N.
+
+	  This driver can also be built as a module, if so, the module
+	  will be called "rtc-bcm-iproc"
+
 comment "HID Sensor RTC drivers"
 
 config RTC_DRV_HID_SENSOR_TIME
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index b188323..eca6560 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -64,6 +64,7 @@  obj-$(CONFIG_RTC_DRV_GENERIC)	+= rtc-generic.o
 obj-$(CONFIG_RTC_DRV_HID_SENSOR_TIME) += rtc-hid-sensor-time.o
 obj-$(CONFIG_RTC_DRV_HYM8563)	+= rtc-hym8563.o
 obj-$(CONFIG_RTC_DRV_IMXDI)	+= rtc-imxdi.o
+obj-$(CONFIG_RTC_DRV_IPROC)	+= rtc-bcm-iproc.o
 obj-$(CONFIG_RTC_DRV_ISL1208)	+= rtc-isl1208.o
 obj-$(CONFIG_RTC_DRV_ISL12022)	+= rtc-isl12022.o
 obj-$(CONFIG_RTC_DRV_ISL12057)	+= rtc-isl12057.o
diff --git a/drivers/rtc/rtc-bcm-iproc.c b/drivers/rtc/rtc-bcm-iproc.c
new file mode 100644
index 0000000..fe29b94
--- /dev/null
+++ b/drivers/rtc/rtc-bcm-iproc.c
@@ -0,0 +1,613 @@ 
+/*
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; 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 <linux/init.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+
+/***********************************************************************
+ * Definitions
+ ***********************************************************************/
+#define IREG_BBL_RTC_PER		0x00000000
+#define IREG_BBL_RTC_MATCH		0x00000004
+#define IREG_BBL_RTC_DIV		0x00000008
+#define IREG_BBL_RTC_SECOND		0x0000000C
+#define IREG_BBL_INTERRUPT_EN		0x00000010
+#define IREG_BBL_INTERRUPT_STAT		0x00000014
+#define IREG_BBL_INTERRUPT_CLR		0x00000018
+#define IREG_BBL_CONTROL		0x0000001C
+#define IREG_BBL_ADDR_MASK		0x3FF
+
+#define BBL_PER_1s	0x00000008
+
+#define CRMU_AUTH_CODE_PWD	0x12345678
+#define CRMU_AUTH_CODE_PWD_RST	0x99999999
+#define CRMU_AUTH_CODE_PWD_CLR	0x0
+
+#define RTC_REG_ACC_DONE	BIT(0)
+#define RTC_REG_RTC_STOP	BIT(0)
+#define RTC_REG_PERIO_INTR	BIT(0)
+#define RTC_REG_ALARM_INTR	BIT(1)
+#define RTC_IND_SOFT_RST_N	BIT(10)
+#define RTC_REG_WR_CMD		BIT(11)
+#define RTC_REG_RD_CMD		BIT(12)
+#define CRMU_ISO_PDBBL		BIT(16)
+#define CRMU_ISO_PDBBL_TAMPER	BIT(24)
+
+/* Timeout when waiting on register
+reads or writes */
+#define REG_TIMEOUT_MICROSECONDS 250
+
+/*  SPRU Source Select status
+	   0 - SPRU is powered by AON power
+	   1 - SPRU is powerd by battery */
+#define CRMU_SPRU_SOURCE_SEL_AON 0
+
+struct rtc_regs_t {
+	u32 SPRU_BBL_WDATA;
+	u32 SPRU_BBL_CMD;
+	u32 SPRU_BBL_STATUS;
+	u32 SPRU_BBL_RDATA;
+};
+
+struct crmu_regs_t {
+	u32 CRMU_PWR_GOOD_STATUS;
+	u32 CRMU_POWER_REQ_CFG;
+	u32 CRMU_POWER_POLL;
+	u32 CRMU_ISO_CELL_CONTROL;
+	u32 rsvd;
+	u32 CRMU_SPRU_SOURCE_SEL_STAT;
+};
+
+struct bbl_auth_t {
+	u32 CRMU_BBL_AUTH_CODE;
+	u32 CRMU_BBL_AUTH_CHECK;
+};
+
+struct iproc_rtc_t {
+	struct rtc_device *rtc;
+	struct rtc_regs_t *regs;
+	struct crmu_regs_t *crmu_regs;
+	struct bbl_auth_t *auth_regs;
+	int periodic_irq;
+	int alarm_irq;
+	spinlock_t lock;
+};
+/***********************************************************************
+ *  End Definitions
+ ***********************************************************************/
+
+static inline int wait_acc_done(struct iproc_rtc_t *iproc_rtc)
+{
+	u32 reg_val;
+	int timeout = REG_TIMEOUT_MICROSECONDS;
+
+	reg_val = readl(&iproc_rtc->regs->SPRU_BBL_STATUS);
+	while (!(reg_val & RTC_REG_ACC_DONE)) {
+		if (--timeout == 0)
+			return -EIO;
+		udelay(1);
+		reg_val = readl(&iproc_rtc->regs->SPRU_BBL_STATUS);
+	}
+
+	return 0;
+}
+
+static inline int rtc_reg_write(u32 reg_addr, u32 reg_val,
+				struct iproc_rtc_t *iproc_rtc)
+{
+	u32 cmd;
+	int ret;
+
+	writel(reg_val, &iproc_rtc->regs->SPRU_BBL_WDATA);
+	/* Write command */
+	cmd = (reg_addr & IREG_BBL_ADDR_MASK) |
+		RTC_REG_WR_CMD | RTC_IND_SOFT_RST_N;
+	writel(cmd, &iproc_rtc->regs->SPRU_BBL_CMD);
+	ret = wait_acc_done(iproc_rtc);
+	if (ret < 0)
+		pr_err("RTC: reg write to 0x%x failed!", reg_addr);
+
+	return ret;
+}
+
+static inline int rtc_reg_read(u32 reg_addr, u32 *data,
+				struct iproc_rtc_t *iproc_rtc)
+{
+	u32 cmd;
+	int ret;
+
+	/* Read command */
+	cmd = (reg_addr & IREG_BBL_ADDR_MASK) |
+		RTC_REG_RD_CMD | RTC_IND_SOFT_RST_N;
+	writel(cmd, &iproc_rtc->regs->SPRU_BBL_CMD);
+	ret = wait_acc_done(iproc_rtc);
+	if (ret < 0)
+		pr_err("RTC: reg read to 0x%x failed", reg_addr);
+	else
+		*data = readl(&iproc_rtc->regs->SPRU_BBL_RDATA);
+
+	return ret;
+}
+
+static int bbl_init(struct iproc_rtc_t *iproc_rtc)
+{
+	u32 reg_val;
+	int timeout = REG_TIMEOUT_MICROSECONDS;
+
+	/* Check SPRU Source Select status
+	   0 - SPRU is powered by AON power
+	   1 - SPRU is powerd by battery */
+	reg_val = readl(&iproc_rtc->crmu_regs->CRMU_SPRU_SOURCE_SEL_STAT);
+	while (reg_val != CRMU_SPRU_SOURCE_SEL_AON) {
+		if (--timeout == 0) {
+			pr_info("RTC: BBL AON power not available\n");
+			return -ENODEV;
+		}
+		udelay(1);
+		reg_val = readl(
+			&iproc_rtc->crmu_regs->CRMU_SPRU_SOURCE_SEL_STAT);
+	}
+
+	/* Wait for reset cycle */
+	writel(0, &iproc_rtc->regs->SPRU_BBL_CMD);
+	udelay(200);
+	writel(RTC_IND_SOFT_RST_N, &iproc_rtc->regs->SPRU_BBL_CMD);
+
+	/* Remove BBL related isolation from CRMU */
+	reg_val = readl(&iproc_rtc->crmu_regs->CRMU_ISO_CELL_CONTROL);
+	reg_val &= ~(CRMU_ISO_PDBBL | CRMU_ISO_PDBBL_TAMPER);
+	writel(reg_val, &iproc_rtc->crmu_regs->CRMU_ISO_CELL_CONTROL);
+
+	/* program CRMU auth_code resister */
+	writel(CRMU_AUTH_CODE_PWD, &iproc_rtc->auth_regs->CRMU_BBL_AUTH_CODE);
+	/* program CRMU auth_code_check register */
+	/* auth_code must equal to auth_code_check */
+	writel(CRMU_AUTH_CODE_PWD, &iproc_rtc->auth_regs->CRMU_BBL_AUTH_CHECK);
+
+	return 0;
+}
+
+static void bbl_exit(struct iproc_rtc_t *iproc_rtc)
+{
+	u32 reg_val;
+
+	/*- Set BBL related isolation from CRMU */
+	reg_val = readl(&iproc_rtc->crmu_regs->CRMU_ISO_CELL_CONTROL);
+	reg_val |= (CRMU_ISO_PDBBL | CRMU_ISO_PDBBL_TAMPER);
+	writel(reg_val, &iproc_rtc->crmu_regs->CRMU_ISO_CELL_CONTROL);
+
+	/* Change the AUTH CODE register so it does not match
+	 * the AUTH_CHECK register */
+	writel(CRMU_AUTH_CODE_PWD_CLR,
+	       &iproc_rtc->auth_regs->CRMU_BBL_AUTH_CODE);
+	writel(CRMU_AUTH_CODE_PWD_RST,
+	       &iproc_rtc->auth_regs->CRMU_BBL_AUTH_CHECK);
+}
+static int iproc_rtc_enable(struct iproc_rtc_t *iproc_rtc)
+{
+	u32 reg_val;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&iproc_rtc->lock, flags);
+
+	/* Set periodic timer as 1 second */
+	ret = rtc_reg_write(IREG_BBL_RTC_PER, BBL_PER_1s, iproc_rtc);
+	if (ret < 0)
+		goto err;
+
+	ret = rtc_reg_read(IREG_BBL_INTERRUPT_EN, &reg_val, iproc_rtc);
+	if (ret < 0)
+		goto err;
+
+	/* Disable alarm&periodic interrupt */
+	reg_val &= ~(RTC_REG_PERIO_INTR | RTC_REG_ALARM_INTR);
+
+	ret = rtc_reg_write(IREG_BBL_INTERRUPT_EN, reg_val, iproc_rtc);
+	if (ret < 0)
+		goto err;
+
+	ret = rtc_reg_write(IREG_BBL_INTERRUPT_CLR,
+		RTC_REG_PERIO_INTR | RTC_REG_ALARM_INTR, iproc_rtc);
+	if (ret < 0)
+		goto err;
+
+	reg_val |= RTC_REG_PERIO_INTR | RTC_REG_ALARM_INTR;
+	/* enable RTC periodic interrupt */
+	ret = rtc_reg_write(IREG_BBL_INTERRUPT_EN, reg_val, iproc_rtc);
+
+err:
+	spin_unlock_irqrestore(&iproc_rtc->lock, flags);
+	return ret;
+}
+
+static int iproc_rtc_disable(struct iproc_rtc_t *iproc_rtc)
+{
+	u32 reg_val;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&iproc_rtc->lock, flags);
+
+	ret = rtc_reg_read(IREG_BBL_INTERRUPT_EN, &reg_val, iproc_rtc);
+	if (ret < 0)
+		goto err;
+
+	reg_val &= ~(RTC_REG_PERIO_INTR | RTC_REG_ALARM_INTR);
+	/* To disable RTC periodic interrupt */
+	ret = rtc_reg_write(IREG_BBL_INTERRUPT_EN, reg_val, iproc_rtc);
+
+err:
+	spin_unlock_irqrestore(&iproc_rtc->lock, flags);
+	return ret;
+}
+
+static irqreturn_t iproc_rtc_interrupt(int irq, void *class_dev)
+{
+	unsigned long flags, events = 0;
+	u32 reg_val, irq_flg;
+	int ret = 0;
+	struct iproc_rtc_t *iproc_rtc =  class_dev;
+
+	spin_lock_irqsave(&iproc_rtc->lock, flags);
+	ret = rtc_reg_read(IREG_BBL_INTERRUPT_STAT, &irq_flg, iproc_rtc);
+	if (ret < 0) {
+		pr_err("RTC: Interrupt Routine failed");
+		goto err;
+	}
+
+	irq_flg &= RTC_REG_PERIO_INTR | RTC_REG_ALARM_INTR;
+	if (!irq_flg)
+		goto err;
+
+	if (irq_flg & RTC_REG_PERIO_INTR) {
+		/* Clear periodic interrupt status */
+		ret = rtc_reg_write(IREG_BBL_INTERRUPT_CLR, RTC_REG_PERIO_INTR,
+					iproc_rtc);
+		if (ret < 0)
+			pr_err("RTC: Failed to clear RTC periodic interrupt event");
+		events |= RTC_IRQF | RTC_PF;
+	}
+
+	if (irq_flg & RTC_REG_ALARM_INTR) {
+		/* Clear alarm interrupt status */
+		ret = rtc_reg_write(IREG_BBL_INTERRUPT_CLR, RTC_REG_ALARM_INTR,
+					iproc_rtc);
+		if (ret < 0)
+			pr_err("RTC: Failed to clear RTC Alarm interrupt event");
+		events |= RTC_IRQF | RTC_AF;
+
+		ret = rtc_reg_read(IREG_BBL_INTERRUPT_EN, &reg_val, iproc_rtc);
+		if (ret < 0) {
+			pr_err("RTC: Failed to disable RTC Alarm interrupt after clear");
+			goto err;
+		}
+		reg_val &= ~RTC_REG_ALARM_INTR;
+		 /* Disable Alarm interrupt */
+		ret = rtc_reg_write(IREG_BBL_INTERRUPT_EN, reg_val, iproc_rtc);
+		if (ret < 0) {
+			pr_err("RTC: Failed to disable RTC Alarm interrupt after clear");
+			goto err;
+		}
+	}
+
+err:
+	if (events)
+		rtc_update_irq(iproc_rtc->rtc, 1, events);
+	spin_unlock_irqrestore(&iproc_rtc->lock, flags);
+	return IRQ_HANDLED;
+}
+
+static int iproc_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	unsigned long flags;
+	u32 seconds;
+	int ret = 0;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct iproc_rtc_t *iproc_rtc = platform_get_drvdata(pdev);
+
+	spin_lock_irqsave(&iproc_rtc->lock, flags);
+
+	ret = rtc_reg_read(IREG_BBL_RTC_SECOND, &seconds, iproc_rtc);
+	if (ret < 0) {
+		pr_err("RTC: iproc_rtc_read_time failed");
+		goto err;
+	}
+	rtc_time_to_tm(seconds, tm);
+
+	ret = rtc_valid_tm(tm);
+
+err:
+	spin_unlock_irqrestore(&iproc_rtc->lock, flags);
+	return ret;
+}
+
+static int iproc_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	unsigned long flags, seconds;
+	int ret = 0;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct iproc_rtc_t *iproc_rtc = platform_get_drvdata(pdev);
+
+	rtc_tm_to_time(tm, &seconds);
+	spin_lock_irqsave(&iproc_rtc->lock, flags);
+
+	/* bbl_rtc_stop = 1, RTC hal */
+	ret = rtc_reg_write(IREG_BBL_CONTROL, RTC_REG_RTC_STOP, iproc_rtc);
+	if (ret < 0)
+		goto err;
+	/* Update DIV */
+	ret = rtc_reg_write(IREG_BBL_RTC_DIV, 0, iproc_rtc);
+	if (ret < 0)
+		goto err;
+	/* Update second */
+	ret = rtc_reg_write(IREG_BBL_RTC_SECOND, seconds, iproc_rtc);
+	if (ret < 0)
+		goto err;
+	/* bl_rtc_stop = 0, RTC release */
+	ret = rtc_reg_write(IREG_BBL_CONTROL, ~RTC_REG_RTC_STOP, iproc_rtc);
+
+err:
+	spin_unlock_irqrestore(&iproc_rtc->lock, flags);
+	if (ret < 0)
+		pr_err("RTC: iproc_rtc_set_time failed");
+	return ret;
+}
+
+static int iproc_rtc_alarm_irq_enable(struct device *dev,
+					unsigned int enabled)
+{
+	unsigned long flags;
+	u32 reg_val;
+	int ret = 0;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct iproc_rtc_t *iproc_rtc = platform_get_drvdata(pdev);
+
+	spin_lock_irqsave(&iproc_rtc->lock, flags);
+	ret = rtc_reg_read(IREG_BBL_INTERRUPT_EN, &reg_val, iproc_rtc);
+	if (ret < 0)
+		goto err;
+
+	if (enabled)
+		reg_val |= RTC_REG_ALARM_INTR;
+	else
+		reg_val &= ~RTC_REG_ALARM_INTR;
+
+	ret = rtc_reg_write(IREG_BBL_INTERRUPT_EN, reg_val, iproc_rtc);
+
+err:
+	spin_unlock_irqrestore(&iproc_rtc->lock, flags);
+	if (ret < 0)
+		pr_err("RTC: iproc_rtc_alarm_irq_enable failed");
+	return ret;
+}
+
+static int iproc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+	unsigned long flags;
+	u32 reg_val, seconds;
+	int ret = 0;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct iproc_rtc_t *iproc_rtc = platform_get_drvdata(pdev);
+
+	spin_lock_irqsave(&iproc_rtc->lock, flags);
+	ret = rtc_reg_read(IREG_BBL_RTC_MATCH, &seconds, iproc_rtc);
+	if (ret < 0)
+		goto err;
+	seconds = (seconds << 7);
+	rtc_time_to_tm(seconds, &alm->time);
+	ret = rtc_reg_read(IREG_BBL_INTERRUPT_EN, &reg_val, iproc_rtc);
+	if (ret < 0)
+		goto err;
+	reg_val &= RTC_REG_ALARM_INTR;
+	alm->pending = !reg_val;
+	alm->enabled = alm->pending && device_may_wakeup(dev);
+
+err:
+	spin_unlock_irqrestore(&iproc_rtc->lock, flags);
+	if (ret < 0)
+		pr_err("RTC: iproc_rtc_read_alarm failed");
+	return ret;
+}
+
+static int iproc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+	unsigned long flags;
+	unsigned long seconds;
+	int ret = 0;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct iproc_rtc_t *iproc_rtc = platform_get_drvdata(pdev);
+
+	spin_lock_irqsave(&iproc_rtc->lock, flags);
+	rtc_tm_to_time(&alm->time, &seconds);
+	seconds = ((seconds & (0xFFFF << 7)) >> 7) & 0xFFFF;
+	ret = rtc_reg_write(IREG_BBL_RTC_MATCH, seconds, iproc_rtc);
+	spin_unlock_irqrestore(&iproc_rtc->lock, flags);
+	if (ret < 0)
+		pr_err("RTC: iproc_rtc_set_alarm failed");
+
+	return ret;
+}
+
+static struct rtc_class_ops iproc_rtc_ops = {
+	.read_time		= iproc_rtc_read_time,
+	.set_time		= iproc_rtc_set_time,
+	.alarm_irq_enable	= iproc_rtc_alarm_irq_enable,
+	.read_alarm		= iproc_rtc_read_alarm,
+	.set_alarm		= iproc_rtc_set_alarm,
+};
+
+static const struct of_device_id iproc_rtc_of_match[] = {
+	{.compatible = "brcm,iproc-rtc",},
+	{ }
+};
+
+static int iproc_rtc_probe(struct platform_device *pdev)
+{
+	struct device_node *dev_of = pdev->dev.of_node;
+	struct resource *res;
+	int ret = 0;
+	struct iproc_rtc_t *iproc_rtc;
+
+	iproc_rtc = devm_kzalloc(&pdev->dev, sizeof(struct iproc_rtc_t),
+				 GFP_KERNEL);
+	spin_lock_init(&iproc_rtc->lock);
+
+	iproc_rtc->periodic_irq = irq_of_parse_and_map(dev_of, 0);
+	if (iproc_rtc->periodic_irq < 0) {
+		dev_err(&pdev->dev, "no RTC periodic irq\n");
+		ret = -ENODEV;
+		goto fail;
+	}
+
+	iproc_rtc->alarm_irq = irq_of_parse_and_map(dev_of, 1);
+	if (iproc_rtc->alarm_irq < 0) {
+		dev_err(&pdev->dev, "no RTC alarm irq\n");
+		ret = -ENODEV;
+		goto fail;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "spru_bbl MEM resource missing\n");
+		ret = -ENOMEM;
+		goto fail;
+	}
+	iproc_rtc->regs = (struct rtc_regs_t *)
+		devm_ioremap_resource(&pdev->dev, res);
+	if (!iproc_rtc->regs) {
+		dev_err(&pdev->dev, "unable to ioremap spru_bbl MEM resource\n");
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "crmu_pwr_good MEM resource missing\n");
+		ret = -ENOMEM;
+		goto fail;
+	}
+	iproc_rtc->crmu_regs = (struct crmu_regs_t *)
+		devm_ioremap_resource(&pdev->dev, res);
+	if (!iproc_rtc->crmu_regs) {
+		dev_err(&pdev->dev, "unable to ioremap crmu_pwr_good MEM resource\n");
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "crmu_bbl_auth MEM resource missing\n");
+		ret = -ENOMEM;
+		goto fail;
+	}
+	iproc_rtc->auth_regs = (struct bbl_auth_t *)
+		devm_ioremap_resource(&pdev->dev, res);
+	if (!iproc_rtc->auth_regs) {
+		dev_err(&pdev->dev, "unable to ioremap crmu_bbl_auth MEM resource\n");
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	platform_set_drvdata(pdev, iproc_rtc);
+
+	ret = bbl_init(iproc_rtc);
+	if (ret < 0)
+		goto fail;
+
+	ret = iproc_rtc_enable(iproc_rtc);
+	if (ret < 0) {
+		pr_err("RTC: Enable failed");
+		goto fail_bbl;
+	}
+
+	ret = devm_request_irq
+		(&pdev->dev, iproc_rtc->periodic_irq, iproc_rtc_interrupt,
+		 IRQF_SHARED, "iproc_rtc_peri", iproc_rtc);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "unable to register iproc RTC periodic interrupt\n");
+		goto fail_rtc;
+	}
+
+	ret = devm_request_irq
+		(&pdev->dev, iproc_rtc->alarm_irq, iproc_rtc_interrupt, 0,
+		"iproc_rtc_alarm", iproc_rtc);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "unable to register iproc RTC alarm interrupt\n");
+		goto fail_rtc;
+	}
+
+	iproc_rtc->rtc =  devm_rtc_device_register(&pdev->dev, "iproc-rtc",
+			&iproc_rtc_ops, THIS_MODULE);
+	if (IS_ERR(iproc_rtc->rtc)) {
+		dev_err(&pdev->dev, "unable to register RTC device, err %ld\n",
+				PTR_ERR(iproc_rtc->rtc));
+		ret = PTR_ERR(iproc_rtc->rtc);
+		goto fail_rtc;
+	}
+
+	device_init_wakeup(&pdev->dev, 1);
+	return 0;
+
+fail_rtc:
+	iproc_rtc_disable(iproc_rtc);
+fail_bbl:
+	bbl_exit(iproc_rtc);
+fail:
+	platform_set_drvdata(pdev, NULL);
+	return ret;
+}
+
+static int iproc_rtc_remove(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct iproc_rtc_t *iproc_rtc = platform_get_drvdata(pdev);
+
+	device_init_wakeup(&pdev->dev, 0);
+	rtc_device_unregister(iproc_rtc->rtc);
+	ret = iproc_rtc_disable(iproc_rtc);
+	if (ret < 0)
+		pr_err("RTC: Disable failed");
+	bbl_exit(iproc_rtc);
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver iproc_rtc_driver = {
+	.probe		= iproc_rtc_probe,
+	.remove		= iproc_rtc_remove,
+	.driver		= {
+		.name = "iproc-rtc",
+		.of_match_table = iproc_rtc_of_match
+	},
+};
+
+module_platform_driver(iproc_rtc_driver);
+
+MODULE_AUTHOR("Broadcom");
+MODULE_DESCRIPTION("Broadcom IPROC RTC Driver");
+MODULE_LICENSE("GPL");