diff options
author | Tom Rini <trini@konsulko.com> | 2016-05-20 20:43:27 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2016-05-23 11:51:37 -0400 |
commit | d7d000311285e4b8d11e089ca13ea456a01be3b8 (patch) | |
tree | d156930b1f4ebf864dd42ec6e43e24045d975c55 /drivers | |
parent | d38fca40c84e6d5f73dfe43cef4c46d42f90aa66 (diff) | |
parent | 40ba13c98627055465709acd67872e381b42f928 (diff) |
Merge branch 'master' of git://git.denx.de/u-boot-mips
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mtd/Kconfig | 7 | ||||
-rw-r--r-- | drivers/mtd/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/pic32_flash.c | 444 | ||||
-rw-r--r-- | drivers/pinctrl/Kconfig | 18 | ||||
-rw-r--r-- | drivers/pinctrl/Makefile | 1 | ||||
-rw-r--r-- | drivers/pinctrl/ath79/Makefile | 6 | ||||
-rw-r--r-- | drivers/pinctrl/ath79/pinctrl_ar933x.c | 136 | ||||
-rw-r--r-- | drivers/pinctrl/ath79/pinctrl_qca953x.c | 156 | ||||
-rw-r--r-- | drivers/serial/Kconfig | 18 | ||||
-rw-r--r-- | drivers/serial/Makefile | 1 | ||||
-rw-r--r-- | drivers/serial/serial_ar933x.c | 243 | ||||
-rw-r--r-- | drivers/spi/Kconfig | 9 | ||||
-rw-r--r-- | drivers/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/spi/ath79_spi.c | 228 |
14 files changed, 1269 insertions, 0 deletions
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index c58841e7d87..390e9e4ea3f 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -28,6 +28,13 @@ config ALTERA_QSPI NOR flash to parallel flash interface. Please find details on the "Embedded Peripherals IP User Guide" of Altera. +config FLASH_PIC32 + bool "Microchip PIC32 Flash driver" + depends on MACH_PIC32 && MTD + help + This enables access to Microchip PIC32 internal non-CFI flash + chips through PIC32 Non-Volatile-Memory Controller. + endmenu source "drivers/mtd/nand/Kconfig" diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 703700aae0b..bd680a784f5 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -19,5 +19,6 @@ obj-$(CONFIG_HAS_DATAFLASH) += dataflash.o obj-$(CONFIG_FTSMC020) += ftsmc020.o obj-$(CONFIG_FLASH_CFI_LEGACY) += jedec_flash.o obj-$(CONFIG_MW_EEPROM) += mw_eeprom.o +obj-$(CONFIG_FLASH_PIC32) += pic32_flash.o obj-$(CONFIG_ST_SMI) += st_smi.o obj-$(CONFIG_STM32_FLASH) += stm32_flash.o diff --git a/drivers/mtd/pic32_flash.c b/drivers/mtd/pic32_flash.c new file mode 100644 index 00000000000..9166fcd9803 --- /dev/null +++ b/drivers/mtd/pic32_flash.c @@ -0,0 +1,444 @@ +/* + * Copyright (C) 2015 + * Cristian Birsan <cristian.birsan@microchip.com> + * Purna Chandra Mandal <purna.mandal@microchip.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <fdt_support.h> +#include <flash.h> +#include <mach/pic32.h> +#include <wait_bit.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* NVM Controller registers */ +struct pic32_reg_nvm { + struct pic32_reg_atomic ctrl; + struct pic32_reg_atomic key; + struct pic32_reg_atomic addr; + struct pic32_reg_atomic data; +}; + +/* NVM operations */ +#define NVMOP_NOP 0 +#define NVMOP_WORD_WRITE 1 +#define NVMOP_PAGE_ERASE 4 + +/* NVM control bits */ +#define NVM_WR BIT(15) +#define NVM_WREN BIT(14) +#define NVM_WRERR BIT(13) +#define NVM_LVDERR BIT(12) + +/* NVM programming unlock register */ +#define LOCK_KEY 0x0 +#define UNLOCK_KEY1 0xaa996655 +#define UNLOCK_KEY2 0x556699aa + +/* + * PIC32 flash banks consist of number of pages, each page + * into number of rows and rows into number of words. + * Here we will maintain page information instead of sector. + */ +flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; +static struct pic32_reg_nvm *nvm_regs_p; + +static inline void flash_initiate_operation(u32 nvmop) +{ + /* set operation */ + writel(nvmop, &nvm_regs_p->ctrl.raw); + + /* enable flash write */ + writel(NVM_WREN, &nvm_regs_p->ctrl.set); + + /* unlock sequence */ + writel(LOCK_KEY, &nvm_regs_p->key.raw); + writel(UNLOCK_KEY1, &nvm_regs_p->key.raw); + writel(UNLOCK_KEY2, &nvm_regs_p->key.raw); + + /* initiate operation */ + writel(NVM_WR, &nvm_regs_p->ctrl.set); +} + +static int flash_wait_till_busy(const char *func, ulong timeout) +{ + int ret = wait_for_bit(__func__, &nvm_regs_p->ctrl.raw, + NVM_WR, false, timeout, false); + + return ret ? ERR_TIMOUT : ERR_OK; +} + +static inline int flash_complete_operation(void) +{ + u32 tmp; + + tmp = readl(&nvm_regs_p->ctrl.raw); + if (tmp & NVM_WRERR) { + printf("Error in Block Erase - Lock Bit may be set!\n"); + flash_initiate_operation(NVMOP_NOP); + return ERR_PROTECTED; + } + + if (tmp & NVM_LVDERR) { + printf("Error in Block Erase - low-vol detected!\n"); + flash_initiate_operation(NVMOP_NOP); + return ERR_NOT_ERASED; + } + + /* disable flash write or erase operation */ + writel(NVM_WREN, &nvm_regs_p->ctrl.clr); + + return ERR_OK; +} + +/* + * Erase flash sectors, returns: + * ERR_OK - OK + * ERR_INVAL - invalid sector arguments + * ERR_TIMOUT - write timeout + * ERR_NOT_ERASED - Flash not erased + * ERR_UNKNOWN_FLASH_VENDOR - incorrect flash + */ +int flash_erase(flash_info_t *info, int s_first, int s_last) +{ + ulong sect_start, sect_end, flags; + int prot, sect; + int rc; + + if ((info->flash_id & FLASH_VENDMASK) != FLASH_MAN_MCHP) { + printf("Can't erase unknown flash type %08lx - aborted\n", + info->flash_id); + return ERR_UNKNOWN_FLASH_VENDOR; + } + + if ((s_first < 0) || (s_first > s_last)) { + printf("- no sectors to erase\n"); + return ERR_INVAL; + } + + prot = 0; + for (sect = s_first; sect <= s_last; ++sect) { + if (info->protect[sect]) + prot++; + } + + if (prot) + printf("- Warning: %d protected sectors will not be erased!\n", + prot); + else + printf("\n"); + + /* erase on unprotected sectors */ + for (sect = s_first; sect <= s_last; sect++) { + if (info->protect[sect]) + continue; + + /* disable interrupts */ + flags = disable_interrupts(); + + /* write destination page address (physical) */ + sect_start = CPHYSADDR(info->start[sect]); + writel(sect_start, &nvm_regs_p->addr.raw); + + /* page erase */ + flash_initiate_operation(NVMOP_PAGE_ERASE); + + /* wait */ + rc = flash_wait_till_busy(__func__, + CONFIG_SYS_FLASH_ERASE_TOUT); + + /* re-enable interrupts if necessary */ + if (flags) + enable_interrupts(); + + if (rc != ERR_OK) + return rc; + + rc = flash_complete_operation(); + if (rc != ERR_OK) + return rc; + + /* + * flash content is updated but cache might contain stale + * data, so invalidate dcache. + */ + sect_end = info->start[sect] + info->size / info->sector_count; + invalidate_dcache_range(info->start[sect], sect_end); + } + + printf(" done\n"); + return ERR_OK; +} + +int page_erase(flash_info_t *info, int sect) +{ + return 0; +} + +/* Write a word to flash */ +static int write_word(flash_info_t *info, ulong dest, ulong word) +{ + ulong flags; + int rc; + + /* read flash to check if it is sufficiently erased */ + if ((readl((void __iomem *)dest) & word) != word) { + printf("Error, Flash not erased!\n"); + return ERR_NOT_ERASED; + } + + /* disable interrupts */ + flags = disable_interrupts(); + + /* update destination page address (physical) */ + writel(CPHYSADDR(dest), &nvm_regs_p->addr.raw); + writel(word, &nvm_regs_p->data.raw); + + /* word write */ + flash_initiate_operation(NVMOP_WORD_WRITE); + + /* wait for operation to complete */ + rc = flash_wait_till_busy(__func__, CONFIG_SYS_FLASH_WRITE_TOUT); + + /* re-enable interrupts if necessary */ + if (flags) + enable_interrupts(); + + if (rc != ERR_OK) + return rc; + + return flash_complete_operation(); +} + +/* + * Copy memory to flash, returns: + * ERR_OK - OK + * ERR_TIMOUT - write timeout + * ERR_NOT_ERASED - Flash not erased + */ +int write_buff(flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ + ulong dst, tmp_le, len = cnt; + int i, l, rc; + uchar *cp; + + /* get lower word aligned address */ + dst = (addr & ~3); + + /* handle unaligned start bytes */ + l = addr - dst; + if (l != 0) { + tmp_le = 0; + for (i = 0, cp = (uchar *)dst; i < l; ++i, ++cp) + tmp_le |= *cp << (i * 8); + + for (; (i < 4) && (cnt > 0); ++i, ++src, --cnt, ++cp) + tmp_le |= *src << (i * 8); + + for (; (cnt == 0) && (i < 4); ++i, ++cp) + tmp_le |= *cp << (i * 8); + + rc = write_word(info, dst, tmp_le); + if (rc) + goto out; + + dst += 4; + } + + /* handle word aligned part */ + while (cnt >= 4) { + tmp_le = src[0] | src[1] << 8 | src[2] << 16 | src[3] << 24; + rc = write_word(info, dst, tmp_le); + if (rc) + goto out; + src += 4; + dst += 4; + cnt -= 4; + } + + if (cnt == 0) { + rc = ERR_OK; + goto out; + } + + /* handle unaligned tail bytes */ + tmp_le = 0; + for (i = 0, cp = (uchar *)dst; (i < 4) && (cnt > 0); ++i, ++cp) { + tmp_le |= *src++ << (i * 8); + --cnt; + } + + for (; i < 4; ++i, ++cp) + tmp_le |= *cp << (i * 8); + + rc = write_word(info, dst, tmp_le); +out: + /* + * flash content updated by nvm controller but CPU cache might + * have stale data, so invalidate dcache. + */ + invalidate_dcache_range(addr, addr + len); + + printf(" done\n"); + return rc; +} + +void flash_print_info(flash_info_t *info) +{ + int i; + + if (info->flash_id == FLASH_UNKNOWN) { + printf("missing or unknown FLASH type\n"); + return; + } + + switch (info->flash_id & FLASH_VENDMASK) { + case FLASH_MAN_MCHP: + printf("Microchip Technology "); + break; + default: + printf("Unknown Vendor "); + break; + } + + switch (info->flash_id & FLASH_TYPEMASK) { + case FLASH_MCHP100T: + printf("Internal (8 Mbit, 64 x 16k)\n"); + break; + default: + printf("Unknown Chip Type\n"); + break; + } + + printf(" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + + printf(" Sector Start Addresses:"); + for (i = 0; i < info->sector_count; ++i) { + if ((i % 5) == 0) + printf("\n "); + + printf(" %08lX%s", info->start[i], + info->protect[i] ? " (RO)" : " "); + } + printf("\n"); +} + +unsigned long flash_init(void) +{ + unsigned long size = 0; + struct udevice *dev; + int bank; + + /* probe every MTD device */ + for (uclass_first_device(UCLASS_MTD, &dev); dev; + uclass_next_device(&dev)) { + /* nop */ + } + + /* calc total flash size */ + for (bank = 0; bank < CONFIG_SYS_MAX_FLASH_BANKS; ++bank) + size += flash_info[bank].size; + + return size; +} + +static void pic32_flash_bank_init(flash_info_t *info, + ulong base, ulong size) +{ + ulong sect_size; + int sect; + + /* device & manufacturer code */ + info->flash_id = FLASH_MAN_MCHP | FLASH_MCHP100T; + info->sector_count = CONFIG_SYS_MAX_FLASH_SECT; + info->size = size; + + /* update sector (i.e page) info */ + sect_size = info->size / info->sector_count; + for (sect = 0; sect < info->sector_count; sect++) { + info->start[sect] = base; + /* protect each sector by default */ + info->protect[sect] = 1; + base += sect_size; + } +} + +static int pic32_flash_probe(struct udevice *dev) +{ + void *blob = (void *)gd->fdt_blob; + int node = dev->of_offset; + const char *list, *end; + const fdt32_t *cell; + unsigned long addr, size; + int parent, addrc, sizec; + flash_info_t *info; + int len, idx; + + /* + * decode regs. there are multiple reg tuples, and they need to + * match with reg-names. + */ + parent = fdt_parent_offset(blob, node); + of_bus_default_count_cells(blob, parent, &addrc, &sizec); + list = fdt_getprop(blob, node, "reg-names", &len); + if (!list) + return -ENOENT; + + end = list + len; + cell = fdt_getprop(blob, node, "reg", &len); + if (!cell) + return -ENOENT; + + for (idx = 0, info = &flash_info[0]; list < end;) { + addr = fdt_translate_address((void *)blob, node, cell + idx); + size = fdt_addr_to_cpu(cell[idx + addrc]); + len = strlen(list); + if (!strncmp(list, "nvm", len)) { + /* NVM controller */ + nvm_regs_p = ioremap(addr, size); + } else if (!strncmp(list, "bank", 4)) { + /* Flash bank: use kseg0 cached address */ + pic32_flash_bank_init(info, CKSEG0ADDR(addr), size); + info++; + } + idx += addrc + sizec; + list += len + 1; + } + + /* disable flash write/erase operations */ + writel(NVM_WREN, &nvm_regs_p->ctrl.clr); + +#if (CONFIG_SYS_MONITOR_BASE >= CONFIG_SYS_FLASH_BASE) + /* monitor protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CONFIG_SYS_MONITOR_BASE, + CONFIG_SYS_MONITOR_BASE + monitor_flash_len - 1, + &flash_info[0]); +#endif + +#ifdef CONFIG_ENV_IS_IN_FLASH + /* ENV protection ON by default */ + flash_protect(FLAG_PROTECT_SET, + CONFIG_ENV_ADDR, + CONFIG_ENV_ADDR + CONFIG_ENV_SECT_SIZE - 1, + &flash_info[0]); +#endif + return 0; +} + +static const struct udevice_id pic32_flash_ids[] = { + { .compatible = "microchip,pic32mzda-flash" }, + {} +}; + +U_BOOT_DRIVER(pic32_flash) = { + .name = "pic32_flash", + .id = UCLASS_MTD, + .of_match = pic32_flash_ids, + .probe = pic32_flash_probe, +}; diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 2a69babec44..567b7662d00 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -105,6 +105,24 @@ config SPL_PINCONF if PINCTRL || SPL_PINCTRL +config AR933X_PINCTRL + bool "QCA/Athores ar933x pin control driver" + depends on DM && SOC_AR933X + help + Support pin multiplexing control on QCA/Athores ar933x 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 QCA953X_PINCTRL + bool "QCA/Athores qca953x pin control driver" + depends on DM && SOC_QCA953X + help + Support pin multiplexing control on QCA/Athores qca953x 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 ROCKCHIP_PINCTRL bool "Rockchip pin control driver" depends on DM diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 37dc904640e..b99ed2f1916 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -6,6 +6,7 @@ obj-y += pinctrl-uclass.o obj-$(CONFIG_$(SPL_)PINCTRL_GENERIC) += pinctrl-generic.o obj-y += nxp/ +obj-$(CONFIG_ARCH_ATH79) += ath79/ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ obj-$(CONFIG_PINCTRL_SANDBOX) += pinctrl-sandbox.o diff --git a/drivers/pinctrl/ath79/Makefile b/drivers/pinctrl/ath79/Makefile new file mode 100644 index 00000000000..dcea10ace6a --- /dev/null +++ b/drivers/pinctrl/ath79/Makefile @@ -0,0 +1,6 @@ +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_AR933X_PINCTRL) += pinctrl_ar933x.o +obj-$(CONFIG_QCA953x_PINCTRL) += pinctrl_qca953x.o diff --git a/drivers/pinctrl/ath79/pinctrl_ar933x.c b/drivers/pinctrl/ath79/pinctrl_ar933x.c new file mode 100644 index 00000000000..e3f64b63553 --- /dev/null +++ b/drivers/pinctrl/ath79/pinctrl_ar933x.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2015-2016 Wills Wang <wills.wang@live.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <asm/io.h> +#include <dm/pinctrl.h> +#include <mach/ar71xx_regs.h> + +DECLARE_GLOBAL_DATA_PTR; + +enum periph_id { + PERIPH_ID_UART0, + PERIPH_ID_SPI0, + PERIPH_ID_NONE = -1, +}; + +struct ar933x_pinctrl_priv { + void __iomem *regs; +}; + +static void pinctrl_ar933x_spi_config(struct ar933x_pinctrl_priv *priv, int cs) +{ + switch (cs) { + case 0: + clrsetbits_be32(priv->regs + AR71XX_GPIO_REG_OE, + AR933X_GPIO(4), AR933X_GPIO(3) | + AR933X_GPIO(5) | AR933X_GPIO(2)); + setbits_be32(priv->regs + AR71XX_GPIO_REG_FUNC, + AR933X_GPIO_FUNC_SPI_EN | + AR933X_GPIO_FUNC_RES_TRUE); + break; + } +} + +static void pinctrl_ar933x_uart_config(struct ar933x_pinctrl_priv *priv, int uart_id) +{ + switch (uart_id) { + case PERIPH_ID_UART0: + clrsetbits_be32(priv->regs + AR71XX_GPIO_REG_OE, + AR933X_GPIO(9), AR933X_GPIO(10)); + setbits_be32(priv->regs + AR71XX_GPIO_REG_FUNC, + AR933X_GPIO_FUNC_UART_EN | + AR933X_GPIO_FUNC_RES_TRUE); + break; + } +} + +static int ar933x_pinctrl_request(struct udevice *dev, int func, int flags) +{ + struct ar933x_pinctrl_priv *priv = dev_get_priv(dev); + + debug("%s: func=%x, flags=%x\n", __func__, func, flags); + switch (func) { + case PERIPH_ID_SPI0: + pinctrl_ar933x_spi_config(priv, flags); + break; + case PERIPH_ID_UART0: + pinctrl_ar933x_uart_config(priv, func); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ar933x_pinctrl_get_periph_id(struct udevice *dev, + struct udevice *periph) +{ + u32 cell[2]; + 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[0]) { + case 128: + return PERIPH_ID_UART0; + case 129: + return PERIPH_ID_SPI0; + } + return -ENOENT; +} + +static int ar933x_pinctrl_set_state_simple(struct udevice *dev, + struct udevice *periph) +{ + int func; + + func = ar933x_pinctrl_get_periph_id(dev, periph); + if (func < 0) + return func; + return ar933x_pinctrl_request(dev, func, 0); +} + +static struct pinctrl_ops ar933x_pinctrl_ops = { + .set_state_simple = ar933x_pinctrl_set_state_simple, + .request = ar933x_pinctrl_request, + .get_periph_id = ar933x_pinctrl_get_periph_id, +}; + +static int ar933x_pinctrl_probe(struct udevice *dev) +{ + struct ar933x_pinctrl_priv *priv = dev_get_priv(dev); + fdt_addr_t addr; + + addr = dev_get_addr(dev); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regs = map_physmem(addr, + AR71XX_GPIO_SIZE, + MAP_NOCACHE); + return 0; +} + +static const struct udevice_id ar933x_pinctrl_ids[] = { + { .compatible = "qca,ar933x-pinctrl" }, + { } +}; + +U_BOOT_DRIVER(pinctrl_ar933x) = { + .name = "pinctrl_ar933x", + .id = UCLASS_PINCTRL, + .of_match = ar933x_pinctrl_ids, + .priv_auto_alloc_size = sizeof(struct ar933x_pinctrl_priv), + .ops = &ar933x_pinctrl_ops, + .probe = ar933x_pinctrl_probe, +}; diff --git a/drivers/pinctrl/ath79/pinctrl_qca953x.c b/drivers/pinctrl/ath79/pinctrl_qca953x.c new file mode 100644 index 00000000000..d02597e968a --- /dev/null +++ b/drivers/pinctrl/ath79/pinctrl_qca953x.c @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2015-2016 Wills Wang <wills.wang@live.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <asm/io.h> +#include <dm/pinctrl.h> +#include <mach/ar71xx_regs.h> + +DECLARE_GLOBAL_DATA_PTR; + +enum periph_id { + PERIPH_ID_UART0, + PERIPH_ID_SPI0, + PERIPH_ID_NONE = -1, +}; + +struct qca953x_pinctrl_priv { + void __iomem *regs; +}; + +static void pinctrl_qca953x_spi_config(struct qca953x_pinctrl_priv *priv, int cs) +{ + switch (cs) { + case 0: + clrsetbits_be32(priv->regs + AR71XX_GPIO_REG_OE, + QCA953X_GPIO(5) | QCA953X_GPIO(6) | + QCA953X_GPIO(7), QCA953X_GPIO(8)); + + clrsetbits_be32(priv->regs + QCA953X_GPIO_REG_OUT_FUNC1, + QCA953X_GPIO_MUX_MASK(8) | + QCA953X_GPIO_MUX_MASK(16) | + QCA953X_GPIO_MUX_MASK(24), + (QCA953X_GPIO_OUT_MUX_SPI_CS0 << 8) | + (QCA953X_GPIO_OUT_MUX_SPI_CLK << 16) | + (QCA953X_GPIO_OUT_MUX_SPI_MOSI << 24)); + + clrsetbits_be32(priv->regs + QCA953X_GPIO_REG_IN_ENABLE0, + QCA953X_GPIO_MUX_MASK(0), + QCA953X_GPIO_IN_MUX_SPI_DATA_IN); + + setbits_be32(priv->regs + AR71XX_GPIO_REG_OUT, + QCA953X_GPIO(8)); + break; + } +} + +static void pinctrl_qca953x_uart_config(struct qca953x_pinctrl_priv *priv, int uart_id) +{ + switch (uart_id) { + case PERIPH_ID_UART0: + clrsetbits_be32(priv->regs + AR71XX_GPIO_REG_OE, + QCA953X_GPIO(9), QCA953X_GPIO(10)); + + clrsetbits_be32(priv->regs + QCA953X_GPIO_REG_OUT_FUNC2, + QCA953X_GPIO_MUX_MASK(16), + QCA953X_GPIO_OUT_MUX_UART0_SOUT << 16); + + clrsetbits_be32(priv->regs + QCA953X_GPIO_REG_IN_ENABLE0, + QCA953X_GPIO_MUX_MASK(8), + QCA953X_GPIO_IN_MUX_UART0_SIN << 8); + + setbits_be32(priv->regs + AR71XX_GPIO_REG_OUT, + QCA953X_GPIO(10)); + break; + } +} + +static int qca953x_pinctrl_request(struct udevice *dev, int func, int flags) +{ + struct qca953x_pinctrl_priv *priv = dev_get_priv(dev); + + debug("%s: func=%x, flags=%x\n", __func__, func, flags); + switch (func) { + case PERIPH_ID_SPI0: + pinctrl_qca953x_spi_config(priv, flags); + break; + case PERIPH_ID_UART0: + pinctrl_qca953x_uart_config(priv, func); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int qca953x_pinctrl_get_periph_id(struct udevice *dev, + struct udevice *periph) +{ + u32 cell[2]; + 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[0]) { + case 128: + return PERIPH_ID_UART0; + case 129: + return PERIPH_ID_SPI0; + } + return -ENOENT; +} + +static int qca953x_pinctrl_set_state_simple(struct udevice *dev, + struct udevice *periph) +{ + int func; + + func = qca953x_pinctrl_get_periph_id(dev, periph); + if (func < 0) + return func; + return qca953x_pinctrl_request(dev, func, 0); +} + +static struct pinctrl_ops qca953x_pinctrl_ops = { + .set_state_simple = qca953x_pinctrl_set_state_simple, + .request = qca953x_pinctrl_request, + .get_periph_id = qca953x_pinctrl_get_periph_id, +}; + +static int qca953x_pinctrl_probe(struct udevice *dev) +{ + struct qca953x_pinctrl_priv *priv = dev_get_priv(dev); + fdt_addr_t addr; + + addr = dev_get_addr(dev); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regs = map_physmem(addr, + AR71XX_GPIO_SIZE, + MAP_NOCACHE); + return 0; +} + +static const struct udevice_id qca953x_pinctrl_ids[] = { + { .compatible = "qca,qca953x-pinctrl" }, + { } +}; + +U_BOOT_DRIVER(pinctrl_qca953x) = { + .name = "pinctrl_qca953x", + .id = UCLASS_PINCTRL, + .of_match = qca953x_pinctrl_ids, + .priv_auto_alloc_size = sizeof(struct qca953x_pinctrl_priv), + .ops = &qca953x_pinctrl_ops, + .probe = qca953x_pinctrl_probe, +}; diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index a9a5d475dd7..2497ae90a09 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -89,6 +89,15 @@ config DEBUG_UART_ALTERA_UART 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_AR933X + bool "QCA/Atheros ar933x" + depends on AR933X_UART + help + Select this to enable a debug UART using the ar933x uart 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 @@ -263,6 +272,15 @@ config ALTERA_UART Select this to enable an UART for Altera devices. Please find details on the "Embedded Peripherals IP User Guide" of Altera. +config AR933X_UART + bool "QCA/Atheros ar933x UART support" + depends on DM_SERIAL && SOC_AR933X + help + Select this to enable UART support for QCA/Atheros ar933x + devices. This driver uses driver model and requires a device + tree binding to operate, please refer to the document at + doc/device-tree-bindings/serial/qca,ar9330-uart.txt. + config FSL_LPUART bool "Freescale LPUART support" help diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index b0ac9d8a563..9def128e890 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -17,6 +17,7 @@ endif obj-$(CONFIG_ALTERA_UART) += altera_uart.o obj-$(CONFIG_ALTERA_JTAG_UART) += altera_jtag_uart.o +obj-$(CONFIG_AR933X_UART) += serial_ar933x.o obj-$(CONFIG_ARM_DCC) += arm_dcc.o obj-$(CONFIG_ATMEL_USART) += atmel_usart.o obj-$(CONFIG_EFI_APP) += serial_efi.o diff --git a/drivers/serial/serial_ar933x.c b/drivers/serial/serial_ar933x.c new file mode 100644 index 00000000000..aae66dc682b --- /dev/null +++ b/drivers/serial/serial_ar933x.c @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2015-2016 Wills Wang <wills.wang@live.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <div64.h> +#include <errno.h> +#include <serial.h> +#include <asm/io.h> +#include <asm/addrspace.h> +#include <asm/types.h> +#include <dm/pinctrl.h> +#include <mach/ar71xx_regs.h> + +#define AR933X_UART_DATA_REG 0x00 +#define AR933X_UART_CS_REG 0x04 +#define AR933X_UART_CLK_REG 0x08 + +#define AR933X_UART_DATA_TX_RX_MASK 0xff +#define AR933X_UART_DATA_RX_CSR BIT(8) +#define AR933X_UART_DATA_TX_CSR BIT(9) +#define AR933X_UART_CS_IF_MODE_S 2 +#define AR933X_UART_CS_IF_MODE_M 0x3 +#define AR933X_UART_CS_IF_MODE_DTE 1 +#define AR933X_UART_CS_IF_MODE_DCE 2 +#define AR933X_UART_CS_TX_RDY_ORIDE BIT(7) +#define AR933X_UART_CS_RX_RDY_ORIDE BIT(8) +#define AR933X_UART_CLK_STEP_M 0xffff +#define AR933X_UART_CLK_SCALE_M 0xfff +#define AR933X_UART_CLK_SCALE_S 16 +#define AR933X_UART_CLK_STEP_S 0 + +struct ar933x_serial_priv { + void __iomem *regs; +}; + +/* + * Baudrate algorithm come from Linux/drivers/tty/serial/ar933x_uart.c + * baudrate = (clk / (scale + 1)) * (step * (1 / 2^17)) + */ +static u32 ar933x_serial_get_baud(u32 clk, u32 scale, u32 step) +{ + u64 t; + u32 div; + + div = (2 << 16) * (scale + 1); + t = clk; + t *= step; + t += (div / 2); + do_div(t, div); + + return t; +} + +static void ar933x_serial_get_scale_step(u32 clk, u32 baud, + u32 *scale, u32 *step) +{ + u32 tscale, baudrate; + long min_diff; + + *scale = 0; + *step = 0; + + min_diff = baud; + for (tscale = 0; tscale < AR933X_UART_CLK_SCALE_M; tscale++) { + u64 tstep; + int diff; + + tstep = baud * (tscale + 1); + tstep *= (2 << 16); + do_div(tstep, clk); + + if (tstep > AR933X_UART_CLK_STEP_M) + break; + + baudrate = ar933x_serial_get_baud(clk, tscale, tstep); + diff = abs(baudrate - baud); + if (diff < min_diff) { + min_diff = diff; + *scale = tscale; + *step = tstep; + } + } +} + +static int ar933x_serial_setbrg(struct udevice *dev, int baudrate) +{ + struct ar933x_serial_priv *priv = dev_get_priv(dev); + u32 val, scale, step; + + val = get_serial_clock(); + ar933x_serial_get_scale_step(val, baudrate, &scale, &step); + + val = (scale & AR933X_UART_CLK_SCALE_M) + << AR933X_UART_CLK_SCALE_S; + val |= (step & AR933X_UART_CLK_STEP_M) + << AR933X_UART_CLK_STEP_S; + writel(val, priv->regs + AR933X_UART_CLK_REG); + + return 0; +} + +static int ar933x_serial_putc(struct udevice *dev, const char c) +{ + struct ar933x_serial_priv *priv = dev_get_priv(dev); + u32 data; + + data = readl(priv->regs + AR933X_UART_DATA_REG); + if (!(data & AR933X_UART_DATA_TX_CSR)) + return -EAGAIN; + + data = (u32)c | AR933X_UART_DATA_TX_CSR; + writel(data, priv->regs + AR933X_UART_DATA_REG); + + return 0; +} + +static int ar933x_serial_getc(struct udevice *dev) +{ + struct ar933x_serial_priv *priv = dev_get_priv(dev); + u32 data; + + data = readl(priv->regs + AR933X_UART_DATA_REG); + if (!(data & AR933X_UART_DATA_RX_CSR)) + return -EAGAIN; + + writel(AR933X_UART_DATA_RX_CSR, priv->regs + AR933X_UART_DATA_REG); + return data & AR933X_UART_DATA_TX_RX_MASK; +} + +static int ar933x_serial_pending(struct udevice *dev, bool input) +{ + struct ar933x_serial_priv *priv = dev_get_priv(dev); + u32 data; + + data = readl(priv->regs + AR933X_UART_DATA_REG); + if (input) + return (data & AR933X_UART_DATA_RX_CSR) ? 1 : 0; + else + return (data & AR933X_UART_DATA_TX_CSR) ? 0 : 1; +} + +static int ar933x_serial_probe(struct udevice *dev) +{ + struct ar933x_serial_priv *priv = dev_get_priv(dev); + fdt_addr_t addr; + u32 val; + + addr = dev_get_addr(dev); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regs = map_physmem(addr, AR933X_UART_SIZE, + MAP_NOCACHE); + + /* + * UART controller configuration: + * - no DMA + * - no interrupt + * - DCE mode + * - no flow control + * - set RX ready oride + * - set TX ready oride + */ + val = (AR933X_UART_CS_IF_MODE_DCE << AR933X_UART_CS_IF_MODE_S) | + AR933X_UART_CS_TX_RDY_ORIDE | AR933X_UART_CS_RX_RDY_ORIDE; + writel(val, priv->regs + AR933X_UART_CS_REG); + return 0; +} + +static const struct dm_serial_ops ar933x_serial_ops = { + .putc = ar933x_serial_putc, + .pending = ar933x_serial_pending, + .getc = ar933x_serial_getc, + .setbrg = ar933x_serial_setbrg, +}; + +static const struct udevice_id ar933x_serial_ids[] = { + { .compatible = "qca,ar9330-uart" }, + { } +}; + +U_BOOT_DRIVER(serial_ar933x) = { + .name = "serial_ar933x", + .id = UCLASS_SERIAL, + .of_match = ar933x_serial_ids, + .priv_auto_alloc_size = sizeof(struct ar933x_serial_priv), + .probe = ar933x_serial_probe, + .ops = &ar933x_serial_ops, + .flags = DM_FLAG_PRE_RELOC, +}; + +#ifdef CONFIG_DEBUG_UART_AR933X + +#include <debug_uart.h> + +static inline void _debug_uart_init(void) +{ + void __iomem *regs = (void *)CONFIG_DEBUG_UART_BASE; + u32 val, scale, step; + + /* + * UART controller configuration: + * - no DMA + * - no interrupt + * - DCE mode + * - no flow control + * - set RX ready oride + * - set TX ready oride + */ + val = (AR933X_UART_CS_IF_MODE_DCE << AR933X_UART_CS_IF_MODE_S) | + AR933X_UART_CS_TX_RDY_ORIDE | AR933X_UART_CS_RX_RDY_ORIDE; + writel(val, regs + AR933X_UART_CS_REG); + + ar933x_serial_get_scale_step(CONFIG_DEBUG_UART_CLOCK, + CONFIG_BAUDRATE, &scale, &step); + + val = (scale & AR933X_UART_CLK_SCALE_M) + << AR933X_UART_CLK_SCALE_S; + val |= (step & AR933X_UART_CLK_STEP_M) + << AR933X_UART_CLK_STEP_S; + writel(val, regs + AR933X_UART_CLK_REG); +} + +static inline void _debug_uart_putc(int c) +{ + void __iomem *regs = (void *)CONFIG_DEBUG_UART_BASE; + u32 data; + + do { + data = readl(regs + AR933X_UART_DATA_REG); + } while (!(data & AR933X_UART_DATA_TX_CSR)); + + data = (u32)c | AR933X_UART_DATA_TX_CSR; + writel(data, regs + AR933X_UART_DATA_REG); +} + +DEBUG_UART_FUNCS + +#endif diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index f0258f84afe..b7fd8e53a2f 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -23,6 +23,15 @@ config ALTERA_SPI IP core. Please find details on the "Embedded Peripherals IP User Guide" of Altera. +config ATH79_SPI + bool "Atheros SPI driver" + depends on ARCH_ATH79 + help + Enable the Atheros ar7xxx/ar9xxx SoC SPI driver, it was used + to access SPI NOR flash and other SPI peripherals. This driver + uses driver model and requires a device tree binding to operate. + please refer to doc/device-tree-bindings/spi/spi-ath79.txt. + config CADENCE_QSPI bool "Cadence QSPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 3eca7456d6a..7fb2926e781 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -17,6 +17,7 @@ endif obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_ARMADA100_SPI) += armada100_spi.o +obj-$(CONFIG_ATH79_SPI) += ath79_spi.o obj-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o obj-$(CONFIG_BFIN_SPI) += bfin_spi.o diff --git a/drivers/spi/ath79_spi.c b/drivers/spi/ath79_spi.c new file mode 100644 index 00000000000..b18c733b670 --- /dev/null +++ b/drivers/spi/ath79_spi.c @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2015-2016 Wills Wang <wills.wang@live.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <spi.h> +#include <dm.h> +#include <div64.h> +#include <errno.h> +#include <asm/io.h> +#include <asm/addrspace.h> +#include <asm/types.h> +#include <dm/pinctrl.h> +#include <mach/ar71xx_regs.h> + +/* CLOCK_DIVIDER = 3 (SPI clock = 200 / 8 ~ 25 MHz) */ +#define ATH79_SPI_CLK_DIV(x) (((x) >> 1) - 1) +#define ATH79_SPI_RRW_DELAY_FACTOR 12000 +#define ATH79_SPI_MHZ (1000 * 1000) + +struct ath79_spi_priv { + void __iomem *regs; + u32 rrw_delay; +}; + +static void spi_cs_activate(struct udevice *dev) +{ + struct udevice *bus = dev_get_parent(dev); + struct ath79_spi_priv *priv = dev_get_priv(bus); + + writel(AR71XX_SPI_FS_GPIO, priv->regs + AR71XX_SPI_REG_FS); + writel(AR71XX_SPI_IOC_CS_ALL, priv->regs + AR71XX_SPI_REG_IOC); +} + +static void spi_cs_deactivate(struct udevice *dev) +{ + struct udevice *bus = dev_get_parent(dev); + struct ath79_spi_priv *priv = dev_get_priv(bus); + + writel(AR71XX_SPI_IOC_CS_ALL, priv->regs + AR71XX_SPI_REG_IOC); + writel(0, priv->regs + AR71XX_SPI_REG_FS); +} + +static int ath79_spi_claim_bus(struct udevice *dev) +{ + return 0; +} + +static int ath79_spi_release_bus(struct udevice *dev) +{ + return 0; +} + +static int ath79_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct udevice *bus = dev_get_parent(dev); + struct ath79_spi_priv *priv = dev_get_priv(bus); + struct dm_spi_slave_platdata *slave = dev_get_parent_platdata(dev); + u8 *rx = din; + const u8 *tx = dout; + u8 curbyte, curbitlen, restbits; + u32 bytes = bitlen / 8; + u32 out, in; + u64 tick; + + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(dev); + + restbits = (bitlen % 8); + if (restbits) + bytes++; + + out = AR71XX_SPI_IOC_CS_ALL & ~(AR71XX_SPI_IOC_CS(slave->cs)); + while (bytes > 0) { + bytes--; + curbyte = 0; + if (tx) + curbyte = *tx++; + + if (restbits && !bytes) { + curbitlen = restbits; + curbyte <<= 8 - restbits; + } else { + curbitlen = 8; + } + + for (curbyte <<= (8 - curbitlen); curbitlen; curbitlen--) { + if (curbyte & 0x80) + out |= AR71XX_SPI_IOC_DO; + else + out &= ~(AR71XX_SPI_IOC_DO); + + writel(out, priv->regs + AR71XX_SPI_REG_IOC); + + /* delay for low level */ + if (priv->rrw_delay) { + tick = get_ticks() + priv->rrw_delay; + while (get_ticks() < tick) + /*NOP*/; + } + + writel(out | AR71XX_SPI_IOC_CLK, + priv->regs + AR71XX_SPI_REG_IOC); + + /* delay for high level */ + if (priv->rrw_delay) { + tick = get_ticks() + priv->rrw_delay; + while (get_ticks() < tick) + /*NOP*/; + } + + curbyte <<= 1; + } + + if (!bytes) + writel(out, priv->regs + AR71XX_SPI_REG_IOC); + + in = readl(priv->regs + AR71XX_SPI_REG_RDS); + if (rx) { + if (restbits && !bytes) + *rx++ = (in << (8 - restbits)); + else + *rx++ = in; + } + } + + if (flags & SPI_XFER_END) + spi_cs_deactivate(dev); + + return 0; +} + + +static int ath79_spi_set_speed(struct udevice *bus, uint speed) +{ + struct ath79_spi_priv *priv = dev_get_priv(bus); + u32 val, div = 0; + u64 time; + + if (speed) + div = get_bus_freq(0) / speed; + + if (div > 63) + div = 63; + + if (div < 5) + div = 5; + + /* calculate delay */ + time = get_tbclk(); + do_div(time, speed / 2); + val = get_bus_freq(0) / ATH79_SPI_MHZ; + val = ATH79_SPI_RRW_DELAY_FACTOR / val; + if (time > val) + priv->rrw_delay = time - val + 1; + else + priv->rrw_delay = 0; + + writel(AR71XX_SPI_FS_GPIO, priv->regs + AR71XX_SPI_REG_FS); + clrsetbits_be32(priv->regs + AR71XX_SPI_REG_CTRL, + AR71XX_SPI_CTRL_DIV_MASK, + ATH79_SPI_CLK_DIV(div)); + writel(0, priv->regs + AR71XX_SPI_REG_FS); + return 0; +} + +static int ath79_spi_set_mode(struct udevice *bus, uint mode) +{ + return 0; +} + +static int ath79_spi_probe(struct udevice *bus) +{ + struct ath79_spi_priv *priv = dev_get_priv(bus); + fdt_addr_t addr; + + addr = dev_get_addr(bus); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->regs = map_physmem(addr, + AR71XX_SPI_SIZE, + MAP_NOCACHE); + + /* Init SPI Hardware, disable remap, set clock */ + writel(AR71XX_SPI_FS_GPIO, priv->regs + AR71XX_SPI_REG_FS); + writel(AR71XX_SPI_CTRL_RD | ATH79_SPI_CLK_DIV(8), + priv->regs + AR71XX_SPI_REG_CTRL); + writel(0, priv->regs + AR71XX_SPI_REG_FS); + + return 0; +} + +static int ath79_cs_info(struct udevice *bus, uint cs, + struct spi_cs_info *info) +{ + /* Always allow activity on CS 0/1/2 */ + if (cs >= 3) + return -ENODEV; + + return 0; +} + +static const struct dm_spi_ops ath79_spi_ops = { + .claim_bus = ath79_spi_claim_bus, + .release_bus = ath79_spi_release_bus, + .xfer = ath79_spi_xfer, + .set_speed = ath79_spi_set_speed, + .set_mode = ath79_spi_set_mode, + .cs_info = ath79_cs_info, +}; + +static const struct udevice_id ath79_spi_ids[] = { + { .compatible = "qca,ar7100-spi" }, + {} +}; + +U_BOOT_DRIVER(ath79_spi) = { + .name = "ath79_spi", + .id = UCLASS_SPI, + .of_match = ath79_spi_ids, + .ops = &ath79_spi_ops, + .priv_auto_alloc_size = sizeof(struct ath79_spi_priv), + .probe = ath79_spi_probe, +}; |