* Re: [RFC PATCH V3 3/5] powerpc/cpuidle: Generic powerpc backend cpuidle driver.
From: Deepthi Dharwar @ 2013-08-19 10:18 UTC (permalink / raw)
To: Wang Dongsheng-B40534
Cc: Wood Scott-B07421, daniel.lezcano@linaro.org,
preeti@linux.vnet.ibm.com, linux-pm@lists.linux-foundation.org,
linuxppc-dev@lists.ozlabs.org
In-Reply-To: <ABB05CD9C9F68C46A5CEDC7F15439259010226D5@039-SN2MPN1-021.039d.mgd.msft.net>
Hi Dongsheng,
On 08/19/2013 11:22 AM, Wang Dongsheng-B40534 wrote:
> I think we should move the states and handle function to arch/power/platform*
> The states and handle function is belong to backend driver, not for this, different platform have different state.
> Different platforms to make their own deal with these states.
>
> I think we cannot put all the status of different platforms and handler in this driver.
The idea here is a single powerpc back-end driver, which does a runtime
detection of the platform it is running and choose the right
idle states table. This was one of outcome of V2 discussion.
I feel there is no harm in keeping the state information in the same
file. We do have x86, which has all its variants information in one
file. One place will have all the idle consolidated information of
all the platform variants. If community does feel, we need to
have just the states information in arch specific file, we can do so.
>> diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig
>> index 0e2cd5c..99ee5d4 100644
>> --- a/drivers/cpuidle/Kconfig
>> +++ b/drivers/cpuidle/Kconfig
>> @@ -42,6 +42,13 @@ config CPU_IDLE_ZYNQ
>> help
>> Select this to enable cpuidle on Xilinx Zynq processors.
>>
>> +config CPU_IDLE_POWERPC
>> + bool "CPU Idle driver for POWERPC platforms"
>> + depends on PPC64
>
> Why not PPC?
PPC64 seems to a good place to began the consolidation work. This
patch-set has not been tested for PPC32 currently.
>
>> + default y
>> + help
>> + Select this option to enable processor idle state management
>> + for POWERPC platform.
>> endif
>>
>> config ARCH_NEEDS_CPU_IDLE_COUPLED
>> diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile
>> index 8767a7b..d12e205 100644
>> --- a/drivers/cpuidle/Makefile
>> +++ b/drivers/cpuidle/Makefile
>> @@ -8,3 +8,5 @@ obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o
>> obj-$(CONFIG_CPU_IDLE_CALXEDA) += cpuidle-calxeda.o
>> obj-$(CONFIG_ARCH_KIRKWOOD) += cpuidle-kirkwood.o
>> obj-$(CONFIG_CPU_IDLE_ZYNQ) += cpuidle-zynq.o
>> +
>> +obj-$(CONFIG_CPU_IDLE_POWERPC) += cpuidle-powerpc.o
>> diff --git a/drivers/cpuidle/cpuidle-powerpc.c b/drivers/cpuidle/cpuidle-
>> powerpc.c
>> new file mode 100644
>> index 0000000..5756085
>> --- /dev/null
>> +++ b/drivers/cpuidle/cpuidle-powerpc.c
>> @@ -0,0 +1,361 @@
>> +/*
>> + * processor_idle - idle state cpuidle driver.
>> + * Adapted from drivers/idle/intel_idle.c and
>> + * drivers/acpi/processor_idle.c
>> + *
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/init.h>
>> +#include <linux/moduleparam.h>
>> +#include <linux/cpuidle.h>
>> +#include <linux/cpu.h>
>> +#include <linux/notifier.h>
>> +
>> +#include <asm/paca.h>
>> +#include <asm/reg.h>
>> +#include <asm/machdep.h>
>> +#include <asm/firmware.h>
>> +#include <asm/runlatch.h>
>> +#include <asm/plpar_wrappers.h>
>> +
>> +struct cpuidle_driver powerpc_idle_driver = {
>> + .name = "powerpc_idle",
>> + .owner = THIS_MODULE,
>> +};
>> +
>> +#define MAX_IDLE_STATE_COUNT 2
>> +
>> +static int max_idle_state = MAX_IDLE_STATE_COUNT - 1;
> If this is a generic driver, do not define MAX_IDLE_STATE_COUNT, because we don't know how many state on other platforms.
>
> How about using ARRAY_SIZE to get the max idle state?
>
Yes, I do agree. We need a generic way to return the no of idle states.
>> +static struct cpuidle_device __percpu *powerpc_cpuidle_devices;
>> +static struct cpuidle_state *cpuidle_state_table;
>> +
> Should be remove all about *device*.
> If the notifier handle using device, you can use "cpuidle_devices"(include/linux/cpuidle.h).
The hotplug notifier has a dependency to the cpu device struct.
Yes, I agree using this is way to go forward. As outlined in the cover
cpuidle cleanups will be taken in subsequent versions.
>> +static inline void idle_loop_prolog(unsigned long *in_purr)
>> +{
>> + *in_purr = mfspr(SPRN_PURR);
>> + /*
>> + * Indicate to the HV that we are idle. Now would be
>> + * a good time to find other work to dispatch.
>> + */
>> + set_lppaca_idle(1);
>> +}
>> +
>> +static inline void idle_loop_epilog(unsigned long in_purr)
>> +{
>> + add_lppaca_wait_state(mfspr(SPRN_PURR) - in_purr);
>> + set_lppaca_idle(0);
>> +}
>> +
>> +static int snooze_loop(struct cpuidle_device *dev,
>> + struct cpuidle_driver *drv,
>> + int index)
>> +{
>> + unsigned long in_purr;
>> +
>> + idle_loop_prolog(&in_purr);
>> + local_irq_enable();
>> + set_thread_flag(TIF_POLLING_NRFLAG);
>> +
>> + while (!need_resched()) {
>> + ppc64_runlatch_off();
>> + HMT_low();
>> + HMT_very_low();
>> + }
>> +
>> + HMT_medium();
>> + clear_thread_flag(TIF_POLLING_NRFLAG);
>> + smp_mb();
>> +
>> + idle_loop_epilog(in_purr);
>> +
>> + return index;
>> +}
>> +
>> +static void check_and_cede_processor(void)
>> +{
>> + /*
>> + * Ensure our interrupt state is properly tracked,
>> + * also checks if no interrupt has occurred while we
>> + * were soft-disabled
>> + */
>> + if (prep_irq_for_idle()) {
>> + cede_processor();
>> +#ifdef CONFIG_TRACE_IRQFLAGS
>> + /* Ensure that H_CEDE returns with IRQs on */
>> + if (WARN_ON(!(mfmsr() & MSR_EE)))
>> + __hard_irq_enable();
>> +#endif
>> + }
>> +}
>> +
>> +static int dedicated_cede_loop(struct cpuidle_device *dev,
>> + struct cpuidle_driver *drv,
>> + int index)
>> +{
>> + unsigned long in_purr;
>> +
>> + idle_loop_prolog(&in_purr);
>> + set_lppaca_donate_dedicated_cpu(1);
>> +
>> + ppc64_runlatch_off();
>> + HMT_medium();
>> + check_and_cede_processor();
>> +
>> + set_lppaca_donate_dedicated_cpu(0);
>> + idle_loop_epilog(in_purr);
>> +
>> + return index;
>> +}
>> +
>> +static int shared_cede_loop(struct cpuidle_device *dev,
>> + struct cpuidle_driver *drv,
>> + int index)
>> +{
>> + unsigned long in_purr;
>> +
>> + idle_loop_prolog(&in_purr);
>> +
>> + /*
>> + * Yield the processor to the hypervisor. We return if
>> + * an external interrupt occurs (which are driven prior
>> + * to returning here) or if a prod occurs from another
>> + * processor. When returning here, external interrupts
>> + * are enabled.
>> + */
>> + check_and_cede_processor();
>> +
>> + idle_loop_epilog(in_purr);
>> +
>> + return index;
>> +}
>> +
>> +/*
>> + * States for dedicated partition case.
>> + */
>> +static struct cpuidle_state dedicated_states[MAX_IDLE_STATE_COUNT] = {
>> + { /* Snooze */
>> + .name = "snooze",
>> + .desc = "snooze",
>> + .flags = CPUIDLE_FLAG_TIME_VALID,
>> + .exit_latency = 0,
>> + .target_residency = 0,
>> + .enter = &snooze_loop },
>> + { /* CEDE */
>> + .name = "CEDE",
>> + .desc = "CEDE",
>> + .flags = CPUIDLE_FLAG_TIME_VALID,
>> + .exit_latency = 10,
>> + .target_residency = 100,
>> + .enter = &dedicated_cede_loop },
>> +};
>> +
>> +/*
>> + * States for shared partition case.
>> + */
>> +static struct cpuidle_state shared_states[MAX_IDLE_STATE_COUNT] = {
>> + { /* Shared Cede */
>> + .name = "Shared Cede",
>> + .desc = "Shared Cede",
>> + .flags = CPUIDLE_FLAG_TIME_VALID,
>> + .exit_latency = 0,
>> + .target_residency = 0,
>> + .enter = &shared_cede_loop },
>> +};
>> +
>> +void update_smt_snooze_delay(int cpu, int residency)
>> +{
>> + struct cpuidle_driver *drv = cpuidle_get_driver();
>> + struct cpuidle_device *dev = per_cpu(cpuidle_devices, cpu);
>> +
>> + if (cpuidle_state_table != dedicated_states)
>> + return;
>> +
>> + if (residency < 0) {
>> + /* Disable the Nap state on that cpu */
>> + if (dev)
>> + dev->states_usage[1].disable = 1;
>> + } else
>> + if (drv)
>> + drv->states[1].target_residency = residency;
>> +}
>> +
>> +static int powerpc_cpuidle_add_cpu_notifier(struct notifier_block *n,
>> + unsigned long action, void *hcpu)
>> +{
>> + int hotcpu = (unsigned long)hcpu;
>> + struct cpuidle_device *dev =
>> + per_cpu_ptr(powerpc_cpuidle_devices, hotcpu);
>> +
>> + if (dev && cpuidle_get_driver()) {
>> + switch (action) {
>> + case CPU_ONLINE:
>> + case CPU_ONLINE_FROZEN:
>> + cpuidle_pause_and_lock();
>> + cpuidle_enable_device(dev);
>> + cpuidle_resume_and_unlock();
>> + break;
>> +
>> + case CPU_DEAD:
>> + case CPU_DEAD_FROZEN:
>> + cpuidle_pause_and_lock();
>> + cpuidle_disable_device(dev);
>> + cpuidle_resume_and_unlock();
>> + break;
>> +
>> + default:
>> + return NOTIFY_DONE;
>> + }
>> + }
>> + return NOTIFY_OK;
>> +}
>> +
>> +static struct notifier_block setup_hotplug_notifier = {
>> + .notifier_call = powerpc_cpuidle_add_cpu_notifier,
>> +};
>> +
> We should discuss this with Daniel.
Yes, having a single cpuidle hotplug notifier across
all archs removes a lot of code duplication. But this would involve
changes across archs that is a huge feature by itself and extensive
testing. This is not in the per-view of current patch series but will be
taken up separately.
>> +/*
>> + * powerpc_cpuidle_driver_init()
>> + */
>> +static int powerpc_cpuidle_driver_init(void)
>> +{
>> + int idle_state;
>> + struct cpuidle_driver *drv = &powerpc_idle_driver;
>> +
>> + drv->state_count = 0;
>> +
>> + for (idle_state = 0; idle_state < MAX_IDLE_STATE_COUNT;
>> ++idle_state) {
>> +
>> + if (idle_state > max_idle_state)
>> + break;
>> +
>> + /* is the state not enabled? */
>> + if (cpuidle_state_table[idle_state].enter == NULL)
>> + continue;
>> +
> Did the state have dependent?
> If yes, may be should break out the loop, not continue.
Dependent in what way ?
>> + drv->states[drv->state_count] = /* structure copy */
>> + cpuidle_state_table[idle_state];
>> +
>> + drv->state_count += 1;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +/* powerpc_idle_devices_uninit(void)
>> + * unregister cpuidle devices and de-allocate memory
>> + */
>> +static void powerpc_idle_devices_uninit(void)
>> +{
>> + int i;
>> + struct cpuidle_device *dev;
>> +
>> + for_each_possible_cpu(i) {
>> + dev = per_cpu_ptr(powerpc_cpuidle_devices, i);
>> + cpuidle_unregister_device(dev);
>> + }
>> +
>> + free_percpu(powerpc_cpuidle_devices);
>> + return;
>> +}
>> +
>> +/* powerpc_idle_devices_init()
>> + * allocate, initialize and register cpuidle device
>> + */
>> +static int powerpc_idle_devices_init(void)
>> +{
>> + int i;
>> + struct cpuidle_driver *drv = &powerpc_idle_driver;
>> + struct cpuidle_device *dev;
>> +
>> + powerpc_cpuidle_devices = alloc_percpu(struct cpuidle_device);
>> + if (powerpc_cpuidle_devices == NULL)
>> + return -ENOMEM;
>> +
>> + for_each_possible_cpu(i) {
>> + dev = per_cpu_ptr(powerpc_cpuidle_devices, i);
>> + dev->state_count = drv->state_count;
>> + dev->cpu = i;
>> + if (cpuidle_register_device(dev)) {
>
> Please use cpuidle_register().
>
>> + printk(KERN_DEBUG \
>> + "cpuidle_register_device %d failed!\n", i);
>> + return -EIO;
>> + }
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +/*
>> + * powerpc_idle_probe()
>> + * Choose state table for shared versus dedicated partition
>> + */
>> +static int powerpc_idle_probe(void)
>> +{
>> +
>> + if (!firmware_has_feature(FW_FEATURE_SPLPAR))
>> + return -ENODEV;
>> +
>> + if (cpuidle_disable != IDLE_NO_OVERRIDE)
>> + return -ENODEV;
>> +
>> + if (max_idle_state == 0) {
>> + printk(KERN_DEBUG "powerpc processor idle disabled.\n");
>> + return -EPERM;
>> + }
>> +
>> + if (firmware_has_feature(FW_FEATURE_SPLPAR)) {
>> + if (get_lppaca_is_shared_proc() == 1)
>> + cpuidle_state_table = shared_states;
>> + else if (get_lppaca_is_shared_proc() == 0)
>> + cpuidle_state_table = dedicated_states;
>> + } else
>> + return -ENODEV;
>> +
>> + return 0;
>> +}
>> +
>> +static int __init powerpc_processor_idle_init(void)
>> +{
>> + int retval;
>> +
>> + retval = powerpc_idle_probe();
>> + if (retval)
>> + return retval;
>> +
>> + powerpc_cpuidle_driver_init();
>> + retval = cpuidle_register_driver(&powerpc_idle_driver);
>> + if (retval) {
>> + printk(KERN_DEBUG "Registration of powerpc driver failed.\n");
>> + return retval;
>> + }
>> +
>> + retval = powerpc_idle_devices_init();
>> + if (retval) {
>> + powerpc_idle_devices_uninit();
>> + cpuidle_unregister_driver(&powerpc_idle_driver);
>> + return retval;
>> + }
>> +
>> + register_cpu_notifier(&setup_hotplug_notifier);
>> + printk(KERN_DEBUG "powerpc_idle_driver registered\n");
>> +
>> + return 0;
>> +}
>> +
>> +static void __exit powerpc_processor_idle_exit(void)
>> +{
>> +
>> + unregister_cpu_notifier(&setup_hotplug_notifier);
>> + powerpc_idle_devices_uninit();
>> + cpuidle_unregister_driver(&powerpc_idle_driver);
>> +
>> + return;
>> +}
>> +
> Did you test module mode? *Remove* the module cannot work.
>>
This is currently in-built module as there is a dependency in
kernel/sysfs.c currently. Going forward we will look to have this as a
module.
This is just an RFC patch to see if we can go forward this line. Thanks
so much for the review. I have duly noted down the issues
that will be addressed in the coming versions.
Regards,
Deepthi
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
>
>
^ permalink raw reply
* Re: [PATCH v7 1/2] ASoC: fsl: Add S/PDIF CPU DAI driver
From: Nicolin Chen @ 2013-08-19 10:13 UTC (permalink / raw)
To: Mark Rutland
Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org,
lars@metafoo.de, swarren@wwwdotorg.org, festevam@gmail.com,
s.hauer@pengutronix.de, timur@tabi.org, rob.herring@calxeda.com,
tomasz.figa@gmail.com, broonie@kernel.org, p.zabel@pengutronix.de,
R65777@freescale.com, shawn.guo@linaro.org,
linuxppc-dev@lists.ozlabs.org
In-Reply-To: <20130819095433.GG3719@e106331-lin.cambridge.arm.com>
On Mon, Aug 19, 2013 at 10:54:33AM +0100, Mark Rutland wrote:
> > I see, so 'Compatible list, must contains "fsl,imx35-spdif"' would be okay?
>
> While that needs to be mentioned, other values which might be present
> (e.g. "fsl,imx6q-spdif") must be mentioned, or we can't rely on them
> if we want to use them in future from drivers to provide additional
> information (and thus they're useless).
Well, imx6q-spdif is identical to the imx35-spdif. So I think the
'must contains 35' would be enough for the current case. If any
future modification happens to the list, I can update this doc later.
> >
> > > > + - interrupts : Contains spdif interrupt.
> > >
> > > Is that the only interrupt the device generates?
> >
> > Yes, how could I improve this description?
>
> It's probably not possible to make it much clearar to be honest,
> "Contains the sole interrupt generated by the device" might be a little
> overkill.
Then I keep it no-change :)
> > > > + "rxtx<0-7>" Clock source list for tx and rx clock.
> > > > + This clock list should be identical to
> > > > + the source list connecting to the spdif
> > > > + clock mux in "SPDIF Transceiver Clock
> > > > + Diagram" of SoC reference manual. It
> > > > + can also be referred to TxClk_Source
> > > > + bit of register SPDIF_STC.
> > The list is also identical to the TxClk_Source bit value list of
> > register SPDIF_STC.
>
> What I don't understand is how the value of the SPDIF_STC register
> within the spdif IP block can describe the necessary details of clocks
> coming from an external clock provider.
>
> What do the TxClk_Source bits represent? The configuration of the clock
> inputs on the spdif IP block, or the outputs on some clock provider? Are
> they writeable, or configured at integration time? If the clock provider
> were replaced with another arbitrary clock provider, what would they
> represent?
Actually there's a clock mux for TxClk and it's connecting with 8 clock
sources. So the TxClk_Source bit show the connection between its value
with the correspond source on the clock mux:
TxClk_Source 000 XTAL clk input
001 CCM spdif0_clk_root input
010 asrc_clk input
011 spdif_extclk input, from pads
100 esai_hckt input
101 frequency divided ipg_clk input
110 mlb_clk input
111 mlb phy clk input
> > I guess it's better to drop the 'imx6q-spdif' here?
>
> That depends:
>
> * If the two IP blocks are identical, only the "imx35-spdif" name is
> necessary, and we can forget about "fsl,imx6q-spdif".
>
> * If "fsl,imx6q-spdif" is a strict superset of "fsl,imx35-spdif", having
> both names documented and in a compatible list for a "fsl,imx6q-spdif"
> device makes sense.
>
> * If "fsl,imx6q-spdif" is a variation of "fsl,imx35-spdif", and the
> "fsl,imx6q-spdif" cannot always be treated identically to a
> "fsl,imx35-spdif", then it makes sense to have separate compatible
> strings, with a device being listed as either "fsl,imx6q-spdif" or
> "fsl,imx35-spdif".
>
> I don't know enough about the hardware to make that judgement call.
Thank you for explaining! I will choose A, because they are internally
identical except their external clock sources.
Best regards,
Nicolin
^ permalink raw reply
* Re: [RFC PATCH v2 3/4] powerpc: refactor of_get_cpu_node to support other architectures
From: Sudeep KarkadaNagesha @ 2013-08-19 10:13 UTC (permalink / raw)
To: Benjamin Herrenschmidt
Cc: Jonas Bonn, devicetree@vger.kernel.org, Michal Simek,
linux-pm@vger.kernel.org, Sudeep KarkadaNagesha,
linux-kernel@vger.kernel.org, rob.herring@calxeda.com,
Rafael J. Wysocki, grant.likely@linaro.org,
linuxppc-dev@lists.ozlabs.org,
linux-arm-kernel@lists.infradead.org
In-Reply-To: <1376691186.25016.4.camel@pasglop>
On 16/08/13 23:13, Benjamin Herrenschmidt wrote:
> On Fri, 2013-08-16 at 18:39 +0100, Sudeep KarkadaNagesha wrote:
>> +static bool __of_find_n_match_cpu_property(struct device_node *cpun,
>> + const char *prop_name, int cpu, unsigned int
>> *thread)
>> +{
>> + const __be32 *cell;
>> + int ac, prop_len, tid;
>> + u64 hwid;
>> +
>> + ac =3D of_n_addr_cells(cpun);
>> + cell =3D of_get_property(cpun, prop_name, &prop_len);
>> + if (!cell)
>> + return false;
>> + prop_len /=3D sizeof(*cell);
>> + for (tid =3D 0; tid < prop_len; tid++) {
>> + hwid =3D of_read_number(cell, ac);
>> + if (arch_match_cpu_phys_id(cpu, hwid)) {
>> + if (thread)
>> + *thread =3D tid;
>> + return true;
>> + }
>> + cell +=3D ac;
>> + }
>> + return false;
>> +}
>=20
> The only problem I can see here is if "ac" is not 1, that will not work
> for the ibm,ppc-interrupt-server#s case. IE. The latter is always 1 cell
> per entry, only "reg" depends on #address-cells.
>=20
> However that's only a theorical problem since on ppc #address-cells of
> /cpus is always 1...
>=20
Ok agreed, but I assume in future if thread ids need 2 ac, it would use
standard 'reg' instead of 'ibm,ppc-interrupt-server#' property.
So I assume the above function is generic and need not be modified to
handle non '1' ac case with non standard 'ibm,ppc-interrupt-server#'.
Regards,
Sudeep
^ permalink raw reply
* Re: [PATCH v7 2/2] ASoC: fsl: Add S/PDIF machine driver
From: Mark Rutland @ 2013-08-19 10:01 UTC (permalink / raw)
To: Nicolin Chen, broonie@kernel.org
Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org,
lars@metafoo.de, swarren@wwwdotorg.org, festevam@gmail.com,
s.hauer@pengutronix.de, timur@tabi.org, rob.herring@calxeda.com,
tomasz.figa@gmail.com, p.zabel@pengutronix.de,
R65777@freescale.com, shawn.guo@linaro.org,
linuxppc-dev@lists.ozlabs.org
In-Reply-To: <20130819095042.GA11402@MrMyself>
On Mon, Aug 19, 2013 at 10:50:43AM +0100, Nicolin Chen wrote:
> Hi,
>
> On Mon, Aug 19, 2013 at 10:24:58AM +0100, Mark Rutland wrote:
> > Is this used semantically, or is it a completely arbitrary string? In
> > either case I don't see why the compatible string doesn't give the
> > driver enough to have a sensible value.
> >
> > I'm confused as to why we need this. The phrase "user-visible" in a
> > device description seems very odd.
>
> The string would be in the ALSA device list:
> ALSA device list:
> #0: imx-spdif
>
> I think it can be a sort of arbitrary as long as users know which this
> device exactly is when they catch the name by 'aplay -l' or 'arecord -l'
>
> The phrase "user-visible" is being used in many current docs, I don't
> dare to change it unless a sage gives me a suggestion.
I can see that there is entrenched usage, but this really seems to be
embedding Linux-specific implementation details into the dt. I don't see
why the driver cannot select a sensible name, but perhaps I'm missing
something.
Mark, is there any reason we need to handle the user-visible name of the
device this way?
>
> > > +
> > > + - spdif-controller : The phandle of the i.MX S/PDIF controller
> > > +
> > > +
> > > +Optional properties:
> > > +
> > > + - spdif-transmitter : The phandle of the spdif-transmitter dummy codec
> > > +
> > > + - spdif-receiver : The phandle of the spdif-receiver dummy codec
> > > +
> > > +* Note: At least one of these two properties should be set in the DT binding.
> >
> > Are all four units (comlpex,controller,transmitter,receiver) really
> > separate blocks?
>
> At least they are separate drivers as I mentioned in the commit comments.
I'm not sure that the boundary of Linux drivers should necessarily
determine the way we carve up the description of IP blocks, though
presumably it's a pretty sensible way of carving it up or we wouldn't
have done it.
Is there any public documentation on the i.MX S/PDIF hardware block(s)?
Thanks,
Mark.
^ permalink raw reply
* Re: [PATCH v7 1/2] ASoC: fsl: Add S/PDIF CPU DAI driver
From: Mark Rutland @ 2013-08-19 9:54 UTC (permalink / raw)
To: Nicolin Chen
Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org,
lars@metafoo.de, swarren@wwwdotorg.org, festevam@gmail.com,
s.hauer@pengutronix.de, timur@tabi.org, rob.herring@calxeda.com,
tomasz.figa@gmail.com, broonie@kernel.org, p.zabel@pengutronix.de,
R65777@freescale.com, shawn.guo@linaro.org,
linuxppc-dev@lists.ozlabs.org
In-Reply-To: <20130819093423.GB10950@MrMyself>
On Mon, Aug 19, 2013 at 10:34:24AM +0100, Nicolin Chen wrote:
> Hi Mark,
>
> Thank you for the commenst. I'll Fix them in v8.
>
> Here are some remaining question:
>
> On Mon, Aug 19, 2013 at 10:18:09AM +0100, Mark Rutland wrote:
> > > +Required properties:
> > > +
> > > + - compatible : Compatible list, contains "fsl,<chip>-spdif".
> >
> > What are valid values for <chip>? The binding should mention this. There
> > are bindings that don't, but they need to be fixed. Undocumented ABIs
> > are a bad idea.
>
> I see, so 'Compatible list, must contains "fsl,imx35-spdif"' would be okay?
While that needs to be mentioned, other values which might be present
(e.g. "fsl,imx6q-spdif") must be mentioned, or we can't rely on them
if we want to use them in future from drivers to provide additional
information (and thus they're useless).
>
> > > + - interrupts : Contains spdif interrupt.
> >
> > Is that the only interrupt the device generates?
>
> Yes, how could I improve this description?
It's probably not possible to make it much clearar to be honest,
"Contains the sole interrupt generated by the device" might be a little
overkill.
>
>
> > > + "core" The core clock of spdif controller
> > > + "rxtx<0-7>" Clock source list for tx and rx clock.
> > > + This clock list should be identical to
> > > + the source list connecting to the spdif
> > > + clock mux in "SPDIF Transceiver Clock
> > > + Diagram" of SoC reference manual. It
> > > + can also be referred to TxClk_Source
> > > + bit of register SPDIF_STC.
> >
> > Could you elaborate on the last sentence? I'm not sure exactly what you
> > meant.
>
> The list is also identical to the TxClk_Source bit value list of
> register SPDIF_STC.
What I don't understand is how the value of the SPDIF_STC register
within the spdif IP block can describe the necessary details of clocks
coming from an external clock provider.
What do the TxClk_Source bits represent? The configuration of the clock
inputs on the spdif IP block, or the outputs on some clock provider? Are
they writeable, or configured at integration time? If the clock provider
were replaced with another arbitrary clock provider, what would they
represent?
>
> >
> > > +
> > > +Example:
> > > +
> > > +spdif: spdif@02004000 {
> > > + compatible = "fsl,imx6q-spdif",
> > > + "fsl,imx35-spdif";
> >
> > Is "fsl,imx35-spdif" necessary in the list, or is it not the case all
> > "fsl,<chip>-spdif" variants are compatible with it?
> >
> > That should be mentioned along with the list of valid compatible
> > strings.
>
> I guess it's better to drop the 'imx6q-spdif' here?
That depends:
* If the two IP blocks are identical, only the "imx35-spdif" name is
necessary, and we can forget about "fsl,imx6q-spdif".
* If "fsl,imx6q-spdif" is a strict superset of "fsl,imx35-spdif", having
both names documented and in a compatible list for a "fsl,imx6q-spdif"
device makes sense.
* If "fsl,imx6q-spdif" is a variation of "fsl,imx35-spdif", and the
"fsl,imx6q-spdif" cannot always be treated identically to a
"fsl,imx35-spdif", then it makes sense to have separate compatible
strings, with a device being listed as either "fsl,imx6q-spdif" or
"fsl,imx35-spdif".
I don't know enough about the hardware to make that judgement call.
Cheers,
Mark.
^ permalink raw reply
* Re: [PATCH v7 2/2] ASoC: fsl: Add S/PDIF machine driver
From: Nicolin Chen @ 2013-08-19 9:50 UTC (permalink / raw)
To: Mark Rutland
Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org,
lars@metafoo.de, swarren@wwwdotorg.org, festevam@gmail.com,
s.hauer@pengutronix.de, timur@tabi.org, rob.herring@calxeda.com,
tomasz.figa@gmail.com, broonie@kernel.org, p.zabel@pengutronix.de,
R65777@freescale.com, shawn.guo@linaro.org,
linuxppc-dev@lists.ozlabs.org
In-Reply-To: <20130819092458.GE3719@e106331-lin.cambridge.arm.com>
Hi,
On Mon, Aug 19, 2013 at 10:24:58AM +0100, Mark Rutland wrote:
> Is this used semantically, or is it a completely arbitrary string? In
> either case I don't see why the compatible string doesn't give the
> driver enough to have a sensible value.
>
> I'm confused as to why we need this. The phrase "user-visible" in a
> device description seems very odd.
The string would be in the ALSA device list:
ALSA device list:
#0: imx-spdif
I think it can be a sort of arbitrary as long as users know which this
device exactly is when they catch the name by 'aplay -l' or 'arecord -l'
The phrase "user-visible" is being used in many current docs, I don't
dare to change it unless a sage gives me a suggestion.
> > +
> > + - spdif-controller : The phandle of the i.MX S/PDIF controller
> > +
> > +
> > +Optional properties:
> > +
> > + - spdif-transmitter : The phandle of the spdif-transmitter dummy codec
> > +
> > + - spdif-receiver : The phandle of the spdif-receiver dummy codec
> > +
> > +* Note: At least one of these two properties should be set in the DT binding.
>
> Are all four units (comlpex,controller,transmitter,receiver) really
> separate blocks?
At least they are separate drivers as I mentioned in the commit comments.
Thank you,
Nicolin
^ permalink raw reply
* Re: [PATCH v5 1/2] ASoC: fsl: Add S/PDIF CPU DAI driver
From: Mark Rutland @ 2013-08-19 9:35 UTC (permalink / raw)
To: Tomasz Figa
Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org,
lars@metafoo.de, mturquette@linaro.org, ian.campbell@citrix.com,
Pawel Moll, swarren@wwwdotorg.org, festevam@gmail.com,
Sascha Hauer, Nicolin Chen, timur@tabi.org,
rob.herring@calxeda.com, broonie@kernel.org,
p.zabel@pengutronix.de, galak@codeaurora.org,
shawn.guo@linaro.org, linuxppc-dev@lists.ozlabs.org
In-Reply-To: <8770601.066pCcRnTh@flatron>
On Sat, Aug 17, 2013 at 04:17:18PM +0100, Tomasz Figa wrote:
> On Saturday 17 of August 2013 16:53:16 Sascha Hauer wrote:
> > On Sat, Aug 17, 2013 at 02:28:04PM +0200, Tomasz Figa wrote:
> > > > > > Also I would make this option required. Use a dummy clock for
> > > > > > mux
> > > > > > inputs that are grounded for a specific SoC.
> > > > >
> > > > > Some clocks are not from CCM and we haven't defined in
> > > > > imx6q-clk.txt,
> > > > > so in most cases we can't provide a phandle for them, eg:
> > > > > spdif_ext.
> > > > > I think it's a bit hard to force it to be 'required'. An
> > > > > 'optional'
> > > > > looks more flexible to me and a default one is ensured even if
> > > > > it's
> > > > > missing.
> > > >
> > > > <&clks 0> is the dummy clock. This can be used for all input clocks
> > > > not
> > > > defined by the SoC.
> > >
> > > Where does this assumption come from? Is it documented anywhere?
> >
> > This is how all i.MX clock bindings currently are. See
> > Documentation/devicetree/bindings/clock/imx*-clock.txt
>
> OK, thanks.
>
> I guess we need some discussion on dummy clocks vs skipped clocks. I think
> we want some consistency on this, don't we?
>
> If we really need a dummy clock, then we might also want a generic way to
> specify it.
What do we actually mean by a "dummy clock"? We already have bindings
for "fixed-clock" and co friends describe relatively simple
preconfigured clocks.
If a clock isn't actually wired, we shouldn't describe it at all, or
we're describing what Linux wants to think rather than what the hardware
actually is. That can easily be handled with clock-names.
Thanks,
Mark.
^ permalink raw reply
* Re: [PATCH v7 1/2] ASoC: fsl: Add S/PDIF CPU DAI driver
From: Nicolin Chen @ 2013-08-19 9:34 UTC (permalink / raw)
To: Mark Rutland
Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org,
lars@metafoo.de, swarren@wwwdotorg.org, festevam@gmail.com,
s.hauer@pengutronix.de, timur@tabi.org, rob.herring@calxeda.com,
tomasz.figa@gmail.com, broonie@kernel.org, p.zabel@pengutronix.de,
R65777@freescale.com, shawn.guo@linaro.org,
linuxppc-dev@lists.ozlabs.org
In-Reply-To: <20130819091809.GD3719@e106331-lin.cambridge.arm.com>
Hi Mark,
Thank you for the commenst. I'll Fix them in v8.
Here are some remaining question:
On Mon, Aug 19, 2013 at 10:18:09AM +0100, Mark Rutland wrote:
> > +Required properties:
> > +
> > + - compatible : Compatible list, contains "fsl,<chip>-spdif".
>
> What are valid values for <chip>? The binding should mention this. There
> are bindings that don't, but they need to be fixed. Undocumented ABIs
> are a bad idea.
I see, so 'Compatible list, must contains "fsl,imx35-spdif"' would be okay?
> > + - interrupts : Contains spdif interrupt.
>
> Is that the only interrupt the device generates?
Yes, how could I improve this description?
> > + "core" The core clock of spdif controller
> > + "rxtx<0-7>" Clock source list for tx and rx clock.
> > + This clock list should be identical to
> > + the source list connecting to the spdif
> > + clock mux in "SPDIF Transceiver Clock
> > + Diagram" of SoC reference manual. It
> > + can also be referred to TxClk_Source
> > + bit of register SPDIF_STC.
>
> Could you elaborate on the last sentence? I'm not sure exactly what you
> meant.
The list is also identical to the TxClk_Source bit value list of
register SPDIF_STC.
>
> > +
> > +Example:
> > +
> > +spdif: spdif@02004000 {
> > + compatible = "fsl,imx6q-spdif",
> > + "fsl,imx35-spdif";
>
> Is "fsl,imx35-spdif" necessary in the list, or is it not the case all
> "fsl,<chip>-spdif" variants are compatible with it?
>
> That should be mentioned along with the list of valid compatible
> strings.
I guess it's better to drop the 'imx6q-spdif' here?
Thank you,
Nicolin
^ permalink raw reply
* Re: [PATCH v7 2/2] ASoC: fsl: Add S/PDIF machine driver
From: Mark Rutland @ 2013-08-19 9:24 UTC (permalink / raw)
To: Nicolin Chen
Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org,
lars@metafoo.de, swarren@wwwdotorg.org, festevam@gmail.com,
s.hauer@pengutronix.de, timur@tabi.org, rob.herring@calxeda.com,
tomasz.figa@gmail.com, broonie@kernel.org, p.zabel@pengutronix.de,
R65777@freescale.com, shawn.guo@linaro.org,
linuxppc-dev@lists.ozlabs.org
In-Reply-To: <e7ceef74714bb4ead624b333ee3969225b4c5cb5.1376901081.git.b42378@freescale.com>
On Mon, Aug 19, 2013 at 09:35:22AM +0100, Nicolin Chen wrote:
> This patch implements a device-tree-only machine driver for Freescale
> i.MX series Soc. It works with spdif_transmitter/spdif_receiver and
> fsl_spdif.c drivers.
>
> Signed-off-by: Nicolin Chen <b42378@freescale.com>
> ---
> .../devicetree/bindings/sound/imx-audio-spdif.txt | 29 +++++
> sound/soc/fsl/Kconfig | 11 ++
> sound/soc/fsl/Makefile | 2 +
> sound/soc/fsl/imx-spdif.c | 134 ++++++++++++++++++++
> 4 files changed, 176 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/sound/imx-audio-spdif.txt
> create mode 100644 sound/soc/fsl/imx-spdif.c
>
> diff --git a/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt b/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt
> new file mode 100644
> index 0000000..9a3fa26
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt
> @@ -0,0 +1,29 @@
> +Freescale i.MX audio complex with S/PDIF transceiver
> +
> +Required properties:
> +
> + - compatible : "fsl,imx-audio-spdif"
> +
> + - model : The user-visible name of this sound complex
Is this used semantically, or is it a completely arbitrary string? In
either case I don't see why the compatible string doesn't give the
driver enough to have a sensible value.
I'm confused as to why we need this. The phrase "user-visible" in a
device description seems very odd.
> +
> + - spdif-controller : The phandle of the i.MX S/PDIF controller
> +
> +
> +Optional properties:
> +
> + - spdif-transmitter : The phandle of the spdif-transmitter dummy codec
> +
> + - spdif-receiver : The phandle of the spdif-receiver dummy codec
> +
> +* Note: At least one of these two properties should be set in the DT binding.
Are all four units (comlpex,controller,transmitter,receiver) really
separate blocks?
Thanks,
Mark.
^ permalink raw reply
* Re: [PATCH v7 1/2] ASoC: fsl: Add S/PDIF CPU DAI driver
From: Mark Rutland @ 2013-08-19 9:18 UTC (permalink / raw)
To: Nicolin Chen
Cc: devicetree@vger.kernel.org, alsa-devel@alsa-project.org,
lars@metafoo.de, swarren@wwwdotorg.org, festevam@gmail.com,
s.hauer@pengutronix.de, timur@tabi.org, rob.herring@calxeda.com,
tomasz.figa@gmail.com, broonie@kernel.org, p.zabel@pengutronix.de,
R65777@freescale.com, shawn.guo@linaro.org,
linuxppc-dev@lists.ozlabs.org
In-Reply-To: <43416f3617951161b6e779277b4719438f844e49.1376901081.git.b42378@freescale.com>
On Mon, Aug 19, 2013 at 09:35:21AM +0100, Nicolin Chen wrote:
> This patch implements a device-tree-only CPU DAI driver for Freescale
> S/PDIF controller that supports stereo playback and record feature.
>
> Signed-off-by: Nicolin Chen <b42378@freescale.com>
> ---
> .../devicetree/bindings/sound/fsl,spdif.txt | 56 +
> sound/soc/fsl/Kconfig | 3 +
> sound/soc/fsl/Makefile | 2 +
> sound/soc/fsl/fsl_spdif.c | 1277 ++++++++++++++++++++
> sound/soc/fsl/fsl_spdif.h | 224 ++++
> 5 files changed, 1562 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/sound/fsl,spdif.txt
> create mode 100644 sound/soc/fsl/fsl_spdif.c
> create mode 100644 sound/soc/fsl/fsl_spdif.h
>
> diff --git a/Documentation/devicetree/bindings/sound/fsl,spdif.txt b/Documentation/devicetree/bindings/sound/fsl,spdif.txt
> new file mode 100644
> index 0000000..e9caf1c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/fsl,spdif.txt
> @@ -0,0 +1,56 @@
> +Freescale Sony/Philips Digital Interface Format (S/PDIF) Controller
> +
> +The Freescale S/PDIF audio block is a stereo transceiver that allows the
> +processor to receive and transmit digital audio via an coaxial cable or
> +a fibre cable.
> +
> +Required properties:
> +
> + - compatible : Compatible list, contains "fsl,<chip>-spdif".
What are valid values for <chip>? The binding should mention this. There
are bindings that don't, but they need to be fixed. Undocumented ABIs
are a bad idea.
> +
> + - reg : Offset and length of the register set for the device.
> +
> + - interrupts : Contains spdif interrupt.
Is that the only interrupt the device generates?
> +
> + - dmas : Generic dma devicetree binding as described in
> + Documentation/devicetree/bindings/dma/dma.txt.
> +
> + - dma-names : Two dmas have to be defined, "tx" and "rx".
> +
> + - clocks : Contains an entry for each entry in clock-names.
> +
> + - clock-names : Includes the following entries:
> + name description
I don't think you need this line, it's obvious enough without it.
> + "core" The core clock of spdif controller
> + "rxtx<0-7>" Clock source list for tx and rx clock.
> + This clock list should be identical to
> + the source list connecting to the spdif
> + clock mux in "SPDIF Transceiver Clock
> + Diagram" of SoC reference manual. It
> + can also be referred to TxClk_Source
> + bit of register SPDIF_STC.
Could you elaborate on the last sentence? I'm not sure exactly what you
meant.
> +
> +Example:
> +
> +spdif: spdif@02004000 {
> + compatible = "fsl,imx6q-spdif",
> + "fsl,imx35-spdif";
Is "fsl,imx35-spdif" necessary in the list, or is it not the case all
"fsl,<chip>-spdif" variants are compatible with it?
That should be mentioned along with the list of valid compatible
strings.
> + reg = <0x02004000 0x4000>;
> + interrupts = <0 52 0x04>;
> + dmas = <&sdma 14 18 0>,
> + <&sdma 15 18 0>;
> + dma-names = "rx", "tx";
> +
> + clocks = <&clks 197>, <&clks 3>,
> + <&clks 197>, <&clks 107>,
> + <&clks 0>, <&clks 118>,
> + <&clks 62>, <&clks 139>,
> + <&clks 0>;
> + clock-names = "core", "rxtx0",
> + "rxtx1", "rxtx2",
> + "rxtx3", "rxtx4",
> + "rxtx5", "rxtx6",
> + "rxtx7";
> +
> + status = "okay";
> +};
[...]
> +static int spdif_clk_set_rate(struct clk *clk, unsigned long rate)
> +{
> + unsigned long rate_actual;
> +
> + rate_actual = clk_round_rate(clk, rate);
> + clk_set_rate(clk, rate_actual);
> +
> + return 0;
> +}
Can't clk_set_rate fail?
[...]
> + /* Select clock source for rx/tx clock */
> + spdif_priv->rxclk = devm_clk_get(&pdev->dev, "rxtx1");
> + if (IS_ERR(spdif_priv->rxclk)) {
> + dev_err(&pdev->dev, "no rxtx1 property in devicetree\n");
Saying "no rxtx1 clock in devicetree" would be clearer.
[...]
> +static const struct of_device_id fsl_spdif_dt_ids[] = {
> + { .compatible = "fsl,imx35-spdif", },
> + {}
> +};
So "fsl,imx35-spdif" *must* be in the compatible list. The binding
should mention this.
Thanks,
Mark.
^ permalink raw reply
* [PATCH v7 2/2] ASoC: fsl: Add S/PDIF machine driver
From: Nicolin Chen @ 2013-08-19 8:35 UTC (permalink / raw)
To: broonie, lars, p.zabel, s.hauer
Cc: mark.rutland, devicetree, alsa-devel, swarren, festevam, timur,
rob.herring, tomasz.figa, R65777, shawn.guo, linuxppc-dev
In-Reply-To: <cover.1376901081.git.b42378@freescale.com>
This patch implements a device-tree-only machine driver for Freescale
i.MX series Soc. It works with spdif_transmitter/spdif_receiver and
fsl_spdif.c drivers.
Signed-off-by: Nicolin Chen <b42378@freescale.com>
---
.../devicetree/bindings/sound/imx-audio-spdif.txt | 29 +++++
sound/soc/fsl/Kconfig | 11 ++
sound/soc/fsl/Makefile | 2 +
sound/soc/fsl/imx-spdif.c | 134 ++++++++++++++++++++
4 files changed, 176 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/sound/imx-audio-spdif.txt
create mode 100644 sound/soc/fsl/imx-spdif.c
diff --git a/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt b/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt
new file mode 100644
index 0000000..9a3fa26
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/imx-audio-spdif.txt
@@ -0,0 +1,29 @@
+Freescale i.MX audio complex with S/PDIF transceiver
+
+Required properties:
+
+ - compatible : "fsl,imx-audio-spdif"
+
+ - model : The user-visible name of this sound complex
+
+ - spdif-controller : The phandle of the i.MX S/PDIF controller
+
+
+Optional properties:
+
+ - spdif-transmitter : The phandle of the spdif-transmitter dummy codec
+
+ - spdif-receiver : The phandle of the spdif-receiver dummy codec
+
+* Note: At least one of these two properties should be set in the DT binding.
+
+
+Example:
+
+sound-spdif {
+ compatible = "fsl,imx-audio-spdif";
+ model = "imx-spdif";
+ spdif-controller = <&spdif>;
+ spdif-transmitter = <&spdif_tx_codec>;
+ spdif-receiver = <&spdif_rx_codec>;
+};
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index cd088cc..a708380 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -193,6 +193,17 @@ config SND_SOC_IMX_SGTL5000
Say Y if you want to add support for SoC audio on an i.MX board with
a sgtl5000 codec.
+config SND_SOC_IMX_SPDIF
+ tristate "SoC Audio support for i.MX boards with S/PDIF"
+ select SND_SOC_IMX_PCM_DMA
+ select SND_SOC_FSL_SPDIF
+ select SND_SOC_FSL_UTILS
+ select SND_SOC_SPDIF
+ help
+ SoC Audio support for i.MX boards with S/PDIF
+ Say Y if you want to add support for SoC audio on an i.MX board with
+ a S/DPDIF.
+
config SND_SOC_IMX_MC13783
tristate "SoC Audio support for I.MX boards with mc13783"
depends on MFD_MC13783 && ARM
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index 4b5970e..e2aaff7 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -45,6 +45,7 @@ snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o
snd-soc-wm1133-ev1-objs := wm1133-ev1.o
snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o
snd-soc-imx-wm8962-objs := imx-wm8962.o
+snd-soc-imx-spdif-objs :=imx-spdif.o
snd-soc-imx-mc13783-objs := imx-mc13783.o
obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
@@ -53,4 +54,5 @@ obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o
obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o
obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o
obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o
+obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o
obj-$(CONFIG_SND_SOC_IMX_MC13783) += snd-soc-imx-mc13783.o
diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c
new file mode 100644
index 0000000..893f3d1
--- /dev/null
+++ b/sound/soc/fsl/imx-spdif.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <sound/soc.h>
+
+struct imx_spdif_data {
+ struct snd_soc_dai_link dai[2];
+ struct snd_soc_card card;
+};
+
+static int imx_spdif_audio_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *spdif_np, *codec_tx_np, *codec_rx_np;
+ struct platform_device *spdif_pdev;
+ struct imx_spdif_data *data;
+ int ret = 0, num_links = 0;
+
+ spdif_np = of_parse_phandle(np, "spdif-controller", 0);
+ if (!spdif_np) {
+ dev_err(&pdev->dev, "failed to find spdif-controller\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ spdif_pdev = of_find_device_by_node(spdif_np);
+ if (!spdif_pdev) {
+ dev_err(&pdev->dev, "failed to find S/PDIF device\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ codec_tx_np = of_parse_phandle(np, "spdif-transmitter", 0);
+ if (codec_tx_np) {
+ data->dai[num_links].name = "S/PDIF TX";
+ data->dai[num_links].stream_name = "S/PDIF PCM Playback";
+ data->dai[num_links].codec_dai_name = "dit-hifi";
+ data->dai[num_links].codec_of_node = codec_tx_np;
+ data->dai[num_links].cpu_of_node = spdif_np;
+ data->dai[num_links].platform_of_node = spdif_np;
+ num_links++;
+ }
+
+ codec_rx_np = of_parse_phandle(np, "spdif-receiver", 0);
+ if (codec_rx_np) {
+ data->dai[num_links].name = "S/PDIF RX";
+ data->dai[num_links].stream_name = "S/PDIF PCM Capture";
+ data->dai[num_links].codec_dai_name = "dir-hifi";
+ data->dai[num_links].codec_of_node = codec_rx_np;
+ data->dai[num_links].cpu_of_node = spdif_np;
+ data->dai[num_links].platform_of_node = spdif_np;
+ num_links++;
+ }
+
+ if (!num_links) {
+ dev_err(&pdev->dev, "no enabled S/PDIF DAI link\n");
+ goto fail;
+ }
+
+ data->card.dev = &pdev->dev;
+ data->card.num_links = num_links;
+ data->card.dai_link = data->dai;
+
+ ret = snd_soc_of_parse_card_name(&data->card, "model");
+ if (ret)
+ goto fail;
+
+ ret = snd_soc_register_card(&data->card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
+ goto fail;
+ }
+
+ platform_set_drvdata(pdev, data);
+
+fail:
+ if (codec_tx_np)
+ of_node_put(codec_tx_np);
+ if (codec_rx_np)
+ of_node_put(codec_rx_np);
+ if (spdif_np)
+ of_node_put(spdif_np);
+
+ return ret;
+}
+
+static int imx_spdif_audio_remove(struct platform_device *pdev)
+{
+ struct imx_spdif_data *data = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_card(&data->card);
+
+ return 0;
+}
+
+static const struct of_device_id imx_spdif_dt_ids[] = {
+ { .compatible = "fsl,imx-audio-spdif", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_spdif_dt_ids);
+
+static struct platform_driver imx_spdif_driver = {
+ .driver = {
+ .name = "imx-spdif",
+ .owner = THIS_MODULE,
+ .of_match_table = imx_spdif_dt_ids,
+ },
+ .probe = imx_spdif_audio_probe,
+ .remove = imx_spdif_audio_remove,
+};
+
+module_platform_driver(imx_spdif_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Freescale i.MX S/PDIF machine driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:imx-spdif");
--
1.7.1
^ permalink raw reply related
* [PATCH v7 1/2] ASoC: fsl: Add S/PDIF CPU DAI driver
From: Nicolin Chen @ 2013-08-19 8:35 UTC (permalink / raw)
To: broonie, lars, p.zabel, s.hauer
Cc: mark.rutland, devicetree, alsa-devel, swarren, festevam, timur,
rob.herring, tomasz.figa, R65777, shawn.guo, linuxppc-dev
In-Reply-To: <cover.1376901081.git.b42378@freescale.com>
This patch implements a device-tree-only CPU DAI driver for Freescale
S/PDIF controller that supports stereo playback and record feature.
Signed-off-by: Nicolin Chen <b42378@freescale.com>
---
.../devicetree/bindings/sound/fsl,spdif.txt | 56 +
sound/soc/fsl/Kconfig | 3 +
sound/soc/fsl/Makefile | 2 +
sound/soc/fsl/fsl_spdif.c | 1277 ++++++++++++++++++++
sound/soc/fsl/fsl_spdif.h | 224 ++++
5 files changed, 1562 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/sound/fsl,spdif.txt
create mode 100644 sound/soc/fsl/fsl_spdif.c
create mode 100644 sound/soc/fsl/fsl_spdif.h
diff --git a/Documentation/devicetree/bindings/sound/fsl,spdif.txt b/Documentation/devicetree/bindings/sound/fsl,spdif.txt
new file mode 100644
index 0000000..e9caf1c
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/fsl,spdif.txt
@@ -0,0 +1,56 @@
+Freescale Sony/Philips Digital Interface Format (S/PDIF) Controller
+
+The Freescale S/PDIF audio block is a stereo transceiver that allows the
+processor to receive and transmit digital audio via an coaxial cable or
+a fibre cable.
+
+Required properties:
+
+ - compatible : Compatible list, contains "fsl,<chip>-spdif".
+
+ - reg : Offset and length of the register set for the device.
+
+ - interrupts : Contains spdif interrupt.
+
+ - dmas : Generic dma devicetree binding as described in
+ Documentation/devicetree/bindings/dma/dma.txt.
+
+ - dma-names : Two dmas have to be defined, "tx" and "rx".
+
+ - clocks : Contains an entry for each entry in clock-names.
+
+ - clock-names : Includes the following entries:
+ name description
+ "core" The core clock of spdif controller
+ "rxtx<0-7>" Clock source list for tx and rx clock.
+ This clock list should be identical to
+ the source list connecting to the spdif
+ clock mux in "SPDIF Transceiver Clock
+ Diagram" of SoC reference manual. It
+ can also be referred to TxClk_Source
+ bit of register SPDIF_STC.
+
+Example:
+
+spdif: spdif@02004000 {
+ compatible = "fsl,imx6q-spdif",
+ "fsl,imx35-spdif";
+ reg = <0x02004000 0x4000>;
+ interrupts = <0 52 0x04>;
+ dmas = <&sdma 14 18 0>,
+ <&sdma 15 18 0>;
+ dma-names = "rx", "tx";
+
+ clocks = <&clks 197>, <&clks 3>,
+ <&clks 197>, <&clks 107>,
+ <&clks 0>, <&clks 118>,
+ <&clks 62>, <&clks 139>,
+ <&clks 0>;
+ clock-names = "core", "rxtx0",
+ "rxtx1", "rxtx2",
+ "rxtx3", "rxtx4",
+ "rxtx5", "rxtx6",
+ "rxtx7";
+
+ status = "okay";
+};
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 3a4808d..cd088cc 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -1,6 +1,9 @@
config SND_SOC_FSL_SSI
tristate
+config SND_SOC_FSL_SPDIF
+ tristate
+
config SND_SOC_FSL_UTILS
tristate
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index d4b4aa8..4b5970e 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -12,9 +12,11 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
# Freescale PowerPC SSI/DMA Platform Support
snd-soc-fsl-ssi-objs := fsl_ssi.o
+snd-soc-fsl-spdif-objs := fsl_spdif.o
snd-soc-fsl-utils-objs := fsl_utils.o
snd-soc-fsl-dma-objs := fsl_dma.o
obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o
+obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o
obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o
obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c
new file mode 100644
index 0000000..4274f2c
--- /dev/null
+++ b/sound/soc/fsl/fsl_spdif.c
@@ -0,0 +1,1277 @@
+/*
+ * Freescale S/PDIF ALSA SoC Digital Audio Interface (DAI) driver
+ *
+ * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ *
+ * Based on stmp3xxx_spdif_dai.c
+ * Vladimir Barinov <vbarinov@embeddedalley.com>
+ * Copyright 2008 SigmaTel, Inc
+ * Copyright 2008 Embedded Alley Solutions, Inc
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/clk-private.h>
+#include <linux/bitrev.h>
+#include <linux/regmap.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+
+#include <sound/asoundef.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#include "fsl_spdif.h"
+#include "imx-pcm.h"
+
+#define FSL_SPDIF_TXFIFO_WML 0x8
+#define FSL_SPDIF_RXFIFO_WML 0x8
+
+#define INTR_FOR_PLAYBACK (INT_TXFIFO_RESYNC)
+#define INTR_FOR_CAPTURE (INT_SYM_ERR | INT_BIT_ERR | INT_URX_FUL | INT_URX_OV|\
+ INT_QRX_FUL | INT_QRX_OV | INT_UQ_SYNC | INT_UQ_ERR |\
+ INT_RXFIFO_RESYNC | INT_LOSS_LOCK | INT_DPLL_LOCKED)
+
+/* Index list for the values that has if (DPLL Locked) condition */
+static u8 srpc_dpll_locked[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0xa, 0xb };
+#define SRPC_NODPLL_START1 0x5
+#define SRPC_NODPLL_START2 0xc
+
+/*
+ * SPDIF control structure
+ * Defines channel status, subcode and Q sub
+ */
+struct spdif_mixer_control {
+ /* spinlock to access control data */
+ spinlock_t ctl_lock;
+
+ /* IEC958 channel tx status bit */
+ unsigned char ch_status[4];
+
+ /* User bits */
+ unsigned char subcode[2 * SPDIF_UBITS_SIZE];
+
+ /* Q subcode part of user bits */
+ unsigned char qsub[2 * SPDIF_QSUB_SIZE];
+
+ /* Buffer offset for U/Q */
+ u32 upos;
+ u32 qpos;
+
+ /* Ready buffer index of the two buffers */
+ u32 ready_buf;
+};
+
+struct fsl_spdif_priv {
+ struct spdif_mixer_control fsl_spdif_control;
+ struct snd_soc_dai_driver cpu_dai_drv;
+ struct platform_device *pdev;
+ struct regmap *regmap;
+ atomic_t dpll_locked;
+ u8 txclk_div[SPDIF_TXRATE_MAX];
+ u8 txclk_src[SPDIF_TXRATE_MAX];
+ u8 rxclk_src;
+ struct clk *txclk[SPDIF_TXRATE_MAX];
+ struct clk *rxclk;
+ struct snd_dmaengine_dai_dma_data dma_params_tx;
+ struct snd_dmaengine_dai_dma_data dma_params_rx;
+
+ /* The name space will be allocated dynamically */
+ char name[0];
+};
+
+
+#ifdef DEBUG
+static void dumpregs(struct fsl_spdif_priv *spdif_priv)
+{
+ struct regmap *regmap = spdif_priv->regmap;
+ struct platform_device *pdev = spdif_priv->pdev;
+ u32 val, i;
+ int ret;
+
+ /* Valid address set of SPDIF is {[0x0-0x38], 0x44, 0x50} */
+ for (i = 0 ; i <= REG_SPDIF_STC; i += 4) {
+ ret = regmap_read(regmap, REG_SPDIF_SCR + i, &val);
+ if (!ret)
+ dev_dbg(&pdev->dev, "REG 0x%02x = 0x%06x\n", i, val);
+ }
+}
+#else
+static void dumpregs(struct fsl_spdif_priv *spdif_priv) {}
+#endif
+
+
+/* DPLL locked and lock loss interrupt handler */
+static void spdif_irq_dpll_lock(struct fsl_spdif_priv *spdif_priv)
+{
+ struct regmap *regmap = spdif_priv->regmap;
+ struct platform_device *pdev = spdif_priv->pdev;
+ u32 locked;
+
+ regmap_read(regmap, REG_SPDIF_SRPC, &locked);
+ locked &= SRPC_DPLL_LOCKED;
+
+ dev_dbg(&pdev->dev, "isr: Rx dpll %s \n",
+ locked ? "locked" : "loss lock");
+
+ atomic_set(&spdif_priv->dpll_locked, locked ? 1 : 0);
+}
+
+/* Receiver found illegal symbol interrupt handler */
+static void spdif_irq_sym_error(struct fsl_spdif_priv *spdif_priv)
+{
+ struct regmap *regmap = spdif_priv->regmap;
+ struct platform_device *pdev = spdif_priv->pdev;
+
+ dev_dbg(&pdev->dev, "isr: receiver found illegal symbol\n");
+
+ if (!atomic_read(&spdif_priv->dpll_locked)) {
+ /* DPLL unlocked seems no audio stream */
+ regmap_update_bits(regmap, REG_SPDIF_SIE, INT_SYM_ERR, 0);
+ }
+}
+
+/* U/Q Channel receive register full */
+static void spdif_irq_uqrx_full(struct fsl_spdif_priv *spdif_priv, char name)
+{
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+ struct regmap *regmap = spdif_priv->regmap;
+ struct platform_device *pdev = spdif_priv->pdev;
+ u32 *pos, size, val, reg;
+
+ switch (name) {
+ case 'U':
+ pos = &ctrl->upos;
+ size = SPDIF_UBITS_SIZE;
+ reg = REG_SPDIF_SRU;
+ break;
+ case 'Q':
+ pos = &ctrl->qpos;
+ size = SPDIF_QSUB_SIZE;
+ reg = REG_SPDIF_SRQ;
+ break;
+ default:
+ dev_err(&pdev->dev, "unsupported channel name\n");
+ return;
+ }
+
+ dev_dbg(&pdev->dev, "isr: %c Channel receive register full\n", name);
+
+ if (*pos >= size * 2) {
+ *pos = 0;
+ } else if (unlikely((*pos % size) + 3 > size)) {
+ dev_err(&pdev->dev, "User bit receivce buffer overflow\n");
+ return;
+ }
+
+ regmap_read(regmap, reg, &val);
+ ctrl->subcode[*pos++] = val >> 16;
+ ctrl->subcode[*pos++] = val >> 8;
+ ctrl->subcode[*pos++] = val;
+}
+
+/* U/Q Channel sync found */
+static void spdif_irq_uq_sync(struct fsl_spdif_priv *spdif_priv)
+{
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+ struct platform_device *pdev = spdif_priv->pdev;
+
+ dev_dbg(&pdev->dev, "isr: U/Q Channel sync found\n");
+
+ /* U/Q buffer reset */
+ if (ctrl->qpos == 0)
+ return;
+
+ /* Set ready to this buffer */
+ ctrl->ready_buf = (ctrl->qpos - 1) / SPDIF_QSUB_SIZE + 1;
+}
+
+/* U/Q Channel framing error */
+static void spdif_irq_uq_err(struct fsl_spdif_priv *spdif_priv)
+{
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+ struct regmap *regmap = spdif_priv->regmap;
+ struct platform_device *pdev = spdif_priv->pdev;
+ u32 val;
+
+ dev_dbg(&pdev->dev, "isr: U/Q Channel framing error\n");
+
+ /* Read U/Q data to clear the irq and do buffer reset */
+ regmap_read(regmap, REG_SPDIF_SRU, &val);
+ regmap_read(regmap, REG_SPDIF_SRQ, &val);
+
+ /* Drop this U/Q buffer */
+ ctrl->ready_buf = 0;
+ ctrl->upos = 0;
+ ctrl->qpos = 0;
+}
+
+/* Get spdif interrupt status and clear the interrupt */
+static u32 spdif_intr_status_clear(struct fsl_spdif_priv *spdif_priv)
+{
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 val, val2;
+
+ regmap_read(regmap, REG_SPDIF_SIS, &val);
+ regmap_read(regmap, REG_SPDIF_SIE, &val2);
+
+ regmap_write(regmap, REG_SPDIF_SIC, val & val2);
+
+ return val;
+}
+
+static irqreturn_t spdif_isr(int irq, void *devid)
+{
+ struct fsl_spdif_priv *spdif_priv = (struct fsl_spdif_priv *)devid;
+ struct platform_device *pdev = spdif_priv->pdev;
+ u32 sis;
+
+ sis = spdif_intr_status_clear(spdif_priv);
+
+ if (sis & INT_DPLL_LOCKED)
+ spdif_irq_dpll_lock(spdif_priv);
+
+ if (sis & INT_TXFIFO_UNOV)
+ dev_dbg(&pdev->dev, "isr: Tx FIFO under/overrun\n");
+
+ if (sis & INT_TXFIFO_RESYNC)
+ dev_dbg(&pdev->dev, "isr: Tx FIFO resync\n");
+
+ if (sis & INT_CNEW)
+ dev_dbg(&pdev->dev, "isr: cstatus new\n");
+
+ if (sis & INT_VAL_NOGOOD)
+ dev_dbg(&pdev->dev, "isr: validity flag no good\n");
+
+ if (sis & INT_SYM_ERR)
+ spdif_irq_sym_error(spdif_priv);
+
+ if (sis & INT_BIT_ERR)
+ dev_dbg(&pdev->dev, "isr: receiver found parity bit error\n");
+
+ if (sis & INT_URX_FUL)
+ spdif_irq_uqrx_full(spdif_priv, 'U');
+
+ if (sis & INT_URX_OV)
+ dev_dbg(&pdev->dev, "isr: U Channel receive register overrun\n");
+
+ if (sis & INT_QRX_FUL)
+ spdif_irq_uqrx_full(spdif_priv, 'Q');
+
+ if (sis & INT_QRX_OV)
+ dev_dbg(&pdev->dev, "isr: Q Channel receive register overrun\n");
+
+ if (sis & INT_UQ_SYNC)
+ spdif_irq_uq_sync(spdif_priv);
+
+ if (sis & INT_UQ_ERR)
+ spdif_irq_uq_err(spdif_priv);
+
+ if (sis & INT_RXFIFO_UNOV)
+ dev_dbg(&pdev->dev, "isr: Rx FIFO under/overrun\n");
+
+ if (sis & INT_RXFIFO_RESYNC)
+ dev_dbg(&pdev->dev, "isr: Rx FIFO resync\n");
+
+ if (sis & INT_LOSS_LOCK)
+ spdif_irq_dpll_lock(spdif_priv);
+
+ /* FIXME: Write Tx FIFO to clear TxEm */
+ if (sis & INT_TX_EM)
+ dev_dbg(&pdev->dev, "isr: Tx FIFO empty\n");
+
+ /* FIXME: Read Rx FIFO to clear RxFIFOFul */
+ if (sis & INT_RXFIFO_FUL)
+ dev_dbg(&pdev->dev, "isr: Rx FIFO full\n");
+
+ return IRQ_HANDLED;
+}
+
+static int spdif_softreset(struct fsl_spdif_priv *spdif_priv)
+{
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 val, cycle = 1000;
+
+ regmap_write(regmap, REG_SPDIF_SCR, SCR_SOFT_RESET);
+
+ /*
+ * RESET bit would be cleared after finishing its reset procedure,
+ * which typically lasts 8 cycles. 1000 cycles will keep it safe.
+ */
+ do {
+ regmap_read(regmap, REG_SPDIF_SCR, &val);
+ } while ((val & SCR_SOFT_RESET) && cycle--);
+
+ if (cycle)
+ return 0;
+ else
+ return -EBUSY;
+}
+
+static void spdif_set_cstatus(struct spdif_mixer_control *ctrl,
+ u8 mask, u8 cstatus)
+{
+ ctrl->ch_status[3] &= ~mask;
+ ctrl->ch_status[3] |= cstatus & mask;
+}
+
+static void spdif_write_channel_status(struct fsl_spdif_priv *spdif_priv)
+{
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+ struct regmap *regmap = spdif_priv->regmap;
+ struct platform_device *pdev = spdif_priv->pdev;
+ u32 ch_status;
+
+ ch_status = (bitrev8(ctrl->ch_status[0]) << 16) |
+ (bitrev8(ctrl->ch_status[1]) << 8) |
+ bitrev8(ctrl->ch_status[2]);
+ regmap_write(regmap, REG_SPDIF_STCSCH, ch_status);
+
+ dev_dbg(&pdev->dev, "STCSCH: 0x%06x\n", ch_status);
+
+ ch_status = bitrev8(ctrl->ch_status[3]) << 16;
+ regmap_write(regmap, REG_SPDIF_STCSCL, ch_status);
+
+ dev_dbg(&pdev->dev, "STCSCL: 0x%06x\n", ch_status);
+}
+
+/* Set SPDIF PhaseConfig register for rx clock */
+static int spdif_set_rx_clksrc(struct fsl_spdif_priv *spdif_priv,
+ enum spdif_gainsel gainsel, int dpll_locked)
+{
+ enum spdif_rxclk_src clksrc = spdif_priv->rxclk_src;
+ struct regmap *regmap = spdif_priv->regmap;
+
+ if (clksrc >= SRPC_CLKSRC_MAX || gainsel >= GAINSEL_MULTI_MAX)
+ return -EINVAL;
+
+ regmap_update_bits(regmap, REG_SPDIF_SRPC,
+ SRPC_CLKSRC_SEL_MASK | SRPC_GAINSEL_MASK,
+ SRPC_CLKSRC_SEL_SET(clksrc) | SRPC_GAINSEL_SET(gainsel));
+
+ return 0;
+}
+
+static int spdif_clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long rate_actual;
+
+ rate_actual = clk_round_rate(clk, rate);
+ clk_set_rate(clk, rate_actual);
+
+ return 0;
+}
+
+static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
+ int sample_rate)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+ struct regmap *regmap = spdif_priv->regmap;
+ struct platform_device *pdev = spdif_priv->pdev;
+ unsigned long csfs = 0;
+ u8 clk, div;
+ u32 stc, mask, rate;
+
+ switch (sample_rate) {
+ case 32000:
+ rate = SPDIF_TXRATE_32000;
+ csfs = IEC958_AES3_CON_FS_32000;
+ break;
+ case 44100:
+ rate = SPDIF_TXRATE_44100;
+ csfs = IEC958_AES3_CON_FS_44100;
+ break;
+ case 48000:
+ rate = SPDIF_TXRATE_48000;
+ csfs = IEC958_AES3_CON_FS_48000;
+ break;
+ default:
+ dev_err(&pdev->dev, "unsupported sample rate %d\n", sample_rate);
+ return -EINVAL;
+ }
+
+ clk = spdif_priv->txclk_src[rate];
+ if (clk >= STC_TXCLK_SRC_MAX) {
+ dev_err(&pdev->dev, "tx clock source is out of range\n");
+ return -EINVAL;
+ }
+
+ div = spdif_priv->txclk_div[rate];
+ if (div == 0) {
+ dev_err(&pdev->dev, "the divisor can't be zero\n");
+ return -EINVAL;
+ }
+
+ /*
+ * The S/PDIF block needs a clock of 64 * fs * div. The S/PDIF block
+ * will divide by (div). So request 64 * fs * (div+1) which will
+ * get rounded.
+ */
+ spdif_clk_set_rate(spdif_priv->txclk[rate], 64 * sample_rate * (div + 1));
+
+ dev_dbg(&pdev->dev, "expected clock rate = %d\n",
+ (int)(64 * sample_rate * div));
+ dev_dbg(&pdev->dev, "acutal clock rate = %d\n",
+ (int)clk_get_rate(spdif_priv->txclk[rate]));
+
+ /* set fs field in consumer channel status */
+ spdif_set_cstatus(ctrl, IEC958_AES3_CON_FS, csfs);
+
+ /* select clock source and divisor */
+ stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) | STC_TXCLK_DIV(div);
+ mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | STC_TXCLK_DIV_MASK;
+ regmap_update_bits(regmap, REG_SPDIF_STC, mask, stc);
+
+ dev_dbg(&pdev->dev, "set sample rate to %d\n", sample_rate);
+
+ return 0;
+}
+
+int fsl_spdif_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct platform_device *pdev = spdif_priv->pdev;
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 scr, mask, i;
+ int ret;
+
+ /* Reset module and interrupts only for first initialization */
+ if (!cpu_dai->active) {
+ ret = spdif_softreset(spdif_priv);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to soft reset\n");
+ return ret;
+ }
+
+ /* Disable all the interrupts */
+ regmap_update_bits(regmap, REG_SPDIF_SIE, 0xffffff, 0);
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ scr = SCR_TXFIFO_AUTOSYNC | SCR_TXFIFO_CTRL_NORMAL |
+ SCR_TXSEL_NORMAL | SCR_USRC_SEL_CHIP |
+ SCR_TXFIFO_FSEL_IF8;
+ mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK |
+ SCR_TXSEL_MASK | SCR_USRC_SEL_MASK |
+ SCR_TXFIFO_FSEL_MASK;
+ for (i = 0; i < SPDIF_TXRATE_MAX; i++)
+ clk_enable(spdif_priv->txclk[i]);
+ } else {
+ scr = SCR_RXFIFO_FSEL_IF8 | SCR_RXFIFO_AUTOSYNC;
+ mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK|
+ SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK;
+ clk_enable(spdif_priv->rxclk);
+ }
+ regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr);
+
+ /* Power up SPDIF module */
+ regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, 0);
+
+ return 0;
+}
+
+static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 scr, mask, i;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ scr = 0;
+ mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK |
+ SCR_TXSEL_MASK | SCR_USRC_SEL_MASK |
+ SCR_TXFIFO_FSEL_MASK;
+ for (i = 0; i < SPDIF_TXRATE_MAX; i++)
+ clk_disable(spdif_priv->txclk[i]);
+ } else {
+ scr = SCR_RXFIFO_OFF | SCR_RXFIFO_CTL_ZERO;
+ mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK|
+ SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK;
+ clk_disable(spdif_priv->rxclk);
+ }
+ regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr);
+
+ /* Power down SPDIF module only if tx&rx are both inactive */
+ if (!cpu_dai->active) {
+ spdif_intr_status_clear(spdif_priv);
+ regmap_update_bits(regmap, REG_SPDIF_SCR,
+ SCR_LOW_POWER, SCR_LOW_POWER);
+ }
+}
+
+static int fsl_spdif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+ struct platform_device *pdev = spdif_priv->pdev;
+ u32 sample_rate = params_rate(params);
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = spdif_set_sample_rate(substream, sample_rate);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: set sample rate failed: %d\n",
+ __func__, sample_rate);
+ return ret;
+ }
+ spdif_set_cstatus(ctrl, IEC958_AES3_CON_CLOCK,
+ IEC958_AES3_CON_CLOCK_1000PPM);
+ spdif_write_channel_status(spdif_priv);
+ } else {
+ /* Setup rx clock source */
+ ret = spdif_set_rx_clksrc(spdif_priv, SPDIF_DEFAULT_GAINSEL, 1);
+ }
+
+ return ret;
+}
+
+static int fsl_spdif_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct regmap *regmap = spdif_priv->regmap;
+ int is_playack = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ u32 intr = is_playack ? INTR_FOR_PLAYBACK : INTR_FOR_CAPTURE;
+ u32 dmaen = is_playack ? SCR_DMA_TX_EN : SCR_DMA_RX_EN;;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ regmap_update_bits(regmap, REG_SPDIF_SIE, intr, intr);
+ regmap_update_bits(regmap, REG_SPDIF_SCR, dmaen, dmaen);
+ dumpregs(spdif_priv);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ regmap_update_bits(regmap, REG_SPDIF_SCR, dmaen, 0);
+ regmap_update_bits(regmap, REG_SPDIF_SIE, intr, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+struct snd_soc_dai_ops fsl_spdif_dai_ops = {
+ .startup = fsl_spdif_startup,
+ .hw_params = fsl_spdif_hw_params,
+ .trigger = fsl_spdif_trigger,
+ .shutdown = fsl_spdif_shutdown,
+};
+
+
+/*
+ * ============================================
+ * FSL SPDIF IEC958 controller(mixer) functions
+ *
+ * Channel status get/put control
+ * User bit value get/put control
+ * Valid bit value get control
+ * DPLL lock status get control
+ * User bit sync mode selection control
+ * ============================================
+ */
+
+static int fsl_spdif_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+ uinfo->count = 1;
+
+ return 0;
+}
+
+static int fsl_spdif_pb_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+
+ uvalue->value.iec958.status[0] = ctrl->ch_status[0];
+ uvalue->value.iec958.status[1] = ctrl->ch_status[1];
+ uvalue->value.iec958.status[2] = ctrl->ch_status[2];
+ uvalue->value.iec958.status[3] = ctrl->ch_status[3];
+
+ return 0;
+}
+
+static int fsl_spdif_pb_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *uvalue)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+
+ ctrl->ch_status[0] = uvalue->value.iec958.status[0];
+ ctrl->ch_status[1] = uvalue->value.iec958.status[1];
+ ctrl->ch_status[2] = uvalue->value.iec958.status[2];
+ ctrl->ch_status[3] = uvalue->value.iec958.status[3];
+
+ spdif_write_channel_status(spdif_priv);
+
+ return 0;
+}
+
+/* Get channel status from SPDIF_RX_CCHAN register */
+static int fsl_spdif_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 cstatus, val;
+
+ regmap_read(regmap, REG_SPDIF_SIS, &val);
+ if (!(val & INT_CNEW)) {
+ return -EAGAIN;
+ }
+
+ regmap_read(regmap, REG_SPDIF_SRCSH, &cstatus);
+ ucontrol->value.iec958.status[0] = (cstatus >> 16) & 0xFF;
+ ucontrol->value.iec958.status[1] = (cstatus >> 8) & 0xFF;
+ ucontrol->value.iec958.status[2] = cstatus & 0xFF;
+
+ regmap_read(regmap, REG_SPDIF_SRCSL, &cstatus);
+ ucontrol->value.iec958.status[3] = (cstatus >> 16) & 0xFF;
+ ucontrol->value.iec958.status[4] = (cstatus >> 8) & 0xFF;
+ ucontrol->value.iec958.status[5] = cstatus & 0xFF;
+
+ /* Clear intr */
+ regmap_write(regmap, REG_SPDIF_SIC, INT_CNEW);
+
+ return 0;
+}
+
+/*
+ * Get User bits (subcode) from chip value which readed out
+ * in UChannel register.
+ */
+static int fsl_spdif_subcode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&ctrl->ctl_lock, flags);
+ if (ctrl->ready_buf) {
+ int idx = (ctrl->ready_buf - 1) * SPDIF_UBITS_SIZE;
+ memcpy(&ucontrol->value.iec958.subcode[0],
+ &ctrl->subcode[idx], SPDIF_UBITS_SIZE);
+ } else {
+ ret = -EAGAIN;
+ }
+ spin_unlock_irqrestore(&ctrl->ctl_lock, flags);
+
+ return ret;
+}
+
+/* Q-subcode infomation. The byte size is SPDIF_UBITS_SIZE/8 */
+static int fsl_spdif_qinfo(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = SPDIF_QSUB_SIZE;
+
+ return 0;
+}
+
+/* Get Q subcode from chip value which readed out in QChannel register */
+static int fsl_spdif_qget(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&ctrl->ctl_lock, flags);
+ if (ctrl->ready_buf) {
+ int idx = (ctrl->ready_buf - 1) * SPDIF_QSUB_SIZE;
+ memcpy(&ucontrol->value.bytes.data[0],
+ &ctrl->qsub[idx], SPDIF_QSUB_SIZE);
+ } else {
+ ret = -EAGAIN;
+ }
+ spin_unlock_irqrestore(&ctrl->ctl_lock, flags);
+
+ return ret;
+}
+
+/* Valid bit infomation */
+static int fsl_spdif_vbit_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+
+ return 0;
+}
+
+/* Get valid good bit from interrupt status register */
+static int fsl_spdif_vbit_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 val;
+
+ val = regmap_read(regmap, REG_SPDIF_SIS, &val);
+ ucontrol->value.integer.value[0] = (val & INT_VAL_NOGOOD) != 0;
+ regmap_write(regmap, REG_SPDIF_SIC, INT_VAL_NOGOOD);
+
+ return 0;
+}
+
+/* DPLL lock infomation */
+static int fsl_spdif_rxrate_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 16000;
+ uinfo->value.integer.max = 96000;
+
+ return 0;
+}
+
+static u32 gainsel_multi[GAINSEL_MULTI_MAX] = {
+ 24, 16, 12, 8, 6, 4, 3,
+};
+
+/* Get RX data clock rate given the SPDIF bus_clk */
+static int spdif_get_rxclk_rate(struct fsl_spdif_priv *spdif_priv,
+ enum spdif_gainsel gainsel)
+{
+ struct regmap *regmap = spdif_priv->regmap;
+ struct platform_device *pdev = spdif_priv->pdev;
+ u64 tmpval64, busclk_freq = 0;
+ u32 freqmeas, phaseconf;
+ enum spdif_rxclk_src clksrc;
+
+ regmap_read(regmap, REG_SPDIF_SRFM, &freqmeas);
+ regmap_read(regmap, REG_SPDIF_SRPC, &phaseconf);
+
+ clksrc = (phaseconf >> SRPC_CLKSRC_SEL_OFFSET) & 0xf;
+ if (srpc_dpll_locked[clksrc] && (phaseconf & SRPC_DPLL_LOCKED)) {
+ /* Get bus clock from system */
+ busclk_freq = clk_get_rate(spdif_priv->rxclk);
+ }
+
+ /* FreqMeas_CLK = (BUS_CLK * FreqMeas) / 2 ^ 10 / GAINSEL / 128 */
+ tmpval64 = (u64) busclk_freq * freqmeas;
+ do_div(tmpval64, gainsel_multi[gainsel] * 1024);
+ do_div(tmpval64, 128 * 1024);
+
+ dev_dbg(&pdev->dev, "FreqMeas: %d\n", (int)freqmeas);
+ dev_dbg(&pdev->dev, "BusclkFreq: %d\n", (int)busclk_freq);
+ dev_dbg(&pdev->dev, "RxRate: %d\n", (int)tmpval64);
+
+ return (int)tmpval64;
+}
+
+/*
+ * Get DPLL lock or not info from stable interrupt status register.
+ * User application must use this control to get locked,
+ * then can do next PCM operation
+ */
+static int fsl_spdif_rxrate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ int rate = spdif_get_rxclk_rate(spdif_priv, SPDIF_DEFAULT_GAINSEL);
+
+ if (atomic_read(&spdif_priv->dpll_locked))
+ ucontrol->value.integer.value[0] = rate;
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ return 0;
+}
+
+/* User bit sync mode info */
+static int fsl_spdif_usync_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+
+ return 0;
+}
+
+/*
+ * User bit sync mode:
+ * 1 CD User channel subcode
+ * 0 Non-CD data
+ */
+static int fsl_spdif_usync_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 val;
+
+ regmap_read(regmap, REG_SPDIF_SRCD, &val);
+ ucontrol->value.integer.value[0] = (val & SRCD_CD_USER) != 0;
+
+ return 0;
+}
+
+/*
+ * User bit sync mode:
+ * 1 CD User channel subcode
+ * 0 Non-CD data
+ */
+static int fsl_spdif_usync_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+ struct regmap *regmap = spdif_priv->regmap;
+ u32 val = ucontrol->value.integer.value[0] << SRCD_CD_USER_OFFSET;
+
+ regmap_update_bits(regmap, REG_SPDIF_SRCD, SRCD_CD_USER, val);
+
+ return 0;
+}
+
+/* FSL SPDIF IEC958 controller defines */
+static struct snd_kcontrol_new fsl_spdif_ctrls[] = {
+ /* Status cchanel controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_WRITE |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = fsl_spdif_info,
+ .get = fsl_spdif_pb_get,
+ .put = fsl_spdif_pb_put,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = fsl_spdif_info,
+ .get = fsl_spdif_capture_get,
+ },
+ /* User bits controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Subcode Capture Default",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = fsl_spdif_info,
+ .get = fsl_spdif_subcode_get,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 Q-subcode Capture Default",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = fsl_spdif_qinfo,
+ .get = fsl_spdif_qget,
+ },
+ /* Valid bit error controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 V-Bit Errors",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = fsl_spdif_vbit_info,
+ .get = fsl_spdif_vbit_get,
+ },
+ /* DPLL lock info get controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "RX Sample Rate",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = fsl_spdif_rxrate_info,
+ .get = fsl_spdif_rxrate_get,
+ },
+ /* User bit sync mode set/get controller */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+ .name = "IEC958 USyncMode CDText",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_WRITE |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = fsl_spdif_usync_info,
+ .get = fsl_spdif_usync_get,
+ .put = fsl_spdif_usync_put,
+ },
+};
+
+static int fsl_spdif_dai_probe(struct snd_soc_dai *dai)
+{
+ struct fsl_spdif_priv *spdif_private = snd_soc_dai_get_drvdata(dai);
+
+ dai->playback_dma_data = &spdif_private->dma_params_tx;
+ dai->capture_dma_data = &spdif_private->dma_params_rx;
+
+ snd_soc_add_dai_controls(dai, fsl_spdif_ctrls, ARRAY_SIZE(fsl_spdif_ctrls));
+
+ return 0;
+}
+
+struct snd_soc_dai_driver fsl_spdif_dai = {
+ .probe = &fsl_spdif_dai_probe,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = FSL_SPDIF_RATES_PLAYBACK,
+ .formats = FSL_SPDIF_FORMATS_PLAYBACK,
+ },
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = FSL_SPDIF_RATES_CAPTURE,
+ .formats = FSL_SPDIF_FORMATS_CAPTURE,
+ },
+ .ops = &fsl_spdif_dai_ops,
+};
+
+static const struct snd_soc_component_driver fsl_spdif_component = {
+ .name = "fsl-spdif",
+};
+
+/*
+ * ================
+ * FSL SPDIF REGMAP
+ * ================
+ */
+
+static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case REG_SPDIF_SCR:
+ case REG_SPDIF_SRCD:
+ case REG_SPDIF_SRPC:
+ case REG_SPDIF_SIE:
+ case REG_SPDIF_SIS:
+ case REG_SPDIF_SRL:
+ case REG_SPDIF_SRR:
+ case REG_SPDIF_SRCSH:
+ case REG_SPDIF_SRCSL:
+ case REG_SPDIF_SRU:
+ case REG_SPDIF_SRQ:
+ case REG_SPDIF_STCSCH:
+ case REG_SPDIF_STCSCL:
+ case REG_SPDIF_SRFM:
+ case REG_SPDIF_STC:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case REG_SPDIF_SCR:
+ case REG_SPDIF_SRCD:
+ case REG_SPDIF_SRPC:
+ case REG_SPDIF_SIE:
+ case REG_SPDIF_SIC:
+ case REG_SPDIF_STL:
+ case REG_SPDIF_STR:
+ case REG_SPDIF_STCSCH:
+ case REG_SPDIF_STCSCL:
+ case REG_SPDIF_STC:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config fsl_spdif_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+
+ .max_register = REG_SPDIF_STC,
+ .readable_reg = fsl_spdif_readable_reg,
+ .writeable_reg = fsl_spdif_writeable_reg,
+};
+
+static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
+ struct clk *clk, u64 savesub,
+ enum spdif_txrate index)
+{
+ const u32 rate[] = { 32000, 44100, 48000 };
+ u64 rate_ideal, rate_actual, sub;
+ u32 div, arate;
+
+ for (div = 1; div <= 128; div++) {
+ rate_ideal = rate[index] * (div + 1) * 64;
+ rate_actual = clk_round_rate(clk, rate_ideal);
+
+ arate = rate_actual / 64;
+ arate /= div;
+
+ if (arate == rate[index]) {
+ /* We are lucky */
+ savesub = 0;
+ spdif_priv->txclk_div[index] = div;
+ break;
+ } else if (arate / rate[index] == 1) {
+ /* A little bigger than expect */
+ sub = (arate - rate[index]) * 100000;
+ do_div(sub, rate[index]);
+ if (sub < savesub) {
+ savesub = sub;
+ spdif_priv->txclk_div[index] = div;
+ }
+ } else if (rate[index] / arate == 1) {
+ /* A little smaller than expect */
+ sub = (rate[index] - arate) * 100000;
+ do_div(sub, rate[index]);
+ if (sub < savesub) {
+ savesub = sub;
+ spdif_priv->txclk_div[index] = div;
+ }
+ }
+ }
+
+ return savesub;
+}
+
+static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
+ enum spdif_txrate index)
+{
+ const u32 rate[] = { 32000, 44100, 48000 };
+ struct platform_device *pdev = spdif_priv->pdev;
+ struct device *dev = &pdev->dev;
+ u64 savesub = 100000, ret;
+ struct clk *clk;
+ char tmp[16];
+ int i;
+
+ for (i = 0; i < STC_TXCLK_SRC_MAX; i++) {
+ sprintf(tmp, "rxtx%d", i);
+ clk = devm_clk_get(&pdev->dev, tmp);
+ if (IS_ERR(clk)) {
+ dev_err(dev, "no rxtx%d property in devicetree\n", i);
+ return PTR_ERR(clk);
+ }
+ if (!clk_get_rate(clk))
+ continue;
+
+ ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index);
+ if (savesub == ret)
+ continue;
+
+ savesub = ret;
+ spdif_priv->txclk[index] = clk;
+ spdif_priv->txclk_src[index] = i;
+
+ /* To quick catch a divisor, we allow a 0.1% deviation */
+ if (savesub < 100)
+ break;
+ }
+
+ dev_dbg(&pdev->dev, "use rxtx%d as tx clock source for %dHz sample rate",
+ spdif_priv->txclk_src[index], rate[index]);
+ dev_dbg(&pdev->dev, "use divisor %d for %dHz sample rate",
+ spdif_priv->txclk_div[index], rate[index]);
+
+ return 0;
+}
+
+static int fsl_spdif_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct fsl_spdif_priv *spdif_priv;
+ struct spdif_mixer_control *ctrl;
+ struct resource *res;
+ void __iomem *regs;
+ int irq, ret, i;
+
+ if (!np)
+ return -ENODEV;
+
+ spdif_priv = devm_kzalloc(&pdev->dev,
+ sizeof(struct fsl_spdif_priv) + strlen(np->name) + 1, GFP_KERNEL);
+ if (!spdif_priv) {
+ dev_err(&pdev->dev, "could not allocate DAI object\n");
+ return -ENOMEM;
+ }
+
+ strcpy(spdif_priv->name, np->name);
+
+ spdif_priv->pdev = pdev;
+
+ /* Initialize this copy of the CPU DAI driver structure */
+ memcpy(&spdif_priv->cpu_dai_drv, &fsl_spdif_dai, sizeof(fsl_spdif_dai));
+ spdif_priv->cpu_dai_drv.name = spdif_priv->name;
+
+ /* Get the addresses and IRQ */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (IS_ERR(res)) {
+ dev_err(&pdev->dev, "could not determine device resources\n");
+ return PTR_ERR(res);
+ }
+
+ regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(regs)) {
+ dev_err(&pdev->dev, "could not map device resources\n");
+ return PTR_ERR(regs);
+ }
+
+ spdif_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
+ "core", regs, &fsl_spdif_regmap_config);
+ if (IS_ERR(spdif_priv->regmap)) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ return PTR_ERR(spdif_priv->regmap);
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
+ return irq;
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq, spdif_isr, 0,
+ spdif_priv->name, spdif_priv);
+ if (ret) {
+ dev_err(&pdev->dev, "could not claim irq %u\n", irq);
+ return ret;
+ }
+
+ /* Select clock source for rx/tx clock */
+ spdif_priv->rxclk = devm_clk_get(&pdev->dev, "rxtx1");
+ if (IS_ERR(spdif_priv->rxclk)) {
+ dev_err(&pdev->dev, "no rxtx1 property in devicetree\n");
+ return PTR_ERR(spdif_priv->rxclk);
+ }
+ spdif_priv->rxclk_src = DEFAULT_RXCLK_SRC;
+
+ for (i = 0; i < SPDIF_TXRATE_MAX; i++) {
+ ret = fsl_spdif_probe_txclk(spdif_priv, i);
+ if (ret)
+ return ret;
+ }
+
+ /* Prepare rx/tx clock */
+ clk_prepare(spdif_priv->rxclk);
+ for (i = 0; i < SPDIF_TXRATE_MAX; i++)
+ clk_prepare(spdif_priv->txclk[i]);
+
+ /* Initial spinlock for control data */
+ ctrl = &spdif_priv->fsl_spdif_control;
+ spin_lock_init(&ctrl->ctl_lock);
+
+ /* Init tx channel status default value */
+ ctrl->ch_status[0] =
+ IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_5015;
+ ctrl->ch_status[1] = IEC958_AES1_CON_DIGDIGCONV_ID;
+ ctrl->ch_status[2] = 0x00;
+ ctrl->ch_status[3] =
+ IEC958_AES3_CON_FS_44100 | IEC958_AES3_CON_CLOCK_1000PPM;
+
+ atomic_set(&spdif_priv->dpll_locked, 0);
+
+ spdif_priv->dma_params_tx.maxburst = FSL_SPDIF_TXFIFO_WML;
+ spdif_priv->dma_params_rx.maxburst = FSL_SPDIF_RXFIFO_WML;
+ spdif_priv->dma_params_tx.addr = res->start + REG_SPDIF_STL;
+ spdif_priv->dma_params_rx.addr = res->start + REG_SPDIF_SRL;
+
+ /* Register with ASoC */
+ dev_set_drvdata(&pdev->dev, spdif_priv);
+
+ ret = snd_soc_register_component(&pdev->dev, &fsl_spdif_component,
+ &spdif_priv->cpu_dai_drv, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
+ goto error_dev;
+ }
+
+ ret = imx_pcm_dma_init(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "imx_pcm_dma_init failed: %d\n", ret);
+ goto error_component;
+ }
+
+ return ret;
+
+error_component:
+ snd_soc_unregister_component(&pdev->dev);
+error_dev:
+ dev_set_drvdata(&pdev->dev, NULL);
+ for (i = 0; i < SPDIF_TXRATE_MAX; i++)
+ clk_unprepare(spdif_priv->txclk[i]);
+ clk_unprepare(spdif_priv->rxclk);
+
+ return ret;
+}
+
+static int fsl_spdif_remove(struct platform_device *pdev)
+{
+ struct fsl_spdif_priv *spdif_priv = platform_get_drvdata(pdev);
+ int i;
+
+ imx_pcm_dma_exit(pdev);
+ snd_soc_unregister_component(&pdev->dev);
+
+ for (i = 0; i < SPDIF_TXRATE_MAX; i++)
+ clk_unprepare(spdif_priv->txclk[i]);
+ clk_unprepare(spdif_priv->rxclk);
+
+ dev_set_drvdata(&pdev->dev, NULL);
+
+ return 0;
+}
+
+static const struct of_device_id fsl_spdif_dt_ids[] = {
+ { .compatible = "fsl,imx35-spdif", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids);
+
+static struct platform_driver fsl_spdif_driver = {
+ .driver = {
+ .name = "fsl-spdif-dai",
+ .owner = THIS_MODULE,
+ .of_match_table = fsl_spdif_dt_ids,
+ },
+ .probe = fsl_spdif_probe,
+ .remove = fsl_spdif_remove,
+};
+
+module_platform_driver(fsl_spdif_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Freescale S/PDIF CPU DAI Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:fsl-spdif-dai");
diff --git a/sound/soc/fsl/fsl_spdif.h b/sound/soc/fsl/fsl_spdif.h
new file mode 100644
index 0000000..f8357f6
--- /dev/null
+++ b/sound/soc/fsl/fsl_spdif.h
@@ -0,0 +1,224 @@
+/*
+ * fsl_spdif.h - ALSA S/PDIF interface for the Freescale i.MX SoC
+ *
+ * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ *
+ * Author: Nicolin Chen <b42378@freescale.com>
+ *
+ * Based on fsl_ssi.h
+ * Author: Timur Tabi <timur@freescale.com>
+ * Copyright 2007-2008 Freescale Semiconductor, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#ifndef _FSL_SPDIF_DAI_H
+#define _FSL_SPDIF_DAI_H
+
+/* S/PDIF Register Map */
+#define REG_SPDIF_SCR 0x0 /* SPDIF Configuration Register */
+#define REG_SPDIF_SRCD 0x4 /* CDText Control Register */
+#define REG_SPDIF_SRPC 0x8 /* PhaseConfig Register */
+#define REG_SPDIF_SIE 0xc /* InterruptEn Register */
+#define REG_SPDIF_SIS 0x10 /* InterruptStat Register */
+#define REG_SPDIF_SIC 0x10 /* InterruptClear Register */
+#define REG_SPDIF_SRL 0x14 /* SPDIFRxLeft Register */
+#define REG_SPDIF_SRR 0x18 /* SPDIFRxRight Register */
+#define REG_SPDIF_SRCSH 0x1c /* SPDIFRxCChannel_h Register */
+#define REG_SPDIF_SRCSL 0x20 /* SPDIFRxCChannel_l Register */
+#define REG_SPDIF_SRU 0x24 /* UchannelRx Register */
+#define REG_SPDIF_SRQ 0x28 /* QchannelRx Register */
+#define REG_SPDIF_STL 0x2C /* SPDIFTxLeft Register */
+#define REG_SPDIF_STR 0x30 /* SPDIFTxRight Register */
+#define REG_SPDIF_STCSCH 0x34 /* SPDIFTxCChannelCons_h Register */
+#define REG_SPDIF_STCSCL 0x38 /* SPDIFTxCChannelCons_l Register */
+#define REG_SPDIF_SRFM 0x44 /* FreqMeas Register */
+#define REG_SPDIF_STC 0x50 /* SPDIFTxClk Register */
+
+
+/* SPDIF Configuration register */
+#define SCR_RXFIFO_CTL_OFFSET 23
+#define SCR_RXFIFO_CTL_MASK (1 << SCR_RXFIFO_CTL_OFFSET)
+#define SCR_RXFIFO_CTL_ZERO (1 << SCR_RXFIFO_CTL_OFFSET)
+#define SCR_RXFIFO_OFF_OFFSET 22
+#define SCR_RXFIFO_OFF_MASK (1 << SCR_RXFIFO_OFF_OFFSET)
+#define SCR_RXFIFO_OFF (1 << SCR_RXFIFO_OFF_OFFSET)
+#define SCR_RXFIFO_RST_OFFSET 21
+#define SCR_RXFIFO_RST_MASK (1 << SCR_RXFIFO_RST_OFFSET)
+#define SCR_RXFIFO_RST (1 << SCR_RXFIFO_RST_OFFSET)
+#define SCR_RXFIFO_FSEL_OFFSET 19
+#define SCR_RXFIFO_FSEL_MASK (0x3 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_FSEL_IF0 (0x0 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_FSEL_IF4 (0x1 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_FSEL_IF8 (0x2 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_FSEL_IF12 (0x3 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_AUTOSYNC_OFFSET 18
+#define SCR_RXFIFO_AUTOSYNC_MASK (1 << SCR_RXFIFO_AUTOSYNC_OFFSET)
+#define SCR_RXFIFO_AUTOSYNC (1 << SCR_RXFIFO_AUTOSYNC_OFFSET)
+#define SCR_TXFIFO_AUTOSYNC_OFFSET 17
+#define SCR_TXFIFO_AUTOSYNC_MASK (1 << SCR_TXFIFO_AUTOSYNC_OFFSET)
+#define SCR_TXFIFO_AUTOSYNC (1 << SCR_TXFIFO_AUTOSYNC_OFFSET)
+#define SCR_TXFIFO_FSEL_OFFSET 15
+#define SCR_TXFIFO_FSEL_MASK (0x3 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_TXFIFO_FSEL_IF0 (0x0 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_TXFIFO_FSEL_IF4 (0x1 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_TXFIFO_FSEL_IF8 (0x2 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_TXFIFO_FSEL_IF12 (0x3 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_LOW_POWER (1 << 13)
+#define SCR_SOFT_RESET (1 << 12)
+#define SCR_TXFIFO_CTRL_OFFSET 10
+#define SCR_TXFIFO_CTRL_MASK (0x3 << SCR_TXFIFO_CTRL_OFFSET)
+#define SCR_TXFIFO_CTRL_ZERO (0x0 << SCR_TXFIFO_CTRL_OFFSET)
+#define SCR_TXFIFO_CTRL_NORMAL (0x1 << SCR_TXFIFO_CTRL_OFFSET)
+#define SCR_TXFIFO_CTRL_ONESAMPLE (0x2 << SCR_TXFIFO_CTRL_OFFSET)
+#define SCR_DMA_RX_EN_OFFSET 9
+#define SCR_DMA_RX_EN_MASK (1 << SCR_DMA_RX_EN_OFFSET)
+#define SCR_DMA_RX_EN (1 << SCR_DMA_RX_EN_OFFSET)
+#define SCR_DMA_TX_EN_OFFSET 8
+#define SCR_DMA_TX_EN_MASK (1 << SCR_DMA_TX_EN_OFFSET)
+#define SCR_DMA_TX_EN (1 << SCR_DMA_TX_EN_OFFSET)
+#define SCR_VAL_OFFSET 5
+#define SCR_VAL_MASK (1 << SCR_VAL_OFFSET)
+#define SCR_VAL_CLEAR (1 << SCR_VAL_OFFSET)
+#define SCR_TXSEL_OFFSET 2
+#define SCR_TXSEL_MASK (0x7 << SCR_TXSEL_OFFSET)
+#define SCR_TXSEL_OFF (0 << SCR_TXSEL_OFFSET)
+#define SCR_TXSEL_RX (1 << SCR_TXSEL_OFFSET)
+#define SCR_TXSEL_NORMAL (0x5 << SCR_TXSEL_OFFSET)
+#define SCR_USRC_SEL_OFFSET 0x0
+#define SCR_USRC_SEL_MASK (0x3 << SCR_USRC_SEL_OFFSET)
+#define SCR_USRC_SEL_NONE (0x0 << SCR_USRC_SEL_OFFSET)
+#define SCR_USRC_SEL_RECV (0x1 << SCR_USRC_SEL_OFFSET)
+#define SCR_USRC_SEL_CHIP (0x3 << SCR_USRC_SEL_OFFSET)
+
+/* SPDIF CDText control */
+#define SRCD_CD_USER_OFFSET 1
+#define SRCD_CD_USER (1 << SRCD_CD_USER_OFFSET)
+
+/* SPDIF Phase Configuration register */
+#define SRPC_DPLL_LOCKED (1 << 6)
+#define SRPC_CLKSRC_SEL_OFFSET 7
+#define SRPC_CLKSRC_SEL_MASK (0xf << SRPC_CLKSRC_SEL_OFFSET)
+#define SRPC_CLKSRC_SEL_SET(x) ((x << SRPC_CLKSRC_SEL_OFFSET) & SRPC_CLKSRC_SEL_MASK)
+#define SRPC_CLKSRC_SEL_LOCKED_OFFSET1 5
+#define SRPC_CLKSRC_SEL_LOCKED_OFFSET2 2
+#define SRPC_GAINSEL_OFFSET 3
+#define SRPC_GAINSEL_MASK (0x7 << SRPC_GAINSEL_OFFSET)
+#define SRPC_GAINSEL_SET(x) ((x << SRPC_GAINSEL_OFFSET) & SRPC_GAINSEL_MASK)
+
+/* SPDIF rx clock source */
+enum spdif_rxclk_src {
+ SRPC_CLKSRC_0 = 0,
+ SRPC_CLKSRC_1,
+ SRPC_CLKSRC_2,
+ SRPC_CLKSRC_3,
+ SRPC_CLKSRC_4,
+ SRPC_CLKSRC_5,
+ SRPC_CLKSRC_6,
+ SRPC_CLKSRC_7,
+ SRPC_CLKSRC_8,
+ SRPC_CLKSRC_9,
+ SRPC_CLKSRC_10,
+ SRPC_CLKSRC_11,
+ SRPC_CLKSRC_12,
+ SRPC_CLKSRC_13,
+ SRPC_CLKSRC_14,
+ SRPC_CLKSRC_15,
+};
+#define SRPC_CLKSRC_MAX (SRPC_CLKSRC_15 + 1)
+#define DEFAULT_RXCLK_SRC SRPC_CLKSRC_1
+
+enum spdif_gainsel {
+ GAINSEL_MULTI_24 = 0,
+ GAINSEL_MULTI_16,
+ GAINSEL_MULTI_12,
+ GAINSEL_MULTI_8,
+ GAINSEL_MULTI_6,
+ GAINSEL_MULTI_4,
+ GAINSEL_MULTI_3,
+};
+#define GAINSEL_MULTI_MAX (GAINSEL_MULTI_3 + 1)
+#define SPDIF_DEFAULT_GAINSEL GAINSEL_MULTI_8
+
+/* SPDIF interrupt mask define */
+#define INT_DPLL_LOCKED (1 << 20)
+#define INT_TXFIFO_UNOV (1 << 19)
+#define INT_TXFIFO_RESYNC (1 << 18)
+#define INT_CNEW (1 << 17)
+#define INT_VAL_NOGOOD (1 << 16)
+#define INT_SYM_ERR (1 << 15)
+#define INT_BIT_ERR (1 << 14)
+#define INT_URX_FUL (1 << 10)
+#define INT_URX_OV (1 << 9)
+#define INT_QRX_FUL (1 << 8)
+#define INT_QRX_OV (1 << 7)
+#define INT_UQ_SYNC (1 << 6)
+#define INT_UQ_ERR (1 << 5)
+#define INT_RXFIFO_UNOV (1 << 4)
+#define INT_RXFIFO_RESYNC (1 << 3)
+#define INT_LOSS_LOCK (1 << 2)
+#define INT_TX_EM (1 << 1)
+#define INT_RXFIFO_FUL (1 << 0)
+
+/* SPDIF Clock register */
+#define STC_SYSCLK_DIV_OFFSET 11
+#define STC_SYSCLK_DIV_MASK (0x1ff << STC_TXCLK_SRC_OFFSET)
+#define STC_SYSCLK_DIV(x) ((((x) - 1) << STC_TXCLK_DIV_OFFSET) & STC_SYSCLK_DIV_MASK)
+#define STC_TXCLK_SRC_OFFSET 8
+#define STC_TXCLK_SRC_MASK (0x7 << STC_TXCLK_SRC_OFFSET)
+#define STC_TXCLK_SRC_SET(x) ((x << STC_TXCLK_SRC_OFFSET) & STC_TXCLK_SRC_MASK)
+#define STC_TXCLK_ALL_EN_OFFSET 7
+#define STC_TXCLK_ALL_EN_MASK (1 << STC_TXCLK_ALL_EN_OFFSET)
+#define STC_TXCLK_ALL_EN (1 << STC_TXCLK_ALL_EN_OFFSET)
+#define STC_TXCLK_DIV_OFFSET 0
+#define STC_TXCLK_DIV_MASK (0x7ff << STC_TXCLK_DIV_OFFSET)
+#define STC_TXCLK_DIV(x) ((((x) - 1) << STC_TXCLK_DIV_OFFSET) & STC_TXCLK_DIV_MASK)
+
+/* SPDIF tx clksrc */
+enum spdif_txclk_src {
+ STC_TXCLK_SRC_0 = 0,
+ STC_TXCLK_SRC_1,
+ STC_TXCLK_SRC_2,
+ STC_TXCLK_SRC_3,
+ STC_TXCLK_SRC_4,
+ STC_TXCLK_SRC_5,
+ STC_TXCLK_SRC_6,
+ STC_TXCLK_SRC_7,
+};
+#define STC_TXCLK_SRC_MAX (STC_TXCLK_SRC_7 + 1)
+#define DEFAULT_TXCLK_SRC STC_TXCLK_SRC_1
+
+/* SPDIF tx rate */
+enum spdif_txrate {
+ SPDIF_TXRATE_32000 = 0,
+ SPDIF_TXRATE_44100,
+ SPDIF_TXRATE_48000,
+};
+#define SPDIF_TXRATE_MAX (SPDIF_TXRATE_48000 + 1)
+
+
+#define SPDIF_CSTATUS_BYTE 6
+#define SPDIF_UBITS_SIZE 96
+#define SPDIF_QSUB_SIZE (SPDIF_UBITS_SIZE / 8)
+
+
+#define FSL_SPDIF_RATES_PLAYBACK (SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
+
+#define FSL_SPDIF_RATES_CAPTURE (SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_64000 | \
+ SNDRV_PCM_RATE_96000)
+
+#define FSL_SPDIF_FORMATS_PLAYBACK (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+#define FSL_SPDIF_FORMATS_CAPTURE (SNDRV_PCM_FMTBIT_S24_LE)
+
+#endif /* _FSL_SPDIF_DAI_H */
--
1.7.1
^ permalink raw reply related
* [PATCH v7 0/2] Add freescale S/PDIF CPU DAI and machine drivers
From: Nicolin Chen @ 2013-08-19 8:35 UTC (permalink / raw)
To: broonie, lars, p.zabel, s.hauer
Cc: mark.rutland, devicetree, alsa-devel, swarren, festevam, timur,
rob.herring, tomasz.figa, R65777, shawn.guo, linuxppc-dev
Changelog:
v6->v7:
* Removed extra comma in array.
* Revised some comments.
* Added some dev_err().
* Check the return value of soft reset.
* Use bitrev8() instead of extra reverse_bits().
* Use cache_bypass as default for regmap.
v5->v6:
* Sorted out rxtx clk source in DT binding.
* Use devm_xxxx() functions.
* Use platform_get_resource() instead.
v4->v5:
* Dropped rx/tx-clksrc-names DT bindings.
* Use standard clock binding instead to pass the clock source list.
* Update the compatible list by using "imx35", the first SoC that has spdif.
v3->v4:
* Use regmap for CPU DAI driver.
* Use individual clock source for 32KHz, 44KHz, 48KHz playback.
* Determine clock source configuration from 'clocks' entry.
* Added imx53 to compatible list, merged imx6q and imx6dl in the list.
* Improve the algorism of reverse_bits().
* Dropped the unneeded clk_put().
v2->v3:
* Removed a wrong tag from the commit of patch-1.
v1->v2:
* Dropped one applied patch for spdif dummy codec drivers.
* Use generic DMA DT binding.
* Let spdif controller driver calculate the clock div.
* Added one optional clock source for spdif tx.
* Reivsed documentation accordingly.
Nicolin Chen (2):
ASoC: fsl: Add S/PDIF CPU DAI driver
ASoC: fsl: Add S/PDIF machine driver
.../devicetree/bindings/sound/fsl,spdif.txt | 56 +
.../devicetree/bindings/sound/imx-audio-spdif.txt | 29 +
sound/soc/fsl/Kconfig | 14 +
sound/soc/fsl/Makefile | 4 +
sound/soc/fsl/fsl_spdif.c | 1277 ++++++++++++++++++++
sound/soc/fsl/fsl_spdif.h | 224 ++++
sound/soc/fsl/imx-spdif.c | 134 ++
7 files changed, 1738 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/sound/fsl,spdif.txt
create mode 100644 Documentation/devicetree/bindings/sound/imx-audio-spdif.txt
create mode 100644 sound/soc/fsl/fsl_spdif.c
create mode 100644 sound/soc/fsl/fsl_spdif.h
create mode 100644 sound/soc/fsl/imx-spdif.c
^ permalink raw reply
* Re: [PATCH v6 1/2] ASoC: fsl: Add S/PDIF CPU DAI driver
From: Nicolin Chen @ 2013-08-19 6:44 UTC (permalink / raw)
To: Bhushan Bharat-R65777
Cc: mark.rutland@arm.com, devicetree@vger.kernel.org,
alsa-devel@alsa-project.org, lars@metafoo.de,
swarren@wwwdotorg.org, linuxppc-dev@lists.ozlabs.org,
s.hauer@pengutronix.de, timur@tabi.org, rob.herring@calxeda.com,
tomasz.figa@gmail.com, broonie@kernel.org, p.zabel@pengutronix.de,
shawn.guo@linaro.org, festevam@gmail.com
In-Reply-To: <6A3DF150A5B70D4F9B66A25E3F7C888D07116CDE@039-SN2MPN1-013.039d.mgd.msft.net>
On Mon, Aug 19, 2013 at 02:31:47PM +0800, Bhushan Bharat-R65777 wrote:
> > > > We here suppose the reset bit would be cleared -- "The software
> > > > reset will last
> > > > 8 cycles." from RM, so if this happened to be a failure, the whole
> > > > IP module won't be normally working as well.
> > >
> > > Also add a comment describing this against why cycle = 1000 is selected.
> >
> > If it is done in 8 cycles, 1000-cycle will be surely a safe value for it.
> > As long as it finished in 8 cycles, it would quit anyway. Why against?
>
> I am not against, I am saying why it was not 200 or 50 or 20 etc. I am saying that write a comment saying this much is sufficient as per specification and so keep 1000/etc as preservative.
I did't mean that. The 'against' is from
"Also add a comment describing this 'against' why cycle = 1000 is selected."
Well, if you insist this extra comment for easy-understand, I'll add them
Thank you.
Nicolin Chen
^ permalink raw reply
* RE: [PATCH v6 1/2] ASoC: fsl: Add S/PDIF CPU DAI driver
From: Bhushan Bharat-R65777 @ 2013-08-19 6:31 UTC (permalink / raw)
To: Chen Guangyu-B42378
Cc: mark.rutland@arm.com, devicetree@vger.kernel.org,
alsa-devel@alsa-project.org, lars@metafoo.de,
swarren@wwwdotorg.org, linuxppc-dev@lists.ozlabs.org,
s.hauer@pengutronix.de, timur@tabi.org, rob.herring@calxeda.com,
tomasz.figa@gmail.com, broonie@kernel.org, p.zabel@pengutronix.de,
shawn.guo@linaro.org, festevam@gmail.com
In-Reply-To: <20130819062438.GB10544@MrMyself>
> -----Original Message-----
> From: Chen Guangyu-B42378
> Sent: Monday, August 19, 2013 11:55 AM
> To: Bhushan Bharat-R65777
> Cc: broonie@kernel.org; lars@metafoo.de; p.zabel@pengutronix.de;
> s.hauer@pengutronix.de; mark.rutland@arm.com; devicetree@vger.kernel.org;=
alsa-
> devel@alsa-project.org; swarren@wwwdotorg.org; festevam@gmail.com;
> timur@tabi.org; rob.herring@calxeda.com; tomasz.figa@gmail.com;
> shawn.guo@linaro.org; linuxppc-dev@lists.ozlabs.org
> Subject: Re: [PATCH v6 1/2] ASoC: fsl: Add S/PDIF CPU DAI driver
>=20
> Hi Bhushan,
>=20
> I'll revise some as you suggest. Just a few replies here.
>=20
> On Mon, Aug 19, 2013 at 12:38:11PM +0800, Bhushan Bharat-R65777 wrote:
> > > We here suppose the reset bit would be cleared -- "The software
> > > reset will last
> > > 8 cycles." from RM, so if this happened to be a failure, the whole
> > > IP module won't be normally working as well.
> >
> > Also add a comment describing this against why cycle =3D 1000 is select=
ed.
>=20
> If it is done in 8 cycles, 1000-cycle will be surely a safe value for it.
> As long as it finished in 8 cycles, it would quit anyway. Why against?
I am not against, I am saying why it was not 200 or 50 or 20 etc. I am sayi=
ng that write a comment saying this much is sufficient as per specification=
and so keep 1000/etc as preservative.
-Bharat
>=20
>=20
> > > > > +static bool fsl_spdif_volatile_reg(struct device *dev, unsigned
> > > > > +int reg) {
> > > > > + /* Sync all registers after reset */
> > > >
> > > > Where us sync :) ?
> > >
> > > The "return true" would do that. For volatile registers, if no "retur=
n true"
> > > here, the whole regmap would use the value in cache, while for some
> > > bits we need to trace its true value from the physical registers not =
from
> cache.
> >
> > Where will be device registers cached? Do not we program them to be non=
-
> cacheable in core?
>=20
> regmap has a regcache for all the mapped registers. Set the regsiters as
> volatile will allow the driver to sync the regcache with physical memory =
each
> time when using regmap_read/write/update_bits().
>=20
> But I think I can try to use the regcache_bypass instead.
>=20
>=20
> Thank you,
> Nicolin Chen
^ permalink raw reply
* Re: [PATCH v6 1/2] ASoC: fsl: Add S/PDIF CPU DAI driver
From: Nicolin Chen @ 2013-08-19 6:24 UTC (permalink / raw)
To: Bhushan Bharat-R65777
Cc: mark.rutland@arm.com, devicetree@vger.kernel.org,
alsa-devel@alsa-project.org, lars@metafoo.de,
swarren@wwwdotorg.org, linuxppc-dev@lists.ozlabs.org,
s.hauer@pengutronix.de, timur@tabi.org, rob.herring@calxeda.com,
tomasz.figa@gmail.com, broonie@kernel.org, p.zabel@pengutronix.de,
shawn.guo@linaro.org, festevam@gmail.com
In-Reply-To: <6A3DF150A5B70D4F9B66A25E3F7C888D07116BA7@039-SN2MPN1-013.039d.mgd.msft.net>
Hi Bhushan,
I'll revise some as you suggest. Just a few replies here.
On Mon, Aug 19, 2013 at 12:38:11PM +0800, Bhushan Bharat-R65777 wrote:
> > We here suppose the reset bit would be cleared -- "The software reset will last
> > 8 cycles." from RM, so if this happened to be a failure, the whole IP module
> > won't be normally working as well.
>
> Also add a comment describing this against why cycle = 1000 is selected.
If it is done in 8 cycles, 1000-cycle will be surely a safe value for it.
As long as it finished in 8 cycles, it would quit anyway. Why against?
> > > > +static bool fsl_spdif_volatile_reg(struct device *dev, unsigned int reg)
> > > > +{
> > > > + /* Sync all registers after reset */
> > >
> > > Where us sync :) ?
> >
> > The "return true" would do that. For volatile registers, if no "return true"
> > here, the whole regmap would use the value in cache, while for some bits
> > we need to trace its true value from the physical registers not from cache.
>
> Where will be device registers cached? Do not we program them to be non-cacheable in core?
regmap has a regcache for all the mapped registers. Set the regsiters as
volatile will allow the driver to sync the regcache with physical memory
each time when using regmap_read/write/update_bits().
But I think I can try to use the regcache_bypass instead.
Thank you,
Nicolin Chen
^ permalink raw reply
* RE: [RFC PATCH V3 3/5] powerpc/cpuidle: Generic powerpc backend cpuidle driver.
From: Wang Dongsheng-B40534 @ 2013-08-19 5:52 UTC (permalink / raw)
To: Deepthi Dharwar, benh@kernel.crashing.org,
daniel.lezcano@linaro.org, kernel@vger.kernel.org,
Wood Scott-B07421, linux-pm@lists.linux-foundation.org,
linuxppc-dev@lists.ozlabs.org
Cc: preeti@linux.vnet.ibm.com
In-Reply-To: <20130819042818.8609.77060.stgit@deepthi.in.ibm.com>
SSB0aGluayB3ZSBzaG91bGQgbW92ZSB0aGUgc3RhdGVzIGFuZCBoYW5kbGUgZnVuY3Rpb24gdG8g
YXJjaC9wb3dlci9wbGF0Zm9ybSoNClRoZSBzdGF0ZXMgYW5kIGhhbmRsZSBmdW5jdGlvbiBpcyBi
ZWxvbmcgdG8gYmFja2VuZCBkcml2ZXIsIG5vdCBmb3IgdGhpcywgZGlmZmVyZW50IHBsYXRmb3Jt
IGhhdmUgZGlmZmVyZW50IHN0YXRlLg0KRGlmZmVyZW50IHBsYXRmb3JtcyB0byBtYWtlIHRoZWly
IG93biBkZWFsIHdpdGggdGhlc2Ugc3RhdGVzLg0KDQpJIHRoaW5rIHdlIGNhbm5vdCBwdXQgYWxs
IHRoZSBzdGF0dXMgb2YgZGlmZmVyZW50IHBsYXRmb3JtcyBhbmQgaGFuZGxlciBpbiB0aGlzIGRy
aXZlci4NCg0KPiBkaWZmIC0tZ2l0IGEvZHJpdmVycy9jcHVpZGxlL0tjb25maWcgYi9kcml2ZXJz
L2NwdWlkbGUvS2NvbmZpZw0KPiBpbmRleCAwZTJjZDVjLi45OWVlNWQ0IDEwMDY0NA0KPiAtLS0g
YS9kcml2ZXJzL2NwdWlkbGUvS2NvbmZpZw0KPiArKysgYi9kcml2ZXJzL2NwdWlkbGUvS2NvbmZp
Zw0KPiBAQCAtNDIsNiArNDIsMTMgQEAgY29uZmlnIENQVV9JRExFX1pZTlENCj4gIAloZWxwDQo+
ICAJICBTZWxlY3QgdGhpcyB0byBlbmFibGUgY3B1aWRsZSBvbiBYaWxpbnggWnlucSBwcm9jZXNz
b3JzLg0KPiANCj4gK2NvbmZpZyBDUFVfSURMRV9QT1dFUlBDDQo+ICsJYm9vbCAiQ1BVIElkbGUg
ZHJpdmVyIGZvciBQT1dFUlBDIHBsYXRmb3JtcyINCj4gKwlkZXBlbmRzIG9uIFBQQzY0DQoNCldo
eSBub3QgUFBDPw0KDQoNCj4gKwlkZWZhdWx0IHkNCj4gKyAgICAgICAgaGVscA0KPiArICAgICAg
ICAgIFNlbGVjdCB0aGlzIG9wdGlvbiB0byBlbmFibGUgcHJvY2Vzc29yIGlkbGUgc3RhdGUgbWFu
YWdlbWVudA0KPiArCSAgZm9yIFBPV0VSUEMgcGxhdGZvcm0uDQo+ICBlbmRpZg0KPiANCj4gIGNv
bmZpZyBBUkNIX05FRURTX0NQVV9JRExFX0NPVVBMRUQNCj4gZGlmZiAtLWdpdCBhL2RyaXZlcnMv
Y3B1aWRsZS9NYWtlZmlsZSBiL2RyaXZlcnMvY3B1aWRsZS9NYWtlZmlsZQ0KPiBpbmRleCA4NzY3
YTdiLi5kMTJlMjA1IDEwMDY0NA0KPiAtLS0gYS9kcml2ZXJzL2NwdWlkbGUvTWFrZWZpbGUNCj4g
KysrIGIvZHJpdmVycy9jcHVpZGxlL01ha2VmaWxlDQo+IEBAIC04LDMgKzgsNSBAQCBvYmotJChD
T05GSUdfQVJDSF9ORUVEU19DUFVfSURMRV9DT1VQTEVEKSArPSBjb3VwbGVkLm8NCj4gIG9iai0k
KENPTkZJR19DUFVfSURMRV9DQUxYRURBKSArPSBjcHVpZGxlLWNhbHhlZGEubw0KPiAgb2JqLSQo
Q09ORklHX0FSQ0hfS0lSS1dPT0QpICs9IGNwdWlkbGUta2lya3dvb2Qubw0KPiAgb2JqLSQoQ09O
RklHX0NQVV9JRExFX1pZTlEpICs9IGNwdWlkbGUtenlucS5vDQo+ICsNCj4gK29iai0kKENPTkZJ
R19DUFVfSURMRV9QT1dFUlBDKSArPSBjcHVpZGxlLXBvd2VycGMubw0KPiBkaWZmIC0tZ2l0IGEv
ZHJpdmVycy9jcHVpZGxlL2NwdWlkbGUtcG93ZXJwYy5jIGIvZHJpdmVycy9jcHVpZGxlL2NwdWlk
bGUtDQo+IHBvd2VycGMuYw0KPiBuZXcgZmlsZSBtb2RlIDEwMDY0NA0KPiBpbmRleCAwMDAwMDAw
Li41NzU2MDg1DQo+IC0tLSAvZGV2L251bGwNCj4gKysrIGIvZHJpdmVycy9jcHVpZGxlL2NwdWlk
bGUtcG93ZXJwYy5jDQo+IEBAIC0wLDAgKzEsMzYxIEBADQo+ICsvKg0KPiArICogIHByb2Nlc3Nv
cl9pZGxlIC0gaWRsZSBzdGF0ZSBjcHVpZGxlIGRyaXZlci4NCj4gKyAqICBBZGFwdGVkIGZyb20g
ZHJpdmVycy9pZGxlL2ludGVsX2lkbGUuYyBhbmQNCj4gKyAqICBkcml2ZXJzL2FjcGkvcHJvY2Vz
c29yX2lkbGUuYw0KPiArICoNCj4gKyAqLw0KPiArDQo+ICsjaW5jbHVkZSA8bGludXgva2VybmVs
Lmg+DQo+ICsjaW5jbHVkZSA8bGludXgvbW9kdWxlLmg+DQo+ICsjaW5jbHVkZSA8bGludXgvaW5p
dC5oPg0KPiArI2luY2x1ZGUgPGxpbnV4L21vZHVsZXBhcmFtLmg+DQo+ICsjaW5jbHVkZSA8bGlu
dXgvY3B1aWRsZS5oPg0KPiArI2luY2x1ZGUgPGxpbnV4L2NwdS5oPg0KPiArI2luY2x1ZGUgPGxp
bnV4L25vdGlmaWVyLmg+DQo+ICsNCj4gKyNpbmNsdWRlIDxhc20vcGFjYS5oPg0KPiArI2luY2x1
ZGUgPGFzbS9yZWcuaD4NCj4gKyNpbmNsdWRlIDxhc20vbWFjaGRlcC5oPg0KPiArI2luY2x1ZGUg
PGFzbS9maXJtd2FyZS5oPg0KPiArI2luY2x1ZGUgPGFzbS9ydW5sYXRjaC5oPg0KPiArI2luY2x1
ZGUgPGFzbS9wbHBhcl93cmFwcGVycy5oPg0KPiArDQo+ICtzdHJ1Y3QgY3B1aWRsZV9kcml2ZXIg
cG93ZXJwY19pZGxlX2RyaXZlciA9IHsNCj4gKwkubmFtZSAgICAgICAgICAgICA9ICJwb3dlcnBj
X2lkbGUiLA0KPiArCS5vd25lciAgICAgICAgICAgID0gVEhJU19NT0RVTEUsDQo+ICt9Ow0KPiAr
DQo+ICsjZGVmaW5lIE1BWF9JRExFX1NUQVRFX0NPVU5UCTINCj4gKw0KPiArc3RhdGljIGludCBt
YXhfaWRsZV9zdGF0ZSA9IE1BWF9JRExFX1NUQVRFX0NPVU5UIC0gMTsNCklmIHRoaXMgaXMgYSBn
ZW5lcmljIGRyaXZlciwgZG8gbm90IGRlZmluZSBNQVhfSURMRV9TVEFURV9DT1VOVCwgYmVjYXVz
ZSB3ZSBkb24ndCBrbm93IGhvdyBtYW55IHN0YXRlIG9uIG90aGVyIHBsYXRmb3Jtcy4NCg0KSG93
IGFib3V0IHVzaW5nIEFSUkFZX1NJWkUgdG8gZ2V0IHRoZSBtYXggaWRsZSBzdGF0ZT8NCg0KPiAr
c3RhdGljIHN0cnVjdCBjcHVpZGxlX2RldmljZSBfX3BlcmNwdSAqcG93ZXJwY19jcHVpZGxlX2Rl
dmljZXM7DQo+ICtzdGF0aWMgc3RydWN0IGNwdWlkbGVfc3RhdGUgKmNwdWlkbGVfc3RhdGVfdGFi
bGU7DQo+ICsNClNob3VsZCBiZSByZW1vdmUgYWxsIGFib3V0ICpkZXZpY2UqLg0KSWYgdGhlIG5v
dGlmaWVyIGhhbmRsZSB1c2luZyBkZXZpY2UsIHlvdSBjYW4gdXNlICJjcHVpZGxlX2RldmljZXMi
KGluY2x1ZGUvbGludXgvY3B1aWRsZS5oKS4NCg0KPiArc3RhdGljIGlubGluZSB2b2lkIGlkbGVf
bG9vcF9wcm9sb2codW5zaWduZWQgbG9uZyAqaW5fcHVycikNCj4gK3sNCj4gKwkqaW5fcHVyciA9
IG1mc3ByKFNQUk5fUFVSUik7DQo+ICsJLyoNCj4gKwkgKiBJbmRpY2F0ZSB0byB0aGUgSFYgdGhh
dCB3ZSBhcmUgaWRsZS4gTm93IHdvdWxkIGJlDQo+ICsJICogYSBnb29kIHRpbWUgdG8gZmluZCBv
dGhlciB3b3JrIHRvIGRpc3BhdGNoLg0KPiArCSAqLw0KPiArCXNldF9scHBhY2FfaWRsZSgxKTsN
Cj4gK30NCj4gKw0KPiArc3RhdGljIGlubGluZSB2b2lkIGlkbGVfbG9vcF9lcGlsb2codW5zaWdu
ZWQgbG9uZyBpbl9wdXJyKQ0KPiArew0KPiArCWFkZF9scHBhY2Ffd2FpdF9zdGF0ZShtZnNwcihT
UFJOX1BVUlIpIC0gaW5fcHVycik7DQo+ICsJc2V0X2xwcGFjYV9pZGxlKDApOw0KPiArfQ0KPiAr
DQo+ICtzdGF0aWMgaW50IHNub296ZV9sb29wKHN0cnVjdCBjcHVpZGxlX2RldmljZSAqZGV2LA0K
PiArCQkJc3RydWN0IGNwdWlkbGVfZHJpdmVyICpkcnYsDQo+ICsJCQlpbnQgaW5kZXgpDQo+ICt7
DQo+ICsJdW5zaWduZWQgbG9uZyBpbl9wdXJyOw0KPiArDQo+ICsJaWRsZV9sb29wX3Byb2xvZygm
aW5fcHVycik7DQo+ICsJbG9jYWxfaXJxX2VuYWJsZSgpOw0KPiArCXNldF90aHJlYWRfZmxhZyhU
SUZfUE9MTElOR19OUkZMQUcpOw0KPiArDQo+ICsJd2hpbGUgKCFuZWVkX3Jlc2NoZWQoKSkgew0K
PiArCQlwcGM2NF9ydW5sYXRjaF9vZmYoKTsNCj4gKwkJSE1UX2xvdygpOw0KPiArCQlITVRfdmVy
eV9sb3coKTsNCj4gKwl9DQo+ICsNCj4gKwlITVRfbWVkaXVtKCk7DQo+ICsJY2xlYXJfdGhyZWFk
X2ZsYWcoVElGX1BPTExJTkdfTlJGTEFHKTsNCj4gKwlzbXBfbWIoKTsNCj4gKw0KPiArCWlkbGVf
bG9vcF9lcGlsb2coaW5fcHVycik7DQo+ICsNCj4gKwlyZXR1cm4gaW5kZXg7DQo+ICt9DQo+ICsN
Cj4gK3N0YXRpYyB2b2lkIGNoZWNrX2FuZF9jZWRlX3Byb2Nlc3Nvcih2b2lkKQ0KPiArew0KPiAr
CS8qDQo+ICsJICogRW5zdXJlIG91ciBpbnRlcnJ1cHQgc3RhdGUgaXMgcHJvcGVybHkgdHJhY2tl
ZCwNCj4gKwkgKiBhbHNvIGNoZWNrcyBpZiBubyBpbnRlcnJ1cHQgaGFzIG9jY3VycmVkIHdoaWxl
IHdlDQo+ICsJICogd2VyZSBzb2Z0LWRpc2FibGVkDQo+ICsJICovDQo+ICsJaWYgKHByZXBfaXJx
X2Zvcl9pZGxlKCkpIHsNCj4gKwkJY2VkZV9wcm9jZXNzb3IoKTsNCj4gKyNpZmRlZiBDT05GSUdf
VFJBQ0VfSVJRRkxBR1MNCj4gKwkJLyogRW5zdXJlIHRoYXQgSF9DRURFIHJldHVybnMgd2l0aCBJ
UlFzIG9uICovDQo+ICsJCWlmIChXQVJOX09OKCEobWZtc3IoKSAmIE1TUl9FRSkpKQ0KPiArCQkJ
X19oYXJkX2lycV9lbmFibGUoKTsNCj4gKyNlbmRpZg0KPiArCX0NCj4gK30NCj4gKw0KPiArc3Rh
dGljIGludCBkZWRpY2F0ZWRfY2VkZV9sb29wKHN0cnVjdCBjcHVpZGxlX2RldmljZSAqZGV2LA0K
PiArCQkJCXN0cnVjdCBjcHVpZGxlX2RyaXZlciAqZHJ2LA0KPiArCQkJCWludCBpbmRleCkNCj4g
K3sNCj4gKwl1bnNpZ25lZCBsb25nIGluX3B1cnI7DQo+ICsNCj4gKwlpZGxlX2xvb3BfcHJvbG9n
KCZpbl9wdXJyKTsNCj4gKwlzZXRfbHBwYWNhX2RvbmF0ZV9kZWRpY2F0ZWRfY3B1KDEpOw0KPiAr
DQo+ICsJcHBjNjRfcnVubGF0Y2hfb2ZmKCk7DQo+ICsJSE1UX21lZGl1bSgpOw0KPiArCWNoZWNr
X2FuZF9jZWRlX3Byb2Nlc3NvcigpOw0KPiArDQo+ICsJc2V0X2xwcGFjYV9kb25hdGVfZGVkaWNh
dGVkX2NwdSgwKTsNCj4gKwlpZGxlX2xvb3BfZXBpbG9nKGluX3B1cnIpOw0KPiArDQo+ICsJcmV0
dXJuIGluZGV4Ow0KPiArfQ0KPiArDQo+ICtzdGF0aWMgaW50IHNoYXJlZF9jZWRlX2xvb3Aoc3Ry
dWN0IGNwdWlkbGVfZGV2aWNlICpkZXYsDQo+ICsJCQlzdHJ1Y3QgY3B1aWRsZV9kcml2ZXIgKmRy
diwNCj4gKwkJCWludCBpbmRleCkNCj4gK3sNCj4gKwl1bnNpZ25lZCBsb25nIGluX3B1cnI7DQo+
ICsNCj4gKwlpZGxlX2xvb3BfcHJvbG9nKCZpbl9wdXJyKTsNCj4gKw0KPiArCS8qDQo+ICsJICog
WWllbGQgdGhlIHByb2Nlc3NvciB0byB0aGUgaHlwZXJ2aXNvci4gIFdlIHJldHVybiBpZg0KPiAr
CSAqIGFuIGV4dGVybmFsIGludGVycnVwdCBvY2N1cnMgKHdoaWNoIGFyZSBkcml2ZW4gcHJpb3IN
Cj4gKwkgKiB0byByZXR1cm5pbmcgaGVyZSkgb3IgaWYgYSBwcm9kIG9jY3VycyBmcm9tIGFub3Ro
ZXINCj4gKwkgKiBwcm9jZXNzb3IuIFdoZW4gcmV0dXJuaW5nIGhlcmUsIGV4dGVybmFsIGludGVy
cnVwdHMNCj4gKwkgKiBhcmUgZW5hYmxlZC4NCj4gKwkgKi8NCj4gKwljaGVja19hbmRfY2VkZV9w
cm9jZXNzb3IoKTsNCj4gKw0KPiArCWlkbGVfbG9vcF9lcGlsb2coaW5fcHVycik7DQo+ICsNCj4g
KwlyZXR1cm4gaW5kZXg7DQo+ICt9DQo+ICsNCj4gKy8qDQo+ICsgKiBTdGF0ZXMgZm9yIGRlZGlj
YXRlZCBwYXJ0aXRpb24gY2FzZS4NCj4gKyAqLw0KPiArc3RhdGljIHN0cnVjdCBjcHVpZGxlX3N0
YXRlIGRlZGljYXRlZF9zdGF0ZXNbTUFYX0lETEVfU1RBVEVfQ09VTlRdID0gew0KPiArCXsgLyog
U25vb3plICovDQo+ICsJCS5uYW1lID0gInNub296ZSIsDQo+ICsJCS5kZXNjID0gInNub296ZSIs
DQo+ICsJCS5mbGFncyA9IENQVUlETEVfRkxBR19USU1FX1ZBTElELA0KPiArCQkuZXhpdF9sYXRl
bmN5ID0gMCwNCj4gKwkJLnRhcmdldF9yZXNpZGVuY3kgPSAwLA0KPiArCQkuZW50ZXIgPSAmc25v
b3plX2xvb3AgfSwNCj4gKwl7IC8qIENFREUgKi8NCj4gKwkJLm5hbWUgPSAiQ0VERSIsDQo+ICsJ
CS5kZXNjID0gIkNFREUiLA0KPiArCQkuZmxhZ3MgPSBDUFVJRExFX0ZMQUdfVElNRV9WQUxJRCwN
Cj4gKwkJLmV4aXRfbGF0ZW5jeSA9IDEwLA0KPiArCQkudGFyZ2V0X3Jlc2lkZW5jeSA9IDEwMCwN
Cj4gKwkJLmVudGVyID0gJmRlZGljYXRlZF9jZWRlX2xvb3AgfSwNCj4gK307DQo+ICsNCj4gKy8q
DQo+ICsgKiBTdGF0ZXMgZm9yIHNoYXJlZCBwYXJ0aXRpb24gY2FzZS4NCj4gKyAqLw0KPiArc3Rh
dGljIHN0cnVjdCBjcHVpZGxlX3N0YXRlIHNoYXJlZF9zdGF0ZXNbTUFYX0lETEVfU1RBVEVfQ09V
TlRdID0gew0KPiArCXsgLyogU2hhcmVkIENlZGUgKi8NCj4gKwkJLm5hbWUgPSAiU2hhcmVkIENl
ZGUiLA0KPiArCQkuZGVzYyA9ICJTaGFyZWQgQ2VkZSIsDQo+ICsJCS5mbGFncyA9IENQVUlETEVf
RkxBR19USU1FX1ZBTElELA0KPiArCQkuZXhpdF9sYXRlbmN5ID0gMCwNCj4gKwkJLnRhcmdldF9y
ZXNpZGVuY3kgPSAwLA0KPiArCQkuZW50ZXIgPSAmc2hhcmVkX2NlZGVfbG9vcCB9LA0KPiArfTsN
Cj4gKw0KPiArdm9pZCB1cGRhdGVfc210X3Nub296ZV9kZWxheShpbnQgY3B1LCBpbnQgcmVzaWRl
bmN5KQ0KPiArew0KPiArCXN0cnVjdCBjcHVpZGxlX2RyaXZlciAqZHJ2ID0gY3B1aWRsZV9nZXRf
ZHJpdmVyKCk7DQo+ICsJc3RydWN0IGNwdWlkbGVfZGV2aWNlICpkZXYgPSBwZXJfY3B1KGNwdWlk
bGVfZGV2aWNlcywgY3B1KTsNCj4gKw0KPiArCWlmIChjcHVpZGxlX3N0YXRlX3RhYmxlICE9IGRl
ZGljYXRlZF9zdGF0ZXMpDQo+ICsJCXJldHVybjsNCj4gKw0KPiArCWlmIChyZXNpZGVuY3kgPCAw
KSB7DQo+ICsJCS8qIERpc2FibGUgdGhlIE5hcCBzdGF0ZSBvbiB0aGF0IGNwdSAqLw0KPiArCQlp
ZiAoZGV2KQ0KPiArCQkJZGV2LT5zdGF0ZXNfdXNhZ2VbMV0uZGlzYWJsZSA9IDE7DQo+ICsJfSBl
bHNlDQo+ICsJCWlmIChkcnYpDQo+ICsJCQlkcnYtPnN0YXRlc1sxXS50YXJnZXRfcmVzaWRlbmN5
ID0gcmVzaWRlbmN5Ow0KPiArfQ0KPiArDQo+ICtzdGF0aWMgaW50IHBvd2VycGNfY3B1aWRsZV9h
ZGRfY3B1X25vdGlmaWVyKHN0cnVjdCBub3RpZmllcl9ibG9jayAqbiwNCj4gKwkJCXVuc2lnbmVk
IGxvbmcgYWN0aW9uLCB2b2lkICpoY3B1KQ0KPiArew0KPiArCWludCBob3RjcHUgPSAodW5zaWdu
ZWQgbG9uZyloY3B1Ow0KPiArCXN0cnVjdCBjcHVpZGxlX2RldmljZSAqZGV2ID0NCj4gKwkJCXBl
cl9jcHVfcHRyKHBvd2VycGNfY3B1aWRsZV9kZXZpY2VzLCBob3RjcHUpOw0KPiArDQo+ICsJaWYg
KGRldiAmJiBjcHVpZGxlX2dldF9kcml2ZXIoKSkgew0KPiArCQlzd2l0Y2ggKGFjdGlvbikgew0K
PiArCQljYXNlIENQVV9PTkxJTkU6DQo+ICsJCWNhc2UgQ1BVX09OTElORV9GUk9aRU46DQo+ICsJ
CQljcHVpZGxlX3BhdXNlX2FuZF9sb2NrKCk7DQo+ICsJCQljcHVpZGxlX2VuYWJsZV9kZXZpY2Uo
ZGV2KTsNCj4gKwkJCWNwdWlkbGVfcmVzdW1lX2FuZF91bmxvY2soKTsNCj4gKwkJCWJyZWFrOw0K
PiArDQo+ICsJCWNhc2UgQ1BVX0RFQUQ6DQo+ICsJCWNhc2UgQ1BVX0RFQURfRlJPWkVOOg0KPiAr
CQkJY3B1aWRsZV9wYXVzZV9hbmRfbG9jaygpOw0KPiArCQkJY3B1aWRsZV9kaXNhYmxlX2Rldmlj
ZShkZXYpOw0KPiArCQkJY3B1aWRsZV9yZXN1bWVfYW5kX3VubG9jaygpOw0KPiArCQkJYnJlYWs7
DQo+ICsNCj4gKwkJZGVmYXVsdDoNCj4gKwkJCXJldHVybiBOT1RJRllfRE9ORTsNCj4gKwkJfQ0K
PiArCX0NCj4gKwlyZXR1cm4gTk9USUZZX09LOw0KPiArfQ0KPiArDQo+ICtzdGF0aWMgc3RydWN0
IG5vdGlmaWVyX2Jsb2NrIHNldHVwX2hvdHBsdWdfbm90aWZpZXIgPSB7DQo+ICsJLm5vdGlmaWVy
X2NhbGwgPSBwb3dlcnBjX2NwdWlkbGVfYWRkX2NwdV9ub3RpZmllciwNCj4gK307DQo+ICsNCldl
IHNob3VsZCBkaXNjdXNzIHRoaXMgd2l0aCBEYW5pZWwuDQoNCj4gKy8qDQo+ICsgKiBwb3dlcnBj
X2NwdWlkbGVfZHJpdmVyX2luaXQoKQ0KPiArICovDQo+ICtzdGF0aWMgaW50IHBvd2VycGNfY3B1
aWRsZV9kcml2ZXJfaW5pdCh2b2lkKQ0KPiArew0KPiArCWludCBpZGxlX3N0YXRlOw0KPiArCXN0
cnVjdCBjcHVpZGxlX2RyaXZlciAqZHJ2ID0gJnBvd2VycGNfaWRsZV9kcml2ZXI7DQo+ICsNCj4g
KwlkcnYtPnN0YXRlX2NvdW50ID0gMDsNCj4gKw0KPiArCWZvciAoaWRsZV9zdGF0ZSA9IDA7IGlk
bGVfc3RhdGUgPCBNQVhfSURMRV9TVEFURV9DT1VOVDsNCj4gKytpZGxlX3N0YXRlKSB7DQo+ICsN
Cj4gKwkJaWYgKGlkbGVfc3RhdGUgPiBtYXhfaWRsZV9zdGF0ZSkNCj4gKwkJCWJyZWFrOw0KPiAr
DQo+ICsJCS8qIGlzIHRoZSBzdGF0ZSBub3QgZW5hYmxlZD8gKi8NCj4gKwkJaWYgKGNwdWlkbGVf
c3RhdGVfdGFibGVbaWRsZV9zdGF0ZV0uZW50ZXIgPT0gTlVMTCkNCj4gKwkJCWNvbnRpbnVlOw0K
PiArDQpEaWQgdGhlIHN0YXRlIGhhdmUgZGVwZW5kZW50Pw0KSWYgeWVzLCBtYXkgYmUgc2hvdWxk
IGJyZWFrIG91dCB0aGUgbG9vcCwgbm90IGNvbnRpbnVlLg0KDQo+ICsJCWRydi0+c3RhdGVzW2Ry
di0+c3RhdGVfY291bnRdID0JLyogc3RydWN0dXJlIGNvcHkgKi8NCj4gKwkJCWNwdWlkbGVfc3Rh
dGVfdGFibGVbaWRsZV9zdGF0ZV07DQo+ICsNCj4gKwkJZHJ2LT5zdGF0ZV9jb3VudCArPSAxOw0K
PiArCX0NCj4gKw0KPiArCXJldHVybiAwOw0KPiArfQ0KPiArDQo+ICsvKiBwb3dlcnBjX2lkbGVf
ZGV2aWNlc191bmluaXQodm9pZCkNCj4gKyAqIHVucmVnaXN0ZXIgY3B1aWRsZSBkZXZpY2VzIGFu
ZCBkZS1hbGxvY2F0ZSBtZW1vcnkNCj4gKyAqLw0KPiArc3RhdGljIHZvaWQgcG93ZXJwY19pZGxl
X2RldmljZXNfdW5pbml0KHZvaWQpDQo+ICt7DQo+ICsJaW50IGk7DQo+ICsJc3RydWN0IGNwdWlk
bGVfZGV2aWNlICpkZXY7DQo+ICsNCj4gKwlmb3JfZWFjaF9wb3NzaWJsZV9jcHUoaSkgew0KPiAr
CQlkZXYgPSBwZXJfY3B1X3B0cihwb3dlcnBjX2NwdWlkbGVfZGV2aWNlcywgaSk7DQo+ICsJCWNw
dWlkbGVfdW5yZWdpc3Rlcl9kZXZpY2UoZGV2KTsNCj4gKwl9DQo+ICsNCj4gKwlmcmVlX3BlcmNw
dShwb3dlcnBjX2NwdWlkbGVfZGV2aWNlcyk7DQo+ICsJcmV0dXJuOw0KPiArfQ0KPiArDQo+ICsv
KiBwb3dlcnBjX2lkbGVfZGV2aWNlc19pbml0KCkNCj4gKyAqIGFsbG9jYXRlLCBpbml0aWFsaXpl
IGFuZCByZWdpc3RlciBjcHVpZGxlIGRldmljZQ0KPiArICovDQo+ICtzdGF0aWMgaW50IHBvd2Vy
cGNfaWRsZV9kZXZpY2VzX2luaXQodm9pZCkNCj4gK3sNCj4gKwlpbnQgaTsNCj4gKwlzdHJ1Y3Qg
Y3B1aWRsZV9kcml2ZXIgKmRydiA9ICZwb3dlcnBjX2lkbGVfZHJpdmVyOw0KPiArCXN0cnVjdCBj
cHVpZGxlX2RldmljZSAqZGV2Ow0KPiArDQo+ICsJcG93ZXJwY19jcHVpZGxlX2RldmljZXMgPSBh
bGxvY19wZXJjcHUoc3RydWN0IGNwdWlkbGVfZGV2aWNlKTsNCj4gKwlpZiAocG93ZXJwY19jcHVp
ZGxlX2RldmljZXMgPT0gTlVMTCkNCj4gKwkJcmV0dXJuIC1FTk9NRU07DQo+ICsNCj4gKwlmb3Jf
ZWFjaF9wb3NzaWJsZV9jcHUoaSkgew0KPiArCQlkZXYgPSBwZXJfY3B1X3B0cihwb3dlcnBjX2Nw
dWlkbGVfZGV2aWNlcywgaSk7DQo+ICsJCWRldi0+c3RhdGVfY291bnQgPSBkcnYtPnN0YXRlX2Nv
dW50Ow0KPiArCQlkZXYtPmNwdSA9IGk7DQo+ICsJCWlmIChjcHVpZGxlX3JlZ2lzdGVyX2Rldmlj
ZShkZXYpKSB7DQoNClBsZWFzZSB1c2UgY3B1aWRsZV9yZWdpc3RlcigpLg0KDQo+ICsJCQlwcmlu
dGsoS0VSTl9ERUJVRyBcDQo+ICsJCQkJImNwdWlkbGVfcmVnaXN0ZXJfZGV2aWNlICVkIGZhaWxl
ZCFcbiIsIGkpOw0KPiArCQkJcmV0dXJuIC1FSU87DQo+ICsJCX0NCj4gKwl9DQo+ICsNCj4gKwly
ZXR1cm4gMDsNCj4gK30NCj4gKw0KPiArLyoNCj4gKyAqIHBvd2VycGNfaWRsZV9wcm9iZSgpDQo+
ICsgKiBDaG9vc2Ugc3RhdGUgdGFibGUgZm9yIHNoYXJlZCB2ZXJzdXMgZGVkaWNhdGVkIHBhcnRp
dGlvbg0KPiArICovDQo+ICtzdGF0aWMgaW50IHBvd2VycGNfaWRsZV9wcm9iZSh2b2lkKQ0KPiAr
ew0KPiArDQo+ICsJaWYgKCFmaXJtd2FyZV9oYXNfZmVhdHVyZShGV19GRUFUVVJFX1NQTFBBUikp
DQo+ICsJCXJldHVybiAtRU5PREVWOw0KPiArDQo+ICsJaWYgKGNwdWlkbGVfZGlzYWJsZSAhPSBJ
RExFX05PX09WRVJSSURFKQ0KPiArCQlyZXR1cm4gLUVOT0RFVjsNCj4gKw0KPiArCWlmIChtYXhf
aWRsZV9zdGF0ZSA9PSAwKSB7DQo+ICsJCXByaW50ayhLRVJOX0RFQlVHICJwb3dlcnBjIHByb2Nl
c3NvciBpZGxlIGRpc2FibGVkLlxuIik7DQo+ICsJCXJldHVybiAtRVBFUk07DQo+ICsJfQ0KPiAr
DQo+ICsJaWYgKGZpcm13YXJlX2hhc19mZWF0dXJlKEZXX0ZFQVRVUkVfU1BMUEFSKSkgew0KPiAr
CQlpZiAoZ2V0X2xwcGFjYV9pc19zaGFyZWRfcHJvYygpID09IDEpDQo+ICsJCQljcHVpZGxlX3N0
YXRlX3RhYmxlID0gc2hhcmVkX3N0YXRlczsNCj4gKwkJZWxzZSBpZiAoZ2V0X2xwcGFjYV9pc19z
aGFyZWRfcHJvYygpID09IDApDQo+ICsJCQljcHVpZGxlX3N0YXRlX3RhYmxlID0gZGVkaWNhdGVk
X3N0YXRlczsNCj4gKwl9IGVsc2UNCj4gKwkJcmV0dXJuIC1FTk9ERVY7DQo+ICsNCj4gKwlyZXR1
cm4gMDsNCj4gK30NCj4gKw0KPiArc3RhdGljIGludCBfX2luaXQgcG93ZXJwY19wcm9jZXNzb3Jf
aWRsZV9pbml0KHZvaWQpDQo+ICt7DQo+ICsJaW50IHJldHZhbDsNCj4gKw0KPiArCXJldHZhbCA9
IHBvd2VycGNfaWRsZV9wcm9iZSgpOw0KPiArCWlmIChyZXR2YWwpDQo+ICsJCXJldHVybiByZXR2
YWw7DQo+ICsNCj4gKwlwb3dlcnBjX2NwdWlkbGVfZHJpdmVyX2luaXQoKTsNCj4gKwlyZXR2YWwg
PSBjcHVpZGxlX3JlZ2lzdGVyX2RyaXZlcigmcG93ZXJwY19pZGxlX2RyaXZlcik7DQo+ICsJaWYg
KHJldHZhbCkgew0KPiArCQlwcmludGsoS0VSTl9ERUJVRyAiUmVnaXN0cmF0aW9uIG9mIHBvd2Vy
cGMgZHJpdmVyIGZhaWxlZC5cbiIpOw0KPiArCQlyZXR1cm4gcmV0dmFsOw0KPiArCX0NCj4gKw0K
PiArCXJldHZhbCA9IHBvd2VycGNfaWRsZV9kZXZpY2VzX2luaXQoKTsNCj4gKwlpZiAocmV0dmFs
KSB7DQo+ICsJCXBvd2VycGNfaWRsZV9kZXZpY2VzX3VuaW5pdCgpOw0KPiArCQljcHVpZGxlX3Vu
cmVnaXN0ZXJfZHJpdmVyKCZwb3dlcnBjX2lkbGVfZHJpdmVyKTsNCj4gKwkJcmV0dXJuIHJldHZh
bDsNCj4gKwl9DQo+ICsNCj4gKwlyZWdpc3Rlcl9jcHVfbm90aWZpZXIoJnNldHVwX2hvdHBsdWdf
bm90aWZpZXIpOw0KPiArCXByaW50ayhLRVJOX0RFQlVHICJwb3dlcnBjX2lkbGVfZHJpdmVyIHJl
Z2lzdGVyZWRcbiIpOw0KPiArDQo+ICsJcmV0dXJuIDA7DQo+ICt9DQo+ICsNCj4gK3N0YXRpYyB2
b2lkIF9fZXhpdCBwb3dlcnBjX3Byb2Nlc3Nvcl9pZGxlX2V4aXQodm9pZCkNCj4gK3sNCj4gKw0K
PiArCXVucmVnaXN0ZXJfY3B1X25vdGlmaWVyKCZzZXR1cF9ob3RwbHVnX25vdGlmaWVyKTsNCj4g
Kwlwb3dlcnBjX2lkbGVfZGV2aWNlc191bmluaXQoKTsNCj4gKwljcHVpZGxlX3VucmVnaXN0ZXJf
ZHJpdmVyKCZwb3dlcnBjX2lkbGVfZHJpdmVyKTsNCj4gKw0KPiArCXJldHVybjsNCj4gK30NCj4g
Kw0KRGlkIHlvdSB0ZXN0IG1vZHVsZSBtb2RlPyAqUmVtb3ZlKiB0aGUgbW9kdWxlIGNhbm5vdCB3
b3JrLg0KPiANCg0K
^ permalink raw reply
* Re: [PATCH V2 5/6] cpuidle/powerpc: Backend-powerpc idle driver for powernv and pseries.
From: Deepthi Dharwar @ 2013-08-19 4:43 UTC (permalink / raw)
To: Scott Wood
Cc: linux-pm, daniel.lezcano, dongsheng.wang, linux-kernel, rjw,
srivatsa.bhat, preeti, linuxppc-dev
In-Reply-To: <1375832471.5600.41.camel@snotra.buserror.net>
On 08/07/2013 05:11 AM, Scott Wood wrote:
> On Wed, 2013-08-07 at 09:30 +1000, Benjamin Herrenschmidt wrote:
>> On Tue, 2013-08-06 at 18:08 -0500, Scott Wood wrote:
>>> Here's another example. get_lppaca() will only build on book3s -- and
>>> yet we get requests for e500 code to use this file.
>>
>> Indeed, Besides there is already accessors afaik for lppaca that compile
>> to nothing on E (and if not they would be trivial to add).
>
> I don't see such an accessor, but if there were, what would happen when
> the caller goes on to dereference that nothing?
>
> There is an accessor for shared_proc specifically (in the spinlock code)
> -- not that it would be much help on booke to just compile away that
> check and always select one of the pseries state tables over the other.
>
> -Scott
Thanks a lot Scott and Ben for the review.
I have addressed the issues in V3 of this patch series which I have just
posted out.
Regards,
Deepthi
>
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
>
>
^ permalink raw reply
* RE: [PATCH v6 1/2] ASoC: fsl: Add S/PDIF CPU DAI driver
From: Bhushan Bharat-R65777 @ 2013-08-19 4:38 UTC (permalink / raw)
To: Chen Guangyu-B42378
Cc: mark.rutland@arm.com, devicetree@vger.kernel.org,
alsa-devel@alsa-project.org, lars@metafoo.de,
swarren@wwwdotorg.org, linuxppc-dev@lists.ozlabs.org,
s.hauer@pengutronix.de, timur@tabi.org, rob.herring@calxeda.com,
tomasz.figa@gmail.com, broonie@kernel.org, p.zabel@pengutronix.de,
shawn.guo@linaro.org, festevam@gmail.com
In-Reply-To: <20130819030743.GA10544@MrMyself>
> -----Original Message-----
> From: Chen Guangyu-B42378
> Sent: Monday, August 19, 2013 8:38 AM
> To: Bhushan Bharat-R65777
> Cc: broonie@kernel.org; lars@metafoo.de; p.zabel@pengutronix.de;
> s.hauer@pengutronix.de; mark.rutland@arm.com; devicetree@vger.kernel.org;=
alsa-
> devel@alsa-project.org; swarren@wwwdotorg.org; festevam@gmail.com;
> timur@tabi.org; rob.herring@calxeda.com; tomasz.figa@gmail.com;
> shawn.guo@linaro.org; linuxppc-dev@lists.ozlabs.org
> Subject: Re: [PATCH v6 1/2] ASoC: fsl: Add S/PDIF CPU DAI driver
>=20
> Hi Bhushan,
>=20
> Thank you for the comments :)
> I'll fix some in v7.
>=20
> Here is my some replies to you.
>=20
> On Sat, Aug 17, 2013 at 02:24:19AM +0800, Bhushan Bharat-R65777 wrote:
> > > This patch add S/PDIF controller driver for Freescale SoC.
> >
> > Please give some more description of the driver?
>=20
> I've referred some ASoC drivers, all of them seem to be brief as mine.
> So I'm not sure what else information I should provide here. It's already=
kinda
> okay to me.
Other does not have description does not mean we also should not add descri=
ption here.
Please describe in few lines about this driver and devices it handles?
>=20
>=20
> > > +struct spdif_mixer_control {
> > > + /* buffer ptrs for writer */
> > > + u32 upos;
> > > + u32 qpos;
> >
> > They does not look like pointer?
>=20
> They are more like offsets to get the correspond pointer.
> But I'll change the confusing comments.
>=20
>=20
> > > +/* U/Q Channel receive register full */ static void
> > > +spdif_irq_uqrx_full(struct fsl_spdif_priv *spdif_priv, char name) {
> > > + struct spdif_mixer_control *ctrl =3D &spdif_priv->fsl_spdif_control=
;
> > > + struct regmap *regmap =3D spdif_priv->regmap;
> > > + struct platform_device *pdev =3D spdif_priv->pdev;
> > > + u32 *pos, size, val, reg;
> > > +
> > > + switch (name) {
> > > + case 'U':
> > > + pos =3D &ctrl->upos;
> > > + size =3D SPDIF_UBITS_SIZE;
> > > + reg =3D REG_SPDIF_SRU;
> > > + break;
> > > + case 'Q':
> > > + pos =3D &ctrl->qpos;
> > > + size =3D SPDIF_QSUB_SIZE;
> > > + reg =3D REG_SPDIF_SRQ;
> > > + break;
> > > + default:
> > > + return;
> >
> > Should return error.
>=20
> IMHO, this should be fine. It's a void type function and being used in th=
e
> isr(). The params 'name' is totally controlled by driver itself, so basic=
ally we
> don't need to worry about the default path.
Silently returning on potential error is bad. At least add a printk/BUGON o=
r something similar which points that some unexpected parameter is passed.
>=20
> > > + if (*pos >=3D size * 2) {
> > > + *pos =3D 0;
> > > + } else if (unlikely((*pos % size) + 3 > size)) {
> > > + dev_err(&pdev->dev, "User bit receivce buffer overflow\n");
> > > + return;
> >
> > Should return error.
>=20
> Ditto, it's being used in isr(), we don't need to detect the return value=
, just
> use dev_err() to warn users and let the driver clear the irq.
Same as above
>=20
>=20
> > > +/* U/Q Channel framing error */
> > > +static void spdif_irq_uq_err(struct fsl_spdif_priv *spdif_priv) {
> > > + struct spdif_mixer_control *ctrl =3D &spdif_priv->fsl_spdif_control=
;
> > > + struct regmap *regmap =3D spdif_priv->regmap;
> > > + struct platform_device *pdev =3D spdif_priv->pdev;
> > > + u32 val;
> > > +
> > > + dev_dbg(&pdev->dev, "isr: U/Q Channel framing error\n");
> > > +
> > > + /* read U/Q data and do buffer reset */
> > > + regmap_read(regmap, REG_SPDIF_SRU, &val);
> > > + regmap_read(regmap, REG_SPDIF_SRQ, &val);
> >
> > Above prints says read u/q data and buffer reset, what is buffer reset?=
Is
> that read on clear?
>=20
> That's the behavior needed by IP, according to the reference manual:
> "U Channel receive register full, can't be cleared with reg. IntClear.
> To clear it, read from U Rx reg." and "Q Channel receive register full, c=
an't be
> cleared with reg. IntClear. To clear it, read from Q Rx reg."
Then please add this behavior in comment.
>=20
>=20
> > > +static void spdif_softreset(struct fsl_spdif_priv *spdif_priv) {
> > > + struct regmap *regmap =3D spdif_priv->regmap;
> > > + u32 val, cycle =3D 1000;
> > > +
> > > + regmap_write(regmap, REG_SPDIF_SCR, SCR_SOFT_RESET);
> > > + regcache_sync(regmap);
> > > +
> > > + /* RESET bit would be cleared after finishing its reset procedure *=
/
> > > + do {
> > > + regmap_read(regmap, REG_SPDIF_SCR, &val);
> > > + } while ((val & SCR_SOFT_RESET) && cycle--);
> >
> > What if reset is not cleared and timeout happen?
>=20
> We here suppose the reset bit would be cleared -- "The software reset wil=
l last
> 8 cycles." from RM, so if this happened to be a failure, the whole IP mod=
ule
> won't be normally working as well.
Also add a comment describing this against why cycle =3D 1000 is selected.
>=20
> Well, but I don't mind to put here an extra failed return to make it clea=
r.
>=20
>=20
> > > +static u8 reverse_bits(u8 input)
> > > +{
> > > + u8 tmp =3D input;
> > > +
> > > + tmp =3D ((tmp & 0b10101010) >> 1) | ((tmp << 1) & 0b10101010);
> > > + tmp =3D ((tmp & 0b11001100) >> 2) | ((tmp << 2) & 0b11001100);
> > > + tmp =3D ((tmp & 0b11110000) >> 4) | ((tmp << 4) & 0b11110000);
> >
> > What is this logic, can the hardcoding be removed and some description =
on
> above calculation?
>=20
> This was provided by Philipp Zabel in his review at patch v3.
> It's pretty clear to me that it just reverses the bits for u8.
This is not obvious. Why not use bitrev8() ?
> I don't think this logic has any problem and the mask here doesn't look l=
ike any
> hardcode to me.
>=20
>=20
> > > +static bool fsl_spdif_volatile_reg(struct device *dev, unsigned int =
reg)
> > > +{
> > > + /* Sync all registers after reset */
> >
> > Where us sync :) ?
>=20
> The "return true" would do that. For volatile registers, if no "return tr=
ue"
> here, the whole regmap would use the value in cache, while for some bits
> we need to trace its true value from the physical registers not from cach=
e.
Where will be device registers cached? Do not we program them to be non-cac=
heable in core?
-Bharat
>=20
>=20
> Best regards,
> Nicolin Chen
^ permalink raw reply
* [RFC PATCH V3 5/5] powernv/cpuidle: Enable idle powernv cpu to call into the cpuidle framework.
From: Deepthi Dharwar @ 2013-08-19 4:28 UTC (permalink / raw)
To: benh, daniel.lezcano, kernel, scottwood, linux-pm, linuxppc-dev
Cc: preeti, dongsheng.wang
In-Reply-To: <20130819042736.8609.17890.stgit@deepthi.in.ibm.com>
This patch enables idle cpu on the powernv platform to hook on to the cpuidle
framework, if available, else call on to default idle platform
code.
Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
---
arch/powerpc/platforms/powernv/setup.c | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c
index 84438af..fc62f21 100644
--- a/arch/powerpc/platforms/powernv/setup.c
+++ b/arch/powerpc/platforms/powernv/setup.c
@@ -25,6 +25,7 @@
#include <linux/of.h>
#include <linux/interrupt.h>
#include <linux/bug.h>
+#include <linux/cpuidle.h>
#include <asm/machdep.h>
#include <asm/firmware.h>
@@ -175,6 +176,17 @@ static void __init pnv_setup_machdep_rtas(void)
}
#endif /* CONFIG_PPC_POWERNV_RTAS */
+void powernv_idle(void)
+{
+ /* Hook to cpuidle framework if available, else
+ * call on default platform idle code
+ */
+ if (cpuidle_idle_call()) {
+ HMT_low();
+ HMT_very_low();
+ }
+}
+
static int __init pnv_probe(void)
{
unsigned long root = of_get_flat_dt_root();
@@ -205,7 +217,7 @@ define_machine(powernv) {
.show_cpuinfo = pnv_show_cpuinfo,
.progress = pnv_progress,
.machine_shutdown = pnv_shutdown,
- .power_save = power7_idle,
+ .power_save = powernv_idle,
.calibrate_decr = generic_calibrate_decr,
#ifdef CONFIG_KEXEC
.kexec_cpu_down = pnv_kexec_cpu_down,
^ permalink raw reply related
* [RFC PATCH V3 4/5] powerpc/cpuidle: Enable powernv cpuidle support.
From: Deepthi Dharwar @ 2013-08-19 4:28 UTC (permalink / raw)
To: benh, daniel.lezcano, kernel, scottwood, linux-pm, linuxppc-dev
Cc: preeti, dongsheng.wang
In-Reply-To: <20130819042736.8609.17890.stgit@deepthi.in.ibm.com>
The following patch extends the current powerpc backend
idle driver to the powernv platform.
Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
---
drivers/cpuidle/cpuidle-powerpc.c | 36 +++++++++++++++++++++++++++++++++---
1 file changed, 33 insertions(+), 3 deletions(-)
diff --git a/drivers/cpuidle/cpuidle-powerpc.c b/drivers/cpuidle/cpuidle-powerpc.c
index 5756085..e1cf599 100644
--- a/drivers/cpuidle/cpuidle-powerpc.c
+++ b/drivers/cpuidle/cpuidle-powerpc.c
@@ -53,7 +53,9 @@ static int snooze_loop(struct cpuidle_device *dev,
{
unsigned long in_purr;
- idle_loop_prolog(&in_purr);
+ if (firmware_has_feature(FW_FEATURE_SPLPAR))
+ idle_loop_prolog(&in_purr);
+
local_irq_enable();
set_thread_flag(TIF_POLLING_NRFLAG);
@@ -67,7 +69,8 @@ static int snooze_loop(struct cpuidle_device *dev,
clear_thread_flag(TIF_POLLING_NRFLAG);
smp_mb();
- idle_loop_epilog(in_purr);
+ if (firmware_has_feature(FW_FEATURE_SPLPAR))
+ idle_loop_epilog(in_purr);
return index;
}
@@ -130,6 +133,15 @@ static int shared_cede_loop(struct cpuidle_device *dev,
return index;
}
+static int nap_loop(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv,
+ int index)
+{
+ ppc64_runlatch_off();
+ power7_idle();
+ return index;
+}
+
/*
* States for dedicated partition case.
*/
@@ -163,6 +175,23 @@ static struct cpuidle_state shared_states[MAX_IDLE_STATE_COUNT] = {
.enter = &shared_cede_loop },
};
+static struct cpuidle_state powernv_states[MAX_IDLE_STATE_COUNT] = {
+ { /* Snooze */
+ .name = "snooze",
+ .desc = "snooze",
+ .flags = CPUIDLE_FLAG_TIME_VALID,
+ .exit_latency = 0,
+ .target_residency = 0,
+ .enter = &snooze_loop },
+ { /* NAP */
+ .name = "NAP",
+ .desc = "NAP",
+ .flags = CPUIDLE_FLAG_TIME_VALID,
+ .exit_latency = 10,
+ .target_residency = 100,
+ .enter = &nap_loop },
+};
+
void update_smt_snooze_delay(int cpu, int residency)
{
struct cpuidle_driver *drv = cpuidle_get_driver();
@@ -282,7 +311,6 @@ static int powerpc_idle_devices_init(void)
return -EIO;
}
}
-
return 0;
}
@@ -309,6 +337,8 @@ static int powerpc_idle_probe(void)
cpuidle_state_table = shared_states;
else if (get_lppaca_is_shared_proc() == 0)
cpuidle_state_table = dedicated_states;
+ } else if (firmware_has_feature(FW_FEATURE_OPALv3)) {
+ cpuidle_state_table = powernv_states;
} else
return -ENODEV;
^ permalink raw reply related
* [RFC PATCH V3 3/5] powerpc/cpuidle: Generic powerpc backend cpuidle driver.
From: Deepthi Dharwar @ 2013-08-19 4:28 UTC (permalink / raw)
To: benh, daniel.lezcano, kernel, scottwood, linux-pm, linuxppc-dev
Cc: preeti, dongsheng.wang
In-Reply-To: <20130819042736.8609.17890.stgit@deepthi.in.ibm.com>
This patch involves moving the current pseries_idle backend driver code
from pseries/processor_idle.c to drivers/cpuidle/cpuidle-powerpc.c,
and making the backend code generic enough to be able to extend this
driver code for complete powerpc platform to exploit the cpuidle framework.
It enables the support for pseries platform, such that going forward the same code
with minimal efforts can be re-used for a common driver on powernv
and can be further extended to support cpuidle idle state mgmt for the rest
of the powerpc platforms. This removes a lot of code duplicacy, making
the code elegant.
Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/paca.h | 23 +
arch/powerpc/include/asm/processor.h | 2
arch/powerpc/platforms/pseries/Kconfig | 9 -
arch/powerpc/platforms/pseries/Makefile | 1
arch/powerpc/platforms/pseries/processor_idle.c | 360 -----------------------
drivers/cpuidle/Kconfig | 7
drivers/cpuidle/Makefile | 2
drivers/cpuidle/cpuidle-powerpc.c | 361 +++++++++++++++++++++++
8 files changed, 394 insertions(+), 371 deletions(-)
delete mode 100644 arch/powerpc/platforms/pseries/processor_idle.c
create mode 100644 drivers/cpuidle/cpuidle-powerpc.c
diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca.h
index 77c91e7..7bd83ff 100644
--- a/arch/powerpc/include/asm/paca.h
+++ b/arch/powerpc/include/asm/paca.h
@@ -175,6 +175,29 @@ extern void setup_paca(struct paca_struct *new_paca);
extern void allocate_pacas(void);
extern void free_unused_pacas(void);
+#ifdef CONFIG_PPC_BOOK3S
+#define get_lppaca_is_shared_proc() get_paca()->lppaca_ptr->shared_proc
+static inline void set_lppaca_idle(u8 idle)
+{
+ get_paca()->lppaca_ptr->idle = idle;
+}
+
+static inline void add_lppaca_wait_state(u64 cycles)
+{
+ get_paca()->lppaca_ptr->wait_state_cycles += cycles;
+}
+
+static inline void set_lppaca_donate_dedicated_cpu(u8 value)
+{
+ get_paca()->lppaca_ptr->donate_dedicated_cpu = value;
+}
+#else
+#define get_lppaca_is_shared_proc() -1
+static inline void set_lppaca_idle(u8 idle) { }
+static inline void add_lppaca_wait_state(u64 cycles) { }
+static inline void set_lppaca_donate_dedicated_cpu(u8 value) { }
+#endif
+
#else /* CONFIG_PPC64 */
static inline void allocate_pacas(void) { };
diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h
index e378ccc..5f57c56 100644
--- a/arch/powerpc/include/asm/processor.h
+++ b/arch/powerpc/include/asm/processor.h
@@ -430,7 +430,7 @@ enum idle_boot_override {IDLE_NO_OVERRIDE = 0, IDLE_POWERSAVE_OFF};
extern int powersave_nap; /* set if nap mode can be used in idle loop */
extern void power7_nap(void);
-#ifdef CONFIG_PSERIES_IDLE
+#ifdef CONFIG_CPU_IDLE_POWERPC
extern void update_smt_snooze_delay(int cpu, int residency);
#else
static inline void update_smt_snooze_delay(int cpu, int residency) {}
diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
index 62b4f80..bb59bb0 100644
--- a/arch/powerpc/platforms/pseries/Kconfig
+++ b/arch/powerpc/platforms/pseries/Kconfig
@@ -119,12 +119,3 @@ config DTL
which are accessible through a debugfs file.
Say N if you are unsure.
-
-config PSERIES_IDLE
- bool "Cpuidle driver for pSeries platforms"
- depends on CPU_IDLE
- depends on PPC_PSERIES
- default y
- help
- Select this option to enable processor idle state management
- through cpuidle subsystem.
diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile
index 8ae0103..4b22379 100644
--- a/arch/powerpc/platforms/pseries/Makefile
+++ b/arch/powerpc/platforms/pseries/Makefile
@@ -21,7 +21,6 @@ obj-$(CONFIG_HCALL_STATS) += hvCall_inst.o
obj-$(CONFIG_CMM) += cmm.o
obj-$(CONFIG_DTL) += dtl.o
obj-$(CONFIG_IO_EVENT_IRQ) += io_event_irq.o
-obj-$(CONFIG_PSERIES_IDLE) += processor_idle.o
ifeq ($(CONFIG_PPC_PSERIES),y)
obj-$(CONFIG_SUSPEND) += suspend.o
diff --git a/arch/powerpc/platforms/pseries/processor_idle.c b/arch/powerpc/platforms/pseries/processor_idle.c
deleted file mode 100644
index c905b99..0000000
--- a/arch/powerpc/platforms/pseries/processor_idle.c
+++ /dev/null
@@ -1,360 +0,0 @@
-/*
- * processor_idle - idle state cpuidle driver.
- * Adapted from drivers/idle/intel_idle.c and
- * drivers/acpi/processor_idle.c
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/moduleparam.h>
-#include <linux/cpuidle.h>
-#include <linux/cpu.h>
-#include <linux/notifier.h>
-
-#include <asm/paca.h>
-#include <asm/reg.h>
-#include <asm/machdep.h>
-#include <asm/firmware.h>
-#include <asm/runlatch.h>
-#include <asm/plpar_wrappers.h>
-
-struct cpuidle_driver pseries_idle_driver = {
- .name = "pseries_idle",
- .owner = THIS_MODULE,
-};
-
-#define MAX_IDLE_STATE_COUNT 2
-
-static int max_idle_state = MAX_IDLE_STATE_COUNT - 1;
-static struct cpuidle_device __percpu *pseries_cpuidle_devices;
-static struct cpuidle_state *cpuidle_state_table;
-
-static inline void idle_loop_prolog(unsigned long *in_purr)
-{
- *in_purr = mfspr(SPRN_PURR);
- /*
- * Indicate to the HV that we are idle. Now would be
- * a good time to find other work to dispatch.
- */
- get_lppaca()->idle = 1;
-}
-
-static inline void idle_loop_epilog(unsigned long in_purr)
-{
- get_lppaca()->wait_state_cycles += mfspr(SPRN_PURR) - in_purr;
- get_lppaca()->idle = 0;
-}
-
-static int snooze_loop(struct cpuidle_device *dev,
- struct cpuidle_driver *drv,
- int index)
-{
- unsigned long in_purr;
- int cpu = dev->cpu;
-
- idle_loop_prolog(&in_purr);
- local_irq_enable();
- set_thread_flag(TIF_POLLING_NRFLAG);
-
- while ((!need_resched()) && cpu_online(cpu)) {
- ppc64_runlatch_off();
- HMT_low();
- HMT_very_low();
- }
-
- HMT_medium();
- clear_thread_flag(TIF_POLLING_NRFLAG);
- smp_mb();
-
- idle_loop_epilog(in_purr);
-
- return index;
-}
-
-static void check_and_cede_processor(void)
-{
- /*
- * Ensure our interrupt state is properly tracked,
- * also checks if no interrupt has occurred while we
- * were soft-disabled
- */
- if (prep_irq_for_idle()) {
- cede_processor();
-#ifdef CONFIG_TRACE_IRQFLAGS
- /* Ensure that H_CEDE returns with IRQs on */
- if (WARN_ON(!(mfmsr() & MSR_EE)))
- __hard_irq_enable();
-#endif
- }
-}
-
-static int dedicated_cede_loop(struct cpuidle_device *dev,
- struct cpuidle_driver *drv,
- int index)
-{
- unsigned long in_purr;
-
- idle_loop_prolog(&in_purr);
- get_lppaca()->donate_dedicated_cpu = 1;
-
- ppc64_runlatch_off();
- HMT_medium();
- check_and_cede_processor();
-
- get_lppaca()->donate_dedicated_cpu = 0;
-
- idle_loop_epilog(in_purr);
-
- return index;
-}
-
-static int shared_cede_loop(struct cpuidle_device *dev,
- struct cpuidle_driver *drv,
- int index)
-{
- unsigned long in_purr;
-
- idle_loop_prolog(&in_purr);
-
- /*
- * Yield the processor to the hypervisor. We return if
- * an external interrupt occurs (which are driven prior
- * to returning here) or if a prod occurs from another
- * processor. When returning here, external interrupts
- * are enabled.
- */
- check_and_cede_processor();
-
- idle_loop_epilog(in_purr);
-
- return index;
-}
-
-/*
- * States for dedicated partition case.
- */
-static struct cpuidle_state dedicated_states[MAX_IDLE_STATE_COUNT] = {
- { /* Snooze */
- .name = "snooze",
- .desc = "snooze",
- .flags = CPUIDLE_FLAG_TIME_VALID,
- .exit_latency = 0,
- .target_residency = 0,
- .enter = &snooze_loop },
- { /* CEDE */
- .name = "CEDE",
- .desc = "CEDE",
- .flags = CPUIDLE_FLAG_TIME_VALID,
- .exit_latency = 10,
- .target_residency = 100,
- .enter = &dedicated_cede_loop },
-};
-
-/*
- * States for shared partition case.
- */
-static struct cpuidle_state shared_states[MAX_IDLE_STATE_COUNT] = {
- { /* Shared Cede */
- .name = "Shared Cede",
- .desc = "Shared Cede",
- .flags = CPUIDLE_FLAG_TIME_VALID,
- .exit_latency = 0,
- .target_residency = 0,
- .enter = &shared_cede_loop },
-};
-
-void update_smt_snooze_delay(int cpu, int residency)
-{
- struct cpuidle_driver *drv = cpuidle_get_driver();
- struct cpuidle_device *dev = per_cpu(cpuidle_devices, cpu);
-
- if (cpuidle_state_table != dedicated_states)
- return;
-
- if (residency < 0) {
- /* Disable the Nap state on that cpu */
- if (dev)
- dev->states_usage[1].disable = 1;
- } else
- if (drv)
- drv->states[1].target_residency = residency;
-}
-
-static int pseries_cpuidle_add_cpu_notifier(struct notifier_block *n,
- unsigned long action, void *hcpu)
-{
- int hotcpu = (unsigned long)hcpu;
- struct cpuidle_device *dev =
- per_cpu_ptr(pseries_cpuidle_devices, hotcpu);
-
- if (dev && cpuidle_get_driver()) {
- switch (action) {
- case CPU_ONLINE:
- case CPU_ONLINE_FROZEN:
- cpuidle_pause_and_lock();
- cpuidle_enable_device(dev);
- cpuidle_resume_and_unlock();
- break;
-
- case CPU_DEAD:
- case CPU_DEAD_FROZEN:
- cpuidle_pause_and_lock();
- cpuidle_disable_device(dev);
- cpuidle_resume_and_unlock();
- break;
-
- default:
- return NOTIFY_DONE;
- }
- }
- return NOTIFY_OK;
-}
-
-static struct notifier_block setup_hotplug_notifier = {
- .notifier_call = pseries_cpuidle_add_cpu_notifier,
-};
-
-/*
- * pseries_cpuidle_driver_init()
- */
-static int pseries_cpuidle_driver_init(void)
-{
- int idle_state;
- struct cpuidle_driver *drv = &pseries_idle_driver;
-
- drv->state_count = 0;
-
- for (idle_state = 0; idle_state < MAX_IDLE_STATE_COUNT; ++idle_state) {
-
- if (idle_state > max_idle_state)
- break;
-
- /* is the state not enabled? */
- if (cpuidle_state_table[idle_state].enter == NULL)
- continue;
-
- drv->states[drv->state_count] = /* structure copy */
- cpuidle_state_table[idle_state];
-
- drv->state_count += 1;
- }
-
- return 0;
-}
-
-/* pseries_idle_devices_uninit(void)
- * unregister cpuidle devices and de-allocate memory
- */
-static void pseries_idle_devices_uninit(void)
-{
- int i;
- struct cpuidle_device *dev;
-
- for_each_possible_cpu(i) {
- dev = per_cpu_ptr(pseries_cpuidle_devices, i);
- cpuidle_unregister_device(dev);
- }
-
- free_percpu(pseries_cpuidle_devices);
- return;
-}
-
-/* pseries_idle_devices_init()
- * allocate, initialize and register cpuidle device
- */
-static int pseries_idle_devices_init(void)
-{
- int i;
- struct cpuidle_driver *drv = &pseries_idle_driver;
- struct cpuidle_device *dev;
-
- pseries_cpuidle_devices = alloc_percpu(struct cpuidle_device);
- if (pseries_cpuidle_devices == NULL)
- return -ENOMEM;
-
- for_each_possible_cpu(i) {
- dev = per_cpu_ptr(pseries_cpuidle_devices, i);
- dev->state_count = drv->state_count;
- dev->cpu = i;
- if (cpuidle_register_device(dev)) {
- printk(KERN_DEBUG \
- "cpuidle_register_device %d failed!\n", i);
- return -EIO;
- }
- }
-
- return 0;
-}
-
-/*
- * pseries_idle_probe()
- * Choose state table for shared versus dedicated partition
- */
-static int pseries_idle_probe(void)
-{
-
- if (!firmware_has_feature(FW_FEATURE_SPLPAR))
- return -ENODEV;
-
- if (cpuidle_disable != IDLE_NO_OVERRIDE)
- return -ENODEV;
-
- if (max_idle_state == 0) {
- printk(KERN_DEBUG "pseries processor idle disabled.\n");
- return -EPERM;
- }
-
- if (get_lppaca()->shared_proc)
- cpuidle_state_table = shared_states;
- else
- cpuidle_state_table = dedicated_states;
-
- return 0;
-}
-
-static int __init pseries_processor_idle_init(void)
-{
- int retval;
-
- retval = pseries_idle_probe();
- if (retval)
- return retval;
-
- pseries_cpuidle_driver_init();
- retval = cpuidle_register_driver(&pseries_idle_driver);
- if (retval) {
- printk(KERN_DEBUG "Registration of pseries driver failed.\n");
- return retval;
- }
-
- retval = pseries_idle_devices_init();
- if (retval) {
- pseries_idle_devices_uninit();
- cpuidle_unregister_driver(&pseries_idle_driver);
- return retval;
- }
-
- register_cpu_notifier(&setup_hotplug_notifier);
- printk(KERN_DEBUG "pseries_idle_driver registered\n");
-
- return 0;
-}
-
-static void __exit pseries_processor_idle_exit(void)
-{
-
- unregister_cpu_notifier(&setup_hotplug_notifier);
- pseries_idle_devices_uninit();
- cpuidle_unregister_driver(&pseries_idle_driver);
-
- return;
-}
-
-module_init(pseries_processor_idle_init);
-module_exit(pseries_processor_idle_exit);
-
-MODULE_AUTHOR("Deepthi Dharwar <deepthi@linux.vnet.ibm.com>");
-MODULE_DESCRIPTION("Cpuidle driver for POWER");
-MODULE_LICENSE("GPL");
diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig
index 0e2cd5c..99ee5d4 100644
--- a/drivers/cpuidle/Kconfig
+++ b/drivers/cpuidle/Kconfig
@@ -42,6 +42,13 @@ config CPU_IDLE_ZYNQ
help
Select this to enable cpuidle on Xilinx Zynq processors.
+config CPU_IDLE_POWERPC
+ bool "CPU Idle driver for POWERPC platforms"
+ depends on PPC64
+ default y
+ help
+ Select this option to enable processor idle state management
+ for POWERPC platform.
endif
config ARCH_NEEDS_CPU_IDLE_COUPLED
diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile
index 8767a7b..d12e205 100644
--- a/drivers/cpuidle/Makefile
+++ b/drivers/cpuidle/Makefile
@@ -8,3 +8,5 @@ obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o
obj-$(CONFIG_CPU_IDLE_CALXEDA) += cpuidle-calxeda.o
obj-$(CONFIG_ARCH_KIRKWOOD) += cpuidle-kirkwood.o
obj-$(CONFIG_CPU_IDLE_ZYNQ) += cpuidle-zynq.o
+
+obj-$(CONFIG_CPU_IDLE_POWERPC) += cpuidle-powerpc.o
diff --git a/drivers/cpuidle/cpuidle-powerpc.c b/drivers/cpuidle/cpuidle-powerpc.c
new file mode 100644
index 0000000..5756085
--- /dev/null
+++ b/drivers/cpuidle/cpuidle-powerpc.c
@@ -0,0 +1,361 @@
+/*
+ * processor_idle - idle state cpuidle driver.
+ * Adapted from drivers/idle/intel_idle.c and
+ * drivers/acpi/processor_idle.c
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <linux/cpuidle.h>
+#include <linux/cpu.h>
+#include <linux/notifier.h>
+
+#include <asm/paca.h>
+#include <asm/reg.h>
+#include <asm/machdep.h>
+#include <asm/firmware.h>
+#include <asm/runlatch.h>
+#include <asm/plpar_wrappers.h>
+
+struct cpuidle_driver powerpc_idle_driver = {
+ .name = "powerpc_idle",
+ .owner = THIS_MODULE,
+};
+
+#define MAX_IDLE_STATE_COUNT 2
+
+static int max_idle_state = MAX_IDLE_STATE_COUNT - 1;
+static struct cpuidle_device __percpu *powerpc_cpuidle_devices;
+static struct cpuidle_state *cpuidle_state_table;
+
+static inline void idle_loop_prolog(unsigned long *in_purr)
+{
+ *in_purr = mfspr(SPRN_PURR);
+ /*
+ * Indicate to the HV that we are idle. Now would be
+ * a good time to find other work to dispatch.
+ */
+ set_lppaca_idle(1);
+}
+
+static inline void idle_loop_epilog(unsigned long in_purr)
+{
+ add_lppaca_wait_state(mfspr(SPRN_PURR) - in_purr);
+ set_lppaca_idle(0);
+}
+
+static int snooze_loop(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv,
+ int index)
+{
+ unsigned long in_purr;
+
+ idle_loop_prolog(&in_purr);
+ local_irq_enable();
+ set_thread_flag(TIF_POLLING_NRFLAG);
+
+ while (!need_resched()) {
+ ppc64_runlatch_off();
+ HMT_low();
+ HMT_very_low();
+ }
+
+ HMT_medium();
+ clear_thread_flag(TIF_POLLING_NRFLAG);
+ smp_mb();
+
+ idle_loop_epilog(in_purr);
+
+ return index;
+}
+
+static void check_and_cede_processor(void)
+{
+ /*
+ * Ensure our interrupt state is properly tracked,
+ * also checks if no interrupt has occurred while we
+ * were soft-disabled
+ */
+ if (prep_irq_for_idle()) {
+ cede_processor();
+#ifdef CONFIG_TRACE_IRQFLAGS
+ /* Ensure that H_CEDE returns with IRQs on */
+ if (WARN_ON(!(mfmsr() & MSR_EE)))
+ __hard_irq_enable();
+#endif
+ }
+}
+
+static int dedicated_cede_loop(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv,
+ int index)
+{
+ unsigned long in_purr;
+
+ idle_loop_prolog(&in_purr);
+ set_lppaca_donate_dedicated_cpu(1);
+
+ ppc64_runlatch_off();
+ HMT_medium();
+ check_and_cede_processor();
+
+ set_lppaca_donate_dedicated_cpu(0);
+ idle_loop_epilog(in_purr);
+
+ return index;
+}
+
+static int shared_cede_loop(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv,
+ int index)
+{
+ unsigned long in_purr;
+
+ idle_loop_prolog(&in_purr);
+
+ /*
+ * Yield the processor to the hypervisor. We return if
+ * an external interrupt occurs (which are driven prior
+ * to returning here) or if a prod occurs from another
+ * processor. When returning here, external interrupts
+ * are enabled.
+ */
+ check_and_cede_processor();
+
+ idle_loop_epilog(in_purr);
+
+ return index;
+}
+
+/*
+ * States for dedicated partition case.
+ */
+static struct cpuidle_state dedicated_states[MAX_IDLE_STATE_COUNT] = {
+ { /* Snooze */
+ .name = "snooze",
+ .desc = "snooze",
+ .flags = CPUIDLE_FLAG_TIME_VALID,
+ .exit_latency = 0,
+ .target_residency = 0,
+ .enter = &snooze_loop },
+ { /* CEDE */
+ .name = "CEDE",
+ .desc = "CEDE",
+ .flags = CPUIDLE_FLAG_TIME_VALID,
+ .exit_latency = 10,
+ .target_residency = 100,
+ .enter = &dedicated_cede_loop },
+};
+
+/*
+ * States for shared partition case.
+ */
+static struct cpuidle_state shared_states[MAX_IDLE_STATE_COUNT] = {
+ { /* Shared Cede */
+ .name = "Shared Cede",
+ .desc = "Shared Cede",
+ .flags = CPUIDLE_FLAG_TIME_VALID,
+ .exit_latency = 0,
+ .target_residency = 0,
+ .enter = &shared_cede_loop },
+};
+
+void update_smt_snooze_delay(int cpu, int residency)
+{
+ struct cpuidle_driver *drv = cpuidle_get_driver();
+ struct cpuidle_device *dev = per_cpu(cpuidle_devices, cpu);
+
+ if (cpuidle_state_table != dedicated_states)
+ return;
+
+ if (residency < 0) {
+ /* Disable the Nap state on that cpu */
+ if (dev)
+ dev->states_usage[1].disable = 1;
+ } else
+ if (drv)
+ drv->states[1].target_residency = residency;
+}
+
+static int powerpc_cpuidle_add_cpu_notifier(struct notifier_block *n,
+ unsigned long action, void *hcpu)
+{
+ int hotcpu = (unsigned long)hcpu;
+ struct cpuidle_device *dev =
+ per_cpu_ptr(powerpc_cpuidle_devices, hotcpu);
+
+ if (dev && cpuidle_get_driver()) {
+ switch (action) {
+ case CPU_ONLINE:
+ case CPU_ONLINE_FROZEN:
+ cpuidle_pause_and_lock();
+ cpuidle_enable_device(dev);
+ cpuidle_resume_and_unlock();
+ break;
+
+ case CPU_DEAD:
+ case CPU_DEAD_FROZEN:
+ cpuidle_pause_and_lock();
+ cpuidle_disable_device(dev);
+ cpuidle_resume_and_unlock();
+ break;
+
+ default:
+ return NOTIFY_DONE;
+ }
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block setup_hotplug_notifier = {
+ .notifier_call = powerpc_cpuidle_add_cpu_notifier,
+};
+
+/*
+ * powerpc_cpuidle_driver_init()
+ */
+static int powerpc_cpuidle_driver_init(void)
+{
+ int idle_state;
+ struct cpuidle_driver *drv = &powerpc_idle_driver;
+
+ drv->state_count = 0;
+
+ for (idle_state = 0; idle_state < MAX_IDLE_STATE_COUNT; ++idle_state) {
+
+ if (idle_state > max_idle_state)
+ break;
+
+ /* is the state not enabled? */
+ if (cpuidle_state_table[idle_state].enter == NULL)
+ continue;
+
+ drv->states[drv->state_count] = /* structure copy */
+ cpuidle_state_table[idle_state];
+
+ drv->state_count += 1;
+ }
+
+ return 0;
+}
+
+/* powerpc_idle_devices_uninit(void)
+ * unregister cpuidle devices and de-allocate memory
+ */
+static void powerpc_idle_devices_uninit(void)
+{
+ int i;
+ struct cpuidle_device *dev;
+
+ for_each_possible_cpu(i) {
+ dev = per_cpu_ptr(powerpc_cpuidle_devices, i);
+ cpuidle_unregister_device(dev);
+ }
+
+ free_percpu(powerpc_cpuidle_devices);
+ return;
+}
+
+/* powerpc_idle_devices_init()
+ * allocate, initialize and register cpuidle device
+ */
+static int powerpc_idle_devices_init(void)
+{
+ int i;
+ struct cpuidle_driver *drv = &powerpc_idle_driver;
+ struct cpuidle_device *dev;
+
+ powerpc_cpuidle_devices = alloc_percpu(struct cpuidle_device);
+ if (powerpc_cpuidle_devices == NULL)
+ return -ENOMEM;
+
+ for_each_possible_cpu(i) {
+ dev = per_cpu_ptr(powerpc_cpuidle_devices, i);
+ dev->state_count = drv->state_count;
+ dev->cpu = i;
+ if (cpuidle_register_device(dev)) {
+ printk(KERN_DEBUG \
+ "cpuidle_register_device %d failed!\n", i);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * powerpc_idle_probe()
+ * Choose state table for shared versus dedicated partition
+ */
+static int powerpc_idle_probe(void)
+{
+
+ if (!firmware_has_feature(FW_FEATURE_SPLPAR))
+ return -ENODEV;
+
+ if (cpuidle_disable != IDLE_NO_OVERRIDE)
+ return -ENODEV;
+
+ if (max_idle_state == 0) {
+ printk(KERN_DEBUG "powerpc processor idle disabled.\n");
+ return -EPERM;
+ }
+
+ if (firmware_has_feature(FW_FEATURE_SPLPAR)) {
+ if (get_lppaca_is_shared_proc() == 1)
+ cpuidle_state_table = shared_states;
+ else if (get_lppaca_is_shared_proc() == 0)
+ cpuidle_state_table = dedicated_states;
+ } else
+ return -ENODEV;
+
+ return 0;
+}
+
+static int __init powerpc_processor_idle_init(void)
+{
+ int retval;
+
+ retval = powerpc_idle_probe();
+ if (retval)
+ return retval;
+
+ powerpc_cpuidle_driver_init();
+ retval = cpuidle_register_driver(&powerpc_idle_driver);
+ if (retval) {
+ printk(KERN_DEBUG "Registration of powerpc driver failed.\n");
+ return retval;
+ }
+
+ retval = powerpc_idle_devices_init();
+ if (retval) {
+ powerpc_idle_devices_uninit();
+ cpuidle_unregister_driver(&powerpc_idle_driver);
+ return retval;
+ }
+
+ register_cpu_notifier(&setup_hotplug_notifier);
+ printk(KERN_DEBUG "powerpc_idle_driver registered\n");
+
+ return 0;
+}
+
+static void __exit powerpc_processor_idle_exit(void)
+{
+
+ unregister_cpu_notifier(&setup_hotplug_notifier);
+ powerpc_idle_devices_uninit();
+ cpuidle_unregister_driver(&powerpc_idle_driver);
+
+ return;
+}
+
+module_init(powerpc_processor_idle_init);
+module_exit(powerpc_processor_idle_exit);
+
+MODULE_AUTHOR("Deepthi Dharwar <deepthi@linux.vnet.ibm.com>");
+MODULE_DESCRIPTION("Cpuidle driver for powerpc");
+MODULE_LICENSE("GPL");
^ permalink raw reply related
* [RFC PATCH V3 2/5] pseries: Move plpar_wrapper.h to powerpc common include/asm location.
From: Deepthi Dharwar @ 2013-08-19 4:28 UTC (permalink / raw)
To: benh, daniel.lezcano, kernel, scottwood, linux-pm, linuxppc-dev
Cc: preeti, dongsheng.wang
In-Reply-To: <20130819042736.8609.17890.stgit@deepthi.in.ibm.com>
As a part of pseries_idle backend driver cleanup to make
the code common to both pseries and powernv platforms, it
is necessary to move the backend-driver code to drivers/cpuidle.
As a pre-requisite for that, it is essential to move plpar_wrapper.h
to include/asm.
Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
---
arch/powerpc/include/asm/plpar_wrappers.h | 325 +++++++++++++++++++++++
arch/powerpc/platforms/pseries/cmm.c | 3
arch/powerpc/platforms/pseries/dtl.c | 3
arch/powerpc/platforms/pseries/hotplug-cpu.c | 3
arch/powerpc/platforms/pseries/hvconsole.c | 2
arch/powerpc/platforms/pseries/iommu.c | 3
arch/powerpc/platforms/pseries/kexec.c | 2
arch/powerpc/platforms/pseries/lpar.c | 2
arch/powerpc/platforms/pseries/plpar_wrappers.h | 324 -----------------------
arch/powerpc/platforms/pseries/processor_idle.c | 3
arch/powerpc/platforms/pseries/setup.c | 2
arch/powerpc/platforms/pseries/smp.c | 2
12 files changed, 336 insertions(+), 338 deletions(-)
create mode 100644 arch/powerpc/include/asm/plpar_wrappers.h
delete mode 100644 arch/powerpc/platforms/pseries/plpar_wrappers.h
diff --git a/arch/powerpc/include/asm/plpar_wrappers.h b/arch/powerpc/include/asm/plpar_wrappers.h
new file mode 100644
index 0000000..e2f84d6
--- /dev/null
+++ b/arch/powerpc/include/asm/plpar_wrappers.h
@@ -0,0 +1,325 @@
+#ifndef _PSERIES_PLPAR_WRAPPERS_H
+#define _PSERIES_PLPAR_WRAPPERS_H
+
+#include <linux/string.h>
+#include <linux/irqflags.h>
+
+#include <asm/hvcall.h>
+#include <asm/paca.h>
+#include <asm/page.h>
+
+/* Get state of physical CPU from query_cpu_stopped */
+int smp_query_cpu_stopped(unsigned int pcpu);
+#define QCSS_STOPPED 0
+#define QCSS_STOPPING 1
+#define QCSS_NOT_STOPPED 2
+#define QCSS_HARDWARE_ERROR -1
+#define QCSS_HARDWARE_BUSY -2
+
+static inline long poll_pending(void)
+{
+ return plpar_hcall_norets(H_POLL_PENDING);
+}
+
+static inline u8 get_cede_latency_hint(void)
+{
+ return get_lppaca()->cede_latency_hint;
+}
+
+static inline void set_cede_latency_hint(u8 latency_hint)
+{
+ get_lppaca()->cede_latency_hint = latency_hint;
+}
+
+static inline long cede_processor(void)
+{
+ return plpar_hcall_norets(H_CEDE);
+}
+
+static inline long extended_cede_processor(unsigned long latency_hint)
+{
+ long rc;
+ u8 old_latency_hint = get_cede_latency_hint();
+
+ set_cede_latency_hint(latency_hint);
+
+ rc = cede_processor();
+#ifdef CONFIG_TRACE_IRQFLAGS
+ /* Ensure that H_CEDE returns with IRQs on */
+ if (WARN_ON(!(mfmsr() & MSR_EE)))
+ __hard_irq_enable();
+#endif
+
+ set_cede_latency_hint(old_latency_hint);
+
+ return rc;
+}
+
+static inline long vpa_call(unsigned long flags, unsigned long cpu,
+ unsigned long vpa)
+{
+ flags = flags << H_VPA_FUNC_SHIFT;
+
+ return plpar_hcall_norets(H_REGISTER_VPA, flags, cpu, vpa);
+}
+
+static inline long unregister_vpa(unsigned long cpu)
+{
+ return vpa_call(H_VPA_DEREG_VPA, cpu, 0);
+}
+
+static inline long register_vpa(unsigned long cpu, unsigned long vpa)
+{
+ return vpa_call(H_VPA_REG_VPA, cpu, vpa);
+}
+
+static inline long unregister_slb_shadow(unsigned long cpu)
+{
+ return vpa_call(H_VPA_DEREG_SLB, cpu, 0);
+}
+
+static inline long register_slb_shadow(unsigned long cpu, unsigned long vpa)
+{
+ return vpa_call(H_VPA_REG_SLB, cpu, vpa);
+}
+
+static inline long unregister_dtl(unsigned long cpu)
+{
+ return vpa_call(H_VPA_DEREG_DTL, cpu, 0);
+}
+
+static inline long register_dtl(unsigned long cpu, unsigned long vpa)
+{
+ return vpa_call(H_VPA_REG_DTL, cpu, vpa);
+}
+
+static inline long plpar_page_set_loaned(unsigned long vpa)
+{
+ unsigned long cmo_page_sz = cmo_get_page_size();
+ long rc = 0;
+ int i;
+
+ for (i = 0; !rc && i < PAGE_SIZE; i += cmo_page_sz)
+ rc = plpar_hcall_norets(H_PAGE_INIT, H_PAGE_SET_LOANED, vpa + i, 0);
+
+ for (i -= cmo_page_sz; rc && i != 0; i -= cmo_page_sz)
+ plpar_hcall_norets(H_PAGE_INIT, H_PAGE_SET_ACTIVE,
+ vpa + i - cmo_page_sz, 0);
+
+ return rc;
+}
+
+static inline long plpar_page_set_active(unsigned long vpa)
+{
+ unsigned long cmo_page_sz = cmo_get_page_size();
+ long rc = 0;
+ int i;
+
+ for (i = 0; !rc && i < PAGE_SIZE; i += cmo_page_sz)
+ rc = plpar_hcall_norets(H_PAGE_INIT, H_PAGE_SET_ACTIVE, vpa + i, 0);
+
+ for (i -= cmo_page_sz; rc && i != 0; i -= cmo_page_sz)
+ plpar_hcall_norets(H_PAGE_INIT, H_PAGE_SET_LOANED,
+ vpa + i - cmo_page_sz, 0);
+
+ return rc;
+}
+
+extern void vpa_init(int cpu);
+
+static inline long plpar_pte_enter(unsigned long flags,
+ unsigned long hpte_group, unsigned long hpte_v,
+ unsigned long hpte_r, unsigned long *slot)
+{
+ long rc;
+ unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
+
+ rc = plpar_hcall(H_ENTER, retbuf, flags, hpte_group, hpte_v, hpte_r);
+
+ *slot = retbuf[0];
+
+ return rc;
+}
+
+static inline long plpar_pte_remove(unsigned long flags, unsigned long ptex,
+ unsigned long avpn, unsigned long *old_pteh_ret,
+ unsigned long *old_ptel_ret)
+{
+ long rc;
+ unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
+
+ rc = plpar_hcall(H_REMOVE, retbuf, flags, ptex, avpn);
+
+ *old_pteh_ret = retbuf[0];
+ *old_ptel_ret = retbuf[1];
+
+ return rc;
+}
+
+/* plpar_pte_remove_raw can be called in real mode. It calls plpar_hcall_raw */
+static inline long plpar_pte_remove_raw(unsigned long flags, unsigned long ptex,
+ unsigned long avpn, unsigned long *old_pteh_ret,
+ unsigned long *old_ptel_ret)
+{
+ long rc;
+ unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
+
+ rc = plpar_hcall_raw(H_REMOVE, retbuf, flags, ptex, avpn);
+
+ *old_pteh_ret = retbuf[0];
+ *old_ptel_ret = retbuf[1];
+
+ return rc;
+}
+
+static inline long plpar_pte_read(unsigned long flags, unsigned long ptex,
+ unsigned long *old_pteh_ret, unsigned long *old_ptel_ret)
+{
+ long rc;
+ unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
+
+ rc = plpar_hcall(H_READ, retbuf, flags, ptex);
+
+ *old_pteh_ret = retbuf[0];
+ *old_ptel_ret = retbuf[1];
+
+ return rc;
+}
+
+/* plpar_pte_read_raw can be called in real mode. It calls plpar_hcall_raw */
+static inline long plpar_pte_read_raw(unsigned long flags, unsigned long ptex,
+ unsigned long *old_pteh_ret, unsigned long *old_ptel_ret)
+{
+ long rc;
+ unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
+
+ rc = plpar_hcall_raw(H_READ, retbuf, flags, ptex);
+
+ *old_pteh_ret = retbuf[0];
+ *old_ptel_ret = retbuf[1];
+
+ return rc;
+}
+
+/*
+ * plpar_pte_read_4_raw can be called in real mode.
+ * ptes must be 8*sizeof(unsigned long)
+ */
+static inline long plpar_pte_read_4_raw(unsigned long flags, unsigned long ptex,
+ unsigned long *ptes)
+
+{
+ long rc;
+ unsigned long retbuf[PLPAR_HCALL9_BUFSIZE];
+
+ rc = plpar_hcall9_raw(H_READ, retbuf, flags | H_READ_4, ptex);
+
+ memcpy(ptes, retbuf, 8*sizeof(unsigned long));
+
+ return rc;
+}
+
+static inline long plpar_pte_protect(unsigned long flags, unsigned long ptex,
+ unsigned long avpn)
+{
+ return plpar_hcall_norets(H_PROTECT, flags, ptex, avpn);
+}
+
+static inline long plpar_tce_get(unsigned long liobn, unsigned long ioba,
+ unsigned long *tce_ret)
+{
+ long rc;
+ unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
+
+ rc = plpar_hcall(H_GET_TCE, retbuf, liobn, ioba);
+
+ *tce_ret = retbuf[0];
+
+ return rc;
+}
+
+static inline long plpar_tce_put(unsigned long liobn, unsigned long ioba,
+ unsigned long tceval)
+{
+ return plpar_hcall_norets(H_PUT_TCE, liobn, ioba, tceval);
+}
+
+static inline long plpar_tce_put_indirect(unsigned long liobn,
+ unsigned long ioba, unsigned long page, unsigned long count)
+{
+ return plpar_hcall_norets(H_PUT_TCE_INDIRECT, liobn, ioba, page, count);
+}
+
+static inline long plpar_tce_stuff(unsigned long liobn, unsigned long ioba,
+ unsigned long tceval, unsigned long count)
+{
+ return plpar_hcall_norets(H_STUFF_TCE, liobn, ioba, tceval, count);
+}
+
+static inline long plpar_get_term_char(unsigned long termno,
+ unsigned long *len_ret, char *buf_ret)
+{
+ long rc;
+ unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
+ unsigned long *lbuf = (unsigned long *)buf_ret; /* TODO: alignment? */
+
+ rc = plpar_hcall(H_GET_TERM_CHAR, retbuf, termno);
+
+ *len_ret = retbuf[0];
+ lbuf[0] = retbuf[1];
+ lbuf[1] = retbuf[2];
+
+ return rc;
+}
+
+static inline long plpar_put_term_char(unsigned long termno, unsigned long len,
+ const char *buffer)
+{
+ unsigned long *lbuf = (unsigned long *)buffer; /* TODO: alignment? */
+ return plpar_hcall_norets(H_PUT_TERM_CHAR, termno, len, lbuf[0],
+ lbuf[1]);
+}
+
+/* Set various resource mode parameters */
+static inline long plpar_set_mode(unsigned long mflags, unsigned long resource,
+ unsigned long value1, unsigned long value2)
+{
+ return plpar_hcall_norets(H_SET_MODE, mflags, resource, value1, value2);
+}
+
+/*
+ * Enable relocation on exceptions on this partition
+ *
+ * Note: this call has a partition wide scope and can take a while to complete.
+ * If it returns H_LONG_BUSY_* it should be retried periodically until it
+ * returns H_SUCCESS.
+ */
+static inline long enable_reloc_on_exceptions(void)
+{
+ /* mflags = 3: Exceptions at 0xC000000000004000 */
+ return plpar_set_mode(3, 3, 0, 0);
+}
+
+/*
+ * Disable relocation on exceptions on this partition
+ *
+ * Note: this call has a partition wide scope and can take a while to complete.
+ * If it returns H_LONG_BUSY_* it should be retried periodically until it
+ * returns H_SUCCESS.
+ */
+static inline long disable_reloc_on_exceptions(void)
+{
+ return plpar_set_mode(0, 3, 0, 0);
+}
+
+static inline long plapr_set_ciabr(unsigned long ciabr)
+{
+ return plpar_set_mode(0, 1, ciabr, 0);
+}
+
+static inline long plapr_set_watchpoint0(unsigned long dawr0, unsigned long dawrx0)
+{
+ return plpar_set_mode(0, 2, dawr0, dawrx0);
+}
+
+#endif /* _PSERIES_PLPAR_WRAPPERS_H */
diff --git a/arch/powerpc/platforms/pseries/cmm.c b/arch/powerpc/platforms/pseries/cmm.c
index c638535..1e561be 100644
--- a/arch/powerpc/platforms/pseries/cmm.c
+++ b/arch/powerpc/platforms/pseries/cmm.c
@@ -40,8 +40,7 @@
#include <asm/pgalloc.h>
#include <asm/uaccess.h>
#include <linux/memory.h>
-
-#include "plpar_wrappers.h"
+#include <asm/plpar_wrappers.h>
#define CMM_DRIVER_VERSION "1.0.0"
#define CMM_DEFAULT_DELAY 1
diff --git a/arch/powerpc/platforms/pseries/dtl.c b/arch/powerpc/platforms/pseries/dtl.c
index 0cc0ac0..f6cb051 100644
--- a/arch/powerpc/platforms/pseries/dtl.c
+++ b/arch/powerpc/platforms/pseries/dtl.c
@@ -29,8 +29,7 @@
#include <asm/firmware.h>
#include <asm/lppaca.h>
#include <asm/debug.h>
-
-#include "plpar_wrappers.h"
+#include <asm/plpar_wrappers.h>
struct dtl {
struct dtl_entry *buf;
diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c
index 217ca5c..a8ef932 100644
--- a/arch/powerpc/platforms/pseries/hotplug-cpu.c
+++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c
@@ -30,7 +30,8 @@
#include <asm/machdep.h>
#include <asm/vdso_datapage.h>
#include <asm/xics.h>
-#include "plpar_wrappers.h"
+#include <asm/plpar_wrappers.h>
+
#include "offline_states.h"
/* This version can't take the spinlock, because it never returns */
diff --git a/arch/powerpc/platforms/pseries/hvconsole.c b/arch/powerpc/platforms/pseries/hvconsole.c
index b344f94..f3f108b 100644
--- a/arch/powerpc/platforms/pseries/hvconsole.c
+++ b/arch/powerpc/platforms/pseries/hvconsole.c
@@ -28,7 +28,7 @@
#include <linux/errno.h>
#include <asm/hvcall.h>
#include <asm/hvconsole.h>
-#include "plpar_wrappers.h"
+#include <asm/plpar_wrappers.h>
/**
* hvc_get_chars - retrieve characters from firmware for denoted vterm adatper
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
index 23fc1dc..4821933 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -48,8 +48,7 @@
#include <asm/ppc-pci.h>
#include <asm/udbg.h>
#include <asm/mmzone.h>
-
-#include "plpar_wrappers.h"
+#include <asm/plpar_wrappers.h>
static void tce_invalidate_pSeries_sw(struct iommu_table *tbl,
diff --git a/arch/powerpc/platforms/pseries/kexec.c b/arch/powerpc/platforms/pseries/kexec.c
index 7d94bdc..13fa95b3 100644
--- a/arch/powerpc/platforms/pseries/kexec.c
+++ b/arch/powerpc/platforms/pseries/kexec.c
@@ -17,9 +17,9 @@
#include <asm/mpic.h>
#include <asm/xics.h>
#include <asm/smp.h>
+#include <asm/plpar_wrappers.h>
#include "pseries.h"
-#include "plpar_wrappers.h"
static void pseries_kexec_cpu_down(int crash_shutdown, int secondary)
{
diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c
index 8bad880..e1873bc 100644
--- a/arch/powerpc/platforms/pseries/lpar.c
+++ b/arch/powerpc/platforms/pseries/lpar.c
@@ -41,8 +41,8 @@
#include <asm/smp.h>
#include <asm/trace.h>
#include <asm/firmware.h>
+#include <asm/plpar_wrappers.h>
-#include "plpar_wrappers.h"
#include "pseries.h"
/* Flag bits for H_BULK_REMOVE */
diff --git a/arch/powerpc/platforms/pseries/plpar_wrappers.h b/arch/powerpc/platforms/pseries/plpar_wrappers.h
deleted file mode 100644
index f35787b..0000000
--- a/arch/powerpc/platforms/pseries/plpar_wrappers.h
+++ /dev/null
@@ -1,324 +0,0 @@
-#ifndef _PSERIES_PLPAR_WRAPPERS_H
-#define _PSERIES_PLPAR_WRAPPERS_H
-
-#include <linux/string.h>
-#include <linux/irqflags.h>
-
-#include <asm/hvcall.h>
-#include <asm/paca.h>
-#include <asm/page.h>
-
-/* Get state of physical CPU from query_cpu_stopped */
-int smp_query_cpu_stopped(unsigned int pcpu);
-#define QCSS_STOPPED 0
-#define QCSS_STOPPING 1
-#define QCSS_NOT_STOPPED 2
-#define QCSS_HARDWARE_ERROR -1
-#define QCSS_HARDWARE_BUSY -2
-
-static inline long poll_pending(void)
-{
- return plpar_hcall_norets(H_POLL_PENDING);
-}
-
-static inline u8 get_cede_latency_hint(void)
-{
- return get_lppaca()->cede_latency_hint;
-}
-
-static inline void set_cede_latency_hint(u8 latency_hint)
-{
- get_lppaca()->cede_latency_hint = latency_hint;
-}
-
-static inline long cede_processor(void)
-{
- return plpar_hcall_norets(H_CEDE);
-}
-
-static inline long extended_cede_processor(unsigned long latency_hint)
-{
- long rc;
- u8 old_latency_hint = get_cede_latency_hint();
-
- set_cede_latency_hint(latency_hint);
-
- rc = cede_processor();
-#ifdef CONFIG_TRACE_IRQFLAGS
- /* Ensure that H_CEDE returns with IRQs on */
- if (WARN_ON(!(mfmsr() & MSR_EE)))
- __hard_irq_enable();
-#endif
-
- set_cede_latency_hint(old_latency_hint);
-
- return rc;
-}
-
-static inline long vpa_call(unsigned long flags, unsigned long cpu,
- unsigned long vpa)
-{
- flags = flags << H_VPA_FUNC_SHIFT;
-
- return plpar_hcall_norets(H_REGISTER_VPA, flags, cpu, vpa);
-}
-
-static inline long unregister_vpa(unsigned long cpu)
-{
- return vpa_call(H_VPA_DEREG_VPA, cpu, 0);
-}
-
-static inline long register_vpa(unsigned long cpu, unsigned long vpa)
-{
- return vpa_call(H_VPA_REG_VPA, cpu, vpa);
-}
-
-static inline long unregister_slb_shadow(unsigned long cpu)
-{
- return vpa_call(H_VPA_DEREG_SLB, cpu, 0);
-}
-
-static inline long register_slb_shadow(unsigned long cpu, unsigned long vpa)
-{
- return vpa_call(H_VPA_REG_SLB, cpu, vpa);
-}
-
-static inline long unregister_dtl(unsigned long cpu)
-{
- return vpa_call(H_VPA_DEREG_DTL, cpu, 0);
-}
-
-static inline long register_dtl(unsigned long cpu, unsigned long vpa)
-{
- return vpa_call(H_VPA_REG_DTL, cpu, vpa);
-}
-
-static inline long plpar_page_set_loaned(unsigned long vpa)
-{
- unsigned long cmo_page_sz = cmo_get_page_size();
- long rc = 0;
- int i;
-
- for (i = 0; !rc && i < PAGE_SIZE; i += cmo_page_sz)
- rc = plpar_hcall_norets(H_PAGE_INIT, H_PAGE_SET_LOANED, vpa + i, 0);
-
- for (i -= cmo_page_sz; rc && i != 0; i -= cmo_page_sz)
- plpar_hcall_norets(H_PAGE_INIT, H_PAGE_SET_ACTIVE,
- vpa + i - cmo_page_sz, 0);
-
- return rc;
-}
-
-static inline long plpar_page_set_active(unsigned long vpa)
-{
- unsigned long cmo_page_sz = cmo_get_page_size();
- long rc = 0;
- int i;
-
- for (i = 0; !rc && i < PAGE_SIZE; i += cmo_page_sz)
- rc = plpar_hcall_norets(H_PAGE_INIT, H_PAGE_SET_ACTIVE, vpa + i, 0);
-
- for (i -= cmo_page_sz; rc && i != 0; i -= cmo_page_sz)
- plpar_hcall_norets(H_PAGE_INIT, H_PAGE_SET_LOANED,
- vpa + i - cmo_page_sz, 0);
-
- return rc;
-}
-
-extern void vpa_init(int cpu);
-
-static inline long plpar_pte_enter(unsigned long flags,
- unsigned long hpte_group, unsigned long hpte_v,
- unsigned long hpte_r, unsigned long *slot)
-{
- long rc;
- unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
-
- rc = plpar_hcall(H_ENTER, retbuf, flags, hpte_group, hpte_v, hpte_r);
-
- *slot = retbuf[0];
-
- return rc;
-}
-
-static inline long plpar_pte_remove(unsigned long flags, unsigned long ptex,
- unsigned long avpn, unsigned long *old_pteh_ret,
- unsigned long *old_ptel_ret)
-{
- long rc;
- unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
-
- rc = plpar_hcall(H_REMOVE, retbuf, flags, ptex, avpn);
-
- *old_pteh_ret = retbuf[0];
- *old_ptel_ret = retbuf[1];
-
- return rc;
-}
-
-/* plpar_pte_remove_raw can be called in real mode. It calls plpar_hcall_raw */
-static inline long plpar_pte_remove_raw(unsigned long flags, unsigned long ptex,
- unsigned long avpn, unsigned long *old_pteh_ret,
- unsigned long *old_ptel_ret)
-{
- long rc;
- unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
-
- rc = plpar_hcall_raw(H_REMOVE, retbuf, flags, ptex, avpn);
-
- *old_pteh_ret = retbuf[0];
- *old_ptel_ret = retbuf[1];
-
- return rc;
-}
-
-static inline long plpar_pte_read(unsigned long flags, unsigned long ptex,
- unsigned long *old_pteh_ret, unsigned long *old_ptel_ret)
-{
- long rc;
- unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
-
- rc = plpar_hcall(H_READ, retbuf, flags, ptex);
-
- *old_pteh_ret = retbuf[0];
- *old_ptel_ret = retbuf[1];
-
- return rc;
-}
-
-/* plpar_pte_read_raw can be called in real mode. It calls plpar_hcall_raw */
-static inline long plpar_pte_read_raw(unsigned long flags, unsigned long ptex,
- unsigned long *old_pteh_ret, unsigned long *old_ptel_ret)
-{
- long rc;
- unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
-
- rc = plpar_hcall_raw(H_READ, retbuf, flags, ptex);
-
- *old_pteh_ret = retbuf[0];
- *old_ptel_ret = retbuf[1];
-
- return rc;
-}
-
-/*
- * plpar_pte_read_4_raw can be called in real mode.
- * ptes must be 8*sizeof(unsigned long)
- */
-static inline long plpar_pte_read_4_raw(unsigned long flags, unsigned long ptex,
- unsigned long *ptes)
-
-{
- long rc;
- unsigned long retbuf[PLPAR_HCALL9_BUFSIZE];
-
- rc = plpar_hcall9_raw(H_READ, retbuf, flags | H_READ_4, ptex);
-
- memcpy(ptes, retbuf, 8*sizeof(unsigned long));
-
- return rc;
-}
-
-static inline long plpar_pte_protect(unsigned long flags, unsigned long ptex,
- unsigned long avpn)
-{
- return plpar_hcall_norets(H_PROTECT, flags, ptex, avpn);
-}
-
-static inline long plpar_tce_get(unsigned long liobn, unsigned long ioba,
- unsigned long *tce_ret)
-{
- long rc;
- unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
-
- rc = plpar_hcall(H_GET_TCE, retbuf, liobn, ioba);
-
- *tce_ret = retbuf[0];
-
- return rc;
-}
-
-static inline long plpar_tce_put(unsigned long liobn, unsigned long ioba,
- unsigned long tceval)
-{
- return plpar_hcall_norets(H_PUT_TCE, liobn, ioba, tceval);
-}
-
-static inline long plpar_tce_put_indirect(unsigned long liobn,
- unsigned long ioba, unsigned long page, unsigned long count)
-{
- return plpar_hcall_norets(H_PUT_TCE_INDIRECT, liobn, ioba, page, count);
-}
-
-static inline long plpar_tce_stuff(unsigned long liobn, unsigned long ioba,
- unsigned long tceval, unsigned long count)
-{
- return plpar_hcall_norets(H_STUFF_TCE, liobn, ioba, tceval, count);
-}
-
-static inline long plpar_get_term_char(unsigned long termno,
- unsigned long *len_ret, char *buf_ret)
-{
- long rc;
- unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
- unsigned long *lbuf = (unsigned long *)buf_ret; /* TODO: alignment? */
-
- rc = plpar_hcall(H_GET_TERM_CHAR, retbuf, termno);
-
- *len_ret = retbuf[0];
- lbuf[0] = retbuf[1];
- lbuf[1] = retbuf[2];
-
- return rc;
-}
-
-static inline long plpar_put_term_char(unsigned long termno, unsigned long len,
- const char *buffer)
-{
- unsigned long *lbuf = (unsigned long *)buffer; /* TODO: alignment? */
- return plpar_hcall_norets(H_PUT_TERM_CHAR, termno, len, lbuf[0],
- lbuf[1]);
-}
-
-/* Set various resource mode parameters */
-static inline long plpar_set_mode(unsigned long mflags, unsigned long resource,
- unsigned long value1, unsigned long value2)
-{
- return plpar_hcall_norets(H_SET_MODE, mflags, resource, value1, value2);
-}
-
-/*
- * Enable relocation on exceptions on this partition
- *
- * Note: this call has a partition wide scope and can take a while to complete.
- * If it returns H_LONG_BUSY_* it should be retried periodically until it
- * returns H_SUCCESS.
- */
-static inline long enable_reloc_on_exceptions(void)
-{
- /* mflags = 3: Exceptions at 0xC000000000004000 */
- return plpar_set_mode(3, 3, 0, 0);
-}
-
-/*
- * Disable relocation on exceptions on this partition
- *
- * Note: this call has a partition wide scope and can take a while to complete.
- * If it returns H_LONG_BUSY_* it should be retried periodically until it
- * returns H_SUCCESS.
- */
-static inline long disable_reloc_on_exceptions(void) {
- return plpar_set_mode(0, 3, 0, 0);
-}
-
-static inline long plapr_set_ciabr(unsigned long ciabr)
-{
- return plpar_set_mode(0, 1, ciabr, 0);
-}
-
-static inline long plapr_set_watchpoint0(unsigned long dawr0, unsigned long dawrx0)
-{
- return plpar_set_mode(0, 2, dawr0, dawrx0);
-}
-
-#endif /* _PSERIES_PLPAR_WRAPPERS_H */
diff --git a/arch/powerpc/platforms/pseries/processor_idle.c b/arch/powerpc/platforms/pseries/processor_idle.c
index ca70279..c905b99 100644
--- a/arch/powerpc/platforms/pseries/processor_idle.c
+++ b/arch/powerpc/platforms/pseries/processor_idle.c
@@ -18,8 +18,7 @@
#include <asm/machdep.h>
#include <asm/firmware.h>
#include <asm/runlatch.h>
-
-#include "plpar_wrappers.h"
+#include <asm/plpar_wrappers.h>
struct cpuidle_driver pseries_idle_driver = {
.name = "pseries_idle",
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index c11c823..4291589 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -66,8 +66,8 @@
#include <asm/firmware.h>
#include <asm/eeh.h>
#include <asm/reg.h>
+#include <asm/plpar_wrappers.h>
-#include "plpar_wrappers.h"
#include "pseries.h"
int CMO_PrPSP = -1;
diff --git a/arch/powerpc/platforms/pseries/smp.c b/arch/powerpc/platforms/pseries/smp.c
index 306643c..1c79af7 100644
--- a/arch/powerpc/platforms/pseries/smp.c
+++ b/arch/powerpc/platforms/pseries/smp.c
@@ -43,8 +43,8 @@
#include <asm/cputhreads.h>
#include <asm/xics.h>
#include <asm/dbell.h>
+#include <asm/plpar_wrappers.h>
-#include "plpar_wrappers.h"
#include "pseries.h"
#include "offline_states.h"
^ permalink raw reply related
* [RFC PATCH V3 1/5] pseries/cpuidle: Remove dependency of pseries.h file
From: Deepthi Dharwar @ 2013-08-19 4:28 UTC (permalink / raw)
To: benh, daniel.lezcano, kernel, scottwood, linux-pm, linuxppc-dev
Cc: preeti, dongsheng.wang
In-Reply-To: <20130819042736.8609.17890.stgit@deepthi.in.ibm.com>
As a part of pseries_idle cleanup to make the backend driver
code common to both pseries and powernv.
Remove non-essential smt_snooze_delay declaration in pseries.h
header file and pseries.h file inclusion in
pseries/processor_idle.c
Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
---
arch/powerpc/platforms/pseries/processor_idle.c | 1 -
arch/powerpc/platforms/pseries/pseries.h | 3 ---
2 files changed, 4 deletions(-)
diff --git a/arch/powerpc/platforms/pseries/processor_idle.c b/arch/powerpc/platforms/pseries/processor_idle.c
index 4644efa0..ca70279 100644
--- a/arch/powerpc/platforms/pseries/processor_idle.c
+++ b/arch/powerpc/platforms/pseries/processor_idle.c
@@ -20,7 +20,6 @@
#include <asm/runlatch.h>
#include "plpar_wrappers.h"
-#include "pseries.h"
struct cpuidle_driver pseries_idle_driver = {
.name = "pseries_idle",
diff --git a/arch/powerpc/platforms/pseries/pseries.h b/arch/powerpc/platforms/pseries/pseries.h
index c2a3a25..d1b07e6 100644
--- a/arch/powerpc/platforms/pseries/pseries.h
+++ b/arch/powerpc/platforms/pseries/pseries.h
@@ -60,9 +60,6 @@ extern struct device_node *dlpar_configure_connector(u32);
extern int dlpar_attach_node(struct device_node *);
extern int dlpar_detach_node(struct device_node *);
-/* Snooze Delay, pseries_idle */
-DECLARE_PER_CPU(long, smt_snooze_delay);
-
/* PCI root bridge prepare function override for pseries */
struct pci_host_bridge;
int pseries_root_bridge_prepare(struct pci_host_bridge *bridge);
^ permalink raw reply related
* [RFC PATCH V3 0/5] powerpc/cpuidle: Generic POWERPC cpuidle driver enabled for POWER and POWERNV platforms
From: Deepthi Dharwar @ 2013-08-19 4:27 UTC (permalink / raw)
To: benh, daniel.lezcano, kernel, scottwood, linux-pm, linuxppc-dev
Cc: preeti, dongsheng.wang
This patch series consolidates the backend cpuidle driver for pseries
and powernv platforms and also enables the new drivers/cpuidle/cpuidle-powerpc.c
to include other powerpc drivers with minimal code duplication.
Current existing backend driver for pseries has been moved to drivers/cpuidle
and has been extended to accommodate powernv idle power mgmt states.
As seen in V1 of this patch series, having a separate powernv backend driver
results in too much code duplication, which is less elegant and can pose
maintenance problems going further.
Using the cpuidle framework to exploit platform low power idle states
management can take advantage of advanced heuristics, tunables and features
provided by framework. The statistics and tracing infrastructure provided
by the cpuidle framework also helps in enabling power management
related tools and help tune the system and applications.
Earlier in 3.3 kernel, pseries idle state management was modified to
exploit the cpuidle framework and the end goal of this patch is to have powernv
platform also to hook its idle states into cpuidle framework with minimal
code duplication between both platforms. This result is a generic powerpc
backend driver currently enabled for pseries and powernv platforms and which
can be extended to accommodate other powerpc archs in the future.
This series aims to maintain compatibility and functionality to existing pseries
and powernv idle cpu management code. There are no new functions or idle
states added as part of this series. This can be extended by adding more
states to this existing framework.
With this patch series, the powernv cpuidle functionalities are on-par with
pSeries idle management. Other POWERPC platforms can use this patch series
to exploit the CPUIDLE framework.
This patch mainly focus on an integrated CPUIDLE backend driver for POWERPC.
Minor cpuidle clean-ups including a generic hotplug cpuidle notifier,
using cpuidle_register calls will be taken up going further.
V1 -> http://lkml.org/lkml/2013/7/23/143
V2 -> https://lkml.org/lkml/2013/7/30/872
Changes from V2:
===============
* This patch series does not include smt-snooze-delay fixes.
This will be taken up later on.
* Integrated POWERPC driver in drivers/cpuidle.
Enabled for all of POWERPC platform.
Currently has PSERIES and POWERNV support.
No compile time flags in .c file. This will be
one consolidated binary that does a run time
detection based on platform and take decisions
accordingly.
Deepthi Dharwar (5):
pseries/cpuidle: Remove dependency of pseries.h file
pseries: Move plpar_wrapper.h to powerpc common include/asm location.
powerpc/cpuidle: Generic powerpc backend cpuidle driver.
powerpc/cpuidle: Enable powernv cpuidle support.
powernv/cpuidle: Enable idle powernv cpu to call into the cpuidle framework.
arch/powerpc/include/asm/paca.h | 23 +
arch/powerpc/include/asm/plpar_wrappers.h | 325 +++++++++++++++++++
arch/powerpc/include/asm/processor.h | 2
arch/powerpc/platforms/powernv/setup.c | 14 +
arch/powerpc/platforms/pseries/Kconfig | 9 -
arch/powerpc/platforms/pseries/Makefile | 1
arch/powerpc/platforms/pseries/cmm.c | 3
arch/powerpc/platforms/pseries/dtl.c | 3
arch/powerpc/platforms/pseries/hotplug-cpu.c | 3
arch/powerpc/platforms/pseries/hvconsole.c | 2
arch/powerpc/platforms/pseries/iommu.c | 3
arch/powerpc/platforms/pseries/kexec.c | 2
arch/powerpc/platforms/pseries/lpar.c | 2
arch/powerpc/platforms/pseries/plpar_wrappers.h | 324 -------------------
arch/powerpc/platforms/pseries/processor_idle.c | 362 ---------------------
arch/powerpc/platforms/pseries/pseries.h | 3
arch/powerpc/platforms/pseries/setup.c | 2
arch/powerpc/platforms/pseries/smp.c | 2
drivers/cpuidle/Kconfig | 7
drivers/cpuidle/Makefile | 2
drivers/cpuidle/cpuidle-powerpc.c | 391 +++++++++++++++++++++++
21 files changed, 772 insertions(+), 713 deletions(-)
create mode 100644 arch/powerpc/include/asm/plpar_wrappers.h
delete mode 100644 arch/powerpc/platforms/pseries/plpar_wrappers.h
delete mode 100644 arch/powerpc/platforms/pseries/processor_idle.c
create mode 100644 drivers/cpuidle/cpuidle-powerpc.c
-- Deepthi
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox