diff options
213 files changed, 9380 insertions, 2238 deletions
diff --git a/Documentation/devicetree/bindings/barebox/barebox,state.rst b/Documentation/devicetree/bindings/barebox/barebox,state.rst index 438cc434a2..00fb592614 100644 --- a/Documentation/devicetree/bindings/barebox/barebox,state.rst +++ b/Documentation/devicetree/bindings/barebox/barebox,state.rst @@ -29,7 +29,8 @@ Required properties: * ``compatible``: should be ``barebox,state``; * ``magic``: A 32bit number used as a magic to identify the state -* ``backend``: describes where the data for this state is stored +* ``backend``: contains a phandle to the device/partition which holds the + actual state data. * ``backend-type``: should be ``raw`` or ``dtb``. Optional properties: @@ -39,9 +40,9 @@ Optional properties: e.g. ``hmac(sha256)``. Only used for ``raw``. * ``backend-stridesize``: Maximum size per copy of the data. Only important for non-MTD devices -* ``backend-storage-type``: Type of the storage. This has two options at the - moment. For MTD with erasing the correct type is ``circular``. For all other - devices and files, ``direct`` is the needed type. +* ``backend-storage-type``: Normally the correct storage type is detected auto- + matically. The circular backend supports the option ``noncircular`` to fall + back to an old storage format. Variable nodes -------------- @@ -77,19 +78,31 @@ Example:: magic = <0x27031977>; compatible = "barebox,state"; backend-type = "raw"; - backend = &eeprom, "partname:state"; + backend = &state_part; foo { - reg = <0x00 0x4>; - type = "uint32"; + reg = <0x00 0x4>; + type = "uint32"; default = <0x0>; }; bar { - reg = <0x10 0x4>; - type = "enum32"; + reg = <0x10 0x4>; + type = "enum32"; names = "baz", "qux"; - default = <1>; + default = <1>; + }; + }; + + &nand_flash { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + state_part: state@10000 { + label = "state"; + reg = <0x10000 0x10000>; + }; }; }; diff --git a/Documentation/user/state.rst b/Documentation/user/state.rst index 5dd5c486e2..73c4be8159 100644 --- a/Documentation/user/state.rst +++ b/Documentation/user/state.rst @@ -23,16 +23,35 @@ available, ``raw`` and ``dtb``. Both format the state data differently. Basically these are serializers. The raw serializer additionally supports a HMAC algorithm to detect manipulations. +The data is always stored in a logical unit called ``bucket``. A ``bucket`` has +its own size depending on some external contraints. These contraints are listed +in more detail below depending on the used memory type and storage backend. A +``bucket`` stores exactly one state. A default number of three buckets is used +to store data redundantely. + +Redundancy +---------- + +The state framework is safe against powerfailures during write operations. To +archieve that multiple buckets are stored to disk. When writing all buckets are +written in order. When reading, the buckets are read in order and the first +one found that passes CRC tests is used. When all data is read the buckets +containing invalid or outdated data are written with the data just read. Also +NAND blocks need cleanup due to excessive bitflips are rewritten in this step. +With this it is made sure that after successful initialization of a state the +data on the storage device is consistent and redundant. + Storage Backends ---------------- -The serialized data can be stored to different backends which are automatically -selected depending on the defined backend in the devicetree. Currently two -implementations exist, ``circular`` and ``direct``. ``circular`` writes the -data sequentially on the backend storage device. Each save is appended until -the storage area is full. It then erases the block and starts from offset 0. -``circular`` is used for MTD devices with erase functionality. ``direct`` -writes the data directly to the file without erasing. +The serialized data can be stored to different backends. Currently two +implementations exist, ``circular`` and ``direct``. The state framework automatically +selects the correct backend depending on the storage medium. Media requiring +erase operations (NAND, NOR flash) use the ``circular`` backend, others use the ``direct`` +backend. The purpose of the ``circular`` backend is to save erase cycles which may +wear out the flash blocks. It continuously fills eraseblocks with updated data +and only when an eraseblock if fully written erases it and starts over writing +new data to the same eraseblock again. For all backends multiple copies are written to handle read errors. @@ -1,5 +1,5 @@ VERSION = 2017 -PATCHLEVEL = 03 +PATCHLEVEL = 04 SUBLEVEL = 0 EXTRAVERSION = NAME = None diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 4d952698fc..2583e9dd1b 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -43,9 +43,9 @@ config ARCH_AT91 select GPIOLIB select CLKDEV_LOOKUP select HAS_DEBUG_LL - select HAVE_MACH_ARM_HEAD select HAVE_CLK select PINCTRL_AT91 + select COMMON_CLK_AT91 if COMMON_CLK_OF_PROVIDER config ARCH_BCM283X @@ -93,7 +93,6 @@ config ARCH_EP93XX config ARCH_HIGHBANK bool "Calxeda Highbank" select HAS_DEBUG_LL - select HAS_POWEROFF select ARCH_HAS_L2X0 select CPU_V7 select ARM_AMBA @@ -117,7 +116,6 @@ config ARCH_MVEBU select CLKDEV_LOOKUP select GPIOLIB select HAS_DEBUG_LL - select HAVE_DEFAULT_ENVIRONMENT_NEW select HAVE_MACH_ARM_HEAD select HAVE_PBL_MULTI_IMAGES select HW_HAS_PCI @@ -154,7 +152,6 @@ config ARCH_OMAP config ARCH_PXA bool "Intel/Marvell PXA based" select GENERIC_GPIO - select HAS_POWEROFF config ARCH_ROCKCHIP bool "Rockchip RX3xxx" diff --git a/arch/arm/boards/at91sam9x5ek/Makefile b/arch/arm/boards/at91sam9x5ek/Makefile index 559df8f7b9..4939b7e17e 100644 --- a/arch/arm/boards/at91sam9x5ek/Makefile +++ b/arch/arm/boards/at91sam9x5ek/Makefile @@ -1,3 +1,4 @@ obj-y += init.o obj-y += hw_version.o bbenv-$(CONFIG_DEFAULT_ENVIRONMENT_GENERIC) += defaultenv-at91sam9x5ek +lwl-y += lowlevel.o diff --git a/arch/arm/boards/at91sam9x5ek/hw_version.c b/arch/arm/boards/at91sam9x5ek/hw_version.c index 2f84d82d5d..d1ca036fe9 100644 --- a/arch/arm/boards/at91sam9x5ek/hw_version.c +++ b/arch/arm/boards/at91sam9x5ek/hw_version.c @@ -15,6 +15,7 @@ */ #include <common.h> +#include <init.h> #include <fs.h> #include <fcntl.h> #include <libbb.h> @@ -250,7 +251,7 @@ static int cm_cogent_fixup(struct device_node *root, void *unused) return 0; } -void at91sam9x5ek_devices_detect_hw(void) +static int at91sam9x5ek_devices_detect_hw(void) { at91sam9x5ek_devices_detect_one("/dev/ds24310"); at91sam9x5ek_devices_detect_one("/dev/ds24311"); @@ -262,4 +263,7 @@ void at91sam9x5ek_devices_detect_hw(void) if (at91sam9x5ek_cm_is_vendor(VENDOR_COGENT)) of_register_fixup(cm_cogent_fixup, NULL); + + return 0; } +late_initcall(at91sam9x5ek_devices_detect_hw); diff --git a/arch/arm/boards/at91sam9x5ek/hw_version.h b/arch/arm/boards/at91sam9x5ek/hw_version.h index 91fd42942d..3f3c8003d9 100644 --- a/arch/arm/boards/at91sam9x5ek/hw_version.h +++ b/arch/arm/boards/at91sam9x5ek/hw_version.h @@ -29,6 +29,5 @@ enum vendor_id { bool at91sam9x5ek_cm_is_vendor(enum vendor_id vid); bool at91sam9x5ek_ek_is_vendor(enum vendor_id vid); bool at91sam9x5ek_dm_is_vendor(enum vendor_id vid); -void at91sam9x5ek_devices_detect_hw(void); #endif /* __HW_REVISION_H__ */ diff --git a/arch/arm/boards/at91sam9x5ek/init.c b/arch/arm/boards/at91sam9x5ek/init.c index 9fe117c2d8..646cff5d60 100644 --- a/arch/arm/boards/at91sam9x5ek/init.c +++ b/arch/arm/boards/at91sam9x5ek/init.c @@ -45,28 +45,6 @@ #include "hw_version.h" -struct w1_gpio_platform_data w1_pdata = { - .pin = AT91_PIN_PB18, - .ext_pullup_enable_pin = -EINVAL, - .is_open_drain = 0, -}; - -static struct atmel_nand_data nand_pdata = { - .ale = 21, - .cle = 22, - .det_pin = -EINVAL, - .rdy_pin = AT91_PIN_PD5, - .enable_pin = AT91_PIN_PD4, - .has_pmecc = 1, - .ecc_mode = NAND_ECC_HW, - .pmecc_sector_size = 512, - .pmecc_corr_cap = 2, -#if defined(CONFIG_MTD_NAND_ATMEL_BUSWIDTH_16) - .bus_width_16 = 1, -#endif - .on_flash_bbt = 1, -}; - static struct sam9_smc_config cm_nand_smc_config = { .ncs_read_setup = 0, .nrd_setup = 1, @@ -85,10 +63,27 @@ static struct sam9_smc_config cm_nand_smc_config = { .tdf_cycles = 1, }; -static void ek_add_device_nand(void) +static int ek_add_device_smc(void) { + unsigned long csa; + csa = at91_sys_read(AT91_MATRIX_EBICSA); + + /* Enable CS3 */ + csa |= AT91_MATRIX_EBI_CS3A_SMC_NANDFLASH; + /* NAND flash on D16 */ + csa |= AT91_MATRIX_NFD0_ON_D16; + + /* Configure IO drive */ + csa &= ~AT91_MATRIX_EBI_EBI_IOSR_NORMAL; + at91_sys_write(AT91_MATRIX_EBICSA, csa); + + add_generic_device("at91sam9-smc", + DEVICE_ID_SINGLE, NULL, + AT91SAM9X5_BASE_SMC, 0x200, + IORESOURCE_MEM, NULL); + /* setup bus-width (8 or 16) */ - if (nand_pdata.bus_width_16) + if (IS_ENABLED(CONFIG_MTD_NAND_ATMEL_BUSWIDTH_16)) cm_nand_smc_config.mode |= AT91_SMC_DBW_16; else cm_nand_smc_config.mode |= AT91_SMC_DBW_8; @@ -97,28 +92,26 @@ static void ek_add_device_nand(void) sam9_smc_configure(0, 3, &cm_nand_smc_config); if (at91sam9x5ek_cm_is_vendor(VENDOR_COGENT)) { - unsigned long csa; - csa = at91_sys_read(AT91_MATRIX_EBICSA); csa |= AT91_MATRIX_EBI_VDDIOMSEL_1_8V; at91_sys_write(AT91_MATRIX_EBICSA, csa); } - at91_add_device_nand(&nand_pdata); + return 0; } +fs_initcall(ek_add_device_smc); -static struct macb_platform_data macb_pdata = { - .phy_interface = PHY_INTERFACE_MODE_RMII, - .phy_addr = 0, -}; - -static void ek_add_device_eth(void) +static int ek_register_mac_address(void) { - if (w1_local_mac_address_register(0, "tml", "w1-2d-0")) - w1_local_mac_address_register(0, "tml", "w1-23-0"); + int ret; - at91_add_device_eth(0, &macb_pdata); + ret = w1_local_mac_address_register(0, "tml", "w1-2d-0"); + if (!ret) + return ret; + + return w1_local_mac_address_register(0, "tml", "w1-23-0"); } +late_initcall(ek_register_mac_address); #if defined(CONFIG_DRIVER_VIDEO_ATMEL_HLCD) /* @@ -163,164 +156,12 @@ static void ek_add_device_lcdc(void) static void ek_add_device_lcdc(void) {} #endif -/* - * MCI (SD/MMC) - */ -/* mci0 detect_pin is revision dependent */ -static struct atmel_mci_platform_data mci0_data = { - .bus_width = 4, - .detect_pin = AT91_PIN_PD15, - .wp_pin = -EINVAL, -}; - -static void ek_add_device_mci(void) -{ - if (at91sam9x5ek_cm_is_vendor(VENDOR_COGENT)) - mci0_data.detect_pin = -EINVAL; - - /* MMC0 */ - at91_add_device_mci(0, &mci0_data); -} - -struct qt1070_platform_data qt1070_pdata = { - .irq_pin = AT91_PIN_PA7, -}; - -static struct i2c_board_info i2c_devices[] = { - { - .platform_data = &qt1070_pdata, - I2C_BOARD_INFO("qt1070", 0x1b), - }, { - I2C_BOARD_INFO("24c512", 0x51) - }, -}; - -static void ek_add_device_i2c(void) -{ - at91_set_gpio_input(qt1070_pdata.irq_pin, 0); - at91_set_deglitch(qt1070_pdata.irq_pin, 1); - at91_add_device_i2c(0, i2c_devices, ARRAY_SIZE(i2c_devices)); -} - -static const struct spi_board_info ek_cm_cogent_spi_devices[] = { - { - .name = "mtd_dataflash", - .chip_select = 0, - .max_speed_hz = 15 * 1000 * 1000, - .bus_num = 0, - } -}; - -static const struct spi_board_info ek_spi_devices[] = { - { - .name = "m25p80", - .chip_select = 0, - .max_speed_hz = 30 * 1000 * 1000, - .bus_num = 0, - } -}; - -static unsigned spi0_standard_cs[] = { AT91_PIN_PA14}; -static struct at91_spi_platform_data spi_pdata = { - .chipselect = spi0_standard_cs, - .num_chipselect = ARRAY_SIZE(spi0_standard_cs), -}; - -static void ek_add_device_spi(void) -{ - if (at91sam9x5ek_cm_is_vendor(VENDOR_COGENT)) - spi_register_board_info(ek_cm_cogent_spi_devices, - ARRAY_SIZE(ek_cm_cogent_spi_devices)); - else - spi_register_board_info(ek_spi_devices, - ARRAY_SIZE(ek_spi_devices)); - at91_add_device_spi(0, &spi_pdata); -} - -#if defined(CONFIG_USB_OHCI) || defined(CONFIG_USB_EHCI) -/* - * USB HS Host port (common to OHCI & EHCI) - */ -static struct at91_usbh_data ek_usbh_hs_data = { - .ports = 2, - .vbus_pin = {AT91_PIN_PD19, AT91_PIN_PD20}, -}; - -static void ek_add_device_usb(void) -{ - at91_add_device_usbh_ohci(&ek_usbh_hs_data); - at91_add_device_usbh_ehci(&ek_usbh_hs_data); -} -#else -static void ek_add_device_usb(void) {} -#endif - -struct gpio_led leds[] = { - { - .gpio = AT91_PIN_PB18, - .active_low = 1, - .led = { - .name = "d1", - }, - }, { - .gpio = AT91_PIN_PD21, - .led = { - .name = "d2", - }, - }, -}; - -static void __init ek_add_led(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(leds); i++) { - at91_set_gpio_output(leds[i].gpio, leds[i].active_low); - led_gpio_register(&leds[i]); - } - led_set_trigger(LED_TRIGGER_HEARTBEAT, &leds[1].led); -} - -static int at91sam9x5ek_mem_init(void) -{ - at91_add_device_sdram(0); - - return 0; -} -mem_initcall(at91sam9x5ek_mem_init); - -static void ek_add_device_w1(void) -{ - at91_set_gpio_input(w1_pdata.pin, 0); - at91_set_multi_drive(w1_pdata.pin, 1); - add_generic_device_res("w1-gpio", DEVICE_ID_SINGLE, NULL, 0, &w1_pdata); - - at91sam9x5ek_devices_detect_hw(); -} - static int at91sam9x5ek_devices_init(void) { - ek_add_device_w1(); - ek_add_device_nand(); - ek_add_device_eth(); - ek_add_device_spi(); - ek_add_device_mci(); - ek_add_device_usb(); - ek_add_led(); - ek_add_device_i2c(); ek_add_device_lcdc(); armlinux_set_architecture(CONFIG_MACH_AT91SAM9X5EK); - devfs_add_partition("nand0", 0x00000, SZ_256K, DEVFS_PARTITION_FIXED, "at91bootstrap_raw"); - dev_add_bb_dev("at91bootstrap_raw", "at91bootstrap"); - devfs_add_partition("nand0", SZ_256K, SZ_256K + SZ_128K, DEVFS_PARTITION_FIXED, "self_raw"); - dev_add_bb_dev("self_raw", "self0"); - devfs_add_partition("nand0", SZ_512K + SZ_128K, SZ_128K, DEVFS_PARTITION_FIXED, "env_raw"); - dev_add_bb_dev("env_raw", "env0"); - devfs_add_partition("nand0", SZ_512K + SZ_256K, SZ_128K, DEVFS_PARTITION_FIXED, "env_raw1"); - dev_add_bb_dev("env_raw1", "env1"); - if (IS_ENABLED(CONFIG_DEFAULT_ENVIRONMENT_GENERIC)) defaultenv_append_directory(defaultenv_at91sam9x5ek); @@ -333,15 +174,6 @@ static int at91sam9x5ek_console_init(void) barebox_set_model("Atmel at91sam9x5-ek"); barebox_set_hostname("at91sam9x5-ek"); - at91_register_uart(0, 0); - at91_register_uart(1, 0); return 0; } console_initcall(at91sam9x5ek_console_init); - -static int at91sam9x5ek_main_clock(void) -{ - at91_set_main_clock(12000000); - return 0; -} -pure_initcall(at91sam9x5ek_main_clock); diff --git a/arch/arm/boards/at91sam9x5ek/lowlevel.c b/arch/arm/boards/at91sam9x5ek/lowlevel.c new file mode 100644 index 0000000000..acf80d7955 --- /dev/null +++ b/arch/arm/boards/at91sam9x5ek/lowlevel.c @@ -0,0 +1,21 @@ +#include <common.h> +#include <linux/sizes.h> +#include <mach/at91sam9_ddrsdr.h> +#include <asm/barebox-arm-head.h> +#include <asm/barebox-arm.h> +#include <io.h> +#include <debug_ll.h> + +extern char __dtb_at91sam9x5ek_start[]; + +ENTRY_FUNCTION(start_at91sam9x5ek, r0, r1, r2) +{ + void *fdt; + + arm_cpu_lowlevel_init(); + arm_setup_stack(AT91SAM9X5_SRAM_BASE + AT91SAM9X5_SRAM_SIZE - 16); + + fdt = __dtb_at91sam9x5ek_start - get_runtime_offset(); + + barebox_arm_entry(AT91_CHIPSELECT_1, at91sam9x5_get_ddram_size(), fdt); +} diff --git a/arch/arm/boards/guf-santaro/board.c b/arch/arm/boards/guf-santaro/board.c index b9a52ee258..e54110886b 100644 --- a/arch/arm/boards/guf-santaro/board.c +++ b/arch/arm/boards/guf-santaro/board.c @@ -11,6 +11,7 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +#define pr_fmt(fmt) "Santaro: " fmt #include <common.h> #include <init.h> @@ -25,6 +26,105 @@ #include <bbu.h> #include <mach/bbu.h> #include <mach/imx6.h> +#include <i2c/i2c.h> +#include <gpio.h> + +static int i2c_device_present(struct i2c_adapter *adapter, int addr) +{ + struct i2c_client client = {}; + u8 reg; + + client.adapter = adapter; + client.addr = addr; + + return i2c_write_reg(&client, 0x00, ®, 0) < 0 ? false : true; +} + +#define TOUCH_RESET_GPIO IMX_GPIO_NR(1, 20) + +static int edt_present, egalax_present; + +static int santaro_touch_fixup(struct device_node *root, void *unused) +{ + struct device_node *i2cnp, *np; + + i2cnp = of_find_node_by_alias(root, "i2c2"); + if (!i2cnp) { + pr_err("Cannot find node i2c2\n"); + return -EINVAL; + } + + for_each_child_of_node(i2cnp, np) { + int present; + + if (of_device_is_compatible(np, "edt,edt-ft5206")) + present = edt_present; + else if (of_device_is_compatible(np, "eeti,egalax_ts")) + present = egalax_present; + else + continue; + + if (present) + of_device_enable(np); + else + of_device_disable(np); + } + + return 0; +} + +static int santaro_detect_touch(void) +{ + struct device_node *np; + struct i2c_adapter *adapter; + const char *model = NULL; + + if (!of_machine_is_compatible("guf,imx6q-santaro")) + return 0; + + /* + * The Santaro has two different possible Touchscreen + * controllers. Both are on different I2C addresses. + * Let's probe both of them and enable in the device tree + * the one that's actually found on the hardware. + */ + + np = of_find_node_by_alias(NULL, "i2c2"); + if (!np) { + pr_err("Cannot find node i2c2\n"); + return -EINVAL; + } + + adapter = of_find_i2c_adapter_by_node(np); + if (!adapter) { + pr_err("Cannot find i2c2 adapter\n"); + return -EINVAL; + } + + gpio_direction_output(TOUCH_RESET_GPIO, 0); + mdelay(10); + gpio_set_value(TOUCH_RESET_GPIO, 1); + mdelay(10); + + edt_present = i2c_device_present(adapter, 0x38); + + gpio_set_value(TOUCH_RESET_GPIO, 0); + mdelay(10); + + egalax_present = i2c_device_present(adapter, 0x4); + + if (edt_present) + model = "edt,edt-ft5206"; + if (egalax_present) + model = "eeti,egalax_ts"; + + pr_info("Found %s Touchscreen controller\n", model); + + of_register_fixup(santaro_touch_fixup, NULL); + + return 0; +} +late_initcall(santaro_detect_touch); static int santaro_device_init(void) { diff --git a/arch/arm/boards/highbank/init.c b/arch/arm/boards/highbank/init.c index 577ccc0b74..32e217321a 100644 --- a/arch/arm/boards/highbank/init.c +++ b/arch/arm/boards/highbank/init.c @@ -37,14 +37,12 @@ static int hb_fixup(struct device_node *root, void *unused) if (!(reg & HB_PWRDOM_STAT_SATA)) { for_each_compatible_node_from(node, root, NULL, "calxeda,hb-ahci") - of_set_property(node, "status", "disabled", - sizeof("disabled"), 1); + of_property_write_string(node, "status", "disabled"); } if (!(reg & HB_PWRDOM_STAT_EMMC)) { for_each_compatible_node_from(node, root, NULL, "calxeda,hb-sdhci") - of_set_property(node, "status", "disabled", - sizeof("disabled"), 1); + of_property_write_string(node, "status", "disabled"); } if ((opp_table[0] >> 16) != HB_OPP_VERSION) diff --git a/arch/arm/boards/mioa701/gpio0_poweroff.c b/arch/arm/boards/mioa701/gpio0_poweroff.c index 2054548aa6..01a5d0cc6e 100644 --- a/arch/arm/boards/mioa701/gpio0_poweroff.c +++ b/arch/arm/boards/mioa701/gpio0_poweroff.c @@ -19,6 +19,7 @@ #include <clock.h> #include <common.h> #include <init.h> +#include <poweroff.h> #include <gpio.h> #include <poller.h> @@ -45,7 +46,7 @@ static void try_poweroff(void) gpio_set_value(GPIO115_LED_nKeyboard, 0); mdelay(2000); - poweroff(); + poweroff_machine(); } static void gpio0_poller_fn(struct poller_struct *poller) diff --git a/arch/arm/boards/vscom-baltos/board.c b/arch/arm/boards/vscom-baltos/board.c index fea992d2ef..39f40a6061 100644 --- a/arch/arm/boards/vscom-baltos/board.c +++ b/arch/arm/boards/vscom-baltos/board.c @@ -41,6 +41,7 @@ #include <linux/err.h> #include <mach/bbu.h> #include <libfile.h> +#include <gpio.h> static struct omap_barebox_part baltos_barebox_part = { .nand_offset = SZ_512K, @@ -109,6 +110,17 @@ static int baltos_read_eeprom(void) sprintf(var_buf, "%d", hw_param.SystemId); globalvar_add_simple("board.id", var_buf); + /* enable mPCIe slot */ + gpio_direction_output(100, 1); + + /* configure output signals of the external GPIO controller */ + if (hw_param.SystemId == 210 || hw_param.SystemId == 211) { + gpio_direction_output(132, 0); + gpio_direction_output(133, 0); + gpio_direction_output(134, 0); + gpio_direction_output(135, 0); + } + return 0; } environment_initcall(baltos_read_eeprom); diff --git a/arch/arm/configs/at91sam9x5ek_defconfig b/arch/arm/configs/at91sam9x5ek_defconfig index 6ee005222a..dd427555f9 100644 --- a/arch/arm/configs/at91sam9x5ek_defconfig +++ b/arch/arm/configs/at91sam9x5ek_defconfig @@ -1,12 +1,15 @@ CONFIG_ARCH_AT91SAM9X5=y +CONFIG_AT91_MULTI_BOARDS=y +CONFIG_MACH_AT91SAM9X5EK=y CONFIG_BAREBOX_MAX_IMAGE_SIZE=0x40000 CONFIG_AEABI=y CONFIG_ARM_OPTIMZED_STRING_FUNCTIONS=y -CONFIG_PBL_IMAGE=y CONFIG_MMU=y CONFIG_MALLOC_SIZE=0xa00000 CONFIG_EXPERIMENTAL=y CONFIG_MALLOC_TLSF=y +CONFIG_KALLSYMS=y +CONFIG_RELOCATABLE=y CONFIG_PROMPT="9G20-EK:" CONFIG_GLOB=y CONFIG_PROMPT_HUSH_PS2="y" @@ -48,6 +51,7 @@ CONFIG_CMD_OFTREE=y CONFIG_NET=y CONFIG_NET_NFS=y CONFIG_NET_NETCONSOLE=y +CONFIG_OF_BAREBOX_DRIVERS=y CONFIG_DRIVER_NET_MACB=y CONFIG_NET_USB=y CONFIG_NET_USB_ASIX=y @@ -71,6 +75,7 @@ CONFIG_USB_STORAGE=y CONFIG_MCI=y CONFIG_MCI_STARTUP=y CONFIG_MCI_ATMEL=y +CONFIG_MFD_SYSCON=y CONFIG_LED=y CONFIG_LED_GPIO=y CONFIG_LED_TRIGGERS=y @@ -85,3 +90,4 @@ CONFIG_FS_TFTP=y CONFIG_FS_FAT=y CONFIG_FS_FAT_WRITE=y CONFIG_FS_FAT_LFN=y +CONFIG_LED_GPIO_OF=y diff --git a/arch/arm/configs/kindle3_defconfig b/arch/arm/configs/kindle3_defconfig index 0b7088e867..a4e08dfad0 100644 --- a/arch/arm/configs/kindle3_defconfig +++ b/arch/arm/configs/kindle3_defconfig @@ -1,8 +1,8 @@ CONFIG_ARCH_IMX=y CONFIG_BAREBOX_MAX_IMAGE_SIZE=0x30000 CONFIG_MACH_KINDLE3=y -CONFIG_ARCH_IMX_USBLOADER=y CONFIG_IMX_IIM=y +CONFIG_ARCH_IMX_USBLOADER=y CONFIG_AEABI=y CONFIG_ARM_BOARD_APPEND_ATAG=y CONFIG_ARM_BOARD_PREPEND_ATAG=y @@ -46,6 +46,7 @@ CONFIG_CMD_TIMEOUT=y CONFIG_CMD_CRC=y CONFIG_CMD_CRC_CMP=y CONFIG_CMD_MEMTEST=y +CONFIG_CMD_CLK=y CONFIG_CMD_GPIO=y CONFIG_CMD_I2C=y CONFIG_CMD_SPI=y diff --git a/arch/arm/cpu/psci.c b/arch/arm/cpu/psci.c index d650c23ea2..eafb361a0a 100644 --- a/arch/arm/cpu/psci.c +++ b/arch/arm/cpu/psci.c @@ -204,13 +204,11 @@ static int of_psci_fixup(struct device_node *root, void *unused) if (!psci) return -EINVAL; - ret = of_set_property(psci, "compatible", "arm,psci-1.0", - strlen("arm,psci-1.0") + 1, 1); + ret = of_property_write_string(psci, "compatible", "arm,psci-1.0"); if (ret) return ret; - ret = of_set_property(psci, "method", "smc", - strlen("smc") + 1, 1); + ret = of_property_write_string(psci, "method", "smc"); if (ret) return ret; diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index 0c9e0e8dad..e8ad43bfd8 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile @@ -91,5 +91,7 @@ pbl-dtb-$(CONFIG_MACH_ZII_VF610_DEV) += \ vf610-zii-spu3-rev-a.dtb.o \ vf610-zii-scu4-aib-rev-c.dtb.o +pbl-dtb-$(CONFIG_MACH_AT91SAM9X5EK) += at91sam9x5ek.dtb.o + clean-files := *.dtb *.dtb.S .*.dtc .*.pre .*.dts *.dtb.lzo diff --git a/arch/arm/dts/at91sam9x5ek.dts b/arch/arm/dts/at91sam9x5ek.dts new file mode 100644 index 0000000000..d5c7a8fe5c --- /dev/null +++ b/arch/arm/dts/at91sam9x5ek.dts @@ -0,0 +1,70 @@ +/dts-v1/; + +#include <dt-bindings/gpio/gpio.h> +#include <dt-bindings/pinctrl/at91.h> + +#include <arm/at91sam9x5.dtsi> +#include <arm/at91sam9x5_macb0.dtsi> +#include <arm/at91sam9x5_lcd.dtsi> +#include <arm/at91sam9x5dm.dtsi> +#include <arm/at91sam9x5ek.dtsi> + +/ { + aliases { + mmc0 = &mmc0; + mmc1 = &mmc1; + }; + + i2c-gpio-0 { + status = "okay"; + }; + + ahb { + apb { + pinctrl@fffff400 { + spi { + pinctrl_board_spi: spi-board { + atmel,pins = <AT91_PIOA 14 AT91_PERIPH_GPIO AT91_PINCTRL_NONE>; + }; + }; + }; + }; + }; + + leds { + /* + * PB18 has a resource conflict since it is both used + * as a heartbeat LED and 1-wire bus in the kernel + * device tree. Because 1-wire EEPROMs contains + * importatnt revision information we move heartbeat + * to PD21 and remove the original pb18 node + */ + /delete-node/ pb18; + + pd21 { + linux,default-trigger = "heartbeat"; + }; + }; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spi0>, <&pinctrl_board_spi>; +}; + +&i2c0 { + status = "disabled"; +}; + +/* + * This one conflicts with SPI NOR on the SoM + */ +&mmc1 { + status = "disabled"; +}; + +&macb0 { + status = "okay"; + phy-mode = "rmii"; +}; + diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig index 2d4721a26a..9db53b2261 100644 --- a/arch/arm/mach-at91/Kconfig +++ b/arch/arm/mach-at91/Kconfig @@ -9,6 +9,26 @@ config HAVE_AT91_DBGU1 config HAVE_AT91_DBGU2 bool +config HAVE_AT91_UTMI + bool + +config HAVE_AT91_USB_CLK + bool + +config COMMON_CLK_AT91 + bool + select COMMON_CLK + select MFD_SYSCON + +config HAVE_AT91_SMD + bool + +config HAVE_AT91_H32MX + bool + +config HAVE_AT91_GENERATED_CLK + bool + config HAVE_AT91_LOWLEVEL_INIT bool @@ -20,6 +40,7 @@ config SOC_AT91SAM9 select CPU_ARM926T select AT91SAM9_SMC select CLOCKSOURCE_ATMEL_PIT + select PINCTRL config SOC_SAMA5 bool @@ -106,6 +127,10 @@ config SOC_AT91SAM9X5 select HAVE_AT91_DBGU0 select HAS_MACB select AT91SAM9G45_RESET + select HAVE_AT91_SMD + select HAVE_AT91_USB_CLK + select HAVE_AT91_UTMI + select COMMON_CLK_OF_PROVIDER help Select this if you are using one of Atmel's AT91SAM9x5 family SoC. This means that your SAM9 name finishes with a '5' (except if it is @@ -140,26 +165,32 @@ config ARCH_AT91SAM9261 config ARCH_AT91SAM9263 bool "AT91SAM9263" select SOC_AT91SAM9263 + select HAVE_MACH_ARM_HEAD config ARCH_AT91SAM9G10 bool "AT91SAM9G10" select SOC_AT91SAM9261 + select HAVE_MACH_ARM_HEAD config ARCH_AT91SAM9G20 bool "AT91SAM9G20" select SOC_AT91SAM9260 + select HAVE_MACH_ARM_HEAD config ARCH_AT91SAM9G45 bool "AT91SAM9G45 or AT91SAM9M10" select SOC_AT91SAM9G45 + select HAVE_MACH_ARM_HEAD config ARCH_AT91SAM9X5 bool "AT91SAM9X5" select SOC_AT91SAM9X5 + select OFDEVICE config ARCH_AT91SAM9N12 bool "AT91SAM9N12" select SOC_AT91SAM9N12 + select HAVE_MACH_ARM_HEAD config ARCH_SAMA5D3 bool "SAMA5D3x" @@ -167,6 +198,7 @@ config ARCH_SAMA5D3 select HAVE_AT91_DBGU1 select HAS_MACB select AT91SAM9G45_RESET + select HAVE_MACH_ARM_HEAD config ARCH_SAMA5D4 bool "SAMA5D4" @@ -174,6 +206,7 @@ config ARCH_SAMA5D4 select HAVE_AT91_DBGU2 select HAS_MACB select AT91SAM9G45_RESET + select HAVE_MACH_ARM_HEAD endchoice @@ -429,23 +462,6 @@ endif # ---------------------------------------------------------- -if ARCH_AT91SAM9X5 - -choice - prompt "AT91SAM9x5 Series Board Type" - -config MACH_AT91SAM9X5EK - bool "Atmel AT91SAM9x5 Series Evaluation Kit" - help - Select this if you re using Atmel's AT91SAM9x5-EK Evaluation Kit. - Supported chips are sam9g15, sam9g25, sam9x25, sam9g35 and sam9x35. - -endchoice - -endif - -# ---------------------------------------------------------- - if ARCH_AT91SAM9N12 choice @@ -504,6 +520,22 @@ endif # ---------------------------------------------------------- + +config AT91_MULTI_BOARDS + bool "Allow multiple boards to be selected" + select HAVE_PBL_MULTI_IMAGES + +if AT91_MULTI_BOARDS + +config MACH_AT91SAM9X5EK + bool "Atmel AT91SAM9x5 Series Evaluation Kit" + depends on ARCH_AT91SAM9X5 + help + Select this if you re using Atmel's AT91SAM9x5-EK Evaluation Kit. + Supported chips are sam9g15, sam9g25, sam9x25, sam9g35 and sam9x35. + +endif + comment "AT91 Board Options" config MTD_AT91_DATAFLASH_CARD diff --git a/arch/arm/mach-at91/Makefile b/arch/arm/mach-at91/Makefile index 1f63b09aaa..0892fb4b57 100644 --- a/arch/arm/mach-at91/Makefile +++ b/arch/arm/mach-at91/Makefile @@ -1,4 +1,9 @@ -obj-y += setup.o clock.o irq_fixup.o +obj-y += setup.o irq_fixup.o + +ifeq ($(CONFIG_COMMON_CLK_OF_PROVIDER),) +obj-y += clock.o +endif + obj-$(CONFIG_CMD_AT91_BOOT_TEST) += boot_test_cmd.o obj-$(CONFIG_AT91_BOOTSTRAP) += bootstrap.o @@ -28,7 +33,7 @@ obj-$(CONFIG_ARCH_AT91SAM9263) += at91sam9263.o at91sam9263_devices.o obj-$(CONFIG_ARCH_AT91SAM9G10) += at91sam9261.o at91sam9261_devices.o obj-$(CONFIG_ARCH_AT91SAM9G20) += at91sam9260.o at91sam9260_devices.o obj-$(CONFIG_ARCH_AT91SAM9G45) += at91sam9g45.o at91sam9g45_devices.o -obj-$(CONFIG_ARCH_AT91SAM9X5) += at91sam9x5.o at91sam9x5_devices.o +obj-$(CONFIG_ARCH_AT91SAM9X5) += at91sam9x5_devices.o obj-$(CONFIG_ARCH_AT91SAM9N12) += at91sam9n12.o at91sam9n12_devices.o obj-$(CONFIG_ARCH_SAMA5D3) += sama5d3.o sama5d3_devices.o obj-$(CONFIG_ARCH_SAMA5D4) += sama5d4.o sama5d4_devices.o diff --git a/arch/arm/mach-at91/at91sam9x5.c b/arch/arm/mach-at91/at91sam9x5.c deleted file mode 100644 index 000b7483b2..0000000000 --- a/arch/arm/mach-at91/at91sam9x5.c +++ /dev/null @@ -1,311 +0,0 @@ -#include <common.h> -#include <gpio.h> -#include <init.h> -#include <mach/hardware.h> -#include <mach/at91_pmc.h> -#include <mach/io.h> -#include <mach/cpu.h> - -#include "soc.h" -#include "generic.h" -#include "clock.h" - -/* -------------------------------------------------------------------- - * Clocks - * -------------------------------------------------------------------- */ - -/* - * The peripheral clocks. - */ -static struct clk pioAB_clk = { - .name = "pioAB_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_PIOAB, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk pioCD_clk = { - .name = "pioCD_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_PIOCD, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk smd_clk = { - .name = "smd_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_SMD, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk usart0_clk = { - .name = "usart0_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_USART0, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk usart1_clk = { - .name = "usart1_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_USART1, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk usart2_clk = { - .name = "usart2_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_USART2, - .type = CLK_TYPE_PERIPHERAL, -}; -/* USART3 clock - Only for sam9g25/sam9x25 */ -static struct clk usart3_clk = { - .name = "usart3_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_USART3, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk twi0_clk = { - .name = "twi0_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_TWI0, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk twi1_clk = { - .name = "twi1_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_TWI1, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk twi2_clk = { - .name = "twi2_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_TWI2, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk mmc0_clk = { - .name = "mci0_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_MCI0, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk spi0_clk = { - .name = "spi0_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_SPI0, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk spi1_clk = { - .name = "spi1_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_SPI1, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk uart0_clk = { - .name = "uart0_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_UART0, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk uart1_clk = { - .name = "uart1_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_UART1, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk tcb0_clk = { - .name = "tcb0_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_TCB, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk pwm_clk = { - .name = "pwm_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_PWM, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk adc_clk = { - .name = "adc_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_ADC, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk dma0_clk = { - .name = "dma0_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_DMA0, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk dma1_clk = { - .name = "dma1_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_DMA1, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk uhphs_clk = { - .name = "uhphs", - .pmc_mask = 1 << AT91SAM9X5_ID_UHPHS, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk udphs_clk = { - .name = "udphs_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_UDPHS, - .type = CLK_TYPE_PERIPHERAL, -}; -/* emac0 clock - Only for sam9g25/sam9x25/sam9g35/sam9x35 */ -static struct clk macb0_clk = { - .name = "macb0_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_EMAC0, - .type = CLK_TYPE_PERIPHERAL, -}; -/* lcd clock - Only for sam9g15/sam9g35/sam9x35 */ -static struct clk lcdc_clk = { - .name = "lcdc_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_LCDC, - .type = CLK_TYPE_PERIPHERAL, -}; -/* isi clock - Only for sam9g25 */ -static struct clk isi_clk = { - .name = "isi_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_ISI, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk mmc1_clk = { - .name = "mci1_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_MCI1, - .type = CLK_TYPE_PERIPHERAL, -}; -/* emac1 clock - Only for sam9x25 */ -static struct clk macb1_clk = { - .name = "macb1_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_EMAC1, - .type = CLK_TYPE_PERIPHERAL, -}; -static struct clk ssc_clk = { - .name = "ssc_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_SSC, - .type = CLK_TYPE_PERIPHERAL, -}; -/* can0 clock - Only for sam9x35 */ -static struct clk can0_clk = { - .name = "can0_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_CAN0, - .type = CLK_TYPE_PERIPHERAL, -}; -/* can1 clock - Only for sam9x35 */ -static struct clk can1_clk = { - .name = "can1_clk", - .pmc_mask = 1 << AT91SAM9X5_ID_CAN1, - .type = CLK_TYPE_PERIPHERAL, -}; - -static struct clk *periph_clocks[] __initdata = { - &pioAB_clk, - &pioCD_clk, - &smd_clk, - &usart0_clk, - &usart1_clk, - &usart2_clk, - &twi0_clk, - &twi1_clk, - &twi2_clk, - &mmc0_clk, - &spi0_clk, - &spi1_clk, - &uart0_clk, - &uart1_clk, - &tcb0_clk, - &pwm_clk, - &adc_clk, - &dma0_clk, - &dma1_clk, - &uhphs_clk, - &udphs_clk, - &mmc1_clk, - &ssc_clk, - // irq0 -}; - -static struct clk_lookup periph_clocks_lookups[] = { - CLKDEV_CON_DEV_ID("macb_clk", "macb0", &macb0_clk), - CLKDEV_CON_DEV_ID("macb_clk", "macb1", &macb1_clk), - CLKDEV_CON_ID("ohci_clk", &uhphs_clk), - CLKDEV_CON_DEV_ID("ehci_clk", "atmel-ehci", &uhphs_clk), - CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi0", &spi0_clk), - CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi1", &spi1_clk), - CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci0", &mmc0_clk), - CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci1", &mmc1_clk), - CLKDEV_DEV_ID("at91sam9x5-gpio0", &pioAB_clk), - CLKDEV_DEV_ID("at91sam9x5-gpio1", &pioAB_clk), - CLKDEV_DEV_ID("at91sam9x5-gpio2", &pioCD_clk), - CLKDEV_DEV_ID("at91sam9x5-gpio3", &pioCD_clk), - CLKDEV_DEV_ID("at91-pit", &mck), - CLKDEV_CON_DEV_ID("hck1", "atmel_hlcdfb", &lcdc_clk), -}; - -static struct clk_lookup usart_clocks_lookups[] = { - CLKDEV_CON_DEV_ID("usart", "atmel_usart0", &mck), - CLKDEV_CON_DEV_ID("usart", "atmel_usart1", &usart0_clk), - CLKDEV_CON_DEV_ID("usart", "atmel_usart2", &usart1_clk), - CLKDEV_CON_DEV_ID("usart", "atmel_usart3", &usart2_clk), - CLKDEV_CON_DEV_ID("usart", "atmel_usart4", &usart3_clk), -}; - -/* - * The two programmable clocks. - * You must configure pin multiplexing to bring these signals out. - */ -static struct clk pck0 = { - .name = "pck0", - .pmc_mask = AT91_PMC_PCK0, - .type = CLK_TYPE_PROGRAMMABLE, - .id = 0, -}; -static struct clk pck1 = { - .name = "pck1", - .pmc_mask = AT91_PMC_PCK1, - .type = CLK_TYPE_PROGRAMMABLE, - .id = 1, -}; - -static void __init at91sam9x5_register_clocks(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(periph_clocks); i++) - clk_register(periph_clocks[i]); - - clkdev_add_table(periph_clocks_lookups, - ARRAY_SIZE(periph_clocks_lookups)); - clkdev_add_table(usart_clocks_lookups, - ARRAY_SIZE(usart_clocks_lookups)); - - if (cpu_is_at91sam9g25() - || cpu_is_at91sam9x25()) - clk_register(&usart3_clk); - - if (cpu_is_at91sam9g25() - || cpu_is_at91sam9x25() - || cpu_is_at91sam9g35() - || cpu_is_at91sam9x35()) - clk_register(&macb0_clk); - - if (cpu_is_at91sam9g15() - || cpu_is_at91sam9g35() - || cpu_is_at91sam9x35()) - clk_register(&lcdc_clk); - - if (cpu_is_at91sam9g25()) - clk_register(&isi_clk); - - if (cpu_is_at91sam9x25()) - clk_register(&macb1_clk); - - if (cpu_is_at91sam9x25() - || cpu_is_at91sam9x35()) { - clk_register(&can0_clk); - clk_register(&can1_clk); - } - - clk_register(&pck0); - clk_register(&pck1); -} - -/* -------------------------------------------------------------------- - * AT91SAM9x5 processor initialization - * -------------------------------------------------------------------- */ - -static void at91sam9x5_initialize(void) -{ - /* Register the processor-specific clocks */ - at91sam9x5_register_clocks(); - - /* Register GPIO subsystem */ - at91_add_sam9x5_gpio(0, AT91SAM9X5_BASE_PIOA); - at91_add_sam9x5_gpio(1, AT91SAM9X5_BASE_PIOB); - at91_add_sam9x5_gpio(2, AT91SAM9X5_BASE_PIOC); - at91_add_sam9x5_gpio(3, AT91SAM9X5_BASE_PIOD); - - at91_add_pit(AT91SAM9X5_BASE_PIT); - at91_add_sam9_smc(DEVICE_ID_SINGLE, AT91SAM9X5_BASE_SMC, 0x200); -} - -AT91_SOC_START(sam9x5) - .init = at91sam9x5_initialize, -AT91_SOC_END diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h index 491b220cad..5d76e00aef 100644 --- a/arch/arm/mach-at91/include/mach/board.h +++ b/arch/arm/mach-at91/include/mach/board.h @@ -32,11 +32,13 @@ void at91_set_main_clock(unsigned long rate); +#define AT91_MAX_USBH_PORTS 3 + /* USB Host */ struct at91_usbh_data { u8 ports; /* number of ports on root hub */ - int vbus_pin[2]; /* port power-control pin */ - u8 vbus_pin_active_low[2]; /* vbus polarity */ + int vbus_pin[AT91_MAX_USBH_PORTS]; /* port power-control pin */ + u8 vbus_pin_active_low[AT91_MAX_USBH_PORTS]; /* vbus polarity */ }; extern void __init at91_add_device_usbh_ohci(struct at91_usbh_data *data); extern void __init at91_add_device_usbh_ehci(struct at91_usbh_data *data); diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c index 1fa50ac054..8f32af043d 100644 --- a/arch/arm/mach-at91/setup.c +++ b/arch/arm/mach-at91/setup.c @@ -85,7 +85,6 @@ static void __init soc_detect(u32 dbgu_base) case ARCH_ID_AT91SAM9X5: at91_soc_initdata.type = AT91_SOC_SAM9X5; - at91_boot_soc = at91sam9x5_soc; break; case ARCH_ID_AT91SAM9N12: @@ -284,6 +283,9 @@ static int at91_detect(void) pr_info("AT91: Detected soc subtype: %s\n", at91_get_soc_subtype(&at91_soc_initdata)); + if (IS_ENABLED(CONFIG_COMMON_CLK_OF_PROVIDER)) + return 0; + if (!at91_soc_is_enabled()) panic("AT91: Soc not enabled"); diff --git a/arch/arm/mach-highbank/reset.c b/arch/arm/mach-highbank/reset.c index 929ded5951..b60f34452e 100644 --- a/arch/arm/mach-highbank/reset.c +++ b/arch/arm/mach-highbank/reset.c @@ -6,6 +6,7 @@ #include <common.h> #include <io.h> +#include <poweroff.h> #include <restart.h> #include <init.h> @@ -20,15 +21,7 @@ static void __noreturn highbank_restart_soc(struct restart_handler *rst) hang(); } -static int restart_register_feature(void) -{ - restart_handler_register_fn(highbank_restart_soc); - - return 0; -} -coredevice_initcall(restart_register_feature); - -void __noreturn poweroff() +void __noreturn highbank_poweroff(struct poweroff_handler *handler) { shutdown_barebox(); @@ -37,3 +30,12 @@ void __noreturn poweroff() while(1); } + +static int highbank_init(void) +{ + restart_handler_register_fn(highbank_restart_soc); + poweroff_handler_register_fn(highbank_poweroff); + + return 0; +} +coredevice_initcall(highbank_init); diff --git a/arch/arm/mach-pxa/pxa2xx.c b/arch/arm/mach-pxa/pxa2xx.c index b712b388c8..e28378e6db 100644 --- a/arch/arm/mach-pxa/pxa2xx.c +++ b/arch/arm/mach-pxa/pxa2xx.c @@ -14,6 +14,7 @@ #include <common.h> #include <init.h> +#include <poweroff.h> #include <reset_source.h> #include <mach/hardware.h> #include <mach/pxa-regs.h> @@ -46,9 +47,7 @@ void pxa_clear_reset_source(void) RCSR = RCSR_GPR | RCSR_SMR | RCSR_WDR | RCSR_HWR; } -device_initcall(pxa_detect_reset_source); - -void __noreturn poweroff(void) +static void __noreturn pxa2xx_poweroff(struct poweroff_handler *handler) { shutdown_barebox(); @@ -57,3 +56,13 @@ void __noreturn poweroff(void) pxa_suspend(PWRMODE_DEEPSLEEP); unreachable(); } + +static int pxa2xx_init(void) +{ + poweroff_handler_register_fn(pxa2xx_poweroff); + + pxa_detect_reset_source(); + + return 0; +} +device_initcall(pxa2xx_init); diff --git a/arch/arm/mach-pxa/pxa3xx.c b/arch/arm/mach-pxa/pxa3xx.c index 86ca63b160..ccfd952b5e 100644 --- a/arch/arm/mach-pxa/pxa3xx.c +++ b/arch/arm/mach-pxa/pxa3xx.c @@ -14,6 +14,7 @@ #include <common.h> #include <init.h> +#include <poweroff.h> #include <reset_source.h> #include <mach/hardware.h> #include <mach/pxa-regs.h> @@ -48,7 +49,7 @@ void pxa_clear_reset_source(void) device_initcall(pxa_detect_reset_source); -void __noreturn poweroff(void) +static void __noreturn pxa3xx_poweroff(struct poweroff_handler *handler) { shutdown_barebox(); @@ -57,3 +58,13 @@ void __noreturn poweroff(void) pxa3xx_suspend(PXA3xx_PM_S3D4C4); unreachable(); } + +static int pxa3xx_init(void) +{ + poweroff_handler_register_fn(pxa3xx_poweroff); + + pxa_detect_reset_source(); + + return 0; +} +device_initcall(pxa3xx_init); diff --git a/arch/mips/mach-xburst/Kconfig b/arch/mips/mach-xburst/Kconfig index fd106fefe0..ee79ff6167 100644 --- a/arch/mips/mach-xburst/Kconfig +++ b/arch/mips/mach-xburst/Kconfig @@ -21,7 +21,6 @@ choice config BOARD_RZX50 bool "Ritmix RZX-50" - select HAS_POWEROFF select CPU_JZ4755 config BOARD_CI20 diff --git a/arch/mips/mach-xburst/reset-jz4750.c b/arch/mips/mach-xburst/reset-jz4750.c index 25830f130e..1fdcc7b8af 100644 --- a/arch/mips/mach-xburst/reset-jz4750.c +++ b/arch/mips/mach-xburst/reset-jz4750.c @@ -22,6 +22,8 @@ #include <common.h> #include <io.h> +#include <init.h> +#include <poweroff.h> #include <mach/jz4750d_regs.h> static void __noreturn jz4750d_halt(void) @@ -37,7 +39,7 @@ static void __noreturn jz4750d_halt(void) unreachable(); } -void __noreturn poweroff() +static void __noreturn jz4750_poweroff(struct poweroff_handler *handler) { u32 ctrl; @@ -50,4 +52,11 @@ void __noreturn poweroff() writel(RTC_HCR_PD, (u32 *)RTC_HCR); jz4750d_halt(); } -EXPORT_SYMBOL(poweroff); + +static int jz4750_init(void) +{ + poweroff_handler_register_fn(jz4750_poweroff); + + return 0; +} +coredevice_initcall(jz4750_init); diff --git a/arch/sandbox/board/hostfile.c b/arch/sandbox/board/hostfile.c index c1a2643bc7..e7d92ea031 100644 --- a/arch/sandbox/board/hostfile.c +++ b/arch/sandbox/board/hostfile.c @@ -133,8 +133,7 @@ static int of_hostfile_fixup(struct device_node *root, void *ctx) node = of_new_node(root, hf->devname); - ret = of_set_property(node, "compatible", hostfile_dt_ids->compatible, - strlen(hostfile_dt_ids->compatible) + 1, 1); + ret = of_property_write_string(node, "compatible", hostfile_dt_ids->compatible); if (ret) return ret; @@ -146,8 +145,7 @@ static int of_hostfile_fixup(struct device_node *root, void *ctx) if (ret) return ret; - ret = of_set_property(node, "barebox,filename", hf->filename, - strlen(hf->filename) + 1, 1); + ret = of_property_write_string(node, "barebox,filename", hf->filename); return ret; } diff --git a/arch/sandbox/configs/sandbox_defconfig b/arch/sandbox/configs/sandbox_defconfig index c20dd15a0b..dbaff12bfb 100644 --- a/arch/sandbox/configs/sandbox_defconfig +++ b/arch/sandbox/configs/sandbox_defconfig @@ -79,6 +79,7 @@ CONFIG_FS_FAT_WRITE=y CONFIG_FS_FAT_LFN=y CONFIG_FS_BPKFS=y CONFIG_FS_UIMAGEFS=y +CONFIG_FS_SQUASHFS=y CONFIG_BZLIB=y CONFIG_LZ4_DECOMPRESS=y CONFIG_XZ_DECOMPRESS=y diff --git a/commands/Kconfig b/commands/Kconfig index bc0885c69d..ae2dc4b094 100644 --- a/commands/Kconfig +++ b/commands/Kconfig @@ -6,10 +6,6 @@ config COMMAND_SUPPORT depends on !SHELL_NONE default y -config HAS_POWEROFF - bool - default n - if COMMAND_SUPPORT config COMPILE_HASH @@ -1848,7 +1844,6 @@ config CMD_NAND_BITFLIP config CMD_POWEROFF tristate - depends on HAS_POWEROFF prompt "poweroff" help Turn the power off. @@ -1930,6 +1925,11 @@ config CMD_WD_DEFAULT_TIMOUT 'wd' is done without a timeout value (which means the watchdog gets enabled and re-triggered with the default timeout value). +config CMD_HAB + bool + depends on HAB + prompt "High Assurance boot (hab)" + # end Hardware manipulation commands endmenu @@ -1967,6 +1967,12 @@ config CMD_FIRMWARELOAD Provides the "firmwareload" command which deals with devices which need firmware to work. It is also used to upload firmware to FPGA devices. +config CMD_KEYSTORE + depends on CRYPTO_KEYSTORE + bool + prompt "keystore" + help + keystore provides access to the barebox keystore. config CMD_LINUX_EXEC bool "linux exec" @@ -2115,6 +2121,12 @@ config CMD_SPD_DECODE help decode spd eeprom +config CMD_SEED + tristate + prompt "seed" + help + Seed the pseudo random number generator (PRNG) + # end Miscellaneous commands endmenu diff --git a/commands/Makefile b/commands/Makefile index 601f15fc38..37486dceb1 100644 --- a/commands/Makefile +++ b/commands/Makefile @@ -87,11 +87,13 @@ obj-$(CONFIG_CMD_AUTOMOUNT) += automount.o obj-$(CONFIG_CMD_GLOBAL) += global.o obj-$(CONFIG_CMD_DMESG) += dmesg.o obj-$(CONFIG_CMD_BASENAME) += basename.o +obj-$(CONFIG_CMD_HAB) += hab.o obj-$(CONFIG_CMD_DIRNAME) += dirname.o obj-$(CONFIG_CMD_READLINK) += readlink.o obj-$(CONFIG_CMD_LET) += let.o obj-$(CONFIG_CMD_LN) += ln.o obj-$(CONFIG_CMD_CLK) += clk.o +obj-$(CONFIG_CMD_KEYSTORE) += keystore.o obj-$(CONFIG_CMD_TFTP) += tftp.o obj-$(CONFIG_CMD_FILETYPE) += filetype.o obj-$(CONFIG_CMD_BAREBOX_UPDATE)+= barebox-update.o @@ -120,3 +122,4 @@ obj-$(CONFIG_CMD_DHRYSTONE) += dhrystone.o obj-$(CONFIG_CMD_SPD_DECODE) += spd_decode.o obj-$(CONFIG_CMD_MMC_EXTCSD) += mmc_extcsd.o obj-$(CONFIG_CMD_NAND_BITFLIP) += nand-bitflip.o +obj-$(CONFIG_CMD_SEED) += seed.o diff --git a/commands/digest.c b/commands/digest.c index 02a9f6f0de..0edbbec32c 100644 --- a/commands/digest.c +++ b/commands/digest.c @@ -36,12 +36,16 @@ int __do_digest(struct digest *d, unsigned char *sig, while (*argv) { char *filename = "/dev/mem"; loff_t start = 0, size = ~0; + int show_area = 1; /* arguments are either file, file+area or area */ if (parse_area_spec(*argv, &start, &size)) { filename = *argv; - if (argv[1] && !parse_area_spec(argv[1], &start, &size)) + show_area = 0; + if (argv[1] && !parse_area_spec(argv[1], &start, &size)) { argv++; + show_area = 1; + } } ret = digest_file_window(d, filename, @@ -53,8 +57,12 @@ int __do_digest(struct digest *d, unsigned char *sig, for (i = 0; i < digest_length(d); i++) printf("%02x", hash[i]); - printf(" %s\t0x%08llx ... 0x%08llx\n", - filename, start, start + size); + printf(" %s", filename); + if (show_area) + printf("\t0x%08llx ... 0x%08llx", + start, start + size); + + puts("\n"); } } diff --git a/commands/hab.c b/commands/hab.c new file mode 100644 index 0000000000..0d7ee8e76c --- /dev/null +++ b/commands/hab.c @@ -0,0 +1,120 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <common.h> +#include <command.h> +#include <complete.h> +#include <fs.h> +#include <fcntl.h> +#include <getopt.h> +#include <linux/ctype.h> +#include <errno.h> +#include <hab.h> + +static int do_hab(int argc, char *argv[]) +{ + int opt, ret, i; + char *srkhashfile = NULL, *srkhash = NULL; + unsigned flags = 0; + u8 srk[SRK_HASH_SIZE]; + int lockdown = 0, info = 0; + + while ((opt = getopt(argc, argv, "s:fpx:li")) > 0) { + switch (opt) { + case 's': + srkhashfile = optarg; + break; + case 'f': + flags |= IMX_SRK_HASH_FORCE; + break; + case 'p': + flags |= IMX_SRK_HASH_WRITE_PERMANENT; + break; + case 'x': + srkhash = optarg; + break; + case 'l': + lockdown = 1; + break; + case 'i': + info = 1; + break; + default: + return COMMAND_ERROR_USAGE; + } + } + + if (!info && !lockdown && !srkhashfile && !srkhash) { + printf("Nothing to do\n"); + return COMMAND_ERROR_USAGE; + } + + if (info) { + ret = imx_hab_read_srk_hash(srk); + if (ret) + return ret; + + printf("Current SRK hash: "); + for (i = 0; i < SRK_HASH_SIZE; i++) + printf("%02x", srk[i]); + printf("\n"); + + if (imx_hab_device_locked_down()) + printf("secure mode\n"); + else + printf("devel mode\n"); + + return 0; + } + + if (srkhashfile && srkhash) { + printf("-s and -x options may not be given together\n"); + return COMMAND_ERROR_USAGE; + } + + if (srkhashfile) { + ret = imx_hab_write_srk_hash_file(srkhashfile, flags); + if (ret) + return ret; + } else if (srkhash) { + ret = imx_hab_write_srk_hash_hex(srkhash, flags); + if (ret) + return ret; + } + + if (lockdown) { + ret = imx_hab_lockdown_device(flags); + if (ret) + return ret; + printf("Device successfully locked down\n"); + } + + return 0; +} + +BAREBOX_CMD_HELP_START(hab) +BAREBOX_CMD_HELP_TEXT("Handle i.MX HAB (High Assurance Boot)") +BAREBOX_CMD_HELP_TEXT("") +BAREBOX_CMD_HELP_OPT ("-s <file>", "Burn Super Root Key hash from <file>") +BAREBOX_CMD_HELP_OPT ("-x <sha256>", "Burn Super Root Key hash from hex string") +BAREBOX_CMD_HELP_OPT ("-i", "Print HAB info") +BAREBOX_CMD_HELP_OPT ("-f", "Force. Write even when a key is already written") +BAREBOX_CMD_HELP_OPT ("-l", "Lockdown device. Dangerous! After executing only signed images can be booted") +BAREBOX_CMD_HELP_OPT ("-p", "Permanent. Really burn fuses. Be careful!") +BAREBOX_CMD_HELP_END + +BAREBOX_CMD_START(hab) + .cmd = do_hab, + BAREBOX_CMD_DESC("Handle i.MX HAB") + BAREBOX_CMD_OPTS("sxfp") + BAREBOX_CMD_GROUP(CMD_GRP_HWMANIP) + BAREBOX_CMD_HELP(cmd_hab_help) +BAREBOX_CMD_END diff --git a/commands/keystore.c b/commands/keystore.c new file mode 100644 index 0000000000..52c4be2639 --- /dev/null +++ b/commands/keystore.c @@ -0,0 +1,100 @@ +#include <common.h> +#include <command.h> +#include <getopt.h> +#include <libfile.h> +#include <crypto/keystore.h> +#include <linux/kernel.h> +#include <fs.h> + +static int do_keystore(int argc, char *argv[]) +{ + int opt; + int ret; + int do_remove = 0; + const char *name; + const char *file = NULL; + char *secret_str = NULL; + void *secret; + int s_len; + + while ((opt = getopt(argc, argv, "rs:f:")) > 0) { + switch (opt) { + case 'r': + do_remove = 1; + break; + case 's': + secret_str = optarg; + break; + case 'f': + file = optarg; + break; + default: + return COMMAND_ERROR_USAGE; + } + } + + if (argc == optind) + return COMMAND_ERROR_USAGE; + + if (!do_remove && !file && !secret_str) + return COMMAND_ERROR_USAGE; + + if (file && secret_str) + return COMMAND_ERROR_USAGE; + + name = argv[optind]; + + if (do_remove) { + keystore_forget_secret(name); + printf("forgotten secret for key %s\n", name); + return 0; + } + + if (file) { + ret = read_file_2(file, &s_len, (void *)&secret_str, FILESIZE_MAX); + if (ret) { + printf("Cannot open %s: %s\n", file, strerror(-ret)); + return 1; + } + } else if (secret_str) { + s_len = strlen(secret_str); + } + + if (s_len & 1) { + printf("invalid secret len. Must be whole bytes\n"); + return 1; + } + + secret = xzalloc(s_len / 2); + ret = hex2bin(secret, secret_str, s_len / 2); + if (ret) { + printf("Cannot convert %s to binary: %s\n", secret_str, strerror(-ret)); + return 1; + } + + ret = keystore_set_secret(name, secret, s_len / 2); + if (ret) + printf("cannot set secret for key %s: %s\n", name, strerror(-ret)); + else + printf("Added secret for key %s\n", name); + + free(secret); + + return ret ? 1 : 0; +} + +BAREBOX_CMD_HELP_START(keystore) +BAREBOX_CMD_HELP_TEXT("") +BAREBOX_CMD_HELP_TEXT("Options:") +BAREBOX_CMD_HELP_OPT("-r", "remove a key from the keystore") +BAREBOX_CMD_HELP_OPT("-s <key>", "set a key in the keystore") +BAREBOX_CMD_HELP_OPT("-f <keyfile>", "set a key in the keystore, read secret from file") +BAREBOX_CMD_HELP_END + +BAREBOX_CMD_START(keystore) + .cmd = do_keystore, + BAREBOX_CMD_DESC("manage keys") + BAREBOX_CMD_OPTS("[-rsf] <keyname>") + BAREBOX_CMD_GROUP(CMD_GRP_MISC) + BAREBOX_CMD_HELP(cmd_keystore_help) +BAREBOX_CMD_END diff --git a/commands/led.c b/commands/led.c index 354f74df8f..a53f0df6a2 100644 --- a/commands/led.c +++ b/commands/led.c @@ -29,6 +29,44 @@ static int do_led(int argc, char *argv[]) unsigned long value; struct led *led; int ret; + int opt; + int flash = 0, blink = 0; + int blink_on_ms = 500; + int blink_off_ms = 500; + + while ((opt = getopt(argc, argv, "fb")) > 0) { + switch(opt) { + case 'f': + flash = 1; + break; + case 'b': + blink = 1; + break; + } + } + + if (flash || blink) { + int args = argc - optind; + + if (!args || (flash && blink)) + return COMMAND_ERROR_USAGE; + + led = led_by_name_or_number(argv[optind]); + if (!led) { + printf("no such LED: %s\n", argv[optind]); + return 1; + } + + if (args > 1) + blink_on_ms = simple_strtoul(argv[optind + 1], NULL, 0); + if (args > 2) + blink_off_ms = simple_strtoul(argv[optind + 2], NULL, 0); + + if (flash) + return led_flash(led, blink_on_ms); + if (blink) + return led_blink(led, blink_on_ms, blink_off_ms); + } if (argc == 1) { int i = 0; @@ -73,9 +111,13 @@ static int do_led(int argc, char *argv[]) BAREBOX_CMD_HELP_START(led) BAREBOX_CMD_HELP_TEXT("Control the value of a LED. The exact meaning of VALUE is unspecified,") BAREBOX_CMD_HELP_TEXT("it can be a brightness, or a color. Most often a value of '1' means on") -BAREBOX_CMD_HELP_TEXT("and '0' means off.") +BAREBOX_CMD_HELP_TEXT("and '0' means off. Basic usage is 'led <led> <value>'. LEDs can be given") +BAREBOX_CMD_HELP_TEXT("by name or number.") BAREBOX_CMD_HELP_TEXT("") BAREBOX_CMD_HELP_TEXT("Without arguments the available LEDs are listed.") +BAREBOX_CMD_HELP_TEXT("Options:") +BAREBOX_CMD_HELP_OPT ("-b <led> [duration-on-ms] [duration-off-ms]", "blink a LED") +BAREBOX_CMD_HELP_OPT ("-f <led> [duration-ms]", "flash a LED") BAREBOX_CMD_HELP_END BAREBOX_CMD_START(led) diff --git a/commands/nand-bitflip.c b/commands/nand-bitflip.c index fe56f222cf..a8a97c153a 100644 --- a/commands/nand-bitflip.c +++ b/commands/nand-bitflip.c @@ -69,6 +69,10 @@ static int do_nand_bitflip(int argc, char *argv[]) block += mtd_div_by_eb(offset, meminfo.mtd); offset = mtd_mod_by_eb(offset, meminfo.mtd); + if ((int)offset % meminfo.mtd->writesize) { + printf("offset has to be pagesize aligned\n"); + return 1; + } if (!check) { ret = mtd_peb_create_bitflips(meminfo.mtd, block, offset, meminfo.writesize, diff --git a/commands/poweroff.c b/commands/poweroff.c index e8c726a7f2..bbafa13bd0 100644 --- a/commands/poweroff.c +++ b/commands/poweroff.c @@ -19,10 +19,11 @@ #include <common.h> #include <command.h> +#include <poweroff.h> static int cmd_poweroff(int argc, char *argv[]) { - poweroff(); + poweroff_machine(); /* Not reached */ return 1; diff --git a/commands/seed.c b/commands/seed.c new file mode 100644 index 0000000000..e378cd7635 --- /dev/null +++ b/commands/seed.c @@ -0,0 +1,44 @@ +/* + * (c) 2017 Oleksij Rempel <kernel@pengutronix.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <common.h> +#include <command.h> +#include <stdlib.h> +#include <linux/ctype.h> + +static int do_seed(int argc, char *argv[]) +{ + if (argc < 2) + return COMMAND_ERROR_USAGE; + + if (isdigit(*argv[1])) { + srand(simple_strtoul(argv[1], NULL, 0)); + return 0; + } + + printf("numerical parameter expected\n"); + return 1; +} + +BAREBOX_CMD_HELP_START(seed) +BAREBOX_CMD_HELP_TEXT("Seed the pseudo random number generator") +BAREBOX_CMD_HELP_END + +BAREBOX_CMD_START(seed) + .cmd = do_seed, + BAREBOX_CMD_DESC("seed the PRNG") + BAREBOX_CMD_OPTS("VALUE") + BAREBOX_CMD_GROUP(CMD_GRP_MISC) + BAREBOX_CMD_HELP(cmd_seed_help) +BAREBOX_CMD_END diff --git a/commands/state.c b/commands/state.c index 4b51759e3e..c57a906ff0 100644 --- a/commands/state.c +++ b/commands/state.c @@ -21,20 +21,27 @@ static int do_state(int argc, char *argv[]) { int opt, ret = 0; struct state *state = NULL; - int do_save = 0; + int do_save = 0, do_load = 0; const char *statename = "state"; + int no_auth = 0; - while ((opt = getopt(argc, argv, "s")) > 0) { + while ((opt = getopt(argc, argv, "sln")) > 0) { switch (opt) { case 's': do_save = 1; break; + case 'l': + do_load = 1; + break; + case 'n': + no_auth = 1; + break; default: return COMMAND_ERROR_USAGE; } } - if (!do_save) { + if (!do_save && !do_load) { state_info(); return 0; } @@ -48,8 +55,14 @@ static int do_state(int argc, char *argv[]) return -ENOENT; } - if (do_save) + if (do_load) { + if (no_auth) + ret = state_load_no_auth(state); + else + ret = state_load(state); + } else if (do_save) { ret = state_save(state); + } return ret; } diff --git a/commands/stddev.c b/commands/stddev.c index 318d057417..93da2c7398 100644 --- a/commands/stddev.c +++ b/commands/stddev.c @@ -17,6 +17,7 @@ #include <common.h> #include <init.h> +#include <stdlib.h> static ssize_t zero_read(struct cdev *cdev, void *buf, size_t count, loff_t offset, ulong flags) { @@ -100,3 +101,31 @@ static int null_init(void) } device_initcall(null_init); + +static ssize_t prng_read(struct cdev *cdev, void *buf, size_t count, loff_t offset, ulong flags) +{ + get_random_bytes(buf, count); + return count; +} + +static struct file_operations prngops = { + .read = prng_read, + .lseek = dev_lseek_default, +}; + +static int prng_init(void) +{ + struct cdev *cdev; + + cdev = xzalloc(sizeof (*cdev)); + + cdev->name = "prng"; + cdev->flags = DEVFS_IS_CHARACTER_DEV; + cdev->ops = &prngops; + + devfs_create(cdev); + + return 0; +} + +device_initcall(prng_init); diff --git a/commands/trigger.c b/commands/trigger.c index 2758ce74e8..0dd3b346fa 100644 --- a/commands/trigger.c +++ b/commands/trigger.c @@ -28,59 +28,62 @@ #define LED_COMMAND_SHOW_INFO 2 #define LED_COMMAND_DISABLE_TRIGGER 3 -static char *trigger_names[] = { - [LED_TRIGGER_PANIC] = "panic", - [LED_TRIGGER_HEARTBEAT] = "heartbeat", - [LED_TRIGGER_NET_RX] = "net rx", - [LED_TRIGGER_NET_TX] = "net tx", - [LED_TRIGGER_NET_TXRX] = "net", - [LED_TRIGGER_DEFAULT_ON] = "default on", -}; static int do_trigger(int argc, char *argv[]) { - struct led *led; - int i, opt, ret = 0; + struct led *led = NULL; + int opt, ret = 0; int cmd = LED_COMMAND_SHOW_INFO; - unsigned long trigger = 0; + enum led_trigger trigger; + const char *led_name = NULL; + const char *trigger_name = NULL; while((opt = getopt(argc, argv, "t:d:")) > 0) { switch(opt) { case 't': - trigger = simple_strtoul(optarg, NULL, 0); + trigger_name = optarg; cmd = LED_COMMAND_SET_TRIGGER; break; case 'd': - trigger = simple_strtoul(optarg, NULL, 0); + led_name = optarg; cmd = LED_COMMAND_DISABLE_TRIGGER; } } + if (optind < argc) + led = led_by_name_or_number(argv[optind]); + switch (cmd) { case LED_COMMAND_SHOW_INFO: - for (i = 0; i < LED_TRIGGER_MAX; i++) { - int led = led_get_trigger(i); - printf("%d: %s", i, trigger_names[i]); - if (led >= 0) - printf(" (led %d)", led); - printf("\n"); - } + led_triggers_show_info(); break; case LED_COMMAND_DISABLE_TRIGGER: - ret = led_set_trigger(trigger, NULL); + led = led_by_name_or_number(led_name); + if (!led) { + printf("no such led: %s\n", led_name); + return 1; + } + + led_trigger_disable(led); break; case LED_COMMAND_SET_TRIGGER: if (argc - optind != 1) return COMMAND_ERROR_USAGE; - led = led_by_name_or_number(argv[optind]); + led = led_by_name_or_number(argv[optind]); if (!led) { printf("no such led: %s\n", argv[optind]); return 1; } + trigger = trigger_by_name(trigger_name); + if (trigger == LED_TRIGGER_INVALID) { + printf("no such trigger: %s\n", trigger_name); + return 1; + } + ret = led_set_trigger(trigger, led); break; } @@ -92,16 +95,17 @@ static int do_trigger(int argc, char *argv[]) BAREBOX_CMD_HELP_START(trigger) BAREBOX_CMD_HELP_TEXT("Control a LED trigger. Without options assigned triggers are shown.") +BAREBOX_CMD_HELP_TEXT("triggers are given with their name, LEDs are given with their name or number") BAREBOX_CMD_HELP_TEXT("") BAREBOX_CMD_HELP_TEXT("Options:") -BAREBOX_CMD_HELP_OPT ("-t", "set a trigger (needs LED argument)") -BAREBOX_CMD_HELP_OPT ("-d", "disable a trigger") +BAREBOX_CMD_HELP_OPT ("-t <trigger> <led>", "set a trigger") +BAREBOX_CMD_HELP_OPT ("-d <led>", "disable a trigger") BAREBOX_CMD_HELP_END BAREBOX_CMD_START(trigger) .cmd = do_trigger, BAREBOX_CMD_DESC("handle LED triggers") - BAREBOX_CMD_OPTS("[-td] TRIGGER [LED]") + BAREBOX_CMD_OPTS("[-td] [LED]") BAREBOX_CMD_GROUP(CMD_GRP_HWMANIP) BAREBOX_CMD_HELP(cmd_trigger_help) BAREBOX_CMD_END diff --git a/commands/usbgadget.c b/commands/usbgadget.c index 314884aee8..02c2c96b02 100644 --- a/commands/usbgadget.c +++ b/commands/usbgadget.c @@ -20,6 +20,7 @@ */ #include <common.h> #include <command.h> +#include <environment.h> #include <errno.h> #include <malloc.h> #include <getopt.h> @@ -32,11 +33,11 @@ static int do_usbgadget(int argc, char *argv[]) { int opt, ret; - int acm = 1, create_serial = 0; - char *fastboot_opts = NULL, *dfu_opts = NULL; + int acm = 1, create_serial = 0, fastboot_set = 0; + const char *fastboot_opts = NULL, *dfu_opts = NULL; struct f_multi_opts *opts; - while ((opt = getopt(argc, argv, "asdA:D:")) > 0) { + while ((opt = getopt(argc, argv, "asdA::D:")) > 0) { switch (opt) { case 'a': acm = 1; @@ -51,6 +52,7 @@ static int do_usbgadget(int argc, char *argv[]) break; case 'A': fastboot_opts = optarg; + fastboot_set = 1; break; case 'd': usb_multi_unregister(); @@ -60,6 +62,9 @@ static int do_usbgadget(int argc, char *argv[]) } } + if (fastboot_set && !fastboot_opts) + fastboot_opts = getenv("global.usbgadget.fastboot_function"); + if (!dfu_opts && !fastboot_opts && !create_serial) return COMMAND_ERROR_USAGE; diff --git a/common/Makefile b/common/Makefile index 5f58c81d22..8cd0ab3001 100644 --- a/common/Makefile +++ b/common/Makefile @@ -9,6 +9,7 @@ obj-pbl-y += memsize.o obj-y += resource.o obj-y += bootsource.o obj-y += restart.o +obj-y += poweroff.o obj-$(CONFIG_AUTO_COMPLETE) += complete.o obj-$(CONFIG_BANNER) += version.o obj-$(CONFIG_BAREBOX_UPDATE) += bbu.o diff --git a/common/blspec.c b/common/blspec.c index ec63ddb407..8132d141ab 100644 --- a/common/blspec.c +++ b/common/blspec.c @@ -732,3 +732,27 @@ int blspec_scan_devicename(struct bootentries *bootentries, const char *devname) return blspec_scan_device(bootentries, dev); } + +static int blspec_bootentry_provider(struct bootentries *bootentries, + const char *name) +{ + int ret, found = 0; + + ret = blspec_scan_devicename(bootentries, name); + if (ret > 0) + found += ret; + + if (*name == '/' || !strncmp(name, "nfs://", 6)) { + ret = blspec_scan_directory(bootentries, name); + if (ret > 0) + found += ret; + } + + return found; +} + +static int blspec_init(void) +{ + return bootentry_register_provider(blspec_bootentry_provider); +} +device_initcall(blspec_init);
\ No newline at end of file diff --git a/common/boot.c b/common/boot.c index 4306319331..cef3d5e514 100644 --- a/common/boot.c +++ b/common/boot.c @@ -244,6 +244,25 @@ static int bootscript_scan_path(struct bootentries *bootentries, const char *pat return ret; } +static LIST_HEAD(bootentry_providers); + +struct bootentry_provider { + int (*fn)(struct bootentries *bootentries, const char *name); + struct list_head list; +}; + +int bootentry_register_provider(int (*fn)(struct bootentries *bootentries, const char *name)) +{ + struct bootentry_provider *p; + + p = xzalloc(sizeof(*p)); + p->fn = fn; + + list_add_tail(&p->list, &bootentry_providers); + + return 0; +} + /* * bootentry_create_from_name - create boot entries from a name * @@ -261,22 +280,11 @@ static int bootscript_scan_path(struct bootentries *bootentries, const char *pat int bootentry_create_from_name(struct bootentries *bootentries, const char *name) { + struct bootentry_provider *p; int found = 0, ret; - if (IS_ENABLED(CONFIG_BLSPEC)) { - ret = blspec_scan_devicename(bootentries, name); - if (ret > 0) - found += ret; - - if (*name == '/' || !strncmp(name, "nfs://", 6)) { - ret = blspec_scan_directory(bootentries, name); - if (ret > 0) - found += ret; - } - } - - if (IS_ENABLED(CONFIG_BOOTCHOOSER) && !strcmp(name, "bootchooser")) { - ret = bootchooser_create_bootentry(bootentries); + list_for_each_entry(p, &bootentry_providers, list) { + ret = p->fn(bootentries, name); if (ret > 0) found += ret; } diff --git a/common/bootchooser.c b/common/bootchooser.c index 9c110f267e..455f290fa2 100644 --- a/common/bootchooser.c +++ b/common/bootchooser.c @@ -827,15 +827,10 @@ out: return ret; } -static int bootchooser_boot(struct bootentry *entry, int verbose, int dryrun) +int bootchooser_boot(struct bootchooser *bc) { - struct bootchooser *bc = container_of(entry, struct bootchooser, - entry); int ret, tryagain; - bc->verbose = verbose; - bc->dryrun = dryrun; - do { ret = bootchooser_boot_one(bc, &tryagain); @@ -846,6 +841,16 @@ static int bootchooser_boot(struct bootentry *entry, int verbose, int dryrun) return ret; } +static int bootchooser_entry_boot(struct bootentry *entry, int verbose, int dryrun) +{ + struct bootchooser *bc = container_of(entry, struct bootchooser, + entry); + bc->verbose = verbose; + bc->dryrun = dryrun; + + return bootchooser_boot(bc); +} + static void bootchooser_release(struct bootentry *entry) { struct bootchooser *bc = container_of(entry, struct bootchooser, @@ -863,14 +868,18 @@ static void bootchooser_release(struct bootentry *entry) * * Return: The number of entries added to the list */ -int bootchooser_create_bootentry(struct bootentries *entries) +static int bootchooser_add_entry(struct bootentries *entries, const char *name) { - struct bootchooser *bc = bootchooser_get(); + struct bootchooser *bc; + + if (strcmp(name, "bootchooser")) + return 0; + bc = bootchooser_get(); if (IS_ERR(bc)) return PTR_ERR(bc); - bc->entry.boot = bootchooser_boot; + bc->entry.boot = bootchooser_entry_boot; bc->entry.release = bootchooser_release; bc->entry.title = xstrdup("bootchooser"); bc->entry.description = xstrdup("bootchooser"); @@ -904,6 +913,9 @@ static int bootchooser_init(void) reset_attempts_names, ARRAY_SIZE(reset_attempts_names)); globalvar_add_simple_bitmask("bootchooser.reset_priorities", &reset_priorities, reset_priorities_names, ARRAY_SIZE(reset_priorities_names)); + + bootentry_register_provider(bootchooser_add_entry); + return 0; } device_initcall(bootchooser_init); diff --git a/common/globalvar.c b/common/globalvar.c index 52808f8852..32ca6a24db 100644 --- a/common/globalvar.c +++ b/common/globalvar.c @@ -29,22 +29,6 @@ void nv_var_set_clean(void) nv_dirty = 0; } -int globalvar_add(const char *name, - int (*set)(struct device_d *dev, struct param_d *p, const char *val), - const char *(*get)(struct device_d *, struct param_d *p), - unsigned long flags) -{ - struct param_d *param; - - if (!strncmp(name, "global.", 7)) - name += 7; - - param = dev_add_param(&global_device, name, set, get, flags); - if (IS_ERR(param)) - return PTR_ERR(param); - return 0; -} - void globalvar_remove(const char *name) { struct param_d *p, *tmp; @@ -181,14 +165,18 @@ static int nvvar_device_dispatch(const char *name, struct device_d **dev, static int nv_set(struct device_d *dev, struct param_d *p, const char *val) { + struct param_d *g; int ret; if (!val) val = ""; - ret = dev_set_param(&global_device, p->name, val); - if (ret) - return ret; + g = get_param_by_name(&global_device, p->name); + if (g) { + ret = dev_set_param(&global_device, p->name, val); + if (ret) + return ret; + } free(p->value); p->value = xstrdup(val); @@ -215,7 +203,6 @@ static int nv_param_set(struct device_d *dev, struct param_d *p, const char *val static int __nvvar_add(const char *name, const char *value) { struct param_d *p; - int ret; if (!IS_ENABLED(CONFIG_NVVAR)) return -ENOSYS; @@ -228,15 +215,16 @@ static int __nvvar_add(const char *name, const char *value) return PTR_ERR(p); } - /* Create corresponding globalvar if it doesn't exist yet */ - ret = globalvar_add_simple(name, value); - if (ret && ret != -EEXIST) - return ret; + if (value) + return nv_set(&nv_device, p, value); - if (!value) - value = dev_get_param(&global_device, name); + value = dev_get_param(&global_device, name); + if (value) { + free(p->value); + p->value = xstrdup(value); + } - return nv_set(&nv_device, p, value); + return 0; } int nvvar_add(const char *name, const char *value) @@ -321,7 +309,10 @@ static void device_param_print(struct device_d *dev) if (IS_ENABLED(CONFIG_NVVAR) && dev != &nv_device) nv = dev_get_param(&nv_device, param->name); - printf("%s%s: %s\n", nv ? "* " : " ", param->name, p); + printf("%s%s: %s", nv ? "* " : " ", param->name, p); + if (param->info) + param->info(param); + printf("\n"); } } @@ -400,6 +391,15 @@ static int globalvar_simple_set(struct device_d *dev, struct param_d *p, const c return dev_param_set_generic(dev, p, val); } +static void globalvar_nv_sync(const char *name) +{ + const char *val; + + val = dev_get_param(&nv_device, name); + if (val) + dev_set_param(&global_device, name, val); +} + /* * globalvar_add_simple * @@ -410,51 +410,23 @@ int globalvar_add_simple(const char *name, const char *value) struct param_d *param; param = dev_add_param(&global_device, name, globalvar_simple_set, NULL, - PARAM_GLOBALVAR_UNQUALIFIED); + 0); if (IS_ERR(param)) { if (PTR_ERR(param) != -EEXIST) return PTR_ERR(param); } - if (!value) - return 0; - - return dev_set_param(&global_device, name, value); -} - -static int globalvar_remove_unqualified(const char *name) -{ - struct param_d *p; - - p = get_param_by_name(&global_device, name); - if (!p) - return 0; + if (value) + dev_set_param(&global_device, name, value); - if (!(p->flags & PARAM_GLOBALVAR_UNQUALIFIED)) - return -EEXIST; - - dev_remove_param(p); + globalvar_nv_sync(name); return 0; } -static void globalvar_nv_sync(const char *name) -{ - const char *val; - - val = dev_get_param(&nv_device, name); - if (val) - dev_set_param(&global_device, name, val); -} - int globalvar_add_simple_string(const char *name, char **value) { struct param_d *p; - int ret; - - ret = globalvar_remove_unqualified(name); - if (ret) - return ret; p = dev_add_param_string(&global_device, name, NULL, NULL, value, NULL); @@ -471,11 +443,6 @@ int globalvar_add_simple_int(const char *name, int *value, const char *format) { struct param_d *p; - int ret; - - ret = globalvar_remove_unqualified(name); - if (ret) - return ret; p = dev_add_param_int(&global_device, name, NULL, NULL, value, format, NULL); @@ -491,11 +458,6 @@ int globalvar_add_simple_int(const char *name, int *value, int globalvar_add_simple_bool(const char *name, int *value) { struct param_d *p; - int ret; - - ret = globalvar_remove_unqualified(name); - if (ret) - return ret; p = dev_add_param_bool(&global_device, name, NULL, NULL, value, NULL); @@ -512,11 +474,6 @@ int globalvar_add_simple_enum(const char *name, int *value, const char * const *names, int max) { struct param_d *p; - int ret; - - ret = globalvar_remove_unqualified(name); - if (ret) - return ret; p = dev_add_param_enum(&global_device, name, NULL, NULL, value, names, max, NULL); @@ -546,11 +503,6 @@ int globalvar_add_simple_bitmask(const char *name, unsigned long *value, int globalvar_add_simple_ip(const char *name, IPaddr_t *ip) { struct param_d *p; - int ret; - - ret = globalvar_remove_unqualified(name); - if (ret) - return ret; p = dev_add_param_ip(&global_device, name, NULL, NULL, ip, NULL); diff --git a/common/image-fit.c b/common/image-fit.c index 6a01c614cc..81433e6ecf 100644 --- a/common/image-fit.c +++ b/common/image-fit.c @@ -254,6 +254,8 @@ static int fit_verify_signature(struct device_node *sig_node, void *fit) } if (strcmp(algo_name, "sha1,rsa2048") == 0) { algo = HASH_ALGO_SHA1; + } else if (strcmp(algo_name, "sha256,rsa2048") == 0) { + algo = HASH_ALGO_SHA256; } else if (strcmp(algo_name, "sha256,rsa4096") == 0) { algo = HASH_ALGO_SHA256; } else { @@ -489,6 +491,38 @@ static int fit_config_verify_signature(struct fit_handle *handle, struct device_ return ret; } +static int fit_find_compatible_unit(struct device_node *conf_node, + const char **unit) +{ + struct device_node *child = NULL; + struct device_node *barebox_root; + const char *machine; + int ret; + + barebox_root = of_get_root_node(); + if (!barebox_root) + goto default_unit; + + ret = of_property_read_string(barebox_root, "compatible", &machine); + if (ret) + return -ENOENT; + + for_each_child_of_node(conf_node, child) { + if (of_device_is_compatible(child, machine)) { + *unit = child->name; + pr_info("matching unit '%s' found\n", *unit); + return 0; + } + } + +default_unit: + pr_info("No match found. Trying default.\n"); + if (of_property_read_string(conf_node, "default", unit) == 0) + return 0; + + return -ENOENT; +} + static int fit_open_configuration(struct fit_handle *handle, const char *name) { struct device_node *conf_node = NULL; @@ -501,8 +535,12 @@ static int fit_open_configuration(struct fit_handle *handle, const char *name) if (name) { unit = name; - } else if (of_property_read_string(conf_node, "default", &unit)) { - unit = "conf@1"; + } else { + ret = fit_find_compatible_unit(conf_node, &unit); + if (ret) { + pr_info("Couldn't get a valid configuration. Aborting.\n"); + return ret; + } } conf_node = of_get_child_by_name(conf_node, unit); diff --git a/common/memory.c b/common/memory.c index ad38b00ecb..ff5bdc14e2 100644 --- a/common/memory.c +++ b/common/memory.c @@ -185,7 +185,7 @@ static int of_memory_fixup(struct device_node *node, void *unused) if (!memnode) return -ENOMEM; - err = of_set_property(memnode, "device_type", "memory", sizeof("memory"), 1); + err = of_property_write_string(memnode, "device_type", "memory"); if (err) return err; diff --git a/common/oftree.c b/common/oftree.c index e98b908738..09a4455a9e 100644 --- a/common/oftree.c +++ b/common/oftree.c @@ -128,7 +128,7 @@ static int of_fixup_bootargs(struct device_node *root, void *unused) return -ENOMEM; - err = of_set_property(node, "bootargs", str, strlen(str) + 1, 1); + err = of_property_write_string(node, "bootargs", str); return err; } diff --git a/common/partitions.c b/common/partitions.c index 69a2b1fefb..574b31fbbe 100644 --- a/common/partitions.c +++ b/common/partitions.c @@ -63,6 +63,8 @@ static int register_one_partition(struct block_device *blk, goto out; } + cdev->flags |= DEVFS_PARTITION_FROM_TABLE; + cdev->dos_partition_type = part->dos_partition_type; strcpy(cdev->partuuid, part->partuuid); diff --git a/common/password.c b/common/password.c index d52b746f0f..74d328f4b2 100644 --- a/common/password.c +++ b/common/password.c @@ -365,7 +365,11 @@ int set_env_passwd(unsigned char* passwd, size_t length) char *salt = passwd_sum; int keylen = PBKDF2_LENGTH - PBKDF2_SALT_LEN; - get_random_bytes(passwd_sum, PBKDF2_SALT_LEN); + ret = get_crypto_bytes(passwd_sum, PBKDF2_SALT_LEN); + if (ret) { + pr_err("Can't get random numbers\n"); + return ret; + } ret = pkcs5_pbkdf2_hmac_sha1(passwd, length, salt, PBKDF2_SALT_LEN, PBKDF2_COUNT, keylen, key); diff --git a/common/poweroff.c b/common/poweroff.c new file mode 100644 index 0000000000..32a78280d3 --- /dev/null +++ b/common/poweroff.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2015 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#define pr_fmt(fmt) "poweroff: " fmt + +#include <common.h> +#include <poweroff.h> +#include <malloc.h> +#include <of.h> + +static LIST_HEAD(poweroff_handler_list); + +/** + * poweroff_handler_register() - register a handler for poweroffing the system + * @rst: The handler struct + * + * This adds @rst to the list of registered poweroff handlers. + * + * return: 0 for success or negative error code + */ +int poweroff_handler_register(struct poweroff_handler *handler) +{ + if (!handler->name) + handler->name = POWEROFF_DEFAULT_NAME; + if (!handler->priority) + handler->priority = POWEROFF_DEFAULT_PRIORITY; + + list_add_tail(&handler->list, &poweroff_handler_list); + + pr_debug("registering poweroff handler \"%s\" with priority %d\n", + handler->name, handler->priority); + + return 0; +} + +/** + * poweroff_handler_register_fn() - register a handler function + * @poweroff_fn: The poweroff function + * + * convenience wrapper for poweroff_handler_register() to register a handler + * with given function and default values otherwise. + * + * return: 0 for success or negative error code + */ +int poweroff_handler_register_fn(void (*poweroff_fn)(struct poweroff_handler *)) +{ + struct poweroff_handler *handler; + int ret; + + handler = xzalloc(sizeof(*handler)); + + handler->poweroff = poweroff_fn; + + ret = poweroff_handler_register(handler); + + if (ret) + free(handler); + + return ret; +} + +/** + * poweroff_machine() - power off the machine + */ +void __noreturn poweroff_machine(void) +{ + struct poweroff_handler *handler = NULL, *tmp; + unsigned int priority = 0; + + list_for_each_entry(tmp, &poweroff_handler_list, list) { + if (tmp->priority > priority) { + priority = tmp->priority; + handler = tmp; + } + } + + if (handler) { + pr_debug("using poweroff handler %s\n", handler->name); + console_flush(); + handler->poweroff(handler); + } else { + pr_err("No poweroff handler found!\n"); + } + + hang(); +} + +/** + * of_get_poweroff_priority() - get the desired poweroff priority from device tree + * @node: The device_node to read the property from + * + * return: The priority + */ +unsigned int of_get_poweroff_priority(struct device_node *node) +{ + unsigned int priority = POWEROFF_DEFAULT_PRIORITY; + + of_property_read_u32(node, "poweroff-priority", &priority); + + return priority; +} diff --git a/common/state/Makefile b/common/state/Makefile index 3e0e2c6e55..fcf9add52c 100644 --- a/common/state/Makefile +++ b/common/state/Makefile @@ -1,9 +1,7 @@ obj-y += state.o obj-y += state_variables.o -obj-y += backend.o obj-y += backend_format_dtb.o obj-y += backend_format_raw.o obj-y += backend_storage.o obj-y += backend_bucket_direct.o obj-$(CONFIG_MTD) += backend_bucket_circular.o -obj-y += backend_bucket_cached.o diff --git a/common/state/backend.c b/common/state/backend.c deleted file mode 100644 index 5235bb0283..0000000000 --- a/common/state/backend.c +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) 2016 Pengutronix, Markus Pargmann <mpa@pengutronix.de> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include <linux/kernel.h> -#include <linux/list.h> -#include <linux/string.h> -#include <malloc.h> -#include <printk.h> - -#include "state.h" - - -/** - * Save the state - * @param state - * @return - */ -int state_save(struct state *state) -{ - uint8_t *buf; - ssize_t len; - int ret; - struct state_backend *backend = &state->backend; - - if (!state->dirty) - return 0; - - ret = backend->format->pack(backend->format, state, &buf, &len); - if (ret) { - dev_err(&state->dev, "Failed to pack state with backend format %s, %d\n", - backend->format->name, ret); - return ret; - } - - ret = state_storage_write(&backend->storage, buf, len); - if (ret) { - dev_err(&state->dev, "Failed to write packed state, %d\n", ret); - goto out; - } - - state->dirty = 0; - -out: - free(buf); - return ret; -} - -/** - * state_load - Loads a state from the backend - * @param state The state that should be updated to contain the loaded data - * @return 0 on success, -errno on failure. If no state is loaded the previous - * values remain in the state. - * - * This function uses the registered storage backend to read data. All data that - * we read is checked for integrity by the formatter. After that we unpack the - * data into our state. - */ -int state_load(struct state *state) -{ - uint8_t *buf; - ssize_t len; - ssize_t len_hint = 0; - int ret; - struct state_backend *backend = &state->backend; - - if (backend->format->get_packed_len) - len_hint = backend->format->get_packed_len(backend->format, - state); - ret = state_storage_read(&backend->storage, backend->format, - state->magic, &buf, &len, len_hint); - if (ret) { - dev_err(&state->dev, "Failed to read state with format %s, %d\n", - backend->format->name, ret); - return ret; - } - - ret = backend->format->unpack(backend->format, state, buf, len); - if (ret) { - dev_err(&state->dev, "Failed to unpack read data with format %s although verified, %d\n", - backend->format->name, ret); - goto out; - } - - state->dirty = 0; - -out: - free(buf); - return ret; -} - -static int state_format_init(struct state_backend *backend, - struct device_d *dev, const char *backend_format, - struct device_node *node, const char *state_name) -{ - int ret; - - if (!strcmp(backend_format, "raw")) { - ret = backend_format_raw_create(&backend->format, node, - state_name, dev); - } else if (!strcmp(backend_format, "dtb")) { - ret = backend_format_dtb_create(&backend->format, dev); - } else { - dev_err(dev, "Invalid backend format %s\n", - backend_format); - return -EINVAL; - } - - if (ret && ret != -EPROBE_DEFER) - dev_err(dev, "Failed to initialize format %s, %d\n", - backend_format, ret); - - return ret; -} - -static void state_format_free(struct state_backend_format *format) -{ - if (format->free) - format->free(format); -} - -/** - * state_backend_init - Initiates the backend storage and format using the - * passed arguments - * @param backend state backend - * @param dev Device pointer used for prints - * @param node the DT device node corresponding to the state - * @param backend_format a string describing the format. Valid values are 'raw' - * and 'dtb' currently - * @param storage_path Path to the backend storage file/device/partition/... - * @param state_name Name of the state - * @param of_path Path in the devicetree - * @param stridesize stridesize in case we have a medium without eraseblocks. - * stridesize describes how far apart copies of the same data should be stored. - * For blockdevices it makes sense to align them on blocksize. - * @param storagetype Type of the storage backend. This may be NULL where we - * autoselect some backwardscompatible backend options - * @return 0 on success, -errno otherwise - */ -int state_backend_init(struct state_backend *backend, struct device_d *dev, - struct device_node *node, const char *backend_format, - const char *storage_path, const char *state_name, const - char *of_path, off_t offset, size_t max_size, - uint32_t stridesize, const char *storagetype) -{ - int ret; - - ret = state_format_init(backend, dev, backend_format, node, state_name); - if (ret) - return ret; - - ret = state_storage_init(&backend->storage, dev, storage_path, offset, - max_size, stridesize, storagetype); - if (ret) - goto out_free_format; - - backend->of_path = xstrdup(of_path); - - return 0; - -out_free_format: - state_format_free(backend->format); - backend->format = NULL; - - return ret; -} - -void state_backend_set_readonly(struct state_backend *backend) -{ - state_storage_set_readonly(&backend->storage); -} - -void state_backend_free(struct state_backend *backend) -{ - state_storage_free(&backend->storage); - if (backend->format) - state_format_free(backend->format); - free(backend->of_path); -} diff --git a/common/state/backend_bucket_cached.c b/common/state/backend_bucket_cached.c deleted file mode 100644 index ba0af7f373..0000000000 --- a/common/state/backend_bucket_cached.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (C) 2016 Pengutronix, Markus Pargmann <mpa@pengutronix.de> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include <common.h> -#include "state.h" - -struct state_backend_storage_bucket_cache { - struct state_backend_storage_bucket bucket; - - struct state_backend_storage_bucket *raw; - - u8 *data; - ssize_t data_len; - bool force_write; - - /* For outputs */ - struct device_d *dev; -}; - -static inline struct state_backend_storage_bucket_cache - *get_bucket_cache(struct state_backend_storage_bucket *bucket) -{ - return container_of(bucket, - struct state_backend_storage_bucket_cache, - bucket); -} - -static inline void state_backend_bucket_cache_drop( - struct state_backend_storage_bucket_cache *cache) -{ - if (cache->data) { - free(cache->data); - cache->data = NULL; - cache->data_len = 0; - } -} - -static int state_backend_bucket_cache_fill( - struct state_backend_storage_bucket_cache *cache) -{ - int ret; - - ret = cache->raw->read(cache->raw, &cache->data, &cache->data_len); - if (ret == -EUCLEAN) { - cache->force_write = true; - ret = 0; - } - - return ret; -} - -static int state_backend_bucket_cache_read(struct state_backend_storage_bucket *bucket, - uint8_t ** buf_out, - ssize_t * len_hint) -{ - struct state_backend_storage_bucket_cache *cache = - get_bucket_cache(bucket); - int ret; - - if (!cache->data) { - ret = state_backend_bucket_cache_fill(cache); - if (ret) - return ret; - } - - if (cache->data) { - *buf_out = xmemdup(cache->data, cache->data_len); - if (!*buf_out) - return -ENOMEM; - *len_hint = cache->data_len; - } - - return 0; -} - -static int state_backend_bucket_cache_write(struct state_backend_storage_bucket *bucket, - const uint8_t * buf, ssize_t len) -{ - struct state_backend_storage_bucket_cache *cache = - get_bucket_cache(bucket); - int ret; - - if (!cache->force_write) { - if (!cache->data) - ret = state_backend_bucket_cache_fill(cache); - - if (cache->data_len == len && !memcmp(cache->data, buf, len)) - return 0; - } - - state_backend_bucket_cache_drop(cache); - - ret = cache->raw->write(cache->raw, buf, len); - if (ret) - return ret; - - cache->data = xmemdup(buf, len); - cache->data_len = len; - return 0; -} - -static int state_backend_bucket_cache_init( - struct state_backend_storage_bucket *bucket) -{ - struct state_backend_storage_bucket_cache *cache = - get_bucket_cache(bucket); - - if (cache->raw->init) { - return cache->raw->init(cache->raw); - } - - return 0; -} - -static void state_backend_bucket_cache_free( - struct state_backend_storage_bucket *bucket) -{ - struct state_backend_storage_bucket_cache *cache = - get_bucket_cache(bucket); - - state_backend_bucket_cache_drop(cache); - cache->raw->free(cache->raw); - free(cache); -} - -int state_backend_bucket_cached_create(struct device_d *dev, - struct state_backend_storage_bucket *raw, - struct state_backend_storage_bucket **out) -{ - struct state_backend_storage_bucket_cache *cache; - - cache = xzalloc(sizeof(*cache)); - cache->raw = raw; - cache->dev = dev; - - cache->bucket.free = state_backend_bucket_cache_free; - cache->bucket.read = state_backend_bucket_cache_read; - cache->bucket.write = state_backend_bucket_cache_write; - cache->bucket.init = state_backend_bucket_cache_init; - - *out = &cache->bucket; - - return 0; -} diff --git a/common/state/backend_bucket_circular.c b/common/state/backend_bucket_circular.c index 0bce900d0d..5279ec9ce2 100644 --- a/common/state/backend_bucket_circular.c +++ b/common/state/backend_bucket_circular.c @@ -25,7 +25,21 @@ #include "state.h" - +/* + * The circular backend bucket code. The circular backend bucket is intended + * for mtd devices which need an erase operation. + * + * Erasing blocks is an operation that should be avoided. On NOR flashes erasing + * blocks is very time consuming and on NAND flashes each block only has a limited + * number of erase cycles allowed. For this reason we continuously write more data + * into each eraseblock and only erase it when no more free space is available. + * Don't confuse these multiple writes into a single eraseblock with buckets. A bucket + * is the whole eraseblock, we just happen to reuse the same bucket for storing + * new data. + * + * If your device is a mtd device, but does not have eraseblocks, like MRAMs, then + * the direct bucket is used instead. + */ struct state_backend_storage_bucket_circular { struct state_backend_storage_bucket bucket; @@ -47,6 +61,11 @@ struct state_backend_storage_bucket_circular { struct device_d *dev; }; +/* + * The metadata will be written directly before writesize aligned offsets. + * When searching backwards through the pages it allows us to find the + * beginning of the data. + */ struct __attribute__((__packed__)) state_backend_storage_bucket_circular_meta { uint32_t magic; uint32_t written_length; @@ -65,7 +84,7 @@ static inline struct state_backend_storage_bucket_circular #ifdef __BAREBOX__ static int state_mtd_peb_read(struct state_backend_storage_bucket_circular *circ, - char *buf, int offset, int len) + void *buf, int offset, int len) { int ret; @@ -102,7 +121,7 @@ static int state_mtd_peb_read(struct state_backend_storage_bucket_circular *circ } static int state_mtd_peb_write(struct state_backend_storage_bucket_circular *circ, - const char *buf, int offset, int len) + const void *buf, int offset, int len) { int ret; @@ -133,7 +152,7 @@ static int state_mtd_peb_erase(struct state_backend_storage_bucket_circular *cir } #else static int state_mtd_peb_read(struct state_backend_storage_bucket_circular *circ, - char *buf, int suboffset, int len) + void *buf, int suboffset, int len) { int ret; off_t offset = suboffset; @@ -152,40 +171,19 @@ static int state_mtd_peb_read(struct state_backend_storage_bucket_circular *circ dev_dbg(circ->dev, "Read state from %ld length %zd\n", offset, len); - ret = ioctl(circ->fd, ECCGETSTATS, &stat1); - if (ret) - nostats = true; ret = read_full(circ->fd, buf, len); if (ret < 0) { dev_err(circ->dev, "Failed to read circular storage len %zd, %d\n", len, ret); free(buf); - return ret; - } - - if (nostats) - return 0; - - ret = ioctl(circ->fd, ECCGETSTATS, &stat2); - if (ret) - return 0; - - if (stat2.failed - stat1.failed > 0) { - ret = -EUCLEAN; - dev_dbg(circ->dev, "PEB %u has ECC error, forcing rewrite\n", - circ->eraseblock); - } else if (stat2.corrected - stat1.corrected > 0) { - ret = -EUCLEAN; - dev_dbg(circ->dev, "PEB %u is unclean, forcing rewrite\n", - circ->eraseblock); } return ret; } static int state_mtd_peb_write(struct state_backend_storage_bucket_circular *circ, - const char *buf, int suboffset, int len) + const void *buf, int suboffset, int len) { int ret; off_t offset = suboffset; @@ -226,14 +224,14 @@ static int state_mtd_peb_erase(struct state_backend_storage_bucket_circular *cir #endif static int state_backend_bucket_circular_read(struct state_backend_storage_bucket *bucket, - uint8_t ** buf_out, - ssize_t * len_hint) + void ** buf_out, + ssize_t * len_out) { struct state_backend_storage_bucket_circular *circ = get_bucket_circular(bucket); ssize_t read_len; off_t offset; - uint8_t *buf; + void *buf; int ret; /* Storage is empty */ @@ -282,13 +280,13 @@ static int state_backend_bucket_circular_read(struct state_backend_storage_bucke } *buf_out = buf; - *len_hint = read_len - sizeof(struct state_backend_storage_bucket_circular_meta); + *len_out = read_len - sizeof(struct state_backend_storage_bucket_circular_meta); return ret; } static int state_backend_bucket_circular_write(struct state_backend_storage_bucket *bucket, - const uint8_t * buf, + const void * buf, ssize_t len) { struct state_backend_storage_bucket_circular *circ = @@ -297,7 +295,7 @@ static int state_backend_bucket_circular_write(struct state_backend_storage_buck struct state_backend_storage_bucket_circular_meta *meta; uint32_t written_length = ALIGN(len + sizeof(*meta), circ->writesize); int ret; - uint8_t *write_buf; + void *write_buf; if (written_length > circ->max_size) { dev_err(circ->dev, "Error, state data too big to be written, to write: %zd, writesize: %zd, length: %zd, available: %zd\n", @@ -379,26 +377,24 @@ static int state_backend_bucket_circular_init( int sub_offset; uint32_t written_length = 0; uint8_t *buf; + int ret; - buf = xmalloc(circ->writesize); + buf = xmalloc(circ->max_size); if (!buf) return -ENOMEM; + ret = state_mtd_peb_read(circ, buf, 0, circ->max_size); + if (ret && ret != -EUCLEAN) + return ret; + for (sub_offset = circ->max_size - circ->writesize; sub_offset >= 0; sub_offset -= circ->writesize) { - int ret; - - ret = state_mtd_peb_read(circ, buf, sub_offset, - circ->writesize); - if (ret && ret != -EUCLEAN) - return ret; - - ret = mtd_buf_all_ff(buf, circ->writesize); + ret = mtd_buf_all_ff(buf + sub_offset, circ->writesize); if (!ret) { struct state_backend_storage_bucket_circular_meta *meta; meta = (struct state_backend_storage_bucket_circular_meta *) - (buf + circ->writesize - sizeof(*meta)); + (buf + sub_offset + circ->writesize - sizeof(*meta)); if (meta->magic != circular_magic) written_length = 0; @@ -458,12 +454,14 @@ int state_backend_bucket_circular_create(struct device_d *dev, const char *path, struct state_backend_storage_bucket **bucket, unsigned int eraseblock, ssize_t writesize, - struct mtd_info_user *mtd_uinfo, - bool lazy_init) + struct mtd_info_user *mtd_uinfo) { struct state_backend_storage_bucket_circular *circ; int ret; + if (writesize < 8) + writesize = 8; + circ = xzalloc(sizeof(*circ)); circ->eraseblock = eraseblock; circ->writesize = writesize; @@ -495,13 +493,9 @@ int state_backend_bucket_circular_create(struct device_d *dev, const char *path, circ->bucket.free = state_backend_bucket_circular_free; *bucket = &circ->bucket; - if (!lazy_init) { - ret = state_backend_bucket_circular_init(*bucket); - if (ret) - goto out_free; - } else { - circ->bucket.init = state_backend_bucket_circular_init; - } + ret = state_backend_bucket_circular_init(*bucket); + if (ret) + goto out_free; return 0; diff --git a/common/state/backend_bucket_direct.c b/common/state/backend_bucket_direct.c index 5225433ec5..4465ed0e41 100644 --- a/common/state/backend_bucket_direct.c +++ b/common/state/backend_bucket_direct.c @@ -46,14 +46,14 @@ static inline struct state_backend_storage_bucket_direct } static int state_backend_bucket_direct_read(struct state_backend_storage_bucket - *bucket, uint8_t ** buf_out, - ssize_t * len_hint) + *bucket, void ** buf_out, + ssize_t * len_out) { struct state_backend_storage_bucket_direct *direct = get_bucket_direct(bucket); struct state_backend_storage_bucket_direct_meta meta; ssize_t read_len; - uint8_t *buf; + void *buf; int ret; ret = lseek(direct->fd, direct->offset, SEEK_SET); @@ -69,18 +69,13 @@ static int state_backend_bucket_direct_read(struct state_backend_storage_bucket if (meta.magic == direct_magic) { read_len = meta.written_length; } else { - if (*len_hint) - read_len = *len_hint; - else - read_len = direct->max_size; + read_len = direct->max_size; ret = lseek(direct->fd, direct->offset, SEEK_SET); if (ret < 0) { dev_err(direct->dev, "Failed to seek file, %d\n", ret); return ret; } } - if (direct->max_size) - read_len = min(read_len, direct->max_size); buf = xmalloc(read_len); if (!buf) @@ -94,13 +89,13 @@ static int state_backend_bucket_direct_read(struct state_backend_storage_bucket } *buf_out = buf; - *len_hint = read_len; + *len_out = read_len; return 0; } static int state_backend_bucket_direct_write(struct state_backend_storage_bucket - *bucket, const uint8_t * buf, + *bucket, const void * buf, ssize_t len) { struct state_backend_storage_bucket_direct *direct = @@ -108,7 +103,7 @@ static int state_backend_bucket_direct_write(struct state_backend_storage_bucket int ret; struct state_backend_storage_bucket_direct_meta meta; - if (direct->max_size && len > direct->max_size) + if (len > direct->max_size - sizeof(meta)) return -E2BIG; ret = lseek(direct->fd, direct->offset, SEEK_SET); @@ -161,7 +156,6 @@ int state_backend_bucket_direct_create(struct device_d *dev, const char *path, fd = open(path, O_RDWR); if (fd < 0) { dev_err(dev, "Failed to open file '%s', %d\n", path, -errno); - close(fd); return -errno; } diff --git a/common/state/backend_format_dtb.c b/common/state/backend_format_dtb.c index dc19c888e5..55fa1fc597 100644 --- a/common/state/backend_format_dtb.c +++ b/common/state/backend_format_dtb.c @@ -39,13 +39,14 @@ static inline struct state_backend_format_dtb *get_format_dtb(struct } static int state_backend_format_dtb_verify(struct state_backend_format *format, - uint32_t magic, const uint8_t * buf, - ssize_t len) + uint32_t magic, const void * buf, + ssize_t *lenp, enum state_flags flags) { struct state_backend_format_dtb *fdtb = get_format_dtb(format); struct device_node *root; struct fdt_header *fdt = (struct fdt_header *)buf; size_t dtb_len = fdt32_to_cpu(fdt->totalsize); + size_t len = *lenp; if (dtb_len > len) { dev_err(fdtb->dev, "Error, stored DTB length (%d) longer than read buffer (%d)\n", @@ -67,18 +68,20 @@ static int state_backend_format_dtb_verify(struct state_backend_format *format, fdtb->root = root; + *lenp = be32_to_cpu(fdt->totalsize); + return 0; } static int state_backend_format_dtb_unpack(struct state_backend_format *format, struct state *state, - const uint8_t * buf, ssize_t len) + const void * buf, ssize_t len) { struct state_backend_format_dtb *fdtb = get_format_dtb(format); int ret; if (!fdtb->root) { - state_backend_format_dtb_verify(format, 0, buf, len); + state_backend_format_dtb_verify(format, 0, buf, &len, 0); } ret = state_from_node(state, fdtb->root, 0); @@ -89,7 +92,7 @@ static int state_backend_format_dtb_unpack(struct state_backend_format *format, } static int state_backend_format_dtb_pack(struct state_backend_format *format, - struct state *state, uint8_t ** buf, + struct state *state, void ** buf, ssize_t * len) { struct state_backend_format_dtb *fdtb = get_format_dtb(format); diff --git a/common/state/backend_format_raw.c b/common/state/backend_format_raw.c index e028ea616c..232856a209 100644 --- a/common/state/backend_format_raw.c +++ b/common/state/backend_format_raw.c @@ -35,6 +35,10 @@ struct state_backend_format_raw { /* For outputs */ struct device_d *dev; + + char *secret_name; + int needs_secret; + char *algo; }; struct __attribute__((__packed__)) backend_raw_header { @@ -53,15 +57,53 @@ static inline struct state_backend_format_raw *get_format_raw( return container_of(format, struct state_backend_format_raw, format); } +static int backend_raw_digest_init(struct state_backend_format_raw *raw) +{ + const unsigned char *key; + int key_len; + int ret; + + if (!raw->digest) { + raw->digest = digest_alloc(raw->algo); + if (!raw->digest) { + dev_err(raw->dev, "algo %s not found\n", + raw->algo); + return -ENODEV; + } + raw->digest_length = digest_length(raw->digest); + } + + ret = keystore_get_secret(raw->secret_name, &key, &key_len); + if (ret) { + dev_err(raw->dev, "Could not get secret '%s'\n", + raw->secret_name); + return ret; + } + + ret = digest_set_key(raw->digest, key, key_len); + if (ret) + return ret; + + ret = digest_init(raw->digest); + if (ret) { + dev_err(raw->dev, "Failed to initialize digest: %s\n", + strerror(-ret)); + return ret; + } + + return 0; +} + static int backend_format_raw_verify(struct state_backend_format *format, - uint32_t magic, const uint8_t * buf, - ssize_t len) + uint32_t magic, const void * buf, + ssize_t *lenp, enum state_flags flags) { uint32_t crc; struct backend_raw_header *header; int d_len = 0; int ret; - const uint8_t *data; + const void *data; + ssize_t len = *lenp; struct state_backend_format_raw *backend_raw = get_format_raw(format); ssize_t complete_len; @@ -85,7 +127,11 @@ static int backend_format_raw_verify(struct state_backend_format *format, return -EINVAL; } - if (backend_raw->digest) { + if (backend_raw->algo && !(flags & STATE_FLAG_NO_AUTHENTIFICATION)) { + ret = backend_raw_digest_init(backend_raw); + if (ret) + return ret; + d_len = digest_length(backend_raw->digest); } @@ -105,26 +151,20 @@ static int backend_format_raw_verify(struct state_backend_format *format, return -EINVAL; } - if (backend_raw->digest) { - struct digest *d = backend_raw->digest; - const void *hmac = data + header->data_len; + *lenp = header->data_len + sizeof(*header); - ret = digest_init(d); - if (ret) { - dev_err(backend_raw->dev, "Failed to initialize digest, %d\n", - ret); - return ret; - } + if (backend_raw->algo && !(flags & STATE_FLAG_NO_AUTHENTIFICATION)) { + const void *hmac = data + header->data_len; /* hmac over header and data */ - ret = digest_update(d, buf, sizeof(*header) + header->data_len); + ret = digest_update(backend_raw->digest, buf, sizeof(*header) + header->data_len); if (ret) { dev_err(backend_raw->dev, "Failed to update digest, %d\n", ret); return ret; } - ret = digest_verify(d, hmac); + ret = digest_verify(backend_raw->digest, hmac); if (ret < 0) { dev_err(backend_raw->dev, "Failed to verify data, hmac, %d\n", ret); @@ -136,12 +176,12 @@ static int backend_format_raw_verify(struct state_backend_format *format, } static int backend_format_raw_unpack(struct state_backend_format *format, - struct state *state, const uint8_t * buf, + struct state *state, const void * buf, ssize_t len) { struct state_variable *sv; const struct backend_raw_header *header; - const uint8_t *data; + const void *data; struct state_backend_format_raw *backend_raw = get_format_raw(format); header = (const struct backend_raw_header *)buf; @@ -160,7 +200,7 @@ static int backend_format_raw_unpack(struct state_backend_format *format, } static int backend_format_raw_pack(struct state_backend_format *format, - struct state *state, uint8_t ** buf_out, + struct state *state, void ** buf_out, ssize_t * len_out) { struct state_backend_format_raw *backend_raw = get_format_raw(format); @@ -192,25 +232,20 @@ static int backend_format_raw_pack(struct state_backend_format *format, header->header_crc = crc32(0, header, sizeof(*header) - sizeof(uint32_t)); - if (backend_raw->digest) { - struct digest *d = backend_raw->digest; - - ret = digest_init(d); - if (ret) { - dev_err(backend_raw->dev, "Failed to initialize digest for packing, %d\n", - ret); - goto out_free; - } + if (backend_raw->algo) { + ret = backend_raw_digest_init(backend_raw); + if (ret) + return ret; /* hmac over header and data */ - ret = digest_update(d, buf, sizeof(*header) + size_data); + ret = digest_update(backend_raw->digest, buf, sizeof(*header) + size_data); if (ret) { dev_err(backend_raw->dev, "Failed to update digest for packing, %d\n", ret); goto out_free; } - ret = digest_final(d, hmac); + ret = digest_final(backend_raw->digest, hmac); if (ret < 0) { dev_err(backend_raw->dev, "Failed to finish digest for packing, %d\n", ret); @@ -240,11 +275,9 @@ static int backend_format_raw_init_digest(struct state_backend_format_raw *raw, struct device_node *root, const char *secret_name) { - struct digest *digest; struct property *p; const char *algo; - const unsigned char *key; - int key_len, ret; + int ret; p = of_find_property(root, "algo", NULL); if (!p) /* does not exist */ @@ -260,30 +293,7 @@ static int backend_format_raw_init_digest(struct state_backend_format_raw *raw, return -EINVAL; } - ret = keystore_get_secret(secret_name, &key, &key_len); - if (ret == -ENOENT) { /* -ENOENT == does not exist */ - dev_info(raw->dev, "Could not get secret '%s' - probe deferred\n", - secret_name); - return -EPROBE_DEFER; - } else if (ret) { - return ret; - } - - digest = digest_alloc(algo); - if (!digest) { - dev_info(raw->dev, "algo %s not found - probe deferred\n", - algo); - return -EPROBE_DEFER; - } - - ret = digest_set_key(digest, key, key_len); - if (ret) { - digest_free(digest); - return ret; - } - - raw->digest = digest; - raw->digest_length = digest_length(digest); + raw->algo = xstrdup(algo); return 0; } @@ -301,15 +311,14 @@ int backend_format_raw_create(struct state_backend_format **format, raw->dev = dev; ret = backend_format_raw_init_digest(raw, node, secret_name); - if (ret == -EPROBE_DEFER) { - return ret; - } else if (ret) { + if (ret) { dev_err(raw->dev, "Failed initializing digest for raw format, %d\n", ret); free(raw); return ret; } + raw->secret_name = xstrdup(secret_name); raw->format.pack = backend_format_raw_pack; raw->format.unpack = backend_format_raw_unpack; raw->format.verify = backend_format_raw_verify; diff --git a/common/state/backend_storage.c b/common/state/backend_storage.c index 5dc8c50267..9ed6ad79ac 100644 --- a/common/state/backend_storage.c +++ b/common/state/backend_storage.c @@ -25,24 +25,29 @@ #include "state.h" -const unsigned int min_copies_written = 1; - -static int bucket_lazy_init(struct state_backend_storage_bucket *bucket) -{ - int ret; - - if (bucket->initialized) - return 0; - - if (bucket->init) { - ret = bucket->init(bucket); - if (ret) - return ret; - } - bucket->initialized = true; +/* + * The state framework stores data in so called buckets. A bucket is + * exactly one copy of the state we want to store. On flash type media + * a bucket corresponds to a single eraseblock. On media which do not + * need an erase operation a bucket corresponds to a storage area of + * @stridesize bytes. + * + * For redundancy and to make sure that we have valid data on the storage + * device at any time the state framework stores multiple buckets. The strategy + * is as follows: + * + * When loading the state from the storage we iterate over the buckets. We + * take the first one we find which has valid crcs. The next step is to + * restore consistency between the different buckets. This means rewriting + * a bucket when it signalled it needs refresh (i.e. returned -EUCLEAN) + * or when contains data different from the bucket we use. + * + * When the state backend initialized successfully we already restored + * consistency which means all buckets contain the same data. This means + * when storing a new state we can just write all buckets in order. + */ - return 0; -} +static const unsigned int min_buckets_written = 1; /** * state_storage_write - Writes the given data to the storage @@ -55,60 +60,64 @@ static int bucket_lazy_init(struct state_backend_storage_bucket *bucket) * operation on all of them. Writes are always in the same sequence. This * ensures, that reading in the same sequence will always return the latest * written valid data first. - * We try to at least write min_copies_written. If this fails we return with an + * We try to at least write min_buckets_written. If this fails we return with an * error. */ int state_storage_write(struct state_backend_storage *storage, - const uint8_t * buf, ssize_t len) + const void * buf, ssize_t len) { struct state_backend_storage_bucket *bucket; int ret; - int copies_written = 0; + int buckets_written = 0; if (storage->readonly) return 0; list_for_each_entry(bucket, &storage->buckets, bucket_list) { - ret = bucket_lazy_init(bucket); - if (ret) { - dev_warn(storage->dev, "Failed to init bucket/write state backend bucket, %d\n", - ret); - continue; - } - ret = bucket->write(bucket, buf, len); if (ret) { dev_warn(storage->dev, "Failed to write state backend bucket, %d\n", ret); } else { - ++copies_written; + ++buckets_written; } } - if (copies_written >= min_copies_written) + if (buckets_written >= min_buckets_written) return 0; dev_err(storage->dev, "Failed to write state to at least %d buckets. Successfully written to %d buckets\n", - min_copies_written, copies_written); + min_buckets_written, buckets_written); return -EIO; } -/** - * state_storage_restore_consistency - Restore consistency on all storage backends - * @param storage Storage object - * @param buf Buffer with valid data that should be on all buckets after this operation - * @param len Length of the buffer - * @return 0 on success, -errno otherwise - * - * This function brings valid data onto all buckets we have to ensure that all - * data copies are in sync. In the current implementation we just write the data - * to all buckets. Bucket implementations that need to keep the number of writes - * low, can read their own copy first and compare it. - */ -int state_storage_restore_consistency(struct state_backend_storage *storage, - const uint8_t * buf, ssize_t len) +static int bucket_refresh(struct state_backend_storage *storage, + struct state_backend_storage_bucket *bucket, void *buf, ssize_t len) { - return state_storage_write(storage, buf, len); + int ret; + + if (bucket->needs_refresh) + goto refresh; + + if (bucket->len != len) + goto refresh; + + if (memcmp(bucket->buf, buf, len)) + goto refresh; + + return 0; + +refresh: + ret = bucket->write(bucket, buf, len); + + if (ret) + dev_warn(storage->dev, "Failed to restore bucket %d@0x%08lx\n", + bucket->num, bucket->offset); + else + dev_info(storage->dev, "restored bucket %d@0x%08lx\n", + bucket->num, bucket->offset); + + return ret; } /** @@ -118,7 +127,7 @@ int state_storage_restore_consistency(struct state_backend_storage *storage, * @param magic state magic value * @param buf The newly allocated data area will be stored in this pointer * @param len The resulting length of the buffer - * @param len_hint Hint of how big the data may be. + * @param flags flags controlling how to load state * @return 0 on success, -errno otherwise. buf and len will be set to valid * values on success. * @@ -129,43 +138,65 @@ int state_storage_restore_consistency(struct state_backend_storage *storage, */ int state_storage_read(struct state_backend_storage *storage, struct state_backend_format *format, - uint32_t magic, uint8_t ** buf, ssize_t * len, - ssize_t len_hint) + uint32_t magic, void **buf, ssize_t *len, + enum state_flags flags) { - struct state_backend_storage_bucket *bucket; + struct state_backend_storage_bucket *bucket, *bucket_used = NULL; int ret; + /* + * Iterate over all buckets. The first valid one we find is the + * one we want to use. + */ list_for_each_entry(bucket, &storage->buckets, bucket_list) { - *len = len_hint; - ret = bucket_lazy_init(bucket); - if (ret) { - dev_warn(storage->dev, "Failed to init bucket/read state backend bucket, %d\n", - ret); + ret = bucket->read(bucket, &bucket->buf, &bucket->len); + if (ret == -EUCLEAN) + bucket->needs_refresh = 1; + else if (ret) continue; - } - ret = bucket->read(bucket, buf, len); - if (ret) { - dev_warn(storage->dev, "Failed to read from state backend bucket, trying next, %d\n", - ret); + /* + * Verify the buffer crcs. The buffer length is passed in the len argument, + * .verify overwrites it with the length actually used. + */ + ret = format->verify(format, magic, bucket->buf, &bucket->len, flags); + if (!ret && !bucket_used) + bucket_used = bucket; + } + + if (!bucket_used) { + dev_err(storage->dev, "Failed to find any valid state copy in any bucket\n"); + + return -ENOENT; + } + + dev_info(storage->dev, "Using bucket %d@0x%08lx\n", bucket_used->num, bucket_used->offset); + + /* + * Restore/refresh all buckets except the one we currently use (in case + * it's the only usable bucket at the moment) + */ + list_for_each_entry(bucket, &storage->buckets, bucket_list) { + if (bucket == bucket_used) continue; - } - ret = format->verify(format, magic, *buf, *len); - if (!ret) { - goto found; - } - free(*buf); - dev_warn(storage->dev, "Failed to verify read copy, trying next bucket, %d\n", - ret); + + ret = bucket_refresh(storage, bucket, bucket_used->buf, bucket_used->len); + + /* Free buffer from the unused buckets */ + free(bucket->buf); + bucket->buf = NULL; } - dev_err(storage->dev, "Failed to find any valid state copy in any bucket\n"); + /* + * Restore/refresh the bucket we currently use + */ + ret = bucket_refresh(storage, bucket_used, bucket_used->buf, bucket_used->len); - return -ENOENT; + *buf = bucket_used->buf; + *len = bucket_used->len; -found: - /* A failed restore consistency is not a failure of reading the state */ - state_storage_restore_consistency(storage, *buf, *len); + /* buffer from the used bucket is passed to the caller, do not free */ + bucket_used->buf = NULL; return 0; } @@ -187,267 +218,127 @@ static int mtd_get_meminfo(const char *path, struct mtd_info_user *meminfo) return ret; } -#ifdef __BAREBOX__ -#define STAT_GIVES_SIZE(s) (S_ISREG(s.st_mode) || S_ISCHR(s.st_mode)) -#define BLKGET_GIVES_SIZE(s) 0 -#else -#define STAT_GIVES_SIZE(s) (S_ISREG(s.st_mode)) -#define BLKGET_GIVES_SIZE(s) (S_ISBLK(s.st_mode)) -#endif -#ifndef BLKGETSIZE64 -#define BLKGETSIZE64 -1 -#endif - -static int state_backend_storage_get_size(const char *path, size_t * out_size) -{ - struct mtd_info_user meminfo; - struct stat s; - int ret; - - ret = stat(path, &s); - if (ret) - return -errno; - - /* - * under Linux, stat() gives the size only on regular files - * under barebox, it works on char dev, too - */ - if (STAT_GIVES_SIZE(s)) { - *out_size = s.st_size; - return 0; - } - - /* this works under Linux on block devs */ - if (BLKGET_GIVES_SIZE(s)) { - int fd; - - fd = open(path, O_RDONLY); - if (fd < 0) - return -errno; - - ret = ioctl(fd, BLKGETSIZE64, out_size); - close(fd); - if (!ret) - return 0; - } - - /* try mtd next */ - ret = mtd_get_meminfo(path, &meminfo); - if (!ret) { - *out_size = meminfo.size; - return 0; - } - - return ret; -} - -/* Number of copies that should be allocated */ -const int desired_copies = 3; +/* Number of buckets that should be used */ +static const int desired_buckets = 3; /** * state_storage_mtd_buckets_init - Creates storage buckets for mtd devices * @param storage Storage object * @param meminfo Info about the mtd device - * @param path Path to the device - * @param non_circular Use non-circular mode to write data that is compatible with the old on-flash format - * @param dev_offset Offset to start at in the device. - * @param max_size Maximum size to use for data. May be 0 for infinite. + * @param circular If false, use non-circular mode to write data that is compatible with the old on-flash format * @return 0 on success, -errno otherwise * - * Starting from offset 0 this function tries to create circular buckets on - * different offsets in the device. Different copies of the data are located in - * different eraseblocks. - * For MTD devices we use circular buckets to minimize the number of erases. - * Circular buckets write new data always in the next free space. + * This function iterates over the eraseblocks and creates one bucket on + * each eraseblock until we have the number of desired buckets. Bad blocks + * will be skipped and the next block will be used. */ static int state_storage_mtd_buckets_init(struct state_backend_storage *storage, - struct mtd_info_user *meminfo, - const char *path, bool non_circular, - off_t dev_offset, size_t max_size) + struct mtd_info_user *meminfo, bool circular) { struct state_backend_storage_bucket *bucket; - ssize_t end = dev_offset + max_size; - int nr_copies = 0; + ssize_t end = storage->offset + storage->max_size; + int n_buckets = 0; off_t offset; + ssize_t writesize; if (!end || end > meminfo->size) end = meminfo->size; - if (!IS_ALIGNED(dev_offset, meminfo->erasesize)) { + if (!IS_ALIGNED(storage->offset, meminfo->erasesize)) { dev_err(storage->dev, "Offset within the device is not aligned to eraseblocks. Offset is %ld, erasesize %zu\n", - dev_offset, meminfo->erasesize); + storage->offset, meminfo->erasesize); return -EINVAL; } - for (offset = dev_offset; offset < end; offset += meminfo->erasesize) { + if (circular) + writesize = meminfo->writesize; + else + writesize = meminfo->erasesize; + + for (offset = storage->offset; offset < end; offset += meminfo->erasesize) { int ret; - ssize_t writesize = meminfo->writesize; unsigned int eraseblock = offset / meminfo->erasesize; - bool lazy_init = true; - - if (non_circular) - writesize = meminfo->erasesize; - ret = state_backend_bucket_circular_create(storage->dev, path, + ret = state_backend_bucket_circular_create(storage->dev, storage->path, &bucket, eraseblock, writesize, - meminfo, - lazy_init); - if (ret) { - dev_warn(storage->dev, "Failed to create bucket at '%s' eraseblock %u\n", - path, eraseblock); + meminfo); + if (ret) continue; - } - ret = state_backend_bucket_cached_create(storage->dev, bucket, - &bucket); - if (ret) { - dev_warn(storage->dev, "Failed to setup cache bucket, continuing without cache, %d\n", - ret); - } + bucket->offset = offset; + bucket->num = n_buckets; list_add_tail(&bucket->bucket_list, &storage->buckets); - ++nr_copies; - if (nr_copies >= desired_copies) + ++n_buckets; + if (n_buckets >= desired_buckets) return 0; } - if (!nr_copies) { + if (!n_buckets) { dev_err(storage->dev, "Failed to initialize any state storage bucket\n"); return -EIO; } dev_warn(storage->dev, "Failed to initialize desired amount of buckets, only %d of %d succeeded\n", - nr_copies, desired_copies); + n_buckets, desired_buckets); return 0; } -static int state_storage_file_create(struct device_d *dev, const char *path, - size_t fd_size) -{ - int fd; - uint8_t *buf; - int ret; - - fd = open(path, O_RDWR | O_CREAT, 0600); - if (fd < 0) { - dev_err(dev, "Failed to open/create file '%s', %d\n", path, - -errno); - return -errno; - } - - buf = xzalloc(fd_size); - if (!buf) { - ret = -ENOMEM; - goto out_close; - } - - ret = write_full(fd, buf, fd_size); - if (ret < 0) { - dev_err(dev, "Failed to initialize empty file '%s', %d\n", path, - ret); - goto out_free; - } - ret = 0; - -out_free: - free(buf); -out_close: - close(fd); - return ret; -} - /** * state_storage_file_buckets_init - Create buckets for a conventional file descriptor * @param storage Storage object - * @param path Path to file/device - * @param dev_offset Offset in the device to start writing at. - * @param max_size Maximum size of the data. May be 0 for infinite. - * @param stridesize How far apart the different data copies are placed. If - * stridesize is 0, only one copy can be created. * @return 0 on success, -errno otherwise * - * For blockdevices and other regular files we create direct buckets beginning - * at offset 0. Direct buckets are simple and write data always to offset 0. + * direct buckets are simpler than circular buckets and can be used on blockdevices + * and mtd devices that don't need erase (MRAM). Also used for EEPROMs. */ -static int state_storage_file_buckets_init(struct state_backend_storage *storage, - const char *path, off_t dev_offset, - size_t max_size, uint32_t stridesize) +static int state_storage_file_buckets_init(struct state_backend_storage *storage) { struct state_backend_storage_bucket *bucket; - size_t fd_size = 0; - int ret; + int ret, n; off_t offset; - int nr_copies = 0; - - ret = state_backend_storage_get_size(path, &fd_size); - if (ret) { - if (ret != -ENOENT) { - dev_err(storage->dev, "Failed to get the filesize of '%s', %d\n", - path, ret); - return ret; - } - if (!stridesize) { - dev_err(storage->dev, "File '%s' does not exist and no information about the needed size. Please specify stridesize\n", - path); - return ret; - } - - if (max_size) - fd_size = min(dev_offset + stridesize * desired_copies, - dev_offset + max_size); - else - fd_size = dev_offset + stridesize * desired_copies; - dev_info(storage->dev, "File '%s' does not exist, creating file of size %zd\n", - path, fd_size); - ret = state_storage_file_create(storage->dev, path, fd_size); - if (ret) { - dev_info(storage->dev, "Failed to create file '%s', %d\n", - path, ret); - return ret; - } - } else if (max_size) { - fd_size = min(fd_size, (size_t)dev_offset + max_size); - } + int n_buckets = 0; + uint32_t stridesize = storage->stridesize; + size_t max_size = storage->max_size; if (!stridesize) { - dev_warn(storage->dev, "WARNING, no stridesize given although we use a direct file write. Starting in degraded mode\n"); - stridesize = fd_size; + dev_err(storage->dev, "stridesize unspecified\n"); + return -EINVAL; } - for (offset = dev_offset; offset < fd_size; offset += stridesize) { - size_t maxsize = min((size_t)stridesize, - (size_t)(fd_size - offset)); + if (max_size && max_size < desired_buckets * stridesize) { + dev_err(storage->dev, "device is too small to hold %d copies\n", desired_buckets); + return -EINVAL; + } - ret = state_backend_bucket_direct_create(storage->dev, path, + for (n = 0; n < desired_buckets; n++) { + offset = storage->offset + n * stridesize; + ret = state_backend_bucket_direct_create(storage->dev, storage->path, &bucket, offset, - maxsize); + stridesize); if (ret) { dev_warn(storage->dev, "Failed to create direct bucket at '%s' offset %ld\n", - path, offset); + storage->path, offset); continue; } - ret = state_backend_bucket_cached_create(storage->dev, bucket, - &bucket); - if (ret) { - dev_warn(storage->dev, "Failed to setup cache bucket, continuing without cache, %d\n", - ret); - } + bucket->offset = offset; + bucket->num = n_buckets; list_add_tail(&bucket->bucket_list, &storage->buckets); - ++nr_copies; - if (nr_copies >= desired_copies) - return 0; + ++n_buckets; } - if (!nr_copies) { + if (!n_buckets) { dev_err(storage->dev, "Failed to initialize any state direct storage bucket\n"); return -EIO; } - dev_warn(storage->dev, "Failed to initialize desired amount of direct buckets, only %d of %d succeeded\n", - nr_copies, desired_copies); + + if (n_buckets < desired_buckets) + dev_warn(storage->dev, "Failed to initialize desired amount of direct buckets, only %d of %d succeeded\n", + n_buckets, desired_buckets); return 0; } @@ -455,7 +346,6 @@ static int state_storage_file_buckets_init(struct state_backend_storage *storage /** * state_storage_init - Init backend storage - * @param storage Storage object * @param path Path to the backend storage file * @param dev_offset Offset in the device to start writing at. * @param max_size Maximum size of the data. May be 0 for infinite. @@ -466,37 +356,39 @@ static int state_storage_file_buckets_init(struct state_backend_storage *storage * * Depending on the filetype, we create mtd buckets or normal file buckets. */ -int state_storage_init(struct state_backend_storage *storage, - struct device_d *dev, const char *path, +int state_storage_init(struct state *state, const char *path, off_t offset, size_t max_size, uint32_t stridesize, const char *storagetype) { + struct state_backend_storage *storage = &state->storage; int ret = -ENODEV; struct mtd_info_user meminfo; INIT_LIST_HEAD(&storage->buckets); - storage->dev = dev; + storage->dev = &state->dev; storage->name = storagetype; storage->stridesize = stridesize; + storage->offset = offset; + storage->max_size = max_size; + storage->path = xstrdup(path); if (IS_ENABLED(CONFIG_MTD)) ret = mtd_get_meminfo(path, &meminfo); if (!ret && !(meminfo.flags & MTD_NO_ERASE)) { - bool non_circular = false; - if (!storagetype) { - non_circular = true; - } else if (strcmp(storagetype, "circular")) { - dev_warn(storage->dev, "Unknown storagetype '%s', falling back to old format circular storage type.\n", - storagetype); - non_circular = true; + bool circular; + if (!storagetype || !strcmp(storagetype, "circular")) { + circular = true; + } else if (!strcmp(storagetype, "noncircular")) { + dev_warn(storage->dev, "using old format circular storage type.\n"); + circular = false; + } else { + dev_warn(storage->dev, "unknown storage type '%s'\n", storagetype); + return -EINVAL; } - return state_storage_mtd_buckets_init(storage, &meminfo, path, - non_circular, offset, - max_size); + return state_storage_mtd_buckets_init(storage, &meminfo, circular); } else { - return state_storage_file_buckets_init(storage, path, offset, - max_size, stridesize); + return state_storage_file_buckets_init(storage); } dev_err(storage->dev, "storage init done\n"); @@ -524,4 +416,6 @@ void state_storage_free(struct state_backend_storage *storage) list_del(&bucket->bucket_list); bucket->free(bucket); } + + free(storage->path); } diff --git a/common/state/state.c b/common/state/state.c index 4020d5e1ea..8369aedac6 100644 --- a/common/state/state.c +++ b/common/state/state.c @@ -34,6 +34,125 @@ /* list of all registered state instances */ static LIST_HEAD(state_list); +/** + * Save the state + * @param state + * @return + */ +int state_save(struct state *state) +{ + void *buf; + ssize_t len; + int ret; + + if (!state->dirty) + return 0; + + ret = state->format->pack(state->format, state, &buf, &len); + if (ret) { + dev_err(&state->dev, "Failed to pack state with backend format %s, %d\n", + state->format->name, ret); + return ret; + } + + ret = state_storage_write(&state->storage, buf, len); + if (ret) { + dev_err(&state->dev, "Failed to write packed state, %d\n", ret); + goto out; + } + + state->dirty = 0; + +out: + free(buf); + return ret; +} + +/** + * state_load - Loads a state from the backend + * @param state The state that should be updated to contain the loaded data + * @return 0 on success, -errno on failure. If no state is loaded the previous + * values remain in the state. + * + * This function uses the registered storage backend to read data. All data that + * we read is checked for integrity by the formatter. After that we unpack the + * data into our state. + */ +static int state_do_load(struct state *state, enum state_flags flags) +{ + void *buf; + ssize_t len; + int ret; + + ret = state_storage_read(&state->storage, state->format, + state->magic, &buf, &len, flags); + if (ret) { + dev_err(&state->dev, "Failed to read state with format %s, %d\n", + state->format->name, ret); + return ret; + } + + ret = state->format->unpack(state->format, state, buf, len); + if (ret) { + dev_err(&state->dev, "Failed to unpack read data with format %s although verified, %d\n", + state->format->name, ret); + goto out; + } + + state->dirty = 0; + +out: + free(buf); + return ret; +} + +int state_load(struct state *state) +{ + return state_do_load(state, 0); +} + +int state_load_no_auth(struct state *state) +{ + return state_do_load(state, STATE_FLAG_NO_AUTHENTIFICATION); +} + +static int state_format_init(struct state *state, const char *backend_format, + struct device_node *node, const char *state_name) +{ + int ret; + + if (!backend_format || !strcmp(backend_format, "raw")) { + ret = backend_format_raw_create(&state->format, node, + state_name, &state->dev); + } else if (!strcmp(backend_format, "dtb")) { + ret = backend_format_dtb_create(&state->format, &state->dev); + } else { + dev_err(&state->dev, "Invalid backend format %s\n", + backend_format); + return -EINVAL; + } + + if (ret && ret != -EPROBE_DEFER) + dev_err(&state->dev, "Failed to initialize format %s, %d\n", + backend_format, ret); + + return ret; +} + +static void state_format_free(struct state_backend_format *format) +{ + if (!format) + return; + + if (format->free) + format->free(format); +} + +void state_backend_set_readonly(struct state *state) +{ + state_storage_set_readonly(&state->storage); +} + static struct state *state_new(const char *name) { struct state *state; @@ -152,7 +271,6 @@ static int state_convert_node_variable(struct state *state, sv->name = name; sv->start = start_size[0]; - sv->type = vtype->type; state_add_var(state, sv); } else { sv = state_find_var(state, name); @@ -167,9 +285,8 @@ static int state_convert_node_variable(struct state *state, if ((conv == STATE_CONVERT_TO_NODE) || (conv == STATE_CONVERT_FIXUP)) { - ret = of_set_property(new_node, "type", - vtype->type_name, - strlen(vtype->type_name) + 1, 1); + ret = of_property_write_string(new_node, "type", + vtype->type_name); if (ret) goto out; @@ -329,21 +446,21 @@ static int of_state_fixup(struct device_node *root, void *ctx) } /* backend-type */ - if (!state->backend.format) { + if (!state->format) { ret = -ENODEV; goto out; } p = of_new_property(new_node, "backend-type", - state->backend.format->name, - strlen(state->backend.format->name) + 1); + state->format->name, + strlen(state->format->name) + 1); if (!p) { ret = -ENOMEM; goto out; } /* backend phandle */ - backend_node = of_find_node_by_path_from(root, state->backend.of_path); + backend_node = of_find_node_by_devpath(root, state->backend_path); if (!backend_node) { ret = -ENODEV; goto out; @@ -354,9 +471,9 @@ static int of_state_fixup(struct device_node *root, void *ctx) if (ret) goto out; - if (!strcmp("raw", state->backend.format->name)) { + if (!strcmp("raw", state->format->name)) { struct digest *digest = - state_backend_format_raw_get_digest(state->backend.format); + state_backend_format_raw_get_digest(state->format); if (digest) { p = of_new_property(new_node, "algo", digest_name(digest), @@ -368,19 +485,19 @@ static int of_state_fixup(struct device_node *root, void *ctx) } } - if (state->backend.storage.name) { + if (state->storage.name) { p = of_new_property(new_node, "backend-storage-type", - state->backend.storage.name, - strlen(state->backend.storage.name) + 1); + state->storage.name, + strlen(state->storage.name) + 1); if (!p) { ret = -ENOMEM; goto out; } } - if (state->backend.storage.stridesize) { + if (state->storage.stridesize) { ret = of_property_write_u32(new_node, "backend-stridesize", - state->backend.storage.stridesize); + state->storage.stridesize); if (ret) goto out; } @@ -409,7 +526,9 @@ void state_release(struct state *state) of_unregister_fixup(of_state_fixup, state); list_del(&state->list); unregister_device(&state->dev); - state_backend_free(&state->backend); + state_storage_free(&state->storage); + state_format_free(state->format); + free(state->backend_path); free(state->of_path); free(state); } @@ -453,19 +572,16 @@ struct state *state_new_from_node(struct device_node *node, char *path, } if (!path) { - /* guess if of_path is a path, not a phandle */ - if (of_path[0] == '/' && len > 1) { - ret = of_find_path(node, "backend", &path, 0); - } else { - struct device_node *partition_node; - - partition_node = of_parse_phandle(node, "backend", 0); - if (!partition_node) - goto out_release_state; - - of_path = partition_node->full_name; - ret = of_find_path_by_node(partition_node, &path, 0); + struct device_node *partition_node; + + partition_node = of_parse_phandle(node, "backend", 0); + if (!partition_node) { + dev_err(&state->dev, "Cannot resolve \"backend\" phandle\n"); + goto out_release_state; } + + of_path = partition_node->full_name; + ret = of_find_path_by_node(partition_node, &path, 0); if (ret) { if (ret != -EPROBE_DEFER) dev_err(&state->dev, "state failed to parse path to backend: %s\n", @@ -474,6 +590,8 @@ struct state *state_new_from_node(struct device_node *node, char *path, } } + state->backend_path = xstrdup(path); + ret = of_property_read_string(node, "backend-type", &backend_type); if (ret) { goto out_release_state; @@ -491,14 +609,17 @@ struct state *state_new_from_node(struct device_node *node, char *path, dev_info(&state->dev, "No backend-storage-type found, using default.\n"); } - ret = state_backend_init(&state->backend, &state->dev, node, - backend_type, path, alias, of_path, offset, + ret = state_format_init(state, backend_type, node, alias); + if (ret) + goto out_release_state; + + ret = state_storage_init(state, path, offset, max_size, stridesize, storage_type); if (ret) goto out_release_state; if (readonly) - state_backend_set_readonly(&state->backend); + state_backend_set_readonly(state); ret = state_from_node(state, node, 1); if (ret) { @@ -510,11 +631,6 @@ struct state *state_new_from_node(struct device_node *node, char *path, goto out_release_state; } - ret = state_load(state); - if (ret) { - dev_warn(&state->dev, "Failed to load persistent state, continuing with defaults, %d\n", ret); - } - dev_info(&state->dev, "New state registered '%s'\n", alias); return state; @@ -573,10 +689,10 @@ void state_info(void) list_for_each_entry(state, &state_list, list) { printf("%-20s ", state->name); - if (state->backend.format) + if (state->format) printf("(backend: %s, path: %s)\n", - state->backend.format->name, - state->backend.of_path); + state->format->name, + state->backend_path); else printf("(no backend)\n"); } diff --git a/common/state/state.h b/common/state/state.h index bc6917de61..81aaec23b6 100644 --- a/common/state/state.h +++ b/common/state/state.h @@ -5,6 +5,10 @@ struct state; struct mtd_info_user; +enum state_flags { + STATE_FLAG_NO_AUTHENTIFICATION = (1 << 0), +}; + /** * state_backend_storage_bucket - This class describes a single backend storage * object copy @@ -20,15 +24,20 @@ struct mtd_info_user; * @bucket_list A list element struct to attach this bucket to a list */ struct state_backend_storage_bucket { - int (*init) (struct state_backend_storage_bucket * bucket); int (*write) (struct state_backend_storage_bucket * bucket, - const uint8_t * buf, ssize_t len); + const void * buf, ssize_t len); int (*read) (struct state_backend_storage_bucket * bucket, - uint8_t ** buf, ssize_t * len_hint); + void ** buf, ssize_t * len_hint); void (*free) (struct state_backend_storage_bucket * bucket); - bool initialized; + int num; + off_t offset; + struct list_head bucket_list; + + void *buf; + ssize_t len; + bool needs_refresh; }; /** @@ -48,13 +57,11 @@ struct state_backend_storage_bucket { */ struct state_backend_format { int (*verify) (struct state_backend_format * format, uint32_t magic, - const uint8_t * buf, ssize_t len); + const void * buf, ssize_t *lenp, enum state_flags flags); int (*pack) (struct state_backend_format * format, struct state * state, - uint8_t ** buf, ssize_t * len); + void ** buf, ssize_t * len); int (*unpack) (struct state_backend_format * format, - struct state * state, const uint8_t * buf, ssize_t len); - ssize_t(*get_packed_len) (struct state_backend_format * format, - struct state * state); + struct state * state, const void * buf, ssize_t len); void (*free) (struct state_backend_format * format); const char *name; }; @@ -63,6 +70,9 @@ struct state_backend_format { * state_backend_storage - Storage backend of the state. * * @buckets List of storage buckets that are available + * @stridesize The distance between copies + * @offset Offset in the backend device where the data starts + * @max_size The maximum size of the data we can use */ struct state_backend_storage { struct list_head buckets; @@ -73,23 +83,13 @@ struct state_backend_storage { const char *name; uint32_t stridesize; + off_t offset; + size_t max_size; + char *path; bool readonly; }; -/** - * state_backend - State Backend object - * - * @format Backend format object - * @storage Backend storage object - * @of_path Path to the DT node - */ -struct state_backend { - struct state_backend_format *format; - struct state_backend_storage storage; - char *of_path; -}; - struct state { struct list_head list; /* Entry to enqueue on list of states */ @@ -102,7 +102,9 @@ struct state { unsigned int dirty; unsigned int save_on_shutdown; - struct state_backend backend; + struct state_backend_format *format; + struct state_backend_storage storage; + char *backend_path; }; enum state_convert { @@ -112,20 +114,10 @@ enum state_convert { STATE_CONVERT_FIXUP, }; -enum state_variable_type { - STATE_TYPE_INVALID = 0, - STATE_TYPE_ENUM, - STATE_TYPE_U8, - STATE_TYPE_U32, - STATE_TYPE_MAC, - STATE_TYPE_STRING, -}; - struct state_variable; /* A variable type (uint32, enum32) */ struct variable_type { - enum state_variable_type type; const char *type_name; struct list_head list; int (*export) (struct state_variable *, struct device_node *, @@ -139,7 +131,6 @@ struct variable_type { /* instance of a single variable */ struct state_variable { struct state *state; - enum state_variable_type type; struct list_head list; const char *name; unsigned int start; @@ -199,8 +190,7 @@ int backend_format_raw_create(struct state_backend_format **format, struct device_d *dev); int backend_format_dtb_create(struct state_backend_format **format, struct device_d *dev); -int state_storage_init(struct state_backend_storage *storage, - struct device_d *dev, const char *path, +int state_storage_init(struct state *state, const char *path, off_t offset, size_t max_size, uint32_t stridesize, const char *storagetype); void state_storage_set_readonly(struct state_backend_storage *storage); @@ -210,34 +200,24 @@ int state_backend_bucket_circular_create(struct device_d *dev, const char *path, struct state_backend_storage_bucket **bucket, unsigned int eraseblock, ssize_t writesize, - struct mtd_info_user *mtd_uinfo, - bool lazy_init); + struct mtd_info_user *mtd_uinfo); int state_backend_bucket_cached_create(struct device_d *dev, struct state_backend_storage_bucket *raw, struct state_backend_storage_bucket **out); struct state_variable *state_find_var(struct state *state, const char *name); struct digest *state_backend_format_raw_get_digest(struct state_backend_format *format); -int state_backend_init(struct state_backend *backend, struct device_d *dev, - struct device_node *node, const char *backend_format, - const char *storage_path, const char *state_name, const - char *of_path, off_t offset, size_t max_size, - uint32_t stridesize, const char *storagetype); -void state_backend_set_readonly(struct state_backend *backend); -void state_backend_free(struct state_backend *backend); +void state_backend_set_readonly(struct state *state); void state_storage_free(struct state_backend_storage *storage); int state_backend_bucket_direct_create(struct device_d *dev, const char *path, struct state_backend_storage_bucket **bucket, off_t offset, ssize_t max_size); int state_storage_write(struct state_backend_storage *storage, - const uint8_t * buf, ssize_t len); -int state_storage_restore_consistency(struct state_backend_storage - *storage, const uint8_t * buf, - ssize_t len); + const void * buf, ssize_t len); int state_storage_read(struct state_backend_storage *storage, struct state_backend_format *format, - uint32_t magic, uint8_t **buf, ssize_t *len, - ssize_t len_hint); + uint32_t magic, void **buf, ssize_t *len, + enum state_flags flags); static inline struct state_uint32 *to_state_uint32(struct state_variable *s) { diff --git a/common/state/state_variables.c b/common/state/state_variables.c index fd072a0c27..5b8e6284d9 100644 --- a/common/state/state_variables.c +++ b/common/state/state_variables.c @@ -439,31 +439,26 @@ static struct state_variable *state_string_create(struct state *state, static struct variable_type types[] = { { - .type = STATE_TYPE_U8, .type_name = "uint8", .export = state_uint32_export, .import = state_uint32_import, .create = state_uint8_create, }, { - .type = STATE_TYPE_U32, .type_name = "uint32", .export = state_uint32_export, .import = state_uint32_import, .create = state_uint32_create, }, { - .type = STATE_TYPE_ENUM, .type_name = "enum32", .export = state_enum32_export, .import = state_enum32_import, .create = state_enum32_create, }, { - .type = STATE_TYPE_MAC, .type_name = "mac", .export = state_mac_export, .import = state_mac_import, .create = state_mac_create, }, { - .type = STATE_TYPE_STRING, .type_name = "string", .export = state_string_export, .import = state_string_import, diff --git a/crypto/hmac.c b/crypto/hmac.c index 05b9b50f12..37b270df7a 100644 --- a/crypto/hmac.c +++ b/crypto/hmac.c @@ -196,4 +196,4 @@ static int digest_hmac_initcall(void) return 0; } -crypto_initcall(digest_hmac_initcall); +coredevice_initcall(digest_hmac_initcall); diff --git a/crypto/keystore.c b/crypto/keystore.c index 90b470fe67..f2b25ca6c9 100644 --- a/crypto/keystore.c +++ b/crypto/keystore.c @@ -16,8 +16,8 @@ static LIST_HEAD(keystore_list); struct keystore_key { struct list_head list; - const char *name; - const u8 *secret; + char *name; + u8 *secret; int secret_len; }; @@ -29,6 +29,17 @@ static int keystore_compare(struct list_head *a, struct list_head *b) return strcmp(na, nb); } +static struct keystore_key *get_key(const char *name) +{ + struct keystore_key *key; + + for_each_key(key) + if (!strcmp(name, key->name)) + return key; + + return NULL; +}; + /** * @param[in] name Name of the secret to get * @param[out] secret Double pointer to memory representing the secret, do _not_ free() after use @@ -38,19 +49,17 @@ int keystore_get_secret(const char *name, const u8 **secret, int *secret_len) { struct keystore_key *key; - for_each_key(key) { - if (!strcmp(name, key->name)) { - if (!secret || !secret_len) - return 0; + if (!secret || !secret_len) + return 0; - *secret = key->secret; - *secret_len = key->secret_len; + key = get_key(name); + if (!key) + return -ENOENT; - return 0; - } - } + *secret = key->secret; + *secret_len = key->secret_len; - return -ENOENT; + return 0; } /** @@ -61,11 +70,10 @@ int keystore_get_secret(const char *name, const u8 **secret, int *secret_len) int keystore_set_secret(const char *name, const u8 *secret, int secret_len) { struct keystore_key *key; - int ret; /* check if key is already in store */ - ret = keystore_get_secret(name, NULL, NULL); - if (!ret) + key = get_key(name); + if (key) return -EBUSY; key = xzalloc(sizeof(*key)); @@ -78,3 +86,18 @@ int keystore_set_secret(const char *name, const u8 *secret, int secret_len) return 0; } + +void keystore_forget_secret(const char *name) +{ + struct keystore_key *key; + + key = get_key(name); + if (!key) + return; + + list_del(&key->list); + + free(key->name); + free(key->secret); + free(key); +} diff --git a/crypto/sha1.c b/crypto/sha1.c index cbde4d28e4..f4b2ded0b0 100644 --- a/crypto/sha1.c +++ b/crypto/sha1.c @@ -303,4 +303,4 @@ static int sha1_digest_register(void) { return digest_algo_register(&m); } -device_initcall(sha1_digest_register); +coredevice_initcall(sha1_digest_register); diff --git a/crypto/sha2.c b/crypto/sha2.c index cb0f11c77e..c62ddb8d25 100644 --- a/crypto/sha2.c +++ b/crypto/sha2.c @@ -372,4 +372,4 @@ static int sha256_digest_register(void) return digest_algo_register(&m256); } -device_initcall(sha256_digest_register); +coredevice_initcall(sha256_digest_register); diff --git a/crypto/sha4.c b/crypto/sha4.c index 4ce37b73e4..aad8081fa5 100644 --- a/crypto/sha4.c +++ b/crypto/sha4.c @@ -292,4 +292,4 @@ static int sha512_digest_register(void) return digest_algo_register(&m512); } -device_initcall(sha512_digest_register); +coredevice_initcall(sha512_digest_register); diff --git a/drivers/Kconfig b/drivers/Kconfig index ab9afb54bd..1e0246da6d 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -22,10 +22,12 @@ source "drivers/eeprom/Kconfig" source "drivers/input/Kconfig" source "drivers/watchdog/Kconfig" source "drivers/pwm/Kconfig" +source "drivers/hw_random/Kconfig" source "drivers/dma/Kconfig" source "drivers/gpio/Kconfig" source "drivers/w1/Kconfig" source "drivers/pinctrl/Kconfig" +source "drivers/nvmem/Kconfig" source "drivers/bus/Kconfig" source "drivers/regulator/Kconfig" source "drivers/reset/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index a1d2d23fef..767789d541 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -19,9 +19,11 @@ obj-y += eeprom/ obj-$(CONFIG_PWM) += pwm/ obj-y += input/ obj-y += misc/ +obj-$(CONFIG_NVMEM) += nvmem/ obj-y += dma/ obj-y += watchdog/ obj-y += gpio/ +obj-$(CONFIG_HWRNG) += hw_random/ obj-$(CONFIG_OFTREE) += of/ obj-$(CONFIG_W1) += w1/ obj-y += pinctrl/ diff --git a/drivers/ata/disk_ata_drive.c b/drivers/ata/disk_ata_drive.c index 1aa1bb1456..5ebddbdec8 100644 --- a/drivers/ata/disk_ata_drive.c +++ b/drivers/ata/disk_ata_drive.c @@ -237,6 +237,9 @@ static int ata_port_init(struct ata_port *port) #ifdef DEBUG ata_dump_id(port->id); #endif + + port->lba48 = ata_id_has_lba48(port->id); + if (port->devname) { port->blk.cdev.name = xstrdup(port->devname); } else { diff --git a/drivers/ata/ide-sff.c b/drivers/ata/ide-sff.c index 6dc89d79a5..b7c8847266 100644 --- a/drivers/ata/ide-sff.c +++ b/drivers/ata/ide-sff.c @@ -136,17 +136,33 @@ static int ata_wait_ready(struct ide_port *ide, unsigned timeout) * @param io Register file * @param drive 0 master drive, 1 slave drive * @param num Sector number - * - * @todo LBA48 support */ -static int ata_set_lba_sector(struct ide_port *ide, unsigned drive, uint64_t num) +static int ata_set_lba_sector(struct ata_port *port, unsigned drive, + uint64_t num) { - if (num > 0x0FFFFFFF || drive > 1) + struct ide_port *ide = to_ata_drive_access(port); + + if (drive > 1) return -EINVAL; - ata_wr_byte(ide, 0xA0 | LBA_FLAG | drive << 4 | num >> 24, - ide->io.device_addr); - ata_wr_byte(ide, 0x00, ide->io.error_addr); + if (port->lba48) { + if (num > (1ULL << 48) - 1) + return -EINVAL; + + ata_wr_byte(ide, LBA_FLAG | drive << 4, ide->io.device_addr); + + ata_wr_byte(ide, 0x00, ide->io.nsect_addr); + ata_wr_byte(ide, num >> 24, ide->io.lbal_addr); + ata_wr_byte(ide, num >> 32, ide->io.lbam_addr); + ata_wr_byte(ide, num >> 40, ide->io.lbah_addr); + } else { + if (num > (1ULL << 28) - 1) + return -EINVAL; + + ata_wr_byte(ide, 0xA0 | LBA_FLAG | drive << 4 | num >> 24, + ide->io.device_addr); + } + ata_wr_byte(ide, 0x01, ide->io.nsect_addr); ata_wr_byte(ide, num, ide->io.lbal_addr); /* 0 ... 7 */ ata_wr_byte(ide, num >> 8, ide->io.lbam_addr); /* 8 ... 15 */ @@ -316,10 +332,18 @@ static int ide_read(struct ata_port *port, void *buffer, unsigned int block, struct ide_port *ide = to_ata_drive_access(port); while (num_blocks) { - rc = ata_set_lba_sector(ide, DISK_MASTER, sector); + uint8_t cmd; + + rc = ata_set_lba_sector(port, DISK_MASTER, sector); if (rc != 0) return rc; - rc = ata_wr_cmd(ide, ATA_CMD_READ); + + if (port->lba48) + cmd = ATA_CMD_PIO_READ_EXT; + else + cmd = ATA_CMD_READ; + + rc = ata_wr_cmd(ide, cmd); if (rc != 0) return rc; rc = ata_wait_ready(ide, MAX_TIMEOUT); @@ -355,10 +379,18 @@ static int __maybe_unused ide_write(struct ata_port *port, struct ide_port *ide = to_ata_drive_access(port); while (num_blocks) { - rc = ata_set_lba_sector(ide, DISK_MASTER, sector); + uint8_t cmd; + + rc = ata_set_lba_sector(port, DISK_MASTER, sector); if (rc != 0) return rc; - rc = ata_wr_cmd(ide, ATA_CMD_WRITE); + + if (port->lba48) + cmd = ATA_CMD_PIO_WRITE_EXT; + else + cmd = ATA_CMD_WRITE; + + rc = ata_wr_cmd(ide, cmd); if (rc != 0) return rc; rc = ata_wait_ready(ide, MAX_TIMEOUT); diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 5867fe45d0..83260990af 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -221,7 +221,7 @@ int unregister_device(struct device_d *old_dev) } list_for_each_entry_safe(cdev, ct, &old_dev->cdevs, devices_list) { - if (cdev->flags & DEVFS_IS_PARTITION) { + if (cdev->master) { dev_dbg(old_dev, "unregister part %s\n", cdev->name); devfs_del_partition(cdev->name); } diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 5811d28b88..d75b954a4e 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_CLK_SOCFPGA) += socfpga.o obj-$(CONFIG_MACH_MIPS_ATH79) += clk-ar933x.o obj-$(CONFIG_ARCH_IMX) += imx/ +obj-$(CONFIG_COMMON_CLK_AT91) += at91/ diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile new file mode 100644 index 0000000000..13e67bd35c --- /dev/null +++ b/drivers/clk/at91/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for at91 specific clk +# + +obj-y += pmc.o sckc.o +obj-y += clk-slow.o clk-main.o clk-pll.o clk-plldiv.o clk-master.o +obj-y += clk-system.o clk-peripheral.o clk-programmable.o + +obj-$(CONFIG_HAVE_AT91_UTMI) += clk-utmi.o +obj-$(CONFIG_HAVE_AT91_USB_CLK) += clk-usb.o +obj-$(CONFIG_HAVE_AT91_SMD) += clk-smd.o +obj-$(CONFIG_HAVE_AT91_H32MX) += clk-h32mx.o +obj-$(CONFIG_HAVE_AT91_GENERATED_CLK) += clk-generated.o diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c new file mode 100644 index 0000000000..4e1cd5aa69 --- /dev/null +++ b/drivers/clk/at91/clk-generated.c @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2015 Atmel Corporation, + * Nicolas Ferre <nicolas.ferre@atmel.com> + * + * Based on clk-programmable & clk-peripheral drivers by Boris BREZILLON. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include <linux/clk/at91_pmc.h> +#include <linux/of.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> + +#include "pmc.h" + +#define PERIPHERAL_MAX 64 +#define PERIPHERAL_ID_MIN 2 + +#define GENERATED_SOURCE_MAX 6 +#define GENERATED_MAX_DIV 255 + +struct clk_generated { + struct clk_hw hw; + struct regmap *regmap; + struct clk_range range; + spinlock_t *lock; + u32 id; + u32 gckdiv; + u8 parent_id; +}; + +#define to_clk_generated(hw) \ + container_of(hw, struct clk_generated, hw) + +static int clk_generated_enable(struct clk_hw *hw) +{ + struct clk_generated *gck = to_clk_generated(hw); + unsigned long flags; + + pr_debug("GCLK: %s, gckdiv = %d, parent id = %d\n", + __func__, gck->gckdiv, gck->parent_id); + + spin_lock_irqsave(gck->lock, flags); + regmap_write(gck->regmap, AT91_PMC_PCR, + (gck->id & AT91_PMC_PCR_PID_MASK)); + regmap_update_bits(gck->regmap, AT91_PMC_PCR, + AT91_PMC_PCR_GCKDIV_MASK | AT91_PMC_PCR_GCKCSS_MASK | + AT91_PMC_PCR_CMD | AT91_PMC_PCR_GCKEN, + AT91_PMC_PCR_GCKCSS(gck->parent_id) | + AT91_PMC_PCR_CMD | + AT91_PMC_PCR_GCKDIV(gck->gckdiv) | + AT91_PMC_PCR_GCKEN); + spin_unlock_irqrestore(gck->lock, flags); + return 0; +} + +static void clk_generated_disable(struct clk_hw *hw) +{ + struct clk_generated *gck = to_clk_generated(hw); + unsigned long flags; + + spin_lock_irqsave(gck->lock, flags); + regmap_write(gck->regmap, AT91_PMC_PCR, + (gck->id & AT91_PMC_PCR_PID_MASK)); + regmap_update_bits(gck->regmap, AT91_PMC_PCR, + AT91_PMC_PCR_CMD | AT91_PMC_PCR_GCKEN, + AT91_PMC_PCR_CMD); + spin_unlock_irqrestore(gck->lock, flags); +} + +static int clk_generated_is_enabled(struct clk_hw *hw) +{ + struct clk_generated *gck = to_clk_generated(hw); + unsigned long flags; + unsigned int status; + + spin_lock_irqsave(gck->lock, flags); + regmap_write(gck->regmap, AT91_PMC_PCR, + (gck->id & AT91_PMC_PCR_PID_MASK)); + regmap_read(gck->regmap, AT91_PMC_PCR, &status); + spin_unlock_irqrestore(gck->lock, flags); + + return status & AT91_PMC_PCR_GCKEN ? 1 : 0; +} + +static unsigned long +clk_generated_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_generated *gck = to_clk_generated(hw); + + return DIV_ROUND_CLOSEST(parent_rate, gck->gckdiv + 1); +} + +static int clk_generated_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_generated *gck = to_clk_generated(hw); + struct clk_hw *parent = NULL; + long best_rate = -EINVAL; + unsigned long tmp_rate, min_rate; + int best_diff = -1; + int tmp_diff; + int i; + + for (i = 0; i < clk_hw_get_num_parents(hw); i++) { + u32 div; + unsigned long parent_rate; + + parent = clk_hw_get_parent_by_index(hw, i); + if (!parent) + continue; + + parent_rate = clk_hw_get_rate(parent); + min_rate = DIV_ROUND_CLOSEST(parent_rate, GENERATED_MAX_DIV + 1); + if (!parent_rate || + (gck->range.max && min_rate > gck->range.max)) + continue; + + for (div = 1; div < GENERATED_MAX_DIV + 2; div++) { + tmp_rate = DIV_ROUND_CLOSEST(parent_rate, div); + tmp_diff = abs(req->rate - tmp_rate); + + if (best_diff < 0 || best_diff > tmp_diff) { + best_rate = tmp_rate; + best_diff = tmp_diff; + req->best_parent_rate = parent_rate; + req->best_parent_hw = parent; + } + + if (!best_diff || tmp_rate < req->rate) + break; + } + + if (!best_diff) + break; + } + + pr_debug("GCLK: %s, best_rate = %ld, parent clk: %s @ %ld\n", + __func__, best_rate, + __clk_get_name((req->best_parent_hw)->clk), + req->best_parent_rate); + + if (best_rate < 0) + return best_rate; + + req->rate = best_rate; + return 0; +} + +/* No modification of hardware as we have the flag CLK_SET_PARENT_GATE set */ +static int clk_generated_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_generated *gck = to_clk_generated(hw); + + if (index >= clk_hw_get_num_parents(hw)) + return -EINVAL; + + gck->parent_id = index; + return 0; +} + +static u8 clk_generated_get_parent(struct clk_hw *hw) +{ + struct clk_generated *gck = to_clk_generated(hw); + + return gck->parent_id; +} + +/* No modification of hardware as we have the flag CLK_SET_RATE_GATE set */ +static int clk_generated_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct clk_generated *gck = to_clk_generated(hw); + u32 div; + + if (!rate) + return -EINVAL; + + if (gck->range.max && rate > gck->range.max) + return -EINVAL; + + div = DIV_ROUND_CLOSEST(parent_rate, rate); + if (div > GENERATED_MAX_DIV + 1 || !div) + return -EINVAL; + + gck->gckdiv = div - 1; + return 0; +} + +static const struct clk_ops generated_ops = { + .enable = clk_generated_enable, + .disable = clk_generated_disable, + .is_enabled = clk_generated_is_enabled, + .recalc_rate = clk_generated_recalc_rate, + .determine_rate = clk_generated_determine_rate, + .get_parent = clk_generated_get_parent, + .set_parent = clk_generated_set_parent, + .set_rate = clk_generated_set_rate, +}; + +/** + * clk_generated_startup - Initialize a given clock to its default parent and + * divisor parameter. + * + * @gck: Generated clock to set the startup parameters for. + * + * Take parameters from the hardware and update local clock configuration + * accordingly. + */ +static void clk_generated_startup(struct clk_generated *gck) +{ + u32 tmp; + unsigned long flags; + + spin_lock_irqsave(gck->lock, flags); + regmap_write(gck->regmap, AT91_PMC_PCR, + (gck->id & AT91_PMC_PCR_PID_MASK)); + regmap_read(gck->regmap, AT91_PMC_PCR, &tmp); + spin_unlock_irqrestore(gck->lock, flags); + + gck->parent_id = (tmp & AT91_PMC_PCR_GCKCSS_MASK) + >> AT91_PMC_PCR_GCKCSS_OFFSET; + gck->gckdiv = (tmp & AT91_PMC_PCR_GCKDIV_MASK) + >> AT91_PMC_PCR_GCKDIV_OFFSET; +} + +static struct clk_hw * __init +at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock, + const char *name, const char **parent_names, + u8 num_parents, u8 id, + const struct clk_range *range) +{ + struct clk_generated *gck; + struct clk_init_data init; + struct clk_hw *hw; + int ret; + + gck = kzalloc(sizeof(*gck), GFP_KERNEL); + if (!gck) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &generated_ops; + init.parent_names = parent_names; + init.num_parents = num_parents; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; + + gck->id = id; + gck->hw.init = &init; + gck->regmap = regmap; + gck->lock = lock; + gck->range = *range; + + hw = &gck->hw; + ret = clk_hw_register(NULL, &gck->hw); + if (ret) { + kfree(gck); + hw = ERR_PTR(ret); + } else + clk_generated_startup(gck); + + return hw; +} + +static void __init of_sama5d2_clk_generated_setup(struct device_node *np) +{ + int num; + u32 id; + const char *name; + struct clk_hw *hw; + unsigned int num_parents; + const char *parent_names[GENERATED_SOURCE_MAX]; + struct device_node *gcknp; + struct clk_range range = CLK_RANGE(0, 0); + struct regmap *regmap; + + num_parents = of_clk_get_parent_count(np); + if (num_parents == 0 || num_parents > GENERATED_SOURCE_MAX) + return; + + of_clk_parent_fill(np, parent_names, num_parents); + + num = of_get_child_count(np); + if (!num || num > PERIPHERAL_MAX) + return; + + regmap = syscon_node_to_regmap(of_get_parent(np)); + if (IS_ERR(regmap)) + return; + + for_each_child_of_node(np, gcknp) { + if (of_property_read_u32(gcknp, "reg", &id)) + continue; + + if (id < PERIPHERAL_ID_MIN || id >= PERIPHERAL_MAX) + continue; + + if (of_property_read_string(np, "clock-output-names", &name)) + name = gcknp->name; + + of_at91_get_clk_range(gcknp, "atmel,clk-output-range", + &range); + + hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, name, + parent_names, num_parents, + id, &range); + if (IS_ERR(hw)) + continue; + + of_clk_add_hw_provider(gcknp, of_clk_hw_simple_get, hw); + } +} +CLK_OF_DECLARE(of_sama5d2_clk_generated_setup, "atmel,sama5d2-clk-generated", + of_sama5d2_clk_generated_setup); diff --git a/drivers/clk/at91/clk-h32mx.c b/drivers/clk/at91/clk-h32mx.c new file mode 100644 index 0000000000..e0daa4a31f --- /dev/null +++ b/drivers/clk/at91/clk-h32mx.c @@ -0,0 +1,125 @@ +/* + * clk-h32mx.c + * + * Copyright (C) 2014 Atmel + * + * Alexandre Belloni <alexandre.belloni@free-electrons.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include <linux/clk/at91_pmc.h> +#include <linux/of.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> + +#include "pmc.h" + +#define H32MX_MAX_FREQ 90000000 + +struct clk_sama5d4_h32mx { + struct clk_hw hw; + struct regmap *regmap; +}; + +#define to_clk_sama5d4_h32mx(hw) container_of(hw, struct clk_sama5d4_h32mx, hw) + +static unsigned long clk_sama5d4_h32mx_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_sama5d4_h32mx *h32mxclk = to_clk_sama5d4_h32mx(hw); + unsigned int mckr; + + regmap_read(h32mxclk->regmap, AT91_PMC_MCKR, &mckr); + if (mckr & AT91_PMC_H32MXDIV) + return parent_rate / 2; + + if (parent_rate > H32MX_MAX_FREQ) + pr_warn("H32MX clock is too fast\n"); + return parent_rate; +} + +static long clk_sama5d4_h32mx_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned long div; + + if (rate > *parent_rate) + return *parent_rate; + div = *parent_rate / 2; + if (rate < div) + return div; + + if (rate - div < *parent_rate - rate) + return div; + + return *parent_rate; +} + +static int clk_sama5d4_h32mx_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_sama5d4_h32mx *h32mxclk = to_clk_sama5d4_h32mx(hw); + u32 mckr = 0; + + if (parent_rate != rate && (parent_rate / 2) != rate) + return -EINVAL; + + if ((parent_rate / 2) == rate) + mckr = AT91_PMC_H32MXDIV; + + regmap_update_bits(h32mxclk->regmap, AT91_PMC_MCKR, + AT91_PMC_H32MXDIV, mckr); + + return 0; +} + +static const struct clk_ops h32mx_ops = { + .recalc_rate = clk_sama5d4_h32mx_recalc_rate, + .round_rate = clk_sama5d4_h32mx_round_rate, + .set_rate = clk_sama5d4_h32mx_set_rate, +}; + +static void __init of_sama5d4_clk_h32mx_setup(struct device_node *np) +{ + struct clk_sama5d4_h32mx *h32mxclk; + struct clk_init_data init; + const char *parent_name; + struct regmap *regmap; + int ret; + + regmap = syscon_node_to_regmap(of_get_parent(np)); + if (IS_ERR(regmap)) + return; + + h32mxclk = kzalloc(sizeof(*h32mxclk), GFP_KERNEL); + if (!h32mxclk) + return; + + parent_name = of_clk_get_parent_name(np, 0); + + init.name = np->name; + init.ops = &h32mx_ops; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + init.flags = CLK_SET_RATE_GATE; + + h32mxclk->hw.init = &init; + h32mxclk->regmap = regmap; + + ret = clk_hw_register(NULL, &h32mxclk->hw); + if (ret) { + kfree(h32mxclk); + return; + } + + of_clk_add_hw_provider(np, of_clk_hw_simple_get, &h32mxclk->hw); +} +CLK_OF_DECLARE(of_sama5d4_clk_h32mx_setup, "atmel,sama5d4-clk-h32mx", + of_sama5d4_clk_h32mx_setup); diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c new file mode 100644 index 0000000000..55bc618a37 --- /dev/null +++ b/drivers/clk/at91/clk-main.c @@ -0,0 +1,576 @@ +/* + * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ +#include <common.h> +#include <clock.h> +#include <of.h> +#include <linux/list.h> +#include <linux/clk.h> +#include <linux/clk/at91_pmc.h> +#include <mfd/syscon.h> +#include <regmap.h> + +#include "pmc.h" + +#define SLOW_CLOCK_FREQ 32768 +#define MAINF_DIV 16 +#define MAINFRDY_TIMEOUT (((MAINF_DIV + 1) * SECOND) / \ + SLOW_CLOCK_FREQ) +#define MAINF_LOOP_MIN_WAIT (SECOND / SLOW_CLOCK_FREQ) +#define MAINF_LOOP_MAX_WAIT MAINFRDY_TIMEOUT + +#define MOR_KEY_MASK (0xff << 16) + +struct clk_main_osc { + struct clk clk; + struct regmap *regmap; + const char *parent; +}; + +#define to_clk_main_osc(clk) container_of(clk, struct clk_main_osc, clk) + +struct clk_main_rc_osc { + struct clk clk; + struct regmap *regmap; + unsigned long frequency; +}; + +#define to_clk_main_rc_osc(clk) container_of(clk, struct clk_main_rc_osc, clk) + +struct clk_rm9200_main { + struct clk clk; + struct regmap *regmap; + const char *parent; +}; + +#define to_clk_rm9200_main(clk) container_of(clk, struct clk_rm9200_main, clk) + +struct clk_sam9x5_main { + struct clk clk; + struct regmap *regmap; + u8 parent; +}; + +#define to_clk_sam9x5_main(clk) container_of(clk, struct clk_sam9x5_main, clk) + +static inline bool clk_main_osc_ready(struct regmap *regmap) +{ + unsigned int status; + + regmap_read(regmap, AT91_PMC_SR, &status); + + return status & AT91_PMC_MOSCS; +} + +static int clk_main_osc_enable(struct clk *clk) +{ + struct clk_main_osc *osc = to_clk_main_osc(clk); + struct regmap *regmap = osc->regmap; + u32 tmp; + + regmap_read(regmap, AT91_CKGR_MOR, &tmp); + tmp &= ~MOR_KEY_MASK; + + if (tmp & AT91_PMC_OSCBYPASS) + return 0; + + if (!(tmp & AT91_PMC_MOSCEN)) { + tmp |= AT91_PMC_MOSCEN | AT91_PMC_KEY; + regmap_write(regmap, AT91_CKGR_MOR, tmp); + } + + while (!clk_main_osc_ready(regmap)) + barrier(); + + return 0; +} + +static void clk_main_osc_disable(struct clk *clk) +{ + struct clk_main_osc *osc = to_clk_main_osc(clk); + struct regmap *regmap = osc->regmap; + u32 tmp; + + regmap_read(regmap, AT91_CKGR_MOR, &tmp); + if (tmp & AT91_PMC_OSCBYPASS) + return; + + if (!(tmp & AT91_PMC_MOSCEN)) + return; + + tmp &= ~(AT91_PMC_KEY | AT91_PMC_MOSCEN); + regmap_write(regmap, AT91_CKGR_MOR, tmp | AT91_PMC_KEY); +} + +static int clk_main_osc_is_enabled(struct clk *clk) +{ + struct clk_main_osc *osc = to_clk_main_osc(clk); + struct regmap *regmap = osc->regmap; + u32 tmp, status; + + regmap_read(regmap, AT91_CKGR_MOR, &tmp); + if (tmp & AT91_PMC_OSCBYPASS) + return 1; + + regmap_read(regmap, AT91_PMC_SR, &status); + + return (status & AT91_PMC_MOSCS) && (tmp & AT91_PMC_MOSCEN); +} + +static const struct clk_ops main_osc_ops = { + .enable = clk_main_osc_enable, + .disable = clk_main_osc_disable, + .is_enabled = clk_main_osc_is_enabled, +}; + +static struct clk * +at91_clk_register_main_osc(struct regmap *regmap, + const char *name, + const char *parent_name, + bool bypass) +{ + struct clk_main_osc *osc; + int ret; + + if (!name || !parent_name) + return ERR_PTR(-EINVAL); + + osc = xzalloc(sizeof(*osc)); + + osc->parent = parent_name; + osc->clk.name = name; + osc->clk.ops = &main_osc_ops; + osc->clk.parent_names = &osc->parent; + osc->clk.num_parents = 1; + osc->regmap = regmap; + + if (bypass) + regmap_write_bits(regmap, + AT91_CKGR_MOR, MOR_KEY_MASK | + AT91_PMC_MOSCEN, + AT91_PMC_OSCBYPASS | AT91_PMC_KEY); + + ret = clk_register(&osc->clk); + if (ret) { + free(osc); + return ERR_PTR(ret); + } + + return &osc->clk; +} + +static int of_at91rm9200_clk_main_osc_setup(struct device_node *np) +{ + struct clk *clk; + const char *name = np->name; + const char *parent_name; + struct regmap *regmap; + bool bypass; + + of_property_read_string(np, "clock-output-names", &name); + bypass = of_property_read_bool(np, "atmel,osc-bypass"); + parent_name = of_clk_get_parent_name(np, 0); + + regmap = syscon_node_to_regmap(of_get_parent(np)); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + clk = at91_clk_register_main_osc(regmap, name, parent_name, bypass); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + return of_clk_add_provider(np, of_clk_src_simple_get, clk); +} +CLK_OF_DECLARE(at91rm9200_clk_main_osc, "atmel,at91rm9200-clk-main-osc", + of_at91rm9200_clk_main_osc_setup); + +static bool clk_main_rc_osc_ready(struct regmap *regmap) +{ + unsigned int status; + + regmap_read(regmap, AT91_PMC_SR, &status); + + return status & AT91_PMC_MOSCRCS; +} + +static int clk_main_rc_osc_enable(struct clk *clk) +{ + struct clk_main_rc_osc *osc = to_clk_main_rc_osc(clk); + struct regmap *regmap = osc->regmap; + unsigned int mor; + + regmap_read(regmap, AT91_CKGR_MOR, &mor); + + if (!(mor & AT91_PMC_MOSCRCEN)) + regmap_write_bits(regmap, AT91_CKGR_MOR, + MOR_KEY_MASK | AT91_PMC_MOSCRCEN, + AT91_PMC_MOSCRCEN | AT91_PMC_KEY); + + while (!clk_main_rc_osc_ready(regmap)) + barrier(); + + return 0; +} + +static void clk_main_rc_osc_disable(struct clk *clk) +{ + struct clk_main_rc_osc *osc = to_clk_main_rc_osc(clk); + struct regmap *regmap = osc->regmap; + unsigned int mor; + + regmap_read(regmap, AT91_CKGR_MOR, &mor); + + if (!(mor & AT91_PMC_MOSCRCEN)) + return; + + regmap_write_bits(regmap, AT91_CKGR_MOR, + MOR_KEY_MASK | AT91_PMC_MOSCRCEN, AT91_PMC_KEY); +} + +static int clk_main_rc_osc_is_enabled(struct clk *clk) +{ + struct clk_main_rc_osc *osc = to_clk_main_rc_osc(clk); + struct regmap *regmap = osc->regmap; + unsigned int mor, status; + + regmap_read(regmap, AT91_CKGR_MOR, &mor); + regmap_read(regmap, AT91_PMC_SR, &status); + + return (mor & AT91_PMC_MOSCRCEN) && (status & AT91_PMC_MOSCRCS); +} + +static unsigned long clk_main_rc_osc_recalc_rate(struct clk *clk, + unsigned long parent_rate) +{ + struct clk_main_rc_osc *osc = to_clk_main_rc_osc(clk); + + return osc->frequency; +} + +static const struct clk_ops main_rc_osc_ops = { + .enable = clk_main_rc_osc_enable, + .disable = clk_main_rc_osc_disable, + .is_enabled = clk_main_rc_osc_is_enabled, + .recalc_rate = clk_main_rc_osc_recalc_rate, +}; + +static struct clk * +at91_clk_register_main_rc_osc(struct regmap *regmap, + const char *name, + u32 frequency) +{ + int ret; + struct clk_main_rc_osc *osc; + + if (!name || !frequency) + return ERR_PTR(-EINVAL); + + osc = xzalloc(sizeof(*osc)); + + osc->clk.name = name; + osc->clk.ops = &main_rc_osc_ops; + osc->clk.parent_names = NULL; + osc->clk.num_parents = 0; + + osc->regmap = regmap; + osc->frequency = frequency; + + ret = clk_register(&osc->clk); + if (ret) { + kfree(osc); + return ERR_PTR(ret); + } + + return &osc->clk; +} + +static int of_at91sam9x5_clk_main_rc_osc_setup(struct device_node *np) +{ + struct clk *clk; + u32 frequency = 0; + const char *name = np->name; + struct regmap *regmap; + + of_property_read_string(np, "clock-output-names", &name); + of_property_read_u32(np, "clock-frequency", &frequency); + + regmap = syscon_node_to_regmap(of_get_parent(np)); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + clk = at91_clk_register_main_rc_osc(regmap, name, frequency); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + return of_clk_add_provider(np, of_clk_src_simple_get, clk); +} +CLK_OF_DECLARE(at91sam9x5_clk_main_rc_osc, "atmel,at91sam9x5-clk-main-rc-osc", + of_at91sam9x5_clk_main_rc_osc_setup); + + +static int clk_main_probe_frequency(struct regmap *regmap) +{ + unsigned int mcfr; + uint64_t start = get_time_ns(); + + do { + regmap_read(regmap, AT91_CKGR_MCFR, &mcfr); + if (mcfr & AT91_PMC_MAINRDY) + return 0; + } while (!is_timeout(start, MAINFRDY_TIMEOUT * USECOND)); + + return -ETIMEDOUT; +} + +static unsigned long clk_main_recalc_rate(struct regmap *regmap, + unsigned long parent_rate) +{ + unsigned int mcfr; + + if (parent_rate) + return parent_rate; + + pr_warn("Main crystal frequency not set, using approximate value\n"); + regmap_read(regmap, AT91_CKGR_MCFR, &mcfr); + if (!(mcfr & AT91_PMC_MAINRDY)) + return 0; + + return ((mcfr & AT91_PMC_MAINF) * SLOW_CLOCK_FREQ) / MAINF_DIV; +} + +static int clk_rm9200_main_enable(struct clk *clk) +{ + struct clk_rm9200_main *clkmain = to_clk_rm9200_main(clk); + + return clk_main_probe_frequency(clkmain->regmap); +} + +static int clk_rm9200_main_is_enabled(struct clk *clk) +{ + struct clk_rm9200_main *clkmain = to_clk_rm9200_main(clk); + unsigned int status; + + regmap_read(clkmain->regmap, AT91_CKGR_MCFR, &status); + + return status & AT91_PMC_MAINRDY ? 1 : 0; +} + +static unsigned long clk_rm9200_main_recalc_rate(struct clk *clk, + unsigned long parent_rate) +{ + struct clk_rm9200_main *clkmain = to_clk_rm9200_main(clk); + + return clk_main_recalc_rate(clkmain->regmap, parent_rate); +} + +static const struct clk_ops rm9200_main_ops = { + .enable = clk_rm9200_main_enable, + .is_enabled = clk_rm9200_main_is_enabled, + .recalc_rate = clk_rm9200_main_recalc_rate, +}; + +static struct clk * +at91_clk_register_rm9200_main(struct regmap *regmap, + const char *name, + const char *parent_name) +{ + int ret; + struct clk_rm9200_main *clkmain; + + if (!name) + return ERR_PTR(-EINVAL); + + if (!parent_name) + return ERR_PTR(-EINVAL); + + clkmain = xzalloc(sizeof(*clkmain)); + + clkmain->clk.name = name; + clkmain->clk.ops = &rm9200_main_ops; + clkmain->clk.parent_names = &clkmain->parent; + clkmain->clk.num_parents = 1; + clkmain->regmap = regmap; + + ret = clk_register(&clkmain->clk); + if (ret) { + kfree(clkmain); + return ERR_PTR(ret); + } + + return &clkmain->clk; +} + +static int of_at91rm9200_clk_main_setup(struct device_node *np) +{ + struct clk *clk; + const char *parent_name; + const char *name = np->name; + struct regmap *regmap; + + parent_name = of_clk_get_parent_name(np, 0); + of_property_read_string(np, "clock-output-names", &name); + + regmap = syscon_node_to_regmap(of_get_parent(np)); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + clk = at91_clk_register_rm9200_main(regmap, name, parent_name); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + return of_clk_add_provider(np, of_clk_src_simple_get, clk); +} +CLK_OF_DECLARE(at91rm9200_clk_main, "atmel,at91rm9200-clk-main", + of_at91rm9200_clk_main_setup); + +static inline bool clk_sam9x5_main_ready(struct regmap *regmap) +{ + unsigned int status; + + regmap_read(regmap, AT91_PMC_SR, &status); + + return status & AT91_PMC_MOSCSELS ? 1 : 0; +} + +static int clk_sam9x5_main_enable(struct clk *clk) +{ + struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk); + struct regmap *regmap = clkmain->regmap; + + while (!clk_sam9x5_main_ready(regmap)) + barrier(); + + return clk_main_probe_frequency(regmap); +} + +static int clk_sam9x5_main_is_enabled(struct clk *clk) +{ + struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk); + + return clk_sam9x5_main_ready(clkmain->regmap); +} + +static unsigned long clk_sam9x5_main_recalc_rate(struct clk *clk, + unsigned long parent_rate) +{ + struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk); + + return clk_main_recalc_rate(clkmain->regmap, parent_rate); +} + +static int clk_sam9x5_main_set_parent(struct clk *clk, u8 index) +{ + struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk); + struct regmap *regmap = clkmain->regmap; + unsigned int tmp; + + if (index > 1) + return -EINVAL; + + regmap_read(regmap, AT91_CKGR_MOR, &tmp); + tmp &= ~MOR_KEY_MASK; + + if (index && !(tmp & AT91_PMC_MOSCSEL)) + regmap_write(regmap, AT91_CKGR_MOR, tmp | AT91_PMC_MOSCSEL); + else if (!index && (tmp & AT91_PMC_MOSCSEL)) + regmap_write(regmap, AT91_CKGR_MOR, tmp & ~AT91_PMC_MOSCSEL); + + while (!clk_sam9x5_main_ready(regmap)) + barrier(); + + return 0; +} + +static int clk_sam9x5_main_get_parent(struct clk *clk) +{ + struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk); + unsigned int status; + + regmap_read(clkmain->regmap, AT91_CKGR_MOR, &status); + + return status & AT91_PMC_MOSCEN ? 1 : 0; +} + +static const struct clk_ops sam9x5_main_ops = { + .enable = clk_sam9x5_main_enable, + .is_enabled = clk_sam9x5_main_is_enabled, + .recalc_rate = clk_sam9x5_main_recalc_rate, + .set_parent = clk_sam9x5_main_set_parent, + .get_parent = clk_sam9x5_main_get_parent, +}; + +static struct clk * +at91_clk_register_sam9x5_main(struct regmap *regmap, + const char *name, + const char **parent_names, + int num_parents) +{ + int ret; + unsigned int status; + size_t parents_array_size; + struct clk_sam9x5_main *clkmain; + + if (!name) + return ERR_PTR(-EINVAL); + + if (!parent_names || !num_parents) + return ERR_PTR(-EINVAL); + + clkmain = xzalloc(sizeof(*clkmain)); + + clkmain->clk.name = name; + clkmain->clk.ops = &sam9x5_main_ops; + parents_array_size = num_parents * sizeof (clkmain->clk.parent_names[0]); + clkmain->clk.parent_names = xzalloc(parents_array_size); + memcpy(clkmain->clk.parent_names, parent_names, parents_array_size); + clkmain->clk.num_parents = num_parents; + + /* init.flags = CLK_SET_PARENT_GATE; */ + + clkmain->regmap = regmap; + regmap_read(clkmain->regmap, AT91_CKGR_MOR, &status); + clkmain->parent = status & AT91_PMC_MOSCEN ? 1 : 0; + + ret = clk_register(&clkmain->clk); + if (ret) { + kfree(clkmain); + return ERR_PTR(ret); + } + + return &clkmain->clk; +} + +static int of_at91sam9x5_clk_main_setup(struct device_node *np) +{ + struct clk *clk; + const char *parent_names[2]; + unsigned int num_parents; + const char *name = np->name; + struct regmap *regmap; + + num_parents = of_clk_get_parent_count(np); + if (num_parents == 0 || num_parents > 2) + return -EINVAL; + + of_clk_parent_fill(np, parent_names, num_parents); + regmap = syscon_node_to_regmap(of_get_parent(np)); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + of_property_read_string(np, "clock-output-names", &name); + + clk = at91_clk_register_sam9x5_main(regmap, name, parent_names, + num_parents); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + return of_clk_add_provider(np, of_clk_src_simple_get, clk); +} +CLK_OF_DECLARE(at91sam9x5_clk_main, "atmel,at91sam9x5-clk-main", + of_at91sam9x5_clk_main_setup); diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c new file mode 100644 index 0000000000..b3a50ce542 --- /dev/null +++ b/drivers/clk/at91/clk-master.c @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ +#include <common.h> +#include <clock.h> +#include <of.h> +#include <linux/list.h> +#include <linux/clk.h> +#include <linux/clk/at91_pmc.h> +#include <mfd/syscon.h> +#include <regmap.h> + +#include "pmc.h" + +#define MASTER_SOURCE_MAX 4 + +#define MASTER_PRES_MASK 0x7 +#define MASTER_PRES_MAX MASTER_PRES_MASK +#define MASTER_DIV_SHIFT 8 +#define MASTER_DIV_MASK 0x3 + +struct clk_master_characteristics { + struct clk_range output; + u32 divisors[4]; + u8 have_div3_pres; +}; + +struct clk_master_layout { + u32 mask; + u8 pres_shift; +}; + +#define to_clk_master(clk) container_of(clk, struct clk_master, clk) + +struct clk_master { + struct clk clk; + struct regmap *regmap; + const struct clk_master_layout *layout; + const struct clk_master_characteristics *characteristics; + const char *parents[MASTER_SOURCE_MAX]; +}; + +static inline bool clk_master_ready(struct regmap *regmap) +{ + unsigned int status; + + regmap_read(regmap, AT91_PMC_SR, &status); + + return status & AT91_PMC_MCKRDY ? 1 : 0; +} + +static int clk_master_enable(struct clk *clk) +{ + struct clk_master *master = to_clk_master(clk); + + while (!clk_master_ready(master->regmap)) + barrier(); + + return 0; +} + +static int clk_master_is_enabled(struct clk *clk) +{ + struct clk_master *master = to_clk_master(clk); + + return clk_master_ready(master->regmap); +} + +static unsigned long clk_master_recalc_rate(struct clk *clk, + unsigned long parent_rate) +{ + u8 pres; + u8 div; + unsigned long rate = parent_rate; + struct clk_master *master = to_clk_master(clk); + const struct clk_master_layout *layout = master->layout; + const struct clk_master_characteristics *characteristics = + master->characteristics; + unsigned int mckr; + + regmap_read(master->regmap, AT91_PMC_MCKR, &mckr); + mckr &= layout->mask; + + pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK; + div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK; + + if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX) + rate /= 3; + else + rate >>= pres; + + rate /= characteristics->divisors[div]; + + if (rate < characteristics->output.min) + pr_warn("master clk is underclocked"); + else if (rate > characteristics->output.max) + pr_warn("master clk is overclocked"); + + return rate; +} + +static int clk_master_get_parent(struct clk *clk) +{ + struct clk_master *master = to_clk_master(clk); + unsigned int mckr; + + regmap_read(master->regmap, AT91_PMC_MCKR, &mckr); + + return mckr & AT91_PMC_CSS; +} + +static const struct clk_ops master_ops = { + .enable = clk_master_enable, + .is_enabled = clk_master_is_enabled, + .recalc_rate = clk_master_recalc_rate, + .get_parent = clk_master_get_parent, +}; + +static struct clk * +at91_clk_register_master(struct regmap *regmap, + const char *name, int num_parents, + const char **parent_names, + const struct clk_master_layout *layout, + const struct clk_master_characteristics *characteristics) +{ + int ret; + const size_t parent_names_size = num_parents * sizeof(parent_names[0]); + struct clk_master *master; + + if (!name || !num_parents || !parent_names) + return ERR_PTR(-EINVAL); + + master = xzalloc(sizeof(*master)); + + master->clk.name = name; + master->clk.ops = &master_ops; + memcpy(master->parents, parent_names, parent_names_size); + master->clk.parent_names = master->parents; + master->clk.num_parents = num_parents; + + master->layout = layout; + master->characteristics = characteristics; + master->regmap = regmap; + + ret = clk_register(&master->clk); + if (ret) { + kfree(master); + return ERR_PTR(ret); + } + + return &master->clk; +} + + +static const struct clk_master_layout at91rm9200_master_layout = { + .mask = 0x31F, + .pres_shift = 2, +}; + +static const struct clk_master_layout at91sam9x5_master_layout = { + .mask = 0x373, + .pres_shift = 4, +}; + + +static struct clk_master_characteristics * +of_at91_clk_master_get_characteristics(struct device_node *np) +{ + struct clk_master_characteristics *characteristics; + + characteristics = xzalloc(sizeof(*characteristics)); + + if (of_at91_get_clk_range(np, "atmel,clk-output-range", &characteristics->output)) + goto out_free_characteristics; + + of_property_read_u32_array(np, "atmel,clk-divisors", + characteristics->divisors, 4); + + characteristics->have_div3_pres = + of_property_read_bool(np, "atmel,master-clk-have-div3-pres"); + + return characteristics; + +out_free_characteristics: + kfree(characteristics); + return NULL; +} + +static int +of_at91_clk_master_setup(struct device_node *np, + const struct clk_master_layout *layout) +{ + struct clk *clk; + unsigned int num_parents; + const char *parent_names[MASTER_SOURCE_MAX]; + const char *name = np->name; + struct clk_master_characteristics *characteristics; + struct regmap *regmap; + + num_parents = of_clk_get_parent_count(np); + if (num_parents == 0 || num_parents > MASTER_SOURCE_MAX) + return -EINVAL; + + of_clk_parent_fill(np, parent_names, num_parents); + + of_property_read_string(np, "clock-output-names", &name); + + characteristics = of_at91_clk_master_get_characteristics(np); + if (!characteristics) + return -EINVAL; + + regmap = syscon_node_to_regmap(of_get_parent(np)); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + clk = at91_clk_register_master(regmap, name, num_parents, + parent_names, layout, + characteristics); + if (IS_ERR(clk)) { + kfree(characteristics); + return PTR_ERR(clk); + } + + return of_clk_add_provider(np, of_clk_src_simple_get, clk); +} + +static void __init of_at91rm9200_clk_master_setup(struct device_node *np) +{ + of_at91_clk_master_setup(np, &at91rm9200_master_layout); +} +CLK_OF_DECLARE(at91rm9200_clk_master, "atmel,at91rm9200-clk-master", + of_at91rm9200_clk_master_setup); + +static void __init of_at91sam9x5_clk_master_setup(struct device_node *np) +{ + of_at91_clk_master_setup(np, &at91sam9x5_master_layout); +} +CLK_OF_DECLARE(at91sam9x5_clk_master, "atmel,at91sam9x5-clk-master", + of_at91sam9x5_clk_master_setup); diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c new file mode 100644 index 0000000000..bbe6ffac69 --- /dev/null +++ b/drivers/clk/at91/clk-peripheral.c @@ -0,0 +1,429 @@ +/* + * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <common.h> +#include <clock.h> +#include <of.h> +#include <linux/list.h> +#include <linux/clk.h> +#include <linux/clk/at91_pmc.h> +#include <mfd/syscon.h> +#include <regmap.h> + +#include "pmc.h" + +#define PERIPHERAL_MAX 64 + +#define PERIPHERAL_AT91RM9200 0 +#define PERIPHERAL_AT91SAM9X5 1 + +#define PERIPHERAL_ID_MIN 2 +#define PERIPHERAL_ID_MAX 31 +#define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX)) + +#define PERIPHERAL_RSHIFT_MASK 0x3 +#define PERIPHERAL_RSHIFT(val) (((val) >> 16) & PERIPHERAL_RSHIFT_MASK) + +#define PERIPHERAL_MAX_SHIFT 3 + +struct clk_peripheral { + struct clk clk; + struct regmap *regmap; + u32 id; + const char *parent; +}; + +#define to_clk_peripheral(clk) container_of(clk, struct clk_peripheral, clk) + +struct clk_sam9x5_peripheral { + struct clk clk; + struct regmap *regmap; + struct clk_range range; + u32 id; + u32 div; + bool auto_div; + const char *parent; +}; + +#define to_clk_sam9x5_peripheral(clk) \ + container_of(clk, struct clk_sam9x5_peripheral, clk) + +static int clk_peripheral_enable(struct clk *clk) +{ + struct clk_peripheral *periph = to_clk_peripheral(clk); + int offset = AT91_PMC_PCER; + u32 id = periph->id; + + if (id < PERIPHERAL_ID_MIN) + return 0; + if (id > PERIPHERAL_ID_MAX) + offset = AT91_PMC_PCER1; + regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id)); + + return 0; +} + +static void clk_peripheral_disable(struct clk *clk) +{ + struct clk_peripheral *periph = to_clk_peripheral(clk); + int offset = AT91_PMC_PCDR; + u32 id = periph->id; + + if (id < PERIPHERAL_ID_MIN) + return; + if (id > PERIPHERAL_ID_MAX) + offset = AT91_PMC_PCDR1; + regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id)); +} + +static int clk_peripheral_is_enabled(struct clk *clk) +{ + struct clk_peripheral *periph = to_clk_peripheral(clk); + int offset = AT91_PMC_PCSR; + unsigned int status; + u32 id = periph->id; + + if (id < PERIPHERAL_ID_MIN) + return 1; + if (id > PERIPHERAL_ID_MAX) + offset = AT91_PMC_PCSR1; + regmap_read(periph->regmap, offset, &status); + + return status & PERIPHERAL_MASK(id) ? 1 : 0; +} + +static const struct clk_ops peripheral_ops = { + .enable = clk_peripheral_enable, + .disable = clk_peripheral_disable, + .is_enabled = clk_peripheral_is_enabled, +}; + +static struct clk * +at91_clk_register_peripheral(struct regmap *regmap, const char *name, + const char *parent_name, u32 id) +{ + int ret; + struct clk_peripheral *periph; + + if (!name || !parent_name || id > PERIPHERAL_ID_MAX) + return ERR_PTR(-EINVAL); + + periph = xzalloc(sizeof(*periph)); + + periph->clk.name = name; + periph->clk.ops = &peripheral_ops; + + if (parent_name) { + periph->parent = parent_name; + periph->clk.parent_names = &periph->parent; + periph->clk.num_parents = 1; + } + + periph->id = id; + periph->regmap = regmap; + + ret = clk_register(&periph->clk); + if (ret) { + kfree(periph); + return ERR_PTR(ret); + } + + return &periph->clk; +} + +static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph) +{ + struct clk *parent; + unsigned long parent_rate; + int shift = 0; + + if (!periph->auto_div) + return; + + if (periph->range.max) { + parent = clk_get_parent(&periph->clk); + parent_rate = clk_get_rate(parent); + if (!parent_rate) + return; + + for (; shift < PERIPHERAL_MAX_SHIFT; shift++) { + if (parent_rate >> shift <= periph->range.max) + break; + } + } + + periph->auto_div = false; + periph->div = shift; +} + +static int clk_sam9x5_peripheral_enable(struct clk *clk) +{ + struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk); + + if (periph->id < PERIPHERAL_ID_MIN) + return 0; + + regmap_write(periph->regmap, AT91_PMC_PCR, + (periph->id & AT91_PMC_PCR_PID_MASK)); + regmap_write_bits(periph->regmap, AT91_PMC_PCR, + AT91_PMC_PCR_DIV_MASK | AT91_PMC_PCR_CMD | + AT91_PMC_PCR_EN, + AT91_PMC_PCR_DIV(periph->div) | + AT91_PMC_PCR_CMD | + AT91_PMC_PCR_EN); + + return 0; +} + +static void clk_sam9x5_peripheral_disable(struct clk *clk) +{ + struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk); + + if (periph->id < PERIPHERAL_ID_MIN) + return; + + regmap_write(periph->regmap, AT91_PMC_PCR, + (periph->id & AT91_PMC_PCR_PID_MASK)); + regmap_write_bits(periph->regmap, AT91_PMC_PCR, + AT91_PMC_PCR_EN | AT91_PMC_PCR_CMD, + AT91_PMC_PCR_CMD); +} + +static int clk_sam9x5_peripheral_is_enabled(struct clk *clk) +{ + struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk); + unsigned int status; + + if (periph->id < PERIPHERAL_ID_MIN) + return 1; + + regmap_write(periph->regmap, AT91_PMC_PCR, + (periph->id & AT91_PMC_PCR_PID_MASK)); + regmap_read(periph->regmap, AT91_PMC_PCR, &status); + + return status & AT91_PMC_PCR_EN ? 1 : 0; +} + +static unsigned long +clk_sam9x5_peripheral_recalc_rate(struct clk *clk, + unsigned long parent_rate) +{ + struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk); + unsigned int status; + + if (periph->id < PERIPHERAL_ID_MIN) + return parent_rate; + + regmap_write(periph->regmap, AT91_PMC_PCR, + (periph->id & AT91_PMC_PCR_PID_MASK)); + regmap_read(periph->regmap, AT91_PMC_PCR, &status); + + if (status & AT91_PMC_PCR_EN) { + periph->div = PERIPHERAL_RSHIFT(status); + periph->auto_div = false; + } else { + clk_sam9x5_peripheral_autodiv(periph); + } + + return parent_rate >> periph->div; +} + +static long clk_sam9x5_peripheral_round_rate(struct clk *clk, + unsigned long rate, + unsigned long *parent_rate) +{ + int shift = 0; + unsigned long best_rate; + unsigned long best_diff; + unsigned long cur_rate = *parent_rate; + unsigned long cur_diff; + struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk); + + if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) + return *parent_rate; + + if (periph->range.max) { + for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) { + cur_rate = *parent_rate >> shift; + if (cur_rate <= periph->range.max) + break; + } + } + + if (rate >= cur_rate) + return cur_rate; + + best_diff = cur_rate - rate; + best_rate = cur_rate; + for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) { + cur_rate = *parent_rate >> shift; + if (cur_rate < rate) + cur_diff = rate - cur_rate; + else + cur_diff = cur_rate - rate; + + if (cur_diff < best_diff) { + best_diff = cur_diff; + best_rate = cur_rate; + } + + if (!best_diff || cur_rate < rate) + break; + } + + return best_rate; +} + +static int clk_sam9x5_peripheral_set_rate(struct clk *clk, + unsigned long rate, + unsigned long parent_rate) +{ + int shift; + struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk); + if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) { + if (parent_rate == rate) + return 0; + else + return -EINVAL; + } + + if (periph->range.max && rate > periph->range.max) + return -EINVAL; + + for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) { + if (parent_rate >> shift == rate) { + periph->auto_div = false; + periph->div = shift; + return 0; + } + } + + return -EINVAL; +} + +static const struct clk_ops sam9x5_peripheral_ops = { + .enable = clk_sam9x5_peripheral_enable, + .disable = clk_sam9x5_peripheral_disable, + .is_enabled = clk_sam9x5_peripheral_is_enabled, + .recalc_rate = clk_sam9x5_peripheral_recalc_rate, + .round_rate = clk_sam9x5_peripheral_round_rate, + .set_rate = clk_sam9x5_peripheral_set_rate, +}; + +static struct clk * +at91_clk_register_sam9x5_peripheral(struct regmap *regmap, + const char *name, const char *parent_name, + u32 id, const struct clk_range *range) +{ + int ret; + struct clk_sam9x5_peripheral *periph; + + if (!name || !parent_name) + return ERR_PTR(-EINVAL); + + periph = xzalloc(sizeof(*periph)); + + periph->clk.name = name; + periph->clk.ops = &sam9x5_peripheral_ops; + + if (parent_name) { + periph->parent = parent_name; + periph->clk.parent_names = &periph->parent; + periph->clk.num_parents = 1; + } + + periph->id = id; + periph->div = 0; + periph->regmap = regmap; + periph->auto_div = true; + periph->range = *range; + + ret = clk_register(&periph->clk); + if (ret) { + kfree(periph); + return ERR_PTR(ret); + } + + clk_sam9x5_peripheral_autodiv(periph); + + return &periph->clk; +} + +static int +of_at91_clk_periph_setup(struct device_node *np, u8 type) +{ + int num; + u32 id; + struct clk *clk; + const char *parent_name; + const char *name; + struct device_node *periphclknp; + struct regmap *regmap; + + parent_name = of_clk_get_parent_name(np, 0); + if (!parent_name) + return -ENOENT; + + num = of_get_child_count(np); + if (!num || num > PERIPHERAL_MAX) + return -EINVAL; + + regmap = syscon_node_to_regmap(of_get_parent(np)); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + for_each_child_of_node(np, periphclknp) { + if (of_property_read_u32(periphclknp, "reg", &id)) + continue; + + if (id >= PERIPHERAL_MAX) + continue; + + if (of_property_read_string(np, "clock-output-names", &name)) + name = periphclknp->name; + + if (type == PERIPHERAL_AT91RM9200) { + clk = at91_clk_register_peripheral(regmap, name, + parent_name, id); + } else { + struct clk_range range = CLK_RANGE(0, 0); + + of_at91_get_clk_range(periphclknp, + "atmel,clk-output-range", + &range); + + clk = at91_clk_register_sam9x5_peripheral(regmap, + name, + parent_name, + id, &range); + } + + if (IS_ERR(clk)) + continue; + + of_clk_add_provider(periphclknp, of_clk_src_simple_get, clk); + } + + return 0; +} + +static int of_at91rm9200_clk_periph_setup(struct device_node *np) +{ + return of_at91_clk_periph_setup(np, PERIPHERAL_AT91RM9200); +} +CLK_OF_DECLARE(at91rm9200_clk_periph, "atmel,at91rm9200-clk-peripheral", + of_at91rm9200_clk_periph_setup); + +static int of_at91sam9x5_clk_periph_setup(struct device_node *np) +{ + return of_at91_clk_periph_setup(np, PERIPHERAL_AT91SAM9X5); +} +CLK_OF_DECLARE(at91sam9x5_clk_periph, "atmel,at91sam9x5-clk-peripheral", + of_at91sam9x5_clk_periph_setup); diff --git a/drivers/clk/at91/clk-pll.c b/drivers/clk/at91/clk-pll.c new file mode 100644 index 0000000000..e0af4fe5a8 --- /dev/null +++ b/drivers/clk/at91/clk-pll.c @@ -0,0 +1,516 @@ +/* + * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <common.h> +#include <clock.h> +#include <of.h> +#include <linux/list.h> +#include <linux/clk.h> +#include <linux/clk/at91_pmc.h> +#include <mfd/syscon.h> +#include <regmap.h> + +#include "pmc.h" + +#define PLL_STATUS_MASK(id) (1 << (1 + (id))) +#define PLL_REG(id) (AT91_CKGR_PLLAR + ((id) * 4)) +#define PLL_DIV_MASK 0xff +#define PLL_DIV_MAX PLL_DIV_MASK +#define PLL_DIV(reg) ((reg) & PLL_DIV_MASK) +#define PLL_MUL(reg, layout) (((reg) >> (layout)->mul_shift) & \ + (layout)->mul_mask) +#define PLL_MUL_MIN 2 +#define PLL_MUL_MASK(layout) ((layout)->mul_mask) +#define PLL_MUL_MAX(layout) (PLL_MUL_MASK(layout) + 1) +#define PLL_ICPR_SHIFT(id) ((id) * 16) +#define PLL_ICPR_MASK(id) (0xffff << PLL_ICPR_SHIFT(id)) +#define PLL_MAX_COUNT 0x3f +#define PLL_COUNT_SHIFT 8 +#define PLL_OUT_SHIFT 14 +#define PLL_MAX_ID 1 + +struct clk_pll_characteristics { + struct clk_range input; + int num_output; + struct clk_range *output; + u16 *icpll; + u8 *out; +}; + +struct clk_pll_layout { + u32 pllr_mask; + u16 mul_mask; + u8 mul_shift; +}; + +#define to_clk_pll(clk) container_of(clk, struct clk_pll, clk) + +struct clk_pll { + struct clk clk; + struct regmap *regmap; + u8 id; + u8 div; + u8 range; + u16 mul; + const struct clk_pll_layout *layout; + const struct clk_pll_characteristics *characteristics; + const char *parent; +}; + +static inline bool clk_pll_ready(struct regmap *regmap, int id) +{ + unsigned int status; + + regmap_read(regmap, AT91_PMC_SR, &status); + + return status & PLL_STATUS_MASK(id) ? 1 : 0; +} + +static int clk_pll_enable(struct clk *clk) +{ + struct clk_pll *pll = to_clk_pll(clk); + struct regmap *regmap = pll->regmap; + const struct clk_pll_layout *layout = pll->layout; + const struct clk_pll_characteristics *characteristics = + pll->characteristics; + u8 id = pll->id; + u32 mask = PLL_STATUS_MASK(id); + int offset = PLL_REG(id); + u8 out = 0; + unsigned int pllr; + unsigned int status; + u8 div; + u16 mul; + + regmap_read(regmap, offset, &pllr); + div = PLL_DIV(pllr); + mul = PLL_MUL(pllr, layout); + + regmap_read(regmap, AT91_PMC_SR, &status); + if ((status & mask) && + (div == pll->div && mul == pll->mul)) + return 0; + + if (characteristics->out) + out = characteristics->out[pll->range]; + + if (characteristics->icpll) + regmap_write_bits(regmap, AT91_PMC_PLLICPR, PLL_ICPR_MASK(id), + characteristics->icpll[pll->range] << PLL_ICPR_SHIFT(id)); + + regmap_write_bits(regmap, offset, layout->pllr_mask, + pll->div | (PLL_MAX_COUNT << PLL_COUNT_SHIFT) | + (out << PLL_OUT_SHIFT) | + ((pll->mul & layout->mul_mask) << layout->mul_shift)); + + while (!clk_pll_ready(regmap, pll->id)) + barrier(); + + return 0; +} + +static int clk_pll_is_enabled(struct clk *clk) +{ + struct clk_pll *pll = to_clk_pll(clk); + + return clk_pll_ready(pll->regmap, pll->id); +} + +static void clk_pll_disable(struct clk *clk) +{ + struct clk_pll *pll = to_clk_pll(clk); + unsigned int mask = pll->layout->pllr_mask; + + regmap_write_bits(pll->regmap, PLL_REG(pll->id), mask, ~mask); +} + +static unsigned long clk_pll_recalc_rate(struct clk *clk, + unsigned long parent_rate) +{ + struct clk_pll *pll = to_clk_pll(clk); + unsigned int pllr; + u16 mul; + u8 div; + + regmap_read(pll->regmap, PLL_REG(pll->id), &pllr); + + div = PLL_DIV(pllr); + mul = PLL_MUL(pllr, pll->layout); + + if (!div || !mul) + return 0; + + return (parent_rate / div) * (mul + 1); +} + +static long clk_pll_get_best_div_mul(struct clk_pll *pll, unsigned long rate, + unsigned long parent_rate, + u32 *div, u32 *mul, + u32 *index) { + const struct clk_pll_layout *layout = pll->layout; + const struct clk_pll_characteristics *characteristics = + pll->characteristics; + unsigned long bestremainder = ULONG_MAX; + unsigned long maxdiv, mindiv, tmpdiv; + long bestrate = -ERANGE; + unsigned long bestdiv; + unsigned long bestmul; + int i = 0; + + /* Check if parent_rate is a valid input rate */ + if (parent_rate < characteristics->input.min) + return -ERANGE; + + /* + * Calculate minimum divider based on the minimum multiplier, the + * parent_rate and the requested rate. + * Should always be 2 according to the input and output characteristics + * of the PLL blocks. + */ + mindiv = (parent_rate * PLL_MUL_MIN) / rate; + if (!mindiv) + mindiv = 1; + + if (parent_rate > characteristics->input.max) { + tmpdiv = DIV_ROUND_UP(parent_rate, characteristics->input.max); + if (tmpdiv > PLL_DIV_MAX) + return -ERANGE; + + if (tmpdiv > mindiv) + mindiv = tmpdiv; + } + + /* + * Calculate the maximum divider which is limited by PLL register + * layout (limited by the MUL or DIV field size). + */ + maxdiv = DIV_ROUND_UP(parent_rate * PLL_MUL_MAX(layout), rate); + if (maxdiv > PLL_DIV_MAX) + maxdiv = PLL_DIV_MAX; + + /* + * Iterate over the acceptable divider values to find the best + * divider/multiplier pair (the one that generates the closest + * rate to the requested one). + */ + for (tmpdiv = mindiv; tmpdiv <= maxdiv; tmpdiv++) { + unsigned long remainder; + unsigned long tmprate; + unsigned long tmpmul; + + /* + * Calculate the multiplier associated with the current + * divider that provide the closest rate to the requested one. + */ + tmpmul = DIV_ROUND_CLOSEST(rate, parent_rate / tmpdiv); + tmprate = (parent_rate / tmpdiv) * tmpmul; + if (tmprate > rate) + remainder = tmprate - rate; + else + remainder = rate - tmprate; + + /* + * Compare the remainder with the best remainder found until + * now and elect a new best multiplier/divider pair if the + * current remainder is smaller than the best one. + */ + if (remainder < bestremainder) { + bestremainder = remainder; + bestdiv = tmpdiv; + bestmul = tmpmul; + bestrate = tmprate; + } + + /* + * We've found a perfect match! + * Stop searching now and use this multiplier/divider pair. + */ + if (!remainder) + break; + } + + /* We haven't found any multiplier/divider pair => return -ERANGE */ + if (bestrate < 0) + return bestrate; + + /* Check if bestrate is a valid output rate */ + for (i = 0; i < characteristics->num_output; i++) { + if (bestrate >= characteristics->output[i].min && + bestrate <= characteristics->output[i].max) + break; + } + + if (i >= characteristics->num_output) + return -ERANGE; + + if (div) + *div = bestdiv; + if (mul) + *mul = bestmul - 1; + if (index) + *index = i; + + return bestrate; +} + +static long clk_pll_round_rate(struct clk *clk, unsigned long rate, + unsigned long *parent_rate) +{ + struct clk_pll *pll = to_clk_pll(clk); + + return clk_pll_get_best_div_mul(pll, rate, *parent_rate, + NULL, NULL, NULL); +} + +static int clk_pll_set_rate(struct clk *clk, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_pll *pll = to_clk_pll(clk); + long ret; + u32 div; + u32 mul; + u32 index; + + ret = clk_pll_get_best_div_mul(pll, rate, parent_rate, + &div, &mul, &index); + if (ret < 0) + return ret; + + pll->range = index; + pll->div = div; + pll->mul = mul; + + return 0; +} + +static const struct clk_ops pll_ops = { + .enable = clk_pll_enable, + .disable = clk_pll_disable, + .is_enabled = clk_pll_is_enabled, + .recalc_rate = clk_pll_recalc_rate, + .round_rate = clk_pll_round_rate, + .set_rate = clk_pll_set_rate, +}; + +static struct clk * +at91_clk_register_pll(struct regmap *regmap, const char *name, + const char *parent_name, u8 id, + const struct clk_pll_layout *layout, + const struct clk_pll_characteristics *characteristics) +{ + struct clk_pll *pll; + int offset = PLL_REG(id); + unsigned int pllr; + int ret; + + if (id > PLL_MAX_ID) + return ERR_PTR(-EINVAL); + + pll = xzalloc(sizeof(*pll)); + + pll->parent = parent_name; + pll->clk.name = name; + pll->clk.ops = &pll_ops; + pll->clk.parent_names = &pll->parent; + pll->clk.num_parents = 1; + + /* init.flags = CLK_SET_RATE_GATE; */ + + pll->id = id; + pll->layout = layout; + pll->characteristics = characteristics; + pll->regmap = regmap; + regmap_read(regmap, offset, &pllr); + pll->div = PLL_DIV(pllr); + pll->mul = PLL_MUL(pllr, layout); + + ret = clk_register(&pll->clk); + if (ret) { + kfree(pll); + return ERR_PTR(ret); + } + + return &pll->clk; +} + + +static const struct clk_pll_layout at91rm9200_pll_layout = { + .pllr_mask = 0x7FFFFFF, + .mul_shift = 16, + .mul_mask = 0x7FF, +}; + +static const struct clk_pll_layout at91sam9g45_pll_layout = { + .pllr_mask = 0xFFFFFF, + .mul_shift = 16, + .mul_mask = 0xFF, +}; + +static const struct clk_pll_layout at91sam9g20_pllb_layout = { + .pllr_mask = 0x3FFFFF, + .mul_shift = 16, + .mul_mask = 0x3F, +}; + +static const struct clk_pll_layout sama5d3_pll_layout = { + .pllr_mask = 0x1FFFFFF, + .mul_shift = 18, + .mul_mask = 0x7F, +}; + + +static struct clk_pll_characteristics * +of_at91_clk_pll_get_characteristics(struct device_node *np) +{ + int i; + int offset; + u32 tmp; + int num_output; + u32 num_cells; + struct clk_range input; + struct clk_range *output; + u8 *out = NULL; + u16 *icpll = NULL; + struct clk_pll_characteristics *characteristics; + + if (of_at91_get_clk_range(np, "atmel,clk-input-range", &input)) + return NULL; + + if (of_property_read_u32(np, "#atmel,pll-clk-output-range-cells", + &num_cells)) + return NULL; + + if (num_cells < 2 || num_cells > 4) + return NULL; + + if (!of_get_property(np, "atmel,pll-clk-output-ranges", &tmp)) + return NULL; + num_output = tmp / (sizeof(u32) * num_cells); + + characteristics = xzalloc(sizeof(*characteristics)); + output = xzalloc(sizeof(*output) * num_output); + + if (num_cells > 2) + out = xzalloc(sizeof(*out) * num_output); + + if (num_cells > 3) + icpll = xzalloc(sizeof(*icpll) * num_output); + + + for (i = 0; i < num_output; i++) { + offset = i * num_cells; + if (of_property_read_u32_index(np, + "atmel,pll-clk-output-ranges", + offset, &tmp)) + goto out_free_output; + output[i].min = tmp; + if (of_property_read_u32_index(np, + "atmel,pll-clk-output-ranges", + offset + 1, &tmp)) + goto out_free_output; + output[i].max = tmp; + + if (num_cells == 2) + continue; + + if (of_property_read_u32_index(np, + "atmel,pll-clk-output-ranges", + offset + 2, &tmp)) + goto out_free_output; + out[i] = tmp; + + if (num_cells == 3) + continue; + + if (of_property_read_u32_index(np, + "atmel,pll-clk-output-ranges", + offset + 3, &tmp)) + goto out_free_output; + icpll[i] = tmp; + } + + characteristics->input = input; + characteristics->num_output = num_output; + characteristics->output = output; + characteristics->out = out; + characteristics->icpll = icpll; + return characteristics; + +out_free_output: + kfree(icpll); + kfree(out); + kfree(output); + kfree(characteristics); + return NULL; +} + +static int +of_at91_clk_pll_setup(struct device_node *np, + const struct clk_pll_layout *layout) +{ + u32 id; + struct clk *clk; + struct regmap *regmap; + const char *parent_name; + const char *name = np->name; + struct clk_pll_characteristics *characteristics; + + if (of_property_read_u32(np, "reg", &id)) + return -EINVAL; + + parent_name = of_clk_get_parent_name(np, 0); + + of_property_read_string(np, "clock-output-names", &name); + + regmap = syscon_node_to_regmap(of_get_parent(np)); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + characteristics = of_at91_clk_pll_get_characteristics(np); + if (!characteristics) + return -EINVAL; + + clk = at91_clk_register_pll(regmap, name, parent_name, id, layout, + characteristics); + if (IS_ERR(clk)) { + kfree(characteristics); + return PTR_ERR(clk); + } + + return of_clk_add_provider(np, of_clk_src_simple_get, clk); +} + +static int of_at91rm9200_clk_pll_setup(struct device_node *np) +{ + return of_at91_clk_pll_setup(np, &at91rm9200_pll_layout); +} +CLK_OF_DECLARE(at91rm9200_clk_pll, "atmel,at91rm9200-clk-pll", + of_at91rm9200_clk_pll_setup); + +static int of_at91sam9g45_clk_pll_setup(struct device_node *np) +{ + return of_at91_clk_pll_setup(np, &at91sam9g45_pll_layout); +} +CLK_OF_DECLARE(at91sam9g45_clk_pll, "atmel,at91sam9g45-clk-pll", + of_at91sam9g45_clk_pll_setup); + +static int of_at91sam9g20_clk_pllb_setup(struct device_node *np) +{ + return of_at91_clk_pll_setup(np, &at91sam9g20_pllb_layout); +} +CLK_OF_DECLARE(at91sam9g20_clk_pllb, "atmel,at91sam9g20-clk-pllb", + of_at91sam9g20_clk_pllb_setup); + +static int of_sama5d3_clk_pll_setup(struct device_node *np) +{ + return of_at91_clk_pll_setup(np, &sama5d3_pll_layout); +} +CLK_OF_DECLARE(sama5d3_clk_pll, "atmel,sama5d3-clk-pll", + of_sama5d3_clk_pll_setup); diff --git a/drivers/clk/at91/clk-plldiv.c b/drivers/clk/at91/clk-plldiv.c new file mode 100644 index 0000000000..917108e84c --- /dev/null +++ b/drivers/clk/at91/clk-plldiv.c @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <common.h> +#include <clock.h> +#include <of.h> +#include <linux/list.h> +#include <linux/clk.h> +#include <linux/clk/at91_pmc.h> +#include <mfd/syscon.h> +#include <regmap.h> + +#include "pmc.h" + +#define to_clk_plldiv(hw) container_of(clk, struct clk_plldiv, clk) + +struct clk_plldiv { + struct clk clk; + struct regmap *regmap; + const char *parent; +}; + +static unsigned long clk_plldiv_recalc_rate(struct clk *clk, + unsigned long parent_rate) +{ + struct clk_plldiv *plldiv = to_clk_plldiv(clk); + unsigned int mckr; + + regmap_read(plldiv->regmap, AT91_PMC_MCKR, &mckr); + + if (mckr & AT91_PMC_PLLADIV2) + return parent_rate / 2; + + return parent_rate; +} + +static long clk_plldiv_round_rate(struct clk *clk, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned long div; + + if (rate > *parent_rate) + return *parent_rate; + div = *parent_rate / 2; + if (rate < div) + return div; + + if (rate - div < *parent_rate - rate) + return div; + + return *parent_rate; +} + +static int clk_plldiv_set_rate(struct clk *clk, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_plldiv *plldiv = to_clk_plldiv(clk); + + if ((parent_rate != rate) && (parent_rate / 2 != rate)) + return -EINVAL; + + regmap_write_bits(plldiv->regmap, AT91_PMC_MCKR, AT91_PMC_PLLADIV2, + parent_rate != rate ? AT91_PMC_PLLADIV2 : 0); + + return 0; +} + +static const struct clk_ops plldiv_ops = { + .recalc_rate = clk_plldiv_recalc_rate, + .round_rate = clk_plldiv_round_rate, + .set_rate = clk_plldiv_set_rate, +}; + +static struct clk * +at91_clk_register_plldiv(struct regmap *regmap, const char *name, + const char *parent_name) +{ + int ret; + struct clk_plldiv *plldiv; + + plldiv = xzalloc(sizeof(*plldiv)); + + plldiv->clk.name = name; + plldiv->clk.ops = &plldiv_ops; + + if (parent_name) { + plldiv->parent = parent_name; + plldiv->clk.parent_names = &plldiv->parent; + plldiv->clk.num_parents = 1; + } + + /* init.flags = CLK_SET_RATE_GATE; */ + + plldiv->regmap = regmap; + + ret = clk_register(&plldiv->clk); + if (ret) { + kfree(plldiv); + return ERR_PTR(ret); + } + + return &plldiv->clk; +} + +static int +of_at91sam9x5_clk_plldiv_setup(struct device_node *np) +{ + struct clk *clk; + const char *parent_name; + const char *name = np->name; + struct regmap *regmap; + + parent_name = of_clk_get_parent_name(np, 0); + + of_property_read_string(np, "clock-output-names", &name); + + regmap = syscon_node_to_regmap(of_get_parent(np)); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + clk = at91_clk_register_plldiv(regmap, name, parent_name); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + return of_clk_add_provider(np, of_clk_src_simple_get, clk); +} +CLK_OF_DECLARE(at91sam9x5_clk_plldiv, "atmel,at91sam9x5-clk-plldiv", + of_at91sam9x5_clk_plldiv_setup); diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c new file mode 100644 index 0000000000..ddb18c0f7c --- /dev/null +++ b/drivers/clk/at91/clk-programmable.c @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <common.h> +#include <clock.h> +#include <of.h> +#include <io.h> +#include <linux/list.h> +#include <linux/clk.h> +#include <linux/clk/at91_pmc.h> +#include <mfd/syscon.h> +#include <regmap.h> + +#include "pmc.h" + +#define PROG_SOURCE_MAX 5 +#define PROG_ID_MAX 7 + +#define PROG_STATUS_MASK(id) (1 << ((id) + 8)) +#define PROG_PRES_MASK 0x7 +#define PROG_PRES(layout, pckr) ((pckr >> layout->pres_shift) & PROG_PRES_MASK) +#define PROG_MAX_RM9200_CSS 3 + +struct clk_programmable_layout { + u8 pres_shift; + u8 css_mask; + u8 have_slck_mck; +}; + +struct clk_programmable { + struct clk clk; + struct regmap *regmap; + u8 id; + const struct clk_programmable_layout *layout; + const char *parent_names[PROG_SOURCE_MAX]; +}; + +#define to_clk_programmable(clk) container_of(clk, struct clk_programmable, clk) + +static unsigned long clk_programmable_recalc_rate(struct clk *clk, + unsigned long parent_rate) +{ + struct clk_programmable *prog = to_clk_programmable(clk); + unsigned int pckr; + + regmap_read(prog->regmap, AT91_PMC_PCKR(prog->id), &pckr); + + return parent_rate >> PROG_PRES(prog->layout, pckr); +} + +static int clk_programmable_set_parent(struct clk *clk, u8 index) +{ + struct clk_programmable *prog = to_clk_programmable(clk); + const struct clk_programmable_layout *layout = prog->layout; + unsigned int mask = layout->css_mask; + unsigned int pckr = index; + + if (layout->have_slck_mck) + mask |= AT91_PMC_CSSMCK_MCK; + + if (index > layout->css_mask) { + if (index > PROG_MAX_RM9200_CSS && !layout->have_slck_mck) + return -EINVAL; + + pckr |= AT91_PMC_CSSMCK_MCK; + } + + regmap_write_bits(prog->regmap, AT91_PMC_PCKR(prog->id), mask, pckr); + + return 0; +} + +static int clk_programmable_get_parent(struct clk *clk) +{ + struct clk_programmable *prog = to_clk_programmable(clk); + const struct clk_programmable_layout *layout = prog->layout; + unsigned int pckr; + u8 ret; + + regmap_read(prog->regmap, AT91_PMC_PCKR(prog->id), &pckr); + + ret = pckr & layout->css_mask; + + if (layout->have_slck_mck && (pckr & AT91_PMC_CSSMCK_MCK) && !ret) + ret = PROG_MAX_RM9200_CSS + 1; + + return ret; +} + +static int clk_programmable_set_rate(struct clk *clk, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_programmable *prog = to_clk_programmable(clk); + const struct clk_programmable_layout *layout = prog->layout; + unsigned long div = parent_rate / rate; + unsigned int pckr; + int shift = 0; + + regmap_read(prog->regmap, AT91_PMC_PCKR(prog->id), &pckr); + + if (!div) + return -EINVAL; + + shift = fls(div) - 1; + + if (div != (1 << shift)) + return -EINVAL; + + if (shift >= PROG_PRES_MASK) + return -EINVAL; + + regmap_write_bits(prog->regmap, AT91_PMC_PCKR(prog->id), + PROG_PRES_MASK << layout->pres_shift, + shift << layout->pres_shift); + + return 0; +} + +static const struct clk_ops programmable_ops = { + .recalc_rate = clk_programmable_recalc_rate, + .get_parent = clk_programmable_get_parent, + .set_parent = clk_programmable_set_parent, + .set_rate = clk_programmable_set_rate, +}; + +static struct clk * +at91_clk_register_programmable(struct regmap *regmap, + const char *name, const char **parent_names, + u8 num_parents, u8 id, + const struct clk_programmable_layout *layout) +{ + struct clk_programmable *prog; + int ret; + + if (id > PROG_ID_MAX) + return ERR_PTR(-EINVAL); + + prog = kzalloc(sizeof(*prog), GFP_KERNEL); + if (!prog) + return ERR_PTR(-ENOMEM); + + prog->clk.name = name; + prog->clk.ops = &programmable_ops; + memcpy(prog->parent_names, parent_names, + num_parents * sizeof(prog->parent_names[0])); + prog->clk.parent_names = &prog->parent_names[0]; + prog->clk.num_parents = num_parents; + /* init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; */ + + prog->id = id; + prog->layout = layout; + prog->regmap = regmap; + + ret = clk_register(&prog->clk); + if (ret) { + kfree(prog); + return ERR_PTR(ret); + } + + return &prog->clk; +} + +static const struct clk_programmable_layout at91rm9200_programmable_layout = { + .pres_shift = 2, + .css_mask = 0x3, + .have_slck_mck = 0, +}; + +static const struct clk_programmable_layout at91sam9g45_programmable_layout = { + .pres_shift = 2, + .css_mask = 0x3, + .have_slck_mck = 1, +}; + +static const struct clk_programmable_layout at91sam9x5_programmable_layout = { + .pres_shift = 4, + .css_mask = 0x7, + .have_slck_mck = 0, +}; + +static int +of_at91_clk_prog_setup(struct device_node *np, + const struct clk_programmable_layout *layout) +{ + int num; + u32 id; + struct clk *clk; + unsigned int num_parents; + const char *parent_names[PROG_SOURCE_MAX]; + const char *name; + struct device_node *progclknp; + struct regmap *regmap; + + num_parents = of_clk_get_parent_count(np); + if (num_parents == 0 || num_parents > PROG_SOURCE_MAX) + return -EINVAL; + + of_clk_parent_fill(np, parent_names, num_parents); + + num = of_get_child_count(np); + if (!num || num > (PROG_ID_MAX + 1)) + return -EINVAL; + + regmap = syscon_node_to_regmap(of_get_parent(np)); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + for_each_child_of_node(np, progclknp) { + if (of_property_read_u32(progclknp, "reg", &id)) + continue; + + if (of_property_read_string(np, "clock-output-names", &name)) + name = progclknp->name; + + clk = at91_clk_register_programmable(regmap, name, + parent_names, num_parents, + id, layout); + if (IS_ERR(clk)) + continue; + + of_clk_add_provider(progclknp, of_clk_src_simple_get, clk); + } + + return 0; +} + + +static void __init of_at91rm9200_clk_prog_setup(struct device_node *np) +{ + of_at91_clk_prog_setup(np, &at91rm9200_programmable_layout); +} +CLK_OF_DECLARE(at91rm9200_clk_prog, "atmel,at91rm9200-clk-programmable", + of_at91rm9200_clk_prog_setup); + +static int of_at91sam9g45_clk_prog_setup(struct device_node *np) +{ + return of_at91_clk_prog_setup(np, &at91sam9g45_programmable_layout); +} +CLK_OF_DECLARE(at91sam9g45_clk_prog, "atmel,at91sam9g45-clk-programmable", + of_at91sam9g45_clk_prog_setup); + +static int of_at91sam9x5_clk_prog_setup(struct device_node *np) +{ + return of_at91_clk_prog_setup(np, &at91sam9x5_programmable_layout); +} +CLK_OF_DECLARE(at91sam9x5_clk_prog, "atmel,at91sam9x5-clk-programmable", + of_at91sam9x5_clk_prog_setup); diff --git a/drivers/clk/at91/clk-slow.c b/drivers/clk/at91/clk-slow.c new file mode 100644 index 0000000000..d4981e7b4d --- /dev/null +++ b/drivers/clk/at91/clk-slow.c @@ -0,0 +1,108 @@ +/* + * drivers/clk/at91/clk-slow.c + * + * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <common.h> +#include <clock.h> +#include <of.h> +#include <io.h> +#include <linux/list.h> +#include <linux/clk.h> +#include <linux/clk/at91_pmc.h> +#include <mfd/syscon.h> +#include <regmap.h> + +#include "pmc.h" + +struct clk_sam9260_slow { + struct clk clk; + struct regmap *regmap; + const char *parent_names[2]; +}; + +#define to_clk_sam9260_slow(clk) container_of(clk, struct clk_sam9260_slow, clk) + +static int clk_sam9260_slow_get_parent(struct clk *clk) +{ + struct clk_sam9260_slow *slowck = to_clk_sam9260_slow(clk); + unsigned int status; + + regmap_read(slowck->regmap, AT91_PMC_SR, &status); + + return status & AT91_PMC_OSCSEL ? 1 : 0; +} + +static const struct clk_ops sam9260_slow_ops = { + .get_parent = clk_sam9260_slow_get_parent, +}; + +static struct clk * __init +at91_clk_register_sam9260_slow(struct regmap *regmap, + const char *name, + const char **parent_names, + int num_parents) +{ + struct clk_sam9260_slow *slowck; + int ret; + + if (!name) + return ERR_PTR(-EINVAL); + + if (!parent_names || !num_parents) + return ERR_PTR(-EINVAL); + + slowck = xzalloc(sizeof(*slowck)); + slowck->clk.name = name; + slowck->clk.ops = &sam9260_slow_ops; + memcpy(slowck->parent_names, parent_names, + num_parents * sizeof(slowck->parent_names[0])); + slowck->clk.parent_names = slowck->parent_names; + slowck->clk.num_parents = num_parents; + slowck->regmap = regmap; + + ret = clk_register(&slowck->clk); + if (ret) { + kfree(slowck); + return ERR_PTR(ret); + } + + return &slowck->clk; +} + +static int of_at91sam9260_clk_slow_setup(struct device_node *np) +{ + struct clk *clk; + const char *parent_names[2]; + unsigned int num_parents; + const char *name = np->name; + struct regmap *regmap; + + num_parents = of_clk_get_parent_count(np); + if (num_parents != 2) + return -EINVAL; + + of_clk_parent_fill(np, parent_names, num_parents); + regmap = syscon_node_to_regmap(of_get_parent(np)); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + of_property_read_string(np, "clock-output-names", &name); + + clk = at91_clk_register_sam9260_slow(regmap, name, parent_names, + num_parents); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + return of_clk_add_provider(np, of_clk_src_simple_get, clk); +} + +CLK_OF_DECLARE(at91sam9260_clk_slow, "atmel,at91sam9260-clk-slow", + of_at91sam9260_clk_slow_setup); diff --git a/drivers/clk/at91/clk-smd.c b/drivers/clk/at91/clk-smd.c new file mode 100644 index 0000000000..65c53efbba --- /dev/null +++ b/drivers/clk/at91/clk-smd.c @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <common.h> +#include <clock.h> +#include <of.h> +#include <io.h> +#include <linux/list.h> +#include <linux/clk.h> +#include <linux/clk/at91_pmc.h> +#include <mfd/syscon.h> +#include <regmap.h> + +#include "pmc.h" + +#define SMD_SOURCE_MAX 2 + +#define SMD_DIV_SHIFT 8 +#define SMD_MAX_DIV 0xf + +struct at91sam9x5_clk_smd { + struct clk clk; + struct regmap *regmap; + const char *parent_names[SMD_SOURCE_MAX]; +}; + +#define to_at91sam9x5_clk_smd(clk) \ + container_of(clk, struct at91sam9x5_clk_smd, clk) + +static unsigned long at91sam9x5_clk_smd_recalc_rate(struct clk *clk, + unsigned long parent_rate) +{ + struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(clk); + unsigned int smdr; + u8 smddiv; + + regmap_read(smd->regmap, AT91_PMC_SMD, &smdr); + smddiv = (smdr & AT91_PMC_SMD_DIV) >> SMD_DIV_SHIFT; + + return parent_rate / (smddiv + 1); +} + +static long at91sam9x5_clk_smd_round_rate(struct clk *clk, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned long div; + unsigned long bestrate; + unsigned long tmp; + + if (rate >= *parent_rate) + return *parent_rate; + + div = *parent_rate / rate; + if (div > SMD_MAX_DIV) + return *parent_rate / (SMD_MAX_DIV + 1); + + bestrate = *parent_rate / div; + tmp = *parent_rate / (div + 1); + if (bestrate - rate > rate - tmp) + bestrate = tmp; + + return bestrate; +} + +static int at91sam9x5_clk_smd_set_parent(struct clk *clk, u8 index) +{ + struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(clk); + + if (index > 1) + return -EINVAL; + + regmap_write_bits(smd->regmap, AT91_PMC_SMD, AT91_PMC_SMDS, + index ? AT91_PMC_SMDS : 0); + + return 0; +} + +static int at91sam9x5_clk_smd_get_parent(struct clk *clk) +{ + struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(clk); + unsigned int smdr; + + regmap_read(smd->regmap, AT91_PMC_SMD, &smdr); + + return smdr & AT91_PMC_SMDS; +} + +static int at91sam9x5_clk_smd_set_rate(struct clk *clk, unsigned long rate, + unsigned long parent_rate) +{ + struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(clk); + unsigned long div = parent_rate / rate; + + if (parent_rate % rate || div < 1 || div > (SMD_MAX_DIV + 1)) + return -EINVAL; + + regmap_write_bits(smd->regmap, AT91_PMC_SMD, AT91_PMC_SMD_DIV, + (div - 1) << SMD_DIV_SHIFT); + + return 0; +} + +static const struct clk_ops at91sam9x5_smd_ops = { + .recalc_rate = at91sam9x5_clk_smd_recalc_rate, + .round_rate = at91sam9x5_clk_smd_round_rate, + .get_parent = at91sam9x5_clk_smd_get_parent, + .set_parent = at91sam9x5_clk_smd_set_parent, + .set_rate = at91sam9x5_clk_smd_set_rate, +}; + +static struct clk * +at91sam9x5_clk_register_smd(struct regmap *regmap, const char *name, + const char **parent_names, u8 num_parents) +{ + struct at91sam9x5_clk_smd *smd; + int ret; + + smd = xzalloc(sizeof(*smd)); + smd->clk.name = name; + smd->clk.ops = &at91sam9x5_smd_ops; + memcpy(smd->parent_names, parent_names, + num_parents * sizeof(smd->parent_names[0])); + smd->clk.parent_names = smd->parent_names; + smd->clk.num_parents = num_parents; + /* init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; */ + smd->regmap = regmap; + + ret = clk_register(&smd->clk); + if (ret) { + kfree(smd); + return ERR_PTR(ret); + } + + return &smd->clk; +} + +static int of_at91sam9x5_clk_smd_setup(struct device_node *np) +{ + struct clk *clk; + unsigned int num_parents; + const char *parent_names[SMD_SOURCE_MAX]; + const char *name = np->name; + struct regmap *regmap; + + num_parents = of_clk_get_parent_count(np); + if (num_parents == 0 || num_parents > SMD_SOURCE_MAX) + return -EINVAL; + + of_clk_parent_fill(np, parent_names, num_parents); + + of_property_read_string(np, "clock-output-names", &name); + + regmap = syscon_node_to_regmap(of_get_parent(np)); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + clk = at91sam9x5_clk_register_smd(regmap, name, parent_names, + num_parents); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + return of_clk_add_provider(np, of_clk_src_simple_get, clk); +} +CLK_OF_DECLARE(at91sam9x5_clk_smd, "atmel,at91sam9x5-clk-smd", + of_at91sam9x5_clk_smd_setup); diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c new file mode 100644 index 0000000000..021930e546 --- /dev/null +++ b/drivers/clk/at91/clk-system.c @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ +#include <common.h> +#include <clock.h> +#include <of.h> +#include <io.h> +#include <linux/list.h> +#include <linux/clk.h> +#include <linux/clk/at91_pmc.h> +#include <mfd/syscon.h> +#include <regmap.h> + +#include "pmc.h" + +#define SYSTEM_MAX_ID 31 + +#define SYSTEM_MAX_NAME_SZ 32 + +#define to_clk_system(clk) container_of(clk, struct clk_system, clk) +struct clk_system { + struct clk clk; + struct regmap *regmap; + u8 id; + const char *parent_name; +}; + +static inline int is_pck(int id) +{ + return (id >= 8) && (id <= 15); +} + +static inline bool clk_system_ready(struct regmap *regmap, int id) +{ + unsigned int status; + + regmap_read(regmap, AT91_PMC_SR, &status); + + return status & (1 << id) ? 1 : 0; +} + +static int clk_system_enable(struct clk *clk) +{ + struct clk_system *sys = to_clk_system(clk); + + regmap_write(sys->regmap, AT91_PMC_SCER, 1 << sys->id); + + if (!is_pck(sys->id)) + return 0; + + while (!clk_system_ready(sys->regmap, sys->id)) + barrier(); + + return 0; +} + +static void clk_system_disable(struct clk *clk) +{ + struct clk_system *sys = to_clk_system(clk); + + regmap_write(sys->regmap, AT91_PMC_SCDR, 1 << sys->id); +} + +static int clk_system_is_enabled(struct clk *clk) +{ + struct clk_system *sys = to_clk_system(clk); + unsigned int status; + + regmap_read(sys->regmap, AT91_PMC_SCSR, &status); + + if (!(status & (1 << sys->id))) + return 0; + + if (!is_pck(sys->id)) + return 1; + + regmap_read(sys->regmap, AT91_PMC_SR, &status); + + return status & (1 << sys->id) ? 1 : 0; +} + +static const struct clk_ops system_ops = { + .enable = clk_system_enable, + .disable = clk_system_disable, + .is_enabled = clk_system_is_enabled, +}; + +static struct clk * +at91_clk_register_system(struct regmap *regmap, const char *name, + const char *parent_name, u8 id) +{ + struct clk_system *sys; + int ret; + + if (!parent_name || id > SYSTEM_MAX_ID) + return ERR_PTR(-EINVAL); + + sys = xzalloc(sizeof(*sys)); + sys->clk.name = name; + sys->clk.ops = &system_ops; + sys->parent_name = parent_name; + sys->clk.parent_names = &sys->parent_name; + sys->clk.num_parents = 1; + /* init.flags = CLK_SET_RATE_PARENT; */ + sys->id = id; + sys->regmap = regmap; + + ret = clk_register(&sys->clk); + if (ret) { + kfree(sys); + return ERR_PTR(ret); + } + + return &sys->clk; +} + +static int of_at91rm9200_clk_sys_setup(struct device_node *np) +{ + int num; + u32 id; + struct clk *clk; + const char *name; + struct device_node *sysclknp; + const char *parent_name; + struct regmap *regmap; + + num = of_get_child_count(np); + if (num > (SYSTEM_MAX_ID + 1)) + return -EINVAL; + + regmap = syscon_node_to_regmap(of_get_parent(np)); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + for_each_child_of_node(np, sysclknp) { + if (of_property_read_u32(sysclknp, "reg", &id)) + continue; + + if (of_property_read_string(np, "clock-output-names", &name)) + name = sysclknp->name; + + parent_name = of_clk_get_parent_name(sysclknp, 0); + + clk = at91_clk_register_system(regmap, name, parent_name, id); + if (IS_ERR(clk)) + continue; + + of_clk_add_provider(sysclknp, of_clk_src_simple_get, clk); + } + + return 0; +} +CLK_OF_DECLARE(at91rm9200_clk_sys, "atmel,at91rm9200-clk-system", + of_at91rm9200_clk_sys_setup); diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c new file mode 100644 index 0000000000..99ba671c98 --- /dev/null +++ b/drivers/clk/at91/clk-usb.c @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <common.h> +#include <clock.h> +#include <of.h> +#include <io.h> +#include <linux/list.h> +#include <linux/clk.h> +#include <linux/clk/at91_pmc.h> +#include <mfd/syscon.h> +#include <regmap.h> + +#include "pmc.h" + +#define USB_SOURCE_MAX 2 + +#define SAM9X5_USB_DIV_SHIFT 8 +#define SAM9X5_USB_MAX_DIV 0xf + +#define RM9200_USB_DIV_SHIFT 28 +#define RM9200_USB_DIV_TAB_SIZE 4 + +struct at91sam9x5_clk_usb { + struct clk clk; + struct regmap *regmap; + const char *parent_names[USB_SOURCE_MAX]; +}; + +#define to_at91sam9x5_clk_usb(clk) \ + container_of(clk, struct at91sam9x5_clk_usb, clk) + +struct at91rm9200_clk_usb { + struct clk clk; + struct regmap *regmap; + u32 divisors[4]; + const char *parent_name; +}; + +#define to_at91rm9200_clk_usb(clk) \ + container_of(clk, struct at91rm9200_clk_usb, clk) + +static unsigned long at91sam9x5_clk_usb_recalc_rate(struct clk *clk, + unsigned long parent_rate) +{ + struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk); + unsigned int usbr; + u8 usbdiv; + + regmap_read(usb->regmap, AT91_PMC_USB, &usbr); + usbdiv = (usbr & AT91_PMC_OHCIUSBDIV) >> SAM9X5_USB_DIV_SHIFT; + + return DIV_ROUND_CLOSEST(parent_rate, (usbdiv + 1)); +} + +static int at91sam9x5_clk_usb_set_parent(struct clk *clk, u8 index) +{ + struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk); + + if (index > 1) + return -EINVAL; + + regmap_write_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS, + index ? AT91_PMC_USBS : 0); + + return 0; +} + +static int at91sam9x5_clk_usb_get_parent(struct clk *clk) +{ + struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk); + unsigned int usbr; + + regmap_read(usb->regmap, AT91_PMC_USB, &usbr); + + return usbr & AT91_PMC_USBS; +} + +static int at91sam9x5_clk_usb_set_rate(struct clk *clk, unsigned long rate, + unsigned long parent_rate) +{ + struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk); + unsigned long div; + + if (!rate) + return -EINVAL; + + div = DIV_ROUND_CLOSEST(parent_rate, rate); + if (div > SAM9X5_USB_MAX_DIV + 1 || !div) + return -EINVAL; + + regmap_write_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_OHCIUSBDIV, + (div - 1) << SAM9X5_USB_DIV_SHIFT); + + return 0; +} + +static const struct clk_ops at91sam9x5_usb_ops = { + .recalc_rate = at91sam9x5_clk_usb_recalc_rate, + .get_parent = at91sam9x5_clk_usb_get_parent, + .set_parent = at91sam9x5_clk_usb_set_parent, + .set_rate = at91sam9x5_clk_usb_set_rate, +}; + +static int at91sam9n12_clk_usb_enable(struct clk *clk) +{ + struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk); + + regmap_write_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS, + AT91_PMC_USBS); + + return 0; +} + +static void at91sam9n12_clk_usb_disable(struct clk *clk) +{ + struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk); + + regmap_write_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS, 0); +} + +static int at91sam9n12_clk_usb_is_enabled(struct clk *clk) +{ + struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk); + unsigned int usbr; + + regmap_read(usb->regmap, AT91_PMC_USB, &usbr); + + return usbr & AT91_PMC_USBS; +} + +static const struct clk_ops at91sam9n12_usb_ops = { + .enable = at91sam9n12_clk_usb_enable, + .disable = at91sam9n12_clk_usb_disable, + .is_enabled = at91sam9n12_clk_usb_is_enabled, + .recalc_rate = at91sam9x5_clk_usb_recalc_rate, + .set_rate = at91sam9x5_clk_usb_set_rate, +}; + +static struct clk * +at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name, + const char **parent_names, u8 num_parents) +{ + struct at91sam9x5_clk_usb *usb; + int ret; + + usb = kzalloc(sizeof(*usb), GFP_KERNEL); + usb->clk.name = name; + usb->clk.ops = &at91sam9x5_usb_ops; + memcpy(usb->parent_names, parent_names, + num_parents * sizeof(usb->parent_names[0])); + usb->clk.parent_names = usb->parent_names; + usb->clk.num_parents = num_parents; + usb->clk.flags = CLK_SET_RATE_PARENT; + /* init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | */ + /* CLK_SET_RATE_PARENT; */ + usb->regmap = regmap; + + ret = clk_register(&usb->clk); + if (ret) { + kfree(usb); + return ERR_PTR(ret); + } + + return &usb->clk; +} + +static struct clk * +at91sam9n12_clk_register_usb(struct regmap *regmap, const char *name, + const char *parent_name) +{ + struct at91sam9x5_clk_usb *usb; + int ret; + + usb = xzalloc(sizeof(*usb)); + usb->clk.name = name; + usb->clk.ops = &at91sam9n12_usb_ops; + usb->parent_names[0] = parent_name; + usb->clk.parent_names = &usb->parent_names[0]; + usb->clk.num_parents = 1; + /* init.flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT; */ + usb->regmap = regmap; + + ret = clk_register(&usb->clk); + if (ret) { + kfree(usb); + return ERR_PTR(ret); + } + + return &usb->clk; +} + +static unsigned long at91rm9200_clk_usb_recalc_rate(struct clk *clk, + unsigned long parent_rate) +{ + struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(clk); + unsigned int pllbr; + u8 usbdiv; + + regmap_read(usb->regmap, AT91_CKGR_PLLBR, &pllbr); + + usbdiv = (pllbr & AT91_PMC_USBDIV) >> RM9200_USB_DIV_SHIFT; + if (usb->divisors[usbdiv]) + return parent_rate / usb->divisors[usbdiv]; + + return 0; +} + +static long at91rm9200_clk_usb_round_rate(struct clk *clk, unsigned long rate, + unsigned long *parent_rate) +{ + struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(clk); + struct clk *parent = clk_get_parent(clk); + unsigned long bestrate = 0; + int bestdiff = -1; + unsigned long tmprate; + int tmpdiff; + int i = 0; + + for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) { + unsigned long tmp_parent_rate; + + if (!usb->divisors[i]) + continue; + + tmp_parent_rate = rate * usb->divisors[i]; + tmp_parent_rate = clk_round_rate(parent, tmp_parent_rate); + tmprate = DIV_ROUND_CLOSEST(tmp_parent_rate, usb->divisors[i]); + if (tmprate < rate) + tmpdiff = rate - tmprate; + else + tmpdiff = tmprate - rate; + + if (bestdiff < 0 || bestdiff > tmpdiff) { + bestrate = tmprate; + bestdiff = tmpdiff; + *parent_rate = tmp_parent_rate; + } + + if (!bestdiff) + break; + } + + return bestrate; +} + +static int at91rm9200_clk_usb_set_rate(struct clk *clk, unsigned long rate, + unsigned long parent_rate) +{ + int i; + struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(clk); + unsigned long div; + + if (!rate) + return -EINVAL; + + div = DIV_ROUND_CLOSEST(parent_rate, rate); + + for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) { + if (usb->divisors[i] == div) { + regmap_write_bits(usb->regmap, AT91_CKGR_PLLBR, + AT91_PMC_USBDIV, + i << RM9200_USB_DIV_SHIFT); + + return 0; + } + } + + return -EINVAL; +} + +static const struct clk_ops at91rm9200_usb_ops = { + .recalc_rate = at91rm9200_clk_usb_recalc_rate, + .round_rate = at91rm9200_clk_usb_round_rate, + .set_rate = at91rm9200_clk_usb_set_rate, +}; + +static struct clk * +at91rm9200_clk_register_usb(struct regmap *regmap, const char *name, + const char *parent_name, const u32 *divisors) +{ + struct at91rm9200_clk_usb *usb; + int ret; + + usb = xzalloc(sizeof(*usb)); + usb->clk.name = name; + usb->clk.ops = &at91rm9200_usb_ops; + usb->parent_name = parent_name; + usb->clk.parent_names = &usb->parent_name; + usb->clk.num_parents = 1; + /* init.flags = CLK_SET_RATE_PARENT; */ + + usb->regmap = regmap; + memcpy(usb->divisors, divisors, sizeof(usb->divisors)); + + ret = clk_register(&usb->clk); + if (ret) { + kfree(usb); + return ERR_PTR(ret); + } + + return &usb->clk; +} + +static int of_at91sam9x5_clk_usb_setup(struct device_node *np) +{ + struct clk *clk; + unsigned int num_parents; + const char *parent_names[USB_SOURCE_MAX]; + const char *name = np->name; + struct regmap *regmap; + + num_parents = of_clk_get_parent_count(np); + if (num_parents == 0 || num_parents > USB_SOURCE_MAX) + return -EINVAL; + + of_clk_parent_fill(np, parent_names, num_parents); + + of_property_read_string(np, "clock-output-names", &name); + + regmap = syscon_node_to_regmap(of_get_parent(np)); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + clk = at91sam9x5_clk_register_usb(regmap, name, parent_names, + num_parents); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + return of_clk_add_provider(np, of_clk_src_simple_get, clk); +} +CLK_OF_DECLARE(at91sam9x5_clk_usb, "atmel,at91sam9x5-clk-usb", + of_at91sam9x5_clk_usb_setup); + +static int of_at91sam9n12_clk_usb_setup(struct device_node *np) +{ + struct clk *clk; + const char *parent_name; + const char *name = np->name; + struct regmap *regmap; + + parent_name = of_clk_get_parent_name(np, 0); + if (!parent_name) + return -EINVAL; + + of_property_read_string(np, "clock-output-names", &name); + + regmap = syscon_node_to_regmap(of_get_parent(np)); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + clk = at91sam9n12_clk_register_usb(regmap, name, parent_name); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + return of_clk_add_provider(np, of_clk_src_simple_get, clk); +} +CLK_OF_DECLARE(at91sam9n12_clk_usb, "atmel,at91sam9n12-clk-usb", + of_at91sam9n12_clk_usb_setup); + +static int of_at91rm9200_clk_usb_setup(struct device_node *np) +{ + struct clk *clk; + const char *parent_name; + const char *name = np->name; + u32 divisors[4] = {0, 0, 0, 0}; + struct regmap *regmap; + + parent_name = of_clk_get_parent_name(np, 0); + if (!parent_name) + return -EINVAL; + + of_property_read_u32_array(np, "atmel,clk-divisors", divisors, 4); + if (!divisors[0]) + return -EINVAL; + + of_property_read_string(np, "clock-output-names", &name); + + regmap = syscon_node_to_regmap(of_get_parent(np)); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + clk = at91rm9200_clk_register_usb(regmap, name, parent_name, divisors); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + return of_clk_add_provider(np, of_clk_src_simple_get, clk); +} +CLK_OF_DECLARE(at91rm9200_clk_usb, "atmel,at91rm9200-clk-usb", + of_at91rm9200_clk_usb_setup); diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c new file mode 100644 index 0000000000..6a1c5e6df3 --- /dev/null +++ b/drivers/clk/at91/clk-utmi.c @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <common.h> +#include <clock.h> +#include <of.h> +#include <linux/list.h> +#include <linux/clk.h> +#include <linux/clk/at91_pmc.h> +#include <mfd/syscon.h> +#include <regmap.h> + +#include "pmc.h" + +#define UTMI_FIXED_MUL 40 + +struct clk_utmi { + struct clk clk; + struct regmap *regmap; + const char *parent; +}; + +#define to_clk_utmi(clk) container_of(clk, struct clk_utmi, clk) + +static inline bool clk_utmi_ready(struct regmap *regmap) +{ + unsigned int status; + + regmap_read(regmap, AT91_PMC_SR, &status); + + return status & AT91_PMC_LOCKU; +} + +static int clk_utmi_enable(struct clk *clk) +{ + struct clk_utmi *utmi = to_clk_utmi(clk); + unsigned int uckr = AT91_PMC_UPLLEN | AT91_PMC_UPLLCOUNT | + AT91_PMC_BIASEN; + + regmap_write_bits(utmi->regmap, AT91_CKGR_UCKR, uckr, uckr); + + while (!clk_utmi_ready(utmi->regmap)) + barrier(); + + return 0; +} + +static int clk_utmi_is_enabled(struct clk *clk) +{ + struct clk_utmi *utmi = to_clk_utmi(clk); + + return clk_utmi_ready(utmi->regmap); +} + +static void clk_utmi_disable(struct clk *clk) +{ + struct clk_utmi *utmi = to_clk_utmi(clk); + + regmap_write_bits(utmi->regmap, AT91_CKGR_UCKR, AT91_PMC_UPLLEN, 0); +} + +static unsigned long clk_utmi_recalc_rate(struct clk *clk, + unsigned long parent_rate) +{ + /* UTMI clk is a fixed clk multiplier */ + return parent_rate * UTMI_FIXED_MUL; +} + +static const struct clk_ops utmi_ops = { + .enable = clk_utmi_enable, + .disable = clk_utmi_disable, + .is_enabled = clk_utmi_is_enabled, + .recalc_rate = clk_utmi_recalc_rate, +}; + +static struct clk * __init +at91_clk_register_utmi(struct regmap *regmap, + const char *name, const char *parent_name) +{ + int ret; + struct clk_utmi *utmi; + + utmi = xzalloc(sizeof(*utmi)); + + utmi->clk.name = name; + utmi->clk.ops = &utmi_ops; + + if (parent_name) { + utmi->parent = parent_name; + utmi->clk.parent_names = &utmi->parent; + utmi->clk.num_parents = 1; + } + + /* utmi->clk.flags = CLK_SET_RATE_GATE; */ + + utmi->regmap = regmap; + + ret = clk_register(&utmi->clk); + if (ret) { + kfree(utmi); + return ERR_PTR(ret); + } + + return &utmi->clk; +} +#if defined(CONFIG_OFTREE) && defined(CONFIG_COMMON_CLK_OF_PROVIDER) +static void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np) +{ + struct clk *clk; + const char *parent_name; + const char *name = np->name; + struct regmap *regmap; + + parent_name = of_clk_get_parent_name(np, 0); + + of_property_read_string(np, "clock-output-names", &name); + + regmap = syscon_node_to_regmap(of_get_parent(np)); + if (IS_ERR(regmap)) + return; + + clk = at91_clk_register_utmi(regmap, name, parent_name); + if (IS_ERR(clk)) + return; + + of_clk_add_provider(np, of_clk_src_simple_get, clk); + return; +} +CLK_OF_DECLARE(at91sam9x5_clk_utmi, "atmel,at91sam9x5-clk-utmi", + of_at91sam9x5_clk_utmi_setup); +#endif diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c new file mode 100644 index 0000000000..d156d50ca8 --- /dev/null +++ b/drivers/clk/at91/pmc.c @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <module.h> +#include <linux/list.h> +#include <linux/clkdev.h> +#include <of.h> +#include <mfd/syscon.h> +#include <regmap.h> + +#include "pmc.h" + +int of_at91_get_clk_range(struct device_node *np, const char *propname, + struct clk_range *range) +{ + u32 min, max; + int ret; + + ret = of_property_read_u32_index(np, propname, 0, &min); + if (ret) + return ret; + + ret = of_property_read_u32_index(np, propname, 1, &max); + if (ret) + return ret; + + if (range) { + range->min = min; + range->max = max; + } + + return 0; +} +EXPORT_SYMBOL_GPL(of_at91_get_clk_range); diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h new file mode 100644 index 0000000000..c6c14a79a4 --- /dev/null +++ b/drivers/clk/at91/pmc.h @@ -0,0 +1,27 @@ +/* + * drivers/clk/at91/pmc.h + * + * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __PMC_H_ +#define __PMC_H_ + +#include <io.h> + +struct clk_range { + unsigned long min; + unsigned long max; +}; + +#define CLK_RANGE(MIN, MAX) {.min = MIN, .max = MAX,} + +int of_at91_get_clk_range(struct device_node *np, const char *propname, + struct clk_range *range); + +#endif /* __PMC_H_ */ diff --git a/drivers/clk/at91/sckc.c b/drivers/clk/at91/sckc.c new file mode 100644 index 0000000000..bac28999ea --- /dev/null +++ b/drivers/clk/at91/sckc.c @@ -0,0 +1,485 @@ +/* + * drivers/clk/at91/sckc.c + * + * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include <common.h> +#include <clock.h> +#include <of.h> +#include <of_address.h> +#include <io.h> +#include <linux/list.h> +#include <linux/clk.h> +#include <linux/clk/at91_pmc.h> +#include <mfd/syscon.h> +#include <regmap.h> + + + +#define SLOW_CLOCK_FREQ 32768 +#define SLOWCK_SW_CYCLES 5 +#define SLOWCK_SW_TIME_USEC ((SLOWCK_SW_CYCLES * SECOND) / \ + SLOW_CLOCK_FREQ) + +#define AT91_SCKC_CR 0x00 +#define AT91_SCKC_RCEN (1 << 0) +#define AT91_SCKC_OSC32EN (1 << 1) +#define AT91_SCKC_OSC32BYP (1 << 2) +#define AT91_SCKC_OSCSEL (1 << 3) + +struct clk_slow_osc { + struct clk clk; + void __iomem *sckcr; + unsigned long startup_usec; + const char *parent_name; +}; + +#define to_clk_slow_osc(clk) container_of(clk, struct clk_slow_osc, clk) + +struct clk_sama5d4_slow_osc { + struct clk clk; + void __iomem *sckcr; + unsigned long startup_usec; + bool prepared; + const char *parent_name; +}; + +#define to_clk_sama5d4_slow_osc(clk) container_of(clk, struct clk_sama5d4_slow_osc, clk) + +struct clk_slow_rc_osc { + struct clk clk; + void __iomem *sckcr; + unsigned long frequency; + unsigned long startup_usec; + const char *parent_name; +}; + +#define to_clk_slow_rc_osc(clk) container_of(clk, struct clk_slow_rc_osc, clk) + +struct clk_sam9x5_slow { + struct clk clk; + void __iomem *sckcr; + u8 parent; + const char *parent_names[2]; +}; + +#define to_clk_sam9x5_slow(clk) container_of(clk, struct clk_sam9x5_slow, clk) + +static int clk_slow_osc_enable(struct clk *clk) +{ + struct clk_slow_osc *osc = to_clk_slow_osc(clk); + void __iomem *sckcr = osc->sckcr; + u32 tmp = readl(sckcr); + + if (tmp & (AT91_SCKC_OSC32BYP | AT91_SCKC_OSC32EN)) + return 0; + + writel(tmp | AT91_SCKC_OSC32EN, sckcr); + + udelay(osc->startup_usec); + + return 0; +} + +static void clk_slow_osc_disable(struct clk *clk) +{ + struct clk_slow_osc *osc = to_clk_slow_osc(clk); + void __iomem *sckcr = osc->sckcr; + u32 tmp = readl(sckcr); + + if (tmp & AT91_SCKC_OSC32BYP) + return; + + writel(tmp & ~AT91_SCKC_OSC32EN, sckcr); +} + +static int clk_slow_osc_is_enabled(struct clk *clk) +{ + struct clk_slow_osc *osc = to_clk_slow_osc(clk); + void __iomem *sckcr = osc->sckcr; + u32 tmp = readl(sckcr); + + if (tmp & AT91_SCKC_OSC32BYP) + return 1; + + return !!(tmp & AT91_SCKC_OSC32EN); +} + +static const struct clk_ops slow_osc_ops = { + .enable = clk_slow_osc_enable, + .disable = clk_slow_osc_disable, + .is_enabled = clk_slow_osc_is_enabled, +}; + +static struct clk * +at91_clk_register_slow_osc(void __iomem *sckcr, + const char *name, + const char *parent_name, + unsigned long startup, + bool bypass) +{ + int ret; + struct clk_slow_osc *osc; + + if (!sckcr || !name || !parent_name) + return ERR_PTR(-EINVAL); + + osc = xzalloc(sizeof(*osc)); + + osc->clk.name = name; + osc->clk.ops = &slow_osc_ops; + osc->parent_name = parent_name; + osc->clk.parent_names = &osc->parent_name; + osc->clk.num_parents = 1; + + osc->sckcr = sckcr; + osc->startup_usec = startup; + + if (bypass) + writel((readl(sckcr) & ~AT91_SCKC_OSC32EN) | AT91_SCKC_OSC32BYP, + sckcr); + + ret = clk_register(&osc->clk); + if (ret) { + kfree(osc); + return ERR_PTR(ret); + } + + return &osc->clk; +} + +static void +of_at91sam9x5_clk_slow_osc_setup(struct device_node *np, void __iomem *sckcr) +{ + struct clk *clk; + const char *parent_name; + const char *name = np->name; + u32 startup; + bool bypass; + + parent_name = of_clk_get_parent_name(np, 0); + of_property_read_string(np, "clock-output-names", &name); + of_property_read_u32(np, "atmel,startup-time-usec", &startup); + bypass = of_property_read_bool(np, "atmel,osc-bypass"); + + clk = at91_clk_register_slow_osc(sckcr, name, parent_name, startup, + bypass); + if (IS_ERR(clk)) + return; + + of_clk_add_provider(np, of_clk_src_simple_get, clk); +} + +static unsigned long clk_slow_rc_osc_recalc_rate(struct clk *clk, + unsigned long parent_rate) +{ + struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(clk); + + return osc->frequency; +} + +static int clk_slow_rc_osc_enable(struct clk *clk) +{ + struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(clk); + void __iomem *sckcr = osc->sckcr; + + writel(readl(sckcr) | AT91_SCKC_RCEN, sckcr); + + udelay(osc->startup_usec); + + return 0; +} + +static void clk_slow_rc_osc_disable(struct clk *clk) +{ + struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(clk); + void __iomem *sckcr = osc->sckcr; + + writel(readl(sckcr) & ~AT91_SCKC_RCEN, sckcr); +} + +static int clk_slow_rc_osc_is_enabled(struct clk *clk) +{ + struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(clk); + + return !!(readl(osc->sckcr) & AT91_SCKC_RCEN); +} + +static const struct clk_ops slow_rc_osc_ops = { + .enable = clk_slow_rc_osc_enable, + .disable = clk_slow_rc_osc_disable, + .is_enabled = clk_slow_rc_osc_is_enabled, + .recalc_rate = clk_slow_rc_osc_recalc_rate, +}; + +static struct clk * +at91_clk_register_slow_rc_osc(void __iomem *sckcr, + const char *name, + unsigned long frequency, + unsigned long startup) +{ + struct clk_slow_rc_osc *osc; + int ret; + + if (!sckcr || !name) + return ERR_PTR(-EINVAL); + + osc = xzalloc(sizeof(*osc)); + osc->clk.name = name; + osc->clk.ops = &slow_rc_osc_ops; + osc->clk.parent_names = NULL; + osc->clk.num_parents = 0; + /* init.flags = CLK_IGNORE_UNUSED; */ + + osc->sckcr = sckcr; + osc->frequency = frequency; + osc->startup_usec = startup; + + ret = clk_register(&osc->clk); + if (ret) { + kfree(osc); + return ERR_PTR(ret); + } + + return &osc->clk; +} + +static void +of_at91sam9x5_clk_slow_rc_osc_setup(struct device_node *np, void __iomem *sckcr) +{ + struct clk *clk; + u32 frequency = 0; + u32 startup = 0; + const char *name = np->name; + + of_property_read_string(np, "clock-output-names", &name); + of_property_read_u32(np, "clock-frequency", &frequency); + of_property_read_u32(np, "atmel,startup-time-usec", &startup); + + clk = at91_clk_register_slow_rc_osc(sckcr, name, frequency, startup); + if (IS_ERR(clk)) + return; + + of_clk_add_provider(np, of_clk_src_simple_get, clk); +} + +static int clk_sam9x5_slow_set_parent(struct clk *clk, u8 index) +{ + struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(clk); + void __iomem *sckcr = slowck->sckcr; + u32 tmp; + + if (index > 1) + return -EINVAL; + + tmp = readl(sckcr); + + if ((!index && !(tmp & AT91_SCKC_OSCSEL)) || + (index && (tmp & AT91_SCKC_OSCSEL))) + return 0; + + if (index) + tmp |= AT91_SCKC_OSCSEL; + else + tmp &= ~AT91_SCKC_OSCSEL; + + writel(tmp, sckcr); + + udelay(SLOWCK_SW_TIME_USEC); + + return 0; +} + +static int clk_sam9x5_slow_get_parent(struct clk *clk) +{ + struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(clk); + + return !!(readl(slowck->sckcr) & AT91_SCKC_OSCSEL); +} + +static const struct clk_ops sam9x5_slow_ops = { + .set_parent = clk_sam9x5_slow_set_parent, + .get_parent = clk_sam9x5_slow_get_parent, +}; + +static struct clk * +at91_clk_register_sam9x5_slow(void __iomem *sckcr, + const char *name, + const char **parent_names, + int num_parents) +{ + struct clk_sam9x5_slow *slowck; + int ret; + + if (!sckcr || !name || !parent_names || !num_parents) + return ERR_PTR(-EINVAL); + + slowck = xzalloc(sizeof(*slowck)); + slowck->clk.name = name; + slowck->clk.ops = &sam9x5_slow_ops; + + memcpy(slowck->parent_names, parent_names, + num_parents * sizeof(slowck->parent_names[0])); + slowck->clk.parent_names = slowck->parent_names; + slowck->clk.num_parents = num_parents; + slowck->sckcr = sckcr; + slowck->parent = !!(readl(sckcr) & AT91_SCKC_OSCSEL); + + ret = clk_register(&slowck->clk); + if (ret) { + kfree(slowck); + return ERR_PTR(ret); + } + + return &slowck->clk; +} + +static int +of_at91sam9x5_clk_slow_setup(struct device_node *np, void __iomem *sckcr) +{ + struct clk *clk; + const char *parent_names[2]; + unsigned int num_parents; + const char *name = np->name; + + num_parents = of_clk_get_parent_count(np); + if (num_parents == 0 || num_parents > 2) + return -EINVAL; + + of_clk_parent_fill(np, parent_names, num_parents); + + of_property_read_string(np, "clock-output-names", &name); + + clk = at91_clk_register_sam9x5_slow(sckcr, name, parent_names, + num_parents); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + return of_clk_add_provider(np, of_clk_src_simple_get, clk); +} + +static const struct of_device_id sckc_clk_ids[] = { + /* Slow clock */ + { + .compatible = "atmel,at91sam9x5-clk-slow-osc", + .data = of_at91sam9x5_clk_slow_osc_setup, + }, + { + .compatible = "atmel,at91sam9x5-clk-slow-rc-osc", + .data = of_at91sam9x5_clk_slow_rc_osc_setup, + }, + { + .compatible = "atmel,at91sam9x5-clk-slow", + .data = of_at91sam9x5_clk_slow_setup, + }, + { /*sentinel*/ } +}; + +static int of_at91sam9x5_sckc_setup(struct device_node *np) +{ + struct device_node *childnp; + void (*clk_setup)(struct device_node *, void __iomem *); + const struct of_device_id *clk_id; + void __iomem *regbase = of_iomap(np, 0); + + if (!regbase) + return -ENOMEM; + + for_each_child_of_node(np, childnp) { + clk_id = of_match_node(sckc_clk_ids, childnp); + if (!clk_id) + continue; + clk_setup = clk_id->data; + clk_setup(childnp, regbase); + } + + return 0; +} +CLK_OF_DECLARE(at91sam9x5_clk_sckc, "atmel,at91sam9x5-sckc", + of_at91sam9x5_sckc_setup); + +static int clk_sama5d4_slow_osc_enable(struct clk *clk) +{ + struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(clk); + + if (osc->prepared) + return 0; + + /* + * Assume that if it has already been selected (for example by the + * bootloader), enough time has aready passed. + */ + if ((readl(osc->sckcr) & AT91_SCKC_OSCSEL)) { + osc->prepared = true; + return 0; + } + + udelay(osc->startup_usec); + osc->prepared = true; + + return 0; +} + +static int clk_sama5d4_slow_osc_is_enabled(struct clk *clk) +{ + struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(clk); + + return osc->prepared; +} + +static const struct clk_ops sama5d4_slow_osc_ops = { + .enable = clk_sama5d4_slow_osc_enable, + .is_enabled = clk_sama5d4_slow_osc_is_enabled, +}; + +static int of_sama5d4_sckc_setup(struct device_node *np) +{ + void __iomem *regbase = of_iomap(np, 0); + struct clk *clk; + struct clk_sama5d4_slow_osc *osc; + const char *parent_names[2] = { "slow_rc_osc", "slow_osc" }; + bool bypass; + int ret; + + if (!regbase) + return -ENOMEM; + + clk = clk_fixed(parent_names[0], 32768); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + bypass = of_property_read_bool(np, "atmel,osc-bypass"); + + osc = xzalloc(sizeof(*osc)); + osc->parent_name = of_clk_get_parent_name(np, 0); + osc->clk.name = parent_names[1]; + osc->clk.ops = &sama5d4_slow_osc_ops; + osc->clk.parent_names = &osc->parent_name; + osc->clk.num_parents = 1; + osc->sckcr = regbase; + osc->startup_usec = 1200000; + + if (bypass) + writel((readl(regbase) | AT91_SCKC_OSC32BYP), regbase); + + ret = clk_register(&osc->clk); + if (ret) { + kfree(osc); + return ret; + } + + clk = at91_clk_register_sam9x5_slow(regbase, "slowck", parent_names, 2); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + return of_clk_add_provider(np, of_clk_src_simple_get, clk); +} +CLK_OF_DECLARE(sama5d4_clk_sckc, "atmel,sama5d4-sckc", + of_sama5d4_sckc_setup); diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c index a3dbf334a8..0be48558e6 100644 --- a/drivers/clk/clk-fixed-factor.c +++ b/drivers/clk/clk-fixed-factor.c @@ -93,7 +93,6 @@ struct clk *clk_fixed_factor(const char *name, return &f->clk; } -#if defined(CONFIG_COMMON_CLK_OF_PROVIDER) /** * of_fixed_factor_clk_setup() - Setup function for simple fixed factor clock */ @@ -127,4 +126,3 @@ static int of_fixed_factor_clk_setup(struct device_node *node) } CLK_OF_DECLARE(fixed_factor_clk, "fixed-factor-clock", of_fixed_factor_clk_setup); -#endif diff --git a/drivers/clk/clk-fixed.c b/drivers/clk/clk-fixed.c index f0f7fbaed5..57bf36b39e 100644 --- a/drivers/clk/clk-fixed.c +++ b/drivers/clk/clk-fixed.c @@ -55,7 +55,6 @@ struct clk *clk_fixed(const char *name, int rate) return &fix->clk; } -#if defined(CONFIG_COMMON_CLK_OF_PROVIDER) /** * of_fixed_clk_setup() - Setup function for simple fixed rate clock */ @@ -76,4 +75,3 @@ static int of_fixed_clk_setup(struct device_node *node) return of_clk_add_provider(node, of_clk_src_simple_get, clk); } CLK_OF_DECLARE(fixed_clk, "fixed-clock", of_fixed_clk_setup); -#endif diff --git a/drivers/clk/clk-gate-shared.c b/drivers/clk/clk-gate-shared.c index a95f940dd2..c3b678a311 100644 --- a/drivers/clk/clk-gate-shared.c +++ b/drivers/clk/clk-gate-shared.c @@ -69,8 +69,8 @@ static struct clk_ops clk_gate_shared_ops = { .is_enabled = clk_gate_shared_is_enabled, }; -struct clk *clk_gate_shared_alloc(const char *name, const char *parent, const char *companion, - unsigned flags) +static struct clk *clk_gate_shared_alloc(const char *name, const char *parent, + const char *companion, unsigned flags) { struct clk_gate_shared *g = xzalloc(sizeof(*g)); @@ -86,7 +86,7 @@ struct clk *clk_gate_shared_alloc(const char *name, const char *parent, const ch return &g->clk; } -void clk_gate_shared_free(struct clk *clk) +static void clk_gate_shared_free(struct clk *clk) { struct clk_gate_shared *g = to_clk_gate_shared(clk); diff --git a/drivers/clocksource/mvebu.c b/drivers/clocksource/mvebu.c index cf80571263..59bbc4be22 100644 --- a/drivers/clocksource/mvebu.c +++ b/drivers/clocksource/mvebu.c @@ -60,7 +60,7 @@ static int mvebu_timer_probe(struct device_d *dev) struct clk *clk; u32 rate, div, val; - iores = dev_request_mem_resource(dev, 0); + iores = dev_get_resource(dev, IORESOURCE_MEM, 0); if (IS_ERR(iores)) return PTR_ERR(iores); timer_base = IOMEM(iores->start); diff --git a/drivers/clocksource/timer-atmel-pit.c b/drivers/clocksource/timer-atmel-pit.c index cc7ad2f39a..947a1e7f49 100644 --- a/drivers/clocksource/timer-atmel-pit.c +++ b/drivers/clocksource/timer-atmel-pit.c @@ -102,9 +102,18 @@ static int at91_pit_probe(struct device_d *dev) return init_clock(&cs); } +const static __maybe_unused struct of_device_id at91_pit_dt_ids[] = { + { + .compatible = "atmel,at91sam9260-pit", + }, { + /* sentinel */ + } +}; + static struct driver_d at91_pit_driver = { .name = "at91-pit", .probe = at91_pit_probe, + .of_compatible = DRV_OF_COMPAT(at91_pit_dt_ids), }; static int at91_pit_init(void) diff --git a/drivers/crypto/caam/Kconfig b/drivers/crypto/caam/Kconfig index cf05d1c077..2ab509d110 100644 --- a/drivers/crypto/caam/Kconfig +++ b/drivers/crypto/caam/Kconfig @@ -29,6 +29,7 @@ config CRYPTO_DEV_FSL_CAAM_RINGSIZE config CRYPTO_DEV_FSL_CAAM_RNG bool "Register caam RNG device" depends on CRYPTO_DEV_FSL_CAAM + depends on HWRNG default y help Selecting this will register the SEC4 hardware rng. diff --git a/drivers/crypto/caam/caamrng.c b/drivers/crypto/caam/caamrng.c index 0fef171a2b..31a92731d2 100644 --- a/drivers/crypto/caam/caamrng.c +++ b/drivers/crypto/caam/caamrng.c @@ -35,6 +35,7 @@ #include <driver.h> #include <init.h> #include <linux/spinlock.h> +#include <linux/hw_random.h> #include "regs.h" #include "intern.h" @@ -54,7 +55,7 @@ /* Buffer, its dma address and lock */ struct buf_data { - u8 buf[RN_BUF_SIZE]; + u8 *buf; dma_addr_t addr; u32 hw_desc[DESC_JOB_O_LEN]; #define BUF_NOT_EMPTY 0 @@ -71,7 +72,7 @@ struct caam_rng_ctx { unsigned int cur_buf_idx; int current_buf; struct buf_data bufs[2]; - struct cdev cdev; + struct hwrng rng; }; static struct caam_rng_ctx *rng_ctx; @@ -116,8 +117,9 @@ static inline int submit_job(struct caam_rng_ctx *ctx, int to_current) return err; } -static int caam_read(struct caam_rng_ctx *ctx, void *data, size_t max, bool wait) +static int caam_read(struct hwrng *rng, void *data, size_t max, bool wait) { + struct caam_rng_ctx *ctx = container_of(rng, struct caam_rng_ctx, rng); struct buf_data *bd = &ctx->bufs[ctx->current_buf]; int next_buf_idx, copied_idx; int err; @@ -162,7 +164,7 @@ static int caam_read(struct caam_rng_ctx *ctx, void *data, size_t max, bool wait dev_dbg(ctx->jrdev, "switched to buffer %d\n", ctx->current_buf); /* since there already is some data read, don't wait */ - return copied_idx + caam_read(ctx, data + copied_idx, + return copied_idx + caam_read(rng, data + copied_idx, max - copied_idx, false); } @@ -216,6 +218,8 @@ static int caam_init_buf(struct caam_rng_ctx *ctx, int buf_id) struct buf_data *bd = &ctx->bufs[buf_id]; int err; + bd->buf = dma_alloc(RN_BUF_SIZE); + err = rng_create_job_desc(ctx, buf_id); if (err) return err; @@ -248,29 +252,6 @@ static int caam_init_rng(struct caam_rng_ctx *ctx, struct device_d *jrdev) return 0; } -static ssize_t random_read(struct cdev *cdev, void *buf, size_t count, - loff_t offset, ulong flags) -{ - struct caam_rng_ctx *ctx = container_of(cdev, struct caam_rng_ctx, cdev); - - return caam_read(ctx, buf, count, true); -} - -static struct file_operations randomops = { - .read = random_read, - .lseek = dev_lseek_default, -}; - -static int caam_init_devrandom(struct caam_rng_ctx *ctx, struct device_d *dev) -{ - ctx->cdev.name = "hwrng"; - ctx->cdev.flags = DEVFS_IS_CHARACTER_DEV; - ctx->cdev.ops = &randomops; - ctx->cdev.dev = dev; - - return devfs_create(&ctx->cdev); -} - int caam_rng_probe(struct device_d *dev, struct device_d *jrdev) { int err; @@ -281,9 +262,14 @@ int caam_rng_probe(struct device_d *dev, struct device_d *jrdev) if (err) return err; - err = caam_init_devrandom(rng_ctx, dev); - if (err) + rng_ctx->rng.name = dev->name; + rng_ctx->rng.read = caam_read; + + err = hwrng_register(dev, &rng_ctx->rng); + if (err) { + dev_err(dev, "rng-caam registering failed (%d)\n", err); return err; + } dev_info(dev, "registering rng-caam\n"); diff --git a/drivers/eeprom/at24.c b/drivers/eeprom/at24.c index 4ae3776554..1227286fbe 100644 --- a/drivers/eeprom/at24.c +++ b/drivers/eeprom/at24.c @@ -500,6 +500,7 @@ static int at24_probe(struct device_d *dev) goto err_devfs_create; of_parse_partitions(&at24->cdev, dev->device_node); + of_partitions_register_fixup(&at24->cdev); return 0; diff --git a/drivers/eeprom/at25.c b/drivers/eeprom/at25.c index f7f8368c4b..1caaebd371 100644 --- a/drivers/eeprom/at25.c +++ b/drivers/eeprom/at25.c @@ -360,6 +360,8 @@ static int at25_probe(struct device_d *dev) dev_dbg(dev, "%s probed\n", at25->cdev.name); of_parse_partitions(&at25->cdev, dev->device_node); + of_partitions_register_fixup(&at25->cdev); + return 0; fail: diff --git a/drivers/efi/efi-device.c b/drivers/efi/efi-device.c index 6ed7f12b37..e9b03cb02a 100644 --- a/drivers/efi/efi-device.c +++ b/drivers/efi/efi-device.c @@ -101,7 +101,8 @@ static efi_handle_t *efi_find_parent(efi_handle_t *handle) struct efi_open_protocol_information_entry *entry_buffer; unsigned long entry_count; - ret = efi_locate_handle(all_handles, NULL, NULL, &handle_count, &handles); + ret = efi_locate_handle(by_protocol, &efi_device_path_protocol_guid, + NULL, &handle_count, &handles); if (ret) return NULL; @@ -183,8 +184,6 @@ static struct efi_device *efi_add_device(efi_handle_t *handle, efi_guid_t **guid efidev->dev.info = efi_devinfo; efidev->devpath = devpath; - BS->handle_protocol(handle, &guidarr[0], &efidev->protocol); - sprintf(efidev->dev.name, "handle-%p", handle); efidev->parent_handle = efi_find_parent(efidev->handle); @@ -245,7 +244,8 @@ void efi_register_devices(void) struct efi_device **efidevs; int registered; - ret = efi_locate_handle(all_handles, NULL, NULL, &handle_count, &handles); + ret = efi_locate_handle(by_protocol, &efi_device_path_protocol_guid, + NULL, &handle_count, &handles); if (ret) return; @@ -310,8 +310,11 @@ static int efi_bus_match(struct device_d *dev, struct driver_d *drv) int i; for (i = 0; i < efidev->num_guids; i++) { - if (!memcmp(&efidrv->guid, &efidev->guids[i], sizeof(efi_guid_t))) + if (!memcmp(&efidrv->guid, &efidev->guids[i], sizeof(efi_guid_t))) { + BS->handle_protocol(efidev->handle, &efidev->guids[i], + &efidev->protocol); return 0; + } } return 1; diff --git a/drivers/hab/Makefile b/drivers/hab/Makefile index 8528ef954f..b169a1346d 100644 --- a/drivers/hab/Makefile +++ b/drivers/hab/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_HABV4) += habv4.o obj-$(CONFIG_HABV3) += habv3.o +obj-$(CONFIG_HAB) += hab.o diff --git a/drivers/hab/hab.c b/drivers/hab/hab.c new file mode 100644 index 0000000000..512ff7ecf2 --- /dev/null +++ b/drivers/hab/hab.c @@ -0,0 +1,358 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#define pr_fmt(fmt) "HAB: " fmt + +#include <common.h> +#include <fcntl.h> +#include <environment.h> +#include <libfile.h> +#include <mach/generic.h> +#include <hab.h> +#include <regmap.h> +#include <fs.h> +#include <mach/iim.h> +#include <mach/imx25-fusemap.h> +#include <mach/ocotp.h> +#include <mach/imx6-fusemap.h> + +bool imx_hab_srk_hash_valid(const void *buf) +{ + const u8 *srk = buf; + int all_zero = 1, all_ff = 1; + int i; + + for (i = 0; i < SRK_HASH_SIZE; i++) { + if (srk[i] != 0x0) + all_zero = 0; + if (srk[i] != 0xff) + all_ff = 0; + } + + return !all_zero && !all_ff; +} + +static int imx_hab_read_srk_hash_iim(u8 *srk) +{ + int ret, i; + unsigned int val; + + ret = imx_iim_read_field(IMX25_IIM_SRK0_HASH_0, &val); + if (ret < 0) + return ret; + srk[0] = val; + + for (i = 1; i < SRK_HASH_SIZE; i++) { + ret = imx_iim_read_field(IMX25_IIM_SRK0_HASH_1_31(i), &val); + if (ret < 0) + return ret; + srk[i] = val; + } + + return 0; +} + +static int imx_hab_write_srk_hash_iim(const u8 *srk, unsigned flags) +{ + int ret, i; + + ret = imx_iim_write_field(IMX25_IIM_SRK0_HASH_0, srk[0]); + if (ret < 0) + return ret; + + for (i = 1; i < SRK_HASH_SIZE; i++) { + ret = imx_iim_write_field(IMX25_IIM_SRK0_HASH_1_31(i), srk[i]); + if (ret < 0) + return ret; + } + + if (flags & IMX_SRK_HASH_WRITE_PERMANENT) { + u8 verify[SRK_HASH_SIZE]; + + setenv("iim.explicit_sense_enable", "1"); + ret = imx_hab_read_srk_hash_iim(verify); + if (ret) + return ret; + setenv("iim.explicit_sense_enable", "0"); + + if (memcmp(srk, verify, SRK_HASH_SIZE)) + return -EIO; + } + + if (flags & IMX_SRK_HASH_WRITE_LOCK) { + ret = imx_iim_write_field(IMX25_IIM_SRK0_LOCK96, 1); + if (ret < 0) + return ret; + ret = imx_iim_write_field(IMX25_IIM_SRK0_LOCK160, 1); + if (ret < 0) + return ret; + } + + return 0; +} + +static int imx_hab_permanent_write_enable_iim(int enable) +{ + return imx_iim_permanent_write(enable); +} + +static int imx_hab_lockdown_device_iim(void) +{ + return imx_iim_write_field(IMX25_IIM_HAB_TYPE, 3); +} + +static int imx_hab_device_locked_down_iim(void) +{ + int ret; + unsigned int v; + + ret = imx_iim_read_field(IMX25_IIM_HAB_TYPE, &v); + if (ret < 0) + return ret; + + return v == 1 ? false : true; +} + +static int imx_hab_read_srk_hash_ocotp(u8 *__srk) +{ + u32 *srk = (u32 *)__srk; + int ret, i; + + for (i = 0; i < SRK_HASH_SIZE / sizeof(uint32_t); i++) { + ret = imx_ocotp_read_field(OCOTP_SRK_HASH(i), &srk[i]); + if (ret < 0) + return ret; + } + + return 0; +} + +static int imx_hab_write_srk_hash_ocotp(const u8 *__newsrk, unsigned flags) +{ + u32 *newsrk = (u32 *)__newsrk; + int ret, i; + + for (i = 0; i < SRK_HASH_SIZE / sizeof(uint32_t); i++) { + ret = imx_ocotp_write_field(OCOTP_SRK_HASH(i), newsrk[i]); + if (ret < 0) + return ret; + } + + if (flags & IMX_SRK_HASH_WRITE_LOCK) { + ret = imx_ocotp_write_field(OCOTP_SRK_LOCK, 1); + if (ret < 0) + return ret; + } + + return 0; +} + +static int imx_hab_permanent_write_enable_ocotp(int enable) +{ + return imx_ocotp_permanent_write(enable); +} + +static int imx_hab_lockdown_device_ocotp(void) +{ + return imx_ocotp_write_field(OCOTP_SEC_CONFIG_1, 1); +} + +static int imx_hab_device_locked_down_ocotp(void) +{ + int ret; + unsigned int v; + + ret = imx_ocotp_read_field(OCOTP_SEC_CONFIG_1, &v); + if (ret < 0) + return ret; + + return v; +} + +struct imx_hab_ops { + int (*init)(void); + int (*write_srk_hash)(const u8 *srk, unsigned flags); + int (*read_srk_hash)(u8 *srk); + int (*permanent_write_enable)(int enable); + int (*lockdown_device)(void); + int (*device_locked_down)(void); +}; + +static struct imx_hab_ops imx_hab_ops_iim = { + .write_srk_hash = imx_hab_write_srk_hash_iim, + .read_srk_hash = imx_hab_read_srk_hash_iim, + .lockdown_device = imx_hab_lockdown_device_iim, + .device_locked_down = imx_hab_device_locked_down_iim, + .permanent_write_enable = imx_hab_permanent_write_enable_iim, +}; + +static struct imx_hab_ops imx_hab_ops_ocotp = { + .write_srk_hash = imx_hab_write_srk_hash_ocotp, + .read_srk_hash = imx_hab_read_srk_hash_ocotp, + .lockdown_device = imx_hab_lockdown_device_ocotp, + .device_locked_down = imx_hab_device_locked_down_ocotp, + .permanent_write_enable = imx_hab_permanent_write_enable_ocotp, +}; + +static struct imx_hab_ops *imx_get_hab_ops(void) +{ + static struct imx_hab_ops *ops, *tmp; + int ret; + + if (ops) + return ops; + + if (cpu_is_mx25() || cpu_is_mx35()) + tmp = &imx_hab_ops_iim; + else if (cpu_is_mx6()) + tmp = &imx_hab_ops_ocotp; + else + return NULL; + + if (tmp->init) { + ret = tmp->init(); + if (ret) + return NULL; + } + + ops = tmp; + + return ops; +} + +int imx_hab_read_srk_hash(void *buf) +{ + struct imx_hab_ops *ops = imx_get_hab_ops(); + + if (!ops) + return -ENOSYS; + + return ops->read_srk_hash(buf); +} + +int imx_hab_write_srk_hash(const void *buf, unsigned flags) +{ + u8 cursrk[SRK_HASH_SIZE]; + int ret; + struct imx_hab_ops *ops = imx_get_hab_ops(); + + if (!ops) + return -ENOSYS; + + ret = ops->read_srk_hash(cursrk); + if (ret) { + pr_err("Cannot read current SRK hash: %s\n", strerror(-ret)); + return ret; + } + + if (imx_hab_srk_hash_valid(cursrk)) { + char *str = "Current SRK hash is valid"; + + if (flags & IMX_SRK_HASH_FORCE) { + pr_warn("%s, ignoring\n", str); + } else { + pr_err("%s, refusing to burn again\n", str); + return -EEXIST; + } + } + + if (flags & IMX_SRK_HASH_WRITE_PERMANENT) { + ret = ops->permanent_write_enable(1); + if (ret) + return ret; + } + + ret = ops->write_srk_hash(buf, flags); + + if (flags & IMX_SRK_HASH_WRITE_PERMANENT) + ops->permanent_write_enable(0); + + return ret; +} + +int imx_hab_write_srk_hash_file(const char *filename, unsigned flags) +{ + int ret; + size_t size; + void *srk; + + ret = read_file_2(filename, &size, &srk, FILESIZE_MAX); + if (ret) + return ret; + + if (size != SRK_HASH_SIZE) { + pr_err("File has wrong size, must be %d bytes\n", SRK_HASH_SIZE); + return -EINVAL; + } + + ret = imx_hab_write_srk_hash(srk, flags); + + free(srk); + + return ret; +} + +int imx_hab_write_srk_hash_hex(const char *srkhash, unsigned flags) +{ + int ret; + u8 srk[SRK_HASH_SIZE]; + + if (strlen(srkhash) != SRK_HASH_SIZE * 2) { + pr_err("Invalid srk hash %s\n", srkhash); + return -EINVAL; + } + + ret = hex2bin(srk, srkhash, SRK_HASH_SIZE); + if (ret < 0) { + pr_err("Invalid srk hash %s\n", srkhash); + return -EINVAL; + } + + return imx_hab_write_srk_hash(srk, flags); +} + +int imx_hab_lockdown_device(unsigned flags) +{ + struct imx_hab_ops *ops = imx_get_hab_ops(); + u8 srk[SRK_HASH_SIZE]; + int ret; + + ret = imx_hab_read_srk_hash(srk); + if (ret) + return ret; + + if (!imx_hab_srk_hash_valid(srk)) { + pr_err("No SRK hash burnt into fuses. Refusing to lock device\n"); + return -EINVAL; + } + + if (!ops) + return -ENOSYS; + + if (flags & IMX_SRK_HASH_WRITE_PERMANENT) { + ret = ops->permanent_write_enable(1); + if (ret) + return ret; + } + + ret = ops->lockdown_device(); + + if (flags & IMX_SRK_HASH_WRITE_PERMANENT) + ops->permanent_write_enable(0); + + return ret; +} + +int imx_hab_device_locked_down(void) +{ + struct imx_hab_ops *ops = imx_get_hab_ops(); + + return ops->device_locked_down(); +} diff --git a/drivers/hw_random/Kconfig b/drivers/hw_random/Kconfig new file mode 100644 index 0000000000..807fcadd31 --- /dev/null +++ b/drivers/hw_random/Kconfig @@ -0,0 +1,6 @@ +menuconfig HWRNG + bool "HWRNG Support" + help + Support for HWRNG(Hardware Random Number Generator) devices. + + If unsure, say no. diff --git a/drivers/hw_random/Makefile b/drivers/hw_random/Makefile new file mode 100644 index 0000000000..15307b100f --- /dev/null +++ b/drivers/hw_random/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_HWRNG) += core.o diff --git a/drivers/hw_random/core.c b/drivers/hw_random/core.c new file mode 100644 index 0000000000..ef2a988c76 --- /dev/null +++ b/drivers/hw_random/core.c @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2016 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * derived from Linux kernel drivers/char/hw_random/core.c + */ + +#include <common.h> +#include <linux/hw_random.h> +#include <malloc.h> + +static LIST_HEAD(hwrngs); + +#define RNG_BUFFER_SIZE 32 + +int hwrng_get_data(struct hwrng *rng, void *buffer, size_t size, int wait) +{ + return rng->read(rng, buffer, size, wait); +} + +static int hwrng_init(struct hwrng *rng) +{ + int ret = 0; + + if (rng->init) + ret = rng->init(rng); + + if (!ret) + list_add_tail(&rng->list, &hwrngs); + + return ret; +} + +static ssize_t rng_dev_read(struct cdev *cdev, void *buf, size_t size, + loff_t offset, unsigned long flags) +{ + struct hwrng *rng = container_of(cdev, struct hwrng, cdev); + size_t count = size; + ssize_t cur = 0; + int len; + + memset(buf, 0, size); + + while (count) { + int max = min(count, (size_t)RNG_BUFFER_SIZE); + len = hwrng_get_data(rng, rng->buf, max, true); + if (len < 0) { + cur = len; + break; + } + + memcpy(buf + cur, rng->buf, len); + + count -= len; + cur += len; + } + + return cur; +} + +static struct file_operations rng_chrdev_ops = { + .read = rng_dev_read, + .lseek = dev_lseek_default, +}; + +static int hwrng_register_cdev(struct hwrng *rng) +{ + struct device_d *dev = rng->dev; + const char *alias; + char *devname; + int err; + + alias = of_alias_get(dev->device_node); + if (alias) { + devname = xstrdup(alias); + } else { + err = cdev_find_free_index("hwrng"); + if (err < 0) { + dev_err(dev, "no index found to name device\n"); + return err; + } + devname = xasprintf("hwrng%d", err); + } + + rng->cdev.name = devname; + rng->cdev.flags = DEVFS_IS_CHARACTER_DEV; + rng->cdev.ops = &rng_chrdev_ops; + rng->cdev.dev = rng->dev; + + return devfs_create(&rng->cdev); +} + +struct hwrng *hwrng_get_first(void) +{ + if (list_empty(&hwrngs)) + return ERR_PTR(-ENODEV); + else + return list_first_entry(&hwrngs, struct hwrng, list); +} + +int hwrng_register(struct device_d *dev, struct hwrng *rng) +{ + int err; + + if (rng->name == NULL || rng->read == NULL) + return -EINVAL; + + rng->buf = xzalloc(RNG_BUFFER_SIZE); + rng->dev = dev; + + err = hwrng_init(rng); + if (err) { + free(rng->buf); + return err; + } + + err = hwrng_register_cdev(rng); + if (err) + free(rng->buf); + + return err; +} diff --git a/drivers/led/Kconfig b/drivers/led/Kconfig index 155a78a7df..50f0d8f974 100644 --- a/drivers/led/Kconfig +++ b/drivers/led/Kconfig @@ -1,5 +1,6 @@ menuconfig LED bool "LED support" + select POLLER if LED diff --git a/drivers/led/core.c b/drivers/led/core.c index 30b016bb34..6f66de0fbb 100644 --- a/drivers/led/core.c +++ b/drivers/led/core.c @@ -23,6 +23,7 @@ #include <linux/list.h> #include <errno.h> #include <led.h> +#include <init.h> #include <poller.h> #include <clock.h> #include <linux/ctype.h> @@ -101,7 +102,7 @@ struct led *led_by_name_or_number(const char *str) * @param led the led * @param value the value of the LED (0 is disabled) */ -int led_set(struct led *led, unsigned int value) +static int __led_set(struct led *led, unsigned int value) { if (value > led->max_value) value = led->max_value; @@ -114,6 +115,104 @@ int led_set(struct led *led, unsigned int value) return 0; } +int led_set(struct led *led, unsigned int value) +{ + led->blink = 0; + led->flash = 0; + return __led_set(led, value); +} + +static void led_blink_func(struct poller_struct *poller) +{ + struct led *led; + + list_for_each_entry(led, &leds, list) { + bool on; + + if (!led->blink && !led->flash) + continue; + + if (led->blink_next_event > get_time_ns()) { + continue; + } + + on = !(led->blink_next_state % 2); + + led->blink_next_event = get_time_ns() + + (led->blink_states[led->blink_next_state] * MSECOND); + led->blink_next_state = (led->blink_next_state + 1) % + led->blink_nr_states; + + if (led->flash && !on) + led->flash = 0; + + __led_set(led, on); + } +} + +/** + * led_blink_pattern - Blink a led with flexible timings. + * @led LED used + * @pattern Array of millisecond intervals describing the on and off periods of + * the pattern. At the end of the array/pattern it is repeated. The array + * starts with an on-period. In general every array item with even index + * describes an on-period, every item with odd index an off-period. + * @pattern_len Length of the pattern array. + * + * Returns 0 on success. + * + * Example: + * pattern = {500, 1000}; + * This will enable the LED for 500ms and disable it for 1000ms after + * that. This is repeated forever. + */ +int led_blink_pattern(struct led *led, const unsigned int *pattern, + unsigned int pattern_len) +{ + free(led->blink_states); + led->blink_states = xmemdup(pattern, + pattern_len * sizeof(*led->blink_states)); + led->blink_nr_states = pattern_len; + led->blink_next_state = 0; + led->blink_next_event = get_time_ns(); + led->blink = 1; + led->flash = 0; + + return 0; +} + +int led_blink(struct led *led, unsigned int on_ms, unsigned int off_ms) +{ + unsigned int pattern[] = {on_ms, off_ms}; + + return led_blink_pattern(led, pattern, 2); +} + +int led_flash(struct led *led, unsigned int duration_ms) +{ + unsigned int pattern[] = {duration_ms, 0}; + int ret; + + ret = led_blink_pattern(led, pattern, 2); + if (ret) + return ret; + + led->flash = 1; + led->blink = 0; + + return 0; +} + +static struct poller_struct led_poller = { + .func = led_blink_func, +}; + +static int led_blink_init(void) +{ + return poller_register(&led_poller); +} +late_initcall(led_blink_init); + /** * led_set_num - set the value of a LED * @param num the number of the LED diff --git a/drivers/led/led-triggers.c b/drivers/led/led-triggers.c index dee936739a..76a1481e14 100644 --- a/drivers/led/led-triggers.c +++ b/drivers/led/led-triggers.c @@ -48,33 +48,11 @@ struct led_trigger_struct { struct led *led; - uint64_t flash_start; - int flash; + struct list_head list; + enum led_trigger trigger; }; -static struct led_trigger_struct triggers[LED_TRIGGER_MAX]; - -static void trigger_func(struct poller_struct *poller) -{ - int i; - - for (i = 0; i < LED_TRIGGER_MAX; i++) { - if (triggers[i].led && - triggers[i].flash && - is_timeout(triggers[i].flash_start, 200 * MSECOND)) { - led_set(triggers[i].led, 0); - triggers[i].flash = 0; - } - } - - if (triggers[LED_TRIGGER_HEARTBEAT].led && - is_timeout(triggers[LED_TRIGGER_HEARTBEAT].flash_start, SECOND)) - led_trigger(LED_TRIGGER_HEARTBEAT, TRIGGER_FLASH); -} - -static struct poller_struct trigger_poller = { - .func = trigger_func, -}; +static LIST_HEAD(led_triggers); /** * led_trigger - triggers a trigger @@ -85,21 +63,53 @@ static struct poller_struct trigger_poller = { */ void led_trigger(enum led_trigger trigger, enum trigger_type type) { + struct led_trigger_struct *led_trigger; + if (trigger >= LED_TRIGGER_MAX) return; - if (!triggers[trigger].led) - return; - if (type == TRIGGER_FLASH) { - if (is_timeout(triggers[trigger].flash_start, 400 * MSECOND)) { - led_set(triggers[trigger].led, triggers[trigger].led->max_value); - triggers[trigger].flash_start = get_time_ns(); - triggers[trigger].flash = 1; + list_for_each_entry(led_trigger, &led_triggers, list) { + if (led_trigger->trigger != trigger) + continue; + + switch (type) { + case TRIGGER_FLASH: + led_flash(led_trigger->led, 200); + break; + case TRIGGER_ENABLE: + led_set(led_trigger->led, led_trigger->led->max_value); + break; + case TRIGGER_DISABLE: + led_set(led_trigger->led, 0); + break; } - return; } +} + +static struct led_trigger_struct *led_find_trigger(struct led *led) +{ + struct led_trigger_struct *led_trigger; + + list_for_each_entry(led_trigger, &led_triggers, list) + if (led_trigger->led == led) + return led_trigger; + + return NULL; +} + +void led_trigger_disable(struct led *led) +{ + struct led_trigger_struct *led_trigger; + + led_trigger = led_find_trigger(led); + if (!led_trigger) + return; + + list_del(&led_trigger->list); + + led_set(led, 0); - led_set(triggers[trigger].led, type == TRIGGER_ENABLE ? triggers[trigger].led->max_value : 0); + free(led_trigger); } /** @@ -112,44 +122,73 @@ void led_trigger(enum led_trigger trigger, enum trigger_type type) */ int led_set_trigger(enum led_trigger trigger, struct led *led) { - int i; + struct led_trigger_struct *led_trigger; if (trigger >= LED_TRIGGER_MAX) return -EINVAL; - if (led) - for (i = 0; i < LED_TRIGGER_MAX; i++) - if (triggers[i].led == led) - return -EBUSY; + led_trigger_disable(led); - if (triggers[trigger].led && !led) - led_set(triggers[trigger].led, 0); + led_trigger = xzalloc(sizeof(*led_trigger)); - triggers[trigger].led = led; + led_trigger->led = led; + led_trigger->trigger = trigger; + list_add_tail(&led_trigger->list, &led_triggers); - if (led && trigger == LED_TRIGGER_DEFAULT_ON) - led_set(triggers[trigger].led, triggers[trigger].led->max_value); + if (trigger == LED_TRIGGER_DEFAULT_ON) + led_set(led, led->max_value); + if (trigger == LED_TRIGGER_HEARTBEAT) + led_blink(led, 200, 1000); return 0; } -/** - * led_get_trigger - get the LED for a trigger - * @param trigger The trigger to set a LED for - * - * return the LED number of a trigger. - */ -int led_get_trigger(enum led_trigger trigger) +static char *trigger_names[] = { + [LED_TRIGGER_PANIC] = "panic", + [LED_TRIGGER_HEARTBEAT] = "heartbeat", + [LED_TRIGGER_NET_RX] = "net-rx", + [LED_TRIGGER_NET_TX] = "net-tx", + [LED_TRIGGER_NET_TXRX] = "net", + [LED_TRIGGER_DEFAULT_ON] = "default-on", +}; + +const char *trigger_name(enum led_trigger trigger) { - if (trigger >= LED_TRIGGER_MAX) - return -EINVAL; - if (!triggers[trigger].led) - return -ENODEV; - return led_get_number(triggers[trigger].led); + return trigger_names[trigger]; +} + +enum led_trigger trigger_by_name(const char *name) +{ + int i; + + for (i = 0; i < LED_TRIGGER_MAX; i++) + if (!strcmp(name, trigger_names[i])) + return i; + + return LED_TRIGGER_MAX; } -static int trigger_init(void) +/** + * led_triggers_show_info - Show information about all registered + * triggers + */ +void led_triggers_show_info(void) { - return poller_register(&trigger_poller); + struct led_trigger_struct *led_trigger; + int i; + + for (i = 0; i < LED_TRIGGER_MAX; i++) { + printf("%s", trigger_name(i)); + + list_for_each_entry(led_trigger, &led_triggers, list) { + struct led *led = led_trigger->led; + + if (led_trigger->trigger != i) + continue; + + printf("\n LED %d (%s)", led->num, led->name); + } + + printf("\n"); + } } -late_initcall(trigger_init); diff --git a/drivers/mci/atmel_mci.c b/drivers/mci/atmel_mci.c index 2a0ddb052b..317cf46022 100644 --- a/drivers/mci/atmel_mci.c +++ b/drivers/mci/atmel_mci.c @@ -24,6 +24,7 @@ #include <mach/board.h> #include <linux/clk.h> #include <linux/err.h> +#include <of_gpio.h> #include "atmel-mci-regs.h" @@ -53,6 +54,7 @@ struct atmel_mci { u32 cfg_reg; u32 sdc_reg; bool need_reset; + int detect_pin; }; #define to_mci_host(mci) container_of(mci, struct atmel_mci, mci) @@ -360,14 +362,13 @@ static int atmci_start_cmd(struct atmel_mci *host, struct mci_cmd *cmd, static int atmci_card_present(struct mci_host *mci) { struct atmel_mci *host = to_mci_host(mci); - struct atmel_mci_platform_data *pd = host->hw_dev->platform_data; int ret; /* No gpio, assume card is present */ - if (!gpio_is_valid(pd->detect_pin)) + if (!gpio_is_valid(host->detect_pin)) return 1; - ret = gpio_get_value(pd->detect_pin); + ret = gpio_get_value(host->detect_pin); return ret == 0 ? 1 : 0; } @@ -535,44 +536,71 @@ static int atmci_probe(struct device_d *hw_dev) { struct resource *iores; struct atmel_mci *host; + struct device_node *np = hw_dev->device_node; struct atmel_mci_platform_data *pd = hw_dev->platform_data; int ret; - if (!pd) { - dev_err(hw_dev, "missing platform data\n"); - return -EINVAL; + host = xzalloc(sizeof(*host)); + host->mci.send_cmd = atmci_request; + host->mci.set_ios = atmci_set_ios; + host->mci.init = atmci_reset; + host->mci.card_present = atmci_card_present; + host->mci.hw_dev = hw_dev; + host->detect_pin = -EINVAL; + + if (pd) { + host->detect_pin = pd->detect_pin; + host->mci.devname = pd->devname; + + if (pd->bus_width >= 4) + host->mci.host_caps |= MMC_CAP_4_BIT_DATA; + if (pd->bus_width == 8) + host->mci.host_caps |= MMC_CAP_8_BIT_DATA; + + host->slot_b = pd->slot_b; + } else if (np) { + u32 slot_id; + struct device_node *cnp; + const char *alias = of_alias_get(np); + + if (alias) + host->mci.devname = xstrdup(alias); + + host->detect_pin = of_get_named_gpio(np, "cd-gpios", 0); + + for_each_child_of_node(np, cnp) { + if (of_property_read_u32(cnp, "reg", &slot_id)) { + dev_warn(hw_dev, "reg property is missing for %s\n", + cnp->full_name); + continue; + } + + host->slot_b = slot_id; + mci_of_parse_node(&host->mci, cnp); + break; + } + } else { + dev_err(hw_dev, "Missing device information\n"); + ret = -EINVAL; + goto error_out; } - if (gpio_is_valid(pd->detect_pin)) { - ret = gpio_request(pd->detect_pin, "mci_cd"); + if (gpio_is_valid(host->detect_pin)) { + ret = gpio_request(host->detect_pin, "mci_cd"); if (ret) { dev_err(hw_dev, "Impossible to request CD gpio %d (%d)\n", - ret, pd->detect_pin); - return ret; + ret, host->detect_pin); + goto error_out; } - ret = gpio_direction_input(pd->detect_pin); + ret = gpio_direction_input(host->detect_pin); if (ret) { dev_err(hw_dev, "Impossible to configure CD gpio %d as input (%d)\n", - ret, pd->detect_pin); - goto err_gpio_cd_request; + ret, host->detect_pin); + goto error_out; } } - host = xzalloc(sizeof(*host)); - host->mci.send_cmd = atmci_request; - host->mci.set_ios = atmci_set_ios; - host->mci.init = atmci_reset; - host->mci.card_present = atmci_card_present; - host->mci.hw_dev = hw_dev; - host->mci.devname = pd->devname; - - if (pd->bus_width >= 4) - host->mci.host_caps |= MMC_CAP_4_BIT_DATA; - if (pd->bus_width == 8) - host->mci.host_caps |= MMC_CAP_8_BIT_DATA; - host->slot_b = pd->slot_b; - iores = dev_request_mem_resource(hw_dev, 0); if (IS_ERR(iores)) return PTR_ERR(iores); @@ -583,7 +611,7 @@ static int atmci_probe(struct device_d *hw_dev) if (IS_ERR(host->clk)) { dev_err(hw_dev, "no mci_clk\n"); ret = PTR_ERR(host->clk); - goto err_gpio_cd_request; + goto error_out; } clk_enable(host->clk); @@ -614,15 +642,26 @@ static int atmci_probe(struct device_d *hw_dev) return 0; -err_gpio_cd_request: - if (gpio_is_valid(pd->detect_pin)) - gpio_free(pd->detect_pin); +error_out: + free(host); + + if (gpio_is_valid(host->detect_pin)) + gpio_free(host->detect_pin); return ret; } +static __maybe_unused struct of_device_id atmci_compatible[] = { + { + .compatible = "atmel,hsmci", + }, { + /* sentinel */ + } +}; + static struct driver_d atmci_driver = { .name = "atmel_mci", .probe = atmci_probe, + .of_compatible = DRV_OF_COMPAT(atmci_compatible), }; device_platform_driver(atmci_driver); diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c index 055a5e2b06..b173a173b1 100644 --- a/drivers/mci/mci-core.c +++ b/drivers/mci/mci-core.c @@ -1621,8 +1621,10 @@ static int mci_register_partition(struct mci_part *part) rc = 0; /* it's not a failure */ } - if (np) + if (np) { of_parse_partitions(&part->blk.cdev, np); + of_partitions_register_fixup(&part->blk.cdev); + } return 0; } @@ -1843,20 +1845,18 @@ err_free: return ret; } -void mci_of_parse(struct mci_host *host) +void mci_of_parse_node(struct mci_host *host, + struct device_node *np) { - struct device_node *np; u32 bus_width; u32 dsr_val; if (!IS_ENABLED(CONFIG_OFDEVICE)) return; - if (!host->hw_dev || !host->hw_dev->device_node) + if (!host->hw_dev || !np) return; - np = host->hw_dev->device_node; - /* "bus-width" is translated to MMC_CAP_*_BIT_DATA flags */ if (of_property_read_u32(np, "bus-width", &bus_width) < 0) { /* If bus-width is missing we get the driver's default, which @@ -1897,6 +1897,11 @@ void mci_of_parse(struct mci_host *host) host->non_removable = of_property_read_bool(np, "non-removable"); } +void mci_of_parse(struct mci_host *host) +{ + return mci_of_parse_node(host, host->hw_dev->device_node); +} + struct mci *mci_get_device_by_name(const char *name) { struct mci *mci; diff --git a/drivers/misc/state.c b/drivers/misc/state.c index b43aee60fe..98ed42e757 100644 --- a/drivers/misc/state.c +++ b/drivers/misc/state.c @@ -26,6 +26,7 @@ static int state_probe(struct device_d *dev) struct device_node *np = dev->device_node; struct state *state; bool readonly = false; + int ret; state = state_new_from_node(np, NULL, 0, 0, readonly); if (IS_ERR(state)) { @@ -35,6 +36,11 @@ static int state_probe(struct device_d *dev) return ret; } + ret = state_load(state); + if (ret) + dev_warn(dev, "Failed to load persistent state, continuing with defaults, %d\n", + ret); + return 0; } diff --git a/drivers/mtd/core.c b/drivers/mtd/core.c index 4e7bfdb3da..1eb8dd36d8 100644 --- a/drivers/mtd/core.c +++ b/drivers/mtd/core.c @@ -556,67 +556,6 @@ static int mtd_part_compare(struct list_head *a, struct list_head *b) return 0; } -static int of_mtd_fixup(struct device_node *root, void *ctx) -{ - struct mtd_info *mtd = ctx, *partmtd; - struct device_node *np, *part, *tmp; - int ret; - - np = of_find_node_by_path_from(root, mtd->of_path); - if (!np) { - dev_err(&mtd->class_dev, "Cannot find nodepath %s, cannot fixup\n", - mtd->of_path); - return -EINVAL; - } - - for_each_child_of_node_safe(np, tmp, part) { - if (of_get_property(part, "compatible", NULL)) - continue; - of_delete_node(part); - } - - list_for_each_entry(partmtd, &mtd->partitions, partitions_entry) { - int na, ns, len = 0; - char *name = basprintf("partition@%0llx", - partmtd->master_offset); - void *p; - u8 tmp[16 * 16]; /* Up to 64-bit address + 64-bit size */ - - if (!name) - return -ENOMEM; - - part = of_new_node(np, name); - free(name); - if (!part) - return -ENOMEM; - - p = of_new_property(part, "label", partmtd->cdev.partname, - strlen(partmtd->cdev.partname) + 1); - if (!p) - return -ENOMEM; - - na = of_n_addr_cells(part); - ns = of_n_size_cells(part); - - of_write_number(tmp + len, partmtd->master_offset, na); - len += na * 4; - of_write_number(tmp + len, partmtd->size, ns); - len += ns * 4; - - ret = of_set_property(part, "reg", tmp, len, 1); - if (ret) - return ret; - - if (partmtd->cdev.flags & DEVFS_PARTITION_READONLY) { - ret = of_set_property(part, "read-only", NULL, 0, 1); - if (ret) - return ret; - } - } - - return 0; -} - static int mtd_detect(struct device_d *dev) { struct mtd_info *mtd = container_of(dev, struct mtd_info, class_dev); @@ -732,7 +671,9 @@ int add_mtd_device(struct mtd_info *mtd, const char *devname, int device_id) of_parse_partitions(&mtd->cdev, mtd->parent->device_node); if (IS_ENABLED(CONFIG_OFDEVICE) && mtd->parent->device_node) { mtd->of_path = xstrdup(mtd->parent->device_node->full_name); - of_register_fixup(of_mtd_fixup, mtd); + ret = of_partitions_register_fixup(&mtd->cdev); + if (ret) + goto err1; } } diff --git a/drivers/mtd/partition.c b/drivers/mtd/partition.c index 777cb758ce..013697732d 100644 --- a/drivers/mtd/partition.c +++ b/drivers/mtd/partition.c @@ -225,6 +225,8 @@ struct mtd_info *mtd_add_partition(struct mtd_info *mtd, off_t offset, if (ret) goto err; + part->cdev.master = &part->master->cdev; + return part; err: free(part->cdev.partname); diff --git a/drivers/mtd/peb.c b/drivers/mtd/peb.c index 66227d4ffb..c35b63f2fd 100644 --- a/drivers/mtd/peb.c +++ b/drivers/mtd/peb.c @@ -565,6 +565,8 @@ int mtd_peb_create_bitflips(struct mtd_info *mtd, int pnum, int offset, if (offset < 0 || offset + len > mtd->erasesize) return -EINVAL; + if (offset % mtd->writesize) + return -EINVAL; if (len <= 0) return -EINVAL; if (num_bitflips <= 0) @@ -610,7 +612,7 @@ int mtd_peb_create_bitflips(struct mtd_info *mtd, int pnum, int offset, for (i = 0; i < num_bitflips; i++) { int offs; int bit; - u8 *pos = buf; + u8 *pos = buf + offset; if (random) { offs = random32() % len; diff --git a/drivers/net/macb.c b/drivers/net/macb.c index 5f2e5e5131..739a3dfbef 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -48,6 +48,7 @@ #include <linux/clk.h> #include <linux/err.h> #include <linux/phy.h> +#include <of_net.h> #include "macb.h" @@ -615,14 +616,8 @@ static int macb_probe(struct device_d *dev) struct resource *iores; struct eth_device *edev; struct macb_device *macb; + const char *pclk_name; u32 ncfgr; - struct macb_platform_data *pdata; - - if (!dev->platform_data) { - dev_err(dev, "macb: no platform_data\n"); - return -ENODEV; - } - pdata = dev->platform_data; edev = xzalloc(sizeof(struct eth_device) + sizeof(struct macb_device)); edev->priv = (struct macb_device *)(edev + 1); @@ -633,22 +628,49 @@ static int macb_probe(struct device_d *dev) edev->open = macb_open; edev->send = macb_send; edev->halt = macb_halt; - edev->get_ethaddr = pdata->get_ethaddr ? pdata->get_ethaddr : macb_get_ethaddr; + edev->get_ethaddr = macb_get_ethaddr; edev->set_ethaddr = macb_set_ethaddr; edev->parent = dev; macb->miibus.read = macb_phy_read; macb->miibus.write = macb_phy_write; - macb->phy_addr = pdata->phy_addr; macb->miibus.priv = macb; macb->miibus.parent = dev; - if (pdata->phy_interface == PHY_INTERFACE_MODE_NA) - macb->interface = PHY_INTERFACE_MODE_MII; - else - macb->interface = pdata->phy_interface; + if (dev->platform_data) { + struct macb_platform_data *pdata = dev->platform_data; + + if (pdata->phy_interface == PHY_INTERFACE_MODE_NA) + macb->interface = PHY_INTERFACE_MODE_MII; + else + macb->interface = pdata->phy_interface; + + if (pdata->get_ethaddr) + edev->get_ethaddr = pdata->get_ethaddr; + + macb->phy_addr = pdata->phy_addr; + macb->phy_flags = pdata->phy_flags; + pclk_name = "macb_clk"; + } else if (IS_ENABLED(CONFIG_OFDEVICE) && dev->device_node) { + int ret; + struct device_node *mdiobus; - macb->phy_flags = pdata->phy_flags; + ret = of_get_phy_mode(dev->device_node); + if (ret < 0) + macb->interface = PHY_INTERFACE_MODE_MII; + else + macb->interface = ret; + + mdiobus = of_get_child_by_name(dev->device_node, "mdio"); + if (mdiobus) + macb->miibus.dev.device_node = mdiobus; + + macb->phy_addr = -1; + pclk_name = NULL; + } else { + dev_err(dev, "macb: no platform_data\n"); + return -ENODEV; + } iores = dev_request_mem_resource(dev, 0); if (IS_ERR(iores)) @@ -698,8 +720,14 @@ static int macb_probe(struct device_d *dev) return 0; } +static const struct of_device_id macb_dt_ids[] = { + { .compatible = "cdns,at91sam9260-macb",}, + { /* sentinel */ } +}; + static struct driver_d macb_driver = { .name = "macb", .probe = macb_probe, + .of_compatible = DRV_OF_COMPAT(macb_dt_ids), }; device_platform_driver(macb_driver); diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 38b2ad31f8..ea5a4a9be2 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -31,6 +31,22 @@ #define MII_M1011_PHY_STATUS_RESOLVED BIT(11) #define MII_M1011_PHY_STATUS_LINK BIT(10) +#define MII_M1111_PHY_LED_CONTROL 0x18 +#define MII_M1111_PHY_LED_DIRECT 0x4100 +#define MII_M1111_PHY_LED_COMBINE 0x411c +#define MII_M1111_PHY_EXT_CR 0x14 +#define MII_M1111_RX_DELAY 0x80 +#define MII_M1111_TX_DELAY 0x2 +#define MII_M1111_PHY_EXT_SR 0x1b + +#define MII_M1111_HWCFG_MODE_MASK 0xf +#define MII_M1111_HWCFG_MODE_COPPER_RGMII 0xb +#define MII_M1111_HWCFG_MODE_FIBER_RGMII 0x3 +#define MII_M1111_HWCFG_MODE_SGMII_NO_CLK 0x4 +#define MII_M1111_HWCFG_MODE_COPPER_RTBI 0x9 +#define MII_M1111_HWCFG_FIBER_COPPER_AUTO 0x8000 +#define MII_M1111_HWCFG_FIBER_COPPER_RES 0x2000 + #define MII_M1111_COPPER 0 #define MII_M1111_FIBER 1 @@ -402,6 +418,107 @@ static int m88e1540_config_init(struct phy_device *phydev) return marvell_config_init(phydev); } +static int m88e1111_config_init(struct phy_device *phydev) +{ + int err; + int temp; + + if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) || + (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) || + (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) || + (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)) { + + temp = phy_read(phydev, MII_M1111_PHY_EXT_CR); + if (temp < 0) + return temp; + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) { + temp |= (MII_M1111_RX_DELAY | MII_M1111_TX_DELAY); + } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { + temp &= ~MII_M1111_TX_DELAY; + temp |= MII_M1111_RX_DELAY; + } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { + temp &= ~MII_M1111_RX_DELAY; + temp |= MII_M1111_TX_DELAY; + } + + err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp); + if (err < 0) + return err; + + temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); + if (temp < 0) + return temp; + + temp &= ~(MII_M1111_HWCFG_MODE_MASK); + + if (temp & MII_M1111_HWCFG_FIBER_COPPER_RES) + temp |= MII_M1111_HWCFG_MODE_FIBER_RGMII; + else + temp |= MII_M1111_HWCFG_MODE_COPPER_RGMII; + + err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); + if (err < 0) + return err; + } + + if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { + temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); + if (temp < 0) + return temp; + + temp &= ~(MII_M1111_HWCFG_MODE_MASK); + temp |= MII_M1111_HWCFG_MODE_SGMII_NO_CLK; + temp |= MII_M1111_HWCFG_FIBER_COPPER_AUTO; + + err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); + if (err < 0) + return err; + } + + if (phydev->interface == PHY_INTERFACE_MODE_RTBI) { + temp = phy_read(phydev, MII_M1111_PHY_EXT_CR); + if (temp < 0) + return temp; + temp |= (MII_M1111_RX_DELAY | MII_M1111_TX_DELAY); + err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp); + if (err < 0) + return err; + + temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); + if (temp < 0) + return temp; + temp &= ~(MII_M1111_HWCFG_MODE_MASK | MII_M1111_HWCFG_FIBER_COPPER_RES); + temp |= 0x7 | MII_M1111_HWCFG_FIBER_COPPER_AUTO; + err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); + if (err < 0) + return err; + + /* soft reset */ + err = phy_write(phydev, MII_BMCR, BMCR_RESET); + if (err < 0) + return err; + do + temp = phy_read(phydev, MII_BMCR); + while (temp & BMCR_RESET); + + temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); + if (temp < 0) + return temp; + temp &= ~(MII_M1111_HWCFG_MODE_MASK | MII_M1111_HWCFG_FIBER_COPPER_RES); + temp |= MII_M1111_HWCFG_MODE_COPPER_RTBI | MII_M1111_HWCFG_FIBER_COPPER_AUTO; + err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); + if (err < 0) + return err; + } + + err = marvell_of_reg_init(phydev); + if (err < 0) + return err; + + return phy_write(phydev, MII_BMCR, BMCR_RESET); +} + static int m88e1121_config_init(struct phy_device *phydev) { u16 reg; @@ -502,6 +619,15 @@ static int m88e1510_config_init(struct phy_device *phydev) static struct phy_driver marvell_drivers[] = { { + .phy_id = MARVELL_PHY_ID_88E1111, + .phy_id_mask = MARVELL_PHY_ID_MASK, + .drv.name = "Marvell 88E1111", + .features = PHY_GBIT_FEATURES, + .config_init = &m88e1111_config_init, + .config_aneg = &genphy_config_aneg, + .read_status = &marvell_read_status, + }, + { .phy_id = MARVELL_PHY_ID_88E1121R, .phy_id_mask = MARVELL_PHY_ID_MASK, .drv.name = "Marvell 88E1121R", diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c index a839f2dee8..a846fb28e2 100644 --- a/drivers/net/phy/mdio-gpio.c +++ b/drivers/net/phy/mdio-gpio.c @@ -45,7 +45,7 @@ struct mdio_gpio_info { int mdc_active_low, mdio_active_low, mdo_active_low; }; -struct mdio_gpio_info *mdio_gpio_of_get_info(struct device_d *dev) +static struct mdio_gpio_info *mdio_gpio_of_get_info(struct device_d *dev) { int ret; enum of_gpio_flags flags; diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig new file mode 100644 index 0000000000..d801cc25af --- /dev/null +++ b/drivers/nvmem/Kconfig @@ -0,0 +1,18 @@ +menuconfig NVMEM + bool "NVMEM Support" + help + Support for NVMEM(Non Volatile Memory) devices like EEPROM, EFUSES... + + This framework is designed to provide a generic interface to NVMEM + + If unsure, say no. + +if NVMEM + +config NVMEM_SNVS_LPGPR + tristate "Freescale SNVS LPGPR support" + select MFD_SYSCON + help + If you say yes here you get NVMEM support for the Freescale SNVS + Low Power Generic Purpose Register (LPGPR). +endif diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile new file mode 100644 index 0000000000..32522e9fbf --- /dev/null +++ b/drivers/nvmem/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for nvmem drivers. +# + +obj-$(CONFIG_NVMEM) += nvmem_core.o +nvmem_core-y := core.o + +# Devices +obj-$(CONFIG_NVMEM_SNVS_LPGPR) += nvmem_snvs_lpgpr.o +nvmem_snvs_lpgpr-y := snvs_lpgpr.o diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c new file mode 100644 index 0000000000..4e50a8843f --- /dev/null +++ b/drivers/nvmem/core.c @@ -0,0 +1,764 @@ +/* + * nvmem framework core. + * + * Copyright (C) 2015 Srinivas Kandagatla <srinivas.kandagatla@linaro.org> + * Copyright (C) 2013 Maxime Ripard <maxime.ripard@free-electrons.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <common.h> +#include <libbb.h> +#include <malloc.h> +#include <of.h> +#include <linux/nvmem-consumer.h> +#include <linux/nvmem-provider.h> + +struct nvmem_device { + const char *name; + struct device_d dev; + const struct nvmem_bus *bus; + struct list_head node; + int stride; + int word_size; + int ncells; + int users; + size_t size; + bool read_only; + struct cdev cdev; +}; + +struct nvmem_cell { + const char *name; + int offset; + int bytes; + int bit_offset; + int nbits; + struct nvmem_device *nvmem; + struct list_head node; +}; + +static LIST_HEAD(nvmem_cells); +static LIST_HEAD(nvmem_devs); + +int nvmem_device_read(struct nvmem_device *nvmem, unsigned int offset, + size_t bytes, void *buf); +int nvmem_device_write(struct nvmem_device *nvmem, unsigned int offset, + size_t bytes, const void *buf); + + +static ssize_t nvmem_cdev_read(struct cdev *cdev, void *buf, size_t count, + loff_t offset, unsigned long flags) +{ + struct nvmem_device *nvmem = container_of(cdev, struct nvmem_device, cdev); + ssize_t retlen; + + dev_dbg(cdev->dev, "read ofs: 0x%08llx count: 0x%08zx\n", + offset, count); + + retlen = nvmem_device_read(nvmem, offset, count, buf); + + return retlen; +} + +static ssize_t nvmem_cdev_write(struct cdev *cdev, const void *buf, size_t count, + loff_t offset, unsigned long flags) +{ + struct nvmem_device *nvmem = container_of(cdev, struct nvmem_device, cdev); + ssize_t retlen; + + dev_dbg(cdev->dev, "write ofs: 0x%08llx count: 0x%08zx\n", + offset, count); + + retlen = nvmem_device_write(nvmem, offset, count, buf); + + return retlen; +} + +static struct file_operations nvmem_chrdev_ops = { + .read = nvmem_cdev_read, + .write = nvmem_cdev_write, + .lseek = dev_lseek_default, +}; + +static int nvmem_register_cdev(struct nvmem_device *nvmem) +{ + struct device_d *dev = &nvmem->dev; + const char *alias; + char *devname; + int err; + + alias = of_alias_get(dev->device_node); + if (alias) { + devname = xstrdup(alias); + } else { + err = cdev_find_free_index("nvmem"); + if (err < 0) { + dev_err(dev, "no index found to name device\n"); + return err; + } + devname = xasprintf("nvmem%d", err); + } + + nvmem->cdev.name = devname; + nvmem->cdev.flags = DEVFS_IS_CHARACTER_DEV; + nvmem->cdev.ops = &nvmem_chrdev_ops; + nvmem->cdev.dev = &nvmem->dev; + nvmem->cdev.size = nvmem->size; + + return devfs_create(&nvmem->cdev); +} + +static struct nvmem_device *of_nvmem_find(struct device_node *nvmem_np) +{ + struct nvmem_device *dev; + + if (!nvmem_np) + return NULL; + + list_for_each_entry(dev, &nvmem_devs, node) + if (dev->dev.device_node->name && !strcmp(dev->dev.device_node->name, nvmem_np->name)) + return dev; + + return NULL; +} + +static struct nvmem_cell *nvmem_find_cell(const char *cell_id) +{ + struct nvmem_cell *p; + + list_for_each_entry(p, &nvmem_cells, node) + if (p && !strcmp(p->name, cell_id)) + return p; + + return NULL; +} + +static void nvmem_cell_drop(struct nvmem_cell *cell) +{ + list_del(&cell->node); + kfree(cell); +} + +static void nvmem_cell_add(struct nvmem_cell *cell) +{ + list_add_tail(&cell->node, &nvmem_cells); +} + +static int nvmem_cell_info_to_nvmem_cell(struct nvmem_device *nvmem, + const struct nvmem_cell_info *info, + struct nvmem_cell *cell) +{ + cell->nvmem = nvmem; + cell->offset = info->offset; + cell->bytes = info->bytes; + cell->name = info->name; + + cell->bit_offset = info->bit_offset; + cell->nbits = info->nbits; + + if (cell->nbits) + cell->bytes = DIV_ROUND_UP(cell->nbits + cell->bit_offset, + BITS_PER_BYTE); + + if (!IS_ALIGNED(cell->offset, nvmem->stride)) { + dev_err(&nvmem->dev, + "cell %s unaligned to nvmem stride %d\n", + cell->name, nvmem->stride); + return -EINVAL; + } + + return 0; +} + +/** + * nvmem_register() - Register a nvmem device for given nvmem_config. + * + * @config: nvmem device configuration with which nvmem device is created. + * + * Return: Will be an ERR_PTR() on error or a valid pointer to nvmem_device + * on success. + */ + +struct nvmem_device *nvmem_register(const struct nvmem_config *config) +{ + struct nvmem_device *nvmem; + struct device_node *np; + int rval; + + if (!config->dev) + return ERR_PTR(-EINVAL); + + nvmem = kzalloc(sizeof(*nvmem), GFP_KERNEL); + if (!nvmem) + return ERR_PTR(-ENOMEM); + + nvmem->stride = config->stride; + nvmem->word_size = config->word_size; + nvmem->size = config->size; + nvmem->dev.parent = config->dev; + nvmem->bus = config->bus; + np = config->dev->device_node; + nvmem->dev.device_node = np; + + nvmem->read_only = of_property_read_bool(np, "read-only") | + config->read_only; + + safe_strncpy(nvmem->dev.name, config->name, MAX_DRIVER_NAME); + nvmem->dev.id = DEVICE_ID_DYNAMIC; + + dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name); + + rval = register_device(&nvmem->dev); + if (rval) { + kfree(nvmem); + return ERR_PTR(rval); + } + + rval = nvmem_register_cdev(nvmem); + if (rval) { + kfree(nvmem); + return ERR_PTR(rval); + } + + list_add_tail(&nvmem->node, &nvmem_devs); + + return nvmem; +} +EXPORT_SYMBOL_GPL(nvmem_register); + +static struct nvmem_device *__nvmem_device_get(struct device_node *np, + struct nvmem_cell **cellp, + const char *cell_id) +{ + struct nvmem_device *nvmem = NULL; + + if (np) { + nvmem = of_nvmem_find(np); + if (!nvmem) + return ERR_PTR(-EPROBE_DEFER); + } else { + struct nvmem_cell *cell = nvmem_find_cell(cell_id); + + if (cell) { + nvmem = cell->nvmem; + *cellp = cell; + } + + if (!nvmem) + return ERR_PTR(-ENOENT); + } + + nvmem->users++; + + return nvmem; +} + +static void __nvmem_device_put(struct nvmem_device *nvmem) +{ + nvmem->users--; +} + +static struct nvmem_device *nvmem_find(const char *name) +{ + return ERR_PTR(-ENOSYS); +} + +#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OFTREE) +/** + * of_nvmem_device_get() - Get nvmem device from a given id + * + * @dev node: Device tree node that uses the nvmem device + * @id: nvmem name from nvmem-names property. + * + * Return: ERR_PTR() on error or a valid pointer to a struct nvmem_device + * on success. + */ +struct nvmem_device *of_nvmem_device_get(struct device_node *np, const char *id) +{ + + struct device_node *nvmem_np; + int index; + + index = of_property_match_string(np, "nvmem-names", id); + + nvmem_np = of_parse_phandle(np, "nvmem", index); + if (!nvmem_np) + return ERR_PTR(-EINVAL); + + return __nvmem_device_get(nvmem_np, NULL, NULL); +} +EXPORT_SYMBOL_GPL(of_nvmem_device_get); +#endif + +/** + * nvmem_device_get() - Get nvmem device from a given id + * + * @dev : Device that uses the nvmem device + * @id: nvmem name from nvmem-names property. + * + * Return: ERR_PTR() on error or a valid pointer to a struct nvmem_device + * on success. + */ +struct nvmem_device *nvmem_device_get(struct device_d *dev, const char *dev_name) +{ + if (dev->device_node) { /* try dt first */ + struct nvmem_device *nvmem; + + nvmem = of_nvmem_device_get(dev->device_node, dev_name); + + if (!IS_ERR(nvmem) || PTR_ERR(nvmem) == -EPROBE_DEFER) + return nvmem; + + } + + return nvmem_find(dev_name); +} +EXPORT_SYMBOL_GPL(nvmem_device_get); + +/** + * nvmem_device_put() - put alredy got nvmem device + * + * @nvmem: pointer to nvmem device that needs to be released. + */ +void nvmem_device_put(struct nvmem_device *nvmem) +{ + __nvmem_device_put(nvmem); +} +EXPORT_SYMBOL_GPL(nvmem_device_put); + +static struct nvmem_cell *nvmem_cell_get_from_list(const char *cell_id) +{ + struct nvmem_cell *cell = NULL; + struct nvmem_device *nvmem; + + nvmem = __nvmem_device_get(NULL, &cell, cell_id); + if (IS_ERR(nvmem)) + return ERR_CAST(nvmem); + + return cell; +} + +#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OFTREE) +/** + * of_nvmem_cell_get() - Get a nvmem cell from given device node and cell id + * + * @dev node: Device tree node that uses the nvmem cell + * @id: nvmem cell name from nvmem-cell-names property. + * + * Return: Will be an ERR_PTR() on error or a valid pointer + * to a struct nvmem_cell. The nvmem_cell will be freed by the + * nvmem_cell_put(). + */ +struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, + const char *name) +{ + struct device_node *cell_np, *nvmem_np; + struct nvmem_cell *cell; + struct nvmem_device *nvmem; + const __be32 *addr; + int rval, len, index; + + index = of_property_match_string(np, "nvmem-cell-names", name); + + cell_np = of_parse_phandle(np, "nvmem-cells", index); + if (!cell_np) + return ERR_PTR(-EINVAL); + + nvmem_np = of_get_parent(cell_np); + if (!nvmem_np) + return ERR_PTR(-EINVAL); + + nvmem = __nvmem_device_get(nvmem_np, NULL, NULL); + if (IS_ERR(nvmem)) + return ERR_CAST(nvmem); + + addr = of_get_property(cell_np, "reg", &len); + if (!addr || (len < 2 * sizeof(u32))) { + dev_err(&nvmem->dev, "nvmem: invalid reg on %s\n", + cell_np->full_name); + rval = -EINVAL; + goto err_mem; + } + + cell = kzalloc(sizeof(*cell), GFP_KERNEL); + if (!cell) { + rval = -ENOMEM; + goto err_mem; + } + + cell->nvmem = nvmem; + cell->offset = be32_to_cpup(addr++); + cell->bytes = be32_to_cpup(addr); + cell->name = cell_np->name; + + addr = of_get_property(cell_np, "bits", &len); + if (addr && len == (2 * sizeof(u32))) { + cell->bit_offset = be32_to_cpup(addr++); + cell->nbits = be32_to_cpup(addr); + } + + if (cell->nbits) + cell->bytes = DIV_ROUND_UP(cell->nbits + cell->bit_offset, + BITS_PER_BYTE); + + if (cell->bytes < nvmem->word_size) + cell->bytes = nvmem->word_size; + + if (!IS_ALIGNED(cell->offset, nvmem->stride)) { + dev_err(&nvmem->dev, + "cell %s unaligned to nvmem stride %d\n", + cell->name, nvmem->stride); + rval = -EINVAL; + goto err_sanity; + } + + nvmem_cell_add(cell); + + return cell; + +err_sanity: + kfree(cell); + +err_mem: + __nvmem_device_put(nvmem); + + return ERR_PTR(rval); +} +EXPORT_SYMBOL_GPL(of_nvmem_cell_get); +#endif + +/** + * nvmem_cell_get() - Get nvmem cell of device form a given cell name + * + * @dev node: Device tree node that uses the nvmem cell + * @id: nvmem cell name to get. + * + * Return: Will be an ERR_PTR() on error or a valid pointer + * to a struct nvmem_cell. The nvmem_cell will be freed by the + * nvmem_cell_put(). + */ +struct nvmem_cell *nvmem_cell_get(struct device_d *dev, const char *cell_id) +{ + struct nvmem_cell *cell; + + if (dev->device_node) { /* try dt first */ + cell = of_nvmem_cell_get(dev->device_node, cell_id); + if (!IS_ERR(cell) || PTR_ERR(cell) == -EPROBE_DEFER) + return cell; + } + + return nvmem_cell_get_from_list(cell_id); +} +EXPORT_SYMBOL_GPL(nvmem_cell_get); + +/** + * nvmem_cell_put() - Release previously allocated nvmem cell. + * + * @cell: Previously allocated nvmem cell by nvmem_cell_get() + */ +void nvmem_cell_put(struct nvmem_cell *cell) +{ + struct nvmem_device *nvmem = cell->nvmem; + + __nvmem_device_put(nvmem); + nvmem_cell_drop(cell); +} +EXPORT_SYMBOL_GPL(nvmem_cell_put); + +static inline void nvmem_shift_read_buffer_in_place(struct nvmem_cell *cell, + void *buf) +{ + u8 *p, *b; + int i, bit_offset = cell->bit_offset; + + p = b = buf; + if (bit_offset) { + /* First shift */ + *b++ >>= bit_offset; + + /* setup rest of the bytes if any */ + for (i = 1; i < cell->bytes; i++) { + /* Get bits from next byte and shift them towards msb */ + *p |= *b << (BITS_PER_BYTE - bit_offset); + + p = b; + *b++ >>= bit_offset; + } + + /* result fits in less bytes */ + if (cell->bytes != DIV_ROUND_UP(cell->nbits, BITS_PER_BYTE)) + *p-- = 0; + } + /* clear msb bits if any leftover in the last byte */ + *p &= GENMASK((cell->nbits%BITS_PER_BYTE) - 1, 0); +} + +static int __nvmem_cell_read(struct nvmem_device *nvmem, + struct nvmem_cell *cell, + void *buf, size_t *len) +{ + int rc; + + rc = nvmem->bus->read(&nvmem->dev, cell->offset, buf, cell->bytes); + if (IS_ERR_VALUE(rc)) + return rc; + + /* shift bits in-place */ + if (cell->bit_offset || cell->nbits) + nvmem_shift_read_buffer_in_place(cell, buf); + + *len = cell->bytes; + + return 0; +} + +/** + * nvmem_cell_read() - Read a given nvmem cell + * + * @cell: nvmem cell to be read. + * @len: pointer to length of cell which will be populated on successful read. + * + * Return: ERR_PTR() on error or a valid pointer to a char * buffer on success. + * The buffer should be freed by the consumer with a kfree(). + */ +void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len) +{ + struct nvmem_device *nvmem = cell->nvmem; + u8 *buf; + int rc; + + if (!nvmem) + return ERR_PTR(-EINVAL); + + buf = kzalloc(cell->bytes, GFP_KERNEL); + if (!buf) + return ERR_PTR(-ENOMEM); + + rc = __nvmem_cell_read(nvmem, cell, buf, len); + if (IS_ERR_VALUE(rc)) { + kfree(buf); + return ERR_PTR(rc); + } + + return buf; +} +EXPORT_SYMBOL_GPL(nvmem_cell_read); + +static inline void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell, + u8 *_buf, int len) +{ + struct nvmem_device *nvmem = cell->nvmem; + int i, rc, nbits, bit_offset = cell->bit_offset; + u8 v, *p, *buf, *b, pbyte, pbits; + + nbits = cell->nbits; + buf = kzalloc(cell->bytes, GFP_KERNEL); + if (!buf) + return ERR_PTR(-ENOMEM); + + memcpy(buf, _buf, len); + p = b = buf; + + if (bit_offset) { + pbyte = *b; + *b <<= bit_offset; + + /* setup the first byte with lsb bits from nvmem */ + rc = nvmem->bus->read(&nvmem->dev, cell->offset, &v, 1); + *b++ |= GENMASK(bit_offset - 1, 0) & v; + + /* setup rest of the byte if any */ + for (i = 1; i < cell->bytes; i++) { + /* Get last byte bits and shift them towards lsb */ + pbits = pbyte >> (BITS_PER_BYTE - 1 - bit_offset); + pbyte = *b; + p = b; + *b <<= bit_offset; + *b++ |= pbits; + } + } + + /* if it's not end on byte boundary */ + if ((nbits + bit_offset) % BITS_PER_BYTE) { + /* setup the last byte with msb bits from nvmem */ + rc = nvmem->bus->read(&nvmem->dev, cell->offset + cell->bytes - 1, + &v, 1); + *p |= GENMASK(7, (nbits + bit_offset) % BITS_PER_BYTE) & v; + + } + + return buf; +} + +/** + * nvmem_cell_write() - Write to a given nvmem cell + * + * @cell: nvmem cell to be written. + * @buf: Buffer to be written. + * @len: length of buffer to be written to nvmem cell. + * + * Return: length of bytes written or negative on failure. + */ +int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len) +{ + struct nvmem_device *nvmem = cell->nvmem; + int rc; + + if (!nvmem || nvmem->read_only || + (cell->bit_offset == 0 && len != cell->bytes)) + return -EINVAL; + + if (cell->bit_offset || cell->nbits) { + buf = nvmem_cell_prepare_write_buffer(cell, buf, len); + if (IS_ERR(buf)) + return PTR_ERR(buf); + } + + rc = nvmem->bus->write(&nvmem->dev, cell->offset, buf, cell->bytes); + + /* free the tmp buffer */ + if (cell->bit_offset || cell->nbits) + kfree(buf); + + if (IS_ERR_VALUE(rc)) + return rc; + + return len; +} +EXPORT_SYMBOL_GPL(nvmem_cell_write); + +/** + * nvmem_device_cell_read() - Read a given nvmem device and cell + * + * @nvmem: nvmem device to read from. + * @info: nvmem cell info to be read. + * @buf: buffer pointer which will be populated on successful read. + * + * Return: length of successful bytes read on success and negative + * error code on error. + */ +ssize_t nvmem_device_cell_read(struct nvmem_device *nvmem, + struct nvmem_cell_info *info, void *buf) +{ + struct nvmem_cell cell; + int rc; + ssize_t len; + + if (!nvmem) + return -EINVAL; + + rc = nvmem_cell_info_to_nvmem_cell(nvmem, info, &cell); + if (IS_ERR_VALUE(rc)) + return rc; + + rc = __nvmem_cell_read(nvmem, &cell, buf, &len); + if (IS_ERR_VALUE(rc)) + return rc; + + return len; +} +EXPORT_SYMBOL_GPL(nvmem_device_cell_read); + +/** + * nvmem_device_cell_write() - Write cell to a given nvmem device + * + * @nvmem: nvmem device to be written to. + * @info: nvmem cell info to be written + * @buf: buffer to be written to cell. + * + * Return: length of bytes written or negative error code on failure. + * */ +int nvmem_device_cell_write(struct nvmem_device *nvmem, + struct nvmem_cell_info *info, void *buf) +{ + struct nvmem_cell cell; + int rc; + + if (!nvmem) + return -EINVAL; + + rc = nvmem_cell_info_to_nvmem_cell(nvmem, info, &cell); + if (IS_ERR_VALUE(rc)) + return rc; + + return nvmem_cell_write(&cell, buf, cell.bytes); +} +EXPORT_SYMBOL_GPL(nvmem_device_cell_write); + +/** + * nvmem_device_read() - Read from a given nvmem device + * + * @nvmem: nvmem device to read from. + * @offset: offset in nvmem device. + * @bytes: number of bytes to read. + * @buf: buffer pointer which will be populated on successful read. + * + * Return: length of successful bytes read on success and negative + * error code on error. + */ +int nvmem_device_read(struct nvmem_device *nvmem, + unsigned int offset, + size_t bytes, void *buf) +{ + int rc; + + if (!nvmem) + return -EINVAL; + + if (offset >= nvmem->size || bytes > nvmem->size - offset) + return -EINVAL; + + if (!bytes) + return 0; + + rc = nvmem->bus->read(&nvmem->dev, offset, buf, bytes); + + if (IS_ERR_VALUE(rc)) + return rc; + + return bytes; +} +EXPORT_SYMBOL_GPL(nvmem_device_read); + +/** + * nvmem_device_write() - Write cell to a given nvmem device + * + * @nvmem: nvmem device to be written to. + * @offset: offset in nvmem device. + * @bytes: number of bytes to write. + * @buf: buffer to be written. + * + * Return: length of bytes written or negative error code on failure. + * */ +int nvmem_device_write(struct nvmem_device *nvmem, + unsigned int offset, + size_t bytes, const void *buf) +{ + int rc; + + if (!nvmem || nvmem->read_only) + return -EINVAL; + + if (offset >= nvmem->size || bytes > nvmem->size - offset) + return -EINVAL; + + if (!bytes) + return 0; + + rc = nvmem->bus->write(&nvmem->dev, offset, buf, bytes); + + if (IS_ERR_VALUE(rc)) + return rc; + + + return bytes; +} +EXPORT_SYMBOL_GPL(nvmem_device_write); diff --git a/drivers/nvmem/snvs_lpgpr.c b/drivers/nvmem/snvs_lpgpr.c new file mode 100644 index 0000000000..74118a6b75 --- /dev/null +++ b/drivers/nvmem/snvs_lpgpr.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2015 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de> + * Copyright (c) 2017 Pengutronix, Oleksij Rempel <kernel@pengutronix.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + */ +#include <common.h> +#include <driver.h> +#include <init.h> +#include <io.h> +#include <of.h> +#include <malloc.h> +#include <regmap.h> +#include <mfd/syscon.h> +#include <linux/nvmem-provider.h> + +struct snvs_lpgpr_priv { + struct device_d *dev; + struct regmap *regmap; + int offset; + struct nvmem_config cfg; +}; + +static int snvs_lpgpr_write(struct device_d *dev, const int reg, + const void *_val, int bytes) +{ + struct snvs_lpgpr_priv *priv = dev->parent->priv; + const u32 *val = _val; + int i = 0, words = bytes / 4; + + while (words--) + regmap_write(priv->regmap, priv->offset + reg + (i++ * 4), + *val++); + + return 0; +} + +static int snvs_lpgpr_read(struct device_d *dev, const int reg, void *_val, + int bytes) +{ + struct snvs_lpgpr_priv *priv = dev->parent->priv; + u32 *val = _val; + int i = 0, words = bytes / 4; + + while (words--) + regmap_read(priv->regmap, priv->offset + reg + (i++ * 4), + val++); + + + return 0; +} + +static const struct nvmem_bus snvs_lpgpr_nvmem_bus = { + .write = snvs_lpgpr_write, + .read = snvs_lpgpr_read, +}; + +static int snvs_lpgpr_probe(struct device_d *dev) +{ + struct device_node *node = dev->device_node; + struct snvs_lpgpr_priv *priv; + struct nvmem_config *cfg; + struct nvmem_device *nvmem; + int err; + + if (!node) + return -ENOENT; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->regmap = syscon_node_to_regmap(of_get_parent(node)); + if (IS_ERR(priv->regmap)) { + free(priv); + return PTR_ERR(priv->regmap); + } + + err = of_property_read_u32(node, "offset", &priv->offset); + if (err) + return err; + + cfg = &priv->cfg; + cfg->name = dev_name(dev); + cfg->dev = dev; + cfg->stride = 4, + cfg->word_size = 4, + cfg->size = 4, + cfg->bus = &snvs_lpgpr_nvmem_bus, + + nvmem = nvmem_register(cfg); + if (IS_ERR(nvmem)) { + free(priv); + return PTR_ERR(nvmem); + } + + dev->priv = priv; + + return 0; +} + +static __maybe_unused struct of_device_id snvs_lpgpr_dt_ids[] = { + { .compatible = "fsl,imx6sl-snvs-lpgpr", }, + { .compatible = "fsl,imx6q-snvs-lpgpr", }, + { }, +}; + +static struct driver_d snvs_lpgpr_driver = { + .name = "nvmem-snvs-lpgpr", + .probe = snvs_lpgpr_probe, + .of_compatible = DRV_OF_COMPAT(snvs_lpgpr_dt_ids), +}; +device_platform_driver(snvs_lpgpr_driver); diff --git a/drivers/of/base.c b/drivers/of/base.c index bef8f1de1a..6632f4d9dd 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1217,6 +1217,28 @@ int of_property_write_u64_array(struct device_node *np, } /** + * of_property_write_string - Write a string to a property. If + * the property does not exist, it will be created and appended to the given + * device node. + * + * @np: device node to which the property value is to be written. + * @propname: name of the property to be written. + * value: pointer to the string to write + * + * Search for a property in a device node and write a string to + * it. If the property does not exist, it will be created and appended to + * the device node. Returns 0 on success, -ENOMEM if the property or array + * of elements cannot be created. + */ +int of_property_write_string(struct device_node *np, + const char *propname, const char *value) +{ + size_t len = strlen(value); + + return of_set_property(np, propname, value, len + 1, 1); +} + +/** * of_parse_phandle_from - Resolve a phandle property to a device_node pointer from * a given root node * @np: Pointer to device node holding phandle property @@ -2093,7 +2115,7 @@ int of_device_enable_path(const char *path) */ int of_device_disable(struct device_node *node) { - return of_set_property(node, "status", "disabled", sizeof("disabled"), 1); + return of_property_write_string(node, "status", "disabled"); } /** diff --git a/drivers/of/of_path.c b/drivers/of/of_path.c index e0b2dc1247..a5886dffac 100644 --- a/drivers/of/of_path.c +++ b/drivers/of/of_path.c @@ -104,6 +104,113 @@ int of_find_path_by_node(struct device_node *node, char **outpath, unsigned flag } /** + * of_find_node_by_devpath - translate a device path to a device tree node + * + * @root: The device tree root. Can be NULL, in this case the internal tree is used + * @path: The path to look the node up for. Can be "/dev/cdevname" or "cdevname" directly. + * + * This is the counterpart of of_find_path_by_node(). Given a path this function tries + * to find the corresponding node in the given device tree. + * + * We first have to find the hardware device in the tree we are passed and then find + * a partition matching offset/size in this tree. This is necessary because the + * passed tree may use another partition binding (legacy vs. fixed-partitions). Also + * the node names may differ (some device trees have partition@<num> instead of + * partition@<offset>. + */ +struct device_node *of_find_node_by_devpath(struct device_node *root, const char *path) +{ + struct cdev *cdev; + bool is_partition = false; + struct device_node *np, *partnode, *rnp; + loff_t part_offset = 0, part_size = 0; + + pr_debug("%s: looking for path %s\n", __func__, path); + + if (!strncmp(path, "/dev/", 5)) + path += 5; + + cdev = cdev_by_name(path); + if (!cdev) { + pr_debug("%s: cdev %s not found\n", __func__, path); + return NULL; + } + + /* + * Look for the device node of the master device (the one of_parse_partitions() has + * been called with + */ + if (cdev->master) { + is_partition = true; + part_offset = cdev->offset; + part_size = cdev->size; + pr_debug("%s path %s: is a partition with offset 0x%08llx, size 0x%08llx\n", + __func__, path, part_offset, part_size); + np = cdev->master->device_node; + } else { + np = cdev->device_node; + } + + /* + * Now find the device node of the master device in the device tree we have + * been passed. + */ + rnp = of_find_node_by_path_from(root, np->full_name); + if (!rnp) { + pr_debug("%s path %s: %s not found in passed tree\n", __func__, path, + np->full_name); + return NULL; + } + + if (!is_partition) { + pr_debug("%s path %s: returning full device node %s\n", __func__, path, + rnp->full_name); + return rnp; + } + + /* + * Look for a partition with matching offset/size in the device node of + * the tree we have been passed. + */ + partnode = of_get_child_by_name(rnp, "partitions"); + if (!partnode) { + pr_debug("%s path %s: using legacy partition binding\n", __func__, path); + partnode = rnp; + } + + for_each_child_of_node(partnode, np) { + const __be32 *reg; + int na, ns, len; + loff_t offset, size; + + reg = of_get_property(np, "reg", &len); + if (!reg) + return NULL; + + na = of_n_addr_cells(np); + ns = of_n_size_cells(np); + + if (len < (na + ns) * sizeof(__be32)) { + pr_err("reg property too small in %s\n", np->full_name); + continue; + } + + offset = of_read_number(reg, na); + size = of_read_number(reg + na, ns); + + if (part_offset == offset && part_size == size) { + pr_debug("%s path %s: found matching partition in %s\n", __func__, path, + np->full_name); + return np; + } + } + + pr_debug("%s path %s: no matching node found\n", __func__, path); + + return NULL; +} + +/** * of_find_path - translate a path description in the devicetree to a barebox * path * diff --git a/drivers/of/partition.c b/drivers/of/partition.c index 8c2aef2326..25b41cb012 100644 --- a/drivers/of/partition.c +++ b/drivers/of/partition.c @@ -23,6 +23,16 @@ #include <linux/mtd/mtd.h> #include <linux/err.h> #include <nand.h> +#include <init.h> +#include <globalvar.h> + +static unsigned int of_partition_binding; + +enum of_binding_name { + MTD_OF_BINDING_NEW, + MTD_OF_BINDING_LEGACY, + MTD_OF_BINDING_DONTTOUCH, +}; struct cdev *of_parse_partition(struct cdev *cdev, struct device_node *node) { @@ -30,10 +40,11 @@ struct cdev *of_parse_partition(struct cdev *cdev, struct device_node *node) char *filename; struct cdev *new; const __be32 *reg; - unsigned long offset, size; + u64 offset, size; const char *name; int len; unsigned long flags = 0; + int na, ns; if (!node) return NULL; @@ -42,8 +53,16 @@ struct cdev *of_parse_partition(struct cdev *cdev, struct device_node *node) if (!reg) return NULL; - offset = be32_to_cpu(reg[0]); - size = be32_to_cpu(reg[1]); + na = of_n_addr_cells(node); + ns = of_n_size_cells(node); + + if (len < (na + ns) * sizeof(__be32)) { + pr_err("reg property too small in %s\n", node->full_name); + return NULL; + } + + offset = of_read_number(reg, na); + size = of_read_number(reg + na, ns); partname = of_get_property(node, "label", &len); if (!partname) @@ -53,7 +72,7 @@ struct cdev *of_parse_partition(struct cdev *cdev, struct device_node *node) name = (char *)partname; - debug("add partition: %s.%s 0x%08lx 0x%08lx\n", cdev->name, partname, offset, size); + debug("add partition: %s.%s 0x%08llx 0x%08llx\n", cdev->name, partname, offset, size); if (of_get_property(node, "read-only", &len)) flags = DEVFS_PARTITION_READONLY; @@ -79,6 +98,8 @@ int of_parse_partitions(struct cdev *cdev, struct device_node *node) if (!node) return -EINVAL; + cdev->device_node = node; + subnode = of_get_child_by_name(node, "partitions"); if (subnode) { if (!of_device_is_compatible(subnode, "fixed-partitions")) @@ -92,3 +113,150 @@ int of_parse_partitions(struct cdev *cdev, struct device_node *node) return 0; } + +static void delete_subnodes(struct device_node *np) +{ + struct device_node *part, *tmp; + + for_each_child_of_node_safe(np, tmp, part) { + if (of_get_property(part, "compatible", NULL)) + continue; + + of_delete_node(part); + } +} + +static int of_partition_fixup(struct device_node *root, void *ctx) +{ + struct cdev *cdev = ctx, *partcdev; + struct device_node *np, *part, *partnode; + int ret; + int n_cells, n_parts = 0; + + if (of_partition_binding == MTD_OF_BINDING_DONTTOUCH) + return 0; + + if (!cdev->device_node) + return -EINVAL; + + list_for_each_entry(partcdev, &cdev->partitions, partition_entry) { + if (partcdev->flags & DEVFS_PARTITION_FROM_TABLE) + continue; + n_parts++; + } + + if (!n_parts) + return 0; + + if (cdev->size >= 0x100000000) + n_cells = 2; + else + n_cells = 1; + + np = of_find_node_by_path_from(root, cdev->device_node->full_name); + if (!np) { + dev_err(cdev->dev, "Cannot find nodepath %s, cannot fixup\n", + cdev->device_node->full_name); + return -EINVAL; + } + + partnode = of_get_child_by_name(np, "partitions"); + if (partnode) { + if (of_partition_binding == MTD_OF_BINDING_LEGACY) { + of_delete_node(partnode); + partnode = np; + } + delete_subnodes(partnode); + } else { + delete_subnodes(np); + + if (of_partition_binding == MTD_OF_BINDING_LEGACY) + partnode = np; + else + partnode = of_new_node(np, "partitions"); + } + + if (of_partition_binding == MTD_OF_BINDING_NEW) { + ret = of_property_write_string(partnode, "compatible", + "fixed-partitions"); + if (ret) + return ret; + } + + of_property_write_u32(partnode, "#size-cells", n_cells); + if (ret) + return ret; + + of_property_write_u32(partnode, "#addres-cells", n_cells); + if (ret) + return ret; + + list_for_each_entry(partcdev, &cdev->partitions, partition_entry) { + int na, ns, len = 0; + char *name; + void *p; + u8 tmp[16 * 16]; /* Up to 64-bit address + 64-bit size */ + loff_t partoffset; + + if (partcdev->flags & DEVFS_PARTITION_FROM_TABLE) + continue; + + if (partcdev->mtd) + partoffset = partcdev->mtd->master_offset; + else + partoffset = partcdev->offset; + + name = basprintf("partition@%0llx", partoffset); + if (!name) + return -ENOMEM; + + part = of_new_node(partnode, name); + free(name); + if (!part) + return -ENOMEM; + + p = of_new_property(part, "label", partcdev->partname, + strlen(partcdev->partname) + 1); + if (!p) + return -ENOMEM; + + na = of_n_addr_cells(part); + ns = of_n_size_cells(part); + + of_write_number(tmp + len, partoffset, na); + len += na * 4; + of_write_number(tmp + len, partcdev->size, ns); + len += ns * 4; + + ret = of_set_property(part, "reg", tmp, len, 1); + if (ret) + return ret; + + if (partcdev->flags & DEVFS_PARTITION_READONLY) { + ret = of_set_property(part, "read-only", NULL, 0, 1); + if (ret) + return ret; + } + } + + return 0; +} + +int of_partitions_register_fixup(struct cdev *cdev) +{ + return of_register_fixup(of_partition_fixup, cdev); +} + +static const char *of_binding_names[] = { + "new", "legacy", "donttouch" +}; + +static int of_partition_init(void) +{ + dev_add_param_enum(&global_device, "of_partition_binding", NULL, NULL, + &of_partition_binding, of_binding_names, + ARRAY_SIZE(of_binding_names), NULL); + + return 0; +} +device_initcall(of_partition_init); diff --git a/drivers/serial/atmel.c b/drivers/serial/atmel.c index ab94fd2177..2f8adc989f 100644 --- a/drivers/serial/atmel.c +++ b/drivers/serial/atmel.c @@ -446,8 +446,15 @@ static int atmel_serial_probe(struct device_d *dev) return 0; } +static const struct of_device_id __maybe_unused atmel_serial_dt_ids[] = { + { .compatible = "atmel,at91rm9200-usart" }, + { .compatible = "atmel,at91sam9260-usart" }, + { /* sentinel */ } +}; + static struct driver_d atmel_serial_driver = { .name = "atmel_usart", .probe = atmel_serial_probe, + .of_compatible = DRV_OF_COMPAT(atmel_serial_dt_ids), }; console_platform_driver(atmel_serial_driver); diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index ef57867759..446e1d9932 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -29,6 +29,7 @@ #include <clock.h> #include <xfuncs.h> #include <gpio.h> +#include <of_gpio.h> #include <io.h> #include <spi/spi.h> #include <mach/io.h> @@ -400,10 +401,11 @@ static int atmel_spi_probe(struct device_d *dev) int ret = 0; int i; struct spi_master *master; + struct device_node *node = dev->device_node; struct atmel_spi *as; struct at91_spi_platform_data *pdata = dev->platform_data; - if (!pdata) { + if (!IS_ENABLED(CONFIG_OFDEVICE) && !pdata) { dev_err(dev, "missing platform data\n"); return -EINVAL; } @@ -414,6 +416,23 @@ static int atmel_spi_probe(struct device_d *dev) master->dev = dev; master->bus_num = dev->id; + if (pdata) { + master->num_chipselect = pdata->num_chipselect; + as->cs_pins = pdata->chipselect; + } else { + master->num_chipselect = of_gpio_named_count(node, "cs-gpios"); + as->cs_pins = xzalloc(sizeof(u32) * master->num_chipselect); + + for (i = 0; i < master->num_chipselect; i++) { + as->cs_pins[i] = of_get_named_gpio(node, "cs-gpios", i); + + if (!gpio_is_valid(as->cs_pins[i])) + break; + } + + master->num_chipselect = i; + } + as->clk = clk_get(dev, "spi_clk"); if (IS_ERR(as->clk)) { dev_err(dev, "no spi_clk\n"); @@ -423,8 +442,6 @@ static int atmel_spi_probe(struct device_d *dev) master->setup = atmel_spi_setup; master->transfer = atmel_spi_transfer; - master->num_chipselect = pdata->num_chipselect; - as->cs_pins = pdata->chipselect; iores = dev_request_mem_resource(dev, 0); if (IS_ERR(iores)) return PTR_ERR(iores); @@ -465,8 +482,14 @@ out_free: return ret; } +const static __maybe_unused const struct of_device_id atmel_spi_dt_ids[] = { + { .compatible = "atmel,at91rm9200-spi" }, + { /* sentinel */ } +}; + static struct driver_d atmel_spi_driver = { .name = "atmel_spi", .probe = atmel_spi_probe, + .of_compatible = DRV_OF_COMPAT(atmel_spi_dt_ids), }; device_platform_driver(atmel_spi_driver); diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index eb279ae8df..4292371f09 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -30,6 +30,17 @@ config USB_GADGET_DRIVER_PXA27X default y select USB_GADGET_DUALSPEED +config USB_GADGET_AUTOSTART + bool + default y + select ENVIRONMENT_VARIABLES + select FILE_LIST + prompt "Automatically start usbgadget on boot" + help + Enabling this option allows to automatically start a fastboot + gadget during boot. This behaviour is controlled with the + global.usbgadget.fastboot_function variable. + comment "USB Gadget drivers" config USB_GADGET_DFU diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 9ef594575b..e74cf02664 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_USB_GADGET) += composite.o config.o usbstring.o epautoconf.o udc-core.o functions.o config.o multi.o +obj-$(CONFIG_USB_GADGET_AUTOSTART) += autostart.o obj-$(CONFIG_USB_GADGET_SERIAL) += u_serial.o serial.o f_serial.o f_acm.o obj-$(CONFIG_USB_GADGET_DFU) += dfu.o obj-$(CONFIG_USB_GADGET_FASTBOOT) += f_fastboot.o diff --git a/drivers/usb/gadget/autostart.c b/drivers/usb/gadget/autostart.c new file mode 100644 index 0000000000..4ad1dd6be1 --- /dev/null +++ b/drivers/usb/gadget/autostart.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2017 Oleksij Rempel <o.rempel@pengutronix.de>, Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include <common.h> +#include <command.h> +#include <errno.h> +#include <environment.h> +#include <malloc.h> +#include <getopt.h> +#include <fs.h> +#include <xfuncs.h> +#include <usb/usbserial.h> +#include <usb/dfu.h> +#include <usb/gadget-multi.h> +#include <globalvar.h> +#include <magicvar.h> + +static int autostart; +static int acm; +static char *fastboot_function; + +static int usbgadget_autostart(void) +{ + struct f_multi_opts opts = {}; + + if (!autostart) + return 0; + + setenv("otg.mode", "peripheral"); + + if (fastboot_function) + opts.fastboot_opts.files = file_list_parse(fastboot_function); + + opts.create_acm = acm; + + return usb_multi_register(&opts); +} +postenvironment_initcall(usbgadget_autostart); + +static int usbgadget_globalvars_init(void) +{ + + globalvar_add_simple_bool("usbgadget.autostart", &autostart); + globalvar_add_simple_bool("usbgadget.acm", &acm); + globalvar_add_simple_string("usbgadget.fastboot_function", + &fastboot_function); + + return 0; +} +device_initcall(usbgadget_globalvars_init); + +BAREBOX_MAGICVAR_NAMED(global_usbgadget_autostart, + global.usbgadget.autostart, + "usbgadget: Automatically start usbgadget on boot"); +BAREBOX_MAGICVAR_NAMED(global_usbgadget_acm, + global.usbgadget.acm, + "usbgadget: Create CDC ACM function"); +BAREBOX_MAGICVAR_NAMED(global_usbgadget_fastboot_function, + global.usbgadget.fastboot_function, + "usbgadget: Create Android Fastboot function"); diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 54eaf468b7..db44052525 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -10,20 +10,17 @@ config USB_EHCI_OMAP config USB_EHCI_ATMEL depends on ARCH_AT91 depends on USB_EHCI + select USB_OHCI_AT91 bool "Atmel EHCI driver" config USB_OHCI bool "OHCI driver" depends on !MMU -if USB_OHCI - config USB_OHCI_AT91 depends on ARCH_AT91 bool "AT91 OHCI driver" -endif - config USB_XHCI bool "xHCI driver" depends on HAS_DMA diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c index f075b5080c..1132879c9b 100644 --- a/drivers/usb/host/ehci-atmel.c +++ b/drivers/usb/host/ehci-atmel.c @@ -65,6 +65,9 @@ static int atmel_ehci_probe(struct device_d *dev) struct resource *iores; struct ehci_data data; struct atmel_ehci_priv *atehci; + const char *uclk_name; + + uclk_name = (dev->device_node) ? "usb_clk" : "uhpck"; atehci = xzalloc(sizeof(*atehci)); atehci->dev = dev; @@ -76,7 +79,7 @@ static int atmel_ehci_probe(struct device_d *dev) return -ENOENT; } - atehci->uclk = clk_get(dev, "uhpck"); + atehci->uclk = clk_get(dev, uclk_name); if (IS_ERR(atehci->iclk)) { dev_err(dev, "Error getting function clock\n"); return -ENOENT; @@ -107,9 +110,15 @@ static void atmel_ehci_remove(struct device_d *dev) atmel_stop_clock(dev->priv); } +static const struct of_device_id atmel_ehci_dt_ids[] = { + { .compatible = "atmel,at91sam9g45-ehci" }, + { /* sentinel */ } +}; + static struct driver_d atmel_ehci_driver = { .name = "atmel-ehci", .probe = atmel_ehci_probe, .remove = atmel_ehci_remove, + .of_compatible = DRV_OF_COMPAT(atmel_ehci_dt_ids), }; device_platform_driver(atmel_ehci_driver); diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 5f745264b7..1013ba39c5 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -23,10 +23,18 @@ #include <usb/usb.h> #include <usb/usb_defs.h> #include <errno.h> +#include <gpio.h> +#include <of_gpio.h> #include <io.h> +#include <mach/board.h> + #include "ohci.h" +#define at91_for_each_port(index) \ + for ((index) = 0; (index) < AT91_MAX_USBH_PORTS; (index)++) + + struct ohci_at91_priv { struct device_d *dev; struct clk *iclk; @@ -59,6 +67,59 @@ static void at91_stop_clock(struct ohci_at91_priv *ohci_at91) clk_disable(ohci_at91->iclk); } +static int at91_ohci_probe_dt(struct device_d *dev) +{ + u32 ports; + int i, ret, gpio; + enum of_gpio_flags flags; + struct at91_usbh_data *pdata; + struct device_node *np = dev->device_node; + + pdata = xzalloc(sizeof(*pdata)); + dev->platform_data = pdata; + + if (!of_property_read_u32(np, "num-ports", &ports)) { + pdata->ports = ports; + } else { + dev_err(dev, "Failed to read 'num-ports' property\n"); + return -EINVAL; + } + + at91_for_each_port(i) { + /* + * do not configure PIO if not in relation with + * real USB port on board + */ + if (i >= pdata->ports) { + pdata->vbus_pin[i] = -EINVAL; + continue; + } + + gpio = of_get_named_gpio_flags(np, "atmel,vbus-gpio", i, + &flags); + pdata->vbus_pin[i] = gpio; + if (!gpio_is_valid(gpio)) + continue; + pdata->vbus_pin_active_low[i] = flags & OF_GPIO_ACTIVE_LOW; + + ret = gpio_request(gpio, "ohci_vbus"); + if (ret) { + dev_err(dev, "can't request vbus gpio %d\n", gpio); + continue; + } + ret = gpio_direction_output(gpio, + !pdata->vbus_pin_active_low[i]); + if (ret) { + dev_err(dev, "can't put vbus gpio %d as output %d\n", + gpio, !pdata->vbus_pin_active_low[i]); + gpio_free(gpio); + continue; + } + } + + return 0; +} + static int at91_ohci_probe(struct device_d *dev) { int ret; @@ -68,6 +129,12 @@ static int at91_ohci_probe(struct device_d *dev) dev->priv = ohci_at91; ohci_at91->dev = dev; + if (dev->device_node) { + ret = at91_ohci_probe_dt(dev); + if (ret < 0) + return ret; + } + io = dev_get_resource(dev, IORESOURCE_MEM, 0); if (IS_ERR(io)) { dev_err(dev, "Failed to get IORESOURCE_MEM\n"); @@ -75,13 +142,13 @@ static int at91_ohci_probe(struct device_d *dev) } ohci_at91->regs = IOMEM(io->start); - ohci_at91->iclk = clk_get(NULL, "ohci_clk"); + ohci_at91->iclk = clk_get(dev, "ohci_clk"); if (IS_ERR(ohci_at91->iclk)) { dev_err(dev, "Failed to get 'ohci_clk'\n"); return PTR_ERR(ohci_at91->iclk); } - ohci_at91->fclk = clk_get(NULL, "uhpck"); + ohci_at91->fclk = clk_get(dev, "uhpck"); if (IS_ERR(ohci_at91->fclk)) { dev_err(dev, "Failed to get 'uhpck'\n"); return PTR_ERR(ohci_at91->fclk); @@ -107,6 +174,7 @@ static int at91_ohci_probe(struct device_d *dev) static void at91_ohci_remove(struct device_d *dev) { + struct at91_usbh_data *pdata = dev->platform_data; struct ohci_at91_priv *ohci_at91 = dev->priv; /* @@ -118,11 +186,32 @@ static void at91_ohci_remove(struct device_d *dev) * Stop the USB clocks. */ at91_stop_clock(ohci_at91); + + if (pdata) { + bool active_low; + int i, gpio; + + at91_for_each_port(i) { + gpio = pdata->vbus_pin[i]; + active_low = pdata->vbus_pin_active_low[i]; + + if (gpio_is_valid(gpio)) { + gpio_set_value(gpio, active_low); + gpio_free(gpio); + } + } + } } +static const struct of_device_id at91_ohci_dt_ids[] = { + { .compatible = "atmel,at91rm9200-ohci" }, + { /* sentinel */ } +}; + static struct driver_d at91_ohci_driver = { .name = "at91_ohci", .probe = at91_ohci_probe, .remove = at91_ohci_remove, + .of_compatible = DRV_OF_COMPAT(at91_ohci_dt_ids), }; device_platform_driver(at91_ohci_driver); diff --git a/drivers/video/edid.c b/drivers/video/edid.c index 258526433e..bee4594118 100644 --- a/drivers/video/edid.c +++ b/drivers/video/edid.c @@ -387,7 +387,7 @@ static void fb_timings_vfreq(struct __fb_timings *timings) * REQUIRES: * A valid info->monspecs, otherwise 'safe numbers' will be used. */ -int fb_get_mode(int flags, u32 val, struct fb_videomode *var) +static int fb_get_mode(int flags, u32 val, struct fb_videomode *var) { struct __fb_timings *timings; u32 interlace = 1, dscan = 1; diff --git a/drivers/video/imx-ipu-v3/ipufb.c b/drivers/video/imx-ipu-v3/ipufb.c index 343f9e5578..9597eda0d0 100644 --- a/drivers/video/imx-ipu-v3/ipufb.c +++ b/drivers/video/imx-ipu-v3/ipufb.c @@ -333,10 +333,8 @@ static int ipufb_probe(struct device_d *dev) } ret = vpl_ioctl(&fbi->vpl, 2 + fbi->dino, VPL_GET_VIDEOMODES, &info->modes); - if (ret) { - dev_err(fbi->dev, "failed to get modes: %s\n", strerror(-ret)); - return ret; - } + if (ret) + dev_dbg(fbi->dev, "failed to get modes: %s\n", strerror(-ret)); ret = register_framebuffer(info); if (ret < 0) { diff --git a/drivers/video/simplefb.c b/drivers/video/simplefb.c index 357262988e..a2c59de364 100644 --- a/drivers/video/simplefb.c +++ b/drivers/video/simplefb.c @@ -89,9 +89,6 @@ static const struct simplefb_mode *simplefb_find_mode(const struct fb_info *fbi) static int simplefb_create_node(struct device_node *root, const struct fb_info *fbi, const char *format) { - const char *compat = "simple-framebuffer"; - const char *disabled = "disabled"; - const char *okay = "okay"; struct device_node *node; u32 cells[2]; int ret; @@ -100,12 +97,11 @@ static int simplefb_create_node(struct device_node *root, if (!node) return -ENOMEM; - ret = of_set_property(node, "status", disabled, - strlen(disabled) + 1, 1); + ret = of_property_write_string(node, "status", "disabled"); if (ret < 0) return ret; - ret = of_set_property(node, "compatible", compat, strlen(compat) + 1, 1); + ret = of_property_write_string(node, "compatible", "simple-framebuffer"); if (ret) return ret; @@ -130,14 +126,14 @@ static int simplefb_create_node(struct device_node *root, if (ret < 0) return ret; - ret = of_set_property(node, "format", format, strlen(format) + 1, 1); + ret = of_property_write_string(node, "format", format); if (ret < 0) return ret; of_add_reserve_entry((u32)fbi->screen_base, (u32)fbi->screen_base + fbi->screen_size); - return of_set_property(node, "status", okay, strlen(okay) + 1, 1); + return of_property_write_string(node, "status", "okay"); } static int simplefb_of_fixup(struct device_node *root, void *ctx) diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c index 946e9d3340..916027ea87 100644 --- a/drivers/w1/masters/w1-gpio.c +++ b/drivers/w1/masters/w1-gpio.c @@ -16,6 +16,7 @@ #include <driver.h> #include <linux/w1-gpio.h> #include <gpio.h> +#include <of_gpio.h> #include "../w1.h" @@ -43,12 +44,58 @@ static u8 w1_gpio_read_bit(struct w1_bus *bus) return gpio_get_value(pdata->pin) ? 1 : 0; } +static int w1_gpio_probe_dt(struct device_d *dev) +{ + struct w1_gpio_platform_data *pdata; + struct device_node *np = dev->device_node; + int gpio; + + if (dev->platform_data) + return 0; + + pdata = xzalloc(sizeof(*pdata)); + + if (of_get_property(np, "linux,open-drain", NULL)) + pdata->is_open_drain = 1; + + gpio = of_get_gpio(np, 0); + if (!gpio_is_valid(gpio)) { + if (gpio != -EPROBE_DEFER) + dev_err(dev, + "Failed to parse gpio property for data pin (%d)\n", + gpio); + + goto free_pdata; + } + pdata->pin = gpio; + + gpio = of_get_gpio(np, 1); + if (gpio == -EPROBE_DEFER) + goto free_pdata; + + /* ignore other errors as the pullup gpio is optional */ + pdata->ext_pullup_enable_pin = gpio; + + dev->platform_data = pdata; + return 0; + +free_pdata: + free(pdata); + return gpio; +} + static int __init w1_gpio_probe(struct device_d *dev) { struct w1_bus *master; struct w1_gpio_platform_data *pdata; int err; + if (IS_ENABLED(CONFIG_OFDEVICE)) { + err = w1_gpio_probe_dt(dev); + if (err < 0) + return err; + } + pdata = dev->platform_data; if (!pdata) @@ -104,8 +151,14 @@ static int __init w1_gpio_probe(struct device_d *dev) return err; } +static __maybe_unused const struct of_device_id w1_gpio_dt_ids[] = { + { .compatible = "w1-gpio" }, + {} +}; + static struct driver_d w1_gpio_driver = { .name = "w1-gpio", .probe = w1_gpio_probe, + .of_compatible = DRV_OF_COMPAT(w1_gpio_dt_ids), }; device_platform_driver(w1_gpio_driver); diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 63fb1a8c57..83b6528a5f 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -46,4 +46,10 @@ config WATCHDOG_OMAP help Add support for watchdog on the TI OMAP SoC. +config WATCHDOG_ORION + bool "Watchdog for Armada XP" + depends on ARCH_ARMADA_XP + help + Add support for watchdog on the Marvall Armada XP + endif diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 5fca4c368c..a3b26675ce 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_WATCHDOG_MXS28) += im28wd.o obj-$(CONFIG_WATCHDOG_DW) += dw_wdt.o obj-$(CONFIG_WATCHDOG_JZ4740) += jz4740.o obj-$(CONFIG_WATCHDOG_IMX_RESET_SOURCE) += imxwd.o +obj-$(CONFIG_WATCHDOG_ORION) += orion_wdt.o diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c new file mode 100644 index 0000000000..2802033f71 --- /dev/null +++ b/drivers/watchdog/orion_wdt.c @@ -0,0 +1,123 @@ +/* + * Watchdog driver for Marvell Armada XP. + * + * Copyright (C) 2017 Pengutronix, Uwe Kleine-König <kernel@pengutronix.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <common.h> +#include <errno.h> +#include <init.h> +#include <io.h> +#include <malloc.h> +#include <of.h> +#include <watchdog.h> + +#define CLKRATE 25000000 + +/* registers relative to timer_base (i.e. first reg property) */ +#define TIMER_CTRL 0x00 +#define TIMER_CTRL_WD_TIMER_25MHZ_EN BIT(10) +#define TIMER_CTRL_WD_TIMER_EN BIT(8) + +#define TIMER_STATUS 0x04 +#define TIMER_STATUS_WD_EXPIRED BIT(31) + +#define TIMER_WD_TIMER 0x34 + +/* registers relative to rstout_base (i.e. second reg property) */ +#define WD_RSTOUTn_MASK 0x00 +#define WD_RSTOUTn_MASK_GLOBAL_WD BIT(8) + +struct orion_wdt_ddata { + struct watchdog wd; + void __iomem *timer_base; + void __iomem *rstout_base; +}; + +static int armada_xp_set_timeout(struct watchdog *wd, unsigned timeout) +{ + struct orion_wdt_ddata *ddata = + container_of(wd, struct orion_wdt_ddata, wd); + u32 ctrl; + + if (0xffffffff / CLKRATE < timeout) + return -EINVAL; + + ctrl = readl(ddata->timer_base + TIMER_CTRL); + + if (timeout == 0) { + /* disable timer */ + ctrl &= ~TIMER_CTRL_WD_TIMER_EN; + writel(ctrl, ddata->timer_base + TIMER_CTRL); + + return 0; + } + + /* setup duration */ + writel(CLKRATE * timeout, ddata->timer_base + TIMER_WD_TIMER); + + /* clear expiration status */ + writel(readl(ddata->timer_base + TIMER_STATUS) & ~TIMER_STATUS_WD_EXPIRED, + ddata->timer_base + TIMER_STATUS); + + /* assert reset on expiration */ + writel(WD_RSTOUTn_MASK_GLOBAL_WD, ddata->rstout_base + WD_RSTOUTn_MASK); + + /* enable */ + ctrl |= TIMER_CTRL_WD_TIMER_25MHZ_EN | TIMER_CTRL_WD_TIMER_EN; + writel(ctrl, ddata->timer_base + TIMER_CTRL); + + return 0; +} + +static int orion_wdt_probe(struct device_d *dev) +{ + struct orion_wdt_ddata* ddata; + struct resource *res_timer, *res_rstout; + + ddata = xzalloc(sizeof(*ddata)); + + ddata->wd.set_timeout = armada_xp_set_timeout; + ddata->wd.name = "orion_wdt"; + ddata->wd.dev = dev; + + res_timer = dev_request_mem_resource(dev, 0); + if (IS_ERR(res_timer)) { + dev_err(dev, "could not get timer memory region\n"); + return PTR_ERR(res_timer); + } + ddata->timer_base = IOMEM(res_timer->start); + + res_rstout = dev_request_mem_resource(dev, 1); + if (IS_ERR(res_rstout)) { + dev_err(dev, "could not get rstout memory region\n"); + release_region(res_timer); + + return PTR_ERR(res_rstout); + } + ddata->rstout_base = IOMEM(res_rstout->start); + + return watchdog_register(&ddata->wd); +} + +static const struct of_device_id orion_wdt_of_match[] = { + { + .compatible = "marvell,armada-xp-wdt", + }, { /* sentinel */ } +}; + +static struct driver_d orion_wdt_driver = { + .probe = orion_wdt_probe, + .name = "orion_wdt", + .of_compatible = DRV_OF_COMPAT(orion_wdt_of_match), +}; +device_platform_driver(orion_wdt_driver); diff --git a/dts/Bindings/arm/marvell/cp110-system-controller0.txt b/dts/Bindings/arm/marvell/cp110-system-controller0.txt index 30c546900b..07dbb35818 100644 --- a/dts/Bindings/arm/marvell/cp110-system-controller0.txt +++ b/dts/Bindings/arm/marvell/cp110-system-controller0.txt @@ -45,7 +45,7 @@ The following clocks are available: - 1 15 SATA - 1 16 SATA USB - 1 17 Main - - 1 18 SD/MMC + - 1 18 SD/MMC/GOP - 1 21 Slow IO (SPI, NOR, BootROM, I2C, UART) - 1 22 USB3H0 - 1 23 USB3H1 @@ -65,7 +65,7 @@ Required properties: "cpm-audio", "cpm-communit", "cpm-nand", "cpm-ppv2", "cpm-sdio", "cpm-mg-domain", "cpm-mg-core", "cpm-xor1", "cpm-xor0", "cpm-gop-dp", "none", "cpm-pcie_x10", "cpm-pcie_x11", "cpm-pcie_x4", "cpm-pcie-xor", "cpm-sata", - "cpm-sata-usb", "cpm-main", "cpm-sd-mmc", "none", "none", "cpm-slow-io", + "cpm-sata-usb", "cpm-main", "cpm-sd-mmc-gop", "none", "none", "cpm-slow-io", "cpm-usb3h0", "cpm-usb3h1", "cpm-usb3dev", "cpm-eip150", "cpm-eip197"; Example: @@ -78,6 +78,6 @@ Example: gate-clock-output-names = "cpm-audio", "cpm-communit", "cpm-nand", "cpm-ppv2", "cpm-sdio", "cpm-mg-domain", "cpm-mg-core", "cpm-xor1", "cpm-xor0", "cpm-gop-dp", "none", "cpm-pcie_x10", "cpm-pcie_x11", "cpm-pcie_x4", "cpm-pcie-xor", "cpm-sata", - "cpm-sata-usb", "cpm-main", "cpm-sd-mmc", "none", "none", "cpm-slow-io", + "cpm-sata-usb", "cpm-main", "cpm-sd-mmc-gop", "none", "none", "cpm-slow-io", "cpm-usb3h0", "cpm-usb3h1", "cpm-usb3dev", "cpm-eip150", "cpm-eip197"; }; diff --git a/dts/Bindings/display/exynos/exynos_dsim.txt b/dts/Bindings/display/exynos/exynos_dsim.txt index a782659936..ca5204b3bc 100644 --- a/dts/Bindings/display/exynos/exynos_dsim.txt +++ b/dts/Bindings/display/exynos/exynos_dsim.txt @@ -4,7 +4,6 @@ Required properties: - compatible: value should be one of the following "samsung,exynos3250-mipi-dsi" /* for Exynos3250/3472 SoCs */ "samsung,exynos4210-mipi-dsi" /* for Exynos4 SoCs */ - "samsung,exynos4415-mipi-dsi" /* for Exynos4415 SoC */ "samsung,exynos5410-mipi-dsi" /* for Exynos5410/5420/5440 SoCs */ "samsung,exynos5422-mipi-dsi" /* for Exynos5422/5800 SoCs */ "samsung,exynos5433-mipi-dsi" /* for Exynos5433 SoCs */ diff --git a/dts/Bindings/display/exynos/samsung-fimd.txt b/dts/Bindings/display/exynos/samsung-fimd.txt index 18645e0228..5837402c3a 100644 --- a/dts/Bindings/display/exynos/samsung-fimd.txt +++ b/dts/Bindings/display/exynos/samsung-fimd.txt @@ -11,7 +11,6 @@ Required properties: "samsung,s5pv210-fimd"; /* for S5PV210 SoC */ "samsung,exynos3250-fimd"; /* for Exynos3250/3472 SoCs */ "samsung,exynos4210-fimd"; /* for Exynos4 SoCs */ - "samsung,exynos4415-fimd"; /* for Exynos4415 SoC */ "samsung,exynos5250-fimd"; /* for Exynos5250 SoCs */ "samsung,exynos5420-fimd"; /* for Exynos5420/5422/5800 SoCs */ diff --git a/dts/Bindings/mmc/rockchip-dw-mshc.txt b/dts/Bindings/mmc/rockchip-dw-mshc.txt index ea9c1c9607..520d61dad6 100644 --- a/dts/Bindings/mmc/rockchip-dw-mshc.txt +++ b/dts/Bindings/mmc/rockchip-dw-mshc.txt @@ -13,7 +13,7 @@ Required Properties: - "rockchip,rk2928-dw-mshc": for Rockchip RK2928 and following, before RK3288 - "rockchip,rk3288-dw-mshc": for Rockchip RK3288 - - "rockchip,rk1108-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK1108 + - "rockchip,rv1108-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RV1108 - "rockchip,rk3036-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK3036 - "rockchip,rk3368-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK3368 - "rockchip,rk3399-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK3399 diff --git a/dts/Bindings/phy/brcm,nsp-usb3-phy.txt b/dts/Bindings/phy/brcm,nsp-usb3-phy.txt deleted file mode 100644 index e68ae5dec9..0000000000 --- a/dts/Bindings/phy/brcm,nsp-usb3-phy.txt +++ /dev/null @@ -1,39 +0,0 @@ -Broadcom USB3 phy binding for northstar plus SoC -The USB3 phy is internal to the SoC and is accessed using mdio interface. - -Required mdio bus properties: -- reg: Should be 0x0 for SoC internal USB3 phy -- #address-cells: must be 1 -- #size-cells: must be 0 - -Required USB3 PHY properties: -- compatible: should be "brcm,nsp-usb3-phy" -- reg: USB3 Phy address on SoC internal MDIO bus and it should be 0x10. -- usb3-ctrl-syscon: handler of syscon node defining physical address - of usb3 control register. -- #phy-cells: must be 0 - -Required usb3 control properties: -- compatible: should be "brcm,nsp-usb3-ctrl" -- reg: offset and length of the control registers - -Example: - - mdio@0 { - reg = <0x0>; - #address-cells = <1>; - #size-cells = <0>; - - usb3_phy: usb-phy@10 { - compatible = "brcm,nsp-usb3-phy"; - reg = <0x10>; - usb3-ctrl-syscon = <&usb3_ctrl>; - #phy-cells = <0>; - status = "disabled"; - }; - }; - - usb3_ctrl: syscon@104408 { - compatible = "brcm,nsp-usb3-ctrl", "syscon"; - reg = <0x104408 0x3fc>; - }; diff --git a/dts/Bindings/powerpc/4xx/emac.txt b/dts/Bindings/powerpc/4xx/emac.txt index 712baf6c3e..44b842b6ca 100644 --- a/dts/Bindings/powerpc/4xx/emac.txt +++ b/dts/Bindings/powerpc/4xx/emac.txt @@ -71,6 +71,9 @@ For Axon it can be absent, though my current driver doesn't handle phy-address yet so for now, keep 0x00ffffff in it. + - phy-handle : Used to describe configurations where a external PHY + is used. Please refer to: + Documentation/devicetree/bindings/net/ethernet.txt - rx-fifo-size-gige : 1 cell, Rx fifo size in bytes for 1000 Mb/sec operations (if absent the value is the same as rx-fifo-size). For Axon, either absent or 2048. @@ -81,8 +84,22 @@ offload, phandle of the TAH device node. - tah-channel : 1 cell, optional. If appropriate, channel used on the TAH engine. + - fixed-link : Fixed-link subnode describing a link to a non-MDIO + managed entity. See + Documentation/devicetree/bindings/net/fixed-link.txt + for details. + - mdio subnode : When the EMAC has a phy connected to its local + mdio, which us supported by the kernel's network + PHY library in drivers/net/phy, there must be device + tree subnode with the following required properties: + - #address-cells: Must be <1>. + - #size-cells: Must be <0>. - Example: + For PHY definitions: Please refer to + Documentation/devicetree/bindings/net/phy.txt and + Documentation/devicetree/bindings/net/ethernet.txt + + Examples: EMAC0: ethernet@40000800 { device_type = "network"; @@ -104,6 +121,48 @@ zmii-channel = <0>; }; + EMAC1: ethernet@ef600c00 { + device_type = "network"; + compatible = "ibm,emac-apm821xx", "ibm,emac4sync"; + interrupt-parent = <&EMAC1>; + interrupts = <0 1>; + #interrupt-cells = <1>; + #address-cells = <0>; + #size-cells = <0>; + interrupt-map = <0 &UIC2 0x10 IRQ_TYPE_LEVEL_HIGH /* Status */ + 1 &UIC2 0x14 IRQ_TYPE_LEVEL_HIGH /* Wake */>; + reg = <0xef600c00 0x000000c4>; + local-mac-address = [000000000000]; /* Filled in by U-Boot */ + mal-device = <&MAL0>; + mal-tx-channel = <0>; + mal-rx-channel = <0>; + cell-index = <0>; + max-frame-size = <9000>; + rx-fifo-size = <16384>; + tx-fifo-size = <2048>; + fifo-entry-size = <10>; + phy-mode = "rgmii"; + phy-handle = <&phy0>; + phy-map = <0x00000000>; + rgmii-device = <&RGMII0>; + rgmii-channel = <0>; + tah-device = <&TAH0>; + tah-channel = <0>; + has-inverted-stacr-oc; + has-new-stacr-staopc; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + + phy0: ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0>; + }; + }; + }; + + ii) McMAL node Required properties: @@ -145,4 +204,3 @@ - revision : as provided by the RGMII new version register if available. For Axon: 0x0000012a - diff --git a/dts/Bindings/regulator/ti-abb-regulator.txt b/dts/Bindings/regulator/ti-abb-regulator.txt index c3f6546eba..6a23ad9ac5 100644 --- a/dts/Bindings/regulator/ti-abb-regulator.txt +++ b/dts/Bindings/regulator/ti-abb-regulator.txt @@ -45,7 +45,7 @@ Required Properties: Optional Properties: - reg-names: In addition to the required properties, the following are optional - "efuse-address" - Contains efuse base address used to pick up ABB info. - - "ldo-address" - Contains address of ABB LDO overide register address. + - "ldo-address" - Contains address of ABB LDO override register. "efuse-address" is required for this. - ti,ldovbb-vset-mask - Required if ldo-address is set, mask for LDO override register to provide override vset value. diff --git a/dts/Bindings/rng/omap_rng.txt b/dts/Bindings/rng/omap_rng.txt index 471477299e..9cf7876ab4 100644 --- a/dts/Bindings/rng/omap_rng.txt +++ b/dts/Bindings/rng/omap_rng.txt @@ -12,7 +12,8 @@ Required properties: - reg : Offset and length of the register set for the module - interrupts : the interrupt number for the RNG module. Used for "ti,omap4-rng" and "inside-secure,safexcel-eip76" -- clocks: the trng clock source +- clocks: the trng clock source. Only mandatory for the + "inside-secure,safexcel-eip76" compatible. Example: /* AM335x */ diff --git a/dts/Bindings/usb/usb251xb.txt b/dts/Bindings/usb/usb251xb.txt index 0c065f7765..3957d4edaa 100644 --- a/dts/Bindings/usb/usb251xb.txt +++ b/dts/Bindings/usb/usb251xb.txt @@ -7,18 +7,18 @@ Required properties : - compatible : Should be "microchip,usb251xb" or one of the specific types: "microchip,usb2512b", "microchip,usb2512bi", "microchip,usb2513b", "microchip,usb2513bi", "microchip,usb2514b", "microchip,usb2514bi" - - hub-reset-gpios : Should specify the gpio for hub reset + - reset-gpios : Should specify the gpio for hub reset + - reg : I2C address on the selected bus (default is <0x2C>) Optional properties : - - reg : I2C address on the selected bus (default is <0x2C>) - skip-config : Skip Hub configuration, but only send the USB-Attach command - - vendor-id : USB Vendor ID of the hub (16 bit, default is 0x0424) - - product-id : USB Product ID of the hub (16 bit, default depends on type) - - device-id : USB Device ID of the hub (16 bit, default is 0x0bb3) - - language-id : USB Language ID (16 bit, default is 0x0000) - - manufacturer : USB Manufacturer string (max 31 characters long) - - product : USB Product string (max 31 characters long) - - serial : USB Serial string (max 31 characters long) + - vendor-id : Set USB Vendor ID of the hub (16 bit, default is 0x0424) + - product-id : Set USB Product ID of the hub (16 bit, default depends on type) + - device-id : Set USB Device ID of the hub (16 bit, default is 0x0bb3) + - language-id : Set USB Language ID (16 bit, default is 0x0000) + - manufacturer : Set USB Manufacturer string (max 31 characters long) + - product : Set USB Product string (max 31 characters long) + - serial : Set USB Serial string (max 31 characters long) - {bus,self}-powered : selects between self- and bus-powered operation (default is self-powered) - disable-hi-speed : disable USB Hi-Speed support @@ -31,8 +31,10 @@ Optional properties : (default is individual) - dynamic-power-switching : enable auto-switching from self- to bus-powered operation if the local power source is removed or unavailable - - oc-delay-{100us,4ms,8ms,16ms} : set over current timer delay (default is 8ms) - - compound-device : indicated the hub is part of a compound device + - oc-delay-us : Delay time (in microseconds) for filtering the over-current + sense inputs. Valid values are 100, 4000, 8000 (default) and 16000. If + an invalid value is given, the default is used instead. + - compound-device : indicate the hub is part of a compound device - port-mapping-mode : enable port mapping mode - string-support : enable string descriptor support (required for manufacturer, product and serial string configuration) @@ -40,34 +42,15 @@ Optional properties : device connected. - sp-disabled-ports : Specifies the ports which will be self-power disabled - bp-disabled-ports : Specifies the ports which will be bus-power disabled - - max-sp-power : Specifies the maximum current the hub consumes from an - upstream port when operating as self-powered hub including the power - consumption of a permanently attached peripheral if the hub is - configured as a compound device. The value is given in mA in a 0 - 500 - range (default is 2). - - max-bp-power : Specifies the maximum current the hub consumes from an - upstream port when operating as bus-powered hub including the power - consumption of a permanently attached peripheral if the hub is - configured as a compound device. The value is given in mA in a 0 - 500 - range (default is 100). - - max-sp-current : Specifies the maximum current the hub consumes from an - upstream port when operating as self-powered hub EXCLUDING the power - consumption of a permanently attached peripheral if the hub is - configured as a compound device. The value is given in mA in a 0 - 500 - range (default is 2). - - max-bp-current : Specifies the maximum current the hub consumes from an - upstream port when operating as bus-powered hub EXCLUDING the power - consumption of a permanently attached peripheral if the hub is - configured as a compound device. The value is given in mA in a 0 - 500 - range (default is 100). - - power-on-time : Specifies the time it takes from the time the host initiates - the power-on sequence to a port until the port has adequate power. The - value is given in ms in a 0 - 510 range (default is 100ms). + - power-on-time-ms : Specifies the time it takes from the time the host + initiates the power-on sequence to a port until the port has adequate + power. The value is given in ms in a 0 - 510 range (default is 100ms). Examples: usb2512b@2c { compatible = "microchip,usb2512b"; - hub-reset-gpios = <&gpio1 4 GPIO_ACTIVE_LOW>; + reg = <0x2c>; + reset-gpios = <&gpio1 4 GPIO_ACTIVE_LOW>; }; usb2514b@2c { diff --git a/dts/include/dt-bindings/sound/cs42l42.h b/dts/include/dt-bindings/sound/cs42l42.h index 399a123aed..db69d84ed7 100644 --- a/dts/include/dt-bindings/sound/cs42l42.h +++ b/dts/include/dt-bindings/sound/cs42l42.h @@ -20,7 +20,7 @@ #define CS42L42_HPOUT_LOAD_1NF 0 #define CS42L42_HPOUT_LOAD_10NF 1 -/* HPOUT Clamp to GND Overide */ +/* HPOUT Clamp to GND Override */ #define CS42L42_HPOUT_CLAMP_EN 0 #define CS42L42_HPOUT_CLAMP_DIS 1 diff --git a/dts/src/arc/skeleton.dtsi b/dts/src/arc/skeleton.dtsi index 65808fe0a2..2891cb266c 100644 --- a/dts/src/arc/skeleton.dtsi +++ b/dts/src/arc/skeleton.dtsi @@ -26,6 +26,7 @@ device_type = "cpu"; compatible = "snps,arc770d"; reg = <0>; + clocks = <&core_clk>; }; }; diff --git a/dts/src/arc/skeleton_hs.dtsi b/dts/src/arc/skeleton_hs.dtsi index 2dfe8037df..5e944d3e5b 100644 --- a/dts/src/arc/skeleton_hs.dtsi +++ b/dts/src/arc/skeleton_hs.dtsi @@ -21,6 +21,7 @@ device_type = "cpu"; compatible = "snps,archs38"; reg = <0>; + clocks = <&core_clk>; }; }; diff --git a/dts/src/arc/skeleton_hs_idu.dtsi b/dts/src/arc/skeleton_hs_idu.dtsi index 4c11079f35..54b277d7de 100644 --- a/dts/src/arc/skeleton_hs_idu.dtsi +++ b/dts/src/arc/skeleton_hs_idu.dtsi @@ -19,8 +19,27 @@ cpu@0 { device_type = "cpu"; - compatible = "snps,archs38xN"; + compatible = "snps,archs38"; reg = <0>; + clocks = <&core_clk>; + }; + cpu@1 { + device_type = "cpu"; + compatible = "snps,archs38"; + reg = <1>; + clocks = <&core_clk>; + }; + cpu@2 { + device_type = "cpu"; + compatible = "snps,archs38"; + reg = <2>; + clocks = <&core_clk>; + }; + cpu@3 { + device_type = "cpu"; + compatible = "snps,archs38"; + reg = <3>; + clocks = <&core_clk>; }; }; diff --git a/dts/src/arc/vdk_axs10x_mb.dtsi b/dts/src/arc/vdk_axs10x_mb.dtsi index f0df59b23e..459fc656b7 100644 --- a/dts/src/arc/vdk_axs10x_mb.dtsi +++ b/dts/src/arc/vdk_axs10x_mb.dtsi @@ -112,13 +112,19 @@ interrupts = <7>; bus-width = <4>; }; + }; - /* Embedded Vision subsystem UIO mappings; only relevant for EV VDK */ - uio_ev: uio@0xD0000000 { - compatible = "generic-uio"; - reg = <0xD0000000 0x2000 0xD1000000 0x2000 0x90000000 0x10000000 0xC0000000 0x10000000>; - reg-names = "ev_gsa", "ev_ctrl", "ev_shared_mem", "ev_code_mem"; - interrupts = <23>; - }; + /* + * Embedded Vision subsystem UIO mappings; only relevant for EV VDK + * + * This node is intentionally put outside of MB above becase + * it maps areas outside of MB's 0xEz-0xFz. + */ + uio_ev: uio@0xD0000000 { + compatible = "generic-uio"; + reg = <0xD0000000 0x2000 0xD1000000 0x2000 0x90000000 0x10000000 0xC0000000 0x10000000>; + reg-names = "ev_gsa", "ev_ctrl", "ev_shared_mem", "ev_code_mem"; + interrupt-parent = <&mb_intc>; + interrupts = <23>; }; }; diff --git a/dts/src/arm/am335x-pcm-953.dtsi b/dts/src/arm/am335x-pcm-953.dtsi index 02981eae96..1ec8e0d801 100644 --- a/dts/src/arm/am335x-pcm-953.dtsi +++ b/dts/src/arm/am335x-pcm-953.dtsi @@ -63,14 +63,14 @@ label = "home"; linux,code = <KEY_HOME>; gpios = <&gpio3 7 GPIO_ACTIVE_HIGH>; - gpio-key,wakeup; + wakeup-source; }; button@1 { label = "menu"; linux,code = <KEY_MENU>; gpios = <&gpio3 8 GPIO_ACTIVE_HIGH>; - gpio-key,wakeup; + wakeup-source; }; }; diff --git a/dts/src/arm/am57xx-idk-common.dtsi b/dts/src/arm/am57xx-idk-common.dtsi index 0d341c545b..e5ac1d81d1 100644 --- a/dts/src/arm/am57xx-idk-common.dtsi +++ b/dts/src/arm/am57xx-idk-common.dtsi @@ -315,6 +315,13 @@ /* ID & VBUS GPIOs provided in board dts */ }; }; + + tpic2810: tpic2810@60 { + compatible = "ti,tpic2810"; + reg = <0x60>; + gpio-controller; + #gpio-cells = <2>; + }; }; &mcspi3 { @@ -330,13 +337,6 @@ spi-max-frequency = <1000000>; spi-cpol; }; - - tpic2810: tpic2810@60 { - compatible = "ti,tpic2810"; - reg = <0x60>; - gpio-controller; - #gpio-cells = <2>; - }; }; &uart3 { diff --git a/dts/src/arm/bcm5301x.dtsi b/dts/src/arm/bcm5301x.dtsi index 4fbb089cf5..00de62dc00 100644 --- a/dts/src/arm/bcm5301x.dtsi +++ b/dts/src/arm/bcm5301x.dtsi @@ -66,14 +66,14 @@ timer@20200 { compatible = "arm,cortex-a9-global-timer"; reg = <0x20200 0x100>; - interrupts = <GIC_PPI 11 IRQ_TYPE_LEVEL_HIGH>; + interrupts = <GIC_PPI 11 IRQ_TYPE_EDGE_RISING>; clocks = <&periph_clk>; }; local-timer@20600 { compatible = "arm,cortex-a9-twd-timer"; reg = <0x20600 0x100>; - interrupts = <GIC_PPI 13 IRQ_TYPE_LEVEL_HIGH>; + interrupts = <GIC_PPI 13 IRQ_TYPE_EDGE_RISING>; clocks = <&periph_clk>; }; diff --git a/dts/src/arm/bcm953012k.dts b/dts/src/arm/bcm953012k.dts index bfd923096a..ae31a5826e 100644 --- a/dts/src/arm/bcm953012k.dts +++ b/dts/src/arm/bcm953012k.dts @@ -48,15 +48,14 @@ }; memory { - reg = <0x00000000 0x10000000>; + reg = <0x80000000 0x10000000>; }; }; &uart0 { - clock-frequency = <62499840>; + status = "okay"; }; &uart1 { - clock-frequency = <62499840>; status = "okay"; }; diff --git a/dts/src/arm/bcm958522er.dts b/dts/src/arm/bcm958522er.dts index 3f04a40eb9..df05e7f568 100644 --- a/dts/src/arm/bcm958522er.dts +++ b/dts/src/arm/bcm958522er.dts @@ -55,6 +55,7 @@ gpio-restart { compatible = "gpio-restart"; gpios = <&gpioa 15 GPIO_ACTIVE_LOW>; + open-source; priority = <200>; }; }; diff --git a/dts/src/arm/bcm958525er.dts b/dts/src/arm/bcm958525er.dts index 9fd542200d..4a3ab19c62 100644 --- a/dts/src/arm/bcm958525er.dts +++ b/dts/src/arm/bcm958525er.dts @@ -55,6 +55,7 @@ gpio-restart { compatible = "gpio-restart"; gpios = <&gpioa 15 GPIO_ACTIVE_LOW>; + open-source; priority = <200>; }; }; diff --git a/dts/src/arm/bcm958525xmc.dts b/dts/src/arm/bcm958525xmc.dts index 41e7fd350f..81f78435d8 100644 --- a/dts/src/arm/bcm958525xmc.dts +++ b/dts/src/arm/bcm958525xmc.dts @@ -55,6 +55,7 @@ gpio-restart { compatible = "gpio-restart"; gpios = <&gpioa 31 GPIO_ACTIVE_LOW>; + open-source; priority = <200>; }; }; diff --git a/dts/src/arm/bcm958622hr.dts b/dts/src/arm/bcm958622hr.dts index 477c4860db..c88b8fefcb 100644 --- a/dts/src/arm/bcm958622hr.dts +++ b/dts/src/arm/bcm958622hr.dts @@ -55,6 +55,7 @@ gpio-restart { compatible = "gpio-restart"; gpios = <&gpioa 15 GPIO_ACTIVE_LOW>; + open-source; priority = <200>; }; }; diff --git a/dts/src/arm/bcm958623hr.dts b/dts/src/arm/bcm958623hr.dts index c0a499d5ba..d503fa0dde 100644 --- a/dts/src/arm/bcm958623hr.dts +++ b/dts/src/arm/bcm958623hr.dts @@ -55,6 +55,7 @@ gpio-restart { compatible = "gpio-restart"; gpios = <&gpioa 15 GPIO_ACTIVE_LOW>; + open-source; priority = <200>; }; }; diff --git a/dts/src/arm/bcm958625hr.dts b/dts/src/arm/bcm958625hr.dts index f7eb5854a2..cc0363b843 100644 --- a/dts/src/arm/bcm958625hr.dts +++ b/dts/src/arm/bcm958625hr.dts @@ -55,6 +55,7 @@ gpio-restart { compatible = "gpio-restart"; gpios = <&gpioa 15 GPIO_ACTIVE_LOW>; + open-source; priority = <200>; }; }; diff --git a/dts/src/arm/bcm988312hr.dts b/dts/src/arm/bcm988312hr.dts index 16666324fd..74e15a3cd9 100644 --- a/dts/src/arm/bcm988312hr.dts +++ b/dts/src/arm/bcm988312hr.dts @@ -55,6 +55,7 @@ gpio-restart { compatible = "gpio-restart"; gpios = <&gpioa 15 GPIO_ACTIVE_LOW>; + open-source; priority = <200>; }; }; diff --git a/dts/src/arm/imx6qdl.dtsi b/dts/src/arm/imx6qdl.dtsi index 6d7bf64961..8e90014705 100644 --- a/dts/src/arm/imx6qdl.dtsi +++ b/dts/src/arm/imx6qdl.dtsi @@ -768,6 +768,12 @@ mask = <0x60>; status = "disabled"; }; + + snvs_lpgpr: snvs-lpgpr { + compatible = "fsl,imx6q-snvs-lpgpr"; + regmap = <&snvs>; + offset = <0x68>; + }; }; epit1: epit@020d0000 { /* EPIT1 */ diff --git a/dts/src/arm/imx6sl.dtsi b/dts/src/arm/imx6sl.dtsi index cc9572ea28..eeafba04cf 100644 --- a/dts/src/arm/imx6sl.dtsi +++ b/dts/src/arm/imx6sl.dtsi @@ -655,6 +655,12 @@ mask = <0x60>; status = "disabled"; }; + + snvs_lpgpr: snvs-lpgpr { + compatible = "fsl,imx6sl-snvs-lpgpr"; + regmap = <&snvs>; + offset = <0x68>; + }; }; epit1: epit@020d0000 { diff --git a/dts/src/arm/imx6sx-udoo-neo.dtsi b/dts/src/arm/imx6sx-udoo-neo.dtsi index 49f466fe0b..dcfc975914 100644 --- a/dts/src/arm/imx6sx-udoo-neo.dtsi +++ b/dts/src/arm/imx6sx-udoo-neo.dtsi @@ -121,11 +121,6 @@ }; }; -&cpu0 { - arm-supply = <&sw1a_reg>; - soc-supply = <&sw1c_reg>; -}; - &fec1 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_enet1>; diff --git a/dts/src/arm/sama5d2.dtsi b/dts/src/arm/sama5d2.dtsi index 22332be721..528b4e9c6d 100644 --- a/dts/src/arm/sama5d2.dtsi +++ b/dts/src/arm/sama5d2.dtsi @@ -266,7 +266,7 @@ }; usb1: ohci@00400000 { - compatible = "atmel,sama5d2-ohci", "usb-ohci"; + compatible = "atmel,at91rm9200-ohci", "usb-ohci"; reg = <0x00400000 0x100000>; interrupts = <41 IRQ_TYPE_LEVEL_HIGH 2>; clocks = <&uhphs_clk>, <&uhphs_clk>, <&uhpck>; diff --git a/dts/src/arm/ste-dbx5x0.dtsi b/dts/src/arm/ste-dbx5x0.dtsi index 82d8c47712..162e1eb537 100644 --- a/dts/src/arm/ste-dbx5x0.dtsi +++ b/dts/src/arm/ste-dbx5x0.dtsi @@ -14,6 +14,7 @@ #include <dt-bindings/mfd/dbx500-prcmu.h> #include <dt-bindings/arm/ux500_pm_domains.h> #include <dt-bindings/gpio/gpio.h> +#include <dt-bindings/clock/ste-ab8500.h> #include "skeleton.dtsi" / { @@ -603,6 +604,11 @@ interrupt-controller; #interrupt-cells = <2>; + ab8500_clock: clock-controller { + compatible = "stericsson,ab8500-clk"; + #clock-cells = <1>; + }; + ab8500_gpio: ab8500-gpio { compatible = "stericsson,ab8500-gpio"; gpio-controller; @@ -686,6 +692,8 @@ ab8500-pwm { compatible = "stericsson,ab8500-pwm"; + clocks = <&ab8500_clock AB8500_SYSCLK_INT>; + clock-names = "intclk"; }; ab8500-debugfs { @@ -700,6 +708,9 @@ V-AMIC2-supply = <&ab8500_ldo_anamic2_reg>; V-DMIC-supply = <&ab8500_ldo_dmic_reg>; + clocks = <&ab8500_clock AB8500_SYSCLK_AUDIO>; + clock-names = "audioclk"; + stericsson,earpeice-cmv = <950>; /* Units in mV. */ }; @@ -1095,6 +1106,14 @@ status = "disabled"; }; + sound { + compatible = "stericsson,snd-soc-mop500"; + stericsson,cpu-dai = <&msp1 &msp3>; + stericsson,audio-codec = <&codec>; + clocks = <&prcmu_clk PRCMU_SYSCLK>, <&ab8500_clock AB8500_SYSCLK_ULP>, <&ab8500_clock AB8500_SYSCLK_INT>; + clock-names = "sysclk", "ulpclk", "intclk"; + }; + msp0: msp@80123000 { compatible = "stericsson,ux500-msp-i2s"; reg = <0x80123000 0x1000>; diff --git a/dts/src/arm/ste-href.dtsi b/dts/src/arm/ste-href.dtsi index f37f9e1071..9e359e4f34 100644 --- a/dts/src/arm/ste-href.dtsi +++ b/dts/src/arm/ste-href.dtsi @@ -186,15 +186,6 @@ status = "okay"; }; - sound { - compatible = "stericsson,snd-soc-mop500"; - - stericsson,cpu-dai = <&msp1 &msp3>; - stericsson,audio-codec = <&codec>; - clocks = <&prcmu_clk PRCMU_SYSCLK>; - clock-names = "sysclk"; - }; - msp0: msp@80123000 { pinctrl-names = "default"; pinctrl-0 = <&msp0_default_mode>; diff --git a/dts/src/arm/ste-snowball.dts b/dts/src/arm/ste-snowball.dts index dd5514def6..ade1d0d4e5 100644 --- a/dts/src/arm/ste-snowball.dts +++ b/dts/src/arm/ste-snowball.dts @@ -159,15 +159,6 @@ "", "", "", "", "", "", "", ""; }; - sound { - compatible = "stericsson,snd-soc-mop500"; - - stericsson,cpu-dai = <&msp1 &msp3>; - stericsson,audio-codec = <&codec>; - clocks = <&prcmu_clk PRCMU_SYSCLK>; - clock-names = "sysclk"; - }; - msp0: msp@80123000 { pinctrl-names = "default"; pinctrl-0 = <&msp0_default_mode>; diff --git a/dts/src/arm/sun7i-a20-lamobo-r1.dts b/dts/src/arm/sun7i-a20-lamobo-r1.dts index 72ec0d5ae0..bbf1c8cbaa 100644 --- a/dts/src/arm/sun7i-a20-lamobo-r1.dts +++ b/dts/src/arm/sun7i-a20-lamobo-r1.dts @@ -167,7 +167,7 @@ reg = <8>; label = "cpu"; ethernet = <&gmac>; - phy-mode = "rgmii"; + phy-mode = "rgmii-txid"; fixed-link { speed = <1000>; full-duplex; diff --git a/dts/src/arm/sun8i-a23-a33.dtsi b/dts/src/arm/sun8i-a23-a33.dtsi index a952cc0703..8a3ed21cb7 100644 --- a/dts/src/arm/sun8i-a23-a33.dtsi +++ b/dts/src/arm/sun8i-a23-a33.dtsi @@ -495,7 +495,7 @@ resets = <&ccu RST_BUS_GPU>; assigned-clocks = <&ccu CLK_GPU>; - assigned-clock-rates = <408000000>; + assigned-clock-rates = <384000000>; }; gic: interrupt-controller@01c81000 { diff --git a/dts/src/arm/sun8i-a33.dtsi b/dts/src/arm/sun8i-a33.dtsi index 18c174fef8..0467fb365b 100644 --- a/dts/src/arm/sun8i-a33.dtsi +++ b/dts/src/arm/sun8i-a33.dtsi @@ -113,8 +113,8 @@ simple-audio-card,mclk-fs = <512>; simple-audio-card,aux-devs = <&codec_analog>; simple-audio-card,routing = - "Left DAC", "Digital Left DAC", - "Right DAC", "Digital Right DAC"; + "Left DAC", "AIF1 Slot 0 Left", + "Right DAC", "AIF1 Slot 0 Right"; status = "disabled"; simple-audio-card,cpu { diff --git a/dts/src/arm/sun8i-reference-design-tablet.dtsi b/dts/src/arm/sun8i-reference-design-tablet.dtsi index 7097c18ff4..d6bd15898d 100644 --- a/dts/src/arm/sun8i-reference-design-tablet.dtsi +++ b/dts/src/arm/sun8i-reference-design-tablet.dtsi @@ -50,8 +50,6 @@ backlight: backlight { compatible = "pwm-backlight"; - pinctrl-names = "default"; - pinctrl-0 = <&bl_en_pin>; pwms = <&pwm 0 50000 PWM_POLARITY_INVERTED>; brightness-levels = <0 10 20 30 40 50 60 70 80 90 100>; default-brightness-level = <8>; @@ -93,11 +91,6 @@ }; &pio { - bl_en_pin: bl_en_pin@0 { - pins = "PH6"; - function = "gpio_in"; - }; - mmc0_cd_pin: mmc0_cd_pin@0 { pins = "PB4"; function = "gpio_in"; diff --git a/dts/src/arm64/broadcom/ns2.dtsi b/dts/src/arm64/broadcom/ns2.dtsi index 9f9e203c09..bcb03fc326 100644 --- a/dts/src/arm64/broadcom/ns2.dtsi +++ b/dts/src/arm64/broadcom/ns2.dtsi @@ -114,6 +114,7 @@ pcie0: pcie@20020000 { compatible = "brcm,iproc-pcie"; reg = <0 0x20020000 0 0x1000>; + dma-coherent; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0>; @@ -144,6 +145,7 @@ pcie4: pcie@50020000 { compatible = "brcm,iproc-pcie"; reg = <0 0x50020000 0 0x1000>; + dma-coherent; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0>; @@ -174,6 +176,7 @@ pcie8: pcie@60c00000 { compatible = "brcm,iproc-pcie-paxc"; reg = <0 0x60c00000 0 0x1000>; + dma-coherent; linux,pci-domain = <8>; bus-range = <0x0 0x1>; @@ -203,6 +206,7 @@ <0x61030000 0x100>; reg-names = "amac_base", "idm_base", "nicpm_base"; interrupts = <GIC_SPI 341 IRQ_TYPE_LEVEL_HIGH>; + dma-coherent; phy-handle = <&gphy0>; phy-mode = "rgmii"; status = "disabled"; @@ -213,6 +217,7 @@ reg = <0x612c0000 0x445>; /* PDC FS0 regs */ interrupts = <GIC_SPI 187 IRQ_TYPE_LEVEL_HIGH>; #mbox-cells = <1>; + dma-coherent; brcm,rx-status-len = <32>; brcm,use-bcm-hdr; }; @@ -222,6 +227,7 @@ reg = <0x612e0000 0x445>; /* PDC FS1 regs */ interrupts = <GIC_SPI 189 IRQ_TYPE_LEVEL_HIGH>; #mbox-cells = <1>; + dma-coherent; brcm,rx-status-len = <32>; brcm,use-bcm-hdr; }; @@ -231,6 +237,7 @@ reg = <0x61300000 0x445>; /* PDC FS2 regs */ interrupts = <GIC_SPI 191 IRQ_TYPE_LEVEL_HIGH>; #mbox-cells = <1>; + dma-coherent; brcm,rx-status-len = <32>; brcm,use-bcm-hdr; }; @@ -240,6 +247,7 @@ reg = <0x61320000 0x445>; /* PDC FS3 regs */ interrupts = <GIC_SPI 193 IRQ_TYPE_LEVEL_HIGH>; #mbox-cells = <1>; + dma-coherent; brcm,rx-status-len = <32>; brcm,use-bcm-hdr; }; @@ -644,6 +652,7 @@ sata: ahci@663f2000 { compatible = "brcm,iproc-ahci", "generic-ahci"; reg = <0x663f2000 0x1000>; + dma-coherent; reg-names = "ahci"; interrupts = <GIC_SPI 438 IRQ_TYPE_LEVEL_HIGH>; #address-cells = <1>; @@ -667,6 +676,7 @@ compatible = "brcm,sdhci-iproc-cygnus"; reg = <0x66420000 0x100>; interrupts = <GIC_SPI 421 IRQ_TYPE_LEVEL_HIGH>; + dma-coherent; bus-width = <8>; clocks = <&genpll_sw BCM_NS2_GENPLL_SW_SDIO_CLK>; status = "disabled"; @@ -676,6 +686,7 @@ compatible = "brcm,sdhci-iproc-cygnus"; reg = <0x66430000 0x100>; interrupts = <GIC_SPI 422 IRQ_TYPE_LEVEL_HIGH>; + dma-coherent; bus-width = <8>; clocks = <&genpll_sw BCM_NS2_GENPLL_SW_SDIO_CLK>; status = "disabled"; diff --git a/fs/devfs-core.c b/fs/devfs-core.c index 75ed3b0e66..382606f1cf 100644 --- a/fs/devfs-core.c +++ b/fs/devfs-core.c @@ -37,7 +37,7 @@ int devfs_partition_complete(struct string_list *sl, char *instr) len = strlen(instr); list_for_each_entry(cdev, &cdev_list, list) { - if (cdev->flags & DEVFS_IS_PARTITION && + if (cdev->master && !strncmp(instr, cdev->name, len)) { string_list_add_asprintf(sl, "%s ", cdev->name); } @@ -184,76 +184,8 @@ int cdev_flush(struct cdev *cdev) return cdev->ops->flush(cdev); } -static int partition_ioctl(struct cdev *cdev, int request, void *buf) -{ - int ret = 0; - loff_t offset, *_buf = buf; - struct mtd_info_user *user = buf; - - switch (request) { - case MEMSETBADBLOCK: - case MEMSETGOODBLOCK: - case MEMGETBADBLOCK: - offset = *_buf; - offset += cdev->offset; - ret = cdev->ops->ioctl(cdev, request, &offset); - break; - case MEMGETINFO: - if (cdev->mtd) { - user->type = cdev->mtd->type; - user->flags = cdev->mtd->flags; - user->size = cdev->mtd->size; - user->erasesize = cdev->mtd->erasesize; - user->writesize = cdev->mtd->writesize; - user->oobsize = cdev->mtd->oobsize; - user->subpagesize = cdev->mtd->writesize >> cdev->mtd->subpage_sft; - user->mtd = cdev->mtd; - /* The below fields are obsolete */ - user->ecctype = -1; - user->eccsize = 0; - break; - } - if (!cdev->ops->ioctl) { - ret = -EINVAL; - break; - } - ret = cdev->ops->ioctl(cdev, request, buf); - break; -#if (defined(CONFIG_NAND_ECC_HW) || defined(CONFIG_NAND_ECC_SOFT)) - case ECCGETSTATS: -#endif - case MEMERASE: - if (!cdev->ops->ioctl) { - ret = -EINVAL; - break; - } - ret = cdev->ops->ioctl(cdev, request, buf); - break; -#ifdef CONFIG_PARTITION - case MEMGETREGIONINFO: - if (cdev->mtd) { - struct region_info_user *reg = buf; - int erasesize_shift = ffs(cdev->mtd->erasesize) - 1; - - reg->offset = cdev->offset; - reg->erasesize = cdev->mtd->erasesize; - reg->numblocks = cdev->size >> erasesize_shift; - reg->regionindex = cdev->mtd->index; - } - break; -#endif - default: - ret = -EINVAL; - } - - return ret; -} - int cdev_ioctl(struct cdev *cdev, int request, void *buf) { - if (cdev->flags & DEVFS_IS_PARTITION) - return partition_ioctl(cdev, request, buf); - if (!cdev->ops->ioctl) return -EINVAL; @@ -277,6 +209,7 @@ int devfs_create(struct cdev *new) return -EEXIST; INIT_LIST_HEAD(&new->links); + INIT_LIST_HEAD(&new->partitions); list_add_tail(&new->list, &cdev_list); if (new->dev) { @@ -326,6 +259,9 @@ int devfs_remove(struct cdev *cdev) list_for_each_entry_safe(c, tmp, &cdev->links, link_entry) devfs_remove(c); + if (cdev->master) + list_del(&cdev->partition_entry); + if (cdev->link) free(cdev); @@ -374,6 +310,8 @@ static struct cdev *__devfs_add_partition(struct cdev *cdev, partinfo->flags, partinfo->name); if (IS_ERR(mtd)) return (void *)mtd; + + list_add_tail(&mtd->cdev.partition_entry, &cdev->partitions); return &mtd->cdev; } @@ -388,7 +326,9 @@ static struct cdev *__devfs_add_partition(struct cdev *cdev, new->offset = cdev->offset + offset; new->dev = cdev->dev; - new->flags = partinfo->flags | DEVFS_IS_PARTITION; + new->master = cdev; + + list_add_tail(&new->partition_entry, &cdev->partitions); devfs_create(new); @@ -428,7 +368,7 @@ int devfs_del_partition(const char *name) return ret; } - if (!(cdev->flags & DEVFS_IS_PARTITION)) + if (!cdev->master) return -EINVAL; if (cdev->flags & DEVFS_PARTITION_FIXED) return -EPERM; diff --git a/fs/ext4/ext4_common.c b/fs/ext4/ext4_common.c index 6c4083eae9..55e837b4bd 100644 --- a/fs/ext4/ext4_common.c +++ b/fs/ext4/ext4_common.c @@ -83,19 +83,19 @@ static int ext4fs_blockgroup(struct ext2_data *data, int group, long int blkno; unsigned int blkoff, desc_per_blk; struct ext_filesystem *fs = data->fs; + int desc_size = fs->gdsize; - desc_per_blk = EXT2_BLOCK_SIZE(data) / sizeof(struct ext2_block_group); + desc_per_blk = EXT2_BLOCK_SIZE(data) / desc_size; - blkno = __le32_to_cpu(data->sblock.first_data_block) + 1 + + blkno = le32_to_cpu(data->sblock.first_data_block) + 1 + group / desc_per_blk; - blkoff = (group % desc_per_blk) * sizeof(struct ext2_block_group); + blkoff = (group % desc_per_blk) * desc_size; dev_dbg(fs->dev, "read %d group descriptor (blkno %ld blkoff %u)\n", group, blkno, blkoff); return ext4fs_devread(fs, blkno << LOG2_EXT2_BLOCK_SIZE(data), - blkoff, sizeof(struct ext2_block_group), - (char *)blkgrp); + blkoff, desc_size, (char *)blkgrp); } int ext4fs_read_inode(struct ext2_data *data, int ino, struct ext2_inode *inode) @@ -109,14 +109,14 @@ int ext4fs_read_inode(struct ext2_data *data, int ino, struct ext2_inode *inode) /* It is easier to calculate if the first inode is 0. */ ino--; - ret = ext4fs_blockgroup(data, ino / __le32_to_cpu + ret = ext4fs_blockgroup(data, ino / le32_to_cpu (sblock->inodes_per_group), &blkgrp); if (ret) return ret; inodes_per_block = EXT2_BLOCK_SIZE(data) / fs->inodesz; - blkno = __le32_to_cpu(blkgrp.inode_table_id) + - (ino % __le32_to_cpu(sblock->inodes_per_group)) / inodes_per_block; + blkno = le32_to_cpu(blkgrp.inode_table_id) + + (ino % le32_to_cpu(sblock->inodes_per_group)) / inodes_per_block; blkoff = (ino % inodes_per_block) * fs->inodesz; /* Read the inode. */ ret = ext4fs_devread(fs, blkno << LOG2_EXT2_BLOCK_SIZE(data), blkoff, @@ -212,14 +212,14 @@ long int read_allocated_block(struct ext2fs_node *node, int fileblock) if (fileblock < INDIRECT_BLOCKS) { /* Direct blocks. */ - blknr = __le32_to_cpu(inode->b.blocks.dir_blocks[fileblock]); + blknr = le32_to_cpu(inode->b.blocks.dir_blocks[fileblock]); } else if (fileblock < (INDIRECT_BLOCKS + (blksz / 4))) { /* Indirect. */ ret = ext4fs_get_indir_block(node, &data->indir1, - __le32_to_cpu(inode->b.blocks.indir_block) << log2_blksz); + le32_to_cpu(inode->b.blocks.indir_block) << log2_blksz); if (ret) return ret; - blknr = __le32_to_cpu(data->indir1.data[fileblock - INDIRECT_BLOCKS]); + blknr = le32_to_cpu(data->indir1.data[fileblock - INDIRECT_BLOCKS]); } else if (fileblock < (INDIRECT_BLOCKS + (blksz / 4 * (blksz / 4 + 1)))) { /* Double indirect. */ @@ -227,16 +227,16 @@ long int read_allocated_block(struct ext2fs_node *node, int fileblock) long int rblock = fileblock - (INDIRECT_BLOCKS + blksz / 4); ret = ext4fs_get_indir_block(node, &data->indir1, - __le32_to_cpu(inode->b.blocks.double_indir_block) << log2_blksz); + le32_to_cpu(inode->b.blocks.double_indir_block) << log2_blksz); if (ret) return ret; ret = ext4fs_get_indir_block(node, &data->indir2, - __le32_to_cpu(data->indir1.data[rblock / perblock]) << log2_blksz); + le32_to_cpu(data->indir1.data[rblock / perblock]) << log2_blksz); if (ret) return ret; - blknr = __le32_to_cpu(data->indir2.data[rblock % perblock]); + blknr = le32_to_cpu(data->indir2.data[rblock % perblock]); } else { /* Triple indirect. */ rblock = fileblock - (INDIRECT_BLOCKS + blksz / 4 + @@ -245,21 +245,21 @@ long int read_allocated_block(struct ext2fs_node *node, int fileblock) perblock_parent = ((blksz / 4) * (blksz / 4)); ret = ext4fs_get_indir_block(node, &data->indir1, - __le32_to_cpu(inode->b.blocks.triple_indir_block) << log2_blksz); + le32_to_cpu(inode->b.blocks.triple_indir_block) << log2_blksz); if (ret) return ret; ret = ext4fs_get_indir_block(node, &data->indir2, - __le32_to_cpu(data->indir1.data[rblock / perblock_parent]) << log2_blksz); + le32_to_cpu(data->indir1.data[rblock / perblock_parent]) << log2_blksz); if (ret) return ret; ret = ext4fs_get_indir_block(node, &data->indir3, - __le32_to_cpu(data->indir2.data[rblock / perblock_child]) << log2_blksz); + le32_to_cpu(data->indir2.data[rblock / perblock_child]) << log2_blksz); if (ret) return ret; - blknr = __le32_to_cpu(data->indir3.data[rblock % perblock_child]); + blknr = le32_to_cpu(data->indir3.data[rblock % perblock_child]); } return blknr; @@ -282,7 +282,7 @@ int ext4fs_iterate_dir(struct ext2fs_node *dir, char *name, return ret; } /* Search the file. */ - while (fpos < __le32_to_cpu(diro->inode.size)) { + while (fpos < le32_to_cpu(diro->inode.size)) { struct ext2_dirent dirent; status = ext4fs_read_file(diro, fpos, @@ -308,7 +308,7 @@ int ext4fs_iterate_dir(struct ext2fs_node *dir, char *name, return -ENOMEM; fdiro->data = diro->data; - fdiro->ino = __le32_to_cpu(dirent.inode); + fdiro->ino = le32_to_cpu(dirent.inode); filename[dirent.namelen] = '\0'; @@ -323,7 +323,7 @@ int ext4fs_iterate_dir(struct ext2fs_node *dir, char *name, type = FILETYPE_REG; } else { ret = ext4fs_read_inode(diro->data, - __le32_to_cpu + le32_to_cpu (dirent.inode), &fdiro->inode); if (ret) { @@ -332,15 +332,15 @@ int ext4fs_iterate_dir(struct ext2fs_node *dir, char *name, } fdiro->inode_read = 1; - if ((__le16_to_cpu(fdiro->inode.mode) & + if ((le16_to_cpu(fdiro->inode.mode) & FILETYPE_INO_MASK) == FILETYPE_INO_DIRECTORY) { type = FILETYPE_DIRECTORY; - } else if ((__le16_to_cpu(fdiro->inode.mode) + } else if ((le16_to_cpu(fdiro->inode.mode) & FILETYPE_INO_MASK) == FILETYPE_INO_SYMLINK) { type = FILETYPE_SYMLINK; - } else if ((__le16_to_cpu(fdiro->inode.mode) + } else if ((le16_to_cpu(fdiro->inode.mode) & FILETYPE_INO_MASK) == FILETYPE_INO_REG) { type = FILETYPE_REG; @@ -357,7 +357,7 @@ int ext4fs_iterate_dir(struct ext2fs_node *dir, char *name, free(fdiro); } - fpos += __le16_to_cpu(dirent.direntlen); + fpos += le16_to_cpu(dirent.direntlen); } return -ENOENT; } @@ -373,16 +373,16 @@ char *ext4fs_read_symlink(struct ext2fs_node *node) if (ret) return NULL; } - symlink = zalloc(__le32_to_cpu(diro->inode.size) + 1); + symlink = zalloc(le32_to_cpu(diro->inode.size) + 1); if (!symlink) return 0; - if (__le32_to_cpu(diro->inode.size) < sizeof(diro->inode.b.symlink)) { + if (le32_to_cpu(diro->inode.size) < sizeof(diro->inode.b.symlink)) { strncpy(symlink, diro->inode.b.symlink, - __le32_to_cpu(diro->inode.size)); + le32_to_cpu(diro->inode.size)); } else { status = ext4fs_read_file(diro, 0, - __le32_to_cpu(diro->inode.size), + le32_to_cpu(diro->inode.size), symlink); if (status == 0) { free(symlink); @@ -390,7 +390,7 @@ char *ext4fs_read_symlink(struct ext2fs_node *node) } } - symlink[__le32_to_cpu(diro->inode.size)] = '\0'; + symlink[le32_to_cpu(diro->inode.size)] = '\0'; return symlink; } @@ -501,18 +501,29 @@ int ext4fs_mount(struct ext_filesystem *fs) goto fail; /* Make sure this is an ext2 filesystem. */ - if (__le16_to_cpu(data->sblock.magic) != EXT2_SUPER_MAGIC) { + if (le16_to_cpu(data->sblock.magic) != EXT2_SUPER_MAGIC) { ret = -EINVAL; goto fail; } - if (__le32_to_cpu(data->sblock.revision_level == 0)) + if (le32_to_cpu(data->sblock.revision_level) == 0) { fs->inodesz = 128; - else - fs->inodesz = __le16_to_cpu(data->sblock.inode_size); + fs->gdsize = 32; + } else { + debug("EXT4 features COMPAT: %08x INCOMPAT: %08x RO_COMPAT: %08x\n", + le32_to_cpu(data->sblock.feature_compatibility), + le32_to_cpu(data->sblock.feature_incompat), + le32_to_cpu(data->sblock.feature_ro_compat)); + + fs->inodesz = le16_to_cpu(data->sblock.inode_size); + fs->gdsize = le32_to_cpu(data->sblock.feature_incompat) & + EXT4_FEATURE_INCOMPAT_64BIT ? + le16_to_cpu(data->sblock.descriptor_size) : 32; + } - dev_info(fs->dev, "EXT2 rev %d, inode_size %d\n", - __le32_to_cpu(data->sblock.revision_level), fs->inodesz); + dev_info(fs->dev, "EXT2 rev %d, inode_size %d, descriptor size %d\n", + le32_to_cpu(data->sblock.revision_level), + fs->inodesz, fs->gdsize); data->diropen.data = data; data->diropen.ino = 2; diff --git a/fs/ext4/ext4fs.c b/fs/ext4/ext4fs.c index 1b9af80f80..bfc5f27cc3 100644 --- a/fs/ext4/ext4fs.c +++ b/fs/ext4/ext4fs.c @@ -54,7 +54,7 @@ int ext4fs_read_file(struct ext2fs_node *node, int pos, int blockcnt; int log2blocksize = LOG2_EXT2_BLOCK_SIZE(node->data); int blocksize = 1 << (log2blocksize + DISK_SECTOR_BITS); - unsigned int filesize = __le32_to_cpu(node->inode.size); + unsigned int filesize = le32_to_cpu(node->inode.size); int previous_block_number = -1; int delayed_start = 0; int delayed_extent = 0; diff --git a/fs/ext4/ext4fs.h b/fs/ext4/ext4fs.h index ead212d97a..17a490a943 100644 --- a/fs/ext4/ext4fs.h +++ b/fs/ext4/ext4fs.h @@ -28,6 +28,7 @@ #define EXT4_EXT_MAGIC 0xf30a #define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010 #define EXT4_FEATURE_INCOMPAT_EXTENTS 0x0040 +#define EXT4_FEATURE_INCOMPAT_64BIT 0x0080 #define EXT4_INDIRECT_BLOCKS 12 #define EXT4_BG_INODE_UNINIT 0x0001 @@ -81,6 +82,8 @@ struct ext_filesystem { uint32_t inodesz; /* Sectors per Block */ uint32_t sect_perblk; + /* Group Descriptor size */ + uint16_t gdsize; /* Group Descriptor Block Number */ uint32_t gdtable_blkno; /* Total block groups of partition */ diff --git a/fs/ext4/ext_barebox.c b/fs/ext4/ext_barebox.c index 5ec7ecd918..e40278a5bd 100644 --- a/fs/ext4/ext_barebox.c +++ b/fs/ext4/ext_barebox.c @@ -56,7 +56,7 @@ static int ext_open(struct device_d *dev, FILE *file, const char *filename) if (ret) return ret; - file->size = __le32_to_cpu(inode->inode.size); + file->size = le32_to_cpu(inode->inode.size); file->priv = inode; return 0; @@ -129,7 +129,7 @@ static struct dirent *ext_readdir(struct device_d *dev, DIR *dir) int ret; char *filename; - if (ext4_dir->fpos >= __le32_to_cpu(diro->inode.size)) + if (ext4_dir->fpos >= le32_to_cpu(diro->inode.size)) return NULL; ret = ext4fs_read_file(diro, ext4_dir->fpos, sizeof(struct ext2_dirent), @@ -151,7 +151,7 @@ static struct dirent *ext_readdir(struct device_d *dev, DIR *dir) filename[dirent.namelen] = '\0'; - ext4_dir->fpos += __le16_to_cpu(dirent.direntlen); + ext4_dir->fpos += le16_to_cpu(dirent.direntlen); strcpy(dir->d.d_name, filename); @@ -186,8 +186,8 @@ static int ext_stat(struct device_d *dev, const char *filename, struct stat *s) if (ret) return ret; - s->st_size = __le32_to_cpu(node->inode.size); - s->st_mode = __le16_to_cpu(node->inode.mode); + s->st_size = le32_to_cpu(node->inode.size); + s->st_mode = le16_to_cpu(node->inode.mode); ext4fs_free_node(node, &fs->data->diropen); diff --git a/fs/ext4/ext_common.h b/fs/ext4/ext_common.h index 704d8e66a6..e82b56b86a 100644 --- a/fs/ext4/ext_common.h +++ b/fs/ext4/ext_common.h @@ -59,14 +59,12 @@ #define EXT2_BLOCK_SIZE(data) (1 << LOG2_BLOCK_SIZE(data)) /* Log2 size of ext2 block in 512 blocks. */ -#define LOG2_EXT2_BLOCK_SIZE(data) (__le32_to_cpu \ +#define LOG2_EXT2_BLOCK_SIZE(data) (le32_to_cpu \ (data->sblock.log2_block_size) + 1) /* Log2 size of ext2 block in bytes. */ -#define LOG2_BLOCK_SIZE(data) (__le32_to_cpu \ +#define LOG2_BLOCK_SIZE(data) (le32_to_cpu \ (data->sblock.log2_block_size) + 10) -#define INODE_SIZE_FILESYSTEM(data) (__le32_to_cpu \ - (data->sblock.inode_size)) #define EXT2_FT_DIR 2 #define SUCCESS 1 @@ -79,92 +77,134 @@ /* The ext2 superblock. */ struct ext2_sblock { - uint32_t total_inodes; - uint32_t total_blocks; - uint32_t reserved_blocks; - uint32_t free_blocks; - uint32_t free_inodes; - uint32_t first_data_block; - uint32_t log2_block_size; - uint32_t log2_fragment_size; - uint32_t blocks_per_group; - uint32_t fragments_per_group; - uint32_t inodes_per_group; - uint32_t mtime; - uint32_t utime; - uint16_t mnt_count; - uint16_t max_mnt_count; - uint16_t magic; - uint16_t fs_state; - uint16_t error_handling; - uint16_t minor_revision_level; - uint32_t lastcheck; - uint32_t checkinterval; - uint32_t creator_os; - uint32_t revision_level; - uint16_t uid_reserved; - uint16_t gid_reserved; - uint32_t first_inode; - uint16_t inode_size; - uint16_t block_group_number; - uint32_t feature_compatibility; - uint32_t feature_incompat; - uint32_t feature_ro_compat; - uint32_t unique_id[4]; + __le32 total_inodes; + __le32 total_blocks; + __le32 reserved_blocks; + __le32 free_blocks; + __le32 free_inodes; + __le32 first_data_block; + __le32 log2_block_size; + __le32 log2_fragment_size; + __le32 blocks_per_group; + __le32 fragments_per_group; + __le32 inodes_per_group; + __le32 mtime; + __le32 utime; + __le16 mnt_count; + __le16 max_mnt_count; + __le16 magic; + __le16 fs_state; + __le16 error_handling; + __le16 minor_revision_level; + __le32 lastcheck; + __le32 checkinterval; + __le32 creator_os; + __le32 revision_level; + __le16 uid_reserved; + __le16 gid_reserved; + __le32 first_inode; + __le16 inode_size; + __le16 block_group_number; + __le32 feature_compatibility; + __le32 feature_incompat; + __le32 feature_ro_compat; + __le32 unique_id[4]; char volume_name[16]; char last_mounted_on[64]; - uint32_t compression_info; + __le32 compression_info; + uint8_t prealloc_blocks; + uint8_t prealloc_dir_blocks; + __le16 reserved_gdt_blocks; + uint8_t journal_uuid[16]; + __le32 journal_inode; + __le32 journal_dev; + __le32 last_orphan; + __le32 hash_seed[4]; + uint8_t default_hash_version; + uint8_t journal_backup_type; + __le16 descriptor_size; + __le32 default_mount_options; + __le32 first_meta_block_group; + __le32 mkfs_time; + __le32 journal_blocks[17]; + __le32 total_blocks_high; + __le32 reserved_blocks_high; + __le32 free_blocks_high; + __le16 min_extra_inode_size; + __le16 want_extra_inode_size; + __le32 flags; + __le16 raid_stride; + __le16 mmp_interval; + __le64 mmp_block; + __le32 raid_stripe_width; + uint8_t log2_groups_per_flex; + uint8_t checksum_type; }; struct ext2_block_group { - __u32 block_id; /* Blocks bitmap block */ - __u32 inode_id; /* Inodes bitmap block */ - __u32 inode_table_id; /* Inodes table block */ - __u16 free_blocks; /* Free blocks count */ - __u16 free_inodes; /* Free inodes count */ - __u16 used_dir_cnt; /* Directories count */ - __u16 bg_flags; - __u32 bg_reserved[2]; - __u16 bg_itable_unused; /* Unused inodes count */ - __u16 bg_checksum; /* crc16(s_uuid+grouo_num+group_desc)*/ + __le32 block_id; /* Blocks bitmap block */ + __le32 inode_id; /* Inodes bitmap block */ + __le32 inode_table_id; /* Inodes table block */ + __le16 free_blocks; /* Free blocks count */ + __le16 free_inodes; /* Free inodes count */ + __le16 used_dir_cnt; /* Directories count */ + __le16 bg_flags; + __le32 bg_exclude_bitmap; + __le16 bg_block_id_csum; + __le16 bg_inode_id_csum; + __le16 bg_itable_unused; /* Unused inodes count */ + __le16 bg_checksum; /* crc16(s_uuid+group_num+group_desc)*/ + /* following fields only exist if descriptor size is 64 */ + __le32 block_id_high; + __le32 inode_id_high; + __le32 inode_table_id_high; + __le16 free_blocks_high; + __le16 free_inodes_high; + __le16 used_dir_cnt_high; + __le16 bg_itable_unused_high; + __le32 bg_exclude_bitmap_high; + __le16 bg_block_id_csum_high; + __le16 bg_inode_id_csum_high; + __le32 bg_reserved; }; /* The ext2 inode. */ struct ext2_inode { - uint16_t mode; - uint16_t uid; - uint32_t size; - uint32_t atime; - uint32_t ctime; - uint32_t mtime; - uint32_t dtime; - uint16_t gid; - uint16_t nlinks; - uint32_t blockcnt; /* Blocks of 512 bytes!! */ - uint32_t flags; - uint32_t osd1; + __le16 mode; + __le16 uid; + __le32 size; + __le32 atime; + __le32 ctime; + __le32 mtime; + __le32 dtime; + __le16 gid; + __le16 nlinks; + __le32 blockcnt; /* Blocks of either 512 or block_size bytes */ + __le32 flags; + __le32 osd1; union { struct datablocks { - uint32_t dir_blocks[INDIRECT_BLOCKS]; - uint32_t indir_block; - uint32_t double_indir_block; - uint32_t triple_indir_block; + __le32 dir_blocks[INDIRECT_BLOCKS]; + __le32 indir_block; + __le32 double_indir_block; + __le32 triple_indir_block; } blocks; char symlink[60]; + char inline_data[60]; } b; - uint32_t version; - uint32_t acl; - uint32_t dir_acl; - uint32_t fragment_addr; - uint32_t osd2[3]; + __le32 version; + __le32 acl; + __le32 size_high; /* previously dir_acl, but never used */ + __le32 fragment_addr; + __le32 osd2[3]; }; /* The header of an ext2 directory entry. */ struct ext2_dirent { - uint32_t inode; - uint16_t direntlen; - uint8_t namelen; - uint8_t filetype; + __le32 inode; + __le16 direntlen; + __u8 namelen; + __u8 filetype; }; struct ext2fs_node { diff --git a/images/Makefile b/images/Makefile index 68876e5de9..adf950aa9c 100644 --- a/images/Makefile +++ b/images/Makefile @@ -111,6 +111,7 @@ include $(srctree)/images/Makefile.omap3 include $(srctree)/images/Makefile.rockchip include $(srctree)/images/Makefile.socfpga include $(srctree)/images/Makefile.tegra +include $(srctree)/images/Makefile.at91 targets += $(image-y) pbl.lds barebox.x barebox.z targets += $(patsubst %,%.pblx,$(pblx-y)) diff --git a/images/Makefile.at91 b/images/Makefile.at91 new file mode 100644 index 0000000000..686d5011ef --- /dev/null +++ b/images/Makefile.at91 @@ -0,0 +1,7 @@ +# +# barebox image generation Makefile for AT91 images +# + +pblx-$(MACH_AT91SAM9X5EK) += start_at91sam9x5ek +FILE_barebox-at91sam9x5ek.img = start_at91sam9x5ek.pblx +image-$(MACH_AT91SAM9X5EK) += barebox-at91sam9x5ek.img diff --git a/include/ata_drive.h b/include/ata_drive.h index 44073cb1b1..11685eef12 100644 --- a/include/ata_drive.h +++ b/include/ata_drive.h @@ -37,8 +37,10 @@ #define ATA_CMD_ID_ATA 0xEC #define ATA_CMD_READ 0x20 +#define ATA_CMD_PIO_READ_EXT 0x24 #define ATA_CMD_READ_EXT 0x25 #define ATA_CMD_WRITE 0x30 +#define ATA_CMD_PIO_WRITE_EXT 0x34 #define ATA_CMD_WRITE_EXT 0x35 /* drive's status flags */ @@ -140,6 +142,7 @@ struct ata_port { void *drvdata; struct block_device blk; uint16_t *id; + int lba48; int initialized; int probe; }; diff --git a/include/boot.h b/include/boot.h index a855cbe1e5..4f7612ab80 100644 --- a/include/boot.h +++ b/include/boot.h @@ -36,6 +36,8 @@ struct bootentry { int bootentries_add_entry(struct bootentries *entries, struct bootentry *entry); +int bootentry_register_provider(int (*fn)(struct bootentries *bootentries, const char *name)); + #define bootentries_for_each_entry(bootentries, entry) \ list_for_each_entry(entry, &bootentries->entries, list) diff --git a/include/bootchooser.h b/include/bootchooser.h index c948247722..7822c01459 100644 --- a/include/bootchooser.h +++ b/include/bootchooser.h @@ -13,14 +13,14 @@ int bootchooser_put(struct bootchooser *bootchooser); void bootchooser_info(struct bootchooser *bootchooser); +int bootchooser_boot(struct bootchooser *bc); + struct bootchooser_target *bootchooser_get_last_chosen(struct bootchooser *bootchooser); const char *bootchooser_target_name(struct bootchooser_target *target); struct bootchooser_target *bootchooser_target_by_name(struct bootchooser *bootchooser, const char *name); void bootchooser_target_force_boot(struct bootchooser_target *target); -int bootchooser_create_bootentry(struct bootentries *entries); - int bootchooser_target_set_attempts(struct bootchooser_target *target, int attempts); int bootchooser_target_set_priority(struct bootchooser_target *target, int priority); diff --git a/include/common.h b/include/common.h index 680a0affb6..dd7445e9b6 100644 --- a/include/common.h +++ b/include/common.h @@ -66,9 +66,6 @@ int readline (const char *prompt, char *buf, int len); /* common/memsize.c */ long get_ram_size (volatile long *, long); -/* $(CPU)/cpu.c */ -void __noreturn poweroff(void); - /* common/console.c */ int ctrlc (void); diff --git a/include/crypto/keystore.h b/include/crypto/keystore.h index 29915854b8..89d962628b 100644 --- a/include/crypto/keystore.h +++ b/include/crypto/keystore.h @@ -12,6 +12,7 @@ #ifdef CONFIG_CRYPTO_KEYSTORE int keystore_get_secret(const char *name, const u8 **secret, int *secret_len); int keystore_set_secret(const char *name, const u8 *secret, int secret_len); +void keystore_forget_secret(const char *name); #else static inline int keystore_get_secret(const char *name, const u8 **secret, int *secret_len) { @@ -21,6 +22,9 @@ static inline int keystore_set_secret(const char *name, const u8 *secret, int se { return 0; } +static inline void keystore_forget_secret(const char *name) +{ +} #endif #endif diff --git a/include/driver.h b/include/driver.h index 6abaaad8b4..52e06f7d62 100644 --- a/include/driver.h +++ b/include/driver.h @@ -459,6 +459,8 @@ struct cdev { u8 dos_partition_type; struct cdev *link; struct list_head link_entry, links; + struct list_head partition_entry, partitions; + struct cdev *master; }; int devfs_create(struct cdev *); @@ -481,8 +483,8 @@ int cdev_erase(struct cdev *cdev, loff_t count, loff_t offset); #define DEVFS_PARTITION_FIXED (1U << 0) #define DEVFS_PARTITION_READONLY (1U << 1) -#define DEVFS_IS_PARTITION (1 << 2) #define DEVFS_IS_CHARACTER_DEV (1 << 3) +#define DEVFS_PARTITION_FROM_TABLE (1 << 4) struct cdev *devfs_add_partition(const char *devname, loff_t offset, loff_t size, unsigned int flags, const char *name); diff --git a/include/globalvar.h b/include/globalvar.h index 80bc53e680..df43f1fe66 100644 --- a/include/globalvar.h +++ b/include/globalvar.h @@ -11,10 +11,6 @@ extern struct device_d global_device; #ifdef CONFIG_GLOBALVAR int globalvar_add_simple(const char *name, const char *value); -int globalvar_add(const char *name, - int (*set)(struct device_d *dev, struct param_d *p, const char *val), - const char *(*get)(struct device_d *, struct param_d *p), - unsigned long flags); void globalvar_remove(const char *name); char *globalvar_get_match(const char *match, const char *separator); void globalvar_set_match(const char *match, const char *val); @@ -80,14 +76,6 @@ static inline int globalvar_add_simple_ip(const char *name, return 0; } -static inline int globalvar_add(const char *name, - int (*set)(struct device_d *dev, struct param_d *p, const char *val), - const char *(*get)(struct device_d *, struct param_d *p), - unsigned long flags) -{ - return 0; -} - static inline void globalvar_remove(const char *name) {} static inline void globalvar_print(void) {} diff --git a/include/gui/image_renderer.h b/include/gui/image_renderer.h index e0b1eae258..bfdea1b14e 100644 --- a/include/gui/image_renderer.h +++ b/include/gui/image_renderer.h @@ -32,7 +32,7 @@ struct image_renderer { #ifdef CONFIG_IMAGE_RENDERER int image_renderer_register(struct image_renderer *ir); -void image_render_unregister(struct image_renderer *ir); +void image_renderer_unregister(struct image_renderer *ir); int image_renderer_image(struct screen *sc, struct surface *s, struct image *img); diff --git a/include/hab.h b/include/hab.h index 818d7ca1c5..fb7149ef53 100644 --- a/include/hab.h +++ b/include/hab.h @@ -41,4 +41,25 @@ static inline int imx25_hab_get_status(void) } #endif +#define SRK_HASH_SIZE 32 + +/* Force writing of key, even when a key is already written */ +#define IMX_SRK_HASH_FORCE (1 << 0) +/* Permanently write fuses, without this flag only the shadow registers + * are written. + */ +#define IMX_SRK_HASH_WRITE_PERMANENT (1 << 1) +/* When writing the super root key hash, also burn the write protection + * fuses so that the key hash can not be modified. + */ +#define IMX_SRK_HASH_WRITE_LOCK (1 << 2) + +bool imx_hab_srk_hash_valid(const void *buf); +int imx_hab_write_srk_hash(const void *buf, unsigned flags); +int imx_hab_write_srk_hash_hex(const char *srkhash, unsigned flags); +int imx_hab_write_srk_hash_file(const char *filename, unsigned flags); +int imx_hab_read_srk_hash(void *buf); +int imx_hab_lockdown_device(unsigned flags); +int imx_hab_device_locked_down(void); + #endif /* __HABV4_H */ diff --git a/include/led.h b/include/led.h index 000267cdc5..0ce857129d 100644 --- a/include/led.h +++ b/include/led.h @@ -12,6 +12,13 @@ struct led { char *name; int num; struct list_head list; + + int blink; + int flash; + unsigned int *blink_states; + int blink_nr_states; + int blink_next_state; + uint64_t blink_next_event; }; struct led *led_by_number(int no); @@ -25,6 +32,10 @@ static inline int led_get_number(struct led *led) int led_set_num(int num, unsigned int value); int led_set(struct led *led, unsigned int value); +int led_blink(struct led *led, unsigned int on_ms, unsigned int off_ms); +int led_blink_pattern(struct led *led, const unsigned int *pattern, + unsigned int pattern_len); +int led_flash(struct led *led, unsigned int duration_ms); int led_register(struct led *led); void led_unregister(struct led *led); void led_unregister(struct led *led); @@ -38,6 +49,7 @@ enum led_trigger { LED_TRIGGER_NET_TXRX, LED_TRIGGER_DEFAULT_ON, LED_TRIGGER_MAX, + LED_TRIGGER_INVALID = LED_TRIGGER_MAX, }; enum trigger_type { @@ -48,6 +60,7 @@ enum trigger_type { #ifdef CONFIG_LED_TRIGGERS int led_set_trigger(enum led_trigger trigger, struct led *led); +void led_trigger_disable(struct led *led); void led_trigger(enum led_trigger trigger, enum trigger_type); #else static inline int led_set_trigger(enum led_trigger trigger, struct led *led) @@ -60,7 +73,9 @@ static inline void led_trigger(enum led_trigger trigger, enum trigger_type type) } #endif -int led_get_trigger(enum led_trigger trigger); +void led_triggers_show_info(void); +const char *trigger_name(enum led_trigger trigger); +enum led_trigger trigger_by_name(const char *name); void led_of_parse_trigger(struct led *led, struct device_node *np); diff --git a/include/linux/clk.h b/include/linux/clk.h index a061398555..081a859729 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -324,16 +324,13 @@ struct clk *clk_register_composite(const char *name, struct device_node; struct of_phandle_args; +#if defined(CONFIG_COMMON_CLK_OF_PROVIDER) + #define CLK_OF_DECLARE(name, compat, fn) \ const struct of_device_id __clk_of_table_##name \ __attribute__ ((unused,section (".__clk_of_table"))) \ = { .compatible = compat, .data = fn } -#if defined(CONFIG_COMMON_CLK_OF_PROVIDER) -int of_clk_add_provider(struct device_node *np, - struct clk *(*clk_src_get)(struct of_phandle_args *args, - void *data), - void *data); void of_clk_del_provider(struct device_node *np); typedef int (*of_clk_init_cb_t)(struct device_node *); @@ -349,11 +346,27 @@ struct clk *of_clk_get(struct device_node *np, int index); struct clk *of_clk_get_by_name(struct device_node *np, const char *name); struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec); unsigned int of_clk_get_parent_count(struct device_node *np); -char *of_clk_get_parent_name(struct device_node *np, unsigned int index); int of_clk_parent_fill(struct device_node *np, const char **parents, unsigned int size); int of_clk_init(struct device_node *root, const struct of_device_id *matches); #else + + +/* + * Create a dummy variable to avoid 'unused function' + * warnings. Compiler should be smart enough to throw it out. + */ +#define CLK_OF_DECLARE(name, compat, fn) \ +static const struct of_device_id __clk_of_table_##name \ +__attribute__ ((unused)) = { .data = fn } + + +static inline struct clk * +of_clk_src_simple_get(struct of_phandle_args *clkspec, void *data) +{ + return ERR_PTR(-ENOENT); +} + static inline struct clk *of_clk_get(struct device_node *np, int index) { return ERR_PTR(-ENOENT); @@ -374,4 +387,10 @@ struct string_list; int clk_name_complete(struct string_list *sl, char *instr); +int of_clk_add_provider(struct device_node *np, + struct clk *(*clk_src_get)(struct of_phandle_args *args, + void *data), + void *data); +char *of_clk_get_parent_name(struct device_node *np, unsigned int index); + #endif diff --git a/include/linux/clk/at91_pmc.h b/include/linux/clk/at91_pmc.h new file mode 100644 index 0000000000..17f413bbbe --- /dev/null +++ b/include/linux/clk/at91_pmc.h @@ -0,0 +1,188 @@ +/* + * include/linux/clk/at91_pmc.h + * + * Copyright (C) 2005 Ivan Kokshaysky + * Copyright (C) SAN People + * + * Power Management Controller (PMC) - System peripherals registers. + * Based on AT91RM9200 datasheet revision E. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef AT91_PMC_H +#define AT91_PMC_H + +#define AT91_PMC_SCER 0x00 /* System Clock Enable Register */ +#define AT91_PMC_SCDR 0x04 /* System Clock Disable Register */ + +#define AT91_PMC_SCSR 0x08 /* System Clock Status Register */ +#define AT91_PMC_PCK (1 << 0) /* Processor Clock */ +#define AT91RM9200_PMC_UDP (1 << 1) /* USB Devcice Port Clock [AT91RM9200 only] */ +#define AT91RM9200_PMC_MCKUDP (1 << 2) /* USB Device Port Master Clock Automatic Disable on Suspend [AT91RM9200 only] */ +#define AT91RM9200_PMC_UHP (1 << 4) /* USB Host Port Clock [AT91RM9200 only] */ +#define AT91SAM926x_PMC_UHP (1 << 6) /* USB Host Port Clock [AT91SAM926x only] */ +#define AT91SAM926x_PMC_UDP (1 << 7) /* USB Devcice Port Clock [AT91SAM926x only] */ +#define AT91_PMC_PCK0 (1 << 8) /* Programmable Clock 0 */ +#define AT91_PMC_PCK1 (1 << 9) /* Programmable Clock 1 */ +#define AT91_PMC_PCK2 (1 << 10) /* Programmable Clock 2 */ +#define AT91_PMC_PCK3 (1 << 11) /* Programmable Clock 3 */ +#define AT91_PMC_PCK4 (1 << 12) /* Programmable Clock 4 [AT572D940HF only] */ +#define AT91_PMC_HCK0 (1 << 16) /* AHB Clock (USB host) [AT91SAM9261 only] */ +#define AT91_PMC_HCK1 (1 << 17) /* AHB Clock (LCD) [AT91SAM9261 only] */ + +#define AT91_PMC_PCER 0x10 /* Peripheral Clock Enable Register */ +#define AT91_PMC_PCDR 0x14 /* Peripheral Clock Disable Register */ +#define AT91_PMC_PCSR 0x18 /* Peripheral Clock Status Register */ + +#define AT91_CKGR_UCKR 0x1C /* UTMI Clock Register [some SAM9] */ +#define AT91_PMC_UPLLEN (1 << 16) /* UTMI PLL Enable */ +#define AT91_PMC_UPLLCOUNT (0xf << 20) /* UTMI PLL Start-up Time */ +#define AT91_PMC_BIASEN (1 << 24) /* UTMI BIAS Enable */ +#define AT91_PMC_BIASCOUNT (0xf << 28) /* UTMI BIAS Start-up Time */ + +#define AT91_CKGR_MOR 0x20 /* Main Oscillator Register [not on SAM9RL] */ +#define AT91_PMC_MOSCEN (1 << 0) /* Main Oscillator Enable */ +#define AT91_PMC_OSCBYPASS (1 << 1) /* Oscillator Bypass */ +#define AT91_PMC_MOSCRCEN (1 << 3) /* Main On-Chip RC Oscillator Enable [some SAM9] */ +#define AT91_PMC_OSCOUNT (0xff << 8) /* Main Oscillator Start-up Time */ +#define AT91_PMC_KEY (0x37 << 16) /* MOR Writing Key */ +#define AT91_PMC_MOSCSEL (1 << 24) /* Main Oscillator Selection [some SAM9] */ +#define AT91_PMC_CFDEN (1 << 25) /* Clock Failure Detector Enable [some SAM9] */ + +#define AT91_CKGR_MCFR 0x24 /* Main Clock Frequency Register */ +#define AT91_PMC_MAINF (0xffff << 0) /* Main Clock Frequency */ +#define AT91_PMC_MAINRDY (1 << 16) /* Main Clock Ready */ + +#define AT91_CKGR_PLLAR 0x28 /* PLL A Register */ +#define AT91_CKGR_PLLBR 0x2c /* PLL B Register */ +#define AT91_PMC_DIV (0xff << 0) /* Divider */ +#define AT91_PMC_PLLCOUNT (0x3f << 8) /* PLL Counter */ +#define AT91_PMC_OUT (3 << 14) /* PLL Clock Frequency Range */ +#define AT91_PMC_MUL (0x7ff << 16) /* PLL Multiplier */ +#define AT91_PMC_MUL_GET(n) ((n) >> 16 & 0x7ff) +#define AT91_PMC3_MUL (0x7f << 18) /* PLL Multiplier [SAMA5 only] */ +#define AT91_PMC3_MUL_GET(n) ((n) >> 18 & 0x7f) +#define AT91_PMC_USBDIV (3 << 28) /* USB Divisor (PLLB only) */ +#define AT91_PMC_USBDIV_1 (0 << 28) +#define AT91_PMC_USBDIV_2 (1 << 28) +#define AT91_PMC_USBDIV_4 (2 << 28) +#define AT91_PMC_USB96M (1 << 28) /* Divider by 2 Enable (PLLB only) */ + +#define AT91_PMC_MCKR 0x30 /* Master Clock Register */ +#define AT91_PMC_CSS (3 << 0) /* Master Clock Selection */ +#define AT91_PMC_CSS_SLOW (0 << 0) +#define AT91_PMC_CSS_MAIN (1 << 0) +#define AT91_PMC_CSS_PLLA (2 << 0) +#define AT91_PMC_CSS_PLLB (3 << 0) +#define AT91_PMC_CSS_UPLL (3 << 0) /* [some SAM9 only] */ +#define PMC_PRES_OFFSET 2 +#define AT91_PMC_PRES (7 << PMC_PRES_OFFSET) /* Master Clock Prescaler */ +#define AT91_PMC_PRES_1 (0 << PMC_PRES_OFFSET) +#define AT91_PMC_PRES_2 (1 << PMC_PRES_OFFSET) +#define AT91_PMC_PRES_4 (2 << PMC_PRES_OFFSET) +#define AT91_PMC_PRES_8 (3 << PMC_PRES_OFFSET) +#define AT91_PMC_PRES_16 (4 << PMC_PRES_OFFSET) +#define AT91_PMC_PRES_32 (5 << PMC_PRES_OFFSET) +#define AT91_PMC_PRES_64 (6 << PMC_PRES_OFFSET) +#define PMC_ALT_PRES_OFFSET 4 +#define AT91_PMC_ALT_PRES (7 << PMC_ALT_PRES_OFFSET) /* Master Clock Prescaler [alternate location] */ +#define AT91_PMC_ALT_PRES_1 (0 << PMC_ALT_PRES_OFFSET) +#define AT91_PMC_ALT_PRES_2 (1 << PMC_ALT_PRES_OFFSET) +#define AT91_PMC_ALT_PRES_4 (2 << PMC_ALT_PRES_OFFSET) +#define AT91_PMC_ALT_PRES_8 (3 << PMC_ALT_PRES_OFFSET) +#define AT91_PMC_ALT_PRES_16 (4 << PMC_ALT_PRES_OFFSET) +#define AT91_PMC_ALT_PRES_32 (5 << PMC_ALT_PRES_OFFSET) +#define AT91_PMC_ALT_PRES_64 (6 << PMC_ALT_PRES_OFFSET) +#define AT91_PMC_MDIV (3 << 8) /* Master Clock Division */ +#define AT91RM9200_PMC_MDIV_1 (0 << 8) /* [AT91RM9200 only] */ +#define AT91RM9200_PMC_MDIV_2 (1 << 8) +#define AT91RM9200_PMC_MDIV_3 (2 << 8) +#define AT91RM9200_PMC_MDIV_4 (3 << 8) +#define AT91SAM9_PMC_MDIV_1 (0 << 8) /* [SAM9 only] */ +#define AT91SAM9_PMC_MDIV_2 (1 << 8) +#define AT91SAM9_PMC_MDIV_4 (2 << 8) +#define AT91SAM9_PMC_MDIV_6 (3 << 8) /* [some SAM9 only] */ +#define AT91SAM9_PMC_MDIV_3 (3 << 8) /* [some SAM9 only] */ +#define AT91_PMC_PDIV (1 << 12) /* Processor Clock Division [some SAM9 only] */ +#define AT91_PMC_PDIV_1 (0 << 12) +#define AT91_PMC_PDIV_2 (1 << 12) +#define AT91_PMC_PLLADIV2 (1 << 12) /* PLLA divisor by 2 [some SAM9 only] */ +#define AT91_PMC_PLLADIV2_OFF (0 << 12) +#define AT91_PMC_PLLADIV2_ON (1 << 12) +#define AT91_PMC_H32MXDIV BIT(24) + +#define AT91_PMC_USB 0x38 /* USB Clock Register [some SAM9 only] */ +#define AT91_PMC_USBS (0x1 << 0) /* USB OHCI Input clock selection */ +#define AT91_PMC_USBS_PLLA (0 << 0) +#define AT91_PMC_USBS_UPLL (1 << 0) +#define AT91_PMC_USBS_PLLB (1 << 0) /* [AT91SAMN12 only] */ +#define AT91_PMC_OHCIUSBDIV (0xF << 8) /* Divider for USB OHCI Clock */ +#define AT91_PMC_OHCIUSBDIV_1 (0x0 << 8) +#define AT91_PMC_OHCIUSBDIV_2 (0x1 << 8) + +#define AT91_PMC_SMD 0x3c /* Soft Modem Clock Register [some SAM9 only] */ +#define AT91_PMC_SMDS (0x1 << 0) /* SMD input clock selection */ +#define AT91_PMC_SMD_DIV (0x1f << 8) /* SMD input clock divider */ +#define AT91_PMC_SMDDIV(n) (((n) << 8) & AT91_PMC_SMD_DIV) + +#define AT91_PMC_PCKR(n) (0x40 + ((n) * 4)) /* Programmable Clock 0-N Registers */ +#define AT91_PMC_ALT_PCKR_CSS (0x7 << 0) /* Programmable Clock Source Selection [alternate length] */ +#define AT91_PMC_CSS_MASTER (4 << 0) /* [some SAM9 only] */ +#define AT91_PMC_CSSMCK (0x1 << 8) /* CSS or Master Clock Selection */ +#define AT91_PMC_CSSMCK_CSS (0 << 8) +#define AT91_PMC_CSSMCK_MCK (1 << 8) + +#define AT91_PMC_IER 0x60 /* Interrupt Enable Register */ +#define AT91_PMC_IDR 0x64 /* Interrupt Disable Register */ +#define AT91_PMC_SR 0x68 /* Status Register */ +#define AT91_PMC_MOSCS (1 << 0) /* MOSCS Flag */ +#define AT91_PMC_LOCKA (1 << 1) /* PLLA Lock */ +#define AT91_PMC_LOCKB (1 << 2) /* PLLB Lock */ +#define AT91_PMC_MCKRDY (1 << 3) /* Master Clock */ +#define AT91_PMC_LOCKU (1 << 6) /* UPLL Lock [some SAM9] */ +#define AT91_PMC_OSCSEL (1 << 7) /* Slow Oscillator Selection [some SAM9] */ +#define AT91_PMC_PCK0RDY (1 << 8) /* Programmable Clock 0 */ +#define AT91_PMC_PCK1RDY (1 << 9) /* Programmable Clock 1 */ +#define AT91_PMC_PCK2RDY (1 << 10) /* Programmable Clock 2 */ +#define AT91_PMC_PCK3RDY (1 << 11) /* Programmable Clock 3 */ +#define AT91_PMC_MOSCSELS (1 << 16) /* Main Oscillator Selection [some SAM9] */ +#define AT91_PMC_MOSCRCS (1 << 17) /* Main On-Chip RC [some SAM9] */ +#define AT91_PMC_CFDEV (1 << 18) /* Clock Failure Detector Event [some SAM9] */ +#define AT91_PMC_GCKRDY (1 << 24) /* Generated Clocks */ +#define AT91_PMC_IMR 0x6c /* Interrupt Mask Register */ + +#define AT91_PMC_PLLICPR 0x80 /* PLL Charge Pump Current Register */ + +#define AT91_PMC_PROT 0xe4 /* Write Protect Mode Register [some SAM9] */ +#define AT91_PMC_WPEN (0x1 << 0) /* Write Protect Enable */ +#define AT91_PMC_WPKEY (0xffffff << 8) /* Write Protect Key */ +#define AT91_PMC_PROTKEY (0x504d43 << 8) /* Activation Code */ + +#define AT91_PMC_WPSR 0xe8 /* Write Protect Status Register [some SAM9] */ +#define AT91_PMC_WPVS (0x1 << 0) /* Write Protect Violation Status */ +#define AT91_PMC_WPVSRC (0xffff << 8) /* Write Protect Violation Source */ + +#define AT91_PMC_PCER1 0x100 /* Peripheral Clock Enable Register 1 [SAMA5 only]*/ +#define AT91_PMC_PCDR1 0x104 /* Peripheral Clock Enable Register 1 */ +#define AT91_PMC_PCSR1 0x108 /* Peripheral Clock Enable Register 1 */ + +#define AT91_PMC_PCR 0x10c /* Peripheral Control Register [some SAM9 and SAMA5] */ +#define AT91_PMC_PCR_PID_MASK 0x3f +#define AT91_PMC_PCR_GCKCSS_OFFSET 8 +#define AT91_PMC_PCR_GCKCSS_MASK (0x7 << AT91_PMC_PCR_GCKCSS_OFFSET) +#define AT91_PMC_PCR_GCKCSS(n) ((n) << AT91_PMC_PCR_GCKCSS_OFFSET) /* GCK Clock Source Selection */ +#define AT91_PMC_PCR_CMD (0x1 << 12) /* Command (read=0, write=1) */ +#define AT91_PMC_PCR_DIV_OFFSET 16 +#define AT91_PMC_PCR_DIV_MASK (0x3 << AT91_PMC_PCR_DIV_OFFSET) +#define AT91_PMC_PCR_DIV(n) ((n) << AT91_PMC_PCR_DIV_OFFSET) /* Divisor Value */ +#define AT91_PMC_PCR_GCKDIV_OFFSET 20 +#define AT91_PMC_PCR_GCKDIV_MASK (0xff << AT91_PMC_PCR_GCKDIV_OFFSET) +#define AT91_PMC_PCR_GCKDIV(n) ((n) << AT91_PMC_PCR_GCKDIV_OFFSET) /* Generated Clock Divisor Value */ +#define AT91_PMC_PCR_EN (0x1 << 28) /* Enable */ +#define AT91_PMC_PCR_GCKEN (0x1 << 29) /* GCK Enable */ + +#endif diff --git a/include/linux/hw_random.h b/include/linux/hw_random.h new file mode 100644 index 0000000000..bae442166c --- /dev/null +++ b/include/linux/hw_random.h @@ -0,0 +1,47 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef LINUX_HWRANDOM_H_ +#define LINUX_HWRANDOM_H_ + +#include <linux/list.h> + +/** + * struct hwrng - Hardware Random Number Generator driver + * @name: Unique RNG name. + * @init: Initialization callback (can be NULL). + * @read: New API. drivers can fill up to max bytes of data + * into the buffer. The buffer is aligned for any type. + */ +struct hwrng { + const char *name; + int (*init)(struct hwrng *rng); + int (*read)(struct hwrng *rng, void *data, size_t max, bool wait); + + struct list_head list; + + struct cdev cdev; + struct device_d *dev; + void *buf; +}; + +/* Register a new Hardware Random Number Generator driver. */ +int hwrng_register(struct device_d *dev, struct hwrng *rng); +int hwrng_get_data(struct hwrng *rng, void *buffer, size_t size, int wait); + +#ifdef CONFIG_HWRNG +struct hwrng *hwrng_get_first(void); +#else +static inline struct hwrng *hwrng_get_first(void) { return ERR_PTR(-ENODEV); }; +#endif + +#endif /* LINUX_HWRANDOM_H_ */ diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index f93fac00f2..fa35c7ef39 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -228,6 +228,7 @@ struct mtd_info { struct list_head partitions_entry; char *of_path; + unsigned int of_binding; }; int mtd_erase(struct mtd_info *mtd, struct erase_info *instr); diff --git a/include/linux/nvmem-consumer.h b/include/linux/nvmem-consumer.h new file mode 100644 index 0000000000..606cadb498 --- /dev/null +++ b/include/linux/nvmem-consumer.h @@ -0,0 +1,94 @@ +/* + * nvmem framework consumer. + * + * Copyright (C) 2015 Srinivas Kandagatla <srinivas.kandagatla@linaro.org> + * Copyright (C) 2013 Maxime Ripard <maxime.ripard@free-electrons.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef _LINUX_NVMEM_CONSUMER_H +#define _LINUX_NVMEM_CONSUMER_H + +struct device_d; +struct device_node; +/* consumer cookie */ +struct nvmem_cell; +struct nvmem_device; + +struct nvmem_cell_info { + const char *name; + unsigned int offset; + unsigned int bytes; + unsigned int bit_offset; + unsigned int nbits; +}; + +#if IS_ENABLED(CONFIG_NVMEM) + +/* Cell based interface */ +struct nvmem_cell *nvmem_cell_get(struct device_d *dev, const char *name); +void nvmem_cell_put(struct nvmem_cell *cell); +void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len); +int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len); + +/* direct nvmem device read/write interface */ +struct nvmem_device *nvmem_device_get(struct device_d *dev, const char *name); +void nvmem_device_put(struct nvmem_device *nvmem); + +#else + +static inline struct nvmem_cell *nvmem_cell_get(struct device_d *dev, + const char *name) +{ + return ERR_PTR(-ENOSYS); +} + +static inline void nvmem_cell_put(struct nvmem_cell *cell) +{ +} + +static inline char *nvmem_cell_read(struct nvmem_cell *cell, size_t *len) +{ + return ERR_PTR(-ENOSYS); +} + +static inline int nvmem_cell_write(struct nvmem_cell *cell, + const char *buf, size_t len) +{ + return -ENOSYS; +} + +static inline struct nvmem_device *nvmem_device_get(struct device_d *dev, + const char *name) +{ + return ERR_PTR(-ENOSYS); +} + +static inline void nvmem_device_put(struct nvmem_device *nvmem) +{ +} +#endif /* CONFIG_NVMEM */ + +#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OFTREE) +struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, + const char *name); +struct nvmem_device *of_nvmem_device_get(struct device_node *np, + const char *name); +#else +static inline struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, + const char *name) +{ + return ERR_PTR(-ENOSYS); +} + +static inline struct nvmem_device *of_nvmem_device_get(struct device_node *np, + const char *name) +{ + return ERR_PTR(-ENOSYS); +} +#endif /* CONFIG_NVMEM && CONFIG_OF */ + +#endif /* ifndef _LINUX_NVMEM_CONSUMER_H */ diff --git a/include/linux/nvmem-provider.h b/include/linux/nvmem-provider.h new file mode 100644 index 0000000000..6ef5ea6854 --- /dev/null +++ b/include/linux/nvmem-provider.h @@ -0,0 +1,49 @@ +/* + * nvmem framework provider. + * + * Copyright (C) 2015 Srinivas Kandagatla <srinivas.kandagatla@linaro.org> + * Copyright (C) 2013 Maxime Ripard <maxime.ripard@free-electrons.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef _LINUX_NVMEM_PROVIDER_H +#define _LINUX_NVMEM_PROVIDER_H + +#include <common.h> +#include <linux/types.h> + +struct nvmem_device; + +struct nvmem_bus { + int (*write)(struct device_d *dev, const int reg, const void *val, + int val_size); + int (*read)(struct device_d *dev, const int reg, void *val, + int val_size); +}; + +struct nvmem_config { + struct device_d *dev; + const char *name; + bool read_only; + int stride; + int word_size; + int size; + const struct nvmem_bus *bus; +}; + +#if IS_ENABLED(CONFIG_NVMEM) + +struct nvmem_device *nvmem_register(const struct nvmem_config *cfg); + +#else + +static inline struct nvmem_device *nvmem_register(const struct nvmem_config *c) +{ + return ERR_PTR(-ENOSYS); +} + +#endif /* CONFIG_NVMEM */ +#endif /* ifndef _LINUX_NVMEM_PROVIDER_H */ diff --git a/include/mci.h b/include/mci.h index cc4712cfad..781e6e0f36 100644 --- a/include/mci.h +++ b/include/mci.h @@ -480,6 +480,7 @@ struct mci { int mci_register(struct mci_host*); void mci_of_parse(struct mci_host *host); +void mci_of_parse_node(struct mci_host *host, struct device_node *np); int mci_detect_card(struct mci_host *); int mci_send_ext_csd(struct mci *mci, char *ext_csd); int mci_switch(struct mci *mci, unsigned set, unsigned index, diff --git a/include/of.h b/include/of.h index e3bb452b86..0ba73f197f 100644 --- a/include/of.h +++ b/include/of.h @@ -205,6 +205,8 @@ extern int of_property_write_u32_array(struct device_node *np, extern int of_property_write_u64_array(struct device_node *np, const char *propname, const u64 *values, size_t sz); +extern int of_property_write_string(struct device_node *np, const char *propname, + const char *value); extern struct device_node *of_parse_phandle(const struct device_node *np, const char *phandle_name, @@ -238,6 +240,7 @@ extern struct device_d *of_device_enable_and_register_by_name(const char *name); struct cdev *of_parse_partition(struct cdev *cdev, struct device_node *node); int of_parse_partitions(struct cdev *cdev, struct device_node *node); +int of_partitions_register_fixup(struct cdev *cdev); int of_device_is_stdout_path(struct device_d *dev); const char *of_get_model(void); void *of_flatten_dtb(struct device_node *node); @@ -248,6 +251,7 @@ struct device_d *of_find_device_by_node_path(const char *path); #define OF_FIND_PATH_FLAGS_BB 1 /* return .bb device if available */ int of_find_path(struct device_node *node, const char *propname, char **outpath, unsigned flags); int of_find_path_by_node(struct device_node *node, char **outpath, unsigned flags); +struct device_node *of_find_node_by_devpath(struct device_node *root, const char *path); int of_register_fixup(int (*fixup)(struct device_node *, void *), void *context); int of_unregister_fixup(int (*fixup)(struct device_node *, void *), void *context); int of_register_set_status_fixup(const char *node, bool status); @@ -262,6 +266,11 @@ static inline int of_parse_partitions(struct cdev *cdev, return -EINVAL; } +static inline int of_partitions_register_fixup(struct cdev *cdev) +{ + return -ENOSYS; +} + static inline int of_device_is_stdout_path(struct device_d *dev) { return 0; @@ -466,6 +475,12 @@ static inline int of_property_write_u64_array(struct device_node *np, return -ENOSYS; } +static inline int of_property_write_string(struct device_node *np, const char *propname, + const char *value) +{ + return -ENOSYS; +} + static inline struct device_node *of_parse_phandle(const struct device_node *np, const char *phandle_name, int index) { diff --git a/include/param.h b/include/param.h index f5f82ee70c..f9f3398cae 100644 --- a/include/param.h +++ b/include/param.h @@ -6,7 +6,6 @@ #include <linux/list.h> #define PARAM_FLAG_RO (1 << 0) -#define PARAM_GLOBALVAR_UNQUALIFIED (1 << 1) struct device_d; typedef uint32_t IPaddr_t; diff --git a/include/poweroff.h b/include/poweroff.h new file mode 100644 index 0000000000..ae9557db5d --- /dev/null +++ b/include/poweroff.h @@ -0,0 +1,21 @@ +#ifndef __INCLUDE_POWEROFF_H +#define __INCLUDE_POWEROFF_H + +void __noreturn poweroff_machine(void); + +struct poweroff_handler { + void (*poweroff)(struct poweroff_handler *); + int priority; + const char *name; + struct list_head list; +}; + +int poweroff_handler_register(struct poweroff_handler *); +int poweroff_handler_register_fn(void (*poweroff_fn)(struct poweroff_handler *)); + +#define POWEROFF_DEFAULT_PRIORITY 100 +#define POWEROFF_DEFAULT_NAME "default" + +unsigned int of_get_poweroff_priority(struct device_node *node); + +#endif /* __INCLUDE_POWEROFF_H */ diff --git a/include/state.h b/include/state.h index bc9a574093..63164f92e5 100644 --- a/include/state.h +++ b/include/state.h @@ -18,6 +18,7 @@ struct state *state_by_name(const char *name); struct state *state_by_node(const struct device_node *node); int state_get_name(const struct state *state, char const **name); +int state_load_no_auth(struct state *state); int state_load(struct state *state); int state_save(struct state *state); void state_info(void); diff --git a/include/stdlib.h b/include/stdlib.h index f3185069f3..ee3f229968 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -13,6 +13,7 @@ void srand(unsigned int seed); /* fill a buffer with pseudo-random data */ void get_random_bytes(void *buf, int len); +int get_crypto_bytes(void *buf, int len); static inline u32 random32(void) { diff --git a/lib/Kconfig b/lib/Kconfig index 8a94ce09fb..9562b1b8c2 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -90,6 +90,15 @@ config RATP transferring packets over serial links described in RFC916. This implementation is used for controlling barebox over serial ports. +config ALLOW_PRNG_FALLBACK + bool "Allow fallback to PRNG if HWRNG not available." + help + WARNING: it is not secure!! + + get_crypto_bytes() users like cmd_password relay on HWRNG. If HWRNG is not + available and this option is disabled, cmd_password will fail. + Enable it on your own risk. + source lib/gui/Kconfig source lib/fonts/Kconfig diff --git a/lib/gui/bmp.c b/lib/gui/bmp.c index 143aa28055..8bffc70a42 100644 --- a/lib/gui/bmp.c +++ b/lib/gui/bmp.c @@ -9,7 +9,7 @@ #include <gui/image_renderer.h> #include <asm/unaligned.h> -struct image *bmp_open(char *inbuf, int insize) +static struct image *bmp_open(char *inbuf, int insize) { struct image *img = calloc(1, sizeof(struct image)); struct bmp_image *bmp = (struct bmp_image*)inbuf; @@ -30,7 +30,7 @@ struct image *bmp_open(char *inbuf, int insize) return img; } -void bmp_close(struct image *img) +static void bmp_close(struct image *img) { free(img->data); } diff --git a/lib/random.c b/lib/random.c index 210fea9946..759271f0c8 100644 --- a/lib/random.c +++ b/lib/random.c @@ -1,5 +1,6 @@ #include <common.h> #include <stdlib.h> +#include <linux/hw_random.h> static unsigned int random_seed; @@ -18,6 +19,11 @@ void srand(unsigned int seed) random_seed = seed; } +/** + * get_random_bytes - get pseudo random numbers. + * This interface can be good enough to generate MAC address + * or use for NAND test. + */ void get_random_bytes(void *_buf, int len) { char *buf = _buf; @@ -25,3 +31,49 @@ void get_random_bytes(void *_buf, int len) while (len--) *buf++ = rand() % 256; } + +/** + * get_crypto_bytes - get random numbers suitable for cryptographic needs. + */ +static int _get_crypto_bytes(void *buf, int len) +{ + struct hwrng *rng; + + rng = hwrng_get_first(); + if (IS_ERR(rng)) + return PTR_ERR(rng); + + while (len) { + int bytes = hwrng_get_data(rng, buf, len, true); + if (!bytes) + return -ENOMEDIUM; + + if (bytes < 0) + return bytes; + + len -= bytes; + buf = buf + bytes; + } + + return 0; +} + +int get_crypto_bytes(void *buf, int len) +{ + int err; + + err = _get_crypto_bytes(buf, len); + if (!err) + return 0; + + if (!IS_ENABLED(CONFIG_ALLOW_PRNG_FALLBACK)) { + pr_err("error: no HWRNG available!\n"); + return err; + } + + pr_warn("warning: falling back to Pseudo RNG source!\n"); + + get_random_bytes(buf, len); + + return 0; +} diff --git a/scripts/tags.sh b/scripts/tags.sh index 79fdafb0d2..8ae44642a2 100755 --- a/scripts/tags.sh +++ b/scripts/tags.sh @@ -103,11 +103,6 @@ all_kconfigs() find_other_sources 'Kconfig*' } -all_defconfigs() -{ - find_sources $ALLSOURCE_ARCHS "defconfig" -} - docscope() { (echo \-k; echo \-q; all_sources) > cscope.files @@ -153,22 +148,14 @@ exuberant() --regex-c++='/CLEARPAGEFLAG_NOOP\(([^,)]*).*/ClearPage\1/' \ --regex-c++='/__CLEARPAGEFLAG_NOOP\(([^,)]*).*/__ClearPage\1/' \ --regex-c++='/TESTCLEARFLAG_FALSE\(([^,)]*).*/TestClearPage\1/' \ - --regex-c++='/__TESTCLEARFLAG_FALSE\(([^,)]*).*/__TestClearPage\1/' \ --regex-c++='/_PE\(([^,)]*).*/PEVENT_ERRNO__\1/' \ --regex-c='/PCI_OP_READ\(([a-z]*[a-z]).*[1-4]\)/pci_bus_read_config_\1/' \ --regex-c='/PCI_OP_WRITE\(([a-z]*[a-z]).*[1-4]\)/pci_bus_write_config_\1/' all_kconfigs | xargs $1 -a \ --langdef=kconfig --language-force=kconfig \ - --regex-kconfig='/^[[:blank:]]*(menu|)config[[:blank:]]+([[:alnum:]_]+)/\2/' - - all_kconfigs | xargs $1 -a \ - --langdef=kconfig --language-force=kconfig \ + --regex-kconfig='/^[[:blank:]]*(menu|)config[[:blank:]]+([[:alnum:]_]+)/\2/' \ --regex-kconfig='/^[[:blank:]]*(menu|)config[[:blank:]]+([[:alnum:]_]+)/CONFIG_\2/' - - all_defconfigs | xargs -r $1 -a \ - --langdef=dotconfig --language-force=dotconfig \ - --regex-dotconfig='/^#?[[:blank:]]*(CONFIG_[[:alnum:]_]+)/\1/' } emacs() @@ -198,19 +185,13 @@ emacs() --regex='/CLEARPAGEFLAG_NOOP\(([^,)]*).*/ClearPage\1/' \ --regex='/__CLEARPAGEFLAG_NOOP\(([^,)]*).*/__ClearPage\1/' \ --regex='/TESTCLEARFLAG_FALSE\(([^,)]*).*/TestClearPage\1/' \ - --regex='/__TESTCLEARFLAG_FALSE\(([^,)]*).*/__TestClearPage\1/' \ --regex='/_PE\(([^,)]*).*/PEVENT_ERRNO__\1/' \ --regex='/PCI_OP_READ\(([a-z]*[a-z]).*[1-4]\)/pci_bus_read_config_\1/' \ --regex='/PCI_OP_WRITE\(([a-z]*[a-z]).*[1-4]\)/pci_bus_write_config_\1/' all_kconfigs | xargs $1 -a \ - --regex='/^[ \t]*\(\(menu\)*config\)[ \t]+\([a-zA-Z0-9_]+\)/\3/' - - all_kconfigs | xargs $1 -a \ + --regex='/^[ \t]*\(\(menu\)*config\)[ \t]+\([a-zA-Z0-9_]+\)/\3/' \ --regex='/^[ \t]*\(\(menu\)*config\)[ \t]+\([a-zA-Z0-9_]+\)/CONFIG_\3/' - - all_defconfigs | xargs -r $1 -a \ - --regex='/^#?[ \t]?\(CONFIG_[a-zA-Z0-9_]+\)/\1/' } xtags() |