First set of u-boot-at91 features for the 2023.04 cycle

-----BEGIN PGP SIGNATURE-----
 
 iQFQBAABCgA6FiEEqxhEmNJ6d7ZdeFLIHrMeAg6sL8gFAmO2mnEcHGV1Z2VuLmhy
 aXN0ZXZAbWljcm9jaGlwLmNvbQAKCRAesx4CDqwvyBAOB/4y7e9y0jdKSWDwMdZj
 enXK/U/GREFyuiSdadil0aJl9WfayjwZkh7uHSTj4pi9ApNivfoqsL7WZYpJxhRD
 WlpNhs3TZ70i8CgKUosdzcpquAQZUZhg6iV5DCObrK6yNJRGOXLIwMOd+vw/Xz6/
 YTGqzivEDMBuH/9HLuC0m+26PEpff8nenNEjC2k8ssG26ojLz7oCQh2HoHcSgNRc
 HkEYlFJ/Le8kM8Ak2F3ebmsfgMTnFrRVwV1BsZa5vO0BrMYgJCORsl7Cnfcw6/2N
 LEHG7kwlSorJeETn/gkLiZ+NyqzU+oFH0jGRZ5Ciqg1qcCO3k9yBMgWQzd7nTL6C
 5oZA
 =Ocdd
 -----END PGP SIGNATURE-----

Merge tag 'u-boot-at91-2023.04-a' of https://source.denx.de/u-boot/custodians/u-boot-at91 into next

First set of u-boot-at91 features for the 2023.04 cycle:

This feature set includes the new DM-based NAND flash driver (old non-DM
driver is still kept for backwards compatibility), and the move to DM
NAND flash driver for sam9x60ek board. Feature set also includes
devicetree alignment for sama7g5 with Linux, devicetree alignment on USB
with Linux for all boards (sama5, sam9x60), chip id for sama7g5, minor
configs and tweaks.
This commit is contained in:
Tom Rini 2023-01-06 11:53:26 -05:00
commit b82f12b642
44 changed files with 4841 additions and 107 deletions

View file

@ -409,6 +409,7 @@ T: git https://source.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/memory/atmel-ebi.c
F: drivers/misc/microchip_flexcom.c
F: drivers/timer/atmel_tcb_timer.c
F: include/dt-bindings/mfd/atmel-flexcom.h

View file

@ -49,6 +49,13 @@
atmel,pins =
<AT91_PIOD 14 AT91_PERIPH_GPIO AT91_PINCTRL_PULL_UP>;
};
usb1 {
pinctrl_usb_default: usb_default {
atmel,pins = <AT91_PIOD 15 AT91_PERIPH_GPIO AT91_PINCTRL_NONE
AT91_PIOD 18 AT91_PERIPH_GPIO AT91_PINCTRL_NONE>;
};
};
};
};
};
@ -89,3 +96,17 @@
phy-mode = "rmii";
status = "okay";
};
&usb1 {
num-ports = <3>;
atmel,vbus-gpio = <0
&pioD 15 GPIO_ACTIVE_HIGH
&pioD 18 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usb_default>;
status = "okay";
};
&usb2 {
status = "okay";
};

View file

@ -143,7 +143,32 @@
pinmux = <PIN_PC9__GPIO>;
bias-pull-up;
};
pinctrl_usb_default: usb_default {
pinmux = <PIN_PA10__GPIO>;
bias-disable;
};
pinctrl_usba_vbus: usba_vbus {
pinmux = <PIN_PA16__GPIO>;
bias-disable;
};
};
};
};
};
&usb1 {
num-ports = <3>;
atmel,vbus-gpio = <0
&pioA PIN_PA10 GPIO_ACTIVE_HIGH
0
>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usb_default>;
status = "okay";
};
&usb2 {
status = "okay";
};

View file

@ -154,7 +154,29 @@
<PIN_PA13__SDMMC0_CD>;
bias-disable;
};
pinctrl_usb_default: usb_default {
pinmux = <PIN_PC17__GPIO>;
bias-disable;
};
pinctrl_usba_vbus: usba_vbus {
pinmux = <PIN_PD23__GPIO>;
bias-disable;
};
};
};
};
};
&usb1 {
num-ports = <3>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usb_default>;
status = "okay";
};
&usb2 {
phy_type = "hsic";
status = "okay";
};

View file

@ -10,13 +10,88 @@
*
*/
#include "sama7g5-pinfunc.h"
#include <dt-bindings/reset/sama7g5-reset.h>
#include <dt-bindings/clock/at91.h>
/ {
chosen {
u-boot,dm-pre-reloc;
};
utmi {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <0>;
usb_phy0: phy@0 {
compatible = "microchip,sama7g5-usb-phy";
sfr-phandle = <&sfr>;
reg = <0>;
clocks = <&utmi_clk USB_UTMI1>;
clock-names = "utmi_clk";
status = "disabled";
#phy-cells = <0>;
};
usb_phy1: phy@1 {
compatible = "microchip,sama7g5-usb-phy";
sfr-phandle = <&sfr>;
reg = <1>;
clocks = <&utmi_clk USB_UTMI2>;
clock-names = "utmi_clk";
status = "disabled";
#phy-cells = <0>;
};
usb_phy2: phy@2 {
compatible = "microchip,sama7g5-usb-phy";
sfr-phandle = <&sfr>;
reg = <2>;
clocks = <&utmi_clk USB_UTMI3>;
clock-names = "utmi_clk";
status = "disabled";
#phy-cells = <0>;
};
};
utmi_clk: utmi-clk {
compatible = "microchip,sama7g5-utmi-clk";
sfr-phandle = <&sfr>;
#clock-cells = <1>;
clocks = <&pmc PMC_TYPE_CORE 27>;
clock-names = "utmi_clk";
resets = <&reset_controller SAMA7G5_RESET_USB_PHY1>,
<&reset_controller SAMA7G5_RESET_USB_PHY2>,
<&reset_controller SAMA7G5_RESET_USB_PHY3>;
reset-names = "usb0_reset", "usb1_reset", "usb2_reset";
};
soc {
u-boot,dm-pre-reloc;
usb2: usb@400000 {
compatible = "microchip,sama7g5-ohci", "usb-ohci";
reg = <0x00400000 0x100000>;
interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&pmc PMC_TYPE_PERIPHERAL 106>, <&utmi_clk USB_UTMI1>, <&usb_clk>;
clock-names = "ohci_clk", "hclk", "uhpck";
status = "disabled";
};
usb3: usb@500000 {
compatible = "atmel,at91sam9g45-ehci", "usb-ehci";
reg = <0x00500000 0x100000>;
interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&usb_clk>, <&pmc PMC_TYPE_PERIPHERAL 106>;
clock-names = "usb_clk", "ehci_clk";
status = "disabled";
};
sfr: sfr@e1624000 {
compatible = "microchip,sama7g5-sfr", "syscon";
reg = <0xe1624000 0x4000>;
};
};
};
@ -38,6 +113,11 @@
&pioA {
u-boot,dm-pre-reloc;
pinctrl_usb_default: usb_default {
pinmux = <PIN_PC6__GPIO>;
bias-disable;
};
};
&pit64b0 {
@ -60,3 +140,31 @@
u-boot,dm-pre-reloc;
};
&usb2 {
num-ports = <3>;
atmel,vbus-gpio = <0
0
&pioA PIN_PC6 GPIO_ACTIVE_HIGH
>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usb_default>;
phys = <&usb_phy2>;
phy-names = "usb";
status = "okay";
};
&usb3 {
status = "okay";
};
&usb_phy0 {
status = "okay";
};
&usb_phy1 {
status = "okay";
};
&usb_phy2 {
status = "okay";
};

View file

