summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--arch/arm/Kconfig2
-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/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/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--commands/Kconfig11
-rw-r--r--commands/Makefile2
-rw-r--r--commands/hab.c120
-rw-r--r--commands/led.c44
-rw-r--r--commands/seed.c44
-rw-r--r--commands/stddev.c29
-rw-r--r--commands/trigger.c54
-rw-r--r--common/globalvar.c12
-rw-r--r--common/password.c6
-rw-r--r--drivers/Kconfig1
-rw-r--r--drivers/Makefile1
-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/clocksource/timer-atmel-pit.c9
-rw-r--r--drivers/crypto/caam/Kconfig1
-rw-r--r--drivers/crypto/caam/caamrng.c44
-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.c13
-rw-r--r--drivers/net/macb.c56
-rw-r--r--drivers/serial/atmel.c7
-rw-r--r--drivers/spi/atmel_spi.c29
-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/imx-ipu-v3/ipufb.c6
-rw-r--r--drivers/w1/masters/w1-gpio.c53
-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/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/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/hab.h21
-rw-r--r--include/led.h17
-rw-r--r--include/linux/clk/at91_pmc.h188
-rw-r--r--include/linux/hw_random.h47
-rw-r--r--include/mci.h1
-rw-r--r--include/stdlib.h1
-rw-r--r--lib/Kconfig9
-rw-r--r--lib/random.c52
119 files changed, 6473 insertions, 981 deletions
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..4f936afd73 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
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/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/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/commands/Kconfig b/commands/Kconfig
index bc0885c69d..35b7f43001 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -1930,6 +1930,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
@@ -2115,6 +2120,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..edd713c6bd 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -87,6 +87,7 @@ 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
@@ -120,3 +121,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/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/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/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/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/common/globalvar.c b/common/globalvar.c
index 52808f8852..ab573cc68d 100644
--- a/common/globalvar.c
+++ b/common/globalvar.c
@@ -233,10 +233,16 @@ static int __nvvar_add(const char *name, const char *value)
if (ret && ret != -EEXIST)
return ret;
- if (!value)
- value = dev_get_param(&global_device, name);
+ if (value)
+ return nv_set(&nv_device, p, value);
+
+ 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)
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/drivers/Kconfig b/drivers/Kconfig
index bf31115fa0..44159f83d3 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -22,6 +22,7 @@ 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"
diff --git a/drivers/Makefile b/drivers/Makefile
index 0fadf4e44b..d46cc28869 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -22,6 +22,7 @@ obj-y += misc/
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/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/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/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..928277a924 100644
--- a/drivers/mci/mci-core.c
+++ b/drivers/mci/mci-core.c
@@ -1843,20 +1843,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 +1895,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/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/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/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/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/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/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/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/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/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/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/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/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/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;
+}