summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/barebox/barebox,state.rst33
-rw-r--r--Documentation/user/state.rst33
-rw-r--r--Makefile2
-rw-r--r--arch/arm/Kconfig5
-rw-r--r--arch/arm/boards/at91sam9x5ek/Makefile1
-rw-r--r--arch/arm/boards/at91sam9x5ek/hw_version.c6
-rw-r--r--arch/arm/boards/at91sam9x5ek/hw_version.h1
-rw-r--r--arch/arm/boards/at91sam9x5ek/init.c226
-rw-r--r--arch/arm/boards/at91sam9x5ek/lowlevel.c21
-rw-r--r--arch/arm/boards/guf-santaro/board.c100
-rw-r--r--arch/arm/boards/highbank/init.c6
-rw-r--r--arch/arm/boards/mioa701/gpio0_poweroff.c3
-rw-r--r--arch/arm/boards/vscom-baltos/board.c12
-rw-r--r--arch/arm/configs/at91sam9x5ek_defconfig8
-rw-r--r--arch/arm/configs/kindle3_defconfig3
-rw-r--r--arch/arm/cpu/psci.c6
-rw-r--r--arch/arm/dts/Makefile2
-rw-r--r--arch/arm/dts/at91sam9x5ek.dts70
-rw-r--r--arch/arm/mach-at91/Kconfig66
-rw-r--r--arch/arm/mach-at91/Makefile9
-rw-r--r--arch/arm/mach-at91/at91sam9x5.c311
-rw-r--r--arch/arm/mach-at91/include/mach/board.h6
-rw-r--r--arch/arm/mach-at91/setup.c4
-rw-r--r--arch/arm/mach-highbank/reset.c20
-rw-r--r--arch/arm/mach-pxa/pxa2xx.c15
-rw-r--r--arch/arm/mach-pxa/pxa3xx.c13
-rw-r--r--arch/mips/mach-xburst/Kconfig1
-rw-r--r--arch/mips/mach-xburst/reset-jz4750.c13
-rw-r--r--arch/sandbox/board/hostfile.c6
-rw-r--r--arch/sandbox/configs/sandbox_defconfig1
-rw-r--r--commands/Kconfig22
-rw-r--r--commands/Makefile3
-rw-r--r--commands/digest.c14
-rw-r--r--commands/hab.c120
-rw-r--r--commands/keystore.c100
-rw-r--r--commands/led.c44
-rw-r--r--commands/nand-bitflip.c4
-rw-r--r--commands/poweroff.c3
-rw-r--r--commands/seed.c44
-rw-r--r--commands/state.c21
-rw-r--r--commands/stddev.c29
-rw-r--r--commands/trigger.c54
-rw-r--r--commands/usbgadget.c11
-rw-r--r--common/Makefile1
-rw-r--r--common/blspec.c24
-rw-r--r--common/boot.c36
-rw-r--r--common/bootchooser.c30
-rw-r--r--common/globalvar.c112
-rw-r--r--common/image-fit.c42
-rw-r--r--common/memory.c2
-rw-r--r--common/oftree.c2
-rw-r--r--common/partitions.c2
-rw-r--r--common/password.c6
-rw-r--r--common/poweroff.c114
-rw-r--r--common/state/Makefile2
-rw-r--r--common/state/backend.c189
-rw-r--r--common/state/backend_bucket_cached.c155
-rw-r--r--common/state/backend_bucket_circular.c96
-rw-r--r--common/state/backend_bucket_direct.c20
-rw-r--r--common/state/backend_format_dtb.c13
-rw-r--r--common/state/backend_format_raw.c127
-rw-r--r--common/state/backend_storage.c456
-rw-r--r--common/state/state.c194
-rw-r--r--common/state/state.h82
-rw-r--r--common/state/state_variables.c5
-rw-r--r--crypto/hmac.c2
-rw-r--r--crypto/keystore.c53
-rw-r--r--crypto/sha1.c2
-rw-r--r--crypto/sha2.c2
-rw-r--r--crypto/sha4.c2
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile2
-rw-r--r--drivers/ata/disk_ata_drive.c3
-rw-r--r--drivers/ata/ide-sff.c54
-rw-r--r--drivers/base/driver.c2
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/at91/Makefile13
-rw-r--r--drivers/clk/at91/clk-generated.c323
-rw-r--r--drivers/clk/at91/clk-h32mx.c125
-rw-r--r--drivers/clk/at91/clk-main.c576
-rw-r--r--drivers/clk/at91/clk-master.c245
-rw-r--r--drivers/clk/at91/clk-peripheral.c429
-rw-r--r--drivers/clk/at91/clk-pll.c516
-rw-r--r--drivers/clk/at91/clk-plldiv.c135
-rw-r--r--drivers/clk/at91/clk-programmable.c254
-rw-r--r--drivers/clk/at91/clk-slow.c108
-rw-r--r--drivers/clk/at91/clk-smd.c172
-rw-r--r--drivers/clk/at91/clk-system.c160
-rw-r--r--drivers/clk/at91/clk-usb.c397
-rw-r--r--drivers/clk/at91/clk-utmi.c138
-rw-r--r--drivers/clk/at91/pmc.c41
-rw-r--r--drivers/clk/at91/pmc.h27
-rw-r--r--drivers/clk/at91/sckc.c485
-rw-r--r--drivers/clk/clk-fixed-factor.c2
-rw-r--r--drivers/clk/clk-fixed.c2
-rw-r--r--drivers/clk/clk-gate-shared.c6
-rw-r--r--drivers/clocksource/mvebu.c2
-rw-r--r--drivers/clocksource/timer-atmel-pit.c9
-rw-r--r--drivers/crypto/caam/Kconfig1
-rw-r--r--drivers/crypto/caam/caamrng.c44
-rw-r--r--drivers/eeprom/at24.c1
-rw-r--r--drivers/eeprom/at25.c2
-rw-r--r--drivers/efi/efi-device.c13
-rw-r--r--drivers/hab/Makefile1
-rw-r--r--drivers/hab/hab.c358
-rw-r--r--drivers/hw_random/Kconfig6
-rw-r--r--drivers/hw_random/Makefile1
-rw-r--r--drivers/hw_random/core.c125
-rw-r--r--drivers/led/Kconfig1
-rw-r--r--drivers/led/core.c101
-rw-r--r--drivers/led/led-triggers.c157
-rw-r--r--drivers/mci/atmel_mci.c101
-rw-r--r--drivers/mci/mci-core.c17
-rw-r--r--drivers/misc/state.c6
-rw-r--r--drivers/mtd/core.c65
-rw-r--r--drivers/mtd/partition.c2
-rw-r--r--drivers/mtd/peb.c4
-rw-r--r--drivers/net/macb.c56
-rw-r--r--drivers/net/phy/marvell.c126
-rw-r--r--drivers/net/phy/mdio-gpio.c2
-rw-r--r--drivers/nvmem/Kconfig18
-rw-r--r--drivers/nvmem/Makefile10
-rw-r--r--drivers/nvmem/core.c764
-rw-r--r--drivers/nvmem/snvs_lpgpr.c115
-rw-r--r--drivers/of/base.c24
-rw-r--r--drivers/of/of_path.c107
-rw-r--r--drivers/of/partition.c176
-rw-r--r--drivers/serial/atmel.c7
-rw-r--r--drivers/spi/atmel_spi.c29
-rw-r--r--drivers/usb/gadget/Kconfig11
-rw-r--r--drivers/usb/gadget/Makefile1
-rw-r--r--drivers/usb/gadget/autostart.c70
-rw-r--r--drivers/usb/host/Kconfig5
-rw-r--r--drivers/usb/host/ehci-atmel.c11
-rw-r--r--drivers/usb/host/ohci-at91.c93
-rw-r--r--drivers/video/edid.c2
-rw-r--r--drivers/video/imx-ipu-v3/ipufb.c6
-rw-r--r--drivers/video/simplefb.c12
-rw-r--r--drivers/w1/masters/w1-gpio.c53
-rw-r--r--drivers/watchdog/Kconfig6
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/orion_wdt.c123
-rw-r--r--dts/Bindings/arm/marvell/cp110-system-controller0.txt6
-rw-r--r--dts/Bindings/display/exynos/exynos_dsim.txt1
-rw-r--r--dts/Bindings/display/exynos/samsung-fimd.txt1
-rw-r--r--dts/Bindings/mmc/rockchip-dw-mshc.txt2
-rw-r--r--dts/Bindings/phy/brcm,nsp-usb3-phy.txt39
-rw-r--r--dts/Bindings/powerpc/4xx/emac.txt62
-rw-r--r--dts/Bindings/regulator/ti-abb-regulator.txt2
-rw-r--r--dts/Bindings/rng/omap_rng.txt3
-rw-r--r--dts/Bindings/usb/usb251xb.txt53
-rw-r--r--dts/include/dt-bindings/sound/cs42l42.h2
-rw-r--r--dts/src/arc/skeleton.dtsi1
-rw-r--r--dts/src/arc/skeleton_hs.dtsi1
-rw-r--r--dts/src/arc/skeleton_hs_idu.dtsi21
-rw-r--r--dts/src/arc/vdk_axs10x_mb.dtsi20
-rw-r--r--dts/src/arm/am335x-pcm-953.dtsi4
-rw-r--r--dts/src/arm/am57xx-idk-common.dtsi14
-rw-r--r--dts/src/arm/bcm5301x.dtsi4
-rw-r--r--dts/src/arm/bcm953012k.dts5
-rw-r--r--dts/src/arm/bcm958522er.dts1
-rw-r--r--dts/src/arm/bcm958525er.dts1
-rw-r--r--dts/src/arm/bcm958525xmc.dts1
-rw-r--r--dts/src/arm/bcm958622hr.dts1
-rw-r--r--dts/src/arm/bcm958623hr.dts1
-rw-r--r--dts/src/arm/bcm958625hr.dts1
-rw-r--r--dts/src/arm/bcm988312hr.dts1
-rw-r--r--dts/src/arm/imx6qdl.dtsi6
-rw-r--r--dts/src/arm/imx6sl.dtsi6
-rw-r--r--dts/src/arm/imx6sx-udoo-neo.dtsi5
-rw-r--r--dts/src/arm/sama5d2.dtsi2
-rw-r--r--dts/src/arm/ste-dbx5x0.dtsi19
-rw-r--r--dts/src/arm/ste-href.dtsi9
-rw-r--r--dts/src/arm/ste-snowball.dts9
-rw-r--r--dts/src/arm/sun7i-a20-lamobo-r1.dts2
-rw-r--r--dts/src/arm/sun8i-a23-a33.dtsi2
-rw-r--r--dts/src/arm/sun8i-a33.dtsi4
-rw-r--r--dts/src/arm/sun8i-reference-design-tablet.dtsi7
-rw-r--r--dts/src/arm64/broadcom/ns2.dtsi11
-rw-r--r--fs/devfs-core.c82
-rw-r--r--fs/ext4/ext4_common.c83
-rw-r--r--fs/ext4/ext4fs.c2
-rw-r--r--fs/ext4/ext4fs.h3
-rw-r--r--fs/ext4/ext_barebox.c10
-rw-r--r--fs/ext4/ext_common.h184
-rw-r--r--images/Makefile1
-rw-r--r--images/Makefile.at917
-rw-r--r--include/ata_drive.h3
-rw-r--r--include/boot.h2
-rw-r--r--include/bootchooser.h4
-rw-r--r--include/common.h3
-rw-r--r--include/crypto/keystore.h4
-rw-r--r--include/driver.h4
-rw-r--r--include/globalvar.h12
-rw-r--r--include/gui/image_renderer.h2
-rw-r--r--include/hab.h21
-rw-r--r--include/led.h17
-rw-r--r--include/linux/clk.h31
-rw-r--r--include/linux/clk/at91_pmc.h188
-rw-r--r--include/linux/hw_random.h47
-rw-r--r--include/linux/mtd/mtd.h1
-rw-r--r--include/linux/nvmem-consumer.h94
-rw-r--r--include/linux/nvmem-provider.h49
-rw-r--r--include/mci.h1
-rw-r--r--include/of.h15
-rw-r--r--include/param.h1
-rw-r--r--include/poweroff.h21
-rw-r--r--include/state.h1
-rw-r--r--include/stdlib.h1
-rw-r--r--lib/Kconfig9
-rw-r--r--lib/gui/bmp.c4
-rw-r--r--lib/random.c52
-rwxr-xr-xscripts/tags.sh23
213 files changed, 9380 insertions, 2238 deletions
diff --git a/Documentation/devicetree/bindings/barebox/barebox,state.rst b/Documentation/devicetree/bindings/barebox/barebox,state.rst
index 438cc434a2..00fb592614 100644
--- a/Documentation/devicetree/bindings/barebox/barebox,state.rst
+++ b/Documentation/devicetree/bindings/barebox/barebox,state.rst
@@ -29,7 +29,8 @@ Required properties:
* ``compatible``: should be ``barebox,state``;
* ``magic``: A 32bit number used as a magic to identify the state
-* ``backend``: describes where the data for this state is stored
+* ``backend``: contains a phandle to the device/partition which holds the
+ actual state data.
* ``backend-type``: should be ``raw`` or ``dtb``.
Optional properties:
@@ -39,9 +40,9 @@ Optional properties:
e.g. ``hmac(sha256)``. Only used for ``raw``.
* ``backend-stridesize``: Maximum size per copy of the data. Only important for
non-MTD devices
-* ``backend-storage-type``: Type of the storage. This has two options at the
- moment. For MTD with erasing the correct type is ``circular``. For all other
- devices and files, ``direct`` is the needed type.
+* ``backend-storage-type``: Normally the correct storage type is detected auto-
+ matically. The circular backend supports the option ``noncircular`` to fall
+ back to an old storage format.
Variable nodes
--------------
@@ -77,19 +78,31 @@ Example::
magic = <0x27031977>;
compatible = "barebox,state";
backend-type = "raw";
- backend = &eeprom, "partname:state";
+ backend = &state_part;
foo {
- reg = <0x00 0x4>;
- type = "uint32";
+ reg = <0x00 0x4>;
+ type = "uint32";
default = <0x0>;
};
bar {
- reg = <0x10 0x4>;
- type = "enum32";
+ reg = <0x10 0x4>;
+ type = "enum32";
names = "baz", "qux";
- default = <1>;
+ default = <1>;
+ };
+ };
+
+ &nand_flash {
+ partitions {
+ compatible = "fixed-partitions";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ state_part: state@10000 {
+ label = "state";
+ reg = <0x10000 0x10000>;
+ };
};
};
diff --git a/Documentation/user/state.rst b/Documentation/user/state.rst
index 5dd5c486e2..73c4be8159 100644
--- a/Documentation/user/state.rst
+++ b/Documentation/user/state.rst
@@ -23,16 +23,35 @@ available, ``raw`` and ``dtb``. Both format the state data differently.
Basically these are serializers. The raw serializer additionally supports a
HMAC algorithm to detect manipulations.
+The data is always stored in a logical unit called ``bucket``. A ``bucket`` has
+its own size depending on some external contraints. These contraints are listed
+in more detail below depending on the used memory type and storage backend. A
+``bucket`` stores exactly one state. A default number of three buckets is used
+to store data redundantely.
+
+Redundancy
+----------
+
+The state framework is safe against powerfailures during write operations. To
+archieve that multiple buckets are stored to disk. When writing all buckets are
+written in order. When reading, the buckets are read in order and the first
+one found that passes CRC tests is used. When all data is read the buckets
+containing invalid or outdated data are written with the data just read. Also
+NAND blocks need cleanup due to excessive bitflips are rewritten in this step.
+With this it is made sure that after successful initialization of a state the
+data on the storage device is consistent and redundant.
+
Storage Backends
----------------
-The serialized data can be stored to different backends which are automatically
-selected depending on the defined backend in the devicetree. Currently two
-implementations exist, ``circular`` and ``direct``. ``circular`` writes the
-data sequentially on the backend storage device. Each save is appended until
-the storage area is full. It then erases the block and starts from offset 0.
-``circular`` is used for MTD devices with erase functionality. ``direct``
-writes the data directly to the file without erasing.
+The serialized data can be stored to different backends. Currently two
+implementations exist, ``circular`` and ``direct``. The state framework automatically
+selects the correct backend depending on the storage medium. Media requiring
+erase operations (NAND, NOR flash) use the ``circular`` backend, others use the ``direct``
+backend. The purpose of the ``circular`` backend is to save erase cycles which may
+wear out the flash blocks. It continuously fills eraseblocks with updated data
+and only when an eraseblock if fully written erases it and starts over writing
+new data to the same eraseblock again.
For all backends multiple copies are written to handle read errors.
diff --git a/Makefile b/Makefile
index 76463ab3c0..afa4bf8026 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
VERSION = 2017
-PATCHLEVEL = 03
+PATCHLEVEL = 04
SUBLEVEL = 0
EXTRAVERSION =
NAME = None
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 4d952698fc..2583e9dd1b 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -43,9 +43,9 @@ config ARCH_AT91
select GPIOLIB
select CLKDEV_LOOKUP
select HAS_DEBUG_LL
- select HAVE_MACH_ARM_HEAD
select HAVE_CLK
select PINCTRL_AT91
+ select COMMON_CLK_AT91 if COMMON_CLK_OF_PROVIDER
config ARCH_BCM283X
@@ -93,7 +93,6 @@ config ARCH_EP93XX
config ARCH_HIGHBANK
bool "Calxeda Highbank"
select HAS_DEBUG_LL
- select HAS_POWEROFF
select ARCH_HAS_L2X0
select CPU_V7
select ARM_AMBA
@@ -117,7 +116,6 @@ config ARCH_MVEBU
select CLKDEV_LOOKUP
select GPIOLIB
select HAS_DEBUG_LL
- select HAVE_DEFAULT_ENVIRONMENT_NEW
select HAVE_MACH_ARM_HEAD
select HAVE_PBL_MULTI_IMAGES
select HW_HAS_PCI
@@ -154,7 +152,6 @@ config ARCH_OMAP
config ARCH_PXA
bool "Intel/Marvell PXA based"
select GENERIC_GPIO
- select HAS_POWEROFF
config ARCH_ROCKCHIP
bool "Rockchip RX3xxx"
diff --git a/arch/arm/boards/at91sam9x5ek/Makefile b/arch/arm/boards/at91sam9x5ek/Makefile
index 559df8f7b9..4939b7e17e 100644
--- a/arch/arm/boards/at91sam9x5ek/Makefile
+++ b/arch/arm/boards/at91sam9x5ek/Makefile
@@ -1,3 +1,4 @@
obj-y += init.o
obj-y += hw_version.o
bbenv-$(CONFIG_DEFAULT_ENVIRONMENT_GENERIC) += defaultenv-at91sam9x5ek
+lwl-y += lowlevel.o
diff --git a/arch/arm/boards/at91sam9x5ek/hw_version.c b/arch/arm/boards/at91sam9x5ek/hw_version.c
index 2f84d82d5d..d1ca036fe9 100644
--- a/arch/arm/boards/at91sam9x5ek/hw_version.c
+++ b/arch/arm/boards/at91sam9x5ek/hw_version.c
@@ -15,6 +15,7 @@
*/
#include <common.h>
+#include <init.h>
#include <fs.h>
#include <fcntl.h>
#include <libbb.h>
@@ -250,7 +251,7 @@ static int cm_cogent_fixup(struct device_node *root, void *unused)
return 0;
}
-void at91sam9x5ek_devices_detect_hw(void)
+static int at91sam9x5ek_devices_detect_hw(void)
{
at91sam9x5ek_devices_detect_one("/dev/ds24310");
at91sam9x5ek_devices_detect_one("/dev/ds24311");
@@ -262,4 +263,7 @@ void at91sam9x5ek_devices_detect_hw(void)
if (at91sam9x5ek_cm_is_vendor(VENDOR_COGENT))
of_register_fixup(cm_cogent_fixup, NULL);
+
+ return 0;
}
+late_initcall(at91sam9x5ek_devices_detect_hw);
diff --git a/arch/arm/boards/at91sam9x5ek/hw_version.h b/arch/arm/boards/at91sam9x5ek/hw_version.h
index 91fd42942d..3f3c8003d9 100644
--- a/arch/arm/boards/at91sam9x5ek/hw_version.h
+++ b/arch/arm/boards/at91sam9x5ek/hw_version.h
@@ -29,6 +29,5 @@ enum vendor_id {
bool at91sam9x5ek_cm_is_vendor(enum vendor_id vid);
bool at91sam9x5ek_ek_is_vendor(enum vendor_id vid);
bool at91sam9x5ek_dm_is_vendor(enum vendor_id vid);
-void at91sam9x5ek_devices_detect_hw(void);
#endif /* __HW_REVISION_H__ */
diff --git a/arch/arm/boards/at91sam9x5ek/init.c b/arch/arm/boards/at91sam9x5ek/init.c
index 9fe117c2d8..646cff5d60 100644
--- a/arch/arm/boards/at91sam9x5ek/init.c
+++ b/arch/arm/boards/at91sam9x5ek/init.c
@@ -45,28 +45,6 @@
#include "hw_version.h"
-struct w1_gpio_platform_data w1_pdata = {
- .pin = AT91_PIN_PB18,
- .ext_pullup_enable_pin = -EINVAL,
- .is_open_drain = 0,
-};
-
-static struct atmel_nand_data nand_pdata = {
- .ale = 21,
- .cle = 22,
- .det_pin = -EINVAL,
- .rdy_pin = AT91_PIN_PD5,
- .enable_pin = AT91_PIN_PD4,
- .has_pmecc = 1,
- .ecc_mode = NAND_ECC_HW,
- .pmecc_sector_size = 512,
- .pmecc_corr_cap = 2,
-#if defined(CONFIG_MTD_NAND_ATMEL_BUSWIDTH_16)
- .bus_width_16 = 1,
-#endif
- .on_flash_bbt = 1,
-};
-
static struct sam9_smc_config cm_nand_smc_config = {
.ncs_read_setup = 0,
.nrd_setup = 1,
@@ -85,10 +63,27 @@ static struct sam9_smc_config cm_nand_smc_config = {
.tdf_cycles = 1,
};
-static void ek_add_device_nand(void)
+static int ek_add_device_smc(void)
{
+ unsigned long csa;
+ csa = at91_sys_read(AT91_MATRIX_EBICSA);
+
+ /* Enable CS3 */
+ csa |= AT91_MATRIX_EBI_CS3A_SMC_NANDFLASH;
+ /* NAND flash on D16 */
+ csa |= AT91_MATRIX_NFD0_ON_D16;
+
+ /* Configure IO drive */
+ csa &= ~AT91_MATRIX_EBI_EBI_IOSR_NORMAL;
+ at91_sys_write(AT91_MATRIX_EBICSA, csa);
+
+ add_generic_device("at91sam9-smc",
+ DEVICE_ID_SINGLE, NULL,
+ AT91SAM9X5_BASE_SMC, 0x200,
+ IORESOURCE_MEM, NULL);
+
/* setup bus-width (8 or 16) */
- if (nand_pdata.bus_width_16)
+ if (IS_ENABLED(CONFIG_MTD_NAND_ATMEL_BUSWIDTH_16))
cm_nand_smc_config.mode |= AT91_SMC_DBW_16;
else
cm_nand_smc_config.mode |= AT91_SMC_DBW_8;
@@ -97,28 +92,26 @@ static void ek_add_device_nand(void)
sam9_smc_configure(0, 3, &cm_nand_smc_config);
if (at91sam9x5ek_cm_is_vendor(VENDOR_COGENT)) {
- unsigned long csa;
-
csa = at91_sys_read(AT91_MATRIX_EBICSA);
csa |= AT91_MATRIX_EBI_VDDIOMSEL_1_8V;
at91_sys_write(AT91_MATRIX_EBICSA, csa);
}
- at91_add_device_nand(&nand_pdata);
+ return 0;
}
+fs_initcall(ek_add_device_smc);
-static struct macb_platform_data macb_pdata = {
- .phy_interface = PHY_INTERFACE_MODE_RMII,
- .phy_addr = 0,
-};
-
-static void ek_add_device_eth(void)
+static int ek_register_mac_address(void)
{
- if (w1_local_mac_address_register(0, "tml", "w1-2d-0"))
- w1_local_mac_address_register(0, "tml", "w1-23-0");
+ int ret;
- at91_add_device_eth(0, &macb_pdata);
+ ret = w1_local_mac_address_register(0, "tml", "w1-2d-0");
+ if (!ret)
+ return ret;
+
+ return w1_local_mac_address_register(0, "tml", "w1-23-0");
}
+late_initcall(ek_register_mac_address);
#if defined(CONFIG_DRIVER_VIDEO_ATMEL_HLCD)
/*
@@ -163,164 +156,12 @@ static void ek_add_device_lcdc(void)
static void ek_add_device_lcdc(void) {}
#endif
-/*
- * MCI (SD/MMC)
- */
-/* mci0 detect_pin is revision dependent */
-static struct atmel_mci_platform_data mci0_data = {
- .bus_width = 4,
- .detect_pin = AT91_PIN_PD15,
- .wp_pin = -EINVAL,
-};
-
-static void ek_add_device_mci(void)
-{
- if (at91sam9x5ek_cm_is_vendor(VENDOR_COGENT))
- mci0_data.detect_pin = -EINVAL;
-
- /* MMC0 */
- at91_add_device_mci(0, &mci0_data);
-}
-
-struct qt1070_platform_data qt1070_pdata = {
- .irq_pin = AT91_PIN_PA7,
-};
-
-static struct i2c_board_info i2c_devices[] = {
- {
- .platform_data = &qt1070_pdata,
- I2C_BOARD_INFO("qt1070", 0x1b),
- }, {
- I2C_BOARD_INFO("24c512", 0x51)
- },
-};
-
-static void ek_add_device_i2c(void)
-{
- at91_set_gpio_input(qt1070_pdata.irq_pin, 0);
- at91_set_deglitch(qt1070_pdata.irq_pin, 1);
- at91_add_device_i2c(0, i2c_devices, ARRAY_SIZE(i2c_devices));
-}
-
-static const struct spi_board_info ek_cm_cogent_spi_devices[] = {
- {
- .name = "mtd_dataflash",
- .chip_select = 0,
- .max_speed_hz = 15 * 1000 * 1000,
- .bus_num = 0,
- }
-};
-
-static const struct spi_board_info ek_spi_devices[] = {
- {
- .name = "m25p80",
- .chip_select = 0,
- .max_speed_hz = 30 * 1000 * 1000,
- .bus_num = 0,
- }
-};
-
-static unsigned spi0_standard_cs[] = { AT91_PIN_PA14};
-static struct at91_spi_platform_data spi_pdata = {
- .chipselect = spi0_standard_cs,
- .num_chipselect = ARRAY_SIZE(spi0_standard_cs),
-};
-
-static void ek_add_device_spi(void)
-{
- if (at91sam9x5ek_cm_is_vendor(VENDOR_COGENT))
- spi_register_board_info(ek_cm_cogent_spi_devices,
- ARRAY_SIZE(ek_cm_cogent_spi_devices));
- else
- spi_register_board_info(ek_spi_devices,
- ARRAY_SIZE(ek_spi_devices));
- at91_add_device_spi(0, &spi_pdata);
-}
-
-#if defined(CONFIG_USB_OHCI) || defined(CONFIG_USB_EHCI)
-/*
- * USB HS Host port (common to OHCI & EHCI)
- */
-static struct at91_usbh_data ek_usbh_hs_data = {
- .ports = 2,
- .vbus_pin = {AT91_PIN_PD19, AT91_PIN_PD20},
-};
-
-static void ek_add_device_usb(void)
-{
- at91_add_device_usbh_ohci(&ek_usbh_hs_data);
- at91_add_device_usbh_ehci(&ek_usbh_hs_data);
-}
-#else
-static void ek_add_device_usb(void) {}
-#endif
-
-struct gpio_led leds[] = {
- {
- .gpio = AT91_PIN_PB18,
- .active_low = 1,
- .led = {
- .name = "d1",
- },
- }, {
- .gpio = AT91_PIN_PD21,
- .led = {
- .name = "d2",
- },
- },
-};
-
-static void __init ek_add_led(void)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(leds); i++) {
- at91_set_gpio_output(leds[i].gpio, leds[i].active_low);
- led_gpio_register(&leds[i]);
- }
- led_set_trigger(LED_TRIGGER_HEARTBEAT, &leds[1].led);
-}
-
-static int at91sam9x5ek_mem_init(void)
-{
- at91_add_device_sdram(0);
-
- return 0;
-}
-mem_initcall(at91sam9x5ek_mem_init);
-
-static void ek_add_device_w1(void)
-{
- at91_set_gpio_input(w1_pdata.pin, 0);
- at91_set_multi_drive(w1_pdata.pin, 1);
- add_generic_device_res("w1-gpio", DEVICE_ID_SINGLE, NULL, 0, &w1_pdata);
-
- at91sam9x5ek_devices_detect_hw();
-}
-
static int at91sam9x5ek_devices_init(void)
{
- ek_add_device_w1();
- ek_add_device_nand();
- ek_add_device_eth();
- ek_add_device_spi();
- ek_add_device_mci();
- ek_add_device_usb();
- ek_add_led();
- ek_add_device_i2c();
ek_add_device_lcdc();
armlinux_set_architecture(CONFIG_MACH_AT91SAM9X5EK);
- devfs_add_partition("nand0", 0x00000, SZ_256K, DEVFS_PARTITION_FIXED, "at91bootstrap_raw");
- dev_add_bb_dev("at91bootstrap_raw", "at91bootstrap");
- devfs_add_partition("nand0", SZ_256K, SZ_256K + SZ_128K, DEVFS_PARTITION_FIXED, "self_raw");
- dev_add_bb_dev("self_raw", "self0");
- devfs_add_partition("nand0", SZ_512K + SZ_128K, SZ_128K, DEVFS_PARTITION_FIXED, "env_raw");
- dev_add_bb_dev("env_raw", "env0");
- devfs_add_partition("nand0", SZ_512K + SZ_256K, SZ_128K, DEVFS_PARTITION_FIXED, "env_raw1");
- dev_add_bb_dev("env_raw1", "env1");
-
if (IS_ENABLED(CONFIG_DEFAULT_ENVIRONMENT_GENERIC))
defaultenv_append_directory(defaultenv_at91sam9x5ek);
@@ -333,15 +174,6 @@ static int at91sam9x5ek_console_init(void)
barebox_set_model("Atmel at91sam9x5-ek");
barebox_set_hostname("at91sam9x5-ek");
- at91_register_uart(0, 0);
- at91_register_uart(1, 0);
return 0;
}
console_initcall(at91sam9x5ek_console_init);
-
-static int at91sam9x5ek_main_clock(void)
-{
- at91_set_main_clock(12000000);
- return 0;
-}
-pure_initcall(at91sam9x5ek_main_clock);
diff --git a/arch/arm/boards/at91sam9x5ek/lowlevel.c b/arch/arm/boards/at91sam9x5ek/lowlevel.c
new file mode 100644
index 0000000000..acf80d7955
--- /dev/null
+++ b/arch/arm/boards/at91sam9x5ek/lowlevel.c
@@ -0,0 +1,21 @@
+#include <common.h>
+#include <linux/sizes.h>
+#include <mach/at91sam9_ddrsdr.h>
+#include <asm/barebox-arm-head.h>
+#include <asm/barebox-arm.h>
+#include <io.h>
+#include <debug_ll.h>
+
+extern char __dtb_at91sam9x5ek_start[];
+
+ENTRY_FUNCTION(start_at91sam9x5ek, r0, r1, r2)
+{
+ void *fdt;
+
+ arm_cpu_lowlevel_init();
+ arm_setup_stack(AT91SAM9X5_SRAM_BASE + AT91SAM9X5_SRAM_SIZE - 16);
+
+ fdt = __dtb_at91sam9x5ek_start - get_runtime_offset();
+
+ barebox_arm_entry(AT91_CHIPSELECT_1, at91sam9x5_get_ddram_size(), fdt);
+}
diff --git a/arch/arm/boards/guf-santaro/board.c b/arch/arm/boards/guf-santaro/board.c
index b9a52ee258..e54110886b 100644
--- a/arch/arm/boards/guf-santaro/board.c
+++ b/arch/arm/boards/guf-santaro/board.c
@@ -11,6 +11,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
+#define pr_fmt(fmt) "Santaro: " fmt
#include <common.h>
#include <init.h>
@@ -25,6 +26,105 @@
#include <bbu.h>
#include <mach/bbu.h>
#include <mach/imx6.h>
+#include <i2c/i2c.h>
+#include <gpio.h>
+
+static int i2c_device_present(struct i2c_adapter *adapter, int addr)
+{
+ struct i2c_client client = {};
+ u8 reg;
+
+ client.adapter = adapter;
+ client.addr = addr;
+
+ return i2c_write_reg(&client, 0x00, &reg, 0) < 0 ? false : true;
+}
+
+#define TOUCH_RESET_GPIO IMX_GPIO_NR(1, 20)
+
+static int edt_present, egalax_present;
+
+static int santaro_touch_fixup(struct device_node *root, void *unused)
+{
+ struct device_node *i2cnp, *np;
+
+ i2cnp = of_find_node_by_alias(root, "i2c2");
+ if (!i2cnp) {
+ pr_err("Cannot find node i2c2\n");
+ return -EINVAL;
+ }
+
+ for_each_child_of_node(i2cnp, np) {
+ int present;
+
+ if (of_device_is_compatible(np, "edt,edt-ft5206"))
+ present = edt_present;
+ else if (of_device_is_compatible(np, "eeti,egalax_ts"))
+ present = egalax_present;
+ else
+ continue;
+
+ if (present)
+ of_device_enable(np);
+ else
+ of_device_disable(np);
+ }
+
+ return 0;
+}
+
+static int santaro_detect_touch(void)
+{
+ struct device_node *np;
+ struct i2c_adapter *adapter;
+ const char *model = NULL;
+
+ if (!of_machine_is_compatible("guf,imx6q-santaro"))
+ return 0;
+
+ /*
+ * The Santaro has two different possible Touchscreen
+ * controllers. Both are on different I2C addresses.
+ * Let's probe both of them and enable in the device tree
+ * the one that's actually found on the hardware.
+ */
+
+ np = of_find_node_by_alias(NULL, "i2c2");
+ if (!np) {
+ pr_err("Cannot find node i2c2\n");
+ return -EINVAL;
+ }
+
+ adapter = of_find_i2c_adapter_by_node(np);
+ if (!adapter) {
+ pr_err("Cannot find i2c2 adapter\n");
+ return -EINVAL;
+ }
+
+ gpio_direction_output(TOUCH_RESET_GPIO, 0);
+ mdelay(10);
+ gpio_set_value(TOUCH_RESET_GPIO, 1);
+ mdelay(10);
+
+ edt_present = i2c_device_present(adapter, 0x38);
+
+ gpio_set_value(TOUCH_RESET_GPIO, 0);
+ mdelay(10);
+
+ egalax_present = i2c_device_present(adapter, 0x4);
+
+ if (edt_present)
+ model = "edt,edt-ft5206";
+ if (egalax_present)
+ model = "eeti,egalax_ts";
+
+ pr_info("Found %s Touchscreen controller\n", model);
+
+ of_register_fixup(santaro_touch_fixup, NULL);
+
+ return 0;
+}
+late_initcall(santaro_detect_touch);
static int santaro_device_init(void)
{
diff --git a/arch/arm/boards/highbank/init.c b/arch/arm/boards/highbank/init.c
index 577ccc0b74..32e217321a 100644
--- a/arch/arm/boards/highbank/init.c
+++ b/arch/arm/boards/highbank/init.c
@@ -37,14 +37,12 @@ static int hb_fixup(struct device_node *root, void *unused)
if (!(reg & HB_PWRDOM_STAT_SATA)) {
for_each_compatible_node_from(node, root, NULL, "calxeda,hb-ahci")
- of_set_property(node, "status", "disabled",
- sizeof("disabled"), 1);
+ of_property_write_string(node, "status", "disabled");
}
if (!(reg & HB_PWRDOM_STAT_EMMC)) {
for_each_compatible_node_from(node, root, NULL, "calxeda,hb-sdhci")
- of_set_property(node, "status", "disabled",
- sizeof("disabled"), 1);
+ of_property_write_string(node, "status", "disabled");
}
if ((opp_table[0] >> 16) != HB_OPP_VERSION)
diff --git a/arch/arm/boards/mioa701/gpio0_poweroff.c b/arch/arm/boards/mioa701/gpio0_poweroff.c
index 2054548aa6..01a5d0cc6e 100644
--- a/arch/arm/boards/mioa701/gpio0_poweroff.c
+++ b/arch/arm/boards/mioa701/gpio0_poweroff.c
@@ -19,6 +19,7 @@
#include <clock.h>
#include <common.h>
#include <init.h>
+#include <poweroff.h>
#include <gpio.h>
#include <poller.h>
@@ -45,7 +46,7 @@ static void try_poweroff(void)
gpio_set_value(GPIO115_LED_nKeyboard, 0);
mdelay(2000);
- poweroff();
+ poweroff_machine();
}
static void gpio0_poller_fn(struct poller_struct *poller)
diff --git a/arch/arm/boards/vscom-baltos/board.c b/arch/arm/boards/vscom-baltos/board.c
index fea992d2ef..39f40a6061 100644
--- a/arch/arm/boards/vscom-baltos/board.c
+++ b/arch/arm/boards/vscom-baltos/board.c
@@ -41,6 +41,7 @@
#include <linux/err.h>
#include <mach/bbu.h>
#include <libfile.h>
+#include <gpio.h>
static struct omap_barebox_part baltos_barebox_part = {
.nand_offset = SZ_512K,
@@ -109,6 +110,17 @@ static int baltos_read_eeprom(void)
sprintf(var_buf, "%d", hw_param.SystemId);
globalvar_add_simple("board.id", var_buf);
+ /* enable mPCIe slot */
+ gpio_direction_output(100, 1);
+
+ /* configure output signals of the external GPIO controller */
+ if (hw_param.SystemId == 210 || hw_param.SystemId == 211) {
+ gpio_direction_output(132, 0);
+ gpio_direction_output(133, 0);
+ gpio_direction_output(134, 0);
+ gpio_direction_output(135, 0);
+ }
+
return 0;
}
environment_initcall(baltos_read_eeprom);
diff --git a/arch/arm/configs/at91sam9x5ek_defconfig b/arch/arm/configs/at91sam9x5ek_defconfig
index 6ee005222a..dd427555f9 100644
--- a/arch/arm/configs/at91sam9x5ek_defconfig
+++ b/arch/arm/configs/at91sam9x5ek_defconfig
@@ -1,12 +1,15 @@
CONFIG_ARCH_AT91SAM9X5=y
+CONFIG_AT91_MULTI_BOARDS=y
+CONFIG_MACH_AT91SAM9X5EK=y
CONFIG_BAREBOX_MAX_IMAGE_SIZE=0x40000
CONFIG_AEABI=y
CONFIG_ARM_OPTIMZED_STRING_FUNCTIONS=y
-CONFIG_PBL_IMAGE=y
CONFIG_MMU=y
CONFIG_MALLOC_SIZE=0xa00000
CONFIG_EXPERIMENTAL=y
CONFIG_MALLOC_TLSF=y
+CONFIG_KALLSYMS=y
+CONFIG_RELOCATABLE=y
CONFIG_PROMPT="9G20-EK:"
CONFIG_GLOB=y
CONFIG_PROMPT_HUSH_PS2="y"
@@ -48,6 +51,7 @@ CONFIG_CMD_OFTREE=y
CONFIG_NET=y
CONFIG_NET_NFS=y
CONFIG_NET_NETCONSOLE=y
+CONFIG_OF_BAREBOX_DRIVERS=y
CONFIG_DRIVER_NET_MACB=y
CONFIG_NET_USB=y
CONFIG_NET_USB_ASIX=y
@@ -71,6 +75,7 @@ CONFIG_USB_STORAGE=y
CONFIG_MCI=y
CONFIG_MCI_STARTUP=y
CONFIG_MCI_ATMEL=y
+CONFIG_MFD_SYSCON=y
CONFIG_LED=y
CONFIG_LED_GPIO=y
CONFIG_LED_TRIGGERS=y
@@ -85,3 +90,4 @@ CONFIG_FS_TFTP=y
CONFIG_FS_FAT=y
CONFIG_FS_FAT_WRITE=y
CONFIG_FS_FAT_LFN=y
+CONFIG_LED_GPIO_OF=y
diff --git a/arch/arm/configs/kindle3_defconfig b/arch/arm/configs/kindle3_defconfig
index 0b7088e867..a4e08dfad0 100644
--- a/arch/arm/configs/kindle3_defconfig
+++ b/arch/arm/configs/kindle3_defconfig
@@ -1,8 +1,8 @@
CONFIG_ARCH_IMX=y
CONFIG_BAREBOX_MAX_IMAGE_SIZE=0x30000
CONFIG_MACH_KINDLE3=y
-CONFIG_ARCH_IMX_USBLOADER=y
CONFIG_IMX_IIM=y
+CONFIG_ARCH_IMX_USBLOADER=y
CONFIG_AEABI=y
CONFIG_ARM_BOARD_APPEND_ATAG=y
CONFIG_ARM_BOARD_PREPEND_ATAG=y
@@ -46,6 +46,7 @@ CONFIG_CMD_TIMEOUT=y
CONFIG_CMD_CRC=y
CONFIG_CMD_CRC_CMP=y
CONFIG_CMD_MEMTEST=y
+CONFIG_CMD_CLK=y
CONFIG_CMD_GPIO=y
CONFIG_CMD_I2C=y
CONFIG_CMD_SPI=y
diff --git a/arch/arm/cpu/psci.c b/arch/arm/cpu/psci.c
index d650c23ea2..eafb361a0a 100644
--- a/arch/arm/cpu/psci.c
+++ b/arch/arm/cpu/psci.c
@@ -204,13 +204,11 @@ static int of_psci_fixup(struct device_node *root, void *unused)
if (!psci)
return -EINVAL;
- ret = of_set_property(psci, "compatible", "arm,psci-1.0",
- strlen("arm,psci-1.0") + 1, 1);
+ ret = of_property_write_string(psci, "compatible", "arm,psci-1.0");
if (ret)
return ret;
- ret = of_set_property(psci, "method", "smc",
- strlen("smc") + 1, 1);
+ ret = of_property_write_string(psci, "method", "smc");
if (ret)
return ret;
diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
index 0c9e0e8dad..e8ad43bfd8 100644
--- a/arch/arm/dts/Makefile
+++ b/arch/arm/dts/Makefile
@@ -91,5 +91,7 @@ pbl-dtb-$(CONFIG_MACH_ZII_VF610_DEV) += \
vf610-zii-spu3-rev-a.dtb.o \
vf610-zii-scu4-aib-rev-c.dtb.o
+pbl-dtb-$(CONFIG_MACH_AT91SAM9X5EK) += at91sam9x5ek.dtb.o
+
clean-files := *.dtb *.dtb.S .*.dtc .*.pre .*.dts *.dtb.lzo
diff --git a/arch/arm/dts/at91sam9x5ek.dts b/arch/arm/dts/at91sam9x5ek.dts
new file mode 100644
index 0000000000..d5c7a8fe5c
--- /dev/null
+++ b/arch/arm/dts/at91sam9x5ek.dts
@@ -0,0 +1,70 @@
+/dts-v1/;
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/pinctrl/at91.h>
+
+#include <arm/at91sam9x5.dtsi>
+#include <arm/at91sam9x5_macb0.dtsi>
+#include <arm/at91sam9x5_lcd.dtsi>
+#include <arm/at91sam9x5dm.dtsi>
+#include <arm/at91sam9x5ek.dtsi>
+
+/ {
+ aliases {
+ mmc0 = &mmc0;
+ mmc1 = &mmc1;
+ };
+
+ i2c-gpio-0 {
+ status = "okay";
+ };
+
+ ahb {
+ apb {
+ pinctrl@fffff400 {
+ spi {
+ pinctrl_board_spi: spi-board {
+ atmel,pins = <AT91_PIOA 14 AT91_PERIPH_GPIO AT91_PINCTRL_NONE>;
+ };
+ };
+ };
+ };
+ };
+
+ leds {
+ /*
+ * PB18 has a resource conflict since it is both used
+ * as a heartbeat LED and 1-wire bus in the kernel
+ * device tree. Because 1-wire EEPROMs contains
+ * importatnt revision information we move heartbeat
+ * to PD21 and remove the original pb18 node
+ */
+ /delete-node/ pb18;
+
+ pd21 {
+ linux,default-trigger = "heartbeat";
+ };
+ };
+};
+
+&spi0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_spi0>, <&pinctrl_board_spi>;
+};
+
+&i2c0 {
+ status = "disabled";
+};
+
+/*
+ * This one conflicts with SPI NOR on the SoM
+ */
+&mmc1 {
+ status = "disabled";
+};
+
+&macb0 {
+ status = "okay";
+ phy-mode = "rmii";
+};
+
diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
index 2d4721a26a..9db53b2261 100644
--- a/arch/arm/mach-at91/Kconfig
+++ b/arch/arm/mach-at91/Kconfig
@@ -9,6 +9,26 @@ config HAVE_AT91_DBGU1
config HAVE_AT91_DBGU2
bool
+config HAVE_AT91_UTMI
+ bool
+
+config HAVE_AT91_USB_CLK
+ bool
+
+config COMMON_CLK_AT91
+ bool
+ select COMMON_CLK
+ select MFD_SYSCON
+
+config HAVE_AT91_SMD
+ bool
+
+config HAVE_AT91_H32MX
+ bool
+
+config HAVE_AT91_GENERATED_CLK
+ bool
+
config HAVE_AT91_LOWLEVEL_INIT
bool
@@ -20,6 +40,7 @@ config SOC_AT91SAM9
select CPU_ARM926T
select AT91SAM9_SMC
select CLOCKSOURCE_ATMEL_PIT
+ select PINCTRL
config SOC_SAMA5
bool
@@ -106,6 +127,10 @@ config SOC_AT91SAM9X5
select HAVE_AT91_DBGU0
select HAS_MACB
select AT91SAM9G45_RESET
+ select HAVE_AT91_SMD
+ select HAVE_AT91_USB_CLK
+ select HAVE_AT91_UTMI
+ select COMMON_CLK_OF_PROVIDER
help
Select this if you are using one of Atmel's AT91SAM9x5 family SoC.
This means that your SAM9 name finishes with a '5' (except if it is
@@ -140,26 +165,32 @@ config ARCH_AT91SAM9261
config ARCH_AT91SAM9263
bool "AT91SAM9263"
select SOC_AT91SAM9263
+ select HAVE_MACH_ARM_HEAD
config ARCH_AT91SAM9G10
bool "AT91SAM9G10"
select SOC_AT91SAM9261
+ select HAVE_MACH_ARM_HEAD
config ARCH_AT91SAM9G20
bool "AT91SAM9G20"
select SOC_AT91SAM9260
+ select HAVE_MACH_ARM_HEAD
config ARCH_AT91SAM9G45
bool "AT91SAM9G45 or AT91SAM9M10"
select SOC_AT91SAM9G45
+ select HAVE_MACH_ARM_HEAD
config ARCH_AT91SAM9X5
bool "AT91SAM9X5"
select SOC_AT91SAM9X5
+ select OFDEVICE
config ARCH_AT91SAM9N12
bool "AT91SAM9N12"
select SOC_AT91SAM9N12
+ select HAVE_MACH_ARM_HEAD
config ARCH_SAMA5D3
bool "SAMA5D3x"
@@ -167,6 +198,7 @@ config ARCH_SAMA5D3
select HAVE_AT91_DBGU1
select HAS_MACB
select AT91SAM9G45_RESET
+ select HAVE_MACH_ARM_HEAD
config ARCH_SAMA5D4
bool "SAMA5D4"
@@ -174,6 +206,7 @@ config ARCH_SAMA5D4
select HAVE_AT91_DBGU2
select HAS_MACB
select AT91SAM9G45_RESET
+ select HAVE_MACH_ARM_HEAD
endchoice
@@ -429,23 +462,6 @@ endif
# ----------------------------------------------------------
-if ARCH_AT91SAM9X5
-
-choice
- prompt "AT91SAM9x5 Series Board Type"
-
-config MACH_AT91SAM9X5EK
- bool "Atmel AT91SAM9x5 Series Evaluation Kit"
- help
- Select this if you re using Atmel's AT91SAM9x5-EK Evaluation Kit.
- Supported chips are sam9g15, sam9g25, sam9x25, sam9g35 and sam9x35.
-
-endchoice
-
-endif
-
-# ----------------------------------------------------------
-
if ARCH_AT91SAM9N12
choice
@@ -504,6 +520,22 @@ endif
# ----------------------------------------------------------
+
+config AT91_MULTI_BOARDS
+ bool "Allow multiple boards to be selected"
+ select HAVE_PBL_MULTI_IMAGES
+
+if AT91_MULTI_BOARDS
+
+config MACH_AT91SAM9X5EK
+ bool "Atmel AT91SAM9x5 Series Evaluation Kit"
+ depends on ARCH_AT91SAM9X5
+ help
+ Select this if you re using Atmel's AT91SAM9x5-EK Evaluation Kit.
+ Supported chips are sam9g15, sam9g25, sam9x25, sam9g35 and sam9x35.
+
+endif
+
comment "AT91 Board Options"
config MTD_AT91_DATAFLASH_CARD
diff --git a/arch/arm/mach-at91/Makefile b/arch/arm/mach-at91/Makefile
index 1f63b09aaa..0892fb4b57 100644
--- a/arch/arm/mach-at91/Makefile
+++ b/arch/arm/mach-at91/Makefile
@@ -1,4 +1,9 @@
-obj-y += setup.o clock.o irq_fixup.o
+obj-y += setup.o irq_fixup.o
+
+ifeq ($(CONFIG_COMMON_CLK_OF_PROVIDER),)
+obj-y += clock.o
+endif
+
obj-$(CONFIG_CMD_AT91_BOOT_TEST) += boot_test_cmd.o
obj-$(CONFIG_AT91_BOOTSTRAP) += bootstrap.o
@@ -28,7 +33,7 @@ obj-$(CONFIG_ARCH_AT91SAM9263) += at91sam9263.o at91sam9263_devices.o
obj-$(CONFIG_ARCH_AT91SAM9G10) += at91sam9261.o at91sam9261_devices.o
obj-$(CONFIG_ARCH_AT91SAM9G20) += at91sam9260.o at91sam9260_devices.o
obj-$(CONFIG_ARCH_AT91SAM9G45) += at91sam9g45.o at91sam9g45_devices.o
-obj-$(CONFIG_ARCH_AT91SAM9X5) += at91sam9x5.o at91sam9x5_devices.o
+obj-$(CONFIG_ARCH_AT91SAM9X5) += at91sam9x5_devices.o
obj-$(CONFIG_ARCH_AT91SAM9N12) += at91sam9n12.o at91sam9n12_devices.o
obj-$(CONFIG_ARCH_SAMA5D3) += sama5d3.o sama5d3_devices.o
obj-$(CONFIG_ARCH_SAMA5D4) += sama5d4.o sama5d4_devices.o
diff --git a/arch/arm/mach-at91/at91sam9x5.c b/arch/arm/mach-at91/at91sam9x5.c
deleted file mode 100644
index 000b7483b2..0000000000
--- a/arch/arm/mach-at91/at91sam9x5.c
+++ /dev/null
@@ -1,311 +0,0 @@
-#include <common.h>
-#include <gpio.h>
-#include <init.h>
-#include <mach/hardware.h>
-#include <mach/at91_pmc.h>
-#include <mach/io.h>
-#include <mach/cpu.h>
-
-#include "soc.h"
-#include "generic.h"
-#include "clock.h"
-
-/* --------------------------------------------------------------------
- * Clocks
- * -------------------------------------------------------------------- */
-
-/*
- * The peripheral clocks.
- */
-static struct clk pioAB_clk = {
- .name = "pioAB_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_PIOAB,
- .type = CLK_TYPE_PERIPHERAL,
-};
-static struct clk pioCD_clk = {
- .name = "pioCD_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_PIOCD,
- .type = CLK_TYPE_PERIPHERAL,
-};
-static struct clk smd_clk = {
- .name = "smd_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_SMD,
- .type = CLK_TYPE_PERIPHERAL,
-};
-static struct clk usart0_clk = {
- .name = "usart0_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_USART0,
- .type = CLK_TYPE_PERIPHERAL,
-};
-static struct clk usart1_clk = {
- .name = "usart1_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_USART1,
- .type = CLK_TYPE_PERIPHERAL,
-};
-static struct clk usart2_clk = {
- .name = "usart2_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_USART2,
- .type = CLK_TYPE_PERIPHERAL,
-};
-/* USART3 clock - Only for sam9g25/sam9x25 */
-static struct clk usart3_clk = {
- .name = "usart3_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_USART3,
- .type = CLK_TYPE_PERIPHERAL,
-};
-static struct clk twi0_clk = {
- .name = "twi0_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_TWI0,
- .type = CLK_TYPE_PERIPHERAL,
-};
-static struct clk twi1_clk = {
- .name = "twi1_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_TWI1,
- .type = CLK_TYPE_PERIPHERAL,
-};
-static struct clk twi2_clk = {
- .name = "twi2_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_TWI2,
- .type = CLK_TYPE_PERIPHERAL,
-};
-static struct clk mmc0_clk = {
- .name = "mci0_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_MCI0,
- .type = CLK_TYPE_PERIPHERAL,
-};
-static struct clk spi0_clk = {
- .name = "spi0_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_SPI0,
- .type = CLK_TYPE_PERIPHERAL,
-};
-static struct clk spi1_clk = {
- .name = "spi1_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_SPI1,
- .type = CLK_TYPE_PERIPHERAL,
-};
-static struct clk uart0_clk = {
- .name = "uart0_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_UART0,
- .type = CLK_TYPE_PERIPHERAL,
-};
-static struct clk uart1_clk = {
- .name = "uart1_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_UART1,
- .type = CLK_TYPE_PERIPHERAL,
-};
-static struct clk tcb0_clk = {
- .name = "tcb0_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_TCB,
- .type = CLK_TYPE_PERIPHERAL,
-};
-static struct clk pwm_clk = {
- .name = "pwm_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_PWM,
- .type = CLK_TYPE_PERIPHERAL,
-};
-static struct clk adc_clk = {
- .name = "adc_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_ADC,
- .type = CLK_TYPE_PERIPHERAL,
-};
-static struct clk dma0_clk = {
- .name = "dma0_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_DMA0,
- .type = CLK_TYPE_PERIPHERAL,
-};
-static struct clk dma1_clk = {
- .name = "dma1_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_DMA1,
- .type = CLK_TYPE_PERIPHERAL,
-};
-static struct clk uhphs_clk = {
- .name = "uhphs",
- .pmc_mask = 1 << AT91SAM9X5_ID_UHPHS,
- .type = CLK_TYPE_PERIPHERAL,
-};
-static struct clk udphs_clk = {
- .name = "udphs_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_UDPHS,
- .type = CLK_TYPE_PERIPHERAL,
-};
-/* emac0 clock - Only for sam9g25/sam9x25/sam9g35/sam9x35 */
-static struct clk macb0_clk = {
- .name = "macb0_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_EMAC0,
- .type = CLK_TYPE_PERIPHERAL,
-};
-/* lcd clock - Only for sam9g15/sam9g35/sam9x35 */
-static struct clk lcdc_clk = {
- .name = "lcdc_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_LCDC,
- .type = CLK_TYPE_PERIPHERAL,
-};
-/* isi clock - Only for sam9g25 */
-static struct clk isi_clk = {
- .name = "isi_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_ISI,
- .type = CLK_TYPE_PERIPHERAL,
-};
-static struct clk mmc1_clk = {
- .name = "mci1_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_MCI1,
- .type = CLK_TYPE_PERIPHERAL,
-};
-/* emac1 clock - Only for sam9x25 */
-static struct clk macb1_clk = {
- .name = "macb1_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_EMAC1,
- .type = CLK_TYPE_PERIPHERAL,
-};
-static struct clk ssc_clk = {
- .name = "ssc_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_SSC,
- .type = CLK_TYPE_PERIPHERAL,
-};
-/* can0 clock - Only for sam9x35 */
-static struct clk can0_clk = {
- .name = "can0_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_CAN0,
- .type = CLK_TYPE_PERIPHERAL,
-};
-/* can1 clock - Only for sam9x35 */
-static struct clk can1_clk = {
- .name = "can1_clk",
- .pmc_mask = 1 << AT91SAM9X5_ID_CAN1,
- .type = CLK_TYPE_PERIPHERAL,
-};
-
-static struct clk *periph_clocks[] __initdata = {
- &pioAB_clk,
- &pioCD_clk,
- &smd_clk,
- &usart0_clk,
- &usart1_clk,
- &usart2_clk,
- &twi0_clk,
- &twi1_clk,
- &twi2_clk,
- &mmc0_clk,
- &spi0_clk,
- &spi1_clk,
- &uart0_clk,
- &uart1_clk,
- &tcb0_clk,
- &pwm_clk,
- &adc_clk,
- &dma0_clk,
- &dma1_clk,
- &uhphs_clk,
- &udphs_clk,
- &mmc1_clk,
- &ssc_clk,
- // irq0
-};
-
-static struct clk_lookup periph_clocks_lookups[] = {
- CLKDEV_CON_DEV_ID("macb_clk", "macb0", &macb0_clk),
- CLKDEV_CON_DEV_ID("macb_clk", "macb1", &macb1_clk),
- CLKDEV_CON_ID("ohci_clk", &uhphs_clk),
- CLKDEV_CON_DEV_ID("ehci_clk", "atmel-ehci", &uhphs_clk),
- CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi0", &spi0_clk),
- CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi1", &spi1_clk),
- CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci0", &mmc0_clk),
- CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci1", &mmc1_clk),
- CLKDEV_DEV_ID("at91sam9x5-gpio0", &pioAB_clk),
- CLKDEV_DEV_ID("at91sam9x5-gpio1", &pioAB_clk),
- CLKDEV_DEV_ID("at91sam9x5-gpio2", &pioCD_clk),
- CLKDEV_DEV_ID("at91sam9x5-gpio3", &pioCD_clk),
- CLKDEV_DEV_ID("at91-pit", &mck),
- CLKDEV_CON_DEV_ID("hck1", "atmel_hlcdfb", &lcdc_clk),
-};
-
-static struct clk_lookup usart_clocks_lookups[] = {
- CLKDEV_CON_DEV_ID("usart", "atmel_usart0", &mck),
- CLKDEV_CON_DEV_ID("usart", "atmel_usart1", &usart0_clk),
- CLKDEV_CON_DEV_ID("usart", "atmel_usart2", &usart1_clk),
- CLKDEV_CON_DEV_ID("usart", "atmel_usart3", &usart2_clk),
- CLKDEV_CON_DEV_ID("usart", "atmel_usart4", &usart3_clk),
-};
-
-/*
- * The two programmable clocks.
- * You must configure pin multiplexing to bring these signals out.
- */
-static struct clk pck0 = {
- .name = "pck0",
- .pmc_mask = AT91_PMC_PCK0,
- .type = CLK_TYPE_PROGRAMMABLE,
- .id = 0,
-};
-static struct clk pck1 = {
- .name = "pck1",
- .pmc_mask = AT91_PMC_PCK1,
- .type = CLK_TYPE_PROGRAMMABLE,
- .id = 1,
-};
-
-static void __init at91sam9x5_register_clocks(void)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(periph_clocks); i++)
- clk_register(periph_clocks[i]);
-
- clkdev_add_table(periph_clocks_lookups,
- ARRAY_SIZE(periph_clocks_lookups));
- clkdev_add_table(usart_clocks_lookups,
- ARRAY_SIZE(usart_clocks_lookups));
-
- if (cpu_is_at91sam9g25()
- || cpu_is_at91sam9x25())
- clk_register(&usart3_clk);
-
- if (cpu_is_at91sam9g25()
- || cpu_is_at91sam9x25()
- || cpu_is_at91sam9g35()
- || cpu_is_at91sam9x35())
- clk_register(&macb0_clk);
-
- if (cpu_is_at91sam9g15()
- || cpu_is_at91sam9g35()
- || cpu_is_at91sam9x35())
- clk_register(&lcdc_clk);
-
- if (cpu_is_at91sam9g25())
- clk_register(&isi_clk);
-
- if (cpu_is_at91sam9x25())
- clk_register(&macb1_clk);
-
- if (cpu_is_at91sam9x25()
- || cpu_is_at91sam9x35()) {
- clk_register(&can0_clk);
- clk_register(&can1_clk);
- }
-
- clk_register(&pck0);
- clk_register(&pck1);
-}
-
-/* --------------------------------------------------------------------
- * AT91SAM9x5 processor initialization
- * -------------------------------------------------------------------- */
-
-static void at91sam9x5_initialize(void)
-{
- /* Register the processor-specific clocks */
- at91sam9x5_register_clocks();
-
- /* Register GPIO subsystem */
- at91_add_sam9x5_gpio(0, AT91SAM9X5_BASE_PIOA);
- at91_add_sam9x5_gpio(1, AT91SAM9X5_BASE_PIOB);
- at91_add_sam9x5_gpio(2, AT91SAM9X5_BASE_PIOC);
- at91_add_sam9x5_gpio(3, AT91SAM9X5_BASE_PIOD);
-
- at91_add_pit(AT91SAM9X5_BASE_PIT);
- at91_add_sam9_smc(DEVICE_ID_SINGLE, AT91SAM9X5_BASE_SMC, 0x200);
-}
-
-AT91_SOC_START(sam9x5)
- .init = at91sam9x5_initialize,
-AT91_SOC_END
diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index 491b220cad..5d76e00aef 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -32,11 +32,13 @@
void at91_set_main_clock(unsigned long rate);
+#define AT91_MAX_USBH_PORTS 3
+
/* USB Host */
struct at91_usbh_data {
u8 ports; /* number of ports on root hub */
- int vbus_pin[2]; /* port power-control pin */
- u8 vbus_pin_active_low[2]; /* vbus polarity */
+ int vbus_pin[AT91_MAX_USBH_PORTS]; /* port power-control pin */
+ u8 vbus_pin_active_low[AT91_MAX_USBH_PORTS]; /* vbus polarity */
};
extern void __init at91_add_device_usbh_ohci(struct at91_usbh_data *data);
extern void __init at91_add_device_usbh_ehci(struct at91_usbh_data *data);
diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c
index 1fa50ac054..8f32af043d 100644
--- a/arch/arm/mach-at91/setup.c
+++ b/arch/arm/mach-at91/setup.c
@@ -85,7 +85,6 @@ static void __init soc_detect(u32 dbgu_base)
case ARCH_ID_AT91SAM9X5:
at91_soc_initdata.type = AT91_SOC_SAM9X5;
- at91_boot_soc = at91sam9x5_soc;
break;
case ARCH_ID_AT91SAM9N12:
@@ -284,6 +283,9 @@ static int at91_detect(void)
pr_info("AT91: Detected soc subtype: %s\n",
at91_get_soc_subtype(&at91_soc_initdata));
+ if (IS_ENABLED(CONFIG_COMMON_CLK_OF_PROVIDER))
+ return 0;
+
if (!at91_soc_is_enabled())
panic("AT91: Soc not enabled");
diff --git a/arch/arm/mach-highbank/reset.c b/arch/arm/mach-highbank/reset.c
index 929ded5951..b60f34452e 100644
--- a/arch/arm/mach-highbank/reset.c
+++ b/arch/arm/mach-highbank/reset.c
@@ -6,6 +6,7 @@
#include <common.h>
#include <io.h>
+#include <poweroff.h>
#include <restart.h>
#include <init.h>
@@ -20,15 +21,7 @@ static void __noreturn highbank_restart_soc(struct restart_handler *rst)
hang();
}
-static int restart_register_feature(void)
-{
- restart_handler_register_fn(highbank_restart_soc);
-
- return 0;
-}
-coredevice_initcall(restart_register_feature);
-
-void __noreturn poweroff()
+void __noreturn highbank_poweroff(struct poweroff_handler *handler)
{
shutdown_barebox();
@@ -37,3 +30,12 @@ void __noreturn poweroff()
while(1);
}
+
+static int highbank_init(void)
+{
+ restart_handler_register_fn(highbank_restart_soc);
+ poweroff_handler_register_fn(highbank_poweroff);
+
+ return 0;
+}
+coredevice_initcall(highbank_init);
diff --git a/arch/arm/mach-pxa/pxa2xx.c b/arch/arm/mach-pxa/pxa2xx.c
index b712b388c8..e28378e6db 100644
--- a/arch/arm/mach-pxa/pxa2xx.c
+++ b/arch/arm/mach-pxa/pxa2xx.c
@@ -14,6 +14,7 @@
#include <common.h>
#include <init.h>
+#include <poweroff.h>
#include <reset_source.h>
#include <mach/hardware.h>
#include <mach/pxa-regs.h>
@@ -46,9 +47,7 @@ void pxa_clear_reset_source(void)
RCSR = RCSR_GPR | RCSR_SMR | RCSR_WDR | RCSR_HWR;
}
-device_initcall(pxa_detect_reset_source);
-
-void __noreturn poweroff(void)
+static void __noreturn pxa2xx_poweroff(struct poweroff_handler *handler)
{
shutdown_barebox();
@@ -57,3 +56,13 @@ void __noreturn poweroff(void)
pxa_suspend(PWRMODE_DEEPSLEEP);
unreachable();
}
+
+static int pxa2xx_init(void)
+{
+ poweroff_handler_register_fn(pxa2xx_poweroff);
+
+ pxa_detect_reset_source();
+
+ return 0;
+}
+device_initcall(pxa2xx_init);
diff --git a/arch/arm/mach-pxa/pxa3xx.c b/arch/arm/mach-pxa/pxa3xx.c
index 86ca63b160..ccfd952b5e 100644
--- a/arch/arm/mach-pxa/pxa3xx.c
+++ b/arch/arm/mach-pxa/pxa3xx.c
@@ -14,6 +14,7 @@
#include <common.h>
#include <init.h>
+#include <poweroff.h>
#include <reset_source.h>
#include <mach/hardware.h>
#include <mach/pxa-regs.h>
@@ -48,7 +49,7 @@ void pxa_clear_reset_source(void)
device_initcall(pxa_detect_reset_source);
-void __noreturn poweroff(void)
+static void __noreturn pxa3xx_poweroff(struct poweroff_handler *handler)
{
shutdown_barebox();
@@ -57,3 +58,13 @@ void __noreturn poweroff(void)
pxa3xx_suspend(PXA3xx_PM_S3D4C4);
unreachable();
}
+
+static int pxa3xx_init(void)
+{
+ poweroff_handler_register_fn(pxa3xx_poweroff);
+
+ pxa_detect_reset_source();
+
+ return 0;
+}
+device_initcall(pxa3xx_init);
diff --git a/arch/mips/mach-xburst/Kconfig b/arch/mips/mach-xburst/Kconfig
index fd106fefe0..ee79ff6167 100644
--- a/arch/mips/mach-xburst/Kconfig
+++ b/arch/mips/mach-xburst/Kconfig
@@ -21,7 +21,6 @@ choice
config BOARD_RZX50
bool "Ritmix RZX-50"
- select HAS_POWEROFF
select CPU_JZ4755
config BOARD_CI20
diff --git a/arch/mips/mach-xburst/reset-jz4750.c b/arch/mips/mach-xburst/reset-jz4750.c
index 25830f130e..1fdcc7b8af 100644
--- a/arch/mips/mach-xburst/reset-jz4750.c
+++ b/arch/mips/mach-xburst/reset-jz4750.c
@@ -22,6 +22,8 @@
#include <common.h>
#include <io.h>
+#include <init.h>
+#include <poweroff.h>
#include <mach/jz4750d_regs.h>
static void __noreturn jz4750d_halt(void)
@@ -37,7 +39,7 @@ static void __noreturn jz4750d_halt(void)
unreachable();
}
-void __noreturn poweroff()
+static void __noreturn jz4750_poweroff(struct poweroff_handler *handler)
{
u32 ctrl;
@@ -50,4 +52,11 @@ void __noreturn poweroff()
writel(RTC_HCR_PD, (u32 *)RTC_HCR);
jz4750d_halt();
}
-EXPORT_SYMBOL(poweroff);
+
+static int jz4750_init(void)
+{
+ poweroff_handler_register_fn(jz4750_poweroff);
+
+ return 0;
+}
+coredevice_initcall(jz4750_init);
diff --git a/arch/sandbox/board/hostfile.c b/arch/sandbox/board/hostfile.c
index c1a2643bc7..e7d92ea031 100644
--- a/arch/sandbox/board/hostfile.c
+++ b/arch/sandbox/board/hostfile.c
@@ -133,8 +133,7 @@ static int of_hostfile_fixup(struct device_node *root, void *ctx)
node = of_new_node(root, hf->devname);
- ret = of_set_property(node, "compatible", hostfile_dt_ids->compatible,
- strlen(hostfile_dt_ids->compatible) + 1, 1);
+ ret = of_property_write_string(node, "compatible", hostfile_dt_ids->compatible);
if (ret)
return ret;
@@ -146,8 +145,7 @@ static int of_hostfile_fixup(struct device_node *root, void *ctx)
if (ret)
return ret;
- ret = of_set_property(node, "barebox,filename", hf->filename,
- strlen(hf->filename) + 1, 1);
+ ret = of_property_write_string(node, "barebox,filename", hf->filename);
return ret;
}
diff --git a/arch/sandbox/configs/sandbox_defconfig b/arch/sandbox/configs/sandbox_defconfig
index c20dd15a0b..dbaff12bfb 100644
--- a/arch/sandbox/configs/sandbox_defconfig
+++ b/arch/sandbox/configs/sandbox_defconfig
@@ -79,6 +79,7 @@ CONFIG_FS_FAT_WRITE=y
CONFIG_FS_FAT_LFN=y
CONFIG_FS_BPKFS=y
CONFIG_FS_UIMAGEFS=y
+CONFIG_FS_SQUASHFS=y
CONFIG_BZLIB=y
CONFIG_LZ4_DECOMPRESS=y
CONFIG_XZ_DECOMPRESS=y
diff --git a/commands/Kconfig b/commands/Kconfig
index bc0885c69d..ae2dc4b094 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -6,10 +6,6 @@ config COMMAND_SUPPORT
depends on !SHELL_NONE
default y
-config HAS_POWEROFF
- bool
- default n
-
if COMMAND_SUPPORT
config COMPILE_HASH
@@ -1848,7 +1844,6 @@ config CMD_NAND_BITFLIP
config CMD_POWEROFF
tristate
- depends on HAS_POWEROFF
prompt "poweroff"
help
Turn the power off.
@@ -1930,6 +1925,11 @@ config CMD_WD_DEFAULT_TIMOUT
'wd' is done without a timeout value (which means the watchdog gets
enabled and re-triggered with the default timeout value).
+config CMD_HAB
+ bool
+ depends on HAB
+ prompt "High Assurance boot (hab)"
+
# end Hardware manipulation commands
endmenu
@@ -1967,6 +1967,12 @@ config CMD_FIRMWARELOAD
Provides the "firmwareload" command which deals with devices which need
firmware to work. It is also used to upload firmware to FPGA devices.
+config CMD_KEYSTORE
+ depends on CRYPTO_KEYSTORE
+ bool
+ prompt "keystore"
+ help
+ keystore provides access to the barebox keystore.
config CMD_LINUX_EXEC
bool "linux exec"
@@ -2115,6 +2121,12 @@ config CMD_SPD_DECODE
help
decode spd eeprom
+config CMD_SEED
+ tristate
+ prompt "seed"
+ help
+ Seed the pseudo random number generator (PRNG)
+
# end Miscellaneous commands
endmenu
diff --git a/commands/Makefile b/commands/Makefile
index 601f15fc38..37486dceb1 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -87,11 +87,13 @@ obj-$(CONFIG_CMD_AUTOMOUNT) += automount.o
obj-$(CONFIG_CMD_GLOBAL) += global.o
obj-$(CONFIG_CMD_DMESG) += dmesg.o
obj-$(CONFIG_CMD_BASENAME) += basename.o
+obj-$(CONFIG_CMD_HAB) += hab.o
obj-$(CONFIG_CMD_DIRNAME) += dirname.o
obj-$(CONFIG_CMD_READLINK) += readlink.o
obj-$(CONFIG_CMD_LET) += let.o
obj-$(CONFIG_CMD_LN) += ln.o
obj-$(CONFIG_CMD_CLK) += clk.o
+obj-$(CONFIG_CMD_KEYSTORE) += keystore.o
obj-$(CONFIG_CMD_TFTP) += tftp.o
obj-$(CONFIG_CMD_FILETYPE) += filetype.o
obj-$(CONFIG_CMD_BAREBOX_UPDATE)+= barebox-update.o
@@ -120,3 +122,4 @@ obj-$(CONFIG_CMD_DHRYSTONE) += dhrystone.o
obj-$(CONFIG_CMD_SPD_DECODE) += spd_decode.o
obj-$(CONFIG_CMD_MMC_EXTCSD) += mmc_extcsd.o
obj-$(CONFIG_CMD_NAND_BITFLIP) += nand-bitflip.o
+obj-$(CONFIG_CMD_SEED) += seed.o
diff --git a/commands/digest.c b/commands/digest.c
index 02a9f6f0de..0edbbec32c 100644
--- a/commands/digest.c
+++ b/commands/digest.c
@@ -36,12 +36,16 @@ int __do_digest(struct digest *d, unsigned char *sig,
while (*argv) {
char *filename = "/dev/mem";
loff_t start = 0, size = ~0;
+ int show_area = 1;
/* arguments are either file, file+area or area */
if (parse_area_spec(*argv, &start, &size)) {
filename = *argv;
- if (argv[1] && !parse_area_spec(argv[1], &start, &size))
+ show_area = 0;
+ if (argv[1] && !parse_area_spec(argv[1], &start, &size)) {
argv++;
+ show_area = 1;
+ }
}
ret = digest_file_window(d, filename,
@@ -53,8 +57,12 @@ int __do_digest(struct digest *d, unsigned char *sig,
for (i = 0; i < digest_length(d); i++)
printf("%02x", hash[i]);
- printf(" %s\t0x%08llx ... 0x%08llx\n",
- filename, start, start + size);
+ printf(" %s", filename);
+ if (show_area)
+ printf("\t0x%08llx ... 0x%08llx",
+ start, start + size);
+
+ puts("\n");
}
}
diff --git a/commands/hab.c b/commands/hab.c
new file mode 100644
index 0000000000..0d7ee8e76c
--- /dev/null
+++ b/commands/hab.c
@@ -0,0 +1,120 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2.
+ *
+ * This program is distributed 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 <common.h>
+#include <command.h>
+#include <complete.h>
+#include <fs.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <linux/ctype.h>
+#include <errno.h>
+#include <hab.h>
+
+static int do_hab(int argc, char *argv[])
+{
+ int opt, ret, i;
+ char *srkhashfile = NULL, *srkhash = NULL;
+ unsigned flags = 0;
+ u8 srk[SRK_HASH_SIZE];
+ int lockdown = 0, info = 0;
+
+ while ((opt = getopt(argc, argv, "s:fpx:li")) > 0) {
+ switch (opt) {
+ case 's':
+ srkhashfile = optarg;
+ break;
+ case 'f':
+ flags |= IMX_SRK_HASH_FORCE;
+ break;
+ case 'p':
+ flags |= IMX_SRK_HASH_WRITE_PERMANENT;
+ break;
+ case 'x':
+ srkhash = optarg;
+ break;
+ case 'l':
+ lockdown = 1;
+ break;
+ case 'i':
+ info = 1;
+ break;
+ default:
+ return COMMAND_ERROR_USAGE;
+ }
+ }
+
+ if (!info && !lockdown && !srkhashfile && !srkhash) {
+ printf("Nothing to do\n");
+ return COMMAND_ERROR_USAGE;
+ }
+
+ if (info) {
+ ret = imx_hab_read_srk_hash(srk);
+ if (ret)
+ return ret;
+
+ printf("Current SRK hash: ");
+ for (i = 0; i < SRK_HASH_SIZE; i++)
+ printf("%02x", srk[i]);
+ printf("\n");
+
+ if (imx_hab_device_locked_down())
+ printf("secure mode\n");
+ else
+ printf("devel mode\n");
+
+ return 0;
+ }
+
+ if (srkhashfile && srkhash) {
+ printf("-s and -x options may not be given together\n");
+ return COMMAND_ERROR_USAGE;
+ }
+
+ if (srkhashfile) {
+ ret = imx_hab_write_srk_hash_file(srkhashfile, flags);
+ if (ret)
+ return ret;
+ } else if (srkhash) {
+ ret = imx_hab_write_srk_hash_hex(srkhash, flags);
+ if (ret)
+ return ret;
+ }
+
+ if (lockdown) {
+ ret = imx_hab_lockdown_device(flags);
+ if (ret)
+ return ret;
+ printf("Device successfully locked down\n");
+ }
+
+ return 0;
+}
+
+BAREBOX_CMD_HELP_START(hab)
+BAREBOX_CMD_HELP_TEXT("Handle i.MX HAB (High Assurance Boot)")
+BAREBOX_CMD_HELP_TEXT("")
+BAREBOX_CMD_HELP_OPT ("-s <file>", "Burn Super Root Key hash from <file>")
+BAREBOX_CMD_HELP_OPT ("-x <sha256>", "Burn Super Root Key hash from hex string")
+BAREBOX_CMD_HELP_OPT ("-i", "Print HAB info")
+BAREBOX_CMD_HELP_OPT ("-f", "Force. Write even when a key is already written")
+BAREBOX_CMD_HELP_OPT ("-l", "Lockdown device. Dangerous! After executing only signed images can be booted")
+BAREBOX_CMD_HELP_OPT ("-p", "Permanent. Really burn fuses. Be careful!")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(hab)
+ .cmd = do_hab,
+ BAREBOX_CMD_DESC("Handle i.MX HAB")
+ BAREBOX_CMD_OPTS("sxfp")
+ BAREBOX_CMD_GROUP(CMD_GRP_HWMANIP)
+ BAREBOX_CMD_HELP(cmd_hab_help)
+BAREBOX_CMD_END
diff --git a/commands/keystore.c b/commands/keystore.c
new file mode 100644
index 0000000000..52c4be2639
--- /dev/null
+++ b/commands/keystore.c
@@ -0,0 +1,100 @@
+#include <common.h>
+#include <command.h>
+#include <getopt.h>
+#include <libfile.h>
+#include <crypto/keystore.h>
+#include <linux/kernel.h>
+#include <fs.h>
+
+static int do_keystore(int argc, char *argv[])
+{
+ int opt;
+ int ret;
+ int do_remove = 0;
+ const char *name;
+ const char *file = NULL;
+ char *secret_str = NULL;
+ void *secret;
+ int s_len;
+
+ while ((opt = getopt(argc, argv, "rs:f:")) > 0) {
+ switch (opt) {
+ case 'r':
+ do_remove = 1;
+ break;
+ case 's':
+ secret_str = optarg;
+ break;
+ case 'f':
+ file = optarg;
+ break;
+ default:
+ return COMMAND_ERROR_USAGE;
+ }
+ }
+
+ if (argc == optind)
+ return COMMAND_ERROR_USAGE;
+
+ if (!do_remove && !file && !secret_str)
+ return COMMAND_ERROR_USAGE;
+
+ if (file && secret_str)
+ return COMMAND_ERROR_USAGE;
+
+ name = argv[optind];
+
+ if (do_remove) {
+ keystore_forget_secret(name);
+ printf("forgotten secret for key %s\n", name);
+ return 0;
+ }
+
+ if (file) {
+ ret = read_file_2(file, &s_len, (void *)&secret_str, FILESIZE_MAX);
+ if (ret) {
+ printf("Cannot open %s: %s\n", file, strerror(-ret));
+ return 1;
+ }
+ } else if (secret_str) {
+ s_len = strlen(secret_str);
+ }
+
+ if (s_len & 1) {
+ printf("invalid secret len. Must be whole bytes\n");
+ return 1;
+ }
+
+ secret = xzalloc(s_len / 2);
+ ret = hex2bin(secret, secret_str, s_len / 2);
+ if (ret) {
+ printf("Cannot convert %s to binary: %s\n", secret_str, strerror(-ret));
+ return 1;
+ }
+
+ ret = keystore_set_secret(name, secret, s_len / 2);
+ if (ret)
+ printf("cannot set secret for key %s: %s\n", name, strerror(-ret));
+ else
+ printf("Added secret for key %s\n", name);
+
+ free(secret);
+
+ return ret ? 1 : 0;
+}
+
+BAREBOX_CMD_HELP_START(keystore)
+BAREBOX_CMD_HELP_TEXT("")
+BAREBOX_CMD_HELP_TEXT("Options:")
+BAREBOX_CMD_HELP_OPT("-r", "remove a key from the keystore")
+BAREBOX_CMD_HELP_OPT("-s <key>", "set a key in the keystore")
+BAREBOX_CMD_HELP_OPT("-f <keyfile>", "set a key in the keystore, read secret from file")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(keystore)
+ .cmd = do_keystore,
+ BAREBOX_CMD_DESC("manage keys")
+ BAREBOX_CMD_OPTS("[-rsf] <keyname>")
+ BAREBOX_CMD_GROUP(CMD_GRP_MISC)
+ BAREBOX_CMD_HELP(cmd_keystore_help)
+BAREBOX_CMD_END
diff --git a/commands/led.c b/commands/led.c
index 354f74df8f..a53f0df6a2 100644
--- a/commands/led.c
+++ b/commands/led.c
@@ -29,6 +29,44 @@ static int do_led(int argc, char *argv[])
unsigned long value;
struct led *led;
int ret;
+ int opt;
+ int flash = 0, blink = 0;
+ int blink_on_ms = 500;
+ int blink_off_ms = 500;
+
+ while ((opt = getopt(argc, argv, "fb")) > 0) {
+ switch(opt) {
+ case 'f':
+ flash = 1;
+ break;
+ case 'b':
+ blink = 1;
+ break;
+ }
+ }
+
+ if (flash || blink) {
+ int args = argc - optind;
+
+ if (!args || (flash && blink))
+ return COMMAND_ERROR_USAGE;
+
+ led = led_by_name_or_number(argv[optind]);
+ if (!led) {
+ printf("no such LED: %s\n", argv[optind]);
+ return 1;
+ }
+
+ if (args > 1)
+ blink_on_ms = simple_strtoul(argv[optind + 1], NULL, 0);
+ if (args > 2)
+ blink_off_ms = simple_strtoul(argv[optind + 2], NULL, 0);
+
+ if (flash)
+ return led_flash(led, blink_on_ms);
+ if (blink)
+ return led_blink(led, blink_on_ms, blink_off_ms);
+ }
if (argc == 1) {
int i = 0;
@@ -73,9 +111,13 @@ static int do_led(int argc, char *argv[])
BAREBOX_CMD_HELP_START(led)
BAREBOX_CMD_HELP_TEXT("Control the value of a LED. The exact meaning of VALUE is unspecified,")
BAREBOX_CMD_HELP_TEXT("it can be a brightness, or a color. Most often a value of '1' means on")
-BAREBOX_CMD_HELP_TEXT("and '0' means off.")
+BAREBOX_CMD_HELP_TEXT("and '0' means off. Basic usage is 'led <led> <value>'. LEDs can be given")
+BAREBOX_CMD_HELP_TEXT("by name or number.")
BAREBOX_CMD_HELP_TEXT("")
BAREBOX_CMD_HELP_TEXT("Without arguments the available LEDs are listed.")
+BAREBOX_CMD_HELP_TEXT("Options:")
+BAREBOX_CMD_HELP_OPT ("-b <led> [duration-on-ms] [duration-off-ms]", "blink a LED")
+BAREBOX_CMD_HELP_OPT ("-f <led> [duration-ms]", "flash a LED")
BAREBOX_CMD_HELP_END
BAREBOX_CMD_START(led)
diff --git a/commands/nand-bitflip.c b/commands/nand-bitflip.c
index fe56f222cf..a8a97c153a 100644
--- a/commands/nand-bitflip.c
+++ b/commands/nand-bitflip.c
@@ -69,6 +69,10 @@ static int do_nand_bitflip(int argc, char *argv[])
block += mtd_div_by_eb(offset, meminfo.mtd);
offset = mtd_mod_by_eb(offset, meminfo.mtd);
+ if ((int)offset % meminfo.mtd->writesize) {
+ printf("offset has to be pagesize aligned\n");
+ return 1;
+ }
if (!check) {
ret = mtd_peb_create_bitflips(meminfo.mtd, block, offset, meminfo.writesize,
diff --git a/commands/poweroff.c b/commands/poweroff.c
index e8c726a7f2..bbafa13bd0 100644
--- a/commands/poweroff.c
+++ b/commands/poweroff.c
@@ -19,10 +19,11 @@
#include <common.h>
#include <command.h>
+#include <poweroff.h>
static int cmd_poweroff(int argc, char *argv[])
{
- poweroff();
+ poweroff_machine();
/* Not reached */
return 1;
diff --git a/commands/seed.c b/commands/seed.c
new file mode 100644
index 0000000000..e378cd7635
--- /dev/null
+++ b/commands/seed.c
@@ -0,0 +1,44 @@
+/*
+ * (c) 2017 Oleksij Rempel <kernel@pengutronix.de>
+ *
+ * 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 <common.h>
+#include <command.h>
+#include <stdlib.h>
+#include <linux/ctype.h>
+
+static int do_seed(int argc, char *argv[])
+{
+ if (argc < 2)
+ return COMMAND_ERROR_USAGE;
+
+ if (isdigit(*argv[1])) {
+ srand(simple_strtoul(argv[1], NULL, 0));
+ return 0;
+ }
+
+ printf("numerical parameter expected\n");
+ return 1;
+}
+
+BAREBOX_CMD_HELP_START(seed)
+BAREBOX_CMD_HELP_TEXT("Seed the pseudo random number generator")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(seed)
+ .cmd = do_seed,
+ BAREBOX_CMD_DESC("seed the PRNG")
+ BAREBOX_CMD_OPTS("VALUE")
+ BAREBOX_CMD_GROUP(CMD_GRP_MISC)
+ BAREBOX_CMD_HELP(cmd_seed_help)
+BAREBOX_CMD_END
diff --git a/commands/state.c b/commands/state.c
index 4b51759e3e..c57a906ff0 100644
--- a/commands/state.c
+++ b/commands/state.c
@@ -21,20 +21,27 @@ static int do_state(int argc, char *argv[])
{
int opt, ret = 0;
struct state *state = NULL;
- int do_save = 0;
+ int do_save = 0, do_load = 0;
const char *statename = "state";
+ int no_auth = 0;
- while ((opt = getopt(argc, argv, "s")) > 0) {
+ while ((opt = getopt(argc, argv, "sln")) > 0) {
switch (opt) {
case 's':
do_save = 1;
break;
+ case 'l':
+ do_load = 1;
+ break;
+ case 'n':
+ no_auth = 1;
+ break;
default:
return COMMAND_ERROR_USAGE;
}
}
- if (!do_save) {
+ if (!do_save && !do_load) {
state_info();
return 0;
}
@@ -48,8 +55,14 @@ static int do_state(int argc, char *argv[])
return -ENOENT;
}
- if (do_save)
+ if (do_load) {
+ if (no_auth)
+ ret = state_load_no_auth(state);
+ else
+ ret = state_load(state);
+ } else if (do_save) {
ret = state_save(state);
+ }
return ret;
}
diff --git a/commands/stddev.c b/commands/stddev.c
index 318d057417..93da2c7398 100644
--- a/commands/stddev.c
+++ b/commands/stddev.c
@@ -17,6 +17,7 @@
#include <common.h>
#include <init.h>
+#include <stdlib.h>
static ssize_t zero_read(struct cdev *cdev, void *buf, size_t count, loff_t offset, ulong flags)
{
@@ -100,3 +101,31 @@ static int null_init(void)
}
device_initcall(null_init);
+
+static ssize_t prng_read(struct cdev *cdev, void *buf, size_t count, loff_t offset, ulong flags)
+{
+ get_random_bytes(buf, count);
+ return count;
+}
+
+static struct file_operations prngops = {
+ .read = prng_read,
+ .lseek = dev_lseek_default,
+};
+
+static int prng_init(void)
+{
+ struct cdev *cdev;
+
+ cdev = xzalloc(sizeof (*cdev));
+
+ cdev->name = "prng";
+ cdev->flags = DEVFS_IS_CHARACTER_DEV;
+ cdev->ops = &prngops;
+
+ devfs_create(cdev);
+
+ return 0;
+}
+
+device_initcall(prng_init);
diff --git a/commands/trigger.c b/commands/trigger.c
index 2758ce74e8..0dd3b346fa 100644
--- a/commands/trigger.c
+++ b/commands/trigger.c
@@ -28,59 +28,62 @@
#define LED_COMMAND_SHOW_INFO 2
#define LED_COMMAND_DISABLE_TRIGGER 3
-static char *trigger_names[] = {
- [LED_TRIGGER_PANIC] = "panic",
- [LED_TRIGGER_HEARTBEAT] = "heartbeat",
- [LED_TRIGGER_NET_RX] = "net rx",
- [LED_TRIGGER_NET_TX] = "net tx",
- [LED_TRIGGER_NET_TXRX] = "net",
- [LED_TRIGGER_DEFAULT_ON] = "default on",
-};
static int do_trigger(int argc, char *argv[])
{
- struct led *led;
- int i, opt, ret = 0;
+ struct led *led = NULL;
+ int opt, ret = 0;
int cmd = LED_COMMAND_SHOW_INFO;
- unsigned long trigger = 0;
+ enum led_trigger trigger;
+ const char *led_name = NULL;
+ const char *trigger_name = NULL;
while((opt = getopt(argc, argv, "t:d:")) > 0) {
switch(opt) {
case 't':
- trigger = simple_strtoul(optarg, NULL, 0);
+ trigger_name = optarg;
cmd = LED_COMMAND_SET_TRIGGER;
break;
case 'd':
- trigger = simple_strtoul(optarg, NULL, 0);
+ led_name = optarg;
cmd = LED_COMMAND_DISABLE_TRIGGER;
}
}
+ if (optind < argc)
+ led = led_by_name_or_number(argv[optind]);
+
switch (cmd) {
case LED_COMMAND_SHOW_INFO:
- for (i = 0; i < LED_TRIGGER_MAX; i++) {
- int led = led_get_trigger(i);
- printf("%d: %s", i, trigger_names[i]);
- if (led >= 0)
- printf(" (led %d)", led);
- printf("\n");
- }
+ led_triggers_show_info();
break;
case LED_COMMAND_DISABLE_TRIGGER:
- ret = led_set_trigger(trigger, NULL);
+ led = led_by_name_or_number(led_name);
+ if (!led) {
+ printf("no such led: %s\n", led_name);
+ return 1;
+ }
+
+ led_trigger_disable(led);
break;
case LED_COMMAND_SET_TRIGGER:
if (argc - optind != 1)
return COMMAND_ERROR_USAGE;
- led = led_by_name_or_number(argv[optind]);
+ led = led_by_name_or_number(argv[optind]);
if (!led) {
printf("no such led: %s\n", argv[optind]);
return 1;
}
+ trigger = trigger_by_name(trigger_name);
+ if (trigger == LED_TRIGGER_INVALID) {
+ printf("no such trigger: %s\n", trigger_name);
+ return 1;
+ }
+
ret = led_set_trigger(trigger, led);
break;
}
@@ -92,16 +95,17 @@ static int do_trigger(int argc, char *argv[])
BAREBOX_CMD_HELP_START(trigger)
BAREBOX_CMD_HELP_TEXT("Control a LED trigger. Without options assigned triggers are shown.")
+BAREBOX_CMD_HELP_TEXT("triggers are given with their name, LEDs are given with their name or number")
BAREBOX_CMD_HELP_TEXT("")
BAREBOX_CMD_HELP_TEXT("Options:")
-BAREBOX_CMD_HELP_OPT ("-t", "set a trigger (needs LED argument)")
-BAREBOX_CMD_HELP_OPT ("-d", "disable a trigger")
+BAREBOX_CMD_HELP_OPT ("-t <trigger> <led>", "set a trigger")
+BAREBOX_CMD_HELP_OPT ("-d <led>", "disable a trigger")
BAREBOX_CMD_HELP_END
BAREBOX_CMD_START(trigger)
.cmd = do_trigger,
BAREBOX_CMD_DESC("handle LED triggers")
- BAREBOX_CMD_OPTS("[-td] TRIGGER [LED]")
+ BAREBOX_CMD_OPTS("[-td] [LED]")
BAREBOX_CMD_GROUP(CMD_GRP_HWMANIP)
BAREBOX_CMD_HELP(cmd_trigger_help)
BAREBOX_CMD_END
diff --git a/commands/usbgadget.c b/commands/usbgadget.c
index 314884aee8..02c2c96b02 100644
--- a/commands/usbgadget.c
+++ b/commands/usbgadget.c
@@ -20,6 +20,7 @@
*/
#include <common.h>
#include <command.h>
+#include <environment.h>
#include <errno.h>
#include <malloc.h>
#include <getopt.h>
@@ -32,11 +33,11 @@
static int do_usbgadget(int argc, char *argv[])
{
int opt, ret;
- int acm = 1, create_serial = 0;
- char *fastboot_opts = NULL, *dfu_opts = NULL;
+ int acm = 1, create_serial = 0, fastboot_set = 0;
+ const char *fastboot_opts = NULL, *dfu_opts = NULL;
struct f_multi_opts *opts;
- while ((opt = getopt(argc, argv, "asdA:D:")) > 0) {
+ while ((opt = getopt(argc, argv, "asdA::D:")) > 0) {
switch (opt) {
case 'a':
acm = 1;
@@ -51,6 +52,7 @@ static int do_usbgadget(int argc, char *argv[])
break;
case 'A':
fastboot_opts = optarg;
+ fastboot_set = 1;
break;
case 'd':
usb_multi_unregister();
@@ -60,6 +62,9 @@ static int do_usbgadget(int argc, char *argv[])
}
}
+ if (fastboot_set && !fastboot_opts)
+ fastboot_opts = getenv("global.usbgadget.fastboot_function");
+
if (!dfu_opts && !fastboot_opts && !create_serial)
return COMMAND_ERROR_USAGE;
diff --git a/common/Makefile b/common/Makefile
index 5f58c81d22..8cd0ab3001 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -9,6 +9,7 @@ obj-pbl-y += memsize.o
obj-y += resource.o
obj-y += bootsource.o
obj-y += restart.o
+obj-y += poweroff.o
obj-$(CONFIG_AUTO_COMPLETE) += complete.o
obj-$(CONFIG_BANNER) += version.o
obj-$(CONFIG_BAREBOX_UPDATE) += bbu.o
diff --git a/common/blspec.c b/common/blspec.c
index ec63ddb407..8132d141ab 100644
--- a/common/blspec.c
+++ b/common/blspec.c
@@ -732,3 +732,27 @@ int blspec_scan_devicename(struct bootentries *bootentries, const char *devname)
return blspec_scan_device(bootentries, dev);
}
+
+static int blspec_bootentry_provider(struct bootentries *bootentries,
+ const char *name)
+{
+ int ret, found = 0;
+
+ ret = blspec_scan_devicename(bootentries, name);
+ if (ret > 0)
+ found += ret;
+
+ if (*name == '/' || !strncmp(name, "nfs://", 6)) {
+ ret = blspec_scan_directory(bootentries, name);
+ if (ret > 0)
+ found += ret;
+ }
+
+ return found;
+}
+
+static int blspec_init(void)
+{
+ return bootentry_register_provider(blspec_bootentry_provider);
+}
+device_initcall(blspec_init); \ No newline at end of file
diff --git a/common/boot.c b/common/boot.c
index 4306319331..cef3d5e514 100644
--- a/common/boot.c
+++ b/common/boot.c
@@ -244,6 +244,25 @@ static int bootscript_scan_path(struct bootentries *bootentries, const char *pat
return ret;
}
+static LIST_HEAD(bootentry_providers);
+
+struct bootentry_provider {
+ int (*fn)(struct bootentries *bootentries, const char *name);
+ struct list_head list;
+};
+
+int bootentry_register_provider(int (*fn)(struct bootentries *bootentries, const char *name))
+{
+ struct bootentry_provider *p;
+
+ p = xzalloc(sizeof(*p));
+ p->fn = fn;
+
+ list_add_tail(&p->list, &bootentry_providers);
+
+ return 0;
+}
+
/*
* bootentry_create_from_name - create boot entries from a name
*
@@ -261,22 +280,11 @@ static int bootscript_scan_path(struct bootentries *bootentries, const char *pat
int bootentry_create_from_name(struct bootentries *bootentries,
const char *name)
{
+ struct bootentry_provider *p;
int found = 0, ret;
- if (IS_ENABLED(CONFIG_BLSPEC)) {
- ret = blspec_scan_devicename(bootentries, name);
- if (ret > 0)
- found += ret;
-
- if (*name == '/' || !strncmp(name, "nfs://", 6)) {
- ret = blspec_scan_directory(bootentries, name);
- if (ret > 0)
- found += ret;
- }
- }
-
- if (IS_ENABLED(CONFIG_BOOTCHOOSER) && !strcmp(name, "bootchooser")) {
- ret = bootchooser_create_bootentry(bootentries);
+ list_for_each_entry(p, &bootentry_providers, list) {
+ ret = p->fn(bootentries, name);
if (ret > 0)
found += ret;
}
diff --git a/common/bootchooser.c b/common/bootchooser.c
index 9c110f267e..455f290fa2 100644
--- a/common/bootchooser.c
+++ b/common/bootchooser.c
@@ -827,15 +827,10 @@ out:
return ret;
}
-static int bootchooser_boot(struct bootentry *entry, int verbose, int dryrun)
+int bootchooser_boot(struct bootchooser *bc)
{
- struct bootchooser *bc = container_of(entry, struct bootchooser,
- entry);
int ret, tryagain;
- bc->verbose = verbose;
- bc->dryrun = dryrun;
-
do {
ret = bootchooser_boot_one(bc, &tryagain);
@@ -846,6 +841,16 @@ static int bootchooser_boot(struct bootentry *entry, int verbose, int dryrun)
return ret;
}
+static int bootchooser_entry_boot(struct bootentry *entry, int verbose, int dryrun)
+{
+ struct bootchooser *bc = container_of(entry, struct bootchooser,
+ entry);
+ bc->verbose = verbose;
+ bc->dryrun = dryrun;
+
+ return bootchooser_boot(bc);
+}
+
static void bootchooser_release(struct bootentry *entry)
{
struct bootchooser *bc = container_of(entry, struct bootchooser,
@@ -863,14 +868,18 @@ static void bootchooser_release(struct bootentry *entry)
*
* Return: The number of entries added to the list
*/
-int bootchooser_create_bootentry(struct bootentries *entries)
+static int bootchooser_add_entry(struct bootentries *entries, const char *name)
{
- struct bootchooser *bc = bootchooser_get();
+ struct bootchooser *bc;
+
+ if (strcmp(name, "bootchooser"))
+ return 0;
+ bc = bootchooser_get();
if (IS_ERR(bc))
return PTR_ERR(bc);
- bc->entry.boot = bootchooser_boot;
+ bc->entry.boot = bootchooser_entry_boot;
bc->entry.release = bootchooser_release;
bc->entry.title = xstrdup("bootchooser");
bc->entry.description = xstrdup("bootchooser");
@@ -904,6 +913,9 @@ static int bootchooser_init(void)
reset_attempts_names, ARRAY_SIZE(reset_attempts_names));
globalvar_add_simple_bitmask("bootchooser.reset_priorities", &reset_priorities,
reset_priorities_names, ARRAY_SIZE(reset_priorities_names));
+
+ bootentry_register_provider(bootchooser_add_entry);
+
return 0;
}
device_initcall(bootchooser_init);
diff --git a/common/globalvar.c b/common/globalvar.c
index 52808f8852..32ca6a24db 100644
--- a/common/globalvar.c
+++ b/common/globalvar.c
@@ -29,22 +29,6 @@ void nv_var_set_clean(void)
nv_dirty = 0;
}
-int globalvar_add(const char *name,
- int (*set)(struct device_d *dev, struct param_d *p, const char *val),
- const char *(*get)(struct device_d *, struct param_d *p),
- unsigned long flags)
-{
- struct param_d *param;
-
- if (!strncmp(name, "global.", 7))
- name += 7;
-
- param = dev_add_param(&global_device, name, set, get, flags);
- if (IS_ERR(param))
- return PTR_ERR(param);
- return 0;
-}
-
void globalvar_remove(const char *name)
{
struct param_d *p, *tmp;
@@ -181,14 +165,18 @@ static int nvvar_device_dispatch(const char *name, struct device_d **dev,
static int nv_set(struct device_d *dev, struct param_d *p, const char *val)
{
+ struct param_d *g;
int ret;
if (!val)
val = "";
- ret = dev_set_param(&global_device, p->name, val);
- if (ret)
- return ret;
+ g = get_param_by_name(&global_device, p->name);
+ if (g) {
+ ret = dev_set_param(&global_device, p->name, val);
+ if (ret)
+ return ret;
+ }
free(p->value);
p->value = xstrdup(val);
@@ -215,7 +203,6 @@ static int nv_param_set(struct device_d *dev, struct param_d *p, const char *val
static int __nvvar_add(const char *name, const char *value)
{
struct param_d *p;
- int ret;
if (!IS_ENABLED(CONFIG_NVVAR))
return -ENOSYS;
@@ -228,15 +215,16 @@ static int __nvvar_add(const char *name, const char *value)
return PTR_ERR(p);
}
- /* Create corresponding globalvar if it doesn't exist yet */
- ret = globalvar_add_simple(name, value);
- if (ret && ret != -EEXIST)
- return ret;
+ if (value)
+ return nv_set(&nv_device, p, value);
- if (!value)
- value = dev_get_param(&global_device, name);
+ value = dev_get_param(&global_device, name);
+ if (value) {
+ free(p->value);
+ p->value = xstrdup(value);
+ }
- return nv_set(&nv_device, p, value);
+ return 0;
}
int nvvar_add(const char *name, const char *value)
@@ -321,7 +309,10 @@ static void device_param_print(struct device_d *dev)
if (IS_ENABLED(CONFIG_NVVAR) && dev != &nv_device)
nv = dev_get_param(&nv_device, param->name);
- printf("%s%s: %s\n", nv ? "* " : " ", param->name, p);
+ printf("%s%s: %s", nv ? "* " : " ", param->name, p);
+ if (param->info)
+ param->info(param);
+ printf("\n");
}
}
@@ -400,6 +391,15 @@ static int globalvar_simple_set(struct device_d *dev, struct param_d *p, const c
return dev_param_set_generic(dev, p, val);
}
+static void globalvar_nv_sync(const char *name)
+{
+ const char *val;
+
+ val = dev_get_param(&nv_device, name);
+ if (val)
+ dev_set_param(&global_device, name, val);
+}
+
/*
* globalvar_add_simple
*
@@ -410,51 +410,23 @@ int globalvar_add_simple(const char *name, const char *value)
struct param_d *param;
param = dev_add_param(&global_device, name, globalvar_simple_set, NULL,
- PARAM_GLOBALVAR_UNQUALIFIED);
+ 0);
if (IS_ERR(param)) {
if (PTR_ERR(param) != -EEXIST)
return PTR_ERR(param);
}
- if (!value)
- return 0;
-
- return dev_set_param(&global_device, name, value);
-}
-
-static int globalvar_remove_unqualified(const char *name)
-{
- struct param_d *p;
-
- p = get_param_by_name(&global_device, name);
- if (!p)
- return 0;
+ if (value)
+ dev_set_param(&global_device, name, value);
- if (!(p->flags & PARAM_GLOBALVAR_UNQUALIFIED))
- return -EEXIST;
-
- dev_remove_param(p);
+ globalvar_nv_sync(name);
return 0;
}
-static void globalvar_nv_sync(const char *name)
-{
- const char *val;
-
- val = dev_get_param(&nv_device, name);
- if (val)
- dev_set_param(&global_device, name, val);
-}
-
int globalvar_add_simple_string(const char *name, char **value)
{
struct param_d *p;
- int ret;
-
- ret = globalvar_remove_unqualified(name);
- if (ret)
- return ret;
p = dev_add_param_string(&global_device, name, NULL, NULL,
value, NULL);
@@ -471,11 +443,6 @@ int globalvar_add_simple_int(const char *name, int *value,
const char *format)
{
struct param_d *p;
- int ret;
-
- ret = globalvar_remove_unqualified(name);
- if (ret)
- return ret;
p = dev_add_param_int(&global_device, name, NULL, NULL,
value, format, NULL);
@@ -491,11 +458,6 @@ int globalvar_add_simple_int(const char *name, int *value,
int globalvar_add_simple_bool(const char *name, int *value)
{
struct param_d *p;
- int ret;
-
- ret = globalvar_remove_unqualified(name);
- if (ret)
- return ret;
p = dev_add_param_bool(&global_device, name, NULL, NULL,
value, NULL);
@@ -512,11 +474,6 @@ int globalvar_add_simple_enum(const char *name, int *value,
const char * const *names, int max)
{
struct param_d *p;
- int ret;
-
- ret = globalvar_remove_unqualified(name);
- if (ret)
- return ret;
p = dev_add_param_enum(&global_device, name, NULL, NULL,
value, names, max, NULL);
@@ -546,11 +503,6 @@ int globalvar_add_simple_bitmask(const char *name, unsigned long *value,
int globalvar_add_simple_ip(const char *name, IPaddr_t *ip)
{
struct param_d *p;
- int ret;
-
- ret = globalvar_remove_unqualified(name);
- if (ret)
- return ret;
p = dev_add_param_ip(&global_device, name, NULL, NULL,
ip, NULL);
diff --git a/common/image-fit.c b/common/image-fit.c
index 6a01c614cc..81433e6ecf 100644
--- a/common/image-fit.c
+++ b/common/image-fit.c
@@ -254,6 +254,8 @@ static int fit_verify_signature(struct device_node *sig_node, void *fit)
}
if (strcmp(algo_name, "sha1,rsa2048") == 0) {
algo = HASH_ALGO_SHA1;
+ } else if (strcmp(algo_name, "sha256,rsa2048") == 0) {
+ algo = HASH_ALGO_SHA256;
} else if (strcmp(algo_name, "sha256,rsa4096") == 0) {
algo = HASH_ALGO_SHA256;
} else {
@@ -489,6 +491,38 @@ static int fit_config_verify_signature(struct fit_handle *handle, struct device_
return ret;
}
+static int fit_find_compatible_unit(struct device_node *conf_node,
+ const char **unit)
+{
+ struct device_node *child = NULL;
+ struct device_node *barebox_root;
+ const char *machine;
+ int ret;
+
+ barebox_root = of_get_root_node();
+ if (!barebox_root)
+ goto default_unit;
+
+ ret = of_property_read_string(barebox_root, "compatible", &machine);
+ if (ret)
+ return -ENOENT;
+
+ for_each_child_of_node(conf_node, child) {
+ if (of_device_is_compatible(child, machine)) {
+ *unit = child->name;
+ pr_info("matching unit '%s' found\n", *unit);
+ return 0;
+ }
+ }
+
+default_unit:
+ pr_info("No match found. Trying default.\n");
+ if (of_property_read_string(conf_node, "default", unit) == 0)
+ return 0;
+
+ return -ENOENT;
+}
+
static int fit_open_configuration(struct fit_handle *handle, const char *name)
{
struct device_node *conf_node = NULL;
@@ -501,8 +535,12 @@ static int fit_open_configuration(struct fit_handle *handle, const char *name)
if (name) {
unit = name;
- } else if (of_property_read_string(conf_node, "default", &unit)) {
- unit = "conf@1";
+ } else {
+ ret = fit_find_compatible_unit(conf_node, &unit);
+ if (ret) {
+ pr_info("Couldn't get a valid configuration. Aborting.\n");
+ return ret;
+ }
}
conf_node = of_get_child_by_name(conf_node, unit);
diff --git a/common/memory.c b/common/memory.c
index ad38b00ecb..ff5bdc14e2 100644
--- a/common/memory.c
+++ b/common/memory.c
@@ -185,7 +185,7 @@ static int of_memory_fixup(struct device_node *node, void *unused)
if (!memnode)
return -ENOMEM;
- err = of_set_property(memnode, "device_type", "memory", sizeof("memory"), 1);
+ err = of_property_write_string(memnode, "device_type", "memory");
if (err)
return err;
diff --git a/common/oftree.c b/common/oftree.c
index e98b908738..09a4455a9e 100644
--- a/common/oftree.c
+++ b/common/oftree.c
@@ -128,7 +128,7 @@ static int of_fixup_bootargs(struct device_node *root, void *unused)
return -ENOMEM;
- err = of_set_property(node, "bootargs", str, strlen(str) + 1, 1);
+ err = of_property_write_string(node, "bootargs", str);
return err;
}
diff --git a/common/partitions.c b/common/partitions.c
index 69a2b1fefb..574b31fbbe 100644
--- a/common/partitions.c
+++ b/common/partitions.c
@@ -63,6 +63,8 @@ static int register_one_partition(struct block_device *blk,
goto out;
}
+ cdev->flags |= DEVFS_PARTITION_FROM_TABLE;
+
cdev->dos_partition_type = part->dos_partition_type;
strcpy(cdev->partuuid, part->partuuid);
diff --git a/common/password.c b/common/password.c
index d52b746f0f..74d328f4b2 100644
--- a/common/password.c
+++ b/common/password.c
@@ -365,7 +365,11 @@ int set_env_passwd(unsigned char* passwd, size_t length)
char *salt = passwd_sum;
int keylen = PBKDF2_LENGTH - PBKDF2_SALT_LEN;
- get_random_bytes(passwd_sum, PBKDF2_SALT_LEN);
+ ret = get_crypto_bytes(passwd_sum, PBKDF2_SALT_LEN);
+ if (ret) {
+ pr_err("Can't get random numbers\n");
+ return ret;
+ }
ret = pkcs5_pbkdf2_hmac_sha1(passwd, length, salt,
PBKDF2_SALT_LEN, PBKDF2_COUNT, keylen, key);
diff --git a/common/poweroff.c b/common/poweroff.c
new file mode 100644
index 0000000000..32a78280d3
--- /dev/null
+++ b/common/poweroff.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2015 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#define pr_fmt(fmt) "poweroff: " fmt
+
+#include <common.h>
+#include <poweroff.h>
+#include <malloc.h>
+#include <of.h>
+
+static LIST_HEAD(poweroff_handler_list);
+
+/**
+ * poweroff_handler_register() - register a handler for poweroffing the system
+ * @rst: The handler struct
+ *
+ * This adds @rst to the list of registered poweroff handlers.
+ *
+ * return: 0 for success or negative error code
+ */
+int poweroff_handler_register(struct poweroff_handler *handler)
+{
+ if (!handler->name)
+ handler->name = POWEROFF_DEFAULT_NAME;
+ if (!handler->priority)
+ handler->priority = POWEROFF_DEFAULT_PRIORITY;
+
+ list_add_tail(&handler->list, &poweroff_handler_list);
+
+ pr_debug("registering poweroff handler \"%s\" with priority %d\n",
+ handler->name, handler->priority);
+
+ return 0;
+}
+
+/**
+ * poweroff_handler_register_fn() - register a handler function
+ * @poweroff_fn: The poweroff function
+ *
+ * convenience wrapper for poweroff_handler_register() to register a handler
+ * with given function and default values otherwise.
+ *
+ * return: 0 for success or negative error code
+ */
+int poweroff_handler_register_fn(void (*poweroff_fn)(struct poweroff_handler *))
+{
+ struct poweroff_handler *handler;
+ int ret;
+
+ handler = xzalloc(sizeof(*handler));
+
+ handler->poweroff = poweroff_fn;
+
+ ret = poweroff_handler_register(handler);
+
+ if (ret)
+ free(handler);
+
+ return ret;
+}
+
+/**
+ * poweroff_machine() - power off the machine
+ */
+void __noreturn poweroff_machine(void)
+{
+ struct poweroff_handler *handler = NULL, *tmp;
+ unsigned int priority = 0;
+
+ list_for_each_entry(tmp, &poweroff_handler_list, list) {
+ if (tmp->priority > priority) {
+ priority = tmp->priority;
+ handler = tmp;
+ }
+ }
+
+ if (handler) {
+ pr_debug("using poweroff handler %s\n", handler->name);
+ console_flush();
+ handler->poweroff(handler);
+ } else {
+ pr_err("No poweroff handler found!\n");
+ }
+
+ hang();
+}
+
+/**
+ * of_get_poweroff_priority() - get the desired poweroff priority from device tree
+ * @node: The device_node to read the property from
+ *
+ * return: The priority
+ */
+unsigned int of_get_poweroff_priority(struct device_node *node)
+{
+ unsigned int priority = POWEROFF_DEFAULT_PRIORITY;
+
+ of_property_read_u32(node, "poweroff-priority", &priority);
+
+ return priority;
+}
diff --git a/common/state/Makefile b/common/state/Makefile
index 3e0e2c6e55..fcf9add52c 100644
--- a/common/state/Makefile
+++ b/common/state/Makefile
@@ -1,9 +1,7 @@
obj-y += state.o
obj-y += state_variables.o
-obj-y += backend.o
obj-y += backend_format_dtb.o
obj-y += backend_format_raw.o
obj-y += backend_storage.o
obj-y += backend_bucket_direct.o
obj-$(CONFIG_MTD) += backend_bucket_circular.o
-obj-y += backend_bucket_cached.o
diff --git a/common/state/backend.c b/common/state/backend.c
deleted file mode 100644
index 5235bb0283..0000000000
--- a/common/state/backend.c
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright (C) 2016 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/string.h>
-#include <malloc.h>
-#include <printk.h>
-
-#include "state.h"
-
-
-/**
- * Save the state
- * @param state
- * @return
- */
-int state_save(struct state *state)
-{
- uint8_t *buf;
- ssize_t len;
- int ret;
- struct state_backend *backend = &state->backend;
-
- if (!state->dirty)
- return 0;
-
- ret = backend->format->pack(backend->format, state, &buf, &len);
- if (ret) {
- dev_err(&state->dev, "Failed to pack state with backend format %s, %d\n",
- backend->format->name, ret);
- return ret;
- }
-
- ret = state_storage_write(&backend->storage, buf, len);
- if (ret) {
- dev_err(&state->dev, "Failed to write packed state, %d\n", ret);
- goto out;
- }
-
- state->dirty = 0;
-
-out:
- free(buf);
- return ret;
-}
-
-/**
- * state_load - Loads a state from the backend
- * @param state The state that should be updated to contain the loaded data
- * @return 0 on success, -errno on failure. If no state is loaded the previous
- * values remain in the state.
- *
- * This function uses the registered storage backend to read data. All data that
- * we read is checked for integrity by the formatter. After that we unpack the
- * data into our state.
- */
-int state_load(struct state *state)
-{
- uint8_t *buf;
- ssize_t len;
- ssize_t len_hint = 0;
- int ret;
- struct state_backend *backend = &state->backend;
-
- if (backend->format->get_packed_len)
- len_hint = backend->format->get_packed_len(backend->format,
- state);
- ret = state_storage_read(&backend->storage, backend->format,
- state->magic, &buf, &len, len_hint);
- if (ret) {
- dev_err(&state->dev, "Failed to read state with format %s, %d\n",
- backend->format->name, ret);
- return ret;
- }
-
- ret = backend->format->unpack(backend->format, state, buf, len);
- if (ret) {
- dev_err(&state->dev, "Failed to unpack read data with format %s although verified, %d\n",
- backend->format->name, ret);
- goto out;
- }
-
- state->dirty = 0;
-
-out:
- free(buf);
- return ret;
-}
-
-static int state_format_init(struct state_backend *backend,
- struct device_d *dev, const char *backend_format,
- struct device_node *node, const char *state_name)
-{
- int ret;
-
- if (!strcmp(backend_format, "raw")) {
- ret = backend_format_raw_create(&backend->format, node,
- state_name, dev);
- } else if (!strcmp(backend_format, "dtb")) {
- ret = backend_format_dtb_create(&backend->format, dev);
- } else {
- dev_err(dev, "Invalid backend format %s\n",
- backend_format);
- return -EINVAL;
- }
-
- if (ret && ret != -EPROBE_DEFER)
- dev_err(dev, "Failed to initialize format %s, %d\n",
- backend_format, ret);
-
- return ret;
-}
-
-static void state_format_free(struct state_backend_format *format)
-{
- if (format->free)
- format->free(format);
-}
-
-/**
- * state_backend_init - Initiates the backend storage and format using the
- * passed arguments
- * @param backend state backend
- * @param dev Device pointer used for prints
- * @param node the DT device node corresponding to the state
- * @param backend_format a string describing the format. Valid values are 'raw'
- * and 'dtb' currently
- * @param storage_path Path to the backend storage file/device/partition/...
- * @param state_name Name of the state
- * @param of_path Path in the devicetree
- * @param stridesize stridesize in case we have a medium without eraseblocks.
- * stridesize describes how far apart copies of the same data should be stored.
- * For blockdevices it makes sense to align them on blocksize.
- * @param storagetype Type of the storage backend. This may be NULL where we
- * autoselect some backwardscompatible backend options
- * @return 0 on success, -errno otherwise
- */
-int state_backend_init(struct state_backend *backend, struct device_d *dev,
- struct device_node *node, const char *backend_format,
- const char *storage_path, const char *state_name, const
- char *of_path, off_t offset, size_t max_size,
- uint32_t stridesize, const char *storagetype)
-{
- int ret;
-
- ret = state_format_init(backend, dev, backend_format, node, state_name);
- if (ret)
- return ret;
-
- ret = state_storage_init(&backend->storage, dev, storage_path, offset,
- max_size, stridesize, storagetype);
- if (ret)
- goto out_free_format;
-
- backend->of_path = xstrdup(of_path);
-
- return 0;
-
-out_free_format:
- state_format_free(backend->format);
- backend->format = NULL;
-
- return ret;
-}
-
-void state_backend_set_readonly(struct state_backend *backend)
-{
- state_storage_set_readonly(&backend->storage);
-}
-
-void state_backend_free(struct state_backend *backend)
-{
- state_storage_free(&backend->storage);
- if (backend->format)
- state_format_free(backend->format);
- free(backend->of_path);
-}
diff --git a/common/state/backend_bucket_cached.c b/common/state/backend_bucket_cached.c
deleted file mode 100644
index ba0af7f373..0000000000
--- a/common/state/backend_bucket_cached.c
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright (C) 2016 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <common.h>
-#include "state.h"
-
-struct state_backend_storage_bucket_cache {
- struct state_backend_storage_bucket bucket;
-
- struct state_backend_storage_bucket *raw;
-
- u8 *data;
- ssize_t data_len;
- bool force_write;
-
- /* For outputs */
- struct device_d *dev;
-};
-
-static inline struct state_backend_storage_bucket_cache
- *get_bucket_cache(struct state_backend_storage_bucket *bucket)
-{
- return container_of(bucket,
- struct state_backend_storage_bucket_cache,
- bucket);
-}
-
-static inline void state_backend_bucket_cache_drop(
- struct state_backend_storage_bucket_cache *cache)
-{
- if (cache->data) {
- free(cache->data);
- cache->data = NULL;
- cache->data_len = 0;
- }
-}
-
-static int state_backend_bucket_cache_fill(
- struct state_backend_storage_bucket_cache *cache)
-{
- int ret;
-
- ret = cache->raw->read(cache->raw, &cache->data, &cache->data_len);
- if (ret == -EUCLEAN) {
- cache->force_write = true;
- ret = 0;
- }
-
- return ret;
-}
-
-static int state_backend_bucket_cache_read(struct state_backend_storage_bucket *bucket,
- uint8_t ** buf_out,
- ssize_t * len_hint)
-{
- struct state_backend_storage_bucket_cache *cache =
- get_bucket_cache(bucket);
- int ret;
-
- if (!cache->data) {
- ret = state_backend_bucket_cache_fill(cache);
- if (ret)
- return ret;
- }
-
- if (cache->data) {
- *buf_out = xmemdup(cache->data, cache->data_len);
- if (!*buf_out)
- return -ENOMEM;
- *len_hint = cache->data_len;
- }
-
- return 0;
-}
-
-static int state_backend_bucket_cache_write(struct state_backend_storage_bucket *bucket,
- const uint8_t * buf, ssize_t len)
-{
- struct state_backend_storage_bucket_cache *cache =
- get_bucket_cache(bucket);
- int ret;
-
- if (!cache->force_write) {
- if (!cache->data)
- ret = state_backend_bucket_cache_fill(cache);
-
- if (cache->data_len == len && !memcmp(cache->data, buf, len))
- return 0;
- }
-
- state_backend_bucket_cache_drop(cache);
-
- ret = cache->raw->write(cache->raw, buf, len);
- if (ret)
- return ret;
-
- cache->data = xmemdup(buf, len);
- cache->data_len = len;
- return 0;
-}
-
-static int state_backend_bucket_cache_init(
- struct state_backend_storage_bucket *bucket)
-{
- struct state_backend_storage_bucket_cache *cache =
- get_bucket_cache(bucket);
-
- if (cache->raw->init) {
- return cache->raw->init(cache->raw);
- }
-
- return 0;
-}
-
-static void state_backend_bucket_cache_free(
- struct state_backend_storage_bucket *bucket)
-{
- struct state_backend_storage_bucket_cache *cache =
- get_bucket_cache(bucket);
-
- state_backend_bucket_cache_drop(cache);
- cache->raw->free(cache->raw);
- free(cache);
-}
-
-int state_backend_bucket_cached_create(struct device_d *dev,
- struct state_backend_storage_bucket *raw,
- struct state_backend_storage_bucket **out)
-{
- struct state_backend_storage_bucket_cache *cache;
-
- cache = xzalloc(sizeof(*cache));
- cache->raw = raw;
- cache->dev = dev;
-
- cache->bucket.free = state_backend_bucket_cache_free;
- cache->bucket.read = state_backend_bucket_cache_read;
- cache->bucket.write = state_backend_bucket_cache_write;
- cache->bucket.init = state_backend_bucket_cache_init;
-
- *out = &cache->bucket;
-
- return 0;
-}
diff --git a/common/state/backend_bucket_circular.c b/common/state/backend_bucket_circular.c
index 0bce900d0d..5279ec9ce2 100644
--- a/common/state/backend_bucket_circular.c
+++ b/common/state/backend_bucket_circular.c
@@ -25,7 +25,21 @@
#include "state.h"
-
+/*
+ * The circular backend bucket code. The circular backend bucket is intended
+ * for mtd devices which need an erase operation.
+ *
+ * Erasing blocks is an operation that should be avoided. On NOR flashes erasing
+ * blocks is very time consuming and on NAND flashes each block only has a limited
+ * number of erase cycles allowed. For this reason we continuously write more data
+ * into each eraseblock and only erase it when no more free space is available.
+ * Don't confuse these multiple writes into a single eraseblock with buckets. A bucket
+ * is the whole eraseblock, we just happen to reuse the same bucket for storing
+ * new data.
+ *
+ * If your device is a mtd device, but does not have eraseblocks, like MRAMs, then
+ * the direct bucket is used instead.
+ */
struct state_backend_storage_bucket_circular {
struct state_backend_storage_bucket bucket;
@@ -47,6 +61,11 @@ struct state_backend_storage_bucket_circular {
struct device_d *dev;
};
+/*
+ * The metadata will be written directly before writesize aligned offsets.
+ * When searching backwards through the pages it allows us to find the
+ * beginning of the data.
+ */
struct __attribute__((__packed__)) state_backend_storage_bucket_circular_meta {
uint32_t magic;
uint32_t written_length;
@@ -65,7 +84,7 @@ static inline struct state_backend_storage_bucket_circular
#ifdef __BAREBOX__
static int state_mtd_peb_read(struct state_backend_storage_bucket_circular *circ,
- char *buf, int offset, int len)
+ void *buf, int offset, int len)
{
int ret;
@@ -102,7 +121,7 @@ static int state_mtd_peb_read(struct state_backend_storage_bucket_circular *circ
}
static int state_mtd_peb_write(struct state_backend_storage_bucket_circular *circ,
- const char *buf, int offset, int len)
+ const void *buf, int offset, int len)
{
int ret;
@@ -133,7 +152,7 @@ static int state_mtd_peb_erase(struct state_backend_storage_bucket_circular *cir
}
#else
static int state_mtd_peb_read(struct state_backend_storage_bucket_circular *circ,
- char *buf, int suboffset, int len)
+ void *buf, int suboffset, int len)
{
int ret;
off_t offset = suboffset;
@@ -152,40 +171,19 @@ static int state_mtd_peb_read(struct state_backend_storage_bucket_circular *circ
dev_dbg(circ->dev, "Read state from %ld length %zd\n", offset,
len);
- ret = ioctl(circ->fd, ECCGETSTATS, &stat1);
- if (ret)
- nostats = true;
ret = read_full(circ->fd, buf, len);
if (ret < 0) {
dev_err(circ->dev, "Failed to read circular storage len %zd, %d\n",
len, ret);
free(buf);
- return ret;
- }
-
- if (nostats)
- return 0;
-
- ret = ioctl(circ->fd, ECCGETSTATS, &stat2);
- if (ret)
- return 0;
-
- if (stat2.failed - stat1.failed > 0) {
- ret = -EUCLEAN;
- dev_dbg(circ->dev, "PEB %u has ECC error, forcing rewrite\n",
- circ->eraseblock);
- } else if (stat2.corrected - stat1.corrected > 0) {
- ret = -EUCLEAN;
- dev_dbg(circ->dev, "PEB %u is unclean, forcing rewrite\n",
- circ->eraseblock);
}
return ret;
}
static int state_mtd_peb_write(struct state_backend_storage_bucket_circular *circ,
- const char *buf, int suboffset, int len)
+ const void *buf, int suboffset, int len)
{
int ret;
off_t offset = suboffset;
@@ -226,14 +224,14 @@ static int state_mtd_peb_erase(struct state_backend_storage_bucket_circular *cir
#endif
static int state_backend_bucket_circular_read(struct state_backend_storage_bucket *bucket,
- uint8_t ** buf_out,
- ssize_t * len_hint)
+ void ** buf_out,
+ ssize_t * len_out)
{
struct state_backend_storage_bucket_circular *circ =
get_bucket_circular(bucket);
ssize_t read_len;
off_t offset;
- uint8_t *buf;
+ void *buf;
int ret;
/* Storage is empty */
@@ -282,13 +280,13 @@ static int state_backend_bucket_circular_read(struct state_backend_storage_bucke
}
*buf_out = buf;
- *len_hint = read_len - sizeof(struct state_backend_storage_bucket_circular_meta);
+ *len_out = read_len - sizeof(struct state_backend_storage_bucket_circular_meta);
return ret;
}
static int state_backend_bucket_circular_write(struct state_backend_storage_bucket *bucket,
- const uint8_t * buf,
+ const void * buf,
ssize_t len)
{
struct state_backend_storage_bucket_circular *circ =
@@ -297,7 +295,7 @@ static int state_backend_bucket_circular_write(struct state_backend_storage_buck
struct state_backend_storage_bucket_circular_meta *meta;
uint32_t written_length = ALIGN(len + sizeof(*meta), circ->writesize);
int ret;
- uint8_t *write_buf;
+ void *write_buf;
if (written_length > circ->max_size) {
dev_err(circ->dev, "Error, state data too big to be written, to write: %zd, writesize: %zd, length: %zd, available: %zd\n",
@@ -379,26 +377,24 @@ static int state_backend_bucket_circular_init(
int sub_offset;
uint32_t written_length = 0;
uint8_t *buf;
+ int ret;
- buf = xmalloc(circ->writesize);
+ buf = xmalloc(circ->max_size);
if (!buf)
return -ENOMEM;
+ ret = state_mtd_peb_read(circ, buf, 0, circ->max_size);
+ if (ret && ret != -EUCLEAN)
+ return ret;
+
for (sub_offset = circ->max_size - circ->writesize; sub_offset >= 0;
sub_offset -= circ->writesize) {
- int ret;
-
- ret = state_mtd_peb_read(circ, buf, sub_offset,
- circ->writesize);
- if (ret && ret != -EUCLEAN)
- return ret;
-
- ret = mtd_buf_all_ff(buf, circ->writesize);
+ ret = mtd_buf_all_ff(buf + sub_offset, circ->writesize);
if (!ret) {
struct state_backend_storage_bucket_circular_meta *meta;
meta = (struct state_backend_storage_bucket_circular_meta *)
- (buf + circ->writesize - sizeof(*meta));
+ (buf + sub_offset + circ->writesize - sizeof(*meta));
if (meta->magic != circular_magic)
written_length = 0;
@@ -458,12 +454,14 @@ int state_backend_bucket_circular_create(struct device_d *dev, const char *path,
struct state_backend_storage_bucket **bucket,
unsigned int eraseblock,
ssize_t writesize,
- struct mtd_info_user *mtd_uinfo,
- bool lazy_init)
+ struct mtd_info_user *mtd_uinfo)
{
struct state_backend_storage_bucket_circular *circ;
int ret;
+ if (writesize < 8)
+ writesize = 8;
+
circ = xzalloc(sizeof(*circ));
circ->eraseblock = eraseblock;
circ->writesize = writesize;
@@ -495,13 +493,9 @@ int state_backend_bucket_circular_create(struct device_d *dev, const char *path,
circ->bucket.free = state_backend_bucket_circular_free;
*bucket = &circ->bucket;
- if (!lazy_init) {
- ret = state_backend_bucket_circular_init(*bucket);
- if (ret)
- goto out_free;
- } else {
- circ->bucket.init = state_backend_bucket_circular_init;
- }
+ ret = state_backend_bucket_circular_init(*bucket);
+ if (ret)
+ goto out_free;
return 0;
diff --git a/common/state/backend_bucket_direct.c b/common/state/backend_bucket_direct.c
index 5225433ec5..4465ed0e41 100644
--- a/common/state/backend_bucket_direct.c
+++ b/common/state/backend_bucket_direct.c
@@ -46,14 +46,14 @@ static inline struct state_backend_storage_bucket_direct
}
static int state_backend_bucket_direct_read(struct state_backend_storage_bucket
- *bucket, uint8_t ** buf_out,
- ssize_t * len_hint)
+ *bucket, void ** buf_out,
+ ssize_t * len_out)
{
struct state_backend_storage_bucket_direct *direct =
get_bucket_direct(bucket);
struct state_backend_storage_bucket_direct_meta meta;
ssize_t read_len;
- uint8_t *buf;
+ void *buf;
int ret;
ret = lseek(direct->fd, direct->offset, SEEK_SET);
@@ -69,18 +69,13 @@ static int state_backend_bucket_direct_read(struct state_backend_storage_bucket
if (meta.magic == direct_magic) {
read_len = meta.written_length;
} else {
- if (*len_hint)
- read_len = *len_hint;
- else
- read_len = direct->max_size;
+ read_len = direct->max_size;
ret = lseek(direct->fd, direct->offset, SEEK_SET);
if (ret < 0) {
dev_err(direct->dev, "Failed to seek file, %d\n", ret);
return ret;
}
}
- if (direct->max_size)
- read_len = min(read_len, direct->max_size);
buf = xmalloc(read_len);
if (!buf)
@@ -94,13 +89,13 @@ static int state_backend_bucket_direct_read(struct state_backend_storage_bucket
}
*buf_out = buf;
- *len_hint = read_len;
+ *len_out = read_len;
return 0;
}
static int state_backend_bucket_direct_write(struct state_backend_storage_bucket
- *bucket, const uint8_t * buf,
+ *bucket, const void * buf,
ssize_t len)
{
struct state_backend_storage_bucket_direct *direct =
@@ -108,7 +103,7 @@ static int state_backend_bucket_direct_write(struct state_backend_storage_bucket
int ret;
struct state_backend_storage_bucket_direct_meta meta;
- if (direct->max_size && len > direct->max_size)
+ if (len > direct->max_size - sizeof(meta))
return -E2BIG;
ret = lseek(direct->fd, direct->offset, SEEK_SET);
@@ -161,7 +156,6 @@ int state_backend_bucket_direct_create(struct device_d *dev, const char *path,
fd = open(path, O_RDWR);
if (fd < 0) {
dev_err(dev, "Failed to open file '%s', %d\n", path, -errno);
- close(fd);
return -errno;
}
diff --git a/common/state/backend_format_dtb.c b/common/state/backend_format_dtb.c
index dc19c888e5..55fa1fc597 100644
--- a/common/state/backend_format_dtb.c
+++ b/common/state/backend_format_dtb.c
@@ -39,13 +39,14 @@ static inline struct state_backend_format_dtb *get_format_dtb(struct
}
static int state_backend_format_dtb_verify(struct state_backend_format *format,
- uint32_t magic, const uint8_t * buf,
- ssize_t len)
+ uint32_t magic, const void * buf,
+ ssize_t *lenp, enum state_flags flags)
{
struct state_backend_format_dtb *fdtb = get_format_dtb(format);
struct device_node *root;
struct fdt_header *fdt = (struct fdt_header *)buf;
size_t dtb_len = fdt32_to_cpu(fdt->totalsize);
+ size_t len = *lenp;
if (dtb_len > len) {
dev_err(fdtb->dev, "Error, stored DTB length (%d) longer than read buffer (%d)\n",
@@ -67,18 +68,20 @@ static int state_backend_format_dtb_verify(struct state_backend_format *format,
fdtb->root = root;
+ *lenp = be32_to_cpu(fdt->totalsize);
+
return 0;
}
static int state_backend_format_dtb_unpack(struct state_backend_format *format,
struct state *state,
- const uint8_t * buf, ssize_t len)
+ const void * buf, ssize_t len)
{
struct state_backend_format_dtb *fdtb = get_format_dtb(format);
int ret;
if (!fdtb->root) {
- state_backend_format_dtb_verify(format, 0, buf, len);
+ state_backend_format_dtb_verify(format, 0, buf, &len, 0);
}
ret = state_from_node(state, fdtb->root, 0);
@@ -89,7 +92,7 @@ static int state_backend_format_dtb_unpack(struct state_backend_format *format,
}
static int state_backend_format_dtb_pack(struct state_backend_format *format,
- struct state *state, uint8_t ** buf,
+ struct state *state, void ** buf,
ssize_t * len)
{
struct state_backend_format_dtb *fdtb = get_format_dtb(format);
diff --git a/common/state/backend_format_raw.c b/common/state/backend_format_raw.c
index e028ea616c..232856a209 100644
--- a/common/state/backend_format_raw.c
+++ b/common/state/backend_format_raw.c
@@ -35,6 +35,10 @@ struct state_backend_format_raw {
/* For outputs */
struct device_d *dev;
+
+ char *secret_name;
+ int needs_secret;
+ char *algo;
};
struct __attribute__((__packed__)) backend_raw_header {
@@ -53,15 +57,53 @@ static inline struct state_backend_format_raw *get_format_raw(
return container_of(format, struct state_backend_format_raw, format);
}
+static int backend_raw_digest_init(struct state_backend_format_raw *raw)
+{
+ const unsigned char *key;
+ int key_len;
+ int ret;
+
+ if (!raw->digest) {
+ raw->digest = digest_alloc(raw->algo);
+ if (!raw->digest) {
+ dev_err(raw->dev, "algo %s not found\n",
+ raw->algo);
+ return -ENODEV;
+ }
+ raw->digest_length = digest_length(raw->digest);
+ }
+
+ ret = keystore_get_secret(raw->secret_name, &key, &key_len);
+ if (ret) {
+ dev_err(raw->dev, "Could not get secret '%s'\n",
+ raw->secret_name);
+ return ret;
+ }
+
+ ret = digest_set_key(raw->digest, key, key_len);
+ if (ret)
+ return ret;
+
+ ret = digest_init(raw->digest);
+ if (ret) {
+ dev_err(raw->dev, "Failed to initialize digest: %s\n",
+ strerror(-ret));
+ return ret;
+ }
+
+ return 0;
+}
+
static int backend_format_raw_verify(struct state_backend_format *format,
- uint32_t magic, const uint8_t * buf,
- ssize_t len)
+ uint32_t magic, const void * buf,
+ ssize_t *lenp, enum state_flags flags)
{
uint32_t crc;
struct backend_raw_header *header;
int d_len = 0;
int ret;
- const uint8_t *data;
+ const void *data;
+ ssize_t len = *lenp;
struct state_backend_format_raw *backend_raw = get_format_raw(format);
ssize_t complete_len;
@@ -85,7 +127,11 @@ static int backend_format_raw_verify(struct state_backend_format *format,
return -EINVAL;
}
- if (backend_raw->digest) {
+ if (backend_raw->algo && !(flags & STATE_FLAG_NO_AUTHENTIFICATION)) {
+ ret = backend_raw_digest_init(backend_raw);
+ if (ret)
+ return ret;
+
d_len = digest_length(backend_raw->digest);
}
@@ -105,26 +151,20 @@ static int backend_format_raw_verify(struct state_backend_format *format,
return -EINVAL;
}
- if (backend_raw->digest) {
- struct digest *d = backend_raw->digest;
- const void *hmac = data + header->data_len;
+ *lenp = header->data_len + sizeof(*header);
- ret = digest_init(d);
- if (ret) {
- dev_err(backend_raw->dev, "Failed to initialize digest, %d\n",
- ret);
- return ret;
- }
+ if (backend_raw->algo && !(flags & STATE_FLAG_NO_AUTHENTIFICATION)) {
+ const void *hmac = data + header->data_len;
/* hmac over header and data */
- ret = digest_update(d, buf, sizeof(*header) + header->data_len);
+ ret = digest_update(backend_raw->digest, buf, sizeof(*header) + header->data_len);
if (ret) {
dev_err(backend_raw->dev, "Failed to update digest, %d\n",
ret);
return ret;
}
- ret = digest_verify(d, hmac);
+ ret = digest_verify(backend_raw->digest, hmac);
if (ret < 0) {
dev_err(backend_raw->dev, "Failed to verify data, hmac, %d\n",
ret);
@@ -136,12 +176,12 @@ static int backend_format_raw_verify(struct state_backend_format *format,
}
static int backend_format_raw_unpack(struct state_backend_format *format,
- struct state *state, const uint8_t * buf,
+ struct state *state, const void * buf,
ssize_t len)
{
struct state_variable *sv;
const struct backend_raw_header *header;
- const uint8_t *data;
+ const void *data;
struct state_backend_format_raw *backend_raw = get_format_raw(format);
header = (const struct backend_raw_header *)buf;
@@ -160,7 +200,7 @@ static int backend_format_raw_unpack(struct state_backend_format *format,
}
static int backend_format_raw_pack(struct state_backend_format *format,
- struct state *state, uint8_t ** buf_out,
+ struct state *state, void ** buf_out,
ssize_t * len_out)
{
struct state_backend_format_raw *backend_raw = get_format_raw(format);
@@ -192,25 +232,20 @@ static int backend_format_raw_pack(struct state_backend_format *format,
header->header_crc = crc32(0, header,
sizeof(*header) - sizeof(uint32_t));
- if (backend_raw->digest) {
- struct digest *d = backend_raw->digest;
-
- ret = digest_init(d);
- if (ret) {
- dev_err(backend_raw->dev, "Failed to initialize digest for packing, %d\n",
- ret);
- goto out_free;
- }
+ if (backend_raw->algo) {
+ ret = backend_raw_digest_init(backend_raw);
+ if (ret)
+ return ret;
/* hmac over header and data */
- ret = digest_update(d, buf, sizeof(*header) + size_data);
+ ret = digest_update(backend_raw->digest, buf, sizeof(*header) + size_data);
if (ret) {
dev_err(backend_raw->dev, "Failed to update digest for packing, %d\n",
ret);
goto out_free;
}
- ret = digest_final(d, hmac);
+ ret = digest_final(backend_raw->digest, hmac);
if (ret < 0) {
dev_err(backend_raw->dev, "Failed to finish digest for packing, %d\n",
ret);
@@ -240,11 +275,9 @@ static int backend_format_raw_init_digest(struct state_backend_format_raw *raw,
struct device_node *root,
const char *secret_name)
{
- struct digest *digest;
struct property *p;
const char *algo;
- const unsigned char *key;
- int key_len, ret;
+ int ret;
p = of_find_property(root, "algo", NULL);
if (!p) /* does not exist */
@@ -260,30 +293,7 @@ static int backend_format_raw_init_digest(struct state_backend_format_raw *raw,
return -EINVAL;
}
- ret = keystore_get_secret(secret_name, &key, &key_len);
- if (ret == -ENOENT) { /* -ENOENT == does not exist */
- dev_info(raw->dev, "Could not get secret '%s' - probe deferred\n",
- secret_name);
- return -EPROBE_DEFER;
- } else if (ret) {
- return ret;
- }
-
- digest = digest_alloc(algo);
- if (!digest) {
- dev_info(raw->dev, "algo %s not found - probe deferred\n",
- algo);
- return -EPROBE_DEFER;
- }
-
- ret = digest_set_key(digest, key, key_len);
- if (ret) {
- digest_free(digest);
- return ret;
- }
-
- raw->digest = digest;
- raw->digest_length = digest_length(digest);
+ raw->algo = xstrdup(algo);
return 0;
}
@@ -301,15 +311,14 @@ int backend_format_raw_create(struct state_backend_format **format,
raw->dev = dev;
ret = backend_format_raw_init_digest(raw, node, secret_name);
- if (ret == -EPROBE_DEFER) {
- return ret;
- } else if (ret) {
+ if (ret) {
dev_err(raw->dev, "Failed initializing digest for raw format, %d\n",
ret);
free(raw);
return ret;
}
+ raw->secret_name = xstrdup(secret_name);
raw->format.pack = backend_format_raw_pack;
raw->format.unpack = backend_format_raw_unpack;
raw->format.verify = backend_format_raw_verify;
diff --git a/common/state/backend_storage.c b/common/state/backend_storage.c
index 5dc8c50267..9ed6ad79ac 100644
--- a/common/state/backend_storage.c
+++ b/common/state/backend_storage.c
@@ -25,24 +25,29 @@
#include "state.h"
-const unsigned int min_copies_written = 1;
-
-static int bucket_lazy_init(struct state_backend_storage_bucket *bucket)
-{
- int ret;
-
- if (bucket->initialized)
- return 0;
-
- if (bucket->init) {
- ret = bucket->init(bucket);
- if (ret)
- return ret;
- }
- bucket->initialized = true;
+/*
+ * The state framework stores data in so called buckets. A bucket is
+ * exactly one copy of the state we want to store. On flash type media
+ * a bucket corresponds to a single eraseblock. On media which do not
+ * need an erase operation a bucket corresponds to a storage area of
+ * @stridesize bytes.
+ *
+ * For redundancy and to make sure that we have valid data on the storage
+ * device at any time the state framework stores multiple buckets. The strategy
+ * is as follows:
+ *
+ * When loading the state from the storage we iterate over the buckets. We
+ * take the first one we find which has valid crcs. The next step is to
+ * restore consistency between the different buckets. This means rewriting
+ * a bucket when it signalled it needs refresh (i.e. returned -EUCLEAN)
+ * or when contains data different from the bucket we use.
+ *
+ * When the state backend initialized successfully we already restored
+ * consistency which means all buckets contain the same data. This means
+ * when storing a new state we can just write all buckets in order.
+ */
- return 0;
-}
+static const unsigned int min_buckets_written = 1;
/**
* state_storage_write - Writes the given data to the storage
@@ -55,60 +60,64 @@ static int bucket_lazy_init(struct state_backend_storage_bucket *bucket)
* operation on all of them. Writes are always in the same sequence. This
* ensures, that reading in the same sequence will always return the latest
* written valid data first.
- * We try to at least write min_copies_written. If this fails we return with an
+ * We try to at least write min_buckets_written. If this fails we return with an
* error.
*/
int state_storage_write(struct state_backend_storage *storage,
- const uint8_t * buf, ssize_t len)
+ const void * buf, ssize_t len)
{
struct state_backend_storage_bucket *bucket;
int ret;
- int copies_written = 0;
+ int buckets_written = 0;
if (storage->readonly)
return 0;
list_for_each_entry(bucket, &storage->buckets, bucket_list) {
- ret = bucket_lazy_init(bucket);
- if (ret) {
- dev_warn(storage->dev, "Failed to init bucket/write state backend bucket, %d\n",
- ret);
- continue;
- }
-
ret = bucket->write(bucket, buf, len);
if (ret) {
dev_warn(storage->dev, "Failed to write state backend bucket, %d\n",
ret);
} else {
- ++copies_written;
+ ++buckets_written;
}
}
- if (copies_written >= min_copies_written)
+ if (buckets_written >= min_buckets_written)
return 0;
dev_err(storage->dev, "Failed to write state to at least %d buckets. Successfully written to %d buckets\n",
- min_copies_written, copies_written);
+ min_buckets_written, buckets_written);
return -EIO;
}
-/**
- * state_storage_restore_consistency - Restore consistency on all storage backends
- * @param storage Storage object
- * @param buf Buffer with valid data that should be on all buckets after this operation
- * @param len Length of the buffer
- * @return 0 on success, -errno otherwise
- *
- * This function brings valid data onto all buckets we have to ensure that all
- * data copies are in sync. In the current implementation we just write the data
- * to all buckets. Bucket implementations that need to keep the number of writes
- * low, can read their own copy first and compare it.
- */
-int state_storage_restore_consistency(struct state_backend_storage *storage,
- const uint8_t * buf, ssize_t len)
+static int bucket_refresh(struct state_backend_storage *storage,
+ struct state_backend_storage_bucket *bucket, void *buf, ssize_t len)
{
- return state_storage_write(storage, buf, len);
+ int ret;
+
+ if (bucket->needs_refresh)
+ goto refresh;
+
+ if (bucket->len != len)
+ goto refresh;
+
+ if (memcmp(bucket->buf, buf, len))
+ goto refresh;
+
+ return 0;
+
+refresh:
+ ret = bucket->write(bucket, buf, len);
+
+ if (ret)
+ dev_warn(storage->dev, "Failed to restore bucket %d@0x%08lx\n",
+ bucket->num, bucket->offset);
+ else
+ dev_info(storage->dev, "restored bucket %d@0x%08lx\n",
+ bucket->num, bucket->offset);
+
+ return ret;
}
/**
@@ -118,7 +127,7 @@ int state_storage_restore_consistency(struct state_backend_storage *storage,
* @param magic state magic value
* @param buf The newly allocated data area will be stored in this pointer
* @param len The resulting length of the buffer
- * @param len_hint Hint of how big the data may be.
+ * @param flags flags controlling how to load state
* @return 0 on success, -errno otherwise. buf and len will be set to valid
* values on success.
*
@@ -129,43 +138,65 @@ int state_storage_restore_consistency(struct state_backend_storage *storage,
*/
int state_storage_read(struct state_backend_storage *storage,
struct state_backend_format *format,
- uint32_t magic, uint8_t ** buf, ssize_t * len,
- ssize_t len_hint)
+ uint32_t magic, void **buf, ssize_t *len,
+ enum state_flags flags)
{
- struct state_backend_storage_bucket *bucket;
+ struct state_backend_storage_bucket *bucket, *bucket_used = NULL;
int ret;
+ /*
+ * Iterate over all buckets. The first valid one we find is the
+ * one we want to use.
+ */
list_for_each_entry(bucket, &storage->buckets, bucket_list) {
- *len = len_hint;
- ret = bucket_lazy_init(bucket);
- if (ret) {
- dev_warn(storage->dev, "Failed to init bucket/read state backend bucket, %d\n",
- ret);
+ ret = bucket->read(bucket, &bucket->buf, &bucket->len);
+ if (ret == -EUCLEAN)
+ bucket->needs_refresh = 1;
+ else if (ret)
continue;
- }
- ret = bucket->read(bucket, buf, len);
- if (ret) {
- dev_warn(storage->dev, "Failed to read from state backend bucket, trying next, %d\n",
- ret);
+ /*
+ * Verify the buffer crcs. The buffer length is passed in the len argument,
+ * .verify overwrites it with the length actually used.
+ */
+ ret = format->verify(format, magic, bucket->buf, &bucket->len, flags);
+ if (!ret && !bucket_used)
+ bucket_used = bucket;
+ }
+
+ if (!bucket_used) {
+ dev_err(storage->dev, "Failed to find any valid state copy in any bucket\n");
+
+ return -ENOENT;
+ }
+
+ dev_info(storage->dev, "Using bucket %d@0x%08lx\n", bucket_used->num, bucket_used->offset);
+
+ /*
+ * Restore/refresh all buckets except the one we currently use (in case
+ * it's the only usable bucket at the moment)
+ */
+ list_for_each_entry(bucket, &storage->buckets, bucket_list) {
+ if (bucket == bucket_used)
continue;
- }
- ret = format->verify(format, magic, *buf, *len);
- if (!ret) {
- goto found;
- }
- free(*buf);
- dev_warn(storage->dev, "Failed to verify read copy, trying next bucket, %d\n",
- ret);
+
+ ret = bucket_refresh(storage, bucket, bucket_used->buf, bucket_used->len);
+
+ /* Free buffer from the unused buckets */
+ free(bucket->buf);
+ bucket->buf = NULL;
}
- dev_err(storage->dev, "Failed to find any valid state copy in any bucket\n");
+ /*
+ * Restore/refresh the bucket we currently use
+ */
+ ret = bucket_refresh(storage, bucket_used, bucket_used->buf, bucket_used->len);
- return -ENOENT;
+ *buf = bucket_used->buf;
+ *len = bucket_used->len;
-found:
- /* A failed restore consistency is not a failure of reading the state */
- state_storage_restore_consistency(storage, *buf, *len);
+ /* buffer from the used bucket is passed to the caller, do not free */
+ bucket_used->buf = NULL;
return 0;
}
@@ -187,267 +218,127 @@ static int mtd_get_meminfo(const char *path, struct mtd_info_user *meminfo)
return ret;
}
-#ifdef __BAREBOX__
-#define STAT_GIVES_SIZE(s) (S_ISREG(s.st_mode) || S_ISCHR(s.st_mode))
-#define BLKGET_GIVES_SIZE(s) 0
-#else
-#define STAT_GIVES_SIZE(s) (S_ISREG(s.st_mode))
-#define BLKGET_GIVES_SIZE(s) (S_ISBLK(s.st_mode))
-#endif
-#ifndef BLKGETSIZE64
-#define BLKGETSIZE64 -1
-#endif
-
-static int state_backend_storage_get_size(const char *path, size_t * out_size)
-{
- struct mtd_info_user meminfo;
- struct stat s;
- int ret;
-
- ret = stat(path, &s);
- if (ret)
- return -errno;
-
- /*
- * under Linux, stat() gives the size only on regular files
- * under barebox, it works on char dev, too
- */
- if (STAT_GIVES_SIZE(s)) {
- *out_size = s.st_size;
- return 0;
- }
-
- /* this works under Linux on block devs */
- if (BLKGET_GIVES_SIZE(s)) {
- int fd;
-
- fd = open(path, O_RDONLY);
- if (fd < 0)
- return -errno;
-
- ret = ioctl(fd, BLKGETSIZE64, out_size);
- close(fd);
- if (!ret)
- return 0;
- }
-
- /* try mtd next */
- ret = mtd_get_meminfo(path, &meminfo);
- if (!ret) {
- *out_size = meminfo.size;
- return 0;
- }
-
- return ret;
-}
-
-/* Number of copies that should be allocated */
-const int desired_copies = 3;
+/* Number of buckets that should be used */
+static const int desired_buckets = 3;
/**
* state_storage_mtd_buckets_init - Creates storage buckets for mtd devices
* @param storage Storage object
* @param meminfo Info about the mtd device
- * @param path Path to the device
- * @param non_circular Use non-circular mode to write data that is compatible with the old on-flash format
- * @param dev_offset Offset to start at in the device.
- * @param max_size Maximum size to use for data. May be 0 for infinite.
+ * @param circular If false, use non-circular mode to write data that is compatible with the old on-flash format
* @return 0 on success, -errno otherwise
*
- * Starting from offset 0 this function tries to create circular buckets on
- * different offsets in the device. Different copies of the data are located in
- * different eraseblocks.
- * For MTD devices we use circular buckets to minimize the number of erases.
- * Circular buckets write new data always in the next free space.
+ * This function iterates over the eraseblocks and creates one bucket on
+ * each eraseblock until we have the number of desired buckets. Bad blocks
+ * will be skipped and the next block will be used.
*/
static int state_storage_mtd_buckets_init(struct state_backend_storage *storage,
- struct mtd_info_user *meminfo,
- const char *path, bool non_circular,
- off_t dev_offset, size_t max_size)
+ struct mtd_info_user *meminfo, bool circular)
{
struct state_backend_storage_bucket *bucket;
- ssize_t end = dev_offset + max_size;
- int nr_copies = 0;
+ ssize_t end = storage->offset + storage->max_size;
+ int n_buckets = 0;
off_t offset;
+ ssize_t writesize;
if (!end || end > meminfo->size)
end = meminfo->size;
- if (!IS_ALIGNED(dev_offset, meminfo->erasesize)) {
+ if (!IS_ALIGNED(storage->offset, meminfo->erasesize)) {
dev_err(storage->dev, "Offset within the device is not aligned to eraseblocks. Offset is %ld, erasesize %zu\n",
- dev_offset, meminfo->erasesize);
+ storage->offset, meminfo->erasesize);
return -EINVAL;
}
- for (offset = dev_offset; offset < end; offset += meminfo->erasesize) {
+ if (circular)
+ writesize = meminfo->writesize;
+ else
+ writesize = meminfo->erasesize;
+
+ for (offset = storage->offset; offset < end; offset += meminfo->erasesize) {
int ret;
- ssize_t writesize = meminfo->writesize;
unsigned int eraseblock = offset / meminfo->erasesize;
- bool lazy_init = true;
-
- if (non_circular)
- writesize = meminfo->erasesize;
- ret = state_backend_bucket_circular_create(storage->dev, path,
+ ret = state_backend_bucket_circular_create(storage->dev, storage->path,
&bucket,
eraseblock,
writesize,
- meminfo,
- lazy_init);
- if (ret) {
- dev_warn(storage->dev, "Failed to create bucket at '%s' eraseblock %u\n",
- path, eraseblock);
+ meminfo);
+ if (ret)
continue;
- }
- ret = state_backend_bucket_cached_create(storage->dev, bucket,
- &bucket);
- if (ret) {
- dev_warn(storage->dev, "Failed to setup cache bucket, continuing without cache, %d\n",
- ret);
- }
+ bucket->offset = offset;
+ bucket->num = n_buckets;
list_add_tail(&bucket->bucket_list, &storage->buckets);
- ++nr_copies;
- if (nr_copies >= desired_copies)
+ ++n_buckets;
+ if (n_buckets >= desired_buckets)
return 0;
}
- if (!nr_copies) {
+ if (!n_buckets) {
dev_err(storage->dev, "Failed to initialize any state storage bucket\n");
return -EIO;
}
dev_warn(storage->dev, "Failed to initialize desired amount of buckets, only %d of %d succeeded\n",
- nr_copies, desired_copies);
+ n_buckets, desired_buckets);
return 0;
}
-static int state_storage_file_create(struct device_d *dev, const char *path,
- size_t fd_size)
-{
- int fd;
- uint8_t *buf;
- int ret;
-
- fd = open(path, O_RDWR | O_CREAT, 0600);
- if (fd < 0) {
- dev_err(dev, "Failed to open/create file '%s', %d\n", path,
- -errno);
- return -errno;
- }
-
- buf = xzalloc(fd_size);
- if (!buf) {
- ret = -ENOMEM;
- goto out_close;
- }
-
- ret = write_full(fd, buf, fd_size);
- if (ret < 0) {
- dev_err(dev, "Failed to initialize empty file '%s', %d\n", path,
- ret);
- goto out_free;
- }
- ret = 0;
-
-out_free:
- free(buf);
-out_close:
- close(fd);
- return ret;
-}
-
/**
* state_storage_file_buckets_init - Create buckets for a conventional file descriptor
* @param storage Storage object
- * @param path Path to file/device
- * @param dev_offset Offset in the device to start writing at.
- * @param max_size Maximum size of the data. May be 0 for infinite.
- * @param stridesize How far apart the different data copies are placed. If
- * stridesize is 0, only one copy can be created.
* @return 0 on success, -errno otherwise
*
- * For blockdevices and other regular files we create direct buckets beginning
- * at offset 0. Direct buckets are simple and write data always to offset 0.
+ * direct buckets are simpler than circular buckets and can be used on blockdevices
+ * and mtd devices that don't need erase (MRAM). Also used for EEPROMs.
*/
-static int state_storage_file_buckets_init(struct state_backend_storage *storage,
- const char *path, off_t dev_offset,
- size_t max_size, uint32_t stridesize)
+static int state_storage_file_buckets_init(struct state_backend_storage *storage)
{
struct state_backend_storage_bucket *bucket;
- size_t fd_size = 0;
- int ret;
+ int ret, n;
off_t offset;
- int nr_copies = 0;
-
- ret = state_backend_storage_get_size(path, &fd_size);
- if (ret) {
- if (ret != -ENOENT) {
- dev_err(storage->dev, "Failed to get the filesize of '%s', %d\n",
- path, ret);
- return ret;
- }
- if (!stridesize) {
- dev_err(storage->dev, "File '%s' does not exist and no information about the needed size. Please specify stridesize\n",
- path);
- return ret;
- }
-
- if (max_size)
- fd_size = min(dev_offset + stridesize * desired_copies,
- dev_offset + max_size);
- else
- fd_size = dev_offset + stridesize * desired_copies;
- dev_info(storage->dev, "File '%s' does not exist, creating file of size %zd\n",
- path, fd_size);
- ret = state_storage_file_create(storage->dev, path, fd_size);
- if (ret) {
- dev_info(storage->dev, "Failed to create file '%s', %d\n",
- path, ret);
- return ret;
- }
- } else if (max_size) {
- fd_size = min(fd_size, (size_t)dev_offset + max_size);
- }
+ int n_buckets = 0;
+ uint32_t stridesize = storage->stridesize;
+ size_t max_size = storage->max_size;
if (!stridesize) {
- dev_warn(storage->dev, "WARNING, no stridesize given although we use a direct file write. Starting in degraded mode\n");
- stridesize = fd_size;
+ dev_err(storage->dev, "stridesize unspecified\n");
+ return -EINVAL;
}
- for (offset = dev_offset; offset < fd_size; offset += stridesize) {
- size_t maxsize = min((size_t)stridesize,
- (size_t)(fd_size - offset));
+ if (max_size && max_size < desired_buckets * stridesize) {
+ dev_err(storage->dev, "device is too small to hold %d copies\n", desired_buckets);
+ return -EINVAL;
+ }
- ret = state_backend_bucket_direct_create(storage->dev, path,
+ for (n = 0; n < desired_buckets; n++) {
+ offset = storage->offset + n * stridesize;
+ ret = state_backend_bucket_direct_create(storage->dev, storage->path,
&bucket, offset,
- maxsize);
+ stridesize);
if (ret) {
dev_warn(storage->dev, "Failed to create direct bucket at '%s' offset %ld\n",
- path, offset);
+ storage->path, offset);
continue;
}
- ret = state_backend_bucket_cached_create(storage->dev, bucket,
- &bucket);
- if (ret) {
- dev_warn(storage->dev, "Failed to setup cache bucket, continuing without cache, %d\n",
- ret);
- }
+ bucket->offset = offset;
+ bucket->num = n_buckets;
list_add_tail(&bucket->bucket_list, &storage->buckets);
- ++nr_copies;
- if (nr_copies >= desired_copies)
- return 0;
+ ++n_buckets;
}
- if (!nr_copies) {
+ if (!n_buckets) {
dev_err(storage->dev, "Failed to initialize any state direct storage bucket\n");
return -EIO;
}
- dev_warn(storage->dev, "Failed to initialize desired amount of direct buckets, only %d of %d succeeded\n",
- nr_copies, desired_copies);
+
+ if (n_buckets < desired_buckets)
+ dev_warn(storage->dev, "Failed to initialize desired amount of direct buckets, only %d of %d succeeded\n",
+ n_buckets, desired_buckets);
return 0;
}
@@ -455,7 +346,6 @@ static int state_storage_file_buckets_init(struct state_backend_storage *storage
/**
* state_storage_init - Init backend storage
- * @param storage Storage object
* @param path Path to the backend storage file
* @param dev_offset Offset in the device to start writing at.
* @param max_size Maximum size of the data. May be 0 for infinite.
@@ -466,37 +356,39 @@ static int state_storage_file_buckets_init(struct state_backend_storage *storage
*
* Depending on the filetype, we create mtd buckets or normal file buckets.
*/
-int state_storage_init(struct state_backend_storage *storage,
- struct device_d *dev, const char *path,
+int state_storage_init(struct state *state, const char *path,
off_t offset, size_t max_size, uint32_t stridesize,
const char *storagetype)
{
+ struct state_backend_storage *storage = &state->storage;
int ret = -ENODEV;
struct mtd_info_user meminfo;
INIT_LIST_HEAD(&storage->buckets);
- storage->dev = dev;
+ storage->dev = &state->dev;
storage->name = storagetype;
storage->stridesize = stridesize;
+ storage->offset = offset;
+ storage->max_size = max_size;
+ storage->path = xstrdup(path);
if (IS_ENABLED(CONFIG_MTD))
ret = mtd_get_meminfo(path, &meminfo);
if (!ret && !(meminfo.flags & MTD_NO_ERASE)) {
- bool non_circular = false;
- if (!storagetype) {
- non_circular = true;
- } else if (strcmp(storagetype, "circular")) {
- dev_warn(storage->dev, "Unknown storagetype '%s', falling back to old format circular storage type.\n",
- storagetype);
- non_circular = true;
+ bool circular;
+ if (!storagetype || !strcmp(storagetype, "circular")) {
+ circular = true;
+ } else if (!strcmp(storagetype, "noncircular")) {
+ dev_warn(storage->dev, "using old format circular storage type.\n");
+ circular = false;
+ } else {
+ dev_warn(storage->dev, "unknown storage type '%s'\n", storagetype);
+ return -EINVAL;
}
- return state_storage_mtd_buckets_init(storage, &meminfo, path,
- non_circular, offset,
- max_size);
+ return state_storage_mtd_buckets_init(storage, &meminfo, circular);
} else {
- return state_storage_file_buckets_init(storage, path, offset,
- max_size, stridesize);
+ return state_storage_file_buckets_init(storage);
}
dev_err(storage->dev, "storage init done\n");
@@ -524,4 +416,6 @@ void state_storage_free(struct state_backend_storage *storage)
list_del(&bucket->bucket_list);
bucket->free(bucket);
}
+
+ free(storage->path);
}
diff --git a/common/state/state.c b/common/state/state.c
index 4020d5e1ea..8369aedac6 100644
--- a/common/state/state.c
+++ b/common/state/state.c
@@ -34,6 +34,125 @@
/* list of all registered state instances */
static LIST_HEAD(state_list);
+/**
+ * Save the state
+ * @param state
+ * @return
+ */
+int state_save(struct state *state)
+{
+ void *buf;
+ ssize_t len;
+ int ret;
+
+ if (!state->dirty)
+ return 0;
+
+ ret = state->format->pack(state->format, state, &buf, &len);
+ if (ret) {
+ dev_err(&state->dev, "Failed to pack state with backend format %s, %d\n",
+ state->format->name, ret);
+ return ret;
+ }
+
+ ret = state_storage_write(&state->storage, buf, len);
+ if (ret) {
+ dev_err(&state->dev, "Failed to write packed state, %d\n", ret);
+ goto out;
+ }
+
+ state->dirty = 0;
+
+out:
+ free(buf);
+ return ret;
+}
+
+/**
+ * state_load - Loads a state from the backend
+ * @param state The state that should be updated to contain the loaded data
+ * @return 0 on success, -errno on failure. If no state is loaded the previous
+ * values remain in the state.
+ *
+ * This function uses the registered storage backend to read data. All data that
+ * we read is checked for integrity by the formatter. After that we unpack the
+ * data into our state.
+ */
+static int state_do_load(struct state *state, enum state_flags flags)
+{
+ void *buf;
+ ssize_t len;
+ int ret;
+
+ ret = state_storage_read(&state->storage, state->format,
+ state->magic, &buf, &len, flags);
+ if (ret) {
+ dev_err(&state->dev, "Failed to read state with format %s, %d\n",
+ state->format->name, ret);
+ return ret;
+ }
+
+ ret = state->format->unpack(state->format, state, buf, len);
+ if (ret) {
+ dev_err(&state->dev, "Failed to unpack read data with format %s although verified, %d\n",
+ state->format->name, ret);
+ goto out;
+ }
+
+ state->dirty = 0;
+
+out:
+ free(buf);
+ return ret;
+}
+
+int state_load(struct state *state)
+{
+ return state_do_load(state, 0);
+}
+
+int state_load_no_auth(struct state *state)
+{
+ return state_do_load(state, STATE_FLAG_NO_AUTHENTIFICATION);
+}
+
+static int state_format_init(struct state *state, const char *backend_format,
+ struct device_node *node, const char *state_name)
+{
+ int ret;
+
+ if (!backend_format || !strcmp(backend_format, "raw")) {
+ ret = backend_format_raw_create(&state->format, node,
+ state_name, &state->dev);
+ } else if (!strcmp(backend_format, "dtb")) {
+ ret = backend_format_dtb_create(&state->format, &state->dev);
+ } else {
+ dev_err(&state->dev, "Invalid backend format %s\n",
+ backend_format);
+ return -EINVAL;
+ }
+
+ if (ret && ret != -EPROBE_DEFER)
+ dev_err(&state->dev, "Failed to initialize format %s, %d\n",
+ backend_format, ret);
+
+ return ret;
+}
+
+static void state_format_free(struct state_backend_format *format)
+{
+ if (!format)
+ return;
+
+ if (format->free)
+ format->free(format);
+}
+
+void state_backend_set_readonly(struct state *state)
+{
+ state_storage_set_readonly(&state->storage);
+}
+
static struct state *state_new(const char *name)
{
struct state *state;
@@ -152,7 +271,6 @@ static int state_convert_node_variable(struct state *state,
sv->name = name;
sv->start = start_size[0];
- sv->type = vtype->type;
state_add_var(state, sv);
} else {
sv = state_find_var(state, name);
@@ -167,9 +285,8 @@ static int state_convert_node_variable(struct state *state,
if ((conv == STATE_CONVERT_TO_NODE)
|| (conv == STATE_CONVERT_FIXUP)) {
- ret = of_set_property(new_node, "type",
- vtype->type_name,
- strlen(vtype->type_name) + 1, 1);
+ ret = of_property_write_string(new_node, "type",
+ vtype->type_name);
if (ret)
goto out;
@@ -329,21 +446,21 @@ static int of_state_fixup(struct device_node *root, void *ctx)
}
/* backend-type */
- if (!state->backend.format) {
+ if (!state->format) {
ret = -ENODEV;
goto out;
}
p = of_new_property(new_node, "backend-type",
- state->backend.format->name,
- strlen(state->backend.format->name) + 1);
+ state->format->name,
+ strlen(state->format->name) + 1);
if (!p) {
ret = -ENOMEM;
goto out;
}
/* backend phandle */
- backend_node = of_find_node_by_path_from(root, state->backend.of_path);
+ backend_node = of_find_node_by_devpath(root, state->backend_path);
if (!backend_node) {
ret = -ENODEV;
goto out;
@@ -354,9 +471,9 @@ static int of_state_fixup(struct device_node *root, void *ctx)
if (ret)
goto out;
- if (!strcmp("raw", state->backend.format->name)) {
+ if (!strcmp("raw", state->format->name)) {
struct digest *digest =
- state_backend_format_raw_get_digest(state->backend.format);
+ state_backend_format_raw_get_digest(state->format);
if (digest) {
p = of_new_property(new_node, "algo",
digest_name(digest),
@@ -368,19 +485,19 @@ static int of_state_fixup(struct device_node *root, void *ctx)
}
}
- if (state->backend.storage.name) {
+ if (state->storage.name) {
p = of_new_property(new_node, "backend-storage-type",
- state->backend.storage.name,
- strlen(state->backend.storage.name) + 1);
+ state->storage.name,
+ strlen(state->storage.name) + 1);
if (!p) {
ret = -ENOMEM;
goto out;
}
}
- if (state->backend.storage.stridesize) {
+ if (state->storage.stridesize) {
ret = of_property_write_u32(new_node, "backend-stridesize",
- state->backend.storage.stridesize);
+ state->storage.stridesize);
if (ret)
goto out;
}
@@ -409,7 +526,9 @@ void state_release(struct state *state)
of_unregister_fixup(of_state_fixup, state);
list_del(&state->list);
unregister_device(&state->dev);
- state_backend_free(&state->backend);
+ state_storage_free(&state->storage);
+ state_format_free(state->format);
+ free(state->backend_path);
free(state->of_path);
free(state);
}
@@ -453,19 +572,16 @@ struct state *state_new_from_node(struct device_node *node, char *path,
}
if (!path) {
- /* guess if of_path is a path, not a phandle */
- if (of_path[0] == '/' && len > 1) {
- ret = of_find_path(node, "backend", &path, 0);
- } else {
- struct device_node *partition_node;
-
- partition_node = of_parse_phandle(node, "backend", 0);
- if (!partition_node)
- goto out_release_state;
-
- of_path = partition_node->full_name;
- ret = of_find_path_by_node(partition_node, &path, 0);
+ struct device_node *partition_node;
+
+ partition_node = of_parse_phandle(node, "backend", 0);
+ if (!partition_node) {
+ dev_err(&state->dev, "Cannot resolve \"backend\" phandle\n");
+ goto out_release_state;
}
+
+ of_path = partition_node->full_name;
+ ret = of_find_path_by_node(partition_node, &path, 0);
if (ret) {
if (ret != -EPROBE_DEFER)
dev_err(&state->dev, "state failed to parse path to backend: %s\n",
@@ -474,6 +590,8 @@ struct state *state_new_from_node(struct device_node *node, char *path,
}
}
+ state->backend_path = xstrdup(path);
+
ret = of_property_read_string(node, "backend-type", &backend_type);
if (ret) {
goto out_release_state;
@@ -491,14 +609,17 @@ struct state *state_new_from_node(struct device_node *node, char *path,
dev_info(&state->dev, "No backend-storage-type found, using default.\n");
}
- ret = state_backend_init(&state->backend, &state->dev, node,
- backend_type, path, alias, of_path, offset,
+ ret = state_format_init(state, backend_type, node, alias);
+ if (ret)
+ goto out_release_state;
+
+ ret = state_storage_init(state, path, offset,
max_size, stridesize, storage_type);
if (ret)
goto out_release_state;
if (readonly)
- state_backend_set_readonly(&state->backend);
+ state_backend_set_readonly(state);
ret = state_from_node(state, node, 1);
if (ret) {
@@ -510,11 +631,6 @@ struct state *state_new_from_node(struct device_node *node, char *path,
goto out_release_state;
}
- ret = state_load(state);
- if (ret) {
- dev_warn(&state->dev, "Failed to load persistent state, continuing with defaults, %d\n", ret);
- }
-
dev_info(&state->dev, "New state registered '%s'\n", alias);
return state;
@@ -573,10 +689,10 @@ void state_info(void)
list_for_each_entry(state, &state_list, list) {
printf("%-20s ", state->name);
- if (state->backend.format)
+ if (state->format)
printf("(backend: %s, path: %s)\n",
- state->backend.format->name,
- state->backend.of_path);
+ state->format->name,
+ state->backend_path);
else
printf("(no backend)\n");
}
diff --git a/common/state/state.h b/common/state/state.h
index bc6917de61..81aaec23b6 100644
--- a/common/state/state.h
+++ b/common/state/state.h
@@ -5,6 +5,10 @@
struct state;
struct mtd_info_user;
+enum state_flags {
+ STATE_FLAG_NO_AUTHENTIFICATION = (1 << 0),
+};
+
/**
* state_backend_storage_bucket - This class describes a single backend storage
* object copy
@@ -20,15 +24,20 @@ struct mtd_info_user;
* @bucket_list A list element struct to attach this bucket to a list
*/
struct state_backend_storage_bucket {
- int (*init) (struct state_backend_storage_bucket * bucket);
int (*write) (struct state_backend_storage_bucket * bucket,
- const uint8_t * buf, ssize_t len);
+ const void * buf, ssize_t len);
int (*read) (struct state_backend_storage_bucket * bucket,
- uint8_t ** buf, ssize_t * len_hint);
+ void ** buf, ssize_t * len_hint);
void (*free) (struct state_backend_storage_bucket * bucket);
- bool initialized;
+ int num;
+ off_t offset;
+
struct list_head bucket_list;
+
+ void *buf;
+ ssize_t len;
+ bool needs_refresh;
};
/**
@@ -48,13 +57,11 @@ struct state_backend_storage_bucket {
*/
struct state_backend_format {
int (*verify) (struct state_backend_format * format, uint32_t magic,
- const uint8_t * buf, ssize_t len);
+ const void * buf, ssize_t *lenp, enum state_flags flags);
int (*pack) (struct state_backend_format * format, struct state * state,
- uint8_t ** buf, ssize_t * len);
+ void ** buf, ssize_t * len);
int (*unpack) (struct state_backend_format * format,
- struct state * state, const uint8_t * buf, ssize_t len);
- ssize_t(*get_packed_len) (struct state_backend_format * format,
- struct state * state);
+ struct state * state, const void * buf, ssize_t len);
void (*free) (struct state_backend_format * format);
const char *name;
};
@@ -63,6 +70,9 @@ struct state_backend_format {
* state_backend_storage - Storage backend of the state.
*
* @buckets List of storage buckets that are available
+ * @stridesize The distance between copies
+ * @offset Offset in the backend device where the data starts
+ * @max_size The maximum size of the data we can use
*/
struct state_backend_storage {
struct list_head buckets;
@@ -73,23 +83,13 @@ struct state_backend_storage {
const char *name;
uint32_t stridesize;
+ off_t offset;
+ size_t max_size;
+ char *path;
bool readonly;
};
-/**
- * state_backend - State Backend object
- *
- * @format Backend format object
- * @storage Backend storage object
- * @of_path Path to the DT node
- */
-struct state_backend {
- struct state_backend_format *format;
- struct state_backend_storage storage;
- char *of_path;
-};
-
struct state {
struct list_head list; /* Entry to enqueue on list of states */
@@ -102,7 +102,9 @@ struct state {
unsigned int dirty;
unsigned int save_on_shutdown;
- struct state_backend backend;
+ struct state_backend_format *format;
+ struct state_backend_storage storage;
+ char *backend_path;
};
enum state_convert {
@@ -112,20 +114,10 @@ enum state_convert {
STATE_CONVERT_FIXUP,
};
-enum state_variable_type {
- STATE_TYPE_INVALID = 0,
- STATE_TYPE_ENUM,
- STATE_TYPE_U8,
- STATE_TYPE_U32,
- STATE_TYPE_MAC,
- STATE_TYPE_STRING,
-};
-
struct state_variable;
/* A variable type (uint32, enum32) */
struct variable_type {
- enum state_variable_type type;
const char *type_name;
struct list_head list;
int (*export) (struct state_variable *, struct device_node *,
@@ -139,7 +131,6 @@ struct variable_type {
/* instance of a single variable */
struct state_variable {
struct state *state;
- enum state_variable_type type;
struct list_head list;
const char *name;
unsigned int start;
@@ -199,8 +190,7 @@ int backend_format_raw_create(struct state_backend_format **format,
struct device_d *dev);
int backend_format_dtb_create(struct state_backend_format **format,
struct device_d *dev);
-int state_storage_init(struct state_backend_storage *storage,
- struct device_d *dev, const char *path,
+int state_storage_init(struct state *state, const char *path,
off_t offset, size_t max_size, uint32_t stridesize,
const char *storagetype);
void state_storage_set_readonly(struct state_backend_storage *storage);
@@ -210,34 +200,24 @@ int state_backend_bucket_circular_create(struct device_d *dev, const char *path,
struct state_backend_storage_bucket **bucket,
unsigned int eraseblock,
ssize_t writesize,
- struct mtd_info_user *mtd_uinfo,
- bool lazy_init);
+ struct mtd_info_user *mtd_uinfo);
int state_backend_bucket_cached_create(struct device_d *dev,
struct state_backend_storage_bucket *raw,
struct state_backend_storage_bucket **out);
struct state_variable *state_find_var(struct state *state, const char *name);
struct digest *state_backend_format_raw_get_digest(struct state_backend_format
*format);
-int state_backend_init(struct state_backend *backend, struct device_d *dev,
- struct device_node *node, const char *backend_format,
- const char *storage_path, const char *state_name, const
- char *of_path, off_t offset, size_t max_size,
- uint32_t stridesize, const char *storagetype);
-void state_backend_set_readonly(struct state_backend *backend);
-void state_backend_free(struct state_backend *backend);
+void state_backend_set_readonly(struct state *state);
void state_storage_free(struct state_backend_storage *storage);
int state_backend_bucket_direct_create(struct device_d *dev, const char *path,
struct state_backend_storage_bucket **bucket,
off_t offset, ssize_t max_size);
int state_storage_write(struct state_backend_storage *storage,
- const uint8_t * buf, ssize_t len);
-int state_storage_restore_consistency(struct state_backend_storage
- *storage, const uint8_t * buf,
- ssize_t len);
+ const void * buf, ssize_t len);
int state_storage_read(struct state_backend_storage *storage,
struct state_backend_format *format,
- uint32_t magic, uint8_t **buf, ssize_t *len,
- ssize_t len_hint);
+ uint32_t magic, void **buf, ssize_t *len,
+ enum state_flags flags);
static inline struct state_uint32 *to_state_uint32(struct state_variable *s)
{
diff --git a/common/state/state_variables.c b/common/state/state_variables.c
index fd072a0c27..5b8e6284d9 100644
--- a/common/state/state_variables.c
+++ b/common/state/state_variables.c
@@ -439,31 +439,26 @@ static struct state_variable *state_string_create(struct state *state,
static struct variable_type types[] = {
{
- .type = STATE_TYPE_U8,
.type_name = "uint8",
.export = state_uint32_export,
.import = state_uint32_import,
.create = state_uint8_create,
}, {
- .type = STATE_TYPE_U32,
.type_name = "uint32",
.export = state_uint32_export,
.import = state_uint32_import,
.create = state_uint32_create,
}, {
- .type = STATE_TYPE_ENUM,
.type_name = "enum32",
.export = state_enum32_export,
.import = state_enum32_import,
.create = state_enum32_create,
}, {
- .type = STATE_TYPE_MAC,
.type_name = "mac",
.export = state_mac_export,
.import = state_mac_import,
.create = state_mac_create,
}, {
- .type = STATE_TYPE_STRING,
.type_name = "string",
.export = state_string_export,
.import = state_string_import,
diff --git a/crypto/hmac.c b/crypto/hmac.c
index 05b9b50f12..37b270df7a 100644
--- a/crypto/hmac.c
+++ b/crypto/hmac.c
@@ -196,4 +196,4 @@ static int digest_hmac_initcall(void)
return 0;
}
-crypto_initcall(digest_hmac_initcall);
+coredevice_initcall(digest_hmac_initcall);
diff --git a/crypto/keystore.c b/crypto/keystore.c
index 90b470fe67..f2b25ca6c9 100644
--- a/crypto/keystore.c
+++ b/crypto/keystore.c
@@ -16,8 +16,8 @@ static LIST_HEAD(keystore_list);
struct keystore_key {
struct list_head list;
- const char *name;
- const u8 *secret;
+ char *name;
+ u8 *secret;
int secret_len;
};
@@ -29,6 +29,17 @@ static int keystore_compare(struct list_head *a, struct list_head *b)
return strcmp(na, nb);
}
+static struct keystore_key *get_key(const char *name)
+{
+ struct keystore_key *key;
+
+ for_each_key(key)
+ if (!strcmp(name, key->name))
+ return key;
+
+ return NULL;
+};
+
/**
* @param[in] name Name of the secret to get
* @param[out] secret Double pointer to memory representing the secret, do _not_ free() after use
@@ -38,19 +49,17 @@ int keystore_get_secret(const char *name, const u8 **secret, int *secret_len)
{
struct keystore_key *key;
- for_each_key(key) {
- if (!strcmp(name, key->name)) {
- if (!secret || !secret_len)
- return 0;
+ if (!secret || !secret_len)
+ return 0;
- *secret = key->secret;
- *secret_len = key->secret_len;
+ key = get_key(name);
+ if (!key)
+ return -ENOENT;
- return 0;
- }
- }
+ *secret = key->secret;
+ *secret_len = key->secret_len;
- return -ENOENT;
+ return 0;
}
/**
@@ -61,11 +70,10 @@ int keystore_get_secret(const char *name, const u8 **secret, int *secret_len)
int keystore_set_secret(const char *name, const u8 *secret, int secret_len)
{
struct keystore_key *key;
- int ret;
/* check if key is already in store */
- ret = keystore_get_secret(name, NULL, NULL);
- if (!ret)
+ key = get_key(name);
+ if (key)
return -EBUSY;
key = xzalloc(sizeof(*key));
@@ -78,3 +86,18 @@ int keystore_set_secret(const char *name, const u8 *secret, int secret_len)
return 0;
}
+
+void keystore_forget_secret(const char *name)
+{
+ struct keystore_key *key;
+
+ key = get_key(name);
+ if (!key)
+ return;
+
+ list_del(&key->list);
+
+ free(key->name);
+ free(key->secret);
+ free(key);
+}
diff --git a/crypto/sha1.c b/crypto/sha1.c
index cbde4d28e4..f4b2ded0b0 100644
--- a/crypto/sha1.c
+++ b/crypto/sha1.c
@@ -303,4 +303,4 @@ static int sha1_digest_register(void)
{
return digest_algo_register(&m);
}
-device_initcall(sha1_digest_register);
+coredevice_initcall(sha1_digest_register);
diff --git a/crypto/sha2.c b/crypto/sha2.c
index cb0f11c77e..c62ddb8d25 100644
--- a/crypto/sha2.c
+++ b/crypto/sha2.c
@@ -372,4 +372,4 @@ static int sha256_digest_register(void)
return digest_algo_register(&m256);
}
-device_initcall(sha256_digest_register);
+coredevice_initcall(sha256_digest_register);
diff --git a/crypto/sha4.c b/crypto/sha4.c
index 4ce37b73e4..aad8081fa5 100644
--- a/crypto/sha4.c
+++ b/crypto/sha4.c
@@ -292,4 +292,4 @@ static int sha512_digest_register(void)
return digest_algo_register(&m512);
}
-device_initcall(sha512_digest_register);
+coredevice_initcall(sha512_digest_register);
diff --git a/drivers/Kconfig b/drivers/Kconfig
index ab9afb54bd..1e0246da6d 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -22,10 +22,12 @@ source "drivers/eeprom/Kconfig"
source "drivers/input/Kconfig"
source "drivers/watchdog/Kconfig"
source "drivers/pwm/Kconfig"
+source "drivers/hw_random/Kconfig"
source "drivers/dma/Kconfig"
source "drivers/gpio/Kconfig"
source "drivers/w1/Kconfig"
source "drivers/pinctrl/Kconfig"
+source "drivers/nvmem/Kconfig"
source "drivers/bus/Kconfig"
source "drivers/regulator/Kconfig"
source "drivers/reset/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index a1d2d23fef..767789d541 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -19,9 +19,11 @@ obj-y += eeprom/
obj-$(CONFIG_PWM) += pwm/
obj-y += input/
obj-y += misc/
+obj-$(CONFIG_NVMEM) += nvmem/
obj-y += dma/
obj-y += watchdog/
obj-y += gpio/
+obj-$(CONFIG_HWRNG) += hw_random/
obj-$(CONFIG_OFTREE) += of/
obj-$(CONFIG_W1) += w1/
obj-y += pinctrl/
diff --git a/drivers/ata/disk_ata_drive.c b/drivers/ata/disk_ata_drive.c
index 1aa1bb1456..5ebddbdec8 100644
--- a/drivers/ata/disk_ata_drive.c
+++ b/drivers/ata/disk_ata_drive.c
@@ -237,6 +237,9 @@ static int ata_port_init(struct ata_port *port)
#ifdef DEBUG
ata_dump_id(port->id);
#endif
+
+ port->lba48 = ata_id_has_lba48(port->id);
+
if (port->devname) {
port->blk.cdev.name = xstrdup(port->devname);
} else {
diff --git a/drivers/ata/ide-sff.c b/drivers/ata/ide-sff.c
index 6dc89d79a5..b7c8847266 100644
--- a/drivers/ata/ide-sff.c
+++ b/drivers/ata/ide-sff.c
@@ -136,17 +136,33 @@ static int ata_wait_ready(struct ide_port *ide, unsigned timeout)
* @param io Register file
* @param drive 0 master drive, 1 slave drive
* @param num Sector number
- *
- * @todo LBA48 support
*/
-static int ata_set_lba_sector(struct ide_port *ide, unsigned drive, uint64_t num)
+static int ata_set_lba_sector(struct ata_port *port, unsigned drive,
+ uint64_t num)
{
- if (num > 0x0FFFFFFF || drive > 1)
+ struct ide_port *ide = to_ata_drive_access(port);
+
+ if (drive > 1)
return -EINVAL;
- ata_wr_byte(ide, 0xA0 | LBA_FLAG | drive << 4 | num >> 24,
- ide->io.device_addr);
- ata_wr_byte(ide, 0x00, ide->io.error_addr);
+ if (port->lba48) {
+ if (num > (1ULL << 48) - 1)
+ return -EINVAL;
+
+ ata_wr_byte(ide, LBA_FLAG | drive << 4, ide->io.device_addr);
+
+ ata_wr_byte(ide, 0x00, ide->io.nsect_addr);
+ ata_wr_byte(ide, num >> 24, ide->io.lbal_addr);
+ ata_wr_byte(ide, num >> 32, ide->io.lbam_addr);
+ ata_wr_byte(ide, num >> 40, ide->io.lbah_addr);
+ } else {
+ if (num > (1ULL << 28) - 1)
+ return -EINVAL;
+
+ ata_wr_byte(ide, 0xA0 | LBA_FLAG | drive << 4 | num >> 24,
+ ide->io.device_addr);
+ }
+
ata_wr_byte(ide, 0x01, ide->io.nsect_addr);
ata_wr_byte(ide, num, ide->io.lbal_addr); /* 0 ... 7 */
ata_wr_byte(ide, num >> 8, ide->io.lbam_addr); /* 8 ... 15 */
@@ -316,10 +332,18 @@ static int ide_read(struct ata_port *port, void *buffer, unsigned int block,
struct ide_port *ide = to_ata_drive_access(port);
while (num_blocks) {
- rc = ata_set_lba_sector(ide, DISK_MASTER, sector);
+ uint8_t cmd;
+
+ rc = ata_set_lba_sector(port, DISK_MASTER, sector);
if (rc != 0)
return rc;
- rc = ata_wr_cmd(ide, ATA_CMD_READ);
+
+ if (port->lba48)
+ cmd = ATA_CMD_PIO_READ_EXT;
+ else
+ cmd = ATA_CMD_READ;
+
+ rc = ata_wr_cmd(ide, cmd);
if (rc != 0)
return rc;
rc = ata_wait_ready(ide, MAX_TIMEOUT);
@@ -355,10 +379,18 @@ static int __maybe_unused ide_write(struct ata_port *port,
struct ide_port *ide = to_ata_drive_access(port);
while (num_blocks) {
- rc = ata_set_lba_sector(ide, DISK_MASTER, sector);
+ uint8_t cmd;
+
+ rc = ata_set_lba_sector(port, DISK_MASTER, sector);
if (rc != 0)
return rc;
- rc = ata_wr_cmd(ide, ATA_CMD_WRITE);
+
+ if (port->lba48)
+ cmd = ATA_CMD_PIO_WRITE_EXT;
+ else
+ cmd = ATA_CMD_WRITE;
+
+ rc = ata_wr_cmd(ide, cmd);
if (rc != 0)
return rc;
rc = ata_wait_ready(ide, MAX_TIMEOUT);
diff --git a/drivers/base/driver.c b/drivers/base/driver.c
index 5867fe45d0..83260990af 100644
--- a/drivers/base/driver.c
+++ b/drivers/base/driver.c
@@ -221,7 +221,7 @@ int unregister_device(struct device_d *old_dev)
}
list_for_each_entry_safe(cdev, ct, &old_dev->cdevs, devices_list) {
- if (cdev->flags & DEVFS_IS_PARTITION) {
+ if (cdev->master) {
dev_dbg(old_dev, "unregister part %s\n", cdev->name);
devfs_del_partition(cdev->name);
}
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 5811d28b88..d75b954a4e 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_ARCH_TEGRA) += tegra/
obj-$(CONFIG_CLK_SOCFPGA) += socfpga.o
obj-$(CONFIG_MACH_MIPS_ATH79) += clk-ar933x.o
obj-$(CONFIG_ARCH_IMX) += imx/
+obj-$(CONFIG_COMMON_CLK_AT91) += at91/
diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
new file mode 100644
index 0000000000..13e67bd35c
--- /dev/null
+++ b/drivers/clk/at91/Makefile
@@ -0,0 +1,13 @@
+#
+# Makefile for at91 specific clk
+#
+
+obj-y += pmc.o sckc.o
+obj-y += clk-slow.o clk-main.o clk-pll.o clk-plldiv.o clk-master.o
+obj-y += clk-system.o clk-peripheral.o clk-programmable.o
+
+obj-$(CONFIG_HAVE_AT91_UTMI) += clk-utmi.o
+obj-$(CONFIG_HAVE_AT91_USB_CLK) += clk-usb.o
+obj-$(CONFIG_HAVE_AT91_SMD) += clk-smd.o
+obj-$(CONFIG_HAVE_AT91_H32MX) += clk-h32mx.o
+obj-$(CONFIG_HAVE_AT91_GENERATED_CLK) += clk-generated.o
diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c
new file mode 100644
index 0000000000..4e1cd5aa69
--- /dev/null
+++ b/drivers/clk/at91/clk-generated.c
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2015 Atmel Corporation,
+ * Nicolas Ferre <nicolas.ferre@atmel.com>
+ *
+ * Based on clk-programmable & clk-peripheral drivers by Boris BREZILLON.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/of.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include "pmc.h"
+
+#define PERIPHERAL_MAX 64
+#define PERIPHERAL_ID_MIN 2
+
+#define GENERATED_SOURCE_MAX 6
+#define GENERATED_MAX_DIV 255
+
+struct clk_generated {
+ struct clk_hw hw;
+ struct regmap *regmap;
+ struct clk_range range;
+ spinlock_t *lock;
+ u32 id;
+ u32 gckdiv;
+ u8 parent_id;
+};
+
+#define to_clk_generated(hw) \
+ container_of(hw, struct clk_generated, hw)
+
+static int clk_generated_enable(struct clk_hw *hw)
+{
+ struct clk_generated *gck = to_clk_generated(hw);
+ unsigned long flags;
+
+ pr_debug("GCLK: %s, gckdiv = %d, parent id = %d\n",
+ __func__, gck->gckdiv, gck->parent_id);
+
+ spin_lock_irqsave(gck->lock, flags);
+ regmap_write(gck->regmap, AT91_PMC_PCR,
+ (gck->id & AT91_PMC_PCR_PID_MASK));
+ regmap_update_bits(gck->regmap, AT91_PMC_PCR,
+ AT91_PMC_PCR_GCKDIV_MASK | AT91_PMC_PCR_GCKCSS_MASK |
+ AT91_PMC_PCR_CMD | AT91_PMC_PCR_GCKEN,
+ AT91_PMC_PCR_GCKCSS(gck->parent_id) |
+ AT91_PMC_PCR_CMD |
+ AT91_PMC_PCR_GCKDIV(gck->gckdiv) |
+ AT91_PMC_PCR_GCKEN);
+ spin_unlock_irqrestore(gck->lock, flags);
+ return 0;
+}
+
+static void clk_generated_disable(struct clk_hw *hw)
+{
+ struct clk_generated *gck = to_clk_generated(hw);
+ unsigned long flags;
+
+ spin_lock_irqsave(gck->lock, flags);
+ regmap_write(gck->regmap, AT91_PMC_PCR,
+ (gck->id & AT91_PMC_PCR_PID_MASK));
+ regmap_update_bits(gck->regmap, AT91_PMC_PCR,
+ AT91_PMC_PCR_CMD | AT91_PMC_PCR_GCKEN,
+ AT91_PMC_PCR_CMD);
+ spin_unlock_irqrestore(gck->lock, flags);
+}
+
+static int clk_generated_is_enabled(struct clk_hw *hw)
+{
+ struct clk_generated *gck = to_clk_generated(hw);
+ unsigned long flags;
+ unsigned int status;
+
+ spin_lock_irqsave(gck->lock, flags);
+ regmap_write(gck->regmap, AT91_PMC_PCR,
+ (gck->id & AT91_PMC_PCR_PID_MASK));
+ regmap_read(gck->regmap, AT91_PMC_PCR, &status);
+ spin_unlock_irqrestore(gck->lock, flags);
+
+ return status & AT91_PMC_PCR_GCKEN ? 1 : 0;
+}
+
+static unsigned long
+clk_generated_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_generated *gck = to_clk_generated(hw);
+
+ return DIV_ROUND_CLOSEST(parent_rate, gck->gckdiv + 1);
+}
+
+static int clk_generated_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct clk_generated *gck = to_clk_generated(hw);
+ struct clk_hw *parent = NULL;
+ long best_rate = -EINVAL;
+ unsigned long tmp_rate, min_rate;
+ int best_diff = -1;
+ int tmp_diff;
+ int i;
+
+ for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
+ u32 div;
+ unsigned long parent_rate;
+
+ parent = clk_hw_get_parent_by_index(hw, i);
+ if (!parent)
+ continue;
+
+ parent_rate = clk_hw_get_rate(parent);
+ min_rate = DIV_ROUND_CLOSEST(parent_rate, GENERATED_MAX_DIV + 1);
+ if (!parent_rate ||
+ (gck->range.max && min_rate > gck->range.max))
+ continue;
+
+ for (div = 1; div < GENERATED_MAX_DIV + 2; div++) {
+ tmp_rate = DIV_ROUND_CLOSEST(parent_rate, div);
+ tmp_diff = abs(req->rate - tmp_rate);
+
+ if (best_diff < 0 || best_diff > tmp_diff) {
+ best_rate = tmp_rate;
+ best_diff = tmp_diff;
+ req->best_parent_rate = parent_rate;
+ req->best_parent_hw = parent;
+ }
+
+ if (!best_diff || tmp_rate < req->rate)
+ break;
+ }
+
+ if (!best_diff)
+ break;
+ }
+
+ pr_debug("GCLK: %s, best_rate = %ld, parent clk: %s @ %ld\n",
+ __func__, best_rate,
+ __clk_get_name((req->best_parent_hw)->clk),
+ req->best_parent_rate);
+
+ if (best_rate < 0)
+ return best_rate;
+
+ req->rate = best_rate;
+ return 0;
+}
+
+/* No modification of hardware as we have the flag CLK_SET_PARENT_GATE set */
+static int clk_generated_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_generated *gck = to_clk_generated(hw);
+
+ if (index >= clk_hw_get_num_parents(hw))
+ return -EINVAL;
+
+ gck->parent_id = index;
+ return 0;
+}
+
+static u8 clk_generated_get_parent(struct clk_hw *hw)
+{
+ struct clk_generated *gck = to_clk_generated(hw);
+
+ return gck->parent_id;
+}
+
+/* No modification of hardware as we have the flag CLK_SET_RATE_GATE set */
+static int clk_generated_set_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_generated *gck = to_clk_generated(hw);
+ u32 div;
+
+ if (!rate)
+ return -EINVAL;
+
+ if (gck->range.max && rate > gck->range.max)
+ return -EINVAL;
+
+ div = DIV_ROUND_CLOSEST(parent_rate, rate);
+ if (div > GENERATED_MAX_DIV + 1 || !div)
+ return -EINVAL;
+
+ gck->gckdiv = div - 1;
+ return 0;
+}
+
+static const struct clk_ops generated_ops = {
+ .enable = clk_generated_enable,
+ .disable = clk_generated_disable,
+ .is_enabled = clk_generated_is_enabled,
+ .recalc_rate = clk_generated_recalc_rate,
+ .determine_rate = clk_generated_determine_rate,
+ .get_parent = clk_generated_get_parent,
+ .set_parent = clk_generated_set_parent,
+ .set_rate = clk_generated_set_rate,
+};
+
+/**
+ * clk_generated_startup - Initialize a given clock to its default parent and
+ * divisor parameter.
+ *
+ * @gck: Generated clock to set the startup parameters for.
+ *
+ * Take parameters from the hardware and update local clock configuration
+ * accordingly.
+ */
+static void clk_generated_startup(struct clk_generated *gck)
+{
+ u32 tmp;
+ unsigned long flags;
+
+ spin_lock_irqsave(gck->lock, flags);
+ regmap_write(gck->regmap, AT91_PMC_PCR,
+ (gck->id & AT91_PMC_PCR_PID_MASK));
+ regmap_read(gck->regmap, AT91_PMC_PCR, &tmp);
+ spin_unlock_irqrestore(gck->lock, flags);
+
+ gck->parent_id = (tmp & AT91_PMC_PCR_GCKCSS_MASK)
+ >> AT91_PMC_PCR_GCKCSS_OFFSET;
+ gck->gckdiv = (tmp & AT91_PMC_PCR_GCKDIV_MASK)
+ >> AT91_PMC_PCR_GCKDIV_OFFSET;
+}
+
+static struct clk_hw * __init
+at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
+ const char *name, const char **parent_names,
+ u8 num_parents, u8 id,
+ const struct clk_range *range)
+{
+ struct clk_generated *gck;
+ struct clk_init_data init;
+ struct clk_hw *hw;
+ int ret;
+
+ gck = kzalloc(sizeof(*gck), GFP_KERNEL);
+ if (!gck)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &generated_ops;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
+
+ gck->id = id;
+ gck->hw.init = &init;
+ gck->regmap = regmap;
+ gck->lock = lock;
+ gck->range = *range;
+
+ hw = &gck->hw;
+ ret = clk_hw_register(NULL, &gck->hw);
+ if (ret) {
+ kfree(gck);
+ hw = ERR_PTR(ret);
+ } else
+ clk_generated_startup(gck);
+
+ return hw;
+}
+
+static void __init of_sama5d2_clk_generated_setup(struct device_node *np)
+{
+ int num;
+ u32 id;
+ const char *name;
+ struct clk_hw *hw;
+ unsigned int num_parents;
+ const char *parent_names[GENERATED_SOURCE_MAX];
+ struct device_node *gcknp;
+ struct clk_range range = CLK_RANGE(0, 0);
+ struct regmap *regmap;
+
+ num_parents = of_clk_get_parent_count(np);
+ if (num_parents == 0 || num_parents > GENERATED_SOURCE_MAX)
+ return;
+
+ of_clk_parent_fill(np, parent_names, num_parents);
+
+ num = of_get_child_count(np);
+ if (!num || num > PERIPHERAL_MAX)
+ return;
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return;
+
+ for_each_child_of_node(np, gcknp) {
+ if (of_property_read_u32(gcknp, "reg", &id))
+ continue;
+
+ if (id < PERIPHERAL_ID_MIN || id >= PERIPHERAL_MAX)
+ continue;
+
+ if (of_property_read_string(np, "clock-output-names", &name))
+ name = gcknp->name;
+
+ of_at91_get_clk_range(gcknp, "atmel,clk-output-range",
+ &range);
+
+ hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, name,
+ parent_names, num_parents,
+ id, &range);
+ if (IS_ERR(hw))
+ continue;
+
+ of_clk_add_hw_provider(gcknp, of_clk_hw_simple_get, hw);
+ }
+}
+CLK_OF_DECLARE(of_sama5d2_clk_generated_setup, "atmel,sama5d2-clk-generated",
+ of_sama5d2_clk_generated_setup);
diff --git a/drivers/clk/at91/clk-h32mx.c b/drivers/clk/at91/clk-h32mx.c
new file mode 100644
index 0000000000..e0daa4a31f
--- /dev/null
+++ b/drivers/clk/at91/clk-h32mx.c
@@ -0,0 +1,125 @@
+/*
+ * clk-h32mx.c
+ *
+ * Copyright (C) 2014 Atmel
+ *
+ * Alexandre Belloni <alexandre.belloni@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#include "pmc.h"
+
+#define H32MX_MAX_FREQ 90000000
+
+struct clk_sama5d4_h32mx {
+ struct clk_hw hw;
+ struct regmap *regmap;
+};
+
+#define to_clk_sama5d4_h32mx(hw) container_of(hw, struct clk_sama5d4_h32mx, hw)
+
+static unsigned long clk_sama5d4_h32mx_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_sama5d4_h32mx *h32mxclk = to_clk_sama5d4_h32mx(hw);
+ unsigned int mckr;
+
+ regmap_read(h32mxclk->regmap, AT91_PMC_MCKR, &mckr);
+ if (mckr & AT91_PMC_H32MXDIV)
+ return parent_rate / 2;
+
+ if (parent_rate > H32MX_MAX_FREQ)
+ pr_warn("H32MX clock is too fast\n");
+ return parent_rate;
+}
+
+static long clk_sama5d4_h32mx_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ unsigned long div;
+
+ if (rate > *parent_rate)
+ return *parent_rate;
+ div = *parent_rate / 2;
+ if (rate < div)
+ return div;
+
+ if (rate - div < *parent_rate - rate)
+ return div;
+
+ return *parent_rate;
+}
+
+static int clk_sama5d4_h32mx_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_sama5d4_h32mx *h32mxclk = to_clk_sama5d4_h32mx(hw);
+ u32 mckr = 0;
+
+ if (parent_rate != rate && (parent_rate / 2) != rate)
+ return -EINVAL;
+
+ if ((parent_rate / 2) == rate)
+ mckr = AT91_PMC_H32MXDIV;
+
+ regmap_update_bits(h32mxclk->regmap, AT91_PMC_MCKR,
+ AT91_PMC_H32MXDIV, mckr);
+
+ return 0;
+}
+
+static const struct clk_ops h32mx_ops = {
+ .recalc_rate = clk_sama5d4_h32mx_recalc_rate,
+ .round_rate = clk_sama5d4_h32mx_round_rate,
+ .set_rate = clk_sama5d4_h32mx_set_rate,
+};
+
+static void __init of_sama5d4_clk_h32mx_setup(struct device_node *np)
+{
+ struct clk_sama5d4_h32mx *h32mxclk;
+ struct clk_init_data init;
+ const char *parent_name;
+ struct regmap *regmap;
+ int ret;
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return;
+
+ h32mxclk = kzalloc(sizeof(*h32mxclk), GFP_KERNEL);
+ if (!h32mxclk)
+ return;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+
+ init.name = np->name;
+ init.ops = &h32mx_ops;
+ init.parent_names = parent_name ? &parent_name : NULL;
+ init.num_parents = parent_name ? 1 : 0;
+ init.flags = CLK_SET_RATE_GATE;
+
+ h32mxclk->hw.init = &init;
+ h32mxclk->regmap = regmap;
+
+ ret = clk_hw_register(NULL, &h32mxclk->hw);
+ if (ret) {
+ kfree(h32mxclk);
+ return;
+ }
+
+ of_clk_add_hw_provider(np, of_clk_hw_simple_get, &h32mxclk->hw);
+}
+CLK_OF_DECLARE(of_sama5d4_clk_h32mx_setup, "atmel,sama5d4-clk-h32mx",
+ of_sama5d4_clk_h32mx_setup);
diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c
new file mode 100644
index 0000000000..55bc618a37
--- /dev/null
+++ b/drivers/clk/at91/clk-main.c
@@ -0,0 +1,576 @@
+/*
+ * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+#include <common.h>
+#include <clock.h>
+#include <of.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
+#include <mfd/syscon.h>
+#include <regmap.h>
+
+#include "pmc.h"
+
+#define SLOW_CLOCK_FREQ 32768
+#define MAINF_DIV 16
+#define MAINFRDY_TIMEOUT (((MAINF_DIV + 1) * SECOND) / \
+ SLOW_CLOCK_FREQ)
+#define MAINF_LOOP_MIN_WAIT (SECOND / SLOW_CLOCK_FREQ)
+#define MAINF_LOOP_MAX_WAIT MAINFRDY_TIMEOUT
+
+#define MOR_KEY_MASK (0xff << 16)
+
+struct clk_main_osc {
+ struct clk clk;
+ struct regmap *regmap;
+ const char *parent;
+};
+
+#define to_clk_main_osc(clk) container_of(clk, struct clk_main_osc, clk)
+
+struct clk_main_rc_osc {
+ struct clk clk;
+ struct regmap *regmap;
+ unsigned long frequency;
+};
+
+#define to_clk_main_rc_osc(clk) container_of(clk, struct clk_main_rc_osc, clk)
+
+struct clk_rm9200_main {
+ struct clk clk;
+ struct regmap *regmap;
+ const char *parent;
+};
+
+#define to_clk_rm9200_main(clk) container_of(clk, struct clk_rm9200_main, clk)
+
+struct clk_sam9x5_main {
+ struct clk clk;
+ struct regmap *regmap;
+ u8 parent;
+};
+
+#define to_clk_sam9x5_main(clk) container_of(clk, struct clk_sam9x5_main, clk)
+
+static inline bool clk_main_osc_ready(struct regmap *regmap)
+{
+ unsigned int status;
+
+ regmap_read(regmap, AT91_PMC_SR, &status);
+
+ return status & AT91_PMC_MOSCS;
+}
+
+static int clk_main_osc_enable(struct clk *clk)
+{
+ struct clk_main_osc *osc = to_clk_main_osc(clk);
+ struct regmap *regmap = osc->regmap;
+ u32 tmp;
+
+ regmap_read(regmap, AT91_CKGR_MOR, &tmp);
+ tmp &= ~MOR_KEY_MASK;
+
+ if (tmp & AT91_PMC_OSCBYPASS)
+ return 0;
+
+ if (!(tmp & AT91_PMC_MOSCEN)) {
+ tmp |= AT91_PMC_MOSCEN | AT91_PMC_KEY;
+ regmap_write(regmap, AT91_CKGR_MOR, tmp);
+ }
+
+ while (!clk_main_osc_ready(regmap))
+ barrier();
+
+ return 0;
+}
+
+static void clk_main_osc_disable(struct clk *clk)
+{
+ struct clk_main_osc *osc = to_clk_main_osc(clk);
+ struct regmap *regmap = osc->regmap;
+ u32 tmp;
+
+ regmap_read(regmap, AT91_CKGR_MOR, &tmp);
+ if (tmp & AT91_PMC_OSCBYPASS)
+ return;
+
+ if (!(tmp & AT91_PMC_MOSCEN))
+ return;
+
+ tmp &= ~(AT91_PMC_KEY | AT91_PMC_MOSCEN);
+ regmap_write(regmap, AT91_CKGR_MOR, tmp | AT91_PMC_KEY);
+}
+
+static int clk_main_osc_is_enabled(struct clk *clk)
+{
+ struct clk_main_osc *osc = to_clk_main_osc(clk);
+ struct regmap *regmap = osc->regmap;
+ u32 tmp, status;
+
+ regmap_read(regmap, AT91_CKGR_MOR, &tmp);
+ if (tmp & AT91_PMC_OSCBYPASS)
+ return 1;
+
+ regmap_read(regmap, AT91_PMC_SR, &status);
+
+ return (status & AT91_PMC_MOSCS) && (tmp & AT91_PMC_MOSCEN);
+}
+
+static const struct clk_ops main_osc_ops = {
+ .enable = clk_main_osc_enable,
+ .disable = clk_main_osc_disable,
+ .is_enabled = clk_main_osc_is_enabled,
+};
+
+static struct clk *
+at91_clk_register_main_osc(struct regmap *regmap,
+ const char *name,
+ const char *parent_name,
+ bool bypass)
+{
+ struct clk_main_osc *osc;
+ int ret;
+
+ if (!name || !parent_name)
+ return ERR_PTR(-EINVAL);
+
+ osc = xzalloc(sizeof(*osc));
+
+ osc->parent = parent_name;
+ osc->clk.name = name;
+ osc->clk.ops = &main_osc_ops;
+ osc->clk.parent_names = &osc->parent;
+ osc->clk.num_parents = 1;
+ osc->regmap = regmap;
+
+ if (bypass)
+ regmap_write_bits(regmap,
+ AT91_CKGR_MOR, MOR_KEY_MASK |
+ AT91_PMC_MOSCEN,
+ AT91_PMC_OSCBYPASS | AT91_PMC_KEY);
+
+ ret = clk_register(&osc->clk);
+ if (ret) {
+ free(osc);
+ return ERR_PTR(ret);
+ }
+
+ return &osc->clk;
+}
+
+static int of_at91rm9200_clk_main_osc_setup(struct device_node *np)
+{
+ struct clk *clk;
+ const char *name = np->name;
+ const char *parent_name;
+ struct regmap *regmap;
+ bool bypass;
+
+ of_property_read_string(np, "clock-output-names", &name);
+ bypass = of_property_read_bool(np, "atmel,osc-bypass");
+ parent_name = of_clk_get_parent_name(np, 0);
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ clk = at91_clk_register_main_osc(regmap, name, parent_name, bypass);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(at91rm9200_clk_main_osc, "atmel,at91rm9200-clk-main-osc",
+ of_at91rm9200_clk_main_osc_setup);
+
+static bool clk_main_rc_osc_ready(struct regmap *regmap)
+{
+ unsigned int status;
+
+ regmap_read(regmap, AT91_PMC_SR, &status);
+
+ return status & AT91_PMC_MOSCRCS;
+}
+
+static int clk_main_rc_osc_enable(struct clk *clk)
+{
+ struct clk_main_rc_osc *osc = to_clk_main_rc_osc(clk);
+ struct regmap *regmap = osc->regmap;
+ unsigned int mor;
+
+ regmap_read(regmap, AT91_CKGR_MOR, &mor);
+
+ if (!(mor & AT91_PMC_MOSCRCEN))
+ regmap_write_bits(regmap, AT91_CKGR_MOR,
+ MOR_KEY_MASK | AT91_PMC_MOSCRCEN,
+ AT91_PMC_MOSCRCEN | AT91_PMC_KEY);
+
+ while (!clk_main_rc_osc_ready(regmap))
+ barrier();
+
+ return 0;
+}
+
+static void clk_main_rc_osc_disable(struct clk *clk)
+{
+ struct clk_main_rc_osc *osc = to_clk_main_rc_osc(clk);
+ struct regmap *regmap = osc->regmap;
+ unsigned int mor;
+
+ regmap_read(regmap, AT91_CKGR_MOR, &mor);
+
+ if (!(mor & AT91_PMC_MOSCRCEN))
+ return;
+
+ regmap_write_bits(regmap, AT91_CKGR_MOR,
+ MOR_KEY_MASK | AT91_PMC_MOSCRCEN, AT91_PMC_KEY);
+}
+
+static int clk_main_rc_osc_is_enabled(struct clk *clk)
+{
+ struct clk_main_rc_osc *osc = to_clk_main_rc_osc(clk);
+ struct regmap *regmap = osc->regmap;
+ unsigned int mor, status;
+
+ regmap_read(regmap, AT91_CKGR_MOR, &mor);
+ regmap_read(regmap, AT91_PMC_SR, &status);
+
+ return (mor & AT91_PMC_MOSCRCEN) && (status & AT91_PMC_MOSCRCS);
+}
+
+static unsigned long clk_main_rc_osc_recalc_rate(struct clk *clk,
+ unsigned long parent_rate)
+{
+ struct clk_main_rc_osc *osc = to_clk_main_rc_osc(clk);
+
+ return osc->frequency;
+}
+
+static const struct clk_ops main_rc_osc_ops = {
+ .enable = clk_main_rc_osc_enable,
+ .disable = clk_main_rc_osc_disable,
+ .is_enabled = clk_main_rc_osc_is_enabled,
+ .recalc_rate = clk_main_rc_osc_recalc_rate,
+};
+
+static struct clk *
+at91_clk_register_main_rc_osc(struct regmap *regmap,
+ const char *name,
+ u32 frequency)
+{
+ int ret;
+ struct clk_main_rc_osc *osc;
+
+ if (!name || !frequency)
+ return ERR_PTR(-EINVAL);
+
+ osc = xzalloc(sizeof(*osc));
+
+ osc->clk.name = name;
+ osc->clk.ops = &main_rc_osc_ops;
+ osc->clk.parent_names = NULL;
+ osc->clk.num_parents = 0;
+
+ osc->regmap = regmap;
+ osc->frequency = frequency;
+
+ ret = clk_register(&osc->clk);
+ if (ret) {
+ kfree(osc);
+ return ERR_PTR(ret);
+ }
+
+ return &osc->clk;
+}
+
+static int of_at91sam9x5_clk_main_rc_osc_setup(struct device_node *np)
+{
+ struct clk *clk;
+ u32 frequency = 0;
+ const char *name = np->name;
+ struct regmap *regmap;
+
+ of_property_read_string(np, "clock-output-names", &name);
+ of_property_read_u32(np, "clock-frequency", &frequency);
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ clk = at91_clk_register_main_rc_osc(regmap, name, frequency);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(at91sam9x5_clk_main_rc_osc, "atmel,at91sam9x5-clk-main-rc-osc",
+ of_at91sam9x5_clk_main_rc_osc_setup);
+
+
+static int clk_main_probe_frequency(struct regmap *regmap)
+{
+ unsigned int mcfr;
+ uint64_t start = get_time_ns();
+
+ do {
+ regmap_read(regmap, AT91_CKGR_MCFR, &mcfr);
+ if (mcfr & AT91_PMC_MAINRDY)
+ return 0;
+ } while (!is_timeout(start, MAINFRDY_TIMEOUT * USECOND));
+
+ return -ETIMEDOUT;
+}
+
+static unsigned long clk_main_recalc_rate(struct regmap *regmap,
+ unsigned long parent_rate)
+{
+ unsigned int mcfr;
+
+ if (parent_rate)
+ return parent_rate;
+
+ pr_warn("Main crystal frequency not set, using approximate value\n");
+ regmap_read(regmap, AT91_CKGR_MCFR, &mcfr);
+ if (!(mcfr & AT91_PMC_MAINRDY))
+ return 0;
+
+ return ((mcfr & AT91_PMC_MAINF) * SLOW_CLOCK_FREQ) / MAINF_DIV;
+}
+
+static int clk_rm9200_main_enable(struct clk *clk)
+{
+ struct clk_rm9200_main *clkmain = to_clk_rm9200_main(clk);
+
+ return clk_main_probe_frequency(clkmain->regmap);
+}
+
+static int clk_rm9200_main_is_enabled(struct clk *clk)
+{
+ struct clk_rm9200_main *clkmain = to_clk_rm9200_main(clk);
+ unsigned int status;
+
+ regmap_read(clkmain->regmap, AT91_CKGR_MCFR, &status);
+
+ return status & AT91_PMC_MAINRDY ? 1 : 0;
+}
+
+static unsigned long clk_rm9200_main_recalc_rate(struct clk *clk,
+ unsigned long parent_rate)
+{
+ struct clk_rm9200_main *clkmain = to_clk_rm9200_main(clk);
+
+ return clk_main_recalc_rate(clkmain->regmap, parent_rate);
+}
+
+static const struct clk_ops rm9200_main_ops = {
+ .enable = clk_rm9200_main_enable,
+ .is_enabled = clk_rm9200_main_is_enabled,
+ .recalc_rate = clk_rm9200_main_recalc_rate,
+};
+
+static struct clk *
+at91_clk_register_rm9200_main(struct regmap *regmap,
+ const char *name,
+ const char *parent_name)
+{
+ int ret;
+ struct clk_rm9200_main *clkmain;
+
+ if (!name)
+ return ERR_PTR(-EINVAL);
+
+ if (!parent_name)
+ return ERR_PTR(-EINVAL);
+
+ clkmain = xzalloc(sizeof(*clkmain));
+
+ clkmain->clk.name = name;
+ clkmain->clk.ops = &rm9200_main_ops;
+ clkmain->clk.parent_names = &clkmain->parent;
+ clkmain->clk.num_parents = 1;
+ clkmain->regmap = regmap;
+
+ ret = clk_register(&clkmain->clk);
+ if (ret) {
+ kfree(clkmain);
+ return ERR_PTR(ret);
+ }
+
+ return &clkmain->clk;
+}
+
+static int of_at91rm9200_clk_main_setup(struct device_node *np)
+{
+ struct clk *clk;
+ const char *parent_name;
+ const char *name = np->name;
+ struct regmap *regmap;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+ of_property_read_string(np, "clock-output-names", &name);
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ clk = at91_clk_register_rm9200_main(regmap, name, parent_name);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(at91rm9200_clk_main, "atmel,at91rm9200-clk-main",
+ of_at91rm9200_clk_main_setup);
+
+static inline bool clk_sam9x5_main_ready(struct regmap *regmap)
+{
+ unsigned int status;
+
+ regmap_read(regmap, AT91_PMC_SR, &status);
+
+ return status & AT91_PMC_MOSCSELS ? 1 : 0;
+}
+
+static int clk_sam9x5_main_enable(struct clk *clk)
+{
+ struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk);
+ struct regmap *regmap = clkmain->regmap;
+
+ while (!clk_sam9x5_main_ready(regmap))
+ barrier();
+
+ return clk_main_probe_frequency(regmap);
+}
+
+static int clk_sam9x5_main_is_enabled(struct clk *clk)
+{
+ struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk);
+
+ return clk_sam9x5_main_ready(clkmain->regmap);
+}
+
+static unsigned long clk_sam9x5_main_recalc_rate(struct clk *clk,
+ unsigned long parent_rate)
+{
+ struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk);
+
+ return clk_main_recalc_rate(clkmain->regmap, parent_rate);
+}
+
+static int clk_sam9x5_main_set_parent(struct clk *clk, u8 index)
+{
+ struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk);
+ struct regmap *regmap = clkmain->regmap;
+ unsigned int tmp;
+
+ if (index > 1)
+ return -EINVAL;
+
+ regmap_read(regmap, AT91_CKGR_MOR, &tmp);
+ tmp &= ~MOR_KEY_MASK;
+
+ if (index && !(tmp & AT91_PMC_MOSCSEL))
+ regmap_write(regmap, AT91_CKGR_MOR, tmp | AT91_PMC_MOSCSEL);
+ else if (!index && (tmp & AT91_PMC_MOSCSEL))
+ regmap_write(regmap, AT91_CKGR_MOR, tmp & ~AT91_PMC_MOSCSEL);
+
+ while (!clk_sam9x5_main_ready(regmap))
+ barrier();
+
+ return 0;
+}
+
+static int clk_sam9x5_main_get_parent(struct clk *clk)
+{
+ struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk);
+ unsigned int status;
+
+ regmap_read(clkmain->regmap, AT91_CKGR_MOR, &status);
+
+ return status & AT91_PMC_MOSCEN ? 1 : 0;
+}
+
+static const struct clk_ops sam9x5_main_ops = {
+ .enable = clk_sam9x5_main_enable,
+ .is_enabled = clk_sam9x5_main_is_enabled,
+ .recalc_rate = clk_sam9x5_main_recalc_rate,
+ .set_parent = clk_sam9x5_main_set_parent,
+ .get_parent = clk_sam9x5_main_get_parent,
+};
+
+static struct clk *
+at91_clk_register_sam9x5_main(struct regmap *regmap,
+ const char *name,
+ const char **parent_names,
+ int num_parents)
+{
+ int ret;
+ unsigned int status;
+ size_t parents_array_size;
+ struct clk_sam9x5_main *clkmain;
+
+ if (!name)
+ return ERR_PTR(-EINVAL);
+
+ if (!parent_names || !num_parents)
+ return ERR_PTR(-EINVAL);
+
+ clkmain = xzalloc(sizeof(*clkmain));
+
+ clkmain->clk.name = name;
+ clkmain->clk.ops = &sam9x5_main_ops;
+ parents_array_size = num_parents * sizeof (clkmain->clk.parent_names[0]);
+ clkmain->clk.parent_names = xzalloc(parents_array_size);
+ memcpy(clkmain->clk.parent_names, parent_names, parents_array_size);
+ clkmain->clk.num_parents = num_parents;
+
+ /* init.flags = CLK_SET_PARENT_GATE; */
+
+ clkmain->regmap = regmap;
+ regmap_read(clkmain->regmap, AT91_CKGR_MOR, &status);
+ clkmain->parent = status & AT91_PMC_MOSCEN ? 1 : 0;
+
+ ret = clk_register(&clkmain->clk);
+ if (ret) {
+ kfree(clkmain);
+ return ERR_PTR(ret);
+ }
+
+ return &clkmain->clk;
+}
+
+static int of_at91sam9x5_clk_main_setup(struct device_node *np)
+{
+ struct clk *clk;
+ const char *parent_names[2];
+ unsigned int num_parents;
+ const char *name = np->name;
+ struct regmap *regmap;
+
+ num_parents = of_clk_get_parent_count(np);
+ if (num_parents == 0 || num_parents > 2)
+ return -EINVAL;
+
+ of_clk_parent_fill(np, parent_names, num_parents);
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ of_property_read_string(np, "clock-output-names", &name);
+
+ clk = at91_clk_register_sam9x5_main(regmap, name, parent_names,
+ num_parents);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(at91sam9x5_clk_main, "atmel,at91sam9x5-clk-main",
+ of_at91sam9x5_clk_main_setup);
diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c
new file mode 100644
index 0000000000..b3a50ce542
--- /dev/null
+++ b/drivers/clk/at91/clk-master.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+#include <common.h>
+#include <clock.h>
+#include <of.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
+#include <mfd/syscon.h>
+#include <regmap.h>
+
+#include "pmc.h"
+
+#define MASTER_SOURCE_MAX 4
+
+#define MASTER_PRES_MASK 0x7
+#define MASTER_PRES_MAX MASTER_PRES_MASK
+#define MASTER_DIV_SHIFT 8
+#define MASTER_DIV_MASK 0x3
+
+struct clk_master_characteristics {
+ struct clk_range output;
+ u32 divisors[4];
+ u8 have_div3_pres;
+};
+
+struct clk_master_layout {
+ u32 mask;
+ u8 pres_shift;
+};
+
+#define to_clk_master(clk) container_of(clk, struct clk_master, clk)
+
+struct clk_master {
+ struct clk clk;
+ struct regmap *regmap;
+ const struct clk_master_layout *layout;
+ const struct clk_master_characteristics *characteristics;
+ const char *parents[MASTER_SOURCE_MAX];
+};
+
+static inline bool clk_master_ready(struct regmap *regmap)
+{
+ unsigned int status;
+
+ regmap_read(regmap, AT91_PMC_SR, &status);
+
+ return status & AT91_PMC_MCKRDY ? 1 : 0;
+}
+
+static int clk_master_enable(struct clk *clk)
+{
+ struct clk_master *master = to_clk_master(clk);
+
+ while (!clk_master_ready(master->regmap))
+ barrier();
+
+ return 0;
+}
+
+static int clk_master_is_enabled(struct clk *clk)
+{
+ struct clk_master *master = to_clk_master(clk);
+
+ return clk_master_ready(master->regmap);
+}
+
+static unsigned long clk_master_recalc_rate(struct clk *clk,
+ unsigned long parent_rate)
+{
+ u8 pres;
+ u8 div;
+ unsigned long rate = parent_rate;
+ struct clk_master *master = to_clk_master(clk);
+ const struct clk_master_layout *layout = master->layout;
+ const struct clk_master_characteristics *characteristics =
+ master->characteristics;
+ unsigned int mckr;
+
+ regmap_read(master->regmap, AT91_PMC_MCKR, &mckr);
+ mckr &= layout->mask;
+
+ pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK;
+ div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
+
+ if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX)
+ rate /= 3;
+ else
+ rate >>= pres;
+
+ rate /= characteristics->divisors[div];
+
+ if (rate < characteristics->output.min)
+ pr_warn("master clk is underclocked");
+ else if (rate > characteristics->output.max)
+ pr_warn("master clk is overclocked");
+
+ return rate;
+}
+
+static int clk_master_get_parent(struct clk *clk)
+{
+ struct clk_master *master = to_clk_master(clk);
+ unsigned int mckr;
+
+ regmap_read(master->regmap, AT91_PMC_MCKR, &mckr);
+
+ return mckr & AT91_PMC_CSS;
+}
+
+static const struct clk_ops master_ops = {
+ .enable = clk_master_enable,
+ .is_enabled = clk_master_is_enabled,
+ .recalc_rate = clk_master_recalc_rate,
+ .get_parent = clk_master_get_parent,
+};
+
+static struct clk *
+at91_clk_register_master(struct regmap *regmap,
+ const char *name, int num_parents,
+ const char **parent_names,
+ const struct clk_master_layout *layout,
+ const struct clk_master_characteristics *characteristics)
+{
+ int ret;
+ const size_t parent_names_size = num_parents * sizeof(parent_names[0]);
+ struct clk_master *master;
+
+ if (!name || !num_parents || !parent_names)
+ return ERR_PTR(-EINVAL);
+
+ master = xzalloc(sizeof(*master));
+
+ master->clk.name = name;
+ master->clk.ops = &master_ops;
+ memcpy(master->parents, parent_names, parent_names_size);
+ master->clk.parent_names = master->parents;
+ master->clk.num_parents = num_parents;
+
+ master->layout = layout;
+ master->characteristics = characteristics;
+ master->regmap = regmap;
+
+ ret = clk_register(&master->clk);
+ if (ret) {
+ kfree(master);
+ return ERR_PTR(ret);
+ }
+
+ return &master->clk;
+}
+
+
+static const struct clk_master_layout at91rm9200_master_layout = {
+ .mask = 0x31F,
+ .pres_shift = 2,
+};
+
+static const struct clk_master_layout at91sam9x5_master_layout = {
+ .mask = 0x373,
+ .pres_shift = 4,
+};
+
+
+static struct clk_master_characteristics *
+of_at91_clk_master_get_characteristics(struct device_node *np)
+{
+ struct clk_master_characteristics *characteristics;
+
+ characteristics = xzalloc(sizeof(*characteristics));
+
+ if (of_at91_get_clk_range(np, "atmel,clk-output-range", &characteristics->output))
+ goto out_free_characteristics;
+
+ of_property_read_u32_array(np, "atmel,clk-divisors",
+ characteristics->divisors, 4);
+
+ characteristics->have_div3_pres =
+ of_property_read_bool(np, "atmel,master-clk-have-div3-pres");
+
+ return characteristics;
+
+out_free_characteristics:
+ kfree(characteristics);
+ return NULL;
+}
+
+static int
+of_at91_clk_master_setup(struct device_node *np,
+ const struct clk_master_layout *layout)
+{
+ struct clk *clk;
+ unsigned int num_parents;
+ const char *parent_names[MASTER_SOURCE_MAX];
+ const char *name = np->name;
+ struct clk_master_characteristics *characteristics;
+ struct regmap *regmap;
+
+ num_parents = of_clk_get_parent_count(np);
+ if (num_parents == 0 || num_parents > MASTER_SOURCE_MAX)
+ return -EINVAL;
+
+ of_clk_parent_fill(np, parent_names, num_parents);
+
+ of_property_read_string(np, "clock-output-names", &name);
+
+ characteristics = of_at91_clk_master_get_characteristics(np);
+ if (!characteristics)
+ return -EINVAL;
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ clk = at91_clk_register_master(regmap, name, num_parents,
+ parent_names, layout,
+ characteristics);
+ if (IS_ERR(clk)) {
+ kfree(characteristics);
+ return PTR_ERR(clk);
+ }
+
+ return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+
+static void __init of_at91rm9200_clk_master_setup(struct device_node *np)
+{
+ of_at91_clk_master_setup(np, &at91rm9200_master_layout);
+}
+CLK_OF_DECLARE(at91rm9200_clk_master, "atmel,at91rm9200-clk-master",
+ of_at91rm9200_clk_master_setup);
+
+static void __init of_at91sam9x5_clk_master_setup(struct device_node *np)
+{
+ of_at91_clk_master_setup(np, &at91sam9x5_master_layout);
+}
+CLK_OF_DECLARE(at91sam9x5_clk_master, "atmel,at91sam9x5-clk-master",
+ of_at91sam9x5_clk_master_setup);
diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c
new file mode 100644
index 0000000000..bbe6ffac69
--- /dev/null
+++ b/drivers/clk/at91/clk-peripheral.c
@@ -0,0 +1,429 @@
+/*
+ * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <common.h>
+#include <clock.h>
+#include <of.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
+#include <mfd/syscon.h>
+#include <regmap.h>
+
+#include "pmc.h"
+
+#define PERIPHERAL_MAX 64
+
+#define PERIPHERAL_AT91RM9200 0
+#define PERIPHERAL_AT91SAM9X5 1
+
+#define PERIPHERAL_ID_MIN 2
+#define PERIPHERAL_ID_MAX 31
+#define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX))
+
+#define PERIPHERAL_RSHIFT_MASK 0x3
+#define PERIPHERAL_RSHIFT(val) (((val) >> 16) & PERIPHERAL_RSHIFT_MASK)
+
+#define PERIPHERAL_MAX_SHIFT 3
+
+struct clk_peripheral {
+ struct clk clk;
+ struct regmap *regmap;
+ u32 id;
+ const char *parent;
+};
+
+#define to_clk_peripheral(clk) container_of(clk, struct clk_peripheral, clk)
+
+struct clk_sam9x5_peripheral {
+ struct clk clk;
+ struct regmap *regmap;
+ struct clk_range range;
+ u32 id;
+ u32 div;
+ bool auto_div;
+ const char *parent;
+};
+
+#define to_clk_sam9x5_peripheral(clk) \
+ container_of(clk, struct clk_sam9x5_peripheral, clk)
+
+static int clk_peripheral_enable(struct clk *clk)
+{
+ struct clk_peripheral *periph = to_clk_peripheral(clk);
+ int offset = AT91_PMC_PCER;
+ u32 id = periph->id;
+
+ if (id < PERIPHERAL_ID_MIN)
+ return 0;
+ if (id > PERIPHERAL_ID_MAX)
+ offset = AT91_PMC_PCER1;
+ regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id));
+
+ return 0;
+}
+
+static void clk_peripheral_disable(struct clk *clk)
+{
+ struct clk_peripheral *periph = to_clk_peripheral(clk);
+ int offset = AT91_PMC_PCDR;
+ u32 id = periph->id;
+
+ if (id < PERIPHERAL_ID_MIN)
+ return;
+ if (id > PERIPHERAL_ID_MAX)
+ offset = AT91_PMC_PCDR1;
+ regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id));
+}
+
+static int clk_peripheral_is_enabled(struct clk *clk)
+{
+ struct clk_peripheral *periph = to_clk_peripheral(clk);
+ int offset = AT91_PMC_PCSR;
+ unsigned int status;
+ u32 id = periph->id;
+
+ if (id < PERIPHERAL_ID_MIN)
+ return 1;
+ if (id > PERIPHERAL_ID_MAX)
+ offset = AT91_PMC_PCSR1;
+ regmap_read(periph->regmap, offset, &status);
+
+ return status & PERIPHERAL_MASK(id) ? 1 : 0;
+}
+
+static const struct clk_ops peripheral_ops = {
+ .enable = clk_peripheral_enable,
+ .disable = clk_peripheral_disable,
+ .is_enabled = clk_peripheral_is_enabled,
+};
+
+static struct clk *
+at91_clk_register_peripheral(struct regmap *regmap, const char *name,
+ const char *parent_name, u32 id)
+{
+ int ret;
+ struct clk_peripheral *periph;
+
+ if (!name || !parent_name || id > PERIPHERAL_ID_MAX)
+ return ERR_PTR(-EINVAL);
+
+ periph = xzalloc(sizeof(*periph));
+
+ periph->clk.name = name;
+ periph->clk.ops = &peripheral_ops;
+
+ if (parent_name) {
+ periph->parent = parent_name;
+ periph->clk.parent_names = &periph->parent;
+ periph->clk.num_parents = 1;
+ }
+
+ periph->id = id;
+ periph->regmap = regmap;
+
+ ret = clk_register(&periph->clk);
+ if (ret) {
+ kfree(periph);
+ return ERR_PTR(ret);
+ }
+
+ return &periph->clk;
+}
+
+static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph)
+{
+ struct clk *parent;
+ unsigned long parent_rate;
+ int shift = 0;
+
+ if (!periph->auto_div)
+ return;
+
+ if (periph->range.max) {
+ parent = clk_get_parent(&periph->clk);
+ parent_rate = clk_get_rate(parent);
+ if (!parent_rate)
+ return;
+
+ for (; shift < PERIPHERAL_MAX_SHIFT; shift++) {
+ if (parent_rate >> shift <= periph->range.max)
+ break;
+ }
+ }
+
+ periph->auto_div = false;
+ periph->div = shift;
+}
+
+static int clk_sam9x5_peripheral_enable(struct clk *clk)
+{
+ struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
+
+ if (periph->id < PERIPHERAL_ID_MIN)
+ return 0;
+
+ regmap_write(periph->regmap, AT91_PMC_PCR,
+ (periph->id & AT91_PMC_PCR_PID_MASK));
+ regmap_write_bits(periph->regmap, AT91_PMC_PCR,
+ AT91_PMC_PCR_DIV_MASK | AT91_PMC_PCR_CMD |
+ AT91_PMC_PCR_EN,
+ AT91_PMC_PCR_DIV(periph->div) |
+ AT91_PMC_PCR_CMD |
+ AT91_PMC_PCR_EN);
+
+ return 0;
+}
+
+static void clk_sam9x5_peripheral_disable(struct clk *clk)
+{
+ struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
+
+ if (periph->id < PERIPHERAL_ID_MIN)
+ return;
+
+ regmap_write(periph->regmap, AT91_PMC_PCR,
+ (periph->id & AT91_PMC_PCR_PID_MASK));
+ regmap_write_bits(periph->regmap, AT91_PMC_PCR,
+ AT91_PMC_PCR_EN | AT91_PMC_PCR_CMD,
+ AT91_PMC_PCR_CMD);
+}
+
+static int clk_sam9x5_peripheral_is_enabled(struct clk *clk)
+{
+ struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
+ unsigned int status;
+
+ if (periph->id < PERIPHERAL_ID_MIN)
+ return 1;
+
+ regmap_write(periph->regmap, AT91_PMC_PCR,
+ (periph->id & AT91_PMC_PCR_PID_MASK));
+ regmap_read(periph->regmap, AT91_PMC_PCR, &status);
+
+ return status & AT91_PMC_PCR_EN ? 1 : 0;
+}
+
+static unsigned long
+clk_sam9x5_peripheral_recalc_rate(struct clk *clk,
+ unsigned long parent_rate)
+{
+ struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
+ unsigned int status;
+
+ if (periph->id < PERIPHERAL_ID_MIN)
+ return parent_rate;
+
+ regmap_write(periph->regmap, AT91_PMC_PCR,
+ (periph->id & AT91_PMC_PCR_PID_MASK));
+ regmap_read(periph->regmap, AT91_PMC_PCR, &status);
+
+ if (status & AT91_PMC_PCR_EN) {
+ periph->div = PERIPHERAL_RSHIFT(status);
+ periph->auto_div = false;
+ } else {
+ clk_sam9x5_peripheral_autodiv(periph);
+ }
+
+ return parent_rate >> periph->div;
+}
+
+static long clk_sam9x5_peripheral_round_rate(struct clk *clk,
+ unsigned long rate,
+ unsigned long *parent_rate)
+{
+ int shift = 0;
+ unsigned long best_rate;
+ unsigned long best_diff;
+ unsigned long cur_rate = *parent_rate;
+ unsigned long cur_diff;
+ struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
+
+ if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max)
+ return *parent_rate;
+
+ if (periph->range.max) {
+ for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
+ cur_rate = *parent_rate >> shift;
+ if (cur_rate <= periph->range.max)
+ break;
+ }
+ }
+
+ if (rate >= cur_rate)
+ return cur_rate;
+
+ best_diff = cur_rate - rate;
+ best_rate = cur_rate;
+ for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
+ cur_rate = *parent_rate >> shift;
+ if (cur_rate < rate)
+ cur_diff = rate - cur_rate;
+ else
+ cur_diff = cur_rate - rate;
+
+ if (cur_diff < best_diff) {
+ best_diff = cur_diff;
+ best_rate = cur_rate;
+ }
+
+ if (!best_diff || cur_rate < rate)
+ break;
+ }
+
+ return best_rate;
+}
+
+static int clk_sam9x5_peripheral_set_rate(struct clk *clk,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ int shift;
+ struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
+ if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) {
+ if (parent_rate == rate)
+ return 0;
+ else
+ return -EINVAL;
+ }
+
+ if (periph->range.max && rate > periph->range.max)
+ return -EINVAL;
+
+ for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
+ if (parent_rate >> shift == rate) {
+ periph->auto_div = false;
+ periph->div = shift;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static const struct clk_ops sam9x5_peripheral_ops = {
+ .enable = clk_sam9x5_peripheral_enable,
+ .disable = clk_sam9x5_peripheral_disable,
+ .is_enabled = clk_sam9x5_peripheral_is_enabled,
+ .recalc_rate = clk_sam9x5_peripheral_recalc_rate,
+ .round_rate = clk_sam9x5_peripheral_round_rate,
+ .set_rate = clk_sam9x5_peripheral_set_rate,
+};
+
+static struct clk *
+at91_clk_register_sam9x5_peripheral(struct regmap *regmap,
+ const char *name, const char *parent_name,
+ u32 id, const struct clk_range *range)
+{
+ int ret;
+ struct clk_sam9x5_peripheral *periph;
+
+ if (!name || !parent_name)
+ return ERR_PTR(-EINVAL);
+
+ periph = xzalloc(sizeof(*periph));
+
+ periph->clk.name = name;
+ periph->clk.ops = &sam9x5_peripheral_ops;
+
+ if (parent_name) {
+ periph->parent = parent_name;
+ periph->clk.parent_names = &periph->parent;
+ periph->clk.num_parents = 1;
+ }
+
+ periph->id = id;
+ periph->div = 0;
+ periph->regmap = regmap;
+ periph->auto_div = true;
+ periph->range = *range;
+
+ ret = clk_register(&periph->clk);
+ if (ret) {
+ kfree(periph);
+ return ERR_PTR(ret);
+ }
+
+ clk_sam9x5_peripheral_autodiv(periph);
+
+ return &periph->clk;
+}
+
+static int
+of_at91_clk_periph_setup(struct device_node *np, u8 type)
+{
+ int num;
+ u32 id;
+ struct clk *clk;
+ const char *parent_name;
+ const char *name;
+ struct device_node *periphclknp;
+ struct regmap *regmap;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+ if (!parent_name)
+ return -ENOENT;
+
+ num = of_get_child_count(np);
+ if (!num || num > PERIPHERAL_MAX)
+ return -EINVAL;
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ for_each_child_of_node(np, periphclknp) {
+ if (of_property_read_u32(periphclknp, "reg", &id))
+ continue;
+
+ if (id >= PERIPHERAL_MAX)
+ continue;
+
+ if (of_property_read_string(np, "clock-output-names", &name))
+ name = periphclknp->name;
+
+ if (type == PERIPHERAL_AT91RM9200) {
+ clk = at91_clk_register_peripheral(regmap, name,
+ parent_name, id);
+ } else {
+ struct clk_range range = CLK_RANGE(0, 0);
+
+ of_at91_get_clk_range(periphclknp,
+ "atmel,clk-output-range",
+ &range);
+
+ clk = at91_clk_register_sam9x5_peripheral(regmap,
+ name,
+ parent_name,
+ id, &range);
+ }
+
+ if (IS_ERR(clk))
+ continue;
+
+ of_clk_add_provider(periphclknp, of_clk_src_simple_get, clk);
+ }
+
+ return 0;
+}
+
+static int of_at91rm9200_clk_periph_setup(struct device_node *np)
+{
+ return of_at91_clk_periph_setup(np, PERIPHERAL_AT91RM9200);
+}
+CLK_OF_DECLARE(at91rm9200_clk_periph, "atmel,at91rm9200-clk-peripheral",
+ of_at91rm9200_clk_periph_setup);
+
+static int of_at91sam9x5_clk_periph_setup(struct device_node *np)
+{
+ return of_at91_clk_periph_setup(np, PERIPHERAL_AT91SAM9X5);
+}
+CLK_OF_DECLARE(at91sam9x5_clk_periph, "atmel,at91sam9x5-clk-peripheral",
+ of_at91sam9x5_clk_periph_setup);
diff --git a/drivers/clk/at91/clk-pll.c b/drivers/clk/at91/clk-pll.c
new file mode 100644
index 0000000000..e0af4fe5a8
--- /dev/null
+++ b/drivers/clk/at91/clk-pll.c
@@ -0,0 +1,516 @@
+/*
+ * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <common.h>
+#include <clock.h>
+#include <of.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
+#include <mfd/syscon.h>
+#include <regmap.h>
+
+#include "pmc.h"
+
+#define PLL_STATUS_MASK(id) (1 << (1 + (id)))
+#define PLL_REG(id) (AT91_CKGR_PLLAR + ((id) * 4))
+#define PLL_DIV_MASK 0xff
+#define PLL_DIV_MAX PLL_DIV_MASK
+#define PLL_DIV(reg) ((reg) & PLL_DIV_MASK)
+#define PLL_MUL(reg, layout) (((reg) >> (layout)->mul_shift) & \
+ (layout)->mul_mask)
+#define PLL_MUL_MIN 2
+#define PLL_MUL_MASK(layout) ((layout)->mul_mask)
+#define PLL_MUL_MAX(layout) (PLL_MUL_MASK(layout) + 1)
+#define PLL_ICPR_SHIFT(id) ((id) * 16)
+#define PLL_ICPR_MASK(id) (0xffff << PLL_ICPR_SHIFT(id))
+#define PLL_MAX_COUNT 0x3f
+#define PLL_COUNT_SHIFT 8
+#define PLL_OUT_SHIFT 14
+#define PLL_MAX_ID 1
+
+struct clk_pll_characteristics {
+ struct clk_range input;
+ int num_output;
+ struct clk_range *output;
+ u16 *icpll;
+ u8 *out;
+};
+
+struct clk_pll_layout {
+ u32 pllr_mask;
+ u16 mul_mask;
+ u8 mul_shift;
+};
+
+#define to_clk_pll(clk) container_of(clk, struct clk_pll, clk)
+
+struct clk_pll {
+ struct clk clk;
+ struct regmap *regmap;
+ u8 id;
+ u8 div;
+ u8 range;
+ u16 mul;
+ const struct clk_pll_layout *layout;
+ const struct clk_pll_characteristics *characteristics;
+ const char *parent;
+};
+
+static inline bool clk_pll_ready(struct regmap *regmap, int id)
+{
+ unsigned int status;
+
+ regmap_read(regmap, AT91_PMC_SR, &status);
+
+ return status & PLL_STATUS_MASK(id) ? 1 : 0;
+}
+
+static int clk_pll_enable(struct clk *clk)
+{
+ struct clk_pll *pll = to_clk_pll(clk);
+ struct regmap *regmap = pll->regmap;
+ const struct clk_pll_layout *layout = pll->layout;
+ const struct clk_pll_characteristics *characteristics =
+ pll->characteristics;
+ u8 id = pll->id;
+ u32 mask = PLL_STATUS_MASK(id);
+ int offset = PLL_REG(id);
+ u8 out = 0;
+ unsigned int pllr;
+ unsigned int status;
+ u8 div;
+ u16 mul;
+
+ regmap_read(regmap, offset, &pllr);
+ div = PLL_DIV(pllr);
+ mul = PLL_MUL(pllr, layout);
+
+ regmap_read(regmap, AT91_PMC_SR, &status);
+ if ((status & mask) &&
+ (div == pll->div && mul == pll->mul))
+ return 0;
+
+ if (characteristics->out)
+ out = characteristics->out[pll->range];
+
+ if (characteristics->icpll)
+ regmap_write_bits(regmap, AT91_PMC_PLLICPR, PLL_ICPR_MASK(id),
+ characteristics->icpll[pll->range] << PLL_ICPR_SHIFT(id));
+
+ regmap_write_bits(regmap, offset, layout->pllr_mask,
+ pll->div | (PLL_MAX_COUNT << PLL_COUNT_SHIFT) |
+ (out << PLL_OUT_SHIFT) |
+ ((pll->mul & layout->mul_mask) << layout->mul_shift));
+
+ while (!clk_pll_ready(regmap, pll->id))
+ barrier();
+
+ return 0;
+}
+
+static int clk_pll_is_enabled(struct clk *clk)
+{
+ struct clk_pll *pll = to_clk_pll(clk);
+
+ return clk_pll_ready(pll->regmap, pll->id);
+}
+
+static void clk_pll_disable(struct clk *clk)
+{
+ struct clk_pll *pll = to_clk_pll(clk);
+ unsigned int mask = pll->layout->pllr_mask;
+
+ regmap_write_bits(pll->regmap, PLL_REG(pll->id), mask, ~mask);
+}
+
+static unsigned long clk_pll_recalc_rate(struct clk *clk,
+ unsigned long parent_rate)
+{
+ struct clk_pll *pll = to_clk_pll(clk);
+ unsigned int pllr;
+ u16 mul;
+ u8 div;
+
+ regmap_read(pll->regmap, PLL_REG(pll->id), &pllr);
+
+ div = PLL_DIV(pllr);
+ mul = PLL_MUL(pllr, pll->layout);
+
+ if (!div || !mul)
+ return 0;
+
+ return (parent_rate / div) * (mul + 1);
+}
+
+static long clk_pll_get_best_div_mul(struct clk_pll *pll, unsigned long rate,
+ unsigned long parent_rate,
+ u32 *div, u32 *mul,
+ u32 *index) {
+ const struct clk_pll_layout *layout = pll->layout;
+ const struct clk_pll_characteristics *characteristics =
+ pll->characteristics;
+ unsigned long bestremainder = ULONG_MAX;
+ unsigned long maxdiv, mindiv, tmpdiv;
+ long bestrate = -ERANGE;
+ unsigned long bestdiv;
+ unsigned long bestmul;
+ int i = 0;
+
+ /* Check if parent_rate is a valid input rate */
+ if (parent_rate < characteristics->input.min)
+ return -ERANGE;
+
+ /*
+ * Calculate minimum divider based on the minimum multiplier, the
+ * parent_rate and the requested rate.
+ * Should always be 2 according to the input and output characteristics
+ * of the PLL blocks.
+ */
+ mindiv = (parent_rate * PLL_MUL_MIN) / rate;
+ if (!mindiv)
+ mindiv = 1;
+
+ if (parent_rate > characteristics->input.max) {
+ tmpdiv = DIV_ROUND_UP(parent_rate, characteristics->input.max);
+ if (tmpdiv > PLL_DIV_MAX)
+ return -ERANGE;
+
+ if (tmpdiv > mindiv)
+ mindiv = tmpdiv;
+ }
+
+ /*
+ * Calculate the maximum divider which is limited by PLL register
+ * layout (limited by the MUL or DIV field size).
+ */
+ maxdiv = DIV_ROUND_UP(parent_rate * PLL_MUL_MAX(layout), rate);
+ if (maxdiv > PLL_DIV_MAX)
+ maxdiv = PLL_DIV_MAX;
+
+ /*
+ * Iterate over the acceptable divider values to find the best
+ * divider/multiplier pair (the one that generates the closest
+ * rate to the requested one).
+ */
+ for (tmpdiv = mindiv; tmpdiv <= maxdiv; tmpdiv++) {
+ unsigned long remainder;
+ unsigned long tmprate;
+ unsigned long tmpmul;
+
+ /*
+ * Calculate the multiplier associated with the current
+ * divider that provide the closest rate to the requested one.
+ */
+ tmpmul = DIV_ROUND_CLOSEST(rate, parent_rate / tmpdiv);
+ tmprate = (parent_rate / tmpdiv) * tmpmul;
+ if (tmprate > rate)
+ remainder = tmprate - rate;
+ else
+ remainder = rate - tmprate;
+
+ /*
+ * Compare the remainder with the best remainder found until
+ * now and elect a new best multiplier/divider pair if the
+ * current remainder is smaller than the best one.
+ */
+ if (remainder < bestremainder) {
+ bestremainder = remainder;
+ bestdiv = tmpdiv;
+ bestmul = tmpmul;
+ bestrate = tmprate;
+ }
+
+ /*
+ * We've found a perfect match!
+ * Stop searching now and use this multiplier/divider pair.
+ */
+ if (!remainder)
+ break;
+ }
+
+ /* We haven't found any multiplier/divider pair => return -ERANGE */
+ if (bestrate < 0)
+ return bestrate;
+
+ /* Check if bestrate is a valid output rate */
+ for (i = 0; i < characteristics->num_output; i++) {
+ if (bestrate >= characteristics->output[i].min &&
+ bestrate <= characteristics->output[i].max)
+ break;
+ }
+
+ if (i >= characteristics->num_output)
+ return -ERANGE;
+
+ if (div)
+ *div = bestdiv;
+ if (mul)
+ *mul = bestmul - 1;
+ if (index)
+ *index = i;
+
+ return bestrate;
+}
+
+static long clk_pll_round_rate(struct clk *clk, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct clk_pll *pll = to_clk_pll(clk);
+
+ return clk_pll_get_best_div_mul(pll, rate, *parent_rate,
+ NULL, NULL, NULL);
+}
+
+static int clk_pll_set_rate(struct clk *clk, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_pll *pll = to_clk_pll(clk);
+ long ret;
+ u32 div;
+ u32 mul;
+ u32 index;
+
+ ret = clk_pll_get_best_div_mul(pll, rate, parent_rate,
+ &div, &mul, &index);
+ if (ret < 0)
+ return ret;
+
+ pll->range = index;
+ pll->div = div;
+ pll->mul = mul;
+
+ return 0;
+}
+
+static const struct clk_ops pll_ops = {
+ .enable = clk_pll_enable,
+ .disable = clk_pll_disable,
+ .is_enabled = clk_pll_is_enabled,
+ .recalc_rate = clk_pll_recalc_rate,
+ .round_rate = clk_pll_round_rate,
+ .set_rate = clk_pll_set_rate,
+};
+
+static struct clk *
+at91_clk_register_pll(struct regmap *regmap, const char *name,
+ const char *parent_name, u8 id,
+ const struct clk_pll_layout *layout,
+ const struct clk_pll_characteristics *characteristics)
+{
+ struct clk_pll *pll;
+ int offset = PLL_REG(id);
+ unsigned int pllr;
+ int ret;
+
+ if (id > PLL_MAX_ID)
+ return ERR_PTR(-EINVAL);
+
+ pll = xzalloc(sizeof(*pll));
+
+ pll->parent = parent_name;
+ pll->clk.name = name;
+ pll->clk.ops = &pll_ops;
+ pll->clk.parent_names = &pll->parent;
+ pll->clk.num_parents = 1;
+
+ /* init.flags = CLK_SET_RATE_GATE; */
+
+ pll->id = id;
+ pll->layout = layout;
+ pll->characteristics = characteristics;
+ pll->regmap = regmap;
+ regmap_read(regmap, offset, &pllr);
+ pll->div = PLL_DIV(pllr);
+ pll->mul = PLL_MUL(pllr, layout);
+
+ ret = clk_register(&pll->clk);
+ if (ret) {
+ kfree(pll);
+ return ERR_PTR(ret);
+ }
+
+ return &pll->clk;
+}
+
+
+static const struct clk_pll_layout at91rm9200_pll_layout = {
+ .pllr_mask = 0x7FFFFFF,
+ .mul_shift = 16,
+ .mul_mask = 0x7FF,
+};
+
+static const struct clk_pll_layout at91sam9g45_pll_layout = {
+ .pllr_mask = 0xFFFFFF,
+ .mul_shift = 16,
+ .mul_mask = 0xFF,
+};
+
+static const struct clk_pll_layout at91sam9g20_pllb_layout = {
+ .pllr_mask = 0x3FFFFF,
+ .mul_shift = 16,
+ .mul_mask = 0x3F,
+};
+
+static const struct clk_pll_layout sama5d3_pll_layout = {
+ .pllr_mask = 0x1FFFFFF,
+ .mul_shift = 18,
+ .mul_mask = 0x7F,
+};
+
+
+static struct clk_pll_characteristics *
+of_at91_clk_pll_get_characteristics(struct device_node *np)
+{
+ int i;
+ int offset;
+ u32 tmp;
+ int num_output;
+ u32 num_cells;
+ struct clk_range input;
+ struct clk_range *output;
+ u8 *out = NULL;
+ u16 *icpll = NULL;
+ struct clk_pll_characteristics *characteristics;
+
+ if (of_at91_get_clk_range(np, "atmel,clk-input-range", &input))
+ return NULL;
+
+ if (of_property_read_u32(np, "#atmel,pll-clk-output-range-cells",
+ &num_cells))
+ return NULL;
+
+ if (num_cells < 2 || num_cells > 4)
+ return NULL;
+
+ if (!of_get_property(np, "atmel,pll-clk-output-ranges", &tmp))
+ return NULL;
+ num_output = tmp / (sizeof(u32) * num_cells);
+
+ characteristics = xzalloc(sizeof(*characteristics));
+ output = xzalloc(sizeof(*output) * num_output);
+
+ if (num_cells > 2)
+ out = xzalloc(sizeof(*out) * num_output);
+
+ if (num_cells > 3)
+ icpll = xzalloc(sizeof(*icpll) * num_output);
+
+
+ for (i = 0; i < num_output; i++) {
+ offset = i * num_cells;
+ if (of_property_read_u32_index(np,
+ "atmel,pll-clk-output-ranges",
+ offset, &tmp))
+ goto out_free_output;
+ output[i].min = tmp;
+ if (of_property_read_u32_index(np,
+ "atmel,pll-clk-output-ranges",
+ offset + 1, &tmp))
+ goto out_free_output;
+ output[i].max = tmp;
+
+ if (num_cells == 2)
+ continue;
+
+ if (of_property_read_u32_index(np,
+ "atmel,pll-clk-output-ranges",
+ offset + 2, &tmp))
+ goto out_free_output;
+ out[i] = tmp;
+
+ if (num_cells == 3)
+ continue;
+
+ if (of_property_read_u32_index(np,
+ "atmel,pll-clk-output-ranges",
+ offset + 3, &tmp))
+ goto out_free_output;
+ icpll[i] = tmp;
+ }
+
+ characteristics->input = input;
+ characteristics->num_output = num_output;
+ characteristics->output = output;
+ characteristics->out = out;
+ characteristics->icpll = icpll;
+ return characteristics;
+
+out_free_output:
+ kfree(icpll);
+ kfree(out);
+ kfree(output);
+ kfree(characteristics);
+ return NULL;
+}
+
+static int
+of_at91_clk_pll_setup(struct device_node *np,
+ const struct clk_pll_layout *layout)
+{
+ u32 id;
+ struct clk *clk;
+ struct regmap *regmap;
+ const char *parent_name;
+ const char *name = np->name;
+ struct clk_pll_characteristics *characteristics;
+
+ if (of_property_read_u32(np, "reg", &id))
+ return -EINVAL;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+
+ of_property_read_string(np, "clock-output-names", &name);
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ characteristics = of_at91_clk_pll_get_characteristics(np);
+ if (!characteristics)
+ return -EINVAL;
+
+ clk = at91_clk_register_pll(regmap, name, parent_name, id, layout,
+ characteristics);
+ if (IS_ERR(clk)) {
+ kfree(characteristics);
+ return PTR_ERR(clk);
+ }
+
+ return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+
+static int of_at91rm9200_clk_pll_setup(struct device_node *np)
+{
+ return of_at91_clk_pll_setup(np, &at91rm9200_pll_layout);
+}
+CLK_OF_DECLARE(at91rm9200_clk_pll, "atmel,at91rm9200-clk-pll",
+ of_at91rm9200_clk_pll_setup);
+
+static int of_at91sam9g45_clk_pll_setup(struct device_node *np)
+{
+ return of_at91_clk_pll_setup(np, &at91sam9g45_pll_layout);
+}
+CLK_OF_DECLARE(at91sam9g45_clk_pll, "atmel,at91sam9g45-clk-pll",
+ of_at91sam9g45_clk_pll_setup);
+
+static int of_at91sam9g20_clk_pllb_setup(struct device_node *np)
+{
+ return of_at91_clk_pll_setup(np, &at91sam9g20_pllb_layout);
+}
+CLK_OF_DECLARE(at91sam9g20_clk_pllb, "atmel,at91sam9g20-clk-pllb",
+ of_at91sam9g20_clk_pllb_setup);
+
+static int of_sama5d3_clk_pll_setup(struct device_node *np)
+{
+ return of_at91_clk_pll_setup(np, &sama5d3_pll_layout);
+}
+CLK_OF_DECLARE(sama5d3_clk_pll, "atmel,sama5d3-clk-pll",
+ of_sama5d3_clk_pll_setup);
diff --git a/drivers/clk/at91/clk-plldiv.c b/drivers/clk/at91/clk-plldiv.c
new file mode 100644
index 0000000000..917108e84c
--- /dev/null
+++ b/drivers/clk/at91/clk-plldiv.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <common.h>
+#include <clock.h>
+#include <of.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
+#include <mfd/syscon.h>
+#include <regmap.h>
+
+#include "pmc.h"
+
+#define to_clk_plldiv(hw) container_of(clk, struct clk_plldiv, clk)
+
+struct clk_plldiv {
+ struct clk clk;
+ struct regmap *regmap;
+ const char *parent;
+};
+
+static unsigned long clk_plldiv_recalc_rate(struct clk *clk,
+ unsigned long parent_rate)
+{
+ struct clk_plldiv *plldiv = to_clk_plldiv(clk);
+ unsigned int mckr;
+
+ regmap_read(plldiv->regmap, AT91_PMC_MCKR, &mckr);
+
+ if (mckr & AT91_PMC_PLLADIV2)
+ return parent_rate / 2;
+
+ return parent_rate;
+}
+
+static long clk_plldiv_round_rate(struct clk *clk, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ unsigned long div;
+
+ if (rate > *parent_rate)
+ return *parent_rate;
+ div = *parent_rate / 2;
+ if (rate < div)
+ return div;
+
+ if (rate - div < *parent_rate - rate)
+ return div;
+
+ return *parent_rate;
+}
+
+static int clk_plldiv_set_rate(struct clk *clk, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_plldiv *plldiv = to_clk_plldiv(clk);
+
+ if ((parent_rate != rate) && (parent_rate / 2 != rate))
+ return -EINVAL;
+
+ regmap_write_bits(plldiv->regmap, AT91_PMC_MCKR, AT91_PMC_PLLADIV2,
+ parent_rate != rate ? AT91_PMC_PLLADIV2 : 0);
+
+ return 0;
+}
+
+static const struct clk_ops plldiv_ops = {
+ .recalc_rate = clk_plldiv_recalc_rate,
+ .round_rate = clk_plldiv_round_rate,
+ .set_rate = clk_plldiv_set_rate,
+};
+
+static struct clk *
+at91_clk_register_plldiv(struct regmap *regmap, const char *name,
+ const char *parent_name)
+{
+ int ret;
+ struct clk_plldiv *plldiv;
+
+ plldiv = xzalloc(sizeof(*plldiv));
+
+ plldiv->clk.name = name;
+ plldiv->clk.ops = &plldiv_ops;
+
+ if (parent_name) {
+ plldiv->parent = parent_name;
+ plldiv->clk.parent_names = &plldiv->parent;
+ plldiv->clk.num_parents = 1;
+ }
+
+ /* init.flags = CLK_SET_RATE_GATE; */
+
+ plldiv->regmap = regmap;
+
+ ret = clk_register(&plldiv->clk);
+ if (ret) {
+ kfree(plldiv);
+ return ERR_PTR(ret);
+ }
+
+ return &plldiv->clk;
+}
+
+static int
+of_at91sam9x5_clk_plldiv_setup(struct device_node *np)
+{
+ struct clk *clk;
+ const char *parent_name;
+ const char *name = np->name;
+ struct regmap *regmap;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+
+ of_property_read_string(np, "clock-output-names", &name);
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ clk = at91_clk_register_plldiv(regmap, name, parent_name);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(at91sam9x5_clk_plldiv, "atmel,at91sam9x5-clk-plldiv",
+ of_at91sam9x5_clk_plldiv_setup);
diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c
new file mode 100644
index 0000000000..ddb18c0f7c
--- /dev/null
+++ b/drivers/clk/at91/clk-programmable.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <common.h>
+#include <clock.h>
+#include <of.h>
+#include <io.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
+#include <mfd/syscon.h>
+#include <regmap.h>
+
+#include "pmc.h"
+
+#define PROG_SOURCE_MAX 5
+#define PROG_ID_MAX 7
+
+#define PROG_STATUS_MASK(id) (1 << ((id) + 8))
+#define PROG_PRES_MASK 0x7
+#define PROG_PRES(layout, pckr) ((pckr >> layout->pres_shift) & PROG_PRES_MASK)
+#define PROG_MAX_RM9200_CSS 3
+
+struct clk_programmable_layout {
+ u8 pres_shift;
+ u8 css_mask;
+ u8 have_slck_mck;
+};
+
+struct clk_programmable {
+ struct clk clk;
+ struct regmap *regmap;
+ u8 id;
+ const struct clk_programmable_layout *layout;
+ const char *parent_names[PROG_SOURCE_MAX];
+};
+
+#define to_clk_programmable(clk) container_of(clk, struct clk_programmable, clk)
+
+static unsigned long clk_programmable_recalc_rate(struct clk *clk,
+ unsigned long parent_rate)
+{
+ struct clk_programmable *prog = to_clk_programmable(clk);
+ unsigned int pckr;
+
+ regmap_read(prog->regmap, AT91_PMC_PCKR(prog->id), &pckr);
+
+ return parent_rate >> PROG_PRES(prog->layout, pckr);
+}
+
+static int clk_programmable_set_parent(struct clk *clk, u8 index)
+{
+ struct clk_programmable *prog = to_clk_programmable(clk);
+ const struct clk_programmable_layout *layout = prog->layout;
+ unsigned int mask = layout->css_mask;
+ unsigned int pckr = index;
+
+ if (layout->have_slck_mck)
+ mask |= AT91_PMC_CSSMCK_MCK;
+
+ if (index > layout->css_mask) {
+ if (index > PROG_MAX_RM9200_CSS && !layout->have_slck_mck)
+ return -EINVAL;
+
+ pckr |= AT91_PMC_CSSMCK_MCK;
+ }
+
+ regmap_write_bits(prog->regmap, AT91_PMC_PCKR(prog->id), mask, pckr);
+
+ return 0;
+}
+
+static int clk_programmable_get_parent(struct clk *clk)
+{
+ struct clk_programmable *prog = to_clk_programmable(clk);
+ const struct clk_programmable_layout *layout = prog->layout;
+ unsigned int pckr;
+ u8 ret;
+
+ regmap_read(prog->regmap, AT91_PMC_PCKR(prog->id), &pckr);
+
+ ret = pckr & layout->css_mask;
+
+ if (layout->have_slck_mck && (pckr & AT91_PMC_CSSMCK_MCK) && !ret)
+ ret = PROG_MAX_RM9200_CSS + 1;
+
+ return ret;
+}
+
+static int clk_programmable_set_rate(struct clk *clk, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_programmable *prog = to_clk_programmable(clk);
+ const struct clk_programmable_layout *layout = prog->layout;
+ unsigned long div = parent_rate / rate;
+ unsigned int pckr;
+ int shift = 0;
+
+ regmap_read(prog->regmap, AT91_PMC_PCKR(prog->id), &pckr);
+
+ if (!div)
+ return -EINVAL;
+
+ shift = fls(div) - 1;
+
+ if (div != (1 << shift))
+ return -EINVAL;
+
+ if (shift >= PROG_PRES_MASK)
+ return -EINVAL;
+
+ regmap_write_bits(prog->regmap, AT91_PMC_PCKR(prog->id),
+ PROG_PRES_MASK << layout->pres_shift,
+ shift << layout->pres_shift);
+
+ return 0;
+}
+
+static const struct clk_ops programmable_ops = {
+ .recalc_rate = clk_programmable_recalc_rate,
+ .get_parent = clk_programmable_get_parent,
+ .set_parent = clk_programmable_set_parent,
+ .set_rate = clk_programmable_set_rate,
+};
+
+static struct clk *
+at91_clk_register_programmable(struct regmap *regmap,
+ const char *name, const char **parent_names,
+ u8 num_parents, u8 id,
+ const struct clk_programmable_layout *layout)
+{
+ struct clk_programmable *prog;
+ int ret;
+
+ if (id > PROG_ID_MAX)
+ return ERR_PTR(-EINVAL);
+
+ prog = kzalloc(sizeof(*prog), GFP_KERNEL);
+ if (!prog)
+ return ERR_PTR(-ENOMEM);
+
+ prog->clk.name = name;
+ prog->clk.ops = &programmable_ops;
+ memcpy(prog->parent_names, parent_names,
+ num_parents * sizeof(prog->parent_names[0]));
+ prog->clk.parent_names = &prog->parent_names[0];
+ prog->clk.num_parents = num_parents;
+ /* init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; */
+
+ prog->id = id;
+ prog->layout = layout;
+ prog->regmap = regmap;
+
+ ret = clk_register(&prog->clk);
+ if (ret) {
+ kfree(prog);
+ return ERR_PTR(ret);
+ }
+
+ return &prog->clk;
+}
+
+static const struct clk_programmable_layout at91rm9200_programmable_layout = {
+ .pres_shift = 2,
+ .css_mask = 0x3,
+ .have_slck_mck = 0,
+};
+
+static const struct clk_programmable_layout at91sam9g45_programmable_layout = {
+ .pres_shift = 2,
+ .css_mask = 0x3,
+ .have_slck_mck = 1,
+};
+
+static const struct clk_programmable_layout at91sam9x5_programmable_layout = {
+ .pres_shift = 4,
+ .css_mask = 0x7,
+ .have_slck_mck = 0,
+};
+
+static int
+of_at91_clk_prog_setup(struct device_node *np,
+ const struct clk_programmable_layout *layout)
+{
+ int num;
+ u32 id;
+ struct clk *clk;
+ unsigned int num_parents;
+ const char *parent_names[PROG_SOURCE_MAX];
+ const char *name;
+ struct device_node *progclknp;
+ struct regmap *regmap;
+
+ num_parents = of_clk_get_parent_count(np);
+ if (num_parents == 0 || num_parents > PROG_SOURCE_MAX)
+ return -EINVAL;
+
+ of_clk_parent_fill(np, parent_names, num_parents);
+
+ num = of_get_child_count(np);
+ if (!num || num > (PROG_ID_MAX + 1))
+ return -EINVAL;
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ for_each_child_of_node(np, progclknp) {
+ if (of_property_read_u32(progclknp, "reg", &id))
+ continue;
+
+ if (of_property_read_string(np, "clock-output-names", &name))
+ name = progclknp->name;
+
+ clk = at91_clk_register_programmable(regmap, name,
+ parent_names, num_parents,
+ id, layout);
+ if (IS_ERR(clk))
+ continue;
+
+ of_clk_add_provider(progclknp, of_clk_src_simple_get, clk);
+ }
+
+ return 0;
+}
+
+
+static void __init of_at91rm9200_clk_prog_setup(struct device_node *np)
+{
+ of_at91_clk_prog_setup(np, &at91rm9200_programmable_layout);
+}
+CLK_OF_DECLARE(at91rm9200_clk_prog, "atmel,at91rm9200-clk-programmable",
+ of_at91rm9200_clk_prog_setup);
+
+static int of_at91sam9g45_clk_prog_setup(struct device_node *np)
+{
+ return of_at91_clk_prog_setup(np, &at91sam9g45_programmable_layout);
+}
+CLK_OF_DECLARE(at91sam9g45_clk_prog, "atmel,at91sam9g45-clk-programmable",
+ of_at91sam9g45_clk_prog_setup);
+
+static int of_at91sam9x5_clk_prog_setup(struct device_node *np)
+{
+ return of_at91_clk_prog_setup(np, &at91sam9x5_programmable_layout);
+}
+CLK_OF_DECLARE(at91sam9x5_clk_prog, "atmel,at91sam9x5-clk-programmable",
+ of_at91sam9x5_clk_prog_setup);
diff --git a/drivers/clk/at91/clk-slow.c b/drivers/clk/at91/clk-slow.c
new file mode 100644
index 0000000000..d4981e7b4d
--- /dev/null
+++ b/drivers/clk/at91/clk-slow.c
@@ -0,0 +1,108 @@
+/*
+ * drivers/clk/at91/clk-slow.c
+ *
+ * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <common.h>
+#include <clock.h>
+#include <of.h>
+#include <io.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
+#include <mfd/syscon.h>
+#include <regmap.h>
+
+#include "pmc.h"
+
+struct clk_sam9260_slow {
+ struct clk clk;
+ struct regmap *regmap;
+ const char *parent_names[2];
+};
+
+#define to_clk_sam9260_slow(clk) container_of(clk, struct clk_sam9260_slow, clk)
+
+static int clk_sam9260_slow_get_parent(struct clk *clk)
+{
+ struct clk_sam9260_slow *slowck = to_clk_sam9260_slow(clk);
+ unsigned int status;
+
+ regmap_read(slowck->regmap, AT91_PMC_SR, &status);
+
+ return status & AT91_PMC_OSCSEL ? 1 : 0;
+}
+
+static const struct clk_ops sam9260_slow_ops = {
+ .get_parent = clk_sam9260_slow_get_parent,
+};
+
+static struct clk * __init
+at91_clk_register_sam9260_slow(struct regmap *regmap,
+ const char *name,
+ const char **parent_names,
+ int num_parents)
+{
+ struct clk_sam9260_slow *slowck;
+ int ret;
+
+ if (!name)
+ return ERR_PTR(-EINVAL);
+
+ if (!parent_names || !num_parents)
+ return ERR_PTR(-EINVAL);
+
+ slowck = xzalloc(sizeof(*slowck));
+ slowck->clk.name = name;
+ slowck->clk.ops = &sam9260_slow_ops;
+ memcpy(slowck->parent_names, parent_names,
+ num_parents * sizeof(slowck->parent_names[0]));
+ slowck->clk.parent_names = slowck->parent_names;
+ slowck->clk.num_parents = num_parents;
+ slowck->regmap = regmap;
+
+ ret = clk_register(&slowck->clk);
+ if (ret) {
+ kfree(slowck);
+ return ERR_PTR(ret);
+ }
+
+ return &slowck->clk;
+}
+
+static int of_at91sam9260_clk_slow_setup(struct device_node *np)
+{
+ struct clk *clk;
+ const char *parent_names[2];
+ unsigned int num_parents;
+ const char *name = np->name;
+ struct regmap *regmap;
+
+ num_parents = of_clk_get_parent_count(np);
+ if (num_parents != 2)
+ return -EINVAL;
+
+ of_clk_parent_fill(np, parent_names, num_parents);
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ of_property_read_string(np, "clock-output-names", &name);
+
+ clk = at91_clk_register_sam9260_slow(regmap, name, parent_names,
+ num_parents);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+
+CLK_OF_DECLARE(at91sam9260_clk_slow, "atmel,at91sam9260-clk-slow",
+ of_at91sam9260_clk_slow_setup);
diff --git a/drivers/clk/at91/clk-smd.c b/drivers/clk/at91/clk-smd.c
new file mode 100644
index 0000000000..65c53efbba
--- /dev/null
+++ b/drivers/clk/at91/clk-smd.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <common.h>
+#include <clock.h>
+#include <of.h>
+#include <io.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
+#include <mfd/syscon.h>
+#include <regmap.h>
+
+#include "pmc.h"
+
+#define SMD_SOURCE_MAX 2
+
+#define SMD_DIV_SHIFT 8
+#define SMD_MAX_DIV 0xf
+
+struct at91sam9x5_clk_smd {
+ struct clk clk;
+ struct regmap *regmap;
+ const char *parent_names[SMD_SOURCE_MAX];
+};
+
+#define to_at91sam9x5_clk_smd(clk) \
+ container_of(clk, struct at91sam9x5_clk_smd, clk)
+
+static unsigned long at91sam9x5_clk_smd_recalc_rate(struct clk *clk,
+ unsigned long parent_rate)
+{
+ struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(clk);
+ unsigned int smdr;
+ u8 smddiv;
+
+ regmap_read(smd->regmap, AT91_PMC_SMD, &smdr);
+ smddiv = (smdr & AT91_PMC_SMD_DIV) >> SMD_DIV_SHIFT;
+
+ return parent_rate / (smddiv + 1);
+}
+
+static long at91sam9x5_clk_smd_round_rate(struct clk *clk, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ unsigned long div;
+ unsigned long bestrate;
+ unsigned long tmp;
+
+ if (rate >= *parent_rate)
+ return *parent_rate;
+
+ div = *parent_rate / rate;
+ if (div > SMD_MAX_DIV)
+ return *parent_rate / (SMD_MAX_DIV + 1);
+
+ bestrate = *parent_rate / div;
+ tmp = *parent_rate / (div + 1);
+ if (bestrate - rate > rate - tmp)
+ bestrate = tmp;
+
+ return bestrate;
+}
+
+static int at91sam9x5_clk_smd_set_parent(struct clk *clk, u8 index)
+{
+ struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(clk);
+
+ if (index > 1)
+ return -EINVAL;
+
+ regmap_write_bits(smd->regmap, AT91_PMC_SMD, AT91_PMC_SMDS,
+ index ? AT91_PMC_SMDS : 0);
+
+ return 0;
+}
+
+static int at91sam9x5_clk_smd_get_parent(struct clk *clk)
+{
+ struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(clk);
+ unsigned int smdr;
+
+ regmap_read(smd->regmap, AT91_PMC_SMD, &smdr);
+
+ return smdr & AT91_PMC_SMDS;
+}
+
+static int at91sam9x5_clk_smd_set_rate(struct clk *clk, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(clk);
+ unsigned long div = parent_rate / rate;
+
+ if (parent_rate % rate || div < 1 || div > (SMD_MAX_DIV + 1))
+ return -EINVAL;
+
+ regmap_write_bits(smd->regmap, AT91_PMC_SMD, AT91_PMC_SMD_DIV,
+ (div - 1) << SMD_DIV_SHIFT);
+
+ return 0;
+}
+
+static const struct clk_ops at91sam9x5_smd_ops = {
+ .recalc_rate = at91sam9x5_clk_smd_recalc_rate,
+ .round_rate = at91sam9x5_clk_smd_round_rate,
+ .get_parent = at91sam9x5_clk_smd_get_parent,
+ .set_parent = at91sam9x5_clk_smd_set_parent,
+ .set_rate = at91sam9x5_clk_smd_set_rate,
+};
+
+static struct clk *
+at91sam9x5_clk_register_smd(struct regmap *regmap, const char *name,
+ const char **parent_names, u8 num_parents)
+{
+ struct at91sam9x5_clk_smd *smd;
+ int ret;
+
+ smd = xzalloc(sizeof(*smd));
+ smd->clk.name = name;
+ smd->clk.ops = &at91sam9x5_smd_ops;
+ memcpy(smd->parent_names, parent_names,
+ num_parents * sizeof(smd->parent_names[0]));
+ smd->clk.parent_names = smd->parent_names;
+ smd->clk.num_parents = num_parents;
+ /* init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; */
+ smd->regmap = regmap;
+
+ ret = clk_register(&smd->clk);
+ if (ret) {
+ kfree(smd);
+ return ERR_PTR(ret);
+ }
+
+ return &smd->clk;
+}
+
+static int of_at91sam9x5_clk_smd_setup(struct device_node *np)
+{
+ struct clk *clk;
+ unsigned int num_parents;
+ const char *parent_names[SMD_SOURCE_MAX];
+ const char *name = np->name;
+ struct regmap *regmap;
+
+ num_parents = of_clk_get_parent_count(np);
+ if (num_parents == 0 || num_parents > SMD_SOURCE_MAX)
+ return -EINVAL;
+
+ of_clk_parent_fill(np, parent_names, num_parents);
+
+ of_property_read_string(np, "clock-output-names", &name);
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ clk = at91sam9x5_clk_register_smd(regmap, name, parent_names,
+ num_parents);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(at91sam9x5_clk_smd, "atmel,at91sam9x5-clk-smd",
+ of_at91sam9x5_clk_smd_setup);
diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c
new file mode 100644
index 0000000000..021930e546
--- /dev/null
+++ b/drivers/clk/at91/clk-system.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+#include <common.h>
+#include <clock.h>
+#include <of.h>
+#include <io.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
+#include <mfd/syscon.h>
+#include <regmap.h>
+
+#include "pmc.h"
+
+#define SYSTEM_MAX_ID 31
+
+#define SYSTEM_MAX_NAME_SZ 32
+
+#define to_clk_system(clk) container_of(clk, struct clk_system, clk)
+struct clk_system {
+ struct clk clk;
+ struct regmap *regmap;
+ u8 id;
+ const char *parent_name;
+};
+
+static inline int is_pck(int id)
+{
+ return (id >= 8) && (id <= 15);
+}
+
+static inline bool clk_system_ready(struct regmap *regmap, int id)
+{
+ unsigned int status;
+
+ regmap_read(regmap, AT91_PMC_SR, &status);
+
+ return status & (1 << id) ? 1 : 0;
+}
+
+static int clk_system_enable(struct clk *clk)
+{
+ struct clk_system *sys = to_clk_system(clk);
+
+ regmap_write(sys->regmap, AT91_PMC_SCER, 1 << sys->id);
+
+ if (!is_pck(sys->id))
+ return 0;
+
+ while (!clk_system_ready(sys->regmap, sys->id))
+ barrier();
+
+ return 0;
+}
+
+static void clk_system_disable(struct clk *clk)
+{
+ struct clk_system *sys = to_clk_system(clk);
+
+ regmap_write(sys->regmap, AT91_PMC_SCDR, 1 << sys->id);
+}
+
+static int clk_system_is_enabled(struct clk *clk)
+{
+ struct clk_system *sys = to_clk_system(clk);
+ unsigned int status;
+
+ regmap_read(sys->regmap, AT91_PMC_SCSR, &status);
+
+ if (!(status & (1 << sys->id)))
+ return 0;
+
+ if (!is_pck(sys->id))
+ return 1;
+
+ regmap_read(sys->regmap, AT91_PMC_SR, &status);
+
+ return status & (1 << sys->id) ? 1 : 0;
+}
+
+static const struct clk_ops system_ops = {
+ .enable = clk_system_enable,
+ .disable = clk_system_disable,
+ .is_enabled = clk_system_is_enabled,
+};
+
+static struct clk *
+at91_clk_register_system(struct regmap *regmap, const char *name,
+ const char *parent_name, u8 id)
+{
+ struct clk_system *sys;
+ int ret;
+
+ if (!parent_name || id > SYSTEM_MAX_ID)
+ return ERR_PTR(-EINVAL);
+
+ sys = xzalloc(sizeof(*sys));
+ sys->clk.name = name;
+ sys->clk.ops = &system_ops;
+ sys->parent_name = parent_name;
+ sys->clk.parent_names = &sys->parent_name;
+ sys->clk.num_parents = 1;
+ /* init.flags = CLK_SET_RATE_PARENT; */
+ sys->id = id;
+ sys->regmap = regmap;
+
+ ret = clk_register(&sys->clk);
+ if (ret) {
+ kfree(sys);
+ return ERR_PTR(ret);
+ }
+
+ return &sys->clk;
+}
+
+static int of_at91rm9200_clk_sys_setup(struct device_node *np)
+{
+ int num;
+ u32 id;
+ struct clk *clk;
+ const char *name;
+ struct device_node *sysclknp;
+ const char *parent_name;
+ struct regmap *regmap;
+
+ num = of_get_child_count(np);
+ if (num > (SYSTEM_MAX_ID + 1))
+ return -EINVAL;
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ for_each_child_of_node(np, sysclknp) {
+ if (of_property_read_u32(sysclknp, "reg", &id))
+ continue;
+
+ if (of_property_read_string(np, "clock-output-names", &name))
+ name = sysclknp->name;
+
+ parent_name = of_clk_get_parent_name(sysclknp, 0);
+
+ clk = at91_clk_register_system(regmap, name, parent_name, id);
+ if (IS_ERR(clk))
+ continue;
+
+ of_clk_add_provider(sysclknp, of_clk_src_simple_get, clk);
+ }
+
+ return 0;
+}
+CLK_OF_DECLARE(at91rm9200_clk_sys, "atmel,at91rm9200-clk-system",
+ of_at91rm9200_clk_sys_setup);
diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c
new file mode 100644
index 0000000000..99ba671c98
--- /dev/null
+++ b/drivers/clk/at91/clk-usb.c
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <common.h>
+#include <clock.h>
+#include <of.h>
+#include <io.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
+#include <mfd/syscon.h>
+#include <regmap.h>
+
+#include "pmc.h"
+
+#define USB_SOURCE_MAX 2
+
+#define SAM9X5_USB_DIV_SHIFT 8
+#define SAM9X5_USB_MAX_DIV 0xf
+
+#define RM9200_USB_DIV_SHIFT 28
+#define RM9200_USB_DIV_TAB_SIZE 4
+
+struct at91sam9x5_clk_usb {
+ struct clk clk;
+ struct regmap *regmap;
+ const char *parent_names[USB_SOURCE_MAX];
+};
+
+#define to_at91sam9x5_clk_usb(clk) \
+ container_of(clk, struct at91sam9x5_clk_usb, clk)
+
+struct at91rm9200_clk_usb {
+ struct clk clk;
+ struct regmap *regmap;
+ u32 divisors[4];
+ const char *parent_name;
+};
+
+#define to_at91rm9200_clk_usb(clk) \
+ container_of(clk, struct at91rm9200_clk_usb, clk)
+
+static unsigned long at91sam9x5_clk_usb_recalc_rate(struct clk *clk,
+ unsigned long parent_rate)
+{
+ struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk);
+ unsigned int usbr;
+ u8 usbdiv;
+
+ regmap_read(usb->regmap, AT91_PMC_USB, &usbr);
+ usbdiv = (usbr & AT91_PMC_OHCIUSBDIV) >> SAM9X5_USB_DIV_SHIFT;
+
+ return DIV_ROUND_CLOSEST(parent_rate, (usbdiv + 1));
+}
+
+static int at91sam9x5_clk_usb_set_parent(struct clk *clk, u8 index)
+{
+ struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk);
+
+ if (index > 1)
+ return -EINVAL;
+
+ regmap_write_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS,
+ index ? AT91_PMC_USBS : 0);
+
+ return 0;
+}
+
+static int at91sam9x5_clk_usb_get_parent(struct clk *clk)
+{
+ struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk);
+ unsigned int usbr;
+
+ regmap_read(usb->regmap, AT91_PMC_USB, &usbr);
+
+ return usbr & AT91_PMC_USBS;
+}
+
+static int at91sam9x5_clk_usb_set_rate(struct clk *clk, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk);
+ unsigned long div;
+
+ if (!rate)
+ return -EINVAL;
+
+ div = DIV_ROUND_CLOSEST(parent_rate, rate);
+ if (div > SAM9X5_USB_MAX_DIV + 1 || !div)
+ return -EINVAL;
+
+ regmap_write_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_OHCIUSBDIV,
+ (div - 1) << SAM9X5_USB_DIV_SHIFT);
+
+ return 0;
+}
+
+static const struct clk_ops at91sam9x5_usb_ops = {
+ .recalc_rate = at91sam9x5_clk_usb_recalc_rate,
+ .get_parent = at91sam9x5_clk_usb_get_parent,
+ .set_parent = at91sam9x5_clk_usb_set_parent,
+ .set_rate = at91sam9x5_clk_usb_set_rate,
+};
+
+static int at91sam9n12_clk_usb_enable(struct clk *clk)
+{
+ struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk);
+
+ regmap_write_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS,
+ AT91_PMC_USBS);
+
+ return 0;
+}
+
+static void at91sam9n12_clk_usb_disable(struct clk *clk)
+{
+ struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk);
+
+ regmap_write_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS, 0);
+}
+
+static int at91sam9n12_clk_usb_is_enabled(struct clk *clk)
+{
+ struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk);
+ unsigned int usbr;
+
+ regmap_read(usb->regmap, AT91_PMC_USB, &usbr);
+
+ return usbr & AT91_PMC_USBS;
+}
+
+static const struct clk_ops at91sam9n12_usb_ops = {
+ .enable = at91sam9n12_clk_usb_enable,
+ .disable = at91sam9n12_clk_usb_disable,
+ .is_enabled = at91sam9n12_clk_usb_is_enabled,
+ .recalc_rate = at91sam9x5_clk_usb_recalc_rate,
+ .set_rate = at91sam9x5_clk_usb_set_rate,
+};
+
+static struct clk *
+at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name,
+ const char **parent_names, u8 num_parents)
+{
+ struct at91sam9x5_clk_usb *usb;
+ int ret;
+
+ usb = kzalloc(sizeof(*usb), GFP_KERNEL);
+ usb->clk.name = name;
+ usb->clk.ops = &at91sam9x5_usb_ops;
+ memcpy(usb->parent_names, parent_names,
+ num_parents * sizeof(usb->parent_names[0]));
+ usb->clk.parent_names = usb->parent_names;
+ usb->clk.num_parents = num_parents;
+ usb->clk.flags = CLK_SET_RATE_PARENT;
+ /* init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | */
+ /* CLK_SET_RATE_PARENT; */
+ usb->regmap = regmap;
+
+ ret = clk_register(&usb->clk);
+ if (ret) {
+ kfree(usb);
+ return ERR_PTR(ret);
+ }
+
+ return &usb->clk;
+}
+
+static struct clk *
+at91sam9n12_clk_register_usb(struct regmap *regmap, const char *name,
+ const char *parent_name)
+{
+ struct at91sam9x5_clk_usb *usb;
+ int ret;
+
+ usb = xzalloc(sizeof(*usb));
+ usb->clk.name = name;
+ usb->clk.ops = &at91sam9n12_usb_ops;
+ usb->parent_names[0] = parent_name;
+ usb->clk.parent_names = &usb->parent_names[0];
+ usb->clk.num_parents = 1;
+ /* init.flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT; */
+ usb->regmap = regmap;
+
+ ret = clk_register(&usb->clk);
+ if (ret) {
+ kfree(usb);
+ return ERR_PTR(ret);
+ }
+
+ return &usb->clk;
+}
+
+static unsigned long at91rm9200_clk_usb_recalc_rate(struct clk *clk,
+ unsigned long parent_rate)
+{
+ struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(clk);
+ unsigned int pllbr;
+ u8 usbdiv;
+
+ regmap_read(usb->regmap, AT91_CKGR_PLLBR, &pllbr);
+
+ usbdiv = (pllbr & AT91_PMC_USBDIV) >> RM9200_USB_DIV_SHIFT;
+ if (usb->divisors[usbdiv])
+ return parent_rate / usb->divisors[usbdiv];
+
+ return 0;
+}
+
+static long at91rm9200_clk_usb_round_rate(struct clk *clk, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(clk);
+ struct clk *parent = clk_get_parent(clk);
+ unsigned long bestrate = 0;
+ int bestdiff = -1;
+ unsigned long tmprate;
+ int tmpdiff;
+ int i = 0;
+
+ for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) {
+ unsigned long tmp_parent_rate;
+
+ if (!usb->divisors[i])
+ continue;
+
+ tmp_parent_rate = rate * usb->divisors[i];
+ tmp_parent_rate = clk_round_rate(parent, tmp_parent_rate);
+ tmprate = DIV_ROUND_CLOSEST(tmp_parent_rate, usb->divisors[i]);
+ if (tmprate < rate)
+ tmpdiff = rate - tmprate;
+ else
+ tmpdiff = tmprate - rate;
+
+ if (bestdiff < 0 || bestdiff > tmpdiff) {
+ bestrate = tmprate;
+ bestdiff = tmpdiff;
+ *parent_rate = tmp_parent_rate;
+ }
+
+ if (!bestdiff)
+ break;
+ }
+
+ return bestrate;
+}
+
+static int at91rm9200_clk_usb_set_rate(struct clk *clk, unsigned long rate,
+ unsigned long parent_rate)
+{
+ int i;
+ struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(clk);
+ unsigned long div;
+
+ if (!rate)
+ return -EINVAL;
+
+ div = DIV_ROUND_CLOSEST(parent_rate, rate);
+
+ for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) {
+ if (usb->divisors[i] == div) {
+ regmap_write_bits(usb->regmap, AT91_CKGR_PLLBR,
+ AT91_PMC_USBDIV,
+ i << RM9200_USB_DIV_SHIFT);
+
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static const struct clk_ops at91rm9200_usb_ops = {
+ .recalc_rate = at91rm9200_clk_usb_recalc_rate,
+ .round_rate = at91rm9200_clk_usb_round_rate,
+ .set_rate = at91rm9200_clk_usb_set_rate,
+};
+
+static struct clk *
+at91rm9200_clk_register_usb(struct regmap *regmap, const char *name,
+ const char *parent_name, const u32 *divisors)
+{
+ struct at91rm9200_clk_usb *usb;
+ int ret;
+
+ usb = xzalloc(sizeof(*usb));
+ usb->clk.name = name;
+ usb->clk.ops = &at91rm9200_usb_ops;
+ usb->parent_name = parent_name;
+ usb->clk.parent_names = &usb->parent_name;
+ usb->clk.num_parents = 1;
+ /* init.flags = CLK_SET_RATE_PARENT; */
+
+ usb->regmap = regmap;
+ memcpy(usb->divisors, divisors, sizeof(usb->divisors));
+
+ ret = clk_register(&usb->clk);
+ if (ret) {
+ kfree(usb);
+ return ERR_PTR(ret);
+ }
+
+ return &usb->clk;
+}
+
+static int of_at91sam9x5_clk_usb_setup(struct device_node *np)
+{
+ struct clk *clk;
+ unsigned int num_parents;
+ const char *parent_names[USB_SOURCE_MAX];
+ const char *name = np->name;
+ struct regmap *regmap;
+
+ num_parents = of_clk_get_parent_count(np);
+ if (num_parents == 0 || num_parents > USB_SOURCE_MAX)
+ return -EINVAL;
+
+ of_clk_parent_fill(np, parent_names, num_parents);
+
+ of_property_read_string(np, "clock-output-names", &name);
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ clk = at91sam9x5_clk_register_usb(regmap, name, parent_names,
+ num_parents);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(at91sam9x5_clk_usb, "atmel,at91sam9x5-clk-usb",
+ of_at91sam9x5_clk_usb_setup);
+
+static int of_at91sam9n12_clk_usb_setup(struct device_node *np)
+{
+ struct clk *clk;
+ const char *parent_name;
+ const char *name = np->name;
+ struct regmap *regmap;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+ if (!parent_name)
+ return -EINVAL;
+
+ of_property_read_string(np, "clock-output-names", &name);
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ clk = at91sam9n12_clk_register_usb(regmap, name, parent_name);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(at91sam9n12_clk_usb, "atmel,at91sam9n12-clk-usb",
+ of_at91sam9n12_clk_usb_setup);
+
+static int of_at91rm9200_clk_usb_setup(struct device_node *np)
+{
+ struct clk *clk;
+ const char *parent_name;
+ const char *name = np->name;
+ u32 divisors[4] = {0, 0, 0, 0};
+ struct regmap *regmap;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+ if (!parent_name)
+ return -EINVAL;
+
+ of_property_read_u32_array(np, "atmel,clk-divisors", divisors, 4);
+ if (!divisors[0])
+ return -EINVAL;
+
+ of_property_read_string(np, "clock-output-names", &name);
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ clk = at91rm9200_clk_register_usb(regmap, name, parent_name, divisors);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(at91rm9200_clk_usb, "atmel,at91rm9200-clk-usb",
+ of_at91rm9200_clk_usb_setup);
diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c
new file mode 100644
index 0000000000..6a1c5e6df3
--- /dev/null
+++ b/drivers/clk/at91/clk-utmi.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <common.h>
+#include <clock.h>
+#include <of.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
+#include <mfd/syscon.h>
+#include <regmap.h>
+
+#include "pmc.h"
+
+#define UTMI_FIXED_MUL 40
+
+struct clk_utmi {
+ struct clk clk;
+ struct regmap *regmap;
+ const char *parent;
+};
+
+#define to_clk_utmi(clk) container_of(clk, struct clk_utmi, clk)
+
+static inline bool clk_utmi_ready(struct regmap *regmap)
+{
+ unsigned int status;
+
+ regmap_read(regmap, AT91_PMC_SR, &status);
+
+ return status & AT91_PMC_LOCKU;
+}
+
+static int clk_utmi_enable(struct clk *clk)
+{
+ struct clk_utmi *utmi = to_clk_utmi(clk);
+ unsigned int uckr = AT91_PMC_UPLLEN | AT91_PMC_UPLLCOUNT |
+ AT91_PMC_BIASEN;
+
+ regmap_write_bits(utmi->regmap, AT91_CKGR_UCKR, uckr, uckr);
+
+ while (!clk_utmi_ready(utmi->regmap))
+ barrier();
+
+ return 0;
+}
+
+static int clk_utmi_is_enabled(struct clk *clk)
+{
+ struct clk_utmi *utmi = to_clk_utmi(clk);
+
+ return clk_utmi_ready(utmi->regmap);
+}
+
+static void clk_utmi_disable(struct clk *clk)
+{
+ struct clk_utmi *utmi = to_clk_utmi(clk);
+
+ regmap_write_bits(utmi->regmap, AT91_CKGR_UCKR, AT91_PMC_UPLLEN, 0);
+}
+
+static unsigned long clk_utmi_recalc_rate(struct clk *clk,
+ unsigned long parent_rate)
+{
+ /* UTMI clk is a fixed clk multiplier */
+ return parent_rate * UTMI_FIXED_MUL;
+}
+
+static const struct clk_ops utmi_ops = {
+ .enable = clk_utmi_enable,
+ .disable = clk_utmi_disable,
+ .is_enabled = clk_utmi_is_enabled,
+ .recalc_rate = clk_utmi_recalc_rate,
+};
+
+static struct clk * __init
+at91_clk_register_utmi(struct regmap *regmap,
+ const char *name, const char *parent_name)
+{
+ int ret;
+ struct clk_utmi *utmi;
+
+ utmi = xzalloc(sizeof(*utmi));
+
+ utmi->clk.name = name;
+ utmi->clk.ops = &utmi_ops;
+
+ if (parent_name) {
+ utmi->parent = parent_name;
+ utmi->clk.parent_names = &utmi->parent;
+ utmi->clk.num_parents = 1;
+ }
+
+ /* utmi->clk.flags = CLK_SET_RATE_GATE; */
+
+ utmi->regmap = regmap;
+
+ ret = clk_register(&utmi->clk);
+ if (ret) {
+ kfree(utmi);
+ return ERR_PTR(ret);
+ }
+
+ return &utmi->clk;
+}
+#if defined(CONFIG_OFTREE) && defined(CONFIG_COMMON_CLK_OF_PROVIDER)
+static void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np)
+{
+ struct clk *clk;
+ const char *parent_name;
+ const char *name = np->name;
+ struct regmap *regmap;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+
+ of_property_read_string(np, "clock-output-names", &name);
+
+ regmap = syscon_node_to_regmap(of_get_parent(np));
+ if (IS_ERR(regmap))
+ return;
+
+ clk = at91_clk_register_utmi(regmap, name, parent_name);
+ if (IS_ERR(clk))
+ return;
+
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ return;
+}
+CLK_OF_DECLARE(at91sam9x5_clk_utmi, "atmel,at91sam9x5-clk-utmi",
+ of_at91sam9x5_clk_utmi_setup);
+#endif
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
new file mode 100644
index 0000000000..d156d50ca8
--- /dev/null
+++ b/drivers/clk/at91/pmc.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <module.h>
+#include <linux/list.h>
+#include <linux/clkdev.h>
+#include <of.h>
+#include <mfd/syscon.h>
+#include <regmap.h>
+
+#include "pmc.h"
+
+int of_at91_get_clk_range(struct device_node *np, const char *propname,
+ struct clk_range *range)
+{
+ u32 min, max;
+ int ret;
+
+ ret = of_property_read_u32_index(np, propname, 0, &min);
+ if (ret)
+ return ret;
+
+ ret = of_property_read_u32_index(np, propname, 1, &max);
+ if (ret)
+ return ret;
+
+ if (range) {
+ range->min = min;
+ range->max = max;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_at91_get_clk_range);
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
new file mode 100644
index 0000000000..c6c14a79a4
--- /dev/null
+++ b/drivers/clk/at91/pmc.h
@@ -0,0 +1,27 @@
+/*
+ * drivers/clk/at91/pmc.h
+ *
+ * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __PMC_H_
+#define __PMC_H_
+
+#include <io.h>
+
+struct clk_range {
+ unsigned long min;
+ unsigned long max;
+};
+
+#define CLK_RANGE(MIN, MAX) {.min = MIN, .max = MAX,}
+
+int of_at91_get_clk_range(struct device_node *np, const char *propname,
+ struct clk_range *range);
+
+#endif /* __PMC_H_ */
diff --git a/drivers/clk/at91/sckc.c b/drivers/clk/at91/sckc.c
new file mode 100644
index 0000000000..bac28999ea
--- /dev/null
+++ b/drivers/clk/at91/sckc.c
@@ -0,0 +1,485 @@
+/*
+ * drivers/clk/at91/sckc.c
+ *
+ * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <common.h>
+#include <clock.h>
+#include <of.h>
+#include <of_address.h>
+#include <io.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
+#include <mfd/syscon.h>
+#include <regmap.h>
+
+
+
+#define SLOW_CLOCK_FREQ 32768
+#define SLOWCK_SW_CYCLES 5
+#define SLOWCK_SW_TIME_USEC ((SLOWCK_SW_CYCLES * SECOND) / \
+ SLOW_CLOCK_FREQ)
+
+#define AT91_SCKC_CR 0x00
+#define AT91_SCKC_RCEN (1 << 0)
+#define AT91_SCKC_OSC32EN (1 << 1)
+#define AT91_SCKC_OSC32BYP (1 << 2)
+#define AT91_SCKC_OSCSEL (1 << 3)
+
+struct clk_slow_osc {
+ struct clk clk;
+ void __iomem *sckcr;
+ unsigned long startup_usec;
+ const char *parent_name;
+};
+
+#define to_clk_slow_osc(clk) container_of(clk, struct clk_slow_osc, clk)
+
+struct clk_sama5d4_slow_osc {
+ struct clk clk;
+ void __iomem *sckcr;
+ unsigned long startup_usec;
+ bool prepared;
+ const char *parent_name;
+};
+
+#define to_clk_sama5d4_slow_osc(clk) container_of(clk, struct clk_sama5d4_slow_osc, clk)
+
+struct clk_slow_rc_osc {
+ struct clk clk;
+ void __iomem *sckcr;
+ unsigned long frequency;
+ unsigned long startup_usec;
+ const char *parent_name;
+};
+
+#define to_clk_slow_rc_osc(clk) container_of(clk, struct clk_slow_rc_osc, clk)
+
+struct clk_sam9x5_slow {
+ struct clk clk;
+ void __iomem *sckcr;
+ u8 parent;
+ const char *parent_names[2];
+};
+
+#define to_clk_sam9x5_slow(clk) container_of(clk, struct clk_sam9x5_slow, clk)
+
+static int clk_slow_osc_enable(struct clk *clk)
+{
+ struct clk_slow_osc *osc = to_clk_slow_osc(clk);
+ void __iomem *sckcr = osc->sckcr;
+ u32 tmp = readl(sckcr);
+
+ if (tmp & (AT91_SCKC_OSC32BYP | AT91_SCKC_OSC32EN))
+ return 0;
+
+ writel(tmp | AT91_SCKC_OSC32EN, sckcr);
+
+ udelay(osc->startup_usec);
+
+ return 0;
+}
+
+static void clk_slow_osc_disable(struct clk *clk)
+{
+ struct clk_slow_osc *osc = to_clk_slow_osc(clk);
+ void __iomem *sckcr = osc->sckcr;
+ u32 tmp = readl(sckcr);
+
+ if (tmp & AT91_SCKC_OSC32BYP)
+ return;
+
+ writel(tmp & ~AT91_SCKC_OSC32EN, sckcr);
+}
+
+static int clk_slow_osc_is_enabled(struct clk *clk)
+{
+ struct clk_slow_osc *osc = to_clk_slow_osc(clk);
+ void __iomem *sckcr = osc->sckcr;
+ u32 tmp = readl(sckcr);
+
+ if (tmp & AT91_SCKC_OSC32BYP)
+ return 1;
+
+ return !!(tmp & AT91_SCKC_OSC32EN);
+}
+
+static const struct clk_ops slow_osc_ops = {
+ .enable = clk_slow_osc_enable,
+ .disable = clk_slow_osc_disable,
+ .is_enabled = clk_slow_osc_is_enabled,
+};
+
+static struct clk *
+at91_clk_register_slow_osc(void __iomem *sckcr,
+ const char *name,
+ const char *parent_name,
+ unsigned long startup,
+ bool bypass)
+{
+ int ret;
+ struct clk_slow_osc *osc;
+
+ if (!sckcr || !name || !parent_name)
+ return ERR_PTR(-EINVAL);
+
+ osc = xzalloc(sizeof(*osc));
+
+ osc->clk.name = name;
+ osc->clk.ops = &slow_osc_ops;
+ osc->parent_name = parent_name;
+ osc->clk.parent_names = &osc->parent_name;
+ osc->clk.num_parents = 1;
+
+ osc->sckcr = sckcr;
+ osc->startup_usec = startup;
+
+ if (bypass)
+ writel((readl(sckcr) & ~AT91_SCKC_OSC32EN) | AT91_SCKC_OSC32BYP,
+ sckcr);
+
+ ret = clk_register(&osc->clk);
+ if (ret) {
+ kfree(osc);
+ return ERR_PTR(ret);
+ }
+
+ return &osc->clk;
+}
+
+static void
+of_at91sam9x5_clk_slow_osc_setup(struct device_node *np, void __iomem *sckcr)
+{
+ struct clk *clk;
+ const char *parent_name;
+ const char *name = np->name;
+ u32 startup;
+ bool bypass;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+ of_property_read_string(np, "clock-output-names", &name);
+ of_property_read_u32(np, "atmel,startup-time-usec", &startup);
+ bypass = of_property_read_bool(np, "atmel,osc-bypass");
+
+ clk = at91_clk_register_slow_osc(sckcr, name, parent_name, startup,
+ bypass);
+ if (IS_ERR(clk))
+ return;
+
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+
+static unsigned long clk_slow_rc_osc_recalc_rate(struct clk *clk,
+ unsigned long parent_rate)
+{
+ struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(clk);
+
+ return osc->frequency;
+}
+
+static int clk_slow_rc_osc_enable(struct clk *clk)
+{
+ struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(clk);
+ void __iomem *sckcr = osc->sckcr;
+
+ writel(readl(sckcr) | AT91_SCKC_RCEN, sckcr);
+
+ udelay(osc->startup_usec);
+
+ return 0;
+}
+
+static void clk_slow_rc_osc_disable(struct clk *clk)
+{
+ struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(clk);
+ void __iomem *sckcr = osc->sckcr;
+
+ writel(readl(sckcr) & ~AT91_SCKC_RCEN, sckcr);
+}
+
+static int clk_slow_rc_osc_is_enabled(struct clk *clk)
+{
+ struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(clk);
+
+ return !!(readl(osc->sckcr) & AT91_SCKC_RCEN);
+}
+
+static const struct clk_ops slow_rc_osc_ops = {
+ .enable = clk_slow_rc_osc_enable,
+ .disable = clk_slow_rc_osc_disable,
+ .is_enabled = clk_slow_rc_osc_is_enabled,
+ .recalc_rate = clk_slow_rc_osc_recalc_rate,
+};
+
+static struct clk *
+at91_clk_register_slow_rc_osc(void __iomem *sckcr,
+ const char *name,
+ unsigned long frequency,
+ unsigned long startup)
+{
+ struct clk_slow_rc_osc *osc;
+ int ret;
+
+ if (!sckcr || !name)
+ return ERR_PTR(-EINVAL);
+
+ osc = xzalloc(sizeof(*osc));
+ osc->clk.name = name;
+ osc->clk.ops = &slow_rc_osc_ops;
+ osc->clk.parent_names = NULL;
+ osc->clk.num_parents = 0;
+ /* init.flags = CLK_IGNORE_UNUSED; */
+
+ osc->sckcr = sckcr;
+ osc->frequency = frequency;
+ osc->startup_usec = startup;
+
+ ret = clk_register(&osc->clk);
+ if (ret) {
+ kfree(osc);
+ return ERR_PTR(ret);
+ }
+
+ return &osc->clk;
+}
+
+static void
+of_at91sam9x5_clk_slow_rc_osc_setup(struct device_node *np, void __iomem *sckcr)
+{
+ struct clk *clk;
+ u32 frequency = 0;
+ u32 startup = 0;
+ const char *name = np->name;
+
+ of_property_read_string(np, "clock-output-names", &name);
+ of_property_read_u32(np, "clock-frequency", &frequency);
+ of_property_read_u32(np, "atmel,startup-time-usec", &startup);
+
+ clk = at91_clk_register_slow_rc_osc(sckcr, name, frequency, startup);
+ if (IS_ERR(clk))
+ return;
+
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+
+static int clk_sam9x5_slow_set_parent(struct clk *clk, u8 index)
+{
+ struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(clk);
+ void __iomem *sckcr = slowck->sckcr;
+ u32 tmp;
+
+ if (index > 1)
+ return -EINVAL;
+
+ tmp = readl(sckcr);
+
+ if ((!index && !(tmp & AT91_SCKC_OSCSEL)) ||
+ (index && (tmp & AT91_SCKC_OSCSEL)))
+ return 0;
+
+ if (index)
+ tmp |= AT91_SCKC_OSCSEL;
+ else
+ tmp &= ~AT91_SCKC_OSCSEL;
+
+ writel(tmp, sckcr);
+
+ udelay(SLOWCK_SW_TIME_USEC);
+
+ return 0;
+}
+
+static int clk_sam9x5_slow_get_parent(struct clk *clk)
+{
+ struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(clk);
+
+ return !!(readl(slowck->sckcr) & AT91_SCKC_OSCSEL);
+}
+
+static const struct clk_ops sam9x5_slow_ops = {
+ .set_parent = clk_sam9x5_slow_set_parent,
+ .get_parent = clk_sam9x5_slow_get_parent,
+};
+
+static struct clk *
+at91_clk_register_sam9x5_slow(void __iomem *sckcr,
+ const char *name,
+ const char **parent_names,
+ int num_parents)
+{
+ struct clk_sam9x5_slow *slowck;
+ int ret;
+
+ if (!sckcr || !name || !parent_names || !num_parents)
+ return ERR_PTR(-EINVAL);
+
+ slowck = xzalloc(sizeof(*slowck));
+ slowck->clk.name = name;
+ slowck->clk.ops = &sam9x5_slow_ops;
+
+ memcpy(slowck->parent_names, parent_names,
+ num_parents * sizeof(slowck->parent_names[0]));
+ slowck->clk.parent_names = slowck->parent_names;
+ slowck->clk.num_parents = num_parents;
+ slowck->sckcr = sckcr;
+ slowck->parent = !!(readl(sckcr) & AT91_SCKC_OSCSEL);
+
+ ret = clk_register(&slowck->clk);
+ if (ret) {
+ kfree(slowck);
+ return ERR_PTR(ret);
+ }
+
+ return &slowck->clk;
+}
+
+static int
+of_at91sam9x5_clk_slow_setup(struct device_node *np, void __iomem *sckcr)
+{
+ struct clk *clk;
+ const char *parent_names[2];
+ unsigned int num_parents;
+ const char *name = np->name;
+
+ num_parents = of_clk_get_parent_count(np);
+ if (num_parents == 0 || num_parents > 2)
+ return -EINVAL;
+
+ of_clk_parent_fill(np, parent_names, num_parents);
+
+ of_property_read_string(np, "clock-output-names", &name);
+
+ clk = at91_clk_register_sam9x5_slow(sckcr, name, parent_names,
+ num_parents);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+
+static const struct of_device_id sckc_clk_ids[] = {
+ /* Slow clock */
+ {
+ .compatible = "atmel,at91sam9x5-clk-slow-osc",
+ .data = of_at91sam9x5_clk_slow_osc_setup,
+ },
+ {
+ .compatible = "atmel,at91sam9x5-clk-slow-rc-osc",
+ .data = of_at91sam9x5_clk_slow_rc_osc_setup,
+ },
+ {
+ .compatible = "atmel,at91sam9x5-clk-slow",
+ .data = of_at91sam9x5_clk_slow_setup,
+ },
+ { /*sentinel*/ }
+};
+
+static int of_at91sam9x5_sckc_setup(struct device_node *np)
+{
+ struct device_node *childnp;
+ void (*clk_setup)(struct device_node *, void __iomem *);
+ const struct of_device_id *clk_id;
+ void __iomem *regbase = of_iomap(np, 0);
+
+ if (!regbase)
+ return -ENOMEM;
+
+ for_each_child_of_node(np, childnp) {
+ clk_id = of_match_node(sckc_clk_ids, childnp);
+ if (!clk_id)
+ continue;
+ clk_setup = clk_id->data;
+ clk_setup(childnp, regbase);
+ }
+
+ return 0;
+}
+CLK_OF_DECLARE(at91sam9x5_clk_sckc, "atmel,at91sam9x5-sckc",
+ of_at91sam9x5_sckc_setup);
+
+static int clk_sama5d4_slow_osc_enable(struct clk *clk)
+{
+ struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(clk);
+
+ if (osc->prepared)
+ return 0;
+
+ /*
+ * Assume that if it has already been selected (for example by the
+ * bootloader), enough time has aready passed.
+ */
+ if ((readl(osc->sckcr) & AT91_SCKC_OSCSEL)) {
+ osc->prepared = true;
+ return 0;
+ }
+
+ udelay(osc->startup_usec);
+ osc->prepared = true;
+
+ return 0;
+}
+
+static int clk_sama5d4_slow_osc_is_enabled(struct clk *clk)
+{
+ struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(clk);
+
+ return osc->prepared;
+}
+
+static const struct clk_ops sama5d4_slow_osc_ops = {
+ .enable = clk_sama5d4_slow_osc_enable,
+ .is_enabled = clk_sama5d4_slow_osc_is_enabled,
+};
+
+static int of_sama5d4_sckc_setup(struct device_node *np)
+{
+ void __iomem *regbase = of_iomap(np, 0);
+ struct clk *clk;
+ struct clk_sama5d4_slow_osc *osc;
+ const char *parent_names[2] = { "slow_rc_osc", "slow_osc" };
+ bool bypass;
+ int ret;
+
+ if (!regbase)
+ return -ENOMEM;
+
+ clk = clk_fixed(parent_names[0], 32768);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ bypass = of_property_read_bool(np, "atmel,osc-bypass");
+
+ osc = xzalloc(sizeof(*osc));
+ osc->parent_name = of_clk_get_parent_name(np, 0);
+ osc->clk.name = parent_names[1];
+ osc->clk.ops = &sama5d4_slow_osc_ops;
+ osc->clk.parent_names = &osc->parent_name;
+ osc->clk.num_parents = 1;
+ osc->sckcr = regbase;
+ osc->startup_usec = 1200000;
+
+ if (bypass)
+ writel((readl(regbase) | AT91_SCKC_OSC32BYP), regbase);
+
+ ret = clk_register(&osc->clk);
+ if (ret) {
+ kfree(osc);
+ return ret;
+ }
+
+ clk = at91_clk_register_sam9x5_slow(regbase, "slowck", parent_names, 2);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(sama5d4_clk_sckc, "atmel,sama5d4-sckc",
+ of_sama5d4_sckc_setup);
diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c
index a3dbf334a8..0be48558e6 100644
--- a/drivers/clk/clk-fixed-factor.c
+++ b/drivers/clk/clk-fixed-factor.c
@@ -93,7 +93,6 @@ struct clk *clk_fixed_factor(const char *name,
return &f->clk;
}
-#if defined(CONFIG_COMMON_CLK_OF_PROVIDER)
/**
* of_fixed_factor_clk_setup() - Setup function for simple fixed factor clock
*/
@@ -127,4 +126,3 @@ static int of_fixed_factor_clk_setup(struct device_node *node)
}
CLK_OF_DECLARE(fixed_factor_clk, "fixed-factor-clock",
of_fixed_factor_clk_setup);
-#endif
diff --git a/drivers/clk/clk-fixed.c b/drivers/clk/clk-fixed.c
index f0f7fbaed5..57bf36b39e 100644
--- a/drivers/clk/clk-fixed.c
+++ b/drivers/clk/clk-fixed.c
@@ -55,7 +55,6 @@ struct clk *clk_fixed(const char *name, int rate)
return &fix->clk;
}
-#if defined(CONFIG_COMMON_CLK_OF_PROVIDER)
/**
* of_fixed_clk_setup() - Setup function for simple fixed rate clock
*/
@@ -76,4 +75,3 @@ static int of_fixed_clk_setup(struct device_node *node)
return of_clk_add_provider(node, of_clk_src_simple_get, clk);
}
CLK_OF_DECLARE(fixed_clk, "fixed-clock", of_fixed_clk_setup);
-#endif
diff --git a/drivers/clk/clk-gate-shared.c b/drivers/clk/clk-gate-shared.c
index a95f940dd2..c3b678a311 100644
--- a/drivers/clk/clk-gate-shared.c
+++ b/drivers/clk/clk-gate-shared.c
@@ -69,8 +69,8 @@ static struct clk_ops clk_gate_shared_ops = {
.is_enabled = clk_gate_shared_is_enabled,
};
-struct clk *clk_gate_shared_alloc(const char *name, const char *parent, const char *companion,
- unsigned flags)
+static struct clk *clk_gate_shared_alloc(const char *name, const char *parent,
+ const char *companion, unsigned flags)
{
struct clk_gate_shared *g = xzalloc(sizeof(*g));
@@ -86,7 +86,7 @@ struct clk *clk_gate_shared_alloc(const char *name, const char *parent, const ch
return &g->clk;
}
-void clk_gate_shared_free(struct clk *clk)
+static void clk_gate_shared_free(struct clk *clk)
{
struct clk_gate_shared *g = to_clk_gate_shared(clk);
diff --git a/drivers/clocksource/mvebu.c b/drivers/clocksource/mvebu.c
index cf80571263..59bbc4be22 100644
--- a/drivers/clocksource/mvebu.c
+++ b/drivers/clocksource/mvebu.c
@@ -60,7 +60,7 @@ static int mvebu_timer_probe(struct device_d *dev)
struct clk *clk;
u32 rate, div, val;
- iores = dev_request_mem_resource(dev, 0);
+ iores = dev_get_resource(dev, IORESOURCE_MEM, 0);
if (IS_ERR(iores))
return PTR_ERR(iores);
timer_base = IOMEM(iores->start);
diff --git a/drivers/clocksource/timer-atmel-pit.c b/drivers/clocksource/timer-atmel-pit.c
index cc7ad2f39a..947a1e7f49 100644
--- a/drivers/clocksource/timer-atmel-pit.c
+++ b/drivers/clocksource/timer-atmel-pit.c
@@ -102,9 +102,18 @@ static int at91_pit_probe(struct device_d *dev)
return init_clock(&cs);
}
+const static __maybe_unused struct of_device_id at91_pit_dt_ids[] = {
+ {
+ .compatible = "atmel,at91sam9260-pit",
+ }, {
+ /* sentinel */
+ }
+};
+
static struct driver_d at91_pit_driver = {
.name = "at91-pit",
.probe = at91_pit_probe,
+ .of_compatible = DRV_OF_COMPAT(at91_pit_dt_ids),
};
static int at91_pit_init(void)
diff --git a/drivers/crypto/caam/Kconfig b/drivers/crypto/caam/Kconfig
index cf05d1c077..2ab509d110 100644
--- a/drivers/crypto/caam/Kconfig
+++ b/drivers/crypto/caam/Kconfig
@@ -29,6 +29,7 @@ config CRYPTO_DEV_FSL_CAAM_RINGSIZE
config CRYPTO_DEV_FSL_CAAM_RNG
bool "Register caam RNG device"
depends on CRYPTO_DEV_FSL_CAAM
+ depends on HWRNG
default y
help
Selecting this will register the SEC4 hardware rng.
diff --git a/drivers/crypto/caam/caamrng.c b/drivers/crypto/caam/caamrng.c
index 0fef171a2b..31a92731d2 100644
--- a/drivers/crypto/caam/caamrng.c
+++ b/drivers/crypto/caam/caamrng.c
@@ -35,6 +35,7 @@
#include <driver.h>
#include <init.h>
#include <linux/spinlock.h>
+#include <linux/hw_random.h>
#include "regs.h"
#include "intern.h"
@@ -54,7 +55,7 @@
/* Buffer, its dma address and lock */
struct buf_data {
- u8 buf[RN_BUF_SIZE];
+ u8 *buf;
dma_addr_t addr;
u32 hw_desc[DESC_JOB_O_LEN];
#define BUF_NOT_EMPTY 0
@@ -71,7 +72,7 @@ struct caam_rng_ctx {
unsigned int cur_buf_idx;
int current_buf;
struct buf_data bufs[2];
- struct cdev cdev;
+ struct hwrng rng;
};
static struct caam_rng_ctx *rng_ctx;
@@ -116,8 +117,9 @@ static inline int submit_job(struct caam_rng_ctx *ctx, int to_current)
return err;
}
-static int caam_read(struct caam_rng_ctx *ctx, void *data, size_t max, bool wait)
+static int caam_read(struct hwrng *rng, void *data, size_t max, bool wait)
{
+ struct caam_rng_ctx *ctx = container_of(rng, struct caam_rng_ctx, rng);
struct buf_data *bd = &ctx->bufs[ctx->current_buf];
int next_buf_idx, copied_idx;
int err;
@@ -162,7 +164,7 @@ static int caam_read(struct caam_rng_ctx *ctx, void *data, size_t max, bool wait
dev_dbg(ctx->jrdev, "switched to buffer %d\n", ctx->current_buf);
/* since there already is some data read, don't wait */
- return copied_idx + caam_read(ctx, data + copied_idx,
+ return copied_idx + caam_read(rng, data + copied_idx,
max - copied_idx, false);
}
@@ -216,6 +218,8 @@ static int caam_init_buf(struct caam_rng_ctx *ctx, int buf_id)
struct buf_data *bd = &ctx->bufs[buf_id];
int err;
+ bd->buf = dma_alloc(RN_BUF_SIZE);
+
err = rng_create_job_desc(ctx, buf_id);
if (err)
return err;
@@ -248,29 +252,6 @@ static int caam_init_rng(struct caam_rng_ctx *ctx, struct device_d *jrdev)
return 0;
}
-static ssize_t random_read(struct cdev *cdev, void *buf, size_t count,
- loff_t offset, ulong flags)
-{
- struct caam_rng_ctx *ctx = container_of(cdev, struct caam_rng_ctx, cdev);
-
- return caam_read(ctx, buf, count, true);
-}
-
-static struct file_operations randomops = {
- .read = random_read,
- .lseek = dev_lseek_default,
-};
-
-static int caam_init_devrandom(struct caam_rng_ctx *ctx, struct device_d *dev)
-{
- ctx->cdev.name = "hwrng";
- ctx->cdev.flags = DEVFS_IS_CHARACTER_DEV;
- ctx->cdev.ops = &randomops;
- ctx->cdev.dev = dev;
-
- return devfs_create(&ctx->cdev);
-}
-
int caam_rng_probe(struct device_d *dev, struct device_d *jrdev)
{
int err;
@@ -281,9 +262,14 @@ int caam_rng_probe(struct device_d *dev, struct device_d *jrdev)
if (err)
return err;
- err = caam_init_devrandom(rng_ctx, dev);
- if (err)
+ rng_ctx->rng.name = dev->name;
+ rng_ctx->rng.read = caam_read;
+
+ err = hwrng_register(dev, &rng_ctx->rng);
+ if (err) {
+ dev_err(dev, "rng-caam registering failed (%d)\n", err);
return err;
+ }
dev_info(dev, "registering rng-caam\n");
diff --git a/drivers/eeprom/at24.c b/drivers/eeprom/at24.c
index 4ae3776554..1227286fbe 100644
--- a/drivers/eeprom/at24.c
+++ b/drivers/eeprom/at24.c
@@ -500,6 +500,7 @@ static int at24_probe(struct device_d *dev)
goto err_devfs_create;
of_parse_partitions(&at24->cdev, dev->device_node);
+ of_partitions_register_fixup(&at24->cdev);
return 0;
diff --git a/drivers/eeprom/at25.c b/drivers/eeprom/at25.c
index f7f8368c4b..1caaebd371 100644
--- a/drivers/eeprom/at25.c
+++ b/drivers/eeprom/at25.c
@@ -360,6 +360,8 @@ static int at25_probe(struct device_d *dev)
dev_dbg(dev, "%s probed\n", at25->cdev.name);
of_parse_partitions(&at25->cdev, dev->device_node);
+ of_partitions_register_fixup(&at25->cdev);
+
return 0;
fail:
diff --git a/drivers/efi/efi-device.c b/drivers/efi/efi-device.c
index 6ed7f12b37..e9b03cb02a 100644
--- a/drivers/efi/efi-device.c
+++ b/drivers/efi/efi-device.c
@@ -101,7 +101,8 @@ static efi_handle_t *efi_find_parent(efi_handle_t *handle)
struct efi_open_protocol_information_entry *entry_buffer;
unsigned long entry_count;
- ret = efi_locate_handle(all_handles, NULL, NULL, &handle_count, &handles);
+ ret = efi_locate_handle(by_protocol, &efi_device_path_protocol_guid,
+ NULL, &handle_count, &handles);
if (ret)
return NULL;
@@ -183,8 +184,6 @@ static struct efi_device *efi_add_device(efi_handle_t *handle, efi_guid_t **guid
efidev->dev.info = efi_devinfo;
efidev->devpath = devpath;
- BS->handle_protocol(handle, &guidarr[0], &efidev->protocol);
-
sprintf(efidev->dev.name, "handle-%p", handle);
efidev->parent_handle = efi_find_parent(efidev->handle);
@@ -245,7 +244,8 @@ void efi_register_devices(void)
struct efi_device **efidevs;
int registered;
- ret = efi_locate_handle(all_handles, NULL, NULL, &handle_count, &handles);
+ ret = efi_locate_handle(by_protocol, &efi_device_path_protocol_guid,
+ NULL, &handle_count, &handles);
if (ret)
return;
@@ -310,8 +310,11 @@ static int efi_bus_match(struct device_d *dev, struct driver_d *drv)
int i;
for (i = 0; i < efidev->num_guids; i++) {
- if (!memcmp(&efidrv->guid, &efidev->guids[i], sizeof(efi_guid_t)))
+ if (!memcmp(&efidrv->guid, &efidev->guids[i], sizeof(efi_guid_t))) {
+ BS->handle_protocol(efidev->handle, &efidev->guids[i],
+ &efidev->protocol);
return 0;
+ }
}
return 1;
diff --git a/drivers/hab/Makefile b/drivers/hab/Makefile
index 8528ef954f..b169a1346d 100644
--- a/drivers/hab/Makefile
+++ b/drivers/hab/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_HABV4) += habv4.o
obj-$(CONFIG_HABV3) += habv3.o
+obj-$(CONFIG_HAB) += hab.o
diff --git a/drivers/hab/hab.c b/drivers/hab/hab.c
new file mode 100644
index 0000000000..512ff7ecf2
--- /dev/null
+++ b/drivers/hab/hab.c
@@ -0,0 +1,358 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2.
+ *
+ * This program is distributed 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.
+ */
+#define pr_fmt(fmt) "HAB: " fmt
+
+#include <common.h>
+#include <fcntl.h>
+#include <environment.h>
+#include <libfile.h>
+#include <mach/generic.h>
+#include <hab.h>
+#include <regmap.h>
+#include <fs.h>
+#include <mach/iim.h>
+#include <mach/imx25-fusemap.h>
+#include <mach/ocotp.h>
+#include <mach/imx6-fusemap.h>
+
+bool imx_hab_srk_hash_valid(const void *buf)
+{
+ const u8 *srk = buf;
+ int all_zero = 1, all_ff = 1;
+ int i;
+
+ for (i = 0; i < SRK_HASH_SIZE; i++) {
+ if (srk[i] != 0x0)
+ all_zero = 0;
+ if (srk[i] != 0xff)
+ all_ff = 0;
+ }
+
+ return !all_zero && !all_ff;
+}
+
+static int imx_hab_read_srk_hash_iim(u8 *srk)
+{
+ int ret, i;
+ unsigned int val;
+
+ ret = imx_iim_read_field(IMX25_IIM_SRK0_HASH_0, &val);
+ if (ret < 0)
+ return ret;
+ srk[0] = val;
+
+ for (i = 1; i < SRK_HASH_SIZE; i++) {
+ ret = imx_iim_read_field(IMX25_IIM_SRK0_HASH_1_31(i), &val);
+ if (ret < 0)
+ return ret;
+ srk[i] = val;
+ }
+
+ return 0;
+}
+
+static int imx_hab_write_srk_hash_iim(const u8 *srk, unsigned flags)
+{
+ int ret, i;
+
+ ret = imx_iim_write_field(IMX25_IIM_SRK0_HASH_0, srk[0]);
+ if (ret < 0)
+ return ret;
+
+ for (i = 1; i < SRK_HASH_SIZE; i++) {
+ ret = imx_iim_write_field(IMX25_IIM_SRK0_HASH_1_31(i), srk[i]);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (flags & IMX_SRK_HASH_WRITE_PERMANENT) {
+ u8 verify[SRK_HASH_SIZE];
+
+ setenv("iim.explicit_sense_enable", "1");
+ ret = imx_hab_read_srk_hash_iim(verify);
+ if (ret)
+ return ret;
+ setenv("iim.explicit_sense_enable", "0");
+
+ if (memcmp(srk, verify, SRK_HASH_SIZE))
+ return -EIO;
+ }
+
+ if (flags & IMX_SRK_HASH_WRITE_LOCK) {
+ ret = imx_iim_write_field(IMX25_IIM_SRK0_LOCK96, 1);
+ if (ret < 0)
+ return ret;
+ ret = imx_iim_write_field(IMX25_IIM_SRK0_LOCK160, 1);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int imx_hab_permanent_write_enable_iim(int enable)
+{
+ return imx_iim_permanent_write(enable);
+}
+
+static int imx_hab_lockdown_device_iim(void)
+{
+ return imx_iim_write_field(IMX25_IIM_HAB_TYPE, 3);
+}
+
+static int imx_hab_device_locked_down_iim(void)
+{
+ int ret;
+ unsigned int v;
+
+ ret = imx_iim_read_field(IMX25_IIM_HAB_TYPE, &v);
+ if (ret < 0)
+ return ret;
+
+ return v == 1 ? false : true;
+}
+
+static int imx_hab_read_srk_hash_ocotp(u8 *__srk)
+{
+ u32 *srk = (u32 *)__srk;
+ int ret, i;
+
+ for (i = 0; i < SRK_HASH_SIZE / sizeof(uint32_t); i++) {
+ ret = imx_ocotp_read_field(OCOTP_SRK_HASH(i), &srk[i]);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int imx_hab_write_srk_hash_ocotp(const u8 *__newsrk, unsigned flags)
+{
+ u32 *newsrk = (u32 *)__newsrk;
+ int ret, i;
+
+ for (i = 0; i < SRK_HASH_SIZE / sizeof(uint32_t); i++) {
+ ret = imx_ocotp_write_field(OCOTP_SRK_HASH(i), newsrk[i]);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (flags & IMX_SRK_HASH_WRITE_LOCK) {
+ ret = imx_ocotp_write_field(OCOTP_SRK_LOCK, 1);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int imx_hab_permanent_write_enable_ocotp(int enable)
+{
+ return imx_ocotp_permanent_write(enable);
+}
+
+static int imx_hab_lockdown_device_ocotp(void)
+{
+ return imx_ocotp_write_field(OCOTP_SEC_CONFIG_1, 1);
+}
+
+static int imx_hab_device_locked_down_ocotp(void)
+{
+ int ret;
+ unsigned int v;
+
+ ret = imx_ocotp_read_field(OCOTP_SEC_CONFIG_1, &v);
+ if (ret < 0)
+ return ret;
+
+ return v;
+}
+
+struct imx_hab_ops {
+ int (*init)(void);
+ int (*write_srk_hash)(const u8 *srk, unsigned flags);
+ int (*read_srk_hash)(u8 *srk);
+ int (*permanent_write_enable)(int enable);
+ int (*lockdown_device)(void);
+ int (*device_locked_down)(void);
+};
+
+static struct imx_hab_ops imx_hab_ops_iim = {
+ .write_srk_hash = imx_hab_write_srk_hash_iim,
+ .read_srk_hash = imx_hab_read_srk_hash_iim,
+ .lockdown_device = imx_hab_lockdown_device_iim,
+ .device_locked_down = imx_hab_device_locked_down_iim,
+ .permanent_write_enable = imx_hab_permanent_write_enable_iim,
+};
+
+static struct imx_hab_ops imx_hab_ops_ocotp = {
+ .write_srk_hash = imx_hab_write_srk_hash_ocotp,
+ .read_srk_hash = imx_hab_read_srk_hash_ocotp,
+ .lockdown_device = imx_hab_lockdown_device_ocotp,
+ .device_locked_down = imx_hab_device_locked_down_ocotp,
+ .permanent_write_enable = imx_hab_permanent_write_enable_ocotp,
+};
+
+static struct imx_hab_ops *imx_get_hab_ops(void)
+{
+ static struct imx_hab_ops *ops, *tmp;
+ int ret;
+
+ if (ops)
+ return ops;
+
+ if (cpu_is_mx25() || cpu_is_mx35())
+ tmp = &imx_hab_ops_iim;
+ else if (cpu_is_mx6())
+ tmp = &imx_hab_ops_ocotp;
+ else
+ return NULL;
+
+ if (tmp->init) {
+ ret = tmp->init();
+ if (ret)
+ return NULL;
+ }
+
+ ops = tmp;
+
+ return ops;
+}
+
+int imx_hab_read_srk_hash(void *buf)
+{
+ struct imx_hab_ops *ops = imx_get_hab_ops();
+
+ if (!ops)
+ return -ENOSYS;
+
+ return ops->read_srk_hash(buf);
+}
+
+int imx_hab_write_srk_hash(const void *buf, unsigned flags)
+{
+ u8 cursrk[SRK_HASH_SIZE];
+ int ret;
+ struct imx_hab_ops *ops = imx_get_hab_ops();
+
+ if (!ops)
+ return -ENOSYS;
+
+ ret = ops->read_srk_hash(cursrk);
+ if (ret) {
+ pr_err("Cannot read current SRK hash: %s\n", strerror(-ret));
+ return ret;
+ }
+
+ if (imx_hab_srk_hash_valid(cursrk)) {
+ char *str = "Current SRK hash is valid";
+
+ if (flags & IMX_SRK_HASH_FORCE) {
+ pr_warn("%s, ignoring\n", str);
+ } else {
+ pr_err("%s, refusing to burn again\n", str);
+ return -EEXIST;
+ }
+ }
+
+ if (flags & IMX_SRK_HASH_WRITE_PERMANENT) {
+ ret = ops->permanent_write_enable(1);
+ if (ret)
+ return ret;
+ }
+
+ ret = ops->write_srk_hash(buf, flags);
+
+ if (flags & IMX_SRK_HASH_WRITE_PERMANENT)
+ ops->permanent_write_enable(0);
+
+ return ret;
+}
+
+int imx_hab_write_srk_hash_file(const char *filename, unsigned flags)
+{
+ int ret;
+ size_t size;
+ void *srk;
+
+ ret = read_file_2(filename, &size, &srk, FILESIZE_MAX);
+ if (ret)
+ return ret;
+
+ if (size != SRK_HASH_SIZE) {
+ pr_err("File has wrong size, must be %d bytes\n", SRK_HASH_SIZE);
+ return -EINVAL;
+ }
+
+ ret = imx_hab_write_srk_hash(srk, flags);
+
+ free(srk);
+
+ return ret;
+}
+
+int imx_hab_write_srk_hash_hex(const char *srkhash, unsigned flags)
+{
+ int ret;
+ u8 srk[SRK_HASH_SIZE];
+
+ if (strlen(srkhash) != SRK_HASH_SIZE * 2) {
+ pr_err("Invalid srk hash %s\n", srkhash);
+ return -EINVAL;
+ }
+
+ ret = hex2bin(srk, srkhash, SRK_HASH_SIZE);
+ if (ret < 0) {
+ pr_err("Invalid srk hash %s\n", srkhash);
+ return -EINVAL;
+ }
+
+ return imx_hab_write_srk_hash(srk, flags);
+}
+
+int imx_hab_lockdown_device(unsigned flags)
+{
+ struct imx_hab_ops *ops = imx_get_hab_ops();
+ u8 srk[SRK_HASH_SIZE];
+ int ret;
+
+ ret = imx_hab_read_srk_hash(srk);
+ if (ret)
+ return ret;
+
+ if (!imx_hab_srk_hash_valid(srk)) {
+ pr_err("No SRK hash burnt into fuses. Refusing to lock device\n");
+ return -EINVAL;
+ }
+
+ if (!ops)
+ return -ENOSYS;
+
+ if (flags & IMX_SRK_HASH_WRITE_PERMANENT) {
+ ret = ops->permanent_write_enable(1);
+ if (ret)
+ return ret;
+ }
+
+ ret = ops->lockdown_device();
+
+ if (flags & IMX_SRK_HASH_WRITE_PERMANENT)
+ ops->permanent_write_enable(0);
+
+ return ret;
+}
+
+int imx_hab_device_locked_down(void)
+{
+ struct imx_hab_ops *ops = imx_get_hab_ops();
+
+ return ops->device_locked_down();
+}
diff --git a/drivers/hw_random/Kconfig b/drivers/hw_random/Kconfig
new file mode 100644
index 0000000000..807fcadd31
--- /dev/null
+++ b/drivers/hw_random/Kconfig
@@ -0,0 +1,6 @@
+menuconfig HWRNG
+ bool "HWRNG Support"
+ help
+ Support for HWRNG(Hardware Random Number Generator) devices.
+
+ If unsure, say no.
diff --git a/drivers/hw_random/Makefile b/drivers/hw_random/Makefile
new file mode 100644
index 0000000000..15307b100f
--- /dev/null
+++ b/drivers/hw_random/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_HWRNG) += core.o
diff --git a/drivers/hw_random/core.c b/drivers/hw_random/core.c
new file mode 100644
index 0000000000..ef2a988c76
--- /dev/null
+++ b/drivers/hw_random/core.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2016 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de>
+ *
+ * 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.
+ *
+ * derived from Linux kernel drivers/char/hw_random/core.c
+ */
+
+#include <common.h>
+#include <linux/hw_random.h>
+#include <malloc.h>
+
+static LIST_HEAD(hwrngs);
+
+#define RNG_BUFFER_SIZE 32
+
+int hwrng_get_data(struct hwrng *rng, void *buffer, size_t size, int wait)
+{
+ return rng->read(rng, buffer, size, wait);
+}
+
+static int hwrng_init(struct hwrng *rng)
+{
+ int ret = 0;
+
+ if (rng->init)
+ ret = rng->init(rng);
+
+ if (!ret)
+ list_add_tail(&rng->list, &hwrngs);
+
+ return ret;
+}
+
+static ssize_t rng_dev_read(struct cdev *cdev, void *buf, size_t size,
+ loff_t offset, unsigned long flags)
+{
+ struct hwrng *rng = container_of(cdev, struct hwrng, cdev);
+ size_t count = size;
+ ssize_t cur = 0;
+ int len;
+
+ memset(buf, 0, size);
+
+ while (count) {
+ int max = min(count, (size_t)RNG_BUFFER_SIZE);
+ len = hwrng_get_data(rng, rng->buf, max, true);
+ if (len < 0) {
+ cur = len;
+ break;
+ }
+
+ memcpy(buf + cur, rng->buf, len);
+
+ count -= len;
+ cur += len;
+ }
+
+ return cur;
+}
+
+static struct file_operations rng_chrdev_ops = {
+ .read = rng_dev_read,
+ .lseek = dev_lseek_default,
+};
+
+static int hwrng_register_cdev(struct hwrng *rng)
+{
+ struct device_d *dev = rng->dev;
+ const char *alias;
+ char *devname;
+ int err;
+
+ alias = of_alias_get(dev->device_node);
+ if (alias) {
+ devname = xstrdup(alias);
+ } else {
+ err = cdev_find_free_index("hwrng");
+ if (err < 0) {
+ dev_err(dev, "no index found to name device\n");
+ return err;
+ }
+ devname = xasprintf("hwrng%d", err);
+ }
+
+ rng->cdev.name = devname;
+ rng->cdev.flags = DEVFS_IS_CHARACTER_DEV;
+ rng->cdev.ops = &rng_chrdev_ops;
+ rng->cdev.dev = rng->dev;
+
+ return devfs_create(&rng->cdev);
+}
+
+struct hwrng *hwrng_get_first(void)
+{
+ if (list_empty(&hwrngs))
+ return ERR_PTR(-ENODEV);
+ else
+ return list_first_entry(&hwrngs, struct hwrng, list);
+}
+
+int hwrng_register(struct device_d *dev, struct hwrng *rng)
+{
+ int err;
+
+ if (rng->name == NULL || rng->read == NULL)
+ return -EINVAL;
+
+ rng->buf = xzalloc(RNG_BUFFER_SIZE);
+ rng->dev = dev;
+
+ err = hwrng_init(rng);
+ if (err) {
+ free(rng->buf);
+ return err;
+ }
+
+ err = hwrng_register_cdev(rng);
+ if (err)
+ free(rng->buf);
+
+ return err;
+}
diff --git a/drivers/led/Kconfig b/drivers/led/Kconfig
index 155a78a7df..50f0d8f974 100644
--- a/drivers/led/Kconfig
+++ b/drivers/led/Kconfig
@@ -1,5 +1,6 @@
menuconfig LED
bool "LED support"
+ select POLLER
if LED
diff --git a/drivers/led/core.c b/drivers/led/core.c
index 30b016bb34..6f66de0fbb 100644
--- a/drivers/led/core.c
+++ b/drivers/led/core.c
@@ -23,6 +23,7 @@
#include <linux/list.h>
#include <errno.h>
#include <led.h>
+#include <init.h>
#include <poller.h>
#include <clock.h>
#include <linux/ctype.h>
@@ -101,7 +102,7 @@ struct led *led_by_name_or_number(const char *str)
* @param led the led
* @param value the value of the LED (0 is disabled)
*/
-int led_set(struct led *led, unsigned int value)
+static int __led_set(struct led *led, unsigned int value)
{
if (value > led->max_value)
value = led->max_value;
@@ -114,6 +115,104 @@ int led_set(struct led *led, unsigned int value)
return 0;
}
+int led_set(struct led *led, unsigned int value)
+{
+ led->blink = 0;
+ led->flash = 0;
+ return __led_set(led, value);
+}
+
+static void led_blink_func(struct poller_struct *poller)
+{
+ struct led *led;
+
+ list_for_each_entry(led, &leds, list) {
+ bool on;
+
+ if (!led->blink && !led->flash)
+ continue;
+
+ if (led->blink_next_event > get_time_ns()) {
+ continue;
+ }
+
+ on = !(led->blink_next_state % 2);
+
+ led->blink_next_event = get_time_ns() +
+ (led->blink_states[led->blink_next_state] * MSECOND);
+ led->blink_next_state = (led->blink_next_state + 1) %
+ led->blink_nr_states;
+
+ if (led->flash && !on)
+ led->flash = 0;
+
+ __led_set(led, on);
+ }
+}
+
+/**
+ * led_blink_pattern - Blink a led with flexible timings.
+ * @led LED used
+ * @pattern Array of millisecond intervals describing the on and off periods of
+ * the pattern. At the end of the array/pattern it is repeated. The array
+ * starts with an on-period. In general every array item with even index
+ * describes an on-period, every item with odd index an off-period.
+ * @pattern_len Length of the pattern array.
+ *
+ * Returns 0 on success.
+ *
+ * Example:
+ * pattern = {500, 1000};
+ * This will enable the LED for 500ms and disable it for 1000ms after
+ * that. This is repeated forever.
+ */
+int led_blink_pattern(struct led *led, const unsigned int *pattern,
+ unsigned int pattern_len)
+{
+ free(led->blink_states);
+ led->blink_states = xmemdup(pattern,
+ pattern_len * sizeof(*led->blink_states));
+ led->blink_nr_states = pattern_len;
+ led->blink_next_state = 0;
+ led->blink_next_event = get_time_ns();
+ led->blink = 1;
+ led->flash = 0;
+
+ return 0;
+}
+
+int led_blink(struct led *led, unsigned int on_ms, unsigned int off_ms)
+{
+ unsigned int pattern[] = {on_ms, off_ms};
+
+ return led_blink_pattern(led, pattern, 2);
+}
+
+int led_flash(struct led *led, unsigned int duration_ms)
+{
+ unsigned int pattern[] = {duration_ms, 0};
+ int ret;
+
+ ret = led_blink_pattern(led, pattern, 2);
+ if (ret)
+ return ret;
+
+ led->flash = 1;
+ led->blink = 0;
+
+ return 0;
+}
+
+static struct poller_struct led_poller = {
+ .func = led_blink_func,
+};
+
+static int led_blink_init(void)
+{
+ return poller_register(&led_poller);
+}
+late_initcall(led_blink_init);
+
/**
* led_set_num - set the value of a LED
* @param num the number of the LED
diff --git a/drivers/led/led-triggers.c b/drivers/led/led-triggers.c
index dee936739a..76a1481e14 100644
--- a/drivers/led/led-triggers.c
+++ b/drivers/led/led-triggers.c
@@ -48,33 +48,11 @@
struct led_trigger_struct {
struct led *led;
- uint64_t flash_start;
- int flash;
+ struct list_head list;
+ enum led_trigger trigger;
};
-static struct led_trigger_struct triggers[LED_TRIGGER_MAX];
-
-static void trigger_func(struct poller_struct *poller)
-{
- int i;
-
- for (i = 0; i < LED_TRIGGER_MAX; i++) {
- if (triggers[i].led &&
- triggers[i].flash &&
- is_timeout(triggers[i].flash_start, 200 * MSECOND)) {
- led_set(triggers[i].led, 0);
- triggers[i].flash = 0;
- }
- }
-
- if (triggers[LED_TRIGGER_HEARTBEAT].led &&
- is_timeout(triggers[LED_TRIGGER_HEARTBEAT].flash_start, SECOND))
- led_trigger(LED_TRIGGER_HEARTBEAT, TRIGGER_FLASH);
-}
-
-static struct poller_struct trigger_poller = {
- .func = trigger_func,
-};
+static LIST_HEAD(led_triggers);
/**
* led_trigger - triggers a trigger
@@ -85,21 +63,53 @@ static struct poller_struct trigger_poller = {
*/
void led_trigger(enum led_trigger trigger, enum trigger_type type)
{
+ struct led_trigger_struct *led_trigger;
+
if (trigger >= LED_TRIGGER_MAX)
return;
- if (!triggers[trigger].led)
- return;
- if (type == TRIGGER_FLASH) {
- if (is_timeout(triggers[trigger].flash_start, 400 * MSECOND)) {
- led_set(triggers[trigger].led, triggers[trigger].led->max_value);
- triggers[trigger].flash_start = get_time_ns();
- triggers[trigger].flash = 1;
+ list_for_each_entry(led_trigger, &led_triggers, list) {
+ if (led_trigger->trigger != trigger)
+ continue;
+
+ switch (type) {
+ case TRIGGER_FLASH:
+ led_flash(led_trigger->led, 200);
+ break;
+ case TRIGGER_ENABLE:
+ led_set(led_trigger->led, led_trigger->led->max_value);
+ break;
+ case TRIGGER_DISABLE:
+ led_set(led_trigger->led, 0);
+ break;
}
- return;
}
+}
+
+static struct led_trigger_struct *led_find_trigger(struct led *led)
+{
+ struct led_trigger_struct *led_trigger;
+
+ list_for_each_entry(led_trigger, &led_triggers, list)
+ if (led_trigger->led == led)
+ return led_trigger;
+
+ return NULL;
+}
+
+void led_trigger_disable(struct led *led)
+{
+ struct led_trigger_struct *led_trigger;
+
+ led_trigger = led_find_trigger(led);
+ if (!led_trigger)
+ return;
+
+ list_del(&led_trigger->list);
+
+ led_set(led, 0);
- led_set(triggers[trigger].led, type == TRIGGER_ENABLE ? triggers[trigger].led->max_value : 0);
+ free(led_trigger);
}
/**
@@ -112,44 +122,73 @@ void led_trigger(enum led_trigger trigger, enum trigger_type type)
*/
int led_set_trigger(enum led_trigger trigger, struct led *led)
{
- int i;
+ struct led_trigger_struct *led_trigger;
if (trigger >= LED_TRIGGER_MAX)
return -EINVAL;
- if (led)
- for (i = 0; i < LED_TRIGGER_MAX; i++)
- if (triggers[i].led == led)
- return -EBUSY;
+ led_trigger_disable(led);
- if (triggers[trigger].led && !led)
- led_set(triggers[trigger].led, 0);
+ led_trigger = xzalloc(sizeof(*led_trigger));
- triggers[trigger].led = led;
+ led_trigger->led = led;
+ led_trigger->trigger = trigger;
+ list_add_tail(&led_trigger->list, &led_triggers);
- if (led && trigger == LED_TRIGGER_DEFAULT_ON)
- led_set(triggers[trigger].led, triggers[trigger].led->max_value);
+ if (trigger == LED_TRIGGER_DEFAULT_ON)
+ led_set(led, led->max_value);
+ if (trigger == LED_TRIGGER_HEARTBEAT)
+ led_blink(led, 200, 1000);
return 0;
}
-/**
- * led_get_trigger - get the LED for a trigger
- * @param trigger The trigger to set a LED for
- *
- * return the LED number of a trigger.
- */
-int led_get_trigger(enum led_trigger trigger)
+static char *trigger_names[] = {
+ [LED_TRIGGER_PANIC] = "panic",
+ [LED_TRIGGER_HEARTBEAT] = "heartbeat",
+ [LED_TRIGGER_NET_RX] = "net-rx",
+ [LED_TRIGGER_NET_TX] = "net-tx",
+ [LED_TRIGGER_NET_TXRX] = "net",
+ [LED_TRIGGER_DEFAULT_ON] = "default-on",
+};
+
+const char *trigger_name(enum led_trigger trigger)
{
- if (trigger >= LED_TRIGGER_MAX)
- return -EINVAL;
- if (!triggers[trigger].led)
- return -ENODEV;
- return led_get_number(triggers[trigger].led);
+ return trigger_names[trigger];
+}
+
+enum led_trigger trigger_by_name(const char *name)
+{
+ int i;
+
+ for (i = 0; i < LED_TRIGGER_MAX; i++)
+ if (!strcmp(name, trigger_names[i]))
+ return i;
+
+ return LED_TRIGGER_MAX;
}
-static int trigger_init(void)
+/**
+ * led_triggers_show_info - Show information about all registered
+ * triggers
+ */
+void led_triggers_show_info(void)
{
- return poller_register(&trigger_poller);
+ struct led_trigger_struct *led_trigger;
+ int i;
+
+ for (i = 0; i < LED_TRIGGER_MAX; i++) {
+ printf("%s", trigger_name(i));
+
+ list_for_each_entry(led_trigger, &led_triggers, list) {
+ struct led *led = led_trigger->led;
+
+ if (led_trigger->trigger != i)
+ continue;
+
+ printf("\n LED %d (%s)", led->num, led->name);
+ }
+
+ printf("\n");
+ }
}
-late_initcall(trigger_init);
diff --git a/drivers/mci/atmel_mci.c b/drivers/mci/atmel_mci.c
index 2a0ddb052b..317cf46022 100644
--- a/drivers/mci/atmel_mci.c
+++ b/drivers/mci/atmel_mci.c
@@ -24,6 +24,7 @@
#include <mach/board.h>
#include <linux/clk.h>
#include <linux/err.h>
+#include <of_gpio.h>
#include "atmel-mci-regs.h"
@@ -53,6 +54,7 @@ struct atmel_mci {
u32 cfg_reg;
u32 sdc_reg;
bool need_reset;
+ int detect_pin;
};
#define to_mci_host(mci) container_of(mci, struct atmel_mci, mci)
@@ -360,14 +362,13 @@ static int atmci_start_cmd(struct atmel_mci *host, struct mci_cmd *cmd,
static int atmci_card_present(struct mci_host *mci)
{
struct atmel_mci *host = to_mci_host(mci);
- struct atmel_mci_platform_data *pd = host->hw_dev->platform_data;
int ret;
/* No gpio, assume card is present */
- if (!gpio_is_valid(pd->detect_pin))
+ if (!gpio_is_valid(host->detect_pin))
return 1;
- ret = gpio_get_value(pd->detect_pin);
+ ret = gpio_get_value(host->detect_pin);
return ret == 0 ? 1 : 0;
}
@@ -535,44 +536,71 @@ static int atmci_probe(struct device_d *hw_dev)
{
struct resource *iores;
struct atmel_mci *host;
+ struct device_node *np = hw_dev->device_node;
struct atmel_mci_platform_data *pd = hw_dev->platform_data;
int ret;
- if (!pd) {
- dev_err(hw_dev, "missing platform data\n");
- return -EINVAL;
+ host = xzalloc(sizeof(*host));
+ host->mci.send_cmd = atmci_request;
+ host->mci.set_ios = atmci_set_ios;
+ host->mci.init = atmci_reset;
+ host->mci.card_present = atmci_card_present;
+ host->mci.hw_dev = hw_dev;
+ host->detect_pin = -EINVAL;
+
+ if (pd) {
+ host->detect_pin = pd->detect_pin;
+ host->mci.devname = pd->devname;
+
+ if (pd->bus_width >= 4)
+ host->mci.host_caps |= MMC_CAP_4_BIT_DATA;
+ if (pd->bus_width == 8)
+ host->mci.host_caps |= MMC_CAP_8_BIT_DATA;
+
+ host->slot_b = pd->slot_b;
+ } else if (np) {
+ u32 slot_id;
+ struct device_node *cnp;
+ const char *alias = of_alias_get(np);
+
+ if (alias)
+ host->mci.devname = xstrdup(alias);
+
+ host->detect_pin = of_get_named_gpio(np, "cd-gpios", 0);
+
+ for_each_child_of_node(np, cnp) {
+ if (of_property_read_u32(cnp, "reg", &slot_id)) {
+ dev_warn(hw_dev, "reg property is missing for %s\n",
+ cnp->full_name);
+ continue;
+ }
+
+ host->slot_b = slot_id;
+ mci_of_parse_node(&host->mci, cnp);
+ break;
+ }
+ } else {
+ dev_err(hw_dev, "Missing device information\n");
+ ret = -EINVAL;
+ goto error_out;
}
- if (gpio_is_valid(pd->detect_pin)) {
- ret = gpio_request(pd->detect_pin, "mci_cd");
+ if (gpio_is_valid(host->detect_pin)) {
+ ret = gpio_request(host->detect_pin, "mci_cd");
if (ret) {
dev_err(hw_dev, "Impossible to request CD gpio %d (%d)\n",
- ret, pd->detect_pin);
- return ret;
+ ret, host->detect_pin);
+ goto error_out;
}
- ret = gpio_direction_input(pd->detect_pin);
+ ret = gpio_direction_input(host->detect_pin);
if (ret) {
dev_err(hw_dev, "Impossible to configure CD gpio %d as input (%d)\n",
- ret, pd->detect_pin);
- goto err_gpio_cd_request;
+ ret, host->detect_pin);
+ goto error_out;
}
}
- host = xzalloc(sizeof(*host));
- host->mci.send_cmd = atmci_request;
- host->mci.set_ios = atmci_set_ios;
- host->mci.init = atmci_reset;
- host->mci.card_present = atmci_card_present;
- host->mci.hw_dev = hw_dev;
- host->mci.devname = pd->devname;
-
- if (pd->bus_width >= 4)
- host->mci.host_caps |= MMC_CAP_4_BIT_DATA;
- if (pd->bus_width == 8)
- host->mci.host_caps |= MMC_CAP_8_BIT_DATA;
- host->slot_b = pd->slot_b;
-
iores = dev_request_mem_resource(hw_dev, 0);
if (IS_ERR(iores))
return PTR_ERR(iores);
@@ -583,7 +611,7 @@ static int atmci_probe(struct device_d *hw_dev)
if (IS_ERR(host->clk)) {
dev_err(hw_dev, "no mci_clk\n");
ret = PTR_ERR(host->clk);
- goto err_gpio_cd_request;
+ goto error_out;
}
clk_enable(host->clk);
@@ -614,15 +642,26 @@ static int atmci_probe(struct device_d *hw_dev)
return 0;
-err_gpio_cd_request:
- if (gpio_is_valid(pd->detect_pin))
- gpio_free(pd->detect_pin);
+error_out:
+ free(host);
+
+ if (gpio_is_valid(host->detect_pin))
+ gpio_free(host->detect_pin);
return ret;
}
+static __maybe_unused struct of_device_id atmci_compatible[] = {
+ {
+ .compatible = "atmel,hsmci",
+ }, {
+ /* sentinel */
+ }
+};
+
static struct driver_d atmci_driver = {
.name = "atmel_mci",
.probe = atmci_probe,
+ .of_compatible = DRV_OF_COMPAT(atmci_compatible),
};
device_platform_driver(atmci_driver);
diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c
index 055a5e2b06..b173a173b1 100644
--- a/drivers/mci/mci-core.c
+++ b/drivers/mci/mci-core.c
@@ -1621,8 +1621,10 @@ static int mci_register_partition(struct mci_part *part)
rc = 0; /* it's not a failure */
}
- if (np)
+ if (np) {
of_parse_partitions(&part->blk.cdev, np);
+ of_partitions_register_fixup(&part->blk.cdev);
+ }
return 0;
}
@@ -1843,20 +1845,18 @@ err_free:
return ret;
}
-void mci_of_parse(struct mci_host *host)
+void mci_of_parse_node(struct mci_host *host,
+ struct device_node *np)
{
- struct device_node *np;
u32 bus_width;
u32 dsr_val;
if (!IS_ENABLED(CONFIG_OFDEVICE))
return;
- if (!host->hw_dev || !host->hw_dev->device_node)
+ if (!host->hw_dev || !np)
return;
- np = host->hw_dev->device_node;
-
/* "bus-width" is translated to MMC_CAP_*_BIT_DATA flags */
if (of_property_read_u32(np, "bus-width", &bus_width) < 0) {
/* If bus-width is missing we get the driver's default, which
@@ -1897,6 +1897,11 @@ void mci_of_parse(struct mci_host *host)
host->non_removable = of_property_read_bool(np, "non-removable");
}
+void mci_of_parse(struct mci_host *host)
+{
+ return mci_of_parse_node(host, host->hw_dev->device_node);
+}
+
struct mci *mci_get_device_by_name(const char *name)
{
struct mci *mci;
diff --git a/drivers/misc/state.c b/drivers/misc/state.c
index b43aee60fe..98ed42e757 100644
--- a/drivers/misc/state.c
+++ b/drivers/misc/state.c
@@ -26,6 +26,7 @@ static int state_probe(struct device_d *dev)
struct device_node *np = dev->device_node;
struct state *state;
bool readonly = false;
+ int ret;
state = state_new_from_node(np, NULL, 0, 0, readonly);
if (IS_ERR(state)) {
@@ -35,6 +36,11 @@ static int state_probe(struct device_d *dev)
return ret;
}
+ ret = state_load(state);
+ if (ret)
+ dev_warn(dev, "Failed to load persistent state, continuing with defaults, %d\n",
+ ret);
+
return 0;
}
diff --git a/drivers/mtd/core.c b/drivers/mtd/core.c
index 4e7bfdb3da..1eb8dd36d8 100644
--- a/drivers/mtd/core.c
+++ b/drivers/mtd/core.c
@@ -556,67 +556,6 @@ static int mtd_part_compare(struct list_head *a, struct list_head *b)
return 0;
}
-static int of_mtd_fixup(struct device_node *root, void *ctx)
-{
- struct mtd_info *mtd = ctx, *partmtd;
- struct device_node *np, *part, *tmp;
- int ret;
-
- np = of_find_node_by_path_from(root, mtd->of_path);
- if (!np) {
- dev_err(&mtd->class_dev, "Cannot find nodepath %s, cannot fixup\n",
- mtd->of_path);
- return -EINVAL;
- }
-
- for_each_child_of_node_safe(np, tmp, part) {
- if (of_get_property(part, "compatible", NULL))
- continue;
- of_delete_node(part);
- }
-
- list_for_each_entry(partmtd, &mtd->partitions, partitions_entry) {
- int na, ns, len = 0;
- char *name = basprintf("partition@%0llx",
- partmtd->master_offset);
- void *p;
- u8 tmp[16 * 16]; /* Up to 64-bit address + 64-bit size */
-
- if (!name)
- return -ENOMEM;
-
- part = of_new_node(np, name);
- free(name);
- if (!part)
- return -ENOMEM;
-
- p = of_new_property(part, "label", partmtd->cdev.partname,
- strlen(partmtd->cdev.partname) + 1);
- if (!p)
- return -ENOMEM;
-
- na = of_n_addr_cells(part);
- ns = of_n_size_cells(part);
-
- of_write_number(tmp + len, partmtd->master_offset, na);
- len += na * 4;
- of_write_number(tmp + len, partmtd->size, ns);
- len += ns * 4;
-
- ret = of_set_property(part, "reg", tmp, len, 1);
- if (ret)
- return ret;
-
- if (partmtd->cdev.flags & DEVFS_PARTITION_READONLY) {
- ret = of_set_property(part, "read-only", NULL, 0, 1);
- if (ret)
- return ret;
- }
- }
-
- return 0;
-}
-
static int mtd_detect(struct device_d *dev)
{
struct mtd_info *mtd = container_of(dev, struct mtd_info, class_dev);
@@ -732,7 +671,9 @@ int add_mtd_device(struct mtd_info *mtd, const char *devname, int device_id)
of_parse_partitions(&mtd->cdev, mtd->parent->device_node);
if (IS_ENABLED(CONFIG_OFDEVICE) && mtd->parent->device_node) {
mtd->of_path = xstrdup(mtd->parent->device_node->full_name);
- of_register_fixup(of_mtd_fixup, mtd);
+ ret = of_partitions_register_fixup(&mtd->cdev);
+ if (ret)
+ goto err1;
}
}
diff --git a/drivers/mtd/partition.c b/drivers/mtd/partition.c
index 777cb758ce..013697732d 100644
--- a/drivers/mtd/partition.c
+++ b/drivers/mtd/partition.c
@@ -225,6 +225,8 @@ struct mtd_info *mtd_add_partition(struct mtd_info *mtd, off_t offset,
if (ret)
goto err;
+ part->cdev.master = &part->master->cdev;
+
return part;
err:
free(part->cdev.partname);
diff --git a/drivers/mtd/peb.c b/drivers/mtd/peb.c
index 66227d4ffb..c35b63f2fd 100644
--- a/drivers/mtd/peb.c
+++ b/drivers/mtd/peb.c
@@ -565,6 +565,8 @@ int mtd_peb_create_bitflips(struct mtd_info *mtd, int pnum, int offset,
if (offset < 0 || offset + len > mtd->erasesize)
return -EINVAL;
+ if (offset % mtd->writesize)
+ return -EINVAL;
if (len <= 0)
return -EINVAL;
if (num_bitflips <= 0)
@@ -610,7 +612,7 @@ int mtd_peb_create_bitflips(struct mtd_info *mtd, int pnum, int offset,
for (i = 0; i < num_bitflips; i++) {
int offs;
int bit;
- u8 *pos = buf;
+ u8 *pos = buf + offset;
if (random) {
offs = random32() % len;
diff --git a/drivers/net/macb.c b/drivers/net/macb.c
index 5f2e5e5131..739a3dfbef 100644
--- a/drivers/net/macb.c
+++ b/drivers/net/macb.c
@@ -48,6 +48,7 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/phy.h>
+#include <of_net.h>
#include "macb.h"
@@ -615,14 +616,8 @@ static int macb_probe(struct device_d *dev)
struct resource *iores;
struct eth_device *edev;
struct macb_device *macb;
+ const char *pclk_name;
u32 ncfgr;
- struct macb_platform_data *pdata;
-
- if (!dev->platform_data) {
- dev_err(dev, "macb: no platform_data\n");
- return -ENODEV;
- }
- pdata = dev->platform_data;
edev = xzalloc(sizeof(struct eth_device) + sizeof(struct macb_device));
edev->priv = (struct macb_device *)(edev + 1);
@@ -633,22 +628,49 @@ static int macb_probe(struct device_d *dev)
edev->open = macb_open;
edev->send = macb_send;
edev->halt = macb_halt;
- edev->get_ethaddr = pdata->get_ethaddr ? pdata->get_ethaddr : macb_get_ethaddr;
+ edev->get_ethaddr = macb_get_ethaddr;
edev->set_ethaddr = macb_set_ethaddr;
edev->parent = dev;
macb->miibus.read = macb_phy_read;
macb->miibus.write = macb_phy_write;
- macb->phy_addr = pdata->phy_addr;
macb->miibus.priv = macb;
macb->miibus.parent = dev;
- if (pdata->phy_interface == PHY_INTERFACE_MODE_NA)
- macb->interface = PHY_INTERFACE_MODE_MII;
- else
- macb->interface = pdata->phy_interface;
+ if (dev->platform_data) {
+ struct macb_platform_data *pdata = dev->platform_data;
+
+ if (pdata->phy_interface == PHY_INTERFACE_MODE_NA)
+ macb->interface = PHY_INTERFACE_MODE_MII;
+ else
+ macb->interface = pdata->phy_interface;
+
+ if (pdata->get_ethaddr)
+ edev->get_ethaddr = pdata->get_ethaddr;
+
+ macb->phy_addr = pdata->phy_addr;
+ macb->phy_flags = pdata->phy_flags;
+ pclk_name = "macb_clk";
+ } else if (IS_ENABLED(CONFIG_OFDEVICE) && dev->device_node) {
+ int ret;
+ struct device_node *mdiobus;
- macb->phy_flags = pdata->phy_flags;
+ ret = of_get_phy_mode(dev->device_node);
+ if (ret < 0)
+ macb->interface = PHY_INTERFACE_MODE_MII;
+ else
+ macb->interface = ret;
+
+ mdiobus = of_get_child_by_name(dev->device_node, "mdio");
+ if (mdiobus)
+ macb->miibus.dev.device_node = mdiobus;
+
+ macb->phy_addr = -1;
+ pclk_name = NULL;
+ } else {
+ dev_err(dev, "macb: no platform_data\n");
+ return -ENODEV;
+ }
iores = dev_request_mem_resource(dev, 0);
if (IS_ERR(iores))
@@ -698,8 +720,14 @@ static int macb_probe(struct device_d *dev)
return 0;
}
+static const struct of_device_id macb_dt_ids[] = {
+ { .compatible = "cdns,at91sam9260-macb",},
+ { /* sentinel */ }
+};
+
static struct driver_d macb_driver = {
.name = "macb",
.probe = macb_probe,
+ .of_compatible = DRV_OF_COMPAT(macb_dt_ids),
};
device_platform_driver(macb_driver);
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
index 38b2ad31f8..ea5a4a9be2 100644
--- a/drivers/net/phy/marvell.c
+++ b/drivers/net/phy/marvell.c
@@ -31,6 +31,22 @@
#define MII_M1011_PHY_STATUS_RESOLVED BIT(11)
#define MII_M1011_PHY_STATUS_LINK BIT(10)
+#define MII_M1111_PHY_LED_CONTROL 0x18
+#define MII_M1111_PHY_LED_DIRECT 0x4100
+#define MII_M1111_PHY_LED_COMBINE 0x411c
+#define MII_M1111_PHY_EXT_CR 0x14
+#define MII_M1111_RX_DELAY 0x80
+#define MII_M1111_TX_DELAY 0x2
+#define MII_M1111_PHY_EXT_SR 0x1b
+
+#define MII_M1111_HWCFG_MODE_MASK 0xf
+#define MII_M1111_HWCFG_MODE_COPPER_RGMII 0xb
+#define MII_M1111_HWCFG_MODE_FIBER_RGMII 0x3
+#define MII_M1111_HWCFG_MODE_SGMII_NO_CLK 0x4
+#define MII_M1111_HWCFG_MODE_COPPER_RTBI 0x9
+#define MII_M1111_HWCFG_FIBER_COPPER_AUTO 0x8000
+#define MII_M1111_HWCFG_FIBER_COPPER_RES 0x2000
+
#define MII_M1111_COPPER 0
#define MII_M1111_FIBER 1
@@ -402,6 +418,107 @@ static int m88e1540_config_init(struct phy_device *phydev)
return marvell_config_init(phydev);
}
+static int m88e1111_config_init(struct phy_device *phydev)
+{
+ int err;
+ int temp;
+
+ if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) ||
+ (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) ||
+ (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) ||
+ (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)) {
+
+ temp = phy_read(phydev, MII_M1111_PHY_EXT_CR);
+ if (temp < 0)
+ return temp;
+
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
+ temp |= (MII_M1111_RX_DELAY | MII_M1111_TX_DELAY);
+ } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
+ temp &= ~MII_M1111_TX_DELAY;
+ temp |= MII_M1111_RX_DELAY;
+ } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
+ temp &= ~MII_M1111_RX_DELAY;
+ temp |= MII_M1111_TX_DELAY;
+ }
+
+ err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp);
+ if (err < 0)
+ return err;
+
+ temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
+ if (temp < 0)
+ return temp;
+
+ temp &= ~(MII_M1111_HWCFG_MODE_MASK);
+
+ if (temp & MII_M1111_HWCFG_FIBER_COPPER_RES)
+ temp |= MII_M1111_HWCFG_MODE_FIBER_RGMII;
+ else
+ temp |= MII_M1111_HWCFG_MODE_COPPER_RGMII;
+
+ err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
+ if (err < 0)
+ return err;
+ }
+
+ if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
+ temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
+ if (temp < 0)
+ return temp;
+
+ temp &= ~(MII_M1111_HWCFG_MODE_MASK);
+ temp |= MII_M1111_HWCFG_MODE_SGMII_NO_CLK;
+ temp |= MII_M1111_HWCFG_FIBER_COPPER_AUTO;
+
+ err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
+ if (err < 0)
+ return err;
+ }
+
+ if (phydev->interface == PHY_INTERFACE_MODE_RTBI) {
+ temp = phy_read(phydev, MII_M1111_PHY_EXT_CR);
+ if (temp < 0)
+ return temp;
+ temp |= (MII_M1111_RX_DELAY | MII_M1111_TX_DELAY);
+ err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp);
+ if (err < 0)
+ return err;
+
+ temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
+ if (temp < 0)
+ return temp;
+ temp &= ~(MII_M1111_HWCFG_MODE_MASK | MII_M1111_HWCFG_FIBER_COPPER_RES);
+ temp |= 0x7 | MII_M1111_HWCFG_FIBER_COPPER_AUTO;
+ err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
+ if (err < 0)
+ return err;
+
+ /* soft reset */
+ err = phy_write(phydev, MII_BMCR, BMCR_RESET);
+ if (err < 0)
+ return err;
+ do
+ temp = phy_read(phydev, MII_BMCR);
+ while (temp & BMCR_RESET);
+
+ temp = phy_read(phydev, MII_M1111_PHY_EXT_SR);
+ if (temp < 0)
+ return temp;
+ temp &= ~(MII_M1111_HWCFG_MODE_MASK | MII_M1111_HWCFG_FIBER_COPPER_RES);
+ temp |= MII_M1111_HWCFG_MODE_COPPER_RTBI | MII_M1111_HWCFG_FIBER_COPPER_AUTO;
+ err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp);
+ if (err < 0)
+ return err;
+ }
+
+ err = marvell_of_reg_init(phydev);
+ if (err < 0)
+ return err;
+
+ return phy_write(phydev, MII_BMCR, BMCR_RESET);
+}
+
static int m88e1121_config_init(struct phy_device *phydev)
{
u16 reg;
@@ -502,6 +619,15 @@ static int m88e1510_config_init(struct phy_device *phydev)
static struct phy_driver marvell_drivers[] = {
{
+ .phy_id = MARVELL_PHY_ID_88E1111,
+ .phy_id_mask = MARVELL_PHY_ID_MASK,
+ .drv.name = "Marvell 88E1111",
+ .features = PHY_GBIT_FEATURES,
+ .config_init = &m88e1111_config_init,
+ .config_aneg = &genphy_config_aneg,
+ .read_status = &marvell_read_status,
+ },
+ {
.phy_id = MARVELL_PHY_ID_88E1121R,
.phy_id_mask = MARVELL_PHY_ID_MASK,
.drv.name = "Marvell 88E1121R",
diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c
index a839f2dee8..a846fb28e2 100644
--- a/drivers/net/phy/mdio-gpio.c
+++ b/drivers/net/phy/mdio-gpio.c
@@ -45,7 +45,7 @@ struct mdio_gpio_info {
int mdc_active_low, mdio_active_low, mdo_active_low;
};
-struct mdio_gpio_info *mdio_gpio_of_get_info(struct device_d *dev)
+static struct mdio_gpio_info *mdio_gpio_of_get_info(struct device_d *dev)
{
int ret;
enum of_gpio_flags flags;
diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
new file mode 100644
index 0000000000..d801cc25af
--- /dev/null
+++ b/drivers/nvmem/Kconfig
@@ -0,0 +1,18 @@
+menuconfig NVMEM
+ bool "NVMEM Support"
+ help
+ Support for NVMEM(Non Volatile Memory) devices like EEPROM, EFUSES...
+
+ This framework is designed to provide a generic interface to NVMEM
+
+ If unsure, say no.
+
+if NVMEM
+
+config NVMEM_SNVS_LPGPR
+ tristate "Freescale SNVS LPGPR support"
+ select MFD_SYSCON
+ help
+ If you say yes here you get NVMEM support for the Freescale SNVS
+ Low Power Generic Purpose Register (LPGPR).
+endif
diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile
new file mode 100644
index 0000000000..32522e9fbf
--- /dev/null
+++ b/drivers/nvmem/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for nvmem drivers.
+#
+
+obj-$(CONFIG_NVMEM) += nvmem_core.o
+nvmem_core-y := core.o
+
+# Devices
+obj-$(CONFIG_NVMEM_SNVS_LPGPR) += nvmem_snvs_lpgpr.o
+nvmem_snvs_lpgpr-y := snvs_lpgpr.o
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
new file mode 100644
index 0000000000..4e50a8843f
--- /dev/null
+++ b/drivers/nvmem/core.c
@@ -0,0 +1,764 @@
+/*
+ * nvmem framework core.
+ *
+ * Copyright (C) 2015 Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+ * Copyright (C) 2013 Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <common.h>
+#include <libbb.h>
+#include <malloc.h>
+#include <of.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/nvmem-provider.h>
+
+struct nvmem_device {
+ const char *name;
+ struct device_d dev;
+ const struct nvmem_bus *bus;
+ struct list_head node;
+ int stride;
+ int word_size;
+ int ncells;
+ int users;
+ size_t size;
+ bool read_only;
+ struct cdev cdev;
+};
+
+struct nvmem_cell {
+ const char *name;
+ int offset;
+ int bytes;
+ int bit_offset;
+ int nbits;
+ struct nvmem_device *nvmem;
+ struct list_head node;
+};
+
+static LIST_HEAD(nvmem_cells);
+static LIST_HEAD(nvmem_devs);
+
+int nvmem_device_read(struct nvmem_device *nvmem, unsigned int offset,
+ size_t bytes, void *buf);
+int nvmem_device_write(struct nvmem_device *nvmem, unsigned int offset,
+ size_t bytes, const void *buf);
+
+
+static ssize_t nvmem_cdev_read(struct cdev *cdev, void *buf, size_t count,
+ loff_t offset, unsigned long flags)
+{
+ struct nvmem_device *nvmem = container_of(cdev, struct nvmem_device, cdev);
+ ssize_t retlen;
+
+ dev_dbg(cdev->dev, "read ofs: 0x%08llx count: 0x%08zx\n",
+ offset, count);
+
+ retlen = nvmem_device_read(nvmem, offset, count, buf);
+
+ return retlen;
+}
+
+static ssize_t nvmem_cdev_write(struct cdev *cdev, const void *buf, size_t count,
+ loff_t offset, unsigned long flags)
+{
+ struct nvmem_device *nvmem = container_of(cdev, struct nvmem_device, cdev);
+ ssize_t retlen;
+
+ dev_dbg(cdev->dev, "write ofs: 0x%08llx count: 0x%08zx\n",
+ offset, count);
+
+ retlen = nvmem_device_write(nvmem, offset, count, buf);
+
+ return retlen;
+}
+
+static struct file_operations nvmem_chrdev_ops = {
+ .read = nvmem_cdev_read,
+ .write = nvmem_cdev_write,
+ .lseek = dev_lseek_default,
+};
+
+static int nvmem_register_cdev(struct nvmem_device *nvmem)
+{
+ struct device_d *dev = &nvmem->dev;
+ const char *alias;
+ char *devname;
+ int err;
+
+ alias = of_alias_get(dev->device_node);
+ if (alias) {
+ devname = xstrdup(alias);
+ } else {
+ err = cdev_find_free_index("nvmem");
+ if (err < 0) {
+ dev_err(dev, "no index found to name device\n");
+ return err;
+ }
+ devname = xasprintf("nvmem%d", err);
+ }
+
+ nvmem->cdev.name = devname;
+ nvmem->cdev.flags = DEVFS_IS_CHARACTER_DEV;
+ nvmem->cdev.ops = &nvmem_chrdev_ops;
+ nvmem->cdev.dev = &nvmem->dev;
+ nvmem->cdev.size = nvmem->size;
+
+ return devfs_create(&nvmem->cdev);
+}
+
+static struct nvmem_device *of_nvmem_find(struct device_node *nvmem_np)
+{
+ struct nvmem_device *dev;
+
+ if (!nvmem_np)
+ return NULL;
+
+ list_for_each_entry(dev, &nvmem_devs, node)
+ if (dev->dev.device_node->name && !strcmp(dev->dev.device_node->name, nvmem_np->name))
+ return dev;
+
+ return NULL;
+}
+
+static struct nvmem_cell *nvmem_find_cell(const char *cell_id)
+{
+ struct nvmem_cell *p;
+
+ list_for_each_entry(p, &nvmem_cells, node)
+ if (p && !strcmp(p->name, cell_id))
+ return p;
+
+ return NULL;
+}
+
+static void nvmem_cell_drop(struct nvmem_cell *cell)
+{
+ list_del(&cell->node);
+ kfree(cell);
+}
+
+static void nvmem_cell_add(struct nvmem_cell *cell)
+{
+ list_add_tail(&cell->node, &nvmem_cells);
+}
+
+static int nvmem_cell_info_to_nvmem_cell(struct nvmem_device *nvmem,
+ const struct nvmem_cell_info *info,
+ struct nvmem_cell *cell)
+{
+ cell->nvmem = nvmem;
+ cell->offset = info->offset;
+ cell->bytes = info->bytes;
+ cell->name = info->name;
+
+ cell->bit_offset = info->bit_offset;
+ cell->nbits = info->nbits;
+
+ if (cell->nbits)
+ cell->bytes = DIV_ROUND_UP(cell->nbits + cell->bit_offset,
+ BITS_PER_BYTE);
+
+ if (!IS_ALIGNED(cell->offset, nvmem->stride)) {
+ dev_err(&nvmem->dev,
+ "cell %s unaligned to nvmem stride %d\n",
+ cell->name, nvmem->stride);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * nvmem_register() - Register a nvmem device for given nvmem_config.
+ *
+ * @config: nvmem device configuration with which nvmem device is created.
+ *
+ * Return: Will be an ERR_PTR() on error or a valid pointer to nvmem_device
+ * on success.
+ */
+
+struct nvmem_device *nvmem_register(const struct nvmem_config *config)
+{
+ struct nvmem_device *nvmem;
+ struct device_node *np;
+ int rval;
+
+ if (!config->dev)
+ return ERR_PTR(-EINVAL);
+
+ nvmem = kzalloc(sizeof(*nvmem), GFP_KERNEL);
+ if (!nvmem)
+ return ERR_PTR(-ENOMEM);
+
+ nvmem->stride = config->stride;
+ nvmem->word_size = config->word_size;
+ nvmem->size = config->size;
+ nvmem->dev.parent = config->dev;
+ nvmem->bus = config->bus;
+ np = config->dev->device_node;
+ nvmem->dev.device_node = np;
+
+ nvmem->read_only = of_property_read_bool(np, "read-only") |
+ config->read_only;
+
+ safe_strncpy(nvmem->dev.name, config->name, MAX_DRIVER_NAME);
+ nvmem->dev.id = DEVICE_ID_DYNAMIC;
+
+ dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name);
+
+ rval = register_device(&nvmem->dev);
+ if (rval) {
+ kfree(nvmem);
+ return ERR_PTR(rval);
+ }
+
+ rval = nvmem_register_cdev(nvmem);
+ if (rval) {
+ kfree(nvmem);
+ return ERR_PTR(rval);
+ }
+
+ list_add_tail(&nvmem->node, &nvmem_devs);
+
+ return nvmem;
+}
+EXPORT_SYMBOL_GPL(nvmem_register);
+
+static struct nvmem_device *__nvmem_device_get(struct device_node *np,
+ struct nvmem_cell **cellp,
+ const char *cell_id)
+{
+ struct nvmem_device *nvmem = NULL;
+
+ if (np) {
+ nvmem = of_nvmem_find(np);
+ if (!nvmem)
+ return ERR_PTR(-EPROBE_DEFER);
+ } else {
+ struct nvmem_cell *cell = nvmem_find_cell(cell_id);
+
+ if (cell) {
+ nvmem = cell->nvmem;
+ *cellp = cell;
+ }
+
+ if (!nvmem)
+ return ERR_PTR(-ENOENT);
+ }
+
+ nvmem->users++;
+
+ return nvmem;
+}
+
+static void __nvmem_device_put(struct nvmem_device *nvmem)
+{
+ nvmem->users--;
+}
+
+static struct nvmem_device *nvmem_find(const char *name)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OFTREE)
+/**
+ * of_nvmem_device_get() - Get nvmem device from a given id
+ *
+ * @dev node: Device tree node that uses the nvmem device
+ * @id: nvmem name from nvmem-names property.
+ *
+ * Return: ERR_PTR() on error or a valid pointer to a struct nvmem_device
+ * on success.
+ */
+struct nvmem_device *of_nvmem_device_get(struct device_node *np, const char *id)
+{
+
+ struct device_node *nvmem_np;
+ int index;
+
+ index = of_property_match_string(np, "nvmem-names", id);
+
+ nvmem_np = of_parse_phandle(np, "nvmem", index);
+ if (!nvmem_np)
+ return ERR_PTR(-EINVAL);
+
+ return __nvmem_device_get(nvmem_np, NULL, NULL);
+}
+EXPORT_SYMBOL_GPL(of_nvmem_device_get);
+#endif
+
+/**
+ * nvmem_device_get() - Get nvmem device from a given id
+ *
+ * @dev : Device that uses the nvmem device
+ * @id: nvmem name from nvmem-names property.
+ *
+ * Return: ERR_PTR() on error or a valid pointer to a struct nvmem_device
+ * on success.
+ */
+struct nvmem_device *nvmem_device_get(struct device_d *dev, const char *dev_name)
+{
+ if (dev->device_node) { /* try dt first */
+ struct nvmem_device *nvmem;
+
+ nvmem = of_nvmem_device_get(dev->device_node, dev_name);
+
+ if (!IS_ERR(nvmem) || PTR_ERR(nvmem) == -EPROBE_DEFER)
+ return nvmem;
+
+ }
+
+ return nvmem_find(dev_name);
+}
+EXPORT_SYMBOL_GPL(nvmem_device_get);
+
+/**
+ * nvmem_device_put() - put alredy got nvmem device
+ *
+ * @nvmem: pointer to nvmem device that needs to be released.
+ */
+void nvmem_device_put(struct nvmem_device *nvmem)
+{
+ __nvmem_device_put(nvmem);
+}
+EXPORT_SYMBOL_GPL(nvmem_device_put);
+
+static struct nvmem_cell *nvmem_cell_get_from_list(const char *cell_id)
+{
+ struct nvmem_cell *cell = NULL;
+ struct nvmem_device *nvmem;
+
+ nvmem = __nvmem_device_get(NULL, &cell, cell_id);
+ if (IS_ERR(nvmem))
+ return ERR_CAST(nvmem);
+
+ return cell;
+}
+
+#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OFTREE)
+/**
+ * of_nvmem_cell_get() - Get a nvmem cell from given device node and cell id
+ *
+ * @dev node: Device tree node that uses the nvmem cell
+ * @id: nvmem cell name from nvmem-cell-names property.
+ *
+ * Return: Will be an ERR_PTR() on error or a valid pointer
+ * to a struct nvmem_cell. The nvmem_cell will be freed by the
+ * nvmem_cell_put().
+ */
+struct nvmem_cell *of_nvmem_cell_get(struct device_node *np,
+ const char *name)
+{
+ struct device_node *cell_np, *nvmem_np;
+ struct nvmem_cell *cell;
+ struct nvmem_device *nvmem;
+ const __be32 *addr;
+ int rval, len, index;
+
+ index = of_property_match_string(np, "nvmem-cell-names", name);
+
+ cell_np = of_parse_phandle(np, "nvmem-cells", index);
+ if (!cell_np)
+ return ERR_PTR(-EINVAL);
+
+ nvmem_np = of_get_parent(cell_np);
+ if (!nvmem_np)
+ return ERR_PTR(-EINVAL);
+
+ nvmem = __nvmem_device_get(nvmem_np, NULL, NULL);
+ if (IS_ERR(nvmem))
+ return ERR_CAST(nvmem);
+
+ addr = of_get_property(cell_np, "reg", &len);
+ if (!addr || (len < 2 * sizeof(u32))) {
+ dev_err(&nvmem->dev, "nvmem: invalid reg on %s\n",
+ cell_np->full_name);
+ rval = -EINVAL;
+ goto err_mem;
+ }
+
+ cell = kzalloc(sizeof(*cell), GFP_KERNEL);
+ if (!cell) {
+ rval = -ENOMEM;
+ goto err_mem;
+ }
+
+ cell->nvmem = nvmem;
+ cell->offset = be32_to_cpup(addr++);
+ cell->bytes = be32_to_cpup(addr);
+ cell->name = cell_np->name;
+
+ addr = of_get_property(cell_np, "bits", &len);
+ if (addr && len == (2 * sizeof(u32))) {
+ cell->bit_offset = be32_to_cpup(addr++);
+ cell->nbits = be32_to_cpup(addr);
+ }
+
+ if (cell->nbits)
+ cell->bytes = DIV_ROUND_UP(cell->nbits + cell->bit_offset,
+ BITS_PER_BYTE);
+
+ if (cell->bytes < nvmem->word_size)
+ cell->bytes = nvmem->word_size;
+
+ if (!IS_ALIGNED(cell->offset, nvmem->stride)) {
+ dev_err(&nvmem->dev,
+ "cell %s unaligned to nvmem stride %d\n",
+ cell->name, nvmem->stride);
+ rval = -EINVAL;
+ goto err_sanity;
+ }
+
+ nvmem_cell_add(cell);
+
+ return cell;
+
+err_sanity:
+ kfree(cell);
+
+err_mem:
+ __nvmem_device_put(nvmem);
+
+ return ERR_PTR(rval);
+}
+EXPORT_SYMBOL_GPL(of_nvmem_cell_get);
+#endif
+
+/**
+ * nvmem_cell_get() - Get nvmem cell of device form a given cell name
+ *
+ * @dev node: Device tree node that uses the nvmem cell
+ * @id: nvmem cell name to get.
+ *
+ * Return: Will be an ERR_PTR() on error or a valid pointer
+ * to a struct nvmem_cell. The nvmem_cell will be freed by the
+ * nvmem_cell_put().
+ */
+struct nvmem_cell *nvmem_cell_get(struct device_d *dev, const char *cell_id)
+{
+ struct nvmem_cell *cell;
+
+ if (dev->device_node) { /* try dt first */
+ cell = of_nvmem_cell_get(dev->device_node, cell_id);
+ if (!IS_ERR(cell) || PTR_ERR(cell) == -EPROBE_DEFER)
+ return cell;
+ }
+
+ return nvmem_cell_get_from_list(cell_id);
+}
+EXPORT_SYMBOL_GPL(nvmem_cell_get);
+
+/**
+ * nvmem_cell_put() - Release previously allocated nvmem cell.
+ *
+ * @cell: Previously allocated nvmem cell by nvmem_cell_get()
+ */
+void nvmem_cell_put(struct nvmem_cell *cell)
+{
+ struct nvmem_device *nvmem = cell->nvmem;
+
+ __nvmem_device_put(nvmem);
+ nvmem_cell_drop(cell);
+}
+EXPORT_SYMBOL_GPL(nvmem_cell_put);
+
+static inline void nvmem_shift_read_buffer_in_place(struct nvmem_cell *cell,
+ void *buf)
+{
+ u8 *p, *b;
+ int i, bit_offset = cell->bit_offset;
+
+ p = b = buf;
+ if (bit_offset) {
+ /* First shift */
+ *b++ >>= bit_offset;
+
+ /* setup rest of the bytes if any */
+ for (i = 1; i < cell->bytes; i++) {
+ /* Get bits from next byte and shift them towards msb */
+ *p |= *b << (BITS_PER_BYTE - bit_offset);
+
+ p = b;
+ *b++ >>= bit_offset;
+ }
+
+ /* result fits in less bytes */
+ if (cell->bytes != DIV_ROUND_UP(cell->nbits, BITS_PER_BYTE))
+ *p-- = 0;
+ }
+ /* clear msb bits if any leftover in the last byte */
+ *p &= GENMASK((cell->nbits%BITS_PER_BYTE) - 1, 0);
+}
+
+static int __nvmem_cell_read(struct nvmem_device *nvmem,
+ struct nvmem_cell *cell,
+ void *buf, size_t *len)
+{
+ int rc;
+
+ rc = nvmem->bus->read(&nvmem->dev, cell->offset, buf, cell->bytes);
+ if (IS_ERR_VALUE(rc))
+ return rc;
+
+ /* shift bits in-place */
+ if (cell->bit_offset || cell->nbits)
+ nvmem_shift_read_buffer_in_place(cell, buf);
+
+ *len = cell->bytes;
+
+ return 0;
+}
+
+/**
+ * nvmem_cell_read() - Read a given nvmem cell
+ *
+ * @cell: nvmem cell to be read.
+ * @len: pointer to length of cell which will be populated on successful read.
+ *
+ * Return: ERR_PTR() on error or a valid pointer to a char * buffer on success.
+ * The buffer should be freed by the consumer with a kfree().
+ */
+void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len)
+{
+ struct nvmem_device *nvmem = cell->nvmem;
+ u8 *buf;
+ int rc;
+
+ if (!nvmem)
+ return ERR_PTR(-EINVAL);
+
+ buf = kzalloc(cell->bytes, GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ rc = __nvmem_cell_read(nvmem, cell, buf, len);
+ if (IS_ERR_VALUE(rc)) {
+ kfree(buf);
+ return ERR_PTR(rc);
+ }
+
+ return buf;
+}
+EXPORT_SYMBOL_GPL(nvmem_cell_read);
+
+static inline void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell,
+ u8 *_buf, int len)
+{
+ struct nvmem_device *nvmem = cell->nvmem;
+ int i, rc, nbits, bit_offset = cell->bit_offset;
+ u8 v, *p, *buf, *b, pbyte, pbits;
+
+ nbits = cell->nbits;
+ buf = kzalloc(cell->bytes, GFP_KERNEL);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+
+ memcpy(buf, _buf, len);
+ p = b = buf;
+
+ if (bit_offset) {
+ pbyte = *b;
+ *b <<= bit_offset;
+
+ /* setup the first byte with lsb bits from nvmem */
+ rc = nvmem->bus->read(&nvmem->dev, cell->offset, &v, 1);
+ *b++ |= GENMASK(bit_offset - 1, 0) & v;
+
+ /* setup rest of the byte if any */
+ for (i = 1; i < cell->bytes; i++) {
+ /* Get last byte bits and shift them towards lsb */
+ pbits = pbyte >> (BITS_PER_BYTE - 1 - bit_offset);
+ pbyte = *b;
+ p = b;
+ *b <<= bit_offset;
+ *b++ |= pbits;
+ }
+ }
+
+ /* if it's not end on byte boundary */
+ if ((nbits + bit_offset) % BITS_PER_BYTE) {
+ /* setup the last byte with msb bits from nvmem */
+ rc = nvmem->bus->read(&nvmem->dev, cell->offset + cell->bytes - 1,
+ &v, 1);
+ *p |= GENMASK(7, (nbits + bit_offset) % BITS_PER_BYTE) & v;
+
+ }
+
+ return buf;
+}
+
+/**
+ * nvmem_cell_write() - Write to a given nvmem cell
+ *
+ * @cell: nvmem cell to be written.
+ * @buf: Buffer to be written.
+ * @len: length of buffer to be written to nvmem cell.
+ *
+ * Return: length of bytes written or negative on failure.
+ */
+int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len)
+{
+ struct nvmem_device *nvmem = cell->nvmem;
+ int rc;
+
+ if (!nvmem || nvmem->read_only ||
+ (cell->bit_offset == 0 && len != cell->bytes))
+ return -EINVAL;
+
+ if (cell->bit_offset || cell->nbits) {
+ buf = nvmem_cell_prepare_write_buffer(cell, buf, len);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+ }
+
+ rc = nvmem->bus->write(&nvmem->dev, cell->offset, buf, cell->bytes);
+
+ /* free the tmp buffer */
+ if (cell->bit_offset || cell->nbits)
+ kfree(buf);
+
+ if (IS_ERR_VALUE(rc))
+ return rc;
+
+ return len;
+}
+EXPORT_SYMBOL_GPL(nvmem_cell_write);
+
+/**
+ * nvmem_device_cell_read() - Read a given nvmem device and cell
+ *
+ * @nvmem: nvmem device to read from.
+ * @info: nvmem cell info to be read.
+ * @buf: buffer pointer which will be populated on successful read.
+ *
+ * Return: length of successful bytes read on success and negative
+ * error code on error.
+ */
+ssize_t nvmem_device_cell_read(struct nvmem_device *nvmem,
+ struct nvmem_cell_info *info, void *buf)
+{
+ struct nvmem_cell cell;
+ int rc;
+ ssize_t len;
+
+ if (!nvmem)
+ return -EINVAL;
+
+ rc = nvmem_cell_info_to_nvmem_cell(nvmem, info, &cell);
+ if (IS_ERR_VALUE(rc))
+ return rc;
+
+ rc = __nvmem_cell_read(nvmem, &cell, buf, &len);
+ if (IS_ERR_VALUE(rc))
+ return rc;
+
+ return len;
+}
+EXPORT_SYMBOL_GPL(nvmem_device_cell_read);
+
+/**
+ * nvmem_device_cell_write() - Write cell to a given nvmem device
+ *
+ * @nvmem: nvmem device to be written to.
+ * @info: nvmem cell info to be written
+ * @buf: buffer to be written to cell.
+ *
+ * Return: length of bytes written or negative error code on failure.
+ * */
+int nvmem_device_cell_write(struct nvmem_device *nvmem,
+ struct nvmem_cell_info *info, void *buf)
+{
+ struct nvmem_cell cell;
+ int rc;
+
+ if (!nvmem)
+ return -EINVAL;
+
+ rc = nvmem_cell_info_to_nvmem_cell(nvmem, info, &cell);
+ if (IS_ERR_VALUE(rc))
+ return rc;
+
+ return nvmem_cell_write(&cell, buf, cell.bytes);
+}
+EXPORT_SYMBOL_GPL(nvmem_device_cell_write);
+
+/**
+ * nvmem_device_read() - Read from a given nvmem device
+ *
+ * @nvmem: nvmem device to read from.
+ * @offset: offset in nvmem device.
+ * @bytes: number of bytes to read.
+ * @buf: buffer pointer which will be populated on successful read.
+ *
+ * Return: length of successful bytes read on success and negative
+ * error code on error.
+ */
+int nvmem_device_read(struct nvmem_device *nvmem,
+ unsigned int offset,
+ size_t bytes, void *buf)
+{
+ int rc;
+
+ if (!nvmem)
+ return -EINVAL;
+
+ if (offset >= nvmem->size || bytes > nvmem->size - offset)
+ return -EINVAL;
+
+ if (!bytes)
+ return 0;
+
+ rc = nvmem->bus->read(&nvmem->dev, offset, buf, bytes);
+
+ if (IS_ERR_VALUE(rc))
+ return rc;
+
+ return bytes;
+}
+EXPORT_SYMBOL_GPL(nvmem_device_read);
+
+/**
+ * nvmem_device_write() - Write cell to a given nvmem device
+ *
+ * @nvmem: nvmem device to be written to.
+ * @offset: offset in nvmem device.
+ * @bytes: number of bytes to write.
+ * @buf: buffer to be written.
+ *
+ * Return: length of bytes written or negative error code on failure.
+ * */
+int nvmem_device_write(struct nvmem_device *nvmem,
+ unsigned int offset,
+ size_t bytes, const void *buf)
+{
+ int rc;
+
+ if (!nvmem || nvmem->read_only)
+ return -EINVAL;
+
+ if (offset >= nvmem->size || bytes > nvmem->size - offset)
+ return -EINVAL;
+
+ if (!bytes)
+ return 0;
+
+ rc = nvmem->bus->write(&nvmem->dev, offset, buf, bytes);
+
+ if (IS_ERR_VALUE(rc))
+ return rc;
+
+
+ return bytes;
+}
+EXPORT_SYMBOL_GPL(nvmem_device_write);
diff --git a/drivers/nvmem/snvs_lpgpr.c b/drivers/nvmem/snvs_lpgpr.c
new file mode 100644
index 0000000000..74118a6b75
--- /dev/null
+++ b/drivers/nvmem/snvs_lpgpr.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2015 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de>
+ * Copyright (c) 2017 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ */
+#include <common.h>
+#include <driver.h>
+#include <init.h>
+#include <io.h>
+#include <of.h>
+#include <malloc.h>
+#include <regmap.h>
+#include <mfd/syscon.h>
+#include <linux/nvmem-provider.h>
+
+struct snvs_lpgpr_priv {
+ struct device_d *dev;
+ struct regmap *regmap;
+ int offset;
+ struct nvmem_config cfg;
+};
+
+static int snvs_lpgpr_write(struct device_d *dev, const int reg,
+ const void *_val, int bytes)
+{
+ struct snvs_lpgpr_priv *priv = dev->parent->priv;
+ const u32 *val = _val;
+ int i = 0, words = bytes / 4;
+
+ while (words--)
+ regmap_write(priv->regmap, priv->offset + reg + (i++ * 4),
+ *val++);
+
+ return 0;
+}
+
+static int snvs_lpgpr_read(struct device_d *dev, const int reg, void *_val,
+ int bytes)
+{
+ struct snvs_lpgpr_priv *priv = dev->parent->priv;
+ u32 *val = _val;
+ int i = 0, words = bytes / 4;
+
+ while (words--)
+ regmap_read(priv->regmap, priv->offset + reg + (i++ * 4),
+ val++);
+
+
+ return 0;
+}
+
+static const struct nvmem_bus snvs_lpgpr_nvmem_bus = {
+ .write = snvs_lpgpr_write,
+ .read = snvs_lpgpr_read,
+};
+
+static int snvs_lpgpr_probe(struct device_d *dev)
+{
+ struct device_node *node = dev->device_node;
+ struct snvs_lpgpr_priv *priv;
+ struct nvmem_config *cfg;
+ struct nvmem_device *nvmem;
+ int err;
+
+ if (!node)
+ return -ENOENT;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->regmap = syscon_node_to_regmap(of_get_parent(node));
+ if (IS_ERR(priv->regmap)) {
+ free(priv);
+ return PTR_ERR(priv->regmap);
+ }
+
+ err = of_property_read_u32(node, "offset", &priv->offset);
+ if (err)
+ return err;
+
+ cfg = &priv->cfg;
+ cfg->name = dev_name(dev);
+ cfg->dev = dev;
+ cfg->stride = 4,
+ cfg->word_size = 4,
+ cfg->size = 4,
+ cfg->bus = &snvs_lpgpr_nvmem_bus,
+
+ nvmem = nvmem_register(cfg);
+ if (IS_ERR(nvmem)) {
+ free(priv);
+ return PTR_ERR(nvmem);
+ }
+
+ dev->priv = priv;
+
+ return 0;
+}
+
+static __maybe_unused struct of_device_id snvs_lpgpr_dt_ids[] = {
+ { .compatible = "fsl,imx6sl-snvs-lpgpr", },
+ { .compatible = "fsl,imx6q-snvs-lpgpr", },
+ { },
+};
+
+static struct driver_d snvs_lpgpr_driver = {
+ .name = "nvmem-snvs-lpgpr",
+ .probe = snvs_lpgpr_probe,
+ .of_compatible = DRV_OF_COMPAT(snvs_lpgpr_dt_ids),
+};
+device_platform_driver(snvs_lpgpr_driver);
diff --git a/drivers/of/base.c b/drivers/of/base.c
index bef8f1de1a..6632f4d9dd 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -1217,6 +1217,28 @@ int of_property_write_u64_array(struct device_node *np,
}
/**
+ * of_property_write_string - Write a string to a property. If
+ * the property does not exist, it will be created and appended to the given
+ * device node.
+ *
+ * @np: device node to which the property value is to be written.
+ * @propname: name of the property to be written.
+ * value: pointer to the string to write
+ *
+ * Search for a property in a device node and write a string to
+ * it. If the property does not exist, it will be created and appended to
+ * the device node. Returns 0 on success, -ENOMEM if the property or array
+ * of elements cannot be created.
+ */
+int of_property_write_string(struct device_node *np,
+ const char *propname, const char *value)
+{
+ size_t len = strlen(value);
+
+ return of_set_property(np, propname, value, len + 1, 1);
+}
+
+/**
* of_parse_phandle_from - Resolve a phandle property to a device_node pointer from
* a given root node
* @np: Pointer to device node holding phandle property
@@ -2093,7 +2115,7 @@ int of_device_enable_path(const char *path)
*/
int of_device_disable(struct device_node *node)
{
- return of_set_property(node, "status", "disabled", sizeof("disabled"), 1);
+ return of_property_write_string(node, "status", "disabled");
}
/**
diff --git a/drivers/of/of_path.c b/drivers/of/of_path.c
index e0b2dc1247..a5886dffac 100644
--- a/drivers/of/of_path.c
+++ b/drivers/of/of_path.c
@@ -104,6 +104,113 @@ int of_find_path_by_node(struct device_node *node, char **outpath, unsigned flag
}
/**
+ * of_find_node_by_devpath - translate a device path to a device tree node
+ *
+ * @root: The device tree root. Can be NULL, in this case the internal tree is used
+ * @path: The path to look the node up for. Can be "/dev/cdevname" or "cdevname" directly.
+ *
+ * This is the counterpart of of_find_path_by_node(). Given a path this function tries
+ * to find the corresponding node in the given device tree.
+ *
+ * We first have to find the hardware device in the tree we are passed and then find
+ * a partition matching offset/size in this tree. This is necessary because the
+ * passed tree may use another partition binding (legacy vs. fixed-partitions). Also
+ * the node names may differ (some device trees have partition@<num> instead of
+ * partition@<offset>.
+ */
+struct device_node *of_find_node_by_devpath(struct device_node *root, const char *path)
+{
+ struct cdev *cdev;
+ bool is_partition = false;
+ struct device_node *np, *partnode, *rnp;
+ loff_t part_offset = 0, part_size = 0;
+
+ pr_debug("%s: looking for path %s\n", __func__, path);
+
+ if (!strncmp(path, "/dev/", 5))
+ path += 5;
+
+ cdev = cdev_by_name(path);
+ if (!cdev) {
+ pr_debug("%s: cdev %s not found\n", __func__, path);
+ return NULL;
+ }
+
+ /*
+ * Look for the device node of the master device (the one of_parse_partitions() has
+ * been called with
+ */
+ if (cdev->master) {
+ is_partition = true;
+ part_offset = cdev->offset;
+ part_size = cdev->size;
+ pr_debug("%s path %s: is a partition with offset 0x%08llx, size 0x%08llx\n",
+ __func__, path, part_offset, part_size);
+ np = cdev->master->device_node;
+ } else {
+ np = cdev->device_node;
+ }
+
+ /*
+ * Now find the device node of the master device in the device tree we have
+ * been passed.
+ */
+ rnp = of_find_node_by_path_from(root, np->full_name);
+ if (!rnp) {
+ pr_debug("%s path %s: %s not found in passed tree\n", __func__, path,
+ np->full_name);
+ return NULL;
+ }
+
+ if (!is_partition) {
+ pr_debug("%s path %s: returning full device node %s\n", __func__, path,
+ rnp->full_name);
+ return rnp;
+ }
+
+ /*
+ * Look for a partition with matching offset/size in the device node of
+ * the tree we have been passed.
+ */
+ partnode = of_get_child_by_name(rnp, "partitions");
+ if (!partnode) {
+ pr_debug("%s path %s: using legacy partition binding\n", __func__, path);
+ partnode = rnp;
+ }
+
+ for_each_child_of_node(partnode, np) {
+ const __be32 *reg;
+ int na, ns, len;
+ loff_t offset, size;
+
+ reg = of_get_property(np, "reg", &len);
+ if (!reg)
+ return NULL;
+
+ na = of_n_addr_cells(np);
+ ns = of_n_size_cells(np);
+
+ if (len < (na + ns) * sizeof(__be32)) {
+ pr_err("reg property too small in %s\n", np->full_name);
+ continue;
+ }
+
+ offset = of_read_number(reg, na);
+ size = of_read_number(reg + na, ns);
+
+ if (part_offset == offset && part_size == size) {
+ pr_debug("%s path %s: found matching partition in %s\n", __func__, path,
+ np->full_name);
+ return np;
+ }
+ }
+
+ pr_debug("%s path %s: no matching node found\n", __func__, path);
+
+ return NULL;
+}
+
+/**
* of_find_path - translate a path description in the devicetree to a barebox
* path
*
diff --git a/drivers/of/partition.c b/drivers/of/partition.c
index 8c2aef2326..25b41cb012 100644
--- a/drivers/of/partition.c
+++ b/drivers/of/partition.c
@@ -23,6 +23,16 @@
#include <linux/mtd/mtd.h>
#include <linux/err.h>
#include <nand.h>
+#include <init.h>
+#include <globalvar.h>
+
+static unsigned int of_partition_binding;
+
+enum of_binding_name {
+ MTD_OF_BINDING_NEW,
+ MTD_OF_BINDING_LEGACY,
+ MTD_OF_BINDING_DONTTOUCH,
+};
struct cdev *of_parse_partition(struct cdev *cdev, struct device_node *node)
{
@@ -30,10 +40,11 @@ struct cdev *of_parse_partition(struct cdev *cdev, struct device_node *node)
char *filename;
struct cdev *new;
const __be32 *reg;
- unsigned long offset, size;
+ u64 offset, size;
const char *name;
int len;
unsigned long flags = 0;
+ int na, ns;
if (!node)
return NULL;
@@ -42,8 +53,16 @@ struct cdev *of_parse_partition(struct cdev *cdev, struct device_node *node)
if (!reg)
return NULL;
- offset = be32_to_cpu(reg[0]);
- size = be32_to_cpu(reg[1]);
+ na = of_n_addr_cells(node);
+ ns = of_n_size_cells(node);
+
+ if (len < (na + ns) * sizeof(__be32)) {
+ pr_err("reg property too small in %s\n", node->full_name);
+ return NULL;
+ }
+
+ offset = of_read_number(reg, na);
+ size = of_read_number(reg + na, ns);
partname = of_get_property(node, "label", &len);
if (!partname)
@@ -53,7 +72,7 @@ struct cdev *of_parse_partition(struct cdev *cdev, struct device_node *node)
name = (char *)partname;
- debug("add partition: %s.%s 0x%08lx 0x%08lx\n", cdev->name, partname, offset, size);
+ debug("add partition: %s.%s 0x%08llx 0x%08llx\n", cdev->name, partname, offset, size);
if (of_get_property(node, "read-only", &len))
flags = DEVFS_PARTITION_READONLY;
@@ -79,6 +98,8 @@ int of_parse_partitions(struct cdev *cdev, struct device_node *node)
if (!node)
return -EINVAL;
+ cdev->device_node = node;
+
subnode = of_get_child_by_name(node, "partitions");
if (subnode) {
if (!of_device_is_compatible(subnode, "fixed-partitions"))
@@ -92,3 +113,150 @@ int of_parse_partitions(struct cdev *cdev, struct device_node *node)
return 0;
}
+
+static void delete_subnodes(struct device_node *np)
+{
+ struct device_node *part, *tmp;
+
+ for_each_child_of_node_safe(np, tmp, part) {
+ if (of_get_property(part, "compatible", NULL))
+ continue;
+
+ of_delete_node(part);
+ }
+}
+
+static int of_partition_fixup(struct device_node *root, void *ctx)
+{
+ struct cdev *cdev = ctx, *partcdev;
+ struct device_node *np, *part, *partnode;
+ int ret;
+ int n_cells, n_parts = 0;
+
+ if (of_partition_binding == MTD_OF_BINDING_DONTTOUCH)
+ return 0;
+
+ if (!cdev->device_node)
+ return -EINVAL;
+
+ list_for_each_entry(partcdev, &cdev->partitions, partition_entry) {
+ if (partcdev->flags & DEVFS_PARTITION_FROM_TABLE)
+ continue;
+ n_parts++;
+ }
+
+ if (!n_parts)
+ return 0;
+
+ if (cdev->size >= 0x100000000)
+ n_cells = 2;
+ else
+ n_cells = 1;
+
+ np = of_find_node_by_path_from(root, cdev->device_node->full_name);
+ if (!np) {
+ dev_err(cdev->dev, "Cannot find nodepath %s, cannot fixup\n",
+ cdev->device_node->full_name);
+ return -EINVAL;
+ }
+
+ partnode = of_get_child_by_name(np, "partitions");
+ if (partnode) {
+ if (of_partition_binding == MTD_OF_BINDING_LEGACY) {
+ of_delete_node(partnode);
+ partnode = np;
+ }
+ delete_subnodes(partnode);
+ } else {
+ delete_subnodes(np);
+
+ if (of_partition_binding == MTD_OF_BINDING_LEGACY)
+ partnode = np;
+ else
+ partnode = of_new_node(np, "partitions");
+ }
+
+ if (of_partition_binding == MTD_OF_BINDING_NEW) {
+ ret = of_property_write_string(partnode, "compatible",
+ "fixed-partitions");
+ if (ret)
+ return ret;
+ }
+
+ of_property_write_u32(partnode, "#size-cells", n_cells);
+ if (ret)
+ return ret;
+
+ of_property_write_u32(partnode, "#addres-cells", n_cells);
+ if (ret)
+ return ret;
+
+ list_for_each_entry(partcdev, &cdev->partitions, partition_entry) {
+ int na, ns, len = 0;
+ char *name;
+ void *p;
+ u8 tmp[16 * 16]; /* Up to 64-bit address + 64-bit size */
+ loff_t partoffset;
+
+ if (partcdev->flags & DEVFS_PARTITION_FROM_TABLE)
+ continue;
+
+ if (partcdev->mtd)
+ partoffset = partcdev->mtd->master_offset;
+ else
+ partoffset = partcdev->offset;
+
+ name = basprintf("partition@%0llx", partoffset);
+ if (!name)
+ return -ENOMEM;
+
+ part = of_new_node(partnode, name);
+ free(name);
+ if (!part)
+ return -ENOMEM;
+
+ p = of_new_property(part, "label", partcdev->partname,
+ strlen(partcdev->partname) + 1);
+ if (!p)
+ return -ENOMEM;
+
+ na = of_n_addr_cells(part);
+ ns = of_n_size_cells(part);
+
+ of_write_number(tmp + len, partoffset, na);
+ len += na * 4;
+ of_write_number(tmp + len, partcdev->size, ns);
+ len += ns * 4;
+
+ ret = of_set_property(part, "reg", tmp, len, 1);
+ if (ret)
+ return ret;
+
+ if (partcdev->flags & DEVFS_PARTITION_READONLY) {
+ ret = of_set_property(part, "read-only", NULL, 0, 1);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int of_partitions_register_fixup(struct cdev *cdev)
+{
+ return of_register_fixup(of_partition_fixup, cdev);
+}
+
+static const char *of_binding_names[] = {
+ "new", "legacy", "donttouch"
+};
+
+static int of_partition_init(void)
+{
+ dev_add_param_enum(&global_device, "of_partition_binding", NULL, NULL,
+ &of_partition_binding, of_binding_names,
+ ARRAY_SIZE(of_binding_names), NULL);
+
+ return 0;
+}
+device_initcall(of_partition_init);
diff --git a/drivers/serial/atmel.c b/drivers/serial/atmel.c
index ab94fd2177..2f8adc989f 100644
--- a/drivers/serial/atmel.c
+++ b/drivers/serial/atmel.c
@@ -446,8 +446,15 @@ static int atmel_serial_probe(struct device_d *dev)
return 0;
}
+static const struct of_device_id __maybe_unused atmel_serial_dt_ids[] = {
+ { .compatible = "atmel,at91rm9200-usart" },
+ { .compatible = "atmel,at91sam9260-usart" },
+ { /* sentinel */ }
+};
+
static struct driver_d atmel_serial_driver = {
.name = "atmel_usart",
.probe = atmel_serial_probe,
+ .of_compatible = DRV_OF_COMPAT(atmel_serial_dt_ids),
};
console_platform_driver(atmel_serial_driver);
diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c
index ef57867759..446e1d9932 100644
--- a/drivers/spi/atmel_spi.c
+++ b/drivers/spi/atmel_spi.c
@@ -29,6 +29,7 @@
#include <clock.h>
#include <xfuncs.h>
#include <gpio.h>
+#include <of_gpio.h>
#include <io.h>
#include <spi/spi.h>
#include <mach/io.h>
@@ -400,10 +401,11 @@ static int atmel_spi_probe(struct device_d *dev)
int ret = 0;
int i;
struct spi_master *master;
+ struct device_node *node = dev->device_node;
struct atmel_spi *as;
struct at91_spi_platform_data *pdata = dev->platform_data;
- if (!pdata) {
+ if (!IS_ENABLED(CONFIG_OFDEVICE) && !pdata) {
dev_err(dev, "missing platform data\n");
return -EINVAL;
}
@@ -414,6 +416,23 @@ static int atmel_spi_probe(struct device_d *dev)
master->dev = dev;
master->bus_num = dev->id;
+ if (pdata) {
+ master->num_chipselect = pdata->num_chipselect;
+ as->cs_pins = pdata->chipselect;
+ } else {
+ master->num_chipselect = of_gpio_named_count(node, "cs-gpios");
+ as->cs_pins = xzalloc(sizeof(u32) * master->num_chipselect);
+
+ for (i = 0; i < master->num_chipselect; i++) {
+ as->cs_pins[i] = of_get_named_gpio(node, "cs-gpios", i);
+
+ if (!gpio_is_valid(as->cs_pins[i]))
+ break;
+ }
+
+ master->num_chipselect = i;
+ }
+
as->clk = clk_get(dev, "spi_clk");
if (IS_ERR(as->clk)) {
dev_err(dev, "no spi_clk\n");
@@ -423,8 +442,6 @@ static int atmel_spi_probe(struct device_d *dev)
master->setup = atmel_spi_setup;
master->transfer = atmel_spi_transfer;
- master->num_chipselect = pdata->num_chipselect;
- as->cs_pins = pdata->chipselect;
iores = dev_request_mem_resource(dev, 0);
if (IS_ERR(iores))
return PTR_ERR(iores);
@@ -465,8 +482,14 @@ out_free:
return ret;
}
+const static __maybe_unused const struct of_device_id atmel_spi_dt_ids[] = {
+ { .compatible = "atmel,at91rm9200-spi" },
+ { /* sentinel */ }
+};
+
static struct driver_d atmel_spi_driver = {
.name = "atmel_spi",
.probe = atmel_spi_probe,
+ .of_compatible = DRV_OF_COMPAT(atmel_spi_dt_ids),
};
device_platform_driver(atmel_spi_driver);
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index eb279ae8df..4292371f09 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -30,6 +30,17 @@ config USB_GADGET_DRIVER_PXA27X
default y
select USB_GADGET_DUALSPEED
+config USB_GADGET_AUTOSTART
+ bool
+ default y
+ select ENVIRONMENT_VARIABLES
+ select FILE_LIST
+ prompt "Automatically start usbgadget on boot"
+ help
+ Enabling this option allows to automatically start a fastboot
+ gadget during boot. This behaviour is controlled with the
+ global.usbgadget.fastboot_function variable.
+
comment "USB Gadget drivers"
config USB_GADGET_DFU
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 9ef594575b..e74cf02664 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -1,5 +1,6 @@
obj-$(CONFIG_USB_GADGET) += composite.o config.o usbstring.o epautoconf.o udc-core.o functions.o config.o multi.o
+obj-$(CONFIG_USB_GADGET_AUTOSTART) += autostart.o
obj-$(CONFIG_USB_GADGET_SERIAL) += u_serial.o serial.o f_serial.o f_acm.o
obj-$(CONFIG_USB_GADGET_DFU) += dfu.o
obj-$(CONFIG_USB_GADGET_FASTBOOT) += f_fastboot.o
diff --git a/drivers/usb/gadget/autostart.c b/drivers/usb/gadget/autostart.c
new file mode 100644
index 0000000000..4ad1dd6be1
--- /dev/null
+++ b/drivers/usb/gadget/autostart.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2017 Oleksij Rempel <o.rempel@pengutronix.de>, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <common.h>
+#include <command.h>
+#include <errno.h>
+#include <environment.h>
+#include <malloc.h>
+#include <getopt.h>
+#include <fs.h>
+#include <xfuncs.h>
+#include <usb/usbserial.h>
+#include <usb/dfu.h>
+#include <usb/gadget-multi.h>
+#include <globalvar.h>
+#include <magicvar.h>
+
+static int autostart;
+static int acm;
+static char *fastboot_function;
+
+static int usbgadget_autostart(void)
+{
+ struct f_multi_opts opts = {};
+
+ if (!autostart)
+ return 0;
+
+ setenv("otg.mode", "peripheral");
+
+ if (fastboot_function)
+ opts.fastboot_opts.files = file_list_parse(fastboot_function);
+
+ opts.create_acm = acm;
+
+ return usb_multi_register(&opts);
+}
+postenvironment_initcall(usbgadget_autostart);
+
+static int usbgadget_globalvars_init(void)
+{
+
+ globalvar_add_simple_bool("usbgadget.autostart", &autostart);
+ globalvar_add_simple_bool("usbgadget.acm", &acm);
+ globalvar_add_simple_string("usbgadget.fastboot_function",
+ &fastboot_function);
+
+ return 0;
+}
+device_initcall(usbgadget_globalvars_init);
+
+BAREBOX_MAGICVAR_NAMED(global_usbgadget_autostart,
+ global.usbgadget.autostart,
+ "usbgadget: Automatically start usbgadget on boot");
+BAREBOX_MAGICVAR_NAMED(global_usbgadget_acm,
+ global.usbgadget.acm,
+ "usbgadget: Create CDC ACM function");
+BAREBOX_MAGICVAR_NAMED(global_usbgadget_fastboot_function,
+ global.usbgadget.fastboot_function,
+ "usbgadget: Create Android Fastboot function");
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 54eaf468b7..db44052525 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -10,20 +10,17 @@ config USB_EHCI_OMAP
config USB_EHCI_ATMEL
depends on ARCH_AT91
depends on USB_EHCI
+ select USB_OHCI_AT91
bool "Atmel EHCI driver"
config USB_OHCI
bool "OHCI driver"
depends on !MMU
-if USB_OHCI
-
config USB_OHCI_AT91
depends on ARCH_AT91
bool "AT91 OHCI driver"
-endif
-
config USB_XHCI
bool "xHCI driver"
depends on HAS_DMA
diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c
index f075b5080c..1132879c9b 100644
--- a/drivers/usb/host/ehci-atmel.c
+++ b/drivers/usb/host/ehci-atmel.c
@@ -65,6 +65,9 @@ static int atmel_ehci_probe(struct device_d *dev)
struct resource *iores;
struct ehci_data data;
struct atmel_ehci_priv *atehci;
+ const char *uclk_name;
+
+ uclk_name = (dev->device_node) ? "usb_clk" : "uhpck";
atehci = xzalloc(sizeof(*atehci));
atehci->dev = dev;
@@ -76,7 +79,7 @@ static int atmel_ehci_probe(struct device_d *dev)
return -ENOENT;
}
- atehci->uclk = clk_get(dev, "uhpck");
+ atehci->uclk = clk_get(dev, uclk_name);
if (IS_ERR(atehci->iclk)) {
dev_err(dev, "Error getting function clock\n");
return -ENOENT;
@@ -107,9 +110,15 @@ static void atmel_ehci_remove(struct device_d *dev)
atmel_stop_clock(dev->priv);
}
+static const struct of_device_id atmel_ehci_dt_ids[] = {
+ { .compatible = "atmel,at91sam9g45-ehci" },
+ { /* sentinel */ }
+};
+
static struct driver_d atmel_ehci_driver = {
.name = "atmel-ehci",
.probe = atmel_ehci_probe,
.remove = atmel_ehci_remove,
+ .of_compatible = DRV_OF_COMPAT(atmel_ehci_dt_ids),
};
device_platform_driver(atmel_ehci_driver);
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index 5f745264b7..1013ba39c5 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -23,10 +23,18 @@
#include <usb/usb.h>
#include <usb/usb_defs.h>
#include <errno.h>
+#include <gpio.h>
+#include <of_gpio.h>
#include <io.h>
+#include <mach/board.h>
+
#include "ohci.h"
+#define at91_for_each_port(index) \
+ for ((index) = 0; (index) < AT91_MAX_USBH_PORTS; (index)++)
+
+
struct ohci_at91_priv {
struct device_d *dev;
struct clk *iclk;
@@ -59,6 +67,59 @@ static void at91_stop_clock(struct ohci_at91_priv *ohci_at91)
clk_disable(ohci_at91->iclk);
}
+static int at91_ohci_probe_dt(struct device_d *dev)
+{
+ u32 ports;
+ int i, ret, gpio;
+ enum of_gpio_flags flags;
+ struct at91_usbh_data *pdata;
+ struct device_node *np = dev->device_node;
+
+ pdata = xzalloc(sizeof(*pdata));
+ dev->platform_data = pdata;
+
+ if (!of_property_read_u32(np, "num-ports", &ports)) {
+ pdata->ports = ports;
+ } else {
+ dev_err(dev, "Failed to read 'num-ports' property\n");
+ return -EINVAL;
+ }
+
+ at91_for_each_port(i) {
+ /*
+ * do not configure PIO if not in relation with
+ * real USB port on board
+ */
+ if (i >= pdata->ports) {
+ pdata->vbus_pin[i] = -EINVAL;
+ continue;
+ }
+
+ gpio = of_get_named_gpio_flags(np, "atmel,vbus-gpio", i,
+ &flags);
+ pdata->vbus_pin[i] = gpio;
+ if (!gpio_is_valid(gpio))
+ continue;
+ pdata->vbus_pin_active_low[i] = flags & OF_GPIO_ACTIVE_LOW;
+
+ ret = gpio_request(gpio, "ohci_vbus");
+ if (ret) {
+ dev_err(dev, "can't request vbus gpio %d\n", gpio);
+ continue;
+ }
+ ret = gpio_direction_output(gpio,
+ !pdata->vbus_pin_active_low[i]);
+ if (ret) {
+ dev_err(dev, "can't put vbus gpio %d as output %d\n",
+ gpio, !pdata->vbus_pin_active_low[i]);
+ gpio_free(gpio);
+ continue;
+ }
+ }
+
+ return 0;
+}
+
static int at91_ohci_probe(struct device_d *dev)
{
int ret;
@@ -68,6 +129,12 @@ static int at91_ohci_probe(struct device_d *dev)
dev->priv = ohci_at91;
ohci_at91->dev = dev;
+ if (dev->device_node) {
+ ret = at91_ohci_probe_dt(dev);
+ if (ret < 0)
+ return ret;
+ }
+
io = dev_get_resource(dev, IORESOURCE_MEM, 0);
if (IS_ERR(io)) {
dev_err(dev, "Failed to get IORESOURCE_MEM\n");
@@ -75,13 +142,13 @@ static int at91_ohci_probe(struct device_d *dev)
}
ohci_at91->regs = IOMEM(io->start);
- ohci_at91->iclk = clk_get(NULL, "ohci_clk");
+ ohci_at91->iclk = clk_get(dev, "ohci_clk");
if (IS_ERR(ohci_at91->iclk)) {
dev_err(dev, "Failed to get 'ohci_clk'\n");
return PTR_ERR(ohci_at91->iclk);
}
- ohci_at91->fclk = clk_get(NULL, "uhpck");
+ ohci_at91->fclk = clk_get(dev, "uhpck");
if (IS_ERR(ohci_at91->fclk)) {
dev_err(dev, "Failed to get 'uhpck'\n");
return PTR_ERR(ohci_at91->fclk);
@@ -107,6 +174,7 @@ static int at91_ohci_probe(struct device_d *dev)
static void at91_ohci_remove(struct device_d *dev)
{
+ struct at91_usbh_data *pdata = dev->platform_data;
struct ohci_at91_priv *ohci_at91 = dev->priv;
/*
@@ -118,11 +186,32 @@ static void at91_ohci_remove(struct device_d *dev)
* Stop the USB clocks.
*/
at91_stop_clock(ohci_at91);
+
+ if (pdata) {
+ bool active_low;
+ int i, gpio;
+
+ at91_for_each_port(i) {
+ gpio = pdata->vbus_pin[i];
+ active_low = pdata->vbus_pin_active_low[i];
+
+ if (gpio_is_valid(gpio)) {
+ gpio_set_value(gpio, active_low);
+ gpio_free(gpio);
+ }
+ }
+ }
}
+static const struct of_device_id at91_ohci_dt_ids[] = {
+ { .compatible = "atmel,at91rm9200-ohci" },
+ { /* sentinel */ }
+};
+
static struct driver_d at91_ohci_driver = {
.name = "at91_ohci",
.probe = at91_ohci_probe,
.remove = at91_ohci_remove,
+ .of_compatible = DRV_OF_COMPAT(at91_ohci_dt_ids),
};
device_platform_driver(at91_ohci_driver);
diff --git a/drivers/video/edid.c b/drivers/video/edid.c
index 258526433e..bee4594118 100644
--- a/drivers/video/edid.c
+++ b/drivers/video/edid.c
@@ -387,7 +387,7 @@ static void fb_timings_vfreq(struct __fb_timings *timings)
* REQUIRES:
* A valid info->monspecs, otherwise 'safe numbers' will be used.
*/
-int fb_get_mode(int flags, u32 val, struct fb_videomode *var)
+static int fb_get_mode(int flags, u32 val, struct fb_videomode *var)
{
struct __fb_timings *timings;
u32 interlace = 1, dscan = 1;
diff --git a/drivers/video/imx-ipu-v3/ipufb.c b/drivers/video/imx-ipu-v3/ipufb.c
index 343f9e5578..9597eda0d0 100644
--- a/drivers/video/imx-ipu-v3/ipufb.c
+++ b/drivers/video/imx-ipu-v3/ipufb.c
@@ -333,10 +333,8 @@ static int ipufb_probe(struct device_d *dev)
}
ret = vpl_ioctl(&fbi->vpl, 2 + fbi->dino, VPL_GET_VIDEOMODES, &info->modes);
- if (ret) {
- dev_err(fbi->dev, "failed to get modes: %s\n", strerror(-ret));
- return ret;
- }
+ if (ret)
+ dev_dbg(fbi->dev, "failed to get modes: %s\n", strerror(-ret));
ret = register_framebuffer(info);
if (ret < 0) {
diff --git a/drivers/video/simplefb.c b/drivers/video/simplefb.c
index 357262988e..a2c59de364 100644
--- a/drivers/video/simplefb.c
+++ b/drivers/video/simplefb.c
@@ -89,9 +89,6 @@ static const struct simplefb_mode *simplefb_find_mode(const struct fb_info *fbi)
static int simplefb_create_node(struct device_node *root,
const struct fb_info *fbi, const char *format)
{
- const char *compat = "simple-framebuffer";
- const char *disabled = "disabled";
- const char *okay = "okay";
struct device_node *node;
u32 cells[2];
int ret;
@@ -100,12 +97,11 @@ static int simplefb_create_node(struct device_node *root,
if (!node)
return -ENOMEM;
- ret = of_set_property(node, "status", disabled,
- strlen(disabled) + 1, 1);
+ ret = of_property_write_string(node, "status", "disabled");
if (ret < 0)
return ret;
- ret = of_set_property(node, "compatible", compat, strlen(compat) + 1, 1);
+ ret = of_property_write_string(node, "compatible", "simple-framebuffer");
if (ret)
return ret;
@@ -130,14 +126,14 @@ static int simplefb_create_node(struct device_node *root,
if (ret < 0)
return ret;
- ret = of_set_property(node, "format", format, strlen(format) + 1, 1);
+ ret = of_property_write_string(node, "format", format);
if (ret < 0)
return ret;
of_add_reserve_entry((u32)fbi->screen_base,
(u32)fbi->screen_base + fbi->screen_size);
- return of_set_property(node, "status", okay, strlen(okay) + 1, 1);
+ return of_property_write_string(node, "status", "okay");
}
static int simplefb_of_fixup(struct device_node *root, void *ctx)
diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c
index 946e9d3340..916027ea87 100644
--- a/drivers/w1/masters/w1-gpio.c
+++ b/drivers/w1/masters/w1-gpio.c
@@ -16,6 +16,7 @@
#include <driver.h>
#include <linux/w1-gpio.h>
#include <gpio.h>
+#include <of_gpio.h>
#include "../w1.h"
@@ -43,12 +44,58 @@ static u8 w1_gpio_read_bit(struct w1_bus *bus)
return gpio_get_value(pdata->pin) ? 1 : 0;
}
+static int w1_gpio_probe_dt(struct device_d *dev)
+{
+ struct w1_gpio_platform_data *pdata;
+ struct device_node *np = dev->device_node;
+ int gpio;
+
+ if (dev->platform_data)
+ return 0;
+
+ pdata = xzalloc(sizeof(*pdata));
+
+ if (of_get_property(np, "linux,open-drain", NULL))
+ pdata->is_open_drain = 1;
+
+ gpio = of_get_gpio(np, 0);
+ if (!gpio_is_valid(gpio)) {
+ if (gpio != -EPROBE_DEFER)
+ dev_err(dev,
+ "Failed to parse gpio property for data pin (%d)\n",
+ gpio);
+
+ goto free_pdata;
+ }
+ pdata->pin = gpio;
+
+ gpio = of_get_gpio(np, 1);
+ if (gpio == -EPROBE_DEFER)
+ goto free_pdata;
+
+ /* ignore other errors as the pullup gpio is optional */
+ pdata->ext_pullup_enable_pin = gpio;
+
+ dev->platform_data = pdata;
+ return 0;
+
+free_pdata:
+ free(pdata);
+ return gpio;
+}
+
static int __init w1_gpio_probe(struct device_d *dev)
{
struct w1_bus *master;
struct w1_gpio_platform_data *pdata;
int err;
+ if (IS_ENABLED(CONFIG_OFDEVICE)) {
+ err = w1_gpio_probe_dt(dev);
+ if (err < 0)
+ return err;
+ }
+
pdata = dev->platform_data;
if (!pdata)
@@ -104,8 +151,14 @@ static int __init w1_gpio_probe(struct device_d *dev)
return err;
}
+static __maybe_unused const struct of_device_id w1_gpio_dt_ids[] = {
+ { .compatible = "w1-gpio" },
+ {}
+};
+
static struct driver_d w1_gpio_driver = {
.name = "w1-gpio",
.probe = w1_gpio_probe,
+ .of_compatible = DRV_OF_COMPAT(w1_gpio_dt_ids),
};
device_platform_driver(w1_gpio_driver);
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 63fb1a8c57..83b6528a5f 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -46,4 +46,10 @@ config WATCHDOG_OMAP
help
Add support for watchdog on the TI OMAP SoC.
+config WATCHDOG_ORION
+ bool "Watchdog for Armada XP"
+ depends on ARCH_ARMADA_XP
+ help
+ Add support for watchdog on the Marvall Armada XP
+
endif
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 5fca4c368c..a3b26675ce 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_WATCHDOG_MXS28) += im28wd.o
obj-$(CONFIG_WATCHDOG_DW) += dw_wdt.o
obj-$(CONFIG_WATCHDOG_JZ4740) += jz4740.o
obj-$(CONFIG_WATCHDOG_IMX_RESET_SOURCE) += imxwd.o
+obj-$(CONFIG_WATCHDOG_ORION) += orion_wdt.o
diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c
new file mode 100644
index 0000000000..2802033f71
--- /dev/null
+++ b/drivers/watchdog/orion_wdt.c
@@ -0,0 +1,123 @@
+/*
+ * Watchdog driver for Marvell Armada XP.
+ *
+ * Copyright (C) 2017 Pengutronix, Uwe Kleine-König <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <init.h>
+#include <io.h>
+#include <malloc.h>
+#include <of.h>
+#include <watchdog.h>
+
+#define CLKRATE 25000000
+
+/* registers relative to timer_base (i.e. first reg property) */
+#define TIMER_CTRL 0x00
+#define TIMER_CTRL_WD_TIMER_25MHZ_EN BIT(10)
+#define TIMER_CTRL_WD_TIMER_EN BIT(8)
+
+#define TIMER_STATUS 0x04
+#define TIMER_STATUS_WD_EXPIRED BIT(31)
+
+#define TIMER_WD_TIMER 0x34
+
+/* registers relative to rstout_base (i.e. second reg property) */
+#define WD_RSTOUTn_MASK 0x00
+#define WD_RSTOUTn_MASK_GLOBAL_WD BIT(8)
+
+struct orion_wdt_ddata {
+ struct watchdog wd;
+ void __iomem *timer_base;
+ void __iomem *rstout_base;
+};
+
+static int armada_xp_set_timeout(struct watchdog *wd, unsigned timeout)
+{
+ struct orion_wdt_ddata *ddata =
+ container_of(wd, struct orion_wdt_ddata, wd);
+ u32 ctrl;
+
+ if (0xffffffff / CLKRATE < timeout)
+ return -EINVAL;
+
+ ctrl = readl(ddata->timer_base + TIMER_CTRL);
+
+ if (timeout == 0) {
+ /* disable timer */
+ ctrl &= ~TIMER_CTRL_WD_TIMER_EN;
+ writel(ctrl, ddata->timer_base + TIMER_CTRL);
+
+ return 0;
+ }
+
+ /* setup duration */
+ writel(CLKRATE * timeout, ddata->timer_base + TIMER_WD_TIMER);
+
+ /* clear expiration status */
+ writel(readl(ddata->timer_base + TIMER_STATUS) & ~TIMER_STATUS_WD_EXPIRED,
+ ddata->timer_base + TIMER_STATUS);
+
+ /* assert reset on expiration */
+ writel(WD_RSTOUTn_MASK_GLOBAL_WD, ddata->rstout_base + WD_RSTOUTn_MASK);
+
+ /* enable */
+ ctrl |= TIMER_CTRL_WD_TIMER_25MHZ_EN | TIMER_CTRL_WD_TIMER_EN;
+ writel(ctrl, ddata->timer_base + TIMER_CTRL);
+
+ return 0;
+}
+
+static int orion_wdt_probe(struct device_d *dev)
+{
+ struct orion_wdt_ddata* ddata;
+ struct resource *res_timer, *res_rstout;
+
+ ddata = xzalloc(sizeof(*ddata));
+
+ ddata->wd.set_timeout = armada_xp_set_timeout;
+ ddata->wd.name = "orion_wdt";
+ ddata->wd.dev = dev;
+
+ res_timer = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(res_timer)) {
+ dev_err(dev, "could not get timer memory region\n");
+ return PTR_ERR(res_timer);
+ }
+ ddata->timer_base = IOMEM(res_timer->start);
+
+ res_rstout = dev_request_mem_resource(dev, 1);
+ if (IS_ERR(res_rstout)) {
+ dev_err(dev, "could not get rstout memory region\n");
+ release_region(res_timer);
+
+ return PTR_ERR(res_rstout);
+ }
+ ddata->rstout_base = IOMEM(res_rstout->start);
+
+ return watchdog_register(&ddata->wd);
+}
+
+static const struct of_device_id orion_wdt_of_match[] = {
+ {
+ .compatible = "marvell,armada-xp-wdt",
+ }, { /* sentinel */ }
+};
+
+static struct driver_d orion_wdt_driver = {
+ .probe = orion_wdt_probe,
+ .name = "orion_wdt",
+ .of_compatible = DRV_OF_COMPAT(orion_wdt_of_match),
+};
+device_platform_driver(orion_wdt_driver);
diff --git a/dts/Bindings/arm/marvell/cp110-system-controller0.txt b/dts/Bindings/arm/marvell/cp110-system-controller0.txt
index 30c546900b..07dbb35818 100644
--- a/dts/Bindings/arm/marvell/cp110-system-controller0.txt
+++ b/dts/Bindings/arm/marvell/cp110-system-controller0.txt
@@ -45,7 +45,7 @@ The following clocks are available:
- 1 15 SATA
- 1 16 SATA USB
- 1 17 Main
- - 1 18 SD/MMC
+ - 1 18 SD/MMC/GOP
- 1 21 Slow IO (SPI, NOR, BootROM, I2C, UART)
- 1 22 USB3H0
- 1 23 USB3H1
@@ -65,7 +65,7 @@ Required properties:
"cpm-audio", "cpm-communit", "cpm-nand", "cpm-ppv2", "cpm-sdio",
"cpm-mg-domain", "cpm-mg-core", "cpm-xor1", "cpm-xor0", "cpm-gop-dp", "none",
"cpm-pcie_x10", "cpm-pcie_x11", "cpm-pcie_x4", "cpm-pcie-xor", "cpm-sata",
- "cpm-sata-usb", "cpm-main", "cpm-sd-mmc", "none", "none", "cpm-slow-io",
+ "cpm-sata-usb", "cpm-main", "cpm-sd-mmc-gop", "none", "none", "cpm-slow-io",
"cpm-usb3h0", "cpm-usb3h1", "cpm-usb3dev", "cpm-eip150", "cpm-eip197";
Example:
@@ -78,6 +78,6 @@ Example:
gate-clock-output-names = "cpm-audio", "cpm-communit", "cpm-nand", "cpm-ppv2", "cpm-sdio",
"cpm-mg-domain", "cpm-mg-core", "cpm-xor1", "cpm-xor0", "cpm-gop-dp", "none",
"cpm-pcie_x10", "cpm-pcie_x11", "cpm-pcie_x4", "cpm-pcie-xor", "cpm-sata",
- "cpm-sata-usb", "cpm-main", "cpm-sd-mmc", "none", "none", "cpm-slow-io",
+ "cpm-sata-usb", "cpm-main", "cpm-sd-mmc-gop", "none", "none", "cpm-slow-io",
"cpm-usb3h0", "cpm-usb3h1", "cpm-usb3dev", "cpm-eip150", "cpm-eip197";
};
diff --git a/dts/Bindings/display/exynos/exynos_dsim.txt b/dts/Bindings/display/exynos/exynos_dsim.txt
index a782659936..ca5204b3bc 100644
--- a/dts/Bindings/display/exynos/exynos_dsim.txt
+++ b/dts/Bindings/display/exynos/exynos_dsim.txt
@@ -4,7 +4,6 @@ Required properties:
- compatible: value should be one of the following
"samsung,exynos3250-mipi-dsi" /* for Exynos3250/3472 SoCs */
"samsung,exynos4210-mipi-dsi" /* for Exynos4 SoCs */
- "samsung,exynos4415-mipi-dsi" /* for Exynos4415 SoC */
"samsung,exynos5410-mipi-dsi" /* for Exynos5410/5420/5440 SoCs */
"samsung,exynos5422-mipi-dsi" /* for Exynos5422/5800 SoCs */
"samsung,exynos5433-mipi-dsi" /* for Exynos5433 SoCs */
diff --git a/dts/Bindings/display/exynos/samsung-fimd.txt b/dts/Bindings/display/exynos/samsung-fimd.txt
index 18645e0228..5837402c3a 100644
--- a/dts/Bindings/display/exynos/samsung-fimd.txt
+++ b/dts/Bindings/display/exynos/samsung-fimd.txt
@@ -11,7 +11,6 @@ Required properties:
"samsung,s5pv210-fimd"; /* for S5PV210 SoC */
"samsung,exynos3250-fimd"; /* for Exynos3250/3472 SoCs */
"samsung,exynos4210-fimd"; /* for Exynos4 SoCs */
- "samsung,exynos4415-fimd"; /* for Exynos4415 SoC */
"samsung,exynos5250-fimd"; /* for Exynos5250 SoCs */
"samsung,exynos5420-fimd"; /* for Exynos5420/5422/5800 SoCs */
diff --git a/dts/Bindings/mmc/rockchip-dw-mshc.txt b/dts/Bindings/mmc/rockchip-dw-mshc.txt
index ea9c1c9607..520d61dad6 100644
--- a/dts/Bindings/mmc/rockchip-dw-mshc.txt
+++ b/dts/Bindings/mmc/rockchip-dw-mshc.txt
@@ -13,7 +13,7 @@ Required Properties:
- "rockchip,rk2928-dw-mshc": for Rockchip RK2928 and following,
before RK3288
- "rockchip,rk3288-dw-mshc": for Rockchip RK3288
- - "rockchip,rk1108-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK1108
+ - "rockchip,rv1108-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RV1108
- "rockchip,rk3036-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK3036
- "rockchip,rk3368-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK3368
- "rockchip,rk3399-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK3399
diff --git a/dts/Bindings/phy/brcm,nsp-usb3-phy.txt b/dts/Bindings/phy/brcm,nsp-usb3-phy.txt
deleted file mode 100644
index e68ae5dec9..0000000000
--- a/dts/Bindings/phy/brcm,nsp-usb3-phy.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-Broadcom USB3 phy binding for northstar plus SoC
-The USB3 phy is internal to the SoC and is accessed using mdio interface.
-
-Required mdio bus properties:
-- reg: Should be 0x0 for SoC internal USB3 phy
-- #address-cells: must be 1
-- #size-cells: must be 0
-
-Required USB3 PHY properties:
-- compatible: should be "brcm,nsp-usb3-phy"
-- reg: USB3 Phy address on SoC internal MDIO bus and it should be 0x10.
-- usb3-ctrl-syscon: handler of syscon node defining physical address
- of usb3 control register.
-- #phy-cells: must be 0
-
-Required usb3 control properties:
-- compatible: should be "brcm,nsp-usb3-ctrl"
-- reg: offset and length of the control registers
-
-Example:
-
- mdio@0 {
- reg = <0x0>;
- #address-cells = <1>;
- #size-cells = <0>;
-
- usb3_phy: usb-phy@10 {
- compatible = "brcm,nsp-usb3-phy";
- reg = <0x10>;
- usb3-ctrl-syscon = <&usb3_ctrl>;
- #phy-cells = <0>;
- status = "disabled";
- };
- };
-
- usb3_ctrl: syscon@104408 {
- compatible = "brcm,nsp-usb3-ctrl", "syscon";
- reg = <0x104408 0x3fc>;
- };
diff --git a/dts/Bindings/powerpc/4xx/emac.txt b/dts/Bindings/powerpc/4xx/emac.txt
index 712baf6c3e..44b842b6ca 100644
--- a/dts/Bindings/powerpc/4xx/emac.txt
+++ b/dts/Bindings/powerpc/4xx/emac.txt
@@ -71,6 +71,9 @@
For Axon it can be absent, though my current driver
doesn't handle phy-address yet so for now, keep
0x00ffffff in it.
+ - phy-handle : Used to describe configurations where a external PHY
+ is used. Please refer to:
+ Documentation/devicetree/bindings/net/ethernet.txt
- rx-fifo-size-gige : 1 cell, Rx fifo size in bytes for 1000 Mb/sec
operations (if absent the value is the same as
rx-fifo-size). For Axon, either absent or 2048.
@@ -81,8 +84,22 @@
offload, phandle of the TAH device node.
- tah-channel : 1 cell, optional. If appropriate, channel used on the
TAH engine.
+ - fixed-link : Fixed-link subnode describing a link to a non-MDIO
+ managed entity. See
+ Documentation/devicetree/bindings/net/fixed-link.txt
+ for details.
+ - mdio subnode : When the EMAC has a phy connected to its local
+ mdio, which us supported by the kernel's network
+ PHY library in drivers/net/phy, there must be device
+ tree subnode with the following required properties:
+ - #address-cells: Must be <1>.
+ - #size-cells: Must be <0>.
- Example:
+ For PHY definitions: Please refer to
+ Documentation/devicetree/bindings/net/phy.txt and
+ Documentation/devicetree/bindings/net/ethernet.txt
+
+ Examples:
EMAC0: ethernet@40000800 {
device_type = "network";
@@ -104,6 +121,48 @@
zmii-channel = <0>;
};
+ EMAC1: ethernet@ef600c00 {
+ device_type = "network";
+ compatible = "ibm,emac-apm821xx", "ibm,emac4sync";
+ interrupt-parent = <&EMAC1>;
+ interrupts = <0 1>;
+ #interrupt-cells = <1>;
+ #address-cells = <0>;
+ #size-cells = <0>;
+ interrupt-map = <0 &UIC2 0x10 IRQ_TYPE_LEVEL_HIGH /* Status */
+ 1 &UIC2 0x14 IRQ_TYPE_LEVEL_HIGH /* Wake */>;
+ reg = <0xef600c00 0x000000c4>;
+ local-mac-address = [000000000000]; /* Filled in by U-Boot */
+ mal-device = <&MAL0>;
+ mal-tx-channel = <0>;
+ mal-rx-channel = <0>;
+ cell-index = <0>;
+ max-frame-size = <9000>;
+ rx-fifo-size = <16384>;
+ tx-fifo-size = <2048>;
+ fifo-entry-size = <10>;
+ phy-mode = "rgmii";
+ phy-handle = <&phy0>;
+ phy-map = <0x00000000>;
+ rgmii-device = <&RGMII0>;
+ rgmii-channel = <0>;
+ tah-device = <&TAH0>;
+ tah-channel = <0>;
+ has-inverted-stacr-oc;
+ has-new-stacr-staopc;
+
+ mdio {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ phy0: ethernet-phy@0 {
+ compatible = "ethernet-phy-ieee802.3-c22";
+ reg = <0>;
+ };
+ };
+ };
+
+
ii) McMAL node
Required properties:
@@ -145,4 +204,3 @@
- revision : as provided by the RGMII new version register if
available.
For Axon: 0x0000012a
-
diff --git a/dts/Bindings/regulator/ti-abb-regulator.txt b/dts/Bindings/regulator/ti-abb-regulator.txt
index c3f6546eba..6a23ad9ac5 100644
--- a/dts/Bindings/regulator/ti-abb-regulator.txt
+++ b/dts/Bindings/regulator/ti-abb-regulator.txt
@@ -45,7 +45,7 @@ Required Properties:
Optional Properties:
- reg-names: In addition to the required properties, the following are optional
- "efuse-address" - Contains efuse base address used to pick up ABB info.
- - "ldo-address" - Contains address of ABB LDO overide register address.
+ - "ldo-address" - Contains address of ABB LDO override register.
"efuse-address" is required for this.
- ti,ldovbb-vset-mask - Required if ldo-address is set, mask for LDO override
register to provide override vset value.
diff --git a/dts/Bindings/rng/omap_rng.txt b/dts/Bindings/rng/omap_rng.txt
index 471477299e..9cf7876ab4 100644
--- a/dts/Bindings/rng/omap_rng.txt
+++ b/dts/Bindings/rng/omap_rng.txt
@@ -12,7 +12,8 @@ Required properties:
- reg : Offset and length of the register set for the module
- interrupts : the interrupt number for the RNG module.
Used for "ti,omap4-rng" and "inside-secure,safexcel-eip76"
-- clocks: the trng clock source
+- clocks: the trng clock source. Only mandatory for the
+ "inside-secure,safexcel-eip76" compatible.
Example:
/* AM335x */
diff --git a/dts/Bindings/usb/usb251xb.txt b/dts/Bindings/usb/usb251xb.txt
index 0c065f7765..3957d4edaa 100644
--- a/dts/Bindings/usb/usb251xb.txt
+++ b/dts/Bindings/usb/usb251xb.txt
@@ -7,18 +7,18 @@ Required properties :
- compatible : Should be "microchip,usb251xb" or one of the specific types:
"microchip,usb2512b", "microchip,usb2512bi", "microchip,usb2513b",
"microchip,usb2513bi", "microchip,usb2514b", "microchip,usb2514bi"
- - hub-reset-gpios : Should specify the gpio for hub reset
+ - reset-gpios : Should specify the gpio for hub reset
+ - reg : I2C address on the selected bus (default is <0x2C>)
Optional properties :
- - reg : I2C address on the selected bus (default is <0x2C>)
- skip-config : Skip Hub configuration, but only send the USB-Attach command
- - vendor-id : USB Vendor ID of the hub (16 bit, default is 0x0424)
- - product-id : USB Product ID of the hub (16 bit, default depends on type)
- - device-id : USB Device ID of the hub (16 bit, default is 0x0bb3)
- - language-id : USB Language ID (16 bit, default is 0x0000)
- - manufacturer : USB Manufacturer string (max 31 characters long)
- - product : USB Product string (max 31 characters long)
- - serial : USB Serial string (max 31 characters long)
+ - vendor-id : Set USB Vendor ID of the hub (16 bit, default is 0x0424)
+ - product-id : Set USB Product ID of the hub (16 bit, default depends on type)
+ - device-id : Set USB Device ID of the hub (16 bit, default is 0x0bb3)
+ - language-id : Set USB Language ID (16 bit, default is 0x0000)
+ - manufacturer : Set USB Manufacturer string (max 31 characters long)
+ - product : Set USB Product string (max 31 characters long)
+ - serial : Set USB Serial string (max 31 characters long)
- {bus,self}-powered : selects between self- and bus-powered operation (default
is self-powered)
- disable-hi-speed : disable USB Hi-Speed support
@@ -31,8 +31,10 @@ Optional properties :
(default is individual)
- dynamic-power-switching : enable auto-switching from self- to bus-powered
operation if the local power source is removed or unavailable
- - oc-delay-{100us,4ms,8ms,16ms} : set over current timer delay (default is 8ms)
- - compound-device : indicated the hub is part of a compound device
+ - oc-delay-us : Delay time (in microseconds) for filtering the over-current
+ sense inputs. Valid values are 100, 4000, 8000 (default) and 16000. If
+ an invalid value is given, the default is used instead.
+ - compound-device : indicate the hub is part of a compound device
- port-mapping-mode : enable port mapping mode
- string-support : enable string descriptor support (required for manufacturer,
product and serial string configuration)
@@ -40,34 +42,15 @@ Optional properties :
device connected.
- sp-disabled-ports : Specifies the ports which will be self-power disabled
- bp-disabled-ports : Specifies the ports which will be bus-power disabled
- - max-sp-power : Specifies the maximum current the hub consumes from an
- upstream port when operating as self-powered hub including the power
- consumption of a permanently attached peripheral if the hub is
- configured as a compound device. The value is given in mA in a 0 - 500
- range (default is 2).
- - max-bp-power : Specifies the maximum current the hub consumes from an
- upstream port when operating as bus-powered hub including the power
- consumption of a permanently attached peripheral if the hub is
- configured as a compound device. The value is given in mA in a 0 - 500
- range (default is 100).
- - max-sp-current : Specifies the maximum current the hub consumes from an
- upstream port when operating as self-powered hub EXCLUDING the power
- consumption of a permanently attached peripheral if the hub is
- configured as a compound device. The value is given in mA in a 0 - 500
- range (default is 2).
- - max-bp-current : Specifies the maximum current the hub consumes from an
- upstream port when operating as bus-powered hub EXCLUDING the power
- consumption of a permanently attached peripheral if the hub is
- configured as a compound device. The value is given in mA in a 0 - 500
- range (default is 100).
- - power-on-time : Specifies the time it takes from the time the host initiates
- the power-on sequence to a port until the port has adequate power. The
- value is given in ms in a 0 - 510 range (default is 100ms).
+ - power-on-time-ms : Specifies the time it takes from the time the host
+ initiates the power-on sequence to a port until the port has adequate
+ power. The value is given in ms in a 0 - 510 range (default is 100ms).
Examples:
usb2512b@2c {
compatible = "microchip,usb2512b";
- hub-reset-gpios = <&gpio1 4 GPIO_ACTIVE_LOW>;
+ reg = <0x2c>;
+ reset-gpios = <&gpio1 4 GPIO_ACTIVE_LOW>;
};
usb2514b@2c {
diff --git a/dts/include/dt-bindings/sound/cs42l42.h b/dts/include/dt-bindings/sound/cs42l42.h
index 399a123aed..db69d84ed7 100644
--- a/dts/include/dt-bindings/sound/cs42l42.h
+++ b/dts/include/dt-bindings/sound/cs42l42.h
@@ -20,7 +20,7 @@
#define CS42L42_HPOUT_LOAD_1NF 0
#define CS42L42_HPOUT_LOAD_10NF 1
-/* HPOUT Clamp to GND Overide */
+/* HPOUT Clamp to GND Override */
#define CS42L42_HPOUT_CLAMP_EN 0
#define CS42L42_HPOUT_CLAMP_DIS 1
diff --git a/dts/src/arc/skeleton.dtsi b/dts/src/arc/skeleton.dtsi
index 65808fe0a2..2891cb266c 100644
--- a/dts/src/arc/skeleton.dtsi
+++ b/dts/src/arc/skeleton.dtsi
@@ -26,6 +26,7 @@
device_type = "cpu";
compatible = "snps,arc770d";
reg = <0>;
+ clocks = <&core_clk>;
};
};
diff --git a/dts/src/arc/skeleton_hs.dtsi b/dts/src/arc/skeleton_hs.dtsi
index 2dfe8037df..5e944d3e5b 100644
--- a/dts/src/arc/skeleton_hs.dtsi
+++ b/dts/src/arc/skeleton_hs.dtsi
@@ -21,6 +21,7 @@
device_type = "cpu";
compatible = "snps,archs38";
reg = <0>;
+ clocks = <&core_clk>;
};
};
diff --git a/dts/src/arc/skeleton_hs_idu.dtsi b/dts/src/arc/skeleton_hs_idu.dtsi
index 4c11079f35..54b277d7de 100644
--- a/dts/src/arc/skeleton_hs_idu.dtsi
+++ b/dts/src/arc/skeleton_hs_idu.dtsi
@@ -19,8 +19,27 @@
cpu@0 {
device_type = "cpu";
- compatible = "snps,archs38xN";
+ compatible = "snps,archs38";
reg = <0>;
+ clocks = <&core_clk>;
+ };
+ cpu@1 {
+ device_type = "cpu";
+ compatible = "snps,archs38";
+ reg = <1>;
+ clocks = <&core_clk>;
+ };
+ cpu@2 {
+ device_type = "cpu";
+ compatible = "snps,archs38";
+ reg = <2>;
+ clocks = <&core_clk>;
+ };
+ cpu@3 {
+ device_type = "cpu";
+ compatible = "snps,archs38";
+ reg = <3>;
+ clocks = <&core_clk>;
};
};
diff --git a/dts/src/arc/vdk_axs10x_mb.dtsi b/dts/src/arc/vdk_axs10x_mb.dtsi
index f0df59b23e..459fc656b7 100644
--- a/dts/src/arc/vdk_axs10x_mb.dtsi
+++ b/dts/src/arc/vdk_axs10x_mb.dtsi
@@ -112,13 +112,19 @@
interrupts = <7>;
bus-width = <4>;
};
+ };
- /* Embedded Vision subsystem UIO mappings; only relevant for EV VDK */
- uio_ev: uio@0xD0000000 {
- compatible = "generic-uio";
- reg = <0xD0000000 0x2000 0xD1000000 0x2000 0x90000000 0x10000000 0xC0000000 0x10000000>;
- reg-names = "ev_gsa", "ev_ctrl", "ev_shared_mem", "ev_code_mem";
- interrupts = <23>;
- };
+ /*
+ * Embedded Vision subsystem UIO mappings; only relevant for EV VDK
+ *
+ * This node is intentionally put outside of MB above becase
+ * it maps areas outside of MB's 0xEz-0xFz.
+ */
+ uio_ev: uio@0xD0000000 {
+ compatible = "generic-uio";
+ reg = <0xD0000000 0x2000 0xD1000000 0x2000 0x90000000 0x10000000 0xC0000000 0x10000000>;
+ reg-names = "ev_gsa", "ev_ctrl", "ev_shared_mem", "ev_code_mem";
+ interrupt-parent = <&mb_intc>;
+ interrupts = <23>;
};
};
diff --git a/dts/src/arm/am335x-pcm-953.dtsi b/dts/src/arm/am335x-pcm-953.dtsi
index 02981eae96..1ec8e0d801 100644
--- a/dts/src/arm/am335x-pcm-953.dtsi
+++ b/dts/src/arm/am335x-pcm-953.dtsi
@@ -63,14 +63,14 @@
label = "home";
linux,code = <KEY_HOME>;
gpios = <&gpio3 7 GPIO_ACTIVE_HIGH>;
- gpio-key,wakeup;
+ wakeup-source;
};
button@1 {
label = "menu";
linux,code = <KEY_MENU>;
gpios = <&gpio3 8 GPIO_ACTIVE_HIGH>;
- gpio-key,wakeup;
+ wakeup-source;
};
};
diff --git a/dts/src/arm/am57xx-idk-common.dtsi b/dts/src/arm/am57xx-idk-common.dtsi
index 0d341c545b..e5ac1d81d1 100644
--- a/dts/src/arm/am57xx-idk-common.dtsi
+++ b/dts/src/arm/am57xx-idk-common.dtsi
@@ -315,6 +315,13 @@
/* ID & VBUS GPIOs provided in board dts */
};
};
+
+ tpic2810: tpic2810@60 {
+ compatible = "ti,tpic2810";
+ reg = <0x60>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
};
&mcspi3 {
@@ -330,13 +337,6 @@
spi-max-frequency = <1000000>;
spi-cpol;
};
-
- tpic2810: tpic2810@60 {
- compatible = "ti,tpic2810";
- reg = <0x60>;
- gpio-controller;
- #gpio-cells = <2>;
- };
};
&uart3 {
diff --git a/dts/src/arm/bcm5301x.dtsi b/dts/src/arm/bcm5301x.dtsi
index 4fbb089cf5..00de62dc00 100644
--- a/dts/src/arm/bcm5301x.dtsi
+++ b/dts/src/arm/bcm5301x.dtsi
@@ -66,14 +66,14 @@
timer@20200 {
compatible = "arm,cortex-a9-global-timer";
reg = <0x20200 0x100>;
- interrupts = <GIC_PPI 11 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_PPI 11 IRQ_TYPE_EDGE_RISING>;
clocks = <&periph_clk>;
};
local-timer@20600 {
compatible = "arm,cortex-a9-twd-timer";
reg = <0x20600 0x100>;
- interrupts = <GIC_PPI 13 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_PPI 13 IRQ_TYPE_EDGE_RISING>;
clocks = <&periph_clk>;
};
diff --git a/dts/src/arm/bcm953012k.dts b/dts/src/arm/bcm953012k.dts
index bfd923096a..ae31a5826e 100644
--- a/dts/src/arm/bcm953012k.dts
+++ b/dts/src/arm/bcm953012k.dts
@@ -48,15 +48,14 @@
};
memory {
- reg = <0x00000000 0x10000000>;
+ reg = <0x80000000 0x10000000>;
};
};
&uart0 {
- clock-frequency = <62499840>;
+ status = "okay";
};
&uart1 {
- clock-frequency = <62499840>;
status = "okay";
};
diff --git a/dts/src/arm/bcm958522er.dts b/dts/src/arm/bcm958522er.dts
index 3f04a40eb9..df05e7f568 100644
--- a/dts/src/arm/bcm958522er.dts
+++ b/dts/src/arm/bcm958522er.dts
@@ -55,6 +55,7 @@
gpio-restart {
compatible = "gpio-restart";
gpios = <&gpioa 15 GPIO_ACTIVE_LOW>;
+ open-source;
priority = <200>;
};
};
diff --git a/dts/src/arm/bcm958525er.dts b/dts/src/arm/bcm958525er.dts
index 9fd542200d..4a3ab19c62 100644
--- a/dts/src/arm/bcm958525er.dts
+++ b/dts/src/arm/bcm958525er.dts
@@ -55,6 +55,7 @@
gpio-restart {
compatible = "gpio-restart";
gpios = <&gpioa 15 GPIO_ACTIVE_LOW>;
+ open-source;
priority = <200>;
};
};
diff --git a/dts/src/arm/bcm958525xmc.dts b/dts/src/arm/bcm958525xmc.dts
index 41e7fd350f..81f78435d8 100644
--- a/dts/src/arm/bcm958525xmc.dts
+++ b/dts/src/arm/bcm958525xmc.dts
@@ -55,6 +55,7 @@
gpio-restart {
compatible = "gpio-restart";
gpios = <&gpioa 31 GPIO_ACTIVE_LOW>;
+ open-source;
priority = <200>;
};
};
diff --git a/dts/src/arm/bcm958622hr.dts b/dts/src/arm/bcm958622hr.dts
index 477c4860db..c88b8fefcb 100644
--- a/dts/src/arm/bcm958622hr.dts
+++ b/dts/src/arm/bcm958622hr.dts
@@ -55,6 +55,7 @@
gpio-restart {
compatible = "gpio-restart";
gpios = <&gpioa 15 GPIO_ACTIVE_LOW>;
+ open-source;
priority = <200>;
};
};
diff --git a/dts/src/arm/bcm958623hr.dts b/dts/src/arm/bcm958623hr.dts
index c0a499d5ba..d503fa0dde 100644
--- a/dts/src/arm/bcm958623hr.dts
+++ b/dts/src/arm/bcm958623hr.dts
@@ -55,6 +55,7 @@
gpio-restart {
compatible = "gpio-restart";
gpios = <&gpioa 15 GPIO_ACTIVE_LOW>;
+ open-source;
priority = <200>;
};
};
diff --git a/dts/src/arm/bcm958625hr.dts b/dts/src/arm/bcm958625hr.dts
index f7eb5854a2..cc0363b843 100644
--- a/dts/src/arm/bcm958625hr.dts
+++ b/dts/src/arm/bcm958625hr.dts
@@ -55,6 +55,7 @@
gpio-restart {
compatible = "gpio-restart";
gpios = <&gpioa 15 GPIO_ACTIVE_LOW>;
+ open-source;
priority = <200>;
};
};
diff --git a/dts/src/arm/bcm988312hr.dts b/dts/src/arm/bcm988312hr.dts
index 16666324fd..74e15a3cd9 100644
--- a/dts/src/arm/bcm988312hr.dts
+++ b/dts/src/arm/bcm988312hr.dts
@@ -55,6 +55,7 @@
gpio-restart {
compatible = "gpio-restart";
gpios = <&gpioa 15 GPIO_ACTIVE_LOW>;
+ open-source;
priority = <200>;
};
};
diff --git a/dts/src/arm/imx6qdl.dtsi b/dts/src/arm/imx6qdl.dtsi
index 6d7bf64961..8e90014705 100644
--- a/dts/src/arm/imx6qdl.dtsi
+++ b/dts/src/arm/imx6qdl.dtsi
@@ -768,6 +768,12 @@
mask = <0x60>;
status = "disabled";
};
+
+ snvs_lpgpr: snvs-lpgpr {
+ compatible = "fsl,imx6q-snvs-lpgpr";
+ regmap = <&snvs>;
+ offset = <0x68>;
+ };
};
epit1: epit@020d0000 { /* EPIT1 */
diff --git a/dts/src/arm/imx6sl.dtsi b/dts/src/arm/imx6sl.dtsi
index cc9572ea28..eeafba04cf 100644
--- a/dts/src/arm/imx6sl.dtsi
+++ b/dts/src/arm/imx6sl.dtsi
@@ -655,6 +655,12 @@
mask = <0x60>;
status = "disabled";
};
+
+ snvs_lpgpr: snvs-lpgpr {
+ compatible = "fsl,imx6sl-snvs-lpgpr";
+ regmap = <&snvs>;
+ offset = <0x68>;
+ };
};
epit1: epit@020d0000 {
diff --git a/dts/src/arm/imx6sx-udoo-neo.dtsi b/dts/src/arm/imx6sx-udoo-neo.dtsi
index 49f466fe0b..dcfc975914 100644
--- a/dts/src/arm/imx6sx-udoo-neo.dtsi
+++ b/dts/src/arm/imx6sx-udoo-neo.dtsi
@@ -121,11 +121,6 @@
};
};
-&cpu0 {
- arm-supply = <&sw1a_reg>;
- soc-supply = <&sw1c_reg>;
-};
-
&fec1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet1>;
diff --git a/dts/src/arm/sama5d2.dtsi b/dts/src/arm/sama5d2.dtsi
index 22332be721..528b4e9c6d 100644
--- a/dts/src/arm/sama5d2.dtsi
+++ b/dts/src/arm/sama5d2.dtsi
@@ -266,7 +266,7 @@
};
usb1: ohci@00400000 {
- compatible = "atmel,sama5d2-ohci", "usb-ohci";
+ compatible = "atmel,at91rm9200-ohci", "usb-ohci";
reg = <0x00400000 0x100000>;
interrupts = <41 IRQ_TYPE_LEVEL_HIGH 2>;
clocks = <&uhphs_clk>, <&uhphs_clk>, <&uhpck>;
diff --git a/dts/src/arm/ste-dbx5x0.dtsi b/dts/src/arm/ste-dbx5x0.dtsi
index 82d8c47712..162e1eb537 100644
--- a/dts/src/arm/ste-dbx5x0.dtsi
+++ b/dts/src/arm/ste-dbx5x0.dtsi
@@ -14,6 +14,7 @@
#include <dt-bindings/mfd/dbx500-prcmu.h>
#include <dt-bindings/arm/ux500_pm_domains.h>
#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/clock/ste-ab8500.h>
#include "skeleton.dtsi"
/ {
@@ -603,6 +604,11 @@
interrupt-controller;
#interrupt-cells = <2>;
+ ab8500_clock: clock-controller {
+ compatible = "stericsson,ab8500-clk";
+ #clock-cells = <1>;
+ };
+
ab8500_gpio: ab8500-gpio {
compatible = "stericsson,ab8500-gpio";
gpio-controller;
@@ -686,6 +692,8 @@
ab8500-pwm {
compatible = "stericsson,ab8500-pwm";
+ clocks = <&ab8500_clock AB8500_SYSCLK_INT>;
+ clock-names = "intclk";
};
ab8500-debugfs {
@@ -700,6 +708,9 @@
V-AMIC2-supply = <&ab8500_ldo_anamic2_reg>;
V-DMIC-supply = <&ab8500_ldo_dmic_reg>;
+ clocks = <&ab8500_clock AB8500_SYSCLK_AUDIO>;
+ clock-names = "audioclk";
+
stericsson,earpeice-cmv = <950>; /* Units in mV. */
};
@@ -1095,6 +1106,14 @@
status = "disabled";
};
+ sound {
+ compatible = "stericsson,snd-soc-mop500";
+ stericsson,cpu-dai = <&msp1 &msp3>;
+ stericsson,audio-codec = <&codec>;
+ clocks = <&prcmu_clk PRCMU_SYSCLK>, <&ab8500_clock AB8500_SYSCLK_ULP>, <&ab8500_clock AB8500_SYSCLK_INT>;
+ clock-names = "sysclk", "ulpclk", "intclk";
+ };
+
msp0: msp@80123000 {
compatible = "stericsson,ux500-msp-i2s";
reg = <0x80123000 0x1000>;
diff --git a/dts/src/arm/ste-href.dtsi b/dts/src/arm/ste-href.dtsi
index f37f9e1071..9e359e4f34 100644
--- a/dts/src/arm/ste-href.dtsi
+++ b/dts/src/arm/ste-href.dtsi
@@ -186,15 +186,6 @@
status = "okay";
};
- sound {
- compatible = "stericsson,snd-soc-mop500";
-
- stericsson,cpu-dai = <&msp1 &msp3>;
- stericsson,audio-codec = <&codec>;
- clocks = <&prcmu_clk PRCMU_SYSCLK>;
- clock-names = "sysclk";
- };
-
msp0: msp@80123000 {
pinctrl-names = "default";
pinctrl-0 = <&msp0_default_mode>;
diff --git a/dts/src/arm/ste-snowball.dts b/dts/src/arm/ste-snowball.dts
index dd5514def6..ade1d0d4e5 100644
--- a/dts/src/arm/ste-snowball.dts
+++ b/dts/src/arm/ste-snowball.dts
@@ -159,15 +159,6 @@
"", "", "", "", "", "", "", "";
};
- sound {
- compatible = "stericsson,snd-soc-mop500";
-
- stericsson,cpu-dai = <&msp1 &msp3>;
- stericsson,audio-codec = <&codec>;
- clocks = <&prcmu_clk PRCMU_SYSCLK>;
- clock-names = "sysclk";
- };
-
msp0: msp@80123000 {
pinctrl-names = "default";
pinctrl-0 = <&msp0_default_mode>;
diff --git a/dts/src/arm/sun7i-a20-lamobo-r1.dts b/dts/src/arm/sun7i-a20-lamobo-r1.dts
index 72ec0d5ae0..bbf1c8cbaa 100644
--- a/dts/src/arm/sun7i-a20-lamobo-r1.dts
+++ b/dts/src/arm/sun7i-a20-lamobo-r1.dts
@@ -167,7 +167,7 @@
reg = <8>;
label = "cpu";
ethernet = <&gmac>;
- phy-mode = "rgmii";
+ phy-mode = "rgmii-txid";
fixed-link {
speed = <1000>;
full-duplex;
diff --git a/dts/src/arm/sun8i-a23-a33.dtsi b/dts/src/arm/sun8i-a23-a33.dtsi
index a952cc0703..8a3ed21cb7 100644
--- a/dts/src/arm/sun8i-a23-a33.dtsi
+++ b/dts/src/arm/sun8i-a23-a33.dtsi
@@ -495,7 +495,7 @@
resets = <&ccu RST_BUS_GPU>;
assigned-clocks = <&ccu CLK_GPU>;
- assigned-clock-rates = <408000000>;
+ assigned-clock-rates = <384000000>;
};
gic: interrupt-controller@01c81000 {
diff --git a/dts/src/arm/sun8i-a33.dtsi b/dts/src/arm/sun8i-a33.dtsi
index 18c174fef8..0467fb365b 100644
--- a/dts/src/arm/sun8i-a33.dtsi
+++ b/dts/src/arm/sun8i-a33.dtsi
@@ -113,8 +113,8 @@
simple-audio-card,mclk-fs = <512>;
simple-audio-card,aux-devs = <&codec_analog>;
simple-audio-card,routing =
- "Left DAC", "Digital Left DAC",
- "Right DAC", "Digital Right DAC";
+ "Left DAC", "AIF1 Slot 0 Left",
+ "Right DAC", "AIF1 Slot 0 Right";
status = "disabled";
simple-audio-card,cpu {
diff --git a/dts/src/arm/sun8i-reference-design-tablet.dtsi b/dts/src/arm/sun8i-reference-design-tablet.dtsi
index 7097c18ff4..d6bd15898d 100644
--- a/dts/src/arm/sun8i-reference-design-tablet.dtsi
+++ b/dts/src/arm/sun8i-reference-design-tablet.dtsi
@@ -50,8 +50,6 @@
backlight: backlight {
compatible = "pwm-backlight";
- pinctrl-names = "default";
- pinctrl-0 = <&bl_en_pin>;
pwms = <&pwm 0 50000 PWM_POLARITY_INVERTED>;
brightness-levels = <0 10 20 30 40 50 60 70 80 90 100>;
default-brightness-level = <8>;
@@ -93,11 +91,6 @@
};
&pio {
- bl_en_pin: bl_en_pin@0 {
- pins = "PH6";
- function = "gpio_in";
- };
-
mmc0_cd_pin: mmc0_cd_pin@0 {
pins = "PB4";
function = "gpio_in";
diff --git a/dts/src/arm64/broadcom/ns2.dtsi b/dts/src/arm64/broadcom/ns2.dtsi
index 9f9e203c09..bcb03fc326 100644
--- a/dts/src/arm64/broadcom/ns2.dtsi
+++ b/dts/src/arm64/broadcom/ns2.dtsi
@@ -114,6 +114,7 @@
pcie0: pcie@20020000 {
compatible = "brcm,iproc-pcie";
reg = <0 0x20020000 0 0x1000>;
+ dma-coherent;
#interrupt-cells = <1>;
interrupt-map-mask = <0 0 0 0>;
@@ -144,6 +145,7 @@
pcie4: pcie@50020000 {
compatible = "brcm,iproc-pcie";
reg = <0 0x50020000 0 0x1000>;
+ dma-coherent;
#interrupt-cells = <1>;
interrupt-map-mask = <0 0 0 0>;
@@ -174,6 +176,7 @@
pcie8: pcie@60c00000 {
compatible = "brcm,iproc-pcie-paxc";
reg = <0 0x60c00000 0 0x1000>;
+ dma-coherent;
linux,pci-domain = <8>;
bus-range = <0x0 0x1>;
@@ -203,6 +206,7 @@
<0x61030000 0x100>;
reg-names = "amac_base", "idm_base", "nicpm_base";
interrupts = <GIC_SPI 341 IRQ_TYPE_LEVEL_HIGH>;
+ dma-coherent;
phy-handle = <&gphy0>;
phy-mode = "rgmii";
status = "disabled";
@@ -213,6 +217,7 @@
reg = <0x612c0000 0x445>; /* PDC FS0 regs */
interrupts = <GIC_SPI 187 IRQ_TYPE_LEVEL_HIGH>;
#mbox-cells = <1>;
+ dma-coherent;
brcm,rx-status-len = <32>;
brcm,use-bcm-hdr;
};
@@ -222,6 +227,7 @@
reg = <0x612e0000 0x445>; /* PDC FS1 regs */
interrupts = <GIC_SPI 189 IRQ_TYPE_LEVEL_HIGH>;
#mbox-cells = <1>;
+ dma-coherent;
brcm,rx-status-len = <32>;
brcm,use-bcm-hdr;
};
@@ -231,6 +237,7 @@
reg = <0x61300000 0x445>; /* PDC FS2 regs */
interrupts = <GIC_SPI 191 IRQ_TYPE_LEVEL_HIGH>;
#mbox-cells = <1>;
+ dma-coherent;
brcm,rx-status-len = <32>;
brcm,use-bcm-hdr;
};
@@ -240,6 +247,7 @@
reg = <0x61320000 0x445>; /* PDC FS3 regs */
interrupts = <GIC_SPI 193 IRQ_TYPE_LEVEL_HIGH>;
#mbox-cells = <1>;
+ dma-coherent;
brcm,rx-status-len = <32>;
brcm,use-bcm-hdr;
};
@@ -644,6 +652,7 @@
sata: ahci@663f2000 {
compatible = "brcm,iproc-ahci", "generic-ahci";
reg = <0x663f2000 0x1000>;
+ dma-coherent;
reg-names = "ahci";
interrupts = <GIC_SPI 438 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
@@ -667,6 +676,7 @@
compatible = "brcm,sdhci-iproc-cygnus";
reg = <0x66420000 0x100>;
interrupts = <GIC_SPI 421 IRQ_TYPE_LEVEL_HIGH>;
+ dma-coherent;
bus-width = <8>;
clocks = <&genpll_sw BCM_NS2_GENPLL_SW_SDIO_CLK>;
status = "disabled";
@@ -676,6 +686,7 @@
compatible = "brcm,sdhci-iproc-cygnus";
reg = <0x66430000 0x100>;
interrupts = <GIC_SPI 422 IRQ_TYPE_LEVEL_HIGH>;
+ dma-coherent;
bus-width = <8>;
clocks = <&genpll_sw BCM_NS2_GENPLL_SW_SDIO_CLK>;
status = "disabled";
diff --git a/fs/devfs-core.c b/fs/devfs-core.c
index 75ed3b0e66..382606f1cf 100644
--- a/fs/devfs-core.c
+++ b/fs/devfs-core.c
@@ -37,7 +37,7 @@ int devfs_partition_complete(struct string_list *sl, char *instr)
len = strlen(instr);
list_for_each_entry(cdev, &cdev_list, list) {
- if (cdev->flags & DEVFS_IS_PARTITION &&
+ if (cdev->master &&
!strncmp(instr, cdev->name, len)) {
string_list_add_asprintf(sl, "%s ", cdev->name);
}
@@ -184,76 +184,8 @@ int cdev_flush(struct cdev *cdev)
return cdev->ops->flush(cdev);
}
-static int partition_ioctl(struct cdev *cdev, int request, void *buf)
-{
- int ret = 0;
- loff_t offset, *_buf = buf;
- struct mtd_info_user *user = buf;
-
- switch (request) {
- case MEMSETBADBLOCK:
- case MEMSETGOODBLOCK:
- case MEMGETBADBLOCK:
- offset = *_buf;
- offset += cdev->offset;
- ret = cdev->ops->ioctl(cdev, request, &offset);
- break;
- case MEMGETINFO:
- if (cdev->mtd) {
- user->type = cdev->mtd->type;
- user->flags = cdev->mtd->flags;
- user->size = cdev->mtd->size;
- user->erasesize = cdev->mtd->erasesize;
- user->writesize = cdev->mtd->writesize;
- user->oobsize = cdev->mtd->oobsize;
- user->subpagesize = cdev->mtd->writesize >> cdev->mtd->subpage_sft;
- user->mtd = cdev->mtd;
- /* The below fields are obsolete */
- user->ecctype = -1;
- user->eccsize = 0;
- break;
- }
- if (!cdev->ops->ioctl) {
- ret = -EINVAL;
- break;
- }
- ret = cdev->ops->ioctl(cdev, request, buf);
- break;
-#if (defined(CONFIG_NAND_ECC_HW) || defined(CONFIG_NAND_ECC_SOFT))
- case ECCGETSTATS:
-#endif
- case MEMERASE:
- if (!cdev->ops->ioctl) {
- ret = -EINVAL;
- break;
- }
- ret = cdev->ops->ioctl(cdev, request, buf);
- break;
-#ifdef CONFIG_PARTITION
- case MEMGETREGIONINFO:
- if (cdev->mtd) {
- struct region_info_user *reg = buf;
- int erasesize_shift = ffs(cdev->mtd->erasesize) - 1;
-
- reg->offset = cdev->offset;
- reg->erasesize = cdev->mtd->erasesize;
- reg->numblocks = cdev->size >> erasesize_shift;
- reg->regionindex = cdev->mtd->index;
- }
- break;
-#endif
- default:
- ret = -EINVAL;
- }
-
- return ret;
-}
-
int cdev_ioctl(struct cdev *cdev, int request, void *buf)
{
- if (cdev->flags & DEVFS_IS_PARTITION)
- return partition_ioctl(cdev, request, buf);
-
if (!cdev->ops->ioctl)
return -EINVAL;
@@ -277,6 +209,7 @@ int devfs_create(struct cdev *new)
return -EEXIST;
INIT_LIST_HEAD(&new->links);
+ INIT_LIST_HEAD(&new->partitions);
list_add_tail(&new->list, &cdev_list);
if (new->dev) {
@@ -326,6 +259,9 @@ int devfs_remove(struct cdev *cdev)
list_for_each_entry_safe(c, tmp, &cdev->links, link_entry)
devfs_remove(c);
+ if (cdev->master)
+ list_del(&cdev->partition_entry);
+
if (cdev->link)
free(cdev);
@@ -374,6 +310,8 @@ static struct cdev *__devfs_add_partition(struct cdev *cdev,
partinfo->flags, partinfo->name);
if (IS_ERR(mtd))
return (void *)mtd;
+
+ list_add_tail(&mtd->cdev.partition_entry, &cdev->partitions);
return &mtd->cdev;
}
@@ -388,7 +326,9 @@ static struct cdev *__devfs_add_partition(struct cdev *cdev,
new->offset = cdev->offset + offset;
new->dev = cdev->dev;
- new->flags = partinfo->flags | DEVFS_IS_PARTITION;
+ new->master = cdev;
+
+ list_add_tail(&new->partition_entry, &cdev->partitions);
devfs_create(new);
@@ -428,7 +368,7 @@ int devfs_del_partition(const char *name)
return ret;
}
- if (!(cdev->flags & DEVFS_IS_PARTITION))
+ if (!cdev->master)
return -EINVAL;
if (cdev->flags & DEVFS_PARTITION_FIXED)
return -EPERM;
diff --git a/fs/ext4/ext4_common.c b/fs/ext4/ext4_common.c
index 6c4083eae9..55e837b4bd 100644
--- a/fs/ext4/ext4_common.c
+++ b/fs/ext4/ext4_common.c
@@ -83,19 +83,19 @@ static int ext4fs_blockgroup(struct ext2_data *data, int group,
long int blkno;
unsigned int blkoff, desc_per_blk;
struct ext_filesystem *fs = data->fs;
+ int desc_size = fs->gdsize;
- desc_per_blk = EXT2_BLOCK_SIZE(data) / sizeof(struct ext2_block_group);
+ desc_per_blk = EXT2_BLOCK_SIZE(data) / desc_size;
- blkno = __le32_to_cpu(data->sblock.first_data_block) + 1 +
+ blkno = le32_to_cpu(data->sblock.first_data_block) + 1 +
group / desc_per_blk;
- blkoff = (group % desc_per_blk) * sizeof(struct ext2_block_group);
+ blkoff = (group % desc_per_blk) * desc_size;
dev_dbg(fs->dev, "read %d group descriptor (blkno %ld blkoff %u)\n",
group, blkno, blkoff);
return ext4fs_devread(fs, blkno << LOG2_EXT2_BLOCK_SIZE(data),
- blkoff, sizeof(struct ext2_block_group),
- (char *)blkgrp);
+ blkoff, desc_size, (char *)blkgrp);
}
int ext4fs_read_inode(struct ext2_data *data, int ino, struct ext2_inode *inode)
@@ -109,14 +109,14 @@ int ext4fs_read_inode(struct ext2_data *data, int ino, struct ext2_inode *inode)
/* It is easier to calculate if the first inode is 0. */
ino--;
- ret = ext4fs_blockgroup(data, ino / __le32_to_cpu
+ ret = ext4fs_blockgroup(data, ino / le32_to_cpu
(sblock->inodes_per_group), &blkgrp);
if (ret)
return ret;
inodes_per_block = EXT2_BLOCK_SIZE(data) / fs->inodesz;
- blkno = __le32_to_cpu(blkgrp.inode_table_id) +
- (ino % __le32_to_cpu(sblock->inodes_per_group)) / inodes_per_block;
+ blkno = le32_to_cpu(blkgrp.inode_table_id) +
+ (ino % le32_to_cpu(sblock->inodes_per_group)) / inodes_per_block;
blkoff = (ino % inodes_per_block) * fs->inodesz;
/* Read the inode. */
ret = ext4fs_devread(fs, blkno << LOG2_EXT2_BLOCK_SIZE(data), blkoff,
@@ -212,14 +212,14 @@ long int read_allocated_block(struct ext2fs_node *node, int fileblock)
if (fileblock < INDIRECT_BLOCKS) {
/* Direct blocks. */
- blknr = __le32_to_cpu(inode->b.blocks.dir_blocks[fileblock]);
+ blknr = le32_to_cpu(inode->b.blocks.dir_blocks[fileblock]);
} else if (fileblock < (INDIRECT_BLOCKS + (blksz / 4))) {
/* Indirect. */
ret = ext4fs_get_indir_block(node, &data->indir1,
- __le32_to_cpu(inode->b.blocks.indir_block) << log2_blksz);
+ le32_to_cpu(inode->b.blocks.indir_block) << log2_blksz);
if (ret)
return ret;
- blknr = __le32_to_cpu(data->indir1.data[fileblock - INDIRECT_BLOCKS]);
+ blknr = le32_to_cpu(data->indir1.data[fileblock - INDIRECT_BLOCKS]);
} else if (fileblock < (INDIRECT_BLOCKS + (blksz / 4 *
(blksz / 4 + 1)))) {
/* Double indirect. */
@@ -227,16 +227,16 @@ long int read_allocated_block(struct ext2fs_node *node, int fileblock)
long int rblock = fileblock - (INDIRECT_BLOCKS + blksz / 4);
ret = ext4fs_get_indir_block(node, &data->indir1,
- __le32_to_cpu(inode->b.blocks.double_indir_block) << log2_blksz);
+ le32_to_cpu(inode->b.blocks.double_indir_block) << log2_blksz);
if (ret)
return ret;
ret = ext4fs_get_indir_block(node, &data->indir2,
- __le32_to_cpu(data->indir1.data[rblock / perblock]) << log2_blksz);
+ le32_to_cpu(data->indir1.data[rblock / perblock]) << log2_blksz);
if (ret)
return ret;
- blknr = __le32_to_cpu(data->indir2.data[rblock % perblock]);
+ blknr = le32_to_cpu(data->indir2.data[rblock % perblock]);
} else {
/* Triple indirect. */
rblock = fileblock - (INDIRECT_BLOCKS + blksz / 4 +
@@ -245,21 +245,21 @@ long int read_allocated_block(struct ext2fs_node *node, int fileblock)
perblock_parent = ((blksz / 4) * (blksz / 4));
ret = ext4fs_get_indir_block(node, &data->indir1,
- __le32_to_cpu(inode->b.blocks.triple_indir_block) << log2_blksz);
+ le32_to_cpu(inode->b.blocks.triple_indir_block) << log2_blksz);
if (ret)
return ret;
ret = ext4fs_get_indir_block(node, &data->indir2,
- __le32_to_cpu(data->indir1.data[rblock / perblock_parent]) << log2_blksz);
+ le32_to_cpu(data->indir1.data[rblock / perblock_parent]) << log2_blksz);
if (ret)
return ret;
ret = ext4fs_get_indir_block(node, &data->indir3,
- __le32_to_cpu(data->indir2.data[rblock / perblock_child]) << log2_blksz);
+ le32_to_cpu(data->indir2.data[rblock / perblock_child]) << log2_blksz);
if (ret)
return ret;
- blknr = __le32_to_cpu(data->indir3.data[rblock % perblock_child]);
+ blknr = le32_to_cpu(data->indir3.data[rblock % perblock_child]);
}
return blknr;
@@ -282,7 +282,7 @@ int ext4fs_iterate_dir(struct ext2fs_node *dir, char *name,
return ret;
}
/* Search the file. */
- while (fpos < __le32_to_cpu(diro->inode.size)) {
+ while (fpos < le32_to_cpu(diro->inode.size)) {
struct ext2_dirent dirent;
status = ext4fs_read_file(diro, fpos,
@@ -308,7 +308,7 @@ int ext4fs_iterate_dir(struct ext2fs_node *dir, char *name,
return -ENOMEM;
fdiro->data = diro->data;
- fdiro->ino = __le32_to_cpu(dirent.inode);
+ fdiro->ino = le32_to_cpu(dirent.inode);
filename[dirent.namelen] = '\0';
@@ -323,7 +323,7 @@ int ext4fs_iterate_dir(struct ext2fs_node *dir, char *name,
type = FILETYPE_REG;
} else {
ret = ext4fs_read_inode(diro->data,
- __le32_to_cpu
+ le32_to_cpu
(dirent.inode),
&fdiro->inode);
if (ret) {
@@ -332,15 +332,15 @@ int ext4fs_iterate_dir(struct ext2fs_node *dir, char *name,
}
fdiro->inode_read = 1;
- if ((__le16_to_cpu(fdiro->inode.mode) &
+ if ((le16_to_cpu(fdiro->inode.mode) &
FILETYPE_INO_MASK) ==
FILETYPE_INO_DIRECTORY) {
type = FILETYPE_DIRECTORY;
- } else if ((__le16_to_cpu(fdiro->inode.mode)
+ } else if ((le16_to_cpu(fdiro->inode.mode)
& FILETYPE_INO_MASK) ==
FILETYPE_INO_SYMLINK) {
type = FILETYPE_SYMLINK;
- } else if ((__le16_to_cpu(fdiro->inode.mode)
+ } else if ((le16_to_cpu(fdiro->inode.mode)
& FILETYPE_INO_MASK) ==
FILETYPE_INO_REG) {
type = FILETYPE_REG;
@@ -357,7 +357,7 @@ int ext4fs_iterate_dir(struct ext2fs_node *dir, char *name,
free(fdiro);
}
- fpos += __le16_to_cpu(dirent.direntlen);
+ fpos += le16_to_cpu(dirent.direntlen);
}
return -ENOENT;
}
@@ -373,16 +373,16 @@ char *ext4fs_read_symlink(struct ext2fs_node *node)
if (ret)
return NULL;
}
- symlink = zalloc(__le32_to_cpu(diro->inode.size) + 1);
+ symlink = zalloc(le32_to_cpu(diro->inode.size) + 1);
if (!symlink)
return 0;
- if (__le32_to_cpu(diro->inode.size) < sizeof(diro->inode.b.symlink)) {
+ if (le32_to_cpu(diro->inode.size) < sizeof(diro->inode.b.symlink)) {
strncpy(symlink, diro->inode.b.symlink,
- __le32_to_cpu(diro->inode.size));
+ le32_to_cpu(diro->inode.size));
} else {
status = ext4fs_read_file(diro, 0,
- __le32_to_cpu(diro->inode.size),
+ le32_to_cpu(diro->inode.size),
symlink);
if (status == 0) {
free(symlink);
@@ -390,7 +390,7 @@ char *ext4fs_read_symlink(struct ext2fs_node *node)
}
}
- symlink[__le32_to_cpu(diro->inode.size)] = '\0';
+ symlink[le32_to_cpu(diro->inode.size)] = '\0';
return symlink;
}
@@ -501,18 +501,29 @@ int ext4fs_mount(struct ext_filesystem *fs)
goto fail;
/* Make sure this is an ext2 filesystem. */
- if (__le16_to_cpu(data->sblock.magic) != EXT2_SUPER_MAGIC) {
+ if (le16_to_cpu(data->sblock.magic) != EXT2_SUPER_MAGIC) {
ret = -EINVAL;
goto fail;
}
- if (__le32_to_cpu(data->sblock.revision_level == 0))
+ if (le32_to_cpu(data->sblock.revision_level) == 0) {
fs->inodesz = 128;
- else
- fs->inodesz = __le16_to_cpu(data->sblock.inode_size);
+ fs->gdsize = 32;
+ } else {
+ debug("EXT4 features COMPAT: %08x INCOMPAT: %08x RO_COMPAT: %08x\n",
+ le32_to_cpu(data->sblock.feature_compatibility),
+ le32_to_cpu(data->sblock.feature_incompat),
+ le32_to_cpu(data->sblock.feature_ro_compat));
+
+ fs->inodesz = le16_to_cpu(data->sblock.inode_size);
+ fs->gdsize = le32_to_cpu(data->sblock.feature_incompat) &
+ EXT4_FEATURE_INCOMPAT_64BIT ?
+ le16_to_cpu(data->sblock.descriptor_size) : 32;
+ }
- dev_info(fs->dev, "EXT2 rev %d, inode_size %d\n",
- __le32_to_cpu(data->sblock.revision_level), fs->inodesz);
+ dev_info(fs->dev, "EXT2 rev %d, inode_size %d, descriptor size %d\n",
+ le32_to_cpu(data->sblock.revision_level),
+ fs->inodesz, fs->gdsize);
data->diropen.data = data;
data->diropen.ino = 2;
diff --git a/fs/ext4/ext4fs.c b/fs/ext4/ext4fs.c
index 1b9af80f80..bfc5f27cc3 100644
--- a/fs/ext4/ext4fs.c
+++ b/fs/ext4/ext4fs.c
@@ -54,7 +54,7 @@ int ext4fs_read_file(struct ext2fs_node *node, int pos,
int blockcnt;
int log2blocksize = LOG2_EXT2_BLOCK_SIZE(node->data);
int blocksize = 1 << (log2blocksize + DISK_SECTOR_BITS);
- unsigned int filesize = __le32_to_cpu(node->inode.size);
+ unsigned int filesize = le32_to_cpu(node->inode.size);
int previous_block_number = -1;
int delayed_start = 0;
int delayed_extent = 0;
diff --git a/fs/ext4/ext4fs.h b/fs/ext4/ext4fs.h
index ead212d97a..17a490a943 100644
--- a/fs/ext4/ext4fs.h
+++ b/fs/ext4/ext4fs.h
@@ -28,6 +28,7 @@
#define EXT4_EXT_MAGIC 0xf30a
#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010
#define EXT4_FEATURE_INCOMPAT_EXTENTS 0x0040
+#define EXT4_FEATURE_INCOMPAT_64BIT 0x0080
#define EXT4_INDIRECT_BLOCKS 12
#define EXT4_BG_INODE_UNINIT 0x0001
@@ -81,6 +82,8 @@ struct ext_filesystem {
uint32_t inodesz;
/* Sectors per Block */
uint32_t sect_perblk;
+ /* Group Descriptor size */
+ uint16_t gdsize;
/* Group Descriptor Block Number */
uint32_t gdtable_blkno;
/* Total block groups of partition */
diff --git a/fs/ext4/ext_barebox.c b/fs/ext4/ext_barebox.c
index 5ec7ecd918..e40278a5bd 100644
--- a/fs/ext4/ext_barebox.c
+++ b/fs/ext4/ext_barebox.c
@@ -56,7 +56,7 @@ static int ext_open(struct device_d *dev, FILE *file, const char *filename)
if (ret)
return ret;
- file->size = __le32_to_cpu(inode->inode.size);
+ file->size = le32_to_cpu(inode->inode.size);
file->priv = inode;
return 0;
@@ -129,7 +129,7 @@ static struct dirent *ext_readdir(struct device_d *dev, DIR *dir)
int ret;
char *filename;
- if (ext4_dir->fpos >= __le32_to_cpu(diro->inode.size))
+ if (ext4_dir->fpos >= le32_to_cpu(diro->inode.size))
return NULL;
ret = ext4fs_read_file(diro, ext4_dir->fpos, sizeof(struct ext2_dirent),
@@ -151,7 +151,7 @@ static struct dirent *ext_readdir(struct device_d *dev, DIR *dir)
filename[dirent.namelen] = '\0';
- ext4_dir->fpos += __le16_to_cpu(dirent.direntlen);
+ ext4_dir->fpos += le16_to_cpu(dirent.direntlen);
strcpy(dir->d.d_name, filename);
@@ -186,8 +186,8 @@ static int ext_stat(struct device_d *dev, const char *filename, struct stat *s)
if (ret)
return ret;
- s->st_size = __le32_to_cpu(node->inode.size);
- s->st_mode = __le16_to_cpu(node->inode.mode);
+ s->st_size = le32_to_cpu(node->inode.size);
+ s->st_mode = le16_to_cpu(node->inode.mode);
ext4fs_free_node(node, &fs->data->diropen);
diff --git a/fs/ext4/ext_common.h b/fs/ext4/ext_common.h
index 704d8e66a6..e82b56b86a 100644
--- a/fs/ext4/ext_common.h
+++ b/fs/ext4/ext_common.h
@@ -59,14 +59,12 @@
#define EXT2_BLOCK_SIZE(data) (1 << LOG2_BLOCK_SIZE(data))
/* Log2 size of ext2 block in 512 blocks. */
-#define LOG2_EXT2_BLOCK_SIZE(data) (__le32_to_cpu \
+#define LOG2_EXT2_BLOCK_SIZE(data) (le32_to_cpu \
(data->sblock.log2_block_size) + 1)
/* Log2 size of ext2 block in bytes. */
-#define LOG2_BLOCK_SIZE(data) (__le32_to_cpu \
+#define LOG2_BLOCK_SIZE(data) (le32_to_cpu \
(data->sblock.log2_block_size) + 10)
-#define INODE_SIZE_FILESYSTEM(data) (__le32_to_cpu \
- (data->sblock.inode_size))
#define EXT2_FT_DIR 2
#define SUCCESS 1
@@ -79,92 +77,134 @@
/* The ext2 superblock. */
struct ext2_sblock {
- uint32_t total_inodes;
- uint32_t total_blocks;
- uint32_t reserved_blocks;
- uint32_t free_blocks;
- uint32_t free_inodes;
- uint32_t first_data_block;
- uint32_t log2_block_size;
- uint32_t log2_fragment_size;
- uint32_t blocks_per_group;
- uint32_t fragments_per_group;
- uint32_t inodes_per_group;
- uint32_t mtime;
- uint32_t utime;
- uint16_t mnt_count;
- uint16_t max_mnt_count;
- uint16_t magic;
- uint16_t fs_state;
- uint16_t error_handling;
- uint16_t minor_revision_level;
- uint32_t lastcheck;
- uint32_t checkinterval;
- uint32_t creator_os;
- uint32_t revision_level;
- uint16_t uid_reserved;
- uint16_t gid_reserved;
- uint32_t first_inode;
- uint16_t inode_size;
- uint16_t block_group_number;
- uint32_t feature_compatibility;
- uint32_t feature_incompat;
- uint32_t feature_ro_compat;
- uint32_t unique_id[4];
+ __le32 total_inodes;
+ __le32 total_blocks;
+ __le32 reserved_blocks;
+ __le32 free_blocks;
+ __le32 free_inodes;
+ __le32 first_data_block;
+ __le32 log2_block_size;
+ __le32 log2_fragment_size;
+ __le32 blocks_per_group;
+ __le32 fragments_per_group;
+ __le32 inodes_per_group;
+ __le32 mtime;
+ __le32 utime;
+ __le16 mnt_count;
+ __le16 max_mnt_count;
+ __le16 magic;
+ __le16 fs_state;
+ __le16 error_handling;
+ __le16 minor_revision_level;
+ __le32 lastcheck;
+ __le32 checkinterval;
+ __le32 creator_os;
+ __le32 revision_level;
+ __le16 uid_reserved;
+ __le16 gid_reserved;
+ __le32 first_inode;
+ __le16 inode_size;
+ __le16 block_group_number;
+ __le32 feature_compatibility;
+ __le32 feature_incompat;
+ __le32 feature_ro_compat;
+ __le32 unique_id[4];
char volume_name[16];
char last_mounted_on[64];
- uint32_t compression_info;
+ __le32 compression_info;
+ uint8_t prealloc_blocks;
+ uint8_t prealloc_dir_blocks;
+ __le16 reserved_gdt_blocks;
+ uint8_t journal_uuid[16];
+ __le32 journal_inode;
+ __le32 journal_dev;
+ __le32 last_orphan;
+ __le32 hash_seed[4];
+ uint8_t default_hash_version;
+ uint8_t journal_backup_type;
+ __le16 descriptor_size;
+ __le32 default_mount_options;
+ __le32 first_meta_block_group;
+ __le32 mkfs_time;
+ __le32 journal_blocks[17];
+ __le32 total_blocks_high;
+ __le32 reserved_blocks_high;
+ __le32 free_blocks_high;
+ __le16 min_extra_inode_size;
+ __le16 want_extra_inode_size;
+ __le32 flags;
+ __le16 raid_stride;
+ __le16 mmp_interval;
+ __le64 mmp_block;
+ __le32 raid_stripe_width;
+ uint8_t log2_groups_per_flex;
+ uint8_t checksum_type;
};
struct ext2_block_group {
- __u32 block_id; /* Blocks bitmap block */
- __u32 inode_id; /* Inodes bitmap block */
- __u32 inode_table_id; /* Inodes table block */
- __u16 free_blocks; /* Free blocks count */
- __u16 free_inodes; /* Free inodes count */
- __u16 used_dir_cnt; /* Directories count */
- __u16 bg_flags;
- __u32 bg_reserved[2];
- __u16 bg_itable_unused; /* Unused inodes count */
- __u16 bg_checksum; /* crc16(s_uuid+grouo_num+group_desc)*/
+ __le32 block_id; /* Blocks bitmap block */
+ __le32 inode_id; /* Inodes bitmap block */
+ __le32 inode_table_id; /* Inodes table block */
+ __le16 free_blocks; /* Free blocks count */
+ __le16 free_inodes; /* Free inodes count */
+ __le16 used_dir_cnt; /* Directories count */
+ __le16 bg_flags;
+ __le32 bg_exclude_bitmap;
+ __le16 bg_block_id_csum;
+ __le16 bg_inode_id_csum;
+ __le16 bg_itable_unused; /* Unused inodes count */
+ __le16 bg_checksum; /* crc16(s_uuid+group_num+group_desc)*/
+ /* following fields only exist if descriptor size is 64 */
+ __le32 block_id_high;
+ __le32 inode_id_high;
+ __le32 inode_table_id_high;
+ __le16 free_blocks_high;
+ __le16 free_inodes_high;
+ __le16 used_dir_cnt_high;
+ __le16 bg_itable_unused_high;
+ __le32 bg_exclude_bitmap_high;
+ __le16 bg_block_id_csum_high;
+ __le16 bg_inode_id_csum_high;
+ __le32 bg_reserved;
};
/* The ext2 inode. */
struct ext2_inode {
- uint16_t mode;
- uint16_t uid;
- uint32_t size;
- uint32_t atime;
- uint32_t ctime;
- uint32_t mtime;
- uint32_t dtime;
- uint16_t gid;
- uint16_t nlinks;
- uint32_t blockcnt; /* Blocks of 512 bytes!! */
- uint32_t flags;
- uint32_t osd1;
+ __le16 mode;
+ __le16 uid;
+ __le32 size;
+ __le32 atime;
+ __le32 ctime;
+ __le32 mtime;
+ __le32 dtime;
+ __le16 gid;
+ __le16 nlinks;
+ __le32 blockcnt; /* Blocks of either 512 or block_size bytes */
+ __le32 flags;
+ __le32 osd1;
union {
struct datablocks {
- uint32_t dir_blocks[INDIRECT_BLOCKS];
- uint32_t indir_block;
- uint32_t double_indir_block;
- uint32_t triple_indir_block;
+ __le32 dir_blocks[INDIRECT_BLOCKS];
+ __le32 indir_block;
+ __le32 double_indir_block;
+ __le32 triple_indir_block;
} blocks;
char symlink[60];
+ char inline_data[60];
} b;
- uint32_t version;
- uint32_t acl;
- uint32_t dir_acl;
- uint32_t fragment_addr;
- uint32_t osd2[3];
+ __le32 version;
+ __le32 acl;
+ __le32 size_high; /* previously dir_acl, but never used */
+ __le32 fragment_addr;
+ __le32 osd2[3];
};
/* The header of an ext2 directory entry. */
struct ext2_dirent {
- uint32_t inode;
- uint16_t direntlen;
- uint8_t namelen;
- uint8_t filetype;
+ __le32 inode;
+ __le16 direntlen;
+ __u8 namelen;
+ __u8 filetype;
};
struct ext2fs_node {
diff --git a/images/Makefile b/images/Makefile
index 68876e5de9..adf950aa9c 100644
--- a/images/Makefile
+++ b/images/Makefile
@@ -111,6 +111,7 @@ include $(srctree)/images/Makefile.omap3
include $(srctree)/images/Makefile.rockchip
include $(srctree)/images/Makefile.socfpga
include $(srctree)/images/Makefile.tegra
+include $(srctree)/images/Makefile.at91
targets += $(image-y) pbl.lds barebox.x barebox.z
targets += $(patsubst %,%.pblx,$(pblx-y))
diff --git a/images/Makefile.at91 b/images/Makefile.at91
new file mode 100644
index 0000000000..686d5011ef
--- /dev/null
+++ b/images/Makefile.at91
@@ -0,0 +1,7 @@
+#
+# barebox image generation Makefile for AT91 images
+#
+
+pblx-$(MACH_AT91SAM9X5EK) += start_at91sam9x5ek
+FILE_barebox-at91sam9x5ek.img = start_at91sam9x5ek.pblx
+image-$(MACH_AT91SAM9X5EK) += barebox-at91sam9x5ek.img
diff --git a/include/ata_drive.h b/include/ata_drive.h
index 44073cb1b1..11685eef12 100644
--- a/include/ata_drive.h
+++ b/include/ata_drive.h
@@ -37,8 +37,10 @@
#define ATA_CMD_ID_ATA 0xEC
#define ATA_CMD_READ 0x20
+#define ATA_CMD_PIO_READ_EXT 0x24
#define ATA_CMD_READ_EXT 0x25
#define ATA_CMD_WRITE 0x30
+#define ATA_CMD_PIO_WRITE_EXT 0x34
#define ATA_CMD_WRITE_EXT 0x35
/* drive's status flags */
@@ -140,6 +142,7 @@ struct ata_port {
void *drvdata;
struct block_device blk;
uint16_t *id;
+ int lba48;
int initialized;
int probe;
};
diff --git a/include/boot.h b/include/boot.h
index a855cbe1e5..4f7612ab80 100644
--- a/include/boot.h
+++ b/include/boot.h
@@ -36,6 +36,8 @@ struct bootentry {
int bootentries_add_entry(struct bootentries *entries, struct bootentry *entry);
+int bootentry_register_provider(int (*fn)(struct bootentries *bootentries, const char *name));
+
#define bootentries_for_each_entry(bootentries, entry) \
list_for_each_entry(entry, &bootentries->entries, list)
diff --git a/include/bootchooser.h b/include/bootchooser.h
index c948247722..7822c01459 100644
--- a/include/bootchooser.h
+++ b/include/bootchooser.h
@@ -13,14 +13,14 @@ int bootchooser_put(struct bootchooser *bootchooser);
void bootchooser_info(struct bootchooser *bootchooser);
+int bootchooser_boot(struct bootchooser *bc);
+
struct bootchooser_target *bootchooser_get_last_chosen(struct bootchooser *bootchooser);
const char *bootchooser_target_name(struct bootchooser_target *target);
struct bootchooser_target *bootchooser_target_by_name(struct bootchooser *bootchooser,
const char *name);
void bootchooser_target_force_boot(struct bootchooser_target *target);
-int bootchooser_create_bootentry(struct bootentries *entries);
-
int bootchooser_target_set_attempts(struct bootchooser_target *target, int attempts);
int bootchooser_target_set_priority(struct bootchooser_target *target, int priority);
diff --git a/include/common.h b/include/common.h
index 680a0affb6..dd7445e9b6 100644
--- a/include/common.h
+++ b/include/common.h
@@ -66,9 +66,6 @@ int readline (const char *prompt, char *buf, int len);
/* common/memsize.c */
long get_ram_size (volatile long *, long);
-/* $(CPU)/cpu.c */
-void __noreturn poweroff(void);
-
/* common/console.c */
int ctrlc (void);
diff --git a/include/crypto/keystore.h b/include/crypto/keystore.h
index 29915854b8..89d962628b 100644
--- a/include/crypto/keystore.h
+++ b/include/crypto/keystore.h
@@ -12,6 +12,7 @@
#ifdef CONFIG_CRYPTO_KEYSTORE
int keystore_get_secret(const char *name, const u8 **secret, int *secret_len);
int keystore_set_secret(const char *name, const u8 *secret, int secret_len);
+void keystore_forget_secret(const char *name);
#else
static inline int keystore_get_secret(const char *name, const u8 **secret, int *secret_len)
{
@@ -21,6 +22,9 @@ static inline int keystore_set_secret(const char *name, const u8 *secret, int se
{
return 0;
}
+static inline void keystore_forget_secret(const char *name)
+{
+}
#endif
#endif
diff --git a/include/driver.h b/include/driver.h
index 6abaaad8b4..52e06f7d62 100644
--- a/include/driver.h
+++ b/include/driver.h
@@ -459,6 +459,8 @@ struct cdev {
u8 dos_partition_type;
struct cdev *link;
struct list_head link_entry, links;
+ struct list_head partition_entry, partitions;
+ struct cdev *master;
};
int devfs_create(struct cdev *);
@@ -481,8 +483,8 @@ int cdev_erase(struct cdev *cdev, loff_t count, loff_t offset);
#define DEVFS_PARTITION_FIXED (1U << 0)
#define DEVFS_PARTITION_READONLY (1U << 1)
-#define DEVFS_IS_PARTITION (1 << 2)
#define DEVFS_IS_CHARACTER_DEV (1 << 3)
+#define DEVFS_PARTITION_FROM_TABLE (1 << 4)
struct cdev *devfs_add_partition(const char *devname, loff_t offset,
loff_t size, unsigned int flags, const char *name);
diff --git a/include/globalvar.h b/include/globalvar.h
index 80bc53e680..df43f1fe66 100644
--- a/include/globalvar.h
+++ b/include/globalvar.h
@@ -11,10 +11,6 @@ extern struct device_d global_device;
#ifdef CONFIG_GLOBALVAR
int globalvar_add_simple(const char *name, const char *value);
-int globalvar_add(const char *name,
- int (*set)(struct device_d *dev, struct param_d *p, const char *val),
- const char *(*get)(struct device_d *, struct param_d *p),
- unsigned long flags);
void globalvar_remove(const char *name);
char *globalvar_get_match(const char *match, const char *separator);
void globalvar_set_match(const char *match, const char *val);
@@ -80,14 +76,6 @@ static inline int globalvar_add_simple_ip(const char *name,
return 0;
}
-static inline int globalvar_add(const char *name,
- int (*set)(struct device_d *dev, struct param_d *p, const char *val),
- const char *(*get)(struct device_d *, struct param_d *p),
- unsigned long flags)
-{
- return 0;
-}
-
static inline void globalvar_remove(const char *name) {}
static inline void globalvar_print(void) {}
diff --git a/include/gui/image_renderer.h b/include/gui/image_renderer.h
index e0b1eae258..bfdea1b14e 100644
--- a/include/gui/image_renderer.h
+++ b/include/gui/image_renderer.h
@@ -32,7 +32,7 @@ struct image_renderer {
#ifdef CONFIG_IMAGE_RENDERER
int image_renderer_register(struct image_renderer *ir);
-void image_render_unregister(struct image_renderer *ir);
+void image_renderer_unregister(struct image_renderer *ir);
int image_renderer_image(struct screen *sc, struct surface *s, struct image *img);
diff --git a/include/hab.h b/include/hab.h
index 818d7ca1c5..fb7149ef53 100644
--- a/include/hab.h
+++ b/include/hab.h
@@ -41,4 +41,25 @@ static inline int imx25_hab_get_status(void)
}
#endif
+#define SRK_HASH_SIZE 32
+
+/* Force writing of key, even when a key is already written */
+#define IMX_SRK_HASH_FORCE (1 << 0)
+/* Permanently write fuses, without this flag only the shadow registers
+ * are written.
+ */
+#define IMX_SRK_HASH_WRITE_PERMANENT (1 << 1)
+/* When writing the super root key hash, also burn the write protection
+ * fuses so that the key hash can not be modified.
+ */
+#define IMX_SRK_HASH_WRITE_LOCK (1 << 2)
+
+bool imx_hab_srk_hash_valid(const void *buf);
+int imx_hab_write_srk_hash(const void *buf, unsigned flags);
+int imx_hab_write_srk_hash_hex(const char *srkhash, unsigned flags);
+int imx_hab_write_srk_hash_file(const char *filename, unsigned flags);
+int imx_hab_read_srk_hash(void *buf);
+int imx_hab_lockdown_device(unsigned flags);
+int imx_hab_device_locked_down(void);
+
#endif /* __HABV4_H */
diff --git a/include/led.h b/include/led.h
index 000267cdc5..0ce857129d 100644
--- a/include/led.h
+++ b/include/led.h
@@ -12,6 +12,13 @@ struct led {
char *name;
int num;
struct list_head list;
+
+ int blink;
+ int flash;
+ unsigned int *blink_states;
+ int blink_nr_states;
+ int blink_next_state;
+ uint64_t blink_next_event;
};
struct led *led_by_number(int no);
@@ -25,6 +32,10 @@ static inline int led_get_number(struct led *led)
int led_set_num(int num, unsigned int value);
int led_set(struct led *led, unsigned int value);
+int led_blink(struct led *led, unsigned int on_ms, unsigned int off_ms);
+int led_blink_pattern(struct led *led, const unsigned int *pattern,
+ unsigned int pattern_len);
+int led_flash(struct led *led, unsigned int duration_ms);
int led_register(struct led *led);
void led_unregister(struct led *led);
void led_unregister(struct led *led);
@@ -38,6 +49,7 @@ enum led_trigger {
LED_TRIGGER_NET_TXRX,
LED_TRIGGER_DEFAULT_ON,
LED_TRIGGER_MAX,
+ LED_TRIGGER_INVALID = LED_TRIGGER_MAX,
};
enum trigger_type {
@@ -48,6 +60,7 @@ enum trigger_type {
#ifdef CONFIG_LED_TRIGGERS
int led_set_trigger(enum led_trigger trigger, struct led *led);
+void led_trigger_disable(struct led *led);
void led_trigger(enum led_trigger trigger, enum trigger_type);
#else
static inline int led_set_trigger(enum led_trigger trigger, struct led *led)
@@ -60,7 +73,9 @@ static inline void led_trigger(enum led_trigger trigger, enum trigger_type type)
}
#endif
-int led_get_trigger(enum led_trigger trigger);
+void led_triggers_show_info(void);
+const char *trigger_name(enum led_trigger trigger);
+enum led_trigger trigger_by_name(const char *name);
void led_of_parse_trigger(struct led *led, struct device_node *np);
diff --git a/include/linux/clk.h b/include/linux/clk.h
index a061398555..081a859729 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -324,16 +324,13 @@ struct clk *clk_register_composite(const char *name,
struct device_node;
struct of_phandle_args;
+#if defined(CONFIG_COMMON_CLK_OF_PROVIDER)
+
#define CLK_OF_DECLARE(name, compat, fn) \
const struct of_device_id __clk_of_table_##name \
__attribute__ ((unused,section (".__clk_of_table"))) \
= { .compatible = compat, .data = fn }
-#if defined(CONFIG_COMMON_CLK_OF_PROVIDER)
-int of_clk_add_provider(struct device_node *np,
- struct clk *(*clk_src_get)(struct of_phandle_args *args,
- void *data),
- void *data);
void of_clk_del_provider(struct device_node *np);
typedef int (*of_clk_init_cb_t)(struct device_node *);
@@ -349,11 +346,27 @@ struct clk *of_clk_get(struct device_node *np, int index);
struct clk *of_clk_get_by_name(struct device_node *np, const char *name);
struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec);
unsigned int of_clk_get_parent_count(struct device_node *np);
-char *of_clk_get_parent_name(struct device_node *np, unsigned int index);
int of_clk_parent_fill(struct device_node *np, const char **parents,
unsigned int size);
int of_clk_init(struct device_node *root, const struct of_device_id *matches);
#else
+
+
+/*
+ * Create a dummy variable to avoid 'unused function'
+ * warnings. Compiler should be smart enough to throw it out.
+ */
+#define CLK_OF_DECLARE(name, compat, fn) \
+static const struct of_device_id __clk_of_table_##name \
+__attribute__ ((unused)) = { .data = fn }
+
+
+static inline struct clk *
+of_clk_src_simple_get(struct of_phandle_args *clkspec, void *data)
+{
+ return ERR_PTR(-ENOENT);
+}
+
static inline struct clk *of_clk_get(struct device_node *np, int index)
{
return ERR_PTR(-ENOENT);
@@ -374,4 +387,10 @@ struct string_list;
int clk_name_complete(struct string_list *sl, char *instr);
+int of_clk_add_provider(struct device_node *np,
+ struct clk *(*clk_src_get)(struct of_phandle_args *args,
+ void *data),
+ void *data);
+char *of_clk_get_parent_name(struct device_node *np, unsigned int index);
+
#endif
diff --git a/include/linux/clk/at91_pmc.h b/include/linux/clk/at91_pmc.h
new file mode 100644
index 0000000000..17f413bbbe
--- /dev/null
+++ b/include/linux/clk/at91_pmc.h
@@ -0,0 +1,188 @@
+/*
+ * include/linux/clk/at91_pmc.h
+ *
+ * Copyright (C) 2005 Ivan Kokshaysky
+ * Copyright (C) SAN People
+ *
+ * Power Management Controller (PMC) - System peripherals registers.
+ * Based on AT91RM9200 datasheet revision E.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef AT91_PMC_H
+#define AT91_PMC_H
+
+#define AT91_PMC_SCER 0x00 /* System Clock Enable Register */
+#define AT91_PMC_SCDR 0x04 /* System Clock Disable Register */
+
+#define AT91_PMC_SCSR 0x08 /* System Clock Status Register */
+#define AT91_PMC_PCK (1 << 0) /* Processor Clock */
+#define AT91RM9200_PMC_UDP (1 << 1) /* USB Devcice Port Clock [AT91RM9200 only] */
+#define AT91RM9200_PMC_MCKUDP (1 << 2) /* USB Device Port Master Clock Automatic Disable on Suspend [AT91RM9200 only] */
+#define AT91RM9200_PMC_UHP (1 << 4) /* USB Host Port Clock [AT91RM9200 only] */
+#define AT91SAM926x_PMC_UHP (1 << 6) /* USB Host Port Clock [AT91SAM926x only] */
+#define AT91SAM926x_PMC_UDP (1 << 7) /* USB Devcice Port Clock [AT91SAM926x only] */
+#define AT91_PMC_PCK0 (1 << 8) /* Programmable Clock 0 */
+#define AT91_PMC_PCK1 (1 << 9) /* Programmable Clock 1 */
+#define AT91_PMC_PCK2 (1 << 10) /* Programmable Clock 2 */
+#define AT91_PMC_PCK3 (1 << 11) /* Programmable Clock 3 */
+#define AT91_PMC_PCK4 (1 << 12) /* Programmable Clock 4 [AT572D940HF only] */
+#define AT91_PMC_HCK0 (1 << 16) /* AHB Clock (USB host) [AT91SAM9261 only] */
+#define AT91_PMC_HCK1 (1 << 17) /* AHB Clock (LCD) [AT91SAM9261 only] */
+
+#define AT91_PMC_PCER 0x10 /* Peripheral Clock Enable Register */
+#define AT91_PMC_PCDR 0x14 /* Peripheral Clock Disable Register */
+#define AT91_PMC_PCSR 0x18 /* Peripheral Clock Status Register */
+
+#define AT91_CKGR_UCKR 0x1C /* UTMI Clock Register [some SAM9] */
+#define AT91_PMC_UPLLEN (1 << 16) /* UTMI PLL Enable */
+#define AT91_PMC_UPLLCOUNT (0xf << 20) /* UTMI PLL Start-up Time */
+#define AT91_PMC_BIASEN (1 << 24) /* UTMI BIAS Enable */
+#define AT91_PMC_BIASCOUNT (0xf << 28) /* UTMI BIAS Start-up Time */
+
+#define AT91_CKGR_MOR 0x20 /* Main Oscillator Register [not on SAM9RL] */
+#define AT91_PMC_MOSCEN (1 << 0) /* Main Oscillator Enable */
+#define AT91_PMC_OSCBYPASS (1 << 1) /* Oscillator Bypass */
+#define AT91_PMC_MOSCRCEN (1 << 3) /* Main On-Chip RC Oscillator Enable [some SAM9] */
+#define AT91_PMC_OSCOUNT (0xff << 8) /* Main Oscillator Start-up Time */
+#define AT91_PMC_KEY (0x37 << 16) /* MOR Writing Key */
+#define AT91_PMC_MOSCSEL (1 << 24) /* Main Oscillator Selection [some SAM9] */
+#define AT91_PMC_CFDEN (1 << 25) /* Clock Failure Detector Enable [some SAM9] */
+
+#define AT91_CKGR_MCFR 0x24 /* Main Clock Frequency Register */
+#define AT91_PMC_MAINF (0xffff << 0) /* Main Clock Frequency */
+#define AT91_PMC_MAINRDY (1 << 16) /* Main Clock Ready */
+
+#define AT91_CKGR_PLLAR 0x28 /* PLL A Register */
+#define AT91_CKGR_PLLBR 0x2c /* PLL B Register */
+#define AT91_PMC_DIV (0xff << 0) /* Divider */
+#define AT91_PMC_PLLCOUNT (0x3f << 8) /* PLL Counter */
+#define AT91_PMC_OUT (3 << 14) /* PLL Clock Frequency Range */
+#define AT91_PMC_MUL (0x7ff << 16) /* PLL Multiplier */
+#define AT91_PMC_MUL_GET(n) ((n) >> 16 & 0x7ff)
+#define AT91_PMC3_MUL (0x7f << 18) /* PLL Multiplier [SAMA5 only] */
+#define AT91_PMC3_MUL_GET(n) ((n) >> 18 & 0x7f)
+#define AT91_PMC_USBDIV (3 << 28) /* USB Divisor (PLLB only) */
+#define AT91_PMC_USBDIV_1 (0 << 28)
+#define AT91_PMC_USBDIV_2 (1 << 28)
+#define AT91_PMC_USBDIV_4 (2 << 28)
+#define AT91_PMC_USB96M (1 << 28) /* Divider by 2 Enable (PLLB only) */
+
+#define AT91_PMC_MCKR 0x30 /* Master Clock Register */
+#define AT91_PMC_CSS (3 << 0) /* Master Clock Selection */
+#define AT91_PMC_CSS_SLOW (0 << 0)
+#define AT91_PMC_CSS_MAIN (1 << 0)
+#define AT91_PMC_CSS_PLLA (2 << 0)
+#define AT91_PMC_CSS_PLLB (3 << 0)
+#define AT91_PMC_CSS_UPLL (3 << 0) /* [some SAM9 only] */
+#define PMC_PRES_OFFSET 2
+#define AT91_PMC_PRES (7 << PMC_PRES_OFFSET) /* Master Clock Prescaler */
+#define AT91_PMC_PRES_1 (0 << PMC_PRES_OFFSET)
+#define AT91_PMC_PRES_2 (1 << PMC_PRES_OFFSET)
+#define AT91_PMC_PRES_4 (2 << PMC_PRES_OFFSET)
+#define AT91_PMC_PRES_8 (3 << PMC_PRES_OFFSET)
+#define AT91_PMC_PRES_16 (4 << PMC_PRES_OFFSET)
+#define AT91_PMC_PRES_32 (5 << PMC_PRES_OFFSET)
+#define AT91_PMC_PRES_64 (6 << PMC_PRES_OFFSET)
+#define PMC_ALT_PRES_OFFSET 4
+#define AT91_PMC_ALT_PRES (7 << PMC_ALT_PRES_OFFSET) /* Master Clock Prescaler [alternate location] */
+#define AT91_PMC_ALT_PRES_1 (0 << PMC_ALT_PRES_OFFSET)
+#define AT91_PMC_ALT_PRES_2 (1 << PMC_ALT_PRES_OFFSET)
+#define AT91_PMC_ALT_PRES_4 (2 << PMC_ALT_PRES_OFFSET)
+#define AT91_PMC_ALT_PRES_8 (3 << PMC_ALT_PRES_OFFSET)
+#define AT91_PMC_ALT_PRES_16 (4 << PMC_ALT_PRES_OFFSET)
+#define AT91_PMC_ALT_PRES_32 (5 << PMC_ALT_PRES_OFFSET)
+#define AT91_PMC_ALT_PRES_64 (6 << PMC_ALT_PRES_OFFSET)
+#define AT91_PMC_MDIV (3 << 8) /* Master Clock Division */
+#define AT91RM9200_PMC_MDIV_1 (0 << 8) /* [AT91RM9200 only] */
+#define AT91RM9200_PMC_MDIV_2 (1 << 8)
+#define AT91RM9200_PMC_MDIV_3 (2 << 8)
+#define AT91RM9200_PMC_MDIV_4 (3 << 8)
+#define AT91SAM9_PMC_MDIV_1 (0 << 8) /* [SAM9 only] */
+#define AT91SAM9_PMC_MDIV_2 (1 << 8)
+#define AT91SAM9_PMC_MDIV_4 (2 << 8)
+#define AT91SAM9_PMC_MDIV_6 (3 << 8) /* [some SAM9 only] */
+#define AT91SAM9_PMC_MDIV_3 (3 << 8) /* [some SAM9 only] */
+#define AT91_PMC_PDIV (1 << 12) /* Processor Clock Division [some SAM9 only] */
+#define AT91_PMC_PDIV_1 (0 << 12)
+#define AT91_PMC_PDIV_2 (1 << 12)
+#define AT91_PMC_PLLADIV2 (1 << 12) /* PLLA divisor by 2 [some SAM9 only] */
+#define AT91_PMC_PLLADIV2_OFF (0 << 12)
+#define AT91_PMC_PLLADIV2_ON (1 << 12)
+#define AT91_PMC_H32MXDIV BIT(24)
+
+#define AT91_PMC_USB 0x38 /* USB Clock Register [some SAM9 only] */
+#define AT91_PMC_USBS (0x1 << 0) /* USB OHCI Input clock selection */
+#define AT91_PMC_USBS_PLLA (0 << 0)
+#define AT91_PMC_USBS_UPLL (1 << 0)
+#define AT91_PMC_USBS_PLLB (1 << 0) /* [AT91SAMN12 only] */
+#define AT91_PMC_OHCIUSBDIV (0xF << 8) /* Divider for USB OHCI Clock */
+#define AT91_PMC_OHCIUSBDIV_1 (0x0 << 8)
+#define AT91_PMC_OHCIUSBDIV_2 (0x1 << 8)
+
+#define AT91_PMC_SMD 0x3c /* Soft Modem Clock Register [some SAM9 only] */
+#define AT91_PMC_SMDS (0x1 << 0) /* SMD input clock selection */
+#define AT91_PMC_SMD_DIV (0x1f << 8) /* SMD input clock divider */
+#define AT91_PMC_SMDDIV(n) (((n) << 8) & AT91_PMC_SMD_DIV)
+
+#define AT91_PMC_PCKR(n) (0x40 + ((n) * 4)) /* Programmable Clock 0-N Registers */
+#define AT91_PMC_ALT_PCKR_CSS (0x7 << 0) /* Programmable Clock Source Selection [alternate length] */
+#define AT91_PMC_CSS_MASTER (4 << 0) /* [some SAM9 only] */
+#define AT91_PMC_CSSMCK (0x1 << 8) /* CSS or Master Clock Selection */
+#define AT91_PMC_CSSMCK_CSS (0 << 8)
+#define AT91_PMC_CSSMCK_MCK (1 << 8)
+
+#define AT91_PMC_IER 0x60 /* Interrupt Enable Register */
+#define AT91_PMC_IDR 0x64 /* Interrupt Disable Register */
+#define AT91_PMC_SR 0x68 /* Status Register */
+#define AT91_PMC_MOSCS (1 << 0) /* MOSCS Flag */
+#define AT91_PMC_LOCKA (1 << 1) /* PLLA Lock */
+#define AT91_PMC_LOCKB (1 << 2) /* PLLB Lock */
+#define AT91_PMC_MCKRDY (1 << 3) /* Master Clock */
+#define AT91_PMC_LOCKU (1 << 6) /* UPLL Lock [some SAM9] */
+#define AT91_PMC_OSCSEL (1 << 7) /* Slow Oscillator Selection [some SAM9] */
+#define AT91_PMC_PCK0RDY (1 << 8) /* Programmable Clock 0 */
+#define AT91_PMC_PCK1RDY (1 << 9) /* Programmable Clock 1 */
+#define AT91_PMC_PCK2RDY (1 << 10) /* Programmable Clock 2 */
+#define AT91_PMC_PCK3RDY (1 << 11) /* Programmable Clock 3 */
+#define AT91_PMC_MOSCSELS (1 << 16) /* Main Oscillator Selection [some SAM9] */
+#define AT91_PMC_MOSCRCS (1 << 17) /* Main On-Chip RC [some SAM9] */
+#define AT91_PMC_CFDEV (1 << 18) /* Clock Failure Detector Event [some SAM9] */
+#define AT91_PMC_GCKRDY (1 << 24) /* Generated Clocks */
+#define AT91_PMC_IMR 0x6c /* Interrupt Mask Register */
+
+#define AT91_PMC_PLLICPR 0x80 /* PLL Charge Pump Current Register */
+
+#define AT91_PMC_PROT 0xe4 /* Write Protect Mode Register [some SAM9] */
+#define AT91_PMC_WPEN (0x1 << 0) /* Write Protect Enable */
+#define AT91_PMC_WPKEY (0xffffff << 8) /* Write Protect Key */
+#define AT91_PMC_PROTKEY (0x504d43 << 8) /* Activation Code */
+
+#define AT91_PMC_WPSR 0xe8 /* Write Protect Status Register [some SAM9] */
+#define AT91_PMC_WPVS (0x1 << 0) /* Write Protect Violation Status */
+#define AT91_PMC_WPVSRC (0xffff << 8) /* Write Protect Violation Source */
+
+#define AT91_PMC_PCER1 0x100 /* Peripheral Clock Enable Register 1 [SAMA5 only]*/
+#define AT91_PMC_PCDR1 0x104 /* Peripheral Clock Enable Register 1 */
+#define AT91_PMC_PCSR1 0x108 /* Peripheral Clock Enable Register 1 */
+
+#define AT91_PMC_PCR 0x10c /* Peripheral Control Register [some SAM9 and SAMA5] */
+#define AT91_PMC_PCR_PID_MASK 0x3f
+#define AT91_PMC_PCR_GCKCSS_OFFSET 8
+#define AT91_PMC_PCR_GCKCSS_MASK (0x7 << AT91_PMC_PCR_GCKCSS_OFFSET)
+#define AT91_PMC_PCR_GCKCSS(n) ((n) << AT91_PMC_PCR_GCKCSS_OFFSET) /* GCK Clock Source Selection */
+#define AT91_PMC_PCR_CMD (0x1 << 12) /* Command (read=0, write=1) */
+#define AT91_PMC_PCR_DIV_OFFSET 16
+#define AT91_PMC_PCR_DIV_MASK (0x3 << AT91_PMC_PCR_DIV_OFFSET)
+#define AT91_PMC_PCR_DIV(n) ((n) << AT91_PMC_PCR_DIV_OFFSET) /* Divisor Value */
+#define AT91_PMC_PCR_GCKDIV_OFFSET 20
+#define AT91_PMC_PCR_GCKDIV_MASK (0xff << AT91_PMC_PCR_GCKDIV_OFFSET)
+#define AT91_PMC_PCR_GCKDIV(n) ((n) << AT91_PMC_PCR_GCKDIV_OFFSET) /* Generated Clock Divisor Value */
+#define AT91_PMC_PCR_EN (0x1 << 28) /* Enable */
+#define AT91_PMC_PCR_GCKEN (0x1 << 29) /* GCK Enable */
+
+#endif
diff --git a/include/linux/hw_random.h b/include/linux/hw_random.h
new file mode 100644
index 0000000000..bae442166c
--- /dev/null
+++ b/include/linux/hw_random.h
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+#ifndef LINUX_HWRANDOM_H_
+#define LINUX_HWRANDOM_H_
+
+#include <linux/list.h>
+
+/**
+ * struct hwrng - Hardware Random Number Generator driver
+ * @name: Unique RNG name.
+ * @init: Initialization callback (can be NULL).
+ * @read: New API. drivers can fill up to max bytes of data
+ * into the buffer. The buffer is aligned for any type.
+ */
+struct hwrng {
+ const char *name;
+ int (*init)(struct hwrng *rng);
+ int (*read)(struct hwrng *rng, void *data, size_t max, bool wait);
+
+ struct list_head list;
+
+ struct cdev cdev;
+ struct device_d *dev;
+ void *buf;
+};
+
+/* Register a new Hardware Random Number Generator driver. */
+int hwrng_register(struct device_d *dev, struct hwrng *rng);
+int hwrng_get_data(struct hwrng *rng, void *buffer, size_t size, int wait);
+
+#ifdef CONFIG_HWRNG
+struct hwrng *hwrng_get_first(void);
+#else
+static inline struct hwrng *hwrng_get_first(void) { return ERR_PTR(-ENODEV); };
+#endif
+
+#endif /* LINUX_HWRANDOM_H_ */
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index f93fac00f2..fa35c7ef39 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -228,6 +228,7 @@ struct mtd_info {
struct list_head partitions_entry;
char *of_path;
+ unsigned int of_binding;
};
int mtd_erase(struct mtd_info *mtd, struct erase_info *instr);
diff --git a/include/linux/nvmem-consumer.h b/include/linux/nvmem-consumer.h
new file mode 100644
index 0000000000..606cadb498
--- /dev/null
+++ b/include/linux/nvmem-consumer.h
@@ -0,0 +1,94 @@
+/*
+ * nvmem framework consumer.
+ *
+ * Copyright (C) 2015 Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+ * Copyright (C) 2013 Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef _LINUX_NVMEM_CONSUMER_H
+#define _LINUX_NVMEM_CONSUMER_H
+
+struct device_d;
+struct device_node;
+/* consumer cookie */
+struct nvmem_cell;
+struct nvmem_device;
+
+struct nvmem_cell_info {
+ const char *name;
+ unsigned int offset;
+ unsigned int bytes;
+ unsigned int bit_offset;
+ unsigned int nbits;
+};
+
+#if IS_ENABLED(CONFIG_NVMEM)
+
+/* Cell based interface */
+struct nvmem_cell *nvmem_cell_get(struct device_d *dev, const char *name);
+void nvmem_cell_put(struct nvmem_cell *cell);
+void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len);
+int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len);
+
+/* direct nvmem device read/write interface */
+struct nvmem_device *nvmem_device_get(struct device_d *dev, const char *name);
+void nvmem_device_put(struct nvmem_device *nvmem);
+
+#else
+
+static inline struct nvmem_cell *nvmem_cell_get(struct device_d *dev,
+ const char *name)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+static inline void nvmem_cell_put(struct nvmem_cell *cell)
+{
+}
+
+static inline char *nvmem_cell_read(struct nvmem_cell *cell, size_t *len)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+static inline int nvmem_cell_write(struct nvmem_cell *cell,
+ const char *buf, size_t len)
+{
+ return -ENOSYS;
+}
+
+static inline struct nvmem_device *nvmem_device_get(struct device_d *dev,
+ const char *name)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+static inline void nvmem_device_put(struct nvmem_device *nvmem)
+{
+}
+#endif /* CONFIG_NVMEM */
+
+#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OFTREE)
+struct nvmem_cell *of_nvmem_cell_get(struct device_node *np,
+ const char *name);
+struct nvmem_device *of_nvmem_device_get(struct device_node *np,
+ const char *name);
+#else
+static inline struct nvmem_cell *of_nvmem_cell_get(struct device_node *np,
+ const char *name)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+static inline struct nvmem_device *of_nvmem_device_get(struct device_node *np,
+ const char *name)
+{
+ return ERR_PTR(-ENOSYS);
+}
+#endif /* CONFIG_NVMEM && CONFIG_OF */
+
+#endif /* ifndef _LINUX_NVMEM_CONSUMER_H */
diff --git a/include/linux/nvmem-provider.h b/include/linux/nvmem-provider.h
new file mode 100644
index 0000000000..6ef5ea6854
--- /dev/null
+++ b/include/linux/nvmem-provider.h
@@ -0,0 +1,49 @@
+/*
+ * nvmem framework provider.
+ *
+ * Copyright (C) 2015 Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
+ * Copyright (C) 2013 Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef _LINUX_NVMEM_PROVIDER_H
+#define _LINUX_NVMEM_PROVIDER_H
+
+#include <common.h>
+#include <linux/types.h>
+
+struct nvmem_device;
+
+struct nvmem_bus {
+ int (*write)(struct device_d *dev, const int reg, const void *val,
+ int val_size);
+ int (*read)(struct device_d *dev, const int reg, void *val,
+ int val_size);
+};
+
+struct nvmem_config {
+ struct device_d *dev;
+ const char *name;
+ bool read_only;
+ int stride;
+ int word_size;
+ int size;
+ const struct nvmem_bus *bus;
+};
+
+#if IS_ENABLED(CONFIG_NVMEM)
+
+struct nvmem_device *nvmem_register(const struct nvmem_config *cfg);
+
+#else
+
+static inline struct nvmem_device *nvmem_register(const struct nvmem_config *c)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+#endif /* CONFIG_NVMEM */
+#endif /* ifndef _LINUX_NVMEM_PROVIDER_H */
diff --git a/include/mci.h b/include/mci.h
index cc4712cfad..781e6e0f36 100644
--- a/include/mci.h
+++ b/include/mci.h
@@ -480,6 +480,7 @@ struct mci {
int mci_register(struct mci_host*);
void mci_of_parse(struct mci_host *host);
+void mci_of_parse_node(struct mci_host *host, struct device_node *np);
int mci_detect_card(struct mci_host *);
int mci_send_ext_csd(struct mci *mci, char *ext_csd);
int mci_switch(struct mci *mci, unsigned set, unsigned index,
diff --git a/include/of.h b/include/of.h
index e3bb452b86..0ba73f197f 100644
--- a/include/of.h
+++ b/include/of.h
@@ -205,6 +205,8 @@ extern int of_property_write_u32_array(struct device_node *np,
extern int of_property_write_u64_array(struct device_node *np,
const char *propname, const u64 *values,
size_t sz);
+extern int of_property_write_string(struct device_node *np, const char *propname,
+ const char *value);
extern struct device_node *of_parse_phandle(const struct device_node *np,
const char *phandle_name,
@@ -238,6 +240,7 @@ extern struct device_d *of_device_enable_and_register_by_name(const char *name);
struct cdev *of_parse_partition(struct cdev *cdev, struct device_node *node);
int of_parse_partitions(struct cdev *cdev, struct device_node *node);
+int of_partitions_register_fixup(struct cdev *cdev);
int of_device_is_stdout_path(struct device_d *dev);
const char *of_get_model(void);
void *of_flatten_dtb(struct device_node *node);
@@ -248,6 +251,7 @@ struct device_d *of_find_device_by_node_path(const char *path);
#define OF_FIND_PATH_FLAGS_BB 1 /* return .bb device if available */
int of_find_path(struct device_node *node, const char *propname, char **outpath, unsigned flags);
int of_find_path_by_node(struct device_node *node, char **outpath, unsigned flags);
+struct device_node *of_find_node_by_devpath(struct device_node *root, const char *path);
int of_register_fixup(int (*fixup)(struct device_node *, void *), void *context);
int of_unregister_fixup(int (*fixup)(struct device_node *, void *), void *context);
int of_register_set_status_fixup(const char *node, bool status);
@@ -262,6 +266,11 @@ static inline int of_parse_partitions(struct cdev *cdev,
return -EINVAL;
}
+static inline int of_partitions_register_fixup(struct cdev *cdev)
+{
+ return -ENOSYS;
+}
+
static inline int of_device_is_stdout_path(struct device_d *dev)
{
return 0;
@@ -466,6 +475,12 @@ static inline int of_property_write_u64_array(struct device_node *np,
return -ENOSYS;
}
+static inline int of_property_write_string(struct device_node *np, const char *propname,
+ const char *value)
+{
+ return -ENOSYS;
+}
+
static inline struct device_node *of_parse_phandle(const struct device_node *np,
const char *phandle_name, int index)
{
diff --git a/include/param.h b/include/param.h
index f5f82ee70c..f9f3398cae 100644
--- a/include/param.h
+++ b/include/param.h
@@ -6,7 +6,6 @@
#include <linux/list.h>
#define PARAM_FLAG_RO (1 << 0)
-#define PARAM_GLOBALVAR_UNQUALIFIED (1 << 1)
struct device_d;
typedef uint32_t IPaddr_t;
diff --git a/include/poweroff.h b/include/poweroff.h
new file mode 100644
index 0000000000..ae9557db5d
--- /dev/null
+++ b/include/poweroff.h
@@ -0,0 +1,21 @@
+#ifndef __INCLUDE_POWEROFF_H
+#define __INCLUDE_POWEROFF_H
+
+void __noreturn poweroff_machine(void);
+
+struct poweroff_handler {
+ void (*poweroff)(struct poweroff_handler *);
+ int priority;
+ const char *name;
+ struct list_head list;
+};
+
+int poweroff_handler_register(struct poweroff_handler *);
+int poweroff_handler_register_fn(void (*poweroff_fn)(struct poweroff_handler *));
+
+#define POWEROFF_DEFAULT_PRIORITY 100
+#define POWEROFF_DEFAULT_NAME "default"
+
+unsigned int of_get_poweroff_priority(struct device_node *node);
+
+#endif /* __INCLUDE_POWEROFF_H */
diff --git a/include/state.h b/include/state.h
index bc9a574093..63164f92e5 100644
--- a/include/state.h
+++ b/include/state.h
@@ -18,6 +18,7 @@ struct state *state_by_name(const char *name);
struct state *state_by_node(const struct device_node *node);
int state_get_name(const struct state *state, char const **name);
+int state_load_no_auth(struct state *state);
int state_load(struct state *state);
int state_save(struct state *state);
void state_info(void);
diff --git a/include/stdlib.h b/include/stdlib.h
index f3185069f3..ee3f229968 100644
--- a/include/stdlib.h
+++ b/include/stdlib.h
@@ -13,6 +13,7 @@ void srand(unsigned int seed);
/* fill a buffer with pseudo-random data */
void get_random_bytes(void *buf, int len);
+int get_crypto_bytes(void *buf, int len);
static inline u32 random32(void)
{
diff --git a/lib/Kconfig b/lib/Kconfig
index 8a94ce09fb..9562b1b8c2 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -90,6 +90,15 @@ config RATP
transferring packets over serial links described in RFC916. This implementation
is used for controlling barebox over serial ports.
+config ALLOW_PRNG_FALLBACK
+ bool "Allow fallback to PRNG if HWRNG not available."
+ help
+ WARNING: it is not secure!!
+
+ get_crypto_bytes() users like cmd_password relay on HWRNG. If HWRNG is not
+ available and this option is disabled, cmd_password will fail.
+ Enable it on your own risk.
+
source lib/gui/Kconfig
source lib/fonts/Kconfig
diff --git a/lib/gui/bmp.c b/lib/gui/bmp.c
index 143aa28055..8bffc70a42 100644
--- a/lib/gui/bmp.c
+++ b/lib/gui/bmp.c
@@ -9,7 +9,7 @@
#include <gui/image_renderer.h>
#include <asm/unaligned.h>
-struct image *bmp_open(char *inbuf, int insize)
+static struct image *bmp_open(char *inbuf, int insize)
{
struct image *img = calloc(1, sizeof(struct image));
struct bmp_image *bmp = (struct bmp_image*)inbuf;
@@ -30,7 +30,7 @@ struct image *bmp_open(char *inbuf, int insize)
return img;
}
-void bmp_close(struct image *img)
+static void bmp_close(struct image *img)
{
free(img->data);
}
diff --git a/lib/random.c b/lib/random.c
index 210fea9946..759271f0c8 100644
--- a/lib/random.c
+++ b/lib/random.c
@@ -1,5 +1,6 @@
#include <common.h>
#include <stdlib.h>
+#include <linux/hw_random.h>
static unsigned int random_seed;
@@ -18,6 +19,11 @@ void srand(unsigned int seed)
random_seed = seed;
}
+/**
+ * get_random_bytes - get pseudo random numbers.
+ * This interface can be good enough to generate MAC address
+ * or use for NAND test.
+ */
void get_random_bytes(void *_buf, int len)
{
char *buf = _buf;
@@ -25,3 +31,49 @@ void get_random_bytes(void *_buf, int len)
while (len--)
*buf++ = rand() % 256;
}
+
+/**
+ * get_crypto_bytes - get random numbers suitable for cryptographic needs.
+ */
+static int _get_crypto_bytes(void *buf, int len)
+{
+ struct hwrng *rng;
+
+ rng = hwrng_get_first();
+ if (IS_ERR(rng))
+ return PTR_ERR(rng);
+
+ while (len) {
+ int bytes = hwrng_get_data(rng, buf, len, true);
+ if (!bytes)
+ return -ENOMEDIUM;
+
+ if (bytes < 0)
+ return bytes;
+
+ len -= bytes;
+ buf = buf + bytes;
+ }
+
+ return 0;
+}
+
+int get_crypto_bytes(void *buf, int len)
+{
+ int err;
+
+ err = _get_crypto_bytes(buf, len);
+ if (!err)
+ return 0;
+
+ if (!IS_ENABLED(CONFIG_ALLOW_PRNG_FALLBACK)) {
+ pr_err("error: no HWRNG available!\n");
+ return err;
+ }
+
+ pr_warn("warning: falling back to Pseudo RNG source!\n");
+
+ get_random_bytes(buf, len);
+
+ return 0;
+}
diff --git a/scripts/tags.sh b/scripts/tags.sh
index 79fdafb0d2..8ae44642a2 100755
--- a/scripts/tags.sh
+++ b/scripts/tags.sh
@@ -103,11 +103,6 @@ all_kconfigs()
find_other_sources 'Kconfig*'
}
-all_defconfigs()
-{
- find_sources $ALLSOURCE_ARCHS "defconfig"
-}
-
docscope()
{
(echo \-k; echo \-q; all_sources) > cscope.files
@@ -153,22 +148,14 @@ exuberant()
--regex-c++='/CLEARPAGEFLAG_NOOP\(([^,)]*).*/ClearPage\1/' \
--regex-c++='/__CLEARPAGEFLAG_NOOP\(([^,)]*).*/__ClearPage\1/' \
--regex-c++='/TESTCLEARFLAG_FALSE\(([^,)]*).*/TestClearPage\1/' \
- --regex-c++='/__TESTCLEARFLAG_FALSE\(([^,)]*).*/__TestClearPage\1/' \
--regex-c++='/_PE\(([^,)]*).*/PEVENT_ERRNO__\1/' \
--regex-c='/PCI_OP_READ\(([a-z]*[a-z]).*[1-4]\)/pci_bus_read_config_\1/' \
--regex-c='/PCI_OP_WRITE\(([a-z]*[a-z]).*[1-4]\)/pci_bus_write_config_\1/'
all_kconfigs | xargs $1 -a \
--langdef=kconfig --language-force=kconfig \
- --regex-kconfig='/^[[:blank:]]*(menu|)config[[:blank:]]+([[:alnum:]_]+)/\2/'
-
- all_kconfigs | xargs $1 -a \
- --langdef=kconfig --language-force=kconfig \
+ --regex-kconfig='/^[[:blank:]]*(menu|)config[[:blank:]]+([[:alnum:]_]+)/\2/' \
--regex-kconfig='/^[[:blank:]]*(menu|)config[[:blank:]]+([[:alnum:]_]+)/CONFIG_\2/'
-
- all_defconfigs | xargs -r $1 -a \
- --langdef=dotconfig --language-force=dotconfig \
- --regex-dotconfig='/^#?[[:blank:]]*(CONFIG_[[:alnum:]_]+)/\1/'
}
emacs()
@@ -198,19 +185,13 @@ emacs()
--regex='/CLEARPAGEFLAG_NOOP\(([^,)]*).*/ClearPage\1/' \
--regex='/__CLEARPAGEFLAG_NOOP\(([^,)]*).*/__ClearPage\1/' \
--regex='/TESTCLEARFLAG_FALSE\(([^,)]*).*/TestClearPage\1/' \
- --regex='/__TESTCLEARFLAG_FALSE\(([^,)]*).*/__TestClearPage\1/' \
--regex='/_PE\(([^,)]*).*/PEVENT_ERRNO__\1/' \
--regex='/PCI_OP_READ\(([a-z]*[a-z]).*[1-4]\)/pci_bus_read_config_\1/' \
--regex='/PCI_OP_WRITE\(([a-z]*[a-z]).*[1-4]\)/pci_bus_write_config_\1/'
all_kconfigs | xargs $1 -a \
- --regex='/^[ \t]*\(\(menu\)*config\)[ \t]+\([a-zA-Z0-9_]+\)/\3/'
-
- all_kconfigs | xargs $1 -a \
+ --regex='/^[ \t]*\(\(menu\)*config\)[ \t]+\([a-zA-Z0-9_]+\)/\3/' \
--regex='/^[ \t]*\(\(menu\)*config\)[ \t]+\([a-zA-Z0-9_]+\)/CONFIG_\3/'
-
- all_defconfigs | xargs -r $1 -a \
- --regex='/^#?[ \t]?\(CONFIG_[a-zA-Z0-9_]+\)/\1/'
}
xtags()