@ -45,13 +45,13 @@
};
};
gpio_keys {
gpio-keys {
compatible = "gpio-keys";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_key_gpio_default>;
bp1 {
button {
label = "PB_USER";
gpios = <&pioA PIN_PA12 GPIO_ACTIVE_LOW>;
linux,code = <KEY_PROG1>;
@ -244,8 +244,8 @@
regulators {
vdd_3v3: VDD_IO {
regulator-name = "VDD_IO";
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <3700000>;
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-initial-mode = <2>;
regulator-allowed-modes = <2>, <4>;
regulator-always-on;
@ -264,8 +264,8 @@
vddioddr: VDD_DDR {
regulator-name = "VDD_DDR";
regulator-min-microvolt = <1300000>;
regulator-max-microvolt = <1450000>;
regulator-min-microvolt = <1350000>;
regulator-max-microvolt = <1350000>;
regulator-initial-mode = <2>;
regulator-allowed-modes = <2>, <4>;
regulator-always-on;
@ -285,8 +285,8 @@
vddcore: VDD_CORE {
regulator-name = "VDD_CORE";
regulator-min-microvolt = <1100000>;
regulator-max-microvolt = <1850000>;
regulator-min-microvolt = <1150000>;
regulator-max-microvolt = <1150000>;
regulator-initial-mode = <2>;
regulator-allowed-modes = <2>, <4>;
regulator-always-on;
@ -306,7 +306,7 @@
vddcpu: VDD_OTHER {
regulator-name = "VDD_OTHER";
regulator-min-microvolt = <1050000>;
regulator-max-microvolt = <1850000>;
regulator-max-microvolt = <1250000>;
regulator-initial-mode = <2>;
regulator-allowed-modes = <2>, <4>;
regulator-ramp-delay = <3125>;
@ -326,8 +326,8 @@
vldo1: LDO1 {
regulator-name = "LDO1";
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <3700000>;
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
regulator-always-on;
regulator-state-standby {
@ -707,7 +707,6 @@
ck_cd_rstn_vddsel {
pinmux = <PIN_PA0__SDMMC0_CK>,
<PIN_PA2__SDMMC0_RSTN>,
<PIN_PA14__SDMMC0_CD>,
<PIN_PA11__SDMMC0_DS>;
slew-rate = <0>;
bias-pull-up;

View file

@ -69,6 +69,50 @@
#size-cells = <1>;
ranges;
usb1: usb@600000 {
compatible = "atmel,at91rm9200-ohci", "usb-ohci";
reg = <0x00600000 0x100000>;
clocks = <&pmc PMC_TYPE_PERIPHERAL 22>, <&pmc PMC_TYPE_PERIPHERAL 22>, <&pmc PMC_TYPE_SYSTEM 21>;
clock-names = "ohci_clk", "hclk", "uhpck";
status = "disabled";
};
usb2: usb@700000 {
compatible = "atmel,at91sam9g45-ehci", "usb-ehci";
reg = <0x00700000 0x100000>;
clocks = <&pmc PMC_TYPE_CORE 8>, <&pmc PMC_TYPE_PERIPHERAL 22>;
clock-names = "usb_clk", "ehci_clk";
assigned-clocks = <&pmc PMC_TYPE_CORE 8>;
assigned-clock-rates = <480000000>;
status = "disabled";
};
ebi: ebi@10000000 {
compatible = "microchip,sam9x60-ebi";
#address-cells = <2>;
#size-cells = <1>;
atmel,smc = <&smc>;
microchip,sfr = <&sfr>;
reg = <0x10000000 0x60000000>;
ranges = <0x0 0x0 0x10000000 0x10000000
0x1 0x0 0x20000000 0x10000000
0x2 0x0 0x30000000 0x10000000
0x3 0x0 0x40000000 0x10000000
0x4 0x0 0x50000000 0x10000000
0x5 0x0 0x60000000 0x10000000>;
clocks = <&pmc PMC_TYPE_CORE 11>;
status = "disabled";
nand_controller: nand-controller {
compatible = "microchip,sam9x60-nand-controller";
ecc-engine = <&pmecc>;
#address-cells = <2>;
#size-cells = <1>;
ranges;
status = "disabled";
};
};
sdhci0: sdhci-host@80000000 {
compatible = "microchip,sam9x60-sdhci";
reg = <0x80000000 0x300>;
@ -82,6 +126,19 @@
pinctrl-0 = <&pinctrl_sdhci0>;
};
sdhci1: sdhci-host@90000000 {
compatible = "microchip,sam9x60-sdhci";
reg = <0x90000000 0x300>;
clocks = <&pmc PMC_TYPE_PERIPHERAL 26>, <&pmc PMC_TYPE_GCK 26>;
clock-names = "hclock", "multclk";
assigned-clocks = <&pmc PMC_TYPE_GCK 26>;
assigned-clock-rates = <100000000>;
assigned-clock-parents = <&pmc PMC_TYPE_CORE 10>; /* ID_PLL_A_DIV */
bus-width = <4>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sdhci1>;
};
apb {
compatible = "simple-bus";
#address-cells = <1>;
@ -119,6 +176,11 @@
status = "disabled";
};
sfr: sfr@f8050000 {
compatible = "microchip,sam9x60-sfr", "syscon";
reg = <0xf8050000 0x100>;
};
dbgu: serial@fffff200 {
compatible = "atmel,at91sam9260-dbgu", "atmel,at91sam9260-usart";
reg = <0xfffff200 0x200>;
@ -180,6 +242,29 @@
(AT91_PINCTRL_PULL_UP | AT91_PINCTRL_DRIVE_STRENGTH_HI | AT91_PINCTRL_SLEWRATE_ENA)>; /* PA20 DAT3 periph A with pullup */
};
};
sdhci1 {
pinctrl_sdhci1: sdhci1 {
atmel,pins =
<AT91_PIOA 13 AT91_PERIPH_B (AT91_PINCTRL_DRIVE_STRENGTH_HI) /* PA13 CK periph B */
AT91_PIOA 12 AT91_PERIPH_B (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_DRIVE_STRENGTH_HI) /* PA12 CMD periph B with pullup */
AT91_PIOA 11 AT91_PERIPH_B (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_DRIVE_STRENGTH_HI) /* PA11 DAT0 periph B with pullup */
AT91_PIOA 2 AT91_PERIPH_B (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_DRIVE_STRENGTH_HI) /* PA2 DAT1 periph B with pullup */
AT91_PIOA 3 AT91_PERIPH_B (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_DRIVE_STRENGTH_HI) /* PA3 DAT2 periph B with pullup */
AT91_PIOA 4 AT91_PERIPH_B (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_DRIVE_STRENGTH_HI)>; /* PA4 DAT3 periph B with pullup */
};
};
};
pmecc: ecc-engine@ffffe000 {
compatible = "microchip,sam9x60-pmecc", "atmel,at91sam9g45-pmecc";
reg = <0xffffe000 0x300>,
<0xffffe600 0x100>;
};
smc: smc@ffffea00 {
compatible = "microchip,sam9x60-smc", "atmel,at91sam9260-smc", "syscon";
reg = <0xffffea00 0x100>;
};
pioA: gpio@fffff400 {

View file

@ -80,6 +80,44 @@
};
pinctrl {
nand {
pinctrl_nand_oe_we: nand-oe-we-0 {
atmel,pins =
<AT91_PIOD 0 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
AT91_PIOD 1 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)>;
};
pinctrl_nand_rb: nand-rb-0 {
atmel,pins =
<AT91_PIOD 5 AT91_PERIPH_GPIO AT91_PINCTRL_PULL_UP>;
};
pinctrl_nand_cs: nand-cs-0 {
atmel,pins =
<AT91_PIOD 4 AT91_PERIPH_GPIO AT91_PINCTRL_PULL_UP>;
};
};
ebi {
pinctrl_ebi_data_0_7: ebi-data-lsb-0 {
atmel,pins =
<AT91_PIOD 6 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
AT91_PIOD 7 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
AT91_PIOD 8 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
AT91_PIOD 9 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
AT91_PIOD 10 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
AT91_PIOD 11 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
AT91_PIOD 12 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
AT91_PIOD 13 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)>;
};
pinctrl_ebi_addr_nand: ebi-addr-0 {
atmel,pins =
<AT91_PIOD 2 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)
AT91_PIOD 3 AT91_PERIPH_A (AT91_PINCTRL_NONE | AT91_PINCTRL_SLEWRATE_DIS)>;
};
};
pinctrl_qspi: qspi {
atmel,pins =
<AT91_PIOB 19 AT91_PERIPH_A AT91_PINCTRL_NONE
@ -101,6 +139,78 @@
<AT91_PIOD 14 AT91_PERIPH_GPIO AT91_PINCTRL_PULL_UP>;
};
usb1 {
pinctrl_usb_default: usb_default {
atmel,pins = <AT91_PIOD 15 AT91_PERIPH_GPIO AT91_PINCTRL_NONE
AT91_PIOD 16 AT91_PERIPH_GPIO AT91_PINCTRL_NONE>;
};
};
};
};
};
};
&ebi {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ebi_addr_nand &pinctrl_ebi_data_0_7>;
status = "okay";
nand_controller: nand-controller {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_nand_oe_we &pinctrl_nand_cs &pinctrl_nand_rb>;
status = "okay";
nand@3 {
reg = <0x3 0x0 0x800000>;
rb-gpios = <&pioD 5 GPIO_ACTIVE_HIGH>;
cs-gpios = <&pioD 4 GPIO_ACTIVE_HIGH>;
nand-bus-width = <8>;
nand-ecc-mode = "hw";
nand-ecc-strength = <8>;
nand-ecc-step-size = <512>;
nand-on-flash-bbt;
label = "atmel_nand";
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
at91bootstrap@0 {
label = "at91bootstrap";
reg = <0x0 0x40000>;
};
uboot@40000 {
label = "u-boot";
reg = <0x40000 0xc0000>;
};
ubootenvred@100000 {
label = "U-Boot Env Redundant";
reg = <0x100000 0x40000>;
};
ubootenv@140000 {
label = "U-Boot Env";
reg = <0x140000 0x40000>;
};
dtb@180000 {
label = "device tree";
reg = <0x180000 0x80000>;
};
kernel@200000 {
label = "kernel";
reg = <0x200000 0x600000>;
};
rootfs@800000 {
label = "rootfs";
reg = <0x800000 0x1f800000>;
};
};
};
};
@ -110,3 +220,17 @@
phy-mode = "rmii";
status = "okay";
};
&usb1 {
num-ports = <3>;
atmel,vbus-gpio = <0
&pioD 15 GPIO_ACTIVE_HIGH
&pioD 16 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usb_default>;
status = "okay";
};
&usb2 {
status = "okay";
};

View file

@ -84,7 +84,6 @@
reg = <0xf0014000 0x160>;
#address-cells = <1>;
#size-cells = <0>;
#interrupt-cells = <1>;
u-boot,dm-pre-reloc;
main: mainck {

View file

@ -15,6 +15,7 @@
#include <dt-bindings/clk/at91.h>
#include <dt-bindings/dma/at91.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/mfd/at91-usart.h>
/ {
model = "Microchip SAMA7G5 family SoC";
@ -195,11 +196,11 @@
<GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&pmc PMC_TYPE_PERIPHERAL 11>;
interrupt-controller;
#interrupt-cells = <2>;
gpio-controller;
#gpio-cells = <2>;
clocks = <&pmc PMC_TYPE_PERIPHERAL 11>;
};
pmc: pmc@e0018000 {
@ -211,6 +212,13 @@
clock-names = "td_slck", "md_slck", "main_xtal", "main_rc";
};
reset_controller: reset-controller@e001d000 {
compatible = "microchip,sama7g5-rstc";
reg = <0xe001d000 0xc>, <0xe001d0e4 0x4>;
#reset-cells = <1>;
clocks = <&clk32k 0>;
};
shdwc: shdwc@e001d010 {
compatible = "microchip,sama7g5-shdwc", "syscon";
reg = <0xe001d010 0x10>;
@ -229,13 +237,6 @@
clocks = <&clk32k 0>;
};
reset_controller: rstc@e001d000 {
compatible = "microchip,sama7g5-rstc", "microchip,sam9x60-rstc";
reg = <0xe001d000 0xc>, <0xe001d0e4 0x4>;
#reset-cells = <1>;
clocks = <&clk32k 0>;
};
clk32k: clock-controller@e001d050 {
compatible = "microchip,sama7g5-sckc", "microchip,sam9x60-sckc";
reg = <0xe001d050 0x4>;
@ -620,6 +621,7 @@
uart0: serial@200 {
compatible = "atmel,at91sam9260-usart";
reg = <0x200 0x200>;
atmel,usart-mode = <AT91_USART_MODE_SERIAL>;
interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&pmc PMC_TYPE_PERIPHERAL 38>;
clock-names = "usart";
@ -668,6 +670,7 @@
uart3: serial@200 {
compatible = "atmel,at91sam9260-usart";
reg = <0x200 0x200>;
atmel,usart-mode = <AT91_USART_MODE_SERIAL>;
interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&pmc PMC_TYPE_PERIPHERAL 41>;
clock-names = "usart";
@ -711,6 +714,7 @@
uart4: serial@200 {
compatible = "atmel,at91sam9260-usart";
reg = <0x200 0x200>;
atmel,usart-mode = <AT91_USART_MODE_SERIAL>;
interrupts = <GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&pmc PMC_TYPE_PERIPHERAL 42>;
clock-names = "usart";
@ -736,6 +740,7 @@
uart7: serial@200 {
compatible = "atmel,at91sam9260-usart";
reg = <0x200 0x200>;
atmel,usart-mode = <AT91_USART_MODE_SERIAL>;
interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&pmc PMC_TYPE_PERIPHERAL 45>;
clock-names = "usart";
@ -884,9 +889,9 @@
#address-cells = <1>;
#size-cells = <0>;
atmel,fifo-size = <32>;
dmas = <&dma0 AT91_XDMAC_DT_PERID(27)>,
<&dma0 AT91_XDMAC_DT_PERID(28)>;
dma-names = "rx", "tx";
dmas = <&dma0 AT91_XDMAC_DT_PERID(28)>,
<&dma0 AT91_XDMAC_DT_PERID(27)>;
dma-names = "tx", "rx";
status = "disabled";
};
};

View file

@ -4,7 +4,31 @@
* Eugen Hristev <eugen.hristev@microchip.com>
*/
#include <asm/arch/sama7g5.h>
char *get_cpu_name(void)
{
return "SAMA7G5";
unsigned int extension_id = get_extension_chip_id();
if (cpu_is_sama7g5())
switch (extension_id) {
case ARCH_EXID_SAMA7G51:
return "SAMA7G51";
case ARCH_EXID_SAMA7G52:
return "SAMA7G52";
case ARCH_EXID_SAMA7G53:
return "SAMA7G53";
case ARCH_EXID_SAMA7G54:
return "SAMA7G54";
case ARCH_EXID_SAMA7G54_D1G:
return "SAMA7G54 1Gb DDR3L SiP";
case ARCH_EXID_SAMA7G54_D2G:
return "SAMA7G54 2Gb DDR3L SiP";
case ARCH_EXID_SAMA7G54_D4G:
return "SAMA7G54 4Gb DDR3L SiP";
default:
return "Unknown CPU type";
}
else
return "Unknown CPU type";
}

View file

@ -0,0 +1,59 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Microchip SFR (Special Function Registers) registers for SAMA7 family.
*
* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries
*
* Author: Cristian Birsan <cristian.birsan@microchip.com>
*/
#ifndef _LINUX_MFD_SYSCON_AT91_SAMA7_SFR_H
#define _LINUX_MFD_SYSCON_AT91_SAMA7_SFR_H
#define SAMA7_SFR_OHCIICR 0x00 /* OHCI INT Configuration Register */
#define SAMA7_SFR_OHCIISR 0x04 /* OHCI INT Status Register */
/* 0x08 ~ 0xe3: Reserved */
#define SAMA7_SFR_WPMR 0xe4 /* Write Protection Mode Register */
#define SAMA7_SFR_WPSR 0xe4 /* Write Protection Status Register */
/* 0xec ~ 0x200b: Reserved */
#define SAMA7_SFR_DEBUG 0x200c /* Debug Register */
/* 0x2010 ~ 0x2027: Reserved */
#define SAMA7_SFR_EHCIOHCI 0x2020 /* EHCI OHCI Clock Configuration Reg */
#define SAMA7_SFR_HSS_AXI_QOS 0x2028 /* HSS AXI QOS Register */
#define SAMA7_SFR_UDDRC 0x202c /* UDDRC Register */
#define SAMA7_SFR_CAN_SRAM_SEL 0x2030 /* CAN SRAM Select. Register */
/* 0x2034 ~ 0x203f: Reserved */
#define SAMA7_SFR_UTMI0 0x2040
#define SAMA7_SFR_UTMI0R(x) (SAMA7_SFR_UTMI0 + 4 * (x))
#define SAMA7_SFR_UTMI0R0 0x2040 /* UTMI0 Configuration Register */
#define SAMA7_SFR_UTMI0R1 0x2044 /* UTMI1 Configuration Register */
#define SAMA7_SFR_UTMI0R2 0x2048 /* UTMI2 Configuration Register */
/* Field definitions */
#define SAMA7_SFR_OHCIICR_ARIE BIT(0)
#define SAMA7_SFR_OHCIICR_APPSTART BIT(1)
#define SAMA7_SFR_OHCIICR_USB_SUSP(x) BIT(8 + (x))
#define SAMA7_SFR_OHCIICR_USB_SUSPEND GENMASK(10, 8)
#define SAMA7_SFR_OHCIISR_RIS(x) BIT(x)
#define SAMA7_SFR_WPMR_WPEN BIT(0)
#define SAMA7_SFR_WPMR_KEY 0x53465200 /* SFR in ASCII*/
#define SAMA7_SFR_WPMR_WPKEY_MASK GENMASK(31, 8)
#define SAMA7_SFR_WPSR_WPSRC_MASK GENMASK(23, 8)
#define SAMA7_SFR_WPSR_WPVS_MASK BIT(0)
#define SAMA7_SFR_CAN_SRAM_UPPER(x) BIT(x)
#define SAMA7_SFR_UTMI_RX_VBUS BIT(25) /* VBUS Valid bit */
#define SAMA7_SFR_UTMI_RX_TX_PREEM_AMP_TUNE_1X BIT(23) /* TXPREEMPAMPTUNE 1x */
#define SAMA7_SFR_UTMI_COMMONON BIT(3) /* PLL Common ON bit */
#define SAMA7_SFR_EHCIOHCI_PHYCLK BIT(1) /* Alternate PHY Clk */
#endif /* _LINUX_MFD_SYSCON_AT91_SAMA7_SFR_H */

View file

@ -67,7 +67,35 @@
#define ATMEL_BASE_PIT64BC ATMEL_BASE_PIT64B0
/* SAMA7G5 series chip id definitions */
#define ARCH_ID_SAMA7G5 0x80162100
#define ARCH_EXID_SAMA7G51 0x00000003
#define ARCH_EXID_SAMA7G52 0x00000002
#define ARCH_EXID_SAMA7G53 0x00000001
#define ARCH_EXID_SAMA7G54 0x00000000
#define ARCH_EXID_SAMA7G54_D1G 0x00000018
#define ARCH_EXID_SAMA7G54_D2G 0x00000020
#define ARCH_EXID_SAMA7G54_D4G 0x00000028
#define cpu_is_sama7g5() (get_chip_id() == ARCH_ID_SAMA7G5)
#define cpu_is_sama7g51() (cpu_is_sama7g5() && \
(get_extension_chip_id() == ARCH_EXID_SAMA7G51))
#define cpu_is_sama7g52() (cpu_is_sama7g5() && \
(get_extension_chip_id() == ARCH_EXID_SAMA7G52))
#define cpu_is_sama7g53() (cpu_is_sama7g5() && \
(get_extension_chip_id() == ARCH_EXID_SAMA7G53))
#define cpu_is_sama7g54() (cpu_is_sama7g5() && \
(get_extension_chip_id() == ARCH_EXID_SAMA7G54))
#define cpu_is_sama7g54d1g() (cpu_is_sama7g5() && \
(get_extension_chip_id() == ARCH_EXID_SAMA7G54_D1G))
#define cpu_is_sama7g54d2g() (cpu_is_sama7g5() && \
(get_extension_chip_id() == ARCH_EXID_SAMA7G54_D2G))
#define cpu_is_sama7g54d4g() (cpu_is_sama7g5() && \
(get_extension_chip_id() == ARCH_EXID_SAMA7G54_D4G))
#ifndef __ASSEMBLY__
unsigned int get_chip_id(void);
unsigned int get_extension_chip_id(void);
char *get_cpu_name(void);
#endif

View file

