Browse Source

Merge tag 'u-boot-atmel-2021.01-a' of https://gitlab.denx.de/u-boot/custodians/u-boot-atmel into next

First set of u-boot-atmel features for 2021.01 cycle:

This feature set includes a new CPU driver for at91 family, new driver
for PIT64B hardware timer, support for new at91 family SoC named sama7g5
which adds: clock support, including conversion of the clock tree to
CCF; SoC support in mach-at91, pinctrl and mmc drivers update.  The
feature set also includes updates for mmc driver and some other minor
fixes and features regarding building without the old Atmel PIT and the
possibility to read a secondary MAC address from a second i2c EEPROM.
Tom Rini 3 years ago
parent
commit
caebff09ef
46 changed files with 6713 additions and 928 deletions
  1. 2 0
      MAINTAINERS
  2. 924 0
      arch/arm/dts/sama7g5-pinfunc.h
  3. 4 0
      arch/arm/mach-at91/Kconfig
  4. 1 0
      arch/arm/mach-at91/armv7/Makefile
  5. 2 0
      arch/arm/mach-at91/armv7/cpu.c
  6. 11 0
      arch/arm/mach-at91/armv7/sama7g5_devices.c
  7. 1 0
      arch/arm/mach-at91/include/mach/at91_common.h
  8. 2 0
      arch/arm/mach-at91/include/mach/hardware.h
  9. 74 0
      arch/arm/mach-at91/include/mach/sama7g5.h
  10. 33 0
      board/atmel/common/mac_eeprom.c
  11. 7 0
      drivers/clk/at91/Kconfig
  12. 9 6
      drivers/clk/at91/Makefile
  13. 0 178
      drivers/clk/at91/clk-generated.c
  14. 202 0
      drivers/clk/at91/clk-generic.c
  15. 0 56
      drivers/clk/at91/clk-h32mx.c
  16. 357 24
      drivers/clk/at91/clk-main.c
  17. 315 16
      drivers/clk/at91/clk-master.c
  18. 216 75
      drivers/clk/at91/clk-peripheral.c
  19. 0 54
      drivers/clk/at91/clk-plla.c
  20. 0 85
      drivers/clk/at91/clk-plladiv.c
  21. 208 0
      drivers/clk/at91/clk-programmable.c
  22. 442 0
      drivers/clk/at91/clk-sam9x60-pll.c
  23. 0 36
      drivers/clk/at91/clk-slow.c
  24. 72 71
      drivers/clk/at91/clk-system.c
  25. 0 147
      drivers/clk/at91/clk-usb.c
  26. 163 71
      drivers/clk/at91/clk-utmi.c
  27. 1023 0
      drivers/clk/at91/compat.c
  28. 133 85
      drivers/clk/at91/pmc.c
  29. 132 8
      drivers/clk/at91/pmc.h
  30. 1401 0
      drivers/clk/at91/sama7g5.c
  31. 161 8
      drivers/clk/at91/sckc.c
  32. 46 5
      drivers/clk/clk-uclass.c
  33. 3 0
      drivers/clk/clk.c
  34. 22 0
      drivers/core/device.c
  35. 1 0
      drivers/cpu/Makefile
  36. 123 0
      drivers/cpu/at91_cpu.c
  37. 10 3
      drivers/mmc/atmel_sdhci.c
  38. 1 0
      drivers/pinctrl/pinctrl-at91-pio4.c
  39. 7 0
      drivers/timer/Kconfig
  40. 1 0
      drivers/timer/Makefile
  41. 109 0
      drivers/timer/mchp-pit64b-timer.c
  42. 9 0
      include/dm/device-internal.h
  43. 22 0
      include/dt-bindings/clk/at91.h
  44. 247 0
      include/linux/clk/at91_pmc.h
  45. 57 0
      test/dm/clk_ccf.c
  46. 160 0
      test/dm/core.c

+ 2 - 0
MAINTAINERS

@@ -295,7 +295,9 @@ S:	Maintained
 T:	git https://gitlab.denx.de/u-boot/custodians/u-boot-atmel.git
 F:	arch/arm/mach-at91/
 F:	board/atmel/
+F:	drivers/cpu/at91_cpu.c
 F:	drivers/misc/microchip_flexcom.c
+F:	drivers/timer/mchp-pit64b-timer.c
 
 ARM NEXELL S5P4418
 M:	Stefan Bosch <stefan_b@posteo.net>

+ 924 - 0
arch/arm/dts/sama7g5-pinfunc.h

@@ -0,0 +1,924 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#define PINMUX_PIN(no, func, ioset) \
+(((no) & 0xffff) | (((func) & 0xf) << 16) | (((ioset) & 0xff) << 20))
+
+#define PIN_PA0				0
+#define PIN_PA0__GPIO			PINMUX_PIN(PIN_PA0, 0, 0)
+#define PIN_PA0__SDMMC0_CK		PINMUX_PIN(PIN_PA0, 1, 1)
+#define PIN_PA0__FLEXCOM0_IO0		PINMUX_PIN(PIN_PA0, 2, 1)
+#define PIN_PA0__CANTX3			PINMUX_PIN(PIN_PA0, 3, 1)
+#define PIN_PA0__PWML0			PINMUX_PIN(PIN_PA0, 5, 2)
+#define PIN_PA1				1
+#define PIN_PA1__GPIO			PINMUX_PIN(PIN_PA1, 0, 0)
+#define PIN_PA1__SDMMC0_CMD		PINMUX_PIN(PIN_PA1, 1, 1)
+#define PIN_PA1__FLEXCOM0_IO1		PINMUX_PIN(PIN_PA1, 2, 1)
+#define PIN_PA1__CANRX3			PINMUX_PIN(PIN_PA1, 3, 1)
+#define PIN_PA1__D14			PINMUX_PIN(PIN_PA1, 4, 1)
+#define PIN_PA1__PWMH0			PINMUX_PIN(PIN_PA1, 5, 3)
+#define PIN_PA2				2
+#define PIN_PA2__GPIO			PINMUX_PIN(PIN_PA2, 0, 0)
+#define PIN_PA2__SDMMC0_RSTN		PINMUX_PIN(PIN_PA2, 1, 1)
+#define PIN_PA2__FLEXCOM0_IO2		PINMUX_PIN(PIN_PA2, 2, 1)
+#define PIN_PA2__PDMC1_CLK		PINMUX_PIN(PIN_PA2, 3, 1)
+#define PIN_PA2__D15			PINMUX_PIN(PIN_PA2, 4, 1)
+#define PIN_PA2__PWMH1			PINMUX_PIN(PIN_PA2, 5, 3)
+#define PIN_PA2__FLEXCOM1_IO0		PINMUX_PIN(PIN_PA2, 6, 3)
+#define PIN_PA3				3
+#define PIN_PA3__GPIO			PINMUX_PIN(PIN_PA3, 0, 0)
+#define PIN_PA3__SDMMC0_DAT0		PINMUX_PIN(PIN_PA3, 1, 1)
+#define PIN_PA3__FLEXCOM0_IO3		PINMUX_PIN(PIN_PA3, 2, 1)
+#define PIN_PA3__PDMC1_DS0		PINMUX_PIN(PIN_PA3, 3, 1)
+#define PIN_PA3__NWR1_NBS1		PINMUX_PIN(PIN_PA3, 4, 1)
+#define PIN_PA3__PWML3			PINMUX_PIN(PIN_PA3, 5, 3)
+#define PIN_PA3__FLEXCOM1_IO1		PINMUX_PIN(PIN_PA3, 6, 3)
+#define PIN_PA4				4
+#define PIN_PA4__GPIO			PINMUX_PIN(PIN_PA4, 0, 0)
+#define PIN_PA4__SDMMC0_DAT1		PINMUX_PIN(PIN_PA4, 1, 1)
+#define PIN_PA4__FLEXCOM0_IO4		PINMUX_PIN(PIN_PA4, 2, 1)
+#define PIN_PA4__PDMC1_DS1		PINMUX_PIN(PIN_PA4, 3, 1)
+#define PIN_PA4__NCS2			PINMUX_PIN(PIN_PA4, 4, 1)
+#define PIN_PA4__PWMH3			PINMUX_PIN(PIN_PA4, 5, 3)
+#define PIN_PA4__FLEXCOM2_IO0		PINMUX_PIN(PIN_PA4, 6, 3)
+#define PIN_PA5				5
+#define PIN_PA5__GPIO			PINMUX_PIN(PIN_PA5, 0, 0)
+#define PIN_PA5__SDMMC0_DAT2		PINMUX_PIN(PIN_PA5, 1, 1)
+#define PIN_PA5__FLEXCOM1_IO0		PINMUX_PIN(PIN_PA5, 2, 1)
+#define PIN_PA5__CANTX2			PINMUX_PIN(PIN_PA5, 3, 1)
+#define PIN_PA5__A23			PINMUX_PIN(PIN_PA5, 4, 1)
+#define PIN_PA5__PWMEXTRG0		PINMUX_PIN(PIN_PA5, 5, 3)
+#define PIN_PA5__FLEXCOM2_IO1		PINMUX_PIN(PIN_PA5, 6, 3)
+#define PIN_PA6				6
+#define PIN_PA6__GPIO			PINMUX_PIN(PIN_PA6, 0, 0)
+#define PIN_PA6__SDMMC0_DAT3		PINMUX_PIN(PIN_PA6, 1, 1)
+#define PIN_PA6__FLEXCOM1_IO1		PINMUX_PIN(PIN_PA6, 2, 1)
+#define PIN_PA6__CANRX2			PINMUX_PIN(PIN_PA6, 3, 1)
+#define PIN_PA6__A24			PINMUX_PIN(PIN_PA6, 4, 1)
+#define PIN_PA6__PWMEXTRG1		PINMUX_PIN(PIN_PA6, 5, 3)
+#define PIN_PA6__FLEXCOM3_IO0		PINMUX_PIN(PIN_PA6, 6, 3)
+#define PIN_PA7				7
+#define PIN_PA7__GPIO			PINMUX_PIN(PIN_PA7, 0, 0)
+#define PIN_PA7__SDMMC0_DAT4		PINMUX_PIN(PIN_PA7, 1, 1)
+#define PIN_PA7__FLEXCOM2_IO0		PINMUX_PIN(PIN_PA7, 2, 1)
+#define PIN_PA7__CANTX1			PINMUX_PIN(PIN_PA7, 3, 1)
+#define PIN_PA7__NWAIT			PINMUX_PIN(PIN_PA7, 4, 1)
+#define PIN_PA7__PWMFI0			PINMUX_PIN(PIN_PA7, 5, 3)
+#define PIN_PA7__FLEXCOM3_IO1		PINMUX_PIN(PIN_PA7, 6, 3)
+#define PIN_PA8				8
+#define PIN_PA8__GPIO			PINMUX_PIN(PIN_PA8, 0, 0)
+#define PIN_PA8__SDMMC0_DAT5		PINMUX_PIN(PIN_PA8, 1, 1)
+#define PIN_PA8__FLEXCOM2_IO1		PINMUX_PIN(PIN_PA8, 2, 1)
+#define PIN_PA8__CANRX1			PINMUX_PIN(PIN_PA8, 3, 1)
+#define PIN_PA8__NCS0			PINMUX_PIN(PIN_PA8, 4, 1)
+#define PIN_PA8__PWMIF1			PINMUX_PIN(PIN_PA8, 5, 3)
+#define PIN_PA8__FLEXCOM4_IO0		PINMUX_PIN(PIN_PA8, 6, 3)
+#define PIN_PA9				9
+#define PIN_PA9__GPIO			PINMUX_PIN(PIN_PA9, 0, 0)
+#define PIN_PA9__SDMMC0_DAT6		PINMUX_PIN(PIN_PA9, 1, 1)
+#define PIN_PA9__FLEXCOM2_IO2		PINMUX_PIN(PIN_PA9, 2, 1)
+#define PIN_PA9__CANTX0			PINMUX_PIN(PIN_PA9, 3, 1)
+#define PIN_PA9__SMCK			PINMUX_PIN(PIN_PA9, 4, 1)
+#define PIN_PA9__SPDIF_RX		PINMUX_PIN(PIN_PA9, 5, 1)
+#define PIN_PA9__FLEXCOM4_IO1		PINMUX_PIN(PIN_PA9, 6, 3)
+#define PIN_PA10			10
+#define PIN_PA10__GPIO			PINMUX_PIN(PIN_PA10, 0, 0)
+#define PIN_PA10__SDMMC0_DAT7		PINMUX_PIN(PIN_PA10, 1, 1)
+#define PIN_PA10__FLEXCOM2_IO3		PINMUX_PIN(PIN_PA10, 2, 1)
+#define PIN_PA10__CANRX0		PINMUX_PIN(PIN_PA10, 3, 1)
+#define PIN_PA10__NCS1			PINMUX_PIN(PIN_PA10, 4, 1)
+#define PIN_PA10__SPDIF_TX		PINMUX_PIN(PIN_PA10, 5, 1)
+#define PIN_PA10__FLEXCOM5_IO0		PINMUX_PIN(PIN_PA10, 6, 3)
+#define PIN_PA11			11
+#define PIN_PA11__GPIO			PINMUX_PIN(PIN_PA11, 0, 0)
+#define PIN_PA11__SDMMC0_DS		PINMUX_PIN(PIN_PA11, 1, 1)
+#define PIN_PA11__FLEXCOM2_IO4		PINMUX_PIN(PIN_PA11, 2, 1)
+#define PIN_PA11__A0_NBS0		PINMUX_PIN(PIN_PA11, 4, 1)
+#define PIN_PA11__TIOA0			PINMUX_PIN(PIN_PA11, 5, 1)
+#define PIN_PA11__FLEXCOM5_IO1		PINMUX_PIN(PIN_PA11, 6, 3)
+#define PIN_PA12			12
+#define PIN_PA12__GPIO			PINMUX_PIN(PIN_PA12, 0, 0)
+#define PIN_PA12__SDMMC0_WP		PINMUX_PIN(PIN_PA12, 1, 1)
+#define PIN_PA12__FLEXCOM1_IO3		PINMUX_PIN(PIN_PA12, 2, 1)
+#define PIN_PA12__FLEXCOM3_IO5		PINMUX_PIN(PIN_PA12, 4, 1)
+#define PIN_PA12__PWML2			PINMUX_PIN(PIN_PA12, 5, 3)
+#define PIN_PA12__FLEXCOM6_IO0		PINMUX_PIN(PIN_PA12, 6, 3)
+#define PIN_PA13			13
+#define PIN_PA13__GPIO			PINMUX_PIN(PIN_PA13, 0, 0)
+#define PIN_PA13__SDMMC0_1V8SEL		PINMUX_PIN(PIN_PA13, 1, 1)
+#define PIN_PA13__FLEXCOM1_IO2		PINMUX_PIN(PIN_PA13, 2, 1)
+#define PIN_PA13__FLEXCOM3_IO6		PINMUX_PIN(PIN_PA13, 4, 1)
+#define PIN_PA13__PWMH2			PINMUX_PIN(PIN_PA13, 5, 3)
+#define PIN_PA13__FLEXCOM6_IO1		PINMUX_PIN(PIN_PA13, 6, 3)
+#define PIN_PA14			14
+#define PIN_PA14__GPIO			PINMUX_PIN(PIN_PA14, 0, 0)
+#define PIN_PA14__SDMMC0_CD		PINMUX_PIN(PIN_PA14, 1, 1)
+#define PIN_PA14__FLEXCOM1_IO4		PINMUX_PIN(PIN_PA14, 2, 1)
+#define PIN_PA14__A25			PINMUX_PIN(PIN_PA14, 4, 1)
+#define PIN_PA14__PWML1			PINMUX_PIN(PIN_PA14, 5, 3)
+#define PIN_PA15			15
+#define PIN_PA15__GPIO			PINMUX_PIN(PIN_PA15, 0, 0)
+#define PIN_PA15__G0_TXEN		PINMUX_PIN(PIN_PA15, 1, 1)
+#define PIN_PA15__FLEXCOM3_IO0		PINMUX_PIN(PIN_PA15, 2, 1)
+#define PIN_PA15__ISC_MCK		PINMUX_PIN(PIN_PA15, 3, 1)
+#define PIN_PA15__A1			PINMUX_PIN(PIN_PA15, 4, 1)
+#define PIN_PA15__TIOB0			PINMUX_PIN(PIN_PA15, 5, 1)
+#define PIN_PA16			16
+#define PIN_PA16__GPIO			PINMUX_PIN(PIN_PA16, 0, 0)
+#define PIN_PA16__G0_TX0		PINMUX_PIN(PIN_PA16, 1, 1)
+#define PIN_PA16__FLEXCOM3_IO1		PINMUX_PIN(PIN_PA16, 2, 1)
+#define PIN_PA16__ISC_D0		PINMUX_PIN(PIN_PA16, 3, 1)
+#define PIN_PA16__A2			PINMUX_PIN(PIN_PA16, 4, 1)
+#define PIN_PA16__TCLK0			PINMUX_PIN(PIN_PA16, 5, 1)
+#define PIN_PA17			17
+#define PIN_PA17__GPIO			PINMUX_PIN(PIN_PA17, 0, 0)
+#define PIN_PA17__G0_TX1		PINMUX_PIN(PIN_PA17, 1, 1)
+#define PIN_PA17__FLEXCOM3_IO2		PINMUX_PIN(PIN_PA17, 2, 1)
+#define PIN_PA17__ISC_D1		PINMUX_PIN(PIN_PA17, 3, 1)
+#define PIN_PA17__A3			PINMUX_PIN(PIN_PA17, 4, 1)
+#define PIN_PA17__TIOA1			PINMUX_PIN(PIN_PA17, 5, 1)
+#define PIN_PA18			18
+#define PIN_PA18__GPIO			PINMUX_PIN(PIN_PA18, 0, 0)
+#define PIN_PA18__G0_RXDV		PINMUX_PIN(PIN_PA18, 1, 1)
+#define PIN_PA18__FLEXCOM3_IO3		PINMUX_PIN(PIN_PA18, 2, 1)
+#define PIN_PA18__ISC_D2		PINMUX_PIN(PIN_PA18, 3, 1)
+#define PIN_PA18__A4			PINMUX_PIN(PIN_PA18, 4, 1)
+#define PIN_PA18__TIOB1			PINMUX_PIN(PIN_PA18, 5, 1)
+#define PIN_PA19			19
+#define PIN_PA19__GPIO			PINMUX_PIN(PIN_PA19, 0, 0)
+#define PIN_PA19__G0_RX0		PINMUX_PIN(PIN_PA19, 1, 1)
+#define PIN_PA19__FLEXCOM3_IO4		PINMUX_PIN(PIN_PA19, 2, 1)
+#define PIN_PA19__ISC_D3		PINMUX_PIN(PIN_PA19, 3, 1)
+#define PIN_PA19__A5			PINMUX_PIN(PIN_PA19, 4, 1)
+#define PIN_PA19__TCLK1			PINMUX_PIN(PIN_PA19, 5, 1)
+#define PIN_PA20			20
+#define PIN_PA20__GPIO			PINMUX_PIN(PIN_PA20, 0, 0)
+#define PIN_PA20__G0_RX1		PINMUX_PIN(PIN_PA20, 1, 1)
+#define PIN_PA20__FLEXCOM4_IO0		PINMUX_PIN(PIN_PA20, 2, 1)
+#define PIN_PA20__ISC_D4		PINMUX_PIN(PIN_PA20, 3, 1)
+#define PIN_PA20__A6			PINMUX_PIN(PIN_PA20, 4, 1)
+#define PIN_PA20__TIOA2			PINMUX_PIN(PIN_PA20, 5, 1)
+#define PIN_PA21			21
+#define PIN_PA21__GPIO			PINMUX_PIN(PIN_PA21, 0, 0)
+#define PIN_PA21__G0_RXER		PINMUX_PIN(PIN_PA21, 1, 1)
+#define PIN_PA21__FLEXCOM4_IO1		PINMUX_PIN(PIN_PA21, 2, 1)
+#define PIN_PA21__ISC_D5		PINMUX_PIN(PIN_PA21, 3, 1)
+#define PIN_PA21__A7			PINMUX_PIN(PIN_PA21, 4, 1)
+#define PIN_PA21__TIOB2			PINMUX_PIN(PIN_PA21, 5, 1)
+#define PIN_PA22			22
+#define PIN_PA22__GPIO			PINMUX_PIN(PIN_PA22, 0, 0)
+#define PIN_PA22__G0_MDC		PINMUX_PIN(PIN_PA22, 1, 1)
+#define PIN_PA22__FLEXCOM4_IO2		PINMUX_PIN(PIN_PA22, 2, 1)
+#define PIN_PA22__ISC_D6		PINMUX_PIN(PIN_PA22, 3, 1)
+#define PIN_PA22__A8			PINMUX_PIN(PIN_PA22, 4, 1)
+#define PIN_PA22__TCLK2			PINMUX_PIN(PIN_PA22, 5, 1)
+#define PIN_PA23			23
+#define PIN_PA23__GPIO			PINMUX_PIN(PIN_PA23, 0, 0)
+#define PIN_PA23__G0_MDIO		PINMUX_PIN(PIN_PA23, 1, 1)
+#define PIN_PA23__FLEXCOM4_IO3		PINMUX_PIN(PIN_PA23, 2, 1)
+#define PIN_PA23__ISC_D7		PINMUX_PIN(PIN_PA23, 3, 1)
+#define PIN_PA23__A9			PINMUX_PIN(PIN_PA23, 4, 1)
+#define PIN_PA24			24
+#define PIN_PA24__GPIO			PINMUX_PIN(PIN_PA24, 0, 0)
+#define PIN_PA24__G0_TXCK		PINMUX_PIN(PIN_PA24, 1, 1)
+#define PIN_PA24__FLEXCOM4_IO4		PINMUX_PIN(PIN_PA24, 2, 1)
+#define PIN_PA24__ISC_HSYNC		PINMUX_PIN(PIN_PA24, 3, 1)
+#define PIN_PA24__A10			PINMUX_PIN(PIN_PA24, 4, 1)
+#define PIN_PA24__FLEXCOM0_IO5		PINMUX_PIN(PIN_PA24, 5, 1)
+#define PIN_PA25			25
+#define PIN_PA25__GPIO			PINMUX_PIN(PIN_PA25, 0, 0)
+#define PIN_PA25__G0_125CK		PINMUX_PIN(PIN_PA25, 1, 1)
+#define PIN_PA25__FLEXCOM5_IO4		PINMUX_PIN(PIN_PA25, 2, 1)
+#define PIN_PA25__ISC_VSYNC		PINMUX_PIN(PIN_PA25, 3, 1)
+#define PIN_PA25__A11			PINMUX_PIN(PIN_PA25, 4, 1)
+#define PIN_PA25__FLEXCOM0_IO6		PINMUX_PIN(PIN_PA25, 5, 1)
+#define PIN_PA25__FLEXCOM7_IO0		PINMUX_PIN(PIN_PA25, 6, 3)
+#define PIN_PA26			26
+#define PIN_PA26__GPIO			PINMUX_PIN(PIN_PA26, 0, 0)
+#define PIN_PA26__G0_TX2		PINMUX_PIN(PIN_PA26, 1, 1)
+#define PIN_PA26__FLEXCOM5_IO2		PINMUX_PIN(PIN_PA26, 2, 1)
+#define PIN_PA26__ISC_FIELD		PINMUX_PIN(PIN_PA26, 3, 1)
+#define PIN_PA26__A12			PINMUX_PIN(PIN_PA26, 4, 1)
+#define PIN_PA26__TF0			PINMUX_PIN(PIN_PA26, 5, 1)
+#define PIN_PA26__FLEXCOM7_IO1		PINMUX_PIN(PIN_PA26, 6, 3)
+#define PIN_PA27			27
+#define PIN_PA27__GPIO			PINMUX_PIN(PIN_PA27, 0, 0)
+#define PIN_PA27__G0_TX3		PINMUX_PIN(PIN_PA27, 1, 1)
+#define PIN_PA27__FLEXCOM5_IO3		PINMUX_PIN(PIN_PA27, 2, 1)
+#define PIN_PA27__ISC_PCK		PINMUX_PIN(PIN_PA27, 3, 1)
+#define PIN_PA27__A13			PINMUX_PIN(PIN_PA27, 4, 1)
+#define PIN_PA27__TK0			PINMUX_PIN(PIN_PA27, 5, 1)
+#define PIN_PA27__FLEXCOM8_IO0		PINMUX_PIN(PIN_PA27, 6, 3)
+#define PIN_PA28			28
+#define PIN_PA28__GPIO			PINMUX_PIN(PIN_PA28, 0, 0)
+#define PIN_PA28__G0_RX2		PINMUX_PIN(PIN_PA28, 1, 1)
+#define PIN_PA28__FLEXCOM5_IO0		PINMUX_PIN(PIN_PA28, 2, 1)
+#define PIN_PA28__ISC_D8		PINMUX_PIN(PIN_PA28, 3, 1)
+#define PIN_PA28__A14			PINMUX_PIN(PIN_PA28, 4, 1)
+#define PIN_PA28__RD0			PINMUX_PIN(PIN_PA28, 5, 1)
+#define PIN_PA28__FLEXCOM8_IO1		PINMUX_PIN(PIN_PA28, 6, 3)
+#define PIN_PA29			29
+#define PIN_PA29__GPIO			PINMUX_PIN(PIN_PA29, 0, 0)
+#define PIN_PA29__G0_RX3		PINMUX_PIN(PIN_PA29, 1, 1)
+#define PIN_PA29__FLEXCOM5_IO1		PINMUX_PIN(PIN_PA29, 2, 1)
+#define PIN_PA29__ISC_D9		PINMUX_PIN(PIN_PA29, 3, 1)
+#define PIN_PA29__A15			PINMUX_PIN(PIN_PA29, 4, 1)
+#define PIN_PA29__RF0			PINMUX_PIN(PIN_PA29, 5, 1)
+#define PIN_PA29__FLEXCOM9_IO0		PINMUX_PIN(PIN_PA29, 6, 3)
+#define PIN_PA30			30
+#define PIN_PA30__GPIO			PINMUX_PIN(PIN_PA30, 0, 0)
+#define PIN_PA30__G0_RXCK		PINMUX_PIN(PIN_PA30, 1, 1)
+#define PIN_PA30__FLEXCOM6_IO4		PINMUX_PIN(PIN_PA30, 2, 1)
+#define PIN_PA30__ISC_D10		PINMUX_PIN(PIN_PA30, 3, 1)
+#define PIN_PA30__A16			PINMUX_PIN(PIN_PA30, 4, 1)
+#define PIN_PA30__RK0			PINMUX_PIN(PIN_PA30, 5, 1)
+#define PIN_PA30__FLEXCOM9_IO1		PINMUX_PIN(PIN_PA30, 6, 3)
+#define PIN_PA31			31
+#define PIN_PA31__GPIO			PINMUX_PIN(PIN_PA31, 0, 0)
+#define PIN_PA31__G0_TXER		PINMUX_PIN(PIN_PA31, 1, 1)
+#define PIN_PA31__FLEXCOM6_IO2		PINMUX_PIN(PIN_PA31, 2, 1)
+#define PIN_PA31__ISC_D11		PINMUX_PIN(PIN_PA31, 3, 1)
+#define PIN_PA31__A17			PINMUX_PIN(PIN_PA31, 4, 1)
+#define PIN_PA31__TD0			PINMUX_PIN(PIN_PA31, 5, 1)
+#define PIN_PA31__FLEXCOM10_IO0		PINMUX_PIN(PIN_PA31, 6, 3)
+#define PIN_PB0				32
+#define PIN_PB0__GPIO			PINMUX_PIN(PIN_PB0, 0, 0)
+#define PIN_PB0__G0_COL			PINMUX_PIN(PIN_PB0, 1, 1)
+#define PIN_PB0__FLEXCOM6_IO3		PINMUX_PIN(PIN_PB0, 2, 2)
+#define PIN_PB0__EXT_IRQ0		PINMUX_PIN(PIN_PB0, 3, 1)
+#define PIN_PB0__A18			PINMUX_PIN(PIN_PB0, 4, 1)
+#define PIN_PB0__SPDIF_RX		PINMUX_PIN(PIN_PB0, 5, 2)
+#define PIN_PB0__FLEXCOM10_IO1		PINMUX_PIN(PIN_PB0, 6, 3)
+#define PIN_PB1				33
+#define PIN_PB1__GPIO			PINMUX_PIN(PIN_PB1, 0, 0)
+#define PIN_PB1__G0_CRS			PINMUX_PIN(PIN_PB1, 1, 1)
+#define PIN_PB1__FLEXCOM6_IO1		PINMUX_PIN(PIN_PB1, 2, 2)
+#define PIN_PB1__EXT_IRQ1		PINMUX_PIN(PIN_PB1, 3, 1)
+#define PIN_PB1__A19			PINMUX_PIN(PIN_PB1, 4, 1)
+#define PIN_PB1__SPDIF_TX		PINMUX_PIN(PIN_PB1, 5, 2)
+#define PIN_PB1__FLEXCOM11_IO0		PINMUX_PIN(PIN_PB1, 6, 3)
+#define PIN_PB2				34
+#define PIN_PB2__GPIO			PINMUX_PIN(PIN_PB2, 0, 0)
+#define PIN_PB2__G0_TSUCOMP		PINMUX_PIN(PIN_PB2, 1, 1)
+#define PIN_PB2__FLEXCOM6_IO0		PINMUX_PIN(PIN_PB2, 2, 1)
+#define PIN_PB2__ADTRG			PINMUX_PIN(PIN_PB2, 3, 1)
+#define PIN_PB2__A20			PINMUX_PIN(PIN_PB2, 4, 1)
+#define PIN_PB2__FLEXCOM11_IO0		PINMUX_PIN(PIN_PB2, 6, 3)
+#define PIN_PB3				35
+#define PIN_PB3__GPIO			PINMUX_PIN(PIN_PB3, 0, 0)
+#define PIN_PB3__RF1			PINMUX_PIN(PIN_PB3, 1, 1)
+#define PIN_PB3__FLEXCOM11_IO0		PINMUX_PIN(PIN_PB3, 2, 1)
+#define PIN_PB3__PCK2			PINMUX_PIN(PIN_PB3, 3, 2)
+#define PIN_PB3__D8			PINMUX_PIN(PIN_PB3, 4, 1)
+#define PIN_PB4				36
+#define PIN_PB4__GPIO			PINMUX_PIN(PIN_PB4, 0, 0)
+#define PIN_PB4__TF1			PINMUX_PIN(PIN_PB4, 1, 1)
+#define PIN_PB4__FLEXCOM11_IO1		PINMUX_PIN(PIN_PB4, 2, 1)
+#define PIN_PB4__PCK3			PINMUX_PIN(PIN_PB4, 3, 2)
+#define PIN_PB4__D9			PINMUX_PIN(PIN_PB4, 4, 1)
+#define PIN_PB5				37
+#define PIN_PB5__GPIO			PINMUX_PIN(PIN_PB5, 0, 0)
+#define PIN_PB5__TK1			PINMUX_PIN(PIN_PB5, 1, 1)
+#define PIN_PB5__FLEXCOM11_IO2		PINMUX_PIN(PIN_PB5, 2, 1)
+#define PIN_PB5__PCK4			PINMUX_PIN(PIN_PB5, 3, 2)
+#define PIN_PB5__D10			PINMUX_PIN(PIN_PB5, 4, 1)
+#define PIN_PB6				38
+#define PIN_PB6__GPIO			PINMUX_PIN(PIN_PB6, 0, 0)
+#define PIN_PB6__RK1			PINMUX_PIN(PIN_PB6, 1, 1)
+#define PIN_PB6__FLEXCOM11_IO3		PINMUX_PIN(PIN_PB6, 2, 1)
+#define PIN_PB6__PCK5			PINMUX_PIN(PIN_PB6, 3, 2)
+#define PIN_PB6__D11			PINMUX_PIN(PIN_PB6, 4, 1)
+#define PIN_PB7				39
+#define PIN_PB7__GPIO			PINMUX_PIN(PIN_PB7, 0, 0)
+#define PIN_PB7__TD1			PINMUX_PIN(PIN_PB7, 1, 1)
+#define PIN_PB7__FLEXCOM11_IO4		PINMUX_PIN(PIN_PB7, 2, 1)
+#define PIN_PB7__FLEXCOM3_IO5		PINMUX_PIN(PIN_PB7, 3, 2)
+#define PIN_PB7__D12			PINMUX_PIN(PIN_PB7, 4, 1)
+#define PIN_PB8				40
+#define PIN_PB8__GPIO			PINMUX_PIN(PIN_PB8, 0, 0)
+#define PIN_PB8__RD1			PINMUX_PIN(PIN_PB8, 1, 1)
+#define PIN_PB8__FLEXCOM8_IO0		PINMUX_PIN(PIN_PB8, 2, 1)
+#define PIN_PB8__FLEXCOM3_IO6		PINMUX_PIN(PIN_PB8, 3, 2)
+#define PIN_PB8__D13			PINMUX_PIN(PIN_PB8, 4, 1)
+#define PIN_PB9				41
+#define PIN_PB9__GPIO			PINMUX_PIN(PIN_PB9, 0, 0)
+#define PIN_PB9__QSPI0_IO3		PINMUX_PIN(PIN_PB9, 1, 1)
+#define PIN_PB9__FLEXCOM8_IO1		PINMUX_PIN(PIN_PB9, 2, 1)
+#define PIN_PB9__PDMC0_CLK		PINMUX_PIN(PIN_PB9, 3, 1)
+#define PIN_PB9__NCS3_NANDCS		PINMUX_PIN(PIN_PB9, 4, 1)
+#define PIN_PB9__PWML0			PINMUX_PIN(PIN_PB9, 5, 2)
+#define PIN_PB10			42
+#define PIN_PB10__GPIO			PINMUX_PIN(PIN_PB10, 0, 0)
+#define PIN_PB10__QSPI0_IO2		PINMUX_PIN(PIN_PB10, 1, 1)
+#define PIN_PB10__FLEXCOM8_IO2		PINMUX_PIN(PIN_PB10, 2, 1)
+#define PIN_PB10__PDMC0_DS0		PINMUX_PIN(PIN_PB10, 3, 1)
+#define PIN_PB10__NWE_NWR0_NANDWE	PINMUX_PIN(PIN_PB10, 4, 1)
+#define PIN_PB10__PWMH0			PINMUX_PIN(PIN_PB10, 5, 2)
+#define PIN_PB11			43
+#define PIN_PB11__GPIO			PINMUX_PIN(PIN_PB11, 0, 0)
+#define PIN_PB11__QSPI0_IO1		PINMUX_PIN(PIN_PB11, 1, 1)
+#define PIN_PB11__FLEXCOM8_IO3		PINMUX_PIN(PIN_PB11, 2, 1)
+#define PIN_PB11__PDMC0_DS1		PINMUX_PIN(PIN_PB11, 3, 1)
+#define PIN_PB11__NRD_NANDOE		PINMUX_PIN(PIN_PB11, 4, 1)
+#define PIN_PB11__PWML1			PINMUX_PIN(PIN_PB11, 5, 2)
+#define PIN_PB12			44
+#define PIN_PB12__GPIO			PINMUX_PIN(PIN_PB12, 0, 0)
+#define PIN_PB12__QSPI0_IO0		PINMUX_PIN(PIN_PB12, 1, 1)
+#define PIN_PB12__FLEXCOM8_IO4		PINMUX_PIN(PIN_PB12, 2, 1)
+#define PIN_PB12__FLEXCOM6_IO5		PINMUX_PIN(PIN_PB12, 3, 1)
+#define PIN_PB12__A21_NANDALE		PINMUX_PIN(PIN_PB12, 4, 1)
+#define PIN_PB12__PWMH1			PINMUX_PIN(PIN_PB12, 5, 2)
+#define PIN_PB13			45
+#define PIN_PB13__GPIO			PINMUX_PIN(PIN_PB13, 0, 0)
+#define PIN_PB13__QSPI0_CS		PINMUX_PIN(PIN_PB13, 1, 1)
+#define PIN_PB13__FLEXCOM9_IO0		PINMUX_PIN(PIN_PB13, 2, 1)
+#define PIN_PB13__FLEXCOM6_IO6		PINMUX_PIN(PIN_PB13, 3, 1)
+#define PIN_PB13__A22_NANDCLE		PINMUX_PIN(PIN_PB13, 4, 1)
+#define PIN_PB13__PWML2			PINMUX_PIN(PIN_PB13, 5, 2)
+#define PIN_PB14			46
+#define PIN_PB14__GPIO			PINMUX_PIN(PIN_PB14, 0, 0)
+#define PIN_PB14__QSPI0_SCK		PINMUX_PIN(PIN_PB14, 1, 1)
+#define PIN_PB14__FLEXCOM9_IO1		PINMUX_PIN(PIN_PB14, 2, 1)
+#define PIN_PB14__D0			PINMUX_PIN(PIN_PB14, 4, 1)
+#define PIN_PB14__PWMH2			PINMUX_PIN(PIN_PB14, 5, 2)
+#define PIN_PB15			47
+#define PIN_PB15__GPIO			PINMUX_PIN(PIN_PB15, 0, 0)
+#define PIN_PB15__QSPI0_SCKN		PINMUX_PIN(PIN_PB15, 1, 1)
+#define PIN_PB15__FLEXCOM9_IO2		PINMUX_PIN(PIN_PB15, 2, 1)
+#define PIN_PB15__D1			PINMUX_PIN(PIN_PB15, 4, 1)
+#define PIN_PB15__PWML3			PINMUX_PIN(PIN_PB15, 5, 2)
+#define PIN_PB16			48
+#define PIN_PB16__GPIO			PINMUX_PIN(PIN_PB16, 0, 0)
+#define PIN_PB16__QSPI0_IO4		PINMUX_PIN(PIN_PB16, 1, 1)
+#define PIN_PB16__FLEXCOM9_IO3		PINMUX_PIN(PIN_PB16, 2, 1)
+#define PIN_PB16__PCK0			PINMUX_PIN(PIN_PB16, 3, 1)
+#define PIN_PB16__D2			PINMUX_PIN(PIN_PB16, 4, 1)
+#define PIN_PB16__PWMH3			PINMUX_PIN(PIN_PB16, 5, 2)
+#define PIN_PB16__EXT_IRQ0		PINMUX_PIN(PIN_PB16, 6, 2)
+#define PIN_PB17			49
+#define PIN_PB17__GPIO			PINMUX_PIN(PIN_PB17, 0, 0)
+#define PIN_PB17__QSPI0_IO5		PINMUX_PIN(PIN_PB17, 1, 1)
+#define PIN_PB17__FLEXCOM9_IO4		PINMUX_PIN(PIN_PB17, 2, 1)
+#define PIN_PB17__PCK1			PINMUX_PIN(PIN_PB17, 3, 1)
+#define PIN_PB17__D3			PINMUX_PIN(PIN_PB17, 4, 1)
+#define PIN_PB17__PWMEXTRG0		PINMUX_PIN(PIN_PB17, 5, 2)
+#define PIN_PB17__EXT_IRQ1		PINMUX_PIN(PIN_PB17, 6, 2)
+#define PIN_PB18			50
+#define PIN_PB18__GPIO			PINMUX_PIN(PIN_PB18, 0, 0)
+#define PIN_PB18__QSPI0_IO6		PINMUX_PIN(PIN_PB18, 1, 1)
+#define PIN_PB18__FLEXCOM10_IO0		PINMUX_PIN(PIN_PB18, 2, 1)
+#define PIN_PB18__PCK2			PINMUX_PIN(PIN_PB18, 3, 1)
+#define PIN_PB18__D4			PINMUX_PIN(PIN_PB18, 4, 1)
+#define PIN_PB18__PWMEXTRG1		PINMUX_PIN(PIN_PB18, 5, 2)
+#define PIN_PB19			51
+#define PIN_PB19__GPIO			PINMUX_PIN(PIN_PB19, 0, 0)
+#define PIN_PB19__QSPI0_IO7		PINMUX_PIN(PIN_PB19, 1, 1)
+#define PIN_PB19__FLEXCOM10_IO1		PINMUX_PIN(PIN_PB19, 2, 1)
+#define PIN_PB19__PCK3			PINMUX_PIN(PIN_PB19, 3, 1)
+#define PIN_PB19__D5			PINMUX_PIN(PIN_PB19, 4, 1)
+#define PIN_PB19__PWMFI0		PINMUX_PIN(PIN_PB19, 5, 2)
+#define PIN_PB20			52
+#define PIN_PB20__GPIO			PINMUX_PIN(PIN_PB20, 0, 0)
+#define PIN_PB20__QSPI0_DQS		PINMUX_PIN(PIN_PB20, 1, 1)
+#define PIN_PB20__FLEXCOM10_IO2		PINMUX_PIN(PIN_PB20, 2, 1)
+#define PIN_PB20__D6			PINMUX_PIN(PIN_PB20, 4, 1)
+#define PIN_PB20__PWMFI1		PINMUX_PIN(PIN_PB20, 5, 2)
+#define PIN_PB21			53
+#define PIN_PB21__GPIO			PINMUX_PIN(PIN_PB21, 0, 0)
+#define PIN_PB21__QSPI0_INT		PINMUX_PIN(PIN_PB21, 1, 1)
+#define PIN_PB21__FLEXCOM10_IO3		PINMUX_PIN(PIN_PB21, 2, 1)
+#define PIN_PB21__FLEXCOM9_IO5		PINMUX_PIN(PIN_PB21, 3, 1)
+#define PIN_PB21__D7			PINMUX_PIN(PIN_PB21, 4, 1)
+#define PIN_PB22			54
+#define PIN_PB22__GPIO			PINMUX_PIN(PIN_PB22, 0, 0)
+#define PIN_PB22__QSPI1_IO3		PINMUX_PIN(PIN_PB22, 1, 1)
+#define PIN_PB22__FLEXCOM10_IO4		PINMUX_PIN(PIN_PB22, 2, 1)
+#define PIN_PB22__FLEXCOM9_IO6		PINMUX_PIN(PIN_PB22, 3, 1)
+#define PIN_PB22__NANDRDY		PINMUX_PIN(PIN_PB22, 4, 1)
+#define PIN_PB23			55
+#define PIN_PB23__GPIO			PINMUX_PIN(PIN_PB23, 0, 0)
+#define PIN_PB23__QSPI1_IO2		PINMUX_PIN(PIN_PB23, 1, 1)
+#define PIN_PB23__FLEXCOM7_IO0		PINMUX_PIN(PIN_PB23, 2, 1)
+#define PIN_PB23__I2SMCC0_CK		PINMUX_PIN(PIN_PB23, 3, 1)
+#define PIN_PB23__PCK4			PINMUX_PIN(PIN_PB23, 6, 1)
+#define PIN_PB24			56
+#define PIN_PB24__GPIO			PINMUX_PIN(PIN_PB24, 0, 0)
+#define PIN_PB24__QSPI1_IO1		PINMUX_PIN(PIN_PB24, 1, 1)
+#define PIN_PB24__FLEXCOM7_IO1		PINMUX_PIN(PIN_PB24, 2, 1)
+#define PIN_PB24__I2SMCC0_WS		PINMUX_PIN(PIN_PB24, 3, 1)
+#define PIN_PB24__PCK5			PINMUX_PIN(PIN_PB24, 6, 1)
+#define PIN_PB25			57
+#define PIN_PB25__GPIO			PINMUX_PIN(PIN_PB25, 0, 0)
+#define PIN_PB25__QSPI1_IO0		PINMUX_PIN(PIN_PB25, 1, 1)
+#define PIN_PB25__FLEXCOM7_IO2		PINMUX_PIN(PIN_PB25, 2, 1)
+#define PIN_PB25__I2SMCC0_DOUT1		PINMUX_PIN(PIN_PB25, 3, 1)
+#define PIN_PB25__PCK6			PINMUX_PIN(PIN_PB25, 6, 1)
+#define PIN_PB26			58
+#define PIN_PB26__GPIO			PINMUX_PIN(PIN_PB26, 0, 0)
+#define PIN_PB26__QSPI1_CS		PINMUX_PIN(PIN_PB26, 1, 1)
+#define PIN_PB26__FLEXCOM7_IO3		PINMUX_PIN(PIN_PB26, 2, 1)
+#define PIN_PB26__I2SMCC0_DOUT0		PINMUX_PIN(PIN_PB26, 3, 1)
+#define PIN_PB26__PWMEXTRG0		PINMUX_PIN(PIN_PB26, 5, 1)
+#define PIN_PB26__PCK7			PINMUX_PIN(PIN_PB26, 6, 1)
+#define PIN_PB27			59
+#define PIN_PB27__GPIO			PINMUX_PIN(PIN_PB27, 0, 0)
+#define PIN_PB27__QSPI1_SCK		PINMUX_PIN(PIN_PB27, 1, 1)
+#define PIN_PB27__FLEXCOM7_IO4		PINMUX_PIN(PIN_PB27, 2, 1)
+#define PIN_PB27__I2SMCC0_MCK		PINMUX_PIN(PIN_PB27, 3, 1)
+#define PIN_PB27__PWMEXTRG1		PINMUX_PIN(PIN_PB27, 5, 1)
+#define PIN_PB28			60
+#define PIN_PB28__GPIO			PINMUX_PIN(PIN_PB28, 0, 0)
+#define PIN_PB28__SDMMC1_RSTN		PINMUX_PIN(PIN_PB28, 1, 1)
+#define PIN_PB28__ADTRG			PINMUX_PIN(PIN_PB28, 2, 2)
+#define PIN_PB28__PWMFI0		PINMUX_PIN(PIN_PB28, 5, 1)
+#define PIN_PB28__FLEXCOM7_IO0		PINMUX_PIN(PIN_PB28, 6, 4)
+#define PIN_PB29			61
+#define PIN_PB29__GPIO			PINMUX_PIN(PIN_PB29, 0, 0)
+#define PIN_PB29__SDMMC1_CMD		PINMUX_PIN(PIN_PB29, 1, 1)
+#define PIN_PB29__FLEXCOM3_IO2		PINMUX_PIN(PIN_PB29, 2, 2)
+#define PIN_PB29__FLEXCOM0_IO5		PINMUX_PIN(PIN_PB29, 3, 2)
+#define PIN_PB29__TIOA3			PINMUX_PIN(PIN_PB29, 4, 2)
+#define PIN_PB29__PWMFI1		PINMUX_PIN(PIN_PB29, 5, 1)
+#define PIN_PB29__FLEXCOM7_IO1		PINMUX_PIN(PIN_PB29, 6, 4)
+#define PIN_PB30			62
+#define PIN_PB30__GPIO			PINMUX_PIN(PIN_PB30, 0, 0)
+#define PIN_PB30__SDMMC1_CK		PINMUX_PIN(PIN_PB30, 1, 1)
+#define PIN_PB30__FLEXCOM3_IO3		PINMUX_PIN(PIN_PB30, 2, 2)
+#define PIN_PB30__FLEXCOM0_IO6		PINMUX_PIN(PIN_PB30, 3, 2)
+#define PIN_PB30__TIOB3			PINMUX_PIN(PIN_PB30, 4, 1)
+#define PIN_PB30__PWMH0			PINMUX_PIN(PIN_PB30, 5, 1)
+#define PIN_PB30__FLEXCOM8_IO0		PINMUX_PIN(PIN_PB30, 6, 4)
+#define PIN_PB31			63
+#define PIN_PB31__GPIO			PINMUX_PIN(PIN_PB31, 0, 0)
+#define PIN_PB31__SDMMC1_DAT0		PINMUX_PIN(PIN_PB31, 1, 1)
+#define PIN_PB31__FLEXCOM3_IO4		PINMUX_PIN(PIN_PB31, 2, 2)
+#define PIN_PB31__FLEXCOM9_IO5		PINMUX_PIN(PIN_PB31, 3, 2)
+#define PIN_PB31__TCLK3			PINMUX_PIN(PIN_PB31, 4, 1)
+#define PIN_PB31__PWML0			PINMUX_PIN(PIN_PB31, 5, 1)
+#define PIN_PB31__FLEXCOM8_IO1		PINMUX_PIN(PIN_PB31, 6, 4)
+#define PIN_PC0				64
+#define PIN_PC0__GPIO			PINMUX_PIN(PIN_PC0, 0, 0)
+#define PIN_PC0__SDMMC1_DAT1		PINMUX_PIN(PIN_PC0, 1, 1)
+#define PIN_PC0__FLEXCOM3_IO0		PINMUX_PIN(PIN_PC0, 2, 2)
+#define PIN_PC0__TIOA4			PINMUX_PIN(PIN_PC0, 4, 1)
+#define PIN_PC0__PWML1			PINMUX_PIN(PIN_PC0, 5, 1)
+#define PIN_PC0__FLEXCOM9_IO0		PINMUX_PIN(PIN_PC0, 6, 4)
+#define PIN_PC1				65
+#define PIN_PC1__GPIO			PINMUX_PIN(PIN_PC1, 0, 0)
+#define PIN_PC1__SDMMC1_DAT2		PINMUX_PIN(PIN_PC1, 1, 1)
+#define PIN_PC1__FLEXCOM3_IO1		PINMUX_PIN(PIN_PC1, 2, 2)
+#define PIN_PC1__TIOB4			PINMUX_PIN(PIN_PC1, 4, 1)
+#define PIN_PC1__PWMH1			PINMUX_PIN(PIN_PC1, 5, 1)
+#define PIN_PC1__FLEXCOM9_IO1		PINMUX_PIN(PIN_PC1, 6, 4)
+#define PIN_PC2				66
+#define PIN_PC2__GPIO			PINMUX_PIN(PIN_PC2, 0, 0)
+#define PIN_PC2__SDMMC1_DAT3		PINMUX_PIN(PIN_PC2, 1, 1)
+#define PIN_PC2__FLEXCOM4_IO0		PINMUX_PIN(PIN_PC2, 2, 2)
+#define PIN_PC2__TCLK4			PINMUX_PIN(PIN_PC2, 4, 1)
+#define PIN_PC2__PWML2			PINMUX_PIN(PIN_PC2, 5, 1)
+#define PIN_PC2__FLEXCOM10_IO0		PINMUX_PIN(PIN_PC2, 6, 4)
+#define PIN_PC3				67
+#define PIN_PC3__GPIO			PINMUX_PIN(PIN_PC3, 0, 0)
+#define PIN_PC3__SDMMC1_WP		PINMUX_PIN(PIN_PC3, 1, 1)
+#define PIN_PC3__FLEXCOM4_IO1		PINMUX_PIN(PIN_PC3, 2, 2)
+#define PIN_PC3__TIOA5			PINMUX_PIN(PIN_PC3, 4, 1)
+#define PIN_PC3__PWMH2			PINMUX_PIN(PIN_PC3, 5, 1)
+#define PIN_PC3__FLEXCOM10_IO1		PINMUX_PIN(PIN_PC3, 6, 4)
+#define PIN_PC4				68
+#define PIN_PC4__GPIO			PINMUX_PIN(PIN_PC4, 0, 0)
+#define PIN_PC4__SDMMC1_CD		PINMUX_PIN(PIN_PC4, 1, 1)
+#define PIN_PC4__FLEXCOM4_IO2		PINMUX_PIN(PIN_PC4, 2, 2)
+#define PIN_PC4__FLEXCOM9_IO6		PINMUX_PIN(PIN_PC4, 3, 2)
+#define PIN_PC4__TIOB5			PINMUX_PIN(PIN_PC4, 4, 1)
+#define PIN_PC4__PWML3			PINMUX_PIN(PIN_PC4, 5, 1)
+#define PIN_PC4__FLEXCOM11_IO0		PINMUX_PIN(PIN_PC4, 6, 4)
+#define PIN_PC5				69
+#define PIN_PC5__GPIO			PINMUX_PIN(PIN_PC5, 0, 0)
+#define PIN_PC5__SDMMC1_1V8SEL		PINMUX_PIN(PIN_PC5, 1, 1)
+#define PIN_PC5__FLEXCOM4_IO3		PINMUX_PIN(PIN_PC5, 2, 2)
+#define PIN_PC5__FLEXCOM6_IO5		PINMUX_PIN(PIN_PC5, 3, 2)
+#define PIN_PC5__TCLK5			PINMUX_PIN(PIN_PC5, 4, 1)
+#define PIN_PC5__PWMH3			PINMUX_PIN(PIN_PC5, 5, 1)
+#define PIN_PC5__FLEXCOM11_IO1		PINMUX_PIN(PIN_PC5, 6, 4)
+#define PIN_PC6				70
+#define PIN_PC6__GPIO			PINMUX_PIN(PIN_PC6, 0, 0)
+#define PIN_PC6__FLEXCOM4_IO4		PINMUX_PIN(PIN_PC6, 2, 2)
+#define PIN_PC6__FLEXCOM6_IO6		PINMUX_PIN(PIN_PC6, 3, 2)
+#define PIN_PC7				71
+#define PIN_PC7__GPIO			PINMUX_PIN(PIN_PC7, 0, 0)
+#define PIN_PC7__I2SMCC0_DIN0		PINMUX_PIN(PIN_PC7, 1, 1)
+#define PIN_PC7__FLEXCOM7_IO0		PINMUX_PIN(PIN_PC7, 2, 2)
+#define PIN_PC8				72
+#define PIN_PC8__GPIO			PINMUX_PIN(PIN_PC8, 0, 0)
+#define PIN_PC8__I2SMCC0_DIN1		PINMUX_PIN(PIN_PC8, 1, 1)
+#define PIN_PC8__FLEXCOM7_IO1		PINMUX_PIN(PIN_PC8, 2, 2)
+#define PIN_PC9				73
+#define PIN_PC9__GPIO			PINMUX_PIN(PIN_PC9, 0, 0)
+#define PIN_PC9__I2SMCC0_DOUT3		PINMUX_PIN(PIN_PC9, 1, 1)
+#define PIN_PC9__FLEXCOM7_IO2		PINMUX_PIN(PIN_PC9, 2, 2)
+#define PIN_PC9__FLEXCOM1_IO0		PINMUX_PIN(PIN_PC9, 6, 4)
+#define PIN_PC10			74
+#define PIN_PC10__GPIO			PINMUX_PIN(PIN_PC10, 0, 0)
+#define PIN_PC10__I2SMCC0_DOUT2		PINMUX_PIN(PIN_PC10, 1, 1)
+#define PIN_PC10__FLEXCOM7_IO3		PINMUX_PIN(PIN_PC10, 2, 2)
+#define PIN_PC10__FLEXCOM1_IO1		PINMUX_PIN(PIN_PC10, 6, 4)
+#define PIN_PC11			75
+#define PIN_PC11__GPIO			PINMUX_PIN(PIN_PC11, 0, 0)
+#define PIN_PC11__I2SMCC1_CK		PINMUX_PIN(PIN_PC11, 1, 1)
+#define PIN_PC11__FLEXCOM7_IO4		PINMUX_PIN(PIN_PC11, 2, 2)
+#define PIN_PC11__FLEXCOM2_IO0		PINMUX_PIN(PIN_PC11, 6, 4)
+#define PIN_PC12			76
+#define PIN_PC12__GPIO			PINMUX_PIN(PIN_PC12, 0, 0)
+#define PIN_PC12__I2SMCC1_WS		PINMUX_PIN(PIN_PC12, 1, 1)
+#define PIN_PC12__FLEXCOM8_IO2		PINMUX_PIN(PIN_PC12, 2, 2)
+#define PIN_PC12__FLEXCOM2_IO1		PINMUX_PIN(PIN_PC12, 6, 4)
+#define PIN_PC13			77
+#define PIN_PC13__GPIO			PINMUX_PIN(PIN_PC13, 0, 0)
+#define PIN_PC13__I2SMCC1_MCK		PINMUX_PIN(PIN_PC13, 1, 1)
+#define PIN_PC13__FLEXCOM8_IO1		PINMUX_PIN(PIN_PC13, 2, 2)
+#define PIN_PC13__FLEXCOM3_IO0		PINMUX_PIN(PIN_PC13, 6, 4)
+#define PIN_PC14			78
+#define PIN_PC14__GPIO			PINMUX_PIN(PIN_PC14, 0, 0)
+#define PIN_PC14__I2SMCC1_DOUT0		PINMUX_PIN(PIN_PC14, 1, 1)
+#define PIN_PC14__FLEXCOM8_IO0		PINMUX_PIN(PIN_PC14, 2, 2)
+#define PIN_PC14__FLEXCOM3_IO1		PINMUX_PIN(PIN_PC14, 6, 4)
+#define PIN_PC15			79
+#define PIN_PC15__GPIO			PINMUX_PIN(PIN_PC15, 0, 0)
+#define PIN_PC15__I2SMCC1_DOUT1		PINMUX_PIN(PIN_PC15, 1, 1)
+#define PIN_PC15__FLEXCOM8_IO3		PINMUX_PIN(PIN_PC15, 2, 2)
+#define PIN_PC15__FLEXCOM4_IO0		PINMUX_PIN(PIN_PC15, 6, 4)
+#define PIN_PC16			80
+#define PIN_PC16__GPIO			PINMUX_PIN(PIN_PC16, 0, 0)
+#define PIN_PC16__I2SMCC_DOUT2		PINMUX_PIN(PIN_PC16, 1, 1)
+#define PIN_PC16__FLEXCOM8_IO4		PINMUX_PIN(PIN_PC16, 2, 2)
+#define PIN_PC16__FLEXCOM3_IO1		PINMUX_PIN(PIN_PC16, 6, 4)
+#define PIN_PC17			81
+#define PIN_PC17__GPIO			PINMUX_PIN(PIN_PC17, 0, 0)
+#define PIN_PC17__I2SMCC1_DOUT3		PINMUX_PIN(PIN_PC17, 1, 1)
+#define PIN_PC17__EXT_IRQ0		PINMUX_PIN(PIN_PC17, 2, 3)
+#define PIN_PC17__FLEXCOM5_IO0		PINMUX_PIN(PIN_PC17, 6, 4)
+#define PIN_PC18			82
+#define PIN_PC18__GPIO			PINMUX_PIN(PIN_PC18, 0, 0)
+#define PIN_PC18__I2SMCC1_DIN0		PINMUX_PIN(PIN_PC18, 1, 1)
+#define PIN_PC18__FLEXCOM9_IO0		PINMUX_PIN(PIN_PC18, 2, 2)
+#define PIN_PC18__FLEXCOM5_IO1		PINMUX_PIN(PIN_PC18, 6, 4)
+#define PIN_PC19			83
+#define PIN_PC19__GPIO			PINMUX_PIN(PIN_PC19, 0, 0)
+#define PIN_PC19__I2SMCC1_DIN1		PINMUX_PIN(PIN_PC19, 1, 1)
+#define PIN_PC19__FLEXCOM9_IO1		PINMUX_PIN(PIN_PC19, 2, 2)
+#define PIN_PC19__FLEXCOM6_IO0		PINMUX_PIN(PIN_PC19, 6, 4)
+#define PIN_PC20			84
+#define PIN_PC20__GPIO			PINMUX_PIN(PIN_PC20, 0, 0)
+#define PIN_PC20__I2SMCC1_DIN2		PINMUX_PIN(PIN_PC20, 1, 1)
+#define PIN_PC20__FLEXCOM9_IO4		PINMUX_PIN(PIN_PC20, 2, 2)
+#define PIN_PC20__FLEXCOM6_IO1		PINMUX_PIN(PIN_PC20, 6, 4)
+#define PIN_PC21			85
+#define PIN_PC21__GPIO			PINMUX_PIN(PIN_PC21, 0, 0)
+#define PIN_PC21__I2SMCC1_DIN3		PINMUX_PIN(PIN_PC21, 1, 1)
+#define PIN_PC21__FLEXCOM9_IO2		PINMUX_PIN(PIN_PC21, 2, 2)
+#define PIN_PC21__D3			PINMUX_PIN(PIN_PC21, 4, 2)
+#define PIN_PC21__FLEXCOM6_IO0		PINMUX_PIN(PIN_PC21, 6, 5)
+#define PIN_PC22			86
+#define PIN_PC22__GPIO			PINMUX_PIN(PIN_PC22, 0, 0)
+#define PIN_PC22__I2SMCC0_DIN2		PINMUX_PIN(PIN_PC22, 1, 1)
+#define PIN_PC22__FLEXCOM9_IO3		PINMUX_PIN(PIN_PC22, 2, 2)
+#define PIN_PC22__D4			PINMUX_PIN(PIN_PC22, 4, 2)
+#define PIN_PC22__FLEXCOM6_IO1		PINMUX_PIN(PIN_PC22, 6, 5)
+#define PIN_PC23			87
+#define PIN_PC23__GPIO			PINMUX_PIN(PIN_PC23, 0, 0)
+#define PIN_PC23__I2SMCC0_DIN3		PINMUX_PIN(PIN_PC23, 1, 1)
+#define PIN_PC23__FLEXCOM0_IO5		PINMUX_PIN(PIN_PC23, 2, 3)
+#define PIN_PC23__D5			PINMUX_PIN(PIN_PC23, 4, 2)
+#define PIN_PC23__FLEXCOM7_IO0		PINMUX_PIN(PIN_PC23, 6, 5)
+#define PIN_PC24			88
+#define PIN_PC24__GPIO			PINMUX_PIN(PIN_PC24, 0, 0)
+#define PIN_PC24__FLEXCOM0_IO6		PINMUX_PIN(PIN_PC24, 2, 3)
+#define PIN_PC24__EXT_IRQ1		PINMUX_PIN(PIN_PC24, 3, 3)
+#define PIN_PC24__D6			PINMUX_PIN(PIN_PC24, 4, 2)
+#define PIN_PC24__FLEXCOM7_IO1		PINMUX_PIN(PIN_PC24, 6, 5)
+#define PIN_PC25			89
+#define PIN_PC25__GPIO			PINMUX_PIN(PIN_PC25, 0, 0)
+#define PIN_PC25__NTRST			PINMUX_PIN(PIN_PC25, 1, 1)
+#define PIN_PC26			90
+#define PIN_PC26__GPIO			PINMUX_PIN(PIN_PC26, 0, 0)
+#define PIN_PC26__TCK_SWCLK		PINMUX_PIN(PIN_PC26, 1, 1)
+#define PIN_PC27			91
+#define PIN_PC27__GPIO			PINMUX_PIN(PIN_PC27, 0, 0)
+#define PIN_PC27__TMS_SWDIO		PINMUX_PIN(PIN_PC27, 1, 1)
+#define PIN_PC28			92
+#define PIN_PC28__GPIO			PINMUX_PIN(PIN_PC28, 0, 0)
+#define PIN_PC28__TDI			PINMUX_PIN(PIN_PC28, 1, 1)
+#define PIN_PC29			93
+#define PIN_PC29__GPIO			PINMUX_PIN(PIN_PC29, 0, 0)
+#define PIN_PC29__TDO			PINMUX_PIN(PIN_PC29, 1, 1)
+#define PIN_PC30			94
+#define PIN_PC30__GPIO			PINMUX_PIN(PIN_PC30, 0, 0)
+#define PIN_PC30__FLEXCOM10_IO0		PINMUX_PIN(PIN_PC30, 2, 2)
+#define PIN_PC31			95
+#define PIN_PC31__GPIO			PINMUX_PIN(PIN_PC31, 0, 0)
+#define PIN_PC31__FLEXCOM10_IO1		PINMUX_PIN(PIN_PC31, 2, 2)
+#define PIN_PD0				96
+#define PIN_PD0__GPIO			PINMUX_PIN(PIN_PD0, 0, 0)
+#define PIN_PD0__FLEXCOM11_IO0		PINMUX_PIN(PIN_PD0, 2, 2)
+#define PIN_PD1				97
+#define PIN_PD1__GPIO			PINMUX_PIN(PIN_PD1, 0, 0)
+#define PIN_PD1__FLEXCOM11_IO1		PINMUX_PIN(PIN_PD1, 2, 2)
+#define PIN_PD2				98
+#define PIN_PD2__GPIO			PINMUX_PIN(PIN_PD2, 0, 0)
+#define PIN_PD2__SDMMC2_RSTN		PINMUX_PIN(PIN_PD2, 1, 1)
+#define PIN_PD2__PCK0			PINMUX_PIN(PIN_PD2, 2, 2)
+#define PIN_PD2__CANTX4			PINMUX_PIN(PIN_PD2, 3, 1)
+#define PIN_PD2__D7			PINMUX_PIN(PIN_PD2, 4, 2)
+#define PIN_PD2__TIOA0			PINMUX_PIN(PIN_PD2, 5, 2)
+#define PIN_PD2__FLEXCOM8_IO0		PINMUX_PIN(PIN_PD2, 6, 5)
+#define PIN_PD3				99
+#define PIN_PD3__GPIO			PINMUX_PIN(PIN_PD3, 0, 0)
+#define PIN_PD3__SDMMC2_CMD		PINMUX_PIN(PIN_PD3, 1, 1)
+#define PIN_PD3__FLEXCOM0_IO0		PINMUX_PIN(PIN_PD3, 2, 2)
+#define PIN_PD3__CANRX4			PINMUX_PIN(PIN_PD3, 3, 1)
+#define PIN_PD3__NANDRDY		PINMUX_PIN(PIN_PD3, 4, 2)
+#define PIN_PD3__TIOB0			PINMUX_PIN(PIN_PD3, 5, 2)
+#define PIN_PD3__FLEXCOM8_IO1		PINMUX_PIN(PIN_PD3, 6, 5)
+#define PIN_PD4				100
+#define PIN_PD4__GPIO			PINMUX_PIN(PIN_PD4, 0, 0)
+#define PIN_PD4__SDMMC2_CK		PINMUX_PIN(PIN_PD4, 1, 1)
+#define PIN_PD4__FLEXCOM0_IO1		PINMUX_PIN(PIN_PD4, 2, 2)
+#define PIN_PD4__CANTX5			PINMUX_PIN(PIN_PD4, 3, 1)
+#define PIN_PD4__NCS3_NANDCS		PINMUX_PIN(PIN_PD4, 4, 2)
+#define PIN_PD4__TCLK0			PINMUX_PIN(PIN_PD4, 5, 2)
+#define PIN_PD4__FLEXCOM9_IO0		PINMUX_PIN(PIN_PD4, 6, 5)
+#define PIN_PD5				101
+#define PIN_PD5__GPIO			PINMUX_PIN(PIN_PD5, 0, 0)
+#define PIN_PD5__SDMMC2_DAT0		PINMUX_PIN(PIN_PD5, 1, 1)
+#define PIN_PD5__FLEXCOM0_IO2		PINMUX_PIN(PIN_PD5, 2, 2)
+#define PIN_PD5__CANRX5			PINMUX_PIN(PIN_PD5, 3, 1)
+#define PIN_PD5__NWE_NWR0_NANDWE	PINMUX_PIN(PIN_PD5, 4, 2)
+#define PIN_PD5__TIOA1			PINMUX_PIN(PIN_PD5, 5, 2)
+#define PIN_PD5__FLEXCOM9_IO1		PINMUX_PIN(PIN_PD5, 6, 5)
+#define PIN_PD6				102
+#define PIN_PD6__GPIO			PINMUX_PIN(PIN_PD6, 0, 0)
+#define PIN_PD6__SDMMC2_DAT1		PINMUX_PIN(PIN_PD6, 1, 1)
+#define PIN_PD6__FLEXCOM0_IO3		PINMUX_PIN(PIN_PD6, 2, 2)
+#define PIN_PD6__SPDIF_RX		PINMUX_PIN(PIN_PD6, 3, 3)
+#define PIN_PD6__NRD_NANDOE		PINMUX_PIN(PIN_PD6, 4, 2)
+#define PIN_PD6__TIOB1			PINMUX_PIN(PIN_PD6, 5, 2)
+#define PIN_PD6__FLEXCOM10_IO0		PINMUX_PIN(PIN_PD6, 6, 5)
+#define PIN_PD7				103
+#define PIN_PD7__GPIO			PINMUX_PIN(PIN_PD7, 0, 0)
+#define PIN_PD7__SDMMC2_DAT2		PINMUX_PIN(PIN_PD7, 1, 1)
+#define PIN_PD7__FLEXCOM0_IO4		PINMUX_PIN(PIN_PD7, 2, 2)
+#define PIN_PD7__SPDIF_TX		PINMUX_PIN(PIN_PD7, 2, 2)
+#define PIN_PD7__A21_NANDALE		PINMUX_PIN(PIN_PD7, 4, 2)
+#define PIN_PD7__TCLK1			PINMUX_PIN(PIN_PD7, 5, 2)
+#define PIN_PD7__FLEXCOM10_IO1		PINMUX_PIN(PIN_PD7, 6, 5)
+#define PIN_PD8				104
+#define PIN_PD8__GPIO			PINMUX_PIN(PIN_PD8, 0, 0)
+#define PIN_PD8__SDMMC2_DAT3		PINMUX_PIN(PIN_PD8, 1, 1)
+#define PIN_PD8__I2SMCC0_DIN0		PINMUX_PIN(PIN_PD8, 3, 1)
+#define PIN_PD8__A11_NANDCLE		PINMUX_PIN(PIN_PD8, 4, 2)
+#define PIN_PD8__TIOA2			PINMUX_PIN(PIN_PD8, 5, 2)
+#define PIN_PD8__FLEXCOM11_IO0		PINMUX_PIN(PIN_PD8, 6, 5)
+#define PIN_PD9				105
+#define PIN_PD9__GPIO			PINMUX_PIN(PIN_PD9, 0, 0)
+#define PIN_PD9__SDMMC2_WP		PINMUX_PIN(PIN_PD9, 1, 1)
+#define PIN_PD9__I2SMCC0_DIN1		PINMUX_PIN(PIN_PD9, 3, 2)
+#define PIN_PD9__D0			PINMUX_PIN(PIN_PD9, 4, 2)
+#define PIN_PD9__TIOB2			PINMUX_PIN(PIN_PD9, 5, 2)
+#define PIN_PD9__FLEXCOM11_IO1		PINMUX_PIN(PIN_PD9, 6, 5)
+#define PIN_PD10			106
+#define PIN_PD10__GPIO			PINMUX_PIN(PIN_PD10, 0, 0)
+#define PIN_PD10__SDMMC2_CD		PINMUX_PIN(PIN_PD10, 1, 1)
+#define PIN_PD10__PCK6			PINMUX_PIN(PIN_PD10, 2, 2)
+#define PIN_PD10__I2SMCC0_DIN2		PINMUX_PIN(PIN_PD10, 3, 2)
+#define PIN_PD10__D1			PINMUX_PIN(PIN_PD10, 4, 2)
+#define PIN_PD10__TCLK2			PINMUX_PIN(PIN_PD10, 5, 2)
+#define PIN_PD10__FLEXCOM0_IO0		PINMUX_PIN(PIN_PD10, 6, 3)
+#define PIN_PD11			107
+#define PIN_PD11__GPIO			PINMUX_PIN(PIN_PD11, 0, 0)
+#define PIN_PD11__SDMMC2_1V8SEL		PINMUX_PIN(PIN_PD11, 1, 1)
+#define PIN_PD11__PCK7			PINMUX_PIN(PIN_PD11, 2, 2)
+#define PIN_PD11__I2SMCC0_DIN3		PINMUX_PIN(PIN_PD11, 3, 2)
+#define PIN_PD11__D2			PINMUX_PIN(PIN_PD11, 4, 2)
+#define PIN_PD11__TIOA3			PINMUX_PIN(PIN_PD11, 5, 2)
+#define PIN_PD11__FLEXCOM0_IO1		PINMUX_PIN(PIN_PD11, 6, 3)
+#define PIN_PD12			108
+#define PIN_PD12__GPIO			PINMUX_PIN(PIN_PD12, 0, 0)
+#define PIN_PD12__PCK1			PINMUX_PIN(PIN_PD12, 1, 2)
+#define PIN_PD12__FLEXCOM1_IO0		PINMUX_PIN(PIN_PD12, 2, 2)
+#define PIN_PD12__CANTX0		PINMUX_PIN(PIN_PD12, 4, 2)
+#define PIN_PD12__TIOB3			PINMUX_PIN(PIN_PD12, 5, 2)
+#define PIN_PD13			109
+#define PIN_PD13__GPIO			PINMUX_PIN(PIN_PD13, 0, 0)
+#define PIN_PD13__I2SMCC0_CK		PINMUX_PIN(PIN_PD13, 1, 2)
+#define PIN_PD13__FLEXCOM1_IO1		PINMUX_PIN(PIN_PD13, 2, 2)
+#define PIN_PD13__PWML0			PINMUX_PIN(PIN_PD13, 3, 4)
+#define PIN_PD13__CANRX0		PINMUX_PIN(PIN_PD13, 4, 2)
+#define PIN_PD13__TCLK3			PINMUX_PIN(PIN_PD13, 5, 2)
+#define PIN_PD14			110
+#define PIN_PD14__GPIO			PINMUX_PIN(PIN_PD14, 0, 0)
+#define PIN_PD14__I2SMCC0_MCK		PINMUX_PIN(PIN_PD14, 1, 2)
+#define PIN_PD14__FLEXCOM1_IO2		PINMUX_PIN(PIN_PD14, 2, 2)
+#define PIN_PD14__PWMH0			PINMUX_PIN(PIN_PD14, 3, 4)
+#define PIN_PD14__CANTX1		PINMUX_PIN(PIN_PD14, 4, 2)
+#define PIN_PD14__TIOA4			PINMUX_PIN(PIN_PD14, 5, 2)
+#define PIN_PD14__FLEXCOM2_IO0		PINMUX_PIN(PIN_PD14, 6, 5)
+#define PIN_PD15			111
+#define PIN_PD15__GPIO			PINMUX_PIN(PIN_PD15, 0, 0)
+#define PIN_PD15__I2SMCC0_WS		PINMUX_PIN(PIN_PD15, 1, 2)
+#define PIN_PD15__FLEXCOM1_IO3		PINMUX_PIN(PIN_PD15, 2, 2)
+#define PIN_PD15__PWML1			PINMUX_PIN(PIN_PD15, 3, 4)
+#define PIN_PD15__CANRX1		PINMUX_PIN(PIN_PD15, 4, 2)
+#define PIN_PD15__TIOB4			PINMUX_PIN(PIN_PD15, 5, 2)
+#define PIN_PD15__FLEXCOM2_IO1		PINMUX_PIN(PIN_PD15, 6, 5)
+#define PIN_PD16			112
+#define PIN_PD16__GPIO			PINMUX_PIN(PIN_PD16, 0, 0)
+#define PIN_PD16__I2SMCC0_DOUT0		PINMUX_PIN(PIN_PD16, 1, 2)
+#define PIN_PD16__FLEXCOM1_IO4		PINMUX_PIN(PIN_PD16, 2, 2)
+#define PIN_PD16__PWMH1			PINMUX_PIN(PIN_PD16, 3, 4)
+#define PIN_PD16__CANTX2		PINMUX_PIN(PIN_PD16, 4, 2)
+#define PIN_PD16__TCLK4			PINMUX_PIN(PIN_PD16, 5, 2)
+#define PIN_PD16__FLEXCOM3_IO0		PINMUX_PIN(PIN_PD16, 6, 5)
+#define PIN_PD17			113
+#define PIN_PD17__GPIO			PINMUX_PIN(PIN_PD17, 0, 0)
+#define PIN_PD17__I2SMCC0_DOUT1		PINMUX_PIN(PIN_PD17, 1, 2)
+#define PIN_PD17__FLEXCOM2_IO0		PINMUX_PIN(PIN_PD17, 2, 2)
+#define PIN_PD17__PWML2			PINMUX_PIN(PIN_PD17, 3, 4)
+#define PIN_PD17__CANRX2		PINMUX_PIN(PIN_PD17, 4, 2)
+#define PIN_PD17__TIOA5			PINMUX_PIN(PIN_PD17, 5, 2)
+#define PIN_PD17__FLEXCOM3_IO1		PINMUX_PIN(PIN_PD17, 6, 5)
+#define PIN_PD18			114
+#define PIN_PD18__GPIO			PINMUX_PIN(PIN_PD18, 0, 0)
+#define PIN_PD18__I2SMCC0_DOUT2		PINMUX_PIN(PIN_PD18, 1, 2)
+#define PIN_PD18__FLEXCOM2_IO1		PINMUX_PIN(PIN_PD18, 2, 2)
+#define PIN_PD18__PWMH2			PINMUX_PIN(PIN_PD18, 3, 4)
+#define PIN_PD18__CANTX3		PINMUX_PIN(PIN_PD18, 4, 2)
+#define PIN_PD18__TIOB5			PINMUX_PIN(PIN_PD18, 5, 2)
+#define PIN_PD18__FLEXCOM4_IO0		PINMUX_PIN(PIN_PD18, 6, 5)
+#define PIN_PD19			115
+#define PIN_PD19__GPIO			PINMUX_PIN(PIN_PD19, 0, 0)
+#define PIN_PD19__I2SMCC0_DOUT3		PINMUX_PIN(PIN_PD19, 1, 2)
+#define PIN_PD19__FLEXCOM2_IO2		PINMUX_PIN(PIN_PD19, 2, 2)
+#define PIN_PD19__PWML3			PINMUX_PIN(PIN_PD19, 3, 4)
+#define PIN_PD19__CANRX3		PINMUX_PIN(PIN_PD19, 4, 2)
+#define PIN_PD19__TCLK5			PINMUX_PIN(PIN_PD19, 5, 2)
+#define PIN_PD19__FLEXCOM4_IO1		PINMUX_PIN(PIN_PD19, 6, 5)
+#define PIN_PD20			116
+#define PIN_PD20__GPIO			PINMUX_PIN(PIN_PD20, 0, 0)
+#define PIN_PD20__PCK0			PINMUX_PIN(PIN_PD20, 1, 3)
+#define PIN_PD20__FLEXCOM2_IO3		PINMUX_PIN(PIN_PD20, 2, 2)
+#define PIN_PD20__PWMH3			PINMUX_PIN(PIN_PD20, 3, 4)
+#define PIN_PD20__CANTX4		PINMUX_PIN(PIN_PD20, 5, 2)
+#define PIN_PD20__FLEXCOM5_IO0		PINMUX_PIN(PIN_PD20, 6, 5)
+#define PIN_PD21			117
+#define PIN_PD21__GPIO			PINMUX_PIN(PIN_PD21, 0, 0)
+#define PIN_PD21__PCK1			PINMUX_PIN(PIN_PD21, 1, 3)
+#define PIN_PD21__FLEXCOM2_IO4		PINMUX_PIN(PIN_PD21, 2, 2)
+#define PIN_PD21__CANRX4		PINMUX_PIN(PIN_PD21, 4, 2)
+#define PIN_PD21__FLEXCOM5_IO1		PINMUX_PIN(PIN_PD21, 6, 5)
+#define PIN_PD21__G1_TXEN		PINMUX_PIN(PIN_PD21, 7, 1)
+#define PIN_PD22			118
+#define PIN_PD22__GPIO			PINMUX_PIN(PIN_PD22, 0, 0)
+#define PIN_PD22__PDMC0_CLK		PINMUX_PIN(PIN_PD22, 1, 2)
+#define PIN_PD22__PWMEXTRG0		PINMUX_PIN(PIN_PD22, 3, 4)
+#define PIN_PD22__RD1			PINMUX_PIN(PIN_PD22, 4, 2)
+#define PIN_PD22__CANTX5		PINMUX_PIN(PIN_PD22, 6, 2)
+#define PIN_PD22__G1_TX0		PINMUX_PIN(PIN_PD22, 7, 1)
+#define PIN_PD23			119
+#define PIN_PD23__GPIO			PINMUX_PIN(PIN_PD23, 0, 0)
+#define PIN_PD23__PDMC0_DS0		PINMUX_PIN(PIN_PD23, 1, 2)
+#define PIN_PD23__PWMEXTRG1		PINMUX_PIN(PIN_PD23, 3, 4)
+#define PIN_PD23__RF1			PINMUX_PIN(PIN_PD23, 4, 2)
+#define PIN_PD23__ISC_MCK		PINMUX_PIN(PIN_PD23, 5, 2)
+#define PIN_PD23__CANRX5		PINMUX_PIN(PIN_PD23, 6, 2)
+#define PIN_PD23__G1_TX1		PINMUX_PIN(PIN_PD23, 7, 1)
+#define PIN_PD24			120
+#define PIN_PD24__GPIO			PINMUX_PIN(PIN_PD24, 0, 0)
+#define PIN_PD24__PDMC0_DS1		PINMUX_PIN(PIN_PD24, 1, 2)
+#define PIN_PD24__PWMFI0		PINMUX_PIN(PIN_PD24, 3, 4)
+#define PIN_PD24__RK1			PINMUX_PIN(PIN_PD24, 4, 2)
+#define PIN_PD24__ISC_D0		PINMUX_PIN(PIN_PD24, 5, 2)
+#define PIN_PD24__G1_RXDV		PINMUX_PIN(PIN_PD24, 7, 1)
+#define PIN_PD25			121
+#define PIN_PD25__GPIO			PINMUX_PIN(PIN_PD25, 0, 0)
+#define PIN_PD25__PDMC1_CLK		PINMUX_PIN(PIN_PD25, 1, 2)
+#define PIN_PD25__FLEXCOM5_IO0		PINMUX_PIN(PIN_PD25, 2, 2)
+#define PIN_PD25__PWMFI1		PINMUX_PIN(PIN_PD25, 3, 4)
+#define PIN_PD25__TD1			PINMUX_PIN(PIN_PD25, 4, 2)
+#define PIN_PD25__ISC_D1		PINMUX_PIN(PIN_PD25, 5, 2)
+#define PIN_PD25__G1_RX0		PINMUX_PIN(PIN_PD25, 7, 1)
+#define PIN_PD26			122
+#define PIN_PD26__GPIO			PINMUX_PIN(PIN_PD26, 0, 0)
+#define PIN_PD26__PDMC1_DS0		PINMUX_PIN(PIN_PD26, 1, 2)
+#define PIN_PD26__FLEXCOM5_IO1		PINMUX_PIN(PIN_PD26, 2, 2)
+#define PIN_PD26__ADTRG			PINMUX_PIN(PIN_PD26, 3, 3)
+#define PIN_PD26__TF1			PINMUX_PIN(PIN_PD26, 4, 2)
+#define PIN_PD26__ISC_D2		PINMUX_PIN(PIN_PD26, 5, 2)
+#define PIN_PD26__G1_RX1		PINMUX_PIN(PIN_PD26, 7, 1)
+#define PIN_PD27			123
+#define PIN_PD27__GPIO			PINMUX_PIN(PIN_PD27, 0, 0)
+#define PIN_PD27__PDMC1_DS1		PINMUX_PIN(PIN_PD27, 1, 2)
+#define PIN_PD27__FLEXCOM5_IO2		PINMUX_PIN(PIN_PD27, 2, 2)
+#define PIN_PD27__TIOA0			PINMUX_PIN(PIN_PD27, 3, 3)
+#define PIN_PD27__TK1			PINMUX_PIN(PIN_PD27, 4, 2)
+#define PIN_PD27__ISC_D3		PINMUX_PIN(PIN_PD27, 5, 2)
+#define PIN_PD27__G1_RXER		PINMUX_PIN(PIN_PD27, 7, 1)
+#define PIN_PD28			124
+#define PIN_PD28__GPIO			PINMUX_PIN(PIN_PD28, 0, 0)
+#define PIN_PD28__RD0			PINMUX_PIN(PIN_PD28, 1, 2)
+#define PIN_PD28__FLEXCOM5_IO3		PINMUX_PIN(PIN_PD28, 2, 2)
+#define PIN_PD28__TIOB0			PINMUX_PIN(PIN_PD28, 3, 3)
+#define PIN_PD28__I2SMCC1_CK		PINMUX_PIN(PIN_PD28, 4, 2)
+#define PIN_PD28__ISC_D4		PINMUX_PIN(PIN_PD28, 5, 2)
+#define PIN_PD28__PWML3			PINMUX_PIN(PIN_PD28, 6, 5)
+#define PIN_PD28__G1_MDC		PINMUX_PIN(PIN_PD28, 7, 1)
+#define PIN_PD29			125
+#define PIN_PD29__GPIO			PINMUX_PIN(PIN_PD29, 0, 0)
+#define PIN_PD29__RF0			PINMUX_PIN(PIN_PD29, 1, 2)
+#define PIN_PD29__FLEXCOM5_IO4		PINMUX_PIN(PIN_PD29, 2, 2)
+#define PIN_PD29__TCLK0			PINMUX_PIN(PIN_PD29, 3, 3)
+#define PIN_PD29__I2SMCC1_WS		PINMUX_PIN(PIN_PD29, 4, 2)
+#define PIN_PD29__ISC_D5		PINMUX_PIN(PIN_PD29, 5, 2)
+#define PIN_PD29__PWMH3			PINMUX_PIN(PIN_PD29, 6, 5)
+#define PIN_PD29__G1_MDIO		PINMUX_PIN(PIN_PD29, 7, 1)
+#define PIN_PD30			126
+#define PIN_PD30__GPIO			PINMUX_PIN(PIN_PD30, 0, 0)
+#define PIN_PD30__RK0			PINMUX_PIN(PIN_PD30, 1, 2)
+#define PIN_PD30__FLEXCOM6_IO0		PINMUX_PIN(PIN_PD30, 2, 2)
+#define PIN_PD30__TIOA1			PINMUX_PIN(PIN_PD30, 3, 3)
+#define PIN_PD30__I2SMCC1_MCK		PINMUX_PIN(PIN_PD30, 4, 2)
+#define PIN_PD30__ISC_D6		PINMUX_PIN(PIN_PD30, 5, 2)
+#define PIN_PD30__PWMEXTRG0		PINMUX_PIN(PIN_PD30, 6, 5)
+#define PIN_PD30__G1_TXCK		PINMUX_PIN(PIN_PD30, 7, 1)
+#define PIN_PD31			127
+#define PIN_PD31__GPIO			PINMUX_PIN(PIN_PD31, 0, 0)
+#define PIN_PD31__TD0			PINMUX_PIN(PIN_PD31, 1, 2)
+#define PIN_PD31__FLEXCOM6_IO1		PINMUX_PIN(PIN_PD31, 2, 2)
+#define PIN_PD31__TIOB1			PINMUX_PIN(PIN_PD31, 3, 3)
+#define PIN_PD31__I2SMCC1_DOUT0		PINMUX_PIN(PIN_PD31, 4, 2)
+#define PIN_PD31__ISC_D7		PINMUX_PIN(PIN_PD31, 5, 2)
+#define PIN_PD31__PWM_EXTRG1		PINMUX_PIN(PIN_PD31, 6, 5)
+#define PIN_PD31__G1_TX2		PINMUX_PIN(PIN_PD31, 7, 1)
+#define PIN_PE0				128
+#define PIN_PE0__GPIO			PINMUX_PIN(PIN_PE0, 0, 0)
+#define PIN_PE0__TF0			PINMUX_PIN(PIN_PE0, 1, 2)
+#define PIN_PE0__FLEXCOM6_IO2		PINMUX_PIN(PIN_PE0, 2, 2)
+#define PIN_PE0__TCLK1			PINMUX_PIN(PIN_PE0, 3, 3)
+#define PIN_PE0__I2SMCC1_DOUT1		PINMUX_PIN(PIN_PE0, 4, 2)
+#define PIN_PE0__ISC_HSYNC		PINMUX_PIN(PIN_PE0, 5, 2)
+#define PIN_PE0__PWMFI0			PINMUX_PIN(PIN_PE0, 6, 5)
+#define PIN_PE0__G1_TX3			PINMUX_PIN(PIN_PE0, 7, 1)
+#define PIN_PE1				129
+#define PIN_PE1__GPIO			PINMUX_PIN(PIN_PE1, 0, 0)
+#define PIN_PE1__TK0			PINMUX_PIN(PIN_PE1, 1, 2)
+#define PIN_PE1__FLEXCOM6_IO3		PINMUX_PIN(PIN_PE1, 2, 2)
+#define PIN_PE1__TIOA2			PINMUX_PIN(PIN_PE1, 3, 3)
+#define PIN_PE1__I2SMCC1_DOUT2		PINMUX_PIN(PIN_PE1, 4, 2)
+#define PIN_PE1__ISC_VSYNC		PINMUX_PIN(PIN_PE1, 5, 2)
+#define PIN_PE1__PWMFI1			PINMUX_PIN(PIN_PE1, 6, 5)
+#define PIN_PE1__G1_RX2			PINMUX_PIN(PIN_PE1, 7, 1)
+#define PIN_PE2				130
+#define PIN_PE2__GPIO			PINMUX_PIN(PIN_PE2, 0, 0)
+#define PIN_PE2__PWML0			PINMUX_PIN(PIN_PE2, 1, 5)
+#define PIN_PE2__FLEXCOM6_IO4		PINMUX_PIN(PIN_PE2, 2, 2)
+#define PIN_PE2__TIOB2			PINMUX_PIN(PIN_PE2, 3, 3)
+#define PIN_PE2__I2SMCC1_DOUT3		PINMUX_PIN(PIN_PE2, 4, 2)
+#define PIN_PE2__ISC_FIELD		PINMUX_PIN(PIN_PE2, 5, 2)
+#define PIN_PE2__G1_RX3			PINMUX_PIN(PIN_PE2, 7, 1)
+#define PIN_PE3				131
+#define PIN_PE3__GPIO			PINMUX_PIN(PIN_PE3, 0, 0)
+#define PIN_PE3__PWMH0			PINMUX_PIN(PIN_PE3, 1, 5)
+#define PIN_PE3__FLEXCOM0_IO0		PINMUX_PIN(PIN_PE3, 2, 4)
+#define PIN_PE3__TCLK2			PINMUX_PIN(PIN_PE3, 3, 3)
+#define PIN_PE3__I2SMCC1_DIN0		PINMUX_PIN(PIN_PE3, 4, 2)
+#define PIN_PE3__ISC_PCK		PINMUX_PIN(PIN_PE3, 5, 2)
+#define PIN_PE3__G1_RXCK		PINMUX_PIN(PIN_PE3, 7, 1)
+#define PIN_PE4				132
+#define PIN_PE4__GPIO			PINMUX_PIN(PIN_PE4, 0, 0)
+#define PIN_PE4__PWML1			PINMUX_PIN(PIN_PE4, 1, 5)
+#define PIN_PE4__FLEXCOM0_IO1		PINMUX_PIN(PIN_PE4, 2, 4)
+#define PIN_PE4__TIOA3			PINMUX_PIN(PIN_PE4, 3, 3)
+#define PIN_PE4__I2SMCC1_DIN1		PINMUX_PIN(PIN_PE4, 4, 2)
+#define PIN_PE4__ISC_D8			PINMUX_PIN(PIN_PE4, 5, 2)
+#define PIN_PE4__G1_TXER		PINMUX_PIN(PIN_PE4, 7, 1)
+#define PIN_PE5				133
+#define PIN_PE5__GPIO			PINMUX_PIN(PIN_PE5, 0, 0)
+#define PIN_PE5__PWMH1			PINMUX_PIN(PIN_PE5, 1, 5)
+#define PIN_PE5__FLEXCOM0_IO2		PINMUX_PIN(PIN_PE5, 2, 4)
+#define PIN_PE5__TIOB3			PINMUX_PIN(PIN_PE5, 3, 3)
+#define PIN_PE5__I2SMCC1_DIN2		PINMUX_PIN(PIN_PE5, 4, 2)
+#define PIN_PE5__ISC_D9			PINMUX_PIN(PIN_PE5, 5, 2)
+#define PIN_PE5__G1_COL			PINMUX_PIN(PIN_PE5, 7, 1)
+#define PIN_PE6				134
+#define PIN_PE6__GPIO			PINMUX_PIN(PIN_PE6, 0, 0)
+#define PIN_PE6__PWML2			PINMUX_PIN(PIN_PE6, 1, 5)
+#define PIN_PE6__FLEXCOM0_IO3		PINMUX_PIN(PIN_PE6, 2, 4)
+#define PIN_PE6__TCLK3			PINMUX_PIN(PIN_PE6, 3, 3)
+#define PIN_PE6__I2SMCC1_DIN3		PINMUX_PIN(PIN_PE6, 4, 2)
+#define PIN_PE6__ISC_D10		PINMUX_PIN(PIN_PE6, 5, 2)
+#define PIN_PE6__G1_CRS			PINMUX_PIN(PIN_PE6, 7, 1)
+#define PIN_PE7				135
+#define PIN_PE7__GPIO			PINMUX_PIN(PIN_PE7, 0, 0)
+#define PIN_PE7__PWMH2			PINMUX_PIN(PIN_PE7, 1, 5)
+#define PIN_PE7__FLEXCOM0_IO4		PINMUX_PIN(PIN_PE7, 2, 4)
+#define PIN_PE7__TIOA4			PINMUX_PIN(PIN_PE7, 3, 3)
+#define PIN_PE7__ISC_D11		PINMUX_PIN(PIN_PE7, 5, 2)
+#define PIN_PE7__G1_TSUCOMP		PINMUX_PIN(PIN_PE7, 7, 1)
+

+ 4 - 0
arch/arm/mach-at91/Kconfig

@@ -47,6 +47,10 @@ config SAM9X60
 	bool
 	select CPU_ARM926EJS
 
+config SAMA7G5
+	bool
+	select CPU_V7A
+
 config SAMA5D2
 	bool
 	select CPU_V7A

+ 1 - 0
arch/arm/mach-at91/armv7/Makefile

@@ -9,6 +9,7 @@
 obj-$(CONFIG_SAMA5D2)	+= sama5d2_devices.o
 obj-$(CONFIG_SAMA5D3)	+= sama5d3_devices.o
 obj-$(CONFIG_SAMA5D4)	+= sama5d4_devices.o
+obj-$(CONFIG_SAMA7G5)	+= sama7g5_devices.o
 obj-y += clock.o
 obj-y += cpu.o
 obj-y += reset.o

+ 2 - 0
arch/arm/mach-at91/armv7/cpu.c

@@ -29,6 +29,7 @@ int arch_cpu_init(void)
 
 void arch_preboot_os(void)
 {
+#if (IS_ENABLED(CONFIG_ATMEL_PIT_TIMER))
 	ulong cpiv;
 	at91_pit_t *pit = (at91_pit_t *)ATMEL_BASE_PIT;
 
@@ -40,6 +41,7 @@ void arch_preboot_os(void)
 	 * without waiting for wrapping back to 0
 	 */
 	writel(cpiv + 0x1000, &pit->mr);
+#endif
 }
 
 #if defined(CONFIG_DISPLAY_CPUINFO)

+ 11 - 0
arch/arm/mach-at91/armv7/sama7g5_devices.c

@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Microchip Technology, Inc.
+ *		      Eugen Hristev <eugen.hristev@microchip.com>
+ */
+
+char *get_cpu_name(void)
+{
+	return "SAMA7G5";
+}
+

+ 1 - 0
arch/arm/mach-at91/include/mach/at91_common.h

@@ -40,6 +40,7 @@ void configure_ddrcfg_input_buffers(bool open);
 #endif
 
 int at91_set_ethaddr(int offset);
+int at91_set_eth1addr(int offset);
 void at91_spi_nor_set_ethaddr(void);
 int at91_video_show_board_info(void);
 

+ 2 - 0
arch/arm/mach-at91/include/mach/hardware.h

@@ -24,6 +24,8 @@
 # include <asm/arch/at91sam9x5.h>
 #elif defined(CONFIG_SAM9X60)
 # include <asm/arch/sam9x60.h>
+#elif defined(CONFIG_SAMA7G5)
+# include <asm/arch/sama7g5.h>
 #elif defined(CONFIG_SAMA5D2)
 # include <asm/arch/sama5d2.h>
 #elif defined(CONFIG_SAMA5D3)

+ 74 - 0
arch/arm/mach-at91/include/mach/sama7g5.h

@@ -0,0 +1,74 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Chip-specific header file for the SAMA7G5 SoC
+ *
+ * Copyright (C) 2020 Microchip Technology, Inc. and its subsidiaries
+ *		      Eugen Hristev <eugen.hristev@microchip.com>
+ */
+
+#ifndef __SAMA7G5_H__
+#define __SAMA7G5_H__
+
+/*
+ * Peripheral identifiers/interrupts.
+ */
+#define ATMEL_ID_FLEXCOM0	38
+#define ATMEL_ID_FLEXCOM1	39
+#define ATMEL_ID_FLEXCOM2	40
+#define ATMEL_ID_FLEXCOM3	41
+
+#define ATMEL_ID_SDMMC0		80
+#define ATMEL_ID_SDMMC1		81
+
+#define ATMEL_ID_PIT64B0	70
+#define ATMEL_ID_PIT64B		ATMEL_ID_PIT64B0
+
+#define ATMEL_CHIPID_CIDR	0xe0020000
+#define ATMEL_CHIPID_EXID	0xe0020004
+/*
+ * User Peripherals physical base addresses.
+ */
+#define ATMEL_BASE_PIOA		0xe0014000
+#define ATMEL_BASE_PIOB		(ATMEL_BASE_PIOA + 0x40)
+#define ATMEL_BASE_PIOC		(ATMEL_BASE_PIOB + 0x40)
+#define ATMEL_BASE_PIOD		(ATMEL_BASE_PIOC + 0x40)
+#define ATMEL_BASE_PIOE		(ATMEL_BASE_PIOD + 0x40)
+
+#define ATMEL_PIO_PORTS		5
+
+#define CPU_HAS_PCR
+
+#define ATMEL_BASE_PMC		0xe0018000
+
+#define ATMEL_BASE_WDT		0xe001c000
+#define ATMEL_BASE_RSTC		0xe001d000
+#define ATMEL_BASE_WDTS		0xe001d180
+#define ATMEL_BASE_SCKCR	0xe001d050
+
+#define ATMEL_BASE_SDMMC0	0xe1204000
+#define ATMEL_BASE_SDMMC1	0xe1208000
+
+#define ATMEL_BASE_PIT64B0	0xe1800000
+
+#define ATMEL_BASE_FLEXCOM0	0xe1818000
+#define ATMEL_BASE_FLEXCOM1	0xe181c000
+#define ATMEL_BASE_FLEXCOM2	0xe1820000
+#define ATMEL_BASE_FLEXCOM3	0xe1824000
+#define ATMEL_BASE_FLEXCOM4	0xe2018000
+
+#define ATMEL_BASE_TZC400	0xe3000000
+
+#define ATMEL_BASE_UMCTL2	0xe3800000
+#define ATMEL_BASE_UMCTL2_MP	0xe38003f8
+#define ATMEL_BASE_PUBL		0xe3804000
+
+#define ATMEL_NUM_FLEXCOM	12
+#define ATMEL_PIO_PORTS		5
+
+#define ATMEL_BASE_PIT64BC	ATMEL_BASE_PIT64B0
+
+#ifndef __ASSEMBLY__
+char *get_cpu_name(void);
+#endif
+
+#endif /* #ifndef __SAMA7G5_H__ */

+ 33 - 0
board/atmel/common/mac_eeprom.c

@@ -36,3 +36,36 @@ int at91_set_ethaddr(int offset)
 
 	return 0;
 }
+
+/* this function will set eth1addr from a second eeprom, if available */
+int at91_set_eth1addr(int offset)
+{
+	const int ETH_ADDR_LEN = 6;
+	unsigned char ethaddr[ETH_ADDR_LEN];
+	/* configure eth1addr for second interface */
+	const char *ETHADDR_NAME = "eth1addr";
+	struct udevice *dev;
+	int ret;
+
+	if (env_get(ETHADDR_NAME))
+		return 0;
+
+	/* first eeprom is retrieved, this is for the first interface */
+	ret = uclass_first_device_err(UCLASS_I2C_EEPROM, &dev);
+	if (ret)
+		return ret;
+
+	/* attempt to obtain a second eeprom device */
+	ret = uclass_next_device(&dev);
+	if (ret)
+		return ret;
+
+	ret = i2c_eeprom_read(dev, offset, ethaddr, 6);
+	if (ret)
+		return ret;
+
+	if (is_valid_ethaddr(ethaddr))
+		eth_env_set_enetaddr(ETHADDR_NAME, ethaddr);
+
+	return 0;
+}

+ 7 - 0
drivers/clk/at91/Kconfig

@@ -54,3 +54,10 @@ config AT91_GENERIC_CLK
 	  that may be different from the system clock. This second
 	  clock is the generic clock (GCLK) and is managed by
 	  the PMC via PMC_PCR register.
+
+config AT91_SAM9X60_PLL
+	bool "PLL support for SAM9X60 SoCs"
+	depends on CLK_AT91
+	help
+	  This option is used to enable the AT91 SAM9X60's PLL clock
+	  driver.

+ 9 - 6
drivers/clk/at91/Makefile

@@ -2,11 +2,14 @@
 # Makefile for at91 specific clk
 #
 
-obj-y += pmc.o sckc.o
-obj-y += clk-slow.o clk-main.o clk-plla.o clk-plladiv.o clk-master.o
-obj-y += clk-system.o clk-peripheral.o
+ifdef CONFIG_CLK_CCF
+obj-y += pmc.o sckc.o clk-main.o clk-master.o clk-programmable.o clk-system.o
+obj-y += clk-peripheral.o
 
+obj-$(CONFIG_AT91_GENERIC_CLK)	+= clk-generic.o
 obj-$(CONFIG_AT91_UTMI)		+= clk-utmi.o
-obj-$(CONFIG_AT91_USB_CLK)	+= clk-usb.o
-obj-$(CONFIG_AT91_H32MX)	+= clk-h32mx.o
-obj-$(CONFIG_AT91_GENERIC_CLK)	+= clk-generated.o
+obj-$(CONFIG_AT91_SAM9X60_PLL)	+= clk-sam9x60-pll.o
+obj-$(CONFIG_SAMA7G5)		+= sama7g5.o
+else
+obj-y += compat.o
+endif

+ 0 - 178
drivers/clk/at91/clk-generated.c

@@ -1,178 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2016 Atmel Corporation
- *               Wenyou.Yang <wenyou.yang@atmel.com>
- */
-
-#include <common.h>
-#include <clk-uclass.h>
-#include <dm.h>
-#include <log.h>
-#include <malloc.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <mach/at91_pmc.h>
-#include "pmc.h"
-
-DECLARE_GLOBAL_DATA_PTR;
-
-#define GENERATED_SOURCE_MAX	6
-#define GENERATED_MAX_DIV	255
-
-/**
- * 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 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 clock_source, parent_index;
-	int ret;
-
-	writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr);
-	tmp = readl(&pmc->pcr);
-	clock_source = (tmp >> AT91_PMC_PCR_GCKCSS_OFFSET) &
-		    AT91_PMC_PCR_GCKCSS_MASK;
-	gckdiv = (tmp >> AT91_PMC_PCR_GCKDIV_OFFSET) & AT91_PMC_PCR_GCKDIV_MASK;
-
-	parent_index = clock_source - 1;
-	ret = clk_get_by_index(dev_get_parent(clk->dev), parent_index, &parent);
-	if (ret)
-		return 0;
-
-	clk_rate = clk_get_rate(&parent) / (gckdiv + 1);
-
-	clk_free(&parent);
-
-	return clk_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 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;
-	u32 div, best_div = 0;
-	u8 best_parent_index, best_clock_source = 0;
-	u8 i;
-	u32 tmp;
-	int ret;
-
-	for (i = 0; i < priv->num_parents; i++) {
-		ret = clk_get_by_index(dev_get_parent(clk->dev), i, &parent);
-		if (ret)
-			return ret;
-
-		parent_rate = clk_get_rate(&parent);
-		if (IS_ERR_VALUE(parent_rate))
-			return parent_rate;
-
-		for (div = 1; div < GENERATED_MAX_DIV + 2; div++) {
-			tmp_rate = DIV_ROUND_CLOSEST(parent_rate, div);
-			tmp_diff = abs(rate - tmp_rate);
-
-			if (best_diff < 0 || best_diff > tmp_diff) {
-				best_rate = tmp_rate;
-				best_diff = tmp_diff;
-
-				best_div = div - 1;
-				best_parent = parent;
-				best_parent_index = i;
-				best_clock_source = best_parent_index + 1;
-			}
-
-			if (!best_diff || tmp_rate < rate)
-				break;
-		}
-
-		if (!best_diff)
-			break;
-	}
-
-	debug("GCK: best parent: %s, best_rate = %ld, best_div = %d\n",
-	      best_parent.dev->name, best_rate, best_div);
-
-	ret = clk_enable(&best_parent);
-	if (ret)
-		return ret;
-
-	writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr);
-	tmp = readl(&pmc->pcr);
-	tmp &= ~(AT91_PMC_PCR_GCKDIV | AT91_PMC_PCR_GCKCSS);
-	tmp |= AT91_PMC_PCR_GCKCSS_(best_clock_source) |
-	       AT91_PMC_PCR_CMD_WRITE |
-	       AT91_PMC_PCR_GCKDIV_(best_div) |
-	       AT91_PMC_PCR_GCKEN;
-	writel(tmp, &pmc->pcr);
-
-	while (!(readl(&pmc->sr) & AT91_PMC_GCKRDY))
-		;
-
-	return 0;
-}
-
-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 generic_clk_ofdata_to_platdata(struct udevice *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(dev_get_parent(dev)), "clocks", cells,
-			GENERATED_SOURCE_MAX);
-
-	if (!num_parents)
-		return -1;
-
-	priv->num_parents = num_parents;
-
-	return 0;
-}
-
-U_BOOT_DRIVER(generic_clk) = {
-	.name = "generic-clk",
-	.id = UCLASS_CLK,
-	.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 = &generic_clk_ops,
-};

+ 202 - 0
drivers/clk/at91/clk-generic.c

@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Generic clock support for AT91 architectures.
+ *
+ * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
+ *
+ * Based on drivers/clk/at91/clk-generated.c from Linux.
+ */
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <linux/io.h>
+#include <linux/clk-provider.h>
+#include <linux/clk/at91_pmc.h>
+
+#include "pmc.h"
+
+#define UBOOT_DM_CLK_AT91_GCK		"at91-gck-clk"
+
+#define GENERATED_MAX_DIV	255
+
+struct clk_gck {
+	void __iomem *base;
+	const u32 *clk_mux_table;
+	const u32 *mux_table;
+	const struct clk_pcr_layout *layout;
+	struct clk_range range;
+	struct clk clk;
+	u32 num_parents;
+	u32 id;
+};
+
+#define to_clk_gck(_c) container_of(_c, struct clk_gck, clk)
+
+static int clk_gck_enable(struct clk *clk)
+{
+	struct clk_gck *gck = to_clk_gck(clk);
+
+	pmc_write(gck->base, gck->layout->offset,
+		  (gck->id & gck->layout->pid_mask));
+	pmc_update_bits(gck->base, gck->layout->offset,
+			gck->layout->cmd | AT91_PMC_PCR_GCKEN,
+			gck->layout->cmd | AT91_PMC_PCR_GCKEN);
+
+	return 0;
+}
+
+static int clk_gck_disable(struct clk *clk)
+{
+	struct clk_gck *gck = to_clk_gck(clk);
+
+	pmc_write(gck->base, gck->layout->offset,
+		  (gck->id & gck->layout->pid_mask));
+	pmc_update_bits(gck->base, gck->layout->offset,
+			gck->layout->cmd | AT91_PMC_PCR_GCKEN,
+			gck->layout->cmd);
+
+	return 0;
+}
+
+static int clk_gck_set_parent(struct clk *clk, struct clk *parent)
+{
+	struct clk_gck *gck = to_clk_gck(clk);
+	int index;
+
+	index = at91_clk_mux_val_to_index(gck->clk_mux_table, gck->num_parents,
+					  parent->id);
+	if (index < 0)
+		return index;
+
+	index = at91_clk_mux_index_to_val(gck->mux_table, gck->num_parents,
+					  index);
+	if (index < 0)
+		return index;
+
+	pmc_write(gck->base, gck->layout->offset,
+		  (gck->id & gck->layout->pid_mask));
+	pmc_update_bits(gck->base, gck->layout->offset,
+			gck->layout->gckcss_mask | gck->layout->cmd,
+			(index << (ffs(gck->layout->gckcss_mask) - 1)) |
+			gck->layout->cmd);
+
+	return 0;
+}
+
+static ulong clk_gck_set_rate(struct clk *clk, ulong rate)
+{
+	struct clk_gck *gck = to_clk_gck(clk);
+	ulong parent_rate = clk_get_parent_rate(clk);
+	u32 div;
+
+	if (!rate || !parent_rate)
+		return 0;
+
+	if (gck->range.max && rate > gck->range.max)
+		return 0;
+
+	div = DIV_ROUND_CLOSEST(parent_rate, rate);
+	if (div > GENERATED_MAX_DIV + 1 || !div)
+		return 0;
+
+	pmc_write(gck->base, gck->layout->offset,
+		  (gck->id & gck->layout->pid_mask));
+	pmc_update_bits(gck->base, gck->layout->offset,
+			AT91_PMC_PCR_GCKDIV_MASK | gck->layout->cmd,
+			((div - 1) << (ffs(AT91_PMC_PCR_GCKDIV_MASK) - 1)) |
+			gck->layout->cmd);
+
+	return parent_rate / div;
+}
+
+static ulong clk_gck_get_rate(struct clk *clk)
+{
+	struct clk_gck *gck = to_clk_gck(clk);
+	ulong parent_rate = clk_get_parent_rate(clk);
+	u32 val, div;
+
+	if (!parent_rate)
+		return 0;
+
+	pmc_write(gck->base, gck->layout->offset,
+		  (gck->id & gck->layout->pid_mask));
+	pmc_read(gck->base, gck->layout->offset, &val);
+
+	div = (val & AT91_PMC_PCR_GCKDIV_MASK) >>
+		(ffs(AT91_PMC_PCR_GCKDIV_MASK) - 1);
+
+	return parent_rate / (div + 1);
+}
+
+static const struct clk_ops gck_ops = {
+	.enable = clk_gck_enable,
+	.disable = clk_gck_disable,
+	.set_parent = clk_gck_set_parent,
+	.set_rate = clk_gck_set_rate,
+	.get_rate = clk_gck_get_rate,
+};
+
+struct clk *
+at91_clk_register_generic(void __iomem *base,
+			  const struct clk_pcr_layout *layout,
+			  const char *name, const char * const *parent_names,
+			  const u32 *clk_mux_table, const u32 *mux_table,
+			  u8 num_parents, u8 id,
+			  const struct clk_range *range)
+{
+	struct clk_gck *gck;
+	struct clk *clk;
+	int ret, index;
+	u32 val;
+
+	if (!base || !layout || !name || !parent_names || !num_parents ||
+	    !clk_mux_table || !mux_table || !range)
+		return ERR_PTR(-EINVAL);
+
+	gck = kzalloc(sizeof(*gck), GFP_KERNEL);
+	if (!gck)
+		return ERR_PTR(-ENOMEM);
+
+	gck->id = id;
+	gck->base = base;
+	gck->range = *range;
+	gck->layout = layout;
+	gck->clk_mux_table = clk_mux_table;
+	gck->mux_table = mux_table;
+	gck->num_parents = num_parents;
+
+	clk = &gck->clk;
+	clk->flags = CLK_GET_RATE_NOCACHE;
+
+	pmc_write(gck->base, gck->layout->offset,
+		  (gck->id & gck->layout->pid_mask));
+	pmc_read(gck->base, gck->layout->offset, &val);
+
+	val = (val & gck->layout->gckcss_mask) >>
+		(ffs(gck->layout->gckcss_mask) - 1);
+
+	index = at91_clk_mux_val_to_index(gck->mux_table, gck->num_parents,
+					  val);
+	if (index < 0) {
+		kfree(gck);
+		return ERR_PTR(index);
+	}
+
+	ret = clk_register(clk, UBOOT_DM_CLK_AT91_GCK, name,
+			   parent_names[index]);
+	if (ret) {
+		kfree(gck);
+		clk = ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+U_BOOT_DRIVER(at91_gck_clk) = {
+	.name = UBOOT_DM_CLK_AT91_GCK,
+	.id = UCLASS_CLK,
+	.ops = &gck_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};

+ 0 - 56
drivers/clk/at91/clk-h32mx.c

@@ -1,56 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2016 Atmel Corporation
- *               Wenyou.Yang <wenyou.yang@atmel.com>
- */
-
-#include <common.h>
-#include <clk-uclass.h>
-#include <dm.h>
-#include <dm/device_compat.h>
-#include <dm/util.h>
-#include <linux/io.h>
-#include <mach/at91_pmc.h>
-#include "pmc.h"
-
-DECLARE_GLOBAL_DATA_PTR;
-
-#define H32MX_MAX_FREQ	90000000
-
-static ulong sama5d4_h32mx_clk_get_rate(struct clk *clk)
-{
-	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
-	struct at91_pmc *pmc = plat->reg_base;
-	ulong rate = gd->arch.mck_rate_hz;
-
-	if (readl(&pmc->mckr) & AT91_PMC_MCKR_H32MXDIV)
-		rate /= 2;
-
-	if (rate > H32MX_MAX_FREQ)
-		dev_dbg(clk->dev, "H32MX clock is too fast\n");
-
-	return rate;
-}
-
-static struct clk_ops sama5d4_h32mx_clk_ops = {
-	.get_rate = sama5d4_h32mx_clk_get_rate,
-};
-
-static int sama5d4_h32mx_clk_probe(struct udevice *dev)
-{
-	return at91_pmc_core_probe(dev);
-}
-
-static const struct udevice_id sama5d4_h32mx_clk_match[] = {
-	{ .compatible = "atmel,sama5d4-clk-h32mx" },
-	{}
-};
-
-U_BOOT_DRIVER(sama5d4_h32mx_clk) = {
-	.name = "sama5d4-h32mx-clk",
-	.id = UCLASS_CLK,
-	.of_match = sama5d4_h32mx_clk_match,
-	.probe = sama5d4_h32mx_clk_probe,
-	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
-	.ops = &sama5d4_h32mx_clk_ops,
-};

+ 357 - 24
drivers/clk/at91/clk-main.c

@@ -1,54 +1,387 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * Copyright (C) 2016 Atmel Corporation
- *               Wenyou.Yang <wenyou.yang@atmel.com>
+ * Main clock support for AT91 architectures.
+ *
+ * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
+ *
+ * Based on drivers/clk/at91/clk-main.c from Linux.
  */
 
+#include <asm/processor.h>
 #include <common.h>
 #include <clk-uclass.h>
 #include <dm.h>
+#include <linux/clk-provider.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/delay.h>
 #include <linux/io.h>
-#include <mach/at91_pmc.h>
 #include "pmc.h"
 
-DECLARE_GLOBAL_DATA_PTR;
+#define UBOOT_DM_CLK_AT91_MAIN_RC		"at91-main-rc-clk"
+#define UBOOT_DM_CLK_AT91_MAIN_OSC		"at91-main-osc-clk"
+#define UBOOT_DM_CLK_AT91_RM9200_MAIN		"at91-rm9200-main-clk"
+#define UBOOT_DM_CLK_AT91_SAM9X5_MAIN		"at91-sam9x5-main-clk"
 
-static int main_osc_clk_enable(struct clk *clk)
+#define MOR_KEY_MASK		GENMASK(23, 16)
+#define USEC_PER_SEC		1000000UL
+#define SLOW_CLOCK_FREQ		32768
+
+#define clk_main_parent_select(s)	(((s) & \
+					(AT91_PMC_MOSCEN | \
+					AT91_PMC_OSCBYPASS)) ? 1 : 0)
+
+struct clk_main_rc {
+	void __iomem	*reg;
+	struct clk	clk;
+};
+
+#define to_clk_main_rc(_clk) container_of(_clk, struct clk_main_rc, clk)
+
+struct clk_main_osc {
+	void __iomem	*reg;
+	struct clk	clk;
+};
+
+#define to_clk_main_osc(_clk) container_of(_clk, struct clk_main_osc, clk)
+
+struct clk_main {
+	void __iomem		*reg;
+	const unsigned int	*clk_mux_table;
+	const char * const	*parent_names;
+	unsigned int		num_parents;
+	int			type;
+	struct clk		clk;
+};
+
+#define to_clk_main(_clk) container_of(_clk, struct clk_main, clk)
+
+static int main_rc_enable(struct clk *clk)
+{
+	struct clk_main_rc *main_rc = to_clk_main_rc(clk);
+	void __iomem *reg = main_rc->reg;
+	unsigned int val;
+
+	pmc_read(reg, AT91_CKGR_MOR, &val);
+
+	if (!(val & AT91_PMC_MOSCRCEN)) {
+		pmc_update_bits(reg, AT91_CKGR_MOR,
+				MOR_KEY_MASK | AT91_PMC_MOSCRCEN,
+				AT91_PMC_KEY | AT91_PMC_MOSCRCEN);
+	}
+
+	pmc_read(reg, AT91_PMC_SR, &val);
+	while (!(val & AT91_PMC_MOSCRCS)) {
+		pmc_read(reg, AT91_PMC_SR, &val);
+		debug("waiting for main rc...\n");
+		cpu_relax();
+	}
+
+	return 0;
+}
+
+static int main_rc_disable(struct clk *clk)
 {
-	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
-	struct at91_pmc *pmc = plat->reg_base;
+	struct clk_main_rc *main_rc = to_clk_main_rc(clk);
+	struct reg *reg = main_rc->reg;
+	unsigned int val;
+
+	pmc_read(reg, AT91_CKGR_MOR, &val);
 
-	if (readl(&pmc->sr) & AT91_PMC_MOSCSELS)
+	if (!(val & AT91_PMC_MOSCRCEN))
 		return 0;
 
-	return -EINVAL;
+	pmc_update_bits(reg, AT91_CKGR_MOR, MOR_KEY_MASK | AT91_PMC_MOSCRCEN,
+			AT91_PMC_KEY);
+
+	return 0;
 }
 
-static ulong main_osc_clk_get_rate(struct clk *clk)
+static const struct clk_ops main_rc_clk_ops = {
+	.enable = main_rc_enable,
+	.disable = main_rc_disable,
+	.get_rate = clk_generic_get_rate,
+};
+
+struct clk *at91_clk_main_rc(void __iomem *reg, const char *name,
+			     const char *parent_name)
 {
-	return gd->arch.main_clk_rate_hz;
+	struct clk_main_rc *main_rc;
+	struct clk *clk;
+	int ret;
+
+	if (!reg || !name || !parent_name)
+		return ERR_PTR(-EINVAL);
+
+	main_rc = kzalloc(sizeof(*main_rc), GFP_KERNEL);
+	if (!main_rc)
+		return ERR_PTR(-ENOMEM);
+
+	main_rc->reg = reg;
+	clk = &main_rc->clk;
+
+	ret = clk_register(clk, UBOOT_DM_CLK_AT91_MAIN_RC, name,
+			   parent_name);
+	if (ret) {
+		kfree(main_rc);
+		clk = ERR_PTR(ret);
+	}
+
+	return clk;
 }
 
-static struct clk_ops main_osc_clk_ops = {
-	.enable = main_osc_clk_enable,
-	.get_rate = main_osc_clk_get_rate,
+U_BOOT_DRIVER(at91_main_rc_clk) = {
+	.name = UBOOT_DM_CLK_AT91_MAIN_RC,
+	.id = UCLASS_CLK,
+	.ops = &main_rc_clk_ops,
+	.flags = DM_FLAG_PRE_RELOC,
 };
 
-static int main_osc_clk_probe(struct udevice *dev)
+static int clk_main_osc_enable(struct clk *clk)
 {
-	return at91_pmc_core_probe(dev);
+	struct clk_main_osc *main = to_clk_main_osc(clk);
+	void __iomem *reg = main->reg;
+	unsigned int val;
+
+	pmc_read(reg, AT91_CKGR_MOR, &val);
+	val &= ~MOR_KEY_MASK;
+
+	if (val & AT91_PMC_OSCBYPASS)
+		return 0;
+
+	if (!(val & AT91_PMC_MOSCEN)) {
+		val |= AT91_PMC_MOSCEN | AT91_PMC_KEY;
+		pmc_write(reg, AT91_CKGR_MOR, val);
+	}
+
+	pmc_read(reg, AT91_PMC_SR, &val);
+	while (!(val & AT91_PMC_MOSCS)) {
+		pmc_read(reg, AT91_PMC_SR, &val);
+		debug("waiting for main osc..\n");
+		cpu_relax();
+	}
+
+	return 0;
+}
+
+static int clk_main_osc_disable(struct clk *clk)
+{
+	struct clk_main_osc *main = to_clk_main_osc(clk);
+	void __iomem *reg = main->reg;
+	unsigned int val;
+
+	pmc_read(reg, AT91_CKGR_MOR, &val);
+	if (val & AT91_PMC_OSCBYPASS)
+		return 0;
+
+	if (!(val & AT91_PMC_MOSCEN))
+		return 0;
+
+	val &= ~(AT91_PMC_KEY | AT91_PMC_MOSCEN);
+	pmc_write(reg, AT91_CKGR_MOR, val | AT91_PMC_KEY);
+
+	return 0;
 }
 
-static const struct udevice_id main_osc_clk_match[] = {
-	{ .compatible = "atmel,at91sam9x5-clk-main" },
-	{}
+static const struct clk_ops main_osc_clk_ops = {
+	.enable = clk_main_osc_enable,
+	.disable = clk_main_osc_disable,
+	.get_rate = clk_generic_get_rate,
 };
 
-U_BOOT_DRIVER(at91sam9x5_main_osc_clk) = {
-	.name = "at91sam9x5-main-osc-clk",
+struct clk *at91_clk_main_osc(void __iomem *reg, const char *name,
+			      const char *parent_name, bool bypass)
+{
+	struct clk_main_osc *main;
+	struct clk *clk;
+	int ret;
+
+	if (!reg || !name || !parent_name)
+		return ERR_PTR(-EINVAL);
+
+	main = kzalloc(sizeof(*main), GFP_KERNEL);
+	if (!main)
+		return ERR_PTR(-ENOMEM);
+
+	main->reg = reg;
+	clk = &main->clk;
+
+	if (bypass) {
+		pmc_update_bits(reg, AT91_CKGR_MOR,
+				MOR_KEY_MASK | AT91_PMC_OSCBYPASS,
+				AT91_PMC_KEY | AT91_PMC_OSCBYPASS);
+	}
+
+	ret = clk_register(clk, UBOOT_DM_CLK_AT91_MAIN_OSC, name, parent_name);
+	if (ret) {
+		kfree(main);
+		clk = ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+U_BOOT_DRIVER(at91_main_osc_clk) = {
+	.name = UBOOT_DM_CLK_AT91_MAIN_OSC,
 	.id = UCLASS_CLK,
-	.of_match = main_osc_clk_match,
-	.probe = main_osc_clk_probe,
-	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
 	.ops = &main_osc_clk_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
+
+static int clk_main_probe_frequency(void __iomem *reg)
+{
+	unsigned int cycles = 16;
+	unsigned int cycle = DIV_ROUND_UP(USEC_PER_SEC, SLOW_CLOCK_FREQ);
+	unsigned int mcfr;
+
+	while (cycles--) {
+		pmc_read(reg, AT91_CKGR_MCFR, &mcfr);
+		if (mcfr & AT91_PMC_MAINRDY)
+			return 0;
+		udelay(cycle);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int clk_rm9200_main_enable(struct clk *clk)
+{
+	struct clk_main *main = to_clk_main(clk);
+
+	return clk_main_probe_frequency(main->reg);
+}
+
+static const struct clk_ops rm9200_main_clk_ops = {
+	.enable = clk_rm9200_main_enable,
+};
+
+struct clk *at91_clk_rm9200_main(void __iomem *reg, const char *name,
+				 const char *parent_name)
+{
+	struct clk_main *main;
+	struct clk *clk;
+	int ret;
+
+	if (!reg || !name || !parent_name)
+		return ERR_PTR(-EINVAL);
+
+	main = kzalloc(sizeof(*main), GFP_KERNEL);
+	if (!main)
+		return ERR_PTR(-ENOMEM);
+
+	main->reg = reg;
+	clk = &main->clk;
+
+	ret = clk_register(clk, UBOOT_DM_CLK_AT91_RM9200_MAIN, name,
+			   parent_name);
+	if (ret) {
+		kfree(main);
+		clk = ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+U_BOOT_DRIVER(at91_rm9200_main_clk) = {
+	.name = UBOOT_DM_CLK_AT91_RM9200_MAIN,
+	.id = UCLASS_CLK,
+	.ops = &rm9200_main_clk_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
+
+static inline bool clk_sam9x5_main_ready(void __iomem *reg)
+{
+	unsigned int val;
+
+	pmc_read(reg, AT91_PMC_SR, &val);
+
+	return !!(val & AT91_PMC_MOSCSELS);
+}
+
+static int clk_sam9x5_main_enable(struct clk *clk)
+{
+	struct clk_main *main = to_clk_main(clk);
+	void __iomem *reg = main->reg;
+
+	while (!clk_sam9x5_main_ready(reg)) {
+		debug("waiting for main...");
+		cpu_relax();
+	}
+
+	return clk_main_probe_frequency(reg);
+}
+
+static int clk_sam9x5_main_set_parent(struct clk *clk, struct clk *parent)
+{
+	struct clk_main *main = to_clk_main(clk);
+	void __iomem *reg = main->reg;
+	unsigned int tmp, index;
+
+	index = at91_clk_mux_val_to_index(main->clk_mux_table,
+			main->num_parents, AT91_CLK_ID_TO_DID(parent->id));
+	if (index < 0)
+		return index;
+
+	pmc_read(reg, AT91_CKGR_MOR, &tmp);
+	tmp &= ~MOR_KEY_MASK;
+	tmp |= AT91_PMC_KEY;
+
+	if (index && !(tmp & AT91_PMC_MOSCSEL))
+		pmc_write(reg, AT91_CKGR_MOR, tmp | AT91_PMC_MOSCSEL);
+	else if (!index && (tmp & AT91_PMC_MOSCSEL))
+		pmc_write(reg, AT91_CKGR_MOR, tmp & ~AT91_PMC_MOSCSEL);
+
+	while (!clk_sam9x5_main_ready(reg))
+		cpu_relax();
+
+	return 0;
+}
+
+static const struct clk_ops sam9x5_main_clk_ops = {
+	.enable = clk_sam9x5_main_enable,
+	.set_parent = clk_sam9x5_main_set_parent,
+	.get_rate = clk_generic_get_rate,
+};
+
+struct clk *at91_clk_sam9x5_main(void __iomem *reg, const char *name,
+				 const char * const *parent_names,
+				 int num_parents, const u32 *clk_mux_table,
+				 int type)
+{
+	struct clk *clk = ERR_PTR(-ENOMEM);
+	struct clk_main *main = NULL;
+	unsigned int val;
+	int ret;
+
+	if (!reg || !name || !parent_names || !num_parents || !clk_mux_table)
+		return ERR_PTR(-EINVAL);
+
+	main = kzalloc(sizeof(*main), GFP_KERNEL);
+	if (!main)
+		return ERR_PTR(-ENOMEM);
+
+	main->reg = reg;
+	main->parent_names = parent_names;
+	main->num_parents = num_parents;
+	main->clk_mux_table = clk_mux_table;
+	main->type = type;
+	clk = &main->clk;
+	clk->flags = CLK_GET_RATE_NOCACHE;
+	pmc_read(reg, AT91_CKGR_MOR, &val);
+	ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAM9X5_MAIN, name,
+			   main->parent_names[clk_main_parent_select(val)]);
+	if (ret) {
+		kfree(main);
+		clk = ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+U_BOOT_DRIVER(at91_sam9x5_main_clk) = {
+	.name = UBOOT_DM_CLK_AT91_SAM9X5_MAIN,
+	.id = UCLASS_CLK,
+	.ops = &sam9x5_main_clk_ops,
+	.flags = DM_FLAG_PRE_RELOC,
 };

+ 315 - 16
drivers/clk/at91/clk-master.c

@@ -1,33 +1,332 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * Copyright (C) 2016 Atmel Corporation
- *               Wenyou.Yang <wenyou.yang@atmel.com>
+ * Master clock support for AT91 architectures.
+ *
+ * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
+ *
+ * Based on drivers/clk/at91/clk-master.c from Linux.
  */
 
-#include <common.h>
+#include <asm/processor.h>
 #include <clk-uclass.h>
+#include <common.h>
 #include <dm.h>
+#include <linux/clk-provider.h>
+#include <linux/clk/at91_pmc.h>
+
+#include "pmc.h"
+
+#define UBOOT_DM_CLK_AT91_MASTER		"at91-master-clk"
+#define UBOOT_DM_CLK_AT91_SAMA7G5_MASTER	"at91-sama7g5-master-clk"
+
+#define MASTER_PRES_MASK	0x7
+#define MASTER_PRES_MAX		MASTER_PRES_MASK
+#define MASTER_DIV_SHIFT	8
+#define MASTER_DIV_MASK		0x3
+
+#define PMC_MCR			0x30
+#define PMC_MCR_ID_MSK		GENMASK(3, 0)
+#define PMC_MCR_CMD		BIT(7)
+#define PMC_MCR_DIV		GENMASK(10, 8)
+#define PMC_MCR_CSS		GENMASK(20, 16)
+#define PMC_MCR_CSS_SHIFT	(16)
+#define PMC_MCR_EN		BIT(28)
+
+#define PMC_MCR_ID(x)		((x) & PMC_MCR_ID_MSK)
+
+#define MASTER_MAX_ID		4
+
+struct clk_master {
+	void __iomem *base;
+	const struct clk_master_layout *layout;
+	const struct clk_master_characteristics *characteristics;
+	const u32 *mux_table;
+	const u32 *clk_mux_table;
+	u32 num_parents;
+	struct clk clk;
+	u8 id;
+};
+
+#define to_clk_master(_clk) container_of(_clk, struct clk_master, clk)
+
+static inline bool clk_master_ready(struct clk_master *master)
+{
+	unsigned int bit = master->id ? AT91_PMC_MCKXRDY : AT91_PMC_MCKRDY;
+	unsigned int status;
+
+	pmc_read(master->base, AT91_PMC_SR, &status);
+
+	return !!(status & bit);
+}
+
+static int clk_master_enable(struct clk *clk)
+{
+	struct clk_master *master = to_clk_master(clk);
+
+	while (!clk_master_ready(master)) {
+		debug("waiting for mck %d\n", master->id);
+		cpu_relax();
+	}
+
+	return 0;
+}
 
-DECLARE_GLOBAL_DATA_PTR;
+static ulong clk_master_get_rate(struct clk *clk)
+{
+	struct clk_master *master = to_clk_master(clk);
+	const struct clk_master_layout *layout = master->layout;
+	const struct clk_master_characteristics *characteristics =
+						master->characteristics;
+	ulong rate = clk_get_parent_rate(clk);
+	unsigned int mckr;
+	u8 pres, div;
+
+	if (!rate)
+		return 0;
+
+	pmc_read(master->base, master->layout->offset, &mckr);
+	mckr &= layout->mask;
+
+	pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK;
+	div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
+
+	if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX)
+		rate /= 3;
+	else
+		rate >>= pres;
+
+	rate /= characteristics->divisors[div];
+
+	if (rate < characteristics->output.min)
+		pr_warn("master clk is underclocked");
+	else if (rate > characteristics->output.max)
+		pr_warn("master clk is overclocked");
+
+	return rate;
+}
+
+static const struct clk_ops master_ops = {
+	.enable = clk_master_enable,
+	.get_rate = clk_master_get_rate,
+};
 
-static ulong at91_master_clk_get_rate(struct clk *clk)
+struct clk *at91_clk_register_master(void __iomem *base,
+		const char *name, const char * const *parent_names,
+		int num_parents, const struct clk_master_layout *layout,
+		const struct clk_master_characteristics *characteristics,
+		const u32 *mux_table)
 {
-	return gd->arch.mck_rate_hz;
+	struct clk_master *master;
+	struct clk *clk;
+	unsigned int val;
+	int ret;
+
+	if (!base || !name || !num_parents || !parent_names ||
+	    !layout || !characteristics || !mux_table)
+		return ERR_PTR(-EINVAL);
+
+	master = kzalloc(sizeof(*master), GFP_KERNEL);
+	if (!master)
+		return ERR_PTR(-ENOMEM);
+
+	master->layout = layout;
+	master->characteristics = characteristics;
+	master->base = base;
+	master->num_parents = num_parents;
+	master->mux_table = mux_table;
+
+	pmc_read(master->base, master->layout->offset, &val);
+	clk = &master->clk;
+	clk->flags = CLK_GET_RATE_NOCACHE | CLK_IS_CRITICAL;
+	ret = clk_register(clk, UBOOT_DM_CLK_AT91_MASTER, name,
+			   parent_names[val & AT91_PMC_CSS]);
+	if (ret) {
+		kfree(master);
+		clk = ERR_PTR(ret);
+	}
+
+	return clk;
 }
 
-static struct clk_ops at91_master_clk_ops = {
-	.get_rate = at91_master_clk_get_rate,
+U_BOOT_DRIVER(at91_master_clk) = {
+	.name = UBOOT_DM_CLK_AT91_MASTER,
+	.id = UCLASS_CLK,
+	.ops = &master_ops,
+	.flags = DM_FLAG_PRE_RELOC,
 };
 
-static const struct udevice_id at91_master_clk_match[] = {
-	{ .compatible = "atmel,at91rm9200-clk-master" },
-	{ .compatible = "atmel,at91sam9x5-clk-master" },
-	{}
+static int clk_sama7g5_master_set_parent(struct clk *clk, struct clk *parent)
+{
+	struct clk_master *master = to_clk_master(clk);
+	int index;
+
+	index = at91_clk_mux_val_to_index(master->clk_mux_table,
+					  master->num_parents, parent->id);
+	if (index < 0)
+		return index;
+
+	index = at91_clk_mux_index_to_val(master->mux_table,
+					  master->num_parents, index);
+	if (index < 0)
+		return index;
+
+	pmc_write(master->base, PMC_MCR, PMC_MCR_ID(master->id));
+	pmc_update_bits(master->base, PMC_MCR,
+			PMC_MCR_CSS | PMC_MCR_CMD | PMC_MCR_ID_MSK,
+			(index << PMC_MCR_CSS_SHIFT) | PMC_MCR_CMD |
+			PMC_MCR_ID(master->id));
+	return 0;
+}
+
+static int clk_sama7g5_master_enable(struct clk *clk)
+{
+	struct clk_master *master = to_clk_master(clk);
+
+	pmc_write(master->base, PMC_MCR, PMC_MCR_ID(master->id));
+	pmc_update_bits(master->base, PMC_MCR,
+			PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID_MSK,
+			PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID(master->id));
+
+	return 0;
+}
+
+static int clk_sama7g5_master_disable(struct clk *clk)
+{
+	struct clk_master *master = to_clk_master(clk);
+
+	pmc_write(master->base, PMC_MCR, master->id);
+	pmc_update_bits(master->base, PMC_MCR,
+			PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID_MSK,
+			PMC_MCR_CMD | PMC_MCR_ID(master->id));
+
+	return 0;
+}
+
+static ulong clk_sama7g5_master_set_rate(struct clk *clk, ulong rate)
+{
+	struct clk_master *master = to_clk_master(clk);
+	ulong parent_rate = clk_get_parent_rate(clk);
+	ulong div, rrate;
+
+	if (!parent_rate)
+		return 0;
+
+	div = DIV_ROUND_CLOSEST(parent_rate, rate);
+	if ((div > (1 << (MASTER_PRES_MAX - 1))) || (div & (div - 1))) {
+		return 0;
+	} else if (div == 3) {
+		rrate = DIV_ROUND_CLOSEST(parent_rate, MASTER_PRES_MAX);
+		div = MASTER_PRES_MAX;
+	} else {
+		rrate = DIV_ROUND_CLOSEST(parent_rate, div);
+		div = ffs(div) - 1;
+	}
+
+	pmc_write(master->base, PMC_MCR, master->id);
+	pmc_update_bits(master->base, PMC_MCR,
+			PMC_MCR_DIV | PMC_MCR_CMD | PMC_MCR_ID_MSK,
+			(div << MASTER_DIV_SHIFT) | PMC_MCR_CMD |
+			PMC_MCR_ID(master->id));
+
+	return rrate;
+}
+
+static ulong clk_sama7g5_master_get_rate(struct clk *clk)
+{
+	struct clk_master *master = to_clk_master(clk);
+	ulong parent_rate = clk_get_parent_rate(clk);
+	unsigned int val;
+	ulong div;
+
+	if (!parent_rate)
+		return 0;
+
+	pmc_write(master->base, PMC_MCR, master->id);
+	pmc_read(master->base, PMC_MCR, &val);
+
+	div = (val >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
+
+	if (div == MASTER_PRES_MAX)
+		div = 3;
+	else
+		div = 1 << div;
+
+	return DIV_ROUND_CLOSEST(parent_rate, div);
+}
+
+static const struct clk_ops sama7g5_master_ops = {
+	.enable = clk_sama7g5_master_enable,
+	.disable = clk_sama7g5_master_disable,
+	.set_rate = clk_sama7g5_master_set_rate,
+	.get_rate = clk_sama7g5_master_get_rate,
+	.set_parent = clk_sama7g5_master_set_parent,
 };
 
-U_BOOT_DRIVER(atmel_at91rm9200_clk_master) = {
-	.name = "atmel_at91rm9200_clk_master",
+struct clk *at91_clk_sama7g5_register_master(void __iomem *base,
+		const char *name, const char * const *parent_names,
+		int num_parents, const u32 *mux_table, const u32 *clk_mux_table,
+		bool critical, u8 id)
+{
+	struct clk_master *master;
+	struct clk *clk;
+	u32 val, index;
+	int ret;
+
+	if (!base || !name || !num_parents || !parent_names ||
+	    !mux_table || !clk_mux_table || id > MASTER_MAX_ID)
+		return ERR_PTR(-EINVAL);
+
+	master = kzalloc(sizeof(*master), GFP_KERNEL);
+	if (!master)
+		return ERR_PTR(-ENOMEM);
+
+	master->base = base;
+	master->id = id;
+	master->mux_table = mux_table;
+	master->clk_mux_table = clk_mux_table;
+	master->num_parents = num_parents;
+
+	pmc_write(master->base, PMC_MCR, master->id);
+	pmc_read(master->base, PMC_MCR, &val);
+
+	index = at91_clk_mux_val_to_index(master->mux_table,
+				master->num_parents,
+				(val & PMC_MCR_CSS) >> PMC_MCR_CSS_SHIFT);
+	if (index < 0) {
+		kfree(master);
+		return ERR_PTR(index);
+	}
+
+	clk = &master->clk;
+	clk->flags = CLK_GET_RATE_NOCACHE | (critical ? CLK_IS_CRITICAL : 0);
+
+	ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAMA7G5_MASTER, name,
+			   parent_names[index]);
+	if (ret) {
+		kfree(master);
+		clk = ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+U_BOOT_DRIVER(at91_sama7g5_master_clk) = {
+	.name = UBOOT_DM_CLK_AT91_SAMA7G5_MASTER,
 	.id = UCLASS_CLK,
-	.of_match = at91_master_clk_match,
-	.ops = &at91_master_clk_ops,
+	.ops = &sama7g5_master_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
+
+const struct clk_master_layout at91rm9200_master_layout = {
+	.mask = 0x31F,
+	.pres_shift = 2,
+	.offset = AT91_PMC_MCKR,
+};
+
+const struct clk_master_layout at91sam9x5_master_layout = {
+	.mask = 0x373,
+	.pres_shift = 4,
+	.offset = AT91_PMC_MCKR,
 };

+ 216 - 75
drivers/clk/at91/clk-peripheral.c

@@ -1,113 +1,254 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * Copyright (C) 2016 Atmel Corporation
- *               Wenyou.Yang <wenyou.yang@atmel.com>
+ * Peripheral clock support for AT91 architectures.
+ *
+ * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
+ *
+ * Based on drivers/clk/at91/clk-peripheral.c from Linux.
  */
-
 #include <common.h>
 #include <clk-uclass.h>
 #include <dm.h>
-#include <malloc.h>
 #include <linux/io.h>
-#include <mach/at91_pmc.h>
+#include <linux/clk-provider.h>
+#include <linux/clk/at91_pmc.h>
+
 #include "pmc.h"
 
+#define UBOOT_DM_CLK_AT91_PERIPH		"at91-periph-clk"
+#define UBOOT_DM_CLK_AT91_SAM9X5_PERIPH		"at91-sam9x5-periph-clk"
+
 #define PERIPHERAL_ID_MIN	2
 #define PERIPHERAL_ID_MAX	31
 #define PERIPHERAL_MASK(id)	(1 << ((id) & PERIPHERAL_ID_MAX))
 
-enum periph_clk_type {
-	CLK_PERIPH_AT91RM9200 = 0,
-	CLK_PERIPH_AT91SAM9X5,
+#define PERIPHERAL_MAX_SHIFT	3
+
+struct clk_peripheral {
+	void __iomem *base;
+	struct clk clk;
+	u32 id;
 };
-/**
- * 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)
+
+#define to_clk_peripheral(_c) container_of(_c, struct clk_peripheral, clk)
+
+struct clk_sam9x5_peripheral {
+	const struct clk_pcr_layout *layout;
+	void __iomem *base;
+	struct clk clk;
+	struct clk_range range;
+	u32 id;
+	u32 div;
+	bool auto_div;
+};
+
+#define to_clk_sam9x5_peripheral(_c) \
+	container_of(_c, struct clk_sam9x5_peripheral, clk)
+
+static int clk_peripheral_enable(struct clk *clk)
 {
-	return at91_clk_sub_device_bind(dev, "periph-clk");
+	struct clk_peripheral *periph = to_clk_peripheral(clk);
+	int offset = AT91_PMC_PCER;
+	u32 id = periph->id;
+
+	if (id < PERIPHERAL_ID_MIN)
+		return 0;
+	if (id > PERIPHERAL_ID_MAX)
+		offset = AT91_PMC_PCER1;
+	pmc_write(periph->base, offset, PERIPHERAL_MASK(id));
+
+	return 0;
+}
+
+static int clk_peripheral_disable(struct clk *clk)
+{
+	struct clk_peripheral *periph = to_clk_peripheral(clk);
+	int offset = AT91_PMC_PCDR;
+	u32 id = periph->id;
+
+	if (id < PERIPHERAL_ID_MIN)
+		return -EINVAL;
+
+	if (id > PERIPHERAL_ID_MAX)
+		offset = AT91_PMC_PCDR1;
+	pmc_write(periph->base, offset, PERIPHERAL_MASK(id));
+
+	return 0;
 }
 
-static const struct udevice_id sam9x5_periph_clk_match[] = {
-	{
-		.compatible = "atmel,at91rm9200-clk-peripheral",
-		.data = CLK_PERIPH_AT91RM9200,
-	},
-	{
-		.compatible = "atmel,at91sam9x5-clk-peripheral",
-		.data = CLK_PERIPH_AT91SAM9X5,
-	},
-	{}
+static const struct clk_ops peripheral_ops = {
+	.enable = clk_peripheral_enable,
+	.disable = clk_peripheral_disable,
+	.get_rate = clk_generic_get_rate,
 };
 
-U_BOOT_DRIVER(atmel_at91rm9200_clk_peripheral) = {
-	.name = "atmel_at91rm9200_clk_peripheral",
-	.id = UCLASS_MISC,
-	.of_match = sam9x5_periph_clk_match,
-	.bind = sam9x5_periph_clk_bind,
+struct clk *
+at91_clk_register_peripheral(void __iomem *base, const char *name,
+			     const char *parent_name, u32 id)
+{
+	struct clk_peripheral *periph;
+	struct clk *clk;
+	int ret;
+
+	if (!base || !name || !parent_name || id > PERIPHERAL_ID_MAX)
+		return ERR_PTR(-EINVAL);
+
+	periph = kzalloc(sizeof(*periph), GFP_KERNEL);
+	if (!periph)
+		return ERR_PTR(-ENOMEM);
+
+	periph->id = id;
+	periph->base = base;
+
+	clk = &periph->clk;
+	clk->flags = CLK_GET_RATE_NOCACHE;
+	ret = clk_register(clk, UBOOT_DM_CLK_AT91_PERIPH, name, parent_name);
+	if (ret) {
+		kfree(periph);
+		clk = ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+U_BOOT_DRIVER(at91_periph_clk) = {
+	.name = UBOOT_DM_CLK_AT91_PERIPH,
+	.id = UCLASS_CLK,
+	.ops = &peripheral_ops,
+	.flags = DM_FLAG_PRE_RELOC,
 };
 
-/*---------------------------------------------------------*/
+static int clk_sam9x5_peripheral_enable(struct clk *clk)
+{
+	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
+
+	if (periph->id < PERIPHERAL_ID_MIN)
+		return 0;
+
+	pmc_write(periph->base, periph->layout->offset,
+		  (periph->id & periph->layout->pid_mask));
+	pmc_update_bits(periph->base, periph->layout->offset,
+			periph->layout->cmd | AT91_PMC_PCR_EN,
+			periph->layout->cmd | AT91_PMC_PCR_EN);
 
-static int periph_clk_enable(struct clk *clk)
+	return 0;
+}
+
+static int clk_sam9x5_peripheral_disable(struct clk *clk)
 {
-	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
-	struct at91_pmc *pmc = plat->reg_base;
-	enum periph_clk_type clk_type;
-	void *addr;
-
-	if (clk->id < PERIPHERAL_ID_MIN)
-		return -1;
-
-	clk_type = dev_get_driver_data(dev_get_parent(clk->dev));
-	if (clk_type == CLK_PERIPH_AT91RM9200) {
-		addr = &pmc->pcer;
-		if (clk->id > PERIPHERAL_ID_MAX)
-			addr = &pmc->pcer1;
-
-		setbits_le32(addr, PERIPHERAL_MASK(clk->id));
-	} else {
-		writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr);
-		setbits_le32(&pmc->pcr,
-			     AT91_PMC_PCR_CMD_WRITE | AT91_PMC_PCR_EN);
-	}
+	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
+
+	if (periph->id < PERIPHERAL_ID_MIN)
+		return -EINVAL;
+
+	pmc_write(periph->base, periph->layout->offset,
+		  (periph->id & periph->layout->pid_mask));
+	pmc_update_bits(periph->base, periph->layout->offset,
+			AT91_PMC_PCR_EN | periph->layout->cmd,
+			periph->layout->cmd);
 
 	return 0;
 }
 
-static ulong periph_get_rate(struct clk *clk)
+static ulong clk_sam9x5_peripheral_get_rate(struct clk *clk)
 {
-	struct udevice *dev;
-	struct clk clk_dev;
-	ulong clk_rate;
-	int ret;
+	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
+	ulong parent_rate = clk_get_parent_rate(clk);
+	u32 val, shift = ffs(periph->layout->div_mask) - 1;
 
-	dev = dev_get_parent(clk->dev);
+	if (!parent_rate)
+		return 0;
 
-	ret = clk_get_by_index(dev, 0, &clk_dev);
-	if (ret)
-		return ret;
+	pmc_write(periph->base, periph->layout->offset,
+		  (periph->id & periph->layout->pid_mask));
+	pmc_read(periph->base, periph->layout->offset, &val);
+	shift = (val & periph->layout->div_mask) >> shift;
 
-	clk_rate = clk_get_rate(&clk_dev);
+	return parent_rate >> shift;
+}
+
+static ulong clk_sam9x5_peripheral_set_rate(struct clk *clk, ulong rate)
+{
+	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
+	ulong parent_rate = clk_get_parent_rate(clk);
+	int shift;
+
+	if (!parent_rate)
+		return 0;
+
+	if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) {
+		if (parent_rate == rate)
+			return rate;
+		else
+			return 0;
+	}
+
+	if (periph->range.max && rate > periph->range.max)
+		return 0;
+
+	for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
+		if (parent_rate >> shift <= rate)
+			break;
+	}
+	if (shift == PERIPHERAL_MAX_SHIFT + 1)
+		return 0;
 
-	clk_free(&clk_dev);
+	pmc_write(periph->base, periph->layout->offset,
+		  (periph->id & periph->layout->pid_mask));
+	pmc_update_bits(periph->base, periph->layout->offset,
+			periph->layout->div_mask | periph->layout->cmd,
+			(shift << (ffs(periph->layout->div_mask) - 1)) |
+			periph->layout->cmd);
 
-	return clk_rate;
+	return parent_rate >> shift;
 }
 
-static struct clk_ops periph_clk_ops = {
-	.of_xlate = at91_clk_of_xlate,
-	.enable = periph_clk_enable,
-	.get_rate = periph_get_rate,
+static const struct clk_ops sam9x5_peripheral_ops = {
+	.enable = clk_sam9x5_peripheral_enable,
+	.disable = clk_sam9x5_peripheral_disable,
+	.get_rate = clk_sam9x5_peripheral_get_rate,
+	.set_rate = clk_sam9x5_peripheral_set_rate,
 };
 
-U_BOOT_DRIVER(clk_periph) = {
-	.name	= "periph-clk",
-	.id	= UCLASS_CLK,
-	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
-	.probe = at91_clk_probe,
-	.ops	= &periph_clk_ops,
+struct clk *
+at91_clk_register_sam9x5_peripheral(void __iomem *base,
+				    const struct clk_pcr_layout *layout,
+				    const char *name, const char *parent_name,
+				    u32 id, const struct clk_range *range)
+{
+	struct clk_sam9x5_peripheral *periph;
+	struct clk *clk;
+	int ret;
+
+	if (!base || !layout || !name || !parent_name || !range)
+		return ERR_PTR(-EINVAL);
+
+	periph = kzalloc(sizeof(*periph), GFP_KERNEL);
+	if (!periph)
+		return ERR_PTR(-ENOMEM);
+
+	periph->id = id;
+	periph->base = base;
+	periph->layout = layout;
+	periph->range = *range;
+
+	clk = &periph->clk;
+	clk->flags = CLK_GET_RATE_NOCACHE;
+	ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAM9X5_PERIPH, name,
+			   parent_name);
+	if (ret) {
+		kfree(periph);
+		clk = ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+U_BOOT_DRIVER(at91_sam9x5_periph_clk) = {
+	.name = UBOOT_DM_CLK_AT91_SAM9X5_PERIPH,
+	.id = UCLASS_CLK,
+	.ops = &sam9x5_peripheral_ops,
+	.flags = DM_FLAG_PRE_RELOC,
 };

+ 0 - 54
drivers/clk/at91/clk-plla.c

@@ -1,54 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2016 Atmel Corporation
- *               Wenyou.Yang <wenyou.yang@atmel.com>
- */
-
-#include <common.h>
-#include <clk-uclass.h>
-#include <dm.h>
-#include <linux/io.h>
-#include <mach/at91_pmc.h>
-#include "pmc.h"
-
-DECLARE_GLOBAL_DATA_PTR;
-
-static int plla_clk_enable(struct clk *clk)
-{
-	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
-	struct at91_pmc *pmc = plat->reg_base;
-
-	if (readl(&pmc->sr) & AT91_PMC_LOCKA)
-		return 0;
-
-	return -EINVAL;
-}
-
-static ulong plla_clk_get_rate(struct clk *clk)
-{
-	return gd->arch.plla_rate_hz;
-}
-
-static struct clk_ops plla_clk_ops = {
-	.enable = plla_clk_enable,
-	.get_rate = plla_clk_get_rate,
-};
-
-static int plla_clk_probe(struct udevice *dev)
-{
-	return at91_pmc_core_probe(dev);
-}
-
-static const struct udevice_id plla_clk_match[] = {
-	{ .compatible = "atmel,sama5d3-clk-pll" },
-	{}
-};
-
-U_BOOT_DRIVER(at91_plla_clk) = {
-	.name = "at91-plla-clk",
-	.id = UCLASS_CLK,
-	.of_match = plla_clk_match,
-	.probe = plla_clk_probe,
-	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
-	.ops = &plla_clk_ops,
-};

+ 0 - 85
drivers/clk/at91/clk-plladiv.c

@@ -1,85 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2018 Microhip / Atmel Corporation
- *               Wenyou.Yang <wenyou.yang@microchip.com>
- */
-
-#include <common.h>
-#include <clk-uclass.h>
-#include <dm/device.h>
-#include <linux/io.h>
-#include <mach/at91_pmc.h>
-#include "pmc.h"
-
-static int at91_plladiv_clk_enable(struct clk *clk)
-{
-	return 0;
-}
-
-static ulong at91_plladiv_clk_get_rate(struct clk *clk)
-{
-	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
-	struct at91_pmc *pmc = plat->reg_base;
-	struct clk source;
-	ulong clk_rate;
-	int ret;
-
-	ret = clk_get_by_index(clk->dev, 0, &source);
-	if (ret)
-		return -EINVAL;
-
-	clk_rate = clk_get_rate(&source);
-	if (readl(&pmc->mckr) & AT91_PMC_MCKR_PLLADIV_2)
-		clk_rate /= 2;
-
-	return clk_rate;
-}
-
-static ulong at91_plladiv_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 clk source;
-	ulong parent_rate;
-	int ret;
-
-	ret = clk_get_by_index(clk->dev, 0, &source);
-	if (ret)
-		return -EINVAL;
-
-	parent_rate = clk_get_rate(&source);
-	if ((parent_rate != rate) && ((parent_rate) / 2 != rate))
-		return -EINVAL;
-
-	if (parent_rate != rate) {
-		writel((readl(&pmc->mckr) | AT91_PMC_MCKR_PLLADIV_2),
-		       &pmc->mckr);
-	}
-
-	return 0;
-}
-
-static struct clk_ops at91_plladiv_clk_ops = {
-	.enable = at91_plladiv_clk_enable,
-	.get_rate = at91_plladiv_clk_get_rate,
-	.set_rate = at91_plladiv_clk_set_rate,
-};
-
-static int at91_plladiv_clk_probe(struct udevice *dev)
-{
-	return at91_pmc_core_probe(dev);
-}
-
-static const struct udevice_id at91_plladiv_clk_match[] = {
-	{ .compatible = "atmel,at91sam9x5-clk-plldiv" },
-	{}
-};
-
-U_BOOT_DRIVER(at91_plladiv_clk) = {
-	.name = "at91-plladiv-clk",
-	.id = UCLASS_CLK,
-	.of_match = at91_plladiv_clk_match,
-	.probe = at91_plladiv_clk_probe,
-	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
-	.ops = &at91_plladiv_clk_ops,
-};

+ 208 - 0
drivers/clk/at91/clk-programmable.c

@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Programmable clock support for AT91 architectures.
+ *
+ * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
+ *
+ * Based on drivers/clk/at91/clk-programmable.c from Linux.
+ */
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <linux/clk-provider.h>
+#include <linux/clk/at91_pmc.h>
+
+#include "pmc.h"
+
+#define UBOOT_DM_CLK_AT91_PROG		"at91-prog-clk"
+
+#define PROG_ID_MAX		7
+
+#define PROG_STATUS_MASK(id)	(1 << ((id) + 8))
+#define PROG_PRES(_l, _p)	(((_p) >> (_l)->pres_shift) & (_l)->pres_mask)
+#define PROG_MAX_RM9200_CSS	3
+
+struct clk_programmable {
+	void __iomem *base;
+	const u32 *clk_mux_table;
+	const u32 *mux_table;
+	const struct clk_programmable_layout *layout;
+	u32 num_parents;
+	struct clk clk;
+	u8 id;
+};
+
+#define to_clk_programmable(_c) container_of(_c, struct clk_programmable, clk)
+
+static ulong clk_programmable_get_rate(struct clk *clk)
+{
+	struct clk_programmable *prog = to_clk_programmable(clk);
+	const struct clk_programmable_layout *layout = prog->layout;
+	ulong rate, parent_rate = clk_get_parent_rate(clk);
+	unsigned int pckr;
+
+	pmc_read(prog->base, AT91_PMC_PCKR(prog->id), &pckr);
+
+	if (layout->is_pres_direct)
+		rate = parent_rate / (PROG_PRES(layout, pckr) + 1);
+	else
+		rate = parent_rate >> PROG_PRES(layout, pckr);
+
+	return rate;
+}
+
+static int clk_programmable_set_parent(struct clk *clk, struct clk *parent)
+{
+	struct clk_programmable *prog = to_clk_programmable(clk);
+	const struct clk_programmable_layout *layout = prog->layout;
+	unsigned int mask = layout->css_mask;
+	int index;
+
+	index = at91_clk_mux_val_to_index(prog->clk_mux_table,
+					  prog->num_parents, parent->id);
+	if (index < 0)
+		return index;
+
+	index = at91_clk_mux_index_to_val(prog->mux_table, prog->num_parents,
+					  index);
+	if (index < 0)
+		return index;
+
+	if (layout->have_slck_mck)
+		mask |= AT91_PMC_CSSMCK_MCK;
+
+	if (index > layout->css_mask) {
+		if (index > PROG_MAX_RM9200_CSS && !layout->have_slck_mck)
+			return -EINVAL;
+
+		index |= AT91_PMC_CSSMCK_MCK;
+	}
+
+	pmc_update_bits(prog->base, AT91_PMC_PCKR(prog->id), mask, index);
+
+	return 0;
+}
+
+static ulong clk_programmable_set_rate(struct clk *clk, ulong rate)
+{
+	struct clk_programmable *prog = to_clk_programmable(clk);
+	const struct clk_programmable_layout *layout = prog->layout;
+	ulong parent_rate = clk_get_parent_rate(clk);
+	ulong div = parent_rate / rate;
+	int shift = 0;
+
+	if (!parent_rate || !div)
+		return -EINVAL;
+
+	if (layout->is_pres_direct) {
+		shift = div - 1;
+
+		if (shift > layout->pres_mask)
+			return -EINVAL;
+	} else {
+		shift = fls(div) - 1;
+
+		if (div != (1 << shift))
+			return -EINVAL;
+
+		if (shift >= layout->pres_mask)
+			return -EINVAL;
+	}
+
+	pmc_update_bits(prog->base, AT91_PMC_PCKR(prog->id),
+			layout->pres_mask << layout->pres_shift,
+			shift << layout->pres_shift);
+
+	if (layout->is_pres_direct)
+		return (parent_rate / shift + 1);
+
+	return parent_rate >> shift;
+}
+
+static const struct clk_ops programmable_ops = {
+	.get_rate = clk_programmable_get_rate,
+	.set_parent = clk_programmable_set_parent,
+	.set_rate = clk_programmable_set_rate,
+};
+
+struct clk *at91_clk_register_programmable(void __iomem *base, const char *name,
+			const char *const *parent_names, u8 num_parents, u8 id,
+			const struct clk_programmable_layout *layout,
+			const u32 *clk_mux_table, const u32 *mux_table)
+{
+	struct clk_programmable *prog;
+	struct clk *clk;
+	u32 val, tmp;
+	int ret;
+
+	if (!base || !name || !parent_names || !num_parents ||
+	    !layout || !clk_mux_table || !mux_table || id > PROG_ID_MAX)
+		return ERR_PTR(-EINVAL);
+
+	prog = kzalloc(sizeof(*prog), GFP_KERNEL);
+	if (!prog)
+		return ERR_PTR(-ENOMEM);
+
+	prog->id = id;
+	prog->layout = layout;
+	prog->base = base;
+	prog->clk_mux_table = clk_mux_table;
+	prog->mux_table = mux_table;
+	prog->num_parents = num_parents;
+
+	pmc_read(prog->base, AT91_PMC_PCKR(prog->id), &tmp);
+	val = tmp & prog->layout->css_mask;
+	if (layout->have_slck_mck && (tmp & AT91_PMC_CSSMCK_MCK) && !val)
+		ret = PROG_MAX_RM9200_CSS + 1;
+	else
+		ret = at91_clk_mux_val_to_index(prog->mux_table,
+						prog->num_parents, val);
+	if (ret < 0) {
+		kfree(prog);
+		return ERR_PTR(ret);
+	}
+
+	clk = &prog->clk;
+	clk->flags = CLK_GET_RATE_NOCACHE;
+	ret = clk_register(clk, UBOOT_DM_CLK_AT91_PROG, name,
+			   parent_names[ret]);
+	if (ret) {
+		kfree(prog);
+		clk = ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+U_BOOT_DRIVER(at91_prog_clk) = {
+	.name = UBOOT_DM_CLK_AT91_PROG,
+	.id = UCLASS_CLK,
+	.ops = &programmable_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
+
+const struct clk_programmable_layout at91rm9200_programmable_layout = {
+	.pres_mask = 0x7,
+	.pres_shift = 2,
+	.css_mask = 0x3,
+	.have_slck_mck = 0,
+	.is_pres_direct = 0,
+};
+
+const struct clk_programmable_layout at91sam9g45_programmable_layout = {
+	.pres_mask = 0x7,
+	.pres_shift = 2,
+	.css_mask = 0x3,
+	.have_slck_mck = 1,
+	.is_pres_direct = 0,
+};
+
+const struct clk_programmable_layout at91sam9x5_programmable_layout = {
+	.pres_mask = 0x7,
+	.pres_shift = 4,
+	.css_mask = 0x7,
+	.have_slck_mck = 0,
+	.is_pres_direct = 0,
+};

+ 442 - 0
drivers/clk/at91/clk-sam9x60-pll.c

@@ -0,0 +1,442 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * SAM9X60's PLL clock support.
+ *
+ * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
+ *
+ * Based on drivers/clk/at91/clk-sam9x60-pll.c from Linux.
+ *
+ */
+
+#include <asm/processor.h>
+#include <common.h>
+#include <clk-uclass.h>
+#include <div64.h>
+#include <dm.h>
+#include <linux/clk-provider.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/delay.h>
+
+#include "pmc.h"
+
+#define UBOOT_DM_CLK_AT91_SAM9X60_DIV_PLL	"at91-sam9x60-div-pll-clk"
+#define UBOOT_DM_CLK_AT91_SAM9X60_FRAC_PLL	"at91-sam9x60-frac-pll-clk"
+
+#define	PMC_PLL_CTRL0_DIV_MSK	GENMASK(7, 0)
+#define	PMC_PLL_CTRL1_MUL_MSK	GENMASK(31, 24)
+#define PMC_PLL_CTRL1_FRACR_MSK	GENMASK(21, 0)
+
+#define PLL_DIV_MAX		(FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, UINT_MAX) + 1)
+#define UPLL_DIV		2
+#define PLL_MUL_MAX		(FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, UINT_MAX) + 1)
+
+#define FCORE_MIN		(600000000)
+#define FCORE_MAX		(1200000000)
+
+#define PLL_MAX_ID		7
+
+struct sam9x60_pll {
+	void __iomem *base;
+	const struct clk_pll_characteristics *characteristics;
+	const struct clk_pll_layout *layout;
+	struct clk clk;
+	u8 id;
+};
+
+#define to_sam9x60_pll(_clk)	container_of(_clk, struct sam9x60_pll, clk)
+
+static inline bool sam9x60_pll_ready(void __iomem *base, int id)
+{
+	unsigned int status;
+
+	pmc_read(base, AT91_PMC_PLL_ISR0, &status);
+
+	return !!(status & BIT(id));
+}
+
+static long sam9x60_frac_pll_compute_mul_frac(u32 *mul, u32 *frac, ulong rate,
+					      ulong parent_rate)
+{
+	unsigned long tmprate, remainder;
+	unsigned long nmul = 0;
+	unsigned long nfrac = 0;
+
+	if (rate < FCORE_MIN || rate > FCORE_MAX)
+		return -ERANGE;
+
+	/*
+	 * Calculate the multiplier associated with the current
+	 * divider that provide the closest rate to the requested one.
+	 */
+	nmul = mult_frac(rate, 1, parent_rate);
+	tmprate = mult_frac(parent_rate, nmul, 1);
+	remainder = rate - tmprate;
+
+	if (remainder) {
+		nfrac = DIV_ROUND_CLOSEST_ULL((u64)remainder * (1 << 22),
+					      parent_rate);
+
+		tmprate += DIV_ROUND_CLOSEST_ULL((u64)nfrac * parent_rate,
+						 (1 << 22));
+	}
+
+	/* Check if resulted rate is valid.  */
+	if (tmprate < FCORE_MIN || tmprate > FCORE_MAX)
+		return -ERANGE;
+
+	*mul = nmul - 1;
+	*frac = nfrac;
+
+	return tmprate;
+}
+
+static ulong sam9x60_frac_pll_set_rate(struct clk *clk, ulong rate)
+{
+	struct sam9x60_pll *pll = to_sam9x60_pll(clk);
+	void __iomem *base = pll->base;
+	ulong parent_rate = clk_get_parent_rate(clk);
+	u32 nmul, cmul, nfrac, cfrac, val;
+	bool ready = sam9x60_pll_ready(base, pll->id);
+	long ret;
+
+	if (!parent_rate)
+		return 0;
+
+	ret = sam9x60_frac_pll_compute_mul_frac(&nmul, &nfrac, rate,
+						parent_rate);
+	if (ret < 0)
+		return 0;
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
+			pll->id);
+	pmc_read(base, AT91_PMC_PLL_CTRL1, &val);
+	cmul = (val & pll->layout->mul_mask) >> pll->layout->mul_shift;
+	cfrac = (val & pll->layout->frac_mask) >> pll->layout->frac_shift;
+
+	/* Check against current values. */
+	if (sam9x60_pll_ready(base, pll->id) &&
+	    nmul == cmul && nfrac == cfrac)
+		return 0;
+
+	/* Update it to hardware. */
+	pmc_write(base, AT91_PMC_PLL_CTRL1,
+		  (nmul << pll->layout->mul_shift) |
+		  (nfrac << pll->layout->frac_shift));
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT,
+			AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+			AT91_PMC_PLL_UPDT_UPDATE | pll->id);
+
+	while (ready && !sam9x60_pll_ready(base, pll->id)) {
+		debug("waiting for pll %u...\n", pll->id);
+		cpu_relax();
+	}
+
+	return parent_rate * (nmul + 1) + ((u64)parent_rate * nfrac >> 22);
+}
+
+static ulong sam9x60_frac_pll_get_rate(struct clk *clk)
+{
+	struct sam9x60_pll *pll = to_sam9x60_pll(clk);
+	void __iomem *base = pll->base;
+	ulong parent_rate = clk_get_parent_rate(clk);
+	u32 mul, frac, val;
+
+	if (!parent_rate)
+		return 0;
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
+			pll->id);
+	pmc_read(base, AT91_PMC_PLL_CTRL1, &val);
+	mul = (val & pll->layout->mul_mask) >> pll->layout->mul_shift;
+	frac = (val & pll->layout->frac_mask) >> pll->layout->frac_shift;
+
+	return (parent_rate * (mul + 1) + ((u64)parent_rate * frac >> 22));
+}
+
+static int sam9x60_frac_pll_enable(struct clk *clk)
+{
+	struct sam9x60_pll *pll = to_sam9x60_pll(clk);
+	void __iomem *base = pll->base;
+	unsigned int val;
+	ulong crate;
+
+	crate = sam9x60_frac_pll_get_rate(clk);
+	if (crate < FCORE_MIN || crate > FCORE_MAX)
+		return -ERANGE;
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
+			pll->id);
+	pmc_read(base, AT91_PMC_PLL_CTRL1, &val);
+
+	if (sam9x60_pll_ready(base, pll->id))
+		return 0;
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT,
+			AT91_PMC_PMM_UPDT_STUPTIM_MSK |
+			AT91_PMC_PLL_UPDT_ID_MSK,
+			AT91_PMC_PLL_UPDT_STUPTIM(0x3f) | pll->id);
+
+	/* Recommended value for AT91_PMC_PLL_ACR */
+	if (pll->characteristics->upll)
+		val = AT91_PMC_PLL_ACR_DEFAULT_UPLL;
+	else
+		val = AT91_PMC_PLL_ACR_DEFAULT_PLLA;
+	pmc_write(base, AT91_PMC_PLL_ACR, val);
+
+	if (pll->characteristics->upll) {
+		/* Enable the UTMI internal bandgap */
+		val |= AT91_PMC_PLL_ACR_UTMIBG;
+		pmc_write(base, AT91_PMC_PLL_ACR, val);
+
+		udelay(10);
+
+		/* Enable the UTMI internal regulator */
+		val |= AT91_PMC_PLL_ACR_UTMIVR;
+		pmc_write(base, AT91_PMC_PLL_ACR, val);
+
+		udelay(10);
+
+		pmc_update_bits(base, AT91_PMC_PLL_UPDT,
+				AT91_PMC_PLL_UPDT_UPDATE |
+				AT91_PMC_PLL_UPDT_ID_MSK,
+				AT91_PMC_PLL_UPDT_UPDATE | pll->id);
+	}
+
+	pmc_update_bits(base, AT91_PMC_PLL_CTRL0,
+			AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL,
+			AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL);
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT,
+			AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+			AT91_PMC_PLL_UPDT_UPDATE | pll->id);
+
+	while (!sam9x60_pll_ready(base, pll->id)) {
+		debug("waiting for pll %u...\n", pll->id);
+		cpu_relax();
+	}
+
+	return 0;
+}
+
+static int sam9x60_frac_pll_disable(struct clk *clk)
+{
+	struct sam9x60_pll *pll = to_sam9x60_pll(clk);
+	void __iomem *base = pll->base;
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
+			pll->id);
+
+	pmc_update_bits(base, AT91_PMC_PLL_CTRL0,
+			AT91_PMC_PLL_CTRL0_ENPLL, 0);
+
+	if (pll->characteristics->upll)
+		pmc_update_bits(base, AT91_PMC_PLL_ACR,
+				AT91_PMC_PLL_ACR_UTMIBG |
+				AT91_PMC_PLL_ACR_UTMIVR, 0);
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT,
+			AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+			AT91_PMC_PLL_UPDT_UPDATE | pll->id);
+
+	return 0;
+}
+
+static const struct clk_ops sam9x60_frac_pll_ops = {
+	.enable = sam9x60_frac_pll_enable,
+	.disable = sam9x60_frac_pll_disable,
+	.set_rate = sam9x60_frac_pll_set_rate,
+	.get_rate = sam9x60_frac_pll_get_rate,
+};
+
+static int sam9x60_div_pll_enable(struct clk *clk)
+{
+	struct sam9x60_pll *pll = to_sam9x60_pll(clk);
+	void __iomem *base = pll->base;
+	unsigned int val;
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
+			pll->id);
+	pmc_read(base, AT91_PMC_PLL_CTRL0, &val);
+
+	/* Stop if enabled. */
+	if (val & pll->layout->endiv_mask)
+		return 0;
+
+	pmc_update_bits(base, AT91_PMC_PLL_CTRL0,
+			pll->layout->endiv_mask,
+			(1 << pll->layout->endiv_shift));
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT,
+			AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+			AT91_PMC_PLL_UPDT_UPDATE | pll->id);
+
+	while (!sam9x60_pll_ready(base, pll->id)) {
+		debug("waiting for pll %u...\n", pll->id);
+		cpu_relax();
+	}
+
+	return 0;
+}
+
+static int sam9x60_div_pll_disable(struct clk *clk)
+{
+	struct sam9x60_pll *pll = to_sam9x60_pll(clk);
+	void __iomem *base = pll->base;
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
+			pll->id);
+
+	pmc_update_bits(base, AT91_PMC_PLL_CTRL0,
+			pll->layout->endiv_mask, 0);
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT,
+			AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+			AT91_PMC_PLL_UPDT_UPDATE | pll->id);
+
+	return 0;
+}
+
+static ulong sam9x60_div_pll_set_rate(struct clk *clk, ulong rate)
+{
+	struct sam9x60_pll *pll = to_sam9x60_pll(clk);
+	void __iomem *base = pll->base;
+	const struct clk_pll_characteristics *characteristics =
+							pll->characteristics;
+	ulong parent_rate = clk_get_parent_rate(clk);
+	u8 div = DIV_ROUND_CLOSEST_ULL(parent_rate, rate) - 1;
+	ulong req_rate = parent_rate / (div + 1);
+	bool ready = sam9x60_pll_ready(base, pll->id);
+	u32 val;
+
+	if (!parent_rate || div > pll->layout->div_mask ||
+	    req_rate < characteristics->output[0].min ||
+	    req_rate > characteristics->output[0].max)
+		return 0;
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
+			pll->id);
+	pmc_read(base, AT91_PMC_PLL_CTRL0, &val);
+	/* Compare against current value. */
+	if (div == ((val & pll->layout->div_mask) >> pll->layout->div_shift))
+		return 0;
+
+	/* Update it to hardware. */
+	pmc_update_bits(base, AT91_PMC_PLL_CTRL0,
+			pll->layout->div_mask,
+			div << pll->layout->div_shift);
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT,
+			AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+			AT91_PMC_PLL_UPDT_UPDATE | pll->id);
+
+	while (ready && !sam9x60_pll_ready(base, pll->id)) {
+		debug("waiting for pll %u...\n", pll->id);
+		cpu_relax();
+	}
+
+	return req_rate;
+}
+
+static ulong sam9x60_div_pll_get_rate(struct clk *clk)
+{
+	struct sam9x60_pll *pll = to_sam9x60_pll(clk);
+	void __iomem *base = pll->base;
+	ulong parent_rate = clk_get_parent_rate(clk);
+	u32 val;
+	u8 div;
+
+	if (!parent_rate)
+		return 0;
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
+			pll->id);
+
+	pmc_read(base, AT91_PMC_PLL_CTRL0, &val);
+
+	div = (val & pll->layout->div_mask) >> pll->layout->div_shift;
+
+	return parent_rate / (div + 1);
+}
+
+static const struct clk_ops sam9x60_div_pll_ops = {
+	.enable = sam9x60_div_pll_enable,
+	.disable = sam9x60_div_pll_disable,
+	.set_rate = sam9x60_div_pll_set_rate,
+	.get_rate = sam9x60_div_pll_get_rate,
+};
+
+static struct clk *
+sam9x60_clk_register_pll(void __iomem *base, const char *type,
+			 const char *name, const char *parent_name, u8 id,
+			 const struct clk_pll_characteristics *characteristics,
+			 const struct clk_pll_layout *layout, u32 flags)
+{
+	struct sam9x60_pll *pll;
+	struct clk *clk;
+	int ret;
+
+	if (!base || !type || !name || !parent_name || !characteristics ||
+	    !layout || id > PLL_MAX_ID)
+		return ERR_PTR(-EINVAL);
+
+	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+	if (!pll)
+		return ERR_PTR(-ENOMEM);
+
+	pll->id = id;
+	pll->characteristics = characteristics;
+	pll->layout = layout;
+	pll->base = base;
+	clk = &pll->clk;
+	clk->flags = flags;
+
+	ret = clk_register(clk, type, name, parent_name);
+	if (ret) {
+		kfree(pll);
+		clk = ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+struct clk *
+sam9x60_clk_register_div_pll(void __iomem *base, const char *name,
+			     const char *parent_name, u8 id,
+			     const struct clk_pll_characteristics *characteristics,
+			     const struct clk_pll_layout *layout, bool critical)
+{
+	return sam9x60_clk_register_pll(base,
+		UBOOT_DM_CLK_AT91_SAM9X60_DIV_PLL, name, parent_name, id,
+		characteristics, layout,
+		CLK_GET_RATE_NOCACHE | (critical ? CLK_IS_CRITICAL : 0));
+}
+
+struct clk *
+sam9x60_clk_register_frac_pll(void __iomem *base, const char *name,
+			      const char *parent_name, u8 id,
+			      const struct clk_pll_characteristics *characteristics,
+			      const struct clk_pll_layout *layout, bool critical)
+{
+	return sam9x60_clk_register_pll(base,
+		UBOOT_DM_CLK_AT91_SAM9X60_FRAC_PLL, name, parent_name, id,
+		characteristics, layout,
+		CLK_GET_RATE_NOCACHE | (critical ? CLK_IS_CRITICAL : 0));
+}
+
+U_BOOT_DRIVER(at91_sam9x60_div_pll_clk) = {
+	.name = UBOOT_DM_CLK_AT91_SAM9X60_DIV_PLL,
+	.id = UCLASS_CLK,
+	.ops = &sam9x60_div_pll_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
+
+U_BOOT_DRIVER(at91_sam9x60_frac_pll_clk) = {
+	.name = UBOOT_DM_CLK_AT91_SAM9X60_FRAC_PLL,
+	.id = UCLASS_CLK,
+	.ops = &sam9x60_frac_pll_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
+

+ 0 - 36
drivers/clk/at91/clk-slow.c

@@ -1,36 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2016 Atmel Corporation
- *               Wenyou.Yang <wenyou.yang@atmel.com>
- */
-
-#include <common.h>
-#include <clk-uclass.h>
-#include <dm.h>
-
-static int at91_slow_clk_enable(struct clk *clk)
-{
-	return 0;
-}
-
-static ulong at91_slow_clk_get_rate(struct clk *clk)
-{
-	return CONFIG_SYS_AT91_SLOW_CLOCK;
-}
-
-static struct clk_ops at91_slow_clk_ops = {
-	.enable = at91_slow_clk_enable,
-	.get_rate = at91_slow_clk_get_rate,
-};
-
-static const struct udevice_id at91_slow_clk_match[] = {
-	{ .compatible = "atmel,at91sam9x5-clk-slow" },
-	{}
-};
-
-U_BOOT_DRIVER(at91_slow_clk) = {
-	.name = "at91-slow-clk",
-	.id = UCLASS_CLK,
-	.of_match = at91_slow_clk_match,
-	.ops = &at91_slow_clk_ops,
-};

+ 72 - 71
drivers/clk/at91/clk-system.c

@@ -1,111 +1,112 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * Copyright (C) 2016 Atmel Corporation
- *               Wenyou.Yang <wenyou.yang@atmel.com>
+ * System clock support for AT91 architectures.
+ *
+ * Copyright (C) Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
+ *
+ * Based on drivers/clk/at91/clk-system.c from Linux.
  */
-
+#include <asm/processor.h>
 #include <common.h>
 #include <clk-uclass.h>
 #include <dm.h>
-#include <linux/bitops.h>
 #include <linux/io.h>
-#include <mach/at91_pmc.h>
+#include <linux/clk-provider.h>
+#include <linux/clk/at91_pmc.h>
+
 #include "pmc.h"
 
-#define SYSTEM_MAX_ID		31
+#define UBOOT_DM_CLK_AT91_SYSTEM		"at91-system-clk"
 
-/**
- * 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" },
-	{}
-};
+#define SYSTEM_MAX_ID		31
 
-U_BOOT_DRIVER(at91_system_clk) = {
-	.name = "at91-system-clk",
-	.id = UCLASS_MISC,
-	.of_match = at91_system_clk_match,
-	.bind = at91_system_clk_bind,
+struct clk_system {
+	void __iomem *base;
+	struct clk clk;
+	u8 id;
 };
 
-/*----------------------------------------------------------*/
+#define to_clk_system(_c) container_of(_c, struct clk_system, clk)
 
 static inline int is_pck(int id)
 {
 	return (id >= 8) && (id <= 15);
 }
 
-static ulong system_clk_get_rate(struct clk *clk)
+static inline bool clk_system_ready(void __iomem *base, int id)
 {
-	struct clk clk_dev;
-	int ret;
+	unsigned int status;
 
-	ret = clk_get_by_index(clk->dev, 0, &clk_dev);
-	if (ret)
-		return -EINVAL;
+	pmc_read(base, AT91_PMC_SR, &status);
 
-	return clk_get_rate(&clk_dev);
+	return !!(status & (1 << id));
 }
 
-static ulong system_clk_set_rate(struct clk *clk, ulong rate)
+static int clk_system_enable(struct clk *clk)
 {
-	struct clk clk_dev;
-	int ret;
+	struct clk_system *sys = to_clk_system(clk);
 
-	ret = clk_get_by_index(clk->dev, 0, &clk_dev);
-	if (ret)
-		return -EINVAL;
+	pmc_write(sys->base, AT91_PMC_SCER, 1 << sys->id);
 
-	return clk_set_rate(&clk_dev, rate);
+	if (!is_pck(sys->id))
+		return 0;
+
+	while (!clk_system_ready(sys->base, sys->id)) {
+		debug("waiting for pck%u\n", sys->id);
+		cpu_relax();
+	}
+
+	return 0;
 }
 
-static int system_clk_enable(struct clk *clk)
+static int clk_system_disable(struct clk *clk)
 {
-	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
-	struct at91_pmc *pmc = plat->reg_base;
-	u32 mask;
+	struct clk_system *sys = to_clk_system(clk);
 
-	if (clk->id > SYSTEM_MAX_ID)
-		return -EINVAL;
+	pmc_write(sys->base, AT91_PMC_SCDR, 1 << sys->id);
 
-	mask = BIT(clk->id);
+	return 0;
+}
 
-	writel(mask, &pmc->scer);
+static const struct clk_ops system_ops = {
+	.enable = clk_system_enable,
+	.disable = clk_system_disable,
+	.get_rate = clk_generic_get_rate,
+};
 
-	/**
-	 * For the programmable clocks the Ready status in the PMC
-	 * status register should be checked after enabling.
-	 * For other clocks this is unnecessary.
-	 */
-	if (!is_pck(clk->id))
-		return 0;
+struct clk *at91_clk_register_system(void __iomem *base, const char *name,
+				     const char *parent_name, u8 id)
+{
+	struct clk_system *sys;
+	struct clk *clk;
+	int ret;
 
-	while (!(readl(&pmc->sr) & mask))
-		;
+	if (!base || !name || !parent_name || id > SYSTEM_MAX_ID)
+		return ERR_PTR(-EINVAL);
 
-	return 0;
-}
+	sys = kzalloc(sizeof(*sys), GFP_KERNEL);
+	if (!sys)
+		return ERR_PTR(-ENOMEM);
 
-static struct clk_ops system_clk_ops = {
-	.of_xlate = at91_clk_of_xlate,
-	.get_rate = system_clk_get_rate,
-	.set_rate = system_clk_set_rate,
-	.enable = system_clk_enable,
-};
+	sys->id = id;
+	sys->base = base;
 
-U_BOOT_DRIVER(system_clk) = {
-	.name = "system-clk",
+	clk = &sys->clk;
+	clk->flags = CLK_GET_RATE_NOCACHE;
+	ret = clk_register(clk, UBOOT_DM_CLK_AT91_SYSTEM, name, parent_name);
+	if (ret) {
+		kfree(sys);
+		clk = ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+U_BOOT_DRIVER(at91_system_clk) = {
+	.name = UBOOT_DM_CLK_AT91_SYSTEM,
 	.id = UCLASS_CLK,
-	.probe = at91_clk_probe,
-	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
-	.ops = &system_clk_ops,
+	.ops = &system_ops,
+	.flags = DM_FLAG_PRE_RELOC,
 };

+ 0 - 147
drivers/clk/at91/clk-usb.c

@@ -1,147 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2018 Microhip / Atmel Corporation
- *               Wenyou.Yang <wenyou.yang@microchip.com>
- */
-
-#include <common.h>
-#include <clk-uclass.h>
-#include <log.h>
-#include <dm/device.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <mach/at91_pmc.h>
-#include "pmc.h"
-
-DECLARE_GLOBAL_DATA_PTR;
-
-#define AT91_USB_CLK_SOURCE_MAX	2
-#define AT91_USB_CLK_MAX_DIV	15
-
-struct at91_usb_clk_priv {
-	u32 num_clksource;
-};
-
-static ulong at91_usb_clk_get_rate(struct clk *clk)
-{
-	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
-	struct at91_pmc *pmc = plat->reg_base;
-	struct clk source;
-	u32 tmp, usbdiv;
-	u8 source_index;
-	int ret;
-
-	tmp = readl(&pmc->pcr);
-	source_index = (tmp >> AT91_PMC_USB_USBS_OFFSET) &
-			AT91_PMC_USB_USBS_MASK;
-	usbdiv = (tmp >> AT91_PMC_USB_DIV_OFFSET) & AT91_PMC_USB_DIV_MASK;
-
-	ret = clk_get_by_index(clk->dev, source_index, &source);
-	if (ret)
-		return 0;
-
-	return clk_get_rate(&source) / (usbdiv + 1);
-}
-
-static ulong at91_usb_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 at91_usb_clk_priv *priv = dev_get_priv(clk->dev);
-	struct clk source, best_source;
-	ulong tmp_rate, best_rate = rate, source_rate;
-	int tmp_diff, best_diff = -1;
-	u32 div, best_div = 0;
-	u8 best_source_index = 0;
-	u8 i;
-	u32 tmp;
-	int ret;
-
-	for (i = 0; i < priv->num_clksource; i++) {
-		ret = clk_get_by_index(clk->dev, i, &source);
-		if (ret)
-			return ret;
-
-		source_rate = clk_get_rate(&source);
-		if (IS_ERR_VALUE(source_rate))
-			return source_rate;
-
-		for (div = 1; div < AT91_USB_CLK_MAX_DIV + 2; div++) {
-			tmp_rate = DIV_ROUND_CLOSEST(source_rate, div);
-			tmp_diff = abs(rate - tmp_rate);
-
-			if (best_diff < 0 || best_diff > tmp_diff) {
-				best_rate = tmp_rate;
-				best_diff = tmp_diff;
-
-				best_div = div - 1;
-				best_source = source;
-				best_source_index = i;
-			}
-
-			if (!best_diff || tmp_rate < rate)
-				break;
-		}
-
-		if (!best_diff)
-			break;
-	}
-
-	debug("AT91 USB: best sourc: %s, best_rate = %ld, best_div = %d\n",
-	      best_source.dev->name, best_rate, best_div);
-
-	ret = clk_enable(&best_source);
-	if (ret)
-		return ret;
-
-	tmp = AT91_PMC_USB_USBS_(best_source_index) |
-	      AT91_PMC_USB_DIV_(best_div);
-	writel(tmp, &pmc->usb);
-
-	return 0;
-}
-
-static struct clk_ops at91_usb_clk_ops = {
-	.get_rate = at91_usb_clk_get_rate,
-	.set_rate = at91_usb_clk_set_rate,
-};
-
-static int at91_usb_clk_ofdata_to_platdata(struct udevice *dev)
-{
-	struct at91_usb_clk_priv *priv = dev_get_priv(dev);
-	u32 cells[AT91_USB_CLK_SOURCE_MAX];
-	u32 num_clksource;
-
-	num_clksource = fdtdec_get_int_array_count(gd->fdt_blob,
-						   dev_of_offset(dev),
-						   "clocks", cells,
-						   AT91_USB_CLK_SOURCE_MAX);
-
-	if (!num_clksource)
-		return -1;
-
-	priv->num_clksource = num_clksource;
-
-	return 0;
-}
-
-static int at91_usb_clk_probe(struct udevice *dev)
-{
-	return at91_pmc_core_probe(dev);
-}
-
-static const struct udevice_id at91_usb_clk_match[] = {
-	{ .compatible = "atmel,at91sam9x5-clk-usb" },
-	{}
-};
-
-U_BOOT_DRIVER(at91_usb_clk) = {
-	.name = "at91-usb-clk",
-	.id = UCLASS_CLK,
-	.of_match = at91_usb_clk_match,
-	.probe = at91_usb_clk_probe,
-	.ofdata_to_platdata = at91_usb_clk_ofdata_to_platdata,
-	.priv_auto_alloc_size = sizeof(struct at91_usb_clk_priv),
-	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
-	.ops = &at91_usb_clk_ops,
-};

+ 163 - 71
drivers/clk/at91/clk-utmi.c

@@ -1,49 +1,65 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * Copyright (C) 2016 Atmel Corporation
- *               Wenyou.Yang <wenyou.yang@atmel.com>
+ * UTMI clock support for AT91 architectures.
+ *
+ * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
+ *
+ * Based on drivers/clk/at91/clk-utmi.c from Linux.
  */
-
+#include <asm/processor.h>
 #include <common.h>
 #include <clk-uclass.h>
 #include <dm.h>
-#include <syscon.h>
-#include <linux/io.h>
-#include <mach/at91_pmc.h>
+#include <linux/clk-provider.h>
+#include <linux/clk/at91_pmc.h>
 #include <mach/at91_sfr.h>
+#include <regmap.h>
+#include <syscon.h>
+
 #include "pmc.h"
 
+#define UBOOT_DM_CLK_AT91_UTMI			"at91-utmi-clk"
+#define UBOOT_DM_CLK_AT91_SAMA7G5_UTMI		"at91-sama7g5-utmi-clk"
+
 /*
  * The purpose of this clock is to generate a 480 MHz signal. A different
  * rate can't be configured.
  */
 #define UTMI_RATE	480000000
 
-static int utmi_clk_enable(struct clk *clk)
+struct clk_utmi {
+	void __iomem *base;
+	struct regmap *regmap_sfr;
+	struct clk clk;
+};
+
+#define to_clk_utmi(_clk) container_of(_clk, struct clk_utmi, clk)
+
+static inline bool clk_utmi_ready(struct regmap *regmap)
+{
+	unsigned int status;
+
+	pmc_read(regmap, AT91_PMC_SR, &status);
+
+	return !!(status & AT91_PMC_LOCKU);
+}
+
+static int clk_utmi_enable(struct clk *clk)
 {
-	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
-	struct at91_pmc *pmc = plat->reg_base;
-	struct clk clk_dev;
-	ulong clk_rate;
-	u32 utmi_ref_clk_freq;
-	u32 tmp;
-	int err;
-	int timeout = 2000000;
-
-	if (readl(&pmc->sr) & AT91_PMC_LOCKU)
-		return 0;
+	struct clk_utmi *utmi = to_clk_utmi(clk);
+	unsigned int uckr = AT91_PMC_UPLLEN | AT91_PMC_UPLLCOUNT |
+			    AT91_PMC_BIASEN;
+	unsigned int utmi_ref_clk_freq;
+	ulong parent_rate = clk_get_parent_rate(clk);
 
 	/*
 	 * If mainck rate is different from 12 MHz, we have to configure the
 	 * FREQ field of the SFR_UTMICKTRIM register to generate properly
 	 * the utmi clock.
 	 */
-	err = clk_get_by_index(clk->dev, 0, &clk_dev);
-	if (err)
-		return -EINVAL;
-
-	clk_rate = clk_get_rate(&clk_dev);
-	switch (clk_rate) {
+	switch (parent_rate) {
 	case 12000000:
 		utmi_ref_clk_freq = 0;
 		break;
@@ -61,82 +77,158 @@ static int utmi_clk_enable(struct clk *clk)
 		utmi_ref_clk_freq = 3;
 		break;
 	default:
-		printf("UTMICK: unsupported mainck rate\n");
+		debug("UTMICK: unsupported mainck rate\n");
 		return -EINVAL;
 	}
 
-	if (plat->regmap_sfr) {
-		err = regmap_read(plat->regmap_sfr, AT91_SFR_UTMICKTRIM, &tmp);
-		if (err)
-			return -EINVAL;
-
-		tmp &= ~AT91_UTMICKTRIM_FREQ;
-		tmp |= utmi_ref_clk_freq;
-		err = regmap_write(plat->regmap_sfr, AT91_SFR_UTMICKTRIM, tmp);
-		if (err)
-			return -EINVAL;
+	if (utmi->regmap_sfr) {
+		regmap_update_bits(utmi->regmap_sfr, AT91_SFR_UTMICKTRIM,
+				   AT91_UTMICKTRIM_FREQ, utmi_ref_clk_freq);
 	} else if (utmi_ref_clk_freq) {
-		printf("UTMICK: sfr node required\n");
+		debug("UTMICK: sfr node required\n");
 		return -EINVAL;
 	}
 
-	tmp = readl(&pmc->uckr);
-	tmp |= AT91_PMC_UPLLEN |
-	       AT91_PMC_UPLLCOUNT |
-	       AT91_PMC_BIASEN;
-	writel(tmp, &pmc->uckr);
-
-	while ((--timeout) && !(readl(&pmc->sr) & AT91_PMC_LOCKU))
-		;
-	if (!timeout) {
-		printf("UTMICK: timeout waiting for UPLL lock\n");
-		return -ETIMEDOUT;
+	pmc_update_bits(utmi->base, AT91_CKGR_UCKR, uckr, uckr);
+
+	while (!clk_utmi_ready(utmi->base)) {
+		debug("waiting for utmi...\n");
+		cpu_relax();
 	}
 
 	return 0;
 }
 
-static ulong utmi_clk_get_rate(struct clk *clk)
+static int clk_utmi_disable(struct clk *clk)
+{
+	struct clk_utmi *utmi = to_clk_utmi(clk);
+
+	pmc_update_bits(utmi->base, AT91_CKGR_UCKR, AT91_PMC_UPLLEN, 0);
+
+	return 0;
+}
+
+static ulong clk_utmi_get_rate(struct clk *clk)
 {
 	/* UTMI clk rate is fixed. */
 	return UTMI_RATE;
 }
 
-static struct clk_ops utmi_clk_ops = {
-	.enable = utmi_clk_enable,
-	.get_rate = utmi_clk_get_rate,
+static const struct clk_ops utmi_ops = {
+	.enable = clk_utmi_enable,
+	.disable = clk_utmi_disable,
+	.get_rate = clk_utmi_get_rate,
 };
 
-static int utmi_clk_ofdata_to_platdata(struct udevice *dev)
+struct clk *at91_clk_register_utmi(void __iomem *base, struct udevice *dev,
+				   const char *name, const char *parent_name)
 {
-	struct pmc_platdata *plat = dev_get_platdata(dev);
 	struct udevice *syscon;
+	struct clk_utmi *utmi;
+	struct clk *clk;
+	int ret;
+
+	if (!base || !dev || !name || !parent_name)
+		return ERR_PTR(-EINVAL);
+
+	ret = uclass_get_device_by_phandle(UCLASS_SYSCON, dev,
+					   "regmap-sfr", &syscon);
+	if (ret)
+		return ERR_PTR(ret);
+
+	utmi = kzalloc(sizeof(*utmi), GFP_KERNEL);
+	if (!utmi)
+		return ERR_PTR(-ENOMEM);
+
+	utmi->base = base;
+	utmi->regmap_sfr = syscon_get_regmap(syscon);
+	if (!utmi->regmap_sfr) {
+		kfree(utmi);
+		return ERR_PTR(-ENODEV);
+	}
 
-	uclass_get_device_by_phandle(UCLASS_SYSCON, dev,
-				     "regmap-sfr", &syscon);
-
-	if (syscon)
-		plat->regmap_sfr = syscon_get_regmap(syscon);
+	clk = &utmi->clk;
+	clk->flags = CLK_GET_RATE_NOCACHE;
+	ret = clk_register(clk, UBOOT_DM_CLK_AT91_UTMI, name, parent_name);
+	if (ret) {
+		kfree(utmi);
+		clk = ERR_PTR(ret);
+	}
 
-	return 0;
+	return clk;
 }
 
-static int utmi_clk_probe(struct udevice *dev)
+U_BOOT_DRIVER(at91_utmi_clk) = {
+	.name = UBOOT_DM_CLK_AT91_UTMI,
+	.id = UCLASS_CLK,
+	.ops = &utmi_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
+
+static int clk_utmi_sama7g5_enable(struct clk *clk)
 {
-	return at91_pmc_core_probe(dev);
+	struct clk_utmi *utmi = to_clk_utmi(clk);
+	ulong parent_rate = clk_get_parent_rate(clk);
+	unsigned int val;
+
+	switch (parent_rate) {
+	case 16000000:
+		val = 0;
+		break;
+	case 20000000:
+		val = 2;
+		break;
+	case 24000000:
+		val = 3;
+		break;
+	case 32000000:
+		val = 5;
+		break;
+	default:
+		debug("UTMICK: unsupported main_xtal rate\n");
+		return -EINVAL;
+	}
+
+	pmc_write(utmi->base, AT91_PMC_XTALF, val);
+
+	return 0;
 }
 
-static const struct udevice_id utmi_clk_match[] = {
-	{ .compatible = "atmel,at91sam9x5-clk-utmi" },
-	{}
+static const struct clk_ops sama7g5_utmi_ops = {
+	.enable = clk_utmi_sama7g5_enable,
+	.get_rate = clk_utmi_get_rate,
 };
 
-U_BOOT_DRIVER(at91sam9x5_utmi_clk) = {
-	.name = "at91sam9x5-utmi-clk",
+struct clk *at91_clk_sama7g5_register_utmi(void __iomem *base,
+		const char *name, const char *parent_name)
+{
+	struct clk_utmi *utmi;
+	struct clk *clk;
+	int ret;
+
+	if (!base || !name || !parent_name)
+		return ERR_PTR(-EINVAL);
+
+	utmi = kzalloc(sizeof(*utmi), GFP_KERNEL);
+	if (!utmi)
+		return ERR_PTR(-ENOMEM);
+
+	utmi->base = base;
+
+	clk = &utmi->clk;
+	ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAMA7G5_UTMI, name,
+			   parent_name);
+	if (ret) {
+		kfree(utmi);
+		clk = ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+U_BOOT_DRIVER(at91_sama7g5_utmi_clk) = {
+	.name = UBOOT_DM_CLK_AT91_SAMA7G5_UTMI,
 	.id = UCLASS_CLK,
-	.of_match = utmi_clk_match,
-	.probe = utmi_clk_probe,
-	.ofdata_to_platdata = utmi_clk_ofdata_to_platdata,
-	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
-	.ops = &utmi_clk_ops,
+	.ops = &sama7g5_utmi_ops,
+	.flags = DM_FLAG_PRE_RELOC,
 };

+ 1023 - 0
drivers/clk/at91/compat.c

@@ -0,0 +1,1023 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Compatible code for non CCF AT91 platforms.
+ *
+ * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
+ */
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <dm/lists.h>
+#include <dm/util.h>
+#include <mach/at91_pmc.h>
+#include <mach/at91_sfr.h>
+#include <regmap.h>
+#include <syscon.h>
+
+#include "pmc.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct pmc_platdata {
+	struct at91_pmc *reg_base;
+	struct regmap *regmap_sfr;
+};
+
+static const struct udevice_id at91_pmc_match[] = {
+	{ .compatible = "atmel,at91rm9200-pmc" },
+	{ .compatible = "atmel,at91sam9260-pmc" },
+	{ .compatible = "atmel,at91sam9g45-pmc" },
+	{ .compatible = "atmel,at91sam9n12-pmc" },
+	{ .compatible = "atmel,at91sam9x5-pmc" },
+	{ .compatible = "atmel,sama5d3-pmc" },
+	{ .compatible = "atmel,sama5d2-pmc" },
+	{}
+};
+
+U_BOOT_DRIVER(at91_pmc) = {
+	.name = "at91-pmc",
+	.id = UCLASS_SIMPLE_BUS,
+	.of_match = at91_pmc_match,
+};
+
+static int at91_pmc_core_probe(struct udevice *dev)
+{
+	struct pmc_platdata *plat = dev_get_platdata(dev);
+
+	dev = dev_get_parent(dev);
+
+	plat->reg_base = dev_read_addr_ptr(dev);
+
+	return 0;
+}
+
+/**
+ * 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(dev);
+	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 &&
+		    !ofnode_pre_reloc(offset_to_ofnode(offset)))
+			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, drv_name, name,
+					offset_to_ofnode(offset), NULL);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+int at91_clk_of_xlate(struct clk *clk, struct ofnode_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, dev_of_offset(clk->dev), "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 = dev_read_addr_ptr(dev_pmc);
+
+	return 0;
+}
+
+/* SCKC specific code. */
+static const struct udevice_id at91_sckc_match[] = {
+	{ .compatible = "atmel,at91sam9x5-sckc" },
+	{}
+};
+
+U_BOOT_DRIVER(at91_sckc) = {
+	.name = "at91-sckc",
+	.id = UCLASS_SIMPLE_BUS,
+	.of_match = at91_sckc_match,
+};
+
+/* Slow clock specific code. */
+static int at91_slow_clk_enable(struct clk *clk)
+{
+	return 0;
+}
+
+static ulong at91_slow_clk_get_rate(struct clk *clk)
+{
+	return CONFIG_SYS_AT91_SLOW_CLOCK;
+}
+
+static struct clk_ops at91_slow_clk_ops = {
+	.enable = at91_slow_clk_enable,
+	.get_rate = at91_slow_clk_get_rate,
+};
+
+static const struct udevice_id at91_slow_clk_match[] = {
+	{ .compatible = "atmel,at91sam9x5-clk-slow" },
+	{}
+};
+
+U_BOOT_DRIVER(at91_slow_clk) = {
+	.name = "at91-slow-clk",
+	.id = UCLASS_CLK,
+	.of_match = at91_slow_clk_match,
+	.ops = &at91_slow_clk_ops,
+};
+
+/* Master clock specific code. */
+static ulong at91_master_clk_get_rate(struct clk *clk)
+{
+	return gd->arch.mck_rate_hz;
+}
+
+static struct clk_ops at91_master_clk_ops = {
+	.get_rate = at91_master_clk_get_rate,
+};
+
+static const struct udevice_id at91_master_clk_match[] = {
+	{ .compatible = "atmel,at91rm9200-clk-master" },
+	{ .compatible = "atmel,at91sam9x5-clk-master" },
+	{}
+};
+
+U_BOOT_DRIVER(at91_master_clk) = {
+	.name = "at91-master-clk",
+	.id = UCLASS_CLK,
+	.of_match = at91_master_clk_match,
+	.ops = &at91_master_clk_ops,
+};
+
+/* Main osc clock specific code. */
+static int main_osc_clk_enable(struct clk *clk)
+{
+	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
+	struct at91_pmc *pmc = plat->reg_base;
+
+	if (readl(&pmc->sr) & AT91_PMC_MOSCSELS)
+		return 0;
+
+	return -EINVAL;
+}
+
+static ulong main_osc_clk_get_rate(struct clk *clk)
+{
+	return gd->arch.main_clk_rate_hz;
+}
+
+static struct clk_ops main_osc_clk_ops = {
+	.enable = main_osc_clk_enable,
+	.get_rate = main_osc_clk_get_rate,
+};
+
+static int main_osc_clk_probe(struct udevice *dev)
+{
+	return at91_pmc_core_probe(dev);
+}
+
+static const struct udevice_id main_osc_clk_match[] = {
+	{ .compatible = "atmel,at91sam9x5-clk-main" },
+	{}
+};
+
+U_BOOT_DRIVER(at91sam9x5_main_osc_clk) = {
+	.name = "at91sam9x5-main-osc-clk",
+	.id = UCLASS_CLK,
+	.of_match = main_osc_clk_match,
+	.probe = main_osc_clk_probe,
+	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
+	.ops = &main_osc_clk_ops,
+};
+
+/* PLLA clock specific code. */
+static int plla_clk_enable(struct clk *clk)
+{
+	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
+	struct at91_pmc *pmc = plat->reg_base;
+
+	if (readl(&pmc->sr) & AT91_PMC_LOCKA)
+		return 0;
+
+	return -EINVAL;
+}
+
+static ulong plla_clk_get_rate(struct clk *clk)
+{
+	return gd->arch.plla_rate_hz;
+}
+
+static struct clk_ops plla_clk_ops = {
+	.enable = plla_clk_enable,
+	.get_rate = plla_clk_get_rate,
+};
+
+static int plla_clk_probe(struct udevice *dev)
+{
+	return at91_pmc_core_probe(dev);
+}
+
+static const struct udevice_id plla_clk_match[] = {
+	{ .compatible = "atmel,sama5d3-clk-pll" },
+	{}
+};
+
+U_BOOT_DRIVER(at91_plla_clk) = {
+	.name = "at91-plla-clk",
+	.id = UCLASS_CLK,
+	.of_match = plla_clk_match,
+	.probe = plla_clk_probe,
+	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
+	.ops = &plla_clk_ops,
+};
+
+/* PLLA DIV clock specific code. */
+static int at91_plladiv_clk_enable(struct clk *clk)
+{
+	return 0;
+}
+
+static ulong at91_plladiv_clk_get_rate(struct clk *clk)
+{
+	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
+	struct at91_pmc *pmc = plat->reg_base;
+	struct clk source;
+	ulong clk_rate;
+	int ret;
+
+	ret = clk_get_by_index(clk->dev, 0, &source);
+	if (ret)
+		return -EINVAL;
+
+	clk_rate = clk_get_rate(&source);
+	if (readl(&pmc->mckr) & AT91_PMC_MCKR_PLLADIV_2)
+		clk_rate /= 2;
+
+	return clk_rate;
+}
+
+static ulong at91_plladiv_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 clk source;
+	ulong parent_rate;
+	int ret;
+
+	ret = clk_get_by_index(clk->dev, 0, &source);
+	if (ret)
+		return -EINVAL;
+
+	parent_rate = clk_get_rate(&source);
+	if ((parent_rate != rate) && ((parent_rate) / 2 != rate))
+		return -EINVAL;
+
+	if (parent_rate != rate) {
+		writel((readl(&pmc->mckr) | AT91_PMC_MCKR_PLLADIV_2),
+		       &pmc->mckr);
+	}
+
+	return 0;
+}
+
+static struct clk_ops at91_plladiv_clk_ops = {
+	.enable = at91_plladiv_clk_enable,
+	.get_rate = at91_plladiv_clk_get_rate,
+	.set_rate = at91_plladiv_clk_set_rate,
+};
+
+static int at91_plladiv_clk_probe(struct udevice *dev)
+{
+	return at91_pmc_core_probe(dev);
+}
+
+static const struct udevice_id at91_plladiv_clk_match[] = {
+	{ .compatible = "atmel,at91sam9x5-clk-plldiv" },
+	{}
+};
+
+U_BOOT_DRIVER(at91_plladiv_clk) = {
+	.name = "at91-plladiv-clk",
+	.id = UCLASS_CLK,
+	.of_match = at91_plladiv_clk_match,
+	.probe = at91_plladiv_clk_probe,
+	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
+	.ops = &at91_plladiv_clk_ops,
+};
+
+/* System clock specific code. */
+#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 ulong system_clk_get_rate(struct clk *clk)
+{
+	struct clk clk_dev;
+	int ret;
+
+	ret = clk_get_by_index(clk->dev, 0, &clk_dev);
+	if (ret)
+		return -EINVAL;
+
+	return clk_get_rate(&clk_dev);
+}
+
+static ulong system_clk_set_rate(struct clk *clk, ulong rate)
+{
+	struct clk clk_dev;
+	int ret;
+
+	ret = clk_get_by_index(clk->dev, 0, &clk_dev);
+	if (ret)
+		return -EINVAL;
+
+	return clk_set_rate(&clk_dev, rate);
+}
+
+static int system_clk_enable(struct clk *clk)
+{
+	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
+	struct at91_pmc *pmc = plat->reg_base;
+	u32 mask;
+
+	if (clk->id > SYSTEM_MAX_ID)
+		return -EINVAL;
+
+	mask = BIT(clk->id);
+
+	writel(mask, &pmc->scer);
+
+	/**
+	 * For the programmable clocks the Ready status in the PMC
+	 * status register should be checked after enabling.
+	 * For other clocks this is unnecessary.
+	 */
+	if (!is_pck(clk->id))
+		return 0;
+
+	while (!(readl(&pmc->sr) & mask))
+		;
+
+	return 0;
+}
+
+static struct clk_ops system_clk_ops = {
+	.of_xlate = at91_clk_of_xlate,
+	.get_rate = system_clk_get_rate,
+	.set_rate = system_clk_set_rate,
+	.enable = system_clk_enable,
+};
+
+U_BOOT_DRIVER(system_clk) = {
+	.name = "system-clk",
+	.id = UCLASS_CLK,
+	.probe = at91_clk_probe,
+	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
+	.ops = &system_clk_ops,
+};
+
+/* Peripheral clock specific code. */
+#define PERIPHERAL_ID_MIN	2
+#define PERIPHERAL_ID_MAX	31
+#define PERIPHERAL_MASK(id)	(1 << ((id) & PERIPHERAL_ID_MAX))
+
+enum periph_clk_type {
+	CLK_PERIPH_AT91RM9200 = 0,
+	CLK_PERIPH_AT91SAM9X5,
+};
+
+/**
+ * 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,at91rm9200-clk-peripheral",
+		.data = CLK_PERIPH_AT91RM9200,
+	},
+	{
+		.compatible = "atmel,at91sam9x5-clk-peripheral",
+		.data = CLK_PERIPH_AT91SAM9X5,
+	},
+	{}
+};
+
+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;
+	enum periph_clk_type clk_type;
+	void *addr;
+
+	if (clk->id < PERIPHERAL_ID_MIN)
+		return -1;
+
+	clk_type = dev_get_driver_data(dev_get_parent(clk->dev));
+	if (clk_type == CLK_PERIPH_AT91RM9200) {
+		addr = &pmc->pcer;
+		if (clk->id > PERIPHERAL_ID_MAX)
+			addr = &pmc->pcer1;
+
+		setbits_le32(addr, PERIPHERAL_MASK(clk->id));
+	} else {
+		writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr);
+		setbits_le32(&pmc->pcr,
+			     AT91_PMC_PCR_CMD_WRITE | AT91_PMC_PCR_EN);
+	}
+
+	return 0;
+}
+
+static ulong periph_get_rate(struct clk *clk)
+{
+	struct udevice *dev;
+	struct clk clk_dev;
+	ulong clk_rate;
+	int ret;
+
+	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 struct clk_ops periph_clk_ops = {
+	.of_xlate = at91_clk_of_xlate,
+	.enable = periph_clk_enable,
+	.get_rate = periph_get_rate,
+};
+
+U_BOOT_DRIVER(clk_periph) = {
+	.name	= "periph-clk",
+	.id	= UCLASS_CLK,
+	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
+	.probe = at91_clk_probe,
+	.ops	= &periph_clk_ops,
+};
+
+/* UTMI clock specific code. */
+#ifdef CONFIG_AT91_UTMI
+
+/*
+ * The purpose of this clock is to generate a 480 MHz signal. A different
+ * rate can't be configured.
+ */
+#define UTMI_RATE	480000000
+
+static int utmi_clk_enable(struct clk *clk)
+{
+	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
+	struct at91_pmc *pmc = plat->reg_base;
+	struct clk clk_dev;
+	ulong clk_rate;
+	u32 utmi_ref_clk_freq;
+	u32 tmp;
+	int err;
+	int timeout = 2000000;
+
+	if (readl(&pmc->sr) & AT91_PMC_LOCKU)
+		return 0;
+
+	/*
+	 * If mainck rate is different from 12 MHz, we have to configure the
+	 * FREQ field of the SFR_UTMICKTRIM register to generate properly
+	 * the utmi clock.
+	 */
+	err = clk_get_by_index(clk->dev, 0, &clk_dev);
+	if (err)
+		return -EINVAL;
+
+	clk_rate = clk_get_rate(&clk_dev);
+	switch (clk_rate) {
+	case 12000000:
+		utmi_ref_clk_freq = 0;
+		break;
+	case 16000000:
+		utmi_ref_clk_freq = 1;
+		break;
+	case 24000000:
+		utmi_ref_clk_freq = 2;
+		break;
+	/*
+	 * Not supported on SAMA5D2 but it's not an issue since MAINCK
+	 * maximum value is 24 MHz.
+	 */
+	case 48000000:
+		utmi_ref_clk_freq = 3;
+		break;
+	default:
+		printf("UTMICK: unsupported mainck rate\n");
+		return -EINVAL;
+	}
+
+	if (plat->regmap_sfr) {
+		err = regmap_read(plat->regmap_sfr, AT91_SFR_UTMICKTRIM, &tmp);
+		if (err)
+			return -EINVAL;
+
+		tmp &= ~AT91_UTMICKTRIM_FREQ;
+		tmp |= utmi_ref_clk_freq;
+		err = regmap_write(plat->regmap_sfr, AT91_SFR_UTMICKTRIM, tmp);
+		if (err)
+			return -EINVAL;
+	} else if (utmi_ref_clk_freq) {
+		printf("UTMICK: sfr node required\n");
+		return -EINVAL;
+	}
+
+	tmp = readl(&pmc->uckr);
+	tmp |= AT91_PMC_UPLLEN |
+	       AT91_PMC_UPLLCOUNT |
+	       AT91_PMC_BIASEN;
+	writel(tmp, &pmc->uckr);
+
+	while ((--timeout) && !(readl(&pmc->sr) & AT91_PMC_LOCKU))
+		;
+	if (!timeout) {
+		printf("UTMICK: timeout waiting for UPLL lock\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static ulong utmi_clk_get_rate(struct clk *clk)
+{
+	/* UTMI clk rate is fixed. */
+	return UTMI_RATE;
+}
+
+static struct clk_ops utmi_clk_ops = {
+	.enable = utmi_clk_enable,
+	.get_rate = utmi_clk_get_rate,
+};
+
+static int utmi_clk_ofdata_to_platdata(struct udevice *dev)
+{
+	struct pmc_platdata *plat = dev_get_platdata(dev);
+	struct udevice *syscon;
+
+	uclass_get_device_by_phandle(UCLASS_SYSCON, dev,
+				     "regmap-sfr", &syscon);
+
+	if (syscon)
+		plat->regmap_sfr = syscon_get_regmap(syscon);
+
+	return 0;
+}
+
+static int utmi_clk_probe(struct udevice *dev)
+{
+	return at91_pmc_core_probe(dev);
+}
+
+static const struct udevice_id utmi_clk_match[] = {
+	{ .compatible = "atmel,at91sam9x5-clk-utmi" },
+	{}
+};
+
+U_BOOT_DRIVER(at91sam9x5_utmi_clk) = {
+	.name = "at91sam9x5-utmi-clk",
+	.id = UCLASS_CLK,
+	.of_match = utmi_clk_match,
+	.probe = utmi_clk_probe,
+	.ofdata_to_platdata = utmi_clk_ofdata_to_platdata,
+	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
+	.ops = &utmi_clk_ops,
+};
+
+#endif /* CONFIG_AT91_UTMI */
+
+/* H32MX clock specific code. */
+#ifdef CONFIG_AT91_H32MX
+
+#define H32MX_MAX_FREQ	90000000
+
+static ulong sama5d4_h32mx_clk_get_rate(struct clk *clk)
+{
+	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
+	struct at91_pmc *pmc = plat->reg_base;
+	ulong rate = gd->arch.mck_rate_hz;
+
+	if (readl(&pmc->mckr) & AT91_PMC_MCKR_H32MXDIV)
+		rate /= 2;
+
+	if (rate > H32MX_MAX_FREQ)
+		dev_dbg(clk->dev, "H32MX clock is too fast\n");
+
+	return rate;
+}
+
+static struct clk_ops sama5d4_h32mx_clk_ops = {
+	.get_rate = sama5d4_h32mx_clk_get_rate,
+};
+
+static int sama5d4_h32mx_clk_probe(struct udevice *dev)
+{
+	return at91_pmc_core_probe(dev);
+}
+
+static const struct udevice_id sama5d4_h32mx_clk_match[] = {
+	{ .compatible = "atmel,sama5d4-clk-h32mx" },
+	{}
+};
+
+U_BOOT_DRIVER(sama5d4_h32mx_clk) = {
+	.name = "sama5d4-h32mx-clk",
+	.id = UCLASS_CLK,
+	.of_match = sama5d4_h32mx_clk_match,
+	.probe = sama5d4_h32mx_clk_probe,
+	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
+	.ops = &sama5d4_h32mx_clk_ops,
+};
+
+#endif /* CONFIG_AT91_H32MX */
+
+/* Generic clock specific code. */
+#ifdef CONFIG_AT91_GENERIC_CLK
+
+#define GENERATED_SOURCE_MAX	6
+#define GENERATED_MAX_DIV	255
+
+/**
+ * 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 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 clock_source, parent_index;
+	int ret;
+
+	writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr);
+	tmp = readl(&pmc->pcr);
+	clock_source = (tmp >> AT91_PMC_PCR_GCKCSS_OFFSET) &
+		    AT91_PMC_PCR_GCKCSS_MASK;
+	gckdiv = (tmp >> AT91_PMC_PCR_GCKDIV_OFFSET) & AT91_PMC_PCR_GCKDIV_MASK;
+
+	parent_index = clock_source - 1;
+	ret = clk_get_by_index(dev_get_parent(clk->dev), parent_index, &parent);
+	if (ret)
+		return 0;
+
+	clk_rate = clk_get_rate(&parent) / (gckdiv + 1);
+
+	clk_free(&parent);
+
+	return clk_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 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;
+	u32 div, best_div = 0;
+	u8 best_parent_index, best_clock_source = 0;
+	u8 i;
+	u32 tmp;
+	int ret;
+
+	for (i = 0; i < priv->num_parents; i++) {
+		ret = clk_get_by_index(dev_get_parent(clk->dev), i, &parent);
+		if (ret)
+			return ret;
+
+		parent_rate = clk_get_rate(&parent);
+		if (IS_ERR_VALUE(parent_rate))
+			return parent_rate;
+
+		for (div = 1; div < GENERATED_MAX_DIV + 2; div++) {
+			tmp_rate = DIV_ROUND_CLOSEST(parent_rate, div);
+			tmp_diff = abs(rate - tmp_rate);
+
+			if (best_diff < 0 || best_diff > tmp_diff) {
+				best_rate = tmp_rate;
+				best_diff = tmp_diff;
+
+				best_div = div - 1;
+				best_parent = parent;
+				best_parent_index = i;
+				best_clock_source = best_parent_index + 1;
+			}
+
+			if (!best_diff || tmp_rate < rate)
+				break;
+		}
+
+		if (!best_diff)
+			break;
+	}
+
+	debug("GCK: best parent: %s, best_rate = %ld, best_div = %d\n",
+	      best_parent.dev->name, best_rate, best_div);
+
+	ret = clk_enable(&best_parent);
+	if (ret)
+		return ret;
+
+	writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr);
+	tmp = readl(&pmc->pcr);
+	tmp &= ~(AT91_PMC_PCR_GCKDIV | AT91_PMC_PCR_GCKCSS);
+	tmp |= AT91_PMC_PCR_GCKCSS_(best_clock_source) |
+	       AT91_PMC_PCR_CMD_WRITE |
+	       AT91_PMC_PCR_GCKDIV_(best_div) |
+	       AT91_PMC_PCR_GCKEN;
+	writel(tmp, &pmc->pcr);
+
+	while (!(readl(&pmc->sr) & AT91_PMC_GCKRDY))
+		;
+
+	return 0;
+}
+
+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 generic_clk_ofdata_to_platdata(struct udevice *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(dev_get_parent(dev)), "clocks", cells,
+			GENERATED_SOURCE_MAX);
+
+	if (!num_parents)
+		return -1;
+
+	priv->num_parents = num_parents;
+
+	return 0;
+}
+
+U_BOOT_DRIVER(generic_clk) = {
+	.name = "generic-clk",
+	.id = UCLASS_CLK,
+	.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 = &generic_clk_ops,
+};
+
+#endif /* CONFIG_AT91_GENERIC_CLK */
+
+/* USB clock specific code. */
+#ifdef CONFIG_AT91_USB_CLK
+
+#define AT91_USB_CLK_SOURCE_MAX	2
+#define AT91_USB_CLK_MAX_DIV	15
+
+struct at91_usb_clk_priv {
+	u32 num_clksource;
+};
+
+static ulong at91_usb_clk_get_rate(struct clk *clk)
+{
+	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
+	struct at91_pmc *pmc = plat->reg_base;
+	struct clk source;
+	u32 tmp, usbdiv;
+	u8 source_index;
+	int ret;
+
+	tmp = readl(&pmc->pcr);
+	source_index = (tmp >> AT91_PMC_USB_USBS_OFFSET) &
+			AT91_PMC_USB_USBS_MASK;
+	usbdiv = (tmp >> AT91_PMC_USB_DIV_OFFSET) & AT91_PMC_USB_DIV_MASK;
+
+	ret = clk_get_by_index(clk->dev, source_index, &source);
+	if (ret)
+		return 0;
+
+	return clk_get_rate(&source) / (usbdiv + 1);
+}
+
+static ulong at91_usb_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 at91_usb_clk_priv *priv = dev_get_priv(clk->dev);
+	struct clk source, best_source;
+	ulong tmp_rate, best_rate = rate, source_rate;
+	int tmp_diff, best_diff = -1;
+	u32 div, best_div = 0;
+	u8 best_source_index = 0;
+	u8 i;
+	u32 tmp;
+	int ret;
+
+	for (i = 0; i < priv->num_clksource; i++) {
+		ret = clk_get_by_index(clk->dev, i, &source);
+		if (ret)
+			return ret;
+
+		source_rate = clk_get_rate(&source);
+		if (IS_ERR_VALUE(source_rate))
+			return source_rate;
+
+		for (div = 1; div < AT91_USB_CLK_MAX_DIV + 2; div++) {
+			tmp_rate = DIV_ROUND_CLOSEST(source_rate, div);
+			tmp_diff = abs(rate - tmp_rate);
+
+			if (best_diff < 0 || best_diff > tmp_diff) {
+				best_rate = tmp_rate;
+				best_diff = tmp_diff;
+
+				best_div = div - 1;
+				best_source = source;
+				best_source_index = i;
+			}
+
+			if (!best_diff || tmp_rate < rate)
+				break;
+		}
+
+		if (!best_diff)
+			break;
+	}
+
+	debug("AT91 USB: best sourc: %s, best_rate = %ld, best_div = %d\n",
+	      best_source.dev->name, best_rate, best_div);
+
+	ret = clk_enable(&best_source);
+	if (ret)
+		return ret;
+
+	tmp = AT91_PMC_USB_USBS_(best_source_index) |
+	      AT91_PMC_USB_DIV_(best_div);
+	writel(tmp, &pmc->usb);
+
+	return 0;
+}
+
+static struct clk_ops at91_usb_clk_ops = {
+	.get_rate = at91_usb_clk_get_rate,
+	.set_rate = at91_usb_clk_set_rate,
+};
+
+static int at91_usb_clk_ofdata_to_platdata(struct udevice *dev)
+{
+	struct at91_usb_clk_priv *priv = dev_get_priv(dev);
+	u32 cells[AT91_USB_CLK_SOURCE_MAX];
+	u32 num_clksource;
+
+	num_clksource = fdtdec_get_int_array_count(gd->fdt_blob,
+						   dev_of_offset(dev),
+						   "clocks", cells,
+						   AT91_USB_CLK_SOURCE_MAX);
+
+	if (!num_clksource)
+		return -1;
+
+	priv->num_clksource = num_clksource;
+
+	return 0;
+}
+
+static int at91_usb_clk_probe(struct udevice *dev)
+{
+	return at91_pmc_core_probe(dev);
+}
+
+static const struct udevice_id at91_usb_clk_match[] = {
+	{ .compatible = "atmel,at91sam9x5-clk-usb" },
+	{}
+};
+
+U_BOOT_DRIVER(at91_usb_clk) = {
+	.name = "at91-usb-clk",
+	.id = UCLASS_CLK,
+	.of_match = at91_usb_clk_match,
+	.probe = at91_usb_clk_probe,
+	.ofdata_to_platdata = at91_usb_clk_ofdata_to_platdata,
+	.priv_auto_alloc_size = sizeof(struct at91_usb_clk_priv),
+	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
+	.ops = &at91_usb_clk_ops,
+};
+
+#endif /* CONFIG_AT91_USB_CLK */

+ 133 - 85
drivers/clk/at91/pmc.c

@@ -4,119 +4,167 @@
  *               Wenyou.Yang <wenyou.yang@atmel.com>
  */
 
-#include <common.h>
+#include <asm/io.h>
 #include <clk-uclass.h>
-#include <dm.h>
-#include <log.h>
-#include <dm/lists.h>
-#include <dm/util.h>
+#include <common.h>
+
 #include "pmc.h"
 
-DECLARE_GLOBAL_DATA_PTR;
-
-static const struct udevice_id at91_pmc_match[] = {
-	{ .compatible = "atmel,at91rm9200-pmc" },
-	{ .compatible = "atmel,at91sam9260-pmc" },
-	{ .compatible = "atmel,at91sam9g45-pmc" },
-	{ .compatible = "atmel,at91sam9n12-pmc" },
-	{ .compatible = "atmel,at91sam9x5-pmc" },
-	{ .compatible = "atmel,sama5d3-pmc" },
-	{ .compatible = "atmel,sama5d2-pmc" },
-	{}
-};
+static int at91_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args)
+{
+	if (args->args_count != 2) {
+		debug("AT91: clk: Invalid args_count: %d\n", args->args_count);
+		return -EINVAL;
+	}
 
-U_BOOT_DRIVER(atmel_at91rm9200_pmc) = {
-	.name = "atmel_at91rm9200_pmc",
-	.id = UCLASS_SIMPLE_BUS,
-	.of_match = at91_pmc_match,
-};
+	clk->id = AT91_TO_CLK_ID(args->args[0], args->args[1]);
+
+	return 0;
+}
+
+static ulong at91_clk_get_rate(struct clk *clk)
+{
+	struct clk *c;
+	int ret;
+
+	ret = clk_get_by_id(clk->id, &c);
+	if (ret)
+		return ret;
+
+	return clk_get_rate(c);
+}
+
+static ulong at91_clk_set_rate(struct clk *clk, ulong rate)
+{
+	struct clk *c;
+	int ret;
 
-U_BOOT_DRIVER_ALIAS(atmel_at91rm9200_pmc, atmel_at91sam9260_pmc)
+	ret = clk_get_by_id(clk->id, &c);
+	if (ret)
+		return ret;
 
-/*---------------------------------------------------------*/
+	return clk_set_rate(c, rate);
+}
 
-int at91_pmc_core_probe(struct udevice *dev)
+static int at91_clk_enable(struct clk *clk)
 {
-	struct pmc_platdata *plat = dev_get_platdata(dev);
+	struct clk *c;
+	int ret;
 
-	dev = dev_get_parent(dev);
+	ret = clk_get_by_id(clk->id, &c);
+	if (ret)
+		return ret;
 
-	plat->reg_base = dev_read_addr_ptr(dev);
+	return clk_enable(c);
+}
 
-	return 0;
+static int at91_clk_disable(struct clk *clk)
+{
+	struct clk *c;
+	int ret;
+
+	ret = clk_get_by_id(clk->id, &c);
+	if (ret)
+		return ret;
+
+	return clk_disable(c);
 }
 
+const struct clk_ops at91_clk_ops = {
+	.of_xlate	= at91_clk_of_xlate,
+	.set_rate	= at91_clk_set_rate,
+	.get_rate	= at91_clk_get_rate,
+	.enable		= at91_clk_enable,
+	.disable	= at91_clk_disable,
+};
+
 /**
- * at91_clk_sub_device_bind() - for the at91 clock driver
- * Recursively bind its children as clk devices.
+ * pmc_read() - read content at address base + off into val
  *
- * @return: 0 on success, or negative error code on failure
+ * @base: base address
+ * @off: offset to read from
+ * @val: where the content of base + off is stored
+ *
+ * @return: void
  */
-int at91_clk_sub_device_bind(struct udevice *dev, const char *drv_name)
+void pmc_read(void __iomem *base, unsigned int off, unsigned int *val)
 {
-	const void *fdt = gd->fdt_blob;
-	int offset = dev_of_offset(dev);
-	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 &&
-		    !ofnode_pre_reloc(offset_to_ofnode(offset)))
-			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, drv_name, name,
-					offset_to_ofnode(offset), NULL);
-		if (ret)
-			return ret;
-	}
+	*val = readl(base + off);
+}
 
-	return 0;
+/**
+ * pmc_write() - write content of val at address base + off
+ *
+ * @base: base address
+ * @off: offset to write to
+ * @val: content to be written at base + off
+ *
+ * @return: void
+ */
+void pmc_write(void __iomem *base, unsigned int off, unsigned int val)
+{
+	writel(val, base + off);
 }
 
-int at91_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args)
+/**
+ * pmc_update_bits() - update a set of bits at address base + off
+ *
+ * @base: base address
+ * @off: offset to be updated
+ * @mask: mask of bits to be updated
+ * @bits: the new value to be updated
+ *
+ * @return: void
+ */
+void pmc_update_bits(void __iomem *base, unsigned int off,
+		     unsigned int mask, unsigned int bits)
 {
-	int periph;
+	unsigned int tmp;
 
-	if (args->args_count) {
-		debug("Invalid args_count: %d\n", args->args_count);
-		return -EINVAL;
-	}
+	tmp = readl(base + off);
+	tmp &= ~mask;
+	writel(tmp | (bits & mask), base + off);
+}
 
-	periph = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(clk->dev), "reg",
-				 -1);
-	if (periph < 0)
+/**
+ * at91_clk_mux_val_to_index() - get parent index in mux table
+ *
+ * @table: clock mux table
+ * @num_parents: clock number of parents
+ * @val: clock id who's mux index should be retrieved
+ *
+ * @return: clock index in mux table or a negative error number in case of
+ *		failure
+ */
+int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val)
+{
+	int i;
+
+	if (!table || !num_parents)
 		return -EINVAL;
 
-	clk->id = periph;
+	for (i = 0; i < num_parents; i++) {
+		if (table[i] == val)
+			return i;
+	}
 
-	return 0;
+	return -EINVAL;
 }
 
-int at91_clk_probe(struct udevice *dev)
+/**
+ * at91_clk_mux_index_to_val() - get parent ID corresponding to an entry in
+ *	clock's mux table
+ *
+ * @table: clock's mux table
+ * @num_parents: clock's number of parents
+ * @index: index in mux table which clock's ID should be retrieved
+ *
+ * @return: clock ID or a negative error number in case of failure
+ */
+int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index)
 {
-	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 = dev_read_addr_ptr(dev_pmc);
+	if (!table || !num_parents || index < 0 || index > num_parents)
+		return -EINVAL;
 
-	return 0;
+	return table[index];
 }

+ 132 - 8
drivers/clk/at91/pmc.h

@@ -7,17 +7,141 @@
 #ifndef __AT91_PMC_H__
 #define __AT91_PMC_H__
 
-#include <regmap.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
 
-struct pmc_platdata {
-	struct at91_pmc *reg_base;
-	struct regmap *regmap_sfr;
+/* Keep a range of 256 available clocks for every clock type. */
+#define AT91_TO_CLK_ID(_t, _i)		(((_t) << 8) | ((_i) & 0xff))
+#define AT91_CLK_ID_TO_DID(_i)		((_i) & 0xff)
+
+struct clk_range {
+	unsigned long min;
+	unsigned long max;
+};
+
+struct clk_master_layout {
+	u32 offset;
+	u32 mask;
+	u8 pres_shift;
 };
 
-int at91_pmc_core_probe(struct udevice *dev);
-int at91_clk_sub_device_bind(struct udevice *dev, const char *drv_name);
+extern const struct clk_master_layout at91rm9200_master_layout;
+extern const struct clk_master_layout at91sam9x5_master_layout;
+
+struct clk_master_characteristics {
+	struct clk_range output;
+	u32 divisors[4];
+	u8 have_div3_pres;
+};
+
+struct clk_pll_characteristics {
+	struct clk_range input;
+	int num_output;
+	const struct clk_range *output;
+	u16 *icpll;
+	u8 *out;
+	u8 upll : 1;
+};
+
+struct clk_pll_layout {
+	u32 pllr_mask;
+	u32 mul_mask;
+	u32 frac_mask;
+	u32 div_mask;
+	u32 endiv_mask;
+	u8 mul_shift;
+	u8 frac_shift;
+	u8 div_shift;
+	u8 endiv_shift;
+};
+
+struct clk_programmable_layout {
+	u8 pres_mask;
+	u8 pres_shift;
+	u8 css_mask;
+	u8 have_slck_mck;
+	u8 is_pres_direct;
+};
+
+struct clk_pcr_layout {
+	u32 offset;
+	u32 cmd;
+	u32 div_mask;
+	u32 gckcss_mask;
+	u32 pid_mask;
+};
+
+extern const struct clk_programmable_layout at91rm9200_programmable_layout;
+extern const struct clk_programmable_layout at91sam9g45_programmable_layout;
+extern const struct clk_programmable_layout at91sam9x5_programmable_layout;
+
+extern const struct clk_ops at91_clk_ops;
+
+struct clk *at91_clk_main_rc(void __iomem *reg, const char *name,
+			const char *parent_name);
+struct clk *at91_clk_main_osc(void __iomem *reg, const char *name,
+			const char *parent_name, bool bypass);
+struct clk *at91_clk_rm9200_main(void __iomem *reg, const char *name,
+			const char *parent_name);
+struct clk *at91_clk_sam9x5_main(void __iomem *reg, const char *name,
+			const char * const *parent_names, int num_parents,
+			const u32 *mux_table, int type);
+struct clk *
+sam9x60_clk_register_div_pll(void __iomem *base, const char *name,
+			const char *parent_name, u8 id,
+			const struct clk_pll_characteristics *characteristics,
+			const struct clk_pll_layout *layout, bool critical);
+struct clk *
+sam9x60_clk_register_frac_pll(void __iomem *base, const char *name,
+			const char *parent_name, u8 id,
+			const struct clk_pll_characteristics *characteristics,
+			const struct clk_pll_layout *layout, bool critical);
+struct clk *
+at91_clk_register_master(void __iomem *base, const char *name,
+			const char * const *parent_names, int num_parents,
+			const struct clk_master_layout *layout,
+			const struct clk_master_characteristics *characteristics,
+			const u32 *mux_table);
+struct clk *
+at91_clk_sama7g5_register_master(void __iomem *base, const char *name,
+			const char * const *parent_names, int num_parents,
+			const u32 *mux_table, const u32 *clk_mux_table,
+			bool critical, u8 id);
+struct clk *
+at91_clk_register_utmi(void __iomem *base, struct udevice *dev,
+			const char *name, const char *parent_name);
+struct clk *
+at91_clk_sama7g5_register_utmi(void __iomem *base, const char *name,
+			const char *parent_name);
+struct clk *
+at91_clk_register_programmable(void __iomem *base, const char *name,
+			const char * const *parent_names, u8 num_parents, u8 id,
+			const struct clk_programmable_layout *layout,
+			const u32 *clk_mux_table, const u32 *mux_table);
+struct clk *
+at91_clk_register_system(void __iomem *base, const char *name,
+			const char *parent_name, u8 id);
+struct clk *
+at91_clk_register_peripheral(void __iomem *base, const char *name,
+			const char *parent_name, u32 id);
+struct clk *
+at91_clk_register_sam9x5_peripheral(void __iomem *base,
+			const struct clk_pcr_layout *layout,
+			const char *name, const char *parent_name,
+			u32 id, const struct clk_range *range);
+struct clk *
+at91_clk_register_generic(void __iomem *base,
+			const struct clk_pcr_layout *layout, const char *name,
+			const char * const *parent_names,
+			const u32 *clk_mux_table, const u32 *mux_table,
+			u8 num_parents, u8 id, const struct clk_range *range);
+
+int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val);
+int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index);
 
-int at91_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args);
-int at91_clk_probe(struct udevice *dev);
+void pmc_read(void __iomem *base, unsigned int off, unsigned int *val);
+void pmc_write(void __iomem *base, unsigned int off, unsigned int val);
+void pmc_update_bits(void __iomem *base, unsigned int off, unsigned int mask,
+			unsigned int bits);
 
 #endif

+ 1401 - 0
drivers/clk/at91/sama7g5.c

@@ -0,0 +1,1401 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * SAMA7G5 PMC clock support.
+ *
+ * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
+ *
+ * Based on drivers/clk/at91/sama7g5.c from Linux.
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <dt-bindings/clk/at91.h>
+#include <linux/clk-provider.h>
+
+#include "pmc.h"
+
+/**
+ * Clock identifiers to be used in conjunction with macros like
+ * AT91_TO_CLK_ID()
+ *
+ * @ID_MD_SLCK:			TD slow clock identifier
+ * @ID_TD_SLCK:			MD slow clock identifier
+ * @ID_MAIN_XTAL:		Main Xtal clock identifier
+ * @ID_MAIN_RC:			Main RC clock identifier
+ * @ID_MAIN_RC_OSC:		Main RC Oscillator clock identifier
+ * @ID_MAIN_OSC:		Main Oscillator clock identifier
+ * @ID_MAINCK:			MAINCK clock identifier
+ * @ID_PLL_CPU_FRAC:		CPU PLL fractional clock identifier
+ * @ID_PLL_CPU_DIV:		CPU PLL divider clock identifier
+ * @ID_PLL_SYS_FRAC:		SYS PLL fractional clock identifier
+ * @ID_PLL_SYS_DIV:		SYS PLL divider clock identifier
+ * @ID_PLL_DDR_FRAC:		DDR PLL fractional clock identifier
+ * @ID_PLL_DDR_DIV:		DDR PLL divider clock identifier
+ * @ID_PLL_IMG_FRAC:		IMC PLL fractional clock identifier
+ * @ID_PLL_IMG_DIV:		IMG PLL divider clock identifier
+ * @ID_PLL_BAUD_FRAC:		Baud PLL fractional clock identifier
+ * @ID_PLL_BAUD_DIV:		Baud PLL divider clock identifier
+ * @ID_PLL_AUDIO_FRAC:		Audio PLL fractional clock identifier
+ * @ID_PLL_AUDIO_DIVPMC:	Audio PLL PMC divider clock identifier
+ * @ID_PLL_AUDIO_DIVIO:		Audio PLL IO divider clock identifier
+ * @ID_PLL_ETH_FRAC:		Ethernet PLL fractional clock identifier
+ * @ID_PLL_ETH_DIV:		Ethernet PLL divider clock identifier
+
+ * @ID_MCK0:			MCK0 clock identifier
+ * @ID_MCK1:			MCK1 clock identifier
+ * @ID_MCK2:			MCK2 clock identifier
+ * @ID_MCK3:			MCK3 clock identifier
+ * @ID_MCK4:			MCK4 clock identifier
+
+ * @ID_UTMI:			UTMI clock identifier
+
+ * @ID_PROG0:			Programmable 0 clock identifier
+ * @ID_PROG1:			Programmable 1 clock identifier
+ * @ID_PROG2:			Programmable 2 clock identifier
+ * @ID_PROG3:			Programmable 3 clock identifier
+ * @ID_PROG4:			Programmable 4 clock identifier
+ * @ID_PROG5:			Programmable 5 clock identifier
+ * @ID_PROG6:			Programmable 6 clock identifier
+ * @ID_PROG7:			Programmable 7 clock identifier
+
+ * @ID_PCK0:			System clock 0 clock identifier
+ * @ID_PCK1:			System clock 1 clock identifier
+ * @ID_PCK2:			System clock 2 clock identifier
+ * @ID_PCK3:			System clock 3 clock identifier
+ * @ID_PCK4:			System clock 4 clock identifier
+ * @ID_PCK5:			System clock 5 clock identifier
+ * @ID_PCK6:			System clock 6 clock identifier
+ * @ID_PCK7:			System clock 7 clock identifier
+ */
+enum pmc_clk_ids {
+	ID_MD_SLCK		= 0,
+	ID_TD_SLCK		= 1,
+	ID_MAIN_XTAL		= 2,
+	ID_MAIN_RC		= 3,
+	ID_MAIN_RC_OSC		= 4,
+	ID_MAIN_OSC		= 5,
+	ID_MAINCK		= 6,
+
+	ID_PLL_CPU_FRAC		= 7,
+	ID_PLL_CPU_DIV		= 8,
+	ID_PLL_SYS_FRAC		= 9,
+	ID_PLL_SYS_DIV		= 10,
+	ID_PLL_DDR_FRAC		= 11,
+	ID_PLL_DDR_DIV		= 12,
+	ID_PLL_IMG_FRAC		= 13,
+	ID_PLL_IMG_DIV		= 14,
+	ID_PLL_BAUD_FRAC	= 15,
+	ID_PLL_BAUD_DIV		= 16,
+	ID_PLL_AUDIO_FRAC	= 17,
+	ID_PLL_AUDIO_DIVPMC	= 18,
+	ID_PLL_AUDIO_DIVIO	= 19,
+	ID_PLL_ETH_FRAC		= 20,
+	ID_PLL_ETH_DIV		= 21,
+
+	ID_MCK0			= 22,
+	ID_MCK1			= 23,
+	ID_MCK2			= 24,
+	ID_MCK3			= 25,
+	ID_MCK4			= 26,
+
+	ID_UTMI			= 27,
+
+	ID_PROG0		= 28,
+	ID_PROG1		= 29,
+	ID_PROG2		= 30,
+	ID_PROG3		= 31,
+	ID_PROG4		= 32,
+	ID_PROG5		= 33,
+	ID_PROG6		= 34,
+	ID_PROG7		= 35,
+
+	ID_PCK0			= 36,
+	ID_PCK1			= 37,
+	ID_PCK2			= 38,
+	ID_PCK3			= 39,
+	ID_PCK4			= 40,
+	ID_PCK5			= 41,
+	ID_PCK6			= 42,
+	ID_PCK7			= 43,
+
+	ID_MAX,
+};
+
+/**
+ * PLL type identifiers
+ * @PLL_TYPE_FRAC:	fractional PLL identifier
+ * @PLL_TYPE_DIV:	divider PLL identifier
+ */
+enum pll_type {
+	PLL_TYPE_FRAC,
+	PLL_TYPE_DIV,
+};
+
+/* Clock names used as parents for multiple clocks. */
+static const char *clk_names[] = {
+	[ID_MAIN_RC_OSC]	= "main_rc_osc",
+	[ID_MAIN_OSC]		= "main_osc",
+	[ID_MAINCK]		= "mainck",
+	[ID_PLL_CPU_DIV]	= "cpupll_divpmcck",
+	[ID_PLL_SYS_DIV]	= "syspll_divpmcck",
+	[ID_PLL_DDR_DIV]	= "ddrpll_divpmcck",
+	[ID_PLL_IMG_DIV]	= "imgpll_divpmcck",
+	[ID_PLL_BAUD_DIV]	= "baudpll_divpmcck",
+	[ID_PLL_AUDIO_DIVPMC]	= "audiopll_divpmcck",
+	[ID_PLL_AUDIO_DIVIO]	= "audiopll_diviock",
+	[ID_PLL_ETH_DIV]	= "ethpll_divpmcck",
+	[ID_MCK0]		= "mck0",
+};
+
+/* Fractional PLL output range. */
+static const struct clk_range pll_outputs[] = {
+	{ .min = 2343750, .max = 1200000000 },
+};
+
+/* PLL characteristics. */
+static const struct clk_pll_characteristics pll_characteristics = {
+	.input = { .min = 12000000, .max = 50000000 },
+	.num_output = ARRAY_SIZE(pll_outputs),
+	.output = pll_outputs,
+};
+
+/* Layout for fractional PLLs. */
+static const struct clk_pll_layout pll_layout_frac = {
+	.mul_mask	= GENMASK(31, 24),
+	.frac_mask	= GENMASK(21, 0),
+	.mul_shift	= 24,
+	.frac_shift	= 0,
+};
+
+/* Layout for DIVPMC dividers. */
+static const struct clk_pll_layout pll_layout_divpmc = {
+	.div_mask	= GENMASK(7, 0),
+	.endiv_mask	= BIT(29),
+	.div_shift	= 0,
+	.endiv_shift	= 29,
+};
+
+/* Layout for DIVIO dividers. */
+static const struct clk_pll_layout pll_layout_divio = {
+	.div_mask	= GENMASK(19, 12),
+	.endiv_mask	= BIT(30),
+	.div_shift	= 12,
+	.endiv_shift	= 30,
+};
+
+/* MCK0 characteristics. */
+static const struct clk_master_characteristics mck0_characteristics = {
+	.output = { .min = 140000000, .max = 200000000 },
+	.divisors = { 1, 2, 4, 3 },
+	.have_div3_pres = 1,
+};
+
+/* MCK0 layout. */
+static const struct clk_master_layout mck0_layout = {
+	.mask = 0x373,
+	.pres_shift = 4,
+	.offset = 0x28,
+};
+
+/* Programmable clock layout. */
+static const struct clk_programmable_layout programmable_layout = {
+	.pres_mask = 0xff,
+	.pres_shift = 8,
+	.css_mask = 0x1f,
+	.have_slck_mck = 0,
+	.is_pres_direct = 1,
+};
+
+/* Peripheral clock layout. */
+static const struct clk_pcr_layout sama7g5_pcr_layout = {
+	.offset = 0x88,
+	.cmd = BIT(31),
+	.gckcss_mask = GENMASK(12, 8),
+	.pid_mask = GENMASK(6, 0),
+	.div_mask = GENMASK(15, 14),
+};
+
+/**
+ * PLL clocks description
+ * @n:		clock name
+ * @p:		clock parent
+ * @l:		clock layout
+ * @t:		clock type
+ * @c:		true if clock is critical and cannot be disabled
+ * @id:		clock id corresponding to PLL driver
+ * @cid:	clock id corresponding to clock subsystem
+ */
+static const struct {
+	const char *n;
+	const char *p;
+	const struct clk_pll_layout *l;
+	u8 t;
+	u8 c;
+	u8 id;
+	u8 cid;
+} sama7g5_plls[] = {
+	{
+		.n = "cpupll_fracck",
+		.p = "mainck",
+		.l = &pll_layout_frac,
+		.t = PLL_TYPE_FRAC,
+		.c = 1,
+		.id = 0,
+		.cid = ID_PLL_CPU_FRAC,
+	},
+
+	{
+		.n = "cpupll_divpmcck",
+		.p = "cpupll_fracck",
+		.l = &pll_layout_divpmc,
+		.t = PLL_TYPE_DIV,
+		.c = 1,
+		.id = 0,
+		.cid = ID_PLL_CPU_DIV,
+	},
+
+	{
+		.n = "syspll_fracck",
+		.p = "mainck",
+		.l = &pll_layout_frac,
+		.t = PLL_TYPE_FRAC,
+		.c = 1,
+		.id = 1,
+		.cid = ID_PLL_SYS_FRAC,
+	},
+
+	{
+		.n = "syspll_divpmcck",
+		.p = "syspll_fracck",
+		.l = &pll_layout_divpmc,
+		.t = PLL_TYPE_DIV,
+		.c = 1,
+		.id = 1,
+		.cid = ID_PLL_SYS_DIV,
+	},
+
+	{
+		.n = "ddrpll_fracck",
+		.p = "mainck",
+		.l = &pll_layout_frac,
+		.t = PLL_TYPE_FRAC,
+		.c = 1,
+		.id = 2,
+		.cid = ID_PLL_DDR_FRAC,
+	},
+
+	{
+		.n = "ddrpll_divpmcck",
+		.p = "ddrpll_fracck",
+		.l = &pll_layout_divpmc,
+		.t = PLL_TYPE_DIV,
+		.c = 1,
+		.id = 2,
+		.cid = ID_PLL_DDR_DIV,
+	},
+
+	{
+		.n = "imgpll_fracck",
+		.p = "mainck",
+		.l = &pll_layout_frac,
+		.t = PLL_TYPE_FRAC,
+		.id = 3,
+		.cid = ID_PLL_IMG_FRAC,
+	},
+
+	{
+		.n = "imgpll_divpmcck",
+		.p = "imgpll_fracck",
+		.l = &pll_layout_divpmc,
+		.t = PLL_TYPE_DIV,
+		.id = 3,
+		.cid = ID_PLL_IMG_DIV
+	},
+
+	{
+		.n = "baudpll_fracck",
+		.p = "mainck",
+		.l = &pll_layout_frac,
+		.t = PLL_TYPE_FRAC,
+		.id = 4,
+		.cid = ID_PLL_BAUD_FRAC,
+	},
+
+	{
+		.n = "baudpll_divpmcck",
+		.p = "baudpll_fracck",
+		.l = &pll_layout_divpmc,
+		.t = PLL_TYPE_DIV,
+		.id = 4,
+		.cid = ID_PLL_BAUD_DIV,
+	},
+
+	{
+		.n = "audiopll_fracck",
+		.p = "main_osc",
+		.l = &pll_layout_frac,
+		.t = PLL_TYPE_FRAC,
+		.id = 5,
+		.cid = ID_PLL_AUDIO_FRAC,
+	},
+
+	{
+		.n = "audiopll_divpmcck",
+		.p = "audiopll_fracck",
+		.l = &pll_layout_divpmc,
+		.t = PLL_TYPE_DIV,
+		.id = 5,
+		.cid = ID_PLL_AUDIO_DIVPMC,
+	},
+
+	{
+		.n = "audiopll_diviock",
+		.p = "audiopll_fracck",
+		.l = &pll_layout_divio,
+		.t = PLL_TYPE_DIV,
+		.id = 5,
+		.cid = ID_PLL_AUDIO_DIVIO,
+	},
+
+	{
+		.n = "ethpll_fracck",
+		.p = "main_osc",
+		.l = &pll_layout_frac,
+		.t = PLL_TYPE_FRAC,
+		.id = 6,
+		.cid = ID_PLL_ETH_FRAC,
+	},
+
+	{
+		.n = "ethpll_divpmcck",
+		.p = "ethpll_fracck",
+		.l = &pll_layout_divpmc,
+		.t = PLL_TYPE_DIV,
+		.id = 6,
+		.cid = ID_PLL_ETH_DIV,
+	},
+};
+
+/**
+ * Master clock (MCK[1..4]) description
+ * @n:			clock name
+ * @ep:			extra parents names array
+ * @ep_chg_chg_id:	index in parents array that specifies the changeable
+ *			parent
+ * @ep_count:		extra parents count
+ * @ep_mux_table:	mux table for extra parents
+ * @ep_clk_mux_table:	mux table to deal with subsystem clock ids
+ * @id:			clock id corresponding to MCK driver
+ * @cid:		clock id corresponding to clock subsystem
+ * @c:			true if clock is critical and cannot be disabled
+ */
+static const struct {
+	const char *n;
+	const char *ep[4];
+	u8 ep_count;
+	u8 ep_mux_table[4];
+	u8 ep_clk_mux_table[4];
+	u8 id;
+	u8 cid;
+	u8 c;
+} sama7g5_mckx[] = {
+	{
+		.n = "mck1",
+		.id = 1,
+		.cid = ID_MCK1,
+		.ep = { "syspll_divpmcck", },
+		.ep_mux_table = { 5, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, },
+		.ep_count = 1,
+		.c = 1,
+	},
+
+	{
+		.n = "mck2",
+		.id = 2,
+		.cid = ID_MCK2,
+		.ep = { "ddrpll_divpmcck", },
+		.ep_mux_table = { 6, },
+		.ep_clk_mux_table = { ID_PLL_DDR_DIV, },
+		.ep_count = 1,
+		.c = 1,
+	},
+
+	{
+		.n = "mck3",
+		.id = 3,
+		.cid = ID_MCK3,
+		.ep = { "syspll_divpmcck", "ddrpll_divpmcck", "imgpll_divpmcck", },
+		.ep_mux_table = { 5, 6, 7, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_DDR_DIV, ID_PLL_IMG_DIV, },
+		.ep_count = 3,
+	},
+
+	{
+		.n = "mck4",
+		.id = 4,
+		.cid = ID_MCK4,
+		.ep = { "syspll_divpmcck", },
+		.ep_mux_table = { 5, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, },
+		.ep_count = 1,
+		.c = 1,
+	},
+};
+
+/**
+ * Programmable clock description
+ * @n:			clock name
+ * @cid:		clock id corresponding to clock subsystem
+ */
+static const struct {
+	const char *n;
+	u8 cid;
+} sama7g5_prog[] = {
+	{ .n = "prog0", .cid = ID_PROG0, },
+	{ .n = "prog1", .cid = ID_PROG1, },
+	{ .n = "prog2", .cid = ID_PROG2, },
+	{ .n = "prog3", .cid = ID_PROG3, },
+	{ .n = "prog4", .cid = ID_PROG4, },
+	{ .n = "prog5", .cid = ID_PROG5, },
+	{ .n = "prog6", .cid = ID_PROG6, },
+	{ .n = "prog7", .cid = ID_PROG7, },
+};
+
+/* Mux table for programmable clocks. */
+static u32 sama7g5_prog_mux_table[] = { 0, 1, 2, 3, 5, 6, 7, 8, 9, 10, };
+
+/**
+ * System clock description
+ * @n:			clock name
+ * @p:			parent clock name
+ * @id:			clock id corresponding to system clock driver
+ * @cid:		clock id corresponding to clock subsystem
+ */
+static const struct {
+	const char *n;
+	const char *p;
+	u8 id;
+	u8 cid;
+} sama7g5_systemck[] = {
+	{ .n = "pck0", .p = "prog0", .id = 8, .cid = ID_PCK0, },
+	{ .n = "pck1", .p = "prog1", .id = 9, .cid = ID_PCK1, },
+	{ .n = "pck2", .p = "prog2", .id = 10, .cid = ID_PCK2, },
+	{ .n = "pck3", .p = "prog3", .id = 11, .cid = ID_PCK3, },
+	{ .n = "pck4", .p = "prog4", .id = 12, .cid = ID_PCK4, },
+	{ .n = "pck5", .p = "prog5", .id = 13, .cid = ID_PCK5, },
+	{ .n = "pck6", .p = "prog6", .id = 14, .cid = ID_PCK6, },
+	{ .n = "pck7", .p = "prog7", .id = 15, .cid = ID_PCK7, },
+};
+
+/**
+ * Peripheral clock description
+ * @n:		clock name
+ * @p:		clock parent name
+ * @r:		clock range values
+ * @id:		clock id
+ */
+static const struct {
+	const char *n;
+	const char *p;
+	struct clk_range r;
+	u8 id;
+} sama7g5_periphck[] = {
+	{ .n = "pioA_clk",	.p = "mck0", .id = 11, },
+	{ .n = "sfr_clk",	.p = "mck1", .id = 19, },
+	{ .n = "hsmc_clk",	.p = "mck1", .id = 21, },
+	{ .n = "xdmac0_clk",	.p = "mck1", .id = 22, },
+	{ .n = "xdmac1_clk",	.p = "mck1", .id = 23, },
+	{ .n = "xdmac2_clk",	.p = "mck1", .id = 24, },
+	{ .n = "acc_clk",	.p = "mck1", .id = 25, },
+	{ .n = "aes_clk",	.p = "mck1", .id = 27, },
+	{ .n = "tzaesbasc_clk",	.p = "mck1", .id = 28, },
+	{ .n = "asrc_clk",	.p = "mck1", .id = 30, .r = { .max = 200000000, }, },
+	{ .n = "cpkcc_clk",	.p = "mck0", .id = 32, },
+	{ .n = "csi_clk",	.p = "mck3", .id = 33, .r = { .max = 266000000, }, },
+	{ .n = "csi2dc_clk",	.p = "mck3", .id = 34, .r = { .max = 266000000, }, },
+	{ .n = "eic_clk",	.p = "mck1", .id = 37, },
+	{ .n = "flex0_clk",	.p = "mck1", .id = 38, },
+	{ .n = "flex1_clk",	.p = "mck1", .id = 39, },
+	{ .n = "flex2_clk",	.p = "mck1", .id = 40, },
+	{ .n = "flex3_clk",	.p = "mck1", .id = 41, },
+	{ .n = "flex4_clk",	.p = "mck1", .id = 42, },
+	{ .n = "flex5_clk",	.p = "mck1", .id = 43, },
+	{ .n = "flex6_clk",	.p = "mck1", .id = 44, },
+	{ .n = "flex7_clk",	.p = "mck1", .id = 45, },
+	{ .n = "flex8_clk",	.p = "mck1", .id = 46, },
+	{ .n = "flex9_clk",	.p = "mck1", .id = 47, },
+	{ .n = "flex10_clk",	.p = "mck1", .id = 48, },
+	{ .n = "flex11_clk",	.p = "mck1", .id = 49, },
+	{ .n = "gmac0_clk",	.p = "mck1", .id = 51, },
+	{ .n = "gmac1_clk",	.p = "mck1", .id = 52, },
+	{ .n = "gmac0_tsu_clk",	.p = "mck1", .id = 53, },
+	{ .n = "gmac1_tsu_clk",	.p = "mck1", .id = 54, },
+	{ .n = "icm_clk",	.p = "mck1", .id = 55, },
+	{ .n = "isc_clk",	.p = "mck3", .id = 56, .r = { .max = 266000000, }, },
+	{ .n = "i2smcc0_clk",	.p = "mck1", .id = 57, .r = { .max = 200000000, }, },
+	{ .n = "i2smcc1_clk",	.p = "mck1", .id = 58, .r = { .max = 200000000, }, },
+	{ .n = "matrix_clk",	.p = "mck1", .id = 60, },
+	{ .n = "mcan0_clk",	.p = "mck1", .id = 61, .r = { .max = 200000000, }, },
+	{ .n = "mcan1_clk",	.p = "mck1", .id = 62, .r = { .max = 200000000, }, },
+	{ .n = "mcan2_clk",	.p = "mck1", .id = 63, .r = { .max = 200000000, }, },
+	{ .n = "mcan3_clk",	.p = "mck1", .id = 64, .r = { .max = 200000000, }, },
+	{ .n = "mcan4_clk",	.p = "mck1", .id = 65, .r = { .max = 200000000, }, },
+	{ .n = "mcan5_clk",	.p = "mck1", .id = 66, .r = { .max = 200000000, }, },
+	{ .n = "pdmc0_clk",	.p = "mck1", .id = 68, .r = { .max = 200000000, }, },
+	{ .n = "pdmc1_clk",	.p = "mck1", .id = 69, .r = { .max = 200000000, }, },
+	{ .n = "pit64b0_clk",	.p = "mck1", .id = 70, },
+	{ .n = "pit64b1_clk",	.p = "mck1", .id = 71, },
+	{ .n = "pit64b2_clk",	.p = "mck1", .id = 72, },
+	{ .n = "pit64b3_clk",	.p = "mck1", .id = 73, },
+	{ .n = "pit64b4_clk",	.p = "mck1", .id = 74, },
+	{ .n = "pit64b5_clk",	.p = "mck1", .id = 75, },
+	{ .n = "pwm_clk",	.p = "mck1", .id = 77, },
+	{ .n = "qspi0_clk",	.p = "mck1", .id = 78, },
+	{ .n = "qspi1_clk",	.p = "mck1", .id = 79, },
+	{ .n = "sdmmc0_clk",	.p = "mck1", .id = 80, },
+	{ .n = "sdmmc1_clk",	.p = "mck1", .id = 81, },
+	{ .n = "sdmmc2_clk",	.p = "mck1", .id = 82, },
+	{ .n = "sha_clk",	.p = "mck1", .id = 83, },
+	{ .n = "spdifrx_clk",	.p = "mck1", .id = 84, .r = { .max = 200000000, }, },
+	{ .n = "spdiftx_clk",	.p = "mck1", .id = 85, .r = { .max = 200000000, }, },
+	{ .n = "ssc0_clk",	.p = "mck1", .id = 86, .r = { .max = 200000000, }, },
+	{ .n = "ssc1_clk",	.p = "mck1", .id = 87, .r = { .max = 200000000, }, },
+	{ .n = "tcb0_ch0_clk",	.p = "mck1", .id = 88, .r = { .max = 200000000, }, },
+	{ .n = "tcb0_ch1_clk",	.p = "mck1", .id = 89, .r = { .max = 200000000, }, },
+	{ .n = "tcb0_ch2_clk",	.p = "mck1", .id = 90, .r = { .max = 200000000, }, },
+	{ .n = "tcb1_ch0_clk",	.p = "mck1", .id = 91, .r = { .max = 200000000, }, },
+	{ .n = "tcb1_ch1_clk",	.p = "mck1", .id = 92, .r = { .max = 200000000, }, },
+	{ .n = "tcb1_ch2_clk",	.p = "mck1", .id = 93, .r = { .max = 200000000, }, },
+	{ .n = "tcpca_clk",	.p = "mck1", .id = 94, },
+	{ .n = "tcpcb_clk",	.p = "mck1", .id = 95, },
+	{ .n = "tdes_clk",	.p = "mck1", .id = 96, },
+	{ .n = "trng_clk",	.p = "mck1", .id = 97, },
+	{ .n = "udphsa_clk",	.p = "mck1", .id = 104, },
+	{ .n = "udphsb_clk",	.p = "mck1", .id = 105, },
+	{ .n = "uhphs_clk",	.p = "mck1", .id = 106, },
+};
+
+/**
+ * Generic clock description
+ * @n:			clock name
+ * @ep:			extra parents names
+ * @ep_mux_table:	extra parents mux table
+ * @ep_clk_mux_table:	extra parents clock mux table (for CCF)
+ * @r:			clock output range
+ * @ep_count:		extra parents count
+ * @id:			clock id
+ */
+static const struct {
+	const char *n;
+	const char *ep[8];
+	const char ep_mux_table[8];
+	const char ep_clk_mux_table[8];
+	struct clk_range r;
+	u8 ep_count;
+	u8 id;
+} sama7g5_gck[] = {
+	{
+		.n  = "adc_gclk",
+		.id = 26,
+		.r = { .max = 100000000, },
+		.ep = { "syspll_divpmcck", "imgpll_divpmcck", "audiopll_divpmcck", },
+		.ep_mux_table = { 5, 7, 9, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV,
+				      ID_PLL_AUDIO_DIVPMC, },
+		.ep_count = 3,
+	},
+
+	{
+		.n  = "asrc_gclk",
+		.id = 30,
+		.r = { .max = 200000000 },
+		.ep = { "audiopll_divpmcck", },
+		.ep_mux_table = { 9, },
+		.ep_clk_mux_table = { ID_PLL_AUDIO_DIVPMC, },
+		.ep_count = 1,
+	},
+
+	{
+		.n  = "csi_gclk",
+		.id = 33,
+		.r = { .max = 27000000  },
+		.ep = { "ddrpll_divpmcck", "imgpll_divpmcck", },
+		.ep_clk_mux_table = { ID_PLL_DDR_DIV, ID_PLL_IMG_DIV, },
+		.ep_mux_table = { 6, 7, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "flex0_gclk",
+		.id = 38,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "flex1_gclk",
+		.id = 39,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "flex2_gclk",
+		.id = 40,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "flex3_gclk",
+		.id = 41,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "flex4_gclk",
+		.id = 42,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "flex5_gclk",
+		.id = 43,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "flex6_gclk",
+		.id = 44,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "flex7_gclk",
+		.id = 45,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "flex8_gclk",
+		.id = 46,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "flex9_gclk",
+		.id = 47,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "flex10_gclk",
+		.id = 48,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "flex11_gclk",
+		.id = 49,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "gmac0_gclk",
+		.id = 51,
+		.r = { .max = 125000000 },
+		.ep = { "ethpll_divpmcck", },
+		.ep_clk_mux_table = { ID_PLL_ETH_DIV, },
+		.ep_mux_table = { 10, },
+		.ep_count = 1,
+	},
+
+	{
+		.n  = "gmac1_gclk",
+		.id = 52,
+		.r = { .max = 50000000  },
+		.ep = { "ethpll_divpmcck", },
+		.ep_mux_table = { 10, },
+		.ep_clk_mux_table = { ID_PLL_ETH_DIV, },
+		.ep_count = 1,
+	},
+
+	{
+		.n  = "gmac0_tsu_gclk",
+		.id = 53,
+		.r = { .max = 300000000 },
+		.ep = { "audiopll_divpmcck", "ethpll_divpmcck", },
+		.ep_mux_table = { 9, 10, },
+		.ep_clk_mux_table = { ID_PLL_AUDIO_DIVPMC, ID_PLL_ETH_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "gmac1_tsu_gclk",
+		.id = 54,
+		.r = { .max = 300000000 },
+		.ep = { "audiopll_divpmcck", "ethpll_divpmcck", },
+		.ep_mux_table = { 9, 10, },
+		.ep_clk_mux_table = { ID_PLL_AUDIO_DIVPMC, ID_PLL_ETH_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "i2smcc0_gclk",
+		.id = 57,
+		.r = { .max = 100000000 },
+		.ep = { "syspll_divpmcck", "audiopll_divpmcck", },
+		.ep_mux_table = { 5, 9, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_AUDIO_DIVPMC, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "i2smcc1_gclk",
+		.id = 58,
+		.r = { .max = 100000000 },
+		.ep = { "syspll_divpmcck", "audiopll_divpmcck", },
+		.ep_mux_table = { 5, 9, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_AUDIO_DIVPMC, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "mcan0_gclk",
+		.id = 61,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "mcan1_gclk",
+		.id = 62,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "mcan2_gclk",
+		.id = 63,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "mcan3_gclk",
+		.id = 64,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "mcan4_gclk",
+		.id = 65,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "mcan5_gclk",
+		.id = 66,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "pdmc0_gclk",
+		.id = 68,
+		.r = { .max = 50000000  },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "pdmc1_gclk",
+		.id = 69,
+		.r = { .max = 50000000, },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "pit64b0_gclk",
+		.id = 70,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+			"audiopll_divpmcck", "ethpll_divpmcck", },
+		.ep_mux_table = { 5, 7, 8, 9, 10, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV,
+				      ID_PLL_BAUD_DIV, ID_PLL_AUDIO_DIVPMC,
+				      ID_PLL_ETH_DIV, },
+		.ep_count = 5,
+	},
+
+	{
+		.n  = "pit64b1_gclk",
+		.id = 71,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+			"audiopll_divpmcck", "ethpll_divpmcck", },
+		.ep_mux_table = { 5, 7, 8, 9, 10, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV,
+				      ID_PLL_BAUD_DIV, ID_PLL_AUDIO_DIVPMC,
+				      ID_PLL_ETH_DIV, },
+		.ep_count = 5,
+	},
+
+	{
+		.n  = "pit64b2_gclk",
+		.id = 72,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+			"audiopll_divpmcck", "ethpll_divpmcck", },
+		.ep_mux_table = { 5, 7, 8, 9, 10, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV,
+				      ID_PLL_BAUD_DIV, ID_PLL_AUDIO_DIVPMC,
+				      ID_PLL_ETH_DIV, },
+		.ep_count = 5,
+	},
+
+	{
+		.n  = "pit64b3_gclk",
+		.id = 73,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+			"audiopll_divpmcck", "ethpll_divpmcck", },
+		.ep_mux_table = { 5, 7, 8, 9, 10, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV,
+				      ID_PLL_BAUD_DIV, ID_PLL_AUDIO_DIVPMC,
+				      ID_PLL_ETH_DIV, },
+		.ep_count = 5,
+	},
+
+	{
+		.n  = "pit64b4_gclk",
+		.id = 74,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+			"audiopll_divpmcck", "ethpll_divpmcck", },
+		.ep_mux_table = { 5, 7, 8, 9, 10, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV,
+				      ID_PLL_BAUD_DIV, ID_PLL_AUDIO_DIVPMC,
+				      ID_PLL_ETH_DIV, },
+		.ep_count = 5,
+	},
+
+	{
+		.n  = "pit64b5_gclk",
+		.id = 75,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+			"audiopll_divpmcck", "ethpll_divpmcck", },
+		.ep_mux_table = { 5, 7, 8, 9, 10, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV,
+				      ID_PLL_BAUD_DIV, ID_PLL_AUDIO_DIVPMC,
+				      ID_PLL_ETH_DIV, },
+		.ep_count = 5,
+	},
+
+	{
+		.n  = "qspi0_gclk",
+		.id = 78,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV,
+				      ID_PLL_BAUD_DIV, ID_PLL_AUDIO_DIVPMC,
+				      ID_PLL_ETH_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "qspi1_gclk",
+		.id = 79,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "sdmmc0_gclk",
+		.id = 80,
+		.r = { .max = 208000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "sdmmc1_gclk",
+		.id = 81,
+		.r = { .max = 208000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "sdmmc2_gclk",
+		.id = 82,
+		.r = { .max = 208000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "spdifrx_gclk",
+		.id = 84,
+		.r = { .max = 150000000 },
+		.ep = { "syspll_divpmcck", "audiopll_divpmcck", },
+		.ep_mux_table = { 5, 9, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_AUDIO_DIVPMC, },
+		.ep_count = 2,
+	},
+
+	{
+		.n = "spdiftx_gclk",
+		.id = 85,
+		.r = { .max = 25000000  },
+		.ep = { "syspll_divpmcck", "audiopll_divpmcck", },
+		.ep_mux_table = { 5, 9, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_AUDIO_DIVPMC, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "tcb0_ch0_gclk",
+		.id = 88,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+			"audiopll_divpmcck", "ethpll_divpmcck", },
+		.ep_mux_table = { 5, 7, 8, 9, 10, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV,
+				      ID_PLL_BAUD_DIV, ID_PLL_AUDIO_DIVPMC,
+				      ID_PLL_ETH_DIV, },
+		.ep_count = 5,
+	},
+
+	{
+		.n  = "tcb1_ch0_gclk",
+		.id = 91,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+			"audiopll_divpmcck", "ethpll_divpmcck", },
+		.ep_mux_table = { 5, 7, 8, 9, 10, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV,
+				      ID_PLL_BAUD_DIV, ID_PLL_AUDIO_DIVPMC,
+				      ID_PLL_ETH_DIV, },
+		.ep_count = 5,
+	},
+};
+
+/**
+ * Clock setup description
+ * @cid:	clock id corresponding to clock subsystem
+ * @pid:	parent clock id corresponding to clock subsystem
+ * @rate:	clock rate
+ * @prate:	parent rate
+ */
+static const struct pmc_clk_setup {
+	unsigned int cid;
+	unsigned int pid;
+	unsigned long rate;
+	unsigned long prate;
+} sama7g5_clk_setup[] = {
+	{
+		.cid = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_ETH_FRAC),
+		.rate = 625000000,
+	},
+
+	{
+		.cid = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_ETH_DIV),
+		.rate = 625000000,
+	},
+};
+
+#define SAMA7G5_MAX_MUX_ALLOCS		(64)
+
+#define prepare_mux_table(_allocs, _index, _dst, _src, _num, _label)	\
+	do {								\
+		int _i;							\
+		if ((_index) >= SAMA7G5_MAX_MUX_ALLOCS) {		\
+			debug("%s(): AT91: MUX: insufficient space\n",	\
+			      __func__);				\
+			goto _label;					\
+		}							\
+		(_dst) = kzalloc(sizeof(*(_dst)) * (_num), GFP_KERNEL);	\
+		if (!(_dst))						\
+			goto _label;					\
+		(_allocs)[(_index)++] = (_dst);				\
+		for (_i = 0; _i < (_num); _i++)				\
+			(_dst)[_i] = (_src)[_i];			\
+	} while (0)
+
+static int sama7g5_clk_probe(struct udevice *dev)
+{
+	void __iomem *base = (void *)devfdt_get_addr(dev);
+	unsigned int *clkmuxallocs[SAMA7G5_MAX_MUX_ALLOCS];
+	unsigned int *muxallocs[SAMA7G5_MAX_MUX_ALLOCS];
+	const char *p[10];
+	unsigned int cm[10], m[10], *tmpclkmux, *tmpmux;
+	struct clk clk, *c, *parent;
+	bool main_osc_bypass;
+	int ret, muxallocindex = 0, clkmuxallocindex = 0, i, j;
+
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	memset(muxallocs,    0, ARRAY_SIZE(muxallocs));
+	memset(clkmuxallocs, 0, ARRAY_SIZE(clkmuxallocs));
+
+	ret = clk_get_by_index(dev, 0, &clk);
+	if (ret)
+		return ret;
+	ret = clk_get_by_id(clk.id, &c);
+	if (ret)
+		return ret;
+	clk_names[ID_TD_SLCK] = kmemdup(clk_hw_get_name(c),
+		strlen(clk_hw_get_name(c)) + 1, GFP_KERNEL);
+	if (!clk_names[ID_TD_SLCK])
+		return -ENOMEM;
+
+	ret = clk_get_by_index(dev, 1, &clk);
+	if (ret)
+		return ret;
+	ret = clk_get_by_id(clk.id, &c);
+	if (ret)
+		return ret;
+	clk_names[ID_MD_SLCK] = kmemdup(clk_hw_get_name(c),
+		strlen(clk_hw_get_name(c)) + 1, GFP_KERNEL);
+	if (!clk_names[ID_MD_SLCK])
+		return -ENOMEM;
+
+	ret = clk_get_by_index(dev, 2, &clk);
+	if (ret)
+		return ret;
+	clk_names[ID_MAIN_XTAL] = kmemdup(clk_hw_get_name(&clk),
+		strlen(clk_hw_get_name(&clk)) + 1, GFP_KERNEL);
+	if (!clk_names[ID_MAIN_XTAL])
+		return -ENOMEM;
+
+	ret = clk_get_by_index(dev, 3, &clk);
+	if (ret)
+		goto fail;
+	clk_names[ID_MAIN_RC] = kmemdup(clk_hw_get_name(&clk),
+		strlen(clk_hw_get_name(&clk)) + 1, GFP_KERNEL);
+	if (ret)
+		goto fail;
+
+	main_osc_bypass = dev_read_bool(dev, "atmel,main-osc-bypass");
+
+	/* Register main rc oscillator. */
+	clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAIN_RC_OSC),
+		at91_clk_main_rc(base, clk_names[ID_MAIN_RC_OSC],
+		clk_names[ID_MAIN_RC]));
+
+	/* Register main oscillator. */
+	clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAIN_OSC),
+		at91_clk_main_osc(base, clk_names[ID_MAIN_OSC],
+		clk_names[ID_MAIN_XTAL], main_osc_bypass));
+
+	/* Register mainck. */
+	p[0] = clk_names[ID_MAIN_RC_OSC];
+	p[1] = clk_names[ID_MAIN_OSC];
+	cm[0] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAIN_RC_OSC);
+	cm[1] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAIN_OSC);
+	prepare_mux_table(clkmuxallocs, clkmuxallocindex, tmpclkmux, cm, 2,
+			  fail);
+	clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAINCK),
+		at91_clk_sam9x5_main(base, clk_names[ID_MAINCK], p,
+		2, tmpclkmux, PMC_TYPE_CORE));
+
+	/* Register PLL fracs clocks. */
+	for (i = 0; i < ARRAY_SIZE(sama7g5_plls); i++) {
+		if (sama7g5_plls[i].t != PLL_TYPE_FRAC)
+			continue;
+
+		clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, sama7g5_plls[i].cid),
+			sam9x60_clk_register_frac_pll(base, sama7g5_plls[i].n,
+			sama7g5_plls[i].p, sama7g5_plls[i].id,
+			&pll_characteristics, sama7g5_plls[i].l,
+			sama7g5_plls[i].c));
+	}
+
+	/* Register PLL div clocks. */
+	for (i = 0; i < ARRAY_SIZE(sama7g5_plls); i++) {
+		if (sama7g5_plls[i].t != PLL_TYPE_DIV)
+			continue;
+
+		clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, sama7g5_plls[i].cid),
+			sam9x60_clk_register_div_pll(base, sama7g5_plls[i].n,
+			sama7g5_plls[i].p, sama7g5_plls[i].id,
+			&pll_characteristics, sama7g5_plls[i].l,
+			sama7g5_plls[i].c));
+	}
+
+	/* Register MCK0 clock. */
+	p[0] = clk_names[ID_MD_SLCK];
+	p[1] = clk_names[ID_MAINCK];
+	p[2] = clk_names[ID_PLL_CPU_DIV];
+	p[3] = clk_names[ID_PLL_SYS_DIV];
+	cm[0] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MD_SLCK);
+	cm[1] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAINCK);
+	cm[2] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_CPU_DIV);
+	cm[3] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_SYS_DIV);
+	prepare_mux_table(clkmuxallocs, clkmuxallocindex, tmpclkmux, cm, 2,
+			  fail);
+	clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK0),
+		at91_clk_register_master(base, clk_names[ID_MCK0], p,
+		4, &mck0_layout, &mck0_characteristics, tmpclkmux));
+
+	/* Register MCK1-4 clocks. */
+	p[0] = clk_names[ID_MD_SLCK];
+	p[1] = clk_names[ID_TD_SLCK];
+	p[2] = clk_names[ID_MAINCK];
+	p[3] = clk_names[ID_MCK0];
+	m[0] = 0;
+	m[1] = 1;
+	m[2] = 2;
+	m[3] = 3;
+	cm[0] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MD_SLCK);
+	cm[1] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_TD_SLCK);
+	cm[2] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAINCK);
+	cm[3] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK0);
+	for (i = 0; i < ARRAY_SIZE(sama7g5_mckx); i++) {
+		for (j = 0; j < sama7g5_mckx[i].ep_count; j++) {
+			p[4 + j] = sama7g5_mckx[i].ep[j];
+			m[4 + j] = sama7g5_mckx[i].ep_mux_table[j];
+			cm[4 + j] = AT91_TO_CLK_ID(PMC_TYPE_CORE,
+					sama7g5_mckx[i].ep_clk_mux_table[j]);
+		}
+
+		prepare_mux_table(clkmuxallocs, clkmuxallocindex, tmpclkmux, cm,
+			4 + sama7g5_mckx[i].ep_count, fail);
+		prepare_mux_table(muxallocs, muxallocindex, tmpmux, m,
+			4 + sama7g5_mckx[i].ep_count, fail);
+
+		clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, sama7g5_mckx[i].cid),
+			at91_clk_sama7g5_register_master(base,
+			sama7g5_mckx[i].n, p, 4 + sama7g5_mckx[i].ep_count,
+			tmpmux, tmpclkmux, sama7g5_mckx[i].c,
+			sama7g5_mckx[i].id));
+	}
+
+	/* Register UTMI clock. */
+	clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_UTMI),
+		at91_clk_sama7g5_register_utmi(base, "utmick",
+		clk_names[ID_MAIN_XTAL]));
+
+	/* Register programmable clocks. */
+	p[0] = clk_names[ID_MD_SLCK];
+	p[1] = clk_names[ID_TD_SLCK];
+	p[2] = clk_names[ID_MAINCK];
+	p[3] = clk_names[ID_MCK0];
+	p[4] = clk_names[ID_PLL_SYS_DIV];
+	p[5] = clk_names[ID_PLL_DDR_DIV];
+	p[6] = clk_names[ID_PLL_IMG_DIV];
+	p[7] = clk_names[ID_PLL_BAUD_DIV];
+	p[8] = clk_names[ID_PLL_AUDIO_DIVPMC];
+	p[9] = clk_names[ID_PLL_ETH_DIV];
+	cm[0] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MD_SLCK);
+	cm[1] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_TD_SLCK);
+	cm[2] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAINCK);
+	cm[3] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK0);
+	cm[4] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_SYS_DIV);
+	cm[5] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_DDR_DIV);
+	cm[6] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_IMG_DIV);
+	cm[7] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_BAUD_DIV);
+	cm[8] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_AUDIO_DIVPMC);
+	cm[9] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_ETH_DIV);
+	for (i = 0; i < ARRAY_SIZE(sama7g5_prog); i++) {
+		prepare_mux_table(clkmuxallocs, clkmuxallocindex, tmpclkmux, cm,
+			10, fail);
+
+		clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, sama7g5_prog[i].cid),
+			at91_clk_register_programmable(base, sama7g5_prog[i].n,
+			p, 10, i, &programmable_layout, tmpclkmux,
+			sama7g5_prog_mux_table));
+	}
+
+	/* System clocks. */
+	for (i = 0; i < ARRAY_SIZE(sama7g5_systemck); i++) {
+		clk_dm(AT91_TO_CLK_ID(PMC_TYPE_SYSTEM, sama7g5_systemck[i].cid),
+			at91_clk_register_system(base, sama7g5_systemck[i].n,
+			sama7g5_systemck[i].p, sama7g5_systemck[i].id));
+	}
+
+	/* Peripheral clocks. */
+	for (i = 0; i < ARRAY_SIZE(sama7g5_periphck); i++) {
+		clk_dm(AT91_TO_CLK_ID(PMC_TYPE_PERIPHERAL,
+			sama7g5_periphck[i].id),
+			at91_clk_register_sam9x5_peripheral(base,
+			&sama7g5_pcr_layout, sama7g5_periphck[i].n,
+			sama7g5_periphck[i].p, sama7g5_periphck[i].id,
+			&sama7g5_periphck[i].r));
+	}
+
+	/* Generic clocks. */
+	p[0] = clk_names[ID_MD_SLCK];
+	p[1] = clk_names[ID_TD_SLCK];
+	p[2] = clk_names[ID_MAINCK];
+	p[3] = clk_names[ID_MCK0];
+	m[0] = 0;
+	m[1] = 1;
+	m[2] = 2;
+	m[3] = 3;
+	cm[0] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MD_SLCK);
+	cm[1] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_TD_SLCK);
+	cm[2] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAINCK);
+	cm[3] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK0);
+	for (i = 0; i < ARRAY_SIZE(sama7g5_gck); i++) {
+		for (j = 0; j < sama7g5_gck[i].ep_count; j++) {
+			p[4 + j] = sama7g5_gck[i].ep[j];
+			m[4 + j] = sama7g5_gck[i].ep_mux_table[j];
+			cm[4 + j] = AT91_TO_CLK_ID(PMC_TYPE_CORE,
+					sama7g5_gck[i].ep_clk_mux_table[j]);
+		}
+
+		prepare_mux_table(clkmuxallocs, clkmuxallocindex, tmpclkmux, cm,
+			4 + sama7g5_gck[i].ep_count, fail);
+		prepare_mux_table(muxallocs, muxallocindex, tmpmux, m,
+			4 + sama7g5_gck[i].ep_count, fail);
+
+		clk_dm(AT91_TO_CLK_ID(PMC_TYPE_GCK, sama7g5_gck[i].id),
+			at91_clk_register_generic(base, &sama7g5_pcr_layout,
+			sama7g5_gck[i].n, p, tmpclkmux, tmpmux,
+			4 + sama7g5_gck[i].ep_count, sama7g5_gck[i].id,
+			&sama7g5_gck[i].r));
+	}
+
+	/* Setup clocks. */
+	for (i = 0; i < ARRAY_SIZE(sama7g5_clk_setup); i++) {
+		ret = clk_get_by_id(sama7g5_clk_setup[i].cid, &c);
+		if (ret)
+			goto fail;
+
+		if (sama7g5_clk_setup[i].pid) {
+			ret = clk_get_by_id(sama7g5_clk_setup[i].pid, &parent);
+			if (ret)
+				goto fail;
+
+			ret = clk_set_parent(c, parent);
+			if (ret)
+				goto fail;
+
+			if (sama7g5_clk_setup[i].prate) {
+				ret = clk_set_rate(parent,
+					sama7g5_clk_setup[i].prate);
+				if (ret < 0)
+					goto fail;
+			}
+		}
+
+		if (sama7g5_clk_setup[i].rate) {
+			ret = clk_set_rate(c, sama7g5_clk_setup[i].rate);
+			if (ret < 0)
+				goto fail;
+		}
+	}
+
+	return 0;
+
+fail:
+	for (i = 0; i < ARRAY_SIZE(muxallocs); i++)
+		kfree(muxallocs[i]);
+
+	for (i = 0; i < ARRAY_SIZE(clkmuxallocs); i++)
+		kfree(clkmuxallocs[i]);
+
+	return -ENOMEM;
+}
+
+static const struct udevice_id sama7g5_clk_ids[] = {
+	{ .compatible = "microchip,sama7g5-pmc" },
+	{ /* Sentinel. */ },
+};
+
+U_BOOT_DRIVER(at91_sama7g5_pmc) = {
+	.name = "at91-sama7g5-pmc",
+	.id = UCLASS_CLK,
+	.of_match = sama7g5_clk_ids,
+	.ops = &at91_clk_ops,
+	.probe = sama7g5_clk_probe,
+	.flags = DM_FLAG_PRE_RELOC,
+};

+ 161 - 8
drivers/clk/at91/sckc.c

@@ -1,19 +1,172 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * Copyright (C) 2016 Atmel Corporation
- *               Wenyou.Yang <wenyou.yang@atmel.com>
+ * Slow clock support for AT91 architectures.
+ *
+ * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
  */
 
 #include <common.h>
+#include <clk-uclass.h>
 #include <dm.h>
+#include <dt-bindings/clk/at91.h>
+#include <linux/clk-provider.h>
 
-static const struct udevice_id at91_sckc_match[] = {
-	{ .compatible = "atmel,at91sam9x5-sckc" },
-	{}
+#include "pmc.h"
+
+#define UBOOT_DM_CLK_AT91_SAM9X60_TD_SLCK	"at91-sam9x60-td-slck"
+#define UBOOT_DM_CLK_AT91_SCKC			"at91-sckc"
+
+#define AT91_OSC_SEL		BIT(24)
+#define AT91_OSC_SEL_SHIFT	(24)
+
+struct sam9x60_sckc {
+	void __iomem *reg;
+	const char **parent_names;
+	unsigned int num_parents;
+	struct clk clk;
+};
+
+#define to_sam9x60_sckc(c)	container_of(c, struct sam9x60_sckc, clk)
+
+static int sam9x60_sckc_of_xlate(struct clk *clk,
+				 struct ofnode_phandle_args *args)
+{
+	if (args->args_count != 1) {
+		debug("AT91: SCKC: Invalid args_count: %d\n", args->args_count);
+		return -EINVAL;
+	}
+
+	clk->id = AT91_TO_CLK_ID(PMC_TYPE_SLOW, args->args[0]);
+
+	return 0;
+}
+
+static const struct clk_ops sam9x60_sckc_ops = {
+	.of_xlate = sam9x60_sckc_of_xlate,
+	.get_rate = clk_generic_get_rate,
+};
+
+static int sam9x60_td_slck_set_parent(struct clk *clk, struct clk *parent)
+{
+	struct sam9x60_sckc *sckc = to_sam9x60_sckc(clk);
+	u32 i;
+
+	for (i = 0; i < sckc->num_parents; i++) {
+		if (!strcmp(parent->dev->name, sckc->parent_names[i]))
+			break;
+	}
+	if (i == sckc->num_parents)
+		return -EINVAL;
+
+	pmc_update_bits(sckc->reg, 0, AT91_OSC_SEL, (i << AT91_OSC_SEL_SHIFT));
+
+	return 0;
+}
+
+static const struct clk_ops sam9x60_td_slck_ops = {
+	.get_rate = clk_generic_get_rate,
+	.set_parent = sam9x60_td_slck_set_parent,
+};
+
+static struct clk *at91_sam9x60_clk_register_td_slck(struct sam9x60_sckc *sckc,
+		const char *name, const char * const *parent_names,
+		int num_parents)
+{
+	struct clk *clk;
+	int ret = -ENOMEM;
+	u32 val, i;
+
+	if (!sckc || !name || !parent_names || num_parents != 2)
+		return ERR_PTR(-EINVAL);
+
+	sckc->parent_names = kzalloc(sizeof(*sckc->parent_names) * num_parents,
+				     GFP_KERNEL);
+	if (!sckc->parent_names)
+		return ERR_PTR(ret);
+
+	for (i = 0; i < num_parents; i++) {
+		sckc->parent_names[i] = kmemdup(parent_names[i],
+				strlen(parent_names[i]) + 1, GFP_KERNEL);
+		if (!sckc->parent_names[i])
+			goto free;
+	}
+	sckc->num_parents = num_parents;
+
+	pmc_read(sckc->reg, 0, &val);
+	val = (val & AT91_OSC_SEL) >> AT91_OSC_SEL_SHIFT;
+
+	clk = &sckc->clk;
+	ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAM9X60_TD_SLCK, name,
+			   parent_names[val]);
+	if (ret)
+		goto free;
+
+	return clk;
+
+free:
+	for (; i >= 0; i--)
+		kfree(sckc->parent_names[i]);
+	kfree(sckc->parent_names);
+
+	return ERR_PTR(ret);
+}
+
+U_BOOT_DRIVER(at91_sam9x60_td_slck) = {
+	.name = UBOOT_DM_CLK_AT91_SAM9X60_TD_SLCK,
+	.id = UCLASS_CLK,
+	.ops = &sam9x60_td_slck_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
+
+static int at91_sam9x60_sckc_probe(struct udevice *dev)
+{
+	struct sam9x60_sckc *sckc = dev_get_priv(dev);
+	void __iomem *base = (void *)devfdt_get_addr(dev);
+	const char *slow_rc_osc, *slow_osc;
+	const char *parents[2];
+	struct clk *clk, c;
+	int ret;
+
+	ret = clk_get_by_index(dev, 0, &c);
+	if (ret)
+		return ret;
+	slow_rc_osc = clk_hw_get_name(&c);
+
+	ret = clk_get_by_index(dev, 1, &c);
+	if (ret)
+		return ret;
+	slow_osc = clk_hw_get_name(&c);
+
+	clk = clk_register_fixed_factor(NULL, "md_slck", slow_rc_osc, 0, 1, 1);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+	clk_dm(AT91_TO_CLK_ID(PMC_TYPE_SLOW, 0), clk);
+
+	parents[0] = slow_rc_osc;
+	parents[1] = slow_osc;
+	sckc[1].reg = base;
+	clk = at91_sam9x60_clk_register_td_slck(&sckc[1], "td_slck",
+						parents, 2);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+	clk_dm(AT91_TO_CLK_ID(PMC_TYPE_SLOW, 1), clk);
+
+	return 0;
+}
+
+static const struct udevice_id sam9x60_sckc_ids[] = {
+	{ .compatible = "microchip,sam9x60-sckc" },
+	{ /* Sentinel. */ },
 };
 
 U_BOOT_DRIVER(at91_sckc) = {
-	.name = "at91-sckc",
-	.id = UCLASS_SIMPLE_BUS,
-	.of_match = at91_sckc_match,
+	.name = UBOOT_DM_CLK_AT91_SCKC,
+	.id = UCLASS_CLK,
+	.of_match = sam9x60_sckc_ids,
+	.priv_auto_alloc_size = sizeof(struct sam9x60_sckc) * 2,
+	.ops = &sam9x60_sckc_ops,
+	.probe = at91_sam9x60_sckc_probe,
+	.flags = DM_FLAG_PRE_RELOC,
 };

+ 46 - 5
drivers/clk/clk-uclass.c

@@ -14,6 +14,7 @@
 #include <errno.h>
 #include <log.h>
 #include <malloc.h>
+#include <dm/device-internal.h>
 #include <dm/devres.h>
 #include <dm/read.h>
 #include <linux/bug.h>
@@ -187,9 +188,26 @@ bulk_get_err:
 	return ret;
 }
 
+static struct clk *clk_set_default_get_by_id(struct clk *clk)
+{
+	struct clk *c = clk;
+
+	if (CONFIG_IS_ENABLED(CLK_CCF)) {
+		int ret = clk_get_by_id(clk->id, &c);
+
+		if (ret) {
+			debug("%s(): could not get parent clock pointer, id %lu\n",
+			      __func__, clk->id);
+			ERR_PTR(ret);
+		}
+	}
+
+	return c;
+}
+
 static int clk_set_default_parents(struct udevice *dev, int stage)
 {
-	struct clk clk, parent_clk;
+	struct clk clk, parent_clk, *c, *p;
 	int index;
 	int num_parents;
 	int ret;
@@ -215,6 +233,10 @@ static int clk_set_default_parents(struct udevice *dev, int stage)
 			return ret;
 		}
 
+		p = clk_set_default_get_by_id(&parent_clk);
+		if (IS_ERR(p))
+			return PTR_ERR(p);
+
 		ret = clk_get_by_indexed_prop(dev, "assigned-clocks",
 					      index, &clk);
 		if (ret) {
@@ -234,7 +256,11 @@ static int clk_set_default_parents(struct udevice *dev, int stage)
 			/* do not setup twice the parent clocks */
 			continue;
 
-		ret = clk_set_parent(&clk, &parent_clk);
+		c = clk_set_default_get_by_id(&clk);
+		if (IS_ERR(c))
+			return PTR_ERR(c);
+
+		ret = clk_set_parent(c, p);
 		/*
 		 * Not all drivers may support clock-reparenting (as of now).
 		 * Ignore errors due to this.
@@ -254,7 +280,7 @@ static int clk_set_default_parents(struct udevice *dev, int stage)
 
 static int clk_set_default_rates(struct udevice *dev, int stage)
 {
-	struct clk clk;
+	struct clk clk, *c;
 	int index;
 	int num_rates;
 	int size;
@@ -298,7 +324,11 @@ static int clk_set_default_rates(struct udevice *dev, int stage)
 			/* do not setup twice the parent clocks */
 			continue;
 
-		ret = clk_set_rate(&clk, rates[index]);
+		c = clk_set_default_get_by_id(&clk);
+		if (IS_ERR(c))
+			return PTR_ERR(c);
+
+		ret = clk_set_rate(c, rates[index]);
 
 		if (ret < 0) {
 			debug("%s: failed to set rate on clock index %d (%ld) for %s\n",
@@ -512,6 +542,7 @@ ulong clk_set_rate(struct clk *clk, ulong rate)
 int clk_set_parent(struct clk *clk, struct clk *parent)
 {
 	const struct clk_ops *ops;
+	int ret;
 
 	debug("%s(clk=%p, parent=%p)\n", __func__, clk, parent);
 	if (!clk_valid(clk))
@@ -521,7 +552,14 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
 	if (!ops->set_parent)
 		return -ENOSYS;
 
-	return ops->set_parent(clk, parent);
+	ret = ops->set_parent(clk, parent);
+	if (ret)
+		return ret;
+
+	if (CONFIG_IS_ENABLED(CLK_CCF))
+		ret = device_reparent(clk->dev, parent->dev);
+
+	return ret;
 }
 
 int clk_enable(struct clk *clk)
@@ -597,6 +635,9 @@ int clk_disable(struct clk *clk)
 
 	if (CONFIG_IS_ENABLED(CLK_CCF)) {
 		if (clk->id && !clk_get_by_id(clk->id, &clkp)) {
+			if (clkp->flags & CLK_IS_CRITICAL)
+				return 0;
+
 			if (clkp->enable_count == 0) {
 				printf("clk %s already disabled\n",
 				       clkp->dev->name);

+ 3 - 0
drivers/clk/clk.c

@@ -57,6 +57,9 @@ ulong clk_generic_get_rate(struct clk *clk)
 
 const char *clk_hw_get_name(const struct clk *hw)
 {
+	assert(hw);
+	assert(hw->dev);
+
 	return hw->dev->name;
 }
 

+ 22 - 0
drivers/core/device.c

@@ -276,6 +276,28 @@ int device_bind_by_name(struct udevice *parent, bool pre_reloc_only,
 	return ret;
 }
 
+int device_reparent(struct udevice *dev, struct udevice *new_parent)
+{
+	struct udevice *pos, *n;
+
+	assert(dev);
+	assert(new_parent);
+
+	list_for_each_entry_safe(pos, n, &dev->parent->child_head,
+				 sibling_node) {
+		if (pos->driver != dev->driver)
+			continue;
+
+		list_del(&dev->sibling_node);
+		list_add_tail(&dev->sibling_node, &new_parent->child_head);
+		dev->parent = new_parent;
+
+		break;
+	}
+
+	return 0;
+}
+
 static void *alloc_priv(int size, uint flags)
 {
 	void *priv;

+ 1 - 0
drivers/cpu/Makefile

@@ -8,6 +8,7 @@ obj-$(CONFIG_CPU) += cpu-uclass.o
 
 obj-$(CONFIG_ARCH_BMIPS) += bmips_cpu.o
 obj-$(CONFIG_ARCH_IMX8) += imx8_cpu.o
+obj-$(CONFIG_ARCH_AT91) += at91_cpu.o
 obj-$(CONFIG_CPU_MPC83XX) += mpc83xx_cpu.o
 obj-$(CONFIG_CPU_RISCV) += riscv_cpu.o
 obj-$(CONFIG_SANDBOX) += cpu_sandbox.o

+ 123 - 0
drivers/cpu/at91_cpu.c

@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
+ */
+
+#include <common.h>
+#include <cpu.h>
+#include <dm.h>
+#include <div64.h>
+#include <linux/clk-provider.h>
+
+struct at91_cpu_platdata {
+	const char *name;
+	ulong cpufreq_mhz;
+	ulong mckfreq_mhz;
+	ulong xtalfreq_mhz;
+};
+
+extern char *get_cpu_name(void);
+
+const char *at91_cpu_get_name(void)
+{
+	return get_cpu_name();
+}
+
+int at91_cpu_get_desc(const struct udevice *dev, char *buf, int size)
+{
+	struct at91_cpu_platdata *plat = dev_get_platdata(dev);
+
+	snprintf(buf, size, "%s\n"
+		 "Crystal frequency: %8lu MHz\n"
+		 "CPU clock        : %8lu MHz\n"
+		 "Master clock     : %8lu MHz\n",
+		 plat->name, plat->xtalfreq_mhz, plat->cpufreq_mhz,
+		 plat->mckfreq_mhz);
+
+	return 0;
+}
+
+static int at91_cpu_get_info(const struct udevice *dev, struct cpu_info *info)
+{
+	struct at91_cpu_platdata *plat = dev_get_platdata(dev);
+
+	info->cpu_freq = plat->cpufreq_mhz * 1000000;
+	info->features = BIT(CPU_FEAT_L1_CACHE);
+
+	return 0;
+}
+
+static int at91_cpu_get_count(const struct udevice *dev)
+{
+	return 1;
+}
+
+static int at91_cpu_get_vendor(const struct udevice *dev,  char *buf, int size)
+{
+	snprintf(buf, size, "Microchip Technology Inc.");
+
+	return 0;
+}
+
+static const struct cpu_ops at91_cpu_ops = {
+	.get_desc	= at91_cpu_get_desc,
+	.get_info	= at91_cpu_get_info,
+	.get_count	= at91_cpu_get_count,
+	.get_vendor	= at91_cpu_get_vendor,
+};
+
+static const struct udevice_id at91_cpu_ids[] = {
+	{ .compatible = "arm,cortex-a7" },
+	{ /* Sentinel. */ }
+};
+
+static int at91_cpu_probe(struct udevice *dev)
+{
+	struct at91_cpu_platdata *plat = dev_get_platdata(dev);
+	struct clk clk;
+	ulong rate;
+	int ret;
+
+	ret = clk_get_by_index(dev, 0, &clk);
+	if (ret)
+		return ret;
+
+	rate  = clk_get_rate(&clk);
+	if (!rate)
+		return -ENOTSUPP;
+	plat->cpufreq_mhz = DIV_ROUND_CLOSEST_ULL(rate, 1000000);
+
+	ret = clk_get_by_index(dev, 1, &clk);
+	if (ret)
+		return ret;
+
+	rate = clk_get_rate(&clk);
+	if (!rate)
+		return -ENOTSUPP;
+	plat->mckfreq_mhz = DIV_ROUND_CLOSEST_ULL(rate, 1000000);
+
+	ret = clk_get_by_index(dev, 2, &clk);
+	if (ret)
+		return ret;
+
+	rate = clk_get_rate(&clk);
+	if (!rate)
+		return -ENOTSUPP;
+	plat->xtalfreq_mhz = DIV_ROUND_CLOSEST_ULL(rate, 1000000);
+
+	plat->name = get_cpu_name();
+
+	return 0;
+}
+
+U_BOOT_DRIVER(cpu_at91_drv) = {
+	.name		= "at91-cpu",
+	.id		= UCLASS_CPU,
+	.of_match	= at91_cpu_ids,
+	.ops		= &at91_cpu_ops,
+	.probe		= at91_cpu_probe,
+	.platdata_auto_alloc_size = sizeof(struct at91_cpu_platdata),
+	.flags		= DM_FLAG_PRE_RELOC,
+};

+ 10 - 3
drivers/mmc/atmel_sdhci.c

@@ -79,14 +79,20 @@ static int atmel_sdhci_probe(struct udevice *dev)
 	if (ret)
 		return ret;
 
-	ret = clk_set_rate(&clk, ATMEL_SDHC_GCK_RATE);
-	if (ret)
-		return ret;
+	clk_set_rate(&clk, ATMEL_SDHC_GCK_RATE);
 
 	max_clk = clk_get_rate(&clk);
 	if (!max_clk)
 		return -EINVAL;
 
+	ret = clk_enable(&clk);
+	if (ret)
+		return ret;
+
+	ret = mmc_of_parse(dev, &plat->cfg);
+	if (ret)
+		return ret;
+
 	host->max_clk = max_clk;
 	host->mmc = &plat->mmc;
 	host->mmc->dev = dev;
@@ -113,6 +119,7 @@ static int atmel_sdhci_bind(struct udevice *dev)
 static const struct udevice_id atmel_sdhci_ids[] = {
 	{ .compatible = "atmel,sama5d2-sdhci" },
 	{ .compatible = "microchip,sam9x60-sdhci" },
+	{ .compatible = "microchip,sama7g5-sdhci" },
 	{ }
 };
 

+ 1 - 0
drivers/pinctrl/pinctrl-at91-pio4.c

@@ -169,6 +169,7 @@ static int atmel_pinctrl_probe(struct udevice *dev)
 
 static const struct udevice_id atmel_pinctrl_match[] = {
 	{ .compatible = "atmel,sama5d2-pinctrl" },
+	{ .compatible = "microchip,sama7g5-pinctrl" },
 	{}
 };
 

+ 7 - 0
drivers/timer/Kconfig

@@ -213,4 +213,11 @@ config MTK_TIMER
 	  Select this to enable support for the timer found on
 	  MediaTek devices.
 
+config MCHP_PIT64B_TIMER
+	bool "Microchip 64-bit periodic interval timer support"
+	depends on TIMER
+	help
+	  Select this to enable support for Microchip 64-bit periodic
+	  interval timer.
+
 endmenu

+ 1 - 0
drivers/timer/Makefile

@@ -22,3 +22,4 @@ obj-$(CONFIG_STI_TIMER)		+= sti-timer.o
 obj-$(CONFIG_STM32_TIMER)	+= stm32_timer.o
 obj-$(CONFIG_X86_TSC_TIMER)	+= tsc_timer.o
 obj-$(CONFIG_MTK_TIMER)		+= mtk_timer.o
+obj-$(CONFIG_MCHP_PIT64B_TIMER)	+= mchp-pit64b-timer.o

+ 109 - 0
drivers/timer/mchp-pit64b-timer.c

@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * 64-bit Periodic Interval Timer driver
+ *
+ * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <timer.h>
+#include <asm/io.h>
+
+#define MCHP_PIT64B_CR			0x00	/* Control Register */
+#define		MCHP_PIT64B_CR_START	BIT(0)
+#define		MCHP_PIT64B_CR_SWRST	BIT(8)
+#define MCHP_PIT64B_MR			0x04	/* Mode Register */
+#define		MCHP_PIT64B_MR_CONT	BIT(0)
+#define MCHP_PIT64B_LSB_PR		0x08	/* LSB Period Register */
+#define MCHP_PIT64B_MSB_PR		0x0C	/* MSB Period Register */
+#define MCHP_PIT64B_TLSBR		0x20	/* Timer LSB Register */
+#define MCHP_PIT64B_TMSBR		0x24	/* Timer MSB Register */
+
+struct mchp_pit64b_priv {
+	void __iomem *base;
+};
+
+static int mchp_pit64b_get_count(struct udevice *dev, u64 *count)
+{
+	struct mchp_pit64b_priv *priv = dev_get_priv(dev);
+
+	u32 lsb = readl(priv->base + MCHP_PIT64B_TLSBR);
+	u32 msb = readl(priv->base + MCHP_PIT64B_TMSBR);
+
+	*count = ((u64)msb << 32) | lsb;
+
+	return 0;
+}
+
+static int mchp_pit64b_probe(struct udevice *dev)
+{
+	struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+	struct mchp_pit64b_priv *priv = dev_get_priv(dev);
+	struct clk clk;
+	ulong rate;
+	int ret;
+
+	priv->base = dev_read_addr_ptr(dev);
+	if (IS_ERR(priv->base))
+		return PTR_ERR(priv->base);
+
+	ret = clk_get_by_index(dev, 0, &clk);
+	if (ret)
+		return ret;
+
+	ret = clk_enable(&clk);
+	if (ret)
+		return ret;
+
+	rate = clk_get_rate(&clk);
+	if (!rate) {
+		clk_disable(&clk);
+		return -ENOTSUPP;
+	}
+
+	/* Reset the timer in case it was used by previous bootloaders. */
+	writel(MCHP_PIT64B_CR_SWRST, priv->base + MCHP_PIT64B_CR);
+
+	/*
+	 * Use highest prescaller (for a peripheral clock running at 200MHz
+	 * this will lead to the timer running at 12.5MHz) and continuous mode.
+	 */
+	writel((15 << 8) | MCHP_PIT64B_MR_CONT, priv->base + MCHP_PIT64B_MR);
+	uc_priv->clock_rate = rate / 16;
+
+	/*
+	 * Simulate free running counter by setting max values to period
+	 * registers.
+	 */
+	writel(~0UL, priv->base + MCHP_PIT64B_MSB_PR);
+	writel(~0UL, priv->base + MCHP_PIT64B_LSB_PR);
+
+	/* Start the timer. */
+	writel(MCHP_PIT64B_CR_START, priv->base + MCHP_PIT64B_CR);
+
+	return 0;
+}
+
+static const struct timer_ops mchp_pit64b_ops = {
+	.get_count = mchp_pit64b_get_count,
+};
+
+static const struct udevice_id mchp_pit64b_ids[] = {
+	{ .compatible = "microchip,sam9x60-pit64b", },
+	{ .compatible = "microchip,sama7g5-pit64b", },
+	{ }
+};
+
+U_BOOT_DRIVER(mchp_pit64b) = {
+	.name	= "mchp-pit64b",
+	.id	= UCLASS_TIMER,
+	.of_match = mchp_pit64b_ids,
+	.priv_auto_alloc_size = sizeof(struct mchp_pit64b_priv),
+	.probe	= mchp_pit64b_probe,
+	.ops	= &mchp_pit64b_ops,
+	.flags	= DM_FLAG_PRE_RELOC,
+};

+ 9 - 0
include/dm/device-internal.h

@@ -83,6 +83,15 @@ int device_bind_with_driver_data(struct udevice *parent,
 int device_bind_by_name(struct udevice *parent, bool pre_reloc_only,
 			struct driver_info *info, struct udevice **devp);
 
+/**
+ * device_reparent: reparent the device to a new parent
+ *
+ * @dev: pointer to device to be reparented
+ * @new_parent: pointer to new parent device
+ * @return 0 if OK, -ve on error
+ */
+int device_reparent(struct udevice *dev, struct udevice *new_parent);
+
 /**
  * device_ofdata_to_platdata() - Read platform data for a device
  *

+ 22 - 0
include/dt-bindings/clk/at91.h

@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * This header provides constants for AT91 pmc status.
+ * The constants defined in this header are being used in dts and PMC code.
+ *
+ * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
+ *
+ * Based on include/dt-bindings/clock/at91.h on Linux.
+ */
+
+#ifndef _DT_BINDINGS_CLK_AT91_H
+#define _DT_BINDINGS_CLK_AT91_H
+
+#define PMC_TYPE_CORE		1
+#define PMC_TYPE_SYSTEM		2
+#define PMC_TYPE_PERIPHERAL	3
+#define PMC_TYPE_GCK		4
+#define PMC_TYPE_SLOW		5
+
+#endif

+ 247 - 0
include/linux/clk/at91_pmc.h

@@ -0,0 +1,247 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Power Management Controller (PMC) - System peripherals registers.
+ *
+ * Copyright (C) 2005 Ivan Kokshaysky
+ * Copyright (C) SAN People
+ *
+ * Based on AT91RM9200 datasheet revision E.
+ * Based on include/linux/clk/at91_pmc.h on Linux.
+ */
+
+#ifndef AT91_PMC_H_
+#define AT91_PMC_H_
+
+#define AT91_PMC_V1		(1)			/* PMC version 1 */
+#define AT91_PMC_V2		(2)			/* PMC version 2 [SAM9X60] */
+
+#define	AT91_PMC_SCER		0x00			/* System Clock Enable Register */
+#define	AT91_PMC_SCDR		0x04			/* System Clock Disable Register */
+
+#define	AT91_PMC_SCSR		0x08			/* System Clock Status Register */
+#define		AT91_PMC_PCK		(1 <<  0)		/* Processor Clock */
+#define		AT91RM9200_PMC_UDP	(1 <<  1)		/* USB Devcice Port Clock [AT91RM9200 only] */
+#define		AT91RM9200_PMC_MCKUDP	(1 <<  2)		/* USB Device Port Master Clock Automatic Disable on Suspend [AT91RM9200 only] */
+#define		AT91RM9200_PMC_UHP	(1 <<  4)		/* USB Host Port Clock [AT91RM9200 only] */
+#define		AT91SAM926x_PMC_UHP	(1 <<  6)		/* USB Host Port Clock [AT91SAM926x only] */
+#define		AT91SAM926x_PMC_UDP	(1 <<  7)		/* USB Devcice Port Clock [AT91SAM926x only] */
+#define		AT91_PMC_PCK0		(1 <<  8)		/* Programmable Clock 0 */
+#define		AT91_PMC_PCK1		(1 <<  9)		/* Programmable Clock 1 */
+#define		AT91_PMC_PCK2		(1 << 10)		/* Programmable Clock 2 */
+#define		AT91_PMC_PCK3		(1 << 11)		/* Programmable Clock 3 */
+#define		AT91_PMC_PCK4		(1 << 12)		/* Programmable Clock 4 [AT572D940HF only] */
+#define		AT91_PMC_HCK0		(1 << 16)		/* AHB Clock (USB host) [AT91SAM9261 only] */
+#define		AT91_PMC_HCK1		(1 << 17)		/* AHB Clock (LCD) [AT91SAM9261 only] */
+
+#define AT91_PMC_PLL_CTRL0		0x0C		/* PLL Control Register 0 [for SAM9X60] */
+#define		AT91_PMC_PLL_CTRL0_ENPLL	(1 << 28)	/* Enable PLL */
+#define		AT91_PMC_PLL_CTRL0_ENPLLCK	(1 << 29)	/* Enable PLL clock for PMC */
+#define		AT91_PMC_PLL_CTRL0_ENLOCK	(1 << 31)	/* Enable PLL lock */
+
+#define AT91_PMC_PLL_CTRL1		0x10		/* PLL Control Register 1 [for SAM9X60] */
+
+#define	AT91_PMC_PCER		0x10			/* Peripheral Clock Enable Register */
+#define	AT91_PMC_PCDR		0x14			/* Peripheral Clock Disable Register */
+#define	AT91_PMC_PCSR		0x18			/* Peripheral Clock Status Register */
+
+#define AT91_PMC_PLL_ACR	0x18			/* PLL Analog Control Register [for SAM9X60] */
+#define		AT91_PMC_PLL_ACR_DEFAULT_UPLL	0x12020010UL	/* Default PLL ACR value for UPLL */
+#define		AT91_PMC_PLL_ACR_DEFAULT_PLLA	0x00020010UL	/* Default PLL ACR value for PLLA */
+#define		AT91_PMC_PLL_ACR_UTMIVR		(1 << 12)	/* UPLL Voltage regulator Control */
+#define		AT91_PMC_PLL_ACR_UTMIBG		(1 << 13)	/* UPLL Bandgap Control */
+
+#define	AT91_CKGR_UCKR		0x1C			/* UTMI Clock Register [some SAM9] */
+#define		AT91_PMC_UPLLEN		(1   << 16)		/* UTMI PLL Enable */
+#define		AT91_PMC_UPLLCOUNT	(0xf << 20)		/* UTMI PLL Start-up Time */
+#define		AT91_PMC_BIASEN		(1   << 24)		/* UTMI BIAS Enable */
+#define		AT91_PMC_BIASCOUNT	(0xf << 28)		/* UTMI BIAS Start-up Time */
+
+#define AT91_PMC_PLL_UPDT		0x1C		/* PMC PLL update register [for SAM9X60] */
+#define		AT91_PMC_PLL_UPDT_ID_MSK	(0xf)
+#define		AT91_PMC_PLL_UPDT_UPDATE	(1 << 8)	/* Update PLL settings */
+#define		AT91_PMC_PLL_UPDT_ID		(1 << 0)	/* PLL ID */
+#define		AT91_PMC_PLL_UPDT_STUPTIM(n)	((n) << 16)	/* Startup time */
+#define		AT91_PMC_PMM_UPDT_STUPTIM_MSK	(0xff << 16)
+
+#define	AT91_CKGR_MOR		0x20			/* Main Oscillator Register [not on SAM9RL] */
+#define		AT91_PMC_MOSCEN		(1    <<  0)		/* Main Oscillator Enable */
+#define		AT91_PMC_OSCBYPASS	(1    <<  1)		/* Oscillator Bypass */
+#define		AT91_PMC_WAITMODE	(1    <<  2)		/* Wait Mode Command */
+#define		AT91_PMC_MOSCRCEN	(1    <<  3)		/* Main On-Chip RC Oscillator Enable [some SAM9] */
+#define		AT91_PMC_OSCOUNT	(0xff <<  8)		/* Main Oscillator Start-up Time */
+#define		AT91_PMC_KEY_MASK	(0xff << 16)
+#define		AT91_PMC_KEY		(0x37 << 16)		/* MOR Writing Key */
+#define		AT91_PMC_MOSCSEL	(1    << 24)		/* Main Oscillator Selection [some SAM9] */
+#define		AT91_PMC_CFDEN		(1    << 25)		/* Clock Failure Detector Enable [some SAM9] */
+
+#define	AT91_CKGR_MCFR		0x24			/* Main Clock Frequency Register */
+#define		AT91_PMC_MAINF		(0xffff <<  0)		/* Main Clock Frequency */
+#define		AT91_PMC_MAINRDY	(1	<< 16)		/* Main Clock Ready */
+
+#define	AT91_CKGR_PLLAR		0x28			/* PLL A Register */
+#define	AT91_CKGR_PLLBR		0x2c			/* PLL B Register */
+#define		AT91_PMC_DIV		(0xff  <<  0)		/* Divider */
+#define		AT91_PMC_PLLCOUNT	(0x3f  <<  8)		/* PLL Counter */
+#define		AT91_PMC_OUT		(3     << 14)		/* PLL Clock Frequency Range */
+#define		AT91_PMC_MUL		(0x7ff << 16)		/* PLL Multiplier */
+#define		AT91_PMC_MUL_GET(n)	((n) >> 16 & 0x7ff)
+#define		AT91_PMC3_MUL		(0x7f  << 18)		/* PLL Multiplier [SAMA5 only] */
+#define		AT91_PMC3_MUL_GET(n)	((n) >> 18 & 0x7f)
+#define		AT91_PMC_USBDIV		(3     << 28)		/* USB Divisor (PLLB only) */
+#define			AT91_PMC_USBDIV_1		(0 << 28)
+#define			AT91_PMC_USBDIV_2		(1 << 28)
+#define			AT91_PMC_USBDIV_4		(2 << 28)
+#define		AT91_PMC_USB96M		(1     << 28)		/* Divider by 2 Enable (PLLB only) */
+
+#define AT91_PMC_CPU_CKR	0x28			/* CPU Clock Register */
+
+#define	AT91_PMC_MCKR		0x30			/* Master Clock Register */
+#define		AT91_PMC_CSS		(3 <<  0)		/* Master Clock Selection */
+#define			AT91_PMC_CSS_SLOW		(0 << 0)
+#define			AT91_PMC_CSS_MAIN		(1 << 0)
+#define			AT91_PMC_CSS_PLLA		(2 << 0)
+#define			AT91_PMC_CSS_PLLB		(3 << 0)
+#define			AT91_PMC_CSS_UPLL		(3 << 0)	/* [some SAM9 only] */
+#define		PMC_PRES_OFFSET		2
+#define		AT91_PMC_PRES		(7 <<  PMC_PRES_OFFSET)		/* Master Clock Prescaler */
+#define			AT91_PMC_PRES_1			(0 << PMC_PRES_OFFSET)
+#define			AT91_PMC_PRES_2			(1 << PMC_PRES_OFFSET)
+#define			AT91_PMC_PRES_4			(2 << PMC_PRES_OFFSET)
+#define			AT91_PMC_PRES_8			(3 << PMC_PRES_OFFSET)
+#define			AT91_PMC_PRES_16		(4 << PMC_PRES_OFFSET)
+#define			AT91_PMC_PRES_32		(5 << PMC_PRES_OFFSET)
+#define			AT91_PMC_PRES_64		(6 << PMC_PRES_OFFSET)
+#define		PMC_ALT_PRES_OFFSET	4
+#define		AT91_PMC_ALT_PRES	(7 <<  PMC_ALT_PRES_OFFSET)		/* Master Clock Prescaler [alternate location] */
+#define			AT91_PMC_ALT_PRES_1		(0 << PMC_ALT_PRES_OFFSET)
+#define			AT91_PMC_ALT_PRES_2		(1 << PMC_ALT_PRES_OFFSET)
+#define			AT91_PMC_ALT_PRES_4		(2 << PMC_ALT_PRES_OFFSET)
+#define			AT91_PMC_ALT_PRES_8		(3 << PMC_ALT_PRES_OFFSET)
+#define			AT91_PMC_ALT_PRES_16		(4 << PMC_ALT_PRES_OFFSET)
+#define			AT91_PMC_ALT_PRES_32		(5 << PMC_ALT_PRES_OFFSET)
+#define			AT91_PMC_ALT_PRES_64		(6 << PMC_ALT_PRES_OFFSET)
+#define		AT91_PMC_MDIV		(3 <<  8)		/* Master Clock Division */
+#define			AT91RM9200_PMC_MDIV_1		(0 << 8)	/* [AT91RM9200 only] */
+#define			AT91RM9200_PMC_MDIV_2		(1 << 8)
+#define			AT91RM9200_PMC_MDIV_3		(2 << 8)
+#define			AT91RM9200_PMC_MDIV_4		(3 << 8)
+#define			AT91SAM9_PMC_MDIV_1		(0 << 8)	/* [SAM9 only] */
+#define			AT91SAM9_PMC_MDIV_2		(1 << 8)
+#define			AT91SAM9_PMC_MDIV_4		(2 << 8)
+#define			AT91SAM9_PMC_MDIV_6		(3 << 8)	/* [some SAM9 only] */
+#define			AT91SAM9_PMC_MDIV_3		(3 << 8)	/* [some SAM9 only] */
+#define		AT91_PMC_PDIV		(1 << 12)		/* Processor Clock Division [some SAM9 only] */
+#define			AT91_PMC_PDIV_1			(0 << 12)
+#define			AT91_PMC_PDIV_2			(1 << 12)
+#define		AT91_PMC_PLLADIV2	(1 << 12)		/* PLLA divisor by 2 [some SAM9 only] */
+#define			AT91_PMC_PLLADIV2_OFF		(0 << 12)
+#define			AT91_PMC_PLLADIV2_ON		(1 << 12)
+#define		AT91_PMC_H32MXDIV	BIT(24)
+
+#define AT91_PMC_XTALF		0x34			/* Main XTAL Frequency Register [SAMA7G5 only] */
+
+#define	AT91_PMC_USB		0x38			/* USB Clock Register [some SAM9 only] */
+#define		AT91_PMC_USBS		(0x1 <<  0)		/* USB OHCI Input clock selection */
+#define			AT91_PMC_USBS_PLLA		(0 << 0)
+#define			AT91_PMC_USBS_UPLL		(1 << 0)
+#define			AT91_PMC_USBS_PLLB		(1 << 0)	/* [AT91SAMN12 only] */
+#define		AT91_PMC_OHCIUSBDIV	(0xF <<  8)		/* Divider for USB OHCI Clock */
+#define			AT91_PMC_OHCIUSBDIV_1	(0x0 <<  8)
+#define			AT91_PMC_OHCIUSBDIV_2	(0x1 <<  8)
+
+#define	AT91_PMC_SMD		0x3c			/* Soft Modem Clock Register [some SAM9 only] */
+#define		AT91_PMC_SMDS		(0x1  <<  0)		/* SMD input clock selection */
+#define		AT91_PMC_SMD_DIV	(0x1f <<  8)		/* SMD input clock divider */
+#define		AT91_PMC_SMDDIV(n)	(((n) <<  8) & AT91_PMC_SMD_DIV)
+
+#define	AT91_PMC_PCKR(n)	(0x40 + ((n) * 4))	/* Programmable Clock 0-N Registers */
+#define		AT91_PMC_ALT_PCKR_CSS	(0x7 <<  0)		/* Programmable Clock Source Selection [alternate length] */
+#define			AT91_PMC_CSS_MASTER		(4 << 0)	/* [some SAM9 only] */
+#define		AT91_PMC_CSSMCK		(0x1 <<  8)		/* CSS or Master Clock Selection */
+#define			AT91_PMC_CSSMCK_CSS		(0 << 8)
+#define			AT91_PMC_CSSMCK_MCK		(1 << 8)
+
+#define	AT91_PMC_IER		0x60			/* Interrupt Enable Register */
+#define	AT91_PMC_IDR		0x64			/* Interrupt Disable Register */
+#define	AT91_PMC_SR		0x68			/* Status Register */
+#define		AT91_PMC_MOSCS		(1 <<  0)		/* MOSCS Flag */
+#define		AT91_PMC_LOCKA		(1 <<  1)		/* PLLA Lock */
+#define		AT91_PMC_LOCKB		(1 <<  2)		/* PLLB Lock */
+#define		AT91_PMC_MCKRDY		(1 <<  3)		/* Master Clock */
+#define		AT91_PMC_LOCKU		(1 <<  6)		/* UPLL Lock [some SAM9] */
+#define		AT91_PMC_OSCSEL		(1 <<  7)		/* Slow Oscillator Selection [some SAM9] */
+#define		AT91_PMC_PCK0RDY	(1 <<  8)		/* Programmable Clock 0 */
+#define		AT91_PMC_PCK1RDY	(1 <<  9)		/* Programmable Clock 1 */
+#define		AT91_PMC_PCK2RDY	(1 << 10)		/* Programmable Clock 2 */
+#define		AT91_PMC_PCK3RDY	(1 << 11)		/* Programmable Clock 3 */
+#define		AT91_PMC_MOSCSELS	(1 << 16)		/* Main Oscillator Selection [some SAM9] */
+#define		AT91_PMC_MOSCRCS	(1 << 17)		/* Main On-Chip RC [some SAM9] */
+#define		AT91_PMC_CFDEV		(1 << 18)		/* Clock Failure Detector Event [some SAM9] */
+#define		AT91_PMC_GCKRDY		(1 << 24)		/* Generated Clocks */
+#define		AT91_PMC_MCKXRDY	(1 << 26)		/* Master Clock x [x=1..4] Ready Status */
+#define	AT91_PMC_IMR		0x6c			/* Interrupt Mask Register */
+
+#define AT91_PMC_FSMR		0x70		/* Fast Startup Mode Register */
+#define AT91_PMC_FSTT(n)	BIT(n)
+#define AT91_PMC_RTTAL		BIT(16)
+#define AT91_PMC_RTCAL		BIT(17)		/* RTC Alarm Enable */
+#define AT91_PMC_USBAL		BIT(18)		/* USB Resume Enable */
+#define AT91_PMC_SDMMC_CD	BIT(19)		/* SDMMC Card Detect Enable */
+#define AT91_PMC_LPM		BIT(20)		/* Low-power Mode */
+#define AT91_PMC_RXLP_MCE	BIT(24)		/* Backup UART Receive Enable */
+#define AT91_PMC_ACC_CE		BIT(25)		/* ACC Enable */
+
+#define AT91_PMC_FSPR		0x74		/* Fast Startup Polarity Reg */
+
+#define AT91_PMC_FS_INPUT_MASK  0x7ff
+
+#define AT91_PMC_PLLICPR	0x80			/* PLL Charge Pump Current Register */
+
+#define AT91_PMC_PROT		0xe4			/* Write Protect Mode Register [some SAM9] */
+#define		AT91_PMC_WPEN		(0x1  <<  0)		/* Write Protect Enable */
+#define		AT91_PMC_WPKEY		(0xffffff << 8)		/* Write Protect Key */
+#define		AT91_PMC_PROTKEY	(0x504d43 << 8)		/* Activation Code */
+
+#define AT91_PMC_WPSR		0xe8			/* Write Protect Status Register [some SAM9] */
+#define		AT91_PMC_WPVS		(0x1  <<  0)		/* Write Protect Violation Status */
+#define		AT91_PMC_WPVSRC		(0xffff  <<  8)		/* Write Protect Violation Source */
+
+#define AT91_PMC_PLL_ISR0	0xEC			/* PLL Interrupt Status Register 0 [SAM9X60 only] */
+
+#define AT91_PMC_PCER1		0x100			/* Peripheral Clock Enable Register 1 [SAMA5 only]*/
+#define AT91_PMC_PCDR1		0x104			/* Peripheral Clock Enable Register 1 */
+#define AT91_PMC_PCSR1		0x108			/* Peripheral Clock Enable Register 1 */
+
+#define AT91_PMC_PCR		0x10c			/* Peripheral Control Register [some SAM9 and SAMA5] */
+#define		AT91_PMC_PCR_PID_MASK		0x3f
+#define		AT91_PMC_PCR_CMD		(0x1  <<  12)				/* Command (read=0, write=1) */
+#define		AT91_PMC_PCR_GCKDIV_MASK	GENMASK(27, 20)
+#define		AT91_PMC_PCR_EN			(0x1  <<  28)				/* Enable */
+#define		AT91_PMC_PCR_GCKEN		(0x1  <<  29)				/* GCK Enable */
+
+#define AT91_PMC_AUDIO_PLL0	0x14c
+#define		AT91_PMC_AUDIO_PLL_PLLEN	(1  <<  0)
+#define		AT91_PMC_AUDIO_PLL_PADEN	(1  <<  1)
+#define		AT91_PMC_AUDIO_PLL_PMCEN	(1  <<  2)
+#define		AT91_PMC_AUDIO_PLL_RESETN	(1  <<  3)
+#define		AT91_PMC_AUDIO_PLL_ND_OFFSET	8
+#define		AT91_PMC_AUDIO_PLL_ND_MASK	(0x7f << AT91_PMC_AUDIO_PLL_ND_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_ND(n)	((n)  << AT91_PMC_AUDIO_PLL_ND_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_QDPMC_OFFSET	16
+#define		AT91_PMC_AUDIO_PLL_QDPMC_MASK	(0x7f << AT91_PMC_AUDIO_PLL_QDPMC_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_QDPMC(n)	((n)  << AT91_PMC_AUDIO_PLL_QDPMC_OFFSET)
+
+#define AT91_PMC_AUDIO_PLL1	0x150
+#define		AT91_PMC_AUDIO_PLL_FRACR_MASK		0x3fffff
+#define		AT91_PMC_AUDIO_PLL_QDPAD_OFFSET		24
+#define		AT91_PMC_AUDIO_PLL_QDPAD_MASK		(0x7f << AT91_PMC_AUDIO_PLL_QDPAD_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_QDPAD(n)		((n)  << AT91_PMC_AUDIO_PLL_QDPAD_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_QDPAD_DIV_OFFSET	AT91_PMC_AUDIO_PLL_QDPAD_OFFSET
+#define		AT91_PMC_AUDIO_PLL_QDPAD_DIV_MASK	(0x3  << AT91_PMC_AUDIO_PLL_QDPAD_DIV_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_QDPAD_DIV(n)		((n)  << AT91_PMC_AUDIO_PLL_QDPAD_DIV_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_OFFSET	26
+#define		AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX	0x1f
+#define		AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MASK	(AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX << AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV(n)	((n)  << AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_OFFSET)
+
+#endif

+ 57 - 0
test/dm/clk_ccf.c

@@ -22,6 +22,10 @@ static int dm_test_clk_ccf(struct unit_test_state *uts)
 	struct udevice *dev;
 	long long rate;
 	int ret;
+#if CONFIG_IS_ENABLED(CLK_CCF)
+	const char *clkname;
+	int clkid, i;
+#endif
 
 	/* Get the device using the clk device */
 	ut_assertok(uclass_get_device_by_name(UCLASS_CLK, "clk-ccf", &dev));
@@ -130,6 +134,59 @@ static int dm_test_clk_ccf(struct unit_test_state *uts)
 
 	ret = sandbox_clk_enable_count(pclk);
 	ut_asserteq(ret, 0);
+
+	/* Test clock re-parenting. */
+	ret = clk_get_by_id(SANDBOX_CLK_USDHC1_SEL, &clk);
+	ut_assertok(ret);
+	ut_asserteq_str("usdhc1_sel", clk->dev->name);
+
+	pclk = clk_get_parent(clk);
+	ut_assertok_ptr(pclk);
+	if (!strcmp(pclk->dev->name, "pll3_60m")) {
+		clkname = "pll3_80m";
+		clkid = SANDBOX_CLK_PLL3_80M;
+	} else {
+		clkname = "pll3_60m";
+		clkid = SANDBOX_CLK_PLL3_60M;
+	}
+
+	ret = clk_get_by_id(clkid, &pclk);
+	ut_assertok(ret);
+	ret = clk_set_parent(clk, pclk);
+	ut_assertok(ret);
+	pclk = clk_get_parent(clk);
+	ut_assertok_ptr(pclk);
+	ut_asserteq_str(clkname, pclk->dev->name);
+
+	/* Test disabling critical clock. */
+	ret = clk_get_by_id(SANDBOX_CLK_I2C_ROOT, &clk);
+	ut_assertok(ret);
+	ut_asserteq_str("i2c_root", clk->dev->name);
+
+	/* Disable it, if any. */
+	ret = sandbox_clk_enable_count(clk);
+	for (i = 0; i < ret; i++) {
+		ret = clk_disable(clk);
+		ut_assertok(ret);
+	}
+
+	ret = sandbox_clk_enable_count(clk);
+	ut_asserteq(ret, 0);
+
+	clk->flags = CLK_IS_CRITICAL;
+	ret = clk_enable(clk);
+	ut_assertok(ret);
+
+	ret = clk_disable(clk);
+	ut_assertok(ret);
+	ret = sandbox_clk_enable_count(clk);
+	ut_asserteq(ret, 1);
+	clk->flags &= ~CLK_IS_CRITICAL;
+
+	ret = clk_disable(clk);
+	ut_assertok(ret);
+	ret = sandbox_clk_enable_count(clk);
+	ut_asserteq(ret, 0);
 #endif
 
 	return 1;

+ 160 - 0
test/dm/core.c

@@ -643,6 +643,166 @@ static int dm_test_children(struct unit_test_state *uts)
 }
 DM_TEST(dm_test_children, 0);
 
+static int dm_test_device_reparent(struct unit_test_state *uts)
+{
+	struct dm_test_state *dms = uts->priv;
+	struct udevice *top[NODE_COUNT];
+	struct udevice *child[NODE_COUNT];
+	struct udevice *grandchild[NODE_COUNT];
+	struct udevice *dev;
+	int total;
+	int ret;
+	int i;
+
+	/* We don't care about the numbering for this test */
+	dms->skip_post_probe = 1;
+
+	ut_assert(NODE_COUNT > 5);
+
+	/* First create 10 top-level children */
+	ut_assertok(create_children(uts, dms->root, NODE_COUNT, 0, top));
+
+	/* Now a few have their own children */
+	ut_assertok(create_children(uts, top[2], NODE_COUNT, 2, NULL));
+	ut_assertok(create_children(uts, top[5], NODE_COUNT, 5, child));
+
+	/* And grandchildren */
+	for (i = 0; i < NODE_COUNT; i++)
+		ut_assertok(create_children(uts, child[i], NODE_COUNT, 50 * i,
+					    i == 2 ? grandchild : NULL));
+
+	/* Check total number of devices */
+	total = NODE_COUNT * (3 + NODE_COUNT);
+	ut_asserteq(total, dm_testdrv_op_count[DM_TEST_OP_BIND]);
+
+	/* Probe everything */
+	for (i = 0; i < total; i++)
+		ut_assertok(uclass_get_device(UCLASS_TEST, i, &dev));
+
+	/* Re-parent top-level children with no grandchildren. */
+	ut_assertok(device_reparent(top[3], top[0]));
+	/* try to get devices */
+	for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     ret = uclass_find_next_device(&dev)) {
+		ut_assert(!ret);
+		ut_assertnonnull(dev);
+	}
+
+	ut_assertok(device_reparent(top[4], top[0]));
+	/* try to get devices */
+	for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     ret = uclass_find_next_device(&dev)) {
+		ut_assert(!ret);
+		ut_assertnonnull(dev);
+	}
+
+	/* Re-parent top-level children with grandchildren. */
+	ut_assertok(device_reparent(top[2], top[0]));
+	/* try to get devices */
+	for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     ret = uclass_find_next_device(&dev)) {
+		ut_assert(!ret);
+		ut_assertnonnull(dev);
+	}
+
+	ut_assertok(device_reparent(top[5], top[2]));
+	/* try to get devices */
+	for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     ret = uclass_find_next_device(&dev)) {
+		ut_assert(!ret);
+		ut_assertnonnull(dev);
+	}
+
+	/* Re-parent grandchildren. */
+	ut_assertok(device_reparent(grandchild[0], top[1]));
+	/* try to get devices */
+	for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     ret = uclass_find_next_device(&dev)) {
+		ut_assert(!ret);
+		ut_assertnonnull(dev);
+	}
+
+	ut_assertok(device_reparent(grandchild[1], top[1]));
+	/* try to get devices */
+	for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     ret = uclass_find_next_device(&dev)) {
+		ut_assert(!ret);
+		ut_assertnonnull(dev);
+	}
+
+	/* Remove re-pareneted devices. */
+	ut_assertok(device_remove(top[3], DM_REMOVE_NORMAL));
+	/* try to get devices */
+	for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     ret = uclass_find_next_device(&dev)) {
+		ut_assert(!ret);
+		ut_assertnonnull(dev);
+	}
+
+	ut_assertok(device_remove(top[4], DM_REMOVE_NORMAL));
+	/* try to get devices */
+	for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     ret = uclass_find_next_device(&dev)) {
+		ut_assert(!ret);
+		ut_assertnonnull(dev);
+	}
+
+	ut_assertok(device_remove(top[5], DM_REMOVE_NORMAL));
+	/* try to get devices */
+	for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     ret = uclass_find_next_device(&dev)) {
+		ut_assert(!ret);
+		ut_assertnonnull(dev);
+	}
+
+	ut_assertok(device_remove(top[2], DM_REMOVE_NORMAL));
+	for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     ret = uclass_find_next_device(&dev)) {
+		ut_assert(!ret);
+		ut_assertnonnull(dev);
+	}
+
+	ut_assertok(device_remove(grandchild[0], DM_REMOVE_NORMAL));
+	/* try to get devices */
+	for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     ret = uclass_find_next_device(&dev)) {
+		ut_assert(!ret);
+		ut_assertnonnull(dev);
+	}
+
+	ut_assertok(device_remove(grandchild[1], DM_REMOVE_NORMAL));
+	/* try to get devices */
+	for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     ret = uclass_find_next_device(&dev)) {
+		ut_assert(!ret);
+		ut_assertnonnull(dev);
+	}
+
+	/* Try the same with unbind */
+	ut_assertok(device_unbind(top[3]));
+	ut_assertok(device_unbind(top[4]));
+	ut_assertok(device_unbind(top[5]));
+	ut_assertok(device_unbind(top[2]));
+
+	ut_assertok(device_unbind(grandchild[0]));
+	ut_assertok(device_unbind(grandchild[1]));
+
+	return 0;
+}
+DM_TEST(dm_test_device_reparent, 0);
+
 /* Test that pre-relocation devices work as expected */
 static int dm_test_pre_reloc(struct unit_test_state *uts)
 {