diff options
author | Tom Rini <trini@konsulko.com> | 2016-10-28 14:14:18 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2016-10-28 14:14:18 -0400 |
commit | 1df182ddf700de49fb4400ba67c3029278ea88e7 (patch) | |
tree | c709e8a79950a29f1d12fc09fbf52b293bd54092 /drivers | |
parent | 4f892924d238cc415891dbea336a0fdaff2f853b (diff) | |
parent | 0eafd4b77615efdd948e698d83be746dcf026a53 (diff) | |
download | u-boot-imx-1df182ddf700de49fb4400ba67c3029278ea88e7.zip u-boot-imx-1df182ddf700de49fb4400ba67c3029278ea88e7.tar.gz u-boot-imx-1df182ddf700de49fb4400ba67c3029278ea88e7.tar.bz2 |
Merge branch 'master' of git://git.denx.de/u-boot-atmel
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clk/at91/Kconfig | 1 | ||||
-rw-r--r-- | drivers/clk/at91/clk-generated.c | 87 | ||||
-rw-r--r-- | drivers/clk/at91/clk-peripheral.c | 72 | ||||
-rw-r--r-- | drivers/clk/at91/clk-system.c | 57 | ||||
-rw-r--r-- | drivers/clk/at91/pmc.c | 72 | ||||
-rw-r--r-- | drivers/clk/at91/pmc.h | 5 | ||||
-rw-r--r-- | drivers/clk/at91/sckc.c | 17 | ||||
-rw-r--r-- | drivers/clk/clk-uclass.c | 3 | ||||
-rw-r--r-- | drivers/gpio/atmel_pio4.c | 12 | ||||
-rw-r--r-- | drivers/i2c/at91_i2c.c | 18 | ||||
-rw-r--r-- | drivers/mmc/atmel_sdhci.c | 27 | ||||
-rw-r--r-- | drivers/serial/Kconfig | 14 | ||||
-rw-r--r-- | drivers/serial/atmel_usart.c | 22 | ||||
-rw-r--r-- | drivers/spi/Kconfig | 8 | ||||
-rw-r--r-- | drivers/spi/atmel_spi.c | 288 | ||||
-rw-r--r-- | drivers/usb/host/ehci-atmel.c | 15 |
16 files changed, 540 insertions, 178 deletions
diff --git a/drivers/clk/at91/Kconfig b/drivers/clk/at91/Kconfig index 10050d8..904ed48 100644 --- a/drivers/clk/at91/Kconfig +++ b/drivers/clk/at91/Kconfig @@ -1,6 +1,7 @@ config CLK_AT91 bool "AT91 clock drivers" depends on CLK + select MISC help This option is used to enable the AT91 clock driver. The driver supports the AT91 clock generator, including diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c index f6164cc..d36f64f 100644 --- a/drivers/clk/at91/clk-generated.c +++ b/drivers/clk/at91/clk-generated.c @@ -17,15 +17,41 @@ DECLARE_GLOBAL_DATA_PTR; #define GENERATED_SOURCE_MAX 6 #define GENERATED_MAX_DIV 255 -struct generated_clk_priv { +/** + * generated_clk_bind() - for the generated clock driver + * Recursively bind its children as clk devices. + * + * @return: 0 on success, or negative error code on failure + */ +static int generated_clk_bind(struct udevice *dev) +{ + return at91_clk_sub_device_bind(dev, "generic-clk"); +} + +static const struct udevice_id generated_clk_match[] = { + { .compatible = "atmel,sama5d2-clk-generated" }, + {} +}; + +U_BOOT_DRIVER(generated_clk) = { + .name = "generated-clk", + .id = UCLASS_MISC, + .of_match = generated_clk_match, + .bind = generated_clk_bind, +}; + +/*-------------------------------------------------------------*/ + +struct generic_clk_priv { u32 num_parents; }; -static ulong generated_clk_get_rate(struct clk *clk) +static ulong generic_clk_get_rate(struct clk *clk) { struct pmc_platdata *plat = dev_get_platdata(clk->dev); struct at91_pmc *pmc = plat->reg_base; struct clk parent; + ulong clk_rate; u32 tmp, gckdiv; u8 parent_id; int ret; @@ -36,18 +62,22 @@ static ulong generated_clk_get_rate(struct clk *clk) AT91_PMC_PCR_GCKCSS_MASK; gckdiv = (tmp >> AT91_PMC_PCR_GCKDIV_OFFSET) & AT91_PMC_PCR_GCKDIV_MASK; - ret = clk_get_by_index(clk->dev, parent_id, &parent); + ret = clk_get_by_index(dev_get_parent(clk->dev), parent_id, &parent); if (ret) return 0; - return clk_get_rate(&parent) / (gckdiv + 1); + clk_rate = clk_get_rate(&parent) / (gckdiv + 1); + + clk_free(&parent); + + return clk_rate; } -static ulong generated_clk_set_rate(struct clk *clk, ulong rate) +static ulong generic_clk_set_rate(struct clk *clk, ulong rate) { struct pmc_platdata *plat = dev_get_platdata(clk->dev); struct at91_pmc *pmc = plat->reg_base; - struct generated_clk_priv *priv = dev_get_priv(clk->dev); + struct generic_clk_priv *priv = dev_get_priv(clk->dev); struct clk parent, best_parent; ulong tmp_rate, best_rate = rate, parent_rate; int tmp_diff, best_diff = -1; @@ -58,7 +88,7 @@ static ulong generated_clk_set_rate(struct clk *clk, ulong rate) int ret; for (i = 0; i < priv->num_parents; i++) { - ret = clk_get_by_index(clk->dev, i, &parent); + ret = clk_get_by_index(dev_get_parent(clk->dev), i, &parent); if (ret) return ret; @@ -111,18 +141,20 @@ static ulong generated_clk_set_rate(struct clk *clk, ulong rate) return 0; } -static struct clk_ops generated_clk_ops = { - .get_rate = generated_clk_get_rate, - .set_rate = generated_clk_set_rate, +static struct clk_ops generic_clk_ops = { + .of_xlate = at91_clk_of_xlate, + .get_rate = generic_clk_get_rate, + .set_rate = generic_clk_set_rate, }; -static int generated_clk_ofdata_to_platdata(struct udevice *dev) +static int generic_clk_ofdata_to_platdata(struct udevice *dev) { - struct generated_clk_priv *priv = dev_get_priv(dev); + struct generic_clk_priv *priv = dev_get_priv(dev); u32 cells[GENERATED_SOURCE_MAX]; u32 num_parents; - num_parents = fdtdec_get_int_array_count(gd->fdt_blob, dev->of_offset, + num_parents = fdtdec_get_int_array_count(gd->fdt_blob, + dev_get_parent(dev)->of_offset, "clocks", cells, GENERATED_SOURCE_MAX); @@ -134,29 +166,12 @@ static int generated_clk_ofdata_to_platdata(struct udevice *dev) return 0; } -static int generated_clk_bind(struct udevice *dev) -{ - return at91_pmc_clk_node_bind(dev); -} - -static int generated_clk_probe(struct udevice *dev) -{ - return at91_pmc_core_probe(dev); -} - -static const struct udevice_id generated_clk_match[] = { - { .compatible = "atmel,sama5d2-clk-generated" }, - {} -}; - -U_BOOT_DRIVER(generated_clk) = { - .name = "generated-clk", +U_BOOT_DRIVER(generic_clk) = { + .name = "generic-clk", .id = UCLASS_CLK, - .of_match = generated_clk_match, - .bind = generated_clk_bind, - .probe = generated_clk_probe, - .ofdata_to_platdata = generated_clk_ofdata_to_platdata, - .priv_auto_alloc_size = sizeof(struct generated_clk_priv), + .probe = at91_clk_probe, + .ofdata_to_platdata = generic_clk_ofdata_to_platdata, + .priv_auto_alloc_size = sizeof(struct generic_clk_priv), .platdata_auto_alloc_size = sizeof(struct pmc_platdata), - .ops = &generated_clk_ops, + .ops = &generic_clk_ops, }; diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c index 16688e9..e1ed447 100644 --- a/drivers/clk/at91/clk-peripheral.c +++ b/drivers/clk/at91/clk-peripheral.c @@ -16,7 +16,32 @@ #define PERIPHERAL_ID_MAX 31 #define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX)) -static int sam9x5_periph_clk_enable(struct clk *clk) +/** + * sam9x5_periph_clk_bind() - for the periph clock driver + * Recursively bind its children as clk devices. + * + * @return: 0 on success, or negative error code on failure + */ +static int sam9x5_periph_clk_bind(struct udevice *dev) +{ + return at91_clk_sub_device_bind(dev, "periph-clk"); +} + +static const struct udevice_id sam9x5_periph_clk_match[] = { + { .compatible = "atmel,at91sam9x5-clk-peripheral" }, + {} +}; + +U_BOOT_DRIVER(sam9x5_periph_clk) = { + .name = "sam9x5-periph-clk", + .id = UCLASS_MISC, + .of_match = sam9x5_periph_clk_match, + .bind = sam9x5_periph_clk_bind, +}; + +/*---------------------------------------------------------*/ + +static int periph_clk_enable(struct clk *clk) { struct pmc_platdata *plat = dev_get_platdata(clk->dev); struct at91_pmc *pmc = plat->reg_base; @@ -30,31 +55,36 @@ static int sam9x5_periph_clk_enable(struct clk *clk) return 0; } -static struct clk_ops sam9x5_periph_clk_ops = { - .enable = sam9x5_periph_clk_enable, -}; - -static int sam9x5_periph_clk_bind(struct udevice *dev) +static ulong periph_get_rate(struct clk *clk) { - return at91_pmc_clk_node_bind(dev); -} + struct udevice *dev; + struct clk clk_dev; + ulong clk_rate; + int ret; -static int sam9x5_periph_clk_probe(struct udevice *dev) -{ - return at91_pmc_core_probe(dev); + dev = dev_get_parent(clk->dev); + + ret = clk_get_by_index(dev, 0, &clk_dev); + if (ret) + return ret; + + clk_rate = clk_get_rate(&clk_dev); + + clk_free(&clk_dev); + + return clk_rate; } -static const struct udevice_id sam9x5_periph_clk_match[] = { - { .compatible = "atmel,at91sam9x5-clk-peripheral" }, - {} +static struct clk_ops periph_clk_ops = { + .of_xlate = at91_clk_of_xlate, + .enable = periph_clk_enable, + .get_rate = periph_get_rate, }; -U_BOOT_DRIVER(sam9x5_periph_clk) = { - .name = "sam9x5-periph-clk", - .id = UCLASS_CLK, - .of_match = sam9x5_periph_clk_match, - .bind = sam9x5_periph_clk_bind, - .probe = sam9x5_periph_clk_probe, +U_BOOT_DRIVER(clk_periph) = { + .name = "periph-clk", + .id = UCLASS_CLK, .platdata_auto_alloc_size = sizeof(struct pmc_platdata), - .ops = &sam9x5_periph_clk_ops, + .probe = at91_clk_probe, + .ops = &periph_clk_ops, }; diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c index fa80bad..5b59a0c 100644 --- a/drivers/clk/at91/clk-system.c +++ b/drivers/clk/at91/clk-system.c @@ -14,12 +14,37 @@ #define SYSTEM_MAX_ID 31 +/** + * at91_system_clk_bind() - for the system clock driver + * Recursively bind its children as clk devices. + * + * @return: 0 on success, or negative error code on failure + */ +static int at91_system_clk_bind(struct udevice *dev) +{ + return at91_clk_sub_device_bind(dev, "system-clk"); +} + +static const struct udevice_id at91_system_clk_match[] = { + { .compatible = "atmel,at91rm9200-clk-system" }, + {} +}; + +U_BOOT_DRIVER(at91_system_clk) = { + .name = "at91-system-clk", + .id = UCLASS_MISC, + .of_match = at91_system_clk_match, + .bind = at91_system_clk_bind, +}; + +/*----------------------------------------------------------*/ + static inline int is_pck(int id) { return (id >= 8) && (id <= 15); } -static int at91_system_clk_enable(struct clk *clk) +static int system_clk_enable(struct clk *clk) { struct pmc_platdata *plat = dev_get_platdata(clk->dev); struct at91_pmc *pmc = plat->reg_base; @@ -46,31 +71,15 @@ static int at91_system_clk_enable(struct clk *clk) return 0; } -static struct clk_ops at91_system_clk_ops = { - .enable = at91_system_clk_enable, +static struct clk_ops system_clk_ops = { + .of_xlate = at91_clk_of_xlate, + .enable = system_clk_enable, }; -static int at91_system_clk_bind(struct udevice *dev) -{ - return at91_pmc_clk_node_bind(dev); -} - -static int at91_system_clk_probe(struct udevice *dev) -{ - return at91_pmc_core_probe(dev); -} - -static const struct udevice_id at91_system_clk_match[] = { - { .compatible = "atmel,at91rm9200-clk-system" }, - {} -}; - -U_BOOT_DRIVER(at91_system_clk) = { - .name = "at91-system-clk", +U_BOOT_DRIVER(system_clk) = { + .name = "system-clk", .id = UCLASS_CLK, - .of_match = at91_system_clk_match, - .bind = at91_system_clk_bind, - .probe = at91_system_clk_probe, + .probe = at91_clk_probe, .platdata_auto_alloc_size = sizeof(struct pmc_platdata), - .ops = &at91_system_clk_ops, + .ops = &system_clk_ops, }; diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index a08d7e8..76ba91a 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -14,23 +14,19 @@ DECLARE_GLOBAL_DATA_PTR; -static int at91_pmc_bind(struct udevice *dev) -{ - return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); -} - static const struct udevice_id at91_pmc_match[] = { { .compatible = "atmel,sama5d2-pmc" }, {} }; U_BOOT_DRIVER(at91_pmc) = { - .name = "at91-pmc-core", - .id = UCLASS_CLK, + .name = "at91-pmc", + .id = UCLASS_SIMPLE_BUS, .of_match = at91_pmc_match, - .bind = at91_pmc_bind, }; +/*---------------------------------------------------------*/ + int at91_pmc_core_probe(struct udevice *dev) { struct pmc_platdata *plat = dev_get_platdata(dev); @@ -42,21 +38,41 @@ int at91_pmc_core_probe(struct udevice *dev) return 0; } -int at91_pmc_clk_node_bind(struct udevice *dev) +/** + * at91_clk_sub_device_bind() - for the at91 clock driver + * Recursively bind its children as clk devices. + * + * @return: 0 on success, or negative error code on failure + */ +int at91_clk_sub_device_bind(struct udevice *dev, const char *drv_name) { const void *fdt = gd->fdt_blob; int offset = dev->of_offset; + bool pre_reloc_only = !(gd->flags & GD_FLG_RELOC); const char *name; int ret; for (offset = fdt_first_subnode(fdt, offset); offset > 0; offset = fdt_next_subnode(fdt, offset)) { + if (pre_reloc_only && + !fdt_getprop(fdt, offset, "u-boot,dm-pre-reloc", NULL)) + continue; + /* + * If this node has "compatible" property, this is not + * a clock sub-node, but a normal device. skip. + */ + fdt_get_property(fdt, offset, "compatible", &ret); + if (ret >= 0) + continue; + + if (ret != -FDT_ERR_NOTFOUND) + return ret; + name = fdt_get_name(fdt, offset, NULL); if (!name) return -EINVAL; - - ret = device_bind_driver_to_node(dev, "clk", name, + ret = device_bind_driver_to_node(dev, drv_name, name, offset, NULL); if (ret) return ret; @@ -65,7 +81,33 @@ int at91_pmc_clk_node_bind(struct udevice *dev) return 0; } -U_BOOT_DRIVER(clk_generic) = { - .id = UCLASS_CLK, - .name = "clk", -}; +int at91_clk_of_xlate(struct clk *clk, struct fdtdec_phandle_args *args) +{ + int periph; + + if (args->args_count) { + debug("Invalid args_count: %d\n", args->args_count); + return -EINVAL; + } + + periph = fdtdec_get_uint(gd->fdt_blob, clk->dev->of_offset, "reg", -1); + if (periph < 0) + return -EINVAL; + + clk->id = periph; + + return 0; +} + +int at91_clk_probe(struct udevice *dev) +{ + struct udevice *dev_periph_container, *dev_pmc; + struct pmc_platdata *plat = dev_get_platdata(dev); + + dev_periph_container = dev_get_parent(dev); + dev_pmc = dev_get_parent(dev_periph_container); + + plat->reg_base = (struct at91_pmc *)dev_get_addr_ptr(dev_pmc); + + return 0; +} diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h index 5444c84..f222fce 100644 --- a/drivers/clk/at91/pmc.h +++ b/drivers/clk/at91/pmc.h @@ -13,6 +13,9 @@ struct pmc_platdata { }; int at91_pmc_core_probe(struct udevice *dev); -int at91_pmc_clk_node_bind(struct udevice *dev); +int at91_clk_sub_device_bind(struct udevice *dev, const char *drv_name); + +int at91_clk_of_xlate(struct clk *clk, struct fdtdec_phandle_args *args); +int at91_clk_probe(struct udevice *dev); #endif diff --git a/drivers/clk/at91/sckc.c b/drivers/clk/at91/sckc.c index b207611..6035e20 100644 --- a/drivers/clk/at91/sckc.c +++ b/drivers/clk/at91/sckc.c @@ -6,25 +6,18 @@ */ #include <common.h> -#include <clk-uclass.h> #include <dm/device.h> #include <dm/root.h> DECLARE_GLOBAL_DATA_PTR; -static int at91_sckc_clk_bind(struct udevice *dev) -{ - return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); -} - -static const struct udevice_id at91_sckc_clk_match[] = { +static const struct udevice_id at91_sckc_match[] = { { .compatible = "atmel,at91sam9x5-sckc" }, {} }; -U_BOOT_DRIVER(at91_sckc_clk) = { - .name = "at91_sckc_clk", - .id = UCLASS_CLK, - .of_match = at91_sckc_clk_match, - .bind = at91_sckc_clk_bind, +U_BOOT_DRIVER(at91_sckc) = { + .name = "at91-sckc", + .id = UCLASS_SIMPLE_BUS, + .of_match = at91_sckc_match, }; diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index c42fff6..153ceba 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -80,6 +80,9 @@ int clk_get_by_index(struct udevice *dev, int index, struct clk *clk) __func__, ret); return ret; } + + clk->dev = dev_clk; + ops = clk_dev_ops(dev_clk); if (ops->of_xlate) diff --git a/drivers/gpio/atmel_pio4.c b/drivers/gpio/atmel_pio4.c index 7adea88..cb90b02 100644 --- a/drivers/gpio/atmel_pio4.c +++ b/drivers/gpio/atmel_pio4.c @@ -284,27 +284,15 @@ static int atmel_pio4_probe(struct udevice *dev) struct atmel_pio4_platdata *plat = dev_get_platdata(dev); struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); struct atmel_pioctrl_data *pioctrl_data; - struct udevice *dev_clk; struct clk clk; fdt_addr_t addr_base; u32 nbanks; - int periph; int ret; ret = clk_get_by_index(dev, 0, &clk); if (ret) return ret; - periph = fdtdec_get_uint(gd->fdt_blob, clk.dev->of_offset, "reg", -1); - if (periph < 0) - return -EINVAL; - - dev_clk = dev_get_parent(clk.dev); - ret = clk_request(dev_clk, &clk); - if (ret) - return ret; - - clk.id = periph; ret = clk_enable(&clk); if (ret) return ret; diff --git a/drivers/i2c/at91_i2c.c b/drivers/i2c/at91_i2c.c index d71f75c..4bc54ee 100644 --- a/drivers/i2c/at91_i2c.c +++ b/drivers/i2c/at91_i2c.c @@ -176,37 +176,21 @@ static void at91_calc_i2c_clock(struct udevice *dev, int i2c_clk) static int at91_i2c_enable_clk(struct udevice *dev) { struct at91_i2c_bus *bus = dev_get_priv(dev); - struct udevice *dev_clk; struct clk clk; ulong clk_rate; - int periph; int ret; ret = clk_get_by_index(dev, 0, &clk); if (ret) return -EINVAL; - periph = fdtdec_get_uint(gd->fdt_blob, clk.dev->of_offset, "reg", -1); - if (periph < 0) - return -EINVAL; - - dev_clk = dev_get_parent(clk.dev); - ret = clk_request(dev_clk, &clk); - if (ret) - return ret; - - clk.id = periph; ret = clk_enable(&clk); if (ret) return ret; - ret = clk_get_by_index(dev_clk, 0, &clk); - if (ret) - return ret; - clk_rate = clk_get_rate(&clk); if (!clk_rate) - return -ENODEV; + return -EINVAL; bus->bus_clk_rate = clk_rate; diff --git a/drivers/mmc/atmel_sdhci.c b/drivers/mmc/atmel_sdhci.c index 20bba1a..6654b54 100644 --- a/drivers/mmc/atmel_sdhci.c +++ b/drivers/mmc/atmel_sdhci.c @@ -50,29 +50,6 @@ struct atmel_sdhci_plat { struct mmc mmc; }; -static int atmel_sdhci_get_clk(struct udevice *dev, int index, struct clk *clk) -{ - struct udevice *dev_clk; - int periph, ret; - - ret = clk_get_by_index(dev, index, clk); - if (ret) - return ret; - - periph = fdtdec_get_uint(gd->fdt_blob, clk->dev->of_offset, "reg", -1); - if (periph < 0) - return -EINVAL; - - dev_clk = dev_get_parent(clk->dev); - ret = clk_request(dev_clk, clk); - if (ret) - return ret; - - clk->id = periph; - - return 0; -} - static int atmel_sdhci_probe(struct udevice *dev) { struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); @@ -85,7 +62,7 @@ static int atmel_sdhci_probe(struct udevice *dev) struct clk clk; int ret; - ret = atmel_sdhci_get_clk(dev, 0, &clk); + ret = clk_get_by_index(dev, 0, &clk); if (ret) return ret; @@ -106,7 +83,7 @@ static int atmel_sdhci_probe(struct udevice *dev) clk_mul = (caps_1 & SDHCI_CLOCK_MUL_MASK) >> SDHCI_CLOCK_MUL_SHIFT; gck_rate = clk_base * 1000000 * (clk_mul + 1); - ret = atmel_sdhci_get_clk(dev, 1, &clk); + ret = clk_get_by_index(dev, 1, &clk); if (ret) return ret; diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 317d158..56c024f 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -98,6 +98,13 @@ config DEBUG_UART_AR933X driver will be available until the real driver model serial is running. +config DEBUG_UART_ATMEL + bool "Atmel USART" + help + Select this to enable a debug UART using the atmel usart driver. You + will need to provide parameters to make this work. The driver will + be available until the real driver-model serial is running. + config DEBUG_UART_NS16550 bool "ns16550" help @@ -296,6 +303,13 @@ config AR933X_UART tree binding to operate, please refer to the document at doc/device-tree-bindings/serial/qca,ar9330-uart.txt. +config ATMEL_USART + bool "Atmel USART support" + help + Select this to enable USART support for Atmel SoCs. It can be + configured in the device tree, and input clock frequency can + be got from the clk node. + config FSL_LPUART bool "Freescale LPUART support" help diff --git a/drivers/serial/atmel_usart.c b/drivers/serial/atmel_usart.c index e450135..7674f97 100644 --- a/drivers/serial/atmel_usart.c +++ b/drivers/serial/atmel_usart.c @@ -11,6 +11,7 @@ #include <errno.h> #include <watchdog.h> #include <serial.h> +#include <debug_uart.h> #include <linux/compiler.h> #include <asm/io.h> @@ -226,3 +227,24 @@ U_BOOT_DRIVER(serial_atmel) = { .priv_auto_alloc_size = sizeof(struct atmel_serial_priv), }; #endif + +#ifdef CONFIG_DEBUG_UART_ATMEL +static inline void _debug_uart_init(void) +{ + atmel_usart3_t *usart = (atmel_usart3_t *)CONFIG_DEBUG_UART_BASE; + + atmel_serial_setbrg_internal(usart, 0, CONFIG_BAUDRATE); +} + +static inline void _debug_uart_putc(int ch) +{ + atmel_usart3_t *usart = (atmel_usart3_t *)CONFIG_DEBUG_UART_BASE; + + while (!(readl(&usart->csr) & USART3_BIT(TXRDY))) + ; + + writel(ch, &usart->thr); +} + +DEBUG_UART_FUNCS +#endif diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 8724f87..0f51b3a 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -32,6 +32,14 @@ config ATH79_SPI uses driver model and requires a device tree binding to operate. please refer to doc/device-tree-bindings/spi/spi-ath79.txt. +config ATMEL_SPI + bool "Atmel SPI driver" + depends on ARCH_AT91 + help + This enables driver for the Atmel SPI Controller, present on + many AT32 (AVR32) and AT91 (ARM) chips. This driver can be + used to access the SPI Flash, such as AT25DF321. + config CADENCE_QSPI bool "Cadence QSPI driver" help diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index ed6278b..7649114 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -4,16 +4,30 @@ * SPDX-License-Identifier: GPL-2.0+ */ #include <common.h> +#include <clk.h> +#include <dm.h> +#include <fdtdec.h> #include <spi.h> #include <malloc.h> +#include <wait_bit.h> #include <asm/io.h> #include <asm/arch/clk.h> #include <asm/arch/hardware.h> +#ifdef CONFIG_DM_SPI +#include <asm/arch/at91_spi.h> +#endif +#ifdef CONFIG_DM_GPIO +#include <asm/gpio.h> +#endif #include "atmel_spi.h" +DECLARE_GLOBAL_DATA_PTR; + +#ifndef CONFIG_DM_SPI + static int spi_has_wdrbt(struct atmel_spi_slave *slave) { unsigned int ver; @@ -209,3 +223,277 @@ out: return 0; } + +#else + +#define MAX_CS_COUNT 4 + +struct atmel_spi_platdata { + struct at91_spi *regs; +}; + +struct atmel_spi_priv { + unsigned int freq; /* Default frequency */ + unsigned int mode; + ulong bus_clk_rate; + struct gpio_desc cs_gpios[MAX_CS_COUNT]; +}; + +static int atmel_spi_claim_bus(struct udevice *dev) +{ + struct udevice *bus = dev_get_parent(dev); + struct atmel_spi_platdata *bus_plat = dev_get_platdata(bus); + struct atmel_spi_priv *priv = dev_get_priv(bus); + struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev); + struct at91_spi *reg_base = bus_plat->regs; + u32 cs = slave_plat->cs; + u32 freq = priv->freq; + u32 scbr, csrx, mode; + + scbr = (priv->bus_clk_rate + freq - 1) / freq; + if (scbr > ATMEL_SPI_CSRx_SCBR_MAX) + return -EINVAL; + + if (scbr < 1) + scbr = 1; + + csrx = ATMEL_SPI_CSRx_SCBR(scbr); + csrx |= ATMEL_SPI_CSRx_BITS(ATMEL_SPI_BITS_8); + + if (!(priv->mode & SPI_CPHA)) + csrx |= ATMEL_SPI_CSRx_NCPHA; + if (priv->mode & SPI_CPOL) + csrx |= ATMEL_SPI_CSRx_CPOL; + + writel(csrx, ®_base->csr[cs]); + + mode = ATMEL_SPI_MR_MSTR | + ATMEL_SPI_MR_MODFDIS | + ATMEL_SPI_MR_WDRBT | + ATMEL_SPI_MR_PCS(~(1 << cs)); + + writel(mode, ®_base->mr); + + writel(ATMEL_SPI_CR_SPIEN, ®_base->cr); + + return 0; +} + +static int atmel_spi_release_bus(struct udevice *dev) +{ + struct udevice *bus = dev_get_parent(dev); + struct atmel_spi_platdata *bus_plat = dev_get_platdata(bus); + + writel(ATMEL_SPI_CR_SPIDIS, &bus_plat->regs->cr); + + return 0; +} + +static void atmel_spi_cs_activate(struct udevice *dev) +{ + struct udevice *bus = dev_get_parent(dev); + struct atmel_spi_priv *priv = dev_get_priv(bus); + struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev); + u32 cs = slave_plat->cs; + + dm_gpio_set_value(&priv->cs_gpios[cs], 0); +} + +static void atmel_spi_cs_deactivate(struct udevice *dev) +{ + struct udevice *bus = dev_get_parent(dev); + struct atmel_spi_priv *priv = dev_get_priv(bus); + struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev); + u32 cs = slave_plat->cs; + + dm_gpio_set_value(&priv->cs_gpios[cs], 1); +} + +static int atmel_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct udevice *bus = dev_get_parent(dev); + struct atmel_spi_platdata *bus_plat = dev_get_platdata(bus); + struct at91_spi *reg_base = bus_plat->regs; + + u32 len_tx, len_rx, len; + u32 status; + const u8 *txp = dout; + u8 *rxp = din; + u8 value; + + if (bitlen == 0) + goto out; + + /* + * The controller can do non-multiple-of-8 bit + * transfers, but this driver currently doesn't support it. + * + * It's also not clear how such transfers are supposed to be + * represented as a stream of bytes...this is a limitation of + * the current SPI interface. + */ + if (bitlen % 8) { + /* Errors always terminate an ongoing transfer */ + flags |= SPI_XFER_END; + goto out; + } + + len = bitlen / 8; + + /* + * The controller can do automatic CS control, but it is + * somewhat quirky, and it doesn't really buy us much anyway + * in the context of U-Boot. + */ + if (flags & SPI_XFER_BEGIN) { + atmel_spi_cs_activate(dev); + + /* + * sometimes the RDR is not empty when we get here, + * in theory that should not happen, but it DOES happen. + * Read it here to be on the safe side. + * That also clears the OVRES flag. Required if the + * following loop exits due to OVRES! + */ + readl(®_base->rdr); + } + + for (len_tx = 0, len_rx = 0; len_rx < len; ) { + status = readl(®_base->sr); + + if (status & ATMEL_SPI_SR_OVRES) + return -1; + + if ((len_tx < len) && (status & ATMEL_SPI_SR_TDRE)) { + if (txp) + value = *txp++; + else + value = 0; + writel(value, ®_base->tdr); + len_tx++; + } + + if (status & ATMEL_SPI_SR_RDRF) { + value = readl(®_base->rdr); + if (rxp) + *rxp++ = value; + len_rx++; + } + } + +out: + if (flags & SPI_XFER_END) { + /* + * Wait until the transfer is completely done before + * we deactivate CS. + */ + wait_for_bit(__func__, ®_base->sr, + ATMEL_SPI_SR_TXEMPTY, true, 1000, false); + + atmel_spi_cs_deactivate(dev); + } + + return 0; +} + +static int atmel_spi_set_speed(struct udevice *bus, uint speed) +{ + struct atmel_spi_priv *priv = dev_get_priv(bus); + + priv->freq = speed; + + return 0; +} + +static int atmel_spi_set_mode(struct udevice *bus, uint mode) +{ + struct atmel_spi_priv *priv = dev_get_priv(bus); + + priv->mode = mode; + + return 0; +} + +static const struct dm_spi_ops atmel_spi_ops = { + .claim_bus = atmel_spi_claim_bus, + .release_bus = atmel_spi_release_bus, + .xfer = atmel_spi_xfer, + .set_speed = atmel_spi_set_speed, + .set_mode = atmel_spi_set_mode, + /* + * cs_info is not needed, since we require all chip selects to be + * in the device tree explicitly + */ +}; + +static int atmel_spi_enable_clk(struct udevice *bus) +{ + struct atmel_spi_priv *priv = dev_get_priv(bus); + struct clk clk; + ulong clk_rate; + int ret; + + ret = clk_get_by_index(bus, 0, &clk); + if (ret) + return -EINVAL; + + ret = clk_enable(&clk); + if (ret) + return ret; + + clk_rate = clk_get_rate(&clk); + if (!clk_rate) + return -EINVAL; + + priv->bus_clk_rate = clk_rate; + + clk_free(&clk); + + return 0; +} + +static int atmel_spi_probe(struct udevice *bus) +{ + struct atmel_spi_platdata *bus_plat = dev_get_platdata(bus); + struct atmel_spi_priv *priv = dev_get_priv(bus); + int i, ret; + + ret = atmel_spi_enable_clk(bus); + if (ret) + return ret; + + bus_plat->regs = (struct at91_spi *)dev_get_addr(bus); + + ret = gpio_request_list_by_name(bus, "cs-gpios", priv->cs_gpios, + ARRAY_SIZE(priv->cs_gpios), 0); + if (ret < 0) { + error("Can't get %s gpios! Error: %d", bus->name, ret); + return ret; + } + + for(i = 0; i < ARRAY_SIZE(priv->cs_gpios); i++) { + dm_gpio_set_dir_flags(&priv->cs_gpios[i], + GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); + } + + writel(ATMEL_SPI_CR_SWRST, &bus_plat->regs->cr); + + return 0; +} + +static const struct udevice_id atmel_spi_ids[] = { + { .compatible = "atmel,at91rm9200-spi" }, + { } +}; + +U_BOOT_DRIVER(atmel_spi) = { + .name = "atmel_spi", + .id = UCLASS_SPI, + .of_match = atmel_spi_ids, + .ops = &atmel_spi_ops, + .platdata_auto_alloc_size = sizeof(struct atmel_spi_platdata), + .priv_auto_alloc_size = sizeof(struct atmel_spi_priv), + .probe = atmel_spi_probe, +}; +#endif diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c index 2b138c5..a5c6d34 100644 --- a/drivers/usb/host/ehci-atmel.c +++ b/drivers/usb/host/ehci-atmel.c @@ -56,9 +56,7 @@ struct ehci_atmel_priv { static int ehci_atmel_enable_clk(struct udevice *dev) { - struct udevice *dev_clk; struct clk clk; - int periph; int ret; ret = clk_get_by_index(dev, 0, &clk); @@ -73,19 +71,6 @@ static int ehci_atmel_enable_clk(struct udevice *dev) if (ret) return -EINVAL; - periph = fdtdec_get_uint(gd->fdt_blob, clk.dev->of_offset, "reg", -1); - if (periph < 0) - return -EINVAL; - - dev_clk = dev_get_parent(clk.dev); - if (!dev_clk) - return -ENODEV; - - ret = clk_request(dev_clk, &clk); - if (ret) - return ret; - - clk.id = periph; ret = clk_enable(&clk); if (ret) return ret; |