@ -5,3 +5,4 @@ S: Maintained
F: board/atmel/sam9x60_curiosity/
F: include/configs/sam9x60_curiosity.h
F: configs/sam9x60_curiosity_mmc_defconfig
F: configs/sam9x60_curiosity_mmc1_defconfig

View file

@ -25,6 +25,13 @@ DECLARE_GLOBAL_DATA_PTR;
void at91_prepare_cpu_var(void);
static void board_leds_init(void)
{
at91_set_pio_output(AT91_PIO_PORTD, 17, 0); /* LED RED */
at91_set_pio_output(AT91_PIO_PORTD, 19, 0); /* LED GREEN */
at91_set_pio_output(AT91_PIO_PORTD, 21, 1); /* LED BLUE */
}
int board_late_init(void)
{
at91_prepare_cpu_var();
@ -62,6 +69,9 @@ int board_init(void)
{
/* address of boot parameters */
gd->bd->bi_boot_params = gd->bd->bi_dram[0].start + 0x100;
board_leds_init();
return 0;
}

View file

@ -24,61 +24,12 @@ DECLARE_GLOBAL_DATA_PTR;
void at91_prepare_cpu_var(void);
#ifdef CONFIG_CMD_NAND
static void sam9x60ek_nand_hw_init(void)
static void board_leds_init(void)
{
struct at91_smc *smc = (struct at91_smc *)ATMEL_BASE_SMC;
struct atmel_sfr *sfr = (struct atmel_sfr *)ATMEL_BASE_SFR;
unsigned int csa;
at91_pio3_set_a_periph(AT91_PIO_PORTD, 0, 1); /* NAND OE */
at91_pio3_set_a_periph(AT91_PIO_PORTD, 1, 1); /* NAND WE */
at91_pio3_set_a_periph(AT91_PIO_PORTD, 2, 0); /* NAND ALE */
at91_pio3_set_a_periph(AT91_PIO_PORTD, 3, 0); /* NAND CLE */
/* Enable NandFlash */
at91_set_gpio_output(CFG_SYS_NAND_ENABLE_PIN, 1);
/* Configure RDY/BSY */
at91_set_gpio_input(CFG_SYS_NAND_READY_PIN, 1);
at91_pio3_set_a_periph(AT91_PIO_PORTD, 6, 1);
at91_pio3_set_a_periph(AT91_PIO_PORTD, 7, 1);
at91_pio3_set_a_periph(AT91_PIO_PORTD, 8, 1);
at91_pio3_set_a_periph(AT91_PIO_PORTD, 9, 1);
at91_pio3_set_a_periph(AT91_PIO_PORTD, 10, 1);
at91_pio3_set_a_periph(AT91_PIO_PORTD, 11, 1);
at91_pio3_set_a_periph(AT91_PIO_PORTD, 12, 1);
at91_pio3_set_a_periph(AT91_PIO_PORTD, 13, 1);
at91_periph_clk_enable(ATMEL_ID_PIOD);
/* Enable CS3 */
csa = readl(&sfr->ebicsa);
csa |= AT91_SFR_CCFG_EBI_CSA(3, 1) | AT91_SFR_CCFG_NFD0_ON_D16;
/* Configure IO drive */
csa &= ~AT91_SFR_CCFG_EBI_DRIVE_SAM9X60;
writel(csa, &sfr->ebicsa);
/* Configure SMC CS3 for NAND/SmartMedia */
writel(AT91_SMC_SETUP_NWE(4), &smc->cs[3].setup);
writel(AT91_SMC_PULSE_NWE(10) | AT91_SMC_PULSE_NCS_WR(20) |
AT91_SMC_PULSE_NRD(10) | AT91_SMC_PULSE_NCS_RD(20),
&smc->cs[3].pulse);
writel(AT91_SMC_CYCLE_NWE(20) | AT91_SMC_CYCLE_NRD(20),
&smc->cs[3].cycle);
writel(AT91_SMC_MODE_RM_NRD | AT91_SMC_MODE_WM_NWE |
#ifdef CONFIG_SYS_NAND_DBW_16
AT91_SMC_MODE_DBW_16 |
#else /* CONFIG_SYS_NAND_DBW_8 */
AT91_SMC_MODE_DBW_8 |
#endif
AT91_SMC_MODE_TDF | AT91_SMC_MODE_TDF_CYCLE(15),
&smc->cs[3].mode);
at91_set_pio_output(AT91_PIO_PORTB, 11, 0); /* LED RED */
at91_set_pio_output(AT91_PIO_PORTB, 12, 0); /* LED GREEN */
at91_set_pio_output(AT91_PIO_PORTB, 13, 1); /* LED BLUE */
}
#endif
#ifdef CONFIG_BOARD_LATE_INIT
int board_late_init(void)
@ -122,9 +73,8 @@ int board_init(void)
/* address of boot parameters */
gd->bd->bi_boot_params = CFG_SYS_SDRAM_BASE + 0x100;
#ifdef CONFIG_CMD_NAND
sam9x60ek_nand_hw_init();
#endif
board_leds_init();
return 0;
}

View file

@ -0,0 +1,92 @@
CONFIG_ARM=y
CONFIG_SKIP_LOWLEVEL_INIT=y
CONFIG_ARCH_AT91=y
CONFIG_TEXT_BASE=0x23f00000
CONFIG_SYS_MALLOC_LEN=0x81000
CONFIG_SYS_MALLOC_F_LEN=0x8000
CONFIG_TARGET_SAM9X60_CURIOSITY=y
CONFIG_ATMEL_LEGACY=y
CONFIG_NR_DRAM_BANKS=8
CONFIG_ENV_SIZE=0x4000
CONFIG_DM_GPIO=y
CONFIG_DEFAULT_DEVICE_TREE="at91-sam9x60_curiosity"
CONFIG_SYS_PROMPT="U-Boot> "
CONFIG_DEBUG_UART_BASE=0xfffff200
CONFIG_DEBUG_UART_CLOCK=200000000
CONFIG_DEBUG_UART_BOARD_INIT=y
CONFIG_SYS_LOAD_ADDR=0x22000000
CONFIG_DEBUG_UART=y
CONFIG_HAS_CUSTOM_SYS_INIT_SP_ADDR=y
CONFIG_CUSTOM_SYS_INIT_SP_ADDR=0x2000bf00
CONFIG_FIT=y
CONFIG_SD_BOOT=y
CONFIG_BOOTDELAY=3
CONFIG_USE_BOOTARGS=y
CONFIG_BOOTARGS="console=ttyS0,115200 root=/dev/mmcblk1p2 rw rootwait"
CONFIG_USE_BOOTCOMMAND=y
CONFIG_BOOTCOMMAND="fatload mmc 1:1 0x21000000 at91-sam9x60_curiosity.dtb; fatload mmc 1:1 0x22000000 zImage; bootz 0x22000000 - 0x21000000"
CONFIG_SYS_CONSOLE_IS_IN_ENV=y
# CONFIG_DISPLAY_BOARDINFO is not set
CONFIG_MISC_INIT_R=y
CONFIG_HUSH_PARSER=y
CONFIG_SYS_CBSIZE=256
CONFIG_SYS_PBSIZE=281
CONFIG_CMD_BOOTZ=y
CONFIG_CMD_CLK=y
CONFIG_CMD_DM=y
CONFIG_CMD_GPIO=y
CONFIG_CMD_I2C=y
CONFIG_CMD_MMC=y
CONFIG_CMD_NAND=y
CONFIG_CMD_NAND_TRIMFFS=y
# CONFIG_CMD_SETEXPR is not set
CONFIG_CMD_DHCP=y
CONFIG_BOOTP_BOOTFILESIZE=y
CONFIG_CMD_MII=y
CONFIG_CMD_PING=y
CONFIG_CMD_HASH=y
CONFIG_HASH_VERIFY=y
CONFIG_CMD_FAT=y
CONFIG_OF_CONTROL=y
CONFIG_ENV_IS_IN_FAT=y
CONFIG_ENV_FAT_DEVICE_AND_PART="1:1"
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
CONFIG_DM=y
CONFIG_REGMAP=y
CONFIG_SYSCON=y
CONFIG_CLK=y
CONFIG_CLK_CCF=y
CONFIG_CLK_AT91=y
CONFIG_AT91_GENERIC_CLK=y
CONFIG_AT91_SAM9X60_PLL=y
CONFIG_CPU=y
CONFIG_AT91_GPIO=y
CONFIG_DM_I2C=y
CONFIG_SYS_I2C_AT91=y
CONFIG_ATMEL_EBI=y
CONFIG_MFD_ATMEL_SMC=y
CONFIG_I2C_EEPROM=y
CONFIG_MICROCHIP_FLEXCOM=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_ATMEL=y
CONFIG_MTD=y
CONFIG_DM_MTD=y
CONFIG_MTD_RAW_NAND=y
CONFIG_DM_NAND_ATMEL=y
CONFIG_SYS_NAND_ONFI_DETECTION=y
CONFIG_PHY_MICREL=y
CONFIG_MACB=y
CONFIG_PINCTRL=y
CONFIG_PINCTRL_AT91=y
CONFIG_DM_SERIAL=y
CONFIG_DEBUG_UART_ANNOUNCE=y
CONFIG_ATMEL_USART=y
CONFIG_SYSRESET=y
CONFIG_SYSRESET_AT91=y
CONFIG_TIMER=y
CONFIG_MCHP_PIT64B_TIMER=y
CONFIG_W1=y
CONFIG_W1_GPIO=y
CONFIG_W1_EEPROM=y
CONFIG_W1_EEPROM_DS24XXX=y
CONFIG_OF_LIBFDT_OVERLAY=y

View file

@ -52,6 +52,8 @@ CONFIG_OF_CONTROL=y
CONFIG_ENV_IS_IN_FAT=y
CONFIG_ENV_FAT_DEVICE_AND_PART="0:1"
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
CONFIG_REGMAP=y
CONFIG_SYSCON=y
CONFIG_CLK=y
CONFIG_CLK_CCF=y
CONFIG_CLK_AT91=y
@ -61,15 +63,16 @@ CONFIG_CPU=y
CONFIG_AT91_GPIO=y
CONFIG_DM_I2C=y
CONFIG_SYS_I2C_AT91=y
CONFIG_ATMEL_EBI=y
CONFIG_MFD_ATMEL_SMC=y
CONFIG_I2C_EEPROM=y
CONFIG_MICROCHIP_FLEXCOM=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_ATMEL=y
CONFIG_MTD=y
CONFIG_DM_MTD=y
CONFIG_MTD_RAW_NAND=y
CONFIG_NAND_ATMEL=y
CONFIG_ATMEL_NAND_HW_PMECC=y
CONFIG_PMECC_CAP=8
CONFIG_DM_NAND_ATMEL=y
CONFIG_SYS_NAND_ONFI_DETECTION=y
CONFIG_DM_SPI_FLASH=y
CONFIG_SF_DEFAULT_SPEED=50000000

View file

@ -54,6 +54,8 @@ CONFIG_OF_CONTROL=y
CONFIG_ENV_IS_IN_NAND=y
CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
CONFIG_REGMAP=y
CONFIG_SYSCON=y
CONFIG_CLK=y
CONFIG_CLK_CCF=y
CONFIG_CLK_AT91=y
@ -63,14 +65,15 @@ CONFIG_CPU=y
CONFIG_AT91_GPIO=y
CONFIG_DM_I2C=y
CONFIG_SYS_I2C_AT91=y
CONFIG_ATMEL_EBI=y
CONFIG_MFD_ATMEL_SMC=y
CONFIG_I2C_EEPROM=y
CONFIG_MICROCHIP_FLEXCOM=y
CONFIG_GENERIC_ATMEL_MCI=y
CONFIG_MTD=y
CONFIG_DM_MTD=y
# CONFIG_SYS_NAND_USE_FLASH_BBT is not set
CONFIG_NAND_ATMEL=y
CONFIG_ATMEL_NAND_HW_PMECC=y
CONFIG_PMECC_CAP=8
CONFIG_DM_NAND_ATMEL=y
CONFIG_SYS_NAND_ONFI_DETECTION=y
CONFIG_DM_SPI_FLASH=y
CONFIG_SF_DEFAULT_SPEED=50000000

View file

@ -54,6 +54,8 @@ CONFIG_OF_CONTROL=y
CONFIG_ENV_IS_IN_SPI_FLASH=y
CONFIG_ENV_SPI_MAX_HZ=50000000
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
CONFIG_REGMAP=y
CONFIG_SYSCON=y
CONFIG_CLK=y
CONFIG_CLK_CCF=y
CONFIG_CLK_AT91=y
@ -63,6 +65,8 @@ CONFIG_CPU=y
CONFIG_AT91_GPIO=y
CONFIG_DM_I2C=y
CONFIG_SYS_I2C_AT91=y
CONFIG_ATMEL_EBI=y
CONFIG_MFD_ATMEL_SMC=y
CONFIG_I2C_EEPROM=y
CONFIG_MICROCHIP_FLEXCOM=y
CONFIG_GENERIC_ATMEL_MCI=y
@ -70,9 +74,7 @@ CONFIG_MTD=y
CONFIG_DM_MTD=y
CONFIG_MTD_RAW_NAND=y
# CONFIG_SYS_NAND_USE_FLASH_BBT is not set
CONFIG_NAND_ATMEL=y
CONFIG_ATMEL_NAND_HW_PMECC=y
CONFIG_PMECC_CAP=8
CONFIG_DM_NAND_ATMEL=y
CONFIG_SYS_NAND_ONFI_DETECTION=y
CONFIG_DM_SPI_FLASH=y
CONFIG_SPI_FLASH_MACRONIX=y

View file

@ -62,6 +62,8 @@ source "drivers/mailbox/Kconfig"
source "drivers/memory/Kconfig"
source "drivers/mfd/Kconfig"
source "drivers/misc/Kconfig"
source "drivers/mmc/Kconfig"

View file

@ -103,6 +103,7 @@ obj-$(CONFIG_QE) += qe/
obj-$(CONFIG_U_QE) += qe/
obj-y += mailbox/
obj-y += memory/
obj-y += mfd/
obj-y += mtd/
obj-y += pwm/
obj-y += reset/

View file

