From 458a070076dc920a7106f0c8f0cfa880503ce498 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 30 Aug 2015 16:55:12 -0600 Subject: pinctrl: Add help text to Kconfig The pinctrl Kconfig options should have help messages. Add this to a few options. Signed-off-by: Simon Glass --- drivers/pinctrl/Kconfig | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 30b8e452ef..918a8592bb 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -47,7 +47,10 @@ config PINMUX default y help This option enables pin multiplexing through the generic pinctrl - framework. + framework. Most SoCs have their own own multiplexing arrangement + where a single pin can be used for several functions. An SoC pinctrl + driver allows the required function to be selected for each pin. + The driver is typically controlled by the device tree. config PINCONF bool "Support pin configuration controllers" @@ -86,6 +89,12 @@ config SPL_PINMUX help This option is an SPL-variant of the PINMUX option. See the help of PINMUX for details. + The pinctrl subsystem can add a substantial overhead to the SPL + image since it typically requires quite a few tables either in the + driver or in the device tree. If this is acceptable and you need + to adjust pin multiplexing in SPL in order to boot into U-Boot, + enable this option. You will need to enable device tree in SPL + for this to work. config SPL_PINCONF bool "Support pin configuration controllers in SPL" -- cgit v1.2.1 From c5acf4a2b3c6fd49aa0bc02db50f4b625b2e2991 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 30 Aug 2015 16:55:13 -0600 Subject: pinctrl: Add the concept of peripheral IDs My original pinctrl patch operating using a peripheral ID enum. This was shared between pinmux and clock and provides an easy way to specify a device that needs to be controlled, even it is does not (yet) have a driver within driver model. Masahiro's new simple pinctrl gets around this by providing a set_state_simple() pinctrl method. By passing a device to that call the peripheral ID becomes unnecessary. If the driver needs it, it can calculate it itself and use it internally. However this does not solve the problem for peripheral clocks. The 'pure' solution would be to pass a driver to the clock uclass also. But this requires that all devices should have a driver, and a struct udevide. Also a key optimisation of the clock uclass is allowing a peripheral clock to be set even when there is no device for that clock. There may be a better way to achive the same goal, but for now it seems expedient to add in peripheral ID to the pinctrl uclass. Two methods are added - one to get the peripheral ID and one to select it. The existing set_state_simple() is effectively the union of these. Signed-off-by: Simon Glass --- drivers/pinctrl/pinctrl-uclass.c | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-uclass.c b/drivers/pinctrl/pinctrl-uclass.c index d96c201e83..58001ef572 100644 --- a/drivers/pinctrl/pinctrl-uclass.c +++ b/drivers/pinctrl/pinctrl-uclass.c @@ -11,6 +11,7 @@ #include #include #include +#include #include DECLARE_GLOBAL_DATA_PTR; @@ -159,7 +160,8 @@ static int pinctrl_select_state_full(struct udevice *dev, const char *statename) static int pinconfig_post_bind(struct udevice *dev) { - return 0; + /* Scan the bus for devices */ + return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); } #endif @@ -205,6 +207,31 @@ int pinctrl_select_state(struct udevice *dev, const char *statename) return 0; } +int pinctrl_request(struct udevice *dev, int func, int flags) +{ + struct pinctrl_ops *ops = pinctrl_get_ops(dev); + + if (!ops->request) + return -ENOSYS; + + return ops->request(dev, func, flags); +} + +int pinctrl_request_noflags(struct udevice *dev, int func) +{ + return pinctrl_request(dev, func, 0); +} + +int pinctrl_get_periph_id(struct udevice *dev, struct udevice *periph) +{ + struct pinctrl_ops *ops = pinctrl_get_ops(dev); + + if (!ops->get_periph_id) + return -ENOSYS; + + return ops->get_periph_id(dev, periph); +} + /** * pinconfig_post-bind() - post binding for PINCTRL uclass * Recursively bind child nodes as pinconfig devices in case of full pinctrl. @@ -222,15 +249,10 @@ static int pinctrl_post_bind(struct udevice *dev) } /* - * If set_state callback is set, we assume this pinctrl driver is the - * full implementation. In this case, its child nodes should be bound - * so that peripheral devices can easily search in parent devices - * during later DT-parsing. + * The pinctrl driver child nodes should be bound so that peripheral + * devices can easily search in parent devices during later DT-parsing. */ - if (ops->set_state) - return pinconfig_post_bind(dev); - - return 0; + return pinconfig_post_bind(dev); } UCLASS_DRIVER(pinctrl) = { -- cgit v1.2.1 From 6a436c9182a90551739a5b7b3f44254234056915 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 30 Aug 2015 16:55:14 -0600 Subject: dm: led: Tidy up SPL options for the led and led-gpio At present SPL does not have its own option. But these features can increase SPL code size. Adjust the Kconfig and Makefile so that separate a SPL option can be selected. Signed-off-by: Simon Glass --- drivers/led/Kconfig | 9 ++++++++- drivers/led/Makefile | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/led/Kconfig b/drivers/led/Kconfig index 2987337219..fe74403460 100644 --- a/drivers/led/Kconfig +++ b/drivers/led/Kconfig @@ -11,7 +11,7 @@ config LED config SPL_LED bool "Enable LED support in SPL" - depends on LED + depends on SPL && SPL_DM help The LED subsystem adds a small amount of overhead to the image. If this is acceptable and you have a need to use LEDs in SPL, @@ -27,4 +27,11 @@ config LED_GPIO The GPIO driver must used driver model. LEDs are configured using the device tree. +config SPL_LED_GPIO + bool "LED support for GPIO-connected LEDs in SPL" + depends on SPL_LED && DM_GPIO + help + This option is an SPL-variant of the LED_GPIO option. + See the help of LED_GPIO for details. + endmenu diff --git a/drivers/led/Makefile b/drivers/led/Makefile index 990129e08d..02367fdacb 100644 --- a/drivers/led/Makefile +++ b/drivers/led/Makefile @@ -5,5 +5,5 @@ # SPDX-License-Identifier: GPL-2.0+ # -obj-$(CONFIG_LED) += led-uclass.o -obj-$(CONFIG_LED_GPIO) += led_gpio.o +obj-y += led-uclass.o +obj-$(CONFIG_$(SPL_)LED_GPIO) += led_gpio.o -- cgit v1.2.1 From e3563f2ec768fb989149362ca0c6ca4a27513924 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 30 Aug 2015 16:55:15 -0600 Subject: mmc: Support bypass mode with the get_mmc_clk() method Some SoCs want to adjust the input clock to the DWMMC block as a way of controlling the MMC bus clock. Update the get_mmc_clk() method to support this. Signed-off-by: Simon Glass Acked-by: Jaehoon Chung --- drivers/mmc/dw_mmc.c | 2 +- drivers/mmc/exynos_dw_mmc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index 77b87e0365..1117fedefe 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -266,7 +266,7 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) * host->bus_hz should be set by user. */ if (host->get_mmc_clk) - sclk = host->get_mmc_clk(host); + sclk = host->get_mmc_clk(host, freq); else if (host->bus_hz) sclk = host->bus_hz; else { diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index cde2ba7118..863bbb3f64 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -39,7 +39,7 @@ static void exynos_dwmci_clksel(struct dwmci_host *host) dwmci_writel(host, DWMCI_CLKSEL, priv->sdr_timing); } -unsigned int exynos_dwmci_get_clk(struct dwmci_host *host) +unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, uint freq) { unsigned long sclk; int8_t clk_div; -- cgit v1.2.1 From 3346c87625b8a736af5636a0bd1be89f781eb5f2 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 30 Aug 2015 16:55:16 -0600 Subject: dm: Improve handling of a missing uclass When a uclass definition is missing, no drivers in that uclass can operate. This can happen if a board has a strange collection of options (e.g. the driver is enabled but the uclass is not). Unfortunately this is very confusing at present. Starting up driver model results in a -ENOENT error, which is pretty generic. Quite a big of digging is needed to get to the root cause. To help with this, change the error to a very strange one with no other users in U-Boot. Also add a debug message. Signed-off-by: Simon Glass --- drivers/core/device.c | 4 +++- drivers/core/uclass.c | 7 ++++++- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/core/device.c b/drivers/core/device.c index a6cd93698f..0ccd443f25 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -39,8 +39,10 @@ int device_bind(struct udevice *parent, const struct driver *drv, return -EINVAL; ret = uclass_get(drv->id, &uc); - if (ret) + if (ret) { + debug("Missing uclass for driver %s\n", drv->name); return ret; + } dev = calloc(1, sizeof(struct udevice)); if (!dev) diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c index f63ff599a6..e800c28653 100644 --- a/drivers/core/uclass.c +++ b/drivers/core/uclass.c @@ -58,7 +58,12 @@ static int uclass_add(enum uclass_id id, struct uclass **ucp) if (!uc_drv) { debug("Cannot find uclass for id %d: please add the UCLASS_DRIVER() declaration for this UCLASS_... id\n", id); - return -ENOENT; + /* + * Use a strange error to make this case easier to find. When + * a uclass is not available it can prevent driver model from + * starting up and this failure is otherwise hard to debug. + */ + return -EPFNOSUPPORT; } uc = calloc(1, sizeof(*uc)); if (!uc) -- cgit v1.2.1 From bc7b2f431d5e5b48c009007bdf22d2f2d8aed4c4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 30 Aug 2015 16:55:17 -0600 Subject: dm: Provide better debugging when a device fails to bind All devices should bind without error. But when they don't, they can cause driver model init to fail. A real situation where this can happen is when there is a missing uclass. Add a debug() call to dm_scan_fdt_node to make this easier to track. Signed-off-by: Simon Glass --- drivers/core/root.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/core/root.c b/drivers/core/root.c index 78ab00c7bf..bdb394a9ae 100644 --- a/drivers/core/root.c +++ b/drivers/core/root.c @@ -162,8 +162,11 @@ int dm_scan_fdt_node(struct udevice *parent, const void *blob, int offset, continue; } err = lists_bind_fdt(parent, blob, offset, NULL); - if (err && !ret) + if (err && !ret) { ret = err; + debug("%s: ret=%d\n", fdt_get_name(blob, offset, NULL), + ret); + } } if (ret) -- cgit v1.2.1 From 765a1b1eb397f613a1d522224221d1dd4a812888 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 30 Aug 2015 16:55:19 -0600 Subject: rockchip: Add serial support Add support for the Rockchip serial device using the ns16550 driver. This uses driver model and device tree for both SPL and U-Boot proper. Signed-off-by: Simon Glass --- drivers/serial/Kconfig | 9 +++++++++ drivers/serial/Makefile | 1 + drivers/serial/serial_rockchip.c | 43 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 drivers/serial/serial_rockchip.c (limited to 'drivers') diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 4f6a3b87a1..7ddda9f205 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -109,6 +109,15 @@ config DEBUG_UART_SHIFT value. Use this value to specify the shift to use, where 0=byte registers, 2=32-bit word registers, etc. +config ROCKCHIP_SERIAL + bool "Rockchip on-chip UART support" + depends on ARCH_UNIPHIER && DM_SERIAL + help + Select this to enable a debug UART for Rockchip devices. This uses + the ns16550 driver. You will need to #define CONFIG_SYS_NS16550 in + your board config header. The clock input is automatically set to + use the oscillator (24MHz). + config SANDBOX_SERIAL bool "Sandbox UART support" depends on SANDBOX && DM diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 1d1f0361cd..869ea7b8fb 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_ZYNQ_SERIAL) += serial_zynq.o obj-$(CONFIG_BFIN_SERIAL) += serial_bfin.o obj-$(CONFIG_FSL_LPUART) += serial_lpuart.o obj-$(CONFIG_MXS_AUART) += mxs_auart.o +obj-$(CONFIG_ROCKCHIP_SERIAL) += serial_rockchip.o obj-$(CONFIG_ARC_SERIAL) += serial_arc.o obj-$(CONFIG_TEGRA_SERIAL) += serial_tegra.o obj-$(CONFIG_UNIPHIER_SERIAL) += serial_uniphier.o diff --git a/drivers/serial/serial_rockchip.c b/drivers/serial/serial_rockchip.c new file mode 100644 index 0000000000..0e7bbfc690 --- /dev/null +++ b/drivers/serial/serial_rockchip.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +static const struct udevice_id rockchip_serial_ids[] = { + { .compatible = "rockchip,rk3288-uart" }, + { } +}; + +static int rockchip_serial_ofdata_to_platdata(struct udevice *dev) +{ + struct ns16550_platdata *plat = dev_get_platdata(dev); + int ret; + + ret = ns16550_serial_ofdata_to_platdata(dev); + if (ret) + return ret; + + /* Do all Rockchip parts use 24MHz? */ + plat->clock = 24 * 1000000; + + return 0; +} + +U_BOOT_DRIVER(serial_ns16550) = { + .name = "serial_rockchip", + .id = UCLASS_SERIAL, + .of_match = rockchip_serial_ids, + .ofdata_to_platdata = rockchip_serial_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct ns16550_platdata), + .priv_auto_alloc_size = sizeof(struct NS16550), + .probe = ns16550_serial_probe, + .ops = &ns16550_serial_ops, + .flags = DM_FLAG_PRE_RELOC, +}; -- cgit v1.2.1 From 1f8f7730a8a3c8cc73ecb5715f5d87ed55fec541 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 30 Aug 2015 16:55:27 -0600 Subject: rockchip: gpio: Add rockchip GPIO driver This supports RK3288 at present. It does not implement functions or support for pull up/down. Signed-off-by: Simon Glass --- drivers/gpio/Kconfig | 9 ++++ drivers/gpio/Makefile | 1 + drivers/gpio/rk_gpio.c | 123 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+) create mode 100644 drivers/gpio/rk_gpio.c (limited to 'drivers') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 9a5d29debb..ef57a89ea2 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -28,6 +28,15 @@ config LPC32XX_GPIO help Support for the LPC32XX GPIO driver. +config ROCKCHIP_GPIO + bool "Rockchip GPIO driver" + depends on DM_GPIO + help + Support GPIO access on Rockchip SoCs. The GPIOs are arranged into + a number of banks (different for each SoC type) each with 32 GPIOs. + The GPIOs for a device are defined in the device tree with one node + for each bank. + config SANDBOX_GPIO bool "Enable sandbox GPIO driver" depends on SANDBOX && DM && DM_GPIO diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index ba185949bf..c58aa4dfd5 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_MXC_GPIO) += mxc_gpio.o obj-$(CONFIG_MXS_GPIO) += mxs_gpio.o obj-$(CONFIG_PCA953X) += pca953x.o obj-$(CONFIG_PCA9698) += pca9698.o +obj-$(CONFIG_ROCKCHIP_GPIO) += rk_gpio.o obj-$(CONFIG_S5P) += s5p_gpio.o obj-$(CONFIG_SANDBOX_GPIO) += sandbox.o obj-$(CONFIG_SPEAR_GPIO) += spear_gpio.o diff --git a/drivers/gpio/rk_gpio.c b/drivers/gpio/rk_gpio.c new file mode 100644 index 0000000000..fbdf9f3fd9 --- /dev/null +++ b/drivers/gpio/rk_gpio.c @@ -0,0 +1,123 @@ +/* + * (C) Copyright 2015 Google, Inc + * + * (C) Copyright 2008-2014 Rockchip Electronics + * Peter, Software Engineering, . + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +enum { + ROCKCHIP_GPIOS_PER_BANK = 32, +}; + +#define OFFSET_TO_BIT(bit) (1UL << (bit)) + +struct rockchip_gpio_priv { + struct rockchip_gpio_regs *regs; + char name[2]; +}; + +static int rockchip_gpio_direction_input(struct udevice *dev, unsigned offset) +{ + struct rockchip_gpio_priv *priv = dev_get_priv(dev); + struct rockchip_gpio_regs *regs = priv->regs; + + clrbits_le32(®s->swport_ddr, OFFSET_TO_BIT(offset)); + + return 0; +} + +static int rockchip_gpio_direction_output(struct udevice *dev, unsigned offset, + int value) +{ + struct rockchip_gpio_priv *priv = dev_get_priv(dev); + struct rockchip_gpio_regs *regs = priv->regs; + int mask = OFFSET_TO_BIT(offset); + + clrsetbits_le32(®s->swport_dr, mask, value ? mask : 0); + setbits_le32(®s->swport_ddr, mask); + + return 0; +} + +static int rockchip_gpio_get_value(struct udevice *dev, unsigned offset) +{ + struct rockchip_gpio_priv *priv = dev_get_priv(dev); + struct rockchip_gpio_regs *regs = priv->regs; + + return readl(®s->ext_port) & OFFSET_TO_BIT(offset); +} + +static int rockchip_gpio_set_value(struct udevice *dev, unsigned offset, + int value) +{ + struct rockchip_gpio_priv *priv = dev_get_priv(dev); + struct rockchip_gpio_regs *regs = priv->regs; + int mask = OFFSET_TO_BIT(offset); + + clrsetbits_le32(®s->swport_dr, mask, value ? mask : 0); + + return 0; +} + +static int rockchip_gpio_get_function(struct udevice *dev, unsigned offset) +{ + return -ENOSYS; +} + +static int rockchip_gpio_xlate(struct udevice *dev, struct gpio_desc *desc, + struct fdtdec_phandle_args *args) +{ + desc->offset = args->args[0]; + desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0; + + return 0; +} + +static int rockchip_gpio_probe(struct udevice *dev) +{ + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + struct rockchip_gpio_priv *priv = dev_get_priv(dev); + char *end; + int bank; + + priv->regs = (struct rockchip_gpio_regs *)dev_get_addr(dev); + uc_priv->gpio_count = ROCKCHIP_GPIOS_PER_BANK; + end = strrchr(dev->name, '@'); + bank = trailing_strtoln(dev->name, end); + priv->name[0] = 'A' + bank; + uc_priv->bank_name = priv->name; + + return 0; +} + +static const struct dm_gpio_ops gpio_rockchip_ops = { + .direction_input = rockchip_gpio_direction_input, + .direction_output = rockchip_gpio_direction_output, + .get_value = rockchip_gpio_get_value, + .set_value = rockchip_gpio_set_value, + .get_function = rockchip_gpio_get_function, + .xlate = rockchip_gpio_xlate, +}; + +static const struct udevice_id rockchip_gpio_ids[] = { + { .compatible = "rockchip,gpio-bank" }, + { } +}; + +U_BOOT_DRIVER(gpio_rockchip) = { + .name = "gpio_rockchip", + .id = UCLASS_GPIO, + .of_match = rockchip_gpio_ids, + .ops = &gpio_rockchip_ops, + .priv_auto_alloc_size = sizeof(struct rockchip_gpio_priv), + .probe = rockchip_gpio_probe, +}; -- cgit v1.2.1 From d2c88f7d528a3490dc3ce5317db39c0912bd2de1 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 30 Aug 2015 16:55:29 -0600 Subject: power: Add support for ACT8846 PMIC Add a driver for the ACT8846 PMIC. This supports several LDOs and BUCKs and is connected to the I2C bus. This driver supports using a regulator driver to access the regulators. Signed-off-by: Simon Glass --- drivers/power/pmic/Kconfig | 9 +++++ drivers/power/pmic/Makefile | 1 + drivers/power/pmic/act8846.c | 90 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 drivers/power/pmic/act8846.c (limited to 'drivers') diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index fc6a3743dc..547fd1aaa6 100644 --- a/drivers/power/pmic/Kconfig +++ b/drivers/power/pmic/Kconfig @@ -10,6 +10,15 @@ config DM_PMIC - 'drivers/power/pmic/pmic-uclass.c' - 'include/power/pmic.h' +config PMIC_ACT8846 + bool "Enable support for the active-semi 8846 PMIC" + depends on DM_PMIC && DM_I2C + ---help--- + This PMIC includes 4 DC/DC step-down buck regulators and 8 low-dropout + regulators (LDOs). It also provides some GPIO, reset and battery + functions. It uses an I2C interface and is designed for use with + tablets and smartphones. + config DM_PMIC_PFUZE100 bool "Enable Driver Model for PMIC PFUZE100" depends on DM_PMIC diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index 99c5778334..8f6463e127 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_DM_PMIC) += pmic-uclass.o obj-$(CONFIG_DM_PMIC_MAX77686) += max77686.o obj-$(CONFIG_DM_PMIC_PFUZE100) += pfuze100.o obj-$(CONFIG_DM_PMIC_SANDBOX) += sandbox.o i2c_pmic_emul.o +obj-$(CONFIG_PMIC_ACT8846) += act8846.o obj-$(CONFIG_PMIC_TPS65090) += tps65090.o obj-$(CONFIG_PMIC_S5M8767) += s5m8767.o diff --git a/drivers/power/pmic/act8846.c b/drivers/power/pmic/act8846.c new file mode 100644 index 0000000000..ff096b3a9c --- /dev/null +++ b/drivers/power/pmic/act8846.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2015 Google, Inc + * Written by Simon Glass + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +static const struct pmic_child_info pmic_children_info[] = { + { .prefix = "REG", .driver = "act8846_reg"}, + { }, +}; + +static int act8846_reg_count(struct udevice *dev) +{ + return ACT8846_NUM_OF_REGS; +} + +static int act8846_write(struct udevice *dev, uint reg, const uint8_t *buff, + int len) +{ + if (dm_i2c_write(dev, reg, buff, len)) { + debug("write error to device: %p register: %#x!", dev, reg); + return -EIO; + } + + return 0; +} + +static int act8846_read(struct udevice *dev, uint reg, uint8_t *buff, int len) +{ + if (dm_i2c_read(dev, reg, buff, len)) { + debug("read error from device: %p register: %#x!", dev, reg); + return -EIO; + } + + return 0; +} + +static int act8846_bind(struct udevice *dev) +{ + const void *blob = gd->fdt_blob; + int regulators_node; + int children; + + regulators_node = fdt_subnode_offset(blob, dev->of_offset, + "regulators"); + if (regulators_node <= 0) { + debug("%s: %s regulators subnode not found!", __func__, + dev->name); + return -ENXIO; + } + + debug("%s: '%s' - found regulators subnode\n", __func__, dev->name); + + children = pmic_bind_children(dev, regulators_node, pmic_children_info); + if (!children) + debug("%s: %s - no child found\n", __func__, dev->name); + + /* Always return success for this device */ + return 0; +} + +static struct dm_pmic_ops act8846_ops = { + .reg_count = act8846_reg_count, + .read = act8846_read, + .write = act8846_write, +}; + +static const struct udevice_id act8846_ids[] = { + { .compatible = "active-semi,act8846" }, + { } +}; + +U_BOOT_DRIVER(pmic_act8846) = { + .name = "act8846 pmic", + .id = UCLASS_PMIC, + .of_match = act8846_ids, + .bind = act8846_bind, + .ops = &act8846_ops, +}; -- cgit v1.2.1 From 9119820b6bd5a8131ae5d5f6f352ba657fe889a6 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 30 Aug 2015 16:55:30 -0600 Subject: power: regulator: Add a driver for ACT8846 regulators Add a full regulator driver for the ACT8846. This provides easy access to voltage and current settings for each regulator. Signed-off-by: Simon Glass --- drivers/power/regulator/Kconfig | 9 +++ drivers/power/regulator/Makefile | 1 + drivers/power/regulator/act8846.c | 155 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100644 drivers/power/regulator/act8846.c (limited to 'drivers') diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index 77d64e43c6..434dd029b5 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -16,6 +16,15 @@ config DM_REGULATOR for this purpose if PMIC I/O driver is implemented or dm_scan_fdt_node() otherwise. Detailed information can be found in the header file. +config REGULATOR_ACT8846 + bool "Enable driver for ACT8846 regulator" + depends on DM_REGULATOR && PMIC_ACT8846 + ---help--- + Enable support for the regulator functions of the ACT8846 PMIC. The + driver implements get/set api for the various BUCKS and LDOS supported + by the PMIC device. This driver is controlled by a device tree node + which includes voltage limits. + config DM_REGULATOR_PFUZE100 bool "Enable Driver Model for REGULATOR PFUZE100" depends on DM_REGULATOR && DM_PMIC_PFUZE100 diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index 7035936a35..c85978e024 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -6,6 +6,7 @@ # obj-$(CONFIG_DM_REGULATOR) += regulator-uclass.o +obj-$(CONFIG_REGULATOR_ACT8846) += act8846.o obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o obj-$(CONFIG_DM_REGULATOR_PFUZE100) += pfuze100.o obj-$(CONFIG_DM_REGULATOR_FIXED) += fixed.o diff --git a/drivers/power/regulator/act8846.c b/drivers/power/regulator/act8846.c new file mode 100644 index 0000000000..255f8b096e --- /dev/null +++ b/drivers/power/regulator/act8846.c @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2015 Google, Inc + * Written by Simon Glass + * + * Based on Rockchip's drivers/power/pmic/pmic_act8846.c: + * Copyright (C) 2012 rockchips + * zyw + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +static const u16 voltage_map[] = { + 600, 625, 650, 675, 700, 725, 750, 775, + 800, 825, 850, 875, 900, 925, 950, 975, + 1000, 1025, 1050, 1075, 1100, 1125, 1150, 1175, + 1200, 1250, 1300, 1350, 1400, 1450, 1500, 1550, + 1600, 1650, 1700, 1750, 1800, 1850, 1900, 1950, + 2000, 2050, 2100, 2150, 2200, 2250, 2300, 2350, + 2400, 2500, 2600, 2700, 2800, 2900, 3000, 3100, + 3200, 3300, 3400, 3500, 3600, 3700, 3800, 3900, +}; + +enum { + REG_SYS0, + REG_SYS1, + REG1_VOL = 0x10, + REG1_CTL = 0X11, + REG2_VOL0 = 0x20, + REG2_VOL1, + REG2_CTL, + REG3_VOL0 = 0x30, + REG3_VOL1, + REG3_CTL, + REG4_VOL0 = 0x40, + REG4_VOL1, + REG4_CTL, + REG5_VOL = 0x50, + REG5_CTL, + REG6_VOL = 0X58, + REG6_CTL, + REG7_VOL = 0x60, + REG7_CTL, + REG8_VOL = 0x68, + REG8_CTL, + REG9_VOL = 0x70, + REG9_CTL, + REG10_VOL = 0x80, + REG10_CTL, + REG11_VOL = 0x90, + REG11_CTL, + REG12_VOL = 0xa0, + REG12_CTL, + REG13 = 0xb1, +}; + +static const u8 addr_vol[] = { + 0, REG1_VOL, REG2_VOL0, REG3_VOL0, REG4_VOL0, + REG5_VOL, REG6_VOL, REG7_VOL, REG8_VOL, REG9_VOL, + REG10_VOL, REG11_VOL, REG12_VOL, +}; + +static const u8 addr_ctl[] = { + 0, REG1_CTL, REG2_CTL, REG3_CTL, REG4_CTL, + REG5_CTL, REG6_CTL, REG7_CTL, REG8_CTL, REG9_CTL, + REG10_CTL, REG11_CTL, REG12_CTL, +}; + +static int check_volt_table(const u16 *volt_table, int uvolt) +{ + int i; + + for (i = VOL_MIN_IDX; i < VOL_MAX_IDX; i++) { + if (uvolt <= (volt_table[i] * 1000)) + return i; + } + return -EINVAL; +} + +static int reg_get_value(struct udevice *dev) +{ + int reg = dev->driver_data; + int ret; + + ret = pmic_reg_read(dev->parent, reg); + if (ret < 0) + return ret; + + return voltage_map[ret & LDO_VOL_MASK] * 1000; +} + +static int reg_set_value(struct udevice *dev, int uvolt) +{ + int reg = dev->driver_data; + int val; + + val = check_volt_table(voltage_map, uvolt); + if (val < 0) + return val; + + return pmic_clrsetbits(dev->parent, addr_vol[reg], LDO_VOL_MASK, val); +} + +static int reg_set_enable(struct udevice *dev, bool enable) +{ + int reg = dev->driver_data; + + return pmic_clrsetbits(dev->parent, addr_ctl[reg], LDO_EN_MASK, + enable ? LDO_EN_MASK : 0); +} + +static bool reg_get_enable(struct udevice *dev) +{ + int reg = dev->driver_data; + int ret; + + ret = pmic_reg_read(dev->parent, reg); + if (ret < 0) + return ret; + + return ret & LDO_EN_MASK ? true : false; +} + +static int act8846_reg_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_platdata *uc_pdata; + int reg = dev->driver_data; + + uc_pdata = dev_get_uclass_platdata(dev); + + uc_pdata->type = reg <= 4 ? REGULATOR_TYPE_BUCK : REGULATOR_TYPE_LDO; + uc_pdata->mode_count = 0; + + return 0; +} + +static const struct dm_regulator_ops act8846_reg_ops = { + .get_value = reg_get_value, + .set_value = reg_set_value, + .get_enable = reg_get_enable, + .set_enable = reg_set_enable, +}; + +U_BOOT_DRIVER(act8846_buck) = { + .name = "act8846_reg", + .id = UCLASS_REGULATOR, + .ops = &act8846_reg_ops, + .probe = act8846_reg_probe, +}; -- cgit v1.2.1 From 99c156508286ff0338c78b24e2498bb17362af1d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 30 Aug 2015 16:55:31 -0600 Subject: rockchip: rk3288: Add clock driver Add a driver for setting up and modifying the various PLLs and peripheral clocks on the RK3288. Signed-off-by: Simon Glass --- drivers/clk/Makefile | 1 + drivers/clk/clk_rk3288.c | 618 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 619 insertions(+) create mode 100644 drivers/clk/clk_rk3288.c (limited to 'drivers') diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index bb89fb918b..008ec100a1 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -6,4 +6,5 @@ # obj-$(CONFIG_CLK) += clk-uclass.o +obj-$(CONFIG_ROCKCHIP_RK3288) += clk_rk3288.o obj-$(CONFIG_SANDBOX) += clk_sandbox.o diff --git a/drivers/clk/clk_rk3288.c b/drivers/clk/clk_rk3288.c new file mode 100644 index 0000000000..54d49305b0 --- /dev/null +++ b/drivers/clk/clk_rk3288.c @@ -0,0 +1,618 @@ +/* + * (C) Copyright 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +struct rk3288_clk_plat { + enum rk_clk_id clk_id; +}; + +struct rk3288_clk_priv { + struct rk3288_grf *grf; + struct rk3288_cru *cru; + ulong rate; +}; + +struct pll_div { + u32 nr; + u32 nf; + u32 no; +}; + +enum { + VCO_MAX_HZ = 2200U * 1000000, + VCO_MIN_HZ = 440 * 1000000, + OUTPUT_MAX_HZ = 2200U * 1000000, + OUTPUT_MIN_HZ = 27500000, + FREF_MAX_HZ = 2200U * 1000000, + FREF_MIN_HZ = 269 * 1000000, +}; + +enum { + /* PLL CON0 */ + PLL_OD_MASK = 0x0f, + + /* PLL CON1 */ + PLL_NF_MASK = 0x1fff, + + /* PLL CON2 */ + PLL_BWADJ_MASK = 0x0fff, + + /* PLL CON3 */ + PLL_RESET_SHIFT = 5, + + /* CLKSEL1: pd bus clk pll sel: codec or general */ + PD_BUS_SEL_PLL_MASK = 15, + PD_BUS_SEL_CPLL = 0, + PD_BUS_SEL_GPLL, + + /* pd bus pclk div: pclk = pd_bus_aclk /(div + 1) */ + PD_BUS_PCLK_DIV_SHIFT = 12, + PD_BUS_PCLK_DIV_MASK = 7, + + /* pd bus hclk div: aclk_bus: hclk_bus = 1:1 or 2:1 or 4:1 */ + PD_BUS_HCLK_DIV_SHIFT = 8, + PD_BUS_HCLK_DIV_MASK = 3, + + /* pd bus aclk div: pd_bus_aclk = pd_bus_src_clk /(div0 * div1) */ + PD_BUS_ACLK_DIV0_SHIFT = 3, + PD_BUS_ACLK_DIV0_MASK = 0x1f, + PD_BUS_ACLK_DIV1_SHIFT = 0, + PD_BUS_ACLK_DIV1_MASK = 0x7, + + /* + * CLKSEL10 + * peripheral bus pclk div: + * aclk_bus: pclk_bus = 1:1 or 2:1 or 4:1 or 8:1 + */ + PERI_PCLK_DIV_SHIFT = 12, + PERI_PCLK_DIV_MASK = 7, + + /* peripheral bus hclk div: aclk_bus: hclk_bus = 1:1 or 2:1 or 4:1 */ + PERI_HCLK_DIV_SHIFT = 8, + PERI_HCLK_DIV_MASK = 3, + + /* + * peripheral bus aclk div: + * aclk_periph = periph_clk_src / (peri_aclk_div_con + 1) + */ + PERI_ACLK_DIV_SHIFT = 0, + PERI_ACLK_DIV_MASK = 0x1f, + + /* CLKSEL37 */ + DPLL_MODE_MASK = 0x3, + DPLL_MODE_SHIFT = 4, + DPLL_MODE_SLOW = 0, + DPLL_MODE_NORM, + + CPLL_MODE_MASK = 3, + CPLL_MODE_SHIFT = 8, + CPLL_MODE_SLOW = 0, + CPLL_MODE_NORM, + + GPLL_MODE_MASK = 3, + GPLL_MODE_SHIFT = 12, + GPLL_MODE_SLOW = 0, + GPLL_MODE_NORM, + + NPLL_MODE_MASK = 3, + NPLL_MODE_SHIFT = 14, + NPLL_MODE_SLOW = 0, + NPLL_MODE_NORM, + + SOCSTS_DPLL_LOCK = 1 << 5, + SOCSTS_APLL_LOCK = 1 << 6, + SOCSTS_CPLL_LOCK = 1 << 7, + SOCSTS_GPLL_LOCK = 1 << 8, + SOCSTS_NPLL_LOCK = 1 << 9, +}; + +#define RATE_TO_DIV(input_rate, output_rate) \ + ((input_rate) / (output_rate) - 1); + +#define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1)) + +#define PLL_DIVISORS(hz, _nr, _no) {\ + .nr = _nr, .nf = (u32)((u64)hz * _nr * _no / OSC_HZ), .no = _no};\ + _Static_assert(((u64)hz * _nr * _no / OSC_HZ) * OSC_HZ /\ + (_nr * _no) == hz, #hz "Hz cannot be hit with PLL "\ + "divisors on line " __stringify(__LINE__)); + +/* Keep divisors as low as possible to reduce jitter and power usage */ +static const struct pll_div apll_init_cfg = PLL_DIVISORS(APLL_HZ, 1, 1); +static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 2, 2); +static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 1, 2); + +static int rkclk_set_pll(struct rk3288_cru *cru, enum rk_clk_id clk_id, + const struct pll_div *div) +{ + int pll_id = rk_pll_id(clk_id); + struct rk3288_pll *pll = &cru->pll[pll_id]; + /* All PLLs have same VCO and output frequency range restrictions. */ + uint vco_hz = OSC_HZ / 1000 * div->nf / div->nr * 1000; + uint output_hz = vco_hz / div->no; + + debug("PLL at %p: nf=%d, nr=%d, no=%d, vco=%u Hz, output=%u Hz\n", + pll, div->nf, div->nr, div->no, vco_hz, output_hz); + assert(vco_hz >= VCO_MIN_HZ && vco_hz <= VCO_MAX_HZ && + output_hz >= OUTPUT_MIN_HZ && output_hz <= OUTPUT_MAX_HZ && + (div->no == 1 || !(div->no % 2))); + + /* enter rest */ + rk_setreg(&pll->con3, 1 << PLL_RESET_SHIFT); + + rk_clrsetreg(&pll->con0, + CLKR_MASK << CLKR_SHIFT | PLL_OD_MASK, + ((div->nr - 1) << CLKR_SHIFT) | (div->no - 1)); + rk_clrsetreg(&pll->con1, CLKF_MASK, div->nf - 1); + rk_clrsetreg(&pll->con2, PLL_BWADJ_MASK, (div->nf >> 1) - 1); + + udelay(10); + + /* return form rest */ + rk_clrreg(&pll->con3, 1 << PLL_RESET_SHIFT); + + return 0; +} + +static inline unsigned int log2(unsigned int value) +{ + return fls(value) - 1; +} + +static int rkclk_configure_ddr(struct rk3288_cru *cru, struct rk3288_grf *grf, + unsigned int hz) +{ + static const struct pll_div dpll_cfg[] = { + {.nf = 25, .nr = 2, .no = 1}, + {.nf = 400, .nr = 9, .no = 2}, + {.nf = 500, .nr = 9, .no = 2}, + {.nf = 100, .nr = 3, .no = 1}, + }; + int cfg; + + debug("%s: cru=%p, grf=%p, hz=%u\n", __func__, cru, grf, hz); + switch (hz) { + case 300000000: + cfg = 0; + break; + case 533000000: /* actually 533.3P MHz */ + cfg = 1; + break; + case 666000000: /* actually 666.6P MHz */ + cfg = 2; + break; + case 800000000: + cfg = 3; + break; + default: + debug("Unsupported SDRAM frequency, add to clock.c!"); + return -EINVAL; + } + + /* pll enter slow-mode */ + rk_clrsetreg(&cru->cru_mode_con, DPLL_MODE_MASK << DPLL_MODE_SHIFT, + DPLL_MODE_SLOW << DPLL_MODE_SHIFT); + + rkclk_set_pll(cru, CLK_DDR, &dpll_cfg[cfg]); + + /* wait for pll lock */ + while (!(readl(&grf->soc_status[1]) & SOCSTS_DPLL_LOCK)) + udelay(1); + + /* PLL enter normal-mode */ + rk_clrsetreg(&cru->cru_mode_con, DPLL_MODE_MASK << DPLL_MODE_SHIFT, + DPLL_MODE_NORM << DPLL_MODE_SHIFT); + + return 0; +} + +#ifdef CONFIG_SPL_BUILD +static void rkclk_init(struct rk3288_cru *cru, struct rk3288_grf *grf) +{ + u32 aclk_div; + u32 hclk_div; + u32 pclk_div; + + /* pll enter slow-mode */ + rk_clrsetreg(&cru->cru_mode_con, + GPLL_MODE_MASK << GPLL_MODE_SHIFT | + CPLL_MODE_MASK << CPLL_MODE_SHIFT, + GPLL_MODE_SLOW << GPLL_MODE_SHIFT | + CPLL_MODE_SLOW << CPLL_MODE_SHIFT); + + /* init pll */ + rkclk_set_pll(cru, CLK_GENERAL, &gpll_init_cfg); + rkclk_set_pll(cru, CLK_CODEC, &cpll_init_cfg); + + /* waiting for pll lock */ + while ((readl(&grf->soc_status[1]) & + (SOCSTS_CPLL_LOCK | SOCSTS_GPLL_LOCK)) != + (SOCSTS_CPLL_LOCK | SOCSTS_GPLL_LOCK)) + udelay(1); + + /* + * pd_bus clock pll source selection and + * set up dependent divisors for PCLK/HCLK and ACLK clocks. + */ + aclk_div = GPLL_HZ / PD_BUS_ACLK_HZ - 1; + assert((aclk_div + 1) * PD_BUS_ACLK_HZ == GPLL_HZ && aclk_div < 0x1f); + hclk_div = PD_BUS_ACLK_HZ / PD_BUS_HCLK_HZ - 1; + assert((hclk_div + 1) * PD_BUS_HCLK_HZ == + PD_BUS_ACLK_HZ && (hclk_div < 0x4) && (hclk_div != 0x2)); + + pclk_div = PD_BUS_ACLK_HZ / PD_BUS_PCLK_HZ - 1; + assert((pclk_div + 1) * PD_BUS_PCLK_HZ == + PD_BUS_ACLK_HZ && pclk_div < 0x7); + + rk_clrsetreg(&cru->cru_clksel_con[1], + PD_BUS_PCLK_DIV_MASK << PD_BUS_PCLK_DIV_SHIFT | + PD_BUS_HCLK_DIV_MASK << PD_BUS_HCLK_DIV_SHIFT | + PD_BUS_ACLK_DIV0_MASK << PD_BUS_ACLK_DIV0_SHIFT | + PD_BUS_ACLK_DIV1_MASK << PD_BUS_ACLK_DIV1_SHIFT, + pclk_div << PD_BUS_PCLK_DIV_SHIFT | + hclk_div << PD_BUS_HCLK_DIV_SHIFT | + aclk_div << PD_BUS_ACLK_DIV0_SHIFT | + 0 << 0); + + /* + * peri clock pll source selection and + * set up dependent divisors for PCLK/HCLK and ACLK clocks. + */ + aclk_div = GPLL_HZ / PERI_ACLK_HZ - 1; + assert((aclk_div + 1) * PERI_ACLK_HZ == GPLL_HZ && aclk_div < 0x1f); + + hclk_div = log2(PERI_ACLK_HZ / PERI_HCLK_HZ); + assert((1 << hclk_div) * PERI_HCLK_HZ == + PERI_ACLK_HZ && (hclk_div < 0x4)); + + pclk_div = log2(PERI_ACLK_HZ / PERI_PCLK_HZ); + assert((1 << pclk_div) * PERI_PCLK_HZ == + PERI_ACLK_HZ && (pclk_div < 0x4)); + + rk_clrsetreg(&cru->cru_clksel_con[10], + PERI_PCLK_DIV_MASK << PERI_PCLK_DIV_SHIFT | + PERI_HCLK_DIV_MASK << PERI_HCLK_DIV_SHIFT | + PERI_ACLK_DIV_MASK << PERI_ACLK_DIV_SHIFT, + pclk_div << PERI_PCLK_DIV_SHIFT | + hclk_div << PERI_HCLK_DIV_SHIFT | + aclk_div << PERI_ACLK_DIV_SHIFT); + + /* PLL enter normal-mode */ + rk_clrsetreg(&cru->cru_mode_con, + GPLL_MODE_MASK << GPLL_MODE_SHIFT | + CPLL_MODE_MASK << CPLL_MODE_SHIFT, + GPLL_MODE_NORM << GPLL_MODE_SHIFT | + GPLL_MODE_NORM << CPLL_MODE_SHIFT); +} +#endif + +/* Get pll rate by id */ +static uint32_t rkclk_pll_get_rate(struct rk3288_cru *cru, + enum rk_clk_id clk_id) +{ + uint32_t nr, no, nf; + uint32_t con; + int pll_id = rk_pll_id(clk_id); + struct rk3288_pll *pll = &cru->pll[pll_id]; + static u8 clk_shift[CLK_COUNT] = { + 0xff, APLL_WORK_SHIFT, DPLL_WORK_SHIFT, CPLL_WORK_SHIFT, + GPLL_WORK_SHIFT, NPLL_WORK_SHIFT + }; + uint shift; + + con = readl(&cru->cru_mode_con); + shift = clk_shift[clk_id]; + switch ((con >> shift) & APLL_WORK_MASK) { + case APLL_WORK_SLOW: + return OSC_HZ; + case APLL_WORK_NORMAL: + /* normal mode */ + con = readl(&pll->con0); + no = ((con >> CLKOD_SHIFT) & CLKOD_MASK) + 1; + nr = ((con >> CLKR_SHIFT) & CLKR_MASK) + 1; + con = readl(&pll->con1); + nf = ((con >> CLKF_SHIFT) & CLKF_MASK) + 1; + + return (24 * nf / (nr * no)) * 1000000; + case APLL_WORK_DEEP: + default: + return 32768; + } +} + +static ulong rk3288_clk_get_rate(struct udevice *dev) +{ + struct rk3288_clk_plat *plat = dev_get_platdata(dev); + struct rk3288_clk_priv *priv = dev_get_priv(dev); + + debug("%s\n", dev->name); + return rkclk_pll_get_rate(priv->cru, plat->clk_id); +} + +static ulong rk3288_clk_set_rate(struct udevice *dev, ulong rate) +{ + struct rk3288_clk_plat *plat = dev_get_platdata(dev); + struct rk3288_clk_priv *priv = dev_get_priv(dev); + + debug("%s\n", dev->name); + switch (plat->clk_id) { + case CLK_DDR: + rkclk_configure_ddr(priv->cru, priv->grf, rate); + break; + default: + return -ENOENT; + } + + return 0; +} + +static ulong rockchip_mmc_get_clk(struct rk3288_cru *cru, uint clk_general_rate, + enum periph_id periph) +{ + uint src_rate; + uint div, mux; + u32 con; + + switch (periph) { + case PERIPH_ID_EMMC: + con = readl(&cru->cru_clksel_con[12]); + mux = (con >> EMMC_PLL_SHIFT) & EMMC_PLL_MASK; + div = (con >> EMMC_DIV_SHIFT) & EMMC_DIV_MASK; + break; + case PERIPH_ID_SDCARD: + con = readl(&cru->cru_clksel_con[12]); + mux = (con >> MMC0_PLL_SHIFT) & MMC0_PLL_MASK; + div = (con >> MMC0_DIV_SHIFT) & MMC0_DIV_MASK; + break; + case PERIPH_ID_SDMMC2: + con = readl(&cru->cru_clksel_con[12]); + mux = (con >> SDIO0_PLL_SHIFT) & SDIO0_PLL_MASK; + div = (con >> SDIO0_DIV_SHIFT) & SDIO0_DIV_MASK; + break; + default: + return -EINVAL; + } + + src_rate = mux == EMMC_PLL_SELECT_24MHZ ? OSC_HZ : clk_general_rate; + return DIV_TO_RATE(src_rate, div); +} + +static ulong rockchip_mmc_set_clk(struct rk3288_cru *cru, uint clk_general_rate, + enum periph_id periph, uint freq) +{ + int src_clk_div; + int mux; + + debug("%s: clk_general_rate=%u\n", __func__, clk_general_rate); + src_clk_div = RATE_TO_DIV(clk_general_rate, freq); + + if (src_clk_div > 0x3f) { + src_clk_div = RATE_TO_DIV(OSC_HZ, freq); + mux = EMMC_PLL_SELECT_24MHZ; + assert((int)EMMC_PLL_SELECT_24MHZ == + (int)MMC0_PLL_SELECT_24MHZ); + } else { + mux = EMMC_PLL_SELECT_GENERAL; + assert((int)EMMC_PLL_SELECT_GENERAL == + (int)MMC0_PLL_SELECT_GENERAL); + } + switch (periph) { + case PERIPH_ID_EMMC: + rk_clrsetreg(&cru->cru_clksel_con[12], + EMMC_PLL_MASK << EMMC_PLL_SHIFT | + EMMC_DIV_MASK << EMMC_DIV_SHIFT, + mux << EMMC_PLL_SHIFT | + (src_clk_div - 1) << EMMC_DIV_SHIFT); + break; + case PERIPH_ID_SDCARD: + rk_clrsetreg(&cru->cru_clksel_con[11], + MMC0_PLL_MASK << MMC0_PLL_SHIFT | + MMC0_DIV_MASK << MMC0_DIV_SHIFT, + mux << MMC0_PLL_SHIFT | + (src_clk_div - 1) << MMC0_DIV_SHIFT); + break; + case PERIPH_ID_SDMMC2: + rk_clrsetreg(&cru->cru_clksel_con[12], + SDIO0_PLL_MASK << SDIO0_PLL_SHIFT | + SDIO0_DIV_MASK << SDIO0_DIV_SHIFT, + mux << SDIO0_PLL_SHIFT | + (src_clk_div - 1) << SDIO0_DIV_SHIFT); + break; + default: + return -EINVAL; + } + + return rockchip_mmc_get_clk(cru, clk_general_rate, periph); +} + +static ulong rockchip_spi_get_clk(struct rk3288_cru *cru, uint clk_general_rate, + enum periph_id periph) +{ + uint div, mux; + u32 con; + + switch (periph) { + case PERIPH_ID_SPI0: + con = readl(&cru->cru_clksel_con[25]); + mux = (con >> SPI0_PLL_SHIFT) & SPI0_PLL_MASK; + div = (con >> SPI0_DIV_SHIFT) & SPI0_DIV_MASK; + break; + case PERIPH_ID_SPI1: + con = readl(&cru->cru_clksel_con[25]); + mux = (con >> SPI1_PLL_SHIFT) & SPI1_PLL_MASK; + div = (con >> SPI1_DIV_SHIFT) & SPI1_DIV_MASK; + break; + case PERIPH_ID_SPI2: + con = readl(&cru->cru_clksel_con[39]); + mux = (con >> SPI2_PLL_SHIFT) & SPI2_PLL_MASK; + div = (con >> SPI2_DIV_SHIFT) & SPI2_DIV_MASK; + break; + default: + return -EINVAL; + } + assert(mux == SPI0_PLL_SELECT_GENERAL); + + return DIV_TO_RATE(clk_general_rate, div); +} + +static ulong rockchip_spi_set_clk(struct rk3288_cru *cru, uint clk_general_rate, + enum periph_id periph, uint freq) +{ + int src_clk_div; + + debug("%s: clk_general_rate=%u\n", __func__, clk_general_rate); + src_clk_div = RATE_TO_DIV(clk_general_rate, freq); + switch (periph) { + case PERIPH_ID_SPI0: + rk_clrsetreg(&cru->cru_clksel_con[25], + SPI0_PLL_MASK << SPI0_PLL_SHIFT | + SPI0_DIV_MASK << SPI0_DIV_SHIFT, + SPI0_PLL_SELECT_GENERAL << SPI0_PLL_SHIFT | + src_clk_div << SPI0_DIV_SHIFT); + break; + case PERIPH_ID_SPI1: + rk_clrsetreg(&cru->cru_clksel_con[25], + SPI1_PLL_MASK << SPI1_PLL_SHIFT | + SPI1_DIV_MASK << SPI1_DIV_SHIFT, + SPI1_PLL_SELECT_GENERAL << SPI1_PLL_SHIFT | + src_clk_div << SPI1_DIV_SHIFT); + break; + case PERIPH_ID_SPI2: + rk_clrsetreg(&cru->cru_clksel_con[39], + SPI2_PLL_MASK << SPI2_PLL_SHIFT | + SPI2_DIV_MASK << SPI2_DIV_SHIFT, + SPI2_PLL_SELECT_GENERAL << SPI2_PLL_SHIFT | + src_clk_div << SPI2_DIV_SHIFT); + break; + default: + return -EINVAL; + } + + return rockchip_spi_get_clk(cru, clk_general_rate, periph); +} + +ulong rk3288_set_periph_rate(struct udevice *dev, int periph, ulong rate) +{ + struct rk3288_clk_priv *priv = dev_get_priv(dev); + ulong new_rate; + + switch (periph) { + case PERIPH_ID_EMMC: + case PERIPH_ID_SDCARD: + new_rate = rockchip_mmc_set_clk(priv->cru, clk_get_rate(dev), + periph, rate); + break; + case PERIPH_ID_SPI0: + case PERIPH_ID_SPI1: + case PERIPH_ID_SPI2: + new_rate = rockchip_spi_set_clk(priv->cru, clk_get_rate(dev), + periph, rate); + break; + default: + return -ENOENT; + } + + return new_rate; +} + +static struct clk_ops rk3288_clk_ops = { + .get_rate = rk3288_clk_get_rate, + .set_rate = rk3288_clk_set_rate, + .set_periph_rate = rk3288_set_periph_rate, +}; + +static int rk3288_clk_probe(struct udevice *dev) +{ + struct rk3288_clk_plat *plat = dev_get_platdata(dev); + struct rk3288_clk_priv *priv = dev_get_priv(dev); + + if (plat->clk_id != CLK_OSC) { + struct rk3288_clk_priv *parent_priv = dev_get_priv(dev->parent); + + priv->cru = parent_priv->cru; + priv->grf = parent_priv->grf; + return 0; + } + priv->cru = (struct rk3288_cru *)dev_get_addr(dev); + priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); +#ifdef CONFIG_SPL_BUILD + rkclk_init(priv->cru, priv->grf); +#endif + + return 0; +} + +static const char *const clk_name[CLK_COUNT] = { + "osc", + "apll", + "dpll", + "cpll", + "gpll", + "mpll", +}; + +static int rk3288_clk_bind(struct udevice *dev) +{ + struct rk3288_clk_plat *plat = dev_get_platdata(dev); + int pll, ret; + + /* We only need to set up the root clock */ + if (dev->of_offset == -1) { + plat->clk_id = CLK_OSC; + return 0; + } + + /* Create devices for P main clocks */ + for (pll = 1; pll < CLK_COUNT; pll++) { + struct udevice *child; + struct rk3288_clk_plat *cplat; + + debug("%s %s\n", __func__, clk_name[pll]); + ret = device_bind_driver(dev, "clk_rk3288", clk_name[pll], + &child); + if (ret) + return ret; + cplat = dev_get_platdata(child); + cplat->clk_id = pll; + } + + /* The reset driver does not have a device node, so bind it here */ + ret = device_bind_driver(gd->dm_root, "rk3288_reset", "reset", &dev); + if (ret) + debug("Warning: No RK3288 reset driver: ret=%d\n", ret); + + return 0; +} + +static const struct udevice_id rk3288_clk_ids[] = { + { .compatible = "rockchip,rk3288-cru" }, + { } +}; + +U_BOOT_DRIVER(clk_rk3288) = { + .name = "clk_rk3288", + .id = UCLASS_CLK, + .of_match = rk3288_clk_ids, + .priv_auto_alloc_size = sizeof(struct rk3288_clk_priv), + .platdata_auto_alloc_size = sizeof(struct rk3288_clk_plat), + .ops = &rk3288_clk_ops, + .bind = rk3288_clk_bind, + .probe = rk3288_clk_probe, +}; -- cgit v1.2.1 From bb4e4a5d9670ae6e996c22b4fba31aafb2d2a362 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 30 Aug 2015 16:55:35 -0600 Subject: rockchip: rk3288: Add pinctrl driver Add a driver which supports pin multiplexing setup for the most commonly used peripherals. Signed-off-by: Simon Glass --- drivers/pinctrl/Kconfig | 9 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/rockchip/Makefile | 8 + drivers/pinctrl/rockchip/pinctrl_rk3288.c | 441 ++++++++++++++++++++++++++++++ 4 files changed, 459 insertions(+) create mode 100644 drivers/pinctrl/rockchip/Makefile create mode 100644 drivers/pinctrl/rockchip/pinctrl_rk3288.c (limited to 'drivers') diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 918a8592bb..b8146df99b 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -105,6 +105,15 @@ config SPL_PINCONF if PINCTRL || SPL_PINCTRL +config ROCKCHIP_PINCTRL + bool "Rockchip pin control driver" + depends on DM + help + Support pin multiplexing control on Rockchip SoCs. The driver is + controlled by a device tree node which contains both the GPIO + definitions and pin control functions for each available multiplex + function. + config PINCTRL_SANDBOX bool "Sandbox pinctrl driver" depends on SANDBOX diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 35decf49c3..f537df4e88 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -1,4 +1,5 @@ obj-y += pinctrl-uclass.o obj-$(CONFIG_$(SPL_)PINCTRL_GENERIC) += pinctrl-generic.o +obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ obj-$(CONFIG_PINCTRL_SANDBOX) += pinctrl-sandbox.o diff --git a/drivers/pinctrl/rockchip/Makefile b/drivers/pinctrl/rockchip/Makefile new file mode 100644 index 0000000000..251bace797 --- /dev/null +++ b/drivers/pinctrl/rockchip/Makefile @@ -0,0 +1,8 @@ +# +# Copyright (c) 2015 Google, Inc +# Written by Simon Glass +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_ROCKCHIP_PINCTRL) += pinctrl_rk3288.o diff --git a/drivers/pinctrl/rockchip/pinctrl_rk3288.c b/drivers/pinctrl/rockchip/pinctrl_rk3288.c new file mode 100644 index 0000000000..5205498b36 --- /dev/null +++ b/drivers/pinctrl/rockchip/pinctrl_rk3288.c @@ -0,0 +1,441 @@ +/* + * Pinctrl driver for Rockchip SoCs + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +struct rk3288_pinctrl_priv { + struct rk3288_grf *grf; + struct rk3288_pmu *pmu; +}; + +static void pinctrl_rk3288_pwm_config(struct rk3288_grf *grf, int pwm_id) +{ + switch (pwm_id) { + case PERIPH_ID_PWM0: + rk_clrsetreg(&grf->gpio7a_iomux, GPIO7A0_MASK << GPIO7A0_SHIFT, + GPIO7A0_PWM_0 << GPIO7A0_SHIFT); + break; + case PERIPH_ID_PWM1: + rk_clrsetreg(&grf->gpio7a_iomux, GPIO7A1_MASK << GPIO7A1_SHIFT, + GPIO7A1_PWM_1 << GPIO7A1_SHIFT); + break; + case PERIPH_ID_PWM2: + rk_clrsetreg(&grf->gpio7a_iomux, GPIO7C6_MASK << GPIO7C6_SHIFT, + GPIO7C6_PWM_2 << GPIO7C6_SHIFT); + break; + case PERIPH_ID_PWM3: + rk_clrsetreg(&grf->gpio7a_iomux, GPIO7C7_MASK << GPIO7C6_SHIFT, + GPIO7C7_PWM_3 << GPIO7C7_SHIFT); + break; + default: + debug("pwm id = %d iomux error!\n", pwm_id); + break; + } +} + +static void pinctrl_rk3288_i2c_config(struct rk3288_grf *grf, + struct rk3288_pmu *pmu, int i2c_id) +{ + switch (i2c_id) { + case PERIPH_ID_I2C0: + clrsetbits_le32(&pmu->gpio0b_iomux, + GPIO0_B7_MASK << GPIO0_B7_SHIFT, + GPIO0_B7_I2C0PMU_SDA << GPIO0_B7_SHIFT); + clrsetbits_le32(&pmu->gpio0b_iomux, + GPIO0_C0_MASK << GPIO0_C0_SHIFT, + GPIO0_C0_I2C0PMU_SCL << GPIO0_C0_SHIFT); + break; + case PERIPH_ID_I2C1: + rk_clrsetreg(&grf->gpio8a_iomux, + GPIO8A4_MASK << GPIO8A4_SHIFT | + GPIO8A5_MASK << GPIO8A5_SHIFT, + GPIO8A4_I2C2SENSOR_SDA << GPIO8A4_SHIFT | + GPIO8A5_I2C2SENSOR_SCL << GPIO8A5_SHIFT); + break; + case PERIPH_ID_I2C2: + rk_clrsetreg(&grf->gpio6b_iomux, + GPIO6B1_MASK << GPIO6B1_SHIFT | + GPIO6B2_MASK << GPIO6B2_SHIFT, + GPIO6B1_I2C1AUDIO_SDA << GPIO6B1_SHIFT | + GPIO6B2_I2C1AUDIO_SCL << GPIO6B2_SHIFT); + break; + case PERIPH_ID_I2C3: + rk_clrsetreg(&grf->gpio2c_iomux, + GPIO2C1_MASK << GPIO2C1_SHIFT | + GPIO2C0_MASK << GPIO2C0_SHIFT, + GPIO2C1_I2C3CAM_SDA << GPIO2C1_SHIFT | + GPIO2C0_I2C3CAM_SCL << GPIO2C0_SHIFT); + break; + case PERIPH_ID_I2C4: + rk_clrsetreg(&grf->gpio7cl_iomux, + GPIO7C1_MASK << GPIO7C1_SHIFT | + GPIO7C2_MASK << GPIO7C2_SHIFT, + GPIO7C1_I2C4TP_SDA << GPIO7C1_SHIFT | + GPIO7C2_I2C4TP_SCL << GPIO7C2_SHIFT); + break; + case PERIPH_ID_I2C5: + rk_clrsetreg(&grf->gpio7cl_iomux, + GPIO7C3_MASK << GPIO7C3_SHIFT, + GPIO7C3_I2C5HDMI_SDA << GPIO7C3_SHIFT); + rk_clrsetreg(&grf->gpio7ch_iomux, + GPIO7C4_MASK << GPIO7C4_SHIFT, + GPIO7C4_I2C5HDMI_SCL << GPIO7C4_SHIFT); + break; + default: + debug("i2c id = %d iomux error!\n", i2c_id); + break; + } +} + +static void pinctrl_rk3288_lcdc_config(struct rk3288_grf *grf, int lcd_id) +{ + switch (lcd_id) { + case PERIPH_ID_LCDC0: + rk_clrsetreg(&grf->gpio1d_iomux, + GPIO1D3_MASK << GPIO1D0_SHIFT | + GPIO1D2_MASK << GPIO1D2_SHIFT | + GPIO1D1_MASK << GPIO1D1_SHIFT | + GPIO1D0_MASK << GPIO1D0_SHIFT, + GPIO1D3_LCDC0_DCLK << GPIO1D3_SHIFT | + GPIO1D2_LCDC0_DEN << GPIO1D2_SHIFT | + GPIO1D1_LCDC0_VSYNC << GPIO1D1_SHIFT | + GPIO1D0_LCDC0_HSYNC << GPIO1D0_SHIFT); + break; + default: + debug("lcdc id = %d iomux error!\n", lcd_id); + break; + } +} + +static int pinctrl_rk3288_spi_config(struct rk3288_grf *grf, + enum periph_id spi_id, int cs) +{ + switch (spi_id) { + case PERIPH_ID_SPI0: + switch (cs) { + case 0: + rk_clrsetreg(&grf->gpio5b_iomux, + GPIO5B5_MASK << GPIO5B5_SHIFT, + GPIO5B5_SPI0_CSN0 << GPIO5B5_SHIFT); + break; + case 1: + rk_clrsetreg(&grf->gpio5c_iomux, + GPIO5C0_MASK << GPIO5C0_SHIFT, + GPIO5C0_SPI0_CSN1 << GPIO5C0_SHIFT); + break; + default: + goto err; + } + rk_clrsetreg(&grf->gpio5b_iomux, + GPIO5B7_MASK << GPIO5B7_SHIFT | + GPIO5B6_MASK << GPIO5B6_SHIFT | + GPIO5B4_MASK << GPIO5B4_SHIFT, + GPIO5B7_SPI0_RXD << GPIO5B7_SHIFT | + GPIO5B6_SPI0_TXD << GPIO5B6_SHIFT | + GPIO5B4_SPI0_CLK << GPIO5B4_SHIFT); + break; + case PERIPH_ID_SPI1: + if (cs != 0) + goto err; + rk_clrsetreg(&grf->gpio7b_iomux, + GPIO7B6_MASK << GPIO7B6_SHIFT | + GPIO7B7_MASK << GPIO7B7_SHIFT | + GPIO7B5_MASK << GPIO7B5_SHIFT | + GPIO7B4_MASK << GPIO7B4_SHIFT, + GPIO7B6_SPI1_RXD << GPIO7B6_SHIFT | + GPIO7B7_SPI1_TXD << GPIO7B7_SHIFT | + GPIO7B5_SPI1_CSN0 << GPIO7B5_SHIFT | + GPIO7B4_SPI1_CLK << GPIO7B4_SHIFT); + break; + case PERIPH_ID_SPI2: + switch (cs) { + case 0: + rk_clrsetreg(&grf->gpio8a_iomux, + GPIO8A7_MASK << GPIO8A7_SHIFT, + GPIO8A7_SPI2_CSN0 << GPIO8A7_SHIFT); + break; + case 1: + rk_clrsetreg(&grf->gpio8a_iomux, + GPIO8A3_MASK << GPIO8A3_SHIFT, + GPIO8A3_SPI2_CSN1 << GPIO8A3_SHIFT); + break; + default: + goto err; + } + rk_clrsetreg(&grf->gpio8b_iomux, + GPIO8B1_MASK << GPIO8B1_SHIFT | + GPIO8B0_MASK << GPIO8B0_SHIFT, + GPIO8B1_SPI2_TXD << GPIO8B1_SHIFT | + GPIO8B0_SPI2_RXD << GPIO8B0_SHIFT); + rk_clrsetreg(&grf->gpio8a_iomux, + GPIO8A6_MASK << GPIO8A6_SHIFT, + GPIO8A6_SPI2_CLK << GPIO8A6_SHIFT); + break; + default: + goto err; + } + + return 0; +err: + debug("rkspi: periph%d cs=%d not supported", spi_id, cs); + return -ENOENT; +} + +static void pinctrl_rk3288_uart_config(struct rk3288_grf *grf, int uart_id) +{ + switch (uart_id) { + case PERIPH_ID_UART_BT: + rk_clrsetreg(&grf->gpio4c_iomux, + GPIO4C3_MASK << GPIO4C3_SHIFT | + GPIO4C2_MASK << GPIO4C2_SHIFT | + GPIO4C1_MASK << GPIO4C1_SHIFT | + GPIO4C0_MASK << GPIO4C0_SHIFT, + GPIO4C3_UART0BT_RTSN << GPIO4C3_SHIFT | + GPIO4C2_UART0BT_CTSN << GPIO4C2_SHIFT | + GPIO4C1_UART0BT_SOUT << GPIO4C1_SHIFT | + GPIO4C0_UART0BT_SIN << GPIO4C0_SHIFT); + break; + case PERIPH_ID_UART_BB: + rk_clrsetreg(&grf->gpio5b_iomux, + GPIO5B3_MASK << GPIO5B3_SHIFT | + GPIO5B2_MASK << GPIO5B2_SHIFT | + GPIO5B1_MASK << GPIO5B1_SHIFT | + GPIO5B0_MASK << GPIO5B0_SHIFT, + GPIO5B3_UART1BB_RTSN << GPIO5B3_SHIFT | + GPIO5B2_UART1BB_CTSN << GPIO5B2_SHIFT | + GPIO5B1_UART1BB_SOUT << GPIO5B1_SHIFT | + GPIO5B0_UART1BB_SIN << GPIO5B0_SHIFT); + break; + case PERIPH_ID_UART_DBG: + rk_clrsetreg(&grf->gpio7ch_iomux, + GPIO7C7_MASK << GPIO7C7_SHIFT | + GPIO7C6_MASK << GPIO7C6_SHIFT, + GPIO7C7_UART2DBG_SOUT << GPIO7C7_SHIFT | + GPIO7C6_UART2DBG_SIN << GPIO7C6_SHIFT); + break; + case PERIPH_ID_UART_GPS: + rk_clrsetreg(&grf->gpio7b_iomux, + GPIO7B2_MASK << GPIO7B2_SHIFT | + GPIO7B1_MASK << GPIO7B1_SHIFT | + GPIO7B0_MASK << GPIO7B0_SHIFT, + GPIO7B2_UART3GPS_RTSN << GPIO7B2_SHIFT | + GPIO7B1_UART3GPS_CTSN << GPIO7B1_SHIFT | + GPIO7B0_UART3GPS_SOUT << GPIO7B0_SHIFT); + rk_clrsetreg(&grf->gpio7a_iomux, + GPIO7A7_MASK << GPIO7A7_SHIFT, + GPIO7A7_UART3GPS_SIN << GPIO7A7_SHIFT); + break; + case PERIPH_ID_UART_EXP: + rk_clrsetreg(&grf->gpio5b_iomux, + GPIO5B5_MASK << GPIO5B5_SHIFT | + GPIO5B4_MASK << GPIO5B4_SHIFT | + GPIO5B6_MASK << GPIO5B6_SHIFT | + GPIO5B7_MASK << GPIO5B7_SHIFT, + GPIO5B5_UART4EXP_RTSN << GPIO5B5_SHIFT | + GPIO5B4_UART4EXP_CTSN << GPIO5B4_SHIFT | + GPIO5B6_UART4EXP_SOUT << GPIO5B6_SHIFT | + GPIO5B7_UART4EXP_SIN << GPIO5B7_SHIFT); + break; + default: + debug("uart id = %d iomux error!\n", uart_id); + break; + } +} + +static void pinctrl_rk3288_sdmmc_config(struct rk3288_grf *grf, int mmc_id) +{ + switch (mmc_id) { + case PERIPH_ID_EMMC: + rk_clrsetreg(&grf->gpio3a_iomux, 0xffff, + GPIO3A7_EMMC_DATA7 << GPIO3A7_SHIFT | + GPIO3A6_EMMC_DATA6 << GPIO3A6_SHIFT | + GPIO3A5_EMMC_DATA5 << GPIO3A5_SHIFT | + GPIO3A4_EMMC_DATA4 << GPIO3A4_SHIFT | + GPIO3A3_EMMC_DATA3 << GPIO3A3_SHIFT | + GPIO3A2_EMMC_DATA2 << GPIO3A2_SHIFT | + GPIO3A1_EMMC_DATA1 << GPIO3A1_SHIFT | + GPIO3A0_EMMC_DATA0 << GPIO3A0_SHIFT); + rk_clrsetreg(&grf->gpio3b_iomux, GPIO3B1_MASK << GPIO3B1_SHIFT, + GPIO3B1_EMMC_PWREN << GPIO3B1_SHIFT); + rk_clrsetreg(&grf->gpio3c_iomux, + GPIO3C0_MASK << GPIO3C0_SHIFT, + GPIO3C0_EMMC_CMD << GPIO3C0_SHIFT); + break; + case PERIPH_ID_SDCARD: + rk_clrsetreg(&grf->gpio6c_iomux, 0xffff, + GPIO6C6_SDMMC0_DECTN << GPIO6C6_SHIFT | + GPIO6C5_SDMMC0_CMD << GPIO6C5_SHIFT | + GPIO6C4_SDMMC0_CLKOUT << GPIO6C4_SHIFT | + GPIO6C3_SDMMC0_DATA3 << GPIO6C3_SHIFT | + GPIO6C2_SDMMC0_DATA2 << GPIO6C2_SHIFT | + GPIO6C1_SDMMC0_DATA1 << GPIO6C1_SHIFT | + GPIO6C0_SDMMC0_DATA0 << GPIO6C0_SHIFT); + + /* use sdmmc0 io, disable JTAG function */ + rk_clrsetreg(&grf->soc_con0, 1 << GRF_FORCE_JTAG_SHIFT, 0); + break; + default: + debug("mmc id = %d iomux error!\n", mmc_id); + break; + } +} + +static void pinctrl_rk3288_hdmi_config(struct rk3288_grf *grf, int hdmi_id) +{ + switch (hdmi_id) { + case PERIPH_ID_HDMI: + rk_clrsetreg(&grf->gpio7cl_iomux, GPIO7C3_MASK << GPIO7C3_SHIFT, + GPIO7C3_EDPHDMII2C_SDA << GPIO7C3_SHIFT); + rk_clrsetreg(&grf->gpio7ch_iomux, GPIO7C4_MASK << GPIO7C4_SHIFT, + GPIO7C4_EDPHDMII2C_SCL << GPIO7C4_SHIFT); + break; + default: + debug("hdmi id = %d iomux error!\n", hdmi_id); + break; + } +} + +static int rk3288_pinctrl_request(struct udevice *dev, int func, int flags) +{ + struct rk3288_pinctrl_priv *priv = dev_get_priv(dev); + + debug("%s: func=%x, flags=%x\n", __func__, func, flags); + switch (func) { + case PERIPH_ID_PWM0: + case PERIPH_ID_PWM1: + case PERIPH_ID_PWM2: + case PERIPH_ID_PWM3: + case PERIPH_ID_PWM4: + pinctrl_rk3288_pwm_config(priv->grf, func); + break; + case PERIPH_ID_I2C0: + case PERIPH_ID_I2C1: + case PERIPH_ID_I2C2: + case PERIPH_ID_I2C3: + case PERIPH_ID_I2C4: + case PERIPH_ID_I2C5: + pinctrl_rk3288_i2c_config(priv->grf, priv->pmu, func); + break; + case PERIPH_ID_SPI0: + case PERIPH_ID_SPI1: + case PERIPH_ID_SPI2: + pinctrl_rk3288_spi_config(priv->grf, func, flags); + break; + case PERIPH_ID_UART0: + case PERIPH_ID_UART1: + case PERIPH_ID_UART2: + case PERIPH_ID_UART3: + case PERIPH_ID_UART4: + pinctrl_rk3288_uart_config(priv->grf, func); + break; + case PERIPH_ID_LCDC0: + case PERIPH_ID_LCDC1: + pinctrl_rk3288_lcdc_config(priv->grf, func); + break; + case PERIPH_ID_SDMMC0: + case PERIPH_ID_SDMMC1: + pinctrl_rk3288_sdmmc_config(priv->grf, func); + break; + case PERIPH_ID_HDMI: + pinctrl_rk3288_hdmi_config(priv->grf, func); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int rk3288_pinctrl_get_periph_id(struct udevice *dev, + struct udevice *periph) +{ + u32 cell[3]; + int ret; + + ret = fdtdec_get_int_array(gd->fdt_blob, periph->of_offset, + "interrupts", cell, ARRAY_SIZE(cell)); + if (ret < 0) + return -EINVAL; + + switch (cell[1]) { + case 44: + return PERIPH_ID_SPI0; + case 45: + return PERIPH_ID_SPI1; + case 46: + return PERIPH_ID_SPI2; + case 60: + return PERIPH_ID_I2C0; + case 62: /* Note strange order */ + return PERIPH_ID_I2C1; + case 61: + return PERIPH_ID_I2C2; + case 63: + return PERIPH_ID_I2C3; + case 64: + return PERIPH_ID_I2C4; + case 65: + return PERIPH_ID_I2C5; + } + + return -ENOENT; +} + +static int rk3288_pinctrl_set_state_simple(struct udevice *dev, + struct udevice *periph) +{ + int func; + + func = rk3288_pinctrl_get_periph_id(dev, periph); + if (func < 0) + return func; + return rk3288_pinctrl_request(dev, func, 0); +} + +static struct pinctrl_ops rk3288_pinctrl_ops = { + .set_state_simple = rk3288_pinctrl_set_state_simple, + .request = rk3288_pinctrl_request, + .get_periph_id = rk3288_pinctrl_get_periph_id, +}; + +static int rk3288_pinctrl_probe(struct udevice *dev) +{ + struct rk3288_pinctrl_priv *priv = dev_get_priv(dev); + + priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); + priv->pmu = syscon_get_first_range(ROCKCHIP_SYSCON_PMU); + debug("%s: grf=%p, pmu=%p\n", __func__, priv->grf, priv->pmu); + + return 0; +} + +static const struct udevice_id rk3288_pinctrl_ids[] = { + { .compatible = "rockchip,rk3288-pinctrl" }, + { } +}; + +U_BOOT_DRIVER(pinctrl_rk3288) = { + .name = "pinctrl_rk3288", + .id = UCLASS_PINCTRL, + .of_match = rk3288_pinctrl_ids, + .priv_auto_alloc_size = sizeof(struct rk3288_pinctrl_priv), + .ops = &rk3288_pinctrl_ops, + .probe = rk3288_pinctrl_probe, +}; -- cgit v1.2.1 From a8cb4fb56ac53cab89657b7e8295ec001bbf78bf Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 30 Aug 2015 16:55:37 -0600 Subject: rockchip: Add an MMC driver Add an MMC driver which supports RK3288, but may also support other SoCs. It uses the Designware MMC device. Signed-off-by: Simon Glass --- drivers/mmc/Kconfig | 9 ++++ drivers/mmc/Makefile | 1 + drivers/mmc/rockchip_dw_mmc.c | 98 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 drivers/mmc/rockchip_dw_mmc.c (limited to 'drivers') diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 3e835f7bca..6277f92ef5 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -10,6 +10,15 @@ config DM_MMC appear as block devices in U-Boot and can support filesystems such as EXT4 and FAT. +config ROCKCHIP_DWMMC + bool "Rockchip SD/MMC controller support" + depends on DM_MMC && OF_CONTROL + help + This enables support for the Rockchip SD/MMM controller, which is + based on Designware IP. The device is compatible with at least + SD 3.0, SDIO 3.0 and MMC 4.5 and supports common eMMC chips as well + as removeable SD and micro-SD cards. + config SH_SDHI bool "SuperH/Renesas ARM SoCs on-chip SDHI host controller support" depends on RMOBILE diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index cae207c303..99d02954ed 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_MXS_MMC) += mxsmmc.o obj-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o obj-$(CONFIG_X86) += pci_mmc.o obj-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o +obj-$(CONFIG_ROCKCHIP_DWMMC) += rockchip_dw_mmc.o obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o obj-$(CONFIG_S3C_SDI) += s3c_sdi.o obj-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o diff --git a/drivers/mmc/rockchip_dw_mmc.c b/drivers/mmc/rockchip_dw_mmc.c new file mode 100644 index 0000000000..f11c8e0039 --- /dev/null +++ b/drivers/mmc/rockchip_dw_mmc.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +struct rockchip_dwmmc_priv { + struct udevice *clk; + struct rk3288_grf *grf; + struct dwmci_host host; +}; + +static uint rockchip_dwmmc_get_mmc_clk(struct dwmci_host *host, uint freq) +{ + struct udevice *dev = host->priv; + struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); + int ret; + + ret = clk_set_periph_rate(priv->clk, PERIPH_ID_SDMMC0 + host->dev_index, + freq); + if (ret < 0) { + debug("%s: err=%d\n", __func__, ret); + return ret; + } + + return freq; +} + +static int rockchip_dwmmc_ofdata_to_platdata(struct udevice *dev) +{ + struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); + struct dwmci_host *host = &priv->host; + + host->name = dev->name; + host->ioaddr = (void *)dev_get_addr(dev); + host->buswidth = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + "bus-width", 4); + host->get_mmc_clk = rockchip_dwmmc_get_mmc_clk; + host->priv = dev; + + /* TODO(sjg@chromium.org): Remove the need for this hack */ + host->dev_index = (ulong)host->ioaddr == 0xff0f0000 ? 0 : 1; + + return 0; +} + +static int rockchip_dwmmc_probe(struct udevice *dev) +{ + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); + struct dwmci_host *host = &priv->host; + u32 minmax[2]; + int ret; + + priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); + if (IS_ERR(priv->grf)) + return PTR_ERR(priv->grf); + ret = uclass_get_device(UCLASS_CLK, CLK_GENERAL, &priv->clk); + if (ret) + return ret; + + ret = fdtdec_get_int_array(gd->fdt_blob, dev->of_offset, + "clock-freq-min-max", minmax, 2); + if (!ret) + ret = add_dwmci(host, minmax[1], minmax[0]); + if (ret) + return ret; + + upriv->mmc = host->mmc; + + return 0; +} + +static const struct udevice_id rockchip_dwmmc_ids[] = { + { .compatible = "rockchip,rk3288-dw-mshc" }, + { } +}; + +U_BOOT_DRIVER(rockchip_dwmmc_drv) = { + .name = "rockchip_dwmmc", + .id = UCLASS_MMC, + .of_match = rockchip_dwmmc_ids, + .ofdata_to_platdata = rockchip_dwmmc_ofdata_to_platdata, + .probe = rockchip_dwmmc_probe, + .priv_auto_alloc_size = sizeof(struct rockchip_dwmmc_priv), +}; -- cgit v1.2.1 From 3437469985df7c5403fa6e29b224e84c63bdbd52 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 30 Aug 2015 16:55:39 -0600 Subject: rockchip: Add I2C driver Add an I2C driver for the Rockchip RK3288, using driver model. It should work for other Rockchip SoCs also. Signed-off-by: Simon Glass --- drivers/i2c/Kconfig | 9 ++ drivers/i2c/Makefile | 1 + drivers/i2c/rk_i2c.c | 391 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 401 insertions(+) create mode 100644 drivers/i2c/rk_i2c.c (limited to 'drivers') diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index c40bd5c1e0..14adda2857 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -58,6 +58,15 @@ config DM_I2C_GPIO bindings are supported. Binding info: doc/device-tree-bindings/i2c/i2c-gpio.txt +config SYS_I2C_ROCKCHIP + bool "Rockchip I2C driver" + depends on DM_I2C + help + Add support for the Rockchip I2C driver. This is used with various + Rockchip parts such as RK3126, RK3128, RK3036 and RK3288. All chips + have several I2C ports and all are provided, controled by the + device tree. + config SYS_I2C_SANDBOX bool "Sandbox I2C driver" depends on SANDBOX && DM_I2C diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 9b45248e2e..8ce294b02a 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_SYS_I2C_OMAP24XX) += omap24xx_i2c.o obj-$(CONFIG_SYS_I2C_OMAP34XX) += omap24xx_i2c.o obj-$(CONFIG_SYS_I2C_PPC4XX) += ppc4xx_i2c.o obj-$(CONFIG_SYS_I2C_RCAR) += rcar_i2c.o +obj-$(CONFIG_SYS_I2C_ROCKCHIP) += rk_i2c.o obj-$(CONFIG_SYS_I2C_S3C24X0) += s3c24x0_i2c.o obj-$(CONFIG_SYS_I2C_SANDBOX) += sandbox_i2c.o i2c-emul-uclass.o obj-$(CONFIG_SYS_I2C_SH) += sh_i2c.o diff --git a/drivers/i2c/rk_i2c.c b/drivers/i2c/rk_i2c.c new file mode 100644 index 0000000000..ebdba35dc6 --- /dev/null +++ b/drivers/i2c/rk_i2c.c @@ -0,0 +1,391 @@ +/* + * (C) Copyright 2015 Google, Inc + * + * (C) Copyright 2008-2014 Rockchip Electronics + * Peter, Software Engineering, . + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* i2c timerout */ +#define I2C_TIMEOUT_MS 100 +#define I2C_RETRY_COUNT 3 + +/* rk i2c fifo max transfer bytes */ +#define RK_I2C_FIFO_SIZE 32 + +struct rk_i2c { + struct udevice *clk; + struct udevice *pinctrl; + struct i2c_regs *regs; + unsigned int speed; + enum periph_id id; +}; + +static inline void rk_i2c_get_div(int div, int *divh, int *divl) +{ + *divl = div / 2; + if (div % 2 == 0) + *divh = div / 2; + else + *divh = DIV_ROUND_UP(div, 2); +} + +/* + * SCL Divisor = 8 * (CLKDIVL+1 + CLKDIVH+1) + * SCL = PCLK / SCLK Divisor + * i2c_rate = PCLK + */ +static void rk_i2c_set_clk(struct rk_i2c *i2c, uint32_t scl_rate) +{ + uint32_t i2c_rate; + int div, divl, divh; + + /* First get i2c rate from pclk */ + i2c_rate = clk_get_periph_rate(i2c->clk, i2c->id); + + div = DIV_ROUND_UP(i2c_rate, scl_rate * 8) - 2; + divh = 0; + divl = 0; + if (div >= 0) + rk_i2c_get_div(div, &divh, &divl); + writel(I2C_CLKDIV_VAL(divl, divh), &i2c->regs->clkdiv); + + debug("rk_i2c_set_clk: i2c rate = %d, scl rate = %d\n", i2c_rate, + scl_rate); + debug("set i2c clk div = %d, divh = %d, divl = %d\n", div, divh, divl); + debug("set clk(I2C_CLKDIV: 0x%08x)\n", readl(&i2c->regs->clkdiv)); +} + +static void rk_i2c_show_regs(struct i2c_regs *regs) +{ +#ifdef DEBUG + uint i; + + debug("i2c_con: 0x%08x\n", readl(®s->con)); + debug("i2c_clkdiv: 0x%08x\n", readl(®s->clkdiv)); + debug("i2c_mrxaddr: 0x%08x\n", readl(®s->mrxaddr)); + debug("i2c_mrxraddR: 0x%08x\n", readl(®s->mrxraddr)); + debug("i2c_mtxcnt: 0x%08x\n", readl(®s->mtxcnt)); + debug("i2c_mrxcnt: 0x%08x\n", readl(®s->mrxcnt)); + debug("i2c_ien: 0x%08x\n", readl(®s->ien)); + debug("i2c_ipd: 0x%08x\n", readl(®s->ipd)); + debug("i2c_fcnt: 0x%08x\n", readl(®s->fcnt)); + for (i = 0; i < 8; i++) + debug("i2c_txdata%d: 0x%08x\n", i, readl(®s->txdata[i])); + for (i = 0; i < 8; i++) + debug("i2c_rxdata%d: 0x%08x\n", i, readl(®s->rxdata[i])); +#endif +} + +static int rk_i2c_send_start_bit(struct rk_i2c *i2c) +{ + struct i2c_regs *regs = i2c->regs; + ulong start; + + debug("I2c Send Start bit.\n"); + writel(I2C_IPD_ALL_CLEAN, ®s->ipd); + + writel(I2C_CON_EN | I2C_CON_START, ®s->con); + writel(I2C_STARTIEN, ®s->ien); + + start = get_timer(0); + while (1) { + if (readl(®s->ipd) & I2C_STARTIPD) { + writel(I2C_STARTIPD, ®s->ipd); + break; + } + if (get_timer(start) > I2C_TIMEOUT_MS) { + debug("I2C Send Start Bit Timeout\n"); + rk_i2c_show_regs(regs); + return -ETIMEDOUT; + } + udelay(1); + } + + return 0; +} + +static int rk_i2c_send_stop_bit(struct rk_i2c *i2c) +{ + struct i2c_regs *regs = i2c->regs; + ulong start; + + debug("I2c Send Stop bit.\n"); + writel(I2C_IPD_ALL_CLEAN, ®s->ipd); + + writel(I2C_CON_EN | I2C_CON_STOP, ®s->con); + writel(I2C_CON_STOP, ®s->ien); + + start = get_timer(0); + while (1) { + if (readl(®s->ipd) & I2C_STOPIPD) { + writel(I2C_STOPIPD, ®s->ipd); + break; + } + if (get_timer(start) > I2C_TIMEOUT_MS) { + debug("I2C Send Start Bit Timeout\n"); + rk_i2c_show_regs(regs); + return -ETIMEDOUT; + } + udelay(1); + } + + return 0; +} + +static inline void rk_i2c_disable(struct rk_i2c *i2c) +{ + writel(0, &i2c->regs->con); +} + +static int rk_i2c_read(struct rk_i2c *i2c, uchar chip, uint reg, uint r_len, + uchar *buf, uint b_len) +{ + struct i2c_regs *regs = i2c->regs; + uchar *pbuf = buf; + uint bytes_remain_len = b_len; + uint bytes_xferred = 0; + uint words_xferred = 0; + ulong start; + uint con = 0; + uint rxdata; + uint i, j; + int err; + + debug("rk_i2c_read: chip = %d, reg = %d, r_len = %d, b_len = %d\n", + chip, reg, r_len, b_len); + + err = rk_i2c_send_start_bit(i2c); + if (err) + return err; + + writel(I2C_MRXADDR_SET(1, chip << 1 | 1), ®s->mrxaddr); + if (r_len == 0) { + writel(0, ®s->mrxraddr); + } else if (r_len < 4) { + writel(I2C_MRXRADDR_SET(r_len, reg), ®s->mrxraddr); + } else { + debug("I2C Read: addr len %d not supported\n", r_len); + return -EIO; + } + + while (bytes_remain_len) { + if (bytes_remain_len > RK_I2C_FIFO_SIZE) { + con = I2C_CON_EN | I2C_CON_MOD(I2C_MODE_TRX); + bytes_xferred = 32; + } else { + con = I2C_CON_EN | I2C_CON_MOD(I2C_MODE_TRX) | + I2C_CON_LASTACK; + bytes_xferred = bytes_remain_len; + } + words_xferred = DIV_ROUND_UP(bytes_xferred, 4); + + writel(con, ®s->con); + writel(bytes_xferred, ®s->mrxcnt); + writel(I2C_MBRFIEN | I2C_NAKRCVIEN, ®s->ien); + + start = get_timer(0); + while (1) { + if (readl(®s->ipd) & I2C_NAKRCVIPD) { + writel(I2C_NAKRCVIPD, ®s->ipd); + err = -EREMOTEIO; + } + if (readl(®s->ipd) & I2C_MBRFIPD) { + writel(I2C_MBRFIPD, ®s->ipd); + break; + } + if (get_timer(start) > I2C_TIMEOUT_MS) { + debug("I2C Read Data Timeout\n"); + err = -ETIMEDOUT; + rk_i2c_show_regs(regs); + goto i2c_exit; + } + udelay(1); + } + + for (i = 0; i < words_xferred; i++) { + rxdata = readl(®s->rxdata[i]); + debug("I2c Read RXDATA[%d] = 0x%x\n", i, rxdata); + for (j = 0; j < 4; j++) { + if ((i * 4 + j) == bytes_xferred) + break; + *pbuf++ = (rxdata >> (j * 8)) & 0xff; + } + } + + bytes_remain_len -= bytes_xferred; + debug("I2C Read bytes_remain_len %d\n", bytes_remain_len); + } + +i2c_exit: + rk_i2c_send_stop_bit(i2c); + rk_i2c_disable(i2c); + + return err; +} + +static int rk_i2c_write(struct rk_i2c *i2c, uchar chip, uint reg, uint r_len, + uchar *buf, uint b_len) +{ + struct i2c_regs *regs = i2c->regs; + int err; + uchar *pbuf = buf; + uint bytes_remain_len = b_len + r_len + 1; + uint bytes_xferred = 0; + uint words_xferred = 0; + ulong start; + uint txdata; + uint i, j; + + debug("rk_i2c_write: chip = %d, reg = %d, r_len = %d, b_len = %d\n", + chip, reg, r_len, b_len); + err = rk_i2c_send_start_bit(i2c); + if (err) + return err; + + while (bytes_remain_len) { + if (bytes_remain_len > RK_I2C_FIFO_SIZE) + bytes_xferred = 32; + else + bytes_xferred = bytes_remain_len; + words_xferred = DIV_ROUND_UP(bytes_xferred, 4); + + for (i = 0; i < words_xferred; i++) { + txdata = 0; + for (j = 0; j < 4; j++) { + if ((i * 4 + j) == bytes_xferred) + break; + + if (i == 0 && j == 0) { + txdata |= (chip << 1); + } else if (i == 0 && j <= r_len) { + txdata |= (reg & + (0xff << ((j - 1) * 8))) << 8; + } else { + txdata |= (*pbuf++)<<(j * 8); + } + writel(txdata, ®s->txdata[i]); + } + debug("I2c Write TXDATA[%d] = 0x%x\n", i, txdata); + } + + writel(I2C_CON_EN | I2C_CON_MOD(I2C_MODE_TX), ®s->con); + writel(bytes_xferred, ®s->mtxcnt); + writel(I2C_MBTFIEN | I2C_NAKRCVIEN, ®s->ien); + + start = get_timer(0); + while (1) { + if (readl(®s->ipd) & I2C_NAKRCVIPD) { + writel(I2C_NAKRCVIPD, ®s->ipd); + err = -EREMOTEIO; + } + if (readl(®s->ipd) & I2C_MBTFIPD) { + writel(I2C_MBTFIPD, ®s->ipd); + break; + } + if (get_timer(start) > I2C_TIMEOUT_MS) { + debug("I2C Write Data Timeout\n"); + err = -ETIMEDOUT; + rk_i2c_show_regs(regs); + goto i2c_exit; + } + udelay(1); + } + + bytes_remain_len -= bytes_xferred; + debug("I2C Write bytes_remain_len %d\n", bytes_remain_len); + } + +i2c_exit: + rk_i2c_send_stop_bit(i2c); + rk_i2c_disable(i2c); + + return err; +} + +static int rockchip_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, + int nmsgs) +{ + struct rk_i2c *i2c = dev_get_priv(bus); + int ret; + + debug("i2c_xfer: %d messages\n", nmsgs); + for (; nmsgs > 0; nmsgs--, msg++) { + debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len); + if (msg->flags & I2C_M_RD) { + ret = rk_i2c_read(i2c, msg->addr, 0, 0, msg->buf, + msg->len); + } else { + ret = rk_i2c_write(i2c, msg->addr, 0, 0, msg->buf, + msg->len); + } + if (ret) { + debug("i2c_write: error sending\n"); + return -EREMOTEIO; + } + } + + return 0; +} + +int rockchip_i2c_set_bus_speed(struct udevice *bus, unsigned int speed) +{ + struct rk_i2c *i2c = dev_get_priv(bus); + + rk_i2c_set_clk(i2c, speed); + + return 0; +} + +static int rockchip_i2c_probe(struct udevice *bus) +{ + struct rk_i2c *i2c = dev_get_priv(bus); + int ret; + + ret = uclass_get_device(UCLASS_PINCTRL, 0, &i2c->pinctrl); + if (ret) + return ret; + ret = uclass_get_device(UCLASS_CLK, 0, &i2c->clk); + if (ret) + return ret; + ret = pinctrl_get_periph_id(i2c->pinctrl, bus); + if (ret < 0) + return ret; + i2c->id = ret; + i2c->regs = (void *)dev_get_addr(bus); + return pinctrl_request(i2c->pinctrl, i2c->id, 0); +} + +static const struct dm_i2c_ops rockchip_i2c_ops = { + .xfer = rockchip_i2c_xfer, + .set_bus_speed = rockchip_i2c_set_bus_speed, +}; + +static const struct udevice_id rockchip_i2c_ids[] = { + { .compatible = "rockchip,rk3288-i2c" }, + { } +}; + +U_BOOT_DRIVER(i2c_rockchip) = { + .name = "i2c_rockchip", + .id = UCLASS_I2C, + .of_match = rockchip_i2c_ids, + .probe = rockchip_i2c_probe, + .priv_auto_alloc_size = sizeof(struct rk_i2c), + .ops = &rockchip_i2c_ops, +}; -- cgit v1.2.1 From 1b2fd5bf4eedfaf5af9d8fc219781fb521d12c7a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 1 Sep 2015 19:19:37 -0600 Subject: rockchip: Add SPI driver Add a SPI driver for the Rockchip RK3288, using driver model. It should work for other Rockchip SoCs also. Signed-off-by: Simon Glass --- drivers/spi/Kconfig | 8 ++ drivers/spi/Makefile | 1 + drivers/spi/rk_spi.c | 374 +++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/spi/rk_spi.h | 124 +++++++++++++++++ 4 files changed, 507 insertions(+) create mode 100644 drivers/spi/rk_spi.c create mode 100644 drivers/spi/rk_spi.h (limited to 'drivers') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index c84a7b74d6..8e04fce6f2 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -58,6 +58,14 @@ config ICH_SPI access the SPI NOR flash on platforms embedding this Intel ICH IP core. +config ROCKCHIP_SPI + bool "Rockchip SPI driver" + help + Enable the Rockchip SPI driver, used to access SPI NOR flash and + other SPI peripherals (such as the Chrome OS EC) on Rockchip SoCs. + This uses driver model and requires a device tree binding to + operate. + config SANDBOX_SPI bool "Sandbox SPI driver" depends on SANDBOX && DM diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index ee88aa162b..de241be15a 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o obj-$(CONFIG_MXC_SPI) += mxc_spi.o obj-$(CONFIG_MXS_SPI) += mxs_spi.o obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o +obj-$(CONFIG_ROCKCHIP_SPI) += rk_spi.o obj-$(CONFIG_SANDBOX_SPI) += sandbox_spi.o obj-$(CONFIG_SH_SPI) += sh_spi.o obj-$(CONFIG_SH_QSPI) += sh_qspi.o diff --git a/drivers/spi/rk_spi.c b/drivers/spi/rk_spi.c new file mode 100644 index 0000000000..5e0c6ad278 --- /dev/null +++ b/drivers/spi/rk_spi.c @@ -0,0 +1,374 @@ +/* + * spi driver for rockchip + * + * (C) Copyright 2015 Google, Inc + * + * (C) Copyright 2008-2013 Rockchip Electronics + * Peter, Software Engineering, . + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rk_spi.h" + +DECLARE_GLOBAL_DATA_PTR; + +/* Change to 1 to output registers at the start of each transaction */ +#define DEBUG_RK_SPI 0 + +struct rockchip_spi_platdata { + enum periph_id periph_id; + struct udevice *pinctrl; + s32 frequency; /* Default clock frequency, -1 for none */ + fdt_addr_t base; + uint deactivate_delay_us; /* Delay to wait after deactivate */ +}; + +struct rockchip_spi_priv { + struct rockchip_spi *regs; + struct udevice *clk_gpll; + unsigned int max_freq; + unsigned int mode; + enum periph_id periph_id; /* Peripheral ID for this device */ + ulong last_transaction_us; /* Time of last transaction end */ + u8 bits_per_word; /* max 16 bits per word */ + u8 n_bytes; + unsigned int speed_hz; + unsigned int tmode; + uint input_rate; +}; + +#define SPI_FIFO_DEPTH 32 + +static void rkspi_dump_regs(struct rockchip_spi *regs) +{ + debug("ctrl0: \t\t0x%08x\n", readl(®s->ctrlr0)); + debug("ctrl1: \t\t0x%08x\n", readl(®s->ctrlr1)); + debug("ssienr: \t\t0x%08x\n", readl(®s->enr)); + debug("ser: \t\t0x%08x\n", readl(®s->ser)); + debug("baudr: \t\t0x%08x\n", readl(®s->baudr)); + debug("txftlr: \t\t0x%08x\n", readl(®s->txftlr)); + debug("rxftlr: \t\t0x%08x\n", readl(®s->rxftlr)); + debug("txflr: \t\t0x%08x\n", readl(®s->txflr)); + debug("rxflr: \t\t0x%08x\n", readl(®s->rxflr)); + debug("sr: \t\t0x%08x\n", readl(®s->sr)); + debug("imr: \t\t0x%08x\n", readl(®s->imr)); + debug("isr: \t\t0x%08x\n", readl(®s->isr)); + debug("dmacr: \t\t0x%08x\n", readl(®s->dmacr)); + debug("dmatdlr: \t0x%08x\n", readl(®s->dmatdlr)); + debug("dmardlr: \t0x%08x\n", readl(®s->dmardlr)); +} + +static void rkspi_enable_chip(struct rockchip_spi *regs, bool enable) +{ + writel(enable ? 1 : 0, ®s->enr); +} + +static void rkspi_set_clk(struct rockchip_spi_priv *priv, uint speed) +{ + uint clk_div; + + clk_div = clk_get_divisor(priv->input_rate, speed); + debug("spi speed %u, div %u\n", speed, clk_div); + + writel(clk_div, &priv->regs->baudr); +} + +static int rkspi_wait_till_not_busy(struct rockchip_spi *regs) +{ + unsigned long start; + + start = get_timer(0); + while (readl(®s->sr) & SR_BUSY) { + if (get_timer(start) > ROCKCHIP_SPI_TIMEOUT_MS) { + debug("RK SPI: Status keeps busy for 1000us after a read/write!\n"); + return -ETIMEDOUT; + } + } + + return 0; +} + +static void spi_cs_activate(struct rockchip_spi *regs, uint cs) +{ + debug("activate cs%u\n", cs); + writel(1 << cs, ®s->ser); +} + +static void spi_cs_deactivate(struct rockchip_spi *regs, uint cs) +{ + debug("deactivate cs%u\n", cs); + writel(0, ®s->ser); +} + +static int rockchip_spi_ofdata_to_platdata(struct udevice *bus) +{ + struct rockchip_spi_platdata *plat = bus->platdata; + const void *blob = gd->fdt_blob; + int node = bus->of_offset; + int ret; + + plat->base = dev_get_addr(bus); + ret = uclass_get_device(UCLASS_PINCTRL, 0, &plat->pinctrl); + if (ret) + return ret; + ret = pinctrl_get_periph_id(plat->pinctrl, bus); + + if (ret < 0) { + debug("%s: Could not get peripheral ID for %s: %d\n", __func__, + bus->name, ret); + return -FDT_ERR_NOTFOUND; + } + plat->periph_id = ret; + + plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency", + 50000000); + plat->deactivate_delay_us = fdtdec_get_int(blob, node, + "spi-deactivate-delay", 0); + debug("%s: base=%x, periph_id=%d, max-frequency=%d, deactivate_delay=%d\n", + __func__, plat->base, plat->periph_id, plat->frequency, + plat->deactivate_delay_us); + + return 0; +} + +static int rockchip_spi_probe(struct udevice *bus) +{ + struct rockchip_spi_platdata *plat = dev_get_platdata(bus); + struct rockchip_spi_priv *priv = dev_get_priv(bus); + int ret; + + debug("%s: probe\n", __func__); + priv->regs = (struct rockchip_spi *)plat->base; + + priv->last_transaction_us = timer_get_us(); + priv->max_freq = plat->frequency; + priv->periph_id = plat->periph_id; + ret = uclass_get_device(UCLASS_CLK, CLK_GENERAL, &priv->clk_gpll); + if (ret) { + debug("%s: Failed to find CLK_GENERAL: %d\n", __func__, ret); + return ret; + } + + /* + * Use 99 MHz as our clock since it divides nicely into 594 MHz which + * is the assumed speed for CLK_GENERAL. + */ + ret = clk_set_periph_rate(priv->clk_gpll, plat->periph_id, 99000000); + if (ret < 0) { + debug("%s: Failed to set clock: %d\n", __func__, ret); + return ret; + } + priv->input_rate = ret; + debug("%s: rate = %u\n", __func__, priv->input_rate); + priv->bits_per_word = 8; + priv->tmode = TMOD_TR; /* Tx & Rx */ + + return 0; +} + +static int rockchip_spi_claim_bus(struct udevice *dev) +{ + struct udevice *bus = dev->parent; + struct rockchip_spi_platdata *plat = dev_get_platdata(bus); + struct rockchip_spi_priv *priv = dev_get_priv(bus); + struct rockchip_spi *regs = priv->regs; + struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev); + u8 spi_dfs, spi_tf; + uint ctrlr0; + int ret; + + /* Disable the SPI hardware */ + rkspi_enable_chip(regs, 0); + + switch (priv->bits_per_word) { + case 8: + priv->n_bytes = 1; + spi_dfs = DFS_8BIT; + spi_tf = HALF_WORD_OFF; + break; + case 16: + priv->n_bytes = 2; + spi_dfs = DFS_16BIT; + spi_tf = HALF_WORD_ON; + break; + default: + debug("%s: unsupported bits: %dbits\n", __func__, + priv->bits_per_word); + return -EPROTONOSUPPORT; + } + + rkspi_set_clk(priv, priv->speed_hz); + + /* Operation Mode */ + ctrlr0 = OMOD_MASTER << OMOD_SHIFT; + + /* Data Frame Size */ + ctrlr0 |= spi_dfs & DFS_MASK << DFS_SHIFT; + + /* set SPI mode 0..3 */ + if (priv->mode & SPI_CPOL) + ctrlr0 |= SCOL_HIGH << SCOL_SHIFT; + if (priv->mode & SPI_CPHA) + ctrlr0 |= SCPH_TOGSTA << SCPH_SHIFT; + + /* Chip Select Mode */ + ctrlr0 |= CSM_KEEP << CSM_SHIFT; + + /* SSN to Sclk_out delay */ + ctrlr0 |= SSN_DELAY_ONE << SSN_DELAY_SHIFT; + + /* Serial Endian Mode */ + ctrlr0 |= SEM_LITTLE << SEM_SHIFT; + + /* First Bit Mode */ + ctrlr0 |= FBM_MSB << FBM_SHIFT; + + /* Byte and Halfword Transform */ + ctrlr0 |= (spi_tf & HALF_WORD_MASK) << HALF_WORD_TX_SHIFT; + + /* Rxd Sample Delay */ + ctrlr0 |= 0 << RXDSD_SHIFT; + + /* Frame Format */ + ctrlr0 |= FRF_SPI << FRF_SHIFT; + + /* Tx and Rx mode */ + ctrlr0 |= (priv->tmode & TMOD_MASK) << TMOD_SHIFT; + + writel(ctrlr0, ®s->ctrlr0); + + ret = pinctrl_request(plat->pinctrl, priv->periph_id, slave_plat->cs); + if (ret) { + debug("%s: Cannot request pinctrl: %d\n", __func__, ret); + return ret; + } + + return 0; +} + +static int rockchip_spi_release_bus(struct udevice *dev) +{ + return 0; +} + +static int rockchip_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct udevice *bus = dev->parent; + struct rockchip_spi_priv *priv = dev_get_priv(bus); + struct rockchip_spi *regs = priv->regs; + struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev); + int len = bitlen >> 3; + const u8 *out = dout; + u8 *in = din; + int toread, towrite; + int ret; + + debug("%s: dout=%p, din=%p, len=%x, flags=%lx\n", __func__, dout, din, + len, flags); + if (DEBUG_RK_SPI) + rkspi_dump_regs(regs); + + /* Assert CS before transfer */ + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(regs, slave_plat->cs); + + while (len > 0) { + int todo = min(len, 0xffff); + + rkspi_enable_chip(regs, true); + writel(todo - 1, ®s->ctrlr1); + rkspi_enable_chip(regs, true); + + toread = todo; + towrite = todo; + while (toread || towrite) { + u32 status = readl(®s->sr); + + if (towrite && !(status & SR_TF_FULL)) { + writel(out ? *out++ : 0, regs->txdr); + towrite--; + } + if (toread && !(status & SR_RF_EMPT)) { + u32 byte = readl(regs->rxdr); + + if (in) + *in++ = byte; + toread--; + } + } + ret = rkspi_wait_till_not_busy(regs); + if (ret) + break; + len -= todo; + } + + /* Deassert CS after transfer */ + if (flags & SPI_XFER_END) + spi_cs_deactivate(regs, slave_plat->cs); + + rkspi_enable_chip(regs, false); + + return ret; +} + +static int rockchip_spi_set_speed(struct udevice *bus, uint speed) +{ + struct rockchip_spi_priv *priv = dev_get_priv(bus); + + if (speed > ROCKCHIP_SPI_MAX_RATE) + return -EINVAL; + if (speed > priv->max_freq) + speed = priv->max_freq; + priv->speed_hz = speed; + + return 0; +} + +static int rockchip_spi_set_mode(struct udevice *bus, uint mode) +{ + struct rockchip_spi_priv *priv = dev_get_priv(bus); + + priv->mode = mode; + + return 0; +} + +static const struct dm_spi_ops rockchip_spi_ops = { + .claim_bus = rockchip_spi_claim_bus, + .release_bus = rockchip_spi_release_bus, + .xfer = rockchip_spi_xfer, + .set_speed = rockchip_spi_set_speed, + .set_mode = rockchip_spi_set_mode, + /* + * cs_info is not needed, since we require all chip selects to be + * in the device tree explicitly + */ +}; + +static const struct udevice_id rockchip_spi_ids[] = { + { .compatible = "rockchip,rk3288-spi" }, + { } +}; + +U_BOOT_DRIVER(rockchip_spi) = { + .name = "rockchip_spi", + .id = UCLASS_SPI, + .of_match = rockchip_spi_ids, + .ops = &rockchip_spi_ops, + .ofdata_to_platdata = rockchip_spi_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct rockchip_spi_platdata), + .priv_auto_alloc_size = sizeof(struct rockchip_spi_priv), + .probe = rockchip_spi_probe, +}; diff --git a/drivers/spi/rk_spi.h b/drivers/spi/rk_spi.h new file mode 100644 index 0000000000..f1ac81203f --- /dev/null +++ b/drivers/spi/rk_spi.h @@ -0,0 +1,124 @@ +/* + * SPI driver for rockchip + * + * (C) Copyright 2015 Google, Inc + * + * (C) Copyright 2008-2013 Rockchip Electronics + * Peter, Software Engineering, . + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __RK_SPI_H +#define __RK_SPI_H + +struct rockchip_spi { + u32 ctrlr0; + u32 ctrlr1; + u32 enr; + u32 ser; + u32 baudr; + u32 txftlr; + u32 rxftlr; + u32 txflr; + u32 rxflr; + u32 sr; + u32 ipr; + u32 imr; + u32 isr; + u32 risr; + u32 icr; + u32 dmacr; + u32 dmatdlr; + u32 dmardlr; /* 0x44 */ + u32 reserved[0xef]; + u32 txdr[0x100]; /* 0x400 */ + u32 rxdr[0x100]; /* 0x800 */ +}; + +/* CTRLR0 */ +enum { + DFS_SHIFT = 0, /* Data Frame Size */ + DFS_MASK = 3, + DFS_4BIT = 0, + DFS_8BIT, + DFS_16BIT, + DFS_RESV, + + CFS_SHIFT = 2, /* Control Frame Size */ + CFS_MASK = 0xf, + + SCPH_SHIFT = 6, /* Serial Clock Phase */ + SCPH_MASK = 1, + SCPH_TOGMID = 0, /* SCLK toggles in middle of first data bit */ + SCPH_TOGSTA, /* SCLK toggles at start of first data bit */ + + SCOL_SHIFT = 7, /* Serial Clock Polarity */ + SCOL_MASK = 1, + SCOL_LOW = 0, /* Inactive state of serial clock is low */ + SCOL_HIGH, /* Inactive state of serial clock is high */ + + CSM_SHIFT = 8, /* Chip Select Mode */ + CSM_MASK = 0x3, + CSM_KEEP = 0, /* ss_n stays low after each frame */ + CSM_HALF, /* ss_n high for half sclk_out cycles */ + CSM_ONE, /* ss_n high for one sclk_out cycle */ + CSM_RESV, + + SSN_DELAY_SHIFT = 10, /* SSN to Sclk_out delay */ + SSN_DELAY_MASK = 1, + SSN_DELAY_HALF = 0, /* 1/2 sclk_out cycle */ + SSN_DELAY_ONE = 1, /* 1 sclk_out cycle */ + + SEM_SHIFT = 11, /* Serial Endian Mode */ + SEM_MASK = 1, + SEM_LITTLE = 0, /* little endian */ + SEM_BIG, /* big endian */ + + FBM_SHIFT = 12, /* First Bit Mode */ + FBM_MASK = 1, + FBM_MSB = 0, /* first bit is MSB */ + FBM_LSB, /* first bit in LSB */ + + HALF_WORD_TX_SHIFT = 13, /* Byte and Halfword Transform */ + HALF_WORD_MASK = 1, + HALF_WORD_ON = 0, /* apb 16bit write/read, spi 8bit write/read */ + HALF_WORD_OFF, /* apb 8bit write/read, spi 8bit write/read */ + + RXDSD_SHIFT = 14, /* Rxd Sample Delay, in cycles */ + RXDSD_MASK = 3, + + FRF_SHIFT = 16, /* Frame Format */ + FRF_MASK = 3, + FRF_SPI = 0, /* Motorola SPI */ + FRF_SSP, /* Texas Instruments SSP*/ + FRF_MICROWIRE, /* National Semiconductors Microwire */ + FRF_RESV, + + TMOD_SHIFT = 18, /* Transfer Mode */ + TMOD_MASK = 3, + TMOD_TR = 0, /* xmit & recv */ + TMOD_TO, /* xmit only */ + TMOD_RO, /* recv only */ + TMOD_RESV, + + OMOD_SHIFT = 20, /* Operation Mode */ + OMOD_MASK = 1, + OMOD_MASTER = 0, /* Master Mode */ + OMOD_SLAVE, /* Slave Mode */ +}; + +/* SR */ +enum { + SR_MASK = 0x7f, + SR_BUSY = 1 << 0, + SR_TF_FULL = 1 << 1, + SR_TF_EMPT = 1 << 2, + SR_RF_EMPT = 1 << 3, + SR_RF_FULL = 1 << 4, +}; + +#define ROCKCHIP_SPI_TIMEOUT_MS 1000 +#define ROCKCHIP_SPI_MAX_RATE 48000000 + +#endif /* __RK_SPI_H */ -- cgit v1.2.1 From 8e3332e223f2d98024cc54c0dfce69d52cf090cd Mon Sep 17 00:00:00 2001 From: Sjoerd Simons Date: Sun, 30 Aug 2015 16:55:45 -0600 Subject: mmc: Probe DM based mmc devices in u-boot During mmc initialize probe all devices with the MMC Uclass if build with CONFIG_DM_MMC Signed-off-by: Sjoerd Simons Acked-by: Simon Glass Signed-off-by: Simon Glass --- drivers/mmc/mmc.c | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index f12546ac51..371c1ec212 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include #include @@ -1759,10 +1761,44 @@ static void do_preinit(void) } } +#if defined(CONFIG_DM_MMC) && defined(CONFIG_SPL_BUILD) +static int mmc_probe(bd_t *bis) +{ + return 0; +} +#elif defined(CONFIG_DM_MMC) +static int mmc_probe(bd_t *bis) +{ + int ret; + struct uclass *uc; + struct udevice *m; + + ret = uclass_get(UCLASS_MMC, &uc); + if (ret) + return ret; + + uclass_foreach_dev(m, uc) { + ret = device_probe(m); + if (ret) + printf("%s - probe failed: %d\n", m->name, ret); + } + + return 0; +} +#else +static int mmc_probe(bd_t *bis) +{ + if (board_mmc_init(bis) < 0) + cpu_mmc_init(bis); + + return 0; +} +#endif int mmc_initialize(bd_t *bis) { static int initialized = 0; + int ret; if (initialized) /* Avoid initializing mmc multiple times */ return 0; initialized = 1; @@ -1770,10 +1806,9 @@ int mmc_initialize(bd_t *bis) INIT_LIST_HEAD (&mmc_devices); cur_dev_num = 0; -#ifndef CONFIG_DM_MMC - if (board_mmc_init(bis) < 0) - cpu_mmc_init(bis); -#endif + ret = mmc_probe(bis); + if (ret) + return ret; #ifndef CONFIG_SPL_BUILD print_mmc_devices(','); -- cgit v1.2.1