* Re: [PATCH] ARM: omap3: beagleboard-xm: dt: Add ethernet to the device tree
From: Tony Lindgren @ 2016-12-02 0:50 UTC (permalink / raw)
To: Laurent Pinchart
Cc: linux-omap-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Benoît Cousson
In-Reply-To: <20161202002921.GD3703-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org>
* Tony Lindgren <tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org> [161201 16:29]:
> * Tony Lindgren <tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org> [161201 13:45]:
> > * Laurent Pinchart <laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org> [161201 13:37]:
> > > Hi Tony,
> > >
> > > On Thursday 01 Dec 2016 13:12:34 Tony Lindgren wrote:
> > > > * Laurent Pinchart <laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org> [161130 09:58]:
> > > > > The Beagleboard-xM has a LAN9514 USB hub and ethernet controller,
> > > > > connected to port 2 of the OMAP EHCI controller. The board however has
> > > > > no EEPROM to store the ethernet MAC address, which is programmed by the
> > > > > boot loader.
> > > > >
> > > > > To allow Linux to use the same MAC address as the boot loader (or for
> > > > > that matter any fixed MAC address), we need a node in the device tree
> > > > > for the ethernet controller that the boot loader can update at runtime
> > > > > with a local-mac-address property. Add it, along with an alias for the
> > > > > ethernet controller to let the boot loader locate it easily.
> > > >
> > > > Does not seem to work here.. Do I need to set something in u-boot?
> > > > I'm using U-Boot 2016.09-00004-g26bb688.
> > >
> > > Some versions (possibly forked by vendors) might set the MAC address
> > > automatically in DT, but in my case I have the following in my boot script:
> > >
> > > tftp 0x80800000 beagle/omap3-beagle-xm.dtb
> > > fdt addr ${fileaddr} ${filesize}
> > > fdt resize
> > > fdt set /ocp@68000000/usbhshost@48064000/ehci@48064800/usb2@2/usbether@1 local-mac-address "[7a d2 a0 00 d1 f0]"
> >
> > OK. I just added setenv ethaddr ${usbethaddr} to my bootcmd..
>
> Here's a similar patch for omap5-uevm. Somehow u-boot does not populate the
> local-mac-address on it though although set in the environment.
>
> So I had to manually do the fdt set /ocp/usbhshost@4a064000/ehci@4a064c00/usbether@3
> command.
And here's one for pandaboard. That gets configured fine if ethaddr is
set.
Regards,
Tony
8< ------------------
>From tony Mon Sep 17 00:00:00 2001
From: Tony Lindgren <tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org>
Date: Thu, 1 Dec 2016 16:33:20 -0800
Subject: [PATCH] ARM: dts: pandaboard: Allow bootloader to configure USB
Ethernet MAC
Inspired by a patch for beagleboard xm by Laurent Pinchart
<laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org>, similar patch also works for
pandaboard. The only difference is that the hub is address 1 instead
of 2.
Signed-off-by: Tony Lindgren <tony-4v6yS6AI5VpBDgjK7y7TUQ@public.gmane.org>
---
arch/arm/boot/dts/omap4-panda-common.dtsi | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/arch/arm/boot/dts/omap4-panda-common.dtsi b/arch/arm/boot/dts/omap4-panda-common.dtsi
--- a/arch/arm/boot/dts/omap4-panda-common.dtsi
+++ b/arch/arm/boot/dts/omap4-panda-common.dtsi
@@ -16,6 +16,7 @@
aliases {
display0 = &dvi0;
display1 = &hdmi0;
+ ethernet = ðernet;
};
leds: leds {
@@ -520,6 +521,21 @@
&usbhsehci {
phys = <&hsusb1_phy>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ usb2@1 {
+ compatible = "usb424,9514";
+ reg = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ethernet: usbether@1 {
+ compatible = "usb424,ec00";
+ reg = <1>;
+ };
+ };
};
&dss {
--
2.11.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 1/3] of: Support parsing phandle argument lists through a nexus node
From: Stephen Boyd @ 2016-12-02 1:10 UTC (permalink / raw)
To: Rob Herring
Cc: devicetree@vger.kernel.org, Linus Walleij, Pantelis Antoniou,
linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org,
Mark Brown, Frank Rowand, linux-arm-kernel@lists.infradead.org
In-Reply-To: <CAL_JsqKZ_Ohcdi0n6XU+5=3o6OwC=r_5Cv1=gx7mytUUVd2dzA@mail.gmail.com>
Quoting Rob Herring (2016-11-30 15:30:47)
> On Thu, Nov 24, 2016 at 4:25 AM, Stephen Boyd <stephen.boyd@linaro.org> wrote:
> > Platforms like 96boards have a standardized connector/expansion
> > slot that exposes signals like GPIOs to expansion boards in an
> > SoC agnostic way. We'd like the DT overlays for the expansion
> > boards to be written once without knowledge of the SoC on the
> > other side of the connector. This avoids the unscalable
> > combinatorial explosion of a different DT overlay for each
> > expansion board and SoC pair.
> >
> > We need a way to describe the GPIOs routed through the connector
> > in an SoC agnostic way. Let's introduce nexus property parsing
> > into the OF core to do this. This is largely based on the
> > interrupt nexus support we already have. This allows us to remap
> > a phandle list in a consumer node (e.g. reset-gpios) through a
> > connector in a generic way (e.g. via gpio-map). Do this in a
> > generic routine so that we can remap any sort of variable length
> > phandle list.
> >
> > Taking GPIOs as an example, the connector would be a GPIO nexus,
> > supporting the remapping of a GPIO specifier space to multiple
> > GPIO providers on the SoC. DT would look as shown below, where
> > 'soc_gpio1' and 'soc_gpio2' are inside the SoC, 'connector' is an
> > expansion port where boards can be plugged in, and
> > 'expansion_device' is a device on the expansion board.
> >
> > soc {
> > soc_gpio1: gpio-controller1 {
> > #gpio-cells = <2>;
> > };
> >
> > soc_gpio2: gpio-controller2 {
> > #gpio-cells = <2>;
> > };
> > };
> >
> > connector: connector {
> > #gpio-cells = <2>;
> > gpio-map = <0 GPIO_ACTIVE_LOW &soc_gpio1 1 GPIO_ACTIVE_LOW>,
> > <1 GPIO_ACTIVE_LOW &soc_gpio2 4 GPIO_ACTIVE_LOW>,
> > <2 GPIO_ACTIVE_LOW &soc_gpio1 3 GPIO_ACTIVE_LOW>,
> > <3 GPIO_ACTIVE_LOW &soc_gpio2 2 GPIO_ACTIVE_LOW>;
> > gpio-map-mask = <0xf 0x1>;
>
> I think the common case is something more like this:
>
> gpio-map = <0 0 &soc_gpio1 1 0>,
> <1 0 &soc_gpio2 4 0>,
> <2 0 &soc_gpio1 3 0>,
> <3 0 &soc_gpio2 2 0>;
> gpio-map-mask = <0xf 0>;
>
> where we want to pass the 2nd cell of the consumer (e.g. reset-gpios)
> thru. So here the GPIO_ACTIVE_LOW flag below needs to pass thru to
> &soc_gpio1. Otherwise, the gpio-map is has to enumerate every possible
> combination or it will be specific to the daughterboard's usage.
>
> Also, GPIO cells are pretty well standardized, but some cases may need
> a translation function which I guess would be part of a connector
> driver. I don't think that affects the binding nor needs to be solved
> now, but just want to raise that possibility.
Right. I think that translation function could be done with DT though.
For example, we could remap an ACTIVE_LOW flag to an ACTIVE_HIGH flag,
by matching the GPIO on active low and changing it to active high during
the remap. That seems like something we can solve now if it ever happens
by keeping the remapping scheme that interrupts does. Of course, if it
becomes more complicated this breaks down, but then we can always have
the connector driver do the more complicated stuff.
If we want to support pass through, perhaps we should introduce yet
another property to indicate which cells and maybe even which bits in
those cells should be passed through from one side to the other. That
way we can support a compressed scheme without requiring all the
combinations of gpios and flags to be listed out.
For example, gpio-map-pass-thru = <0x0 0xff> would mean that we should
pass through the second cell values that are the lower 8 bits. Map
matching would still be done with the map-mask property, but this
property would indicate which part of the specifier to mask out of the
other side when copying it over.
>
> > };
> >
> > expansion_device {
> > reset-gpios = <&connector 2 GPIO_ACTIVE_LOW>;
> > };
> >
> > The GPIO core would use of_parse_phandle_with_args_map() instead
> > of of_parse_phandle_with_args() and arrive at the same type of
> > result, a phandle and argument list. The difference is that the
> > phandle and arguments will be remapped through the nexus node to
> > the underlying SoC GPIO controller node. In the example above,
> > we would remap 'reset-gpios' from <&connector 2 GPIO_ACTIVE_LOW>
> > to <&soc_gpio1 3 GPIO_ACTIVE_LOW>.
>
> GPIOs also are interrupts frequently, so we need to make sure
> interrupt translation works too. It's a bit tricky as interrupt-map
> depends on #address-cells and #interrupt-cells. I think we just set
> the #address-cells to 0 on the connector node and it will be fine. We
> may need the same pass thru of flags though.
Right I think that should work but I haven't tested it so far.
Unfortunately, interrupt mapping doesn't have pass through support, so
we may want to add the same pass through mask property there and update
the interrupt mapping code too? I was trying to figure out how to make
the interrupt code and this function the same, but the whole
interrupt-parent scan made it feel unwieldy to the point where I gave
up.
>
> >
> > Cc: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
> > Cc: Linus Walleij <linus.walleij@linaro.org>
> > Cc: Mark Brown <broonie@kernel.org>
> > Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org>
> > ---
> > drivers/of/base.c | 146 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> > include/linux/of.h | 14 +++++
> > 2 files changed, 160 insertions(+)
> >
> > diff --git a/drivers/of/base.c b/drivers/of/base.c
> > index d687e6de24a0..693b73f33675 100644
> > --- a/drivers/of/base.c
> > +++ b/drivers/of/base.c
> > @@ -1772,6 +1772,152 @@ int of_parse_phandle_with_args(const struct device_node *np, const char *list_na
> > EXPORT_SYMBOL(of_parse_phandle_with_args);
> >
> > /**
> > + * of_parse_phandle_with_args_map() - Find a node pointed by phandle in a list and remap it
> > + * @np: pointer to a device tree node containing a list
> > + * @list_name: property name that contains a list
> > + * @cells_name: property name that specifies phandles' arguments count
> > + * @index: index of a phandle to parse out
> > + * @out_args: optional pointer to output arguments structure (will be filled)
> > + *
> > + * This function is useful to parse lists of phandles and their arguments.
> > + * Returns 0 on success and fills out_args, on error returns appropriate
> > + * errno value.
> > + *
> > + * Caller is responsible to call of_node_put() on the returned out_args->np
> > + * pointer.
> > + *
> > + * Example:
> > + *
> > + * phandle1: node1 {
> > + * #list-cells = <2>;
> > + * }
> > + *
> > + * phandle2: node2 {
> > + * #list-cells = <1>;
> > + * }
> > + *
> > + * phandle3: node3 {
> > + * #list-cells = <1>;
> > + * list-map = <0 &phandle2 3>,
> > + * <1 &phandle2 2>,
> > + * <2 &phandle1 5 1>;
> > + * list-map-mask = <0x3>;
> > + * };
> > + *
> > + * node4 {
> > + * list = <&phandle1 1 2 &phandle3 0>;
> > + * }
> > + *
> > + * To get a device_node of the `node2' node you may call this:
> > + * of_parse_phandle_with_args(node4, "list", "#list-cells", "list-map",
> > + * "list-map-mask", 1, &args);
> > + */
> > +int of_parse_phandle_with_args_map(const struct device_node *np,
> > + const char *list_name,
> > + const char *cells_name,
> > + const char *map_name,
> > + const char *mask_name,
>
> Perhaps these 3 could be just a single base name (e.g. "gpio")?
> Doesn't really buy much other than enforce we don't mix 'gpios' and
> 'gpio'. That could never happen. ;)
>
I thought about that. I was worried that we wanted to support this API
being called in atomic context, but that seems like it can't possibly be
the case. So I'll have to allocate a string for each of those and free
them on exit. Should be ok.
^ permalink raw reply
* Re: [PATCH v3 2/2] HID: i2c-hid: support regulator power on/off
From: Dmitry Torokhov @ 2016-12-02 1:16 UTC (permalink / raw)
To: Brian Norris
Cc: Jiri Kosina, Benjamin Tissoires, Caesar Wang,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Rob Herring,
linux-input-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Mark Rutland, Doug Anderson
In-Reply-To: <20161202004214.GA112550-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>
On Thu, Dec 01, 2016 at 04:42:15PM -0800, Brian Norris wrote:
> Hi Dmitry,
>
> On Thu, Dec 01, 2016 at 04:37:37PM -0800, Dmitry Torokhov wrote:
> > On Thu, Dec 01, 2016 at 04:31:10PM -0800, Brian Norris wrote:
> > > On some boards, we need to enable a regulator before using the HID, and
> > > it's also nice to save power in suspend by disabling it. Support an
> > > optional "vdd-supply" and a companion initialization delay.
> > >
> > > Signed-off-by: Brian Norris <briannorris-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
> > > Signed-off-by: Caesar Wang <wxt-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> > > Cc: Jiri Kosina <jikos-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> > > Cc: linux-input-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> > > ---
> > > v2:
> > > * support compatible property for wacom, with specific "vdd-supply" name
> > > * support the 100ms delay needed for this digitizer
> > > * target regulator support only at specific device
> > >
> > > v3:
> > > * drop Wacom specifics and allow this to be used generically
> > > * add "init-delay-ms" property support
> > >
> > > drivers/hid/i2c-hid/i2c-hid.c | 48 ++++++++++++++++++++++++++++++++++++++++++-
> > > include/linux/i2c/i2c-hid.h | 6 ++++++
> > > 2 files changed, 53 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c
> > > index b3ec4f2de875..4cb523133d13 100644
> > > --- a/drivers/hid/i2c-hid/i2c-hid.c
> > > +++ b/drivers/hid/i2c-hid/i2c-hid.c
> > > @@ -37,7 +37,9 @@
> > > #include <linux/mutex.h>
> > > #include <linux/acpi.h>
> > > #include <linux/of.h>
> > > +#include <linux/of_device.h>
> > > #include <linux/gpio/consumer.h>
> > > +#include <linux/regulator/consumer.h>
> > >
> > > #include <linux/i2c/i2c-hid.h>
> > >
> > > @@ -937,6 +939,22 @@ static int i2c_hid_of_probe(struct i2c_client *client,
> > > }
> > > pdata->hid_descriptor_address = val;
> > >
> > > + ret = of_property_read_u32(dev->of_node, "init-delay-ms", &val);
> > > + if (!ret)
> > > + pdata->init_delay_ms = ret;
> > > +
> > > + pdata->supply = devm_regulator_get_optional(dev, "vdd");
> >
> > Make it devm_regulator_get(), it's cleaner (you'll get a dummy regulator
> > that you can enable/disbale and not check if it is null or not).
> >
> > pdata->supply = devm_regulator_get_optional(dev, "vdd");
> > if (IS_ERR(pdata->supply)) {
> > ret = PTR_ERR(pdata->supply);
> > if (ret != -EPROBE_DEFER)
> > dev_err(...);
> > return ret;
> > }
>
> I had it as devm_regulator_get() in v1, but at that time, I was faking
> the firmware init delay using a regulator property. Now that I want to
> delay in this driver after enabling the regulator, I'd like to know the
> difference between a dummy and a real regulator. There's no need to wait
> after messing with the dummy regulator.
If there is no regulator in ACPI/DT there would not be "init-delay-ms"
property either.
Thanks.
--
Dmitry
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH] ARM: omap3: beagleboard-xm: dt: Add ethernet to the device tree
From: Tony Lindgren @ 2016-12-02 1:18 UTC (permalink / raw)
To: Laurent Pinchart
Cc: linux-omap-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Benoît Cousson
In-Reply-To: <1480528685-26259-1-git-send-email-laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org>
* Laurent Pinchart <laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org> [161130 09:58]:
> &usbhsehci {
> phys = <0 &hsusb2_phy>;
> +
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + usb2@2 {
I think this should be usb1@2 instead of usb2@2? That's because it's
at /sys/bus/usb/devices/1-2 and not at /sys/bus/usb/devices/2-2?
Or what's the naming standard here?
Regards,
Tony
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH] arm: dts: mt2701: Sort DT nodes by register address
From: James Liao @ 2016-12-02 2:06 UTC (permalink / raw)
To: Matthias Brugger
Cc: Rob Herring, Russell King, Mark Rutland,
srv_heupstream-NuS5LvNUpcJWk0Htik3J/w,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, James Liao
This patch rearrange MT2701 DT nodes to keep them in ascending order.
Signed-off-by: James Liao <jamesjj.liao-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
---
This patch is based on latest v4.9-next/dts branch of [1], to fix MT2701
DT nodes ordering.
[1] https://github.com/mbgg/linux-mediatek/tree/v4.9-next/dts
arch/arm/boot/dts/mt2701.dtsi | 36 ++++++++++++++++++------------------
1 file changed, 18 insertions(+), 18 deletions(-)
diff --git a/arch/arm/boot/dts/mt2701.dtsi b/arch/arm/boot/dts/mt2701.dtsi
index 7eab6f4..73f4b7c 100644
--- a/arch/arm/boot/dts/mt2701.dtsi
+++ b/arch/arm/boot/dts/mt2701.dtsi
@@ -96,24 +96,6 @@
<GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
};
- pio: pinctrl@10005000 {
- compatible = "mediatek,mt2701-pinctrl";
- reg = <0 0x1000b000 0 0x1000>;
- mediatek,pctl-regmap = <&syscfg_pctl_a>;
- pins-are-numbered;
- gpio-controller;
- #gpio-cells = <2>;
- interrupt-controller;
- #interrupt-cells = <2>;
- interrupts = <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>;
- };
-
- syscfg_pctl_a: syscfg@10005000 {
- compatible = "mediatek,mt2701-pctl-a-syscfg", "syscon";
- reg = <0 0x10005000 0 0x1000>;
- };
-
topckgen: syscon@10000000 {
compatible = "mediatek,mt2701-topckgen", "syscon";
reg = <0 0x10000000 0 0x1000>;
@@ -134,6 +116,24 @@
#reset-cells = <1>;
};
+ pio: pinctrl@10005000 {
+ compatible = "mediatek,mt2701-pinctrl";
+ reg = <0 0x1000b000 0 0x1000>;
+ mediatek,pctl-regmap = <&syscfg_pctl_a>;
+ pins-are-numbered;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ interrupts = <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ syscfg_pctl_a: syscfg@10005000 {
+ compatible = "mediatek,mt2701-pctl-a-syscfg", "syscon";
+ reg = <0 0x10005000 0 0x1000>;
+ };
+
watchdog: watchdog@10007000 {
compatible = "mediatek,mt2701-wdt",
"mediatek,mt6589-wdt";
--
1.9.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v3 0/2] mmc: sdhci: one expansion for SDHCI base code and add Cadence SDHCI driver
From: Masahiro Yamada @ 2016-12-02 2:21 UTC (permalink / raw)
To: linux-mmc
Cc: Adrian Hunter, Ulf Hansson, Masahiro Yamada, devicetree,
Joshua Henderson, Linus Walleij, linux-kernel, Stefan Wahren,
Rob Herring, Al Cooper, Wolfram Sang, Andrei Pistirica,
Mark Rutland, Simon Horman, Geert Uytterhoeven
1/2 tweaks sdhci_execute_tuning(), which I want to use for 2/2.
2/2 adds a new driver for Cadence's controller IP.
Masahiro Yamada (2):
mmc: sdhci: continue normal tuning if unsupported by platform tuning
mmc: sdhci-cadence: add Cadence SD4HC support
.../devicetree/bindings/mmc/sdhci-cadence.txt | 30 +++
drivers/mmc/host/Kconfig | 11 +
drivers/mmc/host/Makefile | 1 +
drivers/mmc/host/sdhci-cadence.c | 279 +++++++++++++++++++++
drivers/mmc/host/sdhci.c | 4 +-
5 files changed, 324 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-cadence.txt
create mode 100644 drivers/mmc/host/sdhci-cadence.c
--
2.7.4
^ permalink raw reply
* [PATCH v3 2/2] mmc: sdhci-cadence: add Cadence SD4HC support
From: Masahiro Yamada @ 2016-12-02 2:21 UTC (permalink / raw)
To: linux-mmc-u79uwXL29TY76Z2rM5mHXA
Cc: Adrian Hunter, Ulf Hansson, Masahiro Yamada,
devicetree-u79uwXL29TY76Z2rM5mHXA, Joshua Henderson,
Linus Walleij, linux-kernel-u79uwXL29TY76Z2rM5mHXA, Stefan Wahren,
Rob Herring, Al Cooper, Wolfram Sang, Andrei Pistirica,
Mark Rutland, Simon Horman, Geert Uytterhoeven
In-Reply-To: <1480645311-13004-1-git-send-email-yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A@public.gmane.org>
Add a driver for the Cadence SD4HC SD/SDIO/eMMC Controller.
For SD, it basically relies on the SDHCI standard code.
For eMMC, this driver provides some callbacks to support the
hardware part that is specific to this IP design.
Signed-off-by: Masahiro Yamada <yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A@public.gmane.org>
---
Changes in v3:
- Remove unneeded explanation about HRS and SRS from DT binding;
the offsets to HRS/SRS are fixed for this hardware and this is
quite normal, like each hardware has a fixed register view except
the register base. The detailed register map is what the driver
cares about, so no need to explain it in the binding.
Changes in v2:
- Remove unnecessary "select MMC_SDHCI_IO_ACCESSORS"
.../devicetree/bindings/mmc/sdhci-cadence.txt | 30 +++
drivers/mmc/host/Kconfig | 11 +
drivers/mmc/host/Makefile | 1 +
drivers/mmc/host/sdhci-cadence.c | 279 +++++++++++++++++++++
4 files changed, 321 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-cadence.txt
create mode 100644 drivers/mmc/host/sdhci-cadence.c
diff --git a/Documentation/devicetree/bindings/mmc/sdhci-cadence.txt b/Documentation/devicetree/bindings/mmc/sdhci-cadence.txt
new file mode 100644
index 0000000..750374f
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/sdhci-cadence.txt
@@ -0,0 +1,30 @@
+* Cadence SD/SDIO/eMMC Host Controller
+
+Required properties:
+- compatible: should be "cdns,sd4hc".
+- reg: offset and length of the register set for the device.
+- interrupts: a single interrupt specifier.
+- clocks: phandle to the input clock.
+
+Optional properties:
+For eMMC configuration, supported speed modes are not indicated by the SDHCI
+Capabilities Register. Instead, the following properties should be specified
+if supported. See mmc.txt for details.
+- mmc-ddr-1_8v
+- mmc-ddr-1_2v
+- mmc-hs200-1_8v
+- mmc-hs200-1_2v
+- mmc-hs400-1_8v
+- mmc-hs400-1_2v
+
+Example:
+ emmc: sdhci@5a000000 {
+ compatible = "cdns,sd4hc";
+ reg = <0x5a000000 0x400>;
+ interrupts = <0 78 4>;
+ clocks = <&clk 4>;
+ bus-width = <8>;
+ mmc-ddr-1_8v;
+ mmc-hs200-1_8v;
+ mmc-hs400-1_8v;
+ };
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index ab9181e..8ac1640 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -164,6 +164,17 @@ config MMC_SDHCI_OF_HLWD
If unsure, say N.
+config MMC_SDHCI_CADENCE
+ tristate "SDHCI support for the Cadence SD/SDIO/eMMC controller"
+ depends on MMC_SDHCI_PLTFM
+ depends on OF
+ help
+ This selects the Cadence SD/SDIO/eMMC driver.
+
+ If you have a controller with this interface, say Y or M here.
+
+ If unsure, say N.
+
config MMC_SDHCI_CNS3XXX
tristate "SDHCI support on the Cavium Networks CNS3xxx SoC"
depends on ARCH_CNS3XXX
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index e49a82a..55f7193 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_MMC_REALTEK_PCI) += rtsx_pci_sdmmc.o
obj-$(CONFIG_MMC_REALTEK_USB) += rtsx_usb_sdmmc.o
obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o
+obj-$(CONFIG_MMC_SDHCI_CADENCE) += sdhci-cadence.o
obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o
obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o
obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o
diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c
new file mode 100644
index 0000000..a7daf00
--- /dev/null
+++ b/drivers/mmc/host/sdhci-cadence.c
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2016 Socionext Inc.
+ * Author: Masahiro Yamada <yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/mmc/host.h>
+
+#include "sdhci-pltfm.h"
+
+/* HRS - Host Register Set (specific to Cadence) */
+#define SDHCI_CDNS_HRS04 0x10 /* PHY access port */
+#define SDHCI_CDNS_HRS04_ACK BIT(26)
+#define SDHCI_CDNS_HRS04_RD BIT(25)
+#define SDHCI_CDNS_HRS04_WR BIT(24)
+#define SDHCI_CDNS_HRS04_RDATA_SHIFT 12
+#define SDHCI_CDNS_HRS04_WDATA_SHIFT 8
+#define SDHCI_CDNS_HRS04_ADDR_SHIFT 0
+
+#define SDHCI_CDNS_HRS06 0x18 /* eMMC control */
+#define SDHCI_CDNS_HRS06_TUNE_UP BIT(15)
+#define SDHCI_CDNS_HRS06_TUNE_SHIFT 8
+#define SDHCI_CDNS_HRS06_TUNE_MASK 0x3f
+#define SDHCI_CDNS_HRS06_MODE_MASK 0x7
+#define SDHCI_CDNS_HRS06_MODE_SD 0x0
+#define SDHCI_CDNS_HRS06_MODE_MMC_SDR 0x2
+#define SDHCI_CDNS_HRS06_MODE_MMC_DDR 0x3
+#define SDHCI_CDNS_HRS06_MODE_MMC_HS200 0x4
+#define SDHCI_CDNS_HRS06_MODE_MMC_HS400 0x5
+
+/* SRS - Slot Register Set (SDHCI-compatible) */
+#define SDHCI_CDNS_SRS_BASE 0x200
+
+/* PHY */
+#define SDHCI_CDNS_PHY_DLY_SD_HS 0x00
+#define SDHCI_CDNS_PHY_DLY_SD_DEFAULT 0x01
+#define SDHCI_CDNS_PHY_DLY_UHS_SDR12 0x02
+#define SDHCI_CDNS_PHY_DLY_UHS_SDR25 0x03
+#define SDHCI_CDNS_PHY_DLY_UHS_SDR50 0x04
+#define SDHCI_CDNS_PHY_DLY_UHS_DDR50 0x05
+#define SDHCI_CDNS_PHY_DLY_EMMC_LEGACY 0x06
+#define SDHCI_CDNS_PHY_DLY_EMMC_SDR 0x07
+#define SDHCI_CDNS_PHY_DLY_EMMC_DDR 0x08
+
+/*
+ * The tuned val register is 6 bit-wide, but not the whole of the range is
+ * available. The range 0-42 seems to be available (then 43 wraps around to 0)
+ * but I am not quite sure if it is official. Use only 0 to 39 for safety.
+ */
+#define SDHCI_CDNS_MAX_TUNING_LOOP 40
+
+struct sdhci_cdns_priv {
+ void __iomem *hrs_addr;
+};
+
+static void sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv,
+ u8 addr, u8 data)
+{
+ void __iomem *reg = priv->hrs_addr + SDHCI_CDNS_HRS04;
+ u32 tmp;
+
+ tmp = (data << SDHCI_CDNS_HRS04_WDATA_SHIFT) |
+ (addr << SDHCI_CDNS_HRS04_ADDR_SHIFT);
+ writel(tmp, reg);
+
+ tmp |= SDHCI_CDNS_HRS04_WR;
+ writel(tmp, reg);
+
+ tmp &= ~SDHCI_CDNS_HRS04_WR;
+ writel(tmp, reg);
+}
+
+static void sdhci_cdns_phy_init(struct sdhci_cdns_priv *priv)
+{
+ sdhci_cdns_write_phy_reg(priv, SDHCI_CDNS_PHY_DLY_SD_HS, 4);
+ sdhci_cdns_write_phy_reg(priv, SDHCI_CDNS_PHY_DLY_SD_DEFAULT, 4);
+ sdhci_cdns_write_phy_reg(priv, SDHCI_CDNS_PHY_DLY_EMMC_LEGACY, 9);
+ sdhci_cdns_write_phy_reg(priv, SDHCI_CDNS_PHY_DLY_EMMC_SDR, 2);
+ sdhci_cdns_write_phy_reg(priv, SDHCI_CDNS_PHY_DLY_EMMC_DDR, 3);
+}
+
+static inline void *sdhci_cdns_priv(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+
+ return sdhci_pltfm_priv(pltfm_host);
+}
+
+static unsigned int sdhci_cdns_get_timeout_clock(struct sdhci_host *host)
+{
+ /*
+ * Cadence's spec says the Timeout Clock Frequency is the same as the
+ * Base Clock Frequency. Divide it by 1000 to return a value in kHz.
+ */
+ return host->max_clk / 1000;
+}
+
+static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val)
+{
+ struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
+ void __iomem *reg = priv->hrs_addr + SDHCI_CDNS_HRS06;
+ u32 tmp;
+
+ if (WARN_ON(val > SDHCI_CDNS_HRS06_TUNE_MASK))
+ return -EINVAL;
+
+ tmp = readl(reg);
+ tmp &= ~(SDHCI_CDNS_HRS06_TUNE_MASK << SDHCI_CDNS_HRS06_TUNE_SHIFT);
+ tmp |= val << SDHCI_CDNS_HRS06_TUNE_SHIFT;
+ tmp |= SDHCI_CDNS_HRS06_TUNE_UP;
+ writel(tmp, reg);
+
+ return readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS06_TUNE_UP),
+ 0, 1);
+}
+
+static int sdhci_cdns_execute_tuning(struct sdhci_host *host, u32 opcode)
+{
+ int max_streak = 0;
+ int cur_streak = 0;
+ int end_of_streak, i;
+
+ /*
+ * This handler only implements the eMMC tuning that is specific to
+ * this controller. Fall back to the standard method for SD timing.
+ */
+ if (host->timing != MMC_TIMING_MMC_HS200)
+ return -ENOTSUPP;
+
+ if (WARN_ON(opcode != MMC_SEND_TUNING_BLOCK_HS200))
+ return -EINVAL;
+
+ for (i = 0; i < SDHCI_CDNS_MAX_TUNING_LOOP; i++) {
+ if (sdhci_cdns_set_tune_val(host, i) ||
+ mmc_send_tuning(host->mmc, opcode, NULL)) { /* bad */
+ cur_streak = 0;
+ } else { /* good */
+ cur_streak++;
+ max_streak = max(max_streak, cur_streak);
+ end_of_streak = i;
+ }
+ }
+
+ if (!max_streak) {
+ dev_err(mmc_dev(host->mmc), "no tuning point found\n");
+ return -EIO;
+ }
+
+ return sdhci_cdns_set_tune_val(host, end_of_streak - max_streak / 2);
+}
+
+static void sdhci_cdns_set_uhs_signaling(struct sdhci_host *host,
+ unsigned int timing)
+{
+ struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
+ u32 mode, tmp;
+
+ switch (timing) {
+ case MMC_TIMING_MMC_HS:
+ mode = SDHCI_CDNS_HRS06_MODE_MMC_SDR;
+ break;
+ case MMC_TIMING_MMC_DDR52:
+ mode = SDHCI_CDNS_HRS06_MODE_MMC_DDR;
+ break;
+ case MMC_TIMING_MMC_HS200:
+ mode = SDHCI_CDNS_HRS06_MODE_MMC_HS200;
+ break;
+ case MMC_TIMING_MMC_HS400:
+ mode = SDHCI_CDNS_HRS06_MODE_MMC_HS400;
+ break;
+ default:
+ mode = SDHCI_CDNS_HRS06_MODE_SD;
+ break;
+ }
+
+ /* The speed mode for eMMC is selected by HRS06 register */
+ tmp = readl(priv->hrs_addr + SDHCI_CDNS_HRS06);
+ tmp &= ~SDHCI_CDNS_HRS06_MODE_MASK;
+ tmp |= mode;
+ writel(tmp, priv->hrs_addr + SDHCI_CDNS_HRS06);
+
+ /* For SD, fall back to the default handler */
+ if (mode == SDHCI_CDNS_HRS06_MODE_SD)
+ sdhci_set_uhs_signaling(host, timing);
+}
+
+static const struct sdhci_ops sdhci_cdns_ops = {
+ .set_clock = sdhci_set_clock,
+ .get_timeout_clock = sdhci_cdns_get_timeout_clock,
+ .set_bus_width = sdhci_set_bus_width,
+ .reset = sdhci_reset,
+ .platform_execute_tuning = sdhci_cdns_execute_tuning,
+ .set_uhs_signaling = sdhci_cdns_set_uhs_signaling,
+};
+
+static const struct sdhci_pltfm_data sdhci_cdns_pltfm_data = {
+ .ops = &sdhci_cdns_ops,
+};
+
+static int sdhci_cdns_probe(struct platform_device *pdev)
+{
+ struct sdhci_host *host;
+ struct sdhci_pltfm_host *pltfm_host;
+ struct sdhci_cdns_priv *priv;
+ struct clk *clk;
+ int ret;
+
+ clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ return ret;
+
+ host = sdhci_pltfm_init(pdev, &sdhci_cdns_pltfm_data, sizeof(*priv));
+ if (IS_ERR(host)) {
+ ret = PTR_ERR(host);
+ goto disable_clk;
+ }
+
+ pltfm_host = sdhci_priv(host);
+ pltfm_host->clk = clk;
+
+ priv = sdhci_cdns_priv(host);
+ priv->hrs_addr = host->ioaddr;
+ host->ioaddr += SDHCI_CDNS_SRS_BASE;
+
+ ret = mmc_of_parse(host->mmc);
+ if (ret)
+ goto free;
+
+ sdhci_cdns_phy_init(priv);
+
+ ret = sdhci_add_host(host);
+ if (ret)
+ goto free;
+
+ return 0;
+free:
+ sdhci_pltfm_free(pdev);
+disable_clk:
+ clk_disable_unprepare(clk);
+
+ return ret;
+}
+
+static const struct of_device_id sdhci_cdns_match[] = {
+ { .compatible = "cdns,sd4hc" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sdhci_cdns_match);
+
+static struct platform_driver sdhci_cdns_driver = {
+ .driver = {
+ .name = "sdhci-cdns",
+ .pm = &sdhci_pltfm_pmops,
+ .of_match_table = sdhci_cdns_match,
+ },
+ .probe = sdhci_cdns_probe,
+ .remove = sdhci_pltfm_unregister,
+};
+module_platform_driver(sdhci_cdns_driver);
+
+MODULE_AUTHOR("Masahiro Yamada <yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A@public.gmane.org>");
+MODULE_DESCRIPTION("Cadence SD/SDIO/eMMC Host Controller Driver");
+MODULE_LICENSE("GPL");
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 0/9] arm64: dts: rockchip: support Google Kevin
From: Brian Norris @ 2016-12-02 2:27 UTC (permalink / raw)
To: Heiko Stuebner
Cc: devicetree, Brian Norris, Doug Anderson, linux-kernel,
linux-rockchip, Rob Herring, Chris Zhong, Stephen Barber,
linux-arm-kernel, Caesar Wang
Hi,
This series adds basic support for Google Kevin, a board in the Gru device
family. I do not add a leaf .dts board file for Gru, but I have retained the
split between "things that apply to the Gru family" (rk3399-gru.dtsi) and
"things that apply to Kevin only" (rk3399-gru-kevin.dtsi).
I've included resends of two patches (adding cros-ec*.dtsi symlinks, and adding
RK3399 DWC3). The former is unmodified, but the latter is rewritten to match
current upstream bindings better, and to allow it to function w/o extcon
support (USB2 only).
AFAICT, all these bindings are in -next, except for the root node compatible
property (added doc in this series) and some of the HID digitizer bindings (see
patch 9; bindings were sent separately).
I elaborate on what's working/not working below, but one of the big missing
pieces is cpufreq support. We still need some more work on getting good
bindings and driver support upstream for the PWM regulator + OVP circuit on
these boards. See patch 8 for more info.
Working and tested (to some extent):
* EC support -- including keyboard, battery, PWM, and probably more
* UART / console
* Thermal
* Touchscreen
* Touchpad
* Digitizer (regulator still WIP)
* PCIe / Wifi
* Bluetooth / Webcam
* SD card
* eMMC
* USB2 on TypeC
- This works much of the time, but USB3 devices may or may not detect
properly. Waiting on proper extcon support for USB3 over TypeC.
- Depends on XHCI/DWC3 fixes for ARM64 that still haven't landed
* Backlight
Not working:
* CPUFreq -- relies on special OVP support for our PWM regulator
circuits
* EC / extcon support -- and with it, USB3/TypeC/DP
* DRM -- won't even build on ARM64, so all display, eDP, etc. is not
enabled
Not tested:
* Audio
Brian Norris (8):
arm64: dts: rockchip: add rk3399 thermal_zones phandle
arm64: dts: rockchip: add rk3399 eDP HPD pinctrl
arm64: dts: rockchip: support dwc3 USB for rk3399
arm64: dts: rockchip: add rk3399 OPPs
dt-bindings: Document rk3399 Gru/Kevin
arm64: dts: rockchip: add Gru/Kevin DTS
arm64: dts: rockchip: partially describe PWM regulators for Gru
arm64: dts: rockchip: add regulator info for Kevin digitizer
Douglas Anderson (1):
arm64: dts: Add symlinks for cros-ec-keyboard and cros-ec-sbs
Documentation/devicetree/bindings/arm/rockchip.txt | 20 +
.../boot/dts/include/common/cros-ec-keyboard.dtsi | 1 +
.../arm64/boot/dts/include/common/cros-ec-sbs.dtsi | 1 +
arch/arm64/boot/dts/rockchip/Makefile | 1 +
arch/arm64/boot/dts/rockchip/rk3399-gru-kevin.dts | 315 ++++++
arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi | 1152 ++++++++++++++++++++
arch/arm64/boot/dts/rockchip/rk3399-opp.dtsi | 145 +++
arch/arm64/boot/dts/rockchip/rk3399.dtsi | 70 +-
8 files changed, 1704 insertions(+), 1 deletion(-)
create mode 120000 arch/arm64/boot/dts/include/common/cros-ec-keyboard.dtsi
create mode 120000 arch/arm64/boot/dts/include/common/cros-ec-sbs.dtsi
create mode 100644 arch/arm64/boot/dts/rockchip/rk3399-gru-kevin.dts
create mode 100644 arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi
create mode 100644 arch/arm64/boot/dts/rockchip/rk3399-opp.dtsi
--
2.8.0.rc3.226.g39d4020
^ permalink raw reply
* [PATCH 1/9] arm64: dts: Add symlinks for cros-ec-keyboard and cros-ec-sbs
From: Brian Norris @ 2016-12-02 2:27 UTC (permalink / raw)
To: Heiko Stuebner
Cc: devicetree, Brian Norris, Doug Anderson, linux-kernel,
linux-rockchip, Rob Herring, Chris Zhong, Stephen Barber,
linux-arm-kernel, Caesar Wang
In-Reply-To: <1480645653-36943-1-git-send-email-briannorris@chromium.org>
From: Douglas Anderson <dianders@chromium.org>
We'd like to be able to use the cros-ec-keyboard.dtsi and
cros-ec-sbs.dtsi snippets for arm64 devices. Currently those files live
in the arm/boot/dts directory.
Let's follow the convention set by commit 8ee57b8182c4 ("ARM64: dts:
vexpress: Use a symlink to vexpress-v2m-rs1.dtsi from arch=arm") and use
a symlink. Note that in this case we put the files in a new
"include/common" directory since these snippets may need to be
referenced by dts files in many different subdirectories.
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Brian Norris <briannorris@chromium.org>
Reviewed-by: Heiko Stueber <heiko@sntech.de>
Tested-by: Heiko Stuebner <heiko@sntech.de>
---
This was submitted months ago but had no users
arch/arm64/boot/dts/include/common/cros-ec-keyboard.dtsi | 1 +
arch/arm64/boot/dts/include/common/cros-ec-sbs.dtsi | 1 +
2 files changed, 2 insertions(+)
create mode 120000 arch/arm64/boot/dts/include/common/cros-ec-keyboard.dtsi
create mode 120000 arch/arm64/boot/dts/include/common/cros-ec-sbs.dtsi
diff --git a/arch/arm64/boot/dts/include/common/cros-ec-keyboard.dtsi b/arch/arm64/boot/dts/include/common/cros-ec-keyboard.dtsi
new file mode 120000
index 000000000000..1c1889f0a791
--- /dev/null
+++ b/arch/arm64/boot/dts/include/common/cros-ec-keyboard.dtsi
@@ -0,0 +1 @@
+../../../../../arm/boot/dts/cros-ec-keyboard.dtsi
\ No newline at end of file
diff --git a/arch/arm64/boot/dts/include/common/cros-ec-sbs.dtsi b/arch/arm64/boot/dts/include/common/cros-ec-sbs.dtsi
new file mode 120000
index 000000000000..3d7ae9c88bcd
--- /dev/null
+++ b/arch/arm64/boot/dts/include/common/cros-ec-sbs.dtsi
@@ -0,0 +1 @@
+../../../../../arm/boot/dts/cros-ec-sbs.dtsi
\ No newline at end of file
--
2.8.0.rc3.226.g39d4020
^ permalink raw reply related
* [PATCH 2/9] arm64: dts: rockchip: add rk3399 thermal_zones phandle
From: Brian Norris @ 2016-12-02 2:27 UTC (permalink / raw)
To: Heiko Stuebner
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Brian Norris, Doug Anderson,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Rob Herring,
Chris Zhong, Stephen Barber,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Caesar Wang
In-Reply-To: <1480645653-36943-1-git-send-email-briannorris-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
We're going to need to amend this table in board files.
Signed-off-by: Brian Norris <briannorris-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
---
arch/arm64/boot/dts/rockchip/rk3399.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index 66a11d1a6eac..28772f6e77f0 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -606,7 +606,7 @@
status = "disabled";
};
- thermal-zones {
+ thermal_zones: thermal-zones {
cpu_thermal: cpu {
polling-delay-passive = <100>;
polling-delay = <1000>;
--
2.8.0.rc3.226.g39d4020
^ permalink raw reply related
* [PATCH 3/9] arm64: dts: rockchip: add rk3399 eDP HPD pinctrl
From: Brian Norris @ 2016-12-02 2:27 UTC (permalink / raw)
To: Heiko Stuebner
Cc: linux-rockchip, linux-kernel, Caesar Wang, Doug Anderson,
devicetree, Rob Herring, Stephen Barber, linux-arm-kernel,
Chris Zhong, Brian Norris
In-Reply-To: <1480645653-36943-1-git-send-email-briannorris@chromium.org>
We haven't enabled eDP support yet, but we might as well describe the
pin now.
Signed-off-by: Brian Norris <briannorris@chromium.org>
---
arch/arm64/boot/dts/rockchip/rk3399.dtsi | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index 28772f6e77f0..4ca8f9a7601c 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -1435,6 +1435,13 @@
};
};
+ edp {
+ edp_hpd: edp-hpd {
+ rockchip,pins =
+ <4 23 RK_FUNC_2 &pcfg_pull_none>;
+ };
+ };
+
gmac {
rgmii_pins: rgmii-pins {
rockchip,pins =
--
2.8.0.rc3.226.g39d4020
^ permalink raw reply related
* [PATCH 4/9] arm64: dts: rockchip: support dwc3 USB for rk3399
From: Brian Norris @ 2016-12-02 2:27 UTC (permalink / raw)
To: Heiko Stuebner
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Brian Norris, Doug Anderson,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Rob Herring,
Chris Zhong, Stephen Barber,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Caesar Wang
In-Reply-To: <1480645653-36943-1-git-send-email-briannorris-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
Add the dwc3 usb needed node information for rk3399.
Signed-off-by: Brian Norris <briannorris-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
---
Somewhat rewritten from Caesar's reposting (v2) of my patch.
Changes:
* Include USB2 PHY (which is now in -next)
* Don't include USB3 PHY, as extcon support is not ready yet
* Drop non-upstream properties
* Fixup whitespace a bit
---
arch/arm64/boot/dts/rockchip/rk3399.dtsi | 60 ++++++++++++++++++++++++++++++++
1 file changed, 60 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index 4ca8f9a7601c..1e97fb8c6415 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -316,6 +316,66 @@
};
};
+ usbdrd3_0: usb@fe800000 {
+ compatible = "rockchip,rk3399-dwc3";
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+ clocks = <&cru SCLK_USB3OTG0_REF>, <&cru SCLK_USB3OTG0_SUSPEND>,
+ <&cru ACLK_USB3OTG0>, <&cru ACLK_USB3_RKSOC_AXI_PERF>,
+ <&cru ACLK_USB3>, <&cru ACLK_USB3_GRF>;
+ clock-names = "clk_usb3otg0_ref", "clk_usb3otg0_suspend",
+ "aclk_usb3otg0", "aclk_usb3_rksoc_axi_perf",
+ "aclk_usb3", "aclk_usb3_grf";
+ resets = <&cru SRST_A_USB3_OTG0>;
+ reset-names = "usb3-otg";
+ status = "disabled";
+
+ usbdrd_dwc3_0: dwc3 {
+ compatible = "snps,dwc3";
+ reg = <0x0 0xfe800000 0x0 0x100000>;
+ interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH 0>;
+ dr_mode = "otg";
+ phys = <&u2phy0_otg>;
+ phy-names = "usb2-phy";
+ snps,dis_enblslpm_quirk;
+ snps,dis-u2-freeclk-exists-quirk;
+ snps,dis_u2_susphy_quirk;
+ snps,dis-del-phy-power-chg-quirk;
+ status = "disabled";
+ };
+ };
+
+ usbdrd3_1: usb@fe900000 {
+ compatible = "rockchip,rk3399-dwc3";
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+ clocks = <&cru SCLK_USB3OTG1_REF>, <&cru SCLK_USB3OTG1_SUSPEND>,
+ <&cru ACLK_USB3OTG1>, <&cru ACLK_USB3_RKSOC_AXI_PERF>,
+ <&cru ACLK_USB3>, <&cru ACLK_USB3_GRF>;
+ clock-names = "clk_usb3otg1_ref", "clk_usb3otg1_suspend",
+ "aclk_usb3otg1", "aclk_usb3_rksoc_axi_perf",
+ "aclk_usb3", "aclk_usb3_grf";
+ resets = <&cru SRST_A_USB3_OTG1>;
+ reset-names = "usb3-otg";
+ status = "disabled";
+
+ usbdrd_dwc3_1: dwc3 {
+ compatible = "snps,dwc3";
+ reg = <0x0 0xfe900000 0x0 0x100000>;
+ interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH 0>;
+ dr_mode = "otg";
+ phys = <&u2phy1_otg>;
+ phy-names = "usb2-phy";
+ snps,dis_enblslpm_quirk;
+ snps,dis-u2-freeclk-exists-quirk;
+ snps,dis_u2_susphy_quirk;
+ snps,dis-del-phy-power-chg-quirk;
+ status = "disabled";
+ };
+ };
+
usb_host0_ehci: usb@fe380000 {
compatible = "generic-ehci";
reg = <0x0 0xfe380000 0x0 0x20000>;
--
2.8.0.rc3.226.g39d4020
^ permalink raw reply related
* [PATCH 5/9] arm64: dts: rockchip: add rk3399 OPPs
From: Brian Norris @ 2016-12-02 2:27 UTC (permalink / raw)
To: Heiko Stuebner
Cc: linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Caesar Wang, Doug Anderson,
devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Stephen Barber,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Chris Zhong,
Brian Norris
In-Reply-To: <1480645653-36943-1-git-send-email-briannorris-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
Used for Gru/Kevin, but they should be conservative enough for all
boards. (And ideally, any board-to-board differences can be represented
via, e.g., describing regulator offsets.)
Signed-off-by: Brian Norris <briannorris-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
---
arch/arm64/boot/dts/rockchip/rk3399-opp.dtsi | 145 +++++++++++++++++++++++++++
arch/arm64/boot/dts/rockchip/rk3399.dtsi | 1 +
2 files changed, 146 insertions(+)
create mode 100644 arch/arm64/boot/dts/rockchip/rk3399-opp.dtsi
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-opp.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-opp.dtsi
new file mode 100644
index 000000000000..e1038cd92ce5
--- /dev/null
+++ b/arch/arm64/boot/dts/rockchip/rk3399-opp.dtsi
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2016 Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Or, alternatively,
+ *
+ * b) Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/ {
+ cluster0_opp: opp-table0 {
+ compatible = "operating-points-v2";
+ opp-shared;
+
+ opp00 {
+ opp-hz = /bits/ 64 <408000000>;
+ opp-microvolt = <800000>;
+ clock-latency-ns = <40000>;
+ };
+ opp01 {
+ opp-hz = /bits/ 64 <600000000>;
+ opp-microvolt = <800000>;
+ };
+ opp02 {
+ opp-hz = /bits/ 64 <816000000>;
+ opp-microvolt = <800000>;
+ };
+ opp03 {
+ opp-hz = /bits/ 64 <1008000000>;
+ opp-microvolt = <875000>;
+ };
+ opp04 {
+ opp-hz = /bits/ 64 <1200000000>;
+ opp-microvolt = <925000>;
+ };
+ opp05 {
+ opp-hz = /bits/ 64 <1416000000>;
+ opp-microvolt = <1050000>;
+ };
+ opp06 {
+ opp-hz = /bits/ 64 <1512000000>;
+ opp-microvolt = <1125000>;
+ };
+ };
+
+ cluster1_opp: opp-table1 {
+ compatible = "operating-points-v2";
+ opp-shared;
+
+ opp00 {
+ opp-hz = /bits/ 64 <408000000>;
+ opp-microvolt = <800000>;
+ clock-latency-ns = <40000>;
+ };
+ opp01 {
+ opp-hz = /bits/ 64 <600000000>;
+ opp-microvolt = <800000>;
+ };
+ opp02 {
+ opp-hz = /bits/ 64 <816000000>;
+ opp-microvolt = <825000>;
+ };
+ opp03 {
+ opp-hz = /bits/ 64 <1008000000>;
+ opp-microvolt = <875000>;
+ };
+ opp04 {
+ opp-hz = /bits/ 64 <1200000000>;
+ opp-microvolt = <950000>;
+ };
+ opp05 {
+ opp-hz = /bits/ 64 <1416000000>;
+ opp-microvolt = <1025000>;
+ };
+ opp06 {
+ opp-hz = /bits/ 64 <1608000000>;
+ opp-microvolt = <1075000>;
+ };
+ opp07 {
+ opp-hz = /bits/ 64 <1800000000>;
+ opp-microvolt = <1150000>;
+ };
+ opp08 {
+ opp-hz = /bits/ 64 <2016000000>;
+ opp-microvolt = <1250000>;
+ };
+ };
+};
+
+&cpu_l0 {
+ operating-points-v2 = <&cluster0_opp>;
+};
+
+&cpu_l1 {
+ operating-points-v2 = <&cluster0_opp>;
+};
+
+&cpu_l2 {
+ operating-points-v2 = <&cluster0_opp>;
+};
+
+&cpu_l3 {
+ operating-points-v2 = <&cluster0_opp>;
+};
+
+&cpu_b0 {
+ operating-points-v2 = <&cluster1_opp>;
+};
+
+&cpu_b1 {
+ operating-points-v2 = <&cluster1_opp>;
+};
diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index 1e97fb8c6415..02392c084607 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -1948,3 +1948,4 @@
};
};
+#include "rk3399-opp.dtsi"
--
2.8.0.rc3.226.g39d4020
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 6/9] dt-bindings: Document rk3399 Gru/Kevin
From: Brian Norris @ 2016-12-02 2:27 UTC (permalink / raw)
To: Heiko Stuebner
Cc: linux-rockchip, linux-kernel, Caesar Wang, Doug Anderson,
devicetree, Rob Herring, Stephen Barber, linux-arm-kernel,
Chris Zhong, Brian Norris
In-Reply-To: <1480645653-36943-1-git-send-email-briannorris@chromium.org>
Gru is a base dev board for a family of devices, including Kevin. Both
utilize Rockchip RK3399, and they share much of their design.
Signed-off-by: Brian Norris <briannorris@chromium.org>
---
Documentation/devicetree/bindings/arm/rockchip.txt | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/Documentation/devicetree/bindings/arm/rockchip.txt b/Documentation/devicetree/bindings/arm/rockchip.txt
index cc4ace6397ab..830e13f5890c 100644
--- a/Documentation/devicetree/bindings/arm/rockchip.txt
+++ b/Documentation/devicetree/bindings/arm/rockchip.txt
@@ -99,6 +99,26 @@ Rockchip platforms device tree bindings
"google,veyron-speedy-rev3", "google,veyron-speedy-rev2",
"google,veyron-speedy", "google,veyron", "rockchip,rk3288";
+- Google Gru (dev-board):
+ Required root node properties:
+ - compatible = "google,gru-rev15", "google,gru-rev14",
+ "google,gru-rev13", "google,gru-rev12",
+ "google,gru-rev11", "google,gru-rev10",
+ "google,gru-rev9", "google,gru-rev8",
+ "google,gru-rev7", "google,gru-rev6",
+ "google,gru-rev5", "google,gru-rev4",
+ "google,gru-rev3", "google,gru-rev2",
+ "google,gru", "rockchip,rk3399";
+
+- Google Kevin:
+ Required root node properties:
+ - compatible = "google,kevin-rev15", "google,kevin-rev14",
+ "google,kevin-rev13", "google,kevin-rev12",
+ "google,kevin-rev11", "google,kevin-rev10",
+ "google,kevin-rev9", "google,kevin-rev8",
+ "google,kevin-rev7", "google,kevin-rev6",
+ "google,kevin", "google,gru", "rockchip,rk3399";
+
- mqmaker MiQi:
Required root node properties:
- compatible = "mqmaker,miqi", "rockchip,rk3288";
--
2.8.0.rc3.226.g39d4020
^ permalink raw reply related
* [PATCH 7/9] arm64: dts: rockchip: add Gru/Kevin DTS
From: Brian Norris @ 2016-12-02 2:27 UTC (permalink / raw)
To: Heiko Stuebner
Cc: linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Caesar Wang, Doug Anderson,
devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Stephen Barber,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Chris Zhong,
Brian Norris
In-Reply-To: <1480645653-36943-1-git-send-email-briannorris-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
Kevin is part of a family of boards called Gru. As best as possible, the
properties shared by the Gru family are placed in rk3399-gru.dtsi, while
Kevin-specific bits are in rk3399-gru-kevin.dts. This does not add full
support for the base Gru board.
Working and tested (to some extent):
* EC support -- including keyboard, battery, PWM, and probably more
* UART / console
* Thermal
* Touchscreen
* Touchpad
* Digitizer (regulator still WIP)
* PCIe / Wifi
* Bluetooth / Webcam
* SD card
* eMMC
* USB2 on TypeC
- This works much of the time, but USB3 devices may or may not detect
properly. Waiting on proper extcon support for USB3 over TypeC.
- Depends on XHCI/DWC3 fixes for ARM64 that still haven't landed
* Backlight
Not working:
* CPUFreq -- relies on special OVP support for our PWM regulator
circuits
* EC / extcon support -- and with it, USB3/TypeC/DP
* DRM -- won't even build on ARM64, so all display, eDP, etc. is not
enabled
Not tested:
* Audio
Signed-off-by: Brian Norris <briannorris-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
---
arch/arm64/boot/dts/rockchip/Makefile | 1 +
arch/arm64/boot/dts/rockchip/rk3399-gru-kevin.dts | 312 +++++++
arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi | 1006 +++++++++++++++++++++
3 files changed, 1319 insertions(+)
create mode 100644 arch/arm64/boot/dts/rockchip/rk3399-gru-kevin.dts
create mode 100644 arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi
diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile
index 3a862894ea44..b82f7b61ab6f 100644
--- a/arch/arm64/boot/dts/rockchip/Makefile
+++ b/arch/arm64/boot/dts/rockchip/Makefile
@@ -4,6 +4,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-orion-r68-meta.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-px5-evb.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-r88.dtb
dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-evb.dtb
+dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-gru-kevin.dtb
always := $(dtb-y)
subdir-y := $(dts-dirs)
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-gru-kevin.dts b/arch/arm64/boot/dts/rockchip/rk3399-gru-kevin.dts
new file mode 100644
index 000000000000..66db785375a8
--- /dev/null
+++ b/arch/arm64/boot/dts/rockchip/rk3399-gru-kevin.dts
@@ -0,0 +1,312 @@
+/*
+ * Google Gru-Kevin Rev 6+ board device tree source
+ *
+ * Copyright 2016 Google, Inc
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ * a) This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Or, alternatively,
+ *
+ * b) Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/dts-v1/;
+#include "rk3399-gru.dtsi"
+#include <include/dt-bindings/input/linux-event-codes.h>
+
+/*
+ * Kevin-specific things
+ *
+ * Things in this section should use names from Kevin schematic since no
+ * equivalent exists in Gru schematic. If referring to signals that exist
+ * in Gru we use the Gru names, though. Confusing enough for you?
+ */
+/ {
+ model = "Google Kevin";
+ compatible = "google,kevin-rev15", "google,kevin-rev14",
+ "google,kevin-rev13", "google,kevin-rev12",
+ "google,kevin-rev11", "google,kevin-rev10",
+ "google,kevin-rev9", "google,kevin-rev8",
+ "google,kevin-rev7", "google,kevin-rev6",
+ "google,kevin", "google,gru", "rockchip,rk3399";
+
+ /* Power tree */
+
+ /* pp3300 children */
+
+ p3_3v_dig: p3-3v-dig {
+ compatible = "regulator-fixed";
+ regulator-name = "p3.3v_dig";
+ pinctrl-names = "default";
+ pinctrl-0 = <&cpu3_pen_pwr_en>;
+
+ enable-active-high;
+ gpio = <&gpio4 30 GPIO_ACTIVE_HIGH>;
+ vin-supply = <&pp3300>;
+ };
+
+ /* END REGULATORS */
+
+ backlight: backlight {
+ compatible = "pwm-backlight";
+ pwms = <&cros_ec_pwm 1>;
+ brightness-levels = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
+ 17 18 19 20 21 22 23 24 25 26 27 28 29 30
+ 31 32 33 34 35 36 37 38 39 40 41 42 43 44
+ 45 46 47 48 49 50 51 52 53 54 55 56 57 58
+ 59 60 61 62 63 64 65 66 67 68 69 70 71 72
+ 73 74 75 76 77 78 79 80 81 82 83 84 85 86
+ 87 88 89 90 91 92 93 94 95 96 97 98 99 100>;
+ default-brightness-level = <51>;
+ enable-gpios = <&gpio1 17 GPIO_ACTIVE_HIGH>;
+ power-supply = <&pp3300_disp>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&bl_en>;
+ pwm-delay-us = <10000>;
+ };
+
+ thermistor_ppvar_bigcpu: thermistor-ppvar-bigcpu {
+ compatible = "murata,ncp15wb473";
+ pullup-uv = <1800000>;
+ pullup-ohm = <25500>;
+ pulldown-ohm = <0>;
+ io-channels = <&saradc 2>;
+ #thermal-sensor-cells = <0>;
+ };
+
+ thermistor_ppvar_litcpu: thermistor-ppvar-litcpu {
+ compatible = "murata,ncp15wb473";
+ pullup-uv = <1800000>;
+ pullup-ohm = <25500>;
+ pulldown-ohm = <0>;
+ io-channels = <&saradc 3>;
+ #thermal-sensor-cells = <0>;
+ };
+};
+
+&gpio_keys {
+ pinctrl-names = "default";
+ pinctrl-0 = <&bt_host_wake_l>, <&cpu1_pen_eject>;
+
+ pen-insert {
+ label = "Pen Insert";
+ /* Insert = low, eject = high */
+ gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
+ linux,code = <SW_PEN_INSERTED>;
+ linux,input-type = <EV_SW>;
+ wakeup-source;
+ };
+};
+
+&thermal_zones {
+ bigcpu_reg_thermal: bigcpu-reg-thermal {
+ polling-delay-passive = <100>; /* milliseconds */
+ polling-delay = <1000>; /* milliseconds */
+ thermal-sensors = <&thermistor_ppvar_bigcpu 0>;
+ sustainable-power = <4000>;
+
+ ppvar_bigcpu_trips: trips {
+ ppvar_bigcpu_on: ppvar-bigcpu-on {
+ temperature = <40000>; /* millicelsius */
+ hysteresis = <2000>; /* millicelsius */
+ type = "passive";
+ };
+
+ ppvar_bigcpu_alert: ppvar-bigcpu-alert {
+ temperature = <50000>; /* millicelsius */
+ hysteresis = <2000>; /* millicelsius */
+ type = "passive";
+ };
+
+ ppvar_bigcpu_crit: ppvar-bigcpu-crit {
+ temperature = <90000>; /* millicelsius */
+ hysteresis = <0>; /* millicelsius */
+ type = "critical";
+ };
+ };
+
+ cooling-maps {
+ map0 {
+ trip = <&ppvar_bigcpu_alert>;
+ cooling-device =
+ <&cpu_l0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ contribution = <4096>;
+ };
+ map1 {
+ trip = <&ppvar_bigcpu_alert>;
+ cooling-device =
+ <&cpu_b0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
+ contribution = <1024>;
+ };
+ };
+ };
+
+ litcpu_reg_thermal: litcpu-reg-thermal {
+ polling-delay-passive = <100>; /* milliseconds */
+ polling-delay = <1000>; /* milliseconds */
+ thermal-sensors = <&thermistor_ppvar_litcpu 0>;
+ sustainable-power = <4000>;
+
+ ppvar_litcpu_trips: trips {
+ ppvar_litcpu_on: ppvar-litcpu-on {
+ temperature = <40000>; /* millicelsius */
+ hysteresis = <2000>; /* millicelsius */
+ type = "passive";
+ };
+
+ ppvar_litcpu_alert: ppvar-litcpu-alert {
+ temperature = <50000>; /* millicelsius */
+ hysteresis = <2000>; /* millicelsius */
+ type = "passive";
+ };
+
+ ppvar_litcpu_crit: ppvar-litcpu-crit {
+ temperature = <90000>; /* millicelsius */
+ hysteresis = <0>; /* millicelsius */
+ type = "critical";
+ };
+ };
+ };
+};
+
+ap_i2c_tpm: &i2c0 {
+ status = "okay";
+
+ clock-frequency = <400000>;
+
+ /* These are relatively safe rise/fall times. */
+ i2c-scl-falling-time-ns = <50>;
+ i2c-scl-rising-time-ns = <300>;
+
+ tpm: tpm@20 {
+ compatible = "infineon,slb9645tt";
+ reg = <0x20>;
+ powered-while-suspended;
+ };
+};
+
+ap_i2c_dig: &i2c2 {
+ status = "okay";
+
+ clock-frequency = <400000>;
+
+ /* These are relatively safe rise/fall times. */
+ i2c-scl-falling-time-ns = <50>;
+ i2c-scl-rising-time-ns = <300>;
+
+ digitizer: digitizer@9 {
+ compatible = "hid-over-i2c";
+ reg = <0x9>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&cpu1_dig_irq_l &cpu1_dig_pdct_l>;
+
+ interrupt-parent = <&gpio2>;
+ interrupts = <4 IRQ_TYPE_LEVEL_LOW>;
+
+ hid-descr-addr = <0x1>;
+ };
+};
+
+/* Adjustments to things in the gru baseboard */
+
+&ap_i2c_tp {
+ trackpad@4a {
+ compatible = "atmel,atmel_mxt_tp";
+ reg = <0x4a>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&trackpad_int_l>;
+ interrupt-parent = <&gpio1>;
+ interrupts = <4 IRQ_TYPE_LEVEL_LOW>;
+ wakeup-source;
+ };
+};
+
+&ap_i2c_ts {
+ touchscreen@4b {
+ compatible = "atmel,atmel_mxt_ts";
+ reg = <0x4b>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&touch_int_l>;
+ interrupt-parent = <&gpio3>;
+ interrupts = <13 IRQ_TYPE_LEVEL_LOW>;
+ };
+};
+
+&saradc {
+ status = "okay";
+ vref-supply = <&pp1800_ap_io>;
+};
+
+&mvl_wifi {
+ marvell,wakeup-pin = <14>; /* GPIO_14 on Marvell */
+};
+
+/* PINCTRL: always below everything else */
+
+&pinctrl {
+ digitizer {
+ /* Has external pullup */
+ cpu1_dig_irq_l: cpu1-dig-irq-l {
+ rockchip,pins = <2 4 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+
+ /* Has external pullup */
+ cpu1_dig_pdct_l: cpu1-dig-pdct-l {
+ rockchip,pins = <2 5 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+ };
+
+ discrete-regulators {
+ cpu3_pen_pwr_en: cpu3-pen-pwr-en {
+ rockchip,pins = <4 30 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+ };
+
+ pen {
+ cpu1_pen_eject: cpu1-pen-eject {
+ rockchip,pins = <0 13 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+ };
+
+ wifi {
+ wlan_host_wake_l: wlan-host-wake-l {
+ rockchip,pins = <0 8 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+ };
+};
+
+/* DON'T PUT ANYTHING BELOW HERE. PUT IT ABOVE PINCTRL */
+/* DON'T PUT ANYTHING BELOW HERE. PUT IT ABOVE PINCTRL */
+/* DON'T PUT ANYTHING BELOW HERE. PUT IT ABOVE PINCTRL */
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi
new file mode 100644
index 000000000000..59b452504106
--- /dev/null
+++ b/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi
@@ -0,0 +1,1006 @@
+/*
+ * Google Gru (and derivatives) board device tree source
+ *
+ * Copyright 2016 Google, Inc
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ * a) This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Or, alternatively,
+ *
+ * b) Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <dt-bindings/input/input.h>
+#include "rk3399.dtsi"
+
+/ {
+ chosen {
+ stdout-path = "serial2:115200n8";
+ };
+
+ /*
+ * Power Tree
+ *
+ * In general an attempt is made to include all rails called out by
+ * the schematic as long as those rails interact in some way with
+ * the AP. AKA:
+ * - Rails that only connect to the EC (or devices that the EC talks to)
+ * are not included.
+ * - Rails _are_ included if the rails go to the AP even if the AP
+ * doesn't currently care about them / they are always on. The idea
+ * here is that it makes it easier to map to the schematic or extend
+ * later.
+ *
+ * If two rails are substantially the same from the AP's point of
+ * view, though, we won't create a full fixed regulator. We'll just
+ * put the child rail as an alias of the parent rail. Sometimes rails
+ * look the same to the AP because one of these is true:
+ * - The EC controls the enable and the EC always enables a rail as
+ * long as the AP is running.
+ * - The rails are actually connected to each other by a jumper and
+ * the distinction is just there to add clarity/flexibility to the
+ * schematic.
+ */
+
+ /* parentless regulators */
+
+ ppvar_sys: ppvar-sys {
+ compatible = "regulator-fixed";
+ regulator-name = "ppvar_sys";
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ /* ppvar_sys children, sorted by name */
+
+ pp900_ap: pp900-ap {
+ compatible = "regulator-fixed";
+ regulator-name = "pp900_ap";
+
+ /* EC turns on w/ pp900_ap_en; always on for AP */
+ regulator-always-on;
+ regulator-boot-on;
+
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <900000>;
+
+ vin-supply = <&ppvar_sys>;
+ };
+
+ pp1200_lpddr: pp1200-lpddr {
+ compatible = "regulator-fixed";
+ regulator-name = "pp1200_lpddr";
+
+ /* EC turns on w/ lpddr_pwr_en; always on for AP */
+ regulator-always-on;
+ regulator-boot-on;
+
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+
+ vin-supply = <&ppvar_sys>;
+ };
+
+ pp1800: pp1800 {
+ compatible = "regulator-fixed";
+ regulator-name = "pp1800";
+
+ /* Always on when ppvar_sys shows power good */
+ regulator-always-on;
+ regulator-boot-on;
+
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ vin-supply = <&ppvar_sys>;
+ };
+
+ pp3000: pp3000 {
+ compatible = "regulator-fixed";
+ regulator-name = "pp3000";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pp3000_en>;
+
+ enable-active-high;
+ gpio = <&gpio0 12 GPIO_ACTIVE_HIGH>;
+
+ regulator-always-on;
+ regulator-boot-on;
+
+ regulator-min-microvolt = <3000000>;
+ regulator-max-microvolt = <3000000>;
+
+ vin-supply = <&ppvar_sys>;
+ };
+
+ pp3300: pp3300 {
+ compatible = "regulator-fixed";
+ regulator-name = "pp3300";
+
+ /* Always on; plain and simple */
+ regulator-always-on;
+ regulator-boot-on;
+
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+
+ vin-supply = <&ppvar_sys>;
+ };
+
+ pp5000: pp5000 {
+ compatible = "regulator-fixed";
+ regulator-name = "pp5000";
+
+ /* EC turns on w/ pp5000_en; always on for AP */
+ regulator-always-on;
+ regulator-boot-on;
+
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5000000>;
+
+ vin-supply = <&ppvar_sys>;
+ };
+
+ /* Schematics call this PPVAR even though it's fixed */
+ ppvar_logic: ppvar-logic {
+ compatible = "regulator-fixed";
+ regulator-name = "ppvar_logic";
+
+ /* EC turns on w/ ppvar_logic_en; always on for AP */
+ regulator-always-on;
+ regulator-boot-on;
+
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <900000>;
+
+ vin-supply = <&ppvar_sys>;
+ };
+
+ /* pp900_ap aliases; these are always on for AP so just use alias */
+
+ /* EC turns on w/ pp900_ddrpll_en */
+ pp900_ddrpll: pp900-ap {
+ };
+
+ /* EC turns on w/ pp900_pcie_en */
+ pp900_pcie: pp900-ap {
+ };
+
+ /* EC turns on w/ pp900_pll_en */
+ pp900_pll: pp900-ap {
+ };
+
+ /* EC turns on w/ pp900_pmu_en */
+ pp900_pmu: pp900-ap {
+ };
+
+ /* EC turns on w/ pp900_usb_en */
+ pp900_usb: pp900-ap {
+ };
+
+ /* pp1800 aliases; these are always on for AP so just use alias */
+
+ /* EC turns on w/ pp1800_s0_en_l */
+ pp1800_ap_io: pp1800_emmc: pp1800_nfc: pp1800_s0: pp1800 {
+ };
+
+ /* EC turns on w/ pp1800_avdd_en_l */
+ pp1800_avdd: pp1800 {
+ };
+
+ /* EC turns on w/ pp1800_lid_en_l */
+ pp1800_lid: pp1800_mic: pp1800 {
+ };
+
+ /* EC turns on w/ lpddr_pwr_en */
+ pp1800_lpddr: pp1800 {
+ };
+
+ /* EC turns on w/ pp1800_pmu_en_l */
+ pp1800_pmu: pp1800 {
+ };
+
+ /* EC turns on w/ pp1800_usb_en_l */
+ pp1800_usb: pp1800 {
+ };
+
+ /* pp1800 children */
+
+ pp1500_ap_io: pp1500-ap-io {
+ compatible = "regulator-fixed";
+ regulator-name = "pp1500_ap_io";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pp1500_en>;
+
+ enable-active-high;
+ gpio = <&gpio0 10 GPIO_ACTIVE_HIGH>;
+
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <1500000>;
+ regulator-max-microvolt = <1500000>;
+
+ vin-supply = <&pp1800>;
+ };
+
+ pp1800_audio: pp1800-audio {
+ compatible = "regulator-fixed";
+ regulator-name = "pp1800_audio";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pp1800_audio_en>;
+
+ regulator-always-on;
+ regulator-boot-on;
+
+ enable-active-high;
+ gpio = <&gpio0 2 GPIO_ACTIVE_HIGH>;
+
+ vin-supply = <&pp1800>;
+ };
+
+ pp1800_pcie: pp1800-pcie {
+ compatible = "regulator-fixed";
+ regulator-name = "pp1800_pcie";
+ pinctrl-names = "default";
+ pinctrl-0 = <&wlan_module_pd_l>;
+
+ /*
+ * Need to wait 1ms + ramp-up time before we can power on WiFi.
+ * This has been approximated as 8ms total.
+ */
+ regulator-enable-ramp-delay = <8000>;
+
+ enable-active-high;
+ gpio = <&gpio0 4 GPIO_ACTIVE_HIGH>;
+
+ vin-supply = <&pp1800>;
+ };
+
+ /*
+ * See http://crosbug.com/p/56069/
+ *
+ * This is a bit of a hack. The WiFi module should be reset at least
+ * 1ms after its regulators have ramped up (max rampup time is ~7ms).
+ * With some stretching of the imagination, we can call the 1.8V
+ * regulator a supply.
+ */
+ wlan_pd_n: wlan-pd-n {
+ compatible = "regulator-fixed";
+ regulator-name = "wlan_pd_n";
+
+ /* Note the wlan_module_reset_l pinctrl */
+ enable-active-high;
+ gpio = <&gpio1 11 GPIO_ACTIVE_HIGH>;
+
+ vin-supply = <&pp1800_pcie>;
+ };
+
+ /* pp3000 aliases; these are always on for AP so just use alias */
+
+ /* Always on; plain and simple */
+ pp3000_ap: pp3000_emmc: pp3000 {
+ };
+
+ /* pp3000 children */
+
+ pp3000_sd_slot: pp3000-sd-slot {
+ compatible = "regulator-fixed";
+ regulator-name = "pp3000_sd_slot";
+ pinctrl-names = "default";
+ pinctrl-0 = <&sd_slot_pwr_en>;
+
+ enable-active-high;
+ gpio = <&gpio4 29 GPIO_ACTIVE_HIGH>;
+
+ vin-supply = <&pp3000>;
+ };
+
+ /*
+ * Technically, this is a small abuse of 'regulator-gpio'; this
+ * regulator is a mux between pp1800 and pp3300. pp1800 and pp3300 are
+ * always on though, so it is sufficient to simply control the mux
+ * here.
+ */
+ ppvar_sd_card_io: ppvar-sd-card-io {
+ compatible = "regulator-gpio";
+ regulator-name = "ppvar_sd_card_io";
+ pinctrl-names = "default";
+ pinctrl-0 = <&sd_io_pwr_en &sd_pwr_1800_sel>;
+
+ enable-active-high;
+ enable-gpio = <&gpio2 2 GPIO_ACTIVE_HIGH>;
+ gpios = <&gpio2 28 GPIO_ACTIVE_HIGH>;
+ states = <1800000 0x1
+ 3000000 0x0>;
+
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3000000>;
+ };
+
+ /* pp3300 aliases; these are always on for AP so just use alias */
+
+ /* EC turns on w/ pp3300_trackpad_en_l */
+ pp3300_trackpad: pp3300-trackpad {
+ };
+
+ /* EC turns on w/ pp3300_usb_en_l */
+ pp3300_usb: pp3300 {
+ };
+
+ /* pp3300 children */
+
+ pp3300_disp: pp3300-disp {
+ compatible = "regulator-fixed";
+ regulator-name = "pp3300_disp";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pp3300_disp_en>;
+
+ enable-active-high;
+
+ gpio = <&gpio4 27 GPIO_ACTIVE_HIGH>;
+
+ startup-delay-us = <2000>;
+ vin-supply = <&pp3300>;
+ };
+
+ pp3300_wifi_bt: pp3300-wifi-bt {
+ compatible = "regulator-fixed";
+ regulator-name = "pp3300_wifi_bt";
+ /* NOTE: wlan_module_pd_l pinctrl in pp1800_pcie */
+
+ enable-active-high;
+
+ /* NOTE: this GPIO also used in pp1800_pcie */
+ gpio = <&gpio0 4 GPIO_ACTIVE_HIGH>;
+
+ vin-supply = <&pp3300>;
+ };
+
+ /* pp5000 aliases; these are always on for AP so just use alias */
+
+ /* EC turns on w/ usb_a_en */
+ pp5000_usb_a_vbus: pp5000 {
+ };
+
+ /* END REGULATORS */
+
+ io-domains {
+ compatible = "rockchip,rk3399-io-voltage-domain";
+ rockchip,grf = <&grf>;
+
+ bt656-supply = <&pp1800_ap_io>; /* APIO2_VDD; 2a 2b */
+ audio-supply = <&pp1800_audio>; /* APIO5_VDD; 3d 4a */
+ sdmmc-supply = <&ppvar_sd_card_io>; /* SDMMC0_VDD; 4b */
+ gpio1830-supply = <&pp3000_ap>; /* APIO4_VDD; 4c 4d */
+ };
+
+ max98357a: max98357a {
+ #sound-dai-cells = <0>;
+ status = "okay";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&sdmode_en>;
+
+ compatible = "maxim,max98357a";
+ sdmode-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>;
+ sdmode-delay = <2>;
+ };
+
+ pmu-io-domains {
+ compatible = "rockchip,rk3399-pmu-io-voltage-domain";
+ rockchip,grf = <&pmugrf>;
+
+ pmu1830-supply = <&pp1800_pmu>; /* PMUIO2_VDD */
+ };
+
+ sound {
+ compatible = "rockchip,rk3399-gru-sound";
+ rockchip,cpu = <&i2s0 &i2s2>;
+ rockchip,codec = <&max98357a &headsetcodec &codec>;
+ };
+
+ gpio_keys: gpio-keys {
+ compatible = "gpio-keys";
+ pinctrl-names = "default";
+ pinctrl-0 = <&bt_host_wake_l>;
+
+ wake-on-bt {
+ label = "Wake-on-Bluetooth";
+ gpios = <&gpio0 3 GPIO_ACTIVE_LOW>;
+ linux,code = <KEY_WAKEUP>;
+ wakeup-source;
+ };
+ };
+};
+
+&cru {
+ assigned-clocks =
+ <&cru PLL_GPLL>, <&cru PLL_CPLL>,
+ <&cru PLL_NPLL>,
+ <&cru ACLK_PERIHP>, <&cru HCLK_PERIHP>,
+ <&cru PCLK_PERIHP>,
+ <&cru ACLK_PERILP0>, <&cru HCLK_PERILP0>,
+ <&cru PCLK_PERILP0>, <&cru ACLK_CCI>,
+ <&cru HCLK_PERILP1>, <&cru PCLK_PERILP1>;
+ assigned-clock-rates =
+ <594000000>, <800000000>,
+ <1000000000>,
+ <150000000>, <75000000>,
+ <37500000>,
+ <100000000>, <100000000>,
+ <50000000>, <800000000>,
+ <100000000>, <50000000>;
+};
+
+&emmc_phy {
+ status = "okay";
+};
+
+ap_i2c_mic: &i2c1 {
+ status = "okay";
+
+ clock-frequency = <400000>;
+
+ /* These are relatively safe rise/fall times */
+ i2c-scl-falling-time-ns = <50>;
+ i2c-scl-rising-time-ns = <300>;
+
+ headsetcodec: rt5514@57 {
+ compatible = "realtek,rt5514";
+ reg = <0x57>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&mic_int>;
+
+ interrupt-parent = <&gpio1>;
+ interrupts = <13 IRQ_TYPE_LEVEL_HIGH>;
+
+ realtek,dmic-init-delay = <20>;
+ wakeup-source;
+ };
+};
+
+ap_i2c_ts: &i2c3 {
+ status = "okay";
+
+ clock-frequency = <400000>;
+
+ /* These are relatively safe rise/fall times */
+ i2c-scl-falling-time-ns = <50>;
+ i2c-scl-rising-time-ns = <300>;
+};
+
+ap_i2c_tp: &i2c5 {
+ status = "okay";
+
+ /*
+ * Note strange pullup enable. Apparently this avoids leakage but
+ * still allows us to get nice 4.7K pullups for high speed i2c
+ * transfers. Basically we want the pullup on whenever the ap is
+ * alive, so the "en" pin just gets set to output high.
+ */
+ pinctrl-0 = <&i2c5_xfer &ap_i2c_tp_pu_en>;
+
+ clock-frequency = <400000>;
+
+ /* These are relatively safe rise/fall times */
+ i2c-scl-falling-time-ns = <50>;
+ i2c-scl-rising-time-ns = <300>;
+};
+
+ap_i2c_audio: &i2c8 {
+ status = "okay";
+
+ clock-frequency = <400000>;
+
+ /* These are relatively safe rise/fall times */
+ i2c-scl-falling-time-ns = <50>;
+ i2c-scl-rising-time-ns = <300>;
+
+ codec: da7219@1a {
+ compatible = "dlg,da7219";
+ reg = <0x1a>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&headset_int_l>;
+
+ interrupt-parent = <&gpio1>;
+ interrupts = <23 IRQ_TYPE_LEVEL_LOW>;
+
+ VDD-supply = <&pp1800>;
+ VDDMIC-supply = <&pp3300>;
+ VDDIO-supply = <&pp1800>;
+
+ clocks = <&cru SCLK_I2S_8CH_OUT>;
+ clock-names = "mclk";
+
+ dlg,ldo-lvl = <1200>;
+ dlg,micbias-lvl = <2600>;
+ dlg,mic-amp-in-sel = "diff";
+
+ da7219_aad {
+ dlg,btn-cfg = <50>;
+ dlg,mic-det-thr = <500>;
+ dlg,jack-ins-deb = <20>;
+ dlg,jack-det-rate = "32ms_64ms";
+ dlg,jack-rem-deb = <1>;
+
+ dlg,a-d-btn-thr = <0xa>;
+ dlg,d-b-btn-thr = <0x16>;
+ dlg,b-c-btn-thr = <0x21>;
+ dlg,c-mic-btn-thr = <0x3E>;
+
+ dlg,btn-avg = <4>;
+ dlg,adc-1bit-rpt = <1>;
+ };
+ };
+};
+
+&i2s0 {
+ status = "okay";
+};
+
+&i2s2 {
+ status = "okay";
+};
+
+&pcie0 {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pcie_clkreqn_cpm>, <&wifi_perst_l>;
+ ep-gpios = <&gpio2 27 GPIO_ACTIVE_HIGH>;
+
+ max-link-speed = <1>;
+
+ vpcie3v3-supply = <&pp3300_wifi_bt>;
+ vpcie1v8-supply = <&wlan_pd_n>; /* HACK: see &wlan_pd_n */
+ vpcie0v9-supply = <&pp900_pcie>;
+
+ pci_rootport: pcie@0,0 {
+ reg = <0x83000000 0x0 0x00000000 0x0 0x00000000>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+
+ mvl_wifi: wifi@0,0 {
+ compatible = "pci1b4b,2b42";
+ reg = <0x83010000 0x0 0x00000000 0x0 0x00100000
+ 0x83010000 0x0 0x00100000 0x0 0x00100000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&wlan_host_wake_l>;
+ interrupt-parent = <&gpio0>;
+ interrupts = <8 IRQ_TYPE_LEVEL_LOW>;
+ wakeup-source;
+ };
+ };
+};
+
+&pcie_phy {
+ status = "okay";
+};
+
+&pwm0 {
+ status = "okay";
+};
+
+&pwm1 {
+ status = "okay";
+};
+
+&pwm2 {
+ status = "okay";
+};
+
+&pwm3 {
+ status = "okay";
+};
+
+&sdhci {
+ /*
+ * Signal integrity isn't great at 200 MHz and 150 MHz (DDR) gives the
+ * same (or nearly the same) performance for all eMMC that are intended
+ * to be used.
+ */
+ assigned-clock-rates = <150000000>;
+
+ bus-width = <8>;
+ mmc-hs400-1_8v;
+ mmc-hs400-enhanced-strobe;
+ non-removable;
+ status = "okay";
+};
+
+&sdmmc {
+ status = "okay";
+
+ /*
+ * Note: configure "sdmmc_cd" as card detect even though it's actually
+ * hooked to ground. Because we specified "cd-gpios" below dw_mmc
+ * should be ignoring card detect anyway. Specifying the pin as
+ * sdmmc_cd means that even if you've got GRF_SOC_CON7[12] (force_jtag)
+ * turned on that the system will still make sure the port is
+ * configured as SDMMC and not JTAG.
+ */
+ pinctrl-names = "default";
+ pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_cd &sdmmc_cd_gpio
+ &sdmmc_bus4>;
+
+ bus-width = <4>;
+ cd-gpios = <&gpio4 24 GPIO_ACTIVE_LOW>;
+ disable-wp;
+
+ cap-mmc-highspeed;
+ cap-sd-highspeed;
+ sd-uhs-sdr12;
+ sd-uhs-sdr25;
+ sd-uhs-sdr50;
+ sd-uhs-sdr104;
+
+ vmmc-supply = <&pp3000_sd_slot>;
+ vqmmc-supply = <&ppvar_sd_card_io>;
+};
+
+&spi1 {
+ status = "okay";
+
+ spiflash@0 {
+ compatible = "jedec,spi-nor";
+
+ /* May run faster once verified. */
+ spi-max-frequency = <10000000>;
+ reg = <0>;
+ };
+};
+
+&spi2 {
+ status = "okay";
+
+ wacky_spi_audio: spi2@0 {
+ compatible = "realtek,rt5514";
+ reg = <0>;
+
+ /* May run faster once verified. */
+ spi-max-frequency = <10000000>;
+ };
+};
+
+&spi5 {
+ status = "okay";
+
+ cros_ec: ec@0 {
+ compatible = "google,cros-ec-spi";
+ reg = <0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&ec_ap_int_l>;
+
+ google,cros-ec-spi-pre-delay = <30>;
+ interrupt-parent = <&gpio0>;
+ interrupts = <1 IRQ_TYPE_LEVEL_LOW>;
+ spi-max-frequency = <3000000>;
+
+ i2c_tunnel: i2c-tunnel {
+ compatible = "google,cros-ec-i2c-tunnel";
+ google,remote-bus = <4>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ cros_ec_pwm: ec-pwm {
+ compatible = "google,cros-ec-pwm";
+ #pwm-cells = <1>;
+ };
+ };
+};
+
+&tsadc {
+ status = "okay";
+
+ rockchip,hw-tshut-mode = <1>; /* tshut mode 0:CRU 1:GPIO */
+ rockchip,hw-tshut-polarity = <1>; /* tshut polarity 0:LOW 1:HIGH */
+};
+
+&u2phy0 {
+ status = "okay";
+};
+
+&u2phy1 {
+ status = "okay";
+};
+
+&u2phy0_host {
+ status = "okay";
+};
+
+&u2phy1_host {
+ status = "okay";
+};
+
+&u2phy0_otg {
+ status = "okay";
+};
+
+&u2phy1_otg {
+ status = "okay";
+};
+
+&uart2 {
+ status = "okay";
+};
+
+&usb_host0_ehci {
+ status = "okay";
+};
+
+&usb_host0_ohci {
+ status = "okay";
+};
+
+&usb_host1_ehci {
+ status = "okay";
+};
+
+&usb_host1_ohci {
+ status = "okay";
+};
+
+&usbdrd3_0 {
+ status = "okay";
+};
+
+&usbdrd_dwc3_0 {
+ status = "okay";
+ dr_mode = "host";
+};
+
+&usbdrd3_1 {
+ status = "okay";
+};
+
+&usbdrd_dwc3_1 {
+ status = "okay";
+ dr_mode = "host";
+};
+
+#include "common/cros-ec-keyboard.dtsi"
+#include "common/cros-ec-sbs.dtsi"
+
+/* PINCTRL: always below everything else */
+
+&pinctrl {
+ /*
+ * pinctrl settings for pins that have no real owners.
+ *
+ * At the moment settings are identical for S0 and S3, but if we later
+ * need to configure things differently for S3 we'll adjust here.
+ */
+ pinctrl-names = "default";
+ pinctrl-0 = <
+ &ap_pwroff /* AP will auto-assert this when in S3 */
+ &clk_32k /* This pin is always 32k on gru boards */
+
+ /*
+ * We want this driven low ASAP; firmware should help us, but
+ * we can help ourselves too.
+ */
+ &wlan_module_reset_l
+ >;
+
+ pcfg_output_low: pcfg-output-low {
+ output-low;
+ };
+
+ pcfg_output_high: pcfg-output-high {
+ output-high;
+ };
+
+ pcfg_pull_none_8ma: pcfg-pull-none-8ma {
+ bias-disable;
+ drive-strength = <8>;
+ };
+
+ cros-ec {
+ ec_ap_int_l: ec-ap-int-l {
+ rockchip,pins = <RK_GPIO0 1 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+ };
+
+ discrete-regulators {
+ pp1500_en: pp1500-en {
+ rockchip,pins = <RK_GPIO0 10 RK_FUNC_GPIO
+ &pcfg_pull_none>;
+ };
+
+ pp1800_audio_en: pp1800-audio-en {
+ rockchip,pins = <RK_GPIO0 2 RK_FUNC_GPIO
+ &pcfg_pull_down>;
+ };
+
+ pp3300_disp_en: pp3300-disp-en {
+ rockchip,pins = <RK_GPIO4 27 RK_FUNC_GPIO
+ &pcfg_pull_none>;
+ };
+
+ pp3000_en: pp3000-en {
+ rockchip,pins = <RK_GPIO0 12 RK_FUNC_GPIO
+ &pcfg_pull_none>;
+ };
+
+ sd_io_pwr_en: sd-io-pwr-en {
+ rockchip,pins = <RK_GPIO2 2 RK_FUNC_GPIO
+ &pcfg_pull_none>;
+ };
+
+ sd_pwr_1800_sel: sd-pwr-1800-sel {
+ rockchip,pins = <RK_GPIO2 28 RK_FUNC_GPIO
+ &pcfg_pull_none>;
+ };
+
+ sd_slot_pwr_en: sd-slot-pwr-en {
+ rockchip,pins = <RK_GPIO4 29 RK_FUNC_GPIO
+ &pcfg_pull_none>;
+ };
+
+ wlan_module_pd_l: wlan-module-pd-l {
+ rockchip,pins = <RK_GPIO0 4 RK_FUNC_GPIO
+ &pcfg_pull_down>;
+ };
+ };
+
+ codec {
+ /* Has external pullup */
+ headset_int_l: headset-int-l {
+ rockchip,pins = <1 23 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+
+ mic_int: mic-int {
+ rockchip,pins = <1 13 RK_FUNC_GPIO &pcfg_pull_down>;
+ };
+ };
+
+ max98357a {
+ sdmode_en: sdmode-en {
+ rockchip,pins = <1 2 RK_FUNC_GPIO &pcfg_pull_down>;
+ };
+ };
+
+ sdmmc {
+ /*
+ * We run sdmmc at max speed; bump up drive strength.
+ * We also have external pulls, so disable the internal ones.
+ */
+ sdmmc_bus4: sdmmc-bus4 {
+ rockchip,pins =
+ <4 8 RK_FUNC_1 &pcfg_pull_none_8ma>,
+ <4 9 RK_FUNC_1 &pcfg_pull_none_8ma>,
+ <4 10 RK_FUNC_1 &pcfg_pull_none_8ma>,
+ <4 11 RK_FUNC_1 &pcfg_pull_none_8ma>;
+ };
+
+ sdmmc_clk: sdmmc-clk {
+ rockchip,pins =
+ <4 12 RK_FUNC_1 &pcfg_pull_none_8ma>;
+ };
+
+ sdmmc_cmd: sdmmc-cmd {
+ rockchip,pins =
+ <4 13 RK_FUNC_1 &pcfg_pull_none_8ma>;
+ };
+
+ /*
+ * In our case the official card detect is hooked to ground
+ * to avoid getting access to JTAG just by sticking something
+ * in the SD card slot (see the force_jtag bit in the TRM).
+ *
+ * We still configure it as card detect because it doesn't
+ * hurt and dw_mmc will ignore it. We make sure to disable
+ * the pull though so we don't burn needless power.
+ */
+ sdmmc_cd: sdmcc-cd {
+ rockchip,pins =
+ <0 7 RK_FUNC_1 &pcfg_pull_none>;
+ };
+
+ /* This is where we actually hook up CD; has external pull */
+ sdmmc_cd_gpio: sdmmc-cd-gpio {
+ rockchip,pins = <4 24 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+ };
+
+ trackpad {
+ ap_i2c_tp_pu_en: ap-i2c-tp-pu-en {
+ rockchip,pins = <3 12 RK_FUNC_GPIO &pcfg_output_high>;
+ };
+
+ trackpad_int_l: trackpad-int-l {
+ rockchip,pins = <1 4 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+ };
+
+ touchscreen {
+ touch_int_l: touch-int-l {
+ rockchip,pins = <3 13 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+
+ touch_reset_l: touch-reset-l {
+ rockchip,pins = <4 26 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+ };
+
+ wifi {
+ wifi_perst_l: wifi-perst-l {
+ rockchip,pins = <2 27 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+
+ wlan_module_reset_l: wlan-module-reset-l {
+ /*
+ * We want this driven low ASAP (As {Soon,Strongly} As
+ * Possible), to avoid leakage through the powered-down
+ * WiFi.
+ */
+ rockchip,pins = <1 11 RK_FUNC_GPIO &pcfg_output_low>;
+ };
+
+ bt_host_wake_l: bt-host-wake-l {
+ /* Kevin has an external pull up, but Gru does not */
+ rockchip,pins = <0 3 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+ };
+
+ backlight-enable {
+ bl_en: bl-en {
+ rockchip,pins = <1 17 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+ };
+
+ write-protect {
+ ap_fw_wp: ap-fw-wp {
+ rockchip,pins = <1 18 RK_FUNC_GPIO &pcfg_pull_up>;
+ };
+ };
+
+ pcie {
+ pcie_clkreqn_cpm: pci-clkreqn-cpm {
+ /*
+ * Since our pcie doesn't support ClockPM(CPM), we want
+ * to hack this as gpio, so the EP could be able to
+ * de-assert it along and make ClockPM(CPM) work.
+ */
+ rockchip,pins = <2 26 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+ };
+};
+
+/* DON'T PUT ANYTHING BELOW HERE. PUT IT ABOVE PINCTRL */
+/* DON'T PUT ANYTHING BELOW HERE. PUT IT ABOVE PINCTRL */
+/* DON'T PUT ANYTHING BELOW HERE. PUT IT ABOVE PINCTRL */
+
--
2.8.0.rc3.226.g39d4020
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 8/9] arm64: dts: rockchip: partially describe PWM regulators for Gru
From: Brian Norris @ 2016-12-02 2:27 UTC (permalink / raw)
To: Heiko Stuebner
Cc: linux-rockchip, linux-kernel, Caesar Wang, Doug Anderson,
devicetree, Rob Herring, Stephen Barber, linux-arm-kernel,
Chris Zhong, Brian Norris
In-Reply-To: <1480645653-36943-1-git-send-email-briannorris@chromium.org>
We need to add regulators to the CPU nodes, so cpufreq doesn't think it
can crank up the clock speed without changing the voltage. However, we
don't yet have the DT bindings to fully describe the Over Voltage
Protection (OVP) circuits on these boards. Without that description, we
might end up changing the voltage too much, too fast.
Add the pwm-regulator descriptions and associate the CPU OPPs, but leave
them disabled.
Signed-off-by: Brian Norris <briannorris@chromium.org>
---
arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi | 146 +++++++++++++++++++++++++++
1 file changed, 146 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi
index 59b452504106..90adfb5cba38 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi
@@ -172,6 +172,98 @@
vin-supply = <&ppvar_sys>;
};
+ ppvar_bigcpu: ppvar-bigcpu {
+ compatible = "pwm-regulator";
+ regulator-name = "ppvar_bigcpu";
+ /*
+ * OVP circuit requires special handling which is not yet
+ * represented. Keep disabled for now.
+ */
+ status = "disabled";
+
+ pwms = <&pwm1 0 3337 0>;
+
+ /* EC turns on w/ ap_core_en; always on for AP */
+ regulator-always-on;
+ regulator-boot-on;
+
+ regulator-min-microvolt = <798674>;
+ regulator-max-microvolt = <1302172>;
+
+ pwm-supply = <&ppvar_sys>;
+ pwm-dutycycle-range = <100 0>;
+ pwm-dutycycle-unit = <100>;
+ };
+
+ ppvar_litcpu: ppvar-litcpu {
+ compatible = "pwm-regulator";
+ regulator-name = "ppvar_litcpu";
+ /*
+ * OVP circuit requires special handling which is not yet
+ * represented. Keep disabled for now.
+ */
+ status = "disabled";
+
+ pwms = <&pwm2 0 3337 0>;
+
+ /* EC turns on w/ ap_core_en; always on for AP */
+ regulator-always-on;
+ regulator-boot-on;
+
+ regulator-min-microvolt = <799065>;
+ regulator-max-microvolt = <1303738>;
+
+ pwm-supply = <&ppvar_sys>;
+ pwm-dutycycle-range = <100 0>;
+ pwm-dutycycle-unit = <100>;
+ };
+
+ ppvar_gpu: ppvar-gpu {
+ compatible = "pwm-regulator";
+ regulator-name = "ppvar_gpu";
+ /*
+ * OVP circuit requires special handling which is not yet
+ * represented. Keep disabled for now.
+ */
+ status = "disabled";
+
+ pwms = <&pwm0 0 3337 0>;
+
+ /* EC turns on w/ ap_core_en; always on for AP */
+ regulator-always-on;
+ regulator-boot-on;
+
+ regulator-min-microvolt = <785782>;
+ regulator-max-microvolt = <1217729>;
+
+ pwm-supply = <&ppvar_sys>;
+ pwm-dutycycle-range = <100 0>;
+ pwm-dutycycle-unit = <100>;
+ };
+
+ ppvar_centerlogic: ppvar-centerlogic {
+ compatible = "pwm-regulator";
+ regulator-name = "ppvar_centerlogic";
+ /*
+ * OVP circuit requires special handling which is not yet
+ * represented. Keep disabled for now.
+ */
+ status = "disabled";
+
+ pwms = <&pwm3 0 3337 0>;
+
+ /* EC turns on w/ ppvar_centerlogic_en; always on for AP */
+ regulator-always-on;
+ regulator-boot-on;
+
+ regulator-min-microvolt = <800069>;
+ regulator-max-microvolt = <1049692>;
+
+ pwm-supply = <&ppvar_sys>;
+ pwm-dutycycle-range = <100 0>;
+ pwm-dutycycle-unit = <100>;
+ };
+
/* Schematics call this PPVAR even though it's fixed */
ppvar_logic: ppvar-logic {
compatible = "regulator-fixed";
@@ -444,6 +536,60 @@
};
};
+/*
+ * Set some suspend operating points to avoid OVP in suspend
+ *
+ * When we go into S3 ARM Trusted Firmware will transition our PWM regulators
+ * from wherever they're at back to the "default" operating point (whatever
+ * voltage we get when we set the PWM pins to "input").
+ *
+ * This quick transition under light load has the possibility to trigger the
+ * regulator "over voltage protection" (OVP).
+ *
+ * To make extra certain that we don't hit this OVP at suspend time, we'll
+ * transition to a voltage that's much closer to the default (~1.0 V) so that
+ * there will not be a big jump. Technically we only need to get within 200 mV
+ * of the default voltage, but the speed here should be fast enough and we need
+ * suspend/resume to be rock solid.
+ */
+
+&cluster0_opp {
+ opp05 {
+ opp-suspend;
+ };
+};
+
+&cluster1_opp {
+ opp06 {
+ opp-suspend;
+ };
+};
+
+&cpu_l0 {
+ cpu-supply = <&ppvar_litcpu>;
+};
+
+&cpu_l1 {
+ cpu-supply = <&ppvar_litcpu>;
+};
+
+&cpu_l2 {
+ cpu-supply = <&ppvar_litcpu>;
+};
+
+&cpu_l3 {
+ cpu-supply = <&ppvar_litcpu>;
+};
+
+&cpu_b0 {
+ cpu-supply = <&ppvar_bigcpu>;
+};
+
+&cpu_b1 {
+ cpu-supply = <&ppvar_bigcpu>;
+};
+
+
&cru {
assigned-clocks =
<&cru PLL_GPLL>, <&cru PLL_CPLL>,
--
2.8.0.rc3.226.g39d4020
^ permalink raw reply related
* [PATCH 9/9] arm64: dts: rockchip: add regulator info for Kevin digitizer
From: Brian Norris @ 2016-12-02 2:27 UTC (permalink / raw)
To: Heiko Stuebner
Cc: linux-rockchip, linux-kernel, Caesar Wang, Doug Anderson,
devicetree, Rob Herring, Stephen Barber, linux-arm-kernel,
Chris Zhong, Brian Norris
In-Reply-To: <1480645653-36943-1-git-send-email-briannorris@chromium.org>
We need to enable this regulator before the digitizer can be used. Wacom
recommended waiting for 100 ms before talking to the HID.
Signed-off-by: Brian Norris <briannorris@chromium.org>
---
Uses WIP bindings:
[PATCH v3 1/2] devicetree: i2c-hid: Add regulator support
https://patchwork.kernel.org/patch/9457493/
---
arch/arm64/boot/dts/rockchip/rk3399-gru-kevin.dts | 3 +++
1 file changed, 3 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-gru-kevin.dts b/arch/arm64/boot/dts/rockchip/rk3399-gru-kevin.dts
index 66db785375a8..d260079de2ab 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-gru-kevin.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-gru-kevin.dts
@@ -232,6 +232,9 @@ ap_i2c_dig: &i2c2 {
pinctrl-names = "default";
pinctrl-0 = <&cpu1_dig_irq_l &cpu1_dig_pdct_l>;
+ vdd-supply = <&p3_3v_dig>;
+ init-delay-ms = <100>;
+
interrupt-parent = <&gpio2>;
interrupts = <4 IRQ_TYPE_LEVEL_LOW>;
--
2.8.0.rc3.226.g39d4020
^ permalink raw reply related
* Re: arm64: dts: zx: support cpu-freq for zx296718
From: Shawn Guo @ 2016-12-02 2:36 UTC (permalink / raw)
To: Baoyou Xie
Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
catalin.marinas-5wv7dgnIgG8, will.deacon-5wv7dgnIgG8,
jun.nie-QSEj5FYQhm4dnm+yROfE0A, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
xie.baoyou-Th6q7B73Y6EnDS1+zs4M5A,
chen.chaokai-Th6q7B73Y6EnDS1+zs4M5A,
wang.qiang01-Th6q7B73Y6EnDS1+zs4M5A, Viresh Kumar
In-Reply-To: <1480594135-27273-1-git-send-email-baoyou.xie-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
+ Viresh, the author of the bindings.
On Thu, Dec 01, 2016 at 08:08:55PM +0800, Baoyou Xie wrote:
> This patch adds the CPU clock phandle in CPU's node
> and uses operating-points-v2 to register operating points.
>
> So it can be used by cpufreq-dt driver.
>
> Signed-off-by: Baoyou Xie <baoyou.xie-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Just out of curiosity, why is there no '[PATCH]' prefix in the subject?
> ---
> arch/arm64/boot/dts/zte/zx296718.dtsi | 37 +++++++++++++++++++++++++++++++++++
> 1 file changed, 37 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/zte/zx296718.dtsi b/arch/arm64/boot/dts/zte/zx296718.dtsi
> index 7a1aed7..16f7d5e 100644
> --- a/arch/arm64/boot/dts/zte/zx296718.dtsi
> +++ b/arch/arm64/boot/dts/zte/zx296718.dtsi
> @@ -44,6 +44,7 @@
> #include <dt-bindings/input/input.h>
> #include <dt-bindings/interrupt-controller/arm-gic.h>
> #include <dt-bindings/gpio/gpio.h>
> +#include <dt-bindings/clock/zx296718-clock.h>
>
> / {
> compatible = "zte,zx296718";
> @@ -81,6 +82,8 @@
> compatible = "arm,cortex-a53","arm,armv8";
> reg = <0x0 0x0>;
> enable-method = "psci";
> + clocks = <&topcrm A53_GATE>;
> + operating-points-v2 = <&cluster0_opp>;
> };
>
> cpu1: cpu@1 {
> @@ -88,6 +91,7 @@
> compatible = "arm,cortex-a53","arm,armv8";
> reg = <0x0 0x1>;
> enable-method = "psci";
> + operating-points-v2 = <&cluster0_opp>;
> };
>
> cpu2: cpu@2 {
> @@ -95,6 +99,7 @@
> compatible = "arm,cortex-a53","arm,armv8";
> reg = <0x0 0x2>;
> enable-method = "psci";
> + operating-points-v2 = <&cluster0_opp>;
> };
>
> cpu3: cpu@3 {
> @@ -102,6 +107,38 @@
> compatible = "arm,cortex-a53","arm,armv8";
> reg = <0x0 0x3>;
> enable-method = "psci";
> + operating-points-v2 = <&cluster0_opp>;
> + };
> + };
> +
> + cluster0_opp: opp_table0 {
I know this is how examples in the bindings doc written, but it's
recommended to use hyphen rather than underscore in node name. That
said, the following naming form is better.
cluster0_opp: opp-table0
> + compatible = "operating-points-v2";
> + opp-shared;
> +
> + opp@500000000 {
> + opp-hz = /bits/ 64 <500000000>;
> + opp-microvolt = <857000>;
> + clock-latency-ns = <500000>;
> + };
We prefer to have a newline between nodes.
> + opp@648000000 {
> + opp-hz = /bits/ 64 <648000000>;
> + opp-microvolt = <857000>;
> + clock-latency-ns = <500000>;
> + };
> + opp@800000000 {
> + opp-hz = /bits/ 64 <800000000>;
> + opp-microvolt = <882000>;
> + clock-latency-ns = <500000>;
> + };
> + opp@1000000000 {
> + opp-hz = /bits/ 64 <1000000000>;
> + opp-microvolt = <892000>;
> + clock-latency-ns = <500000>;
> + };
> + opp@1188000000 {
> + opp-hz = /bits/ 64 <1188000000>;
> + opp-microvolt = <1009000>;
So we have 5 setpoints with different frequency-voltage pair. I have
seen 'clocks' specified in cpu0 node and understand how frequency
scaling works. But what about voltage scaling? There is no
'cpu-supply' defined, and how does voltage scale among these
opp-microvolt settings?
Another related question: if we do not support voltage scaling for now,
what's the actually voltage when system is up running? Is that voltage
safe for cpu to run at all those 5 frequencies?
Shawn
> + clock-latency-ns = <500000>;
> };
> };
>
> --
> 2.7.4
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v3 2/2] HID: i2c-hid: support regulator power on/off
From: Brian Norris @ 2016-12-02 2:43 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA, Jiri Kosina,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Doug Anderson,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Benjamin Tissoires, linux-input-u79uwXL29TY76Z2rM5mHXA,
Caesar Wang
In-Reply-To: <20161202011615.GB24901@dtor-ws>
On Thu, Dec 01, 2016 at 05:16:15PM -0800, Dmitry Torokhov wrote:
> On Thu, Dec 01, 2016 at 04:42:15PM -0800, Brian Norris wrote:
> > On Thu, Dec 01, 2016 at 04:37:37PM -0800, Dmitry Torokhov wrote:
> > > On Thu, Dec 01, 2016 at 04:31:10PM -0800, Brian Norris wrote:
> > > > On some boards, we need to enable a regulator before using the HID, and
> > > > it's also nice to save power in suspend by disabling it. Support an
> > > > optional "vdd-supply" and a companion initialization delay.
> > > >
> > > > Signed-off-by: Brian Norris <briannorris-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
> > > > Signed-off-by: Caesar Wang <wxt-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> > > > Cc: Jiri Kosina <jikos-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> > > > Cc: linux-input-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> > > > ---
> > > > v2:
> > > > * support compatible property for wacom, with specific "vdd-supply" name
> > > > * support the 100ms delay needed for this digitizer
> > > > * target regulator support only at specific device
> > > >
> > > > v3:
> > > > * drop Wacom specifics and allow this to be used generically
> > > > * add "init-delay-ms" property support
> > > >
> > > > drivers/hid/i2c-hid/i2c-hid.c | 48 ++++++++++++++++++++++++++++++++++++++++++-
> > > > include/linux/i2c/i2c-hid.h | 6 ++++++
> > > > 2 files changed, 53 insertions(+), 1 deletion(-)
> > > >
> > > > diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c
> > > > index b3ec4f2de875..4cb523133d13 100644
> > > > --- a/drivers/hid/i2c-hid/i2c-hid.c
> > > > +++ b/drivers/hid/i2c-hid/i2c-hid.c
...
> > > > @@ -937,6 +939,22 @@ static int i2c_hid_of_probe(struct i2c_client *client,
> > > > }
> > > > pdata->hid_descriptor_address = val;
> > > >
> > > > + ret = of_property_read_u32(dev->of_node, "init-delay-ms", &val);
> > > > + if (!ret)
> > > > + pdata->init_delay_ms = ret;
> > > > +
> > > > + pdata->supply = devm_regulator_get_optional(dev, "vdd");
> > >
> > > Make it devm_regulator_get(), it's cleaner (you'll get a dummy regulator
> > > that you can enable/disbale and not check if it is null or not).
> > >
> > > pdata->supply = devm_regulator_get_optional(dev, "vdd");
> > > if (IS_ERR(pdata->supply)) {
> > > ret = PTR_ERR(pdata->supply);
> > > if (ret != -EPROBE_DEFER)
> > > dev_err(...);
> > > return ret;
> > > }
> >
> > I had it as devm_regulator_get() in v1, but at that time, I was faking
> > the firmware init delay using a regulator property. Now that I want to
> > delay in this driver after enabling the regulator, I'd like to know the
> > difference between a dummy and a real regulator. There's no need to wait
> > after messing with the dummy regulator.
>
> If there is no regulator in ACPI/DT there would not be "init-delay-ms"
> property either.
I suppose that's a fair assumption... the difference being that you
assumed it, and I enforced it :)
Anyway, I can respin without the _optional(). I'll wait at least a day
or so, as the DT binding could use some review (it's the more
controversial piece).
Brian
^ permalink raw reply
* Re: [PATCH 39/39] mtd: nand: denali_dt: add compatible strings for UniPhier SoC variants
From: Masahiro Yamada @ 2016-12-02 2:54 UTC (permalink / raw)
To: Rob Herring
Cc: linux-mtd-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, Linux Kernel Mailing List,
Boris Brezillon, Marek Vasut, Brian Norris, Richard Weinberger,
David Woodhouse, Cyrille Pitchen, Mark Rutland, Dinh Nguyen
In-Reply-To: <20161201160511.ahlibszokg547wxk@rob-hp-laptop>
Hi Rob,
(+CC Dinh)
2016-12-02 1:05 GMT+09:00 Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>:
> On Sun, Nov 27, 2016 at 03:06:25AM +0900, Masahiro Yamada wrote:
>> Add two compatible strings for UniPhier SoCs. The revision register
>> on both shows revision 5.0, but they are different hardware.
>>
>> Features:
>> - DMA engine with 64 bit physical address support
>> - 1024 byte ECC step size
>> - 8 / 16 / 24 bit ECC strength
>> - The n_banks format depends on SoC
>>
>> Signed-off-by: Masahiro Yamada <yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A@public.gmane.org>
>> ---
>>
>> .../devicetree/bindings/mtd/denali-nand.txt | 10 +++++--
>> drivers/mtd/nand/denali_dt.c | 33 ++++++++++++++++++++--
>> 2 files changed, 38 insertions(+), 5 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/mtd/denali-nand.txt b/Documentation/devicetree/bindings/mtd/denali-nand.txt
>> index 51fe195..cea46e2 100644
>> --- a/Documentation/devicetree/bindings/mtd/denali-nand.txt
>> +++ b/Documentation/devicetree/bindings/mtd/denali-nand.txt
>> @@ -1,13 +1,19 @@
>> * Denali NAND controller
>>
>> Required properties:
>> - - compatible : should be "denali,denali-nand-dt"
>> + - compatible : should be one of the following:
>> + "denali,denali-nand-dt"
>
> There are multiple things wrong with this string. denali,denali is
> redundant is one.
One more redundancy; "-dt" is weird because
DT compatible should be a name of hardware.
> It's also fairly useless as this IP has several
> versions and numerous configuration options IIRC. This should be
> deprecated IMO.
Right. There are several customizable parameters for this IP,
so a generic compatible string like this is probably useless.
This DT binding was added by commit 30f9f2f for Altera SOCFPGA,
A funny thing is that they upstreamed DT binding, but they did not upstream
needed changes for the Denali driver core.
So, the mainline driver has never worked on SOCFPGA
(or any of DT-based SoCs).
>> + "denali,denali-nand-uniphier-v5a"
>> + "denali,denali-nand-uniphier-v5b"
>
> Use your vendor prefix, not denali. The 2nd denali can probably be
> dropped because it is not likely you have another kind of nand
> controller in the SoC.
Hmm, your statement implies that a vendor prefix
belongs to an SoC vendor, not an IP vendor.
(I was not quite sure about this.)
It is unlikely to happen to have two different NAND controllers on one SoC.
But, we used a different NAND controller for our SoC family before
introducing the Denali IP.
It also implies that Socionext may use a different NAND IP in the future.
I'd like to include "denali" somewhere because it is clearly associated with
the driver name.
Also, this will give an idea what kind of _basic_ hardware is used,
even though we know various parameters are customizable.
(Plan A)
"denali,socfpga-nand" (for Altera SOCFPGA variant)
"denali,uniphier-nand-v1" (for old Socionext UniPhier family variant)
"denali,uniphier-nand-v2" (for new Socionext UniPhier family variant)
(Plan B)
"altera,denali-nand" (for Altera SOCFPGA variant)
"socionext,denali-nand-v5a" (for old Socionext UniPhier family variant)
"socionext,denali-nand-v5b" (for new Socionext UniPhier family variant)
I think Plan B is nearer to your suggestion,
and I think it is OK for Socionext (hopefully for Altera too).
--
Best Regards
Masahiro Yamada
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: arm64: dts: zx: support cpu-freq for zx296718
From: Viresh Kumar @ 2016-12-02 2:54 UTC (permalink / raw)
To: Shawn Guo
Cc: Baoyou Xie, robh+dt, mark.rutland, catalin.marinas, will.deacon,
jun.nie, devicetree, linux-arm-kernel, linux-kernel, xie.baoyou,
chen.chaokai, wang.qiang01
In-Reply-To: <20161202023627.GA6657@dragon>
On 02-12-16, 10:36, Shawn Guo wrote:
> + Viresh, the author of the bindings.
>
> On Thu, Dec 01, 2016 at 08:08:55PM +0800, Baoyou Xie wrote:
> > This patch adds the CPU clock phandle in CPU's node
> > and uses operating-points-v2 to register operating points.
> >
> > So it can be used by cpufreq-dt driver.
> >
> > Signed-off-by: Baoyou Xie <baoyou.xie@linaro.org>
>
> Just out of curiosity, why is there no '[PATCH]' prefix in the subject?
>
> > ---
> > arch/arm64/boot/dts/zte/zx296718.dtsi | 37 +++++++++++++++++++++++++++++++++++
> > 1 file changed, 37 insertions(+)
> >
> > diff --git a/arch/arm64/boot/dts/zte/zx296718.dtsi b/arch/arm64/boot/dts/zte/zx296718.dtsi
> > index 7a1aed7..16f7d5e 100644
> > --- a/arch/arm64/boot/dts/zte/zx296718.dtsi
> > +++ b/arch/arm64/boot/dts/zte/zx296718.dtsi
> > @@ -44,6 +44,7 @@
> > #include <dt-bindings/input/input.h>
> > #include <dt-bindings/interrupt-controller/arm-gic.h>
> > #include <dt-bindings/gpio/gpio.h>
> > +#include <dt-bindings/clock/zx296718-clock.h>
> >
> > / {
> > compatible = "zte,zx296718";
> > @@ -81,6 +82,8 @@
> > compatible = "arm,cortex-a53","arm,armv8";
> > reg = <0x0 0x0>;
> > enable-method = "psci";
> > + clocks = <&topcrm A53_GATE>;
> > + operating-points-v2 = <&cluster0_opp>;
> > };
> >
> > cpu1: cpu@1 {
> > @@ -88,6 +91,7 @@
> > compatible = "arm,cortex-a53","arm,armv8";
> > reg = <0x0 0x1>;
> > enable-method = "psci";
> > + operating-points-v2 = <&cluster0_opp>;
> > };
> >
> > cpu2: cpu@2 {
> > @@ -95,6 +99,7 @@
> > compatible = "arm,cortex-a53","arm,armv8";
> > reg = <0x0 0x2>;
> > enable-method = "psci";
> > + operating-points-v2 = <&cluster0_opp>;
> > };
> >
> > cpu3: cpu@3 {
> > @@ -102,6 +107,38 @@
> > compatible = "arm,cortex-a53","arm,armv8";
> > reg = <0x0 0x3>;
> > enable-method = "psci";
> > + operating-points-v2 = <&cluster0_opp>;
> > + };
> > + };
> > +
> > + cluster0_opp: opp_table0 {
Also, you better add "clocks" property in all above nodes.
--
viresh
^ permalink raw reply
* Re: Overlays and boolean properties
From: David Gibson @ 2016-12-02 3:04 UTC (permalink / raw)
To: Phil Elwell
Cc: Pantelis Antoniou, devicetree-u79uwXL29TY76Z2rM5mHXA,
Devicetree Compiler
In-Reply-To: <5f4b9a49-8e63-5542-b00a-f415d2b4af6d-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 4428 bytes --]
On Wed, Nov 30, 2016 at 09:29:22AM +0000, Phil Elwell wrote:
> Hi David,
>
> On 30/11/2016 03:47, David Gibson wrote:
> > On Tue, Nov 29, 2016 at 03:10:40PM +0200, Pantelis Antoniou wrote:
> >> Hi Phil,
> >>
> >>> On Nov 29, 2016, at 15:06 , Phil Elwell <phil-FnsA7b+Nu9XbIbC87yuRow@public.gmane.org> wrote:
> >>>
> >>> Boolean properties are defined as being properties with no content, that
> >>> are true if present and false if absent. They pose a problem for DT
> >>> overlays, since the proposed (and widely used) overlay mechanism does
> >>> not allow for properties (or nodes) to be deleted; overlays can only
> >>> make a false property true, so boolean properties are effectively
> >>> monostable - once true they become immutable.
> >>>
> >>> The standard DT syntax includes /delete-property/ and /delete-node/
> >>> directives that do what you would expect from their names, but that
> >>> facility is not available to overlays. There is no FDT node that
> >>> represents the deletion - the directives are acted on immediately
> > Uh.. only partially true. They're acted on during the compile run,
> > but not during the parse. dtc does have an internal representation of
> > node or property deletions that gets resolved when we combine the
> > fragments in the source file.
> >>> - so
> >>> we would need some extra markup - say __delete_property__ and
> >>> __delete_node__ - to hold the names of items to be deleted.
> > So, in principle this wouldn't be that hard - we'd just need to
> > translate dtc's internal representation into a representation in the
> > dtb. That could be done with special properties, or with new opcodes
> > at the dtb encoding level.
> >
> >>> Before I take this further, does anybody have any thoughts on the idea?
> > So.. the first question, is do we have a pressing use case for this?
> > dtbos can (apart from this) alter anything in a base tree, but doing
> > so isn't often a good idea. Usually they'll just add new nodes and
> > properties.
>
> This is more of a real world example than a pressing use case, since the
> number of people affected is small and there is workaround, albeit an
> ugly one.
>
> The Raspberry Pi SoCs have two SD interfaces - one SDIO-capable and one
> not. On the Pi3B the SDIO-capable interface is used to drive the WiFi
> interface. This WiFi interface doesn't like being continuously prodded
> to see if it is still there, so the base DTB sets the "non-removable"
> boolean property. Not all Pi3 users want WiFi, preferring instead to
> wire up an MMC device or second SD slot via the expansion header. A DT
> overlay allows them to modify the pin functions to achieve this, but the
> overlay can't (easily) remove the aptly-named "non-removable". The ugly
> workaround is to disable the original MMC node and create a clone with a
> different name and without the unwanted property.
Ah, I see. Yes, I can certainly see the use
> >> The original patchset did support removing properties (by prefixing them with -).
> >>
> >> I can revive that if we have consensus about the format/method.
> > On the whole, I'd prefer not to see extensions of the existing overlay
> > format - instead I'd like to see focus on a new and better thought out
> > connector format.
> Will this connector mechanism allow a populated DT to be modified, or
> would you always have to start with an "empty" board and only add that
> which you want? I like the flexibility to be able to perform arbitrary
> DT modifications (except deletions) - it has been very useful for
> testing and performing staggered rollouts of new functionality.
In general, no, or at least connectors will only allow pieces of the
DT to be changed that have been allowed to change by the base DT.
The thing is overlays have really been used for two different
purposes: 1) for "production" use where we assemble the DT from
different pieces relevant for this board / system, 2) for testing and
debug where we make arbitrary runtime changes for experimentation.
I'd envisage connectors taking over function (1), but retaining a
mechanism similar to current overlays available (if discouraged) for
usage (2).
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [PATCH v10 2/4] dtc: Document the dynamic plugin internals
From: David Gibson @ 2016-12-02 3:25 UTC (permalink / raw)
To: Pantelis Antoniou
Cc: Frank Rowand, Jon Loeliger, Grant Likely, Rob Herring, Jan Luebbe,
Sascha Hauer, Phil Elwell, Simon Glass, Maxime Ripard,
Thomas Petazzoni, Boris Brezillon, Antoine Tenart, Stephen Boyd,
Devicetree Compiler, devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <234832FB-F181-46AF-9732-E5780FFC38B9-OWPKS81ov/FWk0Htik3J/w@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 18769 bytes --]
On Tue, Nov 29, 2016 at 01:21:40PM +0200, Pantelis Antoniou wrote:
> Hi Frank,
>
> > On Nov 29, 2016, at 03:36 , Frank Rowand <frowand.list-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> >
> > On 11/25/16 04:32, Pantelis Antoniou wrote:
> >> Provides the document explaining the internal mechanics of
> >> plugins and options.
> >>
> >> Signed-off-by: Pantelis Antoniou <pantelis.antoniou-OWPKS81ov/FWk0Htik3J/w@public.gmane.org>
> >> ---
> >> Documentation/dt-object-internal.txt | 318 +++++++++++++++++++++++++++++++++++
> >> 1 file changed, 318 insertions(+)
> >> create mode 100644 Documentation/dt-object-internal.txt
> >>
> >> diff --git a/Documentation/dt-object-internal.txt b/Documentation/dt-object-internal.txt
> >> new file mode 100644
> >> index 0000000..d5b841e
> >> --- /dev/null
> >> +++ b/Documentation/dt-object-internal.txt
> >> @@ -0,0 +1,318 @@
> >> +Device Tree Dynamic Object format internals
> >> +-------------------------------------------
> >> +
> >> +The Device Tree for most platforms is a static representation of
> >> +the hardware capabilities. This is insufficient for many platforms
> >> +that need to dynamically insert device tree fragments to the
> >> +running kernel's live tree.
> >> +
> >> +This document explains the the device tree object format and the
> >> +modifications made to the device tree compiler, which make it possible.
> >> +
> >> +1. Simplified Problem Definition
> >> +--------------------------------
> >> +
> >> +Assume we have a platform which boots using following simplified device tree.
> >> +
> >> +---- foo.dts -----------------------------------------------------------------
> >> + /* FOO platform */
> >> + / {
> >> + compatible = "corp,foo";
> >> +
> >> + /* shared resources */
> >> + res: res {
> >> + };
> >> +
> >> + /* On chip peripherals */
> >> + ocp: ocp {
> >> + /* peripherals that are always instantiated */
> >> + peripheral1 { ... };
> >> + };
> >> + };
> >> +---- foo.dts -----------------------------------------------------------------
> >> +
> >> +We have a number of peripherals that after probing (using some undefined method)
> >> +should result in different device tree configuration.
> >> +
> >> +We cannot boot with this static tree because due to the configuration of the
> >> +foo platform there exist multiple conficting peripherals DT fragments.
> >
> > ^^^^^^^^^^ conflicting
> >
> > I assume conflicting because, for instance, the different peripherals might
> > occupy the same address space, use the same interrupt, or use the same gpio.
> > Mentioning that would provide a fuller picture for the neophyte.
> >
>
> Yes, thanks for bringing this to my attention. This document is heavy on the neophyte for sure.
>
> >> +
> >> +So for the bar peripheral we would have this:
> >> +
> >> +---- foo+bar.dts -------------------------------------------------------------
> >> + /* FOO platform + bar peripheral */
> >> + / {
> >> + compatible = "corp,foo";
> >> +
> >> + /* shared resources */
> >> + res: res {
> >> + };
> >> +
> >> + /* On chip peripherals */
> >> + ocp: ocp {
> >> + /* peripherals that are always instantiated */
> >> + peripheral1 { ... };
> >> +
> >> + /* bar peripheral */
> >> + bar {
> >> + compatible = "corp,bar";
> >> + ... /* various properties and child nodes */
> >> + };
> >> + };
> >> + };
> >> +---- foo+bar.dts -------------------------------------------------------------
> >> +
> >> +While for the baz peripheral we would have this:
> >> +
> >> +---- foo+baz.dts -------------------------------------------------------------
> >> + /* FOO platform + baz peripheral */
> >> + / {
> >> + compatible = "corp,foo";
> >> +
> >> + /* shared resources */
> >> + res: res {
> >> + /* baz resources */
> >> + baz_res: res_baz { ... };
> >> + };
> >> +
> >> + /* On chip peripherals */
> >> + ocp: ocp {
> >> + /* peripherals that are always instantiated */
> >> + peripheral1 { ... };
> >> +
> >> + /* baz peripheral */
> >> + baz {
> >> + compatible = "corp,baz";
> >> + /* reference to another point in the tree */
> >> + ref-to-res = <&baz_res>;
> >> + ... /* various properties and child nodes */
> >> + };
> >> + };
> >> + };
> >> +---- foo+baz.dts -------------------------------------------------------------
> >> +
> >> +We note that the baz case is more complicated, since the baz peripheral needs to
> >> +reference another node in the DT tree.
> >
> > I know that there are other situations that can justify overlays, so not
> > contesting the basic need with this comment. But the above situation could
> > be handled in a much simpler fashion by setting the status property of each
> > of the conflicting devices to disabled, then after probing setting the status
> > to ok. That method removes a lot of complexity.
> >
> > A big driver for the concept of overlays was being able to describe different
> > add on boards at run time, instead of when the base dtb was created. I think
> > we have agreed that moving to a connector model instead of a raw overlay is
> > the proper way to address add on boards.
> >
> > Can you address how an overlay can be created that will work for a board
> > plugged into any of the identical sockets that is compatible with the
> > board?
> >
> >
>
> Yes, I will try to do so.
>
> >> +
> >> +2. Device Tree Object Format Requirements
> >> +-----------------------------------------
> >> +
> >> +Since the device tree is used for booting a number of very different hardware
> >> +platforms it is imperative that we tread very carefully.
> >> +
> >> +2.a) No changes to the Device Tree binary format for the base tree. We cannot
> >> +modify the tree format at all and all the information we require should be
> >> +encoded using device tree itself. We can add nodes that can be safely ignored
> >> +by both bootloaders and the kernel. The plugin dtb's are optionally tagged
> >> +with a different magic number in the header but otherwise they too are simple
> >> +blobs.
> >> +
> >> +2.b) Changes to the DTS source format should be absolutely minimal, and should
> >> +only be needed for the DT fragment definitions, and not the base boot DT.
> >> +
> >> +2.c) An explicit option should be used to instruct DTC to generate the required
> >> +information needed for object resolution. Platforms that don't use the
> >> +dynamic object format can safely ignore it.
> >> +
> >> +2.d) Finally, DT syntax changes should be kept to a minimum. It should be
> >> +possible to express everything using the existing DT syntax.
> >> +
> >> +3. Implementation
> >> +-----------------
> >> +
> >> +The basic unit of addressing in Device Tree is the phandle. Turns out it's
> >> +relatively simple to extend the way phandles are generated and referenced
> >> +so that it's possible to dynamically convert symbolic references (labels)
> >> +to phandle values. This is a valid assumption as long as the author uses
> >> +reference syntax and does not assign phandle values manually (which might
> >> +be a problem with decompiled source files).
> >> +
> >> +We can roughly divide the operation into two steps.
> >> +
> >> +3.a) Compilation of the base board DTS file using the '-@' option
> >> +generates a valid DT blob with an added __symbols__ node at the root node,
> >> +containing a list of all nodes that are marked with a label.
> >> +
> >> +Using the foo.dts file above the following node will be generated;
> >> +
> >> +$ dtc -@ -O dtb -o foo.dtb -b 0 foo.dts
> >> +$ fdtdump foo.dtb
> >> +...
> >> +/ {
> >> + ...
> >> + res {
> >> + ...
> >> + phandle = <0x00000001>;
> >> + ...
> >> + };
> >> + ocp {
> >> + ...
> >> + phandle = <0x00000002>;
> >> + ...
> >> + };
> >> + __symbols__ {
> >> + res="/res";
> >> + ocp="/ocp";
> >> + };
> >> +};
> >> +
> >> +Notice that all the nodes that had a label have been recorded, and that
> >> +phandles have been generated for them.
> >> +
> >> +This blob can be used to boot the board normally, the __symbols__ node will
> >> +be safely ignored both by the bootloader and the kernel (the only loss will
> >> +be a few bytes of memory and disk space).
> >> +
> >> +3.b) The Device Tree fragments must be compiled with the same option but they
> >> +must also have a tag (/plugin/) that allows undefined references to nodes
> >> +that are not present at compilation time to be recorded so that the runtime
> >> +loader can fix them.
> >> +
> >> +So the bar peripheral's DTS format would be of the form:
> >> +
> >> +/dts-v1/ /plugin/; /* allow undefined references and record them */
> >> +/ {
> >> + .... /* various properties for loader use; i.e. part id etc. */
> >> + fragment@0 {
> >> + target = <&ocp>;
> >> + __overlay__ {
> >> + /* bar peripheral */
> >> + bar {
> >> + compatible = "corp,bar";
> >> + ... /* various properties and child nodes */
> >> + }
> >> + };
> >> + };
> >> +};
> >
> > The last version of your patches that I tested did not require specifying
> > the target property, the fragment node, and the __overlay__ node. dtc
> > properly created all of those items automatically. For example, I could
> > go to all of the trouble of creating those items in a dts like:
> >
> > $ cat example_1_hand_coded.dts
> > /dts-v1/;
> > /plugin/;
> >
> > / {
> >
> > fragment@0 {
> > target = <&am3353x_pinmux>;
> >
> > __overlay__ {
> >
> > i2c1_pins: pinmux_i2c1_pins {
> > pinctrl-single,pins = <
> > 0x158 0x72
> > 0x15c 0x72
> > >;
> > };
> > };
> > };
> >
> > fragment@1 {
> > target = <&i2c1>;
> >
> > __overlay__ {
> > pinctrl-names = "default";
> > pinctrl-0 = <&i2c1_pins>;
> > clock-frequency = <400000>;
> > status = "okay";
> >
> > at24@50 {
> > compatible = "at,24c256";
> > pagesize = <64>;
> > reg = <0x50>;
> > };
> > };
> > };
> > };
> >
> >
> > Or I could let dtc automagically create all the special features
> > (target, fragment, __overlay__) from an equivalent dts:
> >
> > $ cat example_1.dts
> > /dts-v1/;
> > /plugin/;
> >
> >
> > &am3353x_pinmux {
> > i2c1_pins: pinmux_i2c1_pins {
> > pinctrl-single,pins = <
> > 0x158 0x72
> > 0x15c 0x72
> > >;
> > };
> > };
> >
> > &i2c1 {
> > #address-cells = <1>;
> > #size-cells = <0>;
> > pinctrl-names = "default";
> > pinctrl-0 = <&i2c1_pins>;
> > clock-frequency = <400000>;
> > status = "okay";
> >
> > at24@50 {
> > compatible = "at,24c256";
> > pagesize = <64>;
> > reg = <0x50>;
> > };
> > };
> >
> >
> > I would much prefer that people never hand code the target, fragment, and
> > __overlay__ in a dts source file. Exposing them at the source level adds
> > complexity, confusion, and an increased chance of creating an invalid
> > overlay dtb.
> >
> > If possible, I would prefer target, fragment, and __overlay__ not be valid
> > input to dtc. It would probably be difficult to prohibit target and fragment,
> > because however unlikely they are as property and node names, they are valid
> > dts syntax before adding the overlay enhancements to dtc. However __overlay__
> > is not a valid node name without the overlay enhancements and could remain
> > invalid dts input.
> >
> > I prefer that target, fragment, and __overlay__ be documented as a dtb to
> > target system API. In this case, for the normal developer, they are
> > hidden in the binary dtb format and in the kernel (or boot loader)
> > overlay framework code.
> >
> > I do recognize that if __overlay__ is not valid dtc input then it is not
> > possible to decompile an overlay into a dts containing __overlay__ and
> > then recompile that dts. This could be resolved by a more complex
> > decompile that turned the overlay dtb back into the form of example_1.dts
> > above.
> >
> > After reading to the end of this patch, I see that the simpler form of
> > .dts (like example_1.dts) is also noted as "an alternative syntax to
> > the expanded form for overlays".
> >
> >
>
> Phew.
>
> Let me address all that.
>
> When I started on this the main problem was that there was no support for applying
> overlays in the kernel. The original patch series for dtc is meant to support the
> encoding of the required information into device tree format.
>
> The syntax of overlays like this '&foo { };’ is a new thing that can be subject to
> change.
Well.. yes and no. What I'm going to call "compile time overlays"
using that syntax have been around for ages (rather longer than
dynamic overlays). The semantics you hve for runtime overlay
application are pretty much identical to those for compile time
overlays, except (duh) applied later.
That's why I want to unify the syntax between the two. And, up to a
point, to unify the concepts as well. This is why I want to treat
this as having dtc parse the source into a bundle of overlays which it
then decides whether it needs to apply immediately (compile time
overlay) or encode them into the dtbo format for the bootloader or
kernel to apply later (dynamic overlay).
> On the last patchset I’ve split it out so that it is clear.
Yeah, but you're splitting it based on the history, rather than what I
think is the conceptually clearer approach: first, allow the overlay
(&ref { ... }) syntax to be either compile-time or dynamic. second,
add in backwards compatiblity hacks for manually encoded dts files.
> Now, since we’ve settled on the internal encoding format (__overlays__, target, etc)
> we can tackle the syntax cases and alternative target options.
But that's not an internal encoding format, it's an _external_
encoding format.
> So, yes we should forbid __overlay__ to be a valid node name eventually along with
> a bunch of other syntax stuff.
>
> Having come to mind, we should see what we need for the connector
> format to work.
No argument there.
>
> >> +
> >> +Note that there's a target property that specifies the location where the
> >> +contents of the overlay node will be placed, and it references the node
> >> +in the foo.dts file.
> >> +
> >> +$ dtc -@ -O dtb -o bar.dtbo -b 0 bar.dts
> >> +$ fdtdump bar.dtbo
> >> +...
> >> +/ {
> >> + ... /* properties */
> >> + fragment@0 {
> >> + target = <0xffffffff>;
> >> + __overlay__ {
> >> + bar {
> >> + compatible = "corp,bar";
> >> + ... /* various properties and child nodes */
> >> + }
> >> + };
> >> + };
> >> + __fixups__ {
> >> + ocp = "/fragment@0:target:0";
> >> + };
> >> +};
> >> +
> >> +No __symbols__ has been generated (no label in bar.dts).
> >> +Note that the target's ocp label is undefined, so the phandle handle
> >> +value is filled with the illegal value '0xffffffff', while a __fixups__
> >> +node has been generated, which marks the location in the tree where
> >> +the label lookup should store the runtime phandle value of the ocp node.
> >> +
> >> +The format of the __fixups__ node entry is
> >> +
> >> + <label> = "<local-full-path>:<property-name>:<offset>";
> >> +
> >> +<label> Is the label we're referring
> >> +<local-full-path> Is the full path of the node the reference is
> >> +<property-name> Is the name of the property containing the
> >> + reference
> >> +<offset> The offset (in bytes) of where the property's
> >> + phandle value is located.
> >> +
> >> +Doing the same with the baz peripheral's DTS format is a little bit more
> >> +involved, since baz contains references to local labels which require
> >> +local fixups.
> >> +
> >> +/dts-v1/ /plugin/; /* allow undefined label references and record them */
> >> +/ {
> >> + .... /* various properties for loader use; i.e. part id etc. */
> >> + fragment@0 {
> >> + target = <&res>;
> >> + __overlay__ {
> >> + /* baz resources */
> >> + baz_res: res_baz { ... };
> >> + };
> >> + };
> >> + fragment@1 {
> >> + target = <&ocp>;
> >> + __overlay__ {
> >> + /* baz peripheral */
> >> + baz {
> >> + compatible = "corp,baz";
> >> + /* reference to another point in the tree */
> >> + ref-to-res = <&baz_res>;
> >> + ... /* various properties and child nodes */
> >> + }
> >> + };
> >> + };
> >> +};
> >> +
> >> +Note that &bar_res reference.
> >> +
> >> +$ dtc -@ -O dtb -o baz.dtbo -b 0 baz.dts
> >> +$ fdtdump baz.dtbo
> >> +...
> >> +/ {
> >> + ... /* properties */
> >> + fragment@0 {
> >> + target = <0xffffffff>;
> >> + __overlay__ {
> >> + res_baz {
> >> + ....
> >> + phandle = <0x00000001>;
> >> + };
> >> + };
> >> + };
> >> + fragment@1 {
> >> + target = <0xffffffff>;
> >> + __overlay__ {
> >> + baz {
> >> + compatible = "corp,baz";
> >> + ... /* various properties and child nodes */
> >> + ref-to-res = <0x00000001>;
> >> + }
> >> + };
> >> + };
> >> + __fixups__ {
> >> + res = "/fragment@0:target:0";
> >> + ocp = "/fragment@1:target:0";
> >> + };
> >> + __local_fixups__ {
> >> + fragment@1 {
> >> + __overlay__ {
> >> + baz {
> >> + ref-to-res = <0>;
> >> + };
> >> + };
> >> + };
> >> + };
> >> +};
> >> +
> >> +This is similar to the bar case, but the reference of a local label by the
> >> +baz node generates a __local_fixups__ entry that records the place that the
> >> +local reference is being made. No matter how phandles are allocated from dtc
> >> +the run time loader must apply an offset to each phandle in every dynamic
> >> +DT object loaded. The __local_fixups__ node records the place of every
> >> +local reference so that the loader can apply the offset.
> >> +
> >> +There is an alternative syntax to the expanded form for overlays with phandle
> >> +targets which makes the format similar to the one using in .dtsi include files.
> >> +
> >> +So for the &ocp target example above one can simply write:
> >> +
> >> +/dts-v1/ /plugin/;
> >> +&ocp {
> >> + /* bar peripheral */
> >> + bar {
> >> + compatible = "corp,bar";
> >> + ... /* various properties and child nodes */
> >> + }
> >> +};
> >> +
> >> +The resulting dtb object is identical.
> >>
> >
>
> Regards
>
> — Pantelis
>
--
David Gibson | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_
| _way_ _around_!
http://www.ozlabs.org/~dgibson
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* [PATCH 0/2] Add MediaTek crypto acclelrator driver
From: Ryder Lee @ 2016-12-02 3:26 UTC (permalink / raw)
To: Herbert Xu, David S. Miller, Matthias Brugger
Cc: devicetree, linux-mediatek, linux-kernel, linux-crypto,
linux-arm-kernel, Sean Wang, Ryder Lee
This adds support for the MediaTek hardware accelerator on
mt7623 SoC.
This driver currently implement:
- SHA1 and SHA2 family(HMAC) hash alogrithms.
- AES block cipher in CBC/ECB mode with 128/196/256 bits keys.
Ryder Lee (2):
Add crypto driver support for some MediaTek chips
crypto: mediatek - add DT bindings documentation
.../devicetree/bindings/crypto/mediatek-crypto.txt | 32 +
drivers/crypto/Kconfig | 17 +
drivers/crypto/Makefile | 1 +
drivers/crypto/mediatek/Makefile | 2 +
drivers/crypto/mediatek/mtk-aes.c | 734 +++++++++++
drivers/crypto/mediatek/mtk-platform.c | 575 ++++++++
drivers/crypto/mediatek/mtk-platform.h | 230 ++++
drivers/crypto/mediatek/mtk-regs.h | 194 +++
drivers/crypto/mediatek/mtk-sha.c | 1384 ++++++++++++++++++++
9 files changed, 3169 insertions(+)
create mode 100644 Documentation/devicetree/bindings/crypto/mediatek-crypto.txt
create mode 100644 drivers/crypto/mediatek/Makefile
create mode 100644 drivers/crypto/mediatek/mtk-aes.c
create mode 100644 drivers/crypto/mediatek/mtk-platform.c
create mode 100644 drivers/crypto/mediatek/mtk-platform.h
create mode 100644 drivers/crypto/mediatek/mtk-regs.h
create mode 100644 drivers/crypto/mediatek/mtk-sha.c
--
1.9.1
^ permalink raw reply
* [PATCH 1/2] Add crypto driver support for some MediaTek chips
From: Ryder Lee @ 2016-12-02 3:26 UTC (permalink / raw)
To: Herbert Xu, David S. Miller, Matthias Brugger
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-crypto-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Sean Wang,
Ryder Lee
In-Reply-To: <1480649205-52695-1-git-send-email-ryder.lee-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
This adds support for the MediaTek hardware accelerator on
mt7623/mt2701/mt8521p SoC.
This driver currently implement:
- SHA1 and SHA2 family(HMAC) hash alogrithms.
- AES block cipher in CBC/ECB mode with 128/196/256 bits keys.
Signed-off-by: Ryder Lee <ryder.lee-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
---
drivers/crypto/Kconfig | 17 +
drivers/crypto/Makefile | 1 +
drivers/crypto/mediatek/Makefile | 2 +
drivers/crypto/mediatek/mtk-aes.c | 734 +++++++++++++++++
drivers/crypto/mediatek/mtk-platform.c | 575 +++++++++++++
drivers/crypto/mediatek/mtk-platform.h | 230 ++++++
drivers/crypto/mediatek/mtk-regs.h | 194 +++++
drivers/crypto/mediatek/mtk-sha.c | 1384 ++++++++++++++++++++++++++++++++
8 files changed, 3137 insertions(+)
create mode 100644 drivers/crypto/mediatek/Makefile
create mode 100644 drivers/crypto/mediatek/mtk-aes.c
create mode 100644 drivers/crypto/mediatek/mtk-platform.c
create mode 100644 drivers/crypto/mediatek/mtk-platform.h
create mode 100644 drivers/crypto/mediatek/mtk-regs.h
create mode 100644 drivers/crypto/mediatek/mtk-sha.c
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index 4d2b81f..5d9c803 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -553,6 +553,23 @@ config CRYPTO_DEV_ROCKCHIP
This driver interfaces with the hardware crypto accelerator.
Supporting cbc/ecb chainmode, and aes/des/des3_ede cipher mode.
+config CRYPTO_DEV_MEDIATEK
+ tristate "MediaTek's Cryptographic Engine driver"
+ depends on ARM && ARCH_MEDIATEK
+ select NEON
+ select KERNEL_MODE_NEON
+ select ARM_CRYPTO
+ select CRYPTO_AES
+ select CRYPTO_BLKCIPHER
+ select CRYPTO_SHA1_ARM_NEON
+ select CRYPTO_SHA256_ARM
+ select CRYPTO_SHA512_ARM
+ select CRYPTO_HMAC
+ help
+ This driver allows you to utilize the hardware crypto accelerator
+ which can be found on the MT7623 MT2701, MT8521p, etc ....
+ Select this if you want to use it for AES/SHA1/SHA2 algorithms.
+
source "drivers/crypto/chelsio/Kconfig"
endif # CRYPTO_HW
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index ad7250f..272b51a 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_CRYPTO_DEV_IMGTEC_HASH) += img-hash.o
obj-$(CONFIG_CRYPTO_DEV_IXP4XX) += ixp4xx_crypto.o
obj-$(CONFIG_CRYPTO_DEV_MV_CESA) += mv_cesa.o
obj-$(CONFIG_CRYPTO_DEV_MARVELL_CESA) += marvell/
+obj-$(CONFIG_CRYPTO_DEV_MEDIATEK) += mediatek/
obj-$(CONFIG_CRYPTO_DEV_MXS_DCP) += mxs-dcp.o
obj-$(CONFIG_CRYPTO_DEV_NIAGARA2) += n2_crypto.o
n2_crypto-y := n2_core.o n2_asm.o
diff --git a/drivers/crypto/mediatek/Makefile b/drivers/crypto/mediatek/Makefile
new file mode 100644
index 0000000..187be79
--- /dev/null
+++ b/drivers/crypto/mediatek/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_CRYPTO_DEV_MEDIATEK) += mtk-crypto.o
+mtk-crypto-objs:= mtk-platform.o mtk-aes.o mtk-sha.o
diff --git a/drivers/crypto/mediatek/mtk-aes.c b/drivers/crypto/mediatek/mtk-aes.c
new file mode 100644
index 0000000..feb0e57
--- /dev/null
+++ b/drivers/crypto/mediatek/mtk-aes.c
@@ -0,0 +1,734 @@
+/*
+ * Cryptographic API.
+ *
+ * Support for MediaTek AES hardware accelerator.
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Ryder Lee <ryder.lee-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Some ideas are from atmel-aes.c drivers.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/scatterlist.h>
+#include <crypto/scatterwalk.h>
+#include <crypto/algapi.h>
+#include <crypto/aes.h>
+#include "mtk-platform.h"
+#include "mtk-regs.h"
+
+#define AES_QUEUE_LENGTH 512
+#define AES_BUFFER_ORDER 2
+#define AES_BUFFER_SIZE ((PAGE_SIZE << AES_BUFFER_ORDER) \
+ & ~(AES_BLOCK_SIZE - 1))
+
+/* AES command token */
+#define AES_CT_SIZE_ECB 2
+#define AES_CT_SIZE_CBC 3
+#define AES_CT_CTRL_HDR 0x00220000
+#define AES_COMMAND0 0x05000000
+#define AES_COMMAND1 0x2d060000
+#define AES_COMMAND2 0xe4a63806
+
+/* AES transform information */
+#define AES_TFM_ECB (0x0 << 0)
+#define AES_TFM_CBC (0x1 << 0)
+#define AES_TFM_DECRYPT (0x5 << 0)
+#define AES_TFM_ENCRYPT (0x4 << 0)
+#define AES_TFM_SIZE(x) ((x) << 8)
+#define AES_TFM_128BITS (0xb << 16)
+#define AES_TFM_192BITS (0xd << 16)
+#define AES_TFM_256BITS (0xf << 16)
+#define AES_TFM_FULL_IV (0xf << 5)
+
+/* AES flags */
+#define AES_FLAGS_MODE_MSK GENMASK(2, 0)
+#define AES_FLAGS_ECB BIT(0)
+#define AES_FLAGS_CBC BIT(1)
+#define AES_FLAGS_ENCRYPT BIT(2)
+#define AES_FLAGS_BUSY BIT(3)
+
+/* AES command token */
+struct mtk_aes_ct {
+ u32 ct_ctrl0;
+ u32 ct_ctrl1;
+ u32 ct_ctrl2;
+};
+
+/* AES transform info */
+struct mtk_aes_tfm {
+ u32 tfm_ctrl0;
+ u32 tfm_ctrl1;
+
+ /* keys and IVs */
+ u8 state[AES_KEYSIZE_256 + AES_BLOCK_SIZE] __aligned(sizeof(u32));
+};
+
+struct mtk_aes_info {
+ struct mtk_aes_ct ct;
+ struct mtk_aes_tfm tfm;
+};
+
+struct mtk_aes_reqctx {
+ u64 mode;
+};
+
+struct mtk_aes_ctx {
+ struct mtk_cryp *cryp;
+ struct mtk_aes_info info;
+ u32 keylen;
+
+ unsigned long flags;
+};
+
+struct mtk_aes_drv {
+ struct list_head dev_list;
+ /* device list lock */
+ spinlock_t lock;
+};
+
+static struct mtk_aes_drv mtk_aes = {
+ .dev_list = LIST_HEAD_INIT(mtk_aes.dev_list),
+ .lock = __SPIN_LOCK_UNLOCKED(mtk_aes.lock),
+};
+
+static inline u32 mtk_aes_read(struct mtk_cryp *cryp, u32 offset)
+{
+ return readl_relaxed(cryp->base + offset);
+}
+
+static inline void mtk_aes_write(struct mtk_cryp *cryp,
+ u32 offset, u32 value)
+{
+ writel_relaxed(value, cryp->base + offset);
+}
+
+static struct mtk_cryp *mtk_aes_find_dev(struct mtk_aes_ctx *ctx)
+{
+ struct mtk_cryp *cryp = NULL;
+ struct mtk_cryp *tmp;
+
+ spin_lock_bh(&mtk_aes.lock);
+ if (!ctx->cryp) {
+ list_for_each_entry(tmp, &mtk_aes.dev_list, aes_list) {
+ cryp = tmp;
+ break;
+ }
+ ctx->cryp = cryp;
+ } else {
+ cryp = ctx->cryp;
+ }
+ spin_unlock_bh(&mtk_aes.lock);
+
+ return cryp;
+}
+
+static inline size_t mtk_aes_padlen(size_t len)
+{
+ len &= AES_BLOCK_SIZE - 1;
+ return len ? AES_BLOCK_SIZE - len : 0;
+}
+
+static bool mtk_aes_check_aligned(struct scatterlist *sg,
+ size_t len, struct mtk_aes_dma *dma)
+{
+ int nents;
+
+ if (!IS_ALIGNED(len, AES_BLOCK_SIZE))
+ return false;
+
+ for (nents = 0; sg; sg = sg_next(sg), ++nents) {
+ if (!IS_ALIGNED(sg->offset, sizeof(u32)))
+ return false;
+
+ if (len <= sg->length) {
+ if (!IS_ALIGNED(len, AES_BLOCK_SIZE))
+ return false;
+
+ dma->nents = nents + 1;
+ dma->remainder = sg->length - len;
+ sg->length = len;
+ return true;
+ }
+
+ if (!IS_ALIGNED(sg->length, AES_BLOCK_SIZE))
+ return false;
+
+ len -= sg->length;
+ }
+
+ return false;
+}
+
+static int mtk_aes_info_map(struct mtk_cryp *cryp,
+ struct mtk_aes *aes, size_t len)
+{
+ struct mtk_aes_ctx *ctx = crypto_ablkcipher_ctx(
+ crypto_ablkcipher_reqtfm(aes->req));
+ struct mtk_aes_info *info = aes->info;
+ struct mtk_aes_ct *ct = &info->ct;
+ struct mtk_aes_tfm *tfm = &info->tfm;
+ u32 keylen = ctx->keylen;
+
+ aes->ct_hdr = AES_CT_CTRL_HDR | len;
+ ct->ct_ctrl0 = AES_COMMAND0 | len;
+ ct->ct_ctrl1 = AES_COMMAND1;
+
+ if (aes->flags & AES_FLAGS_ENCRYPT)
+ tfm->tfm_ctrl0 = AES_TFM_ENCRYPT;
+ else
+ tfm->tfm_ctrl0 = AES_TFM_DECRYPT;
+
+ if (aes->flags & AES_FLAGS_CBC) {
+ aes->ct_size = AES_CT_SIZE_CBC;
+ ct->ct_ctrl2 = AES_COMMAND2;
+
+ tfm->tfm_ctrl0 |= AES_TFM_SIZE(WORD(keylen + AES_BLOCK_SIZE));
+ tfm->tfm_ctrl1 = AES_TFM_CBC;
+ tfm->tfm_ctrl1 |= AES_TFM_FULL_IV;
+
+ memcpy(tfm->state + keylen, aes->req->info, AES_BLOCK_SIZE);
+ } else if (aes->flags & AES_FLAGS_ECB) {
+ aes->ct_size = AES_CT_SIZE_ECB;
+ tfm->tfm_ctrl0 |= AES_TFM_SIZE(WORD(keylen));
+ tfm->tfm_ctrl1 = AES_TFM_ECB;
+ }
+
+ if (keylen == AES_KEYSIZE_128)
+ tfm->tfm_ctrl0 |= AES_TFM_128BITS;
+ else if (keylen == AES_KEYSIZE_256)
+ tfm->tfm_ctrl0 |= AES_TFM_256BITS;
+ else if (keylen == AES_KEYSIZE_192)
+ tfm->tfm_ctrl0 |= AES_TFM_192BITS;
+
+ aes->ct_dma = dma_map_single(cryp->dev, info, sizeof(*info),
+ DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(cryp->dev, aes->ct_dma))) {
+ dev_err(cryp->dev, "dma %d bytes error\n", sizeof(*info));
+ return -EINVAL;
+ }
+ aes->tfm_dma = aes->ct_dma + sizeof(*ct);
+
+ return 0;
+}
+
+static int mtk_aes_xmit(struct mtk_cryp *cryp, struct mtk_aes *aes)
+{
+ struct mtk_ring *ring = cryp->ring[aes->id];
+ struct mtk_desc *cmd = NULL, *res = NULL;
+ struct scatterlist *ssg, *dsg;
+ u32 len = aes->src.sg_len;
+ int nents;
+
+ for (nents = 0; nents < len; ++nents) {
+ ssg = &aes->src.sg[nents];
+ dsg = &aes->dst.sg[nents];
+
+ cmd = ring->cmd_base + ring->pos;
+ res = ring->res_base + ring->pos;
+
+ res->hdr = MTK_DESC_BUF_LEN(dsg->length);
+ res->buf = sg_dma_address(dsg);
+
+ cmd->hdr = MTK_DESC_BUF_LEN(ssg->length);
+ cmd->buf = sg_dma_address(ssg);
+
+ if (nents == 0) {
+ res->hdr |= MTK_DESC_FIRST;
+ cmd->hdr |= MTK_DESC_FIRST;
+ cmd->hdr |= MTK_DESC_CT_LEN(aes->ct_size);
+ cmd->ct = aes->ct_dma;
+ cmd->ct_hdr = aes->ct_hdr;
+ cmd->tfm = aes->tfm_dma;
+ }
+
+ if (++ring->pos == MTK_MAX_DESC_NUM)
+ ring->pos = 0;
+ }
+
+ cmd->hdr |= MTK_DESC_LAST;
+ res->hdr |= MTK_DESC_LAST;
+
+ /* make sure all descriptor are filled done */
+ wmb();
+ mtk_aes_write(cryp, RDR_PREP_COUNT(aes->id), MTK_DESC_CNT(len));
+ mtk_aes_write(cryp, CDR_PREP_COUNT(aes->id), MTK_DESC_CNT(len));
+
+ return -EINPROGRESS;
+}
+
+static inline void mtk_aes_restore_sg(const struct mtk_aes_dma *dma)
+{
+ struct scatterlist *sg = dma->sg;
+ int nents = dma->nents;
+
+ if (!dma->remainder)
+ return;
+
+ while (--nents > 0 && sg)
+ sg = sg_next(sg);
+
+ if (!sg)
+ return;
+
+ sg->length += dma->remainder;
+}
+
+static int mtk_aes_map(struct mtk_cryp *cryp, struct mtk_aes *aes)
+{
+ struct scatterlist *src = aes->req->src;
+ struct scatterlist *dst = aes->req->dst;
+ size_t len = aes->req->nbytes;
+ size_t padlen = 0;
+ bool src_aligned, dst_aligned;
+
+ aes->total = len;
+ aes->src.sg = src;
+ aes->dst.sg = dst;
+ aes->real_dst = dst;
+
+ src_aligned = mtk_aes_check_aligned(src, len, &aes->src);
+ if (src == dst)
+ dst_aligned = src_aligned;
+ else
+ dst_aligned = mtk_aes_check_aligned(dst, len, &aes->dst);
+
+ if (!src_aligned || !dst_aligned) {
+ padlen = mtk_aes_padlen(len);
+
+ if (len + padlen > AES_BUFFER_SIZE)
+ return -ENOMEM;
+
+ if (!src_aligned) {
+ sg_copy_to_buffer(src, sg_nents(src), aes->buf, len);
+ aes->src.sg = &aes->aligned_sg;
+ aes->src.nents = 1;
+ aes->src.remainder = 0;
+ }
+
+ if (!dst_aligned) {
+ aes->dst.sg = &aes->aligned_sg;
+ aes->dst.nents = 1;
+ aes->dst.remainder = 0;
+ }
+
+ sg_init_table(&aes->aligned_sg, 1);
+ sg_set_buf(&aes->aligned_sg, aes->buf, len + padlen);
+ }
+
+ if (aes->src.sg == aes->dst.sg) {
+ aes->src.sg_len = dma_map_sg(cryp->dev, aes->src.sg,
+ aes->src.nents, DMA_BIDIRECTIONAL);
+ aes->dst.sg_len = aes->src.sg_len;
+ if (unlikely(!aes->src.sg_len))
+ return -EFAULT;
+ } else {
+ aes->src.sg_len = dma_map_sg(cryp->dev, aes->src.sg,
+ aes->src.nents, DMA_TO_DEVICE);
+ if (unlikely(!aes->src.sg_len))
+ return -EFAULT;
+
+ aes->dst.sg_len = dma_map_sg(cryp->dev, aes->dst.sg,
+ aes->dst.nents, DMA_FROM_DEVICE);
+ if (unlikely(!aes->dst.sg_len)) {
+ dma_unmap_sg(cryp->dev, aes->src.sg,
+ aes->src.nents, DMA_TO_DEVICE);
+ return -EFAULT;
+ }
+ }
+
+ return mtk_aes_info_map(cryp, aes, len + padlen);
+}
+
+static int mtk_aes_handle_queue(struct mtk_cryp *cryp, u8 id,
+ struct ablkcipher_request *req)
+{
+ struct mtk_aes *aes = cryp->aes[id];
+ struct crypto_async_request *areq, *backlog;
+ struct mtk_aes_reqctx *rctx;
+ struct mtk_aes_ctx *ctx;
+ unsigned long flags;
+ int err, ret = 0;
+
+ spin_lock_irqsave(&aes->lock, flags);
+ if (req)
+ ret = ablkcipher_enqueue_request(&aes->queue, req);
+ if (aes->flags & AES_FLAGS_BUSY) {
+ spin_unlock_irqrestore(&aes->lock, flags);
+ return ret;
+ }
+ backlog = crypto_get_backlog(&aes->queue);
+ areq = crypto_dequeue_request(&aes->queue);
+ if (areq)
+ aes->flags |= AES_FLAGS_BUSY;
+ spin_unlock_irqrestore(&aes->lock, flags);
+
+ if (!areq)
+ return ret;
+
+ if (backlog)
+ backlog->complete(backlog, -EINPROGRESS);
+
+ req = ablkcipher_request_cast(areq);
+ ctx = crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(req));
+ rctx = ablkcipher_request_ctx(req);
+ rctx->mode &= AES_FLAGS_MODE_MSK;
+ /* assign new request to device */
+ aes->req = req;
+ aes->info = &ctx->info;
+ aes->flags = (aes->flags & ~AES_FLAGS_MODE_MSK) | rctx->mode;
+
+ err = mtk_aes_map(cryp, aes);
+ if (err)
+ return err;
+
+ return mtk_aes_xmit(cryp, aes);
+}
+
+static void mtk_aes_unmap(struct mtk_cryp *cryp, struct mtk_aes *aes)
+{
+ dma_unmap_single(cryp->dev, aes->ct_dma,
+ sizeof(struct mtk_aes_info), DMA_TO_DEVICE);
+
+ if (aes->src.sg == aes->dst.sg) {
+ dma_unmap_sg(cryp->dev, aes->src.sg,
+ aes->src.nents, DMA_BIDIRECTIONAL);
+
+ if (aes->src.sg != &aes->aligned_sg)
+ mtk_aes_restore_sg(&aes->src);
+ } else {
+ dma_unmap_sg(cryp->dev, aes->dst.sg,
+ aes->dst.nents, DMA_FROM_DEVICE);
+
+ if (aes->dst.sg != &aes->aligned_sg)
+ mtk_aes_restore_sg(&aes->dst);
+
+ dma_unmap_sg(cryp->dev, aes->src.sg,
+ aes->src.nents, DMA_TO_DEVICE);
+
+ if (aes->src.sg != &aes->aligned_sg)
+ mtk_aes_restore_sg(&aes->src);
+ }
+
+ if (aes->dst.sg == &aes->aligned_sg)
+ sg_copy_from_buffer(aes->real_dst,
+ sg_nents(aes->real_dst),
+ aes->buf, aes->total);
+}
+
+static inline void mtk_aes_complete(struct mtk_cryp *cryp,
+ struct mtk_aes *aes)
+{
+ aes->flags &= ~AES_FLAGS_BUSY;
+
+ aes->req->base.complete(&aes->req->base, 0);
+
+ mtk_aes_handle_queue(cryp, aes->id, NULL);
+}
+
+static int mtk_aes_setkey(struct crypto_ablkcipher *tfm,
+ const u8 *key, u32 keylen)
+{
+ struct mtk_aes_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+ u8 *state = ctx->info.tfm.state;
+
+ if (keylen != AES_KEYSIZE_128 &&
+ keylen != AES_KEYSIZE_192 &&
+ keylen != AES_KEYSIZE_256) {
+ crypto_ablkcipher_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
+ return -EINVAL;
+ }
+
+ ctx->keylen = keylen;
+ memcpy(state, key, keylen);
+
+ return 0;
+}
+
+static int mtk_aes_crypt(struct ablkcipher_request *req, u64 mode)
+{
+ struct mtk_aes_ctx *ctx = crypto_ablkcipher_ctx(
+ crypto_ablkcipher_reqtfm(req));
+ struct mtk_aes_reqctx *rctx = ablkcipher_request_ctx(req);
+
+ rctx->mode = mode;
+
+ return mtk_aes_handle_queue(ctx->cryp,
+ !(mode & AES_FLAGS_ENCRYPT), req);
+}
+
+static int mtk_ecb_encrypt(struct ablkcipher_request *req)
+{
+ return mtk_aes_crypt(req, AES_FLAGS_ENCRYPT | AES_FLAGS_ECB);
+}
+
+static int mtk_ecb_decrypt(struct ablkcipher_request *req)
+{
+ return mtk_aes_crypt(req, AES_FLAGS_ECB);
+}
+
+static int mtk_cbc_encrypt(struct ablkcipher_request *req)
+{
+ return mtk_aes_crypt(req, AES_FLAGS_ENCRYPT | AES_FLAGS_CBC);
+}
+
+static int mtk_cbc_decrypt(struct ablkcipher_request *req)
+{
+ return mtk_aes_crypt(req, AES_FLAGS_CBC);
+}
+
+static int mtk_aes_cra_init(struct crypto_tfm *tfm)
+{
+ struct mtk_aes_ctx *ctx = crypto_tfm_ctx(tfm);
+ struct mtk_cryp *cryp = NULL;
+
+ tfm->crt_ablkcipher.reqsize = sizeof(struct mtk_aes_reqctx);
+
+ cryp = mtk_aes_find_dev(ctx);
+ if (!cryp) {
+ pr_err("can't find crypto device\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static struct crypto_alg aes_algs[] = {
+{
+ .cra_name = "cbc(aes)",
+ .cra_driver_name = "cbc-aes-mtk",
+ .cra_priority = 400,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_ASYNC,
+ .cra_init = mtk_aes_cra_init,
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct mtk_aes_ctx),
+ .cra_alignmask = 15,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_u.ablkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .setkey = mtk_aes_setkey,
+ .encrypt = mtk_cbc_encrypt,
+ .decrypt = mtk_cbc_decrypt,
+ .ivsize = AES_BLOCK_SIZE,
+ }
+},
+{
+ .cra_name = "ecb(aes)",
+ .cra_driver_name = "ecb-aes-mtk",
+ .cra_priority = 400,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+ CRYPTO_ALG_ASYNC,
+ .cra_init = mtk_aes_cra_init,
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct mtk_aes_ctx),
+ .cra_alignmask = 15,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_u.ablkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .setkey = mtk_aes_setkey,
+ .encrypt = mtk_ecb_encrypt,
+ .decrypt = mtk_ecb_decrypt,
+ }
+},
+};
+
+static void mtk_aes_enc_task(unsigned long data)
+{
+ struct mtk_cryp *cryp = (struct mtk_cryp *)data;
+ struct mtk_aes *aes = cryp->aes[0];
+
+ mtk_aes_unmap(cryp, aes);
+ mtk_aes_complete(cryp, aes);
+}
+
+static void mtk_aes_dec_task(unsigned long data)
+{
+ struct mtk_cryp *cryp = (struct mtk_cryp *)data;
+ struct mtk_aes *aes = cryp->aes[1];
+
+ mtk_aes_unmap(cryp, aes);
+ mtk_aes_complete(cryp, aes);
+}
+
+static irqreturn_t mtk_aes_enc_irq(int irq, void *dev_id)
+{
+ struct mtk_cryp *cryp = (struct mtk_cryp *)dev_id;
+ struct mtk_aes *aes = cryp->aes[0];
+ u32 val = mtk_aes_read(cryp, RDR_STAT(RING0));
+
+ mtk_aes_write(cryp, RDR_STAT(RING0), val);
+
+ if (likely(AES_FLAGS_BUSY & aes->flags)) {
+ mtk_aes_write(cryp, RDR_PROC_COUNT(RING0), MTK_DESC_CNT_CLR);
+ mtk_aes_write(cryp, RDR_THRESH(RING0), MTK_RDR_THRESH_DEF);
+
+ tasklet_schedule(&aes->task);
+ } else {
+ dev_warn(cryp->dev, "AES interrupt when no active requests.\n");
+ }
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mtk_aes_dec_irq(int irq, void *dev_id)
+{
+ struct mtk_cryp *cryp = (struct mtk_cryp *)dev_id;
+ struct mtk_aes *aes = cryp->aes[1];
+ u32 val = mtk_aes_read(cryp, RDR_STAT(RING1));
+
+ mtk_aes_write(cryp, RDR_STAT(RING1), val);
+
+ if (likely(AES_FLAGS_BUSY & aes->flags)) {
+ mtk_aes_write(cryp, RDR_PROC_COUNT(RING1), MTK_DESC_CNT_CLR);
+ mtk_aes_write(cryp, RDR_THRESH(RING1), MTK_RDR_THRESH_DEF);
+
+ tasklet_schedule(&aes->task);
+ } else {
+ dev_warn(cryp->dev, "AES interrupt when no active requests.\n");
+ }
+ return IRQ_HANDLED;
+}
+
+/* AES encryption record and decryption record */
+static int mtk_aes_record_init(struct mtk_cryp *cryp)
+{
+ struct mtk_aes **aes = cryp->aes;
+ int i, err = -ENOMEM;
+
+ for (i = 0; i < RECORD_NUM; i++) {
+ aes[i] = kzalloc(sizeof(**aes), GFP_KERNEL);
+ if (!aes[i])
+ goto err_cleanup;
+
+ aes[i]->buf = (void *)__get_free_pages(GFP_KERNEL,
+ AES_BUFFER_ORDER);
+ if (!aes[i]->buf)
+ goto err_cleanup;
+
+ aes[i]->id = i;
+
+ spin_lock_init(&aes[i]->lock);
+ crypto_init_queue(&aes[i]->queue, AES_QUEUE_LENGTH);
+ }
+
+ tasklet_init(&aes[0]->task, mtk_aes_enc_task, (unsigned long)cryp);
+ tasklet_init(&aes[1]->task, mtk_aes_dec_task, (unsigned long)cryp);
+
+ return 0;
+
+err_cleanup:
+ for (; i--; ) {
+ free_page((unsigned long)aes[i]->buf);
+ kfree(aes[i]);
+ }
+
+ return err;
+}
+
+static void mtk_aes_record_free(struct mtk_cryp *cryp)
+{
+ int i;
+
+ for (i = 0; i < RECORD_NUM; i++) {
+ tasklet_kill(&cryp->aes[i]->task);
+ free_page((unsigned long)cryp->aes[i]->buf);
+ kfree(cryp->aes[i]);
+ }
+}
+
+static void mtk_aes_unregister_algs(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(aes_algs); i++)
+ crypto_unregister_alg(&aes_algs[i]);
+}
+
+static int mtk_aes_register_algs(void)
+{
+ int err, i, j;
+
+ for (i = 0; i < ARRAY_SIZE(aes_algs); i++) {
+ err = crypto_register_alg(&aes_algs[i]);
+ if (err)
+ goto err_aes_algs;
+ }
+
+ return 0;
+
+err_aes_algs:
+ for (j = 0; j < i; j++)
+ crypto_unregister_alg(&aes_algs[j]);
+
+ return err;
+}
+
+int mtk_cipher_alg_register(struct mtk_cryp *cryp)
+{
+ int ret;
+
+ INIT_LIST_HEAD(&cryp->aes_list);
+
+ ret = mtk_aes_record_init(cryp);
+ if (ret)
+ goto err_record;
+
+ ret = devm_request_irq(cryp->dev, cryp->irq[RING0], mtk_aes_enc_irq,
+ IRQF_TRIGGER_LOW, "mtk-aes", cryp);
+ if (ret) {
+ dev_err(cryp->dev, "unable to request AES encryption irq.\n");
+ goto err_res;
+ }
+
+ ret = devm_request_irq(cryp->dev, cryp->irq[RING1], mtk_aes_dec_irq,
+ IRQF_TRIGGER_LOW, "mtk-aes", cryp);
+ if (ret) {
+ dev_err(cryp->dev, "unable to request AES decryption irq.\n");
+ goto err_res;
+ }
+
+ /* enable ring0 and ring1 interrupt for cipher */
+ mtk_aes_write(cryp, AIC_ENABLE_SET(RING0), MTK_IRQ_RDR0);
+ mtk_aes_write(cryp, AIC_ENABLE_SET(RING1), MTK_IRQ_RDR1);
+
+ spin_lock(&mtk_aes.lock);
+ list_add_tail(&cryp->aes_list, &mtk_aes.dev_list);
+ spin_unlock(&mtk_aes.lock);
+
+ ret = mtk_aes_register_algs();
+ if (ret)
+ goto err_algs;
+
+ return 0;
+
+err_algs:
+ spin_lock(&mtk_aes.lock);
+ list_del(&cryp->aes_list);
+ spin_unlock(&mtk_aes.lock);
+err_res:
+ mtk_aes_record_free(cryp);
+err_record:
+
+ dev_err(cryp->dev, "mtk-aes initialization failed.\n");
+ return ret;
+}
+EXPORT_SYMBOL(mtk_cipher_alg_register);
+
+void mtk_cipher_alg_release(struct mtk_cryp *cryp)
+{
+ spin_lock(&mtk_aes.lock);
+ list_del(&cryp->aes_list);
+ spin_unlock(&mtk_aes.lock);
+
+ mtk_aes_unregister_algs();
+ mtk_aes_record_free(cryp);
+}
+EXPORT_SYMBOL(mtk_cipher_alg_release);
diff --git a/drivers/crypto/mediatek/mtk-platform.c b/drivers/crypto/mediatek/mtk-platform.c
new file mode 100644
index 0000000..d3a465f
--- /dev/null
+++ b/drivers/crypto/mediatek/mtk-platform.c
@@ -0,0 +1,575 @@
+/*
+ * Support for MediaTek cryptographic accelerator.
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Ryder Lee <ryder.lee-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include "mtk-platform.h"
+#include "mtk-regs.h"
+
+#define MTK_BURST_SIZE(x, y) (((x) & ~0xf0) | ((y) << 4))
+#define MTK_DESC_SIZE_SET(x) ((x) << 0)
+#define MTK_DESC_OFFSET_SET(x) ((x) << 16)
+#define MTK_DFSE_RING_ID(x) (((x) >> 12) & 0xf)
+#define MTK_DSE_MIN_DATA(x) ((x) << 0)
+#define MTK_DSE_MAX_DATA(x) ((x) << 8)
+#define MTK_DFE_MIN_DATA(x) ((x) << 0)
+#define MTK_DFE_MAX_DATA(x) ((x) << 8)
+#define MTK_DFE_MIN_CTRL(x) ((x) << 16)
+#define MTK_DFE_MAX_CTRL(x) ((x) << 24)
+#define MTK_FETCH_SIZE_SET(x) ((x) << 0)
+#define MTK_FETCH_THRESH_SET(x) ((x) << 16)
+#define MTK_IN_BUF_MIN_THRESH(x) ((x) << 8)
+#define MTK_IN_BUF_MAX_THRESH(x) ((x) << 12)
+#define MTK_OUT_BUF_MIN_THRESH(x) ((x) << 0)
+#define MTK_OUT_BUF_MAX_THRESH(x) ((x) << 4)
+#define MTK_CMD_FIFO_SIZE(x) (((x) >> 8) & 0xf)
+#define MTK_RES_FIFO_SIZE(x) (((x) >> 12) & 0xf)
+#define MTK_HIA_DATA_WIDTH(x) (((x) >> 25) & 0x3)
+#define MTK_HIA_DMA_LENGTH(x) (((x) >> 20) & 0x1f)
+#define MTK_IN_TBUF_SIZE(x) (((x) >> 4) & 0xf)
+#define MTK_IN_DBUF_SIZE(x) (((x) >> 8) & 0xf)
+#define MTK_OUT_DBUF_SIZE(x) (((x) >> 16) & 0xf)
+#define MTK_AIC_INT_NUM(x) ((x) & 0x3f)
+#define MTK_AIC_VER_GET(x) ((x) & 0x0ff0ffff)
+#define MTK_PE_TOKEN_CTRL_DEF 0x00014004
+#define MTK_PE_INT_CTRL_DEF 0xc00f400f
+#define MTK_PRNG_CTRL_EN BIT(0)
+#define MTK_PRNG_CTRL_AUTO BIT(1)
+#define MTK_TOKEN_TIMEOUT_EN BIT(22)
+#define MTK_OVL_IRQ_EN BIT(25)
+#define MTK_ATP_PRESENT BIT(30)
+#define MTK_DFSE_THR_CTRL_EN BIT(30)
+#define MTK_DFSE_THR_CTRL_RESET BIT(31)
+#define MTK_HIA_SIGNATURE ((u16)0x35ca)
+#define MTK_CDR_STAT_CLR 0x1f
+#define MTK_RDR_STAT_CLR 0xff
+#define MTK_AIC_VER11 0x011036C9
+#define MTK_AIC_VER12 0x012036C9
+#define MTK_AIC_GLOBAL_CLR 0x7FF00000
+#define MTK_DFSE_IDLE 0xf
+
+/* Cryptographic engine capabilities */
+struct mtk_sys_cap {
+ /* Host interface adapter */
+ u32 hia_ver;
+ u32 hia_opt;
+
+ /* Packet engine */
+ u32 pkt_eng_opt;
+
+ /* Global hardware */
+ u32 hw_opt;
+};
+
+static void mtk_prng_reseed(struct mtk_cryp *cryp)
+{
+ /* 8 words to seed the PRNG to provide IVs */
+ void __iomem *base = cryp->base;
+ const u32 prng_key[8] = {0x48c24cfd, 0x6c07f742,
+ 0xaee75681, 0x0f27c239,
+ 0x79947198, 0xe2991275,
+ 0x21ac3c7c, 0xd008c4b4};
+
+ /* Disable PRNG and set PRNG manual mode */
+ writel(0, base + PE_PRNG_CTRL);
+
+ /* Write new seed data */
+ writel(prng_key[0], base + PE_PRNG_SEED_L);
+ writel(prng_key[1], base + PE_PRNG_SEED_H);
+
+ /* Write new key data */
+ writel(prng_key[2], base + PE_PRNG_KEY_0_L);
+ writel(prng_key[3], base + PE_PRNG_KEY_0_H);
+ writel(prng_key[4], base + PE_PRNG_KEY_1_L);
+ writel(prng_key[5], base + PE_PRNG_KEY_1_H);
+
+ /* Write new LFSR data */
+ writel(prng_key[6], base + PE_PRNG_LFSR_L);
+ writel(prng_key[7], base + PE_PRNG_LFSR_H);
+
+ /* Enable PRNG and set PRNG auto mode */
+ writel(MTK_PRNG_CTRL_EN | MTK_PRNG_CTRL_AUTO, base + PE_PRNG_CTRL);
+}
+
+static void mtk_desc_ring_link(struct mtk_cryp *cryp, u32 mask)
+{
+ /* Assign rings to DFE DSE thread and enable it */
+ writel(MTK_DFSE_THR_CTRL_EN | mask, cryp->base + DFE_THR_CTRL);
+ writel(MTK_DFSE_THR_CTRL_EN | mask, cryp->base + DSE_THR_CTRL);
+}
+
+static void mtk_dfe_dse_buf_setup(struct mtk_cryp *cryp,
+ struct mtk_sys_cap *cap)
+{
+ u32 width = MTK_HIA_DATA_WIDTH(cap->hia_opt) + 2;
+ u32 len = MTK_HIA_DMA_LENGTH(cap->hia_opt) - 1;
+ u32 ipbuf = min(MTK_IN_DBUF_SIZE(cap->hw_opt) + width, len);
+ u32 opbuf = min(MTK_OUT_DBUF_SIZE(cap->hw_opt) + width, len);
+ u32 itbuf = min(MTK_IN_TBUF_SIZE(cap->hw_opt) + width, len);
+ u32 val;
+
+ val = MTK_DFE_MIN_DATA(ipbuf - 1) | MTK_DFE_MAX_DATA(ipbuf) |
+ MTK_DFE_MIN_CTRL(itbuf - 1) | MTK_DFE_MAX_CTRL(itbuf);
+ writel(val, cryp->base + DFE_CFG);
+
+ val = MTK_DFE_MIN_DATA(opbuf - 1) | MTK_DFE_MAX_DATA(opbuf);
+ writel(val, cryp->base + DSE_CFG);
+
+ val = MTK_IN_BUF_MIN_THRESH(ipbuf - 1) | MTK_IN_BUF_MAX_THRESH(ipbuf);
+ writel(val, cryp->base + PE_IN_DBUF_THRESH);
+
+ val = MTK_IN_BUF_MIN_THRESH(itbuf - 1) | MTK_IN_BUF_MAX_THRESH(itbuf);
+ writel(val, cryp->base + PE_IN_TBUF_THRESH);
+
+ val = MTK_OUT_BUF_MIN_THRESH(opbuf - 1) | MTK_OUT_BUF_MAX_THRESH(opbuf);
+ writel(val, cryp->base + PE_OUT_DBUF_THRESH);
+
+ writel(0, cryp->base + PE_OUT_TBUF_THRESH);
+ writel(0, cryp->base + PE_OUT_BUF_CTRL);
+}
+
+static int mtk_dfe_dse_state_check(struct mtk_cryp *cryp)
+{
+ int ret = -EINVAL;
+ u32 val;
+
+ /* Check for completion of all DMA transfers */
+ val = readl(cryp->base + DFE_THR_STAT);
+ if (MTK_DFSE_RING_ID(val) == MTK_DFSE_IDLE) {
+ val = readl(cryp->base + DSE_THR_STAT);
+ if (MTK_DFSE_RING_ID(val) == MTK_DFSE_IDLE)
+ ret = 0;
+ }
+
+ if (!ret) {
+ /* Take DFE and DSE thread out of reset */
+ writel(0, cryp->base + DFE_THR_CTRL);
+ writel(0, cryp->base + DSE_THR_CTRL);
+ } else {
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static int mtk_dfe_dse_reset(struct mtk_cryp *cryp)
+{
+ int err;
+
+ /* Reset DSE DFE and correct system priorities for all rings. */
+ writel(MTK_DFSE_THR_CTRL_RESET, cryp->base + DFE_THR_CTRL);
+ writel(0, cryp->base + DFE_PRIO_0);
+ writel(0, cryp->base + DFE_PRIO_1);
+ writel(0, cryp->base + DFE_PRIO_2);
+ writel(0, cryp->base + DFE_PRIO_3);
+
+ writel(MTK_DFSE_THR_CTRL_RESET, cryp->base + DSE_THR_CTRL);
+ writel(0, cryp->base + DSE_PRIO_0);
+ writel(0, cryp->base + DSE_PRIO_1);
+ writel(0, cryp->base + DSE_PRIO_2);
+ writel(0, cryp->base + DSE_PRIO_3);
+
+ err = mtk_dfe_dse_state_check(cryp);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static void mtk_cmd_desc_ring_setup(struct mtk_cryp *cryp,
+ int i, struct mtk_sys_cap *cap)
+{
+ /* Full descriptor that fits FIFO minus one */
+ u32 count =
+ ((1 << MTK_CMD_FIFO_SIZE(cap->hia_opt)) / MTK_DESC_SIZE) - 1;
+ u32 size = count * MTK_DESC_OFFSET;
+ u32 thresh = count * MTK_DESC_SIZE;
+ u32 val;
+
+ /* Temporarily disable external triggering */
+ writel(0, cryp->base + CDR_CFG(i));
+
+ /* Clear CDR count */
+ writel(MTK_DESC_CNT_CLR, cryp->base + CDR_PREP_COUNT(i));
+ writel(MTK_DESC_CNT_CLR, cryp->base + CDR_PROC_COUNT(i));
+
+ writel(0, cryp->base + CDR_PREP_PNTR(i));
+ writel(0, cryp->base + CDR_PROC_PNTR(i));
+ writel(0, cryp->base + CDR_DMA_CFG(i));
+
+ /* Configure command ring host address space */
+ writel(0, cryp->base + CDR_BASE_ADDR_HI(i));
+ writel(cryp->ring[i]->cmd_dma, cryp->base + CDR_BASE_ADDR_LO(i));
+
+ writel(MTK_MAX_RING_SIZE, cryp->base + CDR_RING_SIZE(i));
+
+ /* Clear and disable all CDR interrupts */
+ writel(MTK_CDR_STAT_CLR, cryp->base + CDR_STAT(i));
+
+ /*
+ * Set command descriptor offset and enable additional
+ * token present in descriptor.
+ */
+ val = MTK_DESC_SIZE_SET(MTK_DESC_SIZE) |
+ MTK_DESC_OFFSET_SET(MTK_DESC_OFFSET) |
+ MTK_ATP_PRESENT;
+ writel(val, cryp->base + CDR_DESC_SIZE(i));
+
+ val = MTK_FETCH_SIZE_SET(size) | MTK_FETCH_THRESH_SET(thresh);
+ writel(val, cryp->base + CDR_CFG(i));
+}
+
+static void mtk_res_desc_ring_setup(struct mtk_cryp *cryp,
+ int i, struct mtk_sys_cap *cap)
+{
+ u32 rndup = 2;
+ u32 count = ((1 << MTK_RES_FIFO_SIZE(cap->hia_opt)) / rndup) - 1;
+ u32 size = count * MTK_DESC_OFFSET;
+ u32 thresh = count * rndup;
+ u32 val;
+
+ writel(0, cryp->base + RDR_CFG(i));
+
+ writel(MTK_DESC_CNT_CLR, cryp->base + RDR_PREP_COUNT(i));
+ writel(MTK_DESC_CNT_CLR, cryp->base + RDR_PROC_COUNT(i));
+
+ writel(0, cryp->base + RDR_PREP_PNTR(i));
+ writel(0, cryp->base + RDR_PROC_PNTR(i));
+ writel(0, cryp->base + RDR_DMA_CFG(i));
+
+ writel(0, cryp->base + RDR_BASE_ADDR_HI(i));
+ writel(cryp->ring[i]->res_dma, cryp->base + RDR_BASE_ADDR_LO(i));
+
+ writel(MTK_MAX_RING_SIZE, cryp->base + RDR_RING_SIZE(i));
+ writel(MTK_RDR_STAT_CLR, cryp->base + RDR_STAT(i));
+
+ /*
+ * RDR manager generates update interrupts on a per-completed-packet,
+ * and the rd_proc_thresh_irq interrupt is fired when proc_pkt_count
+ * for the RDR exceeds the number of packets.
+ */
+ writel(MTK_RDR_THRESH_DEF, cryp->base + RDR_THRESH(i));
+
+ /*
+ * Configure a threshold and time-out value for the processed
+ * result descriptors (or complete packets) that are written to
+ * the RDR.
+ */
+ val = MTK_DESC_SIZE_SET(MTK_DESC_SIZE) |
+ MTK_DESC_OFFSET_SET(MTK_DESC_OFFSET);
+ writel(val, cryp->base + RDR_DESC_SIZE(i));
+
+ /*
+ * Configure HIA fetch size and fetch threshold that are used to
+ * fetch blocks of multiple descriptors.
+ */
+ val = MTK_FETCH_SIZE_SET(size) |
+ MTK_FETCH_THRESH_SET(thresh) |
+ MTK_OVL_IRQ_EN;
+ writel(val, cryp->base + RDR_CFG(i));
+}
+
+static int mtk_packet_engine_setup(struct mtk_cryp *cryp)
+{
+ struct mtk_sys_cap cap;
+ int i, err;
+ u32 val;
+
+ cap.hia_ver = readl(cryp->base + HIA_VERSION);
+ cap.hia_opt = readl(cryp->base + HIA_OPTIONS);
+ cap.hw_opt = readl(cryp->base + EIP97_OPTIONS);
+
+ if (!(((u16)cap.hia_ver) == MTK_HIA_SIGNATURE))
+ return -EINVAL;
+
+ /* Configure endianness conversion method for master (DMA) interface */
+ writel(0, cryp->base + EIP97_MST_CTRL);
+
+ /* Set HIA burst size */
+ val = readl(cryp->base + HIA_MST_CTRL);
+ writel(MTK_BURST_SIZE(val, 5), cryp->base + HIA_MST_CTRL);
+
+ err = mtk_dfe_dse_reset(cryp);
+ if (err) {
+ dev_err(cryp->dev, "Failed to reset DFE and DSE.\n");
+ return err;
+ }
+
+ mtk_dfe_dse_buf_setup(cryp, &cap);
+
+ /* Enable the 4 rings for the packet engines. */
+ mtk_desc_ring_link(cryp, 0xf);
+
+ for (i = 0; i < RING_MAX; i++) {
+ mtk_cmd_desc_ring_setup(cryp, i, &cap);
+ mtk_res_desc_ring_setup(cryp, i, &cap);
+ }
+
+ val = MTK_PE_TOKEN_CTRL_DEF | MTK_TOKEN_TIMEOUT_EN;
+ writel(val, cryp->base + PE_TOKEN_CTRL_STAT);
+
+ /* Clear all pending interrupts */
+ writel(MTK_AIC_GLOBAL_CLR, cryp->base + AIC_G_ACK);
+ writel(MTK_PE_INT_CTRL_DEF, cryp->base + PE_INTERRUPT_CTRL_STAT);
+
+ return 0;
+}
+
+static int mtk_aic_cap_check(struct mtk_cryp *cryp, int hw)
+{
+ u32 val;
+
+ if (hw == RING_MAX)
+ val = readl(cryp->base + AIC_G_VERSION);
+ else
+ val = readl(cryp->base + AIC_VERSION(hw));
+
+ val = MTK_AIC_VER_GET(val);
+ if (val != MTK_AIC_VER11 && val != MTK_AIC_VER12)
+ return -ENXIO;
+
+ if (hw == RING_MAX)
+ val = readl(cryp->base + AIC_G_OPTIONS);
+ else
+ val = readl(cryp->base + AIC_OPTIONS(hw));
+
+ val = MTK_AIC_INT_NUM(val);
+ if (!val || val > 32)
+ return -ENXIO;
+
+ return 0;
+}
+
+static int mtk_aic_init(struct mtk_cryp *cryp, int hw)
+{
+ int err;
+
+ err = mtk_aic_cap_check(cryp, hw);
+ if (err)
+ return err;
+
+ /* Disable all interrupts and set initial configuration */
+ if (hw == RING_MAX) {
+ writel(0, cryp->base + AIC_G_ENABLE_CTRL);
+ writel(0, cryp->base + AIC_G_POL_CTRL);
+ writel(0, cryp->base + AIC_G_TYPE_CTRL);
+ writel(0, cryp->base + AIC_G_ENABLE_SET);
+ } else {
+ writel(0, cryp->base + AIC_ENABLE_CTRL(hw));
+ writel(0, cryp->base + AIC_POL_CTRL(hw));
+ writel(0, cryp->base + AIC_TYPE_CTRL(hw));
+ writel(0, cryp->base + AIC_ENABLE_SET(hw));
+ }
+
+ return 0;
+}
+
+static int mtk_accelerator_init(struct mtk_cryp *cryp)
+{
+ int i, err;
+
+ /* Initialize advanced interrupt controller(AIC) */
+ for (i = 0; i < 5; i++) {
+ err = mtk_aic_init(cryp, i);
+ if (err) {
+ dev_err(cryp->dev, "Failed to initialize AIC.\n");
+ return err;
+ }
+ }
+
+ /* Initialize packet engine */
+ err = mtk_packet_engine_setup(cryp);
+ if (err) {
+ dev_err(cryp->dev, "Failed to configure packet engine.\n");
+ return err;
+ }
+
+ mtk_prng_reseed(cryp);
+ return 0;
+}
+
+static void mtk_desc_dma_free(struct mtk_cryp *cryp)
+{
+ int i;
+
+ for (i = 0; i < RING_MAX; i++) {
+ dma_free_coherent(cryp->dev, MTK_MAX_RING_SIZE,
+ cryp->ring[i]->res_base,
+ cryp->ring[i]->res_dma);
+ dma_free_coherent(cryp->dev, MTK_MAX_RING_SIZE,
+ cryp->ring[i]->cmd_base,
+ cryp->ring[i]->cmd_dma);
+ kfree(cryp->ring[i]);
+ }
+}
+
+static int mtk_desc_ring_alloc(struct mtk_cryp *cryp)
+{
+ struct mtk_ring **ring = cryp->ring;
+ int i, err = ENOMEM;
+
+ for (i = 0; i < RING_MAX; i++) {
+ ring[i] = kzalloc(sizeof(**ring), GFP_KERNEL);
+ if (!ring[i])
+ goto err_cleanup;
+
+ ring[i]->cmd_base = dma_zalloc_coherent(cryp->dev,
+ MTK_MAX_RING_SIZE, &ring[i]->cmd_dma,
+ GFP_KERNEL);
+ if (!ring[i]->cmd_base)
+ goto err_cleanup;
+
+ ring[i]->res_base = dma_zalloc_coherent(cryp->dev,
+ MTK_MAX_RING_SIZE, &ring[i]->res_dma,
+ GFP_KERNEL);
+ if (!ring[i]->res_base)
+ goto err_cleanup;
+ }
+ return 0;
+
+err_cleanup:
+ for (; i--; ) {
+ dma_free_coherent(cryp->dev, MTK_MAX_RING_SIZE,
+ ring[i]->res_base, ring[i]->res_dma);
+ dma_free_coherent(cryp->dev, MTK_MAX_RING_SIZE,
+ ring[i]->cmd_base, ring[i]->cmd_dma);
+ kfree(ring[i]);
+ }
+ return err;
+}
+
+static int mtk_crypto_probe(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct mtk_cryp *cryp;
+ int i, err;
+
+ cryp = devm_kzalloc(&pdev->dev, sizeof(*cryp), GFP_KERNEL);
+ if (!cryp)
+ return -ENOMEM;
+
+ cryp->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(cryp->base))
+ return PTR_ERR(cryp->base);
+
+ for (i = 0; i < 5; i++) {
+ cryp->irq[i] = platform_get_irq(pdev, i);
+ if (cryp->irq[i] < 0) {
+ dev_err(cryp->dev, "no IRQ:%d resource info\n", i);
+ return -ENXIO;
+ }
+ }
+
+ cryp->clk_ethif = devm_clk_get(&pdev->dev, "ethif");
+ cryp->clk_cryp = devm_clk_get(&pdev->dev, "cryp");
+ if (IS_ERR(cryp->clk_ethif) || IS_ERR(cryp->clk_cryp))
+ return -EPROBE_DEFER;
+
+ cryp->dev = &pdev->dev;
+ pm_runtime_enable(cryp->dev);
+ pm_runtime_get_sync(cryp->dev);
+
+ err = clk_prepare_enable(cryp->clk_ethif);
+ if (err)
+ goto err_clk_ethif;
+
+ err = clk_prepare_enable(cryp->clk_cryp);
+ if (err)
+ goto err_clk_cryp;
+
+ err = mtk_desc_ring_alloc(cryp);
+ if (err) {
+ dev_err(cryp->dev, "Unable to allocate descriptor rings.\n");
+ goto err_resource;
+ }
+
+ err = mtk_accelerator_init(cryp);
+ if (err) {
+ dev_err(cryp->dev, "Failed to initialize cryptographic engine.\n");
+ goto err_engine;
+ }
+
+ err = mtk_cipher_alg_register(cryp);
+ if (err) {
+ dev_err(cryp->dev, "Unable to register MTK-AES.\n");
+ goto err_cipher;
+ }
+
+ err = mtk_hash_alg_register(cryp);
+ if (err) {
+ dev_err(cryp->dev, "Unable to register MTK-SHA.\n");
+ goto err_hash;
+ }
+
+ platform_set_drvdata(pdev, cryp);
+ return 0;
+
+err_hash:
+ mtk_cipher_alg_release(cryp);
+err_cipher:
+ mtk_dfe_dse_reset(cryp);
+err_engine:
+ mtk_desc_dma_free(cryp);
+err_resource:
+ clk_disable_unprepare(cryp->clk_cryp);
+err_clk_cryp:
+ clk_disable_unprepare(cryp->clk_ethif);
+err_clk_ethif:
+ pm_runtime_put_sync(cryp->dev);
+ pm_runtime_disable(cryp->dev);
+
+ return err;
+}
+
+static int mtk_crypto_remove(struct platform_device *pdev)
+{
+ struct mtk_cryp *cryp = platform_get_drvdata(pdev);
+
+ mtk_hash_alg_release(cryp);
+ mtk_cipher_alg_release(cryp);
+ mtk_desc_dma_free(cryp);
+
+ clk_disable_unprepare(cryp->clk_cryp);
+ clk_disable_unprepare(cryp->clk_ethif);
+
+ pm_runtime_put_sync(cryp->dev);
+ pm_runtime_disable(cryp->dev);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+const struct of_device_id of_crypto_id[] = {
+ { .compatible = "mediatek,mt7623-crypto" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, of_crypto_id);
+
+static struct platform_driver mtk_crypto_driver = {
+ .probe = mtk_crypto_probe,
+ .remove = mtk_crypto_remove,
+ .driver = {
+ .name = "mtk-crypto",
+ .owner = THIS_MODULE,
+ .of_match_table = of_crypto_id,
+ },
+};
+module_platform_driver(mtk_crypto_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ryder Lee <ryder.lee-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>");
+MODULE_DESCRIPTION("Cryptographic accelerator driver for MediaTek SoC");
diff --git a/drivers/crypto/mediatek/mtk-platform.h b/drivers/crypto/mediatek/mtk-platform.h
new file mode 100644
index 0000000..5390ef4
--- /dev/null
+++ b/drivers/crypto/mediatek/mtk-platform.h
@@ -0,0 +1,230 @@
+/*
+ * Support for MediaTek cryptographic accelerator.
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Ryder Lee <ryder.lee-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ *
+ */
+
+#ifndef __MTK_PLATFORM_H_
+#define __MTK_PLATFORM_H_
+
+#include <linux/crypto.h>
+#include <crypto/internal/hash.h>
+#include <linux/interrupt.h>
+
+#define MTK_RDR_THRESH_DEF 0x800001
+
+#define MTK_IRQ_RDR0 BIT(1)
+#define MTK_IRQ_RDR1 BIT(3)
+#define MTK_IRQ_RDR2 BIT(5)
+#define MTK_IRQ_RDR3 BIT(7)
+
+#define MTK_DESC_CNT_CLR BIT(31)
+#define MTK_DESC_LAST BIT(22)
+#define MTK_DESC_FIRST BIT(23)
+#define MTK_DESC_BUF_LEN(x) ((x) & 0x1ffff)
+#define MTK_DESC_CT_LEN(x) (((x) & 0xff) << 24)
+
+#define WORD(x) ((x) >> 2)
+
+/**
+ * Ring 0/1 are used by AES encrypt and decrypt.
+ * Ring 2/3 are used by SHA.
+ */
+enum {
+ RING0 = 0,
+ RING1,
+ RING2,
+ RING3,
+ RING_MAX,
+};
+
+#define RECORD_NUM (RING_MAX / 2)
+
+/**
+ * struct mtk_desc - DMA descriptor
+ * @hdr: the descriptor control header
+ * @buf: DMA address of input buffer
+ * @ct: the command token that control operation flow
+ * @ct_hdr: the command token control header
+ * @tag: the user-defined field
+ * @tfm: DMA address of transform state
+ * @bound: align descriptors offset boundary
+ *
+ * Structure passed to the crypto engine to describe the crypto
+ * operation to be executed.
+ */
+struct mtk_desc {
+ u32 hdr;
+ u32 buf;
+ u32 ct;
+ u32 ct_hdr;
+ u32 tag;
+ u32 tfm;
+ u32 bound[2];
+};
+
+/**
+ * struct mtk_ring - Descriptor ring
+ * @cmd_base: pointer to command descriptor ring base
+ * @cmd_dma: DMA address of command descriptor ring
+ * @res_base: pointer to result descriptor ring base
+ * @res_dma: DMA address of result descriptor ring
+ * @pos: current position in the ring
+ */
+struct mtk_ring {
+ struct mtk_desc *cmd_base;
+ dma_addr_t cmd_dma;
+ struct mtk_desc *res_base;
+ dma_addr_t res_dma;
+ u32 pos;
+};
+
+#define MTK_MAX_DESC_NUM 512
+#define MTK_DESC_OFFSET WORD(sizeof(struct mtk_desc))
+#define MTK_DESC_SIZE (MTK_DESC_OFFSET - 2)
+#define MTK_MAX_RING_SIZE ((sizeof(struct mtk_desc) * MTK_MAX_DESC_NUM))
+#define MTK_DESC_CNT(x) ((MTK_DESC_OFFSET * (x)) << 2)
+
+/**
+ * struct mtk_aes_dma - Structure that holds sg list info
+ * @sg: pointer to scatter-gather list
+ * @nents: number of entries in the sg list
+ * @remainder: remainder of sg list
+ * @sg_len: number of entries in the sg mapped list
+ */
+struct mtk_aes_dma {
+ struct scatterlist *sg;
+ int nents;
+ u32 remainder;
+ u32 sg_len;
+};
+
+/**
+ * struct mtk_aes - AES operation record
+ * @queue: crypto request queue
+ * @req: pointer to ablkcipher request
+ * @task: the tasklet is use in AES interrupt
+ * @src: the structure that holds source sg list info
+ * @dst: the structure that holds destination sg list info
+ * @aligned_sg: the scatter list is use to alignment
+ * @real_dst: pointer to the destination sg list
+ * @total: request buffer length
+ * @buf: pointer to page buffer
+ * @info: pointer to AES transform state and command token
+ * @ct_hdr: AES command token control field
+ * @ct_size: size of AES command token
+ * @ct_dma: DMA address of AES command token
+ * @tfm_dma: DMA address of AES transform state
+ * @id: record identification
+ * @flags: it's describing AES operation state
+ * @lock: the ablkcipher queue lock
+ *
+ * Structure used to record AES execution state
+ */
+struct mtk_aes {
+ struct crypto_queue queue;
+ struct ablkcipher_request *req;
+ struct tasklet_struct task;
+ struct mtk_aes_dma src;
+ struct mtk_aes_dma dst;
+
+ struct scatterlist aligned_sg;
+ struct scatterlist *real_dst;
+
+ size_t total;
+ void *buf;
+
+ void *info;
+ u32 ct_hdr;
+ u32 ct_size;
+ dma_addr_t ct_dma;
+ dma_addr_t tfm_dma;
+
+ u8 id;
+ unsigned long flags;
+ /* queue lock */
+ spinlock_t lock;
+};
+
+/**
+ * struct mtk_sha - SHA operation record
+ * @queue: crypto request queue
+ * @req: pointer to ahash request
+ * @task: the tasklet is use in SHA interrupt
+ * @info: pointer to SHA transform state and command token
+ * @ct_hdr: SHA command token control field
+ * @ct_size: size of SHA command token
+ * @ct_dma: DMA address of SHA command token
+ * @tfm_dma: DMA address of SHA transform state
+ * @id: record identification
+ * @flags: it's describing SHA operation state
+ * @lock: the ablkcipher queue lock
+ *
+ * Structure used to record SHA execution state.
+ */
+struct mtk_sha {
+ struct crypto_queue queue;
+ struct ahash_request *req;
+ struct tasklet_struct task;
+
+ void *info;
+ u32 ct_hdr;
+ u32 ct_size;
+ dma_addr_t ct_dma;
+ dma_addr_t tfm_dma;
+
+ u8 id;
+ unsigned long flags;
+ /* queue lock */
+ spinlock_t lock;
+};
+
+/**
+ * struct mtk_cryp - Cryptographic device
+ * @base: pointer to mapped register I/O base
+ * @dev: pointer to device
+ * @clk_ethif: pointer to ethif clock
+ * @clk_cryp: pointer to crypto clock
+ * @irq: global system and rings IRQ
+ * @ring: pointer to execution state of AES
+ * @aes: pointer to execution state of SHA
+ * @sha: each execution record map to a ring
+ * @aes_list: device list of AES
+ * @sha_list: device list of SHA
+ * @tmp: pointer to temporary buffer for internal use
+ * @tmp_dma: DMA address of temporary buffer
+ * @rec: it's used to select SHA record for tfm
+ *
+ * Structure storing cryptographic device information.
+ */
+struct mtk_cryp {
+ void __iomem *base;
+ struct device *dev;
+ struct clk *clk_ethif;
+ struct clk *clk_cryp;
+ int irq[5];
+
+ struct mtk_ring *ring[RING_MAX];
+ struct mtk_aes *aes[RECORD_NUM];
+ struct mtk_sha *sha[RECORD_NUM];
+
+ struct list_head aes_list;
+ struct list_head sha_list;
+
+ void *tmp;
+ dma_addr_t tmp_dma;
+ bool rec;
+};
+
+int mtk_cipher_alg_register(struct mtk_cryp *cryp);
+void mtk_cipher_alg_release(struct mtk_cryp *cryp);
+int mtk_hash_alg_register(struct mtk_cryp *cryp);
+void mtk_hash_alg_release(struct mtk_cryp *cryp);
+
+#endif /* __MTK_PLATFORM_H_ */
diff --git a/drivers/crypto/mediatek/mtk-regs.h b/drivers/crypto/mediatek/mtk-regs.h
new file mode 100644
index 0000000..94f4eb8
--- /dev/null
+++ b/drivers/crypto/mediatek/mtk-regs.h
@@ -0,0 +1,194 @@
+/*
+ * Support for MediaTek cryptographic accelerator.
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Ryder Lee <ryder.lee-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ *
+ */
+
+#ifndef __MTK_REGS_H__
+#define __MTK_REGS_H__
+
+/* HIA, Command Descriptor Ring Manager */
+#define CDR_BASE_ADDR_LO(x) (0x0 + ((x) << 12))
+#define CDR_BASE_ADDR_HI(x) (0x4 + ((x) << 12))
+#define CDR_DATA_BASE_ADDR_LO(x) (0x8 + ((x) << 12))
+#define CDR_DATA_BASE_ADDR_HI(x) (0xC + ((x) << 12))
+#define CDR_ACD_BASE_ADDR_LO(x) (0x10 + ((x) << 12))
+#define CDR_ACD_BASE_ADDR_HI(x) (0x14 + ((x) << 12))
+#define CDR_RING_SIZE(x) (0x18 + ((x) << 12))
+#define CDR_DESC_SIZE(x) (0x1C + ((x) << 12))
+#define CDR_CFG(x) (0x20 + ((x) << 12))
+#define CDR_DMA_CFG(x) (0x24 + ((x) << 12))
+#define CDR_THRESH(x) (0x28 + ((x) << 12))
+#define CDR_PREP_COUNT(x) (0x2C + ((x) << 12))
+#define CDR_PROC_COUNT(x) (0x30 + ((x) << 12))
+#define CDR_PREP_PNTR(x) (0x34 + ((x) << 12))
+#define CDR_PROC_PNTR(x) (0x38 + ((x) << 12))
+#define CDR_STAT(x) (0x3C + ((x) << 12))
+
+/* HIA, Result Descriptor Ring Manager */
+#define RDR_BASE_ADDR_LO(x) (0x800 + ((x) << 12))
+#define RDR_BASE_ADDR_HI(x) (0x804 + ((x) << 12))
+#define RDR_DATA_BASE_ADDR_LO(x) (0x808 + ((x) << 12))
+#define RDR_DATA_BASE_ADDR_HI(x) (0x80C + ((x) << 12))
+#define RDR_ACD_BASE_ADDR_LO(x) (0x810 + ((x) << 12))
+#define RDR_ACD_BASE_ADDR_HI(x) (0x814 + ((x) << 12))
+#define RDR_RING_SIZE(x) (0x818 + ((x) << 12))
+#define RDR_DESC_SIZE(x) (0x81C + ((x) << 12))
+#define RDR_CFG(x) (0x820 + ((x) << 12))
+#define RDR_DMA_CFG(x) (0x824 + ((x) << 12))
+#define RDR_THRESH(x) (0x828 + ((x) << 12))
+#define RDR_PREP_COUNT(x) (0x82C + ((x) << 12))
+#define RDR_PROC_COUNT(x) (0x830 + ((x) << 12))
+#define RDR_PREP_PNTR(x) (0x834 + ((x) << 12))
+#define RDR_PROC_PNTR(x) (0x838 + ((x) << 12))
+#define RDR_STAT(x) (0x83C + ((x) << 12))
+
+/* HIA, Ring AIC */
+#define AIC_POL_CTRL(x) (0xE000 - ((x) << 12))
+#define AIC_TYPE_CTRL(x) (0xE004 - ((x) << 12))
+#define AIC_ENABLE_CTRL(x) (0xE008 - ((x) << 12))
+#define AIC_RAW_STAL(x) (0xE00C - ((x) << 12))
+#define AIC_ENABLE_SET(x) (0xE00C - ((x) << 12))
+#define AIC_ENABLED_STAT(x) (0xE010 - ((x) << 12))
+#define AIC_ACK(x) (0xE010 - ((x) << 12))
+#define AIC_ENABLE_CLR(x) (0xE014 - ((x) << 12))
+#define AIC_OPTIONS(x) (0xE018 - ((x) << 12))
+#define AIC_VERSION(x) (0xE01C - ((x) << 12))
+
+/* HIA, Global AIC */
+#define AIC_G_POL_CTRL 0xF800
+#define AIC_G_TYPE_CTRL 0xF804
+#define AIC_G_ENABLE_CTRL 0xF808
+#define AIC_G_RAW_STAT 0xF80C
+#define AIC_G_ENABLE_SET 0xF80C
+#define AIC_G_ENABLED_STAT 0xF810
+#define AIC_G_ACK 0xF810
+#define AIC_G_ENABLE_CLR 0xF814
+#define AIC_G_OPTIONS 0xF818
+#define AIC_G_VERSION 0xF81C
+
+/* HIA, Data Fetch Engine */
+#define DFE_CFG 0xF000
+#define DFE_PRIO_0 0xF010
+#define DFE_PRIO_1 0xF014
+#define DFE_PRIO_2 0xF018
+#define DFE_PRIO_3 0xF01C
+
+/* HIA, Data Fetch Engine access monitoring for CDR */
+#define DFE_RING_REGION_LO(x) (0xF080 + ((x) << 3))
+#define DFE_RING_REGION_HI(x) (0xF084 + ((x) << 3))
+
+/* HIA, Data Fetch Engine thread control and status for thread */
+#define DFE_THR_CTRL 0xF200
+#define DFE_THR_STAT 0xF204
+#define DFE_THR_DESC_CTRL 0xF208
+#define DFE_THR_DESC_DPTR_LO 0xF210
+#define DFE_THR_DESC_DPTR_HI 0xF214
+#define DFE_THR_DESC_ACDPTR_LO 0xF218
+#define DFE_THR_DESC_ACDPTR_HI 0xF21C
+
+/* HIA, Data Store Engine */
+#define DSE_CFG 0xF400
+#define DSE_PRIO_0 0xF410
+#define DSE_PRIO_1 0xF414
+#define DSE_PRIO_2 0xF418
+#define DSE_PRIO_3 0xF41C
+
+/* HIA, Data Store Engine access monitoring for RDR */
+#define DSE_RING_REGION_LO(x) (0xF480 + ((x) << 3))
+#define DSE_RING_REGION_HI(x) (0xF484 + ((x) << 3))
+
+/* HIA, Data Store Engine thread control and status for thread */
+#define DSE_THR_CTRL 0xF600
+#define DSE_THR_STAT 0xF604
+#define DSE_THR_DESC_CTRL 0xF608
+#define DSE_THR_DESC_DPTR_LO 0xF610
+#define DSE_THR_DESC_DPTR_HI 0xF614
+#define DSE_THR_DESC_S_DPTR_LO 0xF618
+#define DSE_THR_DESC_S_DPTR_HI 0xF61C
+#define DSE_THR_ERROR_STAT 0xF620
+
+/* HIA Global */
+#define HIA_MST_CTRL 0xFFF4
+#define HIA_OPTIONS 0xFFF8
+#define HIA_VERSION 0xFFFC
+
+/* Processing Engine Input Side, Processing Engine */
+#define PE_IN_DBUF_THRESH 0x10000
+#define PE_IN_TBUF_THRESH 0x10100
+
+/* Packet Engine Configuration / Status Registers */
+#define PE_TOKEN_CTRL_STAT 0x11000
+#define PE_FUNCTION_EN 0x11004
+#define PE_CONTEXT_CTRL 0x11008
+#define PE_INTERRUPT_CTRL_STAT 0x11010
+#define PE_CONTEXT_STAT 0x1100C
+#define PE_OUT_TRANS_CTRL_STAT 0x11018
+#define PE_OUT_BUF_CTRL 0x1101C
+
+/* Packet Engine PRNG Registers */
+#define PE_PRNG_STAT 0x11040
+#define PE_PRNG_CTRL 0x11044
+#define PE_PRNG_SEED_L 0x11048
+#define PE_PRNG_SEED_H 0x1104C
+#define PE_PRNG_KEY_0_L 0x11050
+#define PE_PRNG_KEY_0_H 0x11054
+#define PE_PRNG_KEY_1_L 0x11058
+#define PE_PRNG_KEY_1_H 0x1105C
+#define PE_PRNG_RES_0 0x11060
+#define PE_PRNG_RES_1 0x11064
+#define PE_PRNG_RES_2 0x11068
+#define PE_PRNG_RES_3 0x1106C
+#define PE_PRNG_LFSR_L 0x11070
+#define PE_PRNG_LFSR_H 0x11074
+
+/* Packet Engine AIC */
+#define PE_EIP96_AIC_POL_CTRL 0x113C0
+#define PE_EIP96_AIC_TYPE_CTRL 0x113C4
+#define PE_EIP96_AIC_ENABLE_CTRL 0x113C8
+#define PE_EIP96_AIC_RAW_STAT 0x113CC
+#define PE_EIP96_AIC_ENABLE_SET 0x113CC
+#define PE_EIP96_AIC_ENABLED_STAT 0x113D0
+#define PE_EIP96_AIC_ACK 0x113D0
+#define PE_EIP96_AIC_ENABLE_CLR 0x113D4
+#define PE_EIP96_AIC_OPTIONS 0x113D8
+#define PE_EIP96_AIC_VERSION 0x113DC
+
+/* Packet Engine Options & Version Registers */
+#define PE_EIP96_OPTIONS 0x113F8
+#define PE_EIP96_VERSION 0x113FC
+
+/* Processing Engine Output Side */
+#define PE_OUT_DBUF_THRESH 0x11C00
+#define PE_OUT_TBUF_THRESH 0x11D00
+
+/* Processing Engine Local AIC */
+#define PE_AIC_POL_CTRL 0x11F00
+#define PE_AIC_TYPE_CTRL 0x11F04
+#define PE_AIC_ENABLE_CTRL 0x11F08
+#define PE_AIC_RAW_STAT 0x11F0C
+#define PE_AIC_ENABLE_SET 0x11F0C
+#define PE_AIC_ENABLED_STAT 0x11F10
+#define PE_AIC_ENABLE_CLR 0x11F14
+#define PE_AIC_OPTIONS 0x11F18
+#define PE_AIC_VERSION 0x11F1C
+
+/* Processing Engine General Configuration and Version */
+#define PE_IN_FLIGHT 0x11FF0
+#define PE_OPTIONS 0x11FF8
+#define PE_VERSION 0x11FFC
+
+/* EIP-97 - Global */
+#define EIP97_CLOCK_STATE 0x1FFE4
+#define EIP97_FORCE_CLOCK_ON 0x1FFE8
+#define EIP97_FORCE_CLOCK_OFF 0x1FFEC
+#define EIP97_MST_CTRL 0x1FFF4
+#define EIP97_OPTIONS 0x1FFF8
+#define EIP97_VERSION 0x1FFFC
+#endif /* __MTK_REGS_H__ */
diff --git a/drivers/crypto/mediatek/mtk-sha.c b/drivers/crypto/mediatek/mtk-sha.c
new file mode 100644
index 0000000..45479bb
--- /dev/null
+++ b/drivers/crypto/mediatek/mtk-sha.c
@@ -0,0 +1,1384 @@
+/*
+ * Cryptographic API.
+ *
+ * Support for MediaTek SHA1/SHA2 hardware accelerator.
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Ryder Lee <ryder.lee-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Some ideas are from atmel-sha.c and omap-sham.c drivers.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/scatterlist.h>
+#include <linux/crypto.h>
+#include <crypto/scatterwalk.h>
+#include <crypto/algapi.h>
+#include <crypto/sha.h>
+#include <crypto/internal/hash.h>
+#include "mtk-platform.h"
+#include "mtk-regs.h"
+
+#define SHA_ALIGN_MSK (sizeof(u32) - 1)
+#define SHA_QUEUE_SIZE 512
+#define SHA_TMP_STATE_SIZE 512
+
+#define SHA_DATA_LEN_MSK GENMASK(16, 0)
+#define SHA_BUFFER_LEN ((u32)PAGE_SIZE)
+
+#define SHA_OP_UPDATE 1
+#define SHA_OP_FINAL 2
+
+/* SHA command token */
+#define SHA_CT_SIZE 5
+#define SHA_CT_CTRL_HDR 0x02220000
+#define SHA_COMMAND0 0x03020000
+#define SHA_COMMAND1 0x21060000
+#define SHA_COMMAND2 0xe0e63802
+
+/* SHA transform information */
+#define SHA_TFM_HASH (0x2 << 0)
+#define SHA_TFM_DIG_TYPE (0x1 << 21)
+#define SHA_TFM_SIZE(x) ((x) << 8)
+#define SHA_TFM_START (0x1 << 4)
+#define SHA_TFM_CONTINUE (0x1 << 5)
+#define SHA_TFM_HASH_STORE (0x1 << 19)
+#define SHA_TFM_SHA1 (0x2 << 23)
+#define SHA_TFM_SHA256 (0x3 << 23)
+#define SHA_TFM_SHA224 (0x4 << 23)
+#define SHA_TFM_SHA512 (0x5 << 23)
+#define SHA_TFM_SHA384 (0x6 << 23)
+#define SHA_TFM_DIGEST(x) (((x) & 0xf) << 24)
+
+/* SHA flags */
+#define SHA_FLAGS_BUSY BIT(0)
+#define SHA_FLAGS_FINAL BIT(1)
+#define SHA_FLAGS_FINUP BIT(2)
+#define SHA_FLAGS_SG BIT(3)
+#define SHA_FLAGS_ALGO_MASK GENMASK(8, 4)
+#define SHA_FLAGS_SHA1 BIT(4)
+#define SHA_FLAGS_SHA224 BIT(5)
+#define SHA_FLAGS_SHA256 BIT(6)
+#define SHA_FLAGS_SHA384 BIT(7)
+#define SHA_FLAGS_SHA512 BIT(8)
+#define SHA_FLAGS_HMAC BIT(9)
+#define SHA_FLAGS_PAD BIT(10)
+
+/* SHA command token */
+struct mtk_sha_ct {
+ u32 tfm_ctrl0;
+ u32 tfm_ctrl1;
+ u32 ct_ctrl0;
+ u32 ct_ctrl1;
+ u32 ct_ctrl2;
+};
+
+/* SHA transform state */
+struct mtk_sha_tfm {
+ u32 tfm_ctrl0;
+ u32 tfm_ctrl1;
+ u8 digest[SHA512_DIGEST_SIZE] __aligned(sizeof(u32));
+};
+
+struct mtk_sha_info {
+ struct mtk_sha_ct ct;
+ struct mtk_sha_tfm tfm;
+};
+
+struct mtk_sha_reqctx {
+ struct mtk_sha_info info;
+ unsigned long flags;
+ unsigned long op;
+
+ u64 digcnt;
+ bool start;
+ size_t bufcnt;
+ dma_addr_t dma_addr;
+
+ /* walk state */
+ struct scatterlist *sg;
+ u32 offset; /* offset in current sg */
+ u32 total; /* total request */
+ size_t ds;
+ size_t bs;
+
+ u8 *buffer;
+};
+
+struct mtk_sha_hmac_ctx {
+ struct crypto_shash *shash;
+ u8 ipad[SHA512_BLOCK_SIZE] __aligned(sizeof(u32));
+ u8 opad[SHA512_BLOCK_SIZE] __aligned(sizeof(u32));
+};
+
+struct mtk_sha_ctx {
+ struct mtk_cryp *cryp;
+ unsigned long flags;
+ u8 id;
+ u8 buf[SHA_BUFFER_LEN] __aligned(sizeof(u32));
+
+ struct mtk_sha_hmac_ctx base[0];
+};
+
+struct mtk_sha_drv {
+ struct list_head dev_list;
+ /* device list lock */
+ spinlock_t lock;
+};
+
+static struct mtk_sha_drv mtk_sha = {
+ .dev_list = LIST_HEAD_INIT(mtk_sha.dev_list),
+ .lock = __SPIN_LOCK_UNLOCKED(mtk_sha.lock),
+};
+
+static int mtk_sha_handle_queue(struct mtk_cryp *cryp, u8 id,
+ struct ahash_request *req);
+
+static inline u32 mtk_sha_read(struct mtk_cryp *cryp, u32 offset)
+{
+ return readl_relaxed(cryp->base + offset);
+}
+
+static inline void mtk_sha_write(struct mtk_cryp *cryp,
+ u32 offset, u32 value)
+{
+ writel_relaxed(value, cryp->base + offset);
+}
+
+static struct mtk_cryp *mtk_sha_find_dev(struct mtk_sha_ctx *tctx)
+{
+ struct mtk_cryp *cryp = NULL;
+ struct mtk_cryp *tmp;
+
+ spin_lock_bh(&mtk_sha.lock);
+ if (!tctx->cryp) {
+ list_for_each_entry(tmp, &mtk_sha.dev_list, sha_list) {
+ cryp = tmp;
+ break;
+ }
+ tctx->cryp = cryp;
+ } else {
+ cryp = tctx->cryp;
+ }
+
+ /*
+ * Assign record id to tfm in round-robin fashion, and this
+ * help us to link to descriptor ring and record.
+ */
+ tctx->id = cryp->rec;
+ cryp->rec = !cryp->rec;
+
+ spin_unlock_bh(&mtk_sha.lock);
+
+ return cryp;
+}
+
+static int mtk_sha_append_sg(struct mtk_sha_reqctx *ctx)
+{
+ size_t count;
+
+ while ((ctx->bufcnt < SHA_BUFFER_LEN) && ctx->total) {
+ count = min(ctx->sg->length - ctx->offset, ctx->total);
+ count = min(count, SHA_BUFFER_LEN - ctx->bufcnt);
+
+ if (count <= 0) {
+ /*
+ * Check if count <= 0 because the buffer is full or
+ * because the sg length is 0. In the latest case,
+ * check if there is another sg in the list, a 0 length
+ * sg doesn't necessarily mean the end of the sg list.
+ */
+ if ((ctx->sg->length == 0) && !sg_is_last(ctx->sg)) {
+ ctx->sg = sg_next(ctx->sg);
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ scatterwalk_map_and_copy(ctx->buffer + ctx->bufcnt, ctx->sg,
+ ctx->offset, count, 0);
+
+ ctx->bufcnt += count;
+ ctx->offset += count;
+ ctx->total -= count;
+
+ if (ctx->offset == ctx->sg->length) {
+ ctx->sg = sg_next(ctx->sg);
+ if (ctx->sg)
+ ctx->offset = 0;
+ else
+ ctx->total = 0;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * The purpose of this padding is to ensure that the padded message is a
+ * multiple of 512 bits (SHA1/SHA224/SHA256) or 1024 bits (SHA384/SHA512).
+ * The bit "1" is appended at the end of the message followed by
+ * "padlen-1" zero bits. Then a 64 bits block (SHA1/SHA224/SHA256) or
+ * 128 bits block (SHA384/SHA512) equals to the message length in bits
+ * is appended.
+ *
+ * For SHA1/SHA224/SHA256, padlen is calculated as followed:
+ * - if message length < 56 bytes then padlen = 56 - message length
+ * - else padlen = 64 + 56 - message length
+ *
+ * For SHA384/SHA512, padlen is calculated as followed:
+ * - if message length < 112 bytes then padlen = 112 - message length
+ * - else padlen = 128 + 112 - message length
+ */
+static void mtk_sha_fill_padding(struct mtk_sha_reqctx *ctx, u32 len)
+{
+ u32 index, padlen;
+ u64 bits[2];
+ u64 size = ctx->digcnt;
+
+ size += ctx->bufcnt;
+ size += len;
+
+ bits[1] = cpu_to_be64(size << 3);
+ bits[0] = cpu_to_be64(size >> 61);
+
+ if (ctx->flags & (SHA_FLAGS_SHA384 | SHA_FLAGS_SHA512)) {
+ index = ctx->bufcnt & 0x7f;
+ padlen = (index < 112) ? (112 - index) : ((128 + 112) - index);
+ *(ctx->buffer + ctx->bufcnt) = 0x80;
+ memset(ctx->buffer + ctx->bufcnt + 1, 0, padlen - 1);
+ memcpy(ctx->buffer + ctx->bufcnt + padlen, bits, 16);
+ ctx->bufcnt += padlen + 16;
+ ctx->flags |= SHA_FLAGS_PAD;
+ } else {
+ index = ctx->bufcnt & 0x3f;
+ padlen = (index < 56) ? (56 - index) : ((64 + 56) - index);
+ *(ctx->buffer + ctx->bufcnt) = 0x80;
+ memset(ctx->buffer + ctx->bufcnt + 1, 0, padlen - 1);
+ memcpy(ctx->buffer + ctx->bufcnt + padlen, &bits[1], 8);
+ ctx->bufcnt += padlen + 8;
+ ctx->flags |= SHA_FLAGS_PAD;
+ }
+}
+
+static void mtk_sha_info_init(struct mtk_sha *sha,
+ struct mtk_sha_reqctx *ctx)
+{
+ struct mtk_sha_info *info = sha->info;
+ struct mtk_sha_ct *ct = &info->ct;
+ struct mtk_sha_tfm *tfm = &info->tfm;
+
+ sha->ct_hdr = SHA_CT_CTRL_HDR;
+ sha->ct_size = SHA_CT_SIZE;
+
+ tfm->tfm_ctrl0 = SHA_TFM_HASH | SHA_TFM_DIG_TYPE |
+ SHA_TFM_SIZE(WORD(ctx->ds));
+
+ switch (ctx->flags & SHA_FLAGS_ALGO_MASK) {
+ case SHA_FLAGS_SHA1:
+ tfm->tfm_ctrl0 |= SHA_TFM_SHA1;
+ break;
+ case SHA_FLAGS_SHA224:
+ tfm->tfm_ctrl0 |= SHA_TFM_SHA224;
+ break;
+ case SHA_FLAGS_SHA256:
+ tfm->tfm_ctrl0 |= SHA_TFM_SHA256;
+ break;
+ case SHA_FLAGS_SHA384:
+ tfm->tfm_ctrl0 |= SHA_TFM_SHA384;
+ break;
+ case SHA_FLAGS_SHA512:
+ tfm->tfm_ctrl0 |= SHA_TFM_SHA512;
+ break;
+
+ default:
+ /* Should not happen... */
+ return;
+ }
+
+ tfm->tfm_ctrl1 = SHA_TFM_HASH_STORE;
+ ct->tfm_ctrl0 = tfm->tfm_ctrl0 | SHA_TFM_CONTINUE | SHA_TFM_START;
+ ct->tfm_ctrl1 = tfm->tfm_ctrl1;
+
+ ct->ct_ctrl0 = SHA_COMMAND0;
+ ct->ct_ctrl1 = SHA_COMMAND1;
+ ct->ct_ctrl2 = SHA_COMMAND2 | SHA_TFM_DIGEST(WORD(ctx->ds));
+}
+
+static int mtk_sha_info_map(struct mtk_cryp *cryp,
+ struct mtk_sha *sha, size_t len)
+{
+ struct mtk_sha_reqctx *ctx = ahash_request_ctx(sha->req);
+ struct mtk_sha_info *info = sha->info;
+ struct mtk_sha_ct *ct = &info->ct;
+
+ if (ctx->start)
+ ctx->start = false;
+ else
+ ct->tfm_ctrl0 &= ~SHA_TFM_START;
+
+ sha->ct_hdr = (sha->ct_hdr & ~SHA_DATA_LEN_MSK) | len;
+ ct->ct_ctrl0 = (ct->ct_ctrl0 & ~SHA_DATA_LEN_MSK) | len;
+
+ ctx->digcnt += len;
+
+ sha->ct_dma = dma_map_single(cryp->dev, info, sizeof(*info),
+ DMA_BIDIRECTIONAL);
+ if (unlikely(dma_mapping_error(cryp->dev, sha->ct_dma))) {
+ dev_err(cryp->dev, "dma %d bytes error\n", sizeof(*info));
+ return -EINVAL;
+ }
+ sha->tfm_dma = sha->ct_dma + sizeof(*ct);
+
+ return 0;
+}
+
+static int mtk_sha_finish_hmac(struct ahash_request *req)
+{
+ struct mtk_sha_ctx *tctx = crypto_tfm_ctx(req->base.tfm);
+ struct mtk_sha_hmac_ctx *bctx = tctx->base;
+ struct mtk_sha_reqctx *ctx = ahash_request_ctx(req);
+
+ SHASH_DESC_ON_STACK(shash, bctx->shash);
+
+ shash->tfm = bctx->shash;
+ shash->flags = 0; /* not CRYPTO_TFM_REQ_MAY_SLEEP */
+
+ return crypto_shash_init(shash) ?:
+ crypto_shash_update(shash, bctx->opad, ctx->bs) ?:
+ crypto_shash_finup(shash, req->result, ctx->ds, req->result);
+}
+
+static int mtk_sha_init(struct ahash_request *req)
+{
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct mtk_sha_ctx *tctx = crypto_ahash_ctx(tfm);
+ struct mtk_sha_reqctx *ctx = ahash_request_ctx(req);
+
+ ctx->flags = 0;
+ ctx->ds = crypto_ahash_digestsize(tfm);
+
+ switch (ctx->ds) {
+ case SHA1_DIGEST_SIZE:
+ ctx->flags |= SHA_FLAGS_SHA1;
+ ctx->bs = SHA1_BLOCK_SIZE;
+ break;
+ case SHA224_DIGEST_SIZE:
+ ctx->flags |= SHA_FLAGS_SHA224;
+ ctx->bs = SHA224_BLOCK_SIZE;
+ break;
+ case SHA256_DIGEST_SIZE:
+ ctx->flags |= SHA_FLAGS_SHA256;
+ ctx->bs = SHA256_BLOCK_SIZE;
+ break;
+ case SHA384_DIGEST_SIZE:
+ ctx->flags |= SHA_FLAGS_SHA384;
+ ctx->bs = SHA384_BLOCK_SIZE;
+ break;
+ case SHA512_DIGEST_SIZE:
+ ctx->flags |= SHA_FLAGS_SHA512;
+ ctx->bs = SHA512_BLOCK_SIZE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ctx->bufcnt = 0;
+ ctx->digcnt = 0;
+ ctx->buffer = tctx->buf;
+ ctx->start = true;
+
+ if (tctx->flags & SHA_FLAGS_HMAC) {
+ struct mtk_sha_hmac_ctx *bctx = tctx->base;
+
+ memcpy(ctx->buffer, bctx->ipad, ctx->bs);
+ ctx->bufcnt = ctx->bs;
+ ctx->flags |= SHA_FLAGS_HMAC;
+ }
+
+ return 0;
+}
+
+static int mtk_sha_xmit(struct mtk_cryp *cryp, struct mtk_sha *sha,
+ dma_addr_t addr, size_t len)
+{
+ struct mtk_ring *ring = cryp->ring[sha->id];
+ struct mtk_desc *cmd = ring->cmd_base + ring->pos;
+ struct mtk_desc *res = ring->res_base + ring->pos;
+ int err;
+
+ err = mtk_sha_info_map(cryp, sha, len);
+ if (err)
+ return err;
+
+ res->hdr = MTK_DESC_FIRST | MTK_DESC_LAST |
+ MTK_DESC_BUF_LEN(len);
+
+ res->buf = cryp->tmp_dma;
+
+ cmd->hdr = MTK_DESC_FIRST | MTK_DESC_LAST |
+ MTK_DESC_BUF_LEN(len) |
+ MTK_DESC_CT_LEN(sha->ct_size);
+
+ cmd->buf = addr;
+ cmd->ct = sha->ct_dma;
+ cmd->ct_hdr = sha->ct_hdr;
+ cmd->tfm = sha->tfm_dma;
+
+ if (++ring->pos == MTK_MAX_DESC_NUM)
+ ring->pos = 0;
+
+ /* make sure all descriptor are filled done */
+ wmb();
+ mtk_sha_write(cryp, RDR_PREP_COUNT(sha->id), MTK_DESC_CNT(1));
+ mtk_sha_write(cryp, CDR_PREP_COUNT(sha->id), MTK_DESC_CNT(1));
+
+ return -EINPROGRESS;
+}
+
+static int mtk_sha_xmit2(struct mtk_cryp *cryp, struct mtk_sha *sha,
+ struct mtk_sha_reqctx *ctx, size_t len1, size_t len2)
+{
+ struct mtk_ring *ring = cryp->ring[sha->id];
+ struct mtk_desc *cmd = ring->cmd_base + ring->pos;
+ struct mtk_desc *res = ring->res_base + ring->pos;
+ int err;
+
+ err = mtk_sha_info_map(cryp, sha, len1 + len2);
+ if (err)
+ return err;
+
+ res->hdr = MTK_DESC_BUF_LEN(len1) | MTK_DESC_FIRST;
+ res->buf = cryp->tmp_dma;
+
+ cmd->hdr = MTK_DESC_BUF_LEN(len1) | MTK_DESC_FIRST |
+ MTK_DESC_CT_LEN(sha->ct_size);
+ cmd->buf = sg_dma_address(ctx->sg);
+ cmd->ct = sha->ct_dma;
+ cmd->ct_hdr = sha->ct_hdr;
+ cmd->tfm = sha->tfm_dma;
+
+ if (++ring->pos == MTK_MAX_DESC_NUM)
+ ring->pos = 0;
+
+ cmd = ring->cmd_base + ring->pos;
+ res = ring->res_base + ring->pos;
+
+ res->hdr = MTK_DESC_BUF_LEN(len2) | MTK_DESC_LAST;
+ res->buf = cryp->tmp_dma;
+
+ cmd->hdr = MTK_DESC_BUF_LEN(len2) | MTK_DESC_LAST;
+ cmd->buf = ctx->dma_addr;
+
+ if (++ring->pos == MTK_MAX_DESC_NUM)
+ ring->pos = 0;
+
+ /* make sure all descriptor are filled done */
+ wmb();
+ mtk_sha_write(cryp, RDR_PREP_COUNT(sha->id), MTK_DESC_CNT(2));
+ mtk_sha_write(cryp, CDR_PREP_COUNT(sha->id), MTK_DESC_CNT(2));
+
+ return -EINPROGRESS;
+}
+
+static int mtk_sha_dma_map(struct mtk_cryp *cryp, struct mtk_sha *sha,
+ struct mtk_sha_reqctx *ctx, size_t count)
+{
+ ctx->dma_addr = dma_map_single(cryp->dev, ctx->buffer,
+ SHA_BUFFER_LEN, DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(cryp->dev, ctx->dma_addr))) {
+ dev_err(cryp->dev, "dma map error\n");
+ return -EINVAL;
+ }
+
+ ctx->flags &= ~SHA_FLAGS_SG;
+
+ return mtk_sha_xmit(cryp, sha, ctx->dma_addr, count);
+}
+
+static int mtk_sha_update_slow(struct mtk_cryp *cryp, struct mtk_sha *sha)
+{
+ struct mtk_sha_reqctx *ctx = ahash_request_ctx(sha->req);
+ size_t count;
+ u32 final;
+
+ mtk_sha_append_sg(ctx);
+
+ final = (ctx->flags & SHA_FLAGS_FINUP) && !ctx->total;
+
+ dev_dbg(cryp->dev, "slow: bufcnt: %u\n", ctx->bufcnt);
+
+ if (final) {
+ sha->flags |= SHA_FLAGS_FINAL;
+ mtk_sha_fill_padding(ctx, 0);
+ }
+
+ if (final || (ctx->bufcnt == SHA_BUFFER_LEN && ctx->total)) {
+ count = ctx->bufcnt;
+ ctx->bufcnt = 0;
+
+ return mtk_sha_dma_map(cryp, sha, ctx, count);
+ }
+ return 0;
+}
+
+static int mtk_sha_update_req(struct mtk_cryp *cryp, struct mtk_sha *sha)
+{
+ struct mtk_sha_reqctx *ctx = ahash_request_ctx(sha->req);
+ u32 len, final, tail;
+ struct scatterlist *sg;
+
+ if (!ctx->total)
+ return 0;
+
+ if (ctx->bufcnt || ctx->offset)
+ return mtk_sha_update_slow(cryp, sha);
+
+ sg = ctx->sg;
+
+ if (!IS_ALIGNED(sg->offset, sizeof(u32)))
+ return mtk_sha_update_slow(cryp, sha);
+
+ if (!sg_is_last(sg) && !IS_ALIGNED(sg->length, ctx->bs))
+ /* size is not bs aligned */
+ return mtk_sha_update_slow(cryp, sha);
+
+ len = min(ctx->total, sg->length);
+
+ if (sg_is_last(sg)) {
+ if (!(ctx->flags & SHA_FLAGS_FINUP)) {
+ /* not last sg must be bs aligned */
+ tail = len & (ctx->bs - 1);
+ len -= tail;
+ }
+ }
+
+ ctx->total -= len;
+ ctx->offset = len; /* offset where to start slow */
+
+ final = (ctx->flags & SHA_FLAGS_FINUP) && !ctx->total;
+
+ /* Add padding */
+ if (final) {
+ size_t count;
+
+ tail = len & (ctx->bs - 1);
+ len -= tail;
+ ctx->total += tail;
+ ctx->offset = len; /* offset where to start slow */
+
+ sg = ctx->sg;
+ mtk_sha_append_sg(ctx);
+ mtk_sha_fill_padding(ctx, len);
+
+ ctx->dma_addr = dma_map_single(cryp->dev, ctx->buffer,
+ SHA_BUFFER_LEN, DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(cryp->dev, ctx->dma_addr))) {
+ dev_err(cryp->dev, "dma map bytes error\n");
+ return -EINVAL;
+ }
+
+ sha->flags |= SHA_FLAGS_FINAL;
+ count = ctx->bufcnt;
+ ctx->bufcnt = 0;
+
+ if (len == 0) {
+ ctx->flags &= ~SHA_FLAGS_SG;
+ return mtk_sha_xmit(cryp, sha, ctx->dma_addr, count);
+
+ } else {
+ ctx->sg = sg;
+ if (!dma_map_sg(cryp->dev, ctx->sg, 1, DMA_TO_DEVICE)) {
+ dev_err(cryp->dev, "dma_map_sg error\n");
+ return -EINVAL;
+ }
+
+ ctx->flags |= SHA_FLAGS_SG;
+ return mtk_sha_xmit2(cryp, sha, ctx, len, count);
+ }
+ }
+
+ if (!dma_map_sg(cryp->dev, ctx->sg, 1, DMA_TO_DEVICE)) {
+ dev_err(cryp->dev, "dma_map_sg error\n");
+ return -EINVAL;
+ }
+
+ ctx->flags |= SHA_FLAGS_SG;
+
+ return mtk_sha_xmit(cryp, sha, sg_dma_address(ctx->sg), len);
+}
+
+static int mtk_sha_final_req(struct mtk_cryp *cryp, struct mtk_sha *sha)
+{
+ struct ahash_request *req = sha->req;
+ struct mtk_sha_reqctx *ctx = ahash_request_ctx(req);
+ size_t count;
+
+ mtk_sha_fill_padding(ctx, 0);
+
+ sha->flags |= SHA_FLAGS_FINAL;
+ count = ctx->bufcnt;
+ ctx->bufcnt = 0;
+
+ return mtk_sha_dma_map(cryp, sha, ctx, count);
+}
+
+static int mtk_sha_finish(struct ahash_request *req)
+{
+ struct mtk_sha_reqctx *ctx = ahash_request_ctx(req);
+ u8 *digest = ctx->info.tfm.digest;
+
+ memcpy(req->result, digest, ctx->ds);
+
+ if (ctx->flags & SHA_FLAGS_HMAC)
+ return mtk_sha_finish_hmac(req);
+
+ return 0;
+}
+
+static void mtk_sha_finish_req(struct mtk_cryp *cryp,
+ struct mtk_sha *sha, int err)
+{
+ if (likely(!err && (SHA_FLAGS_FINAL & sha->flags)))
+ err = mtk_sha_finish(sha->req);
+
+ sha->flags &= ~(SHA_FLAGS_BUSY | SHA_FLAGS_FINAL);
+
+ sha->req->base.complete(&sha->req->base, err);
+ mtk_sha_handle_queue(cryp, sha->id - RING2, NULL);
+}
+
+static int mtk_sha_handle_queue(struct mtk_cryp *cryp, u8 id,
+ struct ahash_request *req)
+{
+ struct mtk_sha *sha = cryp->sha[id];
+ struct crypto_async_request *async_req, *backlog;
+ struct mtk_sha_reqctx *ctx;
+ unsigned long flags;
+ int err = 0, ret = 0;
+
+ spin_lock_irqsave(&sha->lock, flags);
+ if (req)
+ ret = ahash_enqueue_request(&sha->queue, req);
+
+ if (SHA_FLAGS_BUSY & sha->flags) {
+ spin_unlock_irqrestore(&sha->lock, flags);
+ return ret;
+ }
+
+ backlog = crypto_get_backlog(&sha->queue);
+ async_req = crypto_dequeue_request(&sha->queue);
+ if (async_req)
+ sha->flags |= SHA_FLAGS_BUSY;
+ spin_unlock_irqrestore(&sha->lock, flags);
+
+ if (!async_req)
+ return ret;
+
+ if (backlog)
+ backlog->complete(backlog, -EINPROGRESS);
+
+ req = ahash_request_cast(async_req);
+ ctx = ahash_request_ctx(req);
+
+ sha->req = req;
+ sha->info = &ctx->info;
+
+ mtk_sha_info_init(sha, ctx);
+
+ if (ctx->op == SHA_OP_UPDATE) {
+ err = mtk_sha_update_req(cryp, sha);
+ if (err != -EINPROGRESS && (ctx->flags & SHA_FLAGS_FINUP))
+ /* no final() after finup() */
+ err = mtk_sha_final_req(cryp, sha);
+ } else if (ctx->op == SHA_OP_FINAL) {
+ err = mtk_sha_final_req(cryp, sha);
+ }
+
+ if (unlikely(err != -EINPROGRESS))
+ /* task will not finish it, so do it here */
+ mtk_sha_finish_req(cryp, sha, err);
+
+ return ret;
+}
+
+static int mtk_sha_enqueue(struct ahash_request *req, u32 op)
+{
+ struct mtk_sha_reqctx *ctx = ahash_request_ctx(req);
+ struct mtk_sha_ctx *tctx = crypto_tfm_ctx(req->base.tfm);
+
+ ctx->op = op;
+
+ return mtk_sha_handle_queue(tctx->cryp, tctx->id, req);
+}
+
+static void mtk_sha_unmap(struct mtk_cryp *cryp, struct mtk_sha *sha)
+{
+ struct mtk_sha_reqctx *ctx = ahash_request_ctx(sha->req);
+
+ dma_unmap_single(cryp->dev, sha->ct_dma,
+ sizeof(struct mtk_sha_info), DMA_BIDIRECTIONAL);
+
+ if (ctx->flags & SHA_FLAGS_SG) {
+ dma_unmap_sg(cryp->dev, ctx->sg, 1, DMA_TO_DEVICE);
+ if (ctx->sg->length == ctx->offset) {
+ ctx->sg = sg_next(ctx->sg);
+ if (ctx->sg)
+ ctx->offset = 0;
+ }
+ if (ctx->flags & SHA_FLAGS_PAD) {
+ dma_unmap_single(cryp->dev, ctx->dma_addr,
+ SHA_BUFFER_LEN, DMA_TO_DEVICE);
+ }
+ } else
+ dma_unmap_single(cryp->dev, ctx->dma_addr,
+ SHA_BUFFER_LEN, DMA_TO_DEVICE);
+}
+
+static void mtk_sha_complete(struct mtk_cryp *cryp, struct mtk_sha *sha)
+{
+ int err = 0;
+
+ err = mtk_sha_update_req(cryp, sha);
+ if (err != -EINPROGRESS)
+ mtk_sha_finish_req(cryp, sha, err);
+}
+
+static int mtk_sha_update(struct ahash_request *req)
+{
+ struct mtk_sha_reqctx *ctx = ahash_request_ctx(req);
+
+ ctx->total = req->nbytes;
+ ctx->sg = req->src;
+ ctx->offset = 0;
+
+ if ((ctx->bufcnt + ctx->total < SHA_BUFFER_LEN) &&
+ !(ctx->flags & SHA_FLAGS_FINUP))
+ return mtk_sha_append_sg(ctx);
+
+ return mtk_sha_enqueue(req, SHA_OP_UPDATE);
+}
+
+static int mtk_sha_final(struct ahash_request *req)
+{
+ struct mtk_sha_reqctx *ctx = ahash_request_ctx(req);
+
+ ctx->flags |= SHA_FLAGS_FINUP;
+
+ if (ctx->flags & SHA_FLAGS_PAD)
+ return mtk_sha_finish(req);
+
+ return mtk_sha_enqueue(req, SHA_OP_FINAL);
+}
+
+static int mtk_sha_finup(struct ahash_request *req)
+{
+ struct mtk_sha_reqctx *ctx = ahash_request_ctx(req);
+ int err1, err2;
+
+ ctx->flags |= SHA_FLAGS_FINUP;
+
+ err1 = mtk_sha_update(req);
+ if (err1 == -EINPROGRESS || err1 == -EBUSY)
+ return err1;
+ /*
+ * final() has to be always called to cleanup resources
+ * even if update() failed
+ */
+ err2 = mtk_sha_final(req);
+
+ return err1 ?: err2;
+}
+
+static int mtk_sha_digest(struct ahash_request *req)
+{
+ return mtk_sha_init(req) ?: mtk_sha_finup(req);
+}
+
+static int mtk_sha_setkey(struct crypto_ahash *tfm,
+ const unsigned char *key, u32 keylen)
+{
+ struct mtk_sha_ctx *tctx = crypto_ahash_ctx(tfm);
+ struct mtk_sha_hmac_ctx *bctx = tctx->base;
+ size_t bs = crypto_shash_blocksize(bctx->shash);
+ size_t ds = crypto_shash_digestsize(bctx->shash);
+ int err, i;
+
+ SHASH_DESC_ON_STACK(shash, bctx->shash);
+
+ shash->tfm = bctx->shash;
+ shash->flags = crypto_shash_get_flags(bctx->shash) &
+ CRYPTO_TFM_REQ_MAY_SLEEP;
+
+ if (keylen > bs) {
+ err = crypto_shash_digest(shash, key, keylen, bctx->ipad);
+ if (err)
+ return err;
+ keylen = ds;
+ } else {
+ memcpy(bctx->ipad, key, keylen);
+ }
+
+ memset(bctx->ipad + keylen, 0, bs - keylen);
+ memcpy(bctx->opad, bctx->ipad, bs);
+
+ for (i = 0; i < bs; i++) {
+ bctx->ipad[i] ^= 0x36;
+ bctx->opad[i] ^= 0x5c;
+ }
+
+ return err;
+}
+
+static int mtk_sha_export(struct ahash_request *req, void *out)
+{
+ const struct mtk_sha_reqctx *ctx = ahash_request_ctx(req);
+
+ memcpy(out, ctx, sizeof(*ctx));
+ return 0;
+}
+
+static int mtk_sha_import(struct ahash_request *req, const void *in)
+{
+ struct mtk_sha_reqctx *ctx = ahash_request_ctx(req);
+
+ memcpy(ctx, in, sizeof(*ctx));
+ return 0;
+}
+
+static int mtk_sha_cra_init_alg(struct crypto_tfm *tfm,
+ const char *alg_base)
+{
+ struct mtk_sha_ctx *tctx = crypto_tfm_ctx(tfm);
+ struct mtk_cryp *cryp = NULL;
+
+ cryp = mtk_sha_find_dev(tctx);
+ if (!cryp)
+ return -ENODEV;
+
+ crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
+ sizeof(struct mtk_sha_reqctx));
+
+ if (alg_base) {
+ struct mtk_sha_hmac_ctx *bctx = tctx->base;
+
+ tctx->flags |= SHA_FLAGS_HMAC;
+ bctx->shash = crypto_alloc_shash(alg_base, 0,
+ CRYPTO_ALG_NEED_FALLBACK);
+ if (IS_ERR(bctx->shash)) {
+ pr_err("base driver %s could not be loaded.\n",
+ alg_base);
+
+ return PTR_ERR(bctx->shash);
+ }
+ }
+ return 0;
+}
+
+static int mtk_sha_cra_init(struct crypto_tfm *tfm)
+{
+ return mtk_sha_cra_init_alg(tfm, NULL);
+}
+
+static int mtk_sha_cra_sha1_init(struct crypto_tfm *tfm)
+{
+ return mtk_sha_cra_init_alg(tfm, "sha1");
+}
+
+static int mtk_sha_cra_sha224_init(struct crypto_tfm *tfm)
+{
+ return mtk_sha_cra_init_alg(tfm, "sha224");
+}
+
+static int mtk_sha_cra_sha256_init(struct crypto_tfm *tfm)
+{
+ return mtk_sha_cra_init_alg(tfm, "sha256");
+}
+
+static int mtk_sha_cra_sha384_init(struct crypto_tfm *tfm)
+{
+ return mtk_sha_cra_init_alg(tfm, "sha384");
+}
+
+static int mtk_sha_cra_sha512_init(struct crypto_tfm *tfm)
+{
+ return mtk_sha_cra_init_alg(tfm, "sha512");
+}
+
+static void mtk_sha_cra_exit(struct crypto_tfm *tfm)
+{
+ struct mtk_sha_ctx *tctx = crypto_tfm_ctx(tfm);
+
+ if (tctx->flags & SHA_FLAGS_HMAC) {
+ struct mtk_sha_hmac_ctx *bctx = tctx->base;
+
+ crypto_free_shash(bctx->shash);
+ }
+}
+
+static struct ahash_alg algs_sha1_sha224_sha256[] = {
+{
+ .init = mtk_sha_init,
+ .update = mtk_sha_update,
+ .final = mtk_sha_final,
+ .finup = mtk_sha_finup,
+ .digest = mtk_sha_digest,
+ .export = mtk_sha_export,
+ .import = mtk_sha_import,
+ .halg.digestsize = SHA1_DIGEST_SIZE,
+ .halg.statesize = sizeof(struct mtk_sha_reqctx),
+ .halg.base = {
+ .cra_name = "sha1",
+ .cra_driver_name = "mtk-sha1",
+ .cra_priority = 400,
+ .cra_flags = CRYPTO_ALG_ASYNC,
+ .cra_blocksize = SHA1_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct mtk_sha_ctx),
+ .cra_alignmask = SHA_ALIGN_MSK,
+ .cra_module = THIS_MODULE,
+ .cra_init = mtk_sha_cra_init,
+ .cra_exit = mtk_sha_cra_exit,
+ }
+},
+{
+ .init = mtk_sha_init,
+ .update = mtk_sha_update,
+ .final = mtk_sha_final,
+ .finup = mtk_sha_finup,
+ .digest = mtk_sha_digest,
+ .export = mtk_sha_export,
+ .import = mtk_sha_import,
+ .halg.digestsize = SHA224_DIGEST_SIZE,
+ .halg.statesize = sizeof(struct mtk_sha_reqctx),
+ .halg.base = {
+ .cra_name = "sha224",
+ .cra_driver_name = "mtk-sha224",
+ .cra_priority = 400,
+ .cra_flags = CRYPTO_ALG_ASYNC,
+ .cra_blocksize = SHA224_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct mtk_sha_ctx),
+ .cra_alignmask = SHA_ALIGN_MSK,
+ .cra_module = THIS_MODULE,
+ .cra_init = mtk_sha_cra_init,
+ .cra_exit = mtk_sha_cra_exit,
+ }
+},
+{
+ .init = mtk_sha_init,
+ .update = mtk_sha_update,
+ .final = mtk_sha_final,
+ .finup = mtk_sha_finup,
+ .digest = mtk_sha_digest,
+ .export = mtk_sha_export,
+ .import = mtk_sha_import,
+ .halg.digestsize = SHA256_DIGEST_SIZE,
+ .halg.statesize = sizeof(struct mtk_sha_reqctx),
+ .halg.base = {
+ .cra_name = "sha256",
+ .cra_driver_name = "mtk-sha256",
+ .cra_priority = 400,
+ .cra_flags = CRYPTO_ALG_ASYNC,
+ .cra_blocksize = SHA256_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct mtk_sha_ctx),
+ .cra_alignmask = SHA_ALIGN_MSK,
+ .cra_module = THIS_MODULE,
+ .cra_init = mtk_sha_cra_init,
+ .cra_exit = mtk_sha_cra_exit,
+ }
+},
+{
+ .init = mtk_sha_init,
+ .update = mtk_sha_update,
+ .final = mtk_sha_final,
+ .finup = mtk_sha_finup,
+ .digest = mtk_sha_digest,
+ .export = mtk_sha_export,
+ .import = mtk_sha_import,
+ .setkey = mtk_sha_setkey,
+ .halg.digestsize = SHA1_DIGEST_SIZE,
+ .halg.statesize = sizeof(struct mtk_sha_reqctx),
+ .halg.base = {
+ .cra_name = "hmac(sha1)",
+ .cra_driver_name = "mtk-hmac-sha1",
+ .cra_priority = 400,
+ .cra_flags = CRYPTO_ALG_ASYNC |
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_blocksize = SHA1_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct mtk_sha_ctx) +
+ sizeof(struct mtk_sha_hmac_ctx),
+ .cra_alignmask = SHA_ALIGN_MSK,
+ .cra_module = THIS_MODULE,
+ .cra_init = mtk_sha_cra_sha1_init,
+ .cra_exit = mtk_sha_cra_exit,
+ }
+},
+{
+ .init = mtk_sha_init,
+ .update = mtk_sha_update,
+ .final = mtk_sha_final,
+ .finup = mtk_sha_finup,
+ .digest = mtk_sha_digest,
+ .export = mtk_sha_export,
+ .import = mtk_sha_import,
+ .setkey = mtk_sha_setkey,
+ .halg.digestsize = SHA224_DIGEST_SIZE,
+ .halg.statesize = sizeof(struct mtk_sha_reqctx),
+ .halg.base = {
+ .cra_name = "hmac(sha224)",
+ .cra_driver_name = "mtk-hmac-sha224",
+ .cra_priority = 400,
+ .cra_flags = CRYPTO_ALG_ASYNC |
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_blocksize = SHA224_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct mtk_sha_ctx) +
+ sizeof(struct mtk_sha_hmac_ctx),
+ .cra_alignmask = SHA_ALIGN_MSK,
+ .cra_module = THIS_MODULE,
+ .cra_init = mtk_sha_cra_sha224_init,
+ .cra_exit = mtk_sha_cra_exit,
+ }
+},
+{
+ .init = mtk_sha_init,
+ .update = mtk_sha_update,
+ .final = mtk_sha_final,
+ .finup = mtk_sha_finup,
+ .digest = mtk_sha_digest,
+ .export = mtk_sha_export,
+ .import = mtk_sha_import,
+ .setkey = mtk_sha_setkey,
+ .halg.digestsize = SHA256_DIGEST_SIZE,
+ .halg.statesize = sizeof(struct mtk_sha_reqctx),
+ .halg.base = {
+ .cra_name = "hmac(sha256)",
+ .cra_driver_name = "mtk-hmac-sha256",
+ .cra_priority = 400,
+ .cra_flags = CRYPTO_ALG_ASYNC |
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_blocksize = SHA256_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct mtk_sha_ctx) +
+ sizeof(struct mtk_sha_hmac_ctx),
+ .cra_alignmask = SHA_ALIGN_MSK,
+ .cra_module = THIS_MODULE,
+ .cra_init = mtk_sha_cra_sha256_init,
+ .cra_exit = mtk_sha_cra_exit,
+ }
+},
+};
+
+static struct ahash_alg algs_sha384_sha512[] = {
+{
+ .init = mtk_sha_init,
+ .update = mtk_sha_update,
+ .final = mtk_sha_final,
+ .finup = mtk_sha_finup,
+ .digest = mtk_sha_digest,
+ .export = mtk_sha_export,
+ .import = mtk_sha_import,
+ .halg.digestsize = SHA384_DIGEST_SIZE,
+ .halg.statesize = sizeof(struct mtk_sha_reqctx),
+ .halg.base = {
+ .cra_name = "sha384",
+ .cra_driver_name = "mtk-sha384",
+ .cra_priority = 400,
+ .cra_flags = CRYPTO_ALG_ASYNC,
+ .cra_blocksize = SHA384_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct mtk_sha_ctx),
+ .cra_alignmask = SHA_ALIGN_MSK,
+ .cra_module = THIS_MODULE,
+ .cra_init = mtk_sha_cra_init,
+ .cra_exit = mtk_sha_cra_exit,
+ }
+},
+{
+ .init = mtk_sha_init,
+ .update = mtk_sha_update,
+ .final = mtk_sha_final,
+ .finup = mtk_sha_finup,
+ .digest = mtk_sha_digest,
+ .export = mtk_sha_export,
+ .import = mtk_sha_import,
+ .halg.digestsize = SHA512_DIGEST_SIZE,
+ .halg.statesize = sizeof(struct mtk_sha_reqctx),
+ .halg.base = {
+ .cra_name = "sha512",
+ .cra_driver_name = "mtk-sha512",
+ .cra_priority = 400,
+ .cra_flags = CRYPTO_ALG_ASYNC,
+ .cra_blocksize = SHA512_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct mtk_sha_ctx),
+ .cra_alignmask = SHA_ALIGN_MSK,
+ .cra_module = THIS_MODULE,
+ .cra_init = mtk_sha_cra_init,
+ .cra_exit = mtk_sha_cra_exit,
+ }
+},
+{
+ .init = mtk_sha_init,
+ .update = mtk_sha_update,
+ .final = mtk_sha_final,
+ .finup = mtk_sha_finup,
+ .digest = mtk_sha_digest,
+ .export = mtk_sha_export,
+ .import = mtk_sha_import,
+ .setkey = mtk_sha_setkey,
+ .halg.digestsize = SHA384_DIGEST_SIZE,
+ .halg.statesize = sizeof(struct mtk_sha_reqctx),
+ .halg.base = {
+ .cra_name = "hmac(sha384)",
+ .cra_driver_name = "mtk-hmac-sha384",
+ .cra_priority = 400,
+ .cra_flags = CRYPTO_ALG_ASYNC |
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_blocksize = SHA384_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct mtk_sha_ctx) +
+ sizeof(struct mtk_sha_hmac_ctx),
+ .cra_alignmask = SHA_ALIGN_MSK,
+ .cra_module = THIS_MODULE,
+ .cra_init = mtk_sha_cra_sha384_init,
+ .cra_exit = mtk_sha_cra_exit,
+ }
+},
+{
+ .init = mtk_sha_init,
+ .update = mtk_sha_update,
+ .final = mtk_sha_final,
+ .finup = mtk_sha_finup,
+ .digest = mtk_sha_digest,
+ .export = mtk_sha_export,
+ .import = mtk_sha_import,
+ .setkey = mtk_sha_setkey,
+ .halg.digestsize = SHA512_DIGEST_SIZE,
+ .halg.statesize = sizeof(struct mtk_sha_reqctx),
+ .halg.base = {
+ .cra_name = "hmac(sha512)",
+ .cra_driver_name = "mtk-hmac-sha512",
+ .cra_priority = 400,
+ .cra_flags = CRYPTO_ALG_ASYNC |
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_blocksize = SHA512_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct mtk_sha_ctx) +
+ sizeof(struct mtk_sha_hmac_ctx),
+ .cra_alignmask = SHA_ALIGN_MSK,
+ .cra_module = THIS_MODULE,
+ .cra_init = mtk_sha_cra_sha512_init,
+ .cra_exit = mtk_sha_cra_exit,
+ }
+},
+};
+
+static void mtk_sha_task0(unsigned long data)
+{
+ struct mtk_cryp *cryp = (struct mtk_cryp *)data;
+ struct mtk_sha *sha = cryp->sha[0];
+
+ mtk_sha_unmap(cryp, sha);
+ mtk_sha_complete(cryp, sha);
+}
+
+static void mtk_sha_task1(unsigned long data)
+{
+ struct mtk_cryp *cryp = (struct mtk_cryp *)data;
+ struct mtk_sha *sha = cryp->sha[1];
+
+ mtk_sha_unmap(cryp, sha);
+ mtk_sha_complete(cryp, sha);
+}
+
+static irqreturn_t mtk_sha_ring2_irq(int irq, void *dev_id)
+{
+ struct mtk_cryp *cryp = (struct mtk_cryp *)dev_id;
+ struct mtk_sha *sha = cryp->sha[0];
+ u32 val = mtk_sha_read(cryp, RDR_STAT(RING2));
+
+ mtk_sha_write(cryp, RDR_STAT(RING2), val);
+
+ if (likely((SHA_FLAGS_BUSY & sha->flags))) {
+ mtk_sha_write(cryp, RDR_PROC_COUNT(RING2), MTK_DESC_CNT_CLR);
+ mtk_sha_write(cryp, RDR_THRESH(RING2), MTK_RDR_THRESH_DEF);
+
+ tasklet_schedule(&sha->task);
+ } else {
+ dev_warn(cryp->dev, "AES interrupt when no active requests.\n");
+ }
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mtk_sha_ring3_irq(int irq, void *dev_id)
+{
+ struct mtk_cryp *cryp = (struct mtk_cryp *)dev_id;
+ struct mtk_sha *sha = cryp->sha[1];
+ u32 val = mtk_sha_read(cryp, RDR_STAT(RING3));
+
+ mtk_sha_write(cryp, RDR_STAT(RING3), val);
+
+ if (likely((SHA_FLAGS_BUSY & sha->flags))) {
+ mtk_sha_write(cryp, RDR_PROC_COUNT(RING3), MTK_DESC_CNT_CLR);
+ mtk_sha_write(cryp, RDR_THRESH(RING3), MTK_RDR_THRESH_DEF);
+
+ tasklet_schedule(&sha->task);
+ } else {
+ dev_warn(cryp->dev, "AES interrupt when no active requests.\n");
+ }
+ return IRQ_HANDLED;
+}
+
+static int mtk_sha_record_init(struct mtk_cryp *cryp)
+{
+ struct mtk_sha **sha = cryp->sha;
+ int i, err = -ENOMEM;
+
+ for (i = 0; i < RECORD_NUM; i++) {
+ sha[i] = kzalloc(sizeof(**sha), GFP_KERNEL);
+ if (!sha[i])
+ goto err_cleanup;
+
+ sha[i]->id = i + RING2;
+
+ spin_lock_init(&sha[i]->lock);
+ crypto_init_queue(&sha[i]->queue, SHA_QUEUE_SIZE);
+ }
+
+ tasklet_init(&sha[0]->task, mtk_sha_task0, (unsigned long)cryp);
+ tasklet_init(&sha[1]->task, mtk_sha_task1, (unsigned long)cryp);
+
+ cryp->rec = 1;
+
+ return 0;
+
+err_cleanup:
+ for (; i--; )
+ kfree(sha[i]);
+ return err;
+}
+
+static void mtk_sha_record_free(struct mtk_cryp *cryp)
+{
+ int i;
+
+ for (i = 0; i < RECORD_NUM; i++) {
+ tasklet_kill(&cryp->sha[i]->task);
+ kfree(cryp->sha[i]);
+ }
+}
+
+static void mtk_sha_unregister_algs(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(algs_sha1_sha224_sha256); i++)
+ crypto_unregister_ahash(&algs_sha1_sha224_sha256[i]);
+
+ for (i = 0; i < ARRAY_SIZE(algs_sha384_sha512); i++)
+ crypto_unregister_ahash(&algs_sha384_sha512[i]);
+}
+
+static int mtk_sha_register_algs(void)
+{
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(algs_sha1_sha224_sha256); i++) {
+ err = crypto_register_ahash(&algs_sha1_sha224_sha256[i]);
+ if (err)
+ goto err_sha_224_256_algs;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(algs_sha384_sha512); i++) {
+ err = crypto_register_ahash(&algs_sha384_sha512[i]);
+ if (err)
+ goto err_sha_384_512_algs;
+ }
+
+ return 0;
+
+err_sha_384_512_algs:
+ for (; i--; )
+ crypto_unregister_ahash(&algs_sha384_sha512[i]);
+ i = ARRAY_SIZE(algs_sha1_sha224_sha256);
+err_sha_224_256_algs:
+ for (; i--; )
+ crypto_unregister_ahash(&algs_sha1_sha224_sha256[i]);
+
+ return err;
+}
+
+int mtk_hash_alg_register(struct mtk_cryp *cryp)
+{
+ int err;
+
+ INIT_LIST_HEAD(&cryp->sha_list);
+
+ err = mtk_sha_record_init(cryp);
+ if (err)
+ goto err_record;
+
+ err = devm_request_irq(cryp->dev, cryp->irq[RING2],
+ mtk_sha_ring2_irq, IRQF_TRIGGER_LOW,
+ "mtk-sha", cryp);
+ if (err) {
+ dev_err(cryp->dev, "unable to request sha irq0.\n");
+ goto err_res;
+ }
+
+ err = devm_request_irq(cryp->dev, cryp->irq[RING3],
+ mtk_sha_ring3_irq, IRQF_TRIGGER_LOW,
+ "mtk-sha", cryp);
+ if (err) {
+ dev_err(cryp->dev, "unable to request sha irq1.\n");
+ goto err_res;
+ }
+
+ /* enable ring2 and ring3 interrupt for hash */
+ mtk_sha_write(cryp, AIC_ENABLE_SET(RING2), MTK_IRQ_RDR2);
+ mtk_sha_write(cryp, AIC_ENABLE_SET(RING3), MTK_IRQ_RDR3);
+
+ cryp->tmp = dma_alloc_coherent(cryp->dev, SHA_TMP_STATE_SIZE,
+ &cryp->tmp_dma, GFP_KERNEL);
+ if (!cryp->tmp) {
+ dev_err(cryp->dev, "unable to allocate tmp buffer.\n");
+ err = -EINVAL;
+ goto err_res;
+ }
+
+ spin_lock(&mtk_sha.lock);
+ list_add_tail(&cryp->sha_list, &mtk_sha.dev_list);
+ spin_unlock(&mtk_sha.lock);
+
+ err = mtk_sha_register_algs();
+ if (err)
+ goto err_algs;
+
+ return 0;
+
+err_algs:
+ spin_lock(&mtk_sha.lock);
+ list_del(&cryp->sha_list);
+ spin_unlock(&mtk_sha.lock);
+ dma_free_coherent(cryp->dev, SHA_TMP_STATE_SIZE,
+ cryp->tmp, cryp->tmp_dma);
+err_res:
+ mtk_sha_record_free(cryp);
+err_record:
+
+ dev_err(cryp->dev, "mtk-sha initialization failed.\n");
+ return err;
+}
+EXPORT_SYMBOL(mtk_hash_alg_register);
+
+void mtk_hash_alg_release(struct mtk_cryp *cryp)
+{
+ spin_lock(&mtk_sha.lock);
+ list_del(&cryp->sha_list);
+ spin_unlock(&mtk_sha.lock);
+
+ mtk_sha_unregister_algs();
+ dma_free_coherent(cryp->dev, SHA_TMP_STATE_SIZE,
+ cryp->tmp, cryp->tmp_dma);
+ mtk_sha_record_free(cryp);
+}
+EXPORT_SYMBOL(mtk_hash_alg_release);
--
1.9.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
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