@ -13,6 +13,13 @@ config MEMORY
SRAM, Ethernet adapters, FPGAs, etc.
For now this uclass has no methods yet.
config ATMEL_EBI
bool "Support for Atmel EBI"
help
Driver for Atmel EBI controller. This is a dummy
driver. Doesn't provide an access to EBI controller. Select
this option to enable the NAND flash controller driver
config SANDBOX_MEMORY
bool "Enable Sandbox Memory Controller driver"
depends on SANDBOX && MEMORY

View file

@ -2,5 +2,6 @@
obj-$(CONFIG_MEMORY) += memory-uclass.o
obj-$(CONFIG_SANDBOX_MEMORY) += memory-sandbox.o
obj-$(CONFIG_STM32_FMC2_EBI) += stm32-fmc2-ebi.o
obj-$(CONFIG_ATMEL_EBI) += atmel_ebi.o
obj-$(CONFIG_TI_AEMIF) += ti-aemif.o
obj-$(CONFIG_TI_GPMC) += ti-gpmc.o

View file

@ -0,0 +1,37 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries
*/
#include <dm/device.h>
#include <dm/read.h>
#include <dm/uclass.h>
#include <fdtdec.h>
static int atmel_ebi_probe(struct udevice *dev)
{
int ret;
struct udevice *ndev;
ret = uclass_get_device_by_driver(UCLASS_MTD,
DM_DRIVER_GET(atmel_nand_controller),
&ndev);
if (ret)
printf("Failed to probe nand driver (err = %d)\n", ret);
return ret;
}
static const struct udevice_id atmel_ebi_match[] = {
{.compatible = "microchip,sam9x60-ebi"},
{.compatible = "atmel,sama5d3-ebi"},
{ /* Sentinel */ }
};
U_BOOT_DRIVER(atmel_ebi) = {
.name = "atmel_ebi",
.id = UCLASS_NOP,
.of_match = atmel_ebi_match,
.probe = atmel_ebi_probe,
.bind = dm_scan_fdt_dev,
};

4
drivers/mfd/Kconfig Normal file
View file

@ -0,0 +1,4 @@
config MFD_ATMEL_SMC
bool "Atmel Static Memory Controller driver"
help
Say yes here to support Atmel Static Memory Controller driver.

1
drivers/mfd/Makefile Normal file
View file

@ -0,0 +1 @@
obj-$(CONFIG_MFD_ATMEL_SMC) += atmel-smc.o

364
drivers/mfd/atmel-smc.c Normal file
View file

@ -0,0 +1,364 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Atmel SMC (Static Memory Controller) helper functions.
*
* Copyright (C) 2022 Microchip Technology Inc.
* Copyright (C) 2017 Free Electrons
*
* Author: Boris Brezillon <boris.brezillon@free-electrons.com>
*/
#include <clk.h>
#include <dm/device.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/mfd/syscon/atmel-smc.h>
#include <linux/string.h>
/**
* atmel_smc_cs_conf_init - initialize a SMC CS conf
* @conf: the SMC CS conf to initialize
*
* Set all fields to 0 so that one can start defining a new config.
*/
void atmel_smc_cs_conf_init(struct atmel_smc_cs_conf *conf)
{
memset(conf, 0, sizeof(*conf));
}
EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_init);
/**
* atmel_smc_cs_encode_ncycles - encode a number of MCK clk cycles in the
* format expected by the SMC engine
* @ncycles: number of MCK clk cycles
* @msbpos: position of the MSB part of the timing field
* @msbwidth: width of the MSB part of the timing field
* @msbfactor: factor applied to the MSB
* @encodedval: param used to store the encoding result
*
* This function encodes the @ncycles value as described in the datasheet
* (section "SMC Setup/Pulse/Cycle/Timings Register"). This is a generic
* helper which called with different parameter depending on the encoding
* scheme.
*
* If the @ncycles value is too big to be encoded, -ERANGE is returned and
* the encodedval is contains the maximum val. Otherwise, 0 is returned.
*/
static int atmel_smc_cs_encode_ncycles(unsigned int ncycles,
unsigned int msbpos,
unsigned int msbwidth,
unsigned int msbfactor,
unsigned int *encodedval)
{
unsigned int lsbmask = GENMASK(msbpos - 1, 0);
unsigned int msbmask = GENMASK(msbwidth - 1, 0);
unsigned int msb, lsb;
int ret = 0;
msb = ncycles / msbfactor;
lsb = ncycles % msbfactor;
if (lsb > lsbmask) {
lsb = 0;
msb++;
}
/*
* Let's just put the maximum we can if the requested setting does
* not fit in the register field.
* We still return -ERANGE in case the caller cares.
*/
if (msb > msbmask) {
msb = msbmask;
lsb = lsbmask;
ret = -ERANGE;
}
*encodedval = (msb << msbpos) | lsb;
return ret;
}
/**
* atmel_smc_cs_conf_set_timing - set the SMC CS conf Txx parameter to a
* specific value
* @conf: SMC CS conf descriptor
* @shift: the position of the Txx field in the TIMINGS register
* @ncycles: value (expressed in MCK clk cycles) to assign to this Txx
* parameter
*
* This function encodes the @ncycles value as described in the datasheet
* (section "SMC Timings Register"), and then stores the result in the
* @conf->timings field at @shift position.
*
* Returns -EINVAL if shift is invalid, -ERANGE if ncycles does not fit in
* the field, and 0 otherwise.
*/
int atmel_smc_cs_conf_set_timing(struct atmel_smc_cs_conf *conf,
unsigned int shift, unsigned int ncycles)
{
unsigned int val;
int ret;
if (shift != ATMEL_HSMC_TIMINGS_TCLR_SHIFT &&
shift != ATMEL_HSMC_TIMINGS_TADL_SHIFT &&
shift != ATMEL_HSMC_TIMINGS_TAR_SHIFT &&
shift != ATMEL_HSMC_TIMINGS_TRR_SHIFT &&
shift != ATMEL_HSMC_TIMINGS_TWB_SHIFT)
return -EINVAL;
/*
* The formula described in atmel datasheets (section "HSMC Timings
* Register"):
*
* ncycles = (Txx[3] * 64) + Txx[2:0]
*/
ret = atmel_smc_cs_encode_ncycles(ncycles, 3, 1, 64, &val);
conf->timings &= ~GENMASK(shift + 3, shift);
conf->timings |= val << shift;
return ret;
}
EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_timing);
/**
* atmel_smc_cs_conf_set_setup - set the SMC CS conf xx_SETUP parameter to a
* specific value
* @conf: SMC CS conf descriptor
* @shift: the position of the xx_SETUP field in the SETUP register
* @ncycles: value (expressed in MCK clk cycles) to assign to this xx_SETUP
* parameter
*
* This function encodes the @ncycles value as described in the datasheet
* (section "SMC Setup Register"), and then stores the result in the
* @conf->setup field at @shift position.
*
* Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in
* the field, and 0 otherwise.
*/
int atmel_smc_cs_conf_set_setup(struct atmel_smc_cs_conf *conf,
unsigned int shift, unsigned int ncycles)
{
unsigned int val;
int ret;
if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NCS_WR_SHIFT &&
shift != ATMEL_SMC_NRD_SHIFT && shift != ATMEL_SMC_NCS_RD_SHIFT)
return -EINVAL;
/*
* The formula described in atmel datasheets (section "SMC Setup
* Register"):
*
* ncycles = (128 * xx_SETUP[5]) + xx_SETUP[4:0]
*/
ret = atmel_smc_cs_encode_ncycles(ncycles, 5, 1, 128, &val);
conf->setup &= ~GENMASK(shift + 7, shift);
conf->setup |= val << shift;
return ret;
}
EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_setup);
/**
* atmel_smc_cs_conf_set_pulse - set the SMC CS conf xx_PULSE parameter to a
* specific value
* @conf: SMC CS conf descriptor
* @shift: the position of the xx_PULSE field in the PULSE register
* @ncycles: value (expressed in MCK clk cycles) to assign to this xx_PULSE
* parameter
*
* This function encodes the @ncycles value as described in the datasheet
* (section "SMC Pulse Register"), and then stores the result in the
* @conf->setup field at @shift position.
*
* Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in
* the field, and 0 otherwise.
*/
int atmel_smc_cs_conf_set_pulse(struct atmel_smc_cs_conf *conf,
unsigned int shift, unsigned int ncycles)
{
unsigned int val;
int ret;
if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NCS_WR_SHIFT &&
shift != ATMEL_SMC_NRD_SHIFT && shift != ATMEL_SMC_NCS_RD_SHIFT)
return -EINVAL;
/*
* The formula described in atmel datasheets (section "SMC Pulse
* Register"):
*
* ncycles = (256 * xx_PULSE[6]) + xx_PULSE[5:0]
*/
ret = atmel_smc_cs_encode_ncycles(ncycles, 6, 1, 256, &val);
conf->pulse &= ~GENMASK(shift + 7, shift);
conf->pulse |= val << shift;
return ret;
}
EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_pulse);
/**
* atmel_smc_cs_conf_set_cycle - set the SMC CS conf xx_CYCLE parameter to a
* specific value
* @conf: SMC CS conf descriptor
* @shift: the position of the xx_CYCLE field in the CYCLE register
* @ncycles: value (expressed in MCK clk cycles) to assign to this xx_CYCLE
* parameter
*
* This function encodes the @ncycles value as described in the datasheet
* (section "SMC Cycle Register"), and then stores the result in the
* @conf->setup field at @shift position.
*
* Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in
* the field, and 0 otherwise.
*/
int atmel_smc_cs_conf_set_cycle(struct atmel_smc_cs_conf *conf,
unsigned int shift, unsigned int ncycles)
{
unsigned int val;
int ret;
if (shift != ATMEL_SMC_NWE_SHIFT && shift != ATMEL_SMC_NRD_SHIFT)
return -EINVAL;
/*
* The formula described in atmel datasheets (section "SMC Cycle
* Register"):
*
* ncycles = (xx_CYCLE[8:7] * 256) + xx_CYCLE[6:0]
*/
ret = atmel_smc_cs_encode_ncycles(ncycles, 7, 2, 256, &val);
conf->cycle &= ~GENMASK(shift + 15, shift);
conf->cycle |= val << shift;
return ret;
}
EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_cycle);
/**
* atmel_smc_cs_conf_apply - apply an SMC CS conf
* @regmap: the SMC regmap
* @cs: the CS id
* @conf: the SMC CS conf to apply
*
* Applies an SMC CS configuration.
* Only valid on at91sam9/avr32 SoCs.
*/
void atmel_smc_cs_conf_apply(struct regmap *regmap, int cs,
const struct atmel_smc_cs_conf *conf)
{
regmap_write(regmap, ATMEL_SMC_SETUP(cs), conf->setup);
regmap_write(regmap, ATMEL_SMC_PULSE(cs), conf->pulse);
regmap_write(regmap, ATMEL_SMC_CYCLE(cs), conf->cycle);
regmap_write(regmap, ATMEL_SMC_MODE(cs), conf->mode);
}
EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_apply);
/**
* atmel_hsmc_cs_conf_apply - apply an SMC CS conf
* @regmap: the HSMC regmap
* @cs: the CS id
* @layout: the layout of registers
* @conf: the SMC CS conf to apply
*
* Applies an SMC CS configuration.
* Only valid on post-sama5 SoCs.
*/
void atmel_hsmc_cs_conf_apply(struct regmap *regmap,
const struct atmel_hsmc_reg_layout *layout,
int cs, const struct atmel_smc_cs_conf *conf)
{
regmap_write(regmap, ATMEL_HSMC_SETUP(layout, cs), conf->setup);
regmap_write(regmap, ATMEL_HSMC_PULSE(layout, cs), conf->pulse);
regmap_write(regmap, ATMEL_HSMC_CYCLE(layout, cs), conf->cycle);
regmap_write(regmap, ATMEL_HSMC_TIMINGS(layout, cs), conf->timings);
regmap_write(regmap, ATMEL_HSMC_MODE(layout, cs), conf->mode);
}
EXPORT_SYMBOL_GPL(atmel_hsmc_cs_conf_apply);
/**
* atmel_smc_cs_conf_get - retrieve the current SMC CS conf
* @regmap: the SMC regmap
* @cs: the CS id
* @conf: the SMC CS conf object to store the current conf
*
* Retrieve the SMC CS configuration.
* Only valid on at91sam9/avr32 SoCs.
*/
void atmel_smc_cs_conf_get(struct regmap *regmap, int cs,
struct atmel_smc_cs_conf *conf)
{
regmap_read(regmap, ATMEL_SMC_SETUP(cs), &conf->setup);
regmap_read(regmap, ATMEL_SMC_PULSE(cs), &conf->pulse);
regmap_read(regmap, ATMEL_SMC_CYCLE(cs), &conf->cycle);
regmap_read(regmap, ATMEL_SMC_MODE(cs), &conf->mode);
}
EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_get);
/**
* atmel_hsmc_cs_conf_get - retrieve the current SMC CS conf
* @regmap: the HSMC regmap
* @cs: the CS id
* @layout: the layout of registers
* @conf: the SMC CS conf object to store the current conf
*
* Retrieve the SMC CS configuration.
* Only valid on post-sama5 SoCs.
*/
void atmel_hsmc_cs_conf_get(struct regmap *regmap,
const struct atmel_hsmc_reg_layout *layout,
int cs, struct atmel_smc_cs_conf *conf)
{
regmap_read(regmap, ATMEL_HSMC_SETUP(layout, cs), &conf->setup);
regmap_read(regmap, ATMEL_HSMC_PULSE(layout, cs), &conf->pulse);
regmap_read(regmap, ATMEL_HSMC_CYCLE(layout, cs), &conf->cycle);
regmap_read(regmap, ATMEL_HSMC_TIMINGS(layout, cs), &conf->timings);
regmap_read(regmap, ATMEL_HSMC_MODE(layout, cs), &conf->mode);
}
EXPORT_SYMBOL_GPL(atmel_hsmc_cs_conf_get);
static const struct atmel_hsmc_reg_layout sama5d3_reg_layout = {
.timing_regs_offset = 0x600,
};
static const struct atmel_hsmc_reg_layout sama5d2_reg_layout = {
.timing_regs_offset = 0x700,
};
static const struct udevice_id atmel_smc_ids[] = {
{ .compatible = "atmel,at91sam9260-smc", .data = (ulong)0 },
{ .compatible = "atmel,sama5d3-smc", .data = (ulong)&sama5d3_reg_layout },
{ .compatible = "atmel,sama5d2-smc", .data = (ulong)&sama5d2_reg_layout },
{ /* sentinel */ },
};
/**
* atmel_hsmc_get_reg_layout - retrieve the layout of HSMC registers
* @np: the HSMC regmap
*
* Retrieve the layout of HSMC registers.
*
* Returns NULL in case of SMC, a struct atmel_hsmc_reg_layout pointer
* in HSMC case, otherwise ERR_PTR(-EINVAL).
*/
const struct atmel_hsmc_reg_layout *
atmel_hsmc_get_reg_layout(ofnode np)
{
int i;
const struct udevice_id *match;
const char *name;
int len;
name = ofnode_get_property(np, "compatible", &len);
for (i = 0; i < ARRAY_SIZE(atmel_smc_ids); i++) {
if (!strcmp(name, atmel_smc_ids[i].compatible)) {
match = &atmel_smc_ids[i];
break;
}
}
return match ? (struct atmel_hsmc_reg_layout *)match->data : ERR_PTR(-EINVAL);
}

View file

@ -45,6 +45,14 @@ config SYS_NAND_NO_SUBPAGE_WRITE
bool "Disable subpage write support"
depends on NAND_ARASAN || NAND_DAVINCI || NAND_KIRKWOOD
config DM_NAND_ATMEL
bool "Support Atmel NAND controller with DM support"
select SYS_NAND_SELF_INIT
imply SYS_NAND_USE_FLASH_BBT
help
Enable this driver for NAND flash platforms using an Atmel NAND
controller.
config NAND_ATMEL
bool "Support Atmel NAND controller"
select SYS_NAND_SELF_INIT

View file

@ -48,6 +48,7 @@ ifdef NORMAL_DRIVERS
obj-$(CONFIG_NAND_ECC_BCH) += nand_bch.o
obj-$(CONFIG_NAND_ATMEL) += atmel_nand.o
obj-$(CONFIG_DM_NAND_ATMEL) += atmel/
obj-$(CONFIG_NAND_ARASAN) += arasan_nfc.o
obj-$(CONFIG_NAND_BRCMNAND) += brcmnand/
obj-$(CONFIG_NAND_DAVINCI) += davinci_nand.o

View file

@ -0,0 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_DM_NAND_ATMEL) += atmel-nand-controller.o atmel-pmecc.o
atmel-nand-controller-objs := nand-controller.o
atmel-pmecc-objs := pmecc.o

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,965 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2017 ATMEL
* Copyright 2017 Free Electrons
*
* Author: Boris Brezillon <boris.brezillon@free-electrons.com>
*
* Derived from the atmel_nand.c driver which contained the following
* copyrights:
*
* Copyright 2003 Rick Bronson
*
* Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8)
* Copyright 2001 Thomas Gleixner (gleixner@autronix.de)
*
* Derived from drivers/mtd/spia.c (removed in v3.8)
* Copyright 2000 Steven J. Hill (sjhill@cotw.com)
*
* Add Hardware ECC support for AT91SAM9260 / AT91SAM9263
* Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright 2007
*
* Derived from Das U-Boot source code
* (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c)
* Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
*
* Add Programmable Multibit ECC support for various AT91 SoC
* Copyright 2012 ATMEL, Hong Xu
*
* Add Nand Flash Controller support for SAMA5 SoC
* Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com)
*
* The PMECC is an hardware assisted BCH engine, which means part of the
* ECC algorithm is left to the software. The hardware/software repartition
* is explained in the "PMECC Controller Functional Description" chapter in
* Atmel datasheets, and some of the functions in this file are directly
* implementing the algorithms described in the "Software Implementation"
* sub-section.
*
* TODO: it seems that the software BCH implementation in lib/bch.c is already
* providing some of the logic we are implementing here. It would be smart
* to expose the needed lib/bch.c helpers/functions and re-use them here.
*/
#include <linux/iopoll.h>
#include <linux/mtd/rawnand.h>
#include <dm/of_access.h>
#include <dm/ofnode.h>
#include <asm/io.h>
#include "pmecc.h"
#include <dm/uclass.h>
#include <dm/device_compat.h>
#include <dm/devres.h>
#include <linux/ioport.h>
/* Galois field dimension */
#define PMECC_GF_DIMENSION_13 13
#define PMECC_GF_DIMENSION_14 14
/* Primitive Polynomial used by PMECC */
#define PMECC_GF_13_PRIMITIVE_POLY 0x201b
#define PMECC_GF_14_PRIMITIVE_POLY 0x4443
#define PMECC_LOOKUP_TABLE_SIZE_512 0x2000
#define PMECC_LOOKUP_TABLE_SIZE_1024 0x4000
/* Time out value for reading PMECC status register */
#define PMECC_MAX_TIMEOUT_MS 100
/* PMECC Register Definitions */
#define ATMEL_PMECC_CFG 0x0
#define PMECC_CFG_BCH_STRENGTH(x) (x)
#define PMECC_CFG_BCH_STRENGTH_MASK GENMASK(2, 0)
#define PMECC_CFG_SECTOR512 (0 << 4)
#define PMECC_CFG_SECTOR1024 BIT(4)
#define PMECC_CFG_NSECTORS(x) ((fls(x) - 1) << 8)
#define PMECC_CFG_READ_OP (0 << 12)
#define PMECC_CFG_WRITE_OP BIT(12)
#define PMECC_CFG_SPARE_ENABLE BIT(16)
#define PMECC_CFG_AUTO_ENABLE BIT(20)
#define ATMEL_PMECC_SAREA 0x4
#define ATMEL_PMECC_SADDR 0x8
#define ATMEL_PMECC_EADDR 0xc
#define ATMEL_PMECC_CLK 0x10
#define PMECC_CLK_133MHZ (2 << 0)
#define ATMEL_PMECC_CTRL 0x14
#define PMECC_CTRL_RST BIT(0)
#define PMECC_CTRL_DATA BIT(1)
#define PMECC_CTRL_USER BIT(2)
#define PMECC_CTRL_ENABLE BIT(4)
#define PMECC_CTRL_DISABLE BIT(5)
#define ATMEL_PMECC_SR 0x18
#define PMECC_SR_BUSY BIT(0)
#define PMECC_SR_ENABLE BIT(4)
#define ATMEL_PMECC_IER 0x1c
#define ATMEL_PMECC_IDR 0x20
#define ATMEL_PMECC_IMR 0x24
#define ATMEL_PMECC_ISR 0x28
#define PMECC_ERROR_INT BIT(0)
#define ATMEL_PMECC_ECC(sector, n) \
((((sector) + 1) * 0x40) + (n))
#define ATMEL_PMECC_REM(sector, n) \
((((sector) + 1) * 0x40) + ((n) * 4) + 0x200)
/* PMERRLOC Register Definitions */
#define ATMEL_PMERRLOC_ELCFG 0x0
#define PMERRLOC_ELCFG_SECTOR_512 (0 << 0)
#define PMERRLOC_ELCFG_SECTOR_1024 BIT(0)
#define PMERRLOC_ELCFG_NUM_ERRORS(n) ((n) << 16)
#define ATMEL_PMERRLOC_ELPRIM 0x4
#define ATMEL_PMERRLOC_ELEN 0x8
#define ATMEL_PMERRLOC_ELDIS 0xc
#define PMERRLOC_DISABLE BIT(0)
#define ATMEL_PMERRLOC_ELSR 0x10
#define PMERRLOC_ELSR_BUSY BIT(0)
#define ATMEL_PMERRLOC_ELIER 0x14
#define ATMEL_PMERRLOC_ELIDR 0x18
#define ATMEL_PMERRLOC_ELIMR 0x1c
#define ATMEL_PMERRLOC_ELISR 0x20
#define PMERRLOC_ERR_NUM_MASK GENMASK(12, 8)
#define PMERRLOC_CALC_DONE BIT(0)
#define ATMEL_PMERRLOC_SIGMA(x) (((x) * 0x4) + 0x28)
#define ATMEL_PMERRLOC_EL(offs, x) (((x) * 0x4) + (offs))
struct atmel_pmecc_gf_tables {
u16 *alpha_to;
u16 *index_of;
};
struct atmel_pmecc_caps {
const int *strengths;
int nstrengths;
int el_offset;
bool correct_erased_chunks;
};
struct atmel_pmecc_user_conf_cache {
u32 cfg;
u32 sarea;
u32 saddr;
u32 eaddr;
};
struct atmel_pmecc_user {
struct atmel_pmecc_user_conf_cache cache;
struct atmel_pmecc *pmecc;
const struct atmel_pmecc_gf_tables *gf_tables;
int eccbytes;
s16 *partial_syn;
s16 *si;
s16 *lmu;
s16 *smu;
s32 *mu;
s32 *dmu;
s32 *delta;
u32 isr;
};
/* Serialize table access */
DEFINE_MUTEX(pmecc_gf_tables_lock);
static const struct atmel_pmecc_gf_tables *pmecc_gf_tables_512;
static const struct atmel_pmecc_gf_tables *pmecc_gf_tables_1024;
static inline int deg(unsigned int poly)
{
/* polynomial degree is the most-significant bit index */
return fls(poly) - 1;
}
static int atmel_pmecc_build_gf_tables(int mm, unsigned int poly,
struct atmel_pmecc_gf_tables *gf_tables)
{
unsigned int i, x = 1;
const unsigned int k = BIT(deg(poly));
unsigned int nn = BIT(mm) - 1;
/* primitive polynomial must be of degree m */
if (k != (1u << mm))
return -EINVAL;
for (i = 0; i < nn; i++) {
gf_tables->alpha_to[i] = x;
gf_tables->index_of[x] = i;
if (i && (x == 1))
/* polynomial is not primitive (a^i=1 with 0<i<2^m-1) */
return -EINVAL;
x <<= 1;
if (x & k)
x ^= poly;
}
gf_tables->alpha_to[nn] = 1;
gf_tables->index_of[0] = 0;
return 0;
}
static const struct atmel_pmecc_gf_tables *
atmel_pmecc_create_gf_tables(const struct atmel_pmecc_user_req *req)
{
struct atmel_pmecc_gf_tables *gf_tables;
unsigned int poly, degree, table_size;
int ret;
if (req->ecc.sectorsize == 512) {
degree = PMECC_GF_DIMENSION_13;
poly = PMECC_GF_13_PRIMITIVE_POLY;
table_size = PMECC_LOOKUP_TABLE_SIZE_512;
} else {
degree = PMECC_GF_DIMENSION_14;
poly = PMECC_GF_14_PRIMITIVE_POLY;
table_size = PMECC_LOOKUP_TABLE_SIZE_1024;
}
gf_tables = kzalloc(sizeof(*gf_tables) +
(2 * table_size * sizeof(u16)),
GFP_KERNEL);
if (!gf_tables)
return ERR_PTR(-ENOMEM);
gf_tables->alpha_to = (void *)(gf_tables + 1);
gf_tables->index_of = gf_tables->alpha_to + table_size;
ret = atmel_pmecc_build_gf_tables(degree, poly, gf_tables);
if (ret) {
kfree(gf_tables);
return ERR_PTR(ret);
}
return gf_tables;
}
static const struct atmel_pmecc_gf_tables *
atmel_pmecc_get_gf_tables(const struct atmel_pmecc_user_req *req)
{
const struct atmel_pmecc_gf_tables **gf_tables, *ret;
mutex_lock(&pmecc_gf_tables_lock);
if (req->ecc.sectorsize == 512)
gf_tables = &pmecc_gf_tables_512;
else
gf_tables = &pmecc_gf_tables_1024;
ret = *gf_tables;
if (!ret) {
ret = atmel_pmecc_create_gf_tables(req);
if (!IS_ERR(ret))
*gf_tables = ret;
}
mutex_unlock(&pmecc_gf_tables_lock);
return ret;
}
static int atmel_pmecc_prepare_user_req(struct atmel_pmecc *pmecc,
struct atmel_pmecc_user_req *req)
{
int i, max_eccbytes, eccbytes = 0, eccstrength = 0;
if (req->pagesize <= 0 || req->oobsize <= 0 || req->ecc.bytes <= 0)
return -EINVAL;
if (req->ecc.ooboffset >= 0 &&
req->ecc.ooboffset + req->ecc.bytes > req->oobsize)
return -EINVAL;
if (req->ecc.sectorsize == ATMEL_PMECC_SECTOR_SIZE_AUTO) {
if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH)
return -EINVAL;
if (req->pagesize > 512)
req->ecc.sectorsize = 1024;
else
req->ecc.sectorsize = 512;
}
if (req->ecc.sectorsize != 512 && req->ecc.sectorsize != 1024)
return -EINVAL;
if (req->pagesize % req->ecc.sectorsize)
return -EINVAL;
req->ecc.nsectors = req->pagesize / req->ecc.sectorsize;
max_eccbytes = req->ecc.bytes;
for (i = 0; i < pmecc->caps->nstrengths; i++) {
int nbytes, strength = pmecc->caps->strengths[i];
if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH &&
strength < req->ecc.strength)
continue;
nbytes = DIV_ROUND_UP(strength * fls(8 * req->ecc.sectorsize),
8);
nbytes *= req->ecc.nsectors;
if (nbytes > max_eccbytes)
break;
eccstrength = strength;
eccbytes = nbytes;
if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH)
break;
}
if (!eccstrength)
return -EINVAL;
req->ecc.bytes = eccbytes;
req->ecc.strength = eccstrength;
if (req->ecc.ooboffset < 0)
req->ecc.ooboffset = req->oobsize - eccbytes;
return 0;
}
struct atmel_pmecc_user *
atmel_pmecc_create_user(struct atmel_pmecc *pmecc,
struct atmel_pmecc_user_req *req)
{
struct atmel_pmecc_user *user;
const struct atmel_pmecc_gf_tables *gf_tables;
int strength, size, ret;
ret = atmel_pmecc_prepare_user_req(pmecc, req);
if (ret)
return ERR_PTR(ret);
size = sizeof(*user);
size = ALIGN(size, sizeof(u16));
/* Reserve space for partial_syn, si and smu */
size += ((2 * req->ecc.strength) + 1) * sizeof(u16) *
(2 + req->ecc.strength + 2);
/* Reserve space for lmu. */
size += (req->ecc.strength + 1) * sizeof(u16);
/* Reserve space for mu, dmu and delta. */
size = ALIGN(size, sizeof(s32));
size += (req->ecc.strength + 1) * sizeof(s32) * 3;
user = kzalloc(size, GFP_KERNEL);
if (!user)
return ERR_PTR(-ENOMEM);
user->pmecc = pmecc;
user->partial_syn = (s16 *)PTR_ALIGN(user + 1, sizeof(u16));
user->si = user->partial_syn + ((2 * req->ecc.strength) + 1);
user->lmu = user->si + ((2 * req->ecc.strength) + 1);
user->smu = user->lmu + (req->ecc.strength + 1);
user->mu = (s32 *)PTR_ALIGN(user->smu +
(((2 * req->ecc.strength) + 1) *
(req->ecc.strength + 2)),
sizeof(s32));
user->dmu = user->mu + req->ecc.strength + 1;
user->delta = user->dmu + req->ecc.strength + 1;
gf_tables = atmel_pmecc_get_gf_tables(req);
if (IS_ERR(gf_tables)) {
kfree(user);
return ERR_CAST(gf_tables);
}
user->gf_tables = gf_tables;
user->eccbytes = req->ecc.bytes / req->ecc.nsectors;
for (strength = 0; strength < pmecc->caps->nstrengths; strength++) {
if (pmecc->caps->strengths[strength] == req->ecc.strength)
break;
}
user->cache.cfg = PMECC_CFG_BCH_STRENGTH(strength) |
PMECC_CFG_NSECTORS(req->ecc.nsectors);
if (req->ecc.sectorsize == 1024)
user->cache.cfg |= PMECC_CFG_SECTOR1024;
user->cache.sarea = req->oobsize - 1;
user->cache.saddr = req->ecc.ooboffset;
user->cache.eaddr = req->ecc.ooboffset + req->ecc.bytes - 1;
return user;
}
EXPORT_SYMBOL_GPL(atmel_pmecc_create_user);
void atmel_pmecc_destroy_user(struct atmel_pmecc_user *user)
{
kfree(user);
}
EXPORT_SYMBOL_GPL(atmel_pmecc_destroy_user);
static int get_strength(struct atmel_pmecc_user *user)
{
const int *strengths = user->pmecc->caps->strengths;
return strengths[user->cache.cfg & PMECC_CFG_BCH_STRENGTH_MASK];
}
static int get_sectorsize(struct atmel_pmecc_user *user)
{
return user->cache.cfg & PMECC_CFG_SECTOR1024 ? 1024 : 512;
}
static void atmel_pmecc_gen_syndrome(struct atmel_pmecc_user *user, int sector)
{
int strength = get_strength(user);
u32 value;
int i;
/* Fill odd syndromes */
for (i = 0; i < strength; i++) {
value = readl_relaxed(user->pmecc->regs.base +
ATMEL_PMECC_REM(sector, i / 2));
if (i & 1)
value >>= 16;
user->partial_syn[(2 * i) + 1] = value;
}
}
static void atmel_pmecc_substitute(struct atmel_pmecc_user *user)
{
int degree = get_sectorsize(user) == 512 ? 13 : 14;
int cw_len = BIT(degree) - 1;
int strength = get_strength(user);
s16 *alpha_to = (s16 *)user->gf_tables->alpha_to;
s16 *index_of = (s16 *)user->gf_tables->index_of;
s16 *partial_syn = user->partial_syn;
s16 *si;
int i, j;
/*
* si[] is a table that holds the current syndrome value,
* an element of that table belongs to the field
*/
si = user->si;
memset(&si[1], 0, sizeof(s16) * ((2 * strength) - 1));
/* Computation 2t syndromes based on S(x) */
/* Odd syndromes */
for (i = 1; i < 2 * strength; i += 2) {
for (j = 0; j < degree; j++) {
if (partial_syn[i] & BIT(j))
si[i] = alpha_to[i * j] ^ si[i];
}
}
/* Even syndrome = (Odd syndrome) ** 2 */
for (i = 2, j = 1; j <= strength; i = ++j << 1) {
if (si[j] == 0) {
si[i] = 0;
} else {
s16 tmp;
tmp = index_of[si[j]];
tmp = (tmp * 2) % cw_len;
si[i] = alpha_to[tmp];
}
}
}
static void atmel_pmecc_get_sigma(struct atmel_pmecc_user *user)
{
s16 *lmu = user->lmu;
s16 *si = user->si;
s32 *mu = user->mu;
s32 *dmu = user->dmu;
s32 *delta = user->delta;
int degree = get_sectorsize(user) == 512 ? 13 : 14;
int cw_len = BIT(degree) - 1;
int strength = get_strength(user);
int num = 2 * strength + 1;
s16 *index_of = (s16 *)user->gf_tables->index_of;
s16 *alpha_to = (s16 *)user->gf_tables->alpha_to;
int i, j, k;
u32 dmu_0_count, tmp;
s16 *smu = user->smu;
/* index of largest delta */
int ro;
int largest;
int diff;
dmu_0_count = 0;
/* First Row */
/* Mu */
mu[0] = -1;
memset(smu, 0, sizeof(s16) * num);
smu[0] = 1;
/* discrepancy set to 1 */
dmu[0] = 1;
/* polynom order set to 0 */
lmu[0] = 0;
delta[0] = (mu[0] * 2 - lmu[0]) >> 1;
/* Second Row */
/* Mu */
mu[1] = 0;
/* Sigma(x) set to 1 */
memset(&smu[num], 0, sizeof(s16) * num);
smu[num] = 1;
/* discrepancy set to S1 */
dmu[1] = si[1];
/* polynom order set to 0 */
lmu[1] = 0;
delta[1] = (mu[1] * 2 - lmu[1]) >> 1;
/* Init the Sigma(x) last row */
memset(&smu[(strength + 1) * num], 0, sizeof(s16) * num);
for (i = 1; i <= strength; i++) {
mu[i + 1] = i << 1;
/* Begin Computing Sigma (Mu+1) and L(mu) */
/* check if discrepancy is set to 0 */
if (dmu[i] == 0) {
dmu_0_count++;
tmp = ((strength - (lmu[i] >> 1) - 1) / 2);
if ((strength - (lmu[i] >> 1) - 1) & 0x1)
tmp += 2;
else
tmp += 1;
if (dmu_0_count == tmp) {
for (j = 0; j <= (lmu[i] >> 1) + 1; j++)
smu[(strength + 1) * num + j] =
smu[i * num + j];
lmu[strength + 1] = lmu[i];
return;
}
/* copy polynom */
for (j = 0; j <= lmu[i] >> 1; j++)
smu[(i + 1) * num + j] = smu[i * num + j];
/* copy previous polynom order to the next */
lmu[i + 1] = lmu[i];
} else {
ro = 0;
largest = -1;
/* find largest delta with dmu != 0 */
for (j = 0; j < i; j++) {
if ((dmu[j]) && (delta[j] > largest)) {
largest = delta[j];
ro = j;
}
}
/* compute difference */
diff = (mu[i] - mu[ro]);
/* Compute degree of the new smu polynomial */
if ((lmu[i] >> 1) > ((lmu[ro] >> 1) + diff))
lmu[i + 1] = lmu[i];
else
lmu[i + 1] = ((lmu[ro] >> 1) + diff) * 2;
/* Init smu[i+1] with 0 */
for (k = 0; k < num; k++)
smu[(i + 1) * num + k] = 0;
/* Compute smu[i+1] */
for (k = 0; k <= lmu[ro] >> 1; k++) {
s16 a, b, c;
if (!(smu[ro * num + k] && dmu[i]))
continue;
a = index_of[dmu[i]];
b = index_of[dmu[ro]];
c = index_of[smu[ro * num + k]];
tmp = a + (cw_len - b) + c;
a = alpha_to[tmp % cw_len];
smu[(i + 1) * num + (k + diff)] = a;
}
for (k = 0; k <= lmu[i] >> 1; k++)
smu[(i + 1) * num + k] ^= smu[i * num + k];
}
/* End Computing Sigma (Mu+1) and L(mu) */
/* In either case compute delta */
delta[i + 1] = (mu[i + 1] * 2 - lmu[i + 1]) >> 1;
/* Do not compute discrepancy for the last iteration */
if (i >= strength)
continue;
for (k = 0; k <= (lmu[i + 1] >> 1); k++) {
tmp = 2 * (i - 1);
if (k == 0) {
dmu[i + 1] = si[tmp + 3];
} else if (smu[(i + 1) * num + k] && si[tmp + 3 - k]) {
s16 a, b, c;
a = index_of[smu[(i + 1) * num + k]];
b = si[2 * (i - 1) + 3 - k];
c = index_of[b];
tmp = a + c;
tmp %= cw_len;
dmu[i + 1] = alpha_to[tmp] ^ dmu[i + 1];
}
}
}
}
static int atmel_pmecc_err_location(struct atmel_pmecc_user *user)
{
int sector_size = get_sectorsize(user);
int degree = sector_size == 512 ? 13 : 14;
struct atmel_pmecc *pmecc = user->pmecc;
int strength = get_strength(user);
int ret, roots_nbr, i, err_nbr = 0;
int num = (2 * strength) + 1;
s16 *smu = user->smu;
u32 val;
writel(PMERRLOC_DISABLE, pmecc->regs.errloc + ATMEL_PMERRLOC_ELDIS);
for (i = 0; i <= user->lmu[strength + 1] >> 1; i++) {
writel_relaxed(smu[(strength + 1) * num + i],
pmecc->regs.errloc + ATMEL_PMERRLOC_SIGMA(i));
err_nbr++;
}
val = (err_nbr - 1) << 16;
if (sector_size == 1024)
val |= 1;
writel(val, pmecc->regs.errloc + ATMEL_PMERRLOC_ELCFG);
writel((sector_size * 8) + (degree * strength),
pmecc->regs.errloc + ATMEL_PMERRLOC_ELEN);
ret = readl_relaxed_poll_timeout(pmecc->regs.errloc +
ATMEL_PMERRLOC_ELISR,
val, val & PMERRLOC_CALC_DONE,
PMECC_MAX_TIMEOUT_MS * 1000);
if (ret) {
dev_err(pmecc->dev,
"PMECC: Timeout to calculate error location.\n");
return ret;
}
roots_nbr = (val & PMERRLOC_ERR_NUM_MASK) >> 8;
/* Number of roots == degree of smu hence <= cap */
if (roots_nbr == user->lmu[strength + 1] >> 1)
return err_nbr - 1;
/*
* Number of roots does not match the degree of smu
* unable to correct error.
*/
return -EBADMSG;
}
int atmel_pmecc_correct_sector(struct atmel_pmecc_user *user, int sector,
void *data, void *ecc)
{
struct atmel_pmecc *pmecc = user->pmecc;
int sectorsize = get_sectorsize(user);
int eccbytes = user->eccbytes;
int i, nerrors;
if (!(user->isr & BIT(sector)))
return 0;
atmel_pmecc_gen_syndrome(user, sector);
atmel_pmecc_substitute(user);
atmel_pmecc_get_sigma(user);
nerrors = atmel_pmecc_err_location(user);
if (nerrors < 0)
return nerrors;
for (i = 0; i < nerrors; i++) {
const char *area;
int byte, bit;
u32 errpos;
u8 *ptr;
errpos = readl_relaxed(pmecc->regs.errloc +
ATMEL_PMERRLOC_EL(pmecc->caps->el_offset, i));
errpos--;
byte = errpos / 8;
bit = errpos % 8;
if (byte < sectorsize) {
ptr = data + byte;
area = "data";
} else if (byte < sectorsize + eccbytes) {
ptr = ecc + byte - sectorsize;
area = "ECC";
} else {
dev_dbg(pmecc->dev,
"Invalid errpos value (%d, max is %d)\n",
errpos, (sectorsize + eccbytes) * 8);
return -EINVAL;
}
dev_dbg(pmecc->dev,
"Bit flip in %s area, byte %d: 0x%02x -> 0x%02x\n",
area, byte, *ptr, (unsigned int)(*ptr ^ BIT(bit)));
*ptr ^= BIT(bit);
}
return nerrors;
}
EXPORT_SYMBOL_GPL(atmel_pmecc_correct_sector);
bool atmel_pmecc_correct_erased_chunks(struct atmel_pmecc_user *user)
{
return user->pmecc->caps->correct_erased_chunks;
}
EXPORT_SYMBOL_GPL(atmel_pmecc_correct_erased_chunks);
void atmel_pmecc_get_generated_eccbytes(struct atmel_pmecc_user *user,
int sector, void *ecc)
{
struct atmel_pmecc *pmecc = user->pmecc;
u8 *ptr = ecc;
int i;
for (i = 0; i < user->eccbytes; i++)
ptr[i] = readb_relaxed(pmecc->regs.base +
ATMEL_PMECC_ECC(sector, i));
}
EXPORT_SYMBOL_GPL(atmel_pmecc_get_generated_eccbytes);
void atmel_pmecc_reset(struct atmel_pmecc *pmecc)
{
writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
}
EXPORT_SYMBOL_GPL(atmel_pmecc_reset);
int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op)
{
struct atmel_pmecc *pmecc = user->pmecc;
u32 cfg;
if (op != NAND_ECC_READ && op != NAND_ECC_WRITE) {
dev_err(pmecc->dev, "Bad ECC operation!");
return -EINVAL;
}
mutex_lock(&user->pmecc->lock);
cfg = user->cache.cfg;
if (op == NAND_ECC_WRITE)
cfg |= PMECC_CFG_WRITE_OP;
else
cfg |= PMECC_CFG_AUTO_ENABLE;
writel(cfg, pmecc->regs.base + ATMEL_PMECC_CFG);
writel(user->cache.sarea, pmecc->regs.base + ATMEL_PMECC_SAREA);
writel(user->cache.saddr, pmecc->regs.base + ATMEL_PMECC_SADDR);
writel(user->cache.eaddr, pmecc->regs.base + ATMEL_PMECC_EADDR);
writel(PMECC_CTRL_ENABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
writel(PMECC_CTRL_DATA, pmecc->regs.base + ATMEL_PMECC_CTRL);
return 0;
}
EXPORT_SYMBOL_GPL(atmel_pmecc_enable);
void atmel_pmecc_disable(struct atmel_pmecc_user *user)
{
atmel_pmecc_reset(user->pmecc);
mutex_unlock(&user->pmecc->lock);
}
EXPORT_SYMBOL_GPL(atmel_pmecc_disable);
int atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user)
{
struct atmel_pmecc *pmecc = user->pmecc;
u32 status;
int ret;
ret = readl_relaxed_poll_timeout(pmecc->regs.base +
ATMEL_PMECC_SR,
status, !(status & PMECC_SR_BUSY),
PMECC_MAX_TIMEOUT_MS * 1000);
if (ret) {
dev_err(pmecc->dev,
"Timeout while waiting for PMECC ready.\n");
return ret;
}
user->isr = readl_relaxed(pmecc->regs.base + ATMEL_PMECC_ISR);
return 0;
}
EXPORT_SYMBOL_GPL(atmel_pmecc_wait_rdy);
#define ATMEL_BASE_PMECC 0xffffe000
#define ATMEL_BASE_PMERRLOC 0xffffe600
static struct atmel_pmecc *
atmel_pmecc_create(struct udevice *dev,
const struct atmel_pmecc_caps *caps,
int pmecc_res_idx, int errloc_res_idx,
int timing_res_idx)
{
struct atmel_pmecc *pmecc;
struct resource res;
pmecc = devm_kzalloc(dev, sizeof(*pmecc), GFP_KERNEL);
if (!pmecc)
return ERR_PTR(-ENOMEM);
pmecc->caps = caps;
pmecc->dev = dev;
mutex_init(&pmecc->lock);
ofnode_read_resource(dev->node_, 0, &res);
pmecc->regs.base = (void *)res.start;
ofnode_read_resource(dev->node_, 1, &res);
pmecc->regs.errloc = (void *)res.start;
pmecc->regs.timing = 0;
/* Disable all interrupts before registering the PMECC handler. */
writel(0xffffffff, pmecc->regs.base + ATMEL_PMECC_IDR);
atmel_pmecc_reset(pmecc);
return pmecc;
}
static void devm_atmel_pmecc_put(struct udevice *dev, void *res)
{
}
static struct atmel_pmecc *atmel_pmecc_get_by_node(struct udevice *dev)
{
struct atmel_pmecc *pmecc, **ptr;
int ret;
pmecc = dev_get_plat(dev);
if (!pmecc) {
ret = -EPROBE_DEFER;
goto err_put_device;
}
ptr = devres_alloc(devm_atmel_pmecc_put, sizeof(*ptr), GFP_KERNEL);
if (!ptr) {
ret = -ENOMEM;
goto err_put_device;
}
*ptr = pmecc;
devres_add(dev, ptr);
return pmecc;
err_put_device:
return ERR_PTR(ret);
}
static const int atmel_pmecc_strengths[] = { 2, 4, 8, 12, 24, 32 };
static struct atmel_pmecc_caps at91sam9g45_caps = {
.strengths = atmel_pmecc_strengths,
.nstrengths = 5,
.el_offset = 0x8c,
};
static struct atmel_pmecc_caps sama5d4_caps = {
.strengths = atmel_pmecc_strengths,
.nstrengths = 5,
.el_offset = 0x8c,
.correct_erased_chunks = true,
};
static struct atmel_pmecc_caps sama5d2_caps = {
.strengths = atmel_pmecc_strengths,
.nstrengths = 6,
.el_offset = 0xac,
.correct_erased_chunks = true,
};
struct atmel_pmecc *devm_atmel_pmecc_get(struct udevice *userdev)
{
struct atmel_pmecc *pmecc;
struct ofnode_phandle_args args;
struct udevice *pdev;
int ret;
if (!userdev)
return ERR_PTR(-EINVAL);
ret = ofnode_parse_phandle_with_args(userdev->node_,
"ecc-engine",
NULL, 0, 0, &args);
ret = uclass_get_device_by_ofnode(UCLASS_MTD, args.node, &pdev);
if (ret)
return NULL;
pmecc = atmel_pmecc_get_by_node(pdev);
/* TODO:
* Support old DT bindings
*/
return pmecc;
}
EXPORT_SYMBOL(devm_atmel_pmecc_get);
static const struct udevice_id atmel_pmecc_match[] = {
{ .compatible = "atmel,at91sam9g45-pmecc", (ulong)&at91sam9g45_caps },
{ .compatible = "atmel,sama5d4-pmecc", (ulong)&sama5d4_caps },
{ .compatible = "atmel,sama5d2-pmecc", (ulong)&sama5d2_caps },
{ /* sentinel */ }
};
static int atmel_pmecc_probe(struct udevice *dev)
{
const struct atmel_pmecc_caps *caps;
struct atmel_pmecc *pmecc;
caps = (struct atmel_pmecc_caps *)dev_get_driver_data(dev);
if (!caps) {
dev_err(dev, "Invalid caps\n");
return -EINVAL;
}
pmecc = atmel_pmecc_create(dev, caps, 0, 1, 2);
if (IS_ERR(pmecc))
return PTR_ERR(pmecc);
dev->plat_ = pmecc;
return 0;
}
U_BOOT_DRIVER(atmel_pmecc) = {
.name = "atmel-pmecc",
.id = UCLASS_MTD,
.of_match = atmel_pmecc_match,
.probe = atmel_pmecc_probe,
};

View file

@ -0,0 +1,94 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* © Copyright 2016 ATMEL
* © Copyright 2016 Free Electrons
*
* Author: Boris Brezillon <boris.brezillon@free-electrons.com>
*
* Derived from the atmel_nand.c driver which contained the following
* copyrights:
*
* Copyright © 2003 Rick Bronson
*
* Derived from drivers/mtd/nand/autcpu12.c (removed in v3.8)
* Copyright © 2001 Thomas Gleixner (gleixner@autronix.de)
*
* Derived from drivers/mtd/spia.c (removed in v3.8)
* Copyright © 2000 Steven J. Hill (sjhill@cotw.com)
*
*
* Add Hardware ECC support for AT91SAM9260 / AT91SAM9263
* Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright © 2007
*
* Derived from Das U-Boot source code
* (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c)
* © Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
*
* Add Programmable Multibit ECC support for various AT91 SoC
* © Copyright 2012 ATMEL, Hong Xu
*
* Add Nand Flash Controller support for SAMA5 SoC
* © Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com)
*/
#ifndef ATMEL_PMECC_H
#define ATMEL_PMECC_H
#define ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH 0
#define ATMEL_PMECC_SECTOR_SIZE_AUTO 0
#define ATMEL_PMECC_OOBOFFSET_AUTO -1
struct atmel_pmecc_user_req {
int pagesize;
int oobsize;
struct {
int strength;
int bytes;
int sectorsize;
int nsectors;
int ooboffset;
} ecc;
};
struct atmel_pmecc_suspend_ctx {
u32 setup;
u32 pulse;
u32 cycle;
u32 timings;
u32 mode;
};
struct atmel_pmecc {
struct udevice *dev;
const struct atmel_pmecc_caps *caps;
struct {
void __iomem *base;
void __iomem *errloc;
void __iomem *timing;
} regs;
/* Mutex used for pmecc enable/disable */
struct mutex lock;
struct atmel_pmecc_suspend_ctx suspend;
};
struct atmel_pmecc *devm_atmel_pmecc_get(struct udevice *dev);
struct atmel_pmecc_user *
atmel_pmecc_create_user(struct atmel_pmecc *pmecc,
struct atmel_pmecc_user_req *req);
void atmel_pmecc_destroy_user(struct atmel_pmecc_user *user);
void atmel_pmecc_reset(struct atmel_pmecc *pmecc);
int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op);
void atmel_pmecc_disable(struct atmel_pmecc_user *user);
int atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user);
int atmel_pmecc_correct_sector(struct atmel_pmecc_user *user, int sector,
void *data, void *ecc);
bool atmel_pmecc_correct_erased_chunks(struct atmel_pmecc_user *user);
void atmel_pmecc_get_generated_eccbytes(struct atmel_pmecc_user *user,
int sector, void *ecc);
#endif /* ATMEL_PMECC_H */

View file

@ -211,4 +211,12 @@ config RESET_DRA7
help
Support for TI DRA7-RESET subsystem. Basic Assert/Deassert
is supported.
config RESET_AT91
bool "Enable support for Microchip/Atmel Reset Controller driver"
depends on DM_RESET && ARCH_AT91
help
This enables the Reset Controller driver support for Microchip/Atmel
SoCs. Mainly used to expose assert/deassert methods to other drivers
that require it.
endmenu

View file

@ -31,3 +31,4 @@ obj-$(CONFIG_RESET_RASPBERRYPI) += reset-raspberrypi.o
obj-$(CONFIG_RESET_SCMI) += reset-scmi.o
obj-$(CONFIG_RESET_ZYNQMP) += reset-zynqmp.o
obj-$(CONFIG_RESET_DRA7) += reset-dra7.o
obj-$(CONFIG_RESET_AT91) += reset-at91.o

141
drivers/reset/reset-at91.c Normal file
View file

@ -0,0 +1,141 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Support for Atmel/Microchip Reset Controller.
*
* Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries
*
* Author: Sergiu Moga <sergiu.moga@microchip.com>
*/
#include <clk.h>
#include <asm/io.h>
#include <dm.h>
#include <dm/lists.h>
#include <reset-uclass.h>
#include <asm/arch/at91_rstc.h>
#include <dt-bindings/reset/sama7g5-reset.h>
struct at91_reset {
void __iomem *dev_base;
struct at91_reset_data *data;
};
struct at91_reset_data {
u32 n_device_reset;
u8 device_reset_min_id;
u8 device_reset_max_id;
};
static const struct at91_reset_data sama7g5_data = {
.n_device_reset = 3,
.device_reset_min_id = SAMA7G5_RESET_USB_PHY1,
.device_reset_max_id = SAMA7G5_RESET_USB_PHY3,
};
static int at91_rst_update(struct at91_reset *reset, unsigned long id,
bool assert)
{
u32 val;
if (!reset->dev_base)
return 0;
val = readl(reset->dev_base);
if (assert)
val |= BIT(id);
else
val &= ~BIT(id);
writel(val, reset->dev_base);
return 0;
}
static int at91_reset_of_xlate(struct reset_ctl *reset_ctl,
struct ofnode_phandle_args *args)
{
struct at91_reset *reset = dev_get_priv(reset_ctl->dev);
if (!reset->data->n_device_reset ||
args->args[0] < reset->data->device_reset_min_id ||
args->args[0] > reset->data->device_reset_max_id)
return -EINVAL;
reset_ctl->id = args->args[0];
return 0;
}
static int at91_rst_assert(struct reset_ctl *reset_ctl)
{
struct at91_reset *reset = dev_get_priv(reset_ctl->dev);
return at91_rst_update(reset, reset_ctl->id, true);
}
static int at91_rst_deassert(struct reset_ctl *reset_ctl)
{
struct at91_reset *reset = dev_get_priv(reset_ctl->dev);
return at91_rst_update(reset, reset_ctl->id, false);
}
struct reset_ops at91_reset_ops = {
.of_xlate = at91_reset_of_xlate,
.rst_assert = at91_rst_assert,
.rst_deassert = at91_rst_deassert,
};
static int at91_reset_probe(struct udevice *dev)
{
struct at91_reset *reset = dev_get_priv(dev);
struct clk sclk;
int ret;
reset->data = (struct at91_reset_data *)dev_get_driver_data(dev);
reset->dev_base = dev_remap_addr_index(dev, 1);
if (reset->data && reset->data->n_device_reset && !reset->dev_base)
return -EINVAL;
ret = clk_get_by_index(dev, 0, &sclk);
if (ret)
return ret;
return clk_prepare_enable(&sclk);
}
static int at91_reset_bind(struct udevice *dev)
{
struct udevice *at91_sysreset;
if (CONFIG_IS_ENABLED(SYSRESET_AT91))
return device_bind_driver_to_node(dev, "at91_sysreset",
"at91_sysreset",
dev_ofnode(dev),
&at91_sysreset);
return 0;
}
static const struct udevice_id at91_reset_ids[] = {
{
.compatible = "microchip,sama7g5-rstc",
.data = (ulong)&sama7g5_data,
},
{
.compatible = "atmel,sama5d3-rstc",
},
{
.compatible = "microchip,sam9x60-rstc",
},
{ }
};
U_BOOT_DRIVER(at91_reset) = {
.name = "at91_reset",
.id = UCLASS_RESET,
.of_match = at91_reset_ids,
.bind = at91_reset_bind,
.probe = at91_reset_probe,
.priv_auto = sizeof(struct at91_reset),
.ops = &at91_reset_ops,
};

View file

@ -56,16 +56,9 @@ static struct sysreset_ops at91_sysreset = {
.request = at91_sysreset_request,
};
static const struct udevice_id a91_sysreset_ids[] = {
{ .compatible = "atmel,sama5d3-rstc" },
{ .compatible = "microchip,sam9x60-rstc" },
{ }
};
U_BOOT_DRIVER(sysreset_at91) = {
.id = UCLASS_SYSRESET,
.name = "at91_reset",
.name = "at91_sysreset",
.ops = &at91_sysreset,
.probe = at91_sysreset_probe,
.of_match = a91_sysreset_ids,
};

View file

@ -26,13 +26,4 @@
#define CFG_SYS_SDRAM_BASE 0x20000000
#define CFG_SYS_SDRAM_SIZE 0x10000000 /* 256 megs */
/* NAND flash */
#ifdef CONFIG_CMD_NAND
#define CFG_SYS_NAND_BASE 0x40000000
#define CFG_SYS_NAND_MASK_ALE BIT(21)
#define CFG_SYS_NAND_MASK_CLE BIT(22)
#define CFG_SYS_NAND_ENABLE_PIN AT91_PIN_PD4
#define CFG_SYS_NAND_READY_PIN AT91_PIN_PD5
#endif
#endif

View file

@ -18,5 +18,10 @@
#define PMC_TYPE_PERIPHERAL 3
#define PMC_TYPE_GCK 4
#define PMC_TYPE_SLOW 5
#define USB_UTMI 6
#define USB_UTMI1 0
#define USB_UTMI2 1
#define USB_UTMI3 2
#endif

View file

@ -0,0 +1,17 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* This header provides macros for AT91 USART DT bindings.
*
* Copyright (C) 2018 Microchip Technology
*
* Author: Radu Pirea <radu.pirea@microchip.com>
*
*/
#ifndef __DT_BINDINGS_AT91_USART_H__
#define __DT_BINDINGS_AT91_USART_H__
#define AT91_USART_MODE_SERIAL 0
#define AT91_USART_MODE_SPI 1
#endif /* __DT_BINDINGS_AT91_USART_H__ */

View file

@ -0,0 +1,10 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
#ifndef __DT_BINDINGS_RESET_SAMA7G5_H
#define __DT_BINDINGS_RESET_SAMA7G5_H
#define SAMA7G5_RESET_USB_PHY1 4
#define SAMA7G5_RESET_USB_PHY2 5
#define SAMA7G5_RESET_USB_PHY3 6
#endif /* __DT_BINDINGS_RESET_SAMA7G5_H */

View file

@ -0,0 +1,112 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2014 Atmel Corporation.
*
* Memory Controllers (MATRIX, EBI) - System peripherals registers.
*/
#ifndef _LINUX_MFD_SYSCON_ATMEL_MATRIX_H
#define _LINUX_MFD_SYSCON_ATMEL_MATRIX_H
#define AT91SAM9260_MATRIX_MCFG 0x00
#define AT91SAM9260_MATRIX_SCFG 0x40
#define AT91SAM9260_MATRIX_PRS 0x80
#define AT91SAM9260_MATRIX_MRCR 0x100
#define AT91SAM9260_MATRIX_EBICSA 0x11c
#define AT91SAM9261_MATRIX_MRCR 0x0
#define AT91SAM9261_MATRIX_SCFG 0x4
#define AT91SAM9261_MATRIX_TCR 0x24
#define AT91SAM9261_MATRIX_EBICSA 0x30
#define AT91SAM9261_MATRIX_USBPUCR 0x34
#define AT91SAM9263_MATRIX_MCFG 0x00
#define AT91SAM9263_MATRIX_SCFG 0x40
#define AT91SAM9263_MATRIX_PRS 0x80
#define AT91SAM9263_MATRIX_MRCR 0x100
#define AT91SAM9263_MATRIX_TCR 0x114
#define AT91SAM9263_MATRIX_EBI0CSA 0x120
#define AT91SAM9263_MATRIX_EBI1CSA 0x124
#define AT91SAM9RL_MATRIX_MCFG 0x00
#define AT91SAM9RL_MATRIX_SCFG 0x40
#define AT91SAM9RL_MATRIX_PRS 0x80
#define AT91SAM9RL_MATRIX_MRCR 0x100
#define AT91SAM9RL_MATRIX_TCR 0x114
#define AT91SAM9RL_MATRIX_EBICSA 0x120
#define AT91SAM9G45_MATRIX_MCFG 0x00
#define AT91SAM9G45_MATRIX_SCFG 0x40
#define AT91SAM9G45_MATRIX_PRS 0x80
#define AT91SAM9G45_MATRIX_MRCR 0x100
#define AT91SAM9G45_MATRIX_TCR 0x110
#define AT91SAM9G45_MATRIX_DDRMPR 0x118
#define AT91SAM9G45_MATRIX_EBICSA 0x128
#define AT91SAM9N12_MATRIX_MCFG 0x00
#define AT91SAM9N12_MATRIX_SCFG 0x40
#define AT91SAM9N12_MATRIX_PRS 0x80
#define AT91SAM9N12_MATRIX_MRCR 0x100
#define AT91SAM9N12_MATRIX_EBICSA 0x118
#define AT91SAM9X5_MATRIX_MCFG 0x00
#define AT91SAM9X5_MATRIX_SCFG 0x40
#define AT91SAM9X5_MATRIX_PRS 0x80
#define AT91SAM9X5_MATRIX_MRCR 0x100
#define AT91SAM9X5_MATRIX_EBICSA 0x120
#define SAMA5D3_MATRIX_MCFG 0x00
#define SAMA5D3_MATRIX_SCFG 0x40
#define SAMA5D3_MATRIX_PRS 0x80
#define SAMA5D3_MATRIX_MRCR 0x100
#define AT91_MATRIX_MCFG(o, x) ((o) + ((x) * 0x4))
#define AT91_MATRIX_ULBT GENMASK(2, 0)
#define AT91_MATRIX_ULBT_INFINITE (0 << 0)
#define AT91_MATRIX_ULBT_SINGLE (1 << 0)
#define AT91_MATRIX_ULBT_FOUR (2 << 0)
#define AT91_MATRIX_ULBT_EIGHT (3 << 0)
#define AT91_MATRIX_ULBT_SIXTEEN (4 << 0)
#define AT91_MATRIX_SCFG(o, x) ((o) + ((x) * 0x4))
#define AT91_MATRIX_SLOT_CYCLE GENMASK(7, 0)
#define AT91_MATRIX_DEFMSTR_TYPE GENMASK(17, 16)
#define AT91_MATRIX_DEFMSTR_TYPE_NONE (0 << 16)
#define AT91_MATRIX_DEFMSTR_TYPE_LAST (1 << 16)
#define AT91_MATRIX_DEFMSTR_TYPE_FIXED (2 << 16)
#define AT91_MATRIX_FIXED_DEFMSTR GENMASK(20, 18)
#define AT91_MATRIX_ARBT GENMASK(25, 24)
#define AT91_MATRIX_ARBT_ROUND_ROBIN (0 << 24)
#define AT91_MATRIX_ARBT_FIXED_PRIORITY (1 << 24)
#define AT91_MATRIX_ITCM_SIZE GENMASK(3, 0)
#define AT91_MATRIX_ITCM_0 (0 << 0)
#define AT91_MATRIX_ITCM_16 (5 << 0)
#define AT91_MATRIX_ITCM_32 (6 << 0)
#define AT91_MATRIX_ITCM_64 (7 << 0)
#define AT91_MATRIX_DTCM_SIZE GENMASK(7, 4)
#define AT91_MATRIX_DTCM_0 (0 << 4)
#define AT91_MATRIX_DTCM_16 (5 << 4)
#define AT91_MATRIX_DTCM_32 (6 << 4)
#define AT91_MATRIX_DTCM_64 (7 << 4)
#define AT91_MATRIX_PRAS(o, x) ((o) + ((x) * 0x8))
#define AT91_MATRIX_PRBS(o, x) ((o) + ((x) * 0x8) + 0x4)
#define AT91_MATRIX_MPR(x) GENMASK(((x) * 0x4) + 1, ((x) * 0x4))
#define AT91_MATRIX_RCB(x) BIT(x)
#define AT91_MATRIX_CSA(cs, val) ((val) << (cs))
#define AT91_MATRIX_DBPUC BIT(8)
#define AT91_MATRIX_DBPDC BIT(9)
#define AT91_MATRIX_VDDIOMSEL BIT(16)
#define AT91_MATRIX_VDDIOMSEL_1_8V (0 << 16)
#define AT91_MATRIX_VDDIOMSEL_3_3V (1 << 16)
#define AT91_MATRIX_EBI_IOSR BIT(17)
#define AT91_MATRIX_DDR_IOSR BIT(18)
#define AT91_MATRIX_NFD0_SELECT BIT(24)
#define AT91_MATRIX_DDR_MP_EN BIT(25)
#define AT91_MATRIX_USBPUCR_PUON BIT(30)
#endif /* _LINUX_MFD_SYSCON_ATMEL_MATRIX_H */

View file

@ -0,0 +1,119 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Atmel SMC (Static Memory Controller) register offsets and bit definitions.
*
* Copyright (C) 2014 Atmel
* Copyright (C) 2014 Free Electrons
*
* Author: Boris Brezillon <boris.brezillon@free-electrons.com>
*/
#ifndef _LINUX_MFD_SYSCON_ATMEL_SMC_H_
#define _LINUX_MFD_SYSCON_ATMEL_SMC_H_
#include <linux/kernel.h>
#include <dm/ofnode.h>
#include <regmap.h>
#define ATMEL_SMC_SETUP(cs) (((cs) * 0x10))
#define ATMEL_HSMC_SETUP(layout, cs) \
((layout)->timing_regs_offset + ((cs) * 0x14))
#define ATMEL_SMC_PULSE(cs) (((cs) * 0x10) + 0x4)
#define ATMEL_HSMC_PULSE(layout, cs) \
((layout)->timing_regs_offset + ((cs) * 0x14) + 0x4)
#define ATMEL_SMC_CYCLE(cs) (((cs) * 0x10) + 0x8)
#define ATMEL_HSMC_CYCLE(layout, cs) \
((layout)->timing_regs_offset + ((cs) * 0x14) + 0x8)
#define ATMEL_SMC_NWE_SHIFT 0
#define ATMEL_SMC_NCS_WR_SHIFT 8
#define ATMEL_SMC_NRD_SHIFT 16
#define ATMEL_SMC_NCS_RD_SHIFT 24
#define ATMEL_SMC_MODE(cs) (((cs) * 0x10) + 0xc)
#define ATMEL_HSMC_MODE(layout, cs) \
((layout)->timing_regs_offset + ((cs) * 0x14) + 0x10)
#define ATMEL_SMC_MODE_READMODE_MASK BIT(0)
#define ATMEL_SMC_MODE_READMODE_NCS (0 << 0)
#define ATMEL_SMC_MODE_READMODE_NRD (1 << 0)
#define ATMEL_SMC_MODE_WRITEMODE_MASK BIT(1)
#define ATMEL_SMC_MODE_WRITEMODE_NCS (0 << 1)
#define ATMEL_SMC_MODE_WRITEMODE_NWE (1 << 1)
#define ATMEL_SMC_MODE_EXNWMODE_MASK GENMASK(5, 4)
#define ATMEL_SMC_MODE_EXNWMODE_DISABLE (0 << 4)
#define ATMEL_SMC_MODE_EXNWMODE_FROZEN (2 << 4)
#define ATMEL_SMC_MODE_EXNWMODE_READY (3 << 4)
#define ATMEL_SMC_MODE_BAT_MASK BIT(8)
#define ATMEL_SMC_MODE_BAT_SELECT (0 << 8)
#define ATMEL_SMC_MODE_BAT_WRITE (1 << 8)
#define ATMEL_SMC_MODE_DBW_MASK GENMASK(13, 12)
#define ATMEL_SMC_MODE_DBW_8 (0 << 12)
#define ATMEL_SMC_MODE_DBW_16 (1 << 12)
#define ATMEL_SMC_MODE_DBW_32 (2 << 12)
#define ATMEL_SMC_MODE_TDF_MASK GENMASK(19, 16)
#define ATMEL_SMC_MODE_TDF(x) (((x) - 1) << 16)
#define ATMEL_SMC_MODE_TDF_MAX 16
#define ATMEL_SMC_MODE_TDF_MIN 1
#define ATMEL_SMC_MODE_TDFMODE_OPTIMIZED BIT(20)
#define ATMEL_SMC_MODE_PMEN BIT(24)
#define ATMEL_SMC_MODE_PS_MASK GENMASK(29, 28)
#define ATMEL_SMC_MODE_PS_4 (0 << 28)
#define ATMEL_SMC_MODE_PS_8 (1 << 28)
#define ATMEL_SMC_MODE_PS_16 (2 << 28)
#define ATMEL_SMC_MODE_PS_32 (3 << 28)
#define ATMEL_HSMC_TIMINGS(layout, cs) \
((layout)->timing_regs_offset + ((cs) * 0x14) + 0xc)
#define ATMEL_HSMC_TIMINGS_OCMS BIT(12)
#define ATMEL_HSMC_TIMINGS_RBNSEL(x) ((x) << 28)
#define ATMEL_HSMC_TIMINGS_NFSEL BIT(31)
#define ATMEL_HSMC_TIMINGS_TCLR_SHIFT 0
#define ATMEL_HSMC_TIMINGS_TADL_SHIFT 4
#define ATMEL_HSMC_TIMINGS_TAR_SHIFT 8
#define ATMEL_HSMC_TIMINGS_TRR_SHIFT 16
#define ATMEL_HSMC_TIMINGS_TWB_SHIFT 24
struct atmel_hsmc_reg_layout {
unsigned int timing_regs_offset;
};
/**
* struct atmel_smc_cs_conf - SMC CS config as described in the datasheet.
* @setup: NCS/NWE/NRD setup timings (not applicable to at91rm9200)
* @pulse: NCS/NWE/NRD pulse timings (not applicable to at91rm9200)
* @cycle: NWE/NRD cycle timings (not applicable to at91rm9200)
* @timings: advanced NAND related timings (only applicable to HSMC)
* @mode: all kind of config parameters (see the fields definition above).
* The mode fields are different on at91rm9200
*/
struct atmel_smc_cs_conf {
u32 setup;
u32 pulse;
u32 cycle;
u32 timings;
u32 mode;
};
void atmel_smc_cs_conf_init(struct atmel_smc_cs_conf *conf);
int atmel_smc_cs_conf_set_timing(struct atmel_smc_cs_conf *conf,
unsigned int shift,
unsigned int ncycles);
int atmel_smc_cs_conf_set_setup(struct atmel_smc_cs_conf *conf,
unsigned int shift, unsigned int ncycles);
int atmel_smc_cs_conf_set_pulse(struct atmel_smc_cs_conf *conf,
unsigned int shift, unsigned int ncycles);
int atmel_smc_cs_conf_set_cycle(struct atmel_smc_cs_conf *conf,
unsigned int shift, unsigned int ncycles);
void atmel_smc_cs_conf_apply(struct regmap *regmap, int cs,
const struct atmel_smc_cs_conf *conf);
void atmel_hsmc_cs_conf_apply(struct regmap *regmap,
const struct atmel_hsmc_reg_layout *reglayout,
int cs, const struct atmel_smc_cs_conf *conf);
void atmel_smc_cs_conf_get(struct regmap *regmap, int cs,
struct atmel_smc_cs_conf *conf);
void atmel_hsmc_cs_conf_get(struct regmap *regmap,
const struct atmel_hsmc_reg_layout *reglayout,
int cs, struct atmel_smc_cs_conf *conf);
const struct atmel_hsmc_reg_layout *
atmel_hsmc_get_reg_layout(ofnode np);
#endif /* _LINUX_MFD_SYSCON_ATMEL_SMC_H_ */