diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:252 @ ethernet-phy@0 { reg = <0x0>; + interrupt-parent = <&pioB>; + interrupts = <8 IRQ_TYPE_LEVEL_LOW>; }; }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:358 @ AT91_PIOB 5 AT91_PERIPH_A AT91_PINCTRL_NONE /* PB5 periph A */ AT91_PIOB 6 AT91_PERIPH_A AT91_PINCTRL_NONE /* PB6 periph A */ AT91_PIOB 7 AT91_PERIPH_A AT91_PINCTRL_NONE /* PB7 periph A */ + AT91_PIOB 8 AT91_PERIPH_GPIO AT91_PINCTRL_NONE /* PB8 IRQ GPIO */ AT91_PIOB 9 AT91_PERIPH_A AT91_PINCTRL_NONE /* PB9 periph A */ AT91_PIOB 10 AT91_PERIPH_A AT91_PINCTRL_NONE>; /* PB10 periph A */ }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:445 @ status = "okay"; }; +&rtt { + atmel,rtt-rtc-time-reg = <&gpbr 0x0>; +}; + &sdmmc0 { bus-width = <4>; pinctrl-names = "default"; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:466 @ status = "okay"; }; +&rtt { + atmel,rtt-rtc-time-reg = <&gpbr 0x0>; +}; + &shutdown_controller { debounce-delay-us = <976>; status = "okay"; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/boot/dts/microchip/at91-sam9x60ek.dts linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60ek.dts --- linux-6.6.51/arch/arm/boot/dts/microchip/at91-sam9x60ek.dts 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60ek.dts 2024-11-26 14:02:35.682461166 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:267 @ i2c-digital-filter-width-ns = <35>; status = "okay"; + pac1934@17 { + compatible = "microchip,pac1934"; + reg = <0x17>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + channel@1 { + reg = <0x1>; + shunt-resistor-micro-ohms = <10000>; + label = "VDDIOM"; + }; + + channel@2 { + reg = <0x2>; + shunt-resistor-micro-ohms = <10000>; + label = "VDDCORE"; + }; + + channel@3 { + reg = <0x3>; + shunt-resistor-micro-ohms = <10000>; + label = "VDD3V3_MPU"; + }; + + channel@4 { + reg = <0x4>; + shunt-resistor-micro-ohms = <10000>; + label = "VDD3V3"; + }; + }; + gpio_exp: mcp23008@20 { compatible = "microchip,mcp23008"; reg = <0x20>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:327 @ ethernet-phy@0 { reg = <0x0>; + interrupt-parent = <&pioB>; + interrupts = <8 IRQ_TYPE_LEVEL_LOW>; }; }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:502 @ AT91_PIOB 5 AT91_PERIPH_A AT91_PINCTRL_NONE /* PB5 periph A */ AT91_PIOB 6 AT91_PERIPH_A AT91_PINCTRL_NONE /* PB6 periph A */ AT91_PIOB 7 AT91_PERIPH_A AT91_PINCTRL_NONE /* PB7 periph A */ + AT91_PIOB 8 AT91_PERIPH_GPIO AT91_PINCTRL_NONE /* PB8 IRQ GPIO */ AT91_PIOB 9 AT91_PERIPH_A AT91_PINCTRL_NONE /* PB9 periph A */ AT91_PIOB 10 AT91_PERIPH_A AT91_PINCTRL_NONE>; /* PB10 periph A */ }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:617 @ compatible = "jedec,spi-nor"; reg = <0>; spi-max-frequency = <104000000>; - spi-cs-setup-ns = <7>; + spi-cs-setup-delay-ns = <7>; spi-tx-bus-width = <4>; spi-rx-bus-width = <4>; m25p,fast-read; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/boot/dts/microchip/at91-sam9x75_curiosity.dts linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x75_curiosity.dts --- linux-6.6.51/arch/arm/boot/dts/microchip/at91-sam9x75_curiosity.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x75_curiosity.dts 2024-11-26 14:04:18.560303308 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * at91-sam9x75_curiosity.dts - Device Tree file for Microchip SAM9X75 Curiosity board + * + * Copyright (C) 2023 Microchip Technology Inc. and its subsidiaries + * + * Author: Varshini Rajendran <varshini.rajendran@microchip.com> + */ +/dts-v1/; +#include "sam9x7.dtsi" +#include <dt-bindings/input/input.h> + +/ { + model = "Microchip SAM9X75 Curiosity"; + compatible = "microchip,sam9x75-curiosity", "microchip,sam9x7", "atmel,at91sam9"; + + aliases { + i2c0 = &i2c6; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + gpio-keys { + compatible = "gpio-keys"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_key_gpio_default>; + + button-user { + label = "USER"; + gpios = <&pioC 9 GPIO_ACTIVE_LOW>; + linux,code = <KEY_0>; + wakeup-source; + }; + }; + + leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + led_red: led-red { + pinctrl-0 = <&pinctrl_red_led_gpio_default>; + label = "red"; + gpios = <&pioC 14 GPIO_ACTIVE_HIGH>; + status = "okay"; + }; + + led_green: led-green { + pinctrl-0 = <&pinctrl_green_led_gpio_default>; + label = "green"; + gpios = <&pioC 21 GPIO_ACTIVE_HIGH>; + status = "okay"; + }; + + led_blue: led-blue { + pinctrl-0 = <&pinctrl_blue_led_gpio_default>; + label = "blue"; + gpios = <&pioC 20 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + status = "okay"; + }; + }; + + memory@20000000 { + device_type = "memory"; + reg = <0x20000000 0x10000000>; + }; +}; + +&classd { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_classd>; + atmel,pwm-type = "diff"; + atmel,non-overlap-time = <10>; + status = "okay"; +}; + +&adc { + vddana-supply = <&vdd_3v3>; + vref-supply = <&vdd_3v3>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_adc_default &pinctrl_adtrg_default>; + status = "okay"; +}; + +&dbgu { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_dbgu>; + status = "okay"; +}; + +&dma0 { + status = "okay"; +}; + +&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@2 { + reg = <0x2 0x0 0x800000>; + rb-gpios = <&pioD 14 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"; + }; + }; +}; + +&ehci0 { + status = "okay"; +}; + +&flx1 { + atmel,flexcom-mode = <ATMEL_FLEXCOM_MODE_USART>; + status = "okay"; + + uart1: serial@200 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_flx1_usart_default>; + status = "okay"; + }; +}; + +&flx6 { + atmel,flexcom-mode = <ATMEL_FLEXCOM_MODE_TWI>; + status = "okay"; +}; + +&i2c6 { + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_flx6_twi_default>; + i2c-analog-filter; + i2c-digital-filter; + i2c-digital-filter-width-ns = <35>; + status = "okay"; + + pmic@5b { + compatible = "microchip,mcp16502"; + reg = <0x5b>; + + regulators { + vdd_3v3: VDD_IO { + regulator-name = "VDD_IO"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3600000>; + regulator-initial-mode = <2>; + regulator-allowed-modes = <2>, <4>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + regulator-mode = <4>; + }; + + regulator-state-mem { + regulator-mode = <4>; + }; + }; + + vddioddr: VDD_DDR { + regulator-name = "VDD_DDR"; + regulator-min-microvolt = <1283000>; + regulator-max-microvolt = <1450000>; + regulator-initial-mode = <2>; + regulator-allowed-modes = <2>, <4>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + regulator-mode = <4>; + }; + + regulator-state-mem { + regulator-on-in-suspend; + regulator-mode = <4>; + }; + }; + + vddcore: VDD_CORE { + regulator-name = "VDD_CORE"; + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <1210000>; + regulator-initial-mode = <2>; + regulator-allowed-modes = <2>, <4>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + regulator-mode = <4>; + }; + + regulator-state-mem { + regulator-mode = <4>; + }; + }; + + vddcpu: VDD_OTHER { + regulator-name = "VDD_OTHER"; + regulator-min-microvolt = <1700000>; + regulator-max-microvolt = <3600000>; + regulator-initial-mode = <2>; + regulator-allowed-modes = <2>, <4>; + regulator-ramp-delay = <3125>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + regulator-mode = <4>; + }; + + regulator-state-mem { + regulator-mode = <4>; + }; + }; + + vldo1: LDO1 { + regulator-name = "LDO1"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3700000>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + }; + }; + + vldo2: LDO2 { + regulator-name = "LDO2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3700000>; + + regulator-state-standby { + regulator-on-in-suspend; + }; + }; + }; + }; +}; + +&i2s { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2s_default>; + #sound-dai-cells = <0>; + status = "disabled"; +}; + +&main_xtal { + clock-frequency = <24000000>; +}; + +&ohci0 { + num-ports = <3>; + atmel,vbus-gpio = <0 + &pioC 27 GPIO_ACTIVE_HIGH + &pioC 31 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usb_default>; + status = "okay"; +}; + +&pinctrl { + + classd { + pinctrl_classd: classd { + atmel,pins = + <AT91_PIOA 18 AT91_PERIPH_C AT91_PINCTRL_PULL_UP + AT91_PIOA 19 AT91_PERIPH_C AT91_PINCTRL_PULL_DOWN>; + }; + }; + + adc { + pinctrl_adc_default: adc-default { + atmel,pins = <AT91_PIOA 31 AT91_PERIPH_A AT91_PINCTRL_NONE>; + }; + + pinctrl_adtrg_default: adtrg-default { + atmel,pins = <AT91_PIOB 18 AT91_PERIPH_B AT91_PINCTRL_PULL_UP>; + }; + }; + + dbgu { + pinctrl_dbgu: dbgu { + atmel,pins = <AT91_PIOA 26 AT91_PERIPH_A AT91_PINCTRL_PULL_UP + AT91_PIOA 27 AT91_PERIPH_A AT91_PINCTRL_NONE>; + }; + }; + + 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)>; + }; + }; + + qspi { + pinctrl_qspi: pinctrl-qspi { + atmel,pins = + <AT91_PIOB 19 AT91_PERIPH_A AT91_PINCTRL_SLEWRATE_DIS + AT91_PIOB 20 AT91_PERIPH_A AT91_PINCTRL_SLEWRATE_DIS + AT91_PIOB 21 AT91_PERIPH_A (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_SLEWRATE_DIS) + AT91_PIOB 22 AT91_PERIPH_A (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_SLEWRATE_DIS) + AT91_PIOB 23 AT91_PERIPH_A (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_SLEWRATE_DIS) + AT91_PIOB 24 AT91_PERIPH_A (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_SLEWRATE_DIS)>; + }; + }; + + flexcom { + pinctrl_flx6_twi_default: flx6-twi-default { + atmel,pins = + <AT91_PIOA 24 AT91_PERIPH_A AT91_PINCTRL_PULL_UP + AT91_PIOA 25 AT91_PERIPH_A AT91_PINCTRL_PULL_UP>; + }; + + pinctrl_flx1_usart_default: flx1-usart-default { + atmel,pins = + <AT91_PIOA 28 AT91_PERIPH_A AT91_PINCTRL_NONE + AT91_PIOA 29 AT91_PERIPH_A AT91_PINCTRL_NONE>; + }; + }; + + gpio-keys { + pinctrl_key_gpio_default: key-gpio-default { + atmel,pins = <AT91_PIOC 9 AT91_PERIPH_GPIO AT91_PINCTRL_NONE>; + }; + }; + + i2s { + pinctrl_i2s_default: i2s { + atmel,pins = + <AT91_PIOB 26 AT91_PERIPH_D AT91_PINCTRL_NONE /* I2SCK */ + AT91_PIOB 15 AT91_PERIPH_D AT91_PINCTRL_NONE /* I2SWS */ + AT91_PIOB 16 AT91_PERIPH_D AT91_PINCTRL_NONE /* I2SDIN */ + AT91_PIOB 17 AT91_PERIPH_D AT91_PINCTRL_NONE /* I2SDOUT */ + AT91_PIOB 25 AT91_PERIPH_D AT91_PINCTRL_NONE>; /* I2SMCK */ + }; + }; + + leds { + pinctrl_red_led_gpio_default: red-led-gpio-default { + atmel,pins = <AT91_PIOC 14 AT91_PERIPH_GPIO AT91_PINCTRL_NONE>; + }; + pinctrl_green_led_gpio_default: green-led-gpio-default { + atmel,pins = <AT91_PIOC 21 AT91_PERIPH_GPIO AT91_PINCTRL_NONE>; + }; + pinctrl_blue_led_gpio_default: blue-led-gpio-default { + atmel,pins = <AT91_PIOC 20 AT91_PERIPH_GPIO AT91_PINCTRL_NONE>; + }; + }; + + 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 14 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>; + }; + }; + + ohci0 { + pinctrl_usb_default: usb-default { + atmel,pins = <AT91_PIOC 27 AT91_PERIPH_GPIO AT91_PINCTRL_NONE + AT91_PIOC 31 AT91_PERIPH_GPIO AT91_PINCTRL_NONE>; + }; + }; + + sdmmc0 { + pinctrl_sdmmc0_default: sdmmc0-default { + atmel,pins = + <AT91_PIOA 2 AT91_PERIPH_A (AT91_PINCTRL_DRIVE_STRENGTH_HI | AT91_PINCTRL_SLEWRATE_ENA) /* PA2 CK periph A with pullup */ + AT91_PIOA 1 AT91_PERIPH_A (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_DRIVE_STRENGTH_HI | AT91_PINCTRL_SLEWRATE_ENA) /* PA1 CMD periph A with pullup */ + AT91_PIOA 0 AT91_PERIPH_A (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_DRIVE_STRENGTH_HI | AT91_PINCTRL_SLEWRATE_ENA) /* PA0 DAT0 periph A */ + AT91_PIOA 3 AT91_PERIPH_A (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_DRIVE_STRENGTH_HI | AT91_PINCTRL_SLEWRATE_ENA) /* PA3 DAT1 periph A with pullup */ + AT91_PIOA 4 AT91_PERIPH_A (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_DRIVE_STRENGTH_HI | AT91_PINCTRL_SLEWRATE_ENA) /* PA4 DAT2 periph A with pullup */ + AT91_PIOA 5 AT91_PERIPH_A (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_DRIVE_STRENGTH_HI | AT91_PINCTRL_SLEWRATE_ENA)>; /* PA5 DAT3 periph A with pullup */ + }; + }; + + sdmmc1 { + pinctrl_sdmmc1_default: sdmmc1-default { + atmel,pins = + <AT91_PIOA 11 AT91_PERIPH_B (AT91_PINCTRL_DRIVE_STRENGTH_HI | AT91_PINCTRL_SLEWRATE_ENA) + AT91_PIOA 10 AT91_PERIPH_B (AT91_PINCTRL_DRIVE_STRENGTH_HI | AT91_PINCTRL_SLEWRATE_ENA) + AT91_PIOA 9 AT91_PERIPH_B (AT91_PINCTRL_DRIVE_STRENGTH_HI | AT91_PINCTRL_SLEWRATE_ENA) + AT91_PIOA 6 AT91_PERIPH_B (AT91_PINCTRL_DRIVE_STRENGTH_HI | AT91_PINCTRL_SLEWRATE_ENA) + AT91_PIOA 7 AT91_PERIPH_B (AT91_PINCTRL_DRIVE_STRENGTH_HI | AT91_PINCTRL_SLEWRATE_ENA) + AT91_PIOA 8 AT91_PERIPH_B (AT91_PINCTRL_DRIVE_STRENGTH_HI | AT91_PINCTRL_SLEWRATE_ENA)>; + }; + }; + + usb0 { + pinctrl_usba_vbus: usba-vbus { + atmel,pins = <AT91_PIOC 8 AT91_PERIPH_GPIO AT91_PINCTRL_NONE>; + }; + }; +}; /* pinctrl */ + +&rtt { + atmel,rtt-rtc-time-reg = <&gpbr 0x0>; +}; + +&qspi { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_qspi>; + status = "okay"; + + flash@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <100000000>; + spi-tx-bus-width = <4>; + spi-rx-bus-width = <4>; + m25p,fast-read; + + at91bootstrap@0 { + label = "qspi: at91bootstrap"; + reg = <0x0 0x40000>; + }; + + bootloader@40000 { + label = "qspi: bootloader"; + reg = <0x40000 0xc0000>; + }; + + bootloaderenvred@100000 { + label = "qspi: bootloader env redundant"; + reg = <0x100000 0x40000>; + }; + + bootloaderenv@140000 { + label = "qspi: bootloader env"; + reg = <0x140000 0x40000>; + }; + + dtb@180000 { + label = "qspi: device tree"; + reg = <0x180000 0x80000>; + }; + + kernel@200000 { + label = "qspi: kernel"; + reg = <0x200000 0x600000>; + }; + }; +}; + +&sdmmc0 { + bus-width = <4>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_sdmmc0_default>; + cd-gpios = <&pioA 23 GPIO_ACTIVE_LOW>; + disable-wp; + status = "okay"; +}; + +&sdmmc1 { + bus-width = <4>; + mmc-ddr-3_3v; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_sdmmc1_default>; + status = "okay"; +}; + +&slow_xtal { + clock-frequency = <32768>; +}; + +&shutdown_controller { + debounce-delay-us = <976>; + status = "okay"; + + input@0 { + reg = <0>; + }; +}; + +&trng { + status = "okay"; +}; + +&usb0 { + atmel,vbus-gpio = <&pioC 8 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usba_vbus>; + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/boot/dts/microchip/at91-sam9x75eb.dts linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x75eb.dts --- linux-6.6.51/arch/arm/boot/dts/microchip/at91-sam9x75eb.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x75eb.dts 2024-11-26 14:02:35.682461166 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * at91-sam9x75eb.dts - Device Tree file for Microchip SAM9X75-EB board + * + * Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries + * + * Author: Varshini Rajendran <varshini.rajendran@microchip.com> + */ +/dts-v1/; +#include "sam9x7.dtsi" +#include <dt-bindings/input/input.h> + +/ { + model = "Microchip SAM9X75-EB"; + compatible = "microchip,sam9x75eb", "microchip,sam9x7", "atmel,at91sam9"; + + aliases { + i2c0 = &i2c7; + i2c1 = &i2c10; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + clocks { + slow_xtal { + clock-frequency = <32768>; + }; + + main_xtal { + clock-frequency = <24000000>; + }; + }; + + gpio_keys { + compatible = "gpio-keys"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_key_gpio_default>; + status = "okay"; + + sw1 { + label = "USER"; + gpios = <&pioC 24 GPIO_ACTIVE_LOW>; + linux,code = <KEY_0>; + wakeup-source; + }; + }; + + leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_led_gpio_default>; + status = "okay"; + + red_led: red-led { + label = "red"; + gpios = <&pioC 19 GPIO_ACTIVE_HIGH>; + }; + + green_led: green-led { + label = "green"; + gpios = <&pioC 21 GPIO_ACTIVE_HIGH>; + }; + + blue_led: blue-led { + label = "blue"; + gpios = <&pioC 20 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + }; + }; + + memory@20000000 { + device_type = "memory"; + reg = <0x20000000 0x10000000>; + }; + + sound { + compatible = "mikroe,mikroe-proto"; + model = "wm8731 @ sam9x75eb"; + i2s-controller = <&i2s>; + audio-codec = <&wm8731>; + dai-format = "i2s"; + }; +}; + +&adc { + vddana-supply = <&vdd_3v3>; + vref-supply = <&vdd_3v3>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_adc_default>; + status = "okay"; +}; + +&can0 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_can0_default>; + status = "disabled"; /* Conflict with dbgu0 */ +}; + +&can1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_can1_default>; + status = "okay"; +}; + +&classd { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_classd_default>; + atmel,pwm-type = "diff"; + atmel,non-overlap-time = <10>; + status = "okay"; +}; + +&dbgu { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_dbgu>; + status = "okay"; +}; + +&dma0 { + status = "okay"; +}; + +&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@2 { + reg = <0x2 0x0 0x800000>; + rb-gpios = <&pioD 14 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"; + }; + }; +}; + +&ehci0 { + status = "okay"; +}; + +&flx7 { + atmel,flexcom-mode = <ATMEL_FLEXCOM_MODE_TWI>; + status = "okay"; + + i2c7: i2c@600 { + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_flx7_default>; + i2c-analog-filter; + i2c-digital-filter; + i2c-digital-filter-width-ns = <35>; + status = "okay"; + + eeprom@53 { + compatible = "atmel,24c02"; + reg = <0x53>; + pagesize = <16>; + status = "okay"; + }; + }; +}; + +&flx10 { + atmel,flexcom-mode = <ATMEL_FLEXCOM_MODE_TWI>; + status = "okay"; + + i2c10: i2c@600 { + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_flx10_default>; + i2c-analog-filter; + i2c-digital-filter; + i2c-digital-filter-width-ns = <35>; + status = "okay"; + + mcp16502@5b { + compatible = "microchip,mcp16502"; + reg = <0x5b>; + status = "okay"; + + regulators { + vdd_3v3: VDD_IO { + regulator-name = "VDD_IO"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3600000>; + regulator-initial-mode = <2>; + regulator-allowed-modes = <2>, <4>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + regulator-mode = <4>; + }; + + regulator-state-mem { + regulator-mode = <4>; + }; + }; + + vddioddr: VDD_DDR { + regulator-name = "VDD_DDR"; + regulator-min-microvolt = <1283000>; + regulator-max-microvolt = <1450000>; + regulator-initial-mode = <2>; + regulator-allowed-modes = <2>, <4>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + regulator-mode = <4>; + }; + + regulator-state-mem { + regulator-on-in-suspend; + regulator-mode = <4>; + }; + }; + + vddcore: VDD_CORE { + regulator-name = "VDD_CORE"; + regulator-min-microvolt = <500000>; + regulator-max-microvolt = <1210000>; + regulator-initial-mode = <2>; + regulator-allowed-modes = <2>, <4>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + regulator-mode = <4>; + }; + + regulator-state-mem { + regulator-mode = <4>; + }; + }; + + vddcpu: VDD_OTHER { + regulator-name = "VDD_OTHER"; + regulator-min-microvolt = <1700000>; + regulator-max-microvolt = <3600000>; + regulator-initial-mode = <2>; + regulator-allowed-modes = <2>, <4>; + regulator-ramp-delay = <3125>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + regulator-mode = <4>; + }; + + regulator-state-mem { + regulator-mode = <4>; + }; + }; + + vldo1: LDO1 { + regulator-name = "LDO1"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3700000>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + }; + }; + + vldo2: LDO2 { + regulator-name = "LDO2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3700000>; + + regulator-state-standby { + regulator-on-in-suspend; + }; + }; + }; + }; + + wm8731: codec@1a { + compatible = "wlf,wm8731"; + reg = <0x1a>; + }; + }; +}; + +&gmac { + phy-mode = "rgmii-id"; + #address-cells = <1>; + #size-cells = <0>; + + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_macb0_rgmii>; + magic-packet; + status = "okay"; + + ethernet-phy@7 { + reg = <0x7>; + }; +}; + +&gpbr { + status = "okay"; +}; + +&i2s { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_i2s_default>; + #sound-dai-cells = <0>; + status = "disabled"; /* Conflict with qspi */ +}; + +&ohci0 { + num-ports = <3>; + atmel,vbus-gpio = <0 + &pioC 27 GPIO_ACTIVE_HIGH + &pioC 29 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usb_default>; + status = "okay"; +}; + +&pinctrl { + adc { + pinctrl_adc_default: adc-default { + atmel,pins = <AT91_PIOA 30 AT91_PERIPH_A AT91_PINCTRL_NONE>; + }; + }; + + can0 { + pinctrl_can0_default: can0 { + atmel,pins = <AT91_PIOA 26 AT91_PERIPH_B AT91_PINCTRL_PULL_UP /* CAN0 Rx */ + AT91_PIOA 27 AT91_PERIPH_B AT91_PINCTRL_PULL_UP>; /* CAN0 Tx */ + }; + }; + + can1 { + pinctrl_can1_default: can1 { + atmel,pins = <AT91_PIOA 28 AT91_PERIPH_B AT91_PINCTRL_PULL_UP /* CAN1 Tx */ + AT91_PIOA 29 AT91_PERIPH_B AT91_PINCTRL_PULL_UP>; /* CAN1 Rx */ + }; + }; + + classd { + pinctrl_classd_default: classd { + atmel,pins = + <AT91_PIOA 18 AT91_PERIPH_C AT91_PINCTRL_PULL_UP + AT91_PIOA 19 AT91_PERIPH_C AT91_PINCTRL_PULL_DOWN + AT91_PIOA 20 AT91_PERIPH_C AT91_PINCTRL_PULL_UP + AT91_PIOA 21 AT91_PERIPH_C AT91_PINCTRL_PULL_DOWN>; + }; + }; + + dbgu { + pinctrl_dbgu: dbgu-0 { + atmel,pins = <AT91_PIOA 26 AT91_PERIPH_A AT91_PINCTRL_PULL_UP + AT91_PIOA 27 AT91_PERIPH_A AT91_PINCTRL_NONE>; + }; + }; + + 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)>; + }; + }; + + flexcom { + pinctrl_flx7_default: flx7_twi { + atmel,pins = + <AT91_PIOC 0 AT91_PERIPH_C AT91_PINCTRL_PULL_UP + AT91_PIOC 1 AT91_PERIPH_C AT91_PINCTRL_PULL_UP>; + }; + + pinctrl_flx10_default: flx10_twi { + atmel,pins = + <AT91_PIOC 16 AT91_PERIPH_C AT91_PINCTRL_PULL_UP + AT91_PIOC 17 AT91_PERIPH_C AT91_PINCTRL_PULL_UP>; + }; + }; + + gpio_keys { + pinctrl_key_gpio_default: pinctrl_key_gpio { + atmel,pins = <AT91_PIOC 24 AT91_PERIPH_GPIO AT91_PINCTRL_NONE>; + }; + }; + + i2s { + pinctrl_i2s_default: i2s { + atmel,pins = + <AT91_PIOB 26 AT91_PERIPH_D AT91_PINCTRL_NONE /* I2SCK */ + AT91_PIOB 15 AT91_PERIPH_D AT91_PINCTRL_NONE /* I2SWS */ + AT91_PIOB 16 AT91_PERIPH_D AT91_PINCTRL_NONE /* I2SDIN */ + AT91_PIOB 17 AT91_PERIPH_D AT91_PINCTRL_NONE /* I2SDOUT */ + AT91_PIOB 25 AT91_PERIPH_D AT91_PINCTRL_NONE>; /* I2SMCK */ + }; + }; + + leds { + pinctrl_led_gpio_default: pinctrl_led_gpio { + atmel,pins = <AT91_PIOC 19 AT91_PERIPH_GPIO AT91_PINCTRL_NONE + AT91_PIOC 21 AT91_PERIPH_GPIO AT91_PINCTRL_NONE + AT91_PIOC 20 AT91_PERIPH_GPIO AT91_PINCTRL_NONE>; + }; + }; + + macb0 { + pinctrl_macb0_rgmii: macb0_rgmii-0 { + atmel,pins = + <AT91_PIOB 13 AT91_PERIPH_A AT91_PINCTRL_SLEWRATE_DIS /* ETH_TX0 */ + AT91_PIOB 14 AT91_PERIPH_A AT91_PINCTRL_SLEWRATE_DIS /* ETH_TX1 */ + AT91_PIOB 4 AT91_PERIPH_A AT91_PINCTRL_SLEWRATE_DIS /* ETH_TX2 */ + AT91_PIOB 5 AT91_PERIPH_A AT91_PINCTRL_SLEWRATE_DIS /* ETH_TX3 */ + AT91_PIOB 7 AT91_PERIPH_A AT91_PINCTRL_SLEWRATE_DIS /* ETH_TXCTL */ + AT91_PIOB 6 AT91_PERIPH_A (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_SLEWRATE_DIS) /* ETH_TXCK */ + + AT91_PIOB 11 AT91_PERIPH_A AT91_PINCTRL_SLEWRATE_DIS /* ETH_RX0 */ + AT91_PIOB 12 AT91_PERIPH_A AT91_PINCTRL_SLEWRATE_DIS /* ETH_RX1 */ + AT91_PIOB 0 AT91_PERIPH_A AT91_PINCTRL_SLEWRATE_DIS /* ETH_RX2 */ + AT91_PIOB 1 AT91_PERIPH_A AT91_PINCTRL_SLEWRATE_DIS /* ETH_RX3 */ + AT91_PIOB 8 AT91_PERIPH_A AT91_PINCTRL_SLEWRATE_DIS /* ETH_RXCK */ + AT91_PIOB 3 AT91_PERIPH_A AT91_PINCTRL_SLEWRATE_DIS /* ETH_RXCTL */ + + AT91_PIOB 10 AT91_PERIPH_A AT91_PINCTRL_NONE /* ETH_MDC */ + AT91_PIOB 9 AT91_PERIPH_A AT91_PINCTRL_NONE /* ETH_MDIO */ + + AT91_PIOB 2 AT91_PERIPH_A AT91_PINCTRL_SLEWRATE_DIS /* ETH_125CK */ + AT91_PIOD 5 AT91_PERIPH_A AT91_PINCTRL_SLEWRATE_DIS>; /* ETH_INT */ + }; + }; + + 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 14 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>; + }; + }; + + ohci0 { + pinctrl_usb_default: usb_default { + atmel,pins = <AT91_PIOC 27 AT91_PERIPH_GPIO AT91_PINCTRL_NONE + AT91_PIOC 29 AT91_PERIPH_GPIO AT91_PINCTRL_NONE>; + }; + }; + + qspi { + pinctrl_qspi: qspi { + atmel,pins = + <AT91_PIOB 19 AT91_PERIPH_A AT91_PINCTRL_SLEWRATE_DIS /* QSCK */ + AT91_PIOB 20 AT91_PERIPH_A AT91_PINCTRL_SLEWRATE_DIS /* QCS */ + AT91_PIOB 21 AT91_PERIPH_A (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_SLEWRATE_DIS) /* QIO0 */ + AT91_PIOB 22 AT91_PERIPH_A (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_SLEWRATE_DIS) /* QIO1 */ + AT91_PIOB 23 AT91_PERIPH_A (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_SLEWRATE_DIS) /* QIO2 */ + AT91_PIOB 24 AT91_PERIPH_A (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_SLEWRATE_DIS) /* QIO3 */ + AT91_PIOB 26 AT91_PERIPH_A (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_SLEWRATE_DIS) /* QIO4 */ + AT91_PIOB 15 AT91_PERIPH_A (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_SLEWRATE_DIS) /* QIO5 */ + AT91_PIOB 16 AT91_PERIPH_A (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_SLEWRATE_DIS) /* QIO6 */ + AT91_PIOB 17 AT91_PERIPH_A (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_SLEWRATE_DIS) /* QIO7 */ + AT91_PIOB 25 AT91_PERIPH_A (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_SLEWRATE_DIS)>; /* QINT */ + }; + }; + + sdmmc0 { + pinctrl_sdmmc0_default: sdmmc0 { + atmel,pins = + <AT91_PIOA 2 AT91_PERIPH_A (AT91_PINCTRL_DRIVE_STRENGTH_HI | AT91_PINCTRL_SLEWRATE_DIS) /* PA2 CK periph A with pullup */ + AT91_PIOA 1 AT91_PERIPH_A (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_DRIVE_STRENGTH_HI | AT91_PINCTRL_SLEWRATE_DIS) /* PA1 CMD periph A with pullup */ + AT91_PIOA 0 AT91_PERIPH_A (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_DRIVE_STRENGTH_HI | AT91_PINCTRL_SLEWRATE_DIS) /* PA0 DAT0 periph A */ + AT91_PIOA 3 AT91_PERIPH_A (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_DRIVE_STRENGTH_HI | AT91_PINCTRL_SLEWRATE_DIS) /* PA3 DAT1 periph A with pullup */ + AT91_PIOA 4 AT91_PERIPH_A (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_DRIVE_STRENGTH_HI | AT91_PINCTRL_SLEWRATE_DIS) /* PA4 DAT2 periph A with pullup */ + AT91_PIOA 5 AT91_PERIPH_A (AT91_PINCTRL_PULL_UP | AT91_PINCTRL_DRIVE_STRENGTH_HI | AT91_PINCTRL_SLEWRATE_DIS)>; /* PA5 DAT3 periph A with pullup */ + }; + }; + + usb0 { + pinctrl_usba_vbus: usba_vbus { + atmel,pins = <AT91_PIOC 26 AT91_PERIPH_GPIO AT91_PINCTRL_NONE>; + }; + }; +}; /* pinctrl */ + +&qspi { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_qspi>; + status = "okay"; + + flash@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <100000000>; + spi-tx-bus-width = <8>; + spi-rx-bus-width = <8>; + m25p,fast-read; + + at91bootstrap@0 { + label = "ospi: at91bootstrap"; + reg = <0x0 0x40000>; + }; + + bootloader@40000 { + label = "ospi: bootloader"; + reg = <0x40000 0xc0000>; + }; + + bootloaderenvred@100000 { + label = "ospi: bootloader env redundant"; + reg = <0x100000 0x40000>; + }; + + bootloaderenv@140000 { + label = "ospi: bootloader env"; + reg = <0x140000 0x40000>; + }; + + dtb@180000 { + label = "ospi: device tree"; + reg = <0x180000 0x80000>; + }; + + kernel@200000 { + label = "ospi: kernel"; + reg = <0x200000 0x600000>; + }; + }; +}; + +&rtt { + atmel,rtt-rtc-time-reg = <&gpbr 0x0>; +}; + +&sdmmc0 { + bus-width = <4>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_sdmmc0_default>; + cd-gpios = <&pioA 13 GPIO_ACTIVE_LOW>; + disable-wp; + status = "okay"; +}; + +&shutdown_controller { + atmel,shdwc-debouncer = <976>; + status = "okay"; + + input@0 { + reg = <0>; + }; +}; + +&trng { + status = "okay"; +}; + +&usb0 { + atmel,vbus-gpio = <&pioC 26 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usba_vbus>; + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/boot/dts/microchip/at91-sama5d27_som1.dtsi linux-microchip/arch/arm/boot/dts/microchip/at91-sama5d27_som1.dtsi --- linux-6.6.51/arch/arm/boot/dts/microchip/at91-sama5d27_som1.dtsi 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/boot/dts/microchip/at91-sama5d27_som1.dtsi 2024-11-26 14:02:35.682461166 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:47 @ compatible = "jedec,spi-nor"; reg = <0>; spi-max-frequency = <104000000>; - spi-cs-setup-ns = <7>; + spi-cs-setup-delay-ns = <7>; spi-tx-bus-width = <4>; spi-rx-bus-width = <4>; m25p,fast-read; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/boot/dts/microchip/at91-sama5d27_som1_ek.dts linux-microchip/arch/arm/boot/dts/microchip/at91-sama5d27_som1_ek.dts --- linux-6.6.51/arch/arm/boot/dts/microchip/at91-sama5d27_som1_ek.dts 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/boot/dts/microchip/at91-sama5d27_som1_ek.dts 2024-11-26 14:02:35.682461166 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:57 @ sdmmc0: sdio-host@a0000000 { bus-width = <8>; - mmc-ddr-3_3v; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_sdmmc0_default>; status = "okay"; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/boot/dts/microchip/at91-sama5d27_wlsom1.dtsi linux-microchip/arch/arm/boot/dts/microchip/at91-sama5d27_wlsom1.dtsi --- linux-6.6.51/arch/arm/boot/dts/microchip/at91-sama5d27_wlsom1.dtsi 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/boot/dts/microchip/at91-sama5d27_wlsom1.dtsi 2024-11-26 14:02:35.682461166 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:224 @ compatible = "jedec,spi-nor"; reg = <0>; spi-max-frequency = <104000000>; - spi-cs-setup-ns = <7>; + spi-cs-setup-delay-ns = <7>; spi-rx-bus-width = <4>; spi-tx-bus-width = <4>; m25p,fast-read; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/boot/dts/microchip/at91-sama5d27_wlsom1_ek.dts linux-microchip/arch/arm/boot/dts/microchip/at91-sama5d27_wlsom1_ek.dts --- linux-6.6.51/arch/arm/boot/dts/microchip/at91-sama5d27_wlsom1_ek.dts 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/boot/dts/microchip/at91-sama5d27_wlsom1_ek.dts 2024-11-26 14:04:18.560303308 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:200 @ &sdmmc0 { bus-width = <4>; - mmc-ddr-3_3v; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_sdmmc0_default>; status = "okay"; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/boot/dts/microchip/at91-sama5d29_curiosity.dts linux-microchip/arch/arm/boot/dts/microchip/at91-sama5d29_curiosity.dts --- linux-6.6.51/arch/arm/boot/dts/microchip/at91-sama5d29_curiosity.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/arch/arm/boot/dts/microchip/at91-sama5d29_curiosity.dts 2024-11-26 14:04:18.560303308 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * at91-sama5d29_curiosity.dts - Device Tree file for SAMA5D29 Curiosity board + * + * Copyright (C) 2023 Microchip Technology Inc. and its subsidiaries + * + * Author: Mihai Sain <mihai.sain@microchip.com> + * + */ +/dts-v1/; +#include "sama5d29.dtsi" +#include "sama5d2-pinfunc.h" +#include <dt-bindings/gpio/gpio.h> +#include <dt-bindings/input/input.h> +#include <dt-bindings/mfd/atmel-flexcom.h> + +/ { + model = "Microchip SAMA5D29 Curiosity"; + compatible = "microchip,sama5d29-curiosity", "atmel,sama5d29", "atmel,sama5d2", "atmel,sama5"; + + aliases { + serial0 = &uart0; // debug + serial1 = &uart1; // RPi + serial2 = &uart3; // mikro BUS 2 + serial3 = &uart4; // mikro BUS 1 + serial4 = &uart6; // flx1 Bluetooth + i2c0 = &i2c0; + i2c1 = &i2c1; + }; + + chosen { + bootargs = "console=ttyS0,115200 root=/dev/mmcblk0p2 rw rootwait"; + stdout-path = "serial0:115200n8"; + }; + + clocks { + slow_xtal { + clock-frequency = <32768>; + }; + + main_xtal { + clock-frequency = <24000000>; + }; + }; + + gpio-keys { + compatible = "gpio-keys"; + + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_key_gpio_default>; + + button-1 { + label = "USER BUTTON"; + gpios = <&pioA PIN_PA17 GPIO_ACTIVE_LOW>; + linux,code = <KEY_PROG1>; + wakeup-source; + }; + }; + + leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_led_gpio_default>; + status = "okay"; + + led-red { + label = "red"; + gpios = <&pioA PIN_PA7 GPIO_ACTIVE_HIGH>; + }; + + led-green { + label = "green"; + gpios = <&pioA PIN_PA8 GPIO_ACTIVE_HIGH>; + }; + + led-blue { + label = "blue"; + gpios = <&pioA PIN_PA9 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + }; + }; + + memory@20000000 { + device_type = "memory"; + reg = <0x20000000 0x20000000>; + }; +}; + +&adc { + vddana-supply = <&vdd_3v3>; + vref-supply = <&vdd_3v3>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_adc_default &pinctrl_adtrg_default>; + status = "okay"; +}; + +&can0 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_can0_default>; + status = "okay"; +}; + +&can1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_can1_default>; + status = "okay"; +}; + +&flx1 { + atmel,flexcom-mode = <ATMEL_FLEXCOM_MODE_USART>; + status = "okay"; + + uart6: serial@200 { + pinctrl-0 = <&pinctrl_flx1_default>; + pinctrl-names = "default"; + atmel,use-dma-rx; + atmel,use-dma-tx; + status = "okay"; + }; +}; + +&flx4 { + atmel,flexcom-mode = <ATMEL_FLEXCOM_MODE_SPI>; + status = "okay"; + + spi6: spi@400 { + dmas = <0>, <0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_rpi_spi>; + status = "okay"; + }; +}; + +&i2c0 { + dmas = <0>, <0>; + pinctrl-names = "default", "gpio"; + pinctrl-0 = <&pinctrl_i2c0_default>; + pinctrl-1 = <&pinctrl_i2c0_gpio>; + sda-gpios = <&pioA PIN_PB31 GPIO_ACTIVE_HIGH>; + scl-gpios = <&pioA PIN_PC0 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + i2c-sda-hold-time-ns = <350>; + status = "okay"; + + mcp16502@5b { + compatible = "microchip,mcp16502"; + reg = <0x5b>; + status = "okay"; + lpm-gpios = <&pioBU 0 GPIO_ACTIVE_LOW>; + + regulators { + vdd_3v3: VDD_IO { + regulator-name = "VDD_IO"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-initial-mode = <2>; + regulator-allowed-modes = <2>, <4>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + regulator-mode = <4>; + }; + + regulator-state-mem { + regulator-off-in-suspend; + regulator-mode = <4>; + }; + }; + + vddio_ddr: VDD_DDR { + regulator-name = "VDD_DDR"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = <2>; + regulator-allowed-modes = <2>, <4>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1200000>; + regulator-changeable-in-suspend; + regulator-mode = <4>; + }; + + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1200000>; + regulator-changeable-in-suspend; + regulator-mode = <4>; + }; + }; + + vdd_core: VDD_CORE { + regulator-name = "VDD_CORE"; + regulator-min-microvolt = <1250000>; + regulator-max-microvolt = <1250000>; + regulator-initial-mode = <2>; + regulator-allowed-modes = <2>, <4>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + regulator-mode = <4>; + }; + + regulator-state-mem { + regulator-off-in-suspend; + regulator-mode = <4>; + }; + }; + + vdd_ddr: VDD_OTHER { + regulator-name = "VDD_OTHER"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = <2>; + regulator-allowed-modes = <2>, <4>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + regulator-changeable-in-suspend; + regulator-mode = <4>; + }; + + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1800000>; + regulator-changeable-in-suspend; + regulator-mode = <4>; + }; + }; + + LDO1 { + regulator-name = "LDO1"; + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <2500000>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + }; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + LDO2 { + regulator-name = "LDO2"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + }; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + }; + }; +}; + +&i2c1 { + dmas = <0>, <0>; + pinctrl-names = "default", "gpio"; + pinctrl-0 = <&pinctrl_i2c1_default>; + pinctrl-1 = <&pinctrl_i2c1_gpio>; + i2c-analog-filter; + i2c-digital-filter; + i2c-digital-filter-width-ns = <35>; + sda-gpios = <&pioA PIN_PD4 GPIO_ACTIVE_HIGH>; + scl-gpios = <&pioA PIN_PD5 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + status = "okay"; +}; + +&pioA { + pinctrl_adc_default: adc-default { + pinmux = <PIN_PD25__GPIO>, + <PIN_PD26__GPIO>; + bias-disable; + }; + + pinctrl_adtrg_default: adtrg-default { + pinmux = <PIN_PD31__ADTRG>; + bias-pull-up; + }; + + pinctrl_can0_default: can0-default { + pinmux = <PIN_PC10__CANTX0>, + <PIN_PC11__CANRX0>; + bias-disable; + }; + + pinctrl_can1_default: can1-default { + pinmux = <PIN_PC26__CANTX1>, + <PIN_PC27__CANRX1>; + bias-disable; + }; + + pinctrl_debug_uart: debug-uart { + pinmux = <PIN_PB26__URXD0>, + <PIN_PB27__UTXD0>; + bias-disable; + }; + + pinctrl_flx1_default: flx1-default { + pinmux = <PIN_PA24__FLEXCOM1_IO0>, + <PIN_PA23__FLEXCOM1_IO1>, + <PIN_PA25__FLEXCOM1_IO3>, + <PIN_PA26__FLEXCOM1_IO4>; + bias-disable; + }; + + pinctrl_i2c0_default: i2c0-default { + pinmux = <PIN_PB31__TWD0>, + <PIN_PC0__TWCK0>; + bias-disable; + }; + + pinctrl_i2c0_gpio: i2c0-gpio-default { + pinmux = <PIN_PB31__GPIO>, + <PIN_PC0__GPIO>; + bias-disable; + }; + + pinctrl_i2c1_default: i2c1-default { + pinmux = <PIN_PD4__TWD1>, + <PIN_PD5__TWCK1>; + bias-disable; + }; + + pinctrl_i2c1_gpio: i2c1-gpio-default { + pinmux = <PIN_PD4__GPIO>, + <PIN_PD5__GPIO>; + bias-disable; + }; + + pinctrl_key_gpio_default: key-gpio-default { + pinmux = <PIN_PA17__GPIO>; + bias-pull-up; + }; + + pinctrl_led_gpio_default: led-gpio-default { + pinmux = <PIN_PA7__GPIO>, + <PIN_PA8__GPIO>, + <PIN_PA9__GPIO>; + bias-pull-up; + }; + + pinctrl_mikrobus1_pwm: mikrobus1-pwm { + pinmux = <PIN_PA31__PWML0>; + bias-disable; + }; + + pinctrl_mikrobus2_pwm: mikrobus2-pwm { + pinmux = <PIN_PB0__PWMH1>; + bias-disable; + }; + + pinctrl_mikrobus1_uart: mikrobus1-uart { + pinmux = <PIN_PB3__URXD4>, + <PIN_PB4__UTXD4>; + bias-disable; + }; + + pinctrl_mikrobus2_uart: mikrobus2-uart { + pinmux = <PIN_PB11__URXD3>, + <PIN_PB12__UTXD3>; + bias-disable; + }; + + pinctrl_qspi1_default: qspi1-default { + pinmux = <PIN_PB5__QSPI1_SCK>, + <PIN_PB6__QSPI1_CS>, + <PIN_PB7__QSPI1_IO0>, + <PIN_PB8__QSPI1_IO1>, + <PIN_PB9__QSPI1_IO2>, + <PIN_PB10__QSPI1_IO3>; + bias-disable; + }; + + pinctrl_rpi_spi: rpi-spi { + pinmux = <PIN_PD12__FLEXCOM4_IO0>, + <PIN_PD13__FLEXCOM4_IO1>, + <PIN_PD14__FLEXCOM4_IO2>, + <PIN_PD15__FLEXCOM4_IO3>, + <PIN_PD16__FLEXCOM4_IO4>; + bias-disable; + }; + + pinctrl_rpi_uart: rpi-uart { + pinmux = <PIN_PD2__URXD1>, + <PIN_PD3__UTXD1>; + bias-disable; + }; + + pinctrl_sdmmc0_default: sdmmc0-default { + pinmux = <PIN_PA0__SDMMC0_CK>, + <PIN_PA1__SDMMC0_CMD>, + <PIN_PA2__SDMMC0_DAT0>, + <PIN_PA3__SDMMC0_DAT1>, + <PIN_PA4__SDMMC0_DAT2>, + <PIN_PA5__SDMMC0_DAT3>, + <PIN_PA11__SDMMC0_VDDSEL>, + <PIN_PA13__SDMMC0_CD>; + bias-disable; + }; + + pinctrl_sdmmc1_default: sdmmc1-default { + pinmux = <PIN_PA18__SDMMC1_DAT0>, + <PIN_PA19__SDMMC1_DAT1>, + <PIN_PA20__SDMMC1_DAT2>, + <PIN_PA21__SDMMC1_DAT3>, + <PIN_PA22__SDMMC1_CK>, + <PIN_PA28__SDMMC1_CMD>, + <PIN_PA30__SDMMC1_CD>; + bias-disable; + }; + + pinctrl_spi1_default: spi1-default { + pinmux = <PIN_PC1__SPI1_SPCK>, + <PIN_PC2__SPI1_MOSI>, + <PIN_PC3__SPI1_MISO>, + <PIN_PC4__SPI1_NPCS0>, + <PIN_PC5__SPI1_NPCS1>, + <PIN_PC6__SPI1_NPCS2>, + <PIN_PC7__SPI1_NPCS3>; + bias-disable; + }; + + pinctrl_usb_default: usb-default { + pinmux = <PIN_PA6__GPIO>; + bias-disable; + }; + + pinctrl_usba_vbus: usba-vbus { + pinmux = <PIN_PB13__GPIO>; + bias-disable; + }; +}; + +&pwm0 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_mikrobus1_pwm &pinctrl_mikrobus2_pwm>; + status = "okay"; +}; + +&qspi1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_qspi1_default>; + status = "okay"; + + flash@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <80000000>; + spi-tx-bus-width = <4>; + spi-rx-bus-width = <4>; + m25p,fast-read; + label = "atmel_qspi1"; + status = "okay"; + + at91bootstrap@0 { + label = "at91bootstrap"; + reg = <0x0 0x40000>; + }; + + bootloader@40000 { + label = "bootloader"; + reg = <0x40000 0xc0000>; + }; + + bootloaderenvred@100000 { + label = "bootloader env redundant"; + reg = <0x100000 0x40000>; + }; + + bootloaderenv@140000 { + label = "bootloader env"; + reg = <0x140000 0x40000>; + }; + + dtb@180000 { + label = "device tree"; + reg = <0x180000 0x80000>; + }; + + kernel@200000 { + label = "kernel"; + reg = <0x200000 0x600000>; + }; + }; +}; + +&sdmmc0 { + bus-width = <4>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_sdmmc0_default>; + disable-wp; + status = "okay"; +}; + +&sdmmc1 { + bus-width = <4>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_sdmmc1_default>; + disable-wp; + status = "okay"; +}; + +&shutdown_controller { + debounce-delay-us = <976>; + atmel,wakeup-rtc-timer; + + input@0 { + reg = <0>; + }; +}; + +&spi1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spi1_default>; + status = "okay"; +}; + +&tcb0 { + timer0: timer@0 { + compatible = "atmel,tcb-timer"; + reg = <0>; + }; + + timer1: timer@1 { + compatible = "atmel,tcb-timer"; + reg = <1>; + }; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_debug_uart>; + atmel,use-dma-rx; + atmel,use-dma-tx; + status = "okay"; +}; + +&uart1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_rpi_uart>; + atmel,use-dma-rx; + atmel,use-dma-tx; + status = "okay"; +}; + +&uart3 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_mikrobus2_uart>; + atmel,use-dma-rx; + atmel,use-dma-tx; + status = "okay"; +}; + +&uart4 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_mikrobus1_uart>; + atmel,use-dma-rx; + atmel,use-dma-tx; + status = "okay"; +}; + +&usb0 { + atmel,vbus-gpio = <&pioA PIN_PB13 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usba_vbus>; + status = "okay"; +}; + +&usb1 { + num-ports = <3>; + atmel,vbus-gpio = <0 + &pioA PIN_PA6 GPIO_ACTIVE_HIGH + 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usb_default>; + status = "okay"; +}; + +&usb2 { + status = "okay"; +}; + +&watchdog { + status = "okay"; +}; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/boot/dts/microchip/at91-sama5d2_icp.dts linux-microchip/arch/arm/boot/dts/microchip/at91-sama5d2_icp.dts --- linux-6.6.51/arch/arm/boot/dts/microchip/at91-sama5d2_icp.dts 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/boot/dts/microchip/at91-sama5d2_icp.dts 2024-11-26 14:02:35.682461166 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:190 @ i2c-digital-filter-width-ns = <35>; status = "okay"; + pac1934@10 { + compatible = "microchip,pac1934"; + reg = <0x10>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + channel@1 { + reg = <0x1>; + shunt-resistor-micro-ohms = <10000>; + label = "VDD3V3_1"; + }; + + channel@2 { + reg = <0x2>; + shunt-resistor-micro-ohms = <10000>; + label = "VDD3V3_2"; + }; + + channel@3 { + reg = <0x3>; + shunt-resistor-micro-ohms = <10000>; + label = "VDDCORE"; + }; + + channel@4 { + reg = <0x4>; + shunt-resistor-micro-ohms = <10000>; + label = "VDDIODDR"; + }; + }; + mcp16502@5b { compatible = "microchip,mcp16502"; reg = <0x5b>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:705 @ compatible = "jedec,spi-nor"; reg = <0>; spi-max-frequency = <104000000>; - spi-cs-setup-ns = <7>; + spi-cs-setup-delay-ns = <7>; spi-tx-bus-width = <4>; spi-rx-bus-width = <4>; m25p,fast-read; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/boot/dts/microchip/at91-sama5d2_xplained.dts linux-microchip/arch/arm/boot/dts/microchip/at91-sama5d2_xplained.dts --- linux-6.6.51/arch/arm/boot/dts/microchip/at91-sama5d2_xplained.dts 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/boot/dts/microchip/at91-sama5d2_xplained.dts 2024-11-26 14:02:35.682461166 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:314 @ active-semi,input-voltage-threshold-microvolt = <6600>; active-semi,precondition-timeout = <40>; active-semi,total-timeout = <3>; - status = "okay"; + status = "disabled"; }; }; }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/boot/dts/microchip/at91-sama5d4_xplained.dts linux-microchip/arch/arm/boot/dts/microchip/at91-sama5d4_xplained.dts --- linux-6.6.51/arch/arm/boot/dts/microchip/at91-sama5d4_xplained.dts 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/boot/dts/microchip/at91-sama5d4_xplained.dts 2024-11-26 14:02:35.682461166 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:44 @ i2c0: i2c@f8014000 { i2c-digital-filter; + i2c-digital-filter-width-ns = <70>; status = "okay"; }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/boot/dts/microchip/at91-sama7g54_curiosity.dts linux-microchip/arch/arm/boot/dts/microchip/at91-sama7g54_curiosity.dts --- linux-6.6.51/arch/arm/boot/dts/microchip/at91-sama7g54_curiosity.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/arch/arm/boot/dts/microchip/at91-sama7g54_curiosity.dts 2024-11-26 14:02:35.682461166 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * at91-sama7g54_curiosity.dts - Device Tree file for SAMA7G54 Curiosity Board + * + * Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries + * + * Author: Mihai Sain <mihai.sain@microchip.com> + * + */ +/dts-v1/; +#include "sama7g5-pinfunc.h" +#include "sama7g5.dtsi" +#include <dt-bindings/input/input.h> +#include <dt-bindings/leds/common.h> +#include <dt-bindings/mfd/atmel-flexcom.h> +#include <dt-bindings/pinctrl/at91.h> + +/ { + model = "Microchip SAMA7G54 Curiosity"; + compatible = "microchip,sama7g54-curiosity", "microchip,sama7g5", "microchip,sama7"; + + aliases { + serial0 = &uart3; + i2c0 = &i2c10; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + gpio-keys { + compatible = "gpio-keys"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_key_gpio_default>; + + button-user { + label = "user-button"; + gpios = <&pioA PIN_PD19 GPIO_ACTIVE_LOW>; + linux,code = <KEY_PROG1>; + wakeup-source; + }; + }; + + leds { + compatible = "gpio-leds"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_led_gpio_default>; + + led-red { + color = <LED_COLOR_ID_RED>; + function = LED_FUNCTION_POWER; + gpios = <&pioA PIN_PD13 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + + led-green { + color = <LED_COLOR_ID_GREEN>; + function = LED_FUNCTION_BOOT; + gpios = <&pioA PIN_PD14 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; + + led-blue { + color = <LED_COLOR_ID_BLUE>; + function = LED_FUNCTION_CPU; + gpios = <&pioA PIN_PB15 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "heartbeat"; + }; + }; + + memory@60000000 { + device_type = "memory"; + reg = <0x60000000 0x10000000>; /* 256 MiB DDR3L-1066 16-bit */ + }; +}; + +&adc { + vddana-supply = <&vddout25>; + vref-supply = <&vddout25>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_mikrobus1_an_default &pinctrl_mikrobus2_an_default>; + status = "okay"; +}; + +&cpu0 { + cpu-supply = <&vddcpu>; +}; + +&dma0 { + status = "okay"; +}; + +&dma1 { + status = "okay"; +}; + +&dma2 { + status = "okay"; +}; + +&ebi { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_nand_default>; + status = "okay"; + + nand_controller: nand-controller { + status = "okay"; + + nand@3 { + reg = <0x3 0x0 0x800000>; + atmel,rb = <0>; + nand-bus-width = <8>; + nand-ecc-mode = "hw"; + nand-ecc-strength = <8>; + nand-ecc-step-size = <512>; + nand-on-flash-bbt; + label = "nand"; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + at91bootstrap@0 { + label = "nand: at91bootstrap"; + reg = <0x0 0x40000>; + }; + + bootloader@40000 { + label = "nand: u-boot"; + reg = <0x40000 0x100000>; + }; + + bootloaderenv@140000 { + label = "nand: u-boot env"; + reg = <0x140000 0x40000>; + }; + + dtb@180000 { + label = "nand: device tree"; + reg = <0x180000 0x80000>; + }; + + kernel@200000 { + label = "nand: kernel"; + reg = <0x200000 0x600000>; + }; + + rootfs@800000 { + label = "nand: rootfs"; + reg = <0x800000 0x1f800000>; + }; + }; + }; + }; +}; + +&flx3 { + atmel,flexcom-mode = <ATMEL_FLEXCOM_MODE_USART>; + status = "okay"; + + uart3: serial@200 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_flx3_default>; + status = "okay"; + }; +}; + +&flx10 { + atmel,flexcom-mode = <ATMEL_FLEXCOM_MODE_TWI>; + status = "okay"; + + i2c10: i2c@600 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_flx10_default>; + i2c-analog-filter; + i2c-digital-filter; + i2c-digital-filter-width-ns = <35>; + status = "okay"; + + eeprom@51 { + compatible = "atmel,24c02"; + reg = <0x51>; + pagesize = <16>; + size = <256>; + vcc-supply = <&vdd_3v3>; + }; + + pmic@5b { + compatible = "microchip,mcp16502"; + reg = <0x5b>; + + regulators { + vdd_3v3: VDD_IO { + regulator-name = "VDD_IO"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-initial-mode = <2>; + regulator-allowed-modes = <2>, <4>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + regulator-suspend-microvolt = <3300000>; + regulator-mode = <4>; + }; + + regulator-state-mem { + regulator-off-in-suspend; + regulator-mode = <4>; + }; + }; + + vddioddr: VDD_DDR { + regulator-name = "VDD_DDR"; + regulator-min-microvolt = <1350000>; + regulator-max-microvolt = <1350000>; + regulator-initial-mode = <2>; + regulator-allowed-modes = <2>, <4>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1350000>; + regulator-mode = <4>; + }; + + regulator-state-mem { + regulator-on-in-suspend; + regulator-suspend-microvolt = <1350000>; + regulator-mode = <4>; + }; + }; + + vddcore: VDD_CORE { + regulator-name = "VDD_CORE"; + regulator-min-microvolt = <1150000>; + regulator-max-microvolt = <1150000>; + regulator-initial-mode = <2>; + regulator-allowed-modes = <2>, <4>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + regulator-suspend-voltage = <1150000>; + regulator-mode = <4>; + }; + + regulator-state-mem { + regulator-off-in-suspend; + regulator-mode = <4>; + }; + }; + + vddcpu: VDD_OTHER { + regulator-name = "VDD_OTHER"; + regulator-min-microvolt = <1050000>; + regulator-max-microvolt = <1250000>; + regulator-initial-mode = <2>; + regulator-allowed-modes = <2>, <4>; + regulator-ramp-delay = <3125>; + regulator-always-on; + + regulator-state-standby { + regulator-on-in-suspend; + regulator-suspend-voltage = <1050000>; + regulator-mode = <4>; + }; + + regulator-state-mem { + regulator-off-in-suspend; + regulator-mode = <4>; + }; + }; + + vldo1: LDO1 { + regulator-name = "LDO1"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + + regulator-state-standby { + regulator-suspend-voltage = <1800000>; + regulator-on-in-suspend; + }; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + vldo2: LDO2 { + regulator-name = "LDO2"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + + regulator-state-standby { + regulator-suspend-voltage = <3300000>; + regulator-on-in-suspend; + }; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + }; + }; + }; +}; + +&main_xtal { + clock-frequency = <24000000>; +}; + +&qspi1 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_qspi1_default>; + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0x0>; + spi-max-frequency = <100000000>; + spi-tx-bus-width = <4>; + spi-rx-bus-width = <4>; + m25p,fast-read; + }; +}; + +&pioA { + pinctrl_flx3_default: flx3-default { + pinmux = <PIN_PD16__FLEXCOM3_IO0>, + <PIN_PD17__FLEXCOM3_IO1>; + bias-pull-up; + }; + + pinctrl_flx10_default: flx10-default { + pinmux = <PIN_PC30__FLEXCOM10_IO0>, + <PIN_PC31__FLEXCOM10_IO1>; + bias-pull-up; + }; + + pinctrl_key_gpio_default: key-gpio-default { + pinmux = <PIN_PD19__GPIO>; + bias-pull-up; + }; + + pinctrl_led_gpio_default: led-gpio-default { + pinmux = <PIN_PD13__GPIO>, + <PIN_PD14__GPIO>, + <PIN_PB15__GPIO>; + bias-pull-up; + }; + + pinctrl_mikrobus1_an_default: mikrobus1-an-default { + pinmux = <PIN_PC15__GPIO>; + bias-disable; + }; + + pinctrl_mikrobus2_an_default: mikrobus2-an-default { + pinmux = <PIN_PC13__GPIO>; + bias-disable; + }; + + pinctrl_nand_default: nand-default { + pinmux = <PIN_PD9__D0>, + <PIN_PD10__D1>, + <PIN_PD11__D2>, + <PIN_PC21__D3>, + <PIN_PC22__D4>, + <PIN_PC23__D5>, + <PIN_PC24__D6>, + <PIN_PD2__D7>, + <PIN_PD3__NANDRDY>, + <PIN_PD4__NCS3_NANDCS>, + <PIN_PD5__NWE_NWR0_NANDWE>, + <PIN_PD6__NRD_NANDOE>, + <PIN_PD7__A21_NANDALE>, + <PIN_PD8__A22_NANDCLE>; + bias-disable; + slew-rate = <0>; + }; + + pinctrl_qspi1_default: qspi1-default { + pinmux = <PIN_PB22__QSPI1_IO3>, + <PIN_PB23__QSPI1_IO2>, + <PIN_PB24__QSPI1_IO1>, + <PIN_PB25__QSPI1_IO0>, + <PIN_PB26__QSPI1_CS>, + <PIN_PB27__QSPI1_SCK>; + bias-pull-up; + slew-rate = <0>; + }; + + pinctrl_sdmmc0_default: sdmmc0-default { + pinmux = <PIN_PA0__SDMMC0_CK>, + <PIN_PA1__SDMMC0_CMD>, + <PIN_PA2__SDMMC0_RSTN>, + <PIN_PA3__SDMMC0_DAT0>, + <PIN_PA4__SDMMC0_DAT1>, + <PIN_PA5__SDMMC0_DAT2>, + <PIN_PA6__SDMMC0_DAT3>; + bias-pull-up; + slew-rate = <0>; + }; + + pinctrl_sdmmc1_default: sdmmc1-default { + pinmux = <PIN_PB29__SDMMC1_CMD>, + <PIN_PB30__SDMMC1_CK>, + <PIN_PB31__SDMMC1_DAT0>, + <PIN_PC0__SDMMC1_DAT1>, + <PIN_PC1__SDMMC1_DAT2>, + <PIN_PC2__SDMMC1_DAT3>, + <PIN_PC4__SDMMC1_CD>; + bias-pull-up; + slew-rate = <0>; + }; + + pinctrl_vbus_det: usba-vbus-det { + pinmux = <PIN_PC6__GPIO>; + bias-disable; + }; + + pinctrl_vbus_en: usb-vbus-en { + pinmux = <PIN_PE1__GPIO>, + <PIN_PE2__GPIO>; + bias-disable; + }; + + pinctrl_vbus_oc: usb-vbus-oc { + pinmux = <PIN_PD31__GPIO>, + <PIN_PE0__GPIO>; + bias-disable; + }; +}; + +&rtt { + atmel,rtt-rtc-time-reg = <&gpbr 0x0>; +}; + +/* M.2 slot for wireless card */ +&sdmmc0 { + bus-width = <4>; + cd-gpios = <&pioA 31 GPIO_ACTIVE_LOW>; + disable-wp; + sdhci-caps-mask = <0x0 0x00200000>; + vmmc-supply = <&vdd_3v3>; + vqmmc-supply = <&vdd_3v3>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_sdmmc0_default>; + status = "okay"; +}; + +/* micro SD socket */ +&sdmmc1 { + bus-width = <4>; + disable-wp; + sdhci-caps-mask = <0x0 0x00200000>; + vmmc-supply = <&vdd_3v3>; + vqmmc-supply = <&vdd_3v3>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_sdmmc1_default>; + status = "okay"; +}; + +&slow_xtal { + clock-frequency = <32768>; +}; + +&shdwc { + debounce-delay-us = <976>; + status = "okay"; + + input@0 { + reg = <0>; + }; +}; + +&tcb0 { + timer0: timer@0 { + compatible = "atmel,tcb-timer"; + reg = <0>; + }; + + timer1: timer@1 { + compatible = "atmel,tcb-timer"; + reg = <1>; + }; +}; + +&trng { + status = "okay"; +}; + +&usb0 { + atmel,vbus-gpio = <&pioA PIN_PC6 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_vbus_det>; + phys = <&usb_phy0>; + phy-names = "usb"; + status = "okay"; +}; + +&usb2 { + num-ports = <3>; + atmel,vbus-gpio = <0 + &pioA PIN_PE1 GPIO_ACTIVE_HIGH + &pioA PIN_PE2 GPIO_ACTIVE_HIGH>; + atmel,oc-gpio = <0 + &pioA PIN_PD31 GPIO_ACTIVE_HIGH + &pioA PIN_PE0 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_vbus_en &pinctrl_vbus_oc>; + phys = <&usb_phy1>, <&usb_phy2>; + phy-names = "usb", "usb"; + status = "okay"; +}; + +&usb3 { + status = "okay"; +}; + +&usb_phy0 { + status = "okay"; +}; + +&usb_phy1 { + status = "okay"; +}; + +&usb_phy2 { + status = "okay"; +}; + +&vddout25 { + vin-supply = <&vdd_3v3>; + status = "okay"; +}; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/boot/dts/microchip/at91-sama7g5ek.dts linux-microchip/arch/arm/boot/dts/microchip/at91-sama7g5ek.dts --- linux-6.6.51/arch/arm/boot/dts/microchip/at91-sama7g5ek.dts 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/boot/dts/microchip/at91-sama7g5ek.dts 2024-11-26 14:02:35.682461166 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:17 @ #include <dt-bindings/mfd/atmel-flexcom.h> #include <dt-bindings/input/input.h> #include <dt-bindings/pinctrl/at91.h> +#include <dt-bindings/sound/microchip,asrc-card.h> #include <dt-bindings/sound/microchip,pdmc.h> / { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:93 @ }; sound: sound { - compatible = "simple-audio-card"; - simple-audio-card,name = "sama7g5ek audio"; #address-cells = <1>; #size-cells = <0>; - simple-audio-card,dai-link@0 { + assigned-clocks = <&pmc PMC_TYPE_CORE PMC_AUDIOPMCPLL>; + assigned-clock-rates = <196608000>; + compatible = "microchip,asrc-card"; + microchip,model = "mchp-asrc-card @ sama7g5 EK"; + status = "okay"; + + microchip,audio-asrc = <&asrc 0>, <&asrc 1>, <&asrc 2>, <&asrc 3>; + + microchip,dai-link@0 { reg = <0>; + microchip,convert-channels = <2>; + microchip,convert-rate = <48000>; + microchip,convert-sample-format = <MCHP_ASRC_PCM_FORMAT_S24_LE>; cpu { sound-dai = <&spdiftx>; }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:115 @ sound-dai = <&spdif_out>; }; }; - simple-audio-card,dai-link@1 { + + microchip,dai-link@1 { reg = <1>; + microchip,convert-channels = <2>; + microchip,convert-rate = <48000>; + microchip,convert-sample-format = <MCHP_ASRC_PCM_FORMAT_S24_LE>; cpu { sound-dai = <&spdifrx>; }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:141 @ }; }; +&asrc { + status = "okay"; +}; + &adc { vddana-supply = <&vddout25>; vref-supply = <&vddout25>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_mikrobus1_an_default &pinctrl_mikrobus2_an_default>; + atmel,trigger-edge-type = <IRQ_TYPE_EDGE_RISING>; status = "okay"; }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:251 @ status = "okay"; i2c1: i2c@600 { + dmas = <0>, <0>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_i2c1_default>; i2c-analog-filter; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:259 @ i2c-digital-filter-width-ns = <35>; status = "okay"; + pac1934@10 { + compatible = "microchip,pac1934"; + reg = <0x10>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + channel@1 { + reg = <0x1>; + shunt-resistor-micro-ohms = <10000>; + label = "VDD3V3"; + }; + + channel@2 { + reg = <0x2>; + shunt-resistor-micro-ohms = <10000>; + label = "VDDIODDR"; + }; + + channel@3 { + reg = <0x3>; + shunt-resistor-micro-ohms = <10000>; + label = "VDDCORE"; + }; + + channel@4 { + reg = <0x4>; + shunt-resistor-micro-ohms = <10000>; + label = "VDDCPU"; + }; + }; + mcp16502@5b { compatible = "microchip,mcp16502"; reg = <0x5b>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:452 @ status = "okay"; i2c8: i2c@600 { + dmas = <0>, <0>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_i2c8_default>; i2c-analog-filter; i2c-digital-filter; i2c-digital-filter-width-ns = <35>; status = "okay"; + + eeprom0: eeprom0@52 { + compatible = "atmel,24mac02e4"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x52>; + pagesize = <16>; + size = <256>; + status = "okay"; + + eeprom0_eui48: eui48@fa { + reg = <0xfa 0x6>; + }; + }; + + eeprom1: eeprom1@53 { + compatible = "atmel,24mac02e4"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x53>; + pagesize = <16>; + size = <256>; + status = "okay"; + + eeprom1_eui48: eui48@fa { + reg = <0xfa 0x6>; + }; + }; }; }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:495 @ status = "okay"; i2c9: i2c@600 { + dmas = <0>, <0>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_i2c9_default>; i2c-analog-filter; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:527 @ phy-mode = "rgmii-id"; status = "okay"; + nvmem-cells = <&eeprom0_eui48>; + nvmem-cell-names = "mac-address"; + ethernet-phy@7 { reg = <0x7>; interrupt-parent = <&pioA>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:547 @ phy-mode = "rmii"; status = "okay"; /* Conflict with pdmc0. */ + nvmem-cells = <&eeprom1_eui48>; + nvmem-cell-names = "mac-address"; + ethernet-phy@0 { reg = <0x0>; interrupt-parent = <&pioA>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:840 @ pinmux = <PIN_PB1__SPDIF_TX>; bias-disable; }; + + pinctrl_usba_vbus_det: usba_vbus_det { + pinmux = <PIN_PD11__GPIO>; + bias-disable; + }; + + pinctrl_usbb_vbus_det: usbb_vbus_det { + pinmux = <PIN_PC12__GPIO>; + bias-disable; + }; + + pinctrl_usba_vbus_en: usba_vbus_en { + pinmux = <PIN_PB2__GPIO>; + bias-disable; + }; + + pinctrl_usbb_vbus_en: usbb_vbus_en { + pinmux = <PIN_PC11__GPIO>; + bias-disable; + }; + + pinctrl_usbc_vbus_en: usbc_vbus_en { + pinmux = <PIN_PC6__GPIO>; + bias-disable; + }; }; &pwm { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:880 @ &sdmmc0 { bus-width = <8>; non-removable; - sdhci-caps-mask = <0x0 0x00200000>; + mmc-ddr-1_8v; + cap-mmc-highspeed; + cap-mmc-hw-reset; vmmc-supply = <&vdd_3v3>; vqmmc-supply = <&vldo1>; pinctrl-names = "default"; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:892 @ &sdmmc1 { bus-width = <4>; - no-1-8-v; - sdhci-caps-mask = <0x0 0x00200000>; vmmc-supply = <&vdd_3v3>; vqmmc-supply = <&vdd_3v3>; pinctrl-names = "default"; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:902 @ &sdmmc2 { bus-width = <4>; no-1-8-v; - sdhci-caps-mask = <0x0 0x00200000>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_sdmmc2_default>; }; &shdwc { debounce-delay-us = <976>; + microchip,lpm-connection = <&gmac1 &main_xtal>; status = "okay"; input@0 { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:944 @ status = "okay"; }; +&usb0 { + atmel,vbus-gpio = <&pioA PIN_PD11 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usba_vbus_det>; + phys = <&usb_phy0>; + phy-names = "usb"; + status = "okay"; +}; + +&usb1 { + atmel,vbus-gpio = <&pioA PIN_PC12 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usbb_vbus_det>; + phys = <&usb_phy1>; + phy-names = "usb"; + status = "disabled"; +}; + +&usb2 { + num-ports = <3>; + atmel,vbus-gpio = <0 + &pioA PIN_PC11 GPIO_ACTIVE_HIGH + &pioA PIN_PC6 GPIO_ACTIVE_HIGH + >; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usbb_vbus_en &pinctrl_usbc_vbus_en>; + phys = <&usb_phy1>, <&usb_phy2>; + phy-names = "usb", "usb"; + status = "okay"; +}; + +&usb3 { + status = "okay"; +}; + +&usb_phy0 { + status = "okay"; +}; + +&usb_phy1 { + status = "okay"; +}; + +&usb_phy2 { + status = "okay"; +}; + &vddout25 { vin-supply = <&vdd_3v3>; status = "okay"; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/boot/dts/microchip/Makefile linux-microchip/arch/arm/boot/dts/microchip/Makefile --- linux-6.6.51/arch/arm/boot/dts/microchip/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/boot/dts/microchip/Makefile 2024-11-26 14:02:35.682461166 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:7 @ DTC_FLAGS_at91-sam9x60ek := -@ DTC_FLAGS_at91-sama5d27_som1_ek := -@ DTC_FLAGS_at91-sama5d27_wlsom1_ek := -@ +DTC_FLAGS_at91-sama5d29_curiosity := -@ DTC_FLAGS_at91-sama5d2_icp := -@ DTC_FLAGS_at91-sama5d2_ptc_ek := -@ DTC_FLAGS_at91-sama5d2_xplained := -@ DTC_FLAGS_at91-sama5d3_eds := -@ DTC_FLAGS_at91-sama5d3_xplained := -@ DTC_FLAGS_at91-sama5d4_xplained := -@ +DTC_FLAGS_at91-sama7g54_curiosity := -@ DTC_FLAGS_at91-sama7g5ek := -@ +DTC_FLAGS_at91-sam9x75eb := -@ +DTC_FLAGS_at91-sam9x75_curiosity := -@ dtb-$(CONFIG_SOC_AT91RM9200) += \ at91rm9200ek.dtb \ mpa1600.dtb @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:65 @ dtb-$(CONFIG_SOC_SAM9X60) += \ at91-sam9x60_curiosity.dtb \ at91-sam9x60ek.dtb +dtb-$(CONFIG_SOC_SAM9X7) += \ + at91-sam9x75_curiosity.dtb \ + at91-sam9x75eb.dtb dtb-$(CONFIG_SOC_SAM_V7) += \ at91-kizbox2-2.dtb \ at91-kizbox3-hs.dtb \ at91-nattis-2-natte-2.dtb \ at91-sama5d27_som1_ek.dtb \ at91-sama5d27_wlsom1_ek.dtb \ + at91-sama5d29_curiosity.dtb \ at91-sama5d2_icp.dtb \ at91-sama5d2_ptc_ek.dtb \ at91-sama5d2_xplained.dtb \ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:96 @ at91-sama5d4ek.dtb \ at91-vinco.dtb dtb-$(CONFIG_SOC_SAMA7G5) += \ + at91-sama7g54_curiosity.dtb \ at91-sama7g5ek.dtb dtb-$(CONFIG_SOC_LAN966) += \ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/boot/dts/microchip/sam9x60.dtsi linux-microchip/arch/arm/boot/dts/microchip/sam9x60.dtsi --- linux-6.6.51/arch/arm/boot/dts/microchip/sam9x60.dtsi 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/boot/dts/microchip/sam9x60.dtsi 2024-11-26 14:02:35.682461166 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:182 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(8))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(9))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:205 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(8))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(9))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:223 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(8))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(9))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:251 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(10))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(11))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:274 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(10))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(11))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:292 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(10))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(11))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:347 @ status = "disabled"; }; + gpu: gfx2d@f0018000 { + compatible = "microchip,sam9x60-gfx2d"; + reg = <0xf0018000 0x4000>; + interrupts = <36 IRQ_TYPE_LEVEL_HIGH 0>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 36>; + clock-names = "periph_clk"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + i2s: i2s@f001c000 { compatible = "microchip,sam9x60-i2smcc"; reg = <0xf001c000 0x100>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:391 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(22))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(23))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:413 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(22))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(23))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:440 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(24))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(25))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:462 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(24))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(25))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:597 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(12))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(13))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:619 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(12))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(13))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:646 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(14))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(15))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:668 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(14))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(15))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:695 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(16))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(17))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:717 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(16))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(17))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:744 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(0))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(1))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:767 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(0))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(1))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:785 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(0))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(1))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:812 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(2))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(3))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:835 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(2))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(3))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:853 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(2))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(3))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:880 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(4))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(5))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:903 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(4))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(5))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:921 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(4))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(5))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:948 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(6))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(7))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:971 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(6))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(7))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:989 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(6))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(7))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1071 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(18))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(19))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1093 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(18))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(19))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1120 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(20))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(21))>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1142 @ (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(20))>, - <&dma0 + <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(21))>; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/boot/dts/microchip/sam9x7.dtsi linux-microchip/arch/arm/boot/dts/microchip/sam9x7.dtsi --- linux-6.6.51/arch/arm/boot/dts/microchip/sam9x7.dtsi 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/arch/arm/boot/dts/microchip/sam9x7.dtsi 2024-11-26 14:02:35.682461166 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * sam9x7.dtsi - Device Tree Include file for Microchip SAM9X7 SoC family + * + * Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries + * + * Author: Varshini Rajendran <varshini.rajendran@microchip.com> + */ + +#include <dt-bindings/clock/at91.h> +#include <dt-bindings/dma/at91.h> +#include <dt-bindings/gpio/gpio.h> +#include <dt-bindings/interrupt-controller/arm-gic.h> +#include <dt-bindings/interrupt-controller/irq.h> +#include <dt-bindings/mfd/at91-usart.h> +#include <dt-bindings/mfd/atmel-flexcom.h> +#include <dt-bindings/pinctrl/at91.h> + +/ { + #address-cells = <1>; + #size-cells = <1>; + model = "Microchip SAM9X7 SoC"; + compatible = "microchip,sam9x7"; + interrupt-parent = <&aic>; + + aliases { + serial0 = &dbgu; + gpio0 = &pioA; + gpio1 = &pioB; + gpio2 = &pioC; + gpio3 = &pioD; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + compatible = "arm,arm926ej-s"; + device_type = "cpu"; + reg = <0>; + }; + }; + + clocks { + slow_xtal: slow_xtal { + compatible = "fixed-clock"; + #clock-cells = <0>; + }; + + main_xtal: main_xtal { + compatible = "fixed-clock"; + #clock-cells = <0>; + }; + }; + + sram: sram@300000 { + compatible = "mmio-sram"; + reg = <0x300000 0x10000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0x300000 0x10000>; + }; + + ahb { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + usb0: gadget@500000 { + compatible = "microchip,sam9x60-udc"; + reg = <0x500000 0x100000>, + <0xf803c000 0x400>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <23 IRQ_TYPE_LEVEL_HIGH 2>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 23>, <&pmc PMC_TYPE_CORE PMC_UTMI>; + clock-names = "pclk", "hclk"; + assigned-clocks = <&pmc PMC_TYPE_CORE PMC_UTMI>; + assigned-clock-rates = <480000000>; + status = "disabled"; + }; + + ohci0: usb@600000 { + compatible = "atmel,at91rm9200-ohci", "usb-ohci"; + reg = <0x600000 0x100000>; + interrupts = <22 IRQ_TYPE_LEVEL_HIGH 2>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 22>, <&pmc PMC_TYPE_PERIPHERAL 22>, <&pmc PMC_TYPE_SYSTEM 6>; + clock-names = "ohci_clk", "hclk", "uhpck"; + status = "disabled"; + }; + + ehci0: usb@700000 { + compatible = "atmel,at91sam9g45-ehci", "usb-ehci"; + reg = <0x700000 0x100000>; + interrupts = <22 IRQ_TYPE_LEVEL_HIGH 2>; + clocks = <&pmc PMC_TYPE_CORE PMC_UTMI>, <&pmc PMC_TYPE_PERIPHERAL 22>; + clock-names = "usb_clk", "ehci_clk"; + assigned-clocks = <&pmc PMC_TYPE_CORE PMC_UTMI>; + 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>; + clocks = <&pmc PMC_TYPE_CORE PMC_MCK>; + status = "disabled"; + + nand_controller: nand-controller { + compatible = "microchip,sam9x60-nand-controller"; + ecc-engine = <&pmecc>; + #address-cells = <2>; + #size-cells = <1>; + ranges; + status = "disabled"; + }; + }; + + sdmmc0: sdio-host@80000000 { + compatible = "microchip,sam9x60-sdhci"; + reg = <0x80000000 0x300>; + interrupts = <12 IRQ_TYPE_LEVEL_HIGH 0>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 12>, <&pmc PMC_TYPE_GCK 12>; + clock-names = "hclock", "multclk"; + assigned-clocks = <&pmc PMC_TYPE_GCK 12>; + assigned-clock-rates = <100000000>; + status = "disabled"; + }; + + sdmmc1: sdio-host@90000000 { + compatible = "microchip,sam9x60-sdhci"; + reg = <0x90000000 0x300>; + interrupts = <26 IRQ_TYPE_LEVEL_HIGH 0>; + 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>; + status = "disabled"; + }; + }; + + apb { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + flx4: flexcom@f0000000 { + compatible = "atmel,sama5d2-flexcom"; + reg = <0xf0000000 0x200>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 13>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0xf0000000 0x800>; + status = "disabled"; + + uart4: serial@200 { + compatible = "microchip,sam9x60-usart", "atmel,at91sam9260-usart"; + reg = <0x200 0x200>; + interrupts = <13 IRQ_TYPE_LEVEL_HIGH 7>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(8))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(9))>; + dma-names = "tx", "rx"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 13>; + clock-names = "usart"; + atmel,use-dma-rx; + atmel,use-dma-tx; + atmel,fifo-size = <16>; + status = "disabled"; + }; + + spi4: spi@400 { + compatible = "microchip,sam9x60-spi", "atmel,at91rm9200-spi"; + reg = <0x400 0x200>; + interrupts = <13 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 13>; + clock-names = "spi_clk"; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(8))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(9))>; + dma-names = "tx", "rx"; + atmel,fifo-size = <16>; + status = "disabled"; + }; + + i2c4: i2c@600 { + compatible = "microchip,sam9x60-i2c"; + reg = <0x600 0x200>; + interrupts = <13 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 13>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(8))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(9))>; + dma-names = "tx", "rx"; + atmel,fifo-size = <16>; + status = "disabled"; + }; + }; + + flx5: flexcom@f0004000 { + compatible = "atmel,sama5d2-flexcom"; + reg = <0xf0004000 0x200>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 14>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0xf0004000 0x800>; + status = "disabled"; + + uart5: serial@200 { + compatible = "microchip,sam9x60-usart", "atmel,at91sam9260-usart"; + reg = <0x200 0x200>; + interrupts = <14 IRQ_TYPE_LEVEL_HIGH 7>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(10))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(11))>; + dma-names = "tx", "rx"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 14>; + clock-names = "usart"; + atmel,use-dma-rx; + atmel,use-dma-tx; + atmel,fifo-size = <16>; + status = "disabled"; + }; + + spi5: spi@400 { + compatible = "microchip,sam9x60-spi", "atmel,at91rm9200-spi"; + reg = <0x400 0x200>; + interrupts = <14 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 14>; + clock-names = "spi_clk"; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(10))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(11))>; + dma-names = "tx", "rx"; + atmel,fifo-size = <16>; + status = "disabled"; + }; + + i2c5: i2c@600 { + compatible = "microchip,sam9x60-i2c"; + reg = <0x600 0x200>; + interrupts = <14 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 14>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(10))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(11))>; + dma-names = "tx", "rx"; + atmel,fifo-size = <16>; + status = "disabled"; + }; + }; + + dma0: dma-controller@f0008000 { + compatible = "microchip,sam9x60-dma", "atmel,sama5d4-dma"; + reg = <0xf0008000 0x1000>; + interrupts = <20 IRQ_TYPE_LEVEL_HIGH 0>; + #dma-cells = <1>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 20>; + clock-names = "dma_clk"; + status = "disabled"; + }; + + ssc: ssc@f0010000 { + compatible = "atmel,at91sam9g45-ssc"; + reg = <0xf0010000 0x4000>; + interrupts = <28 IRQ_TYPE_LEVEL_HIGH 5>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(38))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(39))>; + dma-names = "tx", "rx"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 28>; + clock-names = "pclk"; + }; + + qspi: spi@f0014000 { + compatible = "microchip,sam9x7-ospi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xf0014000 0x100>, <0x60000000 0x20000000>; + reg-names = "qspi_base", "qspi_mmap"; + interrupts = <35 IRQ_TYPE_LEVEL_HIGH 7>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(26))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(27))>; + dma-names = "tx", "rx"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 35>, <&pmc PMC_TYPE_GCK 35>; + clock-names = "pclk", "gclk"; + assigned-clocks = <&pmc PMC_TYPE_GCK 35>; + assigned-clock-parents = <&pmc PMC_TYPE_CORE PMC_PLLADIV2>; + status = "disabled"; + }; + + gpu: gfx2d@f0018000 { + compatible = "microchip,sam9x60-gfx2d"; + reg = <0xf0018000 0x4000>; + interrupts = <36 IRQ_TYPE_LEVEL_HIGH 0>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 36>; + clock-names = "periph_clk"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2s: i2s@f001c000 { + compatible = "microchip,sam9x60-i2smcc"; + reg = <0xf001c000 0x100>; + interrupts = <34 IRQ_TYPE_LEVEL_HIGH 7>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(36))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(37))>; + dma-names = "tx", "rx"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 34>, <&pmc PMC_TYPE_GCK 34>; + clock-names = "pclk", "gclk"; + status = "disabled"; + }; + + flx11: flexcom@f0020000 { + compatible = "atmel,sama5d2-flexcom"; + reg = <0xf0020000 0x200>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 32>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0xf0020000 0x800>; + status = "disabled"; + + uart11: serial@200 { + compatible = "microchip,sam9x60-usart", "atmel,at91sam9260-usart"; + reg = <0x200 0x200>; + interrupts = <32 IRQ_TYPE_LEVEL_HIGH 7>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(22))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(23))>; + dma-names = "tx", "rx"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 32>; + clock-names = "usart"; + atmel,use-dma-rx; + atmel,use-dma-tx; + atmel,fifo-size = <16>; + status = "disabled"; + }; + + i2c11: i2c@600 { + compatible = "microchip,sam9x60-i2c"; + reg = <0x600 0x200>; + interrupts = <32 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 32>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(22))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(23))>; + dma-names = "tx", "rx"; + atmel,fifo-size = <16>; + status = "disabled"; + }; + }; + + flx12: flexcom@f0024000 { + compatible = "atmel,sama5d2-flexcom"; + reg = <0xf0024000 0x200>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 33>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0xf0024000 0x800>; + status = "disabled"; + + uart12: serial@200 { + compatible = "microchip,sam9x60-usart", "atmel,at91sam9260-usart"; + reg = <0x200 0x200>; + interrupts = <33 IRQ_TYPE_LEVEL_HIGH 7>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(24))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(25))>; + dma-names = "tx", "rx"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 33>; + clock-names = "usart"; + atmel,use-dma-rx; + atmel,use-dma-tx; + atmel,fifo-size = <16>; + status = "disabled"; + }; + + i2c12: i2c@600 { + compatible = "microchip,sam9x60-i2c"; + reg = <0x600 0x200>; + interrupts = <33 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 33>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(24))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(25))>; + dma-names = "tx", "rx"; + atmel,fifo-size = <16>; + status = "disabled"; + }; + }; + + pit64b0: timer@f0028000 { + compatible = "microchip,sam9x60-pit64b"; + reg = <0xf0028000 0x100>; + interrupts = <37 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 37>, <&pmc PMC_TYPE_GCK 37>; + clock-names = "pclk", "gclk"; + }; + + sha: sha@f002c000 { + compatible = "atmel,at91sam9g46-sha"; + reg = <0xf002c000 0x100>; + interrupts = <41 IRQ_TYPE_LEVEL_HIGH 0>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(34))>; + dma-names = "tx"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 41>; + clock-names = "sha_clk"; + }; + + trng: trng@f0030000 { + compatible = "microchip,sam9x60-trng"; + reg = <0xf0030000 0x100>; + interrupts = <38 IRQ_TYPE_LEVEL_HIGH 0>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 38>; + status = "disabled"; + }; + + aes: aes@f0034000 { + compatible = "atmel,at91sam9g46-aes"; + reg = <0xf0034000 0x100>; + interrupts = <39 IRQ_TYPE_LEVEL_HIGH 0>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(32))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(33))>; + dma-names = "tx", "rx"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 39>; + clock-names = "aes_clk"; + }; + + tdes: tdes@f0038000 { + compatible = "atmel,at91sam9g46-tdes"; + reg = <0xf0038000 0x100>; + interrupts = <40 IRQ_TYPE_LEVEL_HIGH 0>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(31))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(30))>; + dma-names = "tx", "rx"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 40>; + clock-names = "tdes_clk"; + }; + + classd: classd@f003c000 { + compatible = "atmel,sama5d2-classd"; + reg = <0xf003c000 0x100>; + interrupts = <42 IRQ_TYPE_LEVEL_HIGH 7>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(35))>; + dma-names = "tx"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 42>, <&pmc PMC_TYPE_GCK 42>; + clock-names = "pclk", "gclk"; + status = "disabled"; + }; + + pit64b1: timer@f0040000 { + compatible = "microchip,sam9x60-pit64b"; + reg = <0xf0040000 0x100>; + interrupts = <58 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 58>, <&pmc PMC_TYPE_GCK 58>; + clock-names = "pclk", "gclk"; + }; + + can0: can@f8000000 { + compatible = "bosch,m_can"; + reg = <0xf8000000 0x100>, <0x300000 0x7800>; + reg-names = "m_can", "message_ram"; + interrupts = <29 IRQ_TYPE_LEVEL_HIGH 0 + 68 IRQ_TYPE_LEVEL_HIGH 0>; + interrupt-names = "int0", "int1"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 29>, <&pmc PMC_TYPE_GCK 29>; + clock-names = "hclk", "cclk"; + assigned-clocks = <&pmc PMC_TYPE_CORE PMC_UTMI>, <&pmc PMC_TYPE_GCK 29>; + assigned-clock-rates = <480000000>, <40000000>; + assigned-clock-parents = <&pmc PMC_TYPE_CORE PMC_UTMI>, <&pmc PMC_TYPE_CORE PMC_UTMI>; + bosch,mram-cfg = <0x3400 0 0 64 0 0 32 32>; + status = "disabled"; + }; + + can1: can@f8004000 { + compatible = "bosch,m_can"; + reg = <0xf8004000 0x100>, <0x300000 0xbc00>; + reg-names = "m_can", "message_ram"; + interrupts = <30 IRQ_TYPE_LEVEL_HIGH 0 + 69 IRQ_TYPE_LEVEL_HIGH 0>; + interrupt-names = "int0", "int1"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 30>, <&pmc PMC_TYPE_GCK 30>; + clock-names = "hclk", "cclk"; + assigned-clocks = <&pmc PMC_TYPE_CORE PMC_UTMI>, <&pmc PMC_TYPE_GCK 30>; + assigned-clock-rates = <480000000>, <40000000>; + assigned-clock-parents = <&pmc PMC_TYPE_CORE PMC_UTMI>, <&pmc PMC_TYPE_CORE PMC_UTMI>; + bosch,mram-cfg = <0x7800 0 0 64 0 0 32 32>; + status = "disabled"; + }; + + tcb: timer@f8008000 { + compatible = "atmel,sama5d2-tcb", "simple-mfd", "syscon"; + reg = <0xf8008000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <17 IRQ_TYPE_LEVEL_HIGH 0>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 17>, <&pmc PMC_TYPE_GCK 17>, <&clk32k 0>; + clock-names = "t0_clk", "gclk", "slow_clk"; + status = "disabled"; + }; + + flx6: flexcom@f8010000 { + compatible = "atmel,sama5d2-flexcom"; + reg = <0xf8010000 0x200>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 9>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0xf8010000 0x800>; + status = "disabled"; + + uart6: serial@200 { + compatible = "microchip,sam9x60-usart", "atmel,at91sam9260-usart"; + reg = <0x200 0x200>; + interrupts = <9 IRQ_TYPE_LEVEL_HIGH 7>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(12))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(13))>; + dma-names = "tx", "rx"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 9>; + clock-names = "usart"; + atmel,use-dma-rx; + atmel,use-dma-tx; + atmel,fifo-size = <16>; + status = "disabled"; + }; + + i2c6: i2c@600 { + compatible = "microchip,sam9x60-i2c"; + reg = <0x600 0x200>; + interrupts = <9 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 9>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(12))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(13))>; + dma-names = "tx", "rx"; + atmel,fifo-size = <16>; + status = "disabled"; + }; + }; + + flx7: flexcom@f8014000 { + compatible = "atmel,sama5d2-flexcom"; + reg = <0xf8014000 0x200>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 10>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0xf8014000 0x800>; + status = "disabled"; + + uart7: serial@200 { + compatible = "microchip,sam9x60-usart", "atmel,at91sam9260-usart"; + reg = <0x200 0x200>; + interrupts = <10 IRQ_TYPE_LEVEL_HIGH 7>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(14))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(15))>; + dma-names = "tx", "rx"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 10>; + clock-names = "usart"; + atmel,use-dma-rx; + atmel,use-dma-tx; + atmel,fifo-size = <16>; + status = "disabled"; + }; + + i2c7: i2c@600 { + compatible = "microchip,sam9x60-i2c"; + reg = <0x600 0x200>; + interrupts = <10 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 10>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(14))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(15))>; + dma-names = "tx", "rx"; + atmel,fifo-size = <16>; + status = "disabled"; + }; + }; + + flx8: flexcom@f8018000 { + compatible = "atmel,sama5d2-flexcom"; + reg = <0xf8018000 0x200>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 11>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0xf8018000 0x800>; + status = "disabled"; + + uart8: serial@200 { + compatible = "microchip,sam9x60-usart", "atmel,at91sam9260-usart"; + reg = <0x200 0x200>; + interrupts = <11 IRQ_TYPE_LEVEL_HIGH 7>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(16))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(17))>; + dma-names = "tx", "rx"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 11>; + clock-names = "usart"; + atmel,use-dma-rx; + atmel,use-dma-tx; + atmel,fifo-size = <16>; + status = "disabled"; + }; + + i2c8: i2c@600 { + compatible = "microchip,sam9x60-i2c"; + reg = <0x600 0x200>; + interrupts = <11 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 11>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(16))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(17))>; + dma-names = "tx", "rx"; + atmel,fifo-size = <16>; + status = "disabled"; + }; + }; + + flx0: flexcom@f801c000 { + compatible = "atmel,sama5d2-flexcom"; + reg = <0xf801c000 0x200>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 5>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0xf801c000 0x800>; + status = "disabled"; + + uart0: serial@200 { + compatible = "microchip,sam9x60-usart", "atmel,at91sam9260-usart"; + reg = <0x200 0x200>; + interrupts = <5 IRQ_TYPE_LEVEL_HIGH 7>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(0))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(1))>; + dma-names = "tx", "rx"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 5>; + clock-names = "usart"; + atmel,use-dma-rx; + atmel,use-dma-tx; + atmel,fifo-size = <16>; + status = "disabled"; + }; + + spi0: spi@400 { + compatible = "microchip,sam9x60-spi", "atmel,at91rm9200-spi"; + reg = <0x400 0x200>; + interrupts = <5 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 5>; + clock-names = "spi_clk"; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(0))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(1))>; + dma-names = "tx", "rx"; + atmel,fifo-size = <16>; + status = "disabled"; + }; + + i2c0: i2c@600 { + compatible = "microchip,sam9x60-i2c"; + reg = <0x600 0x200>; + interrupts = <5 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 5>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(0))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(1))>; + dma-names = "tx", "rx"; + atmel,fifo-size = <16>; + status = "disabled"; + }; + }; + + flx1: flexcom@f8020000 { + compatible = "atmel,sama5d2-flexcom"; + reg = <0xf8020000 0x200>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 6>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0xf8020000 0x800>; + status = "disabled"; + + uart1: serial@200 { + compatible = "microchip,sam9x60-usart", "atmel,at91sam9260-usart"; + reg = <0x200 0x200>; + interrupts = <6 IRQ_TYPE_LEVEL_HIGH 7>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(2))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(3))>; + dma-names = "tx", "rx"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 6>; + clock-names = "usart"; + atmel,use-dma-rx; + atmel,use-dma-tx; + atmel,fifo-size = <16>; + status = "disabled"; + }; + + spi1: spi@400 { + compatible = "microchip,sam9x60-spi", "atmel,at91rm9200-spi"; + reg = <0x400 0x200>; + interrupts = <6 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 6>; + clock-names = "spi_clk"; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(2))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(3))>; + dma-names = "tx", "rx"; + atmel,fifo-size = <16>; + status = "disabled"; + }; + + i2c1: i2c@600 { + compatible = "microchip,sam9x60-i2c"; + reg = <0x600 0x200>; + interrupts = <6 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 6>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(2))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(3))>; + dma-names = "tx", "rx"; + atmel,fifo-size = <16>; + status = "disabled"; + }; + }; + + flx2: flexcom@f8024000 { + compatible = "atmel,sama5d2-flexcom"; + reg = <0xf8024000 0x200>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 7>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0xf8024000 0x800>; + status = "disabled"; + + uart2: serial@200 { + compatible = "microchip,sam9x60-usart", "atmel,at91sam9260-usart"; + reg = <0x200 0x200>; + interrupts = <7 IRQ_TYPE_LEVEL_HIGH 7>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(4))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(5))>; + dma-names = "tx", "rx"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 7>; + clock-names = "usart"; + atmel,use-dma-rx; + atmel,use-dma-tx; + atmel,fifo-size = <16>; + status = "disabled"; + }; + + spi2: spi@400 { + compatible = "microchip,sam9x60-spi", "atmel,at91rm9200-spi"; + reg = <0x400 0x200>; + interrupts = <7 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 7>; + clock-names = "spi_clk"; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(4))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(5))>; + dma-names = "tx", "rx"; + atmel,fifo-size = <16>; + status = "disabled"; + }; + + i2c2: i2c@600 { + compatible = "microchip,sam9x60-i2c"; + reg = <0x600 0x200>; + interrupts = <7 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 7>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(4))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(5))>; + dma-names = "tx", "rx"; + atmel,fifo-size = <16>; + status = "disabled"; + }; + }; + + flx3: flexcom@f8028000 { + compatible = "atmel,sama5d2-flexcom"; + reg = <0xf8028000 0x200>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 8>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0xf8028000 0x800>; + status = "disabled"; + + uart3: serial@200 { + compatible = "microchip,sam9x60-usart", "atmel,at91sam9260-usart"; + reg = <0x200 0x200>; + interrupts = <8 IRQ_TYPE_LEVEL_HIGH 7>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(6))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(7))>; + dma-names = "tx", "rx"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 8>; + clock-names = "usart"; + atmel,use-dma-rx; + atmel,use-dma-tx; + atmel,fifo-size = <16>; + status = "disabled"; + }; + + spi3: spi@400 { + compatible = "microchip,sam9x60-spi", "atmel,at91rm9200-spi"; + reg = <0x400 0x200>; + interrupts = <8 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 8>; + clock-names = "spi_clk"; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(6))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(7))>; + dma-names = "tx", "rx"; + atmel,fifo-size = <16>; + status = "disabled"; + }; + + i2c3: i2c@600 { + compatible = "microchip,sam9x60-i2c"; + reg = <0x600 0x200>; + interrupts = <8 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 8>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(6))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(7))>; + dma-names = "tx", "rx"; + atmel,fifo-size = <16>; + status = "disabled"; + }; + }; + + gmac: ethernet@f802c000 { + compatible = "microchip,sam9x7-gem", "microchip,sama7g5-gem"; + reg = <0xf802c000 0x1000>; + interrupts = <24 IRQ_TYPE_LEVEL_HIGH 3 /* Queue 0 */ + 60 IRQ_TYPE_LEVEL_HIGH 3 /* Queue 1 */ + 61 IRQ_TYPE_LEVEL_HIGH 3 /* Queue 2 */ + 62 IRQ_TYPE_LEVEL_HIGH 3 /* Queue 3 */ + 63 IRQ_TYPE_LEVEL_HIGH 3 /* Queue 4 */ + 64 IRQ_TYPE_LEVEL_HIGH 3>; /* Queue 5 */ + clocks = <&pmc PMC_TYPE_PERIPHERAL 24>, <&pmc PMC_TYPE_PERIPHERAL 24>, <&pmc PMC_TYPE_GCK 24>, <&pmc PMC_TYPE_GCK 67>; + clock-names = "hclk", "pclk", "tx_clk", "tsu_clk"; + assigned-clocks = <&pmc PMC_TYPE_GCK 67>; + assigned-clock-rates = <266666666>; + status = "disabled"; + }; + + pwm0: pwm@f8034000 { + compatible = "microchip,sam9x60-pwm"; + reg = <0xf8034000 0x300>; + interrupts = <18 IRQ_TYPE_LEVEL_HIGH 4>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 18>; + #pwm-cells = <3>; + status= "disabled"; + }; + + /* + * LCDC Clock related properties will be defined in the DT overlay file + * considering LCDC Generic Clock is disabled with LVDS Display. + */ + hlcdc: hlcdc@f8038000 { + compatible = "microchip,sam9x75-xlcdc"; + reg = <0xf8038000 0x4000>; + interrupts = <25 IRQ_TYPE_LEVEL_HIGH 0>; + status = "disabled"; + + hlcdc-display-controller { + compatible = "atmel,hlcdc-display-controller"; + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + }; + + hlcdc_pwm: hlcdc-pwm { + compatible = "atmel,hlcdc-pwm"; + #pwm-cells = <3>; + }; + }; + + flx9: flexcom@f8040000 { + compatible = "atmel,sama5d2-flexcom"; + reg = <0xf8040000 0x200>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 15>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0xf8040000 0x800>; + status = "disabled"; + + uart9: serial@200 { + compatible = "microchip,sam9x60-usart", "atmel,at91sam9260-usart"; + reg = <0x200 0x200>; + interrupts = <15 IRQ_TYPE_LEVEL_HIGH 7>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(18))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(19))>; + dma-names = "tx", "rx"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 15>; + clock-names = "usart"; + atmel,use-dma-rx; + atmel,use-dma-tx; + atmel,fifo-size = <16>; + status = "disabled"; + }; + + i2c9: i2c@600 { + compatible = "microchip,sam9x60-i2c"; + reg = <0x600 0x200>; + interrupts = <15 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 15>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(18))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(19))>; + dma-names = "tx", "rx"; + atmel,fifo-size = <16>; + status = "disabled"; + }; + }; + + flx10: flexcom@f8044000 { + compatible = "atmel,sama5d2-flexcom"; + reg = <0xf8044000 0x200>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 16>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0xf8044000 0x800>; + status = "disabled"; + + uart10: serial@200 { + compatible = "microchip,sam9x60-usart", "atmel,at91sam9260-usart"; + reg = <0x200 0x200>; + interrupts = <16 IRQ_TYPE_LEVEL_HIGH 7>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(20))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(21))>; + dma-names = "tx", "rx"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 16>; + clock-names = "usart"; + atmel,use-dma-rx; + atmel,use-dma-tx; + atmel,fifo-size = <16>; + status = "disabled"; + }; + + i2c10: i2c@600 { + compatible = "microchip,sam9x60-i2c"; + reg = <0x600 0x200>; + interrupts = <16 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 16>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(20))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | + AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(21))>; + dma-names = "tx", "rx"; + atmel,fifo-size = <16>; + status = "disabled"; + }; + }; + + xisc: xisc@f8048000 { + compatible = "microchip,sama7g5-isc"; + reg = <0xf8048000 0x2000>; + interrupts = <43 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 43>; + clock-names = "hclock"; + #clock-cells = <0>; + clock-output-names = "isc-mck"; + status = "disabled"; + + port { + xisc_in: endpoint { + bus-width = <14>; + hsync-active = <1>; + vsync-active = <1>; + remote-endpoint = <&csi2dc_out>; + }; + }; + }; + + adc: adc@f804c000 { + compatible = "microchip,sam9x7-adc", "atmel,sama5d2-adc"; + reg = <0xf804c000 0x100>; + interrupts = <19 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 19>; + clock-names = "adc_clk"; + dmas = <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(40))>; + dma-names = "rx"; + atmel,min-sample-rate-hz = <200000>; + atmel,max-sample-rate-hz = <20000000>; + atmel,startup-time-ms = <4>; + atmel,trigger-edge-type = <IRQ_TYPE_EDGE_RISING>; + #io-channel-cells = <1>; + status = "disabled"; + }; + + sfr: sfr@f8050000 { + compatible = "microchip,sam9x60-sfr", "syscon"; + reg = <0xf8050000 0x400>; + }; + + dsi: dsi@f8054000 { + compatible = "microchip,sam9x75-mipi-dsi"; + reg = <0xf8054000 0x200>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 54>, <&pmc PMC_TYPE_GCK 55>; + clock-names = "pclk", "refclk"; + microchip,sfr = <&sfr>; + status = "disabled"; + }; + + csi2host: csi2host@f8058000 { + compatible = "snps,dw-csi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xf8058000 0x7FF>; + interrupts = <53 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 53>, <&pmc PMC_TYPE_GCK 55>; + clock-names = "perclk", "phyclk"; + assigned-clocks = <&pmc PMC_TYPE_GCK 55>; + assigned-clock-rates = <26600000>; + snps,output-type = <1>; + phys = <&csi_dphy>; + status = "disabled"; + + port@1 { + reg = <1>; + csi2host_in: endpoint { + }; + }; + + port@2 { + reg = <2>; + csi2host_out: endpoint { + }; + }; + }; + + csi_dphy: dphy@f8058040 { + compatible = "snps,dw-dphy-rx"; + #phy-cells = <0>; + bus-width = <8>; + snps,dphy-frequency = <900000>; + snps,phy_type = <0>; + reg = <0xf8058040 0x20>; + status = "disabled"; + }; + + csi2dc: csi2dc@f805c000 { + compatible = "microchip,sama7g5-csi2dc"; + reg = <0xf805c000 0x500>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 52>, <&xisc>; + clock-names = "pclk", "scck"; + assigned-clocks = <&xisc>; + assigned-clock-rates = <266000000>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + csi2dc_in: endpoint { + }; + }; + + port@1 { + reg = <1>; + csi2dc_out: endpoint { + bus-width = <14>; + hsync-active = <1>; + vsync-active = <1>; + remote-endpoint = <&xisc_in>; + }; + }; + }; + }; + + lvds_controller: lvds-controller@f8060000 { + compatible = "microchip,sam9x7-lvds"; + reg = <0xf8060000 0x100>; + interrupts = <56 IRQ_TYPE_LEVEL_HIGH 0>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 56>; + clock-names = "pclk"; + status = "disabled"; + }; + + matrix: matrix@ffffde00 { + compatible = "microchip,sam9x60-matrix", "atmel,at91sam9x5-matrix", "syscon"; + reg = <0xffffde00 0x200>; + }; + + pmecc: ecc-engine@ffffe000 { + compatible = "microchip,sam9x7-pmecc"; + reg = <0xffffe000 0x300>, + <0xffffe600 0x100>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 48>; + }; + + mpddrc: mpddrc@ffffe800 { + compatible = "microchip,sam9x60-ddramc", "atmel,sama5d3-ddramc"; + reg = <0xffffe800 0x200>; + clocks = <&pmc PMC_TYPE_SYSTEM 2>, <&pmc PMC_TYPE_CORE PMC_MCK>; + clock-names = "ddrck", "mpddr"; + }; + + smc: smc@ffffea00 { + compatible = "microchip,sam9x60-smc", "atmel,at91sam9260-smc", "syscon"; + reg = <0xffffea00 0x100>; + }; + + aic: interrupt-controller@fffff100 { + compatible = "microchip,sam9x7-aic"; + reg = <0xfffff100 0x100>; + #interrupt-cells = <3>; + interrupt-controller; + atmel,external-irqs = <31>; + }; + + dbgu: serial@fffff200 { + compatible = "microchip,sam9x60-dbgu", "microchip,sam9x60-usart", "atmel,at91sam9260-dbgu", "atmel,at91sam9260-usart"; + reg = <0xfffff200 0x200>; + atmel,usart-mode = <AT91_USART_MODE_SERIAL>; + interrupts = <47 IRQ_TYPE_LEVEL_HIGH 7>; + dmas = <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(28))>, + <&dma0 + (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | + AT91_XDMAC_DT_PERID(29))>; + dma-names = "tx", "rx"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 47>; + clock-names = "usart"; + status = "disabled"; + }; + + pinctrl: pinctrl@fffff400 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "microchip,sam9x60-pinctrl", "atmel,at91sam9x5-pinctrl", "simple-mfd"; + ranges = <0xfffff400 0xfffff400 0x800>; + + /* mux-mask corresponding to sam9x7 SoC in TFBGA228L package */ + atmel,mux-mask = < + /* A B C D */ + 0xffffffff 0xffffefc0 0xc0ffd000 0x00000000 /* pioA */ + 0x07ffffff 0x0805fe7f 0x01ff9f81 0x06078000 /* pioB */ + 0xffffffff 0x07dfffff 0xfa3fffff 0x00000000 /* pioC */ + 0x00003fff 0x00003fe0 0x0000003f 0x00000000 /* pioD */ + >; + + pioA: gpio@fffff400 { + compatible = "microchip,sam9x60-gpio", "atmel,at91sam9x5-gpio", "atmel,at91rm9200-gpio"; + reg = <0xfffff400 0x200>; + interrupts = <2 IRQ_TYPE_LEVEL_HIGH 1>; + #gpio-cells = <2>; + gpio-controller; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 2>; + }; + + pioB: gpio@fffff600 { + compatible = "microchip,sam9x60-gpio", "atmel,at91sam9x5-gpio", "atmel,at91rm9200-gpio"; + reg = <0xfffff600 0x200>; + interrupts = <3 IRQ_TYPE_LEVEL_HIGH 1>; + #gpio-cells = <2>; + gpio-controller; + #gpio-lines = <26>; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 3>; + }; + + pioC: gpio@fffff800 { + compatible = "microchip,sam9x60-gpio", "atmel,at91sam9x5-gpio", "atmel,at91rm9200-gpio"; + reg = <0xfffff800 0x200>; + interrupts = <4 IRQ_TYPE_LEVEL_HIGH 1>; + #gpio-cells = <2>; + gpio-controller; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 4>; + }; + + pioD: gpio@fffffa00 { + compatible = "microchip,sam9x60-gpio", "atmel,at91sam9x5-gpio", "atmel,at91rm9200-gpio"; + reg = <0xfffffa00 0x200>; + interrupts = <44 IRQ_TYPE_LEVEL_HIGH 1>; + #gpio-cells = <2>; + gpio-controller; + #gpio-lines = <22>; + interrupt-controller; + #interrupt-cells = <2>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 44>; + }; + }; + + pmc: pmc@fffffc00 { + compatible = "microchip,sam9x7-pmc", "syscon"; + reg = <0xfffffc00 0x200>; + interrupts = <1 IRQ_TYPE_LEVEL_HIGH 7>; + #clock-cells = <2>; + clocks = <&clk32k 1>, <&clk32k 0>, <&main_xtal>; + clock-names = "td_slck", "md_slck", "main_xtal"; + }; + + reset_controller: rstc@fffffe00 { + compatible = "microchip,sam9x60-rstc"; + reg = <0xfffffe00 0x10>; + clocks = <&clk32k 0>; + }; + + shutdown_controller: shdwc@fffffe10 { + compatible = "microchip,sam9x60-shdwc"; + reg = <0xfffffe10 0x10>; + clocks = <&clk32k 0>; + #address-cells = <1>; + #size-cells = <0>; + atmel,wakeup-rtc-timer; + atmel,wakeup-rtt-timer; + status = "disabled"; + }; + + rtt: rtc@fffffe20 { + compatible = "microchip,sam9x60-rtt", "atmel,at91sam9260-rtt"; + reg = <0xfffffe20 0x20>; + interrupts = <1 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&clk32k 0>; + }; + + clk32k: sckc@fffffe50 { + compatible = "microchip,sam9x60-sckc"; + reg = <0xfffffe50 0x4>; + clocks = <&slow_xtal>; + #clock-cells = <1>; + }; + + gpbr: syscon@fffffe60 { + compatible = "microchip,sam9x60-gpbr", "atmel,at91sam9260-gpbr", "syscon"; + reg = <0xfffffe60 0x10>; + }; + + rtc: rtc@fffffea8 { + compatible = "microchip,sam9x60-rtc"; + reg = <0xfffffea8 0x100>; + interrupts = <1 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&clk32k 0>; + }; + + watchdog: watchdog@ffffff80 { + compatible = "microchip,sam9x60-wdt"; + reg = <0xffffff80 0x24>; + interrupts = <1 IRQ_TYPE_LEVEL_HIGH 7>; + status = "disabled"; + }; + }; +}; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/boot/dts/microchip/sama5d2.dtsi linux-microchip/arch/arm/boot/dts/microchip/sama5d2.dtsi --- linux-6.6.51/arch/arm/boot/dts/microchip/sama5d2.dtsi 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/boot/dts/microchip/sama5d2.dtsi 2024-11-26 14:02:35.686461237 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:44 @ interrupts = <2 IRQ_TYPE_LEVEL_HIGH 0>; }; - etb@740000 { + etb: etb@740000 { compatible = "arm,coresight-etb10", "arm,primecell"; reg = <0x740000 0x1000>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:60 @ }; }; - etm@73c000 { + etm: etm@73c000 { compatible = "arm,coresight-etm3x", "arm,primecell"; reg = <0x73c000 0x1000>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1022 @ }; }; - trng@fc01c000 { + trng: rng@fc01c000 { compatible = "atmel,at91sam9g45-trng"; reg = <0xfc01c000 0x100>; interrupts = <47 IRQ_TYPE_LEVEL_HIGH 0>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1160 @ reg = <0xfc05c000 0x20>; }; + ptc@fc060000 { + compatible = "atmel,sama5d2-ptc"; + reg = <0x00800000 0x10000 + 0xfc060000 0xcf>; + interrupts = <58 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 58>, <&pmc PMC_TYPE_CORE PMC_MAIN>, <&clk32k>; + clock-names = "ptc_clk", "ptc_int_osc", "slow_clk"; + status = "disabled"; + }; + chipid@fc069000 { compatible = "atmel,sama5d2-chipid"; reg = <0xfc069000 0x8>; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/boot/dts/microchip/sama5d2-optee.dtsi linux-microchip/arch/arm/boot/dts/microchip/sama5d2-optee.dtsi --- linux-6.6.51/arch/arm/boot/dts/microchip/sama5d2-optee.dtsi 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/arch/arm/boot/dts/microchip/sama5d2-optee.dtsi 2024-11-26 14:02:35.682461166 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ + +/ { + cpus { + cpu@0 { + enable-method = "psci"; + cpu-idle-states = <&psci_standby>; + }; + + idle-states { + entry-method = "psci"; + + psci_standby: psci-standby { + compatible = "arm,idle-state"; + idle-state-name = "psci,standby"; + arm,psci-suspend-param = <0x0>; + local-timer-stop; + entry-latency-us = <1000>; + exit-latency-us = <700>; + min-residency-us = <2000>; + }; + }; + }; + + clocks { + /* Add dummy fixed-clock for TCB (see below) */ + clk83m: clk83m { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <83000000>; + }; + clk32kfixed: clk32kfixed { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; + + reserved-memory { + ranges; + #address-cells = <1>; + #size-cells = <1>; + + optee_core@20000000 { + no-map; + reg = <0x20000000 0x00800000>; + }; + optee_shm@21000000 { + no-map; + reg = <0x21000000 0x00400000>; + }; + scmi0_shmem: scmi0_shmem@21400000 { + compatible = "arm,scmi-shmem"; + no-map; + reg = <0x21400000 0x80>; + }; + }; + + psci { + sys_reset = <0x84000009>; + sys_poweroff = <0x84000008>; + cpu_on = <0x84000003>; + cpu_off = <0x84000002>; + cpu_suspend = <0x84000001>; + method = "smc"; + compatible = "arm,psci-1.0", "arm,psci-0.2", "arm,psci"; + }; + + firmware { + optee { + method = "smc"; + compatible = "linaro,optee-tz"; + }; + scmi0 { + compatible = "arm,scmi-smc"; + shmem = <&scmi0_shmem>; + arm,smc-id = <0x2000200>; + #address-cells = <0x01>; + #size-cells = <0x00>; + + scmi0_clock: scmi0_clock@14 { + #clock-cells = <0x01>; + reg = <0x14>; + }; + }; + }; + + arm_smc_wdt { + compatible = "arm,smc-wdt"; + arm,smc-id = <0x2000500>; + timeout-sec = <15>; + }; +}; + +/* Disable clocks controllers as they are handled by OP-TEE */ +&clk32k { + status = "disabled"; +}; + +&pmc { + status = "disabled"; +}; + +&trng { + status = "disabled"; +}; + +&tcb1 { + status = "disabled"; +}; + +&shutdown_controller { + status = "disabled"; +}; + +&reset_controller { + status = "disabled"; +}; + +&securam { + status = "disabled"; +}; + +&sfrbu { + status = "disabled"; +}; + +&pioBU { + status = "disabled"; +}; + +&pit { + status = "disabled"; +}; + +&rtc { + status = "disabled"; +}; + +&watchdog { + status = "disabled"; +}; + +/* Use "syscon-smc" compatible to forward register access to OP-TEE */ +&sfr { + compatible = "atmel,sama5d2-sfr", "syscon-smc"; + arm,smc-id = <0x02000300>; +}; + +/* + * Use fixed-clock for TCB since SCMI clocks are probed too late for + * clocksource + */ +&tcb0 { + clocks = <&clk83m>, <&clk32kfixed>, <&clk32kfixed>; +}; + +/* Replace all clocks with SCMI clocks identifier */ +&can0 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_CAN0_CLK &scmi0_clock AT91_SCMI_CLK_GCK_CAN0_GCLK>; + /delete-property/assigned-clocks; + /delete-property/assigned-clock-parents; + /delete-property/assigned-clock-rates; +}; + +&can1 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_CAN1_CLK &scmi0_clock AT91_SCMI_CLK_GCK_CAN1_GCLK>; + /delete-property/assigned-clocks; + /delete-property/assigned-clock-parents; + /delete-property/assigned-clock-rates; +}; + +&classd { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_CLASSD_CLK &scmi0_clock AT91_SCMI_CLK_GCK_CLASSD_GCLK>; +}; + +&tdes { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_TDES_CLK>; +}; + +&pioA { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_PIOA_CLK>; +}; + +&adc { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_ADC_CLK>; +}; + +&i2c0 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_TWI0_CLK>; +}; + +&i2c1 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_TWI1_CLK>; +}; + +&i2c2 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX4_CLK>; +}; + +&i2c3 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX1_CLK>; +}; + +&i2c4 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX2_CLK>; +}; + +&i2c5 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX3_CLK>; +}; + +&i2c6 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX4_CLK>; +}; + +&flx0 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX0_CLK>; +}; + +&flx1 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX1_CLK>; +}; + +&flx2 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX2_CLK>; +}; + +&flx3 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX3_CLK>; +}; + +&flx4 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX4_CLK>; +}; + +&uart0 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_UART0_CLK>; +}; + +&uart1 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_UART1_CLK>; +}; + +&uart2 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_UART2_CLK>; +}; + +&uart3 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_UART3_CLK>; +}; + +&uart4 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_UART4_CLK>; +}; + +&uart5 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX0_CLK>; +}; + +&uart6 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX1_CLK>; +}; + +&uart7 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX2_CLK>; +}; + +&uart8 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX3_CLK>; +}; + +&uart9 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX4_CLK>; +}; + +&hsmc { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_HSMC_CLK>; +}; + +&macb0 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_MACB0_CLK &scmi0_clock AT91_SCMI_CLK_PERIPH_MACB0_CLK>; +}; + +&spi0 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_SPI0_CLK>; +}; + +&spi1 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_SPI1_CLK>; +}; + +&spi2 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX0_CLK>; +}; + +&spi3 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX1_CLK>; +}; + +&spi4 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX2_CLK>; +}; + +&spi5 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX3_CLK>; +}; + +&spi6 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX4_CLK>; +}; + +&qspi0 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_QSPI0_CLK>; +}; + +&qspi1 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_QSPI1_CLK>; +}; + +&aes { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_AES_CLK>; +}; + +&sha { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_SHA_CLK>; +}; + +&pwm0 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_PWM_CLK>; +}; + +&pdmic { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_PDMIC_CLK &scmi0_clock AT91_SCMI_CLK_GCK_PDMIC_GCLK>; +}; + +&ssc0 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_SSC0_CLK>; +}; + +&isc { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_ISC_CLK &scmi0_clock AT91_SCMI_CLK_SYSTEM_ISCCK &scmi0_clock AT91_SCMI_CLK_GCK_ISC_GCLK>; +}; + +&hlcdc { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_LCDC_CLK &scmi0_clock AT91_SCMI_CLK_SYSTEM_LCDCK &scmi0_clock AT91_SCMI_CLK_SCKC_SLOWCK_32K>; +}; + +&dma0 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_DMA0_CLK>; +}; + +&dma1 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_DMA1_CLK>; +}; + +&ramc0 { + clocks = <&scmi0_clock AT91_SCMI_CLK_SYSTEM_DDRCK &scmi0_clock AT91_SCMI_CLK_PERIPH_MPDDR_CLK>; +}; + +&sdmmc0 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_SDMMC0_HCLK &scmi0_clock AT91_SCMI_CLK_GCK_SDMMC0_GCLK &scmi0_clock AT91_SCMI_CLK_CORE_MAIN>; + /delete-property/assigned-clocks; + /delete-property/assigned-clock-parents; + /delete-property/assigned-clock-rates; +}; + +&sdmmc1 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_SDMMC1_HCLK &scmi0_clock AT91_SCMI_CLK_GCK_SDMMC1_GCLK &scmi0_clock AT91_SCMI_CLK_CORE_MAIN>; + /delete-property/assigned-clocks; + /delete-property/assigned-clock-parents; + /delete-property/assigned-clock-rates; +}; + +&usb0 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_UDPHS_CLK &scmi0_clock AT91_SCMI_CLK_CORE_UTMI>; +}; + +&usb1 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_UHPHS_CLK &scmi0_clock AT91_SCMI_CLK_PERIPH_UHPHS_CLK &scmi0_clock AT91_SCMI_CLK_SYSTEM_UHPCK>; +}; + +&usb2 { + clocks = <&scmi0_clock AT91_SCMI_CLK_CORE_UTMI &scmi0_clock AT91_SCMI_CLK_PERIPH_UHPHS_CLK>; +}; + +&etm { + clocks = <&scmi0_clock AT91_SCMI_CLK_CORE_MCK>; +}; + +&etb { + clocks = <&scmi0_clock AT91_SCMI_CLK_CORE_MCK>; +}; + +&ebi { + clocks = <&scmi0_clock AT91_SCMI_CLK_CORE_MCK2>; +}; + +&i2s0 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_I2S0_CLK &scmi0_clock AT91_SCMI_CLK_GCK_I2S0_GCLK>; +}; + +&i2s1 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_I2S1_CLK &scmi0_clock AT91_SCMI_CLK_GCK_I2S1_GCLK>; +}; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/boot/dts/microchip/sama5d4.dtsi linux-microchip/arch/arm/boot/dts/microchip/sama5d4.dtsi --- linux-6.6.51/arch/arm/boot/dts/microchip/sama5d4.dtsi 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/boot/dts/microchip/sama5d4.dtsi 2024-11-26 14:02:35.686461237 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:105 @ ranges = <0 0x100000 0x2400>; }; - vdec0: vdec@300000 { - compatible = "microchip,sama5d4-vdec"; - reg = <0x00300000 0x100000>; + vdec0: vdec@00300000 { + compatible = "on2,sama5d4-g1"; + reg = <0x00300000 0x1000>; interrupts = <19 IRQ_TYPE_LEVEL_HIGH 4>; clocks = <&pmc PMC_TYPE_PERIPHERAL 19>; + clock-names = "vdec_clk"; + status = "disabled"; }; usb0: gadget@400000 { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:699 @ clock-names = "aes_clk"; }; - tdes: crpyto@fc04c000 { + tdes: crypto@fc04c000 { compatible = "atmel,at91sam9g46-tdes"; reg = <0xfc04c000 0x100>; interrupts = <14 IRQ_TYPE_LEVEL_HIGH 0>; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/boot/dts/microchip/sama7g5.dtsi linux-microchip/arch/arm/boot/dts/microchip/sama7g5.dtsi --- linux-6.6.51/arch/arm/boot/dts/microchip/sama7g5.dtsi 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/boot/dts/microchip/sama7g5.dtsi 2024-11-26 14:02:35.686461237 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:15 @ #include <dt-bindings/iio/adc/at91-sama5d2_adc.h> #include <dt-bindings/interrupt-controller/irq.h> #include <dt-bindings/interrupt-controller/arm-gic.h> +#include <dt-bindings/reset/sama7g5-reset.h> #include <dt-bindings/clock/at91.h> #include <dt-bindings/dma/at91.h> #include <dt-bindings/gpio/gpio.h> @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:138 @ }; }; + utmi_clk: utmi-clk { + compatible = "microchip,sama7g5-utmi-clk"; + sfr-phandle = <&sfr>; + #clock-cells = <1>; + clocks = <&pmc PMC_TYPE_CORE PMC_UTMI>; + 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"; + }; + + 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 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 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 UTMI3>; + clock-names = "utmi_clk"; + status = "disabled"; + #phy-cells = <0>; + }; + }; + vddout25: fixed-regulator-vddout25 { compatible = "regulator-fixed"; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:200 @ compatible = "mmio-sram"; #address-cells = <1>; #size-cells = <1>; - reg = <0x100000 0x20000>; + reg = <0x100000 0x3400>; ranges; }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:217 @ #size-cells = <1>; ranges; + usb0: gadget@200000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "microchip,sama7g5-udc"; + reg = <0x00200000 0x100000 + 0xe0814000 0x400>; + interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 104>, <&usb_clk>; + clock-names = "pclk", "hclk"; + status = "disabled"; + }; + + usb1: gadget@300000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "microchip,sama7g5-udc"; + reg = <0x00300000 0x100000 + 0xe0818000 0x400>; + interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 105>, <&usb_clk>; + clock-names = "pclk", "hclk"; + status = "disabled"; + }; + + usb2: ohci@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 UTMI1>, <&usb_clk>; + clock-names = "ohci_clk", "hclk", "uhpck"; + status = "disabled"; + }; + + usb3: ehci@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"; + }; + nfc_sram: sram@600000 { compatible = "mmio-sram"; no-memory-wc; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:610 @ status = "disabled"; }; + csi2host: csi2host@e1400000 { + compatible = "snps,dw-csi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0xe1400000 0x7FF>; + interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 33>, <&pmc PMC_TYPE_GCK 33>; + clock-names = "perclk", "phyclk"; + assigned-clocks = <&pmc PMC_TYPE_GCK 33>; + assigned-clock-rates = <26600000>; + snps,output-type = <1>; + phys = <&csi_dphy>; + status = "disabled"; + + port@1 { + reg = <1>; + csi2host_in: endpoint { + }; + }; + + port@2 { + reg = <2>; + csi2host_out: endpoint { + }; + }; + }; + + csi_dphy: dphy@e1400040 { + compatible = "snps,dw-dphy-rx"; + #phy-cells = <0>; + bus-width = <8>; + snps,dphy-frequency = <900000>; + snps,phy_type = <0>; + reg = <0xe1400040 0x20>; + status = "disabled"; + }; + csi2dc: csi2dc@e1404000 { compatible = "microchip,sama7g5-csi2dc"; reg = <0xe1404000 0x500>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:716 @ dma-names = "rx"; clocks = <&pmc PMC_TYPE_PERIPHERAL 68>, <&pmc PMC_TYPE_GCK 68>; clock-names = "pclk", "gclk"; + sound-name-prefix = "PDMC0"; status = "disabled"; }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:729 @ dma-names = "rx"; clocks = <&pmc PMC_TYPE_PERIPHERAL 69>, <&pmc PMC_TYPE_GCK 69>; clock-names = "pclk", "gclk"; + sound-name-prefix = "PDMC1"; + status = "disabled"; + }; + + asrc: sound@e1610000 { + compatible = "microchip,sama7g5-asrc"; + reg = <0xe1610000 0x1000>; + interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>; + #sound-dai-cells = <1>; + dmas = <&dma0 AT91_XDMAC_DT_PERID(55)>, + <&dma0 AT91_XDMAC_DT_PERID(56)>, + <&dma0 AT91_XDMAC_DT_PERID(57)>, + <&dma0 AT91_XDMAC_DT_PERID(58)>, + <&dma0 AT91_XDMAC_DT_PERID(59)>, + <&dma0 AT91_XDMAC_DT_PERID(60)>, + <&dma0 AT91_XDMAC_DT_PERID(61)>, + <&dma0 AT91_XDMAC_DT_PERID(62)>; + dma-names = "rx0", "tx0", "rx1", "tx1", + "rx2", "tx2", "rx3", "tx3"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 30>, <&pmc PMC_TYPE_GCK 30>; + clock-names = "pclk", "gclk"; + microchip,triggers = <&i2s0>, <&i2s1>, <&pdmc0>, <&pdmc1>, + <&ssc0>, <&ssc1>, <&spdiftx>, <&spdifrx>; + microchip,trigger-indexes = <1>, <2>, <3>, <4>, <5>, <8>, <9>, <10>; + assigned-clocks = <&pmc PMC_TYPE_GCK 30>; + assigned-clock-parents = <&pmc PMC_TYPE_CORE PMC_AUDIOPMCPLL>; status = "disabled"; }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:768 @ clocks = <&pmc PMC_TYPE_PERIPHERAL 84>, <&pmc PMC_TYPE_GCK 84>; clock-names = "pclk", "gclk"; status = "disabled"; + sound-name-prefix = "SPDIFRX0"; }; spdiftx: spdiftx@e1618000 { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:780 @ dma-names = "tx"; clocks = <&pmc PMC_TYPE_PERIPHERAL 85>, <&pmc PMC_TYPE_GCK 85>; clock-names = "pclk", "gclk"; + sound-name-prefix = "SPDIFTX0"; }; i2s0: i2s@e161c000 { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:792 @ dma-names = "tx", "rx"; clocks = <&pmc PMC_TYPE_PERIPHERAL 57>, <&pmc PMC_TYPE_GCK 57>; clock-names = "pclk", "gclk"; + sound-name-prefix = "I2SMCC0"; status = "disabled"; }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:805 @ dma-names = "tx", "rx"; clocks = <&pmc PMC_TYPE_PERIPHERAL 58>, <&pmc PMC_TYPE_GCK 58>; clock-names = "pclk", "gclk"; + sound-name-prefix = "I2SMCC1"; status = "disabled"; }; + sfr: sfr@e1624000 { + compatible = "microchip,sama7g5-sfr", "syscon"; + reg = <0xe1624000 0x4000>; + }; + eic: interrupt-controller@e1628000 { compatible = "microchip,sama7g5-eic"; reg = <0xe1628000 0xec>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:843 @ clock-names = "pclk", "gclk"; }; + ssc0: sound@e180c000 { + compatible = "atmel,at91sam9g45-ssc"; + reg = <0xe180c000 0x1000>; + #sound-dai-cells = <0>; + interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&dma0 AT91_XDMAC_DT_PERID(44)>, + <&dma0 AT91_XDMAC_DT_PERID(45)>; + dma-names = "rx", "tx"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 86>; + clock-names = "pclk"; + sound-name-prefix = "SSC0"; + status = "disabled"; + }; + aes: crypto@e1810000 { compatible = "atmel,at91sam9g46-aes"; reg = <0xe1810000 0x100>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:895 @ clocks = <&pmc PMC_TYPE_PERIPHERAL 38>; clock-names = "usart"; dmas = <&dma1 AT91_XDMAC_DT_PERID(6)>, - <&dma1 AT91_XDMAC_DT_PERID(5)>; + <&dma1 AT91_XDMAC_DT_PERID(5)>; dma-names = "tx", "rx"; atmel,use-dma-rx; atmel,use-dma-tx; status = "disabled"; }; + spi0: spi@400 { + compatible = "atmel,at91rm9200-spi"; + reg = <0x400 0x200>; + interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 38>; + clock-names = "spi_clk"; + #address-cells = <1>; + #size-cells = <0>; + atmel,fifo-size = <32>; + dmas = <&dma0 AT91_XDMAC_DT_PERID(6)>, + <&dma0 AT91_XDMAC_DT_PERID(5)>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + i2c0: i2c@600 { + compatible = "microchip,sama7g5-i2c", "microchip,sam9x60-i2c"; + reg = <0x600 0x200>; + interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 38>; + atmel,fifo-size = <32>; + dmas = <&dma0 AT91_XDMAC_DT_PERID(6)>, + <&dma0 AT91_XDMAC_DT_PERID(5)>; + dma-names = "tx", "rx"; + status = "disabled"; + }; }; flx1: flexcom@e181c000 { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:939 @ ranges = <0x0 0xe181c000 0x800>; status = "disabled"; + uart1: serial@200 { + compatible = "atmel,at91sam9260-usart"; + reg = <0x200 0x200>; + atmel,usart-mode = <AT91_USART_MODE_SERIAL>; + interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 39>; + clock-names = "usart"; + dmas = <&dma1 AT91_XDMAC_DT_PERID(8)>, + <&dma1 AT91_XDMAC_DT_PERID(7)>; + dma-names = "tx", "rx"; + atmel,use-dma-rx; + atmel,use-dma-tx; + status = "disabled"; + }; + spi1: spi@400 { + compatible = "atmel,at91rm9200-spi"; + reg = <0x400 0x200>; + interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 39>; + clock-names = "spi_clk"; + #address-cells = <1>; + #size-cells = <0>; + atmel,fifo-size = <32>; + dmas = <&dma0 AT91_XDMAC_DT_PERID(8)>, + <&dma0 AT91_XDMAC_DT_PERID(7)>; + dma-names = "tx", "rx"; + status = "disabled"; + }; i2c1: i2c@600 { compatible = "microchip,sama7g5-i2c", "microchip,sam9x60-i2c"; reg = <0x600 0x200>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:976 @ clocks = <&pmc PMC_TYPE_PERIPHERAL 39>; atmel,fifo-size = <32>; dmas = <&dma0 AT91_XDMAC_DT_PERID(8)>, - <&dma0 AT91_XDMAC_DT_PERID(7)>; + <&dma0 AT91_XDMAC_DT_PERID(7)>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + }; + + flx2: flexcom@e1820000 { + compatible = "atmel,sama5d2-flexcom"; + reg = <0xe1820000 0x200>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 40>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0xe1820000 0x800>; + status = "disabled"; + + uart2: serial@200 { + compatible = "atmel,at91sam9260-usart"; + reg = <0x200 0x200>; + atmel,usart-mode = <AT91_USART_MODE_SERIAL>; + interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 40>; + clock-names = "usart"; + dmas = <&dma1 AT91_XDMAC_DT_PERID(10)>, + <&dma1 AT91_XDMAC_DT_PERID(9)>; + dma-names = "tx", "rx"; + atmel,use-dma-rx; + atmel,use-dma-tx; + status = "disabled"; + }; + spi2: spi@400 { + compatible = "atmel,at91rm9200-spi"; + reg = <0x400 0x200>; + interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 40>; + clock-names = "spi_clk"; + #address-cells = <1>; + #size-cells = <0>; + atmel,fifo-size = <32>; + dmas = <&dma0 AT91_XDMAC_DT_PERID(10)>, + <&dma0 AT91_XDMAC_DT_PERID(9)>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + i2c2: i2c@600 { + compatible = "microchip,sama7g5-i2c", "microchip,sam9x60-i2c"; + reg = <0x600 0x200>; + interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 40>; + atmel,fifo-size = <32>; + dmas = <&dma0 AT91_XDMAC_DT_PERID(10)>, + <&dma0 AT91_XDMAC_DT_PERID(9)>; dma-names = "tx", "rx"; status = "disabled"; }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1051 @ clocks = <&pmc PMC_TYPE_PERIPHERAL 41>; clock-names = "usart"; dmas = <&dma1 AT91_XDMAC_DT_PERID(12)>, - <&dma1 AT91_XDMAC_DT_PERID(11)>; + <&dma1 AT91_XDMAC_DT_PERID(11)>; dma-names = "tx", "rx"; atmel,use-dma-rx; atmel,use-dma-tx; status = "disabled"; }; + spi3: spi@400 { + compatible = "atmel,at91rm9200-spi"; + reg = <0x400 0x200>; + interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 41>; + clock-names = "spi_clk"; + #address-cells = <1>; + #size-cells = <0>; + atmel,fifo-size = <32>; + dmas = <&dma0 AT91_XDMAC_DT_PERID(12)>, + <&dma0 AT91_XDMAC_DT_PERID(11)>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + i2c3: i2c@600 { + compatible = "microchip,sama7g5-i2c", "microchip,sam9x60-i2c"; + reg = <0x600 0x200>; + interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 41>; + atmel,fifo-size = <32>; + dmas = <&dma0 AT91_XDMAC_DT_PERID(12)>, + <&dma0 AT91_XDMAC_DT_PERID(11)>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + }; + + ssc1: sound@e200c000 { + compatible = "atmel,at91sam9g45-ssc"; + reg = <0xe200c000 0x1000>; + #sound-dai-cells = <0>; + interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&dma0 AT91_XDMAC_DT_PERID(46)>, + <&dma0 AT91_XDMAC_DT_PERID(47)>; + dma-names = "rx", "tx"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 87>; + clock-names = "pclk"; + sound-name-prefix = "SSC1"; + status = "disabled"; }; trng: rng@e2010000 { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1136 @ clocks = <&pmc PMC_TYPE_PERIPHERAL 42>; clock-names = "usart"; dmas = <&dma1 AT91_XDMAC_DT_PERID(14)>, - <&dma1 AT91_XDMAC_DT_PERID(13)>; + <&dma1 AT91_XDMAC_DT_PERID(13)>; dma-names = "tx", "rx"; atmel,use-dma-rx; atmel,use-dma-tx; atmel,fifo-size = <16>; status = "disabled"; }; + spi4: spi@400 { + compatible = "atmel,at91rm9200-spi"; + reg = <0x400 0x200>; + interrupts = <GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 42>; + clock-names = "spi_clk"; + #address-cells = <1>; + #size-cells = <0>; + atmel,fifo-size = <32>; + dmas = <&dma0 AT91_XDMAC_DT_PERID(14)>, + <&dma0 AT91_XDMAC_DT_PERID(13)>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + i2c4: i2c@600 { + compatible = "microchip,sama7g5-i2c", "microchip,sam9x60-i2c"; + reg = <0x600 0x200>; + interrupts = <GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 42>; + atmel,fifo-size = <32>; + dmas = <&dma0 AT91_XDMAC_DT_PERID(14)>, + <&dma0 AT91_XDMAC_DT_PERID(13)>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + }; + + flx5: flexcom@e201c000 { + compatible = "atmel,sama5d2-flexcom"; + reg = <0xe201c000 0x200>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 43>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0xe201c000 0x800>; + status = "disabled"; + + uart5: serial@200 { + compatible = "atmel,at91sam9260-usart"; + reg = <0x200 0x200>; + atmel,usart-mode = <AT91_USART_MODE_SERIAL>; + interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 43>; + clock-names = "usart"; + dmas = <&dma1 AT91_XDMAC_DT_PERID(16)>, + <&dma1 AT91_XDMAC_DT_PERID(15)>; + dma-names = "tx", "rx"; + atmel,use-dma-rx; + atmel,use-dma-tx; + status = "disabled"; + }; + spi5: spi@400 { + compatible = "atmel,at91rm9200-spi"; + reg = <0x400 0x200>; + interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 43>; + clock-names = "spi_clk"; + #address-cells = <1>; + #size-cells = <0>; + atmel,fifo-size = <32>; + dmas = <&dma0 AT91_XDMAC_DT_PERID(16)>, + <&dma0 AT91_XDMAC_DT_PERID(15)>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + i2c5: i2c@600 { + compatible = "microchip,sama7g5-i2c", "microchip,sam9x60-i2c"; + reg = <0x600 0x200>; + interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 43>; + atmel,fifo-size = <32>; + dmas = <&dma0 AT91_XDMAC_DT_PERID(16)>, + <&dma0 AT91_XDMAC_DT_PERID(15)>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + }; + + flx6: flexcom@e2020000 { + compatible = "atmel,sama5d2-flexcom"; + reg = <0xe2020000 0x200>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 44>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0xe2020000 0x800>; + status = "disabled"; + + uart6: serial@200 { + compatible = "atmel,at91sam9260-usart"; + reg = <0x200 0x200>; + atmel,usart-mode = <AT91_USART_MODE_SERIAL>; + interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 44>; + clock-names = "usart"; + dmas = <&dma1 AT91_XDMAC_DT_PERID(18)>, + <&dma1 AT91_XDMAC_DT_PERID(17)>; + dma-names = "tx", "rx"; + atmel,use-dma-rx; + atmel,use-dma-tx; + status = "disabled"; + }; + spi6: spi@400 { + compatible = "atmel,at91rm9200-spi"; + reg = <0x400 0x200>; + interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 44>; + clock-names = "spi_clk"; + #address-cells = <1>; + #size-cells = <0>; + atmel,fifo-size = <32>; + dmas = <&dma0 AT91_XDMAC_DT_PERID(18)>, + <&dma0 AT91_XDMAC_DT_PERID(17)>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + i2c6: i2c@600 { + compatible = "microchip,sama7g5-i2c", "microchip,sam9x60-i2c"; + reg = <0x600 0x200>; + interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 44>; + atmel,fifo-size = <32>; + dmas = <&dma0 AT91_XDMAC_DT_PERID(18)>, + <&dma0 AT91_XDMAC_DT_PERID(17)>; + dma-names = "tx", "rx"; + status = "disabled"; + }; }; flx7: flexcom@e2024000 { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1293 @ clocks = <&pmc PMC_TYPE_PERIPHERAL 45>; clock-names = "usart"; dmas = <&dma1 AT91_XDMAC_DT_PERID(20)>, - <&dma1 AT91_XDMAC_DT_PERID(19)>; + <&dma1 AT91_XDMAC_DT_PERID(19)>; dma-names = "tx", "rx"; atmel,use-dma-rx; atmel,use-dma-tx; atmel,fifo-size = <16>; status = "disabled"; }; + spi7: spi@400 { + compatible = "atmel,at91rm9200-spi"; + reg = <0x400 0x200>; + interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 45>; + clock-names = "spi_clk"; + #address-cells = <1>; + #size-cells = <0>; + atmel,fifo-size = <32>; + dmas = <&dma0 AT91_XDMAC_DT_PERID(20)>, + <&dma0 AT91_XDMAC_DT_PERID(19)>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + i2c7: i2c@600 { + compatible = "microchip,sama7g5-i2c", "microchip,sam9x60-i2c"; + reg = <0x600 0x200>; + interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 45>; + atmel,fifo-size = <32>; + dmas = <&dma0 AT91_XDMAC_DT_PERID(20)>, + <&dma0 AT91_XDMAC_DT_PERID(19)>; + dma-names = "tx", "rx"; + status = "disabled"; + }; }; gmac0: ethernet@e2800000 { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1340 @ <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>; clocks = <&pmc PMC_TYPE_PERIPHERAL 51>, <&pmc PMC_TYPE_PERIPHERAL 51>, <&pmc PMC_TYPE_GCK 51>, <&pmc PMC_TYPE_GCK 53>; clock-names = "pclk", "hclk", "tx_clk", "tsu_clk"; - assigned-clocks = <&pmc PMC_TYPE_GCK 51>; - assigned-clock-rates = <125000000>; + assigned-clocks = <&pmc PMC_TYPE_GCK 51>, <&pmc PMC_TYPE_GCK 53>; + assigned-clock-rates = <125000000>, <200000000>; status = "disabled"; }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1350 @ reg = <0xe2804000 0x1000>; interrupts = <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&pmc PMC_TYPE_PERIPHERAL 52>, <&pmc PMC_TYPE_PERIPHERAL 52>; - clock-names = "pclk", "hclk"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 52>, <&pmc PMC_TYPE_PERIPHERAL 52>, <&pmc PMC_TYPE_GCK 54>; + clock-names = "pclk", "hclk", "tsu_clk"; + assigned-clocks = <&pmc PMC_TYPE_GCK 54>; + assigned-clock-rates = <200000000>; status = "disabled"; }; dma0: dma-controller@e2808000 { compatible = "microchip,sama7g5-dma"; reg = <0xe2808000 0x1000>; + dma-channels = <32>; interrupts = <GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>; #dma-cells = <1>; clocks = <&pmc PMC_TYPE_PERIPHERAL 22>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1371 @ dma1: dma-controller@e280c000 { compatible = "microchip,sama7g5-dma"; reg = <0xe280c000 0x1000>; + dma-channels = <32>; interrupts = <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>; #dma-cells = <1>; clocks = <&pmc PMC_TYPE_PERIPHERAL 23>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1383 @ dma2: dma-controller@e1200000 { compatible = "microchip,sama7g5-dma"; reg = <0xe1200000 0x1000>; + dma-channels = <8>; interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>; #dma-cells = <1>; clocks = <&pmc PMC_TYPE_PERIPHERAL 24>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1411 @ ranges = <0x0 0xe2818000 0x800>; status = "disabled"; + uart8: serial@200 { + compatible = "atmel,at91sam9260-usart"; + reg = <0x200 0x200>; + atmel,usart-mode = <AT91_USART_MODE_SERIAL>; + interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 46>; + clock-names = "usart"; + dmas = <&dma1 AT91_XDMAC_DT_PERID(22)>, + <&dma1 AT91_XDMAC_DT_PERID(21)>; + dma-names = "tx", "rx"; + atmel,use-dma-rx; + atmel,use-dma-tx; + atmel,fifo-size = <16>; + status = "disabled"; + }; + spi8: spi@400 { + compatible = "atmel,at91rm9200-spi"; + reg = <0x400 0x200>; + interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 46>; + clock-names = "spi_clk"; + atmel,fifo-size = <32>; + dmas = <&dma0 AT91_XDMAC_DT_PERID(22)>, + <&dma0 AT91_XDMAC_DT_PERID(21)>; + dma-names = "tx", "rx"; + status = "disabled"; + }; i2c8: i2c@600 { compatible = "microchip,sama7g5-i2c", "microchip,sam9x60-i2c"; reg = <0x600 0x200>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1449 @ clocks = <&pmc PMC_TYPE_PERIPHERAL 46>; atmel,fifo-size = <32>; dmas = <&dma0 AT91_XDMAC_DT_PERID(22)>, - <&dma0 AT91_XDMAC_DT_PERID(21)>; + <&dma0 AT91_XDMAC_DT_PERID(21)>; dma-names = "tx", "rx"; status = "disabled"; }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1464 @ ranges = <0x0 0xe281c000 0x800>; status = "disabled"; + uart9: serial@200 { + compatible = "atmel,at91sam9260-usart"; + reg = <0x200 0x200>; + atmel,usart-mode = <AT91_USART_MODE_SERIAL>; + interrupts = <GIC_SPI 47 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 47>; + clock-names = "usart"; + dmas = <&dma1 AT91_XDMAC_DT_PERID(24)>, + <&dma1 AT91_XDMAC_DT_PERID(23)>; + dma-names = "tx", "rx"; + atmel,use-dma-rx; + atmel,use-dma-tx; + atmel,fifo-size = <16>; + status = "disabled"; + }; + spi9: spi@400 { + compatible = "atmel,at91rm9200-spi"; + reg = <0x400 0x200>; + interrupts = <GIC_SPI 47 IRQ_TYPE_LEVEL_HIGH>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 47>; + clock-names = "spi_clk"; + atmel,fifo-size = <32>; + dmas = <&dma0 AT91_XDMAC_DT_PERID(24)>, + <&dma0 AT91_XDMAC_DT_PERID(23)>; + dma-names = "tx", "rx"; + status = "disabled"; + }; i2c9: i2c@600 { compatible = "microchip,sama7g5-i2c", "microchip,sam9x60-i2c"; reg = <0x600 0x200>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1502 @ clocks = <&pmc PMC_TYPE_PERIPHERAL 47>; atmel,fifo-size = <32>; dmas = <&dma0 AT91_XDMAC_DT_PERID(24)>, - <&dma0 AT91_XDMAC_DT_PERID(23)>; + <&dma0 AT91_XDMAC_DT_PERID(23)>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + }; + + flx10: flexcom@e2820000 { + compatible = "atmel,sama5d2-flexcom"; + reg = <0xe2820000 0x200>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 48>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0xe2820000 0x800>; + status = "disabled"; + + uart10: serial@200 { + compatible = "atmel,at91sam9260-usart"; + reg = <0x200 0x200>; + atmel,usart-mode = <AT91_USART_MODE_SERIAL>; + interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 48>; + clock-names = "usart"; + dmas = <&dma1 AT91_XDMAC_DT_PERID(26)>, + <&dma1 AT91_XDMAC_DT_PERID(25)>; + dma-names = "tx", "rx"; + atmel,use-dma-rx; + atmel,use-dma-tx; + atmel,fifo-size = <16>; + status = "disabled"; + }; + spi10: spi@400 { + compatible = "atmel,at91rm9200-spi"; + reg = <0x400 0x200>; + interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 48>; + clock-names = "spi_clk"; + atmel,fifo-size = <32>; + dmas = <&dma0 AT91_XDMAC_DT_PERID(26)>, + <&dma0 AT91_XDMAC_DT_PERID(25)>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + i2c10: i2c@600 { + compatible = "microchip,sama7g5-i2c", "microchip,sam9x60-i2c"; + reg = <0x600 0x200>; + interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 48>; + atmel,fifo-size = <32>; + dmas = <&dma0 AT91_XDMAC_DT_PERID(26)>, + <&dma0 AT91_XDMAC_DT_PERID(25)>; dma-names = "tx", "rx"; status = "disabled"; }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1570 @ ranges = <0x0 0xe2824000 0x800>; status = "disabled"; + uart11: serial@200 { + compatible = "atmel,at91sam9260-usart"; + reg = <0x200 0x200>; + atmel,usart-mode = <AT91_USART_MODE_SERIAL>; + interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 49>; + clock-names = "usart"; + dmas = <&dma1 AT91_XDMAC_DT_PERID(28)>, + <&dma1 AT91_XDMAC_DT_PERID(27)>; + dma-names = "tx", "rx"; + atmel,use-dma-rx; + atmel,use-dma-tx; + atmel,fifo-size = <16>; + status = "disabled"; + }; spi11: spi@400 { compatible = "atmel,at91rm9200-spi"; reg = <0x400 0x200>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1595 @ #size-cells = <0>; atmel,fifo-size = <32>; dmas = <&dma0 AT91_XDMAC_DT_PERID(28)>, - <&dma0 AT91_XDMAC_DT_PERID(27)>; + <&dma0 AT91_XDMAC_DT_PERID(27)>; + dma-names = "tx", "rx"; + status = "disabled"; + }; + i2c11: i2c@600 { + compatible = "microchip,sama7g5-i2c", "microchip,sam9x60-i2c"; + reg = <0x600 0x200>; + interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 49>; + atmel,fifo-size = <32>; + dmas = <&dma0 AT91_XDMAC_DT_PERID(28)>, + <&dma0 AT91_XDMAC_DT_PERID(27)>; dma-names = "tx", "rx"; status = "disabled"; }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/boot/dts/microchip/sama7g5-optee.dtsi linux-microchip/arch/arm/boot/dts/microchip/sama7g5-optee.dtsi --- linux-6.6.51/arch/arm/boot/dts/microchip/sama7g5-optee.dtsi 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/arch/arm/boot/dts/microchip/sama7g5-optee.dtsi 2024-11-26 14:02:35.686461237 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * sama7g5ek-optee.dtsi - Device Tree file for SAMA7G5-EK OP-TEE + * + * Copyright (c) 2024 Microchip Technology Inc. and its subsidiaries + * + */ + +/ { + cpus { + cpu@0 { + enable-method = "psci"; + cpu-idle-states = <&psci_standby>; + clocks = <&scmi0_clock AT91_SCMI_CLK_CPU_OPP>; + }; + + idle-states { + entry-method = "psci"; + + psci_standby: psci-standby { + compatible = "arm,idle-state"; + idle-state-name = "psci,standby"; + local-timer-stop; + entry-latency-us = <1000>; + exit-latency-us = <700>; + min-residency-us = <2000>; + arm,psci-suspend-param = <0x0>; + }; + }; + }; + + clocks { + /* Add dummy fixed-clock for TCB (see below) */ + dummy_clk_tcb_fixed: dummy-clk-tcb-fixed { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <200000000>; + }; + + clk32kfixed: clk32kfixed { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + }; + + reserved-memory { + ranges; + #address-cells = <1>; + #size-cells = <1>; + + optee-core@60000000 { + reg = <0x60000000 0x00800000>; + no-map; + }; + + optee-shm@61000000 { + reg = <0x61000000 0x00400000>; + no-map; + }; + + scmi0_shmem: scmi0-shmem@61400000 { + compatible = "arm,scmi-shmem"; + reg = <0x61400000 0x80>; + no-map; + }; + }; + + psci { + compatible = "arm,psci-1.0", "arm,psci-0.2", "arm,psci"; + method = "smc"; + cpu_suspend = <0x84000001>; + cpu_off = <0x84000002>; + cpu_on = <0x84000003>; + sys-poweroff = <0x84000008>; + sys-reset = <0x84000009>; + }; + + firmware { + optee { + compatible = "linaro,optee-tz"; + method = "smc"; + }; + + scmi0 { + compatible = "arm,scmi-smc"; + shmem = <&scmi0_shmem>; + #address-cells = <0x01>; + #size-cells = <0x00>; + arm,smc-id = <0x2000200>; + + scmi0_clock: scmi0-clock@14 { + reg = <0x14>; + #clock-cells = <0x01>; + }; + }; + }; + + arm-smc-wdt { + compatible = "arm,smc-wdt"; + arm,smc-id = <0x2000500>; + timeout-sec = <15>; + }; +}; + +/* Disable clocks controllers as they are handled by OP-TEE */ +&clk32k { + status = "disabled"; +}; + +&pmc { + status = "disabled"; +}; + +&trng { + status = "disabled"; +}; + +&tcb1 { + status = "disabled"; +}; + +&shdwc { + status = "disabled"; +}; + +&reset_controller { + status = "disabled"; +}; + +&securam { + status = "disabled"; +}; + +&sfrbu { + status = "disabled"; +}; + +&pit64b0 { + status = "disabled"; +}; + +&pit64b1 { + status = "disabled"; +}; + +&rtc { + status = "disabled"; +}; + +&rtt { + status = "disabled"; +}; + +&ps_wdt { + status = "disabled"; +}; + +/* + * Use fixed-clock for TCB since SCMI clocks are probed too late for + * clocksource + */ +&tcb0 { + clocks = <&dummy_clk_tcb_fixed>, <&dummy_clk_tcb_fixed>, <&dummy_clk_tcb_fixed>, <&clk32kfixed>; +}; + +&can0 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_CAN0_CLK>, <&scmi0_clock AT91_SCMI_CLK_GCK_CAN0_GCLK>; + /delete-property/assigned-clocks; + /delete-property/assigned-clock-parents; + /delete-property/assigned-clock-rates; +}; + +&can1 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_CAN1_CLK>, <&scmi0_clock AT91_SCMI_CLK_GCK_CAN1_GCLK>; + /delete-property/assigned-clocks; + /delete-property/assigned-clock-parents; + /delete-property/assigned-clock-rates; +}; + +&can2 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_CAN2_CLK>, <&scmi0_clock AT91_SCMI_CLK_GCK_CAN2_GCLK>; + /delete-property/assigned-clocks; + /delete-property/assigned-clock-parents; + /delete-property/assigned-clock-rates; +}; + +&can3 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_CAN3_CLK>, <&scmi0_clock AT91_SCMI_CLK_GCK_CAN3_GCLK>; + /delete-property/assigned-clocks; + /delete-property/assigned-clock-parents; + /delete-property/assigned-clock-rates; +}; + +&can4 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_CAN4_CLK>, <&scmi0_clock AT91_SCMI_CLK_GCK_CAN4_GCLK>; + /delete-property/assigned-clocks; + /delete-property/assigned-clock-parents; + /delete-property/assigned-clock-rates; +}; + +&can5 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_CAN5_CLK>, <&scmi0_clock AT91_SCMI_CLK_GCK_CAN5_GCLK>; + /delete-property/assigned-clocks; + /delete-property/assigned-clock-parents; + /delete-property/assigned-clock-rates; +}; + +&csi2host { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_CSI_CLK>, <&scmi0_clock AT91_SCMI_CLK_GCK_CSI_GCLK>; + /delete-property/assigned-clocks; + /delete-property/assigned-clock-parents; + /delete-property/assigned-clock-rates; +}; + +&csi2dc { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_CSI2DC_CLK>, <&xisc>; + /delete-property/assigned-clocks; + /delete-property/assigned-clock-parents; + /delete-property/assigned-clock-rates; +}; + +&asrc { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_ASRC_CLK>, <&scmi0_clock AT91_SCMI_CLK_GCK_ASRC_GCLK>; + /delete-property/assigned-clocks; + /delete-property/assigned-clock-parents; + /delete-property/assigned-clock-rates; +}; + +&tdes { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_TDES_CLK>; +}; + +&pioA { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_PIOA_CLK>; +}; + +&adc { + clocks = <&scmi0_clock AT91_SCMI_CLK_GCK_ADC_GCLK>; + /delete-property/assigned-clocks; + /delete-property/assigned-clock-parents; + /delete-property/assigned-clock-rates; +}; + +&i2c1 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX1_CLK>; +}; + +&i2c8 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX8_CLK>; +}; + +&i2c9 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX9_CLK>; +}; + +&flx0 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX0_CLK>; +}; + +&flx1 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX1_CLK>; +}; + +&flx3 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX3_CLK>; +}; + +&flx4 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX4_CLK>; +}; + +&flx7 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX7_CLK>; +}; + +&flx8 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX8_CLK>; +}; + +&flx9 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX9_CLK>; +}; + +&flx11 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX11_CLK>; +}; + +&uart3 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX3_CLK>; +}; + +&uart4 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX4_CLK>; +}; + +&uart7 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX7_CLK>; +}; + +&spi11 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX11_CLK>; +}; + +&qspi0 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_QSPI0_CLK>, <&scmi0_clock AT91_SCMI_CLK_GCK_QSPI0_GCLK>; +}; + +&qspi1 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_QSPI1_CLK>, <&scmi0_clock AT91_SCMI_CLK_GCK_QSPI1_GCLK>; +}; + +&aes { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_AES_CLK>; +}; + +&sha { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_SHA_CLK>; +}; + +&pwm { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_PWM_CLK>; +}; + +&dma0 { + interrupts = <GIC_SPI 22 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_DMA0_CLK>; +}; + +&dma1 { + interrupts = <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_DMA1_CLK>; +}; + +&dma2 { + interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_DMA2_CLK>; +}; + +&sdmmc0 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_SDMMC0_HCLK>, <&scmi0_clock AT91_SCMI_CLK_GCK_SDMMC0_GCLK>; + /delete-property/assigned-clocks; + /delete-property/assigned-clock-parents; + /delete-property/assigned-clock-rates; +}; + +&sdmmc1 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_SDMMC1_HCLK>, <&scmi0_clock AT91_SCMI_CLK_GCK_SDMMC1_GCLK>; + /delete-property/assigned-clocks; + /delete-property/assigned-clock-parents; + /delete-property/assigned-clock-rates; +}; + +&i2c8 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_FLX8_CLK>; + status = "okay"; +}; + +&gmac0 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_MACB0_CLK>, <&scmi0_clock AT91_SCMI_CLK_PERIPH_MACB0_CLK>, <&scmi0_clock AT91_SCMI_CLK_GCK_MACB0_GCLK>, <&scmi0_clock AT91_SCMI_CLK_GCK_MACB0_TSU>; + /delete-property/assigned-clocks; + /delete-property/assigned-clock-parents; + /delete-property/assigned-clock-rates; + status = "okay"; +}; + +&gmac1 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_MACB1_CLK>, <&scmi0_clock AT91_SCMI_CLK_PERIPH_MACB1_CLK>, <&scmi0_clock AT91_SCMI_CLK_GCK_MACB1_TSU>; + /delete-property/assigned-clocks; + /delete-property/assigned-clock-parents; + /delete-property/assigned-clock-rates; +}; + +&spdifrx { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_SPDIFRX_CLK>, <&scmi0_clock AT91_SCMI_CLK_GCK_SPDIFRX_GCLK>; +}; + +&spdiftx { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_SPDIFTX_CLK>, <&scmi0_clock AT91_SCMI_CLK_GCK_SPDIFTX_GCLK>; +}; + +&utmi_clk { + clocks = <&scmi0_clock AT91_SCMI_CLK_CORE_UTMI>; +}; + +&usb_phy0 { + clocks = <&scmi0_clock AT91_SCMI_CLK_UTMI1>; +}; + +&usb_phy1 { + clocks = <&scmi0_clock AT91_SCMI_CLK_UTMI2>; +}; + +&usb_phy2 { + clocks = <&scmi0_clock AT91_SCMI_CLK_UTMI3>; +}; + +&usb0 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_UDPHSA_CLK>, <&usb_clk>; +}; + +&usb1 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_UDPHSB_CLK>, <&usb_clk>; +}; + +&usb2 { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_UHPHS_CLK>, <&scmi0_clock AT91_SCMI_CLK_UTMI1>, <&usb_clk>; +}; + +&usb3 { + clocks = <&usb_clk>, <&scmi0_clock AT91_SCMI_CLK_PERIPH_UHPHS_CLK>; +}; + +&xisc { + clocks = <&scmi0_clock AT91_SCMI_CLK_PERIPH_ISC_CLK>, <&scmi0_clock AT91_SCMI_CLK_SYSTEM_ISCCK>; +}; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/am200epdkit_defconfig linux-microchip/arch/arm/configs/am200epdkit_defconfig --- linux-6.6.51/arch/arm/configs/am200epdkit_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/am200epdkit_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2 @ CONFIG_LOCALVERSION="gum" CONFIG_SYSVIPC=y -CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_PREEMPT=y CONFIG_EXPERT=y # CONFIG_EPOLL is not set diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/aspeed_g4_defconfig linux-microchip/arch/arm/configs/aspeed_g4_defconfig --- linux-6.6.51/arch/arm/configs/aspeed_g4_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/aspeed_g4_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:15 @ # CONFIG_RD_BZIP2 is not set # CONFIG_RD_LZO is not set # CONFIG_RD_LZ4 is not set +CONFIG_EXPERT=y # CONFIG_UID16 is not set # CONFIG_SYSFS_SYSCALL is not set # CONFIG_AIO is not set -CONFIG_EXPERT=y CONFIG_PERF_EVENTS=y +CONFIG_KEXEC=y # CONFIG_ARCH_MULTI_V7 is not set CONFIG_ARCH_ASPEED=y CONFIG_MACH_ASPEED_G4=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:28 @ CONFIG_AEABI=y CONFIG_UACCESS_WITH_MEMCPY=y # CONFIG_ATAGS is not set -CONFIG_KEXEC=y CONFIG_JUMP_LABEL=y CONFIG_STRICT_KERNEL_RWX=y # CONFIG_BLK_DEBUG_FS is not set diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/aspeed_g5_defconfig linux-microchip/arch/arm/configs/aspeed_g5_defconfig --- linux-6.6.51/arch/arm/configs/aspeed_g5_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/aspeed_g5_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:15 @ # CONFIG_RD_BZIP2 is not set # CONFIG_RD_LZO is not set # CONFIG_RD_LZ4 is not set +CONFIG_EXPERT=y # CONFIG_UID16 is not set # CONFIG_SYSFS_SYSCALL is not set # CONFIG_AIO is not set -CONFIG_EXPERT=y CONFIG_PERF_EVENTS=y +CONFIG_KEXEC=y CONFIG_ARCH_MULTI_V6=y CONFIG_ARCH_ASPEED=y CONFIG_MACH_ASPEED_G5=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:33 @ CONFIG_HIGHMEM=y CONFIG_UACCESS_WITH_MEMCPY=y # CONFIG_ATAGS is not set -CONFIG_KEXEC=y CONFIG_VFP=y CONFIG_NEON=y CONFIG_KERNEL_MODE_NEON=y diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/at91_dt_defconfig linux-microchip/arch/arm/configs/at91_dt_defconfig --- linux-6.6.51/arch/arm/configs/at91_dt_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/at91_dt_defconfig 2024-11-26 14:04:18.560303308 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:5 @ CONFIG_SYSVIPC=y CONFIG_NO_HZ_IDLE=y CONFIG_HIGH_RES_TIMERS=y -CONFIG_LOG_BUF_SHIFT=14 +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=16 CONFIG_CGROUPS=y +CONFIG_CGROUP_CPUACCT=y CONFIG_BLK_DEV_INITRD=y CONFIG_CC_OPTIMIZE_FOR_SIZE=y -CONFIG_KALLSYMS_ALL=y CONFIG_EXPERT=y +CONFIG_KEXEC=y CONFIG_ARCH_MULTI_V4T=y CONFIG_ARCH_MULTI_V5=y # CONFIG_ARCH_MULTI_V7 is not set @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:21 @ CONFIG_SOC_AT91RM9200=y CONFIG_SOC_AT91SAM9=y CONFIG_SOC_SAM9X60=y +CONFIG_SOC_SAM9X7=y # CONFIG_ATMEL_CLOCKSOURCE_PIT is not set CONFIG_AEABI=y CONFIG_UACCESS_WITH_MEMCPY=y # CONFIG_ATAGS is not set -CONFIG_ARM_APPENDED_DTB=y -CONFIG_ARM_ATAG_DTB_COMPAT=y CONFIG_CMDLINE="console=ttyS0,115200 initrd=0x21100000,25165824 root=/dev/ram0 rw" -CONFIG_KEXEC=y +CONFIG_CPU_IDLE=y +# CONFIG_ARM_AT91_CPUIDLE is not set +CONFIG_PM_DEBUG=y +CONFIG_PM_ADVANCED_DEBUG=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set # CONFIG_SWAP is not set +CONFIG_CMA=y +CONFIG_CMA_DEBUGFS=y CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:44 @ CONFIG_IP_MULTICAST=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y -CONFIG_IP_PNP_BOOTP=y -CONFIG_IP_PNP_RARP=y # CONFIG_INET_DIAG is not set CONFIG_IPV6_SIT_6RD=y +CONFIG_BRIDGE=m +CONFIG_BRIDGE_VLAN_FILTERING=y +CONFIG_NET_DSA=m +CONFIG_NET_DSA_TAG_OCELOT=m +CONFIG_NET_DSA_TAG_OCELOT_8021Q=m +CONFIG_VLAN_8021Q=m CONFIG_CAN=y -CONFIG_CFG80211=y -CONFIG_MAC80211=y +CONFIG_BT=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=y +CONFIG_BT_HCIBTUSB=y +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_3WIRE=y +CONFIG_BT_HCIVHCI=y +CONFIG_CFG80211=m +# CONFIG_CFG80211_DEFAULT_PS is not set +CONFIG_CFG80211_DEBUGFS=y +CONFIG_CFG80211_WEXT=y +CONFIG_MAC80211=m +CONFIG_RFKILL=y +CONFIG_RFKILL_INPUT=y CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y # CONFIG_STANDALONE is not set @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:79 @ CONFIG_MTD=y CONFIG_MTD_TESTS=m CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_CFI=y CONFIG_MTD_DATAFLASH=y CONFIG_MTD_RAW_NAND=y CONFIG_MTD_NAND_ATMEL=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:92 @ CONFIG_BLK_DEV_RAM_COUNT=4 CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_ATMEL_SSC=y -CONFIG_EEPROM_AT24=m +CONFIG_EEPROM_AT24=y CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y # CONFIG_BLK_DEV_BSG is not set # CONFIG_SCSI_LOWLEVEL is not set CONFIG_NETDEVICES=y +CONFIG_NET_DSA_MICROCHIP_KSZ_COMMON=m +CONFIG_NET_DSA_MICROCHIP_KSZ9477_I2C=m +CONFIG_NET_DSA_MICROCHIP_KSZ_SPI=m +CONFIG_NET_DSA_MICROCHIP_KSZ8863_SMI=m +CONFIG_NET_DSA_SMSC_LAN9303_I2C=m +CONFIG_NET_DSA_SMSC_LAN9303_MDIO=m +CONFIG_NET_DSA_VITESSE_VSC73XX_SPI=m +CONFIG_NET_DSA_VITESSE_VSC73XX_PLATFORM=m # CONFIG_NET_VENDOR_BROADCOM is not set CONFIG_MACB=y CONFIG_DM9000=y # CONFIG_NET_VENDOR_FARADAY is not set # CONFIG_NET_VENDOR_INTEL is not set # CONFIG_NET_VENDOR_MARVELL is not set -# CONFIG_NET_VENDOR_MICREL is not set +CONFIG_KS8842=m +CONFIG_KS8851=m +CONFIG_KS8851_MLL=m +CONFIG_ENC28J60=m +CONFIG_ENCX24J600=m +# CONFIG_NET_VENDOR_MICROSOFT is not set +# CONFIG_NET_VENDOR_NI is not set # CONFIG_NET_VENDOR_NATSEMI is not set # CONFIG_NET_VENDOR_SEEQ is not set -# CONFIG_NET_VENDOR_SMSC is not set +CONFIG_SMC91X=m +CONFIG_SMC911X=m +CONFIG_SMSC911X=m # CONFIG_NET_VENDOR_STMICRO is not set CONFIG_DAVICOM_PHY=y CONFIG_MICREL_PHY=y +CONFIG_MICROCHIP_T1_PHY=m +CONFIG_MICROSEMI_PHY=m +CONFIG_SMSC_PHY=m CONFIG_CAN_AT91=y -CONFIG_LIBERTAS=m -CONFIG_LIBERTAS_SDIO=m -CONFIG_LIBERTAS_SPI=m +CONFIG_CAN_M_CAN=y +CONFIG_CAN_M_CAN_PLATFORM=y +CONFIG_MDIO_MSCC_MIIM=m +CONFIG_USB_LAN78XX=m CONFIG_MWIFIEX=m CONFIG_MWIFIEX_SDIO=m CONFIG_MWIFIEX_USB=m +CONFIG_WILC_SDIO=m +CONFIG_WILC_SPI=m CONFIG_RT2X00=m CONFIG_RT2500USB=m CONFIG_RT73USB=m @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:148 @ CONFIG_RT2800USB_RT55XX=y CONFIG_RT2800USB_UNKNOWN=y CONFIG_RTL8187=m -CONFIG_RTL8192CU=m -# CONFIG_RTLWIFI_DEBUG is not set CONFIG_INPUT_JOYDEV=y CONFIG_INPUT_EVDEV=y # CONFIG_KEYBOARD_ATKBD is not set @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:155 @ CONFIG_KEYBOARD_GPIO=y # CONFIG_INPUT_MOUSE is not set CONFIG_INPUT_TOUCHSCREEN=y -CONFIG_TOUCHSCREEN_ADS7846=y +CONFIG_TOUCHSCREEN_ADS7846=m CONFIG_TOUCHSCREEN_ATMEL_MXT=y # CONFIG_SERIO is not set CONFIG_LEGACY_PTY_COUNT=4 CONFIG_SERIAL_ATMEL=y CONFIG_SERIAL_ATMEL_CONSOLE=y +CONFIG_SERIAL_DEV_BUS=y CONFIG_HW_RANDOM=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_AT91=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:170 @ CONFIG_SPI_ATMEL=y CONFIG_SPI_ATMEL_QUADSPI=y CONFIG_SPI_GPIO=y +CONFIG_SPI_SPIDEV=m CONFIG_PINCTRL_MCP23S08=m CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_PCA953X=m CONFIG_POWER_RESET=y CONFIG_POWER_SUPPLY=y -# CONFIG_HWMON is not set +CONFIG_HWMON=m +CONFIG_SENSORS_LM75=m CONFIG_WATCHDOG=y CONFIG_AT91SAM9X_WATCHDOG=y CONFIG_SAMA5D4_WATCHDOG=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:185 @ CONFIG_MFD_ATMEL_HLCDC=y CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_MCP16502=y CONFIG_MEDIA_SUPPORT=y CONFIG_MEDIA_SUPPORT_FILTER=y # CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:195 @ CONFIG_USB_VIDEO_CLASS=m CONFIG_V4L_PLATFORM_DRIVERS=y CONFIG_VIDEO_ATMEL_ISI=y +CONFIG_DWC_MIPI_CSI2_HOST=m +CONFIG_DWC_MIPI_DPHY_GEN3=m +CONFIG_VIDEO_MICROCHIP_XISC=y +CONFIG_VIDEO_MICROCHIP_CSI2DC=y +CONFIG_VIDEO_IMX219=m CONFIG_VIDEO_MT9V032=m CONFIG_VIDEO_OV2640=m CONFIG_VIDEO_OV7740=m CONFIG_DRM=y +CONFIG_DRM_FBDEV_EMULATION=y CONFIG_DRM_ATMEL_HLCDC=y +CONFIG_DRM_MICROCHIP_DW_MIPI_DSI=y CONFIG_DRM_PANEL_SIMPLE=y -CONFIG_DRM_PANEL_EDP=y +CONFIG_DRM_PANEL_HIMAX_HX8394=y +CONFIG_DRM_MICROCHIP_LVDS_SERIALIZER=y CONFIG_FB_ATMEL=y CONFIG_BACKLIGHT_ATMEL_LCDC=y CONFIG_BACKLIGHT_PWM=y -CONFIG_FRAMEBUFFER_CONSOLE=y -CONFIG_LOGO=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +# CONFIG_FRAMEBUFFER_CONSOLE is not set CONFIG_SOUND=y CONFIG_SND=y +CONFIG_SND_USB_AUDIO=y CONFIG_SND_SOC=y CONFIG_SND_ATMEL_SOC=y CONFIG_SND_AT91_SOC_SAM9G20_WM8731=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:237 @ CONFIG_USB_SERIAL_FTDI_SIO=y CONFIG_USB_SERIAL_PL2303=y CONFIG_USB_GADGET=y -CONFIG_USB_AT91=y -CONFIG_USB_ATMEL_USBA=y -CONFIG_USB_G_SERIAL=y +CONFIG_USB_ATMEL_USBA=m +CONFIG_USB_CONFIGFS=m +CONFIG_USB_CONFIGFS_ACM=y +CONFIG_USB_CONFIGFS_RNDIS=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +CONFIG_USB_ZERO=m +CONFIG_USB_ETH=m +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_G_SERIAL=m CONFIG_MMC=y +CONFIG_PWRSEQ_SD8787=m CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y CONFIG_MMC_SDHCI_OF_AT91=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:260 @ CONFIG_LEDS_TRIGGERS=y CONFIG_LEDS_TRIGGER_TIMER=y CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_CPU=y CONFIG_LEDS_TRIGGER_GPIO=y CONFIG_RTC_CLASS=y -CONFIG_RTC_DRV_RV3029C2=y CONFIG_RTC_DRV_AT91RM9200=y CONFIG_RTC_DRV_AT91SAM9=y CONFIG_DMADEVICES=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:272 @ CONFIG_IIO=y CONFIG_AT91_ADC=y CONFIG_AT91_SAMA5D2_ADC=y +CONFIG_PAC1934=m CONFIG_PWM=y CONFIG_PWM_ATMEL=y CONFIG_PWM_ATMEL_HLCDC_PWM=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:282 @ CONFIG_AUTOFS_FS=m CONFIG_VFAT_FS=y CONFIG_TMPFS=y +CONFIG_CONFIGFS_FS=y CONFIG_UBIFS_FS=y CONFIG_UBIFS_FS_ADVANCED_COMPR=y CONFIG_NFS_FS=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:292 @ CONFIG_NLS_ISO8859_1=y CONFIG_NLS_UTF8=y CONFIG_CRYPTO_DES=y +CONFIG_CRYPTO_ARC4=y CONFIG_CRYPTO_CBC=y CONFIG_CRYPTO_CFB=y CONFIG_CRYPTO_OFB=y CONFIG_CRYPTO_XTS=y -CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_CCM=y +CONFIG_CRYPTO_GCM=y CONFIG_CRYPTO_SHA1=y -CONFIG_CRYPTO_SHA512=y CONFIG_CRYPTO_USER_API_HASH=m CONFIG_CRYPTO_USER_API_SKCIPHER=m CONFIG_CRYPTO_DEV_ATMEL_AES=y CONFIG_CRYPTO_DEV_ATMEL_TDES=y CONFIG_CRYPTO_DEV_ATMEL_SHA=y CONFIG_CRC_CCITT=y -CONFIG_FONTS=y -CONFIG_FONT_8x8=y -CONFIG_FONT_ACORN_8x8=y -CONFIG_FONT_MINI_4x6=y -# CONFIG_DEBUG_BUGVERBOSE is not set +CONFIG_DMA_CMA=y +CONFIG_CMA_SIZE_MBYTES=0 CONFIG_STRIP_ASM_SYMS=y CONFIG_DEBUG_FS=y # CONFIG_SCHED_DEBUG is not set diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/bcm2835_defconfig linux-microchip/arch/arm/configs/bcm2835_defconfig --- linux-6.6.51/arch/arm/configs/bcm2835_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/bcm2835_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:21 @ CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y CONFIG_CC_OPTIMIZE_FOR_SIZE=y -CONFIG_KALLSYMS_ALL=y CONFIG_EXPERT=y +CONFIG_KALLSYMS_ALL=y CONFIG_PROFILING=y CONFIG_CC_STACKPROTECTOR_REGULAR=y +CONFIG_CRASH_DUMP=y CONFIG_ARCH_MULTI_V6=y CONFIG_ARCH_BCM=y CONFIG_ARCH_BCM2835=y CONFIG_AEABI=y CONFIG_SECCOMP=y CONFIG_KEXEC=y -CONFIG_CRASH_DUMP=y CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_STAT=y CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE=y diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/collie_defconfig linux-microchip/arch/arm/configs/collie_defconfig --- linux-6.6.51/arch/arm/configs/collie_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/collie_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:63 @ CONFIG_MCP_UCB1200=y CONFIG_MCP_UCB1200_TS=y CONFIG_FB=y -CONFIG_FB_MODE_HELPERS=y CONFIG_FB_SA1100=y # CONFIG_VGA_CONSOLE is not set +CONFIG_FB_MODE_HELPERS=y CONFIG_FRAMEBUFFER_CONSOLE=y CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y CONFIG_NEW_LEDS=y diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/davinci_all_defconfig linux-microchip/arch/arm/configs/davinci_all_defconfig --- linux-6.6.51/arch/arm/configs/davinci_all_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/davinci_all_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:151 @ CONFIG_DRM_TINYDRM=m CONFIG_TINYDRM_ST7586=m CONFIG_FB=y -CONFIG_FIRMWARE_EDID=y CONFIG_FB_DA8XX=y +CONFIG_FIRMWARE_EDID=y CONFIG_BACKLIGHT_PWM=m CONFIG_BACKLIGHT_GPIO=m CONFIG_FRAMEBUFFER_CONSOLE=y diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/ep93xx_defconfig linux-microchip/arch/arm/configs/ep93xx_defconfig --- linux-6.6.51/arch/arm/configs/ep93xx_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/ep93xx_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:81 @ CONFIG_WATCHDOG=y CONFIG_EP93XX_WATCHDOG=y CONFIG_FB=y -CONFIG_FB_MODE_HELPERS=y CONFIG_FB_EP93XX=y +CONFIG_FB_MODE_HELPERS=y CONFIG_LOGO=y CONFIG_USB=y CONFIG_USB_DYNAMIC_MINORS=y diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/gemini_defconfig linux-microchip/arch/arm/configs/gemini_defconfig --- linux-6.6.51/arch/arm/configs/gemini_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/gemini_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:10 @ CONFIG_USER_NS=y CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y +CONFIG_KEXEC=y CONFIG_ARCH_MULTI_V4=y # CONFIG_ARCH_MULTI_V7 is not set CONFIG_ARCH_GEMINI=y CONFIG_AEABI=y CONFIG_HIGHMEM=y CONFIG_CMDLINE="console=ttyS0,115200n8" -CONFIG_KEXEC=y CONFIG_PM=y CONFIG_PARTITION_ADVANCED=y CONFIG_BINFMT_MISC=y diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/jornada720_defconfig linux-microchip/arch/arm/configs/jornada720_defconfig --- linux-6.6.51/arch/arm/configs/jornada720_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/jornada720_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2 @ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 -CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_ARCH_MULTI_V4=y # CONFIG_ARCH_MULTI_V7 is not set CONFIG_ARCH_SA1100=y diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/lpc32xx_defconfig linux-microchip/arch/arm/configs/lpc32xx_defconfig --- linux-6.6.51/arch/arm/configs/lpc32xx_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/lpc32xx_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:8 @ CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=16 -CONFIG_SYSFS_DEPRECATED=y -CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_EXPERT=y diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/mmp2_defconfig linux-microchip/arch/arm/configs/mmp2_defconfig --- linux-6.6.51/arch/arm/configs/mmp2_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/mmp2_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:5 @ CONFIG_HIGH_RES_TIMERS=y CONFIG_PREEMPT=y CONFIG_LOG_BUF_SHIFT=14 -CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_BLK_DEV_BSG is not set CONFIG_ARCH_MMP=y CONFIG_AEABI=y diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/moxart_defconfig linux-microchip/arch/arm/configs/moxart_defconfig --- linux-6.6.51/arch/arm/configs/moxart_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/moxart_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:7 @ CONFIG_PREEMPT=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y +CONFIG_EXPERT=y # CONFIG_ELF_CORE is not set # CONFIG_BASE_FULL is not set # CONFIG_SIGNALFD is not set # CONFIG_TIMERFD is not set # CONFIG_EVENTFD is not set # CONFIG_AIO is not set -CONFIG_EXPERT=y # CONFIG_BLK_DEV_BSG is not set CONFIG_ARCH_MULTI_V4=y # CONFIG_ARCH_MULTI_V7 is not set @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:129 @ CONFIG_KGDB=y CONFIG_DEBUG_PAGEALLOC=y # CONFIG_SLUB_DEBUG is not set -CONFIG_DEBUG_OBJECTS=y CONFIG_DEBUG_KMEMLEAK=y +CONFIG_DEBUG_OBJECTS=y CONFIG_DEBUG_STACK_USAGE=y CONFIG_DEBUG_MEMORY_INIT=y CONFIG_DEBUG_SHIRQ=y diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/multi_v4t_defconfig linux-microchip/arch/arm/configs/multi_v4t_defconfig --- linux-6.6.51/arch/arm/configs/multi_v4t_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/multi_v4t_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:8 @ CONFIG_EXPERT=y CONFIG_ARCH_MULTI_V4T=y # CONFIG_ARCH_MULTI_V7 is not set +CONFIG_ARCH_NSPIRE=y CONFIG_ARCH_AT91=y CONFIG_SOC_AT91RM9200=y CONFIG_ARCH_CLPS711X=y CONFIG_ARCH_MXC=y CONFIG_SOC_IMX1=y -CONFIG_ARCH_NSPIRE=y CONFIG_ARCH_INTEGRATOR=y CONFIG_ARCH_INTEGRATOR_AP=y CONFIG_INTEGRATOR_IMPD1=y diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/multi_v5_defconfig linux-microchip/arch/arm/configs/multi_v5_defconfig --- linux-6.6.51/arch/arm/configs/multi_v5_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/multi_v5_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:65 @ CONFIG_NET_PKTGEN=m CONFIG_CFG80211=y CONFIG_MAC80211=y -CONFIG_PCI_MVEBU=y CONFIG_PCI_VERSATILE=y +CONFIG_PCI_MVEBU=y CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y CONFIG_IMX_WEIM=y diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/multi_v7_defconfig linux-microchip/arch/arm/configs/multi_v7_defconfig --- linux-6.6.51/arch/arm/configs/multi_v7_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/multi_v7_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:8 @ CONFIG_BLK_DEV_INITRD=y CONFIG_EXPERT=y CONFIG_PERF_EVENTS=y +CONFIG_KEXEC=y CONFIG_ARCH_VIRT=y CONFIG_ARCH_AIROHA=y +CONFIG_ARCH_SUNPLUS=y +CONFIG_ARCH_UNIPHIER=y CONFIG_ARCH_ACTIONS=y CONFIG_ARCH_ALPINE=y CONFIG_ARCH_ARTPEC=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:102 @ CONFIG_MACH_SPEAR1340=y CONFIG_ARCH_STI=y CONFIG_ARCH_STM32=y -CONFIG_ARCH_SUNPLUS=y CONFIG_ARCH_SUNXI=y CONFIG_ARCH_TEGRA=y -CONFIG_ARCH_UNIPHIER=y CONFIG_ARCH_U8500=y CONFIG_ARCH_VEXPRESS=y CONFIG_ARCH_VEXPRESS_TC2_PM=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:113 @ CONFIG_NR_CPUS=16 CONFIG_ARM_APPENDED_DTB=y CONFIG_ARM_ATAG_DTB_COMPAT=y -CONFIG_KEXEC=y CONFIG_EFI=y CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_STAT=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:182 @ CONFIG_PCIEPORTBUS=y CONFIG_PCI_MVEBU=y CONFIG_PCI_TEGRA=y -CONFIG_PCI_RCAR_GEN2=y CONFIG_PCIE_RCAR_HOST=y -CONFIG_PCI_DRA7XX_EP=y +CONFIG_PCI_RCAR_GEN2=y CONFIG_PCI_LAYERSCAPE=y +CONFIG_PCI_DRA7XX_EP=y CONFIG_PCI_ENDPOINT=y CONFIG_PCI_ENDPOINT_CONFIGFS=y CONFIG_PCI_EPF_TEST=m @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:524 @ CONFIG_BATTERY_ACER_A500=m CONFIG_SENSORS_ARM_SCMI=y CONFIG_SENSORS_ASPEED=m +CONFIG_SENSORS_GXP_FAN_CTRL=m CONFIG_SENSORS_IIO_HWMON=y CONFIG_SENSORS_LAN966X=m CONFIG_SENSORS_LM90=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:533 @ CONFIG_SENSORS_PWM_FAN=m CONFIG_SENSORS_RASPBERRYPI_HWMON=m CONFIG_SENSORS_INA2XX=m -CONFIG_SENSORS_GXP_FAN_CTRL=m CONFIG_CPU_THERMAL=y CONFIG_DEVFREQ_THERMAL=y CONFIG_IMX_THERMAL=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1093 @ CONFIG_ASPEED_LPC_CTRL=m CONFIG_ASPEED_LPC_SNOOP=m CONFIG_ASPEED_P2A_CTRL=m -CONFIG_RASPBERRYPI_POWER=y CONFIG_QCOM_COMMAND_DB=m -CONFIG_QCOM_CPR=y CONFIG_QCOM_GSBI=y CONFIG_QCOM_OCMEM=m CONFIG_QCOM_RMTFS_MEM=m CONFIG_QCOM_RPMH=y -CONFIG_QCOM_RPMHPD=y -CONFIG_QCOM_RPMPD=y CONFIG_QCOM_SMEM=y CONFIG_QCOM_SMD_RPM=y CONFIG_QCOM_SMP2P=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1125 @ CONFIG_ARCH_R9A06G032=y CONFIG_ARCH_SH73A0=y CONFIG_ROCKCHIP_IODOMAIN=y -CONFIG_ROCKCHIP_PM_DOMAINS=y CONFIG_ARCH_TEGRA_2x_SOC=y CONFIG_ARCH_TEGRA_3x_SOC=y CONFIG_ARCH_TEGRA_114_SOC=y CONFIG_ARCH_TEGRA_124_SOC=y +CONFIG_RASPBERRYPI_POWER=y +CONFIG_QCOM_CPR=y +CONFIG_QCOM_RPMHPD=y +CONFIG_QCOM_RPMPD=y +CONFIG_ROCKCHIP_PM_DOMAINS=y CONFIG_ARM_EXYNOS_BUS_DEVFREQ=m CONFIG_ARM_TEGRA_DEVFREQ=m CONFIG_DEVFREQ_EVENT_EXYNOS_NOCP=m @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1239 @ CONFIG_INTERCONNECT_QCOM=y CONFIG_INTERCONNECT_QCOM_MSM8916=y CONFIG_COUNTER=m -CONFIG_STM32_TIMER_CNT=m CONFIG_STM32_LPTIMER_CNT=m +CONFIG_STM32_TIMER_CNT=m CONFIG_EXT4_FS=y CONFIG_AUTOFS_FS=y CONFIG_MSDOS_FS=y diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/mv78xx0_defconfig linux-microchip/arch/arm/configs/mv78xx0_defconfig --- linux-6.6.51/arch/arm/configs/mv78xx0_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/mv78xx0_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:6 @ CONFIG_HIGH_RES_TIMERS=y CONFIG_PREEMPT=y CONFIG_LOG_BUF_SHIFT=14 -CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y CONFIG_PROFILING=y diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/omap1_defconfig linux-microchip/arch/arm/configs/omap1_defconfig --- linux-6.6.51/arch/arm/configs/omap1_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/omap1_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:135 @ CONFIG_WATCHDOG_NOWAYOUT=y CONFIG_OMAP_WATCHDOG=y CONFIG_FB=y -CONFIG_FIRMWARE_EDID=y -CONFIG_FB_MODE_HELPERS=y CONFIG_FB_VIRTUAL=y CONFIG_FB_OMAP=y CONFIG_FB_OMAP_LCDC_EXTERNAL=y CONFIG_FB_OMAP_LCDC_HWA742=y CONFIG_FB_OMAP_MANUAL_UPDATE=y CONFIG_FB_OMAP_LCD_MIPID=y +CONFIG_FIRMWARE_EDID=y +CONFIG_FB_MODE_HELPERS=y CONFIG_LCD_CLASS_DEVICE=y CONFIG_FRAMEBUFFER_CONSOLE=y CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/pxa168_defconfig linux-microchip/arch/arm/configs/pxa168_defconfig --- linux-6.6.51/arch/arm/configs/pxa168_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/pxa168_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1 @ CONFIG_SYSVIPC=y -CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_BLK_DEV_BSG is not set CONFIG_NO_HZ_IDLE=y CONFIG_HIGH_RES_TIMERS=y diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/pxa3xx_defconfig linux-microchip/arch/arm/configs/pxa3xx_defconfig --- linux-6.6.51/arch/arm/configs/pxa3xx_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/pxa3xx_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:3 @ CONFIG_SYSVIPC=y CONFIG_PREEMPT=y CONFIG_LOG_BUF_SHIFT=18 -CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set CONFIG_KALLSYMS_ALL=y # CONFIG_BLK_DEV_BSG is not set diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/pxa910_defconfig linux-microchip/arch/arm/configs/pxa910_defconfig --- linux-6.6.51/arch/arm/configs/pxa910_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/pxa910_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:6 @ CONFIG_HIGH_RES_TIMERS=y CONFIG_PREEMPT=y CONFIG_LOG_BUF_SHIFT=14 -CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y CONFIG_ARCH_MMP=y CONFIG_CMDLINE="root=/dev/nfs rootfstype=nfs nfsroot=192.168.2.100:/nfsroot/ ip=192.168.2.101:192.168.2.100::255.255.255.0::eth0:on console=ttyS0,115200 mem=128M earlyprintk" diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/pxa_defconfig linux-microchip/arch/arm/configs/pxa_defconfig --- linux-6.6.51/arch/arm/configs/pxa_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/pxa_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:13 @ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=13 CONFIG_BLK_DEV_INITRD=y -CONFIG_KALLSYMS_ALL=y CONFIG_EXPERT=y +CONFIG_KALLSYMS_ALL=y CONFIG_PROFILING=y +CONFIG_KEXEC=y # CONFIG_ARCH_MULTI_V7 is not set CONFIG_ARCH_PXA=y CONFIG_ARCH_GUMSTIX=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:26 @ CONFIG_AEABI=y CONFIG_ARCH_FORCE_MAX_ORDER=8 CONFIG_CMDLINE="root=/dev/ram0 ro" -CONFIG_KEXEC=y CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_STAT=y CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:384 @ CONFIG_VIDEO_PXA27x=m CONFIG_DRM=m CONFIG_FB=y -CONFIG_FIRMWARE_EDID=y -CONFIG_FB_TILEBLITTING=y CONFIG_FB_PXA=y CONFIG_FB_PXA_OVERLAY=y CONFIG_FB_PXA_PARAMETERS=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:395 @ CONFIG_LCD_PLATFORM=m CONFIG_BACKLIGHT_PWM=m CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FIRMWARE_EDID=y +CONFIG_FB_TILEBLITTING=y CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y CONFIG_LOGO=y CONFIG_SOUND=m diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/qcom_defconfig linux-microchip/arch/arm/configs/qcom_defconfig --- linux-6.6.51/arch/arm/configs/qcom_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/qcom_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:9 @ CONFIG_IKCONFIG_PROC=y CONFIG_CGROUPS=y CONFIG_BLK_DEV_INITRD=y -CONFIG_KALLSYMS_ALL=y CONFIG_EXPERT=y +CONFIG_KALLSYMS_ALL=y CONFIG_PROFILING=y CONFIG_ARCH_QCOM=y CONFIG_ARCH_MSM8X60=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:142 @ CONFIG_PINCTRL_MSM8X74=y CONFIG_PINCTRL_MSM8909=y CONFIG_PINCTRL_MSM8916=y -CONFIG_PINCTRL_QCOM_SPMI_PMIC=y -CONFIG_PINCTRL_QCOM_SSBI_PMIC=y CONFIG_GPIOLIB=y CONFIG_PINCTRL_SDX55=y CONFIG_PINCTRL_SDX65=y +CONFIG_PINCTRL_QCOM_SPMI_PMIC=y +CONFIG_PINCTRL_QCOM_SSBI_PMIC=y CONFIG_GPIO_SYSFS=y CONFIG_POWER_RESET=y CONFIG_POWER_RESET_MSM=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:262 @ CONFIG_QCOM_PM=y CONFIG_QCOM_RMTFS_MEM=y CONFIG_QCOM_RPMH=y -CONFIG_QCOM_RPMHPD=y -CONFIG_QCOM_RPMPD=y CONFIG_QCOM_SMEM=y CONFIG_QCOM_SMD_RPM=y CONFIG_QCOM_SMP2P=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:269 @ CONFIG_QCOM_SOCINFO=y CONFIG_QCOM_STATS=y CONFIG_QCOM_WCNSS_CTRL=y +CONFIG_QCOM_RPMHPD=y +CONFIG_QCOM_RPMPD=y CONFIG_EXTCON_QCOM_SPMI_MISC=y CONFIG_IIO=y CONFIG_IIO_BUFFER_CB=y diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/realview_defconfig linux-microchip/arch/arm/configs/realview_defconfig --- linux-6.6.51/arch/arm/configs/realview_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/realview_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:61 @ # CONFIG_HWMON is not set CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_AUXDISPLAY=y +CONFIG_ARM_CHARLCD=y CONFIG_DRM=y CONFIG_DRM_PANEL_SIMPLE=y CONFIG_DRM_PANEL_EDP=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:90 @ CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_DS1307=y CONFIG_RTC_DRV_PL031=y -CONFIG_AUXDISPLAY=y -CONFIG_ARM_CHARLCD=y CONFIG_VFAT_FS=y CONFIG_TMPFS=y CONFIG_CRAMFS=y diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/rpc_defconfig linux-microchip/arch/arm/configs/rpc_defconfig --- linux-6.6.51/arch/arm/configs/rpc_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/rpc_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:5 @ CONFIG_SYSVIPC=y CONFIG_LOG_BUF_SHIFT=14 CONFIG_BLK_DEV_INITRD=y -CONFIG_MODULES=y -CONFIG_MODULE_UNLOAD=y CONFIG_ARCH_MULTI_V4=y # CONFIG_ARCH_MULTI_V7 is not set CONFIG_ARCH_RPC=y CONFIG_CPU_SA110=y CONFIG_FPE_NWFPE=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y CONFIG_PARTITION_ADVANCED=y CONFIG_BSD_DISKLABEL=y CONFIG_NET=y diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/s3c6400_defconfig linux-microchip/arch/arm/configs/s3c6400_defconfig --- linux-6.6.51/arch/arm/configs/s3c6400_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/s3c6400_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1 @ -CONFIG_SYSFS_DEPRECATED=y -CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y CONFIG_KALLSYMS_ALL=y CONFIG_ARCH_MULTI_V6=y diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/s5pv210_defconfig linux-microchip/arch/arm/configs/s5pv210_defconfig --- linux-6.6.51/arch/arm/configs/s5pv210_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/s5pv210_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:6 @ CONFIG_HIGH_RES_TIMERS=y CONFIG_PREEMPT=y CONFIG_CGROUPS=y -CONFIG_SYSFS_DEPRECATED=y -CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y CONFIG_KALLSYMS_ALL=y CONFIG_ARCH_S5PV210=y diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/sama5_defconfig linux-microchip/arch/arm/configs/sama5_defconfig --- linux-6.6.51/arch/arm/configs/sama5_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/sama5_defconfig 2024-11-26 14:04:18.560303308 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ # CONFIG_LOCALVERSION_AUTO is not set CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y CONFIG_NO_HZ_IDLE=y CONFIG_HIGH_RES_TIMERS=y -CONFIG_LOG_BUF_SHIFT=14 +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=16 CONFIG_CGROUPS=y +CONFIG_MEMCG=y +CONFIG_CGROUP_SCHED=y +CONFIG_CFS_BANDWIDTH=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_NAMESPACES=y +CONFIG_USER_NS=y CONFIG_BLK_DEV_INITRD=y +# CONFIG_KALLSYMS is not set CONFIG_EXPERT=y +CONFIG_KEXEC=y CONFIG_ARCH_AT91=y CONFIG_SOC_SAMA5D2=y CONFIG_SOC_SAMA5D3=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:31 @ CONFIG_UACCESS_WITH_MEMCPY=y # CONFIG_ATAGS is not set CONFIG_CMDLINE="console=ttyS0,115200 initrd=0x21100000,25165824 root=/dev/ram0 rw" -CONFIG_KEXEC=y CONFIG_VFP=y CONFIG_NEON=y CONFIG_KERNEL_MODE_NEON=y CONFIG_PM_DEBUG=y CONFIG_PM_ADVANCED_DEBUG=y +CONFIG_JUMP_LABEL=y CONFIG_MODULES=y CONFIG_MODULE_FORCE_LOAD=y CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set # CONFIG_SWAP is not set +CONFIG_CMA=y +CONFIG_CMA_DEBUGFS=y CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:52 @ CONFIG_IP_MULTICAST=y CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y -CONFIG_IP_PNP_BOOTP=y -CONFIG_IP_PNP_RARP=y # CONFIG_INET_DIAG is not set CONFIG_IPV6_SIT_6RD=y CONFIG_BRIDGE=m CONFIG_BRIDGE_VLAN_FILTERING=y CONFIG_NET_DSA=m +CONFIG_NET_DSA_TAG_OCELOT=m +CONFIG_NET_DSA_TAG_OCELOT_8021Q=m CONFIG_VLAN_8021Q=m CONFIG_CAN=y -CONFIG_CFG80211=y -CONFIG_MAC80211=y +CONFIG_BT=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=y +CONFIG_BT_HCIBTUSB=y +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_3WIRE=y +CONFIG_BT_HCIVHCI=y +CONFIG_CFG80211=m +# CONFIG_CFG80211_DEFAULT_PS is not set +CONFIG_CFG80211_DEBUGFS=y +CONFIG_CFG80211_WEXT=y +CONFIG_MAC80211=m CONFIG_MAC80211_LEDS=y +CONFIG_RFKILL=y +CONFIG_RFKILL_INPUT=y +CONFIG_PCCARD=y CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y # CONFIG_STANDALONE is not set @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:107 @ # CONFIG_BLK_DEV_BSG is not set # CONFIG_SCSI_LOWLEVEL is not set CONFIG_NETDEVICES=y -CONFIG_NET_DSA_MICROCHIP_KSZ9477=m -CONFIG_NET_DSA_MICROCHIP_KSZ9477_SPI=m +CONFIG_NET_DSA_MICROCHIP_KSZ_COMMON=m +CONFIG_NET_DSA_MICROCHIP_KSZ9477_I2C=m +CONFIG_NET_DSA_MICROCHIP_KSZ_SPI=m +CONFIG_NET_DSA_MICROCHIP_KSZ8863_SMI=m +CONFIG_NET_DSA_SMSC_LAN9303_I2C=m +CONFIG_NET_DSA_SMSC_LAN9303_MDIO=m +CONFIG_NET_DSA_VITESSE_VSC73XX_SPI=m +CONFIG_NET_DSA_VITESSE_VSC73XX_PLATFORM=m # CONFIG_NET_VENDOR_BROADCOM is not set CONFIG_MACB=y +# CONFIG_NET_VENDOR_CAVIUM is not set # CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_NET_VENDOR_CORTINA is not set +# CONFIG_NET_VENDOR_EZCHIP is not set # CONFIG_NET_VENDOR_FARADAY is not set +# CONFIG_NET_VENDOR_FUJITSU is not set +# CONFIG_NET_VENDOR_GOOGLE is not set +# CONFIG_NET_VENDOR_HISILICON is not set +# CONFIG_NET_VENDOR_HUAWEI is not set # CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_LITEX is not set # CONFIG_NET_VENDOR_MARVELL is not set -# CONFIG_NET_VENDOR_MICREL is not set -# CONFIG_NET_VENDOR_MICROCHIP is not set +# CONFIG_NET_VENDOR_MELLANOX is not set +CONFIG_KS8842=m +CONFIG_KS8851=m +CONFIG_KS8851_MLL=m +CONFIG_ENC28J60=m +CONFIG_ENCX24J600=m +# CONFIG_NET_VENDOR_MICROSOFT is not set +# CONFIG_NET_VENDOR_NI is not set # CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_NETRONOME is not set +# CONFIG_NET_VENDOR_PENSANDO is not set +# CONFIG_NET_VENDOR_QUALCOMM is not set +# CONFIG_NET_VENDOR_RENESAS is not set +# CONFIG_NET_VENDOR_ROCKER is not set +# CONFIG_NET_VENDOR_SAMSUNG is not set # CONFIG_NET_VENDOR_SEEQ is not set -# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_SOLARFLARE is not set +CONFIG_SMC91X=m +CONFIG_SMC911X=m +CONFIG_SMSC911X=m +# CONFIG_NET_VENDOR_SOCIONEXT is not set # CONFIG_NET_VENDOR_STMICRO is not set +# CONFIG_NET_VENDOR_SYNOPSYS is not set +# CONFIG_NET_VENDOR_VIA is not set # CONFIG_NET_VENDOR_WIZNET is not set +# CONFIG_NET_VENDOR_XILINX is not set +# CONFIG_NET_VENDOR_XIRCOM is not set CONFIG_MICREL_PHY=y +CONFIG_MICROCHIP_T1_PHY=m +CONFIG_MICROSEMI_PHY=m +CONFIG_SMSC_PHY=m CONFIG_CAN_AT91=y CONFIG_CAN_M_CAN=y CONFIG_CAN_M_CAN_PLATFORM=y +CONFIG_MDIO_MSCC_MIIM=m CONFIG_USB_LAN78XX=m CONFIG_LIBERTAS_THINFIRM=m CONFIG_LIBERTAS_THINFIRM_USB=m CONFIG_MWIFIEX=m CONFIG_MWIFIEX_SDIO=m CONFIG_MWIFIEX_USB=m +CONFIG_WILC_SDIO=m +CONFIG_WILC_SPI=m CONFIG_RT2X00=m CONFIG_RT2500USB=m CONFIG_RT73USB=m @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:188 @ CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_ADC=y CONFIG_TOUCHSCREEN_ATMEL_MXT=y +CONFIG_INPUT_MISC=y +CONFIG_INPUT_ATMEL_PTC=m # CONFIG_SERIO is not set CONFIG_LEGACY_PTY_COUNT=4 CONFIG_SERIAL_ATMEL=y CONFIG_SERIAL_ATMEL_CONSOLE=y +CONFIG_SERIAL_DEV_BUS=y CONFIG_HW_RANDOM=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_AT91=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:203 @ CONFIG_SPI_ATMEL=y CONFIG_SPI_ATMEL_QUADSPI=y CONFIG_SPI_GPIO=y +CONFIG_SPI_SPIDEV=m CONFIG_GPIO_SYSFS=y CONFIG_GPIO_SAMA5D2_PIOBU=y +CONFIG_GPIO_PCA953X=m CONFIG_POWER_RESET=y CONFIG_POWER_SUPPLY=y CONFIG_BATTERY_ACT8945A=y -CONFIG_SENSORS_JC42=m +CONFIG_HWMON=m +CONFIG_SENSORS_LM75=m CONFIG_WATCHDOG=y CONFIG_AT91SAM9X_WATCHDOG=y CONFIG_SAMA5D4_WATCHDOG=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:220 @ CONFIG_MFD_ATMEL_HLCDC=y CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_VIRTUAL_CONSUMER=y CONFIG_REGULATOR_ACT8865=y CONFIG_REGULATOR_ACT8945A=y CONFIG_REGULATOR_MCP16502=y -CONFIG_REGULATOR_PWM=m CONFIG_MEDIA_SUPPORT=y CONFIG_MEDIA_SUPPORT_FILTER=y # CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:240 @ CONFIG_VIDEO_OV7670=m CONFIG_VIDEO_OV7740=m CONFIG_DRM=y +CONFIG_DRM_FBDEV_EMULATION=y CONFIG_DRM_ATMEL_HLCDC=y CONFIG_DRM_PANEL_SIMPLE=y CONFIG_DRM_PANEL_EDP=y +CONFIG_DRM_SII902X=y CONFIG_LCD_CLASS_DEVICE=y CONFIG_BACKLIGHT_CLASS_DEVICE=y CONFIG_BACKLIGHT_PWM=y -CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE is not set CONFIG_SOUND=y CONFIG_SND=y +CONFIG_SND_USB_AUDIO=y CONFIG_SND_SOC=y CONFIG_SND_ATMEL_SOC=y CONFIG_SND_ATMEL_SOC_WM8904=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:272 @ CONFIG_USB_SERIAL_FTDI_SIO=y CONFIG_USB_SERIAL_PL2303=y CONFIG_USB_GADGET=y -CONFIG_USB_ATMEL_USBA=y -CONFIG_USB_G_SERIAL=y +CONFIG_USB_ATMEL_USBA=m +CONFIG_USB_CONFIGFS=m +CONFIG_USB_CONFIGFS_ACM=y +CONFIG_USB_CONFIGFS_RNDIS=y +CONFIG_USB_CONFIGFS_MASS_STORAGE=y +CONFIG_USB_ZERO=m +CONFIG_USB_ETH=m +CONFIG_USB_MASS_STORAGE=m +CONFIG_USB_G_SERIAL=m CONFIG_MMC=y +CONFIG_PWRSEQ_SD8787=m CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y CONFIG_MMC_SDHCI_OF_AT91=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:291 @ CONFIG_LEDS_CLASS=y CONFIG_LEDS_GPIO=y CONFIG_LEDS_PWM=y +CONFIG_LEDS_REGULATOR=m +CONFIG_LEDS_BLINKM=m CONFIG_LEDS_TRIGGER_TIMER=y CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_CPU=y CONFIG_LEDS_TRIGGER_GPIO=y CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_AT91RM9200=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:304 @ CONFIG_AT_XDMAC=y CONFIG_STAGING=y CONFIG_STAGING_MEDIA=y -CONFIG_VIDEO_HANTRO=m # CONFIG_IOMMU_SUPPORT is not set CONFIG_IIO=y CONFIG_AT91_ADC=y CONFIG_AT91_SAMA5D2_ADC=y -CONFIG_ENVELOPE_DETECTOR=m -CONFIG_DPOT_DAC=m -CONFIG_MCP4531=m +CONFIG_PAC1934=m CONFIG_PWM=y CONFIG_PWM_ATMEL=y CONFIG_PWM_ATMEL_HLCDC_PWM=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:316 @ CONFIG_EXT4_FS=y CONFIG_FANOTIFY=y CONFIG_AUTOFS_FS=m +CONFIG_OVERLAY_FS=y CONFIG_VFAT_FS=y CONFIG_TMPFS=y +CONFIG_CONFIGFS_FS=y CONFIG_UBIFS_FS=y CONFIG_UBIFS_FS_ADVANCED_COMPR=y CONFIG_NFS_FS=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:329 @ CONFIG_NLS_ISO8859_1=y CONFIG_NLS_UTF8=y CONFIG_CRYPTO_DES=y +CONFIG_CRYPTO_ARC4=y CONFIG_CRYPTO_CBC=y CONFIG_CRYPTO_CFB=y CONFIG_CRYPTO_OFB=y CONFIG_CRYPTO_XTS=y +CONFIG_CRYPTO_CCM=y +CONFIG_CRYPTO_GCM=y CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_SHA1=y CONFIG_CRYPTO_SHA512=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:344 @ CONFIG_CRYPTO_DEV_ATMEL_AES=y CONFIG_CRYPTO_DEV_ATMEL_TDES=y CONFIG_CRYPTO_DEV_ATMEL_SHA=y +CONFIG_CRC7=y +CONFIG_DMA_CMA=y CONFIG_STRIP_ASM_SYMS=y CONFIG_DEBUG_FS=y CONFIG_DEBUG_MEMORY_INIT=y diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/sama7_defconfig linux-microchip/arch/arm/configs/sama7_defconfig --- linux-6.6.51/arch/arm/configs/sama7_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/sama7_defconfig 2024-11-26 14:04:18.560303308 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:5 @ CONFIG_SYSVIPC=y CONFIG_NO_HZ_IDLE=y CONFIG_HIGH_RES_TIMERS=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=16 CONFIG_CGROUPS=y CONFIG_CGROUP_DEBUG=y CONFIG_NAMESPACES=y -CONFIG_SYSFS_DEPRECATED=y -CONFIG_SYSFS_DEPRECATED_V2=y CONFIG_BLK_DEV_INITRD=y +CONFIG_EXPERT=y # CONFIG_FHANDLE is not set # CONFIG_IO_URING is not set -CONFIG_KALLSYMS_ALL=y -CONFIG_EXPERT=y +# CONFIG_KALLSYMS is not set CONFIG_ARCH_AT91=y CONFIG_SOC_SAMA7G5=y CONFIG_ATMEL_CLOCKSOURCE_TCB=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:46 @ # CONFIG_SWAP is not set # CONFIG_COMPACTION is not set CONFIG_CMA=y +CONFIG_CMA_DEBUGFS=y # CONFIG_VM_EVENT_COUNTERS is not set CONFIG_NET=y CONFIG_PACKET=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:60 @ CONFIG_BRIDGE=m CONFIG_BRIDGE_VLAN_FILTERING=y CONFIG_NET_DSA=m +CONFIG_NET_DSA_TAG_OCELOT=m +CONFIG_NET_DSA_TAG_OCELOT_8021Q=m CONFIG_VLAN_8021Q=m CONFIG_CAN=y CONFIG_BT=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:96 @ CONFIG_MTD_NAND_ATMEL=y # CONFIG_MTD_NAND_ECC_SW_HAMMING is not set CONFIG_MTD_SPI_NOR=y +# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set CONFIG_MTD_UBI=y CONFIG_MTD_UBI_FASTMAP=y CONFIG_MTD_UBI_BLOCK=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:104 @ CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_COUNT=1 CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_ATMEL_SSC=y CONFIG_EEPROM_AT24=y CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y # CONFIG_BLK_DEV_BSG is not set CONFIG_NETDEVICES=y +CONFIG_NET_DSA_MICROCHIP_KSZ_COMMON=m +CONFIG_NET_DSA_MICROCHIP_KSZ9477_I2C=m +CONFIG_NET_DSA_MICROCHIP_KSZ_SPI=m +CONFIG_NET_DSA_MICROCHIP_KSZ8863_SMI=m +CONFIG_NET_DSA_SMSC_LAN9303_I2C=m +CONFIG_NET_DSA_SMSC_LAN9303_MDIO=m +CONFIG_NET_DSA_VITESSE_VSC73XX_SPI=m +CONFIG_NET_DSA_VITESSE_VSC73XX_PLATFORM=m CONFIG_MACB=y +CONFIG_KS8842=m +CONFIG_KS8851=m +CONFIG_KS8851_MLL=m +CONFIG_ENC28J60=m +CONFIG_ENCX24J600=m +CONFIG_SMC91X=m +CONFIG_SMC911X=m +CONFIG_SMSC911X=m CONFIG_MICREL_PHY=y +CONFIG_MICROCHIP_T1_PHY=m +CONFIG_MICROSEMI_PHY=m +CONFIG_SMSC_PHY=m CONFIG_CAN_M_CAN=y CONFIG_CAN_M_CAN_PLATFORM=y +CONFIG_MDIO_MSCC_MIIM=m +CONFIG_USB_LAN78XX=m +CONFIG_MWIFIEX=m +CONFIG_MWIFIEX_SDIO=m +CONFIG_MWIFIEX_USB=m +CONFIG_WILC_SDIO=m +CONFIG_WILC_SPI=m CONFIG_INPUT_EVDEV=y CONFIG_KEYBOARD_GPIO=y # CONFIG_INPUT_MOUSE is not set @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:164 @ CONFIG_THERMAL=y CONFIG_THERMAL_STATISTICS=y CONFIG_CPU_THERMAL=y +CONFIG_CPU_IDLE_THERMAL=y CONFIG_GENERIC_ADC_THERMAL=y CONFIG_WATCHDOG=y CONFIG_SAMA5D4_WATCHDOG=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:177 @ # CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set CONFIG_MEDIA_CAMERA_SUPPORT=y CONFIG_MEDIA_PLATFORM_SUPPORT=y +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_USB_VIDEO_CLASS=m CONFIG_V4L_PLATFORM_DRIVERS=y CONFIG_VIDEO_MICROCHIP_XISC=y CONFIG_VIDEO_MICROCHIP_CSI2DC=y +CONFIG_DWC_MIPI_CSI2_HOST=m +CONFIG_DWC_MIPI_DPHY_GEN3=m CONFIG_VIDEO_IMX219=m CONFIG_VIDEO_IMX274=m CONFIG_VIDEO_OV5647=m CONFIG_SOUND=y CONFIG_SND=y +CONFIG_SND_DYNAMIC_MINORS=y +CONFIG_SND_MAX_CARDS=32 CONFIG_SND_SOC=y CONFIG_SND_ATMEL_SOC=y -CONFIG_SND_SOC_MIKROE_PROTO=m +CONFIG_SND_SOC_MIKROE_PROTO=y +CONFIG_SND_ATMEL_SOC_SSC_DMA=y CONFIG_SND_MCHP_SOC_I2S_MCC=y CONFIG_SND_MCHP_SOC_SPDIFTX=y CONFIG_SND_MCHP_SOC_SPDIFRX=y CONFIG_SND_MCHP_SOC_PDMC=y +CONFIG_SND_MCHP_SOC_ASRC=y +CONFIG_SND_MCHP_SOC_ASRC_CARD=y CONFIG_SND_SOC_DMIC=y CONFIG_SND_SOC_PCM5102A=y CONFIG_SND_SOC_SPDIF=y +CONFIG_SND_SOC_WM8731=y +CONFIG_SND_SOC_WM8731_I2C=y +CONFIG_SND_SOC_WM8731_SPI=y CONFIG_SND_SIMPLE_CARD=y CONFIG_USB=y CONFIG_USB_ANNOUNCE_NEW_DEVICES=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:218 @ CONFIG_USB_GADGET=y CONFIG_U_SERIAL_CONSOLE=y CONFIG_USB_ATMEL_USBA=m -CONFIG_USB_CONFIGFS=y +CONFIG_USB_CONFIGFS=m CONFIG_USB_CONFIGFS_ACM=y +CONFIG_USB_CONFIGFS_RNDIS=y CONFIG_USB_CONFIGFS_MASS_STORAGE=y CONFIG_USB_CONFIGFS_F_UVC=y +CONFIG_USB_ZERO=m +CONFIG_USB_ETH=m +CONFIG_USB_MASS_STORAGE=m CONFIG_USB_G_SERIAL=m CONFIG_MMC=y +CONFIG_PWRSEQ_SD8787=m CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y CONFIG_MMC_SDHCI_OF_AT91=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:247 @ CONFIG_IIO=y CONFIG_IIO_SW_TRIGGER=y CONFIG_AT91_SAMA5D2_ADC=y +CONFIG_PAC1934=m CONFIG_PWM=y CONFIG_PWM_ATMEL=y +CONFIG_PHY_MICROCHIP_SAMA7_USB=y CONFIG_MCHP_EIC=y CONFIG_RESET_CONTROLLER=y CONFIG_NVMEM_MICROCHIP_OTPC=y +CONFIG_POWERCAP=y +CONFIG_IDLE_INJECT=y +CONFIG_DTPM=y CONFIG_EXT2_FS=y CONFIG_EXT3_FS=y CONFIG_FANOTIFY=y @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:286 @ CONFIG_CRYPTO_DEV_ATMEL_SHA=y CONFIG_CRC_CCITT=y CONFIG_CRC_ITU_T=y +CONFIG_CRC7=y CONFIG_DMA_CMA=y CONFIG_CMA_SIZE_MBYTES=32 CONFIG_CMA_ALIGNMENT=9 diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/spitz_defconfig linux-microchip/arch/arm/configs/spitz_defconfig --- linux-6.6.51/arch/arm/configs/spitz_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/spitz_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:5 @ CONFIG_PREEMPT=y CONFIG_BSD_PROCESS_ACCT=y CONFIG_LOG_BUF_SHIFT=14 -CONFIG_SYSFS_DEPRECATED_V2=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set CONFIG_EXPERT=y CONFIG_PROFILING=y diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/stm32_defconfig linux-microchip/arch/arm/configs/stm32_defconfig --- linux-6.6.51/arch/arm/configs/stm32_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/stm32_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:7 @ CONFIG_LOG_BUF_SHIFT=16 CONFIG_BLK_DEV_INITRD=y CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_EXPERT=y # CONFIG_UID16 is not set # CONFIG_BASE_FULL is not set # CONFIG_FUTEX is not set @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:15 @ # CONFIG_SIGNALFD is not set # CONFIG_EVENTFD is not set # CONFIG_AIO is not set -CONFIG_EXPERT=y # CONFIG_BLK_DEV_BSG is not set # CONFIG_MMU is not set CONFIG_ARCH_STM32=y diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/tegra_defconfig linux-microchip/arch/arm/configs/tegra_defconfig --- linux-6.6.51/arch/arm/configs/tegra_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/tegra_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:16 @ CONFIG_NAMESPACES=y CONFIG_USER_NS=y CONFIG_BLK_DEV_INITRD=y -# CONFIG_ELF_CORE is not set CONFIG_EXPERT=y +# CONFIG_ELF_CORE is not set CONFIG_PERF_EVENTS=y +CONFIG_KEXEC=y CONFIG_ARCH_TEGRA=y CONFIG_SMP=y CONFIG_HIGHMEM=y -CONFIG_KEXEC=y CONFIG_CPU_FREQ=y CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_USERSPACE=y diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/configs/vf610m4_defconfig linux-microchip/arch/arm/configs/vf610m4_defconfig --- linux-6.6.51/arch/arm/configs/vf610m4_defconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/configs/vf610m4_defconfig 2024-11-26 14:02:35.754462456 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:7 @ # CONFIG_RD_LZMA is not set # CONFIG_RD_XZ is not set # CONFIG_RD_LZ4 is not set -CONFIG_KALLSYMS_ALL=y CONFIG_EXPERT=y +CONFIG_KALLSYMS_ALL=y # CONFIG_MMU is not set CONFIG_ARCH_MXC=y CONFIG_SOC_VF610=y diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/mach-at91/at91_vdec.h linux-microchip/arch/arm/mach-at91/at91_vdec.h --- linux-6.6.51/arch/arm/mach-at91/at91_vdec.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/arch/arm/mach-at91/at91_vdec.h 2024-11-26 14:02:35.766462671 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +/* + * Video Decoder (VDEC) - System peripherals registers. + * + * Copyright (C) 2009 Hantro Products Oy. + * + * Based on SAMA5D4 datasheet. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef AT91_VDEC_H +#define AT91_VDEC_H + +#define VDEC_IDR 0x00 /* ID Register (read-only) */ +#define VDEC_IDR_BUILD_VER 0xf /* Build Version is 0x02. */ +#define VDEC_IDR_MINOR_VER (0xff << 4) /* Minor Version is 0x88. */ +#define VDEC_IDR_MAJOR_VER (0xf << 12) /* Major Version is 0x08. */ +#define VDEC_IDR_PROD_ID (0xffff << 16) /* Product ID is 0x6731. */ + +#define VDEC_DIR 0x04 /* Decoder Interrupt Register */ +#define VDEC_DIR_DE 1 /* 1: Enable decoder; 0: Disable decoder. */ +#define VDEC_DIR_ID 0x10 /* 1: Disable interrupts for decoder; 0: Enable interrupts. */ +#define VDEC_DIR_ABORT 0x20 +#define VDEC_DIR_ISET 0x100 /* Decoder Interrupt Set. 0: Clears the Decoder Interrupt. */ + +#define VDEC_PPIR 0xF0 /* Post Processor Interrupt Register */ +#define VDEC_PPIR_PPE 1 /* 1: Enable post-processor; 0: Disable post-processor */ +#define VDEC_PPIR_ID 0x10 /* 1: Disable interrupts for post-processor; 0: Enable interrupts. */ +#define VDEC_PPIR_ISET 0x100 /* Post-processor Interrupt Set. 0: Clears the post-processor Interrupt. */ + +#endif diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/mach-at91/generic.h linux-microchip/arch/arm/mach-at91/generic.h --- linux-6.6.51/arch/arm/mach-at91/generic.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/mach-at91/generic.h 2024-11-26 14:02:35.766462671 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:11 @ #ifndef _AT91_GENERIC_H #define _AT91_GENERIC_H -#ifdef CONFIG_PM +#if defined(CONFIG_ATMEL_PM) || defined(CONFIG_ATMEL_SECURE_PM) extern void __init at91rm9200_pm_init(void); extern void __init at91sam9_pm_init(void); extern void __init sam9x60_pm_init(void); +extern void __init sam9x7_pm_init(void); extern void __init sama5_pm_init(void); extern void __init sama5d2_pm_init(void); extern void __init sama7_pm_init(void); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:23 @ static inline void __init at91rm9200_pm_init(void) { } static inline void __init at91sam9_pm_init(void) { } static inline void __init sam9x60_pm_init(void) { } +static inline void __init sam9x7_pm_init(void) { } static inline void __init sama5_pm_init(void) { } static inline void __init sama5d2_pm_init(void) { } static inline void __init sama7_pm_init(void) { } diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/mach-at91/hx170dec.h linux-microchip/arch/arm/mach-at91/hx170dec.h --- linux-6.6.51/arch/arm/mach-at91/hx170dec.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/arch/arm/mach-at91/hx170dec.h 2024-11-26 14:02:35.766462671 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +/* + * Decoder device driver (kernel module headers) + * + * Copyright (C) 2009 Hantro Products Oy. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _HX170DEC_H_ +#define _HX170DEC_H_ + +#include <linux/ioctl.h> +#include <linux/types.h> + +struct core_desc +{ + __u32 id; /* id of the core */ + __u32 *regs; /* pointer to user registers */ + __u32 size; /* size of register space */ +}; + +/* Use 'k' as magic number */ +#define HX170DEC_IOC_MAGIC 'k' +/* + * S means "Set" through a ptr, + * T means "Tell" directly with the argument value + * G means "Get": reply by setting through a pointer + * Q means "Query": response is on the return value + * X means "eXchange": G and S atomically + * H means "sHift": T and Q atomically + */ + +#define HX170DEC_IOCGHWOFFSET _IOR(HX170DEC_IOC_MAGIC, 3, unsigned long *) +#define HX170DEC_IOCGHWIOSIZE _IOR(HX170DEC_IOC_MAGIC, 4, unsigned int *) + +#define HX170DEC_IOC_MC_OFFSETS _IOR(HX170DEC_IOC_MAGIC, 7, unsigned long *) +#define HX170DEC_IOC_MC_CORES _IOR(HX170DEC_IOC_MAGIC, 8, unsigned int *) +#define HX170DEC_IOCS_DEC_PUSH_REG _IOW(HX170DEC_IOC_MAGIC, 9, struct core_desc *) +#define HX170DEC_IOCS_PP_PUSH_REG _IOW(HX170DEC_IOC_MAGIC, 10, struct core_desc *) +#define HX170DEC_IOCH_DEC_RESERVE _IO(HX170DEC_IOC_MAGIC, 11) +#define HX170DEC_IOCT_DEC_RELEASE _IO(HX170DEC_IOC_MAGIC, 12) +#define HX170DEC_IOCQ_PP_RESERVE _IO(HX170DEC_IOC_MAGIC, 13) +#define HX170DEC_IOCT_PP_RELEASE _IO(HX170DEC_IOC_MAGIC, 14) +#define HX170DEC_IOCX_DEC_WAIT _IOWR(HX170DEC_IOC_MAGIC, 15, struct core_desc *) +#define HX170DEC_IOCX_PP_WAIT _IOWR(HX170DEC_IOC_MAGIC, 16, struct core_desc *) +#define HX170DEC_IOCS_DEC_PULL_REG _IOWR(HX170DEC_IOC_MAGIC, 17, struct core_desc *) +#define HX170DEC_IOCS_PP_PULL_REG _IOWR(HX170DEC_IOC_MAGIC, 18, struct core_desc *) + +#define HX170DEC_IOX_ASIC_ID _IOWR(HX170DEC_IOC_MAGIC, 20, __u32 *) + +/* + * Following are not used yet: + * + * #define HX170DEC_PP_INSTANCE _IO(HX170DEC_IOC_MAGIC, 1) + * #define HX170DEC_HW_PERFORMANCE _IO(HX170DEC_IOC_MAGIC, 2) + * #define HX170DEC_IOC_CLI _IO(HX170DEC_IOC_MAGIC, 5) + * #define HX170DEC_IOC_STI _IO(HX170DEC_IOC_MAGIC, 6) + * #define HX170DEC_IOCG_CORE_WAIT _IOR(HX170DEC_IOC_MAGIC, 19, int *) + * #define HX170DEC_DEBUG_STATUS _IO(HX170DEC_IOC_MAGIC, 29) + */ + +#define HX170DEC_IOC_MAXNR 29 + +#endif /* !_HX170DEC_H_ */ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/mach-at91/Kconfig linux-microchip/arch/arm/mach-at91/Kconfig --- linux-6.6.51/arch/arm/mach-at91/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/mach-at91/Kconfig 2024-11-26 14:02:35.766462671 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:81 @ help This enables support for ARMv7 based Microchip LAN966 SoC family. +config AT91_VDEC_G1 + bool "Video Decoder for SAMA5D4" + depends on SOC_SAMA5D4 + help + Select this if you are using the G1 video decoder embedded in SAMA5D4 + SoC family. + config SOC_AT91RM9200 bool "AT91RM9200" depends on ARCH_MULTI_V4T @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:151 @ help Select this if you are using Microchip's SAM9X60 SoC +config SOC_SAM9X7 + bool "SAM9X7" + depends on ARCH_MULTI_V5 + select ATMEL_AIC5_IRQ + select ATMEL_PM if PM + select ATMEL_SDRAMC + select CPU_ARM926T + select HAVE_AT91_USB_CLK + select HAVE_AT91_GENERATED_CLK + select HAVE_AT91_SAM9X60_PLL + select MEMORY + select PINCTRL_AT91 + select SOC_SAM_V4_V5 + select SRAM if PM + help + Select this if you are using Microchip's SAM9X7 SoC + comment "Clocksource driver selection" config ATMEL_CLOCKSOURCE_PIT bool "Periodic Interval Timer (PIT) support" - depends on SOC_AT91SAM9 || SOC_SAM9X60 || SOC_SAMA5 + depends on SOC_AT91SAM9 || SOC_SAM9X60 || SOC_SAM9X7 || SOC_SAMA5 default SOC_AT91SAM9 || SOC_SAMA5 select ATMEL_PIT help @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:182 @ config ATMEL_CLOCKSOURCE_TCB bool "Timer Counter Blocks (TCB) support" - default SOC_AT91RM9200 || SOC_AT91SAM9 || SOC_SAM9X60 || SOC_SAMA5 + default SOC_AT91RM9200 || SOC_AT91SAM9 || SOC_SAM9X60 || SOC_SAM9X7 || SOC_SAMA5 select ATMEL_TCB_CLKSRC help Select this to get a high precision clocksource based on a @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:193 @ config MICROCHIP_CLOCKSOURCE_PIT64B bool "64-bit Periodic Interval Timer (PIT64B) support" - default SOC_SAM9X60 || SOC_SAMA7 + default SOC_SAM9X60 || SOC_SAM9X7 || SOC_SAMA7 select MICROCHIP_PIT64B help Select this to get a high resolution clockevent (SAM9X60) or @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:247 @ config ATMEL_SECURE_PM bool "Atmel Secure PM support" - depends on SOC_SAMA5D2 && ATMEL_PM + depends on (SOC_SAMA5D2 || SOC_SAMA7G5) && ATMEL_PM select ARM_PSCI help When running under a TEE, the suspend mode must be requested to be set diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/mach-at91/Makefile linux-microchip/arch/arm/mach-at91/Makefile --- linux-6.6.51/arch/arm/mach-at91/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/mach-at91/Makefile 2024-11-26 14:02:35.766462671 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:10 @ obj-$(CONFIG_SOC_AT91RM9200) += at91rm9200.o obj-$(CONFIG_SOC_AT91SAM9) += at91sam9.o obj-$(CONFIG_SOC_SAM9X60) += sam9x60.o +obj-$(CONFIG_SOC_SAM9X7) += sam9x7.o obj-$(CONFIG_SOC_SAMA5) += sama5.o sam_secure.o -obj-$(CONFIG_SOC_SAMA7) += sama7.o +obj-$(CONFIG_SOC_SAMA7) += sama7.o sam_secure.o obj-$(CONFIG_SOC_SAMV7) += samv7.o +obj-$(CONFIG_AT91_VDEC_G1) += memalloc.o vdec_g1.o # Power Management obj-$(CONFIG_ATMEL_PM) += pm.o pm_suspend.o diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/mach-at91/memalloc.c linux-microchip/arch/arm/mach-at91/memalloc.c --- linux-6.6.51/arch/arm/mach-at91/memalloc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/arch/arm/mach-at91/memalloc.c 2024-11-26 14:02:35.770462742 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +/* + * Physically contiguous memory allocator + * + * Copyright (C) 2009 Hantro Products Oy. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/module.h> +#include <linux/dma-mapping.h> /* dma_zalloc_coherent, dma_free_coherent */ +#include <linux/fs.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/uaccess.h> + +#include "memalloc.h" + +struct memalloc_drv { + struct class *class; + struct device *dev; + int major; + struct list_head opened; /* list of opened files (memalloc_file_context) */ + spinlock_t lock; +}; + +struct memalloc_file_context { + struct list_head n; + struct memalloc_drv *parent; + struct list_head blocks; /* list of allocated blocks (mem_block) */ + spinlock_t lock; + //unsigned int num_blocks; +}; + +struct memalloc_block { + struct list_head n; + dma_addr_t dma_handle; + void *virt_addr; + size_t size; /* page aligned */ + int method; +}; + +static struct memalloc_drv memalloc_ing = { + .opened = LIST_HEAD_INIT(memalloc_ing.opened), + .lock = __SPIN_LOCK_UNLOCKED(memalloc_ing.lock), +}; + +/** + * allocate_large_block - Allocate a physically contiguous memory area + * + * @param dev driver device node + * @param pp_block Result block struct address + * @param p_size Wished size. Output is rounded up to page size (often something like 4k) + * + * @return 0 on success, negative value on error + */ +static int allocate_large_block(struct device *dev, struct memalloc_block **pp_block, size_t *p_size) +{ + struct memalloc_block *p; + + p = kmalloc(sizeof(struct memalloc_block), GFP_KERNEL); + if (p == NULL) { + dev_info(dev, "unable to alloc block struct.\n"); + return -ENOMEM; + } + + p->size = PAGE_ALIGN(*p_size); + + /* Multiple of PAGE_SIZE */ + p->virt_addr = dma_alloc_coherent(dev, p->size, &p->dma_handle, GFP_KERNEL); + if (!p->virt_addr) { + dev_err(dev, "large alloc failed (%d)\n", p->size); + kfree(p); + return -ENOMEM; + } + + dev_dbg(dev, "large alloc ok: VA=%p PA=0x%llx SZ=%d (requested %d)\n", + p->virt_addr, (unsigned long long)p->dma_handle, p->size, *p_size); + + *p_size = p->size; + *pp_block = p; + return 0; +} + +static int free_large_block(struct device *dev, struct memalloc_block *p) +{ + dev_dbg(dev, "large free: VA=%p\n", p->virt_addr); + dma_free_coherent(dev, p->size, p->virt_addr, p->dma_handle); + kfree(p); + return 0; +} + +static long memalloc_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct memalloc_file_context *fc = filp->private_data; + struct memalloc_drv *m = fc->parent; + + int ret = -EFAULT; + MemallocParams mem_params; + struct memalloc_block *p; + size_t sz; + + if (!filp || arg == 0) + return ret; + + switch (cmd) { + case MEMALLOC_IOCXGETBUFFER: + spin_lock(&fc->lock); + if (copy_from_user(&mem_params, (MemallocParams *)arg, sizeof(mem_params))) + dev_dbg(m->dev, "copy_from_user failed\n"); + + sz = mem_params.size; + ret = allocate_large_block(m->dev, &p, &sz); + + if (!ret) { + mem_params.busAddress = (unsigned long)p->dma_handle; /* should be 64-bit! */ + mem_params.size = sz; + if (copy_to_user((MemallocParams *)arg, &mem_params, sizeof(mem_params))) + dev_dbg(m->dev, "copy_to_user failed\n"); + + list_add(&p->n, &fc->blocks); + } + spin_unlock(&fc->lock); + break; + + case MEMALLOC_IOCSFREEBUFFER: + ret = -EINVAL; + spin_lock(&fc->lock); + __get_user(mem_params.busAddress, (unsigned long *)arg); + + /* find memalloc_block */ + list_for_each_entry(p, &fc->blocks, n) { + if ((unsigned long)p->dma_handle == mem_params.busAddress) { + list_del(&p->n); + free_large_block(m->dev, p); + ret = 0; + break; + } + } + spin_unlock(&fc->lock); + break; + + default: + ret = -ENOIOCTLCMD; + } + return ret; +} + +static int memalloc_open(struct inode *inode, struct file *filp) +{ + struct memalloc_file_context *fc; + struct memalloc_drv *m = &memalloc_ing; + int dev = iminor(inode); + + if (dev != 0) { + dev_warn(m->dev, "unsupported minor (%d).\n", dev); + return -EINVAL; + } + + fc = kmalloc(sizeof(struct memalloc_file_context), GFP_KERNEL); + if (fc == NULL) { + dev_err(m->dev, "unable to alloc struct.\n"); + return -ENOMEM; + } + + INIT_LIST_HEAD(&fc->blocks); + spin_lock_init(&fc->lock); + fc->parent = m; + + filp->private_data = fc; + + spin_lock(&m->lock); + list_add_tail(&fc->n, &m->opened); + spin_unlock(&m->lock); + + dev_dbg(m->dev, "file open (%p)\n", fc); + return 0; +} + +static int memalloc_release(struct inode *inode, struct file *filp) +{ + struct memalloc_file_context *fc = filp->private_data; + struct memalloc_drv *m = fc->parent; + struct memalloc_block *p, *tmp; + + list_for_each_entry_safe(p, tmp, &fc->blocks, n) { + list_del(&p->n); + free_large_block(m->dev, p); + } + + spin_lock(&m->lock); + list_del(&fc->n); + spin_unlock(&m->lock); + + kfree(fc); + + dev_dbg(m->dev, "file release (%p)\n", fc); + return 0; +} + +static const struct vm_operations_struct mmap_mem_ops = { +#ifdef CONFIG_HAVE_IOREMAP_PROT + .access = generic_access_phys +#endif +}; + +/* This function is based on mmap_mem (drivers/char/mem.c) */ +static int memalloc_mmap (struct file *filp, struct vm_area_struct *vma) +{ + struct memalloc_file_context *fc = filp->private_data; + struct memalloc_block *p; + + int found = 0; + size_t size = vma->vm_end - vma->vm_start; + + /* Is this a memory chunk provided by our driver ? */ + spin_lock(&fc->lock); + list_for_each_entry(p, &fc->blocks, n) { + if (((u64)p->dma_handle == ((u64)vma->vm_pgoff << PAGE_SHIFT)) && + (size <= p->size)) { + found = 1; + break; + } + } + spin_unlock(&fc->lock); + + if (!found) + return -EPERM; + + vma->vm_page_prot = phys_mem_access_prot(filp, vma->vm_pgoff, + size, + vma->vm_page_prot); + + vma->vm_ops = &mmap_mem_ops; + + /* Remap-pfn-range will mark the range VM_IO */ + if (remap_pfn_range(vma, + vma->vm_start, + vma->vm_pgoff, + size, + vma->vm_page_prot)) { + return -EAGAIN; + } + return 0; +} + +static struct file_operations memalloc_fops = { + .owner = THIS_MODULE, + .open = memalloc_open, + .release = memalloc_release, + .unlocked_ioctl = memalloc_ioctl, + .llseek = noop_llseek, + .mmap = memalloc_mmap, +}; + +static int memalloc_init(void) +{ + struct memalloc_drv *m = &memalloc_ing; + int ret; + + m->major = register_chrdev(0, "memalloc", &memalloc_fops); + if (m->major < 0) { + pr_err("failed to register character device\n"); + return m->major; + } + + /* create /dev/memalloc */ + m->class = class_create("memalloc-cls"); + if (IS_ERR(m->class)) { + ret = PTR_ERR(m->class); + goto err; + } + m->dev = device_create(m->class, NULL, MKDEV(m->major, 0), NULL, "memalloc"); + if (IS_ERR(m->dev)) { + ret = PTR_ERR(m->dev); + class_destroy(m->class); + goto err; + } + + m->dev->coherent_dma_mask = DMA_BIT_MASK(32); + dev_dbg(m->dev, "allocator with major = %d\n", m->major); + + return 0; +err: + unregister_chrdev(m->major, "memalloc"); + return ret; + +} +module_init(memalloc_init); + +static void memalloc_exit(void) +{ + struct memalloc_drv *m = &memalloc_ing; + + device_destroy(m->class, MKDEV(m->major, 0)); + class_destroy(m->class); + unregister_chrdev(m->major, "memalloc"); +} +module_exit(memalloc_exit); + +MODULE_AUTHOR("Hantro Products Oy"); +MODULE_DESCRIPTION("Memory allocator for VDEC"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.5"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/mach-at91/memalloc.h linux-microchip/arch/arm/mach-at91/memalloc.h --- linux-6.6.51/arch/arm/mach-at91/memalloc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/arch/arm/mach-at91/memalloc.h 2024-11-26 14:02:35.770462742 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +/* + * Memalloc, encoder memory allocation driver (kernel module headers) + * + * Copyright (C) 2009 Hantro Products Oy. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef MEMALLOC_H +#define MEMALLOC_H + +#include <linux/ioctl.h> + +/* Use 'k' as magic number */ +#define MEMALLOC_IOC_MAGIC 'k' +/* + * S means "Set" through a ptr, + * T means "Tell" directly with the argument value + * G means "Get": reply by setting through a pointer + * Q means "Query": response is on the return value + * X means "eXchange": G and S atomically + * H means "sHift": T and Q atomically + */ +#define MEMALLOC_IOCXGETBUFFER _IOWR(MEMALLOC_IOC_MAGIC, 1, MemallocParams*) +#define MEMALLOC_IOCSFREEBUFFER _IOW(MEMALLOC_IOC_MAGIC, 2, unsigned long) + +/* + * ... more to come + * + * debugging tool + * #define MEMALLOC_IOCHARDRESET _IO(MEMALLOC_IOC_MAGIC, 15) + * #define MEMALLOC_IOC_MAXNR 15 + * + */ + +typedef struct { + unsigned busAddress; + unsigned size; +} MemallocParams; + +#endif /* MEMALLOC_H */ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/mach-at91/pm.c linux-microchip/arch/arm/mach-at91/pm.c --- linux-6.6.51/arch/arm/mach-at91/pm.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/mach-at91/pm.c 2024-11-26 14:04:18.560303308 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:119 @ * @config_shdwc_ws: wakeup sources configuration function for SHDWC * @config_pmc_ws: wakeup srouces configuration function for PMC * @ws_ids: wakup sources of_device_id array + * @shdwc_np: pointer to shdwc node * @bu: backup unit mapped data (for backup mode) * @quirks: PM quirks * @data: PM data to be used on last phase of suspend @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:130 @ int (*config_shdwc_ws)(void __iomem *shdwc, u32 *mode, u32 *polarity); int (*config_pmc_ws)(void __iomem *pmc, u32 mode, u32 polarity); const struct of_device_id *ws_ids; + struct device_node *shdwc_np; struct at91_pm_bu *bu; struct at91_pm_quirks quirks; struct at91_pm_data data; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:238 @ { /* sentinel */ } }; +static const struct of_device_id sam9x7_ws_ids[] = { + { .compatible = "microchip,sam9x60-rtc", .data = &ws_info[1] }, + { .compatible = "atmel,at91rm9200-ohci", .data = &ws_info[2] }, + { .compatible = "usb-ohci", .data = &ws_info[2] }, + { .compatible = "atmel,at91sam9g45-ehci", .data = &ws_info[2] }, + { .compatible = "usb-ehci", .data = &ws_info[2] }, + { .compatible = "microchip,sam9x60-rtt", .data = &ws_info[4] }, + { .compatible = "microchip,sam9x7-gem", .data = &ws_info[5] }, + { /* sentinel */ } +}; + +static int at91_pm_device_in_list(const struct platform_device *pdev, + const struct of_device_id *ids) +{ + struct platform_device *local_pdev; + const struct of_device_id *match; + struct device_node *np; + int in_list = 0; + + for_each_matching_node_and_match(np, ids, &match) { + local_pdev = of_find_device_by_node(np); + if (!local_pdev) + continue; + + if (pdev == local_pdev) + in_list = 1; + + put_device(&local_pdev->dev); + if (in_list) + return in_list; + } + + return in_list; +} + +static int at91_pm_prepare_lpm(unsigned int pm_mode) +{ + struct platform_device *pdev; + int ndevices, i, ret; + struct of_phandle_args lpmspec; + + if ((pm_mode != AT91_PM_ULP0 && pm_mode != AT91_PM_ULP1) || + !soc_pm.shdwc_np) + return 0; + + ndevices = of_count_phandle_with_args(soc_pm.shdwc_np, + "microchip,lpm-connection", 0); + if (ndevices < 0) + return 0; + + soc_pm.data.lpm = 1; + for (i = 0; i < ndevices; i++) { + ret = of_parse_phandle_with_args(soc_pm.shdwc_np, + "microchip,lpm-connection", + NULL, i, &lpmspec); + if (ret < 0) { + if (ret == -ENOENT) { + continue; + } else { + soc_pm.data.lpm = 0; + return ret; + } + } + + pdev = of_find_device_by_node(lpmspec.np); + if (!pdev) + continue; + + if (device_may_wakeup(&pdev->dev)) { + if (pm_mode == AT91_PM_ULP1) { + /* + * ULP1 wake-up sources are limited. Ignore it if not + * in soc_pm.ws_ids. + */ + if (at91_pm_device_in_list(pdev, soc_pm.ws_ids)) + soc_pm.data.lpm = 0; + } else { + soc_pm.data.lpm = 0; + } + } + + put_device(&pdev->dev); + if (!soc_pm.data.lpm) + break; + } + + return 0; +} + static int at91_pm_config_ws(unsigned int pm_mode, bool set) { const struct wakeup_source_info *wsi; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:565 @ soc_pm.data.mode = -1; } - ret = at91_pm_config_ws(soc_pm.data.mode, true); + ret = at91_pm_prepare_lpm(soc_pm.data.mode); if (ret) return ret; + ret = at91_pm_config_ws(soc_pm.data.mode, true); + if (ret) { + /* Revert LPM if any. */ + soc_pm.data.lpm = 0; + return ret; + } + if (soc_pm.data.mode == AT91_PM_BACKUP) soc_pm.bu->suspended = 1; else if (soc_pm.bu) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:639 @ static int at91_suspend_finish(unsigned long val) { - unsigned char modified_gray_code[] = { - 0x00, 0x01, 0x02, 0x03, 0x06, 0x07, 0x04, 0x05, 0x0c, 0x0d, - 0x0e, 0x0f, 0x0a, 0x0b, 0x08, 0x09, 0x18, 0x19, 0x1a, 0x1b, - 0x1e, 0x1f, 0x1c, 0x1d, 0x14, 0x15, 0x16, 0x17, 0x12, 0x13, - 0x10, 0x11, + /* SYNOPSYS workaround to fix a bug in the calibration logic (SYNOPSYS Case Number 01331341) */ + unsigned char modified_fix_code[] = { + 0x00, 0x01, 0x01, 0x06, 0x07, 0x0c, 0x06, 0x07, 0x0b, 0x18, + 0x0a, 0x0b, 0x0c, 0x0d, 0x0d, 0x0a, 0x13, 0x13, 0x12, 0x13, + 0x14, 0x15, 0x15, 0x12, 0x18, 0x19, 0x19, 0x1e, 0x1f, 0x14, + 0x1e, 0x1f, }; unsigned int tmp, index; int i; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:655 @ * restore the ZQ0SR0 with the value saved here. But the * calibration is buggy and restoring some values from ZQ0SR0 * is forbidden and risky thus we need to provide processed - * values for these (modified gray code values). + * values for these. */ tmp = readl(soc_pm.data.ramc_phy + DDR3PHY_ZQ0SR0); /* Store pull-down output impedance select. */ index = (tmp >> DDR3PHY_ZQ0SR0_PDO_OFF) & 0x1f; - soc_pm.bu->ddr_phy_calibration[0] = modified_gray_code[index]; + soc_pm.bu->ddr_phy_calibration[0] = modified_fix_code[index] << DDR3PHY_ZQ0SR0_PDO_OFF; /* Store pull-up output impedance select. */ index = (tmp >> DDR3PHY_ZQ0SR0_PUO_OFF) & 0x1f; - soc_pm.bu->ddr_phy_calibration[0] |= modified_gray_code[index]; + soc_pm.bu->ddr_phy_calibration[0] |= modified_fix_code[index] << DDR3PHY_ZQ0SR0_PUO_OFF; /* Store pull-down on-die termination impedance select. */ index = (tmp >> DDR3PHY_ZQ0SR0_PDODT_OFF) & 0x1f; - soc_pm.bu->ddr_phy_calibration[0] |= modified_gray_code[index]; + soc_pm.bu->ddr_phy_calibration[0] |= modified_fix_code[index] << DDR3PHY_ZQ0SR0_PDODT_OFF; /* Store pull-up on-die termination impedance select. */ index = (tmp >> DDR3PHY_ZQ0SRO_PUODT_OFF) & 0x1f; - soc_pm.bu->ddr_phy_calibration[0] |= modified_gray_code[index]; + soc_pm.bu->ddr_phy_calibration[0] |= modified_fix_code[index] << DDR3PHY_ZQ0SRO_PUODT_OFF; /* * The 1st 8 words of memory might get corrupted in the process @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1200 @ suspend_mode = soc_pm.data.suspend_mode; - res = sam_smccc_call(SAMA5_SMC_SIP_SET_SUSPEND_MODE, + res = sam_smccc_call(SAM_SMC_SIP_SET_SUSPEND_MODE, suspend_mode, 0); if (res.a0 == 0) { pr_info("AT91: Secure PM: suspend mode set to %s\n", pm_modes[suspend_mode].pattern); + soc_pm.data.mode = suspend_mode; return; } pr_warn("AT91: Secure PM: %s mode not supported !\n", pm_modes[suspend_mode].pattern); - res = sam_smccc_call(SAMA5_SMC_SIP_GET_SUSPEND_MODE, 0, 0); + res = sam_smccc_call(SAM_SMC_SIP_GET_SUSPEND_MODE, 0, 0); if (res.a0 == 0) { pr_warn("AT91: Secure PM: failed to get default mode\n"); + soc_pm.data.mode = -1; return; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1223 @ pm_modes[suspend_mode].pattern); soc_pm.data.suspend_mode = res.a1; + soc_pm.data.mode = soc_pm.data.suspend_mode; } static const struct of_device_id atmel_shdwc_ids[] = { { .compatible = "atmel,sama5d2-shdwc" }, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1237 @ { .compatible = "atmel,sama5d2-gem" }, { .compatible = "atmel,sama5d29-gem" }, { .compatible = "microchip,sama7g5-gem" }, + { .compatible = "microchip,sam9x7-gem" }, { }, }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1343 @ AT91_PM_REPLACE_MODES(maps, SHDWC); } else { soc_pm.data.shdwc = of_iomap(np, 0); - of_node_put(np); + /* + * np is used further on suspend/resume path so we skip the + * of_node_put(np) here. + */ + soc_pm.shdwc_np = np; } } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1469 @ { .compatible = "atmel,sama5d2-pmc", .data = &pmc_infos[1] }, { .compatible = "microchip,sam9x60-pmc", .data = &pmc_infos[4] }, { .compatible = "microchip,sama7g5-pmc", .data = &pmc_infos[5] }, + { .compatible = "microchip,sam9x7-pmc", .data = &pmc_infos[4] }, { /* sentinel */ }, }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1607 @ soc_pm.config_pmc_ws = at91_sam9x60_config_pmc_ws; } +void __init sam9x7_pm_init(void) +{ + static const int modes[] __initconst = { + AT91_PM_STANDBY, AT91_PM_ULP0, + }; + + int ret; + + if (!IS_ENABLED(CONFIG_SOC_SAM9X7)) + return; + + at91_pm_modes_validate(modes, ARRAY_SIZE(modes)); + ret = at91_dt_ramc(false); + if (ret) + return; + + at91_pm_init(NULL); + + soc_pm.ws_ids = sam9x7_ws_ids; + soc_pm.config_pmc_ws = at91_sam9x60_config_pmc_ws; +} + void __init at91sam9_pm_init(void) { int ret; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1742 @ AT91_PM_STANDBY, AT91_PM_ULP0, AT91_PM_ULP1, AT91_PM_BACKUP, }; static const u32 iomaps[] __initconst = { - [AT91_PM_ULP0] = AT91_PM_IOMAP(SFRBU), + [AT91_PM_ULP0] = AT91_PM_IOMAP(SFRBU) | + AT91_PM_IOMAP(SHDWC), [AT91_PM_ULP1] = AT91_PM_IOMAP(SFRBU) | AT91_PM_IOMAP(SHDWC) | AT91_PM_IOMAP(ETHC), @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1755 @ if (!IS_ENABLED(CONFIG_SOC_SAMA7)) return; + if (IS_ENABLED(CONFIG_ATMEL_SECURE_PM)) { + pr_warn("AT91: Secure PM: ignoring standby mode\n"); + at91_pm_secure_init(); + return; + } + at91_pm_modes_validate(modes, ARRAY_SIZE(modes)); ret = at91_dt_ramc(true); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/mach-at91/pm_data-offsets.c linux-microchip/arch/arm/mach-at91/pm_data-offsets.c --- linux-6.6.51/arch/arm/mach-at91/pm_data-offsets.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/mach-at91/pm_data-offsets.c 2024-11-26 14:02:35.770462742 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:21 @ pmc_mckr_offset)); DEFINE(PM_DATA_PMC_VERSION, offsetof(struct at91_pm_data, pmc_version)); + DEFINE(PM_DATA_LPM, offsetof(struct at91_pm_data, lpm)); return 0; } diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/mach-at91/pm.h linux-microchip/arch/arm/mach-at91/pm.h --- linux-6.6.51/arch/arm/mach-at91/pm.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/mach-at91/pm.h 2024-11-26 14:02:35.770462742 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:42 @ unsigned int suspend_mode; unsigned int pmc_mckr_offset; unsigned int pmc_version; + unsigned int lpm; }; #endif diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/mach-at91/pm_suspend.S linux-microchip/arch/arm/mach-at91/pm_suspend.S --- linux-6.6.51/arch/arm/mach-at91/pm_suspend.S 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/mach-at91/pm_suspend.S 2024-11-26 14:04:18.560303308 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:90 @ .endm -/** - * Set state for 2.5V low power regulator - * @ena: 0 - disable regulator - * 1 - enable regulator +/* + * Set LPM + * @ena: 0 - disable LPM + * 1 - enable LPM * - * Side effects: overwrites r7, r8, r9, r10 + * Side effects: overwrites r7, r8, r9 */ - .macro at91_2_5V_reg_set_low_power ena -#ifdef CONFIG_SOC_SAMA7 - ldr r7, .sfrbu - mov r8, #\ena - ldr r9, [r7, #AT91_SFRBU_25LDOCR] - orr r9, r9, #AT91_SFRBU_25LDOCR_LP - cmp r8, #1 - beq lp_done_\ena - bic r9, r9, #AT91_SFRBU_25LDOCR_LP -lp_done_\ena: - ldr r10, =AT91_SFRBU_25LDOCR_LDOANAKEY - orr r9, r9, r10 - str r9, [r7, #AT91_SFRBU_25LDOCR] -#endif - .endm - - .macro at91_backup_set_lpm reg + .macro at91_set_lpm ena #ifdef CONFIG_SOC_SAMA7 - orr \reg, \reg, #0x200000 + ldr r7, .lpm + cmp r7, #1 + bne 21f + ldr r7, .shdwc + cmp r7, #0 + beq 21f + mov r8, #0xA5000000 + add r8, #0x200000 + mov r9, #\ena + cmp r9, #1 + beq 20f + add r8, #0x200000 +20: + str r8, [r7] +21: #endif .endm @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:503 @ ldr tmp1, [pmc, #AT91_PMC_SR] str tmp1, .saved_osc_status tst tmp1, #AT91_PMC_MOSCRCS - bne 1f + bne 7f /* Turn off RC oscillator */ ldr tmp1, [pmc, #AT91_CKGR_MOR] @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:517 @ tst tmp1, #AT91_PMC_MOSCRCS bne 2b + /* Enable LPM. */ +7: at91_set_lpm 1 + /* Wait for interrupt */ 1: at91_cpu_idle @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:537 @ wait_mckrdy tmp3 b 6f -5: /* Restore RC oscillator state */ - ldr tmp1, .saved_osc_status +5: at91_set_lpm 0 + + /* Restore RC oscillator state */ +8: ldr tmp1, .saved_osc_status tst tmp1, #AT91_PMC_MOSCRCS beq 4f @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:617 @ wait_mckrdy tmp3 + /* Enable LPM */ + at91_set_lpm 1 + /* Enter the ULP1 mode by set WAITMODE bit in CKGR_MOR */ ldr tmp1, [pmc, #AT91_CKGR_MOR] orr tmp1, tmp1, #AT91_PMC_WAITMODE @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:633 @ wait_mckrdy tmp3 + /* Disable LPM. */ + at91_set_lpm 0 + /* Enable the crystal oscillator */ ldr tmp1, [pmc, #AT91_CKGR_MOR] orr tmp1, tmp1, #AT91_PMC_MOSCEN @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:966 @ at91_plla_disable - /* Enable low power mode for 2.5V regulator. */ - at91_2_5V_reg_set_low_power 1 - ldr tmp3, .pm_mode cmp tmp3, #AT91_PM_ULP1 beq ulp1_mode @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:978 @ b ulp_exit ulp_exit: - /* Disable low power mode for 2.5V regulator. */ - at91_2_5V_reg_set_low_power 0 - ldr pmc, .pmc_base at91_plla_enable @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1020 @ ldr r0, .shdwc mov tmp1, #0xA5000000 add tmp1, tmp1, #0x1 - at91_backup_set_lpm tmp1 +#ifdef CONFIG_SOC_SAMA7 + orr tmp1, tmp1, #0x200000 +#endif str tmp1, [r0, #0] .endm @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1053 @ str tmp1, .memtype ldr tmp1, [r0, #PM_DATA_MODE] str tmp1, .pm_mode +#ifdef CONFIG_SOC_SAMA7 + ldr tmp1, [r0, #PM_DATA_LPM] + str tmp1, .lpm +#endif /* * ldrne below are here to preload their address in the TLB as access @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1144 @ .word 0 .pmc_version: .word 0 +#ifdef CONFIG_SOC_SAMA7 +.lpm: + .word 0 +#endif .saved_mckr: .word 0 .saved_pllar: diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/mach-at91/sam9x7.c linux-microchip/arch/arm/mach-at91/sam9x7.c --- linux-6.6.51/arch/arm/mach-at91/sam9x7.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/arch/arm/mach-at91/sam9x7.c 2024-11-26 14:02:35.770462742 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Setup code for SAM9X7. + * + * Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries + * + * Author: Varshini Rajendran <varshini.rajendran@microchip.com> + */ + +#include <linux/of.h> +#include <linux/of_platform.h> + +#include <asm/mach/arch.h> +#include <asm/system_misc.h> + +#include "generic.h" + +static void __init sam9x7_init(void) +{ + of_platform_default_populate(NULL, NULL, NULL); + + sam9x7_pm_init(); +} + +static const char *const sam9x7_dt_board_compat[] __initconst = { + "microchip,sam9x7", + NULL +}; + +DT_MACHINE_START(sam9x7_dt, "Microchip SAM9X7") + /* Maintainer: Microchip */ + .init_machine = sam9x7_init, + .dt_compat = sam9x7_dt_board_compat, +MACHINE_END diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/mach-at91/sama5.c linux-microchip/arch/arm/mach-at91/sama5.c --- linux-6.6.51/arch/arm/mach-at91/sama5.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/mach-at91/sama5.c 2024-11-26 14:02:35.770462742 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:21 @ #include "generic.h" #include "sam_secure.h" +#define SAM_SIP_SMC_VAL(func) ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_32, ARM_SMCCC_OWNER_SIP, (func)) + +#define SAM_SMC_SIP_PL310_ENABLE 0x1 + static void sama5_l2c310_write_sec(unsigned long val, unsigned reg) { /* OP-TEE configures the L2 cache and does not allow modifying it yet */ +#ifdef CONFIG_HAVE_ARM_SMCCC + struct arm_smccc_res res; + + arm_smccc_smc(SAM_SIP_SMC_VAL(SAM_SMC_SIP_PL310_ENABLE), val, reg, 0, 0, + 0, 0, 0, &res); + + if (res.a0 != 0) + pr_err("Failed to write l2c310 0x%x: 0x%lx\n", reg, res.a0); +#endif } static void __init sama5_secure_cache_init(void) diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/mach-at91/sama7.c linux-microchip/arch/arm/mach-at91/sama7.c --- linux-6.6.51/arch/arm/mach-at91/sama7.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/mach-at91/sama7.c 2024-11-26 14:02:35.770462742 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:16 @ #include <asm/system_misc.h> #include "generic.h" +#include "sam_secure.h" + +static void __init sama7_secure_cache_init(void) +{ + sam_secure_init(); +} static void __init sama7_dt_device_init(void) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:37 @ DT_MACHINE_START(sama7_dt, "Microchip SAMA7") /* Maintainer: Microchip */ .init_machine = sama7_dt_device_init, + .init_early = sama7_secure_cache_init, .dt_compat = sama7_dt_board_compat, MACHINE_END diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/mach-at91/sam_secure.h linux-microchip/arch/arm/mach-at91/sam_secure.h --- linux-6.6.51/arch/arm/mach-at91/sam_secure.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/arm/mach-at91/sam_secure.h 2024-11-26 14:02:35.770462742 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:12 @ #include <linux/arm-smccc.h> /* Secure Monitor mode APIs */ -#define SAMA5_SMC_SIP_SET_SUSPEND_MODE 0x400 -#define SAMA5_SMC_SIP_GET_SUSPEND_MODE 0x401 +#define SAMA5_SMC_SIP_L2X0_WRITE_REG 0x100 + +#define SAM_SMC_SIP_SET_SUSPEND_MODE 0x400 +#define SAM_SMC_SIP_GET_SUSPEND_MODE 0x401 + +/* SAMA5 SMC return codes */ +#define SAMA5_SMC_SIP_RETURN_SUCCESS 0x0 +#define SAMA5_SMC_SIP_RETURN_EPERM 0x1 +#define SAMA5_SMC_SIP_RETURN_EINVAL 0x2 void __init sam_secure_init(void); struct arm_smccc_res sam_smccc_call(u32 fn, u32 arg0, u32 arg1); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/arm/mach-at91/vdec_g1.c linux-microchip/arch/arm/mach-at91/vdec_g1.c --- linux-6.6.51/arch/arm/mach-at91/vdec_g1.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/arch/arm/mach-at91/vdec_g1.c 2024-11-26 14:02:35.770462742 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +/* + * On2/Hantro G1 decoder/pp driver. Single core version. + * + * Copyright (C) 2009 Hantro Products Oy. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/miscdevice.h> +#include <linux/fs.h> +#include <linux/uaccess.h> +#include <linux/sched.h> + +#include "hx170dec.h" +#include "at91_vdec.h" + +#define VDEC_MAX_CORES 1 /* number of cores of the hardware IP */ +#define VDEC_NUM_REGS_DEC 60 /* number of registers of the Decoder part */ +#define VDEC_NUM_REGS_PP 41 /* number of registers of the Post Processor part */ +#define VDEC_DEC_FIRST_REG 0 /* first register (0-based) index */ +#define VDEC_DEC_LAST_REG 59 /* last register (0-based) index */ +#define VDEC_PP_FIRST_REG 60 +#define VDEC_PP_LAST_REG 100 + +struct vdec_device { + void __iomem *mmio_base; + struct clk *clk; + struct device *dev; + int irq; + int num_cores; + unsigned long iobaseaddr; + unsigned long iosize; + wait_queue_head_t dec_wq; + wait_queue_head_t pp_wq; + bool dec_irq_done; + bool pp_irq_done; + struct semaphore dec_sem; + struct semaphore pp_sem; + struct file *dec_owner; + struct file *pp_owner; + u32 regs[VDEC_NUM_REGS_DEC + VDEC_NUM_REGS_PP]; +}; +static struct vdec_device *vdec6731_global; + +static inline void vdec_writel(const struct vdec_device *p, unsigned offset, u32 val) +{ + writel(val, p->mmio_base + offset); +} + +static inline u32 vdec_readl(const struct vdec_device *p, unsigned offset) +{ + return readl(p->mmio_base + offset); +} + +/** + * Write a range of registers. First register is assumed to be + * "Interrupt Register" and will be written last. + */ +static int vdec_regs_write(struct vdec_device *p, int begin, int end, + const struct core_desc *core) +{ + int i; + + if (copy_from_user(&p->regs[begin], core->regs, (end - begin + 1) * 4)) + { + dev_err(p->dev, "%s: copy_from_user failed\n", __func__); + return -EFAULT; + } + + for (i = end; i >= begin; i--) + vdec_writel(p, 4 * i, p->regs[i]); + + return 0; +} + +/** + * Read a range of registers [begin..end] + */ +static int vdec_regs_read(struct vdec_device *p, int begin, int end, + const struct core_desc *core) +{ + int i; + + for (i = end; i >= begin; i--) + p->regs[i] = vdec_readl(p, 4 * i); + + if (copy_to_user(core->regs, &p->regs[begin], (end - begin + 1) * 4)) + { + dev_err(p->dev, "%s: copy_to_user failed\n", __func__); + return -EFAULT; + } + + return 0; +} + +/** + * Misc driver related + */ + +static int vdec_misc_open(struct inode *inode, struct file *filp) +{ + struct vdec_device *p = vdec6731_global; + filp->private_data = p; + + dev_dbg(p->dev, "open\n"); + clk_prepare_enable(p->clk); + return 0; +} + +static int vdec_misc_release(struct inode *inode, struct file *filp) +{ + struct vdec_device *p = filp->private_data; + + if (p->dec_owner == filp) { + p->dec_irq_done = false; + init_waitqueue_head(&p->dec_wq); + sema_init(&p->dec_sem, VDEC_MAX_CORES); + p->dec_owner = NULL; + } + + if (p->pp_owner == filp) { + p->pp_irq_done = false; + init_waitqueue_head(&p->pp_wq); + sema_init(&p->pp_sem, 1); + p->pp_owner = NULL; + } + + clk_disable_unprepare(p->clk); + dev_dbg(p->dev, "release\n"); + return 0; +} + +static long vdec_misc_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + void __user *argp = (void __user *)arg; + struct vdec_device *p = vdec6731_global; + struct core_desc core; + u32 reg; + + switch (cmd) { + case HX170DEC_IOX_ASIC_ID: + reg = vdec_readl(p, VDEC_IDR); + if (copy_to_user(argp, ®, sizeof(u32))) + ret = -EFAULT; + break; + + case HX170DEC_IOC_MC_OFFSETS: + case HX170DEC_IOCGHWOFFSET: + if (copy_to_user(argp, &p->iobaseaddr, sizeof(p->iobaseaddr))) + ret = -EFAULT; + break; + case HX170DEC_IOCGHWIOSIZE: /* in bytes */ + if (copy_to_user(argp, &p->iosize, sizeof(p->iosize))) + ret = -EFAULT; + break; + case HX170DEC_IOC_MC_CORES: + if (copy_to_user(argp, &p->num_cores, sizeof(p->num_cores))) + ret = -EFAULT; + break; + + case HX170DEC_IOCS_DEC_PUSH_REG: + if (copy_from_user(&core, (void *)arg, sizeof(struct core_desc))) { + dev_err(p->dev, "copy_from_user (dec push reg) failed\n"); + ret = -EFAULT; + } else { + /* Skip VDEC_IDR (ID Register, ro) */ + core.regs++; // core.size -= 4; + ret = vdec_regs_write(p, VDEC_DEC_FIRST_REG + 1, VDEC_DEC_LAST_REG, &core); + } + break; + case HX170DEC_IOCS_PP_PUSH_REG: + if (copy_from_user(&core, (void *)arg, sizeof(struct core_desc))) { + dev_err(p->dev, "copy_from_user (pp push reg) failed\n"); + ret = -EFAULT; + } else { + /* Don't consider the 5 lastest registers (ro or unused) */ + ret = vdec_regs_write(p, VDEC_PP_FIRST_REG, VDEC_PP_LAST_REG - 5, &core); + } + break; + + case HX170DEC_IOCS_DEC_PULL_REG: + if (copy_from_user(&core, (void *)arg, sizeof(struct core_desc))) { + dev_err(p->dev, "copy_from_user (dec pull reg) failed\n"); + ret = -EFAULT; + } else { + ret = vdec_regs_read(p, VDEC_DEC_FIRST_REG, VDEC_DEC_LAST_REG, &core); + } + break; + + case HX170DEC_IOCS_PP_PULL_REG: + if (copy_from_user(&core, (void*)arg, sizeof(struct core_desc))) { + dev_err(p->dev, "copy_from_user (pp pull reg) failed\n"); + ret = -EFAULT; + } else { + ret = vdec_regs_read(p, VDEC_PP_FIRST_REG, VDEC_PP_LAST_REG, &core); + } + break; + + case HX170DEC_IOCX_DEC_WAIT: + if (copy_from_user(&core, (void *)arg, sizeof(struct core_desc))) { + dev_err(p->dev, "copy_from_user (dec wait) failed\n"); + ret = -EFAULT; + } else { + ret = wait_event_interruptible(p->dec_wq, p->dec_irq_done); + p->dec_irq_done = false; + if (unlikely(ret != 0)) { + dev_err(p->dev, "wait_event_interruptible dec error %d\n", ret); + } else { + /* Update dec registers */ + ret = vdec_regs_read(p, VDEC_DEC_FIRST_REG, VDEC_DEC_LAST_REG, &core); + } + } + break; + case HX170DEC_IOCX_PP_WAIT: + if (copy_from_user(&core, (void *)arg, sizeof(struct core_desc))) { + dev_err(p->dev, "copy_from_user (pp wait) failed\n"); + ret = -EFAULT; + } else { + ret = wait_event_interruptible(p->pp_wq, p->pp_irq_done); + p->pp_irq_done = false; + if (unlikely(ret != 0)) { + dev_err(p->dev, "wait_event_interruptible pp error %d\n", ret); + } else { + /* Update pp registers */ + ret = vdec_regs_read(p, VDEC_PP_FIRST_REG, VDEC_PP_LAST_REG, &core); + } + } + break; + + case HX170DEC_IOCH_DEC_RESERVE: + if (likely(down_interruptible(&p->dec_sem) == 0)) { + p->dec_owner = filp; + ret = 0; /* core id */ + dev_dbg(p->dev, "down dec_sem (core id %d)\n", ret); + } else { + dev_err(p->dev, "down_interruptible dec error\n"); + ret = -ERESTARTSYS; + } + break; + case HX170DEC_IOCT_DEC_RELEASE: + dev_dbg(p->dev, "up dec_sem\n"); + p->dec_owner = NULL; + up(&p->dec_sem); + break; + + case HX170DEC_IOCQ_PP_RESERVE: + if (likely(down_interruptible(&p->pp_sem) == 0)) { + p->pp_owner = filp; + ret = 0; /* core id */ + dev_dbg(p->dev, "down pp_sem (core id %d)\n", ret); + } else { + dev_err(p->dev, "down_interruptible pp error\n"); + ret = -ERESTARTSYS; + } + break; + case HX170DEC_IOCT_PP_RELEASE: + dev_dbg(p->dev, "up pp_sem\n"); + p->pp_owner = NULL; + up(&p->pp_sem); + break; + + default: + dev_warn(p->dev, "unknown ioctl %x\n", cmd); + ret = -EINVAL; + } + return ret; +} + +const struct file_operations vdec_misc_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .open = vdec_misc_open, + .release = vdec_misc_release, + .unlocked_ioctl = vdec_misc_ioctl, +}; + +static struct miscdevice vdec_misc_device = { + MISC_DYNAMIC_MINOR, + "vdec", + &vdec_misc_fops +}; + +/* + * Platform driver related + */ + +/* Should we use spin_lock_irqsave here? */ +static irqreturn_t vdec_isr(int irq, void *dev_id) +{ + struct vdec_device *p = dev_id; + u32 irq_status_dec, irq_status_pp; + int handled = 0; + + /* interrupt status register read */ + irq_status_dec = vdec_readl(p, VDEC_DIR); + if (irq_status_dec & VDEC_DIR_ISET) { + /* Clear IRQ */ + vdec_writel(p, VDEC_DIR, irq_status_dec & ~VDEC_DIR_ISET); + + p->dec_irq_done = true; + wake_up_interruptible(&p->dec_wq); + handled++; + } + + irq_status_pp = vdec_readl(p, VDEC_PPIR); + if (irq_status_pp & VDEC_PPIR_ISET) { + /* Clear IRQ */ + vdec_writel(p, VDEC_PPIR, irq_status_pp & ~VDEC_PPIR_ISET); + + p->pp_irq_done = true; + wake_up_interruptible(&p->pp_wq); + handled++; + } + + if (handled == 0) { + dev_warn(p->dev, "Spurious IRQ (DIR=%08x PPIR=%08x)\n", \ + irq_status_dec, irq_status_pp); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static int __init vdec_probe(struct platform_device *pdev) +{ + struct vdec_device *p; + struct resource *res; + int ret; + u32 hwid; + + /* Allocate private data */ + p = devm_kzalloc(&pdev->dev, sizeof(struct vdec_device), GFP_KERNEL); + if (!p) { + dev_dbg(&pdev->dev, "out of memory\n"); + return -ENOMEM; + } + + p->dev = &pdev->dev; + platform_set_drvdata(pdev, p); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + p->mmio_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(p->mmio_base)) + return PTR_ERR(p->mmio_base); + + p->clk = devm_clk_get(&pdev->dev, "vdec_clk"); + if (IS_ERR(p->clk)) { + dev_err(&pdev->dev, "no vdec_clk clock defined\n"); + return -ENXIO; + } + + p->irq = platform_get_irq(pdev, 0); + if (!p->irq) { + dev_err(&pdev->dev, "could not get irq\n"); + return -ENXIO; + } + + ret = devm_request_irq(&pdev->dev, p->irq, vdec_isr, + 0, pdev->name, p); + if (ret) { + dev_err(&pdev->dev, "unable to request VDEC irq\n"); + return ret; + } + + /* Register the miscdevice */ + ret = misc_register(&vdec_misc_device); + if (ret) { + dev_err(&pdev->dev, "unable to register miscdevice\n"); + return ret; + } + + p->num_cores = VDEC_MAX_CORES; + p->iosize = resource_size(res); + p->iobaseaddr = res->start; + vdec6731_global = p; + + p->dec_irq_done = false; + p->pp_irq_done = false; + p->dec_owner = NULL; + p->pp_owner = NULL; + init_waitqueue_head(&p->dec_wq); + init_waitqueue_head(&p->pp_wq); + sema_init(&p->dec_sem, VDEC_MAX_CORES); + sema_init(&p->pp_sem, 1); + + ret = clk_prepare_enable(p->clk); + if (ret) { + dev_err(&pdev->dev, "unable to prepare and enable clock\n"); + misc_deregister(&vdec_misc_device); + return ret; + } + + dev_info(&pdev->dev, "VDEC controller at 0x%p, irq = %d, misc_minor = %d\n", + p->mmio_base, p->irq, vdec_misc_device.minor); + + /* Reset Asic (just in case..) */ + vdec_writel(p, VDEC_DIR, VDEC_DIR_ID | VDEC_DIR_ABORT); + vdec_writel(p, VDEC_PPIR, VDEC_PPIR_ID); + + hwid = vdec_readl(p, VDEC_IDR); + clk_disable_unprepare(p->clk); + + dev_warn(&pdev->dev, "Product ID: %#x (revision %d.%d.%d)\n", \ + (hwid & VDEC_IDR_PROD_ID) >> 16, + (hwid & VDEC_IDR_MAJOR_VER) >> 12, + (hwid & VDEC_IDR_MINOR_VER) >> 4, + (hwid & VDEC_IDR_BUILD_VER)); + return 0; +} + +static int __exit vdec_remove(struct platform_device *pdev) +{ + platform_set_drvdata(pdev, NULL); + misc_deregister(&vdec_misc_device); + return 0; +} + +static const struct of_device_id vdec_of_match[] = { + { .compatible = "on2,sama5d4-g1", .data = NULL }, + {}, +}; +MODULE_DEVICE_TABLE(of, vdec_of_match); + +static struct platform_driver vdec_of_driver = { + .driver = { + .name = "atmel-vdec", + .owner = THIS_MODULE, + .of_match_table = vdec_of_match, + }, + .remove = vdec_remove, +}; + +module_platform_driver_probe(vdec_of_driver, vdec_probe); + +MODULE_AUTHOR("Hantro Products Oy"); +MODULE_DESCRIPTION("G1 decoder/pp driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.4"); +MODULE_ALIAS("platform:vdec"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/riscv/boot/dts/microchip/Makefile linux-microchip/arch/riscv/boot/dts/microchip/Makefile --- linux-6.6.51/arch/riscv/boot/dts/microchip/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/riscv/boot/dts/microchip/Makefile 2024-11-26 14:02:36.078468260 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ # SPDX-License-Identifier: GPL-2.0 +dtb-$(CONFIG_ARCH_MICROCHIP_POLARFIRE) += mpfs-beaglev-fire.dtb +dtb-$(CONFIG_ARCH_MICROCHIP_POLARFIRE) += mpfs-disco-kit.dtb dtb-$(CONFIG_ARCH_MICROCHIP_POLARFIRE) += mpfs-icicle-kit.dtb -dtb-$(CONFIG_ARCH_MICROCHIP_POLARFIRE) += mpfs-m100pfsevp.dtb +dtb-$(CONFIG_ARCH_MICROCHIP_POLARFIRE) += mpfs-m100pfsevp-sdcard.dtb +dtb-$(CONFIG_ARCH_MICROCHIP_POLARFIRE) += mpfs-m100pfsevp-emmc.dtb dtb-$(CONFIG_ARCH_MICROCHIP_POLARFIRE) += mpfs-polarberry.dtb -dtb-$(CONFIG_ARCH_MICROCHIP_POLARFIRE) += mpfs-sev-kit.dtb +dtb-$(CONFIG_ARCH_MICROCHIP_POLARFIRE) += mpfs-video-kit.dtb dtb-$(CONFIG_ARCH_MICROCHIP_POLARFIRE) += mpfs-tysom-m.dtb +dtb-$(CONFIG_ARCH_MICROCHIP_POLARFIRE) += pic64gx-curiosity-kit.dtb obj-$(CONFIG_BUILTIN_DTB) += $(addsuffix .o, $(dtb-y)) diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-beaglev-fire.dts linux-microchip/arch/riscv/boot/dts/microchip/mpfs-beaglev-fire.dts --- linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-beaglev-fire.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/arch/riscv/boot/dts/microchip/mpfs-beaglev-fire.dts 2024-11-26 14:02:36.078468260 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* Copyright (c) 2020-2021 Microchip Technology Inc */ + +/dts-v1/; + +#include <dt-bindings/gpio/gpio.h> +#include "mpfs.dtsi" +#include "mpfs-beaglev-fire-fabric.dtsi" + +/* Clock frequency (in Hz) of the rtcclk */ +#define RTCCLK_FREQ 1000000 + +/ { + #address-cells = <2>; + #size-cells = <2>; + model = "BeagleBoard BeagleV-Fire"; + compatible = "beagle,beaglev-fire", "microchip,mpfs"; + + soc { + dma-ranges = <0x14 0x0 0x0 0x80000000 0x0 0x4000000>, + <0x14 0x4000000 0x0 0xc4000000 0x0 0x6000000>, + <0x14 0xa000000 0x0 0x8a000000 0x0 0x8000000>, + <0x14 0x12000000 0x14 0x12000000 0x0 0x10000000>, + <0x14 0x22000000 0x10 0x22000000 0x0 0x5e000000>; + }; + + aliases { + serial0 = &mmuart0; + serial1 = &mmuart1; + serial2 = &mmuart2; + serial3 = &mmuart3; + serial4 = &mmuart4; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + cpus { + timebase-frequency = <RTCCLK_FREQ>; + }; + + kernel: memory@80000000 { + device_type = "memory"; + reg = <0x0 0x80000000 0x0 0x4000000>; + }; + + ddr_cached_low: memory@8a000000 { + device_type = "memory"; + reg = <0x0 0x8a000000 0x0 0x8000000>; + }; + + ddr_non_cached_low: memory@c4000000 { + device_type = "memory"; + reg = <0x0 0xc4000000 0x0 0x6000000>; + }; + + ddr_cached_high: memory@1022000000 { + device_type = "memory"; + reg = <0x10 0x22000000 0x0 0x5e000000>; + }; + + ddr_non_cached_high: memory@1412000000 { + device_type = "memory"; + reg = <0x14 0x12000000 0x0 0x10000000>; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + hss: hss-buffer@103fc00000 { + compatible = "shared-dma-pool"; + reg = <0x10 0x3fc00000 0x0 0x400000>; + no-map; + }; + + dma_non_cached_low: non-cached-low-buffer { + compatible = "shared-dma-pool"; + size = <0x0 0x4000000>; + no-map; + alloc-ranges = <0x0 0xc4000000 0x0 0x4000000>; + }; + + dma_non_cached_high: non-cached-high-buffer { + compatible = "shared-dma-pool"; + size = <0x0 0x10000000>; + no-map; + linux,dma-default; + alloc-ranges = <0x14 0x12000000 0x0 0x10000000>; + }; + }; + + imx219_vana: fixedregulator-0 { + compatible = "regulator-fixed"; + regulator-name = "imx219_vana"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + }; + + imx219_vdig: fixedregulator-1 { + compatible = "regulator-fixed"; + regulator-name = "imx219_vdig"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + imx219_vddl: fixedregulator-2 { + compatible = "regulator-fixed"; + regulator-name = "imx219_vddl"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + }; + + imx219_clk: camera-clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24000000>; + }; +}; + +&gpio0 { + ngpios=<14>; + gpio-line-names = "", "", "", "", "", "", "", + "", "", "", "", "", "SD_CARD_CS", "USER_BUTTON"; + status = "okay"; + + sd-card-cs-hog { + gpio-hog; + gpios = <12 12>; + output-high; + line-name = "SD_CARD_CS"; + }; + + user-button-hog { + gpio-hog; + gpios = <13 13>; + input; + line-name = "USER_BUTTON"; + }; +}; + +&gpio1 { + ngpios=<24>; + gpio-line-names = "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", + "ADC_IRQn", "", "", "USB_OCn"; + status = "okay"; + + adc-irqn-hog { + gpio-hog; + gpios = <20 20>; + input; + line-name = "ADC_IRQn"; + }; + + usb-ocn-hog { + gpio-hog; + gpios = <23 23>; + input; + line-name = "USB_OCn"; + }; +}; + +&gpio2 { + gpio-line-names = "P8_PIN3_USER_LED_0", "P8_PIN4_USER_LED_1", "P8_PIN5_USER_LED_2", + "P8_PIN6_USER_LED_3", "P8_PIN7_USER_LED_4", "P8_PIN8_USER_LED_5", + "P8_PIN9_USER_LED_6", "P8_PIN10_USER_LED_7", "P8_PIN11_USER_LED_8", + "P8_PIN12_USER_LED_9", "P8_PIN13_USER_LED_10", "P8_PIN14_USER_LED_11", + "P8_PIN15", "P8_PIN16", "P8_PIN17", "P8_PIN18", "P8_PIN19", "P8_PIN20", + "P8_PIN21", "P8_PIN22", "P8_PIN23", "P8_PIN24", "P8_PIN25", "P8_PIN26", + "P8_PIN27", "P8_PIN28", "P8_PIN29", "P8_PIN30", "M2_W_DISABLE1", + "M2_W_DISABLE2", "VIO_ENABLE", "SD_DET"; + status = "okay"; + + vio-enable-hog { + gpio-hog; + gpios = <30 30>; + output-high; + line-name = "VIO_ENABLE"; + }; + + sd-det-hog { + gpio-hog; + gpios = <31 31>; + input; + line-name = "SD_DET"; + }; +}; + +&i2c0 { + status = "okay"; +}; + +&i2c1 { + status = "okay"; + + eeprom: eeprom@50 { + compatible = "at,24c32"; + reg = <0x50>; + }; + + imx219: sensor@10 { + compatible = "sony,imx219"; + reg = <0x10>; + clocks = <&imx219_clk>; + VANA-supply = <&imx219_vana>; /* 2.8v */ + VDIG-supply = <&imx219_vdig>; /* 1.8v */ + VDDL-supply = <&imx219_vddl>; /* 1.2v */ + + port { + imx219_0: endpoint { + data-lanes = <1 2>; + clock-noncontinuous; + link-frequencies = /bits/ 64 <456000000>; + }; + }; + }; +}; + +&mac0 { + dma-noncoherent; + status = "okay"; + phy-mode = "sgmii"; + phy-handle = <&phy0>; + phy0: ethernet-phy@0 { + reg = <0>; + }; +}; + +&mbox { + status = "okay"; +}; + +&mmc { + dma-noncoherent; + bus-width = <4>; + disable-wp; + cap-sd-highspeed; + cap-mmc-highspeed; + mmc-ddr-1_8v; + mmc-hs200-1_8v; + sd-uhs-sdr12; + sd-uhs-sdr25; + sd-uhs-sdr50; + sd-uhs-sdr104; + status = "okay"; +}; + +&mmuart0 { + status = "okay"; +}; + +&mmuart1 { + status = "okay"; +}; + +&refclk { + clock-frequency = <125000000>; +}; + +&refclk_ccc { + clock-frequency = <50000000>; +}; + +&rtc { + status = "okay"; +}; + +&spi0 { + status = "okay"; +}; + +&spi1 { + status = "okay"; +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&qspi { + status = "okay"; + cs-gpios = <&gpio1 17 GPIO_ACTIVE_LOW>, <&gpio0 12 GPIO_ACTIVE_LOW>; + num-cs = <2>; + + adc@0 { + compatible = "microchip,mcp3464r"; + reg = <0>; /* CE0 */ + spi-cpol; + spi-cpha; + spi-max-frequency = <5000000>; + microchip,hw-device-address = <1>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + channel@0 { + /* CH0 to AGND */ + reg = <0>; + label = "CH0"; + }; + + channel@1 { + /* CH1 to AGND */ + reg = <1>; + label = "CH1"; + }; + + channel@2 { + /* CH2 to AGND */ + reg = <2>; + label = "CH2"; + }; + + channel@3 { + /* CH3 to AGND */ + reg = <3>; + label = "CH3"; + }; + + channel@4 { + /* CH4 to AGND */ + reg = <4>; + label = "CH4"; + }; + + channel@5 { + /* CH5 to AGND */ + reg = <5>; + label = "CH5"; + }; + + channel@6 { + /* CH6 to AGND */ + reg = <6>; + label = "CH6"; + }; + + channel@7 { + /* CH7 is connected to AGND */ + reg = <7>; + label = "CH7"; + }; + }; + + mmc-slot@1 { + compatible = "mmc-spi-slot"; + reg = <1>; + gpios = <&gpio2 31 1>; + voltage-ranges = <3300 3300>; + spi-max-frequency = <5000000>; + disable-wp; + }; +}; + + +&syscontroller { + microchip,bitstream-flash = <&sys_ctrl_flash>; + status = "okay"; +}; + +&syscontroller_qspi { + status = "okay"; + + sys_ctrl_flash: flash@0 { // MT25QL01GBBB8ESF-0SIT + compatible = "jedec,spi-nor"; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <20000000>; + spi-rx-bus-width = <1>; + reg = <0>; + }; +}; + +&usb { + dma-noncoherent; + status = "okay"; + dr_mode = "otg"; +}; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-beaglev-fire-fabric.dtsi linux-microchip/arch/riscv/boot/dts/microchip/mpfs-beaglev-fire-fabric.dtsi --- linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-beaglev-fire-fabric.dtsi 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/arch/riscv/boot/dts/microchip/mpfs-beaglev-fire-fabric.dtsi 2024-11-26 14:02:36.078468260 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* Copyright (c) 2020-2021 Microchip Technology Inc */ + +#include "dt-bindings/mailbox/miv-ihc.h" + +/ { + fabric_clk3: fabric-clk3 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <50000000>; + }; + + fabric_clk1: fabric-clk1 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <125000000>; + }; + + fabric-bus@40000000 { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges = <0x0 0x40000000 0x0 0x40000000 0x0 0x20000000>, /* FIC3-FAB */ + <0x0 0x60000000 0x0 0x60000000 0x0 0x20000000>, /* FIC0, LO */ + <0x0 0xe0000000 0x0 0xe0000000 0x0 0x20000000>, /* FIC1, LO */ + <0x20 0x0 0x20 0x0 0x10 0x0>, /* FIC0,HI */ + <0x30 0x0 0x30 0x0 0x10 0x0>; /* FIC1,HI */ + + cape_gpios_p8: gpio@41100000 { + compatible = "microchip,coregpio-rtl-v3"; + reg = <0x0 0x41100000 0x0 0x1000>; + clocks = <&fabric_clk3>; + gpio-controller; + #gpio-cells = <2>; + ngpios = <16>; + gpio-line-names = "P8_PIN31", "P8_PIN32", "P8_PIN33", "P8_PIN34", + "P8_PIN35", "P8_PIN36", "P8_PIN37", "P8_PIN38", + "P8_PIN39", "P8_PIN40", "P8_PIN41", "P8_PIN42", + "P8_PIN43", "P8_PIN44", "P8_PIN45", "P8_PIN46"; + }; + + cape_gpios_p9: gpio@41200000 { + compatible = "microchip,coregpio-rtl-v3"; + reg = <0x0 0x41200000 0x0 0x1000>; + clocks = <&fabric_clk3>; + gpio-controller; + #gpio-cells = <2>; + ngpios = <20>; + gpio-line-names = "P9_PIN11", "P9_PIN12", "P9_PIN13", "P9_PIN14", + "P9_PIN15", "P9_PIN16", "P9_PIN17", "P9_PIN18", + "P9_PIN21", "P9_PIN22", "P9_PIN23", "P9_PIN24", + "P9_PIN25", "P9_PIN26", "P9_PIN27", "P9_PIN28", + "P9_PIN29", "P9_PIN31", "P9_PIN41", "P9_PIN42"; + }; + + hsi_gpios: gpio@44000000 { + compatible = "microchip,coregpio-rtl-v3"; + reg = <0x0 0x44000000 0x0 0x1000>; + clocks = <&fabric_clk3>; + gpio-controller; + #gpio-cells = <2>; + ngpios = <20>; + gpio-line-names = "B0_HSIO70N", "B0_HSIO71N", "B0_HSIO83N", + "B0_HSIO73N_C2P_CLKN", "B0_HSIO70P", "B0_HSIO71P", + "B0_HSIO83P", "B0_HSIO73N_C2P_CLKP", "XCVR1_RX_VALID", + "XCVR1_LOCK", "XCVR1_ERROR", "XCVR2_RX_VALID", + "XCVR2_LOCK", "XCVR2_ERROR", "XCVR3_RX_VALID", + "XCVR3_LOCK", "XCVR3_ERROR", "XCVR_0B_REF_CLK_PLL_LOCK", + "XCVR_0C_REF_CLK_PLL_LOCK", "B0_HSIO81N"; + }; + }; + + ihc: mailbox { + compatible = "microchip,miv-ihc"; + interrupt-parent = <&plic>; + interrupts = <IHC_HART1_INT>; + microchip,miv-ihc-remote-context-id = <IHC_CONTEXT_B>; + #mbox-cells = <1>; + status = "disabled"; + }; + + fabric-pcie-bus@3000000000 { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges = <0x0 0x40000000 0x0 0x40000000 0x0 0x20000000>, + <0x30 0x0 0x30 0x0 0x10 0x0>; + dma-ranges = <0x0 0x0 0x0 0x80000000 0x0 0x4000000>, + <0x0 0x4000000 0x0 0xc4000000 0x0 0x6000000>, + <0x0 0xa000000 0x0 0x8a000000 0x0 0x8000000>, + <0x0 0x12000000 0x14 0x12000000 0x0 0x10000000>, + <0x0 0x22000000 0x10 0x22000000 0x0 0x5e000000>; + + pcie: pcie@3000000000 { + compatible = "microchip,pcie-host-1.0"; + #address-cells = <0x3>; + #interrupt-cells = <0x1>; + #size-cells = <0x2>; + device_type = "pci"; + dma-noncoherent; + reg = <0x30 0x0 0x0 0x8000000>, <0x0 0x43004000 0x0 0x2000>, <0x0 0x43006000 0x0 0x2000>; + reg-names = "cfg", "bridge", "ctrl"; + bus-range = <0x0 0x7f>; + interrupt-parent = <&plic>; + interrupts = <119>; + interrupt-map = <0 0 0 1 &pcie_intc 0>, + <0 0 0 2 &pcie_intc 1>, + <0 0 0 3 &pcie_intc 2>, + <0 0 0 4 &pcie_intc 3>; + interrupt-map-mask = <0 0 0 7>; + clocks = <&ccc_nw CLK_CCC_PLL0_OUT1>, + <&ccc_nw CLK_CCC_PLL0_OUT3>; + clock-names = "fic1", "fic3"; + ranges = <0x43000000 0x0 0x9000000 0x30 0x9000000 0x0 0xf000000>, + <0x1000000 0x0 0x8000000 0x30 0x8000000 0x0 0x1000000>, + <0x3000000 0x0 0x18000000 0x30 0x18000000 0x0 0x70000000>; + dma-ranges = <0x3000000 0x0 0x80000000 0x0 0x0 0x0 0x4000000>, + <0x3000000 0x0 0x84000000 0x0 0x4000000 0x0 0x6000000>, + <0x3000000 0x0 0x8a000000 0x0 0xa000000 0x0 0x8000000>, + <0x3000000 0x0 0x92000000 0x0 0x12000000 0x0 0x10000000>, + <0x3000000 0x0 0xa2000000 0x0 0x22000000 0x0 0x5e000000>; + msi-parent = <&pcie>; + msi-controller; + status = "disabled"; + + pcie_intc: interrupt-controller { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + }; + }; + }; + + refclk_ccc: cccrefclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + }; +}; + +&ccc_nw { + clocks = <&refclk_ccc>, <&refclk_ccc>, <&refclk_ccc>, <&refclk_ccc>, + <&refclk_ccc>, <&refclk_ccc>; + clock-names = "pll0_ref0", "pll0_ref1", "pll1_ref0", "pll1_ref1", + "dll0_ref", "dll1_ref"; + status = "okay"; +}; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-disco-kit.dts linux-microchip/arch/riscv/boot/dts/microchip/mpfs-disco-kit.dts --- linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-disco-kit.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/arch/riscv/boot/dts/microchip/mpfs-disco-kit.dts 2024-11-26 14:02:36.078468260 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* Copyright (c) 2020-2021 Microchip Technology Inc */ + +/dts-v1/; + +#include "mpfs.dtsi" +#include "mpfs-disco-kit-fabric.dtsi" +#include <dt-bindings/gpio/gpio.h> +#include <dt-bindings/leds/common.h> + +/* Clock frequency (in Hz) of the rtcclk */ +#define RTCCLK_FREQ 1000000 + +/ { + #address-cells = <2>; + #size-cells = <2>; + model = "Microchip PolarFire-SoC Discovery Kit"; + compatible = "microchip,mpfs-disco-kit", "microchip,mpfs"; + + soc { + dma-ranges = <0x14 0x0 0x0 0x80000000 0x0 0x4000000>, + <0x14 0x4000000 0x0 0xc4000000 0x0 0x6000000>, + <0x14 0xa000000 0x0 0x8a000000 0x0 0x8000000>, + <0x14 0x12000000 0x14 0x12000000 0x0 0x10000000>, + <0x14 0x22000000 0x10 0x22000000 0x0 0x1e000000>; + }; + + aliases { + ethernet0 = &mac0; + serial4 = &mmuart4; + }; + + chosen { + stdout-path = "serial4:115200n8"; + }; + + cpus { + timebase-frequency = <RTCCLK_FREQ>; + }; + + leds { + compatible = "gpio-leds"; + + led-1 { + gpios = <&gpio2 17 GPIO_ACTIVE_HIGH>; + color = <LED_COLOR_ID_AMBER>; + label = "led1"; + }; + + led-2 { + gpios = <&gpio2 18 GPIO_ACTIVE_HIGH>; + color = <LED_COLOR_ID_RED>; + label = "led2"; + }; + + led-3 { + gpios = <&gpio2 19 GPIO_ACTIVE_HIGH>; + color = <LED_COLOR_ID_AMBER>; + label = "led3"; + }; + + led-4 { + gpios = <&gpio2 20 GPIO_ACTIVE_HIGH>; + color = <LED_COLOR_ID_RED>; + label = "led4"; + }; + + led-5 { + gpios = <&gpio2 21 GPIO_ACTIVE_HIGH>; + color = <LED_COLOR_ID_AMBER>; + label = "led5"; + }; + + led-6 { + gpios = <&gpio2 22 GPIO_ACTIVE_HIGH>; + color = <LED_COLOR_ID_RED>; + label = "led6"; + }; + + led-7 { + gpios = <&gpio2 23 GPIO_ACTIVE_HIGH>; + color = <LED_COLOR_ID_AMBER>; + label = "led7"; + }; + + led-8 { + gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>; + color = <LED_COLOR_ID_RED>; + label = "led8"; + }; + }; + + kernel: memory@80000000 { + device_type = "memory"; + reg = <0x0 0x80000000 0x0 0x4000000>; + }; + + ddr_cached_low: memory@8a000000 { + device_type = "memory"; + reg = <0x0 0x8a000000 0x0 0x8000000>; + }; + + ddr_non_cached_low: memory@c4000000 { + device_type = "memory"; + reg = <0x0 0xc4000000 0x0 0x6000000>; + }; + + ddr_cached_high: memory@1022000000 { + device_type = "memory"; + reg = <0x10 0x22000000 0x0 0x1e000000>; + }; + + ddr_non_cached_high: memory@1412000000 { + device_type = "memory"; + reg = <0x14 0x12000000 0x0 0x10000000>; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + hss: hss-buffer@103fc00000 { + compatible = "shared-dma-pool"; + reg = <0x10 0x3fc00000 0x0 0x200000>; + no-map; + }; + + dma_non_cached_low: non-cached-low-buffer { + compatible = "shared-dma-pool"; + size = <0x0 0x4000000>; + no-map; + alloc-ranges = <0x0 0xc4000000 0x0 0x4000000>; + }; + + dma_non_cached_high: non-cached-high-buffer { + compatible = "shared-dma-pool"; + size = <0x0 0x10000000>; + no-map; + linux,dma-default; + alloc-ranges = <0x14 0x12000000 0x0 0x10000000>; + }; + + fabricbuf0ddrc: buffer@88000000 { + compatible = "shared-dma-pool"; + reg = <0x0 0x88000000 0x0 0x2000000>; + no-map; + }; + + fabricbuf1ddrnc: buffer@c8000000 { + compatible = "shared-dma-pool"; + reg = <0x0 0xc8000000 0x0 0x2000000>; + no-map; + }; + + fabricbuf2ddrncwcb: buffer@d8000000 { + compatible = "shared-dma-pool"; + reg = <0x0 0xd8000000 0x0 0x2000000>; + no-map; + }; + }; + + udmabuf0 { + compatible = "ikwzm,u-dma-buf"; + device-name = "udmabuf-ddr-c0"; + minor-number = <0>; + size = <0x0 0x2000000>; + memory-region = <&fabricbuf0ddrc>; + sync-mode = <3>; + }; + + udmabuf1 { + compatible = "ikwzm,u-dma-buf"; + device-name = "udmabuf-ddr-nc0"; + minor-number = <1>; + size = <0x0 0x2000000>; + memory-region = <&fabricbuf1ddrnc>; + sync-mode = <3>; + }; + + udmabuf2 { + compatible = "ikwzm,u-dma-buf"; + device-name = "udmabuf-ddr-nc-wcb0"; + minor-number = <2>; + size = <0x0 0x2000000>; + memory-region = <&fabricbuf2ddrncwcb>; + sync-mode = <3>; + }; +}; + +&core_pwm0 { + status = "okay"; +}; + +&fpgadma { + status = "okay"; +}; + +&fpgalsram { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&gpio2 { + status = "okay"; +}; + +&i2c0 { + status = "okay"; +}; + +&i2c2 { + status = "okay"; +}; + +&mac0 { + dma-noncoherent; + status = "okay"; + phy-mode = "sgmii"; + phy-handle = <&phy0>; + + phy0: ethernet-phy@b { + reg = <0xb>; + }; +}; + +&mbox { + status = "okay"; +}; + +&mmc { + dma-noncoherent; + bus-width = <4>; + disable-wp; + cap-sd-highspeed; + cap-mmc-highspeed; + sd-uhs-sdr12; + sd-uhs-sdr25; + sd-uhs-sdr50; + sd-uhs-sdr104; + no-1-8-v; + status = "okay"; +}; + +&mmuart1 { + status = "okay"; +}; + +&mmuart4 { + status = "okay"; +}; + +&refclk { + clock-frequency = <125000000>; +}; + +&rtc { + status = "okay"; +}; + +&spi0 { + status = "okay"; +}; + +&spi1 { + status = "okay"; +}; + +&syscontroller { + status = "okay"; +}; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-disco-kit-fabric.dtsi linux-microchip/arch/riscv/boot/dts/microchip/mpfs-disco-kit-fabric.dtsi --- linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-disco-kit-fabric.dtsi 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/arch/riscv/boot/dts/microchip/mpfs-disco-kit-fabric.dtsi 2024-11-26 14:02:36.078468260 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* Copyright (c) 2020-2021 Microchip Technology Inc */ + +#include "dt-bindings/mailbox/miv-ihc.h" + +/ { + compatible = "microchip,mpfs-disco-kit", "microchip,mpfs"; + + fabric-bus@40000000 { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges = <0x0 0x40000000 0x0 0x40000000 0x0 0x20000000>, /* FIC3-FAB */ + <0x0 0x60000000 0x0 0x60000000 0x0 0x20000000>, /* FIC0, LO */ + <0x0 0xe0000000 0x0 0xe0000000 0x0 0x20000000>, /* FIC1, LO */ + <0x20 0x0 0x20 0x0 0x10 0x0>, /* FIC0,HI */ + <0x30 0x0 0x30 0x0 0x10 0x0>; /* FIC1,HI */ + + core_pwm0: pwm@40000000 { + compatible = "microchip,corepwm-rtl-v4"; + reg = <0x0 0x40000000 0x0 0xF0>; + microchip,sync-update-mask = /bits/ 32 <0>; + #pwm-cells = <3>; + clocks = <&ccc_nw CLK_CCC_PLL0_OUT3>; + status = "disabled"; + }; + + fpgadma: dma-controller@60010000 { + compatible = "microchip,mpfs-fpga-dma"; + reg = <0x0 0x60010000 0x0 0x1000>; + interrupt-parent = <&plic>; + interrupts = <120>; + #dma-cells = <1>; + status = "disabled"; + }; + + fpgalsram: uio@60000000 { + compatible = "generic-uio"; + linux,uio-name = "fpga_lsram"; + reg = <0x0 0x60000000 0x0 0x1000>; + status = "disabled"; + }; + + i2c2: i2c@40000200 { + compatible = "microchip,corei2c-rtl-v7"; + reg = <0x0 0x40000200 0x0 0x100>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&ccc_nw CLK_CCC_PLL0_OUT3>; + interrupt-parent = <&plic>; + interrupts = <122>; + clock-frequency = <100000>; + status = "disabled"; + }; + }; + + ihc: mailbox { + compatible = "microchip,miv-ihc"; + interrupt-parent = <&plic>; + interrupts = <IHC_HART1_INT>; + microchip,miv-ihc-remote-context-id = <IHC_CONTEXT_B>; + #mbox-cells = <1>; + status = "disabled"; + }; + + mpfs_dma_proxy: mpfs-dma-proxy { + compatible = "microchip,mpfs-dma-proxy"; + dmas = <&pdma 0>, <&pdma 1>, <&pdma 2>, <&pdma 3>; + dma-names = "dma-proxy0", "dma-proxy1", "dma-proxy2", "dma-proxy3"; + }; +}; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs.dtsi linux-microchip/arch/riscv/boot/dts/microchip/mpfs.dtsi --- linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs.dtsi 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/riscv/boot/dts/microchip/mpfs.dtsi 2024-11-26 14:02:36.078468260 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:25 @ i-cache-size = <16384>; reg = <0>; riscv,isa = "rv64imac"; + riscv,isa-base = "rv64i"; + riscv,isa-extensions = "i", "m", "a", "c", "zicntr", "zicsr", "zifencei", + "zihpm"; clocks = <&clkcfg CLK_CPU>; status = "disabled"; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:54 @ mmu-type = "riscv,sv39"; reg = <1>; riscv,isa = "rv64imafdc"; + riscv,isa-base = "rv64i"; + riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "zicntr", "zicsr", + "zifencei", "zihpm"; clocks = <&clkcfg CLK_CPU>; tlb-split; next-level-cache = <&cctrllr>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:85 @ mmu-type = "riscv,sv39"; reg = <2>; riscv,isa = "rv64imafdc"; + riscv,isa-base = "rv64i"; + riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "zicntr", "zicsr", + "zifencei", "zihpm"; clocks = <&clkcfg CLK_CPU>; tlb-split; next-level-cache = <&cctrllr>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:116 @ mmu-type = "riscv,sv39"; reg = <3>; riscv,isa = "rv64imafdc"; + riscv,isa-base = "rv64i"; + riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "zicntr", "zicsr", + "zifencei", "zihpm"; clocks = <&clkcfg CLK_CPU>; tlb-split; next-level-cache = <&cctrllr>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:147 @ mmu-type = "riscv,sv39"; reg = <4>; riscv,isa = "rv64imafdc"; + riscv,isa-base = "rv64i"; + riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "zicntr", "zicsr", + "zifencei", "zihpm"; clocks = <&clkcfg CLK_CPU>; tlb-split; next-level-cache = <&cctrllr>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:196 @ mboxes = <&mbox 0>; }; + scbclk: mssclkclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <80000000>; + }; + soc { #address-cells = <2>; #size-cells = <2>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:223 @ clint: clint@2000000 { compatible = "sifive,fu540-c000-clint", "sifive,clint0"; reg = <0x0 0x2000000 0x0 0xC000>; - interrupts-extended = <&cpu0_intc 3>, <&cpu0_intc 7>, + interrupts-extended = <&cpu0_intc 0xffffffff>, <&cpu0_intc 0xffffffff>, <&cpu1_intc 3>, <&cpu1_intc 7>, <&cpu2_intc 3>, <&cpu2_intc 7>, <&cpu3_intc 3>, <&cpu3_intc 7>, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:236 @ #address-cells = <0>; #interrupt-cells = <1>; interrupt-controller; - interrupts-extended = <&cpu0_intc 11>, - <&cpu1_intc 11>, <&cpu1_intc 9>, - <&cpu2_intc 11>, <&cpu2_intc 9>, - <&cpu3_intc 11>, <&cpu3_intc 9>, - <&cpu4_intc 11>, <&cpu4_intc 9>; + interrupts-extended = <&cpu0_intc 0xffffffff>, + <&cpu1_intc 0xffffffff>, <&cpu1_intc 9>, + <&cpu2_intc 0xffffffff>, <&cpu2_intc 9>, + <&cpu3_intc 0xffffffff>, <&cpu3_intc 9>, + <&cpu4_intc 0xffffffff>, <&cpu4_intc 9>; riscv,ndev = <186>; }; - + pdma: dma-controller@3000000 { - compatible = "sifive,fu540-c000-pdma", "sifive,pdma0"; + compatible = "microchip,mpfs-pdma", "sifive,fu540-c000-pdma"; reg = <0x0 0x3000000 0x0 0x8000>; interrupt-parent = <&plic>; interrupts = <5 6>, <7 8>, <9 10>, <11 12>; - dma-channels = <4>; #dma-cells = <1>; }; - clkcfg: clkcfg@20002000 { - compatible = "microchip,mpfs-clkcfg"; - reg = <0x0 0x20002000 0x0 0x1000>, <0x0 0x3E001000 0x0 0x1000>; - clocks = <&refclk>; - #clock-cells = <1>; + mss_top_sysreg: syscon@20002000 { + compatible = "microchip,mpfs-mss-top-sysreg", "syscon", "simple-mfd"; + reg = <0x0 0x20002000 0x0 0x1000>; #reset-cells = <1>; + #address-cells = <1>; + #size-cells = <1>; + + irqmux: interrupt-controller@54 { + compatible = "microchip,mpfs-gpio-irq-mux"; + reg = <0x54 0x4>; + interrupt-parent = <&plic>; + interrupt-controller; + #interrupt-cells = <1>; + interrupts = <13>, <14>, <15>, <16>, + <17>, <18>, <19>, <20>, + <21>, <22>, <23>, <24>, + <25>, <26>, <27>, <28>, + <29>, <30>, <31>, <32>, + <33>, <34>, <35>, <36>, + <37>, <38>, <39>, <40>, + <41>, <42>, <43>, <44>, + <45>, <46>, <47>, <48>, + <49>, <50>, <51>, <52>, + <53>; + }; + }; + + sysreg_scb: syscon@20003000 { + compatible = "microchip,mpfs-sysreg-scb", "syscon"; + reg = <0x0 0x20003000 0x0 0x1000>; }; ccc_se: clock-controller@38010000 { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:447 @ can0: can@2010c000 { compatible = "microchip,mpfs-can"; reg = <0x0 0x2010c000 0x0 0x1000>; - clocks = <&clkcfg CLK_CAN0>; + clocks = <&clkcfg CLK_CAN0>, <&clkcfg CLK_MSSPLL3>; interrupt-parent = <&plic>; interrupts = <56>; + resets = <&mss_top_sysreg CLK_CAN0>; status = "disabled"; }; can1: can@2010d000 { compatible = "microchip,mpfs-can"; reg = <0x0 0x2010d000 0x0 0x1000>; - clocks = <&clkcfg CLK_CAN1>; + clocks = <&clkcfg CLK_CAN1>, <&clkcfg CLK_MSSPLL3>; interrupt-parent = <&plic>; interrupts = <57>; + resets = <&mss_top_sysreg CLK_CAN1>; status = "disabled"; }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:474 @ local-mac-address = [00 00 00 00 00 00]; clocks = <&clkcfg CLK_MAC0>, <&clkcfg CLK_AHB>; clock-names = "pclk", "hclk"; - resets = <&clkcfg CLK_MAC0>; + resets = <&mss_top_sysreg CLK_MAC0>; status = "disabled"; }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:488 @ local-mac-address = [00 00 00 00 00 00]; clocks = <&clkcfg CLK_MAC1>, <&clkcfg CLK_AHB>; clock-names = "pclk", "hclk"; - resets = <&clkcfg CLK_MAC1>; + resets = <&mss_top_sysreg CLK_MAC1>; status = "disabled"; }; gpio0: gpio@20120000 { compatible = "microchip,mpfs-gpio"; reg = <0x0 0x20120000 0x0 0x1000>; - interrupt-parent = <&plic>; + interrupt-parent = <&irqmux>; interrupt-controller; - #interrupt-cells = <1>; + #interrupt-cells = <2>; + interrupts = <0>, <1>, <2>, <3>, + <4>, <5>, <6>, <7>, + <8>, <9>, <10>, <11>, + <12>, <13>; clocks = <&clkcfg CLK_GPIO0>; gpio-controller; #gpio-cells = <2>; + ngpios = <14>; status = "disabled"; }; gpio1: gpio@20121000 { compatible = "microchip,mpfs-gpio"; reg = <0x0 0x20121000 0x0 0x1000>; - interrupt-parent = <&plic>; + interrupt-parent = <&irqmux>; interrupt-controller; - #interrupt-cells = <1>; + #interrupt-cells = <2>; + interrupts = <32>, <33>, <34>, <35>, + <36>, <37>, <38>, <39>, + <40>, <41>, <42>, <43>, + <44>, <45>, <46>, <47>, + <48>, <49>, <50>, <51>, + <52>, <53>, <54>, <55>; clocks = <&clkcfg CLK_GPIO1>; gpio-controller; #gpio-cells = <2>; + ngpios = <24>; status = "disabled"; }; gpio2: gpio@20122000 { compatible = "microchip,mpfs-gpio"; reg = <0x0 0x20122000 0x0 0x1000>; - interrupt-parent = <&plic>; + interrupt-parent = <&irqmux>; interrupt-controller; - #interrupt-cells = <1>; + #interrupt-cells = <2>; + interrupts = <64>, <65>, <66>, <67>, + <68>, <69>, <70>, <71>, + <72>, <73>, <74>, <75>, + <76>, <77>, <78>, <79>, + <80>, <81>, <82>, <83>, + <84>, <85>, <86>, <87>, + <88>, <89>, <90>, <91>, + <92>, <93>, <94>, <95>; clocks = <&clkcfg CLK_GPIO2>; gpio-controller; #gpio-cells = <2>; + ngpios = <32>; status = "disabled"; }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:565 @ interrupt-parent = <&plic>; interrupts = <86>, <87>; clocks = <&clkcfg CLK_USB>; - interrupt-names = "dma","mc"; + interrupt-names = "dma", "mc"; status = "disabled"; }; - mbox: mailbox@37020000 { + control_scb: syscon@37020000 { + compatible = "microchip,mpfs-control-scb", "syscon", "simple-mfd"; + reg = <0x0 0x37020000 0x0 0x100>; + }; + + mbox: mailbox@37020800 { compatible = "microchip,mpfs-mailbox"; - reg = <0x0 0x37020000 0x0 0x58>, <0x0 0x2000318C 0x0 0x40>, - <0x0 0x37020800 0x0 0x100>; + reg = <0x0 0x37020800 0x0 0x100>; interrupt-parent = <&plic>; interrupts = <96>; #mbox-cells = <1>; status = "disabled"; }; + + syscontroller_qspi: spi@37020100 { + compatible = "microchip,mpfs-qspi", "microchip,coreqspi-rtl-v2"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x37020100 0x0 0x100>; + interrupt-parent = <&plic>; + interrupts = <110>; + clocks = <&scbclk>; + status = "disabled"; + }; + + clkcfg: clkcfg@3e001000 { + compatible = "microchip,mpfs-clkcfg"; + reg = <0x0 0x3e001000 0x0 0x1000>; + clocks = <&refclk>; + #clock-cells = <1>; + }; + }; + + fabric-bus@22000000 { + compatible = "simple-bus"; + reg = <0x0 0x22000000 0x0 0x10000>; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + crypto: crypto@22000000 { + compatible = "microchip,mpfs-crypto"; + reg = <0x0 0x22000000 0x0 0x10000>; + clocks = <&clkcfg CLK_ATHENA>, <&clkcfg CLK_MSSPLL1>; + interrupt-parent = <&plic>; + interrupts = <112>; + status = "disabled"; + }; }; }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-icicle-kit.dts linux-microchip/arch/riscv/boot/dts/microchip/mpfs-icicle-kit.dts --- linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-icicle-kit.dts 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/riscv/boot/dts/microchip/mpfs-icicle-kit.dts 2024-11-26 14:02:36.078468260 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:15 @ #define RTCCLK_FREQ 1000000 / { + #address-cells = <2>; + #size-cells = <2>; model = "Microchip PolarFire-SoC Icicle Kit"; compatible = "microchip,mpfs-icicle-reference-rtlv2210", "microchip,mpfs-icicle-kit", "microchip,mpfs"; + soc { + dma-ranges = <0x14 0x0 0x0 0x80000000 0x0 0x4000000>, + <0x14 0x4000000 0x0 0xc4000000 0x0 0x6000000>, + <0x14 0xa000000 0x0 0x8a000000 0x0 0x8000000>, + <0x14 0x12000000 0x14 0x12000000 0x0 0x10000000>, + <0x14 0x22000000 0x10 0x22000000 0x0 0x5e000000>; + }; + aliases { ethernet0 = &mac1; serial0 = &mmuart0; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:74 @ }; }; - ddrc_cache_lo: memory@80000000 { + kernel: memory@80000000 { + device_type = "memory"; + reg = <0x0 0x80000000 0x0 0x4000000>; + }; + + ddr_cached_low: memory@8a000000 { + device_type = "memory"; + reg = <0x0 0x8a000000 0x0 0x8000000>; + }; + + ddr_non_cached_low: memory@c4000000 { device_type = "memory"; - reg = <0x0 0x80000000 0x0 0x40000000>; - status = "okay"; + reg = <0x0 0xc4000000 0x0 0x6000000>; }; - ddrc_cache_hi: memory@1040000000 { + ddr_cached_high: memory@1022000000 { device_type = "memory"; - reg = <0x10 0x40000000 0x0 0x40000000>; - status = "okay"; + reg = <0x10 0x22000000 0x0 0x5e000000>; + }; + + ddr_non_cached_high: memory@1412000000 { + device_type = "memory"; + reg = <0x14 0x12000000 0x0 0x10000000>; }; reserved-memory { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:104 @ #size-cells = <2>; ranges; - hss_payload: region@BFC00000 { - reg = <0x0 0xBFC00000 0x0 0x400000>; + hss: hss-buffer@103fc00000 { + compatible = "shared-dma-pool"; + reg = <0x10 0x3fc00000 0x0 0x400000>; + no-map; + }; + + dma_non_cached_low: non-cached-low-buffer { + compatible = "shared-dma-pool"; + size = <0x0 0x4000000>; + no-map; + alloc-ranges = <0x0 0xc4000000 0x0 0x4000000>; + }; + + dma_non_cached_high: non-cached-high-buffer { + compatible = "shared-dma-pool"; + size = <0x0 0x10000000>; + no-map; + linux,dma-default; + alloc-ranges = <0x14 0x12000000 0x0 0x10000000>; + }; + + fabricbuf0ddrc: buffer@88000000 { + compatible = "shared-dma-pool"; + reg = <0x0 0x88000000 0x0 0x2000000>; + no-map; + }; + + fabricbuf1ddrnc: buffer@c8000000 { + compatible = "shared-dma-pool"; + reg = <0x0 0xc8000000 0x0 0x2000000>; no-map; }; + + fabricbuf2ddrncwcb: buffer@d8000000 { + compatible = "shared-dma-pool"; + reg = <0x0 0xd8000000 0x0 0x2000000>; + no-map; + }; + }; + + udmabuf0 { + compatible = "ikwzm,u-dma-buf"; + device-name = "udmabuf-ddr-c0"; + minor-number = <0>; + size = <0x0 0x2000000>; + memory-region = <&fabricbuf0ddrc>; + sync-mode = <3>; + }; + + udmabuf1 { + compatible = "ikwzm,u-dma-buf"; + device-name = "udmabuf-ddr-nc0"; + minor-number = <1>; + size = <0x0 0x2000000>; + memory-region = <&fabricbuf1ddrnc>; + sync-mode = <3>; }; + + udmabuf2 { + compatible = "ikwzm,u-dma-buf"; + device-name = "udmabuf-ddr-nc-wcb0"; + minor-number = <2>; + size = <0x0 0x2000000>; + memory-region = <&fabricbuf2ddrncwcb>; + sync-mode = <3>; + }; +}; + +/* + * The label can0 represents CAN_0 in the MSS configurator which is CAN2 + * in the icicle kit schematic and on the icicle kit the can0 pins are + * routed to J27 header. + * The label can1 represents CAN_1 in the MSS configurator which is CAN1 + * in the icicle kit schematic and on the icicle kit the can1 pins are + * routed to J25 header. + */ +&can0 { + status = "okay"; +}; + +&can1 { + status = "okay"; }; &core_pwm0 { status = "okay"; }; +&fpgadma { + status = "okay"; +}; + +&fpgalsram { + status = "okay"; +}; + &gpio2 { - interrupts = <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>; status = "okay"; }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:210 @ &i2c1 { status = "okay"; + + power-monitor@10 { + compatible = "microchip,pac1934"; + reg = <0x10>; + + #address-cells = <1>; + #size-cells = <0>; + + channel@1 { + reg = <0x1>; + shunt-resistor-micro-ohms = <10000>; + label = "VDDREG"; + }; + + channel@2 { + reg = <0x2>; + shunt-resistor-micro-ohms = <10000>; + label = "VDDA25"; + }; + + channel@3 { + reg = <0x3>; + shunt-resistor-micro-ohms = <10000>; + label = "VDD25"; + }; + + channel@4 { + reg = <0x4>; + shunt-resistor-micro-ohms = <10000>; + label = "VDDA_REG"; + }; + }; }; &i2c2 { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:249 @ }; &mac0 { + dma-noncoherent; + status = "okay"; phy-mode = "sgmii"; phy-handle = <&phy0>; - status = "okay"; }; &mac1 { + dma-noncoherent; + status = "okay"; phy-mode = "sgmii"; phy-handle = <&phy1>; - status = "okay"; phy1: ethernet-phy@9 { reg = <9>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:275 @ }; &mmc { + dma-noncoherent; bus-width = <4>; disable-wp; cap-sd-highspeed; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:337 @ status = "okay"; }; +&syscontroller_qspi { + /* + * The flash *is* there, but Icicle kits that have engineering sample + * silicon where access to this flash to non-functional. The system + * controller itself can actually access it, but the MSS cannot write + * an image there. Instantiating a coreQSPI in the fabric & connecting + * it to the flash instead should work though. Pre-production or later + * silicon does not have this issue. + */ + status = "disabled"; + + sys_ctrl_flash: flash@0 { // MT25QL01GBBB8ESF-0SIT + compatible = "jedec,spi-nor"; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <20000000>; + spi-rx-bus-width = <1>; + reg = <0>; + }; +}; + &usb { + dma-noncoherent; status = "okay"; - dr_mode = "host"; + dr_mode = "otg"; }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-icicle-kit-fabric.dtsi linux-microchip/arch/riscv/boot/dts/microchip/mpfs-icicle-kit-fabric.dtsi --- linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-icicle-kit-fabric.dtsi 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/riscv/boot/dts/microchip/mpfs-icicle-kit-fabric.dtsi 2024-11-26 14:02:36.078468260 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ // SPDX-License-Identifier: (GPL-2.0 OR MIT) /* Copyright (c) 2020-2021 Microchip Technology Inc */ +#include "dt-bindings/mailbox/miv-ihc.h" + / { compatible = "microchip,mpfs-icicle-reference-rtlv2210", "microchip,mpfs-icicle-kit", "microchip,mpfs"; - core_pwm0: pwm@40000000 { - compatible = "microchip,corepwm-rtl-v4"; - reg = <0x0 0x40000000 0x0 0xF0>; - microchip,sync-update-mask = /bits/ 32 <0>; - #pwm-cells = <3>; - clocks = <&ccc_nw CLK_CCC_PLL0_OUT3>; - status = "disabled"; + fabric-bus@40000000 { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges = <0x0 0x40000000 0x0 0x40000000 0x0 0x20000000>, /* FIC3-FAB */ + <0x0 0x60000000 0x0 0x60000000 0x0 0x20000000>, /* FIC0, LO */ + <0x0 0xe0000000 0x0 0xe0000000 0x0 0x20000000>, /* FIC1, LO */ + <0x20 0x0 0x20 0x0 0x10 0x0>, /* FIC0,HI */ + <0x30 0x0 0x30 0x0 0x10 0x0>; /* FIC1,HI */ + + core_pwm0: pwm@40000000 { + compatible = "microchip,corepwm-rtl-v4"; + reg = <0x0 0x40000000 0x0 0xF0>; + microchip,sync-update-mask = /bits/ 32 <0>; + #pwm-cells = <3>; + clocks = <&ccc_nw CLK_CCC_PLL0_OUT3>; + status = "disabled"; + }; + + i2c2: i2c@40000200 { + compatible = "microchip,corei2c-rtl-v7"; + reg = <0x0 0x40000200 0x0 0x100>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&ccc_nw CLK_CCC_PLL0_OUT3>; + interrupt-parent = <&plic>; + interrupts = <122>; + clock-frequency = <100000>; + status = "disabled"; + }; + + fpgadma: dma-controller@60010000 { + compatible = "microchip,mpfs-fpga-dma"; + reg = <0x0 0x60010000 0x0 0x1000>; + interrupt-parent = <&plic>; + interrupts = <120>; + #dma-cells = <1>; + status = "disabled"; + }; + + fpgalsram: uio@60000000 { + compatible = "generic-uio"; + linux,uio-name = "fpga_lsram"; + reg = <0x0 0x60000000 0x0 0x1000>; + status = "disabled"; + }; }; - i2c2: i2c@40000200 { - compatible = "microchip,corei2c-rtl-v7"; - reg = <0x0 0x40000200 0x0 0x100>; - #address-cells = <1>; - #size-cells = <0>; - clocks = <&ccc_nw CLK_CCC_PLL0_OUT3>; + ihc: mailbox { + compatible = "microchip,miv-ihc"; interrupt-parent = <&plic>; - interrupts = <122>; - clock-frequency = <100000>; + interrupts = <IHC_HART1_INT>; + microchip,miv-ihc-remote-context-id = <IHC_CONTEXT_B>; + #mbox-cells = <1>; status = "disabled"; }; - pcie: pcie@3000000000 { - compatible = "microchip,pcie-host-1.0"; - #address-cells = <0x3>; - #interrupt-cells = <0x1>; - #size-cells = <0x2>; - device_type = "pci"; - reg = <0x30 0x0 0x0 0x8000000>, <0x0 0x43000000 0x0 0x10000>; - reg-names = "cfg", "apb"; - bus-range = <0x0 0x7f>; - interrupt-parent = <&plic>; - interrupts = <119>; - interrupt-map = <0 0 0 1 &pcie_intc 0>, - <0 0 0 2 &pcie_intc 1>, - <0 0 0 3 &pcie_intc 2>, - <0 0 0 4 &pcie_intc 3>; - interrupt-map-mask = <0 0 0 7>; - clocks = <&ccc_nw CLK_CCC_PLL0_OUT1>, <&ccc_nw CLK_CCC_PLL0_OUT3>; - clock-names = "fic1", "fic3"; - ranges = <0x3000000 0x0 0x8000000 0x30 0x8000000 0x0 0x80000000>; - dma-ranges = <0x02000000 0x0 0x00000000 0x0 0x00000000 0x1 0x00000000>; - msi-parent = <&pcie>; - msi-controller; - status = "disabled"; - pcie_intc: interrupt-controller { - #address-cells = <0>; - #interrupt-cells = <1>; - interrupt-controller; + mpfs_dma_proxy: mpfs-dma-proxy { + compatible = "microchip,mpfs-dma-proxy"; + dmas = <&pdma 0>, <&pdma 1>, <&pdma 2>, <&pdma 3>; + dma-names = "dma-proxy0", "dma-proxy1", "dma-proxy2", "dma-proxy3"; + }; + + fabric-pcie-bus@3000000000 { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges = <0x0 0x40000000 0x0 0x40000000 0x0 0x20000000>, + <0x30 0x0 0x30 0x0 0x10 0x0>; + dma-ranges = <0x0 0x0 0x0 0x80000000 0x0 0x4000000>, + <0x0 0x4000000 0x0 0xc4000000 0x0 0x6000000>, + <0x0 0xa000000 0x0 0x8a000000 0x0 0x8000000>, + <0x0 0x12000000 0x14 0x12000000 0x0 0x10000000>, + <0x0 0x22000000 0x10 0x22000000 0x0 0x5e000000>; + + pcie: pcie@3000000000 { + compatible = "microchip,pcie-host-1.0"; + #address-cells = <0x3>; + #interrupt-cells = <0x1>; + #size-cells = <0x2>; + device_type = "pci"; + dma-noncoherent; + reg = <0x30 0x0 0x0 0x8000000>, <0x0 0x43008000 0x0 0x2000>, <0x0 0x4300a000 0x0 0x2000>; + reg-names = "cfg", "bridge", "ctrl"; + bus-range = <0x0 0x7f>; + interrupt-parent = <&plic>; + interrupts = <119>; + interrupt-map = <0 0 0 1 &pcie_intc 0>, + <0 0 0 2 &pcie_intc 1>, + <0 0 0 3 &pcie_intc 2>, + <0 0 0 4 &pcie_intc 3>; + interrupt-map-mask = <0 0 0 7>; + clocks = <&ccc_nw CLK_CCC_PLL0_OUT1>, <&ccc_nw CLK_CCC_PLL0_OUT3>; + clock-names = "fic1", "fic3"; + ranges = <0x43000000 0x0 0x9000000 0x30 0x9000000 0x0 0xf000000>, + <0x1000000 0x0 0x8000000 0x30 0x8000000 0x0 0x1000000>, + <0x3000000 0x0 0x18000000 0x30 0x18000000 0x0 0x70000000>; + dma-ranges = <0x3000000 0x0 0x80000000 0x0 0x0 0x0 0x4000000>, + <0x3000000 0x0 0x84000000 0x0 0x4000000 0x0 0x6000000>, + <0x3000000 0x0 0x8a000000 0x0 0xa000000 0x0 0x8000000>, + <0x3000000 0x0 0x92000000 0x0 0x12000000 0x0 0x10000000>, + <0x3000000 0x0 0xa2000000 0x0 0x22000000 0x0 0x5e000000>; + msi-parent = <&pcie>; + msi-controller; + status = "disabled"; + + pcie_intc: interrupt-controller { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + }; }; }; refclk_ccc: cccrefclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + }; + + refclk_ccc: cccrefclk { compatible = "fixed-clock"; #clock-cells = <0>; }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-m100pfsevp.dts linux-microchip/arch/riscv/boot/dts/microchip/mpfs-m100pfsevp.dts --- linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-m100pfsevp.dts 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/riscv/boot/dts/microchip/mpfs-m100pfsevp.dts 1970-01-01 01:00:00.000000000 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1 @ -// SPDX-License-Identifier: GPL-2.0 -/* - * Original all-in-one devicetree: - * Copyright (C) 2021-2022 - Wolfgang Grandegger <wg@aries-embedded.de> - * Rewritten to use includes: - * Copyright (C) 2022 - Conor Dooley <conor.dooley@microchip.com> - */ -/dts-v1/; - -#include "mpfs.dtsi" -#include "mpfs-m100pfs-fabric.dtsi" - -/* Clock frequency (in Hz) of the rtcclk */ -#define MTIMER_FREQ 1000000 - -/ { - model = "Aries Embedded M100PFEVPS"; - compatible = "aries,m100pfsevp", "microchip,mpfs"; - - aliases { - ethernet0 = &mac0; - ethernet1 = &mac1; - serial0 = &mmuart0; - serial1 = &mmuart1; - serial2 = &mmuart2; - serial3 = &mmuart3; - serial4 = &mmuart4; - gpio0 = &gpio0; - gpio1 = &gpio2; - }; - - chosen { - stdout-path = "serial1:115200n8"; - }; - - cpus { - timebase-frequency = <MTIMER_FREQ>; - }; - - ddrc_cache_lo: memory@80000000 { - device_type = "memory"; - reg = <0x0 0x80000000 0x0 0x40000000>; - }; - ddrc_cache_hi: memory@1040000000 { - device_type = "memory"; - reg = <0x10 0x40000000 0x0 0x40000000>; - }; -}; - -&can0 { - status = "okay"; -}; - -&i2c0 { - status = "okay"; -}; - -&i2c1 { - status = "okay"; -}; - -&gpio0 { - interrupts = <13>, <14>, <15>, <16>, - <17>, <18>, <19>, <20>, - <21>, <22>, <23>, <24>, - <25>, <26>; - ngpios = <14>; - status = "okay"; - - pmic-irq-hog { - gpio-hog; - gpios = <13 0>; - input; - }; - - /* Set to low for eMMC, high for SD-card */ - mmc-sel-hog { - gpio-hog; - gpios = <12 0>; - output-high; - }; -}; - -&gpio2 { - interrupts = <13>, <14>, <15>, <16>, - <17>, <18>, <19>, <20>, - <21>, <22>, <23>, <24>, - <25>, <26>, <27>, <28>, - <29>, <30>, <31>, <32>, - <33>, <34>, <35>, <36>, - <37>, <38>, <39>, <40>, - <41>, <42>, <43>, <44>; - status = "okay"; -}; - -&mac0 { - status = "okay"; - phy-mode = "gmii"; - phy-handle = <&phy0>; - phy0: ethernet-phy@0 { - reg = <0>; - }; -}; - -&mac1 { - status = "okay"; - phy-mode = "gmii"; - phy-handle = <&phy1>; - phy1: ethernet-phy@0 { - reg = <0>; - }; -}; - -&mbox { - status = "okay"; -}; - -&mmc { - max-frequency = <50000000>; - bus-width = <4>; - cap-mmc-highspeed; - cap-sd-highspeed; - no-1-8-v; - sd-uhs-sdr12; - sd-uhs-sdr25; - sd-uhs-sdr50; - sd-uhs-sdr104; - disable-wp; - status = "okay"; -}; - -&mmuart1 { - status = "okay"; -}; - -&mmuart2 { - status = "okay"; -}; - -&mmuart3 { - status = "okay"; -}; - -&mmuart4 { - status = "okay"; -}; - -&pcie { - status = "okay"; -}; - -&qspi { - status = "okay"; -}; - -&refclk { - clock-frequency = <125000000>; -}; - -&rtc { - status = "okay"; -}; - -&spi0 { - status = "okay"; -}; - -&spi1 { - status = "okay"; -}; - -&syscontroller { - status = "okay"; -}; - -&usb { - status = "okay"; - dr_mode = "host"; -}; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-m100pfsevp.dtsi linux-microchip/arch/riscv/boot/dts/microchip/mpfs-m100pfsevp.dtsi --- linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-m100pfsevp.dtsi 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/arch/riscv/boot/dts/microchip/mpfs-m100pfsevp.dtsi 2024-11-26 14:02:36.078468260 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Original all-in-one devicetree: + * Copyright (C) 2021-2023 - Wolfgang Grandegger <wg@aries-embedded.de> + * Rewritten to use includes: + * Copyright (C) 2022 - Conor Dooley <conor.dooley@microchip.com> + */ +/dts-v1/; + +#include "mpfs.dtsi" +#include "mpfs-m100pfs-fabric.dtsi" + +/* Clock frequency (in Hz) of the rtcclk */ +#define MTIMER_FREQ 1000000 + +/ { + model = "Aries Embedded M100PFEVPS"; + compatible = "aries,m100pfsevp", "microchip,mpfs"; + + soc { + dma-ranges = <0 0 0 0 0x40 0>; + }; + + aliases { + ethernet0 = &mac0; + ethernet1 = &mac1; + serial0 = &mmuart0; + serial1 = &mmuart1; + serial2 = &mmuart2; + serial3 = &mmuart3; + serial4 = &mmuart4; + gpio0 = &gpio0; + gpio1 = &gpio2; + }; + + chosen { + stdout-path = "serial1:115200n8"; + }; + + cpus { + timebase-frequency = <MTIMER_FREQ>; + }; + + ddrc_cache_lo: memory@80000000 { + device_type = "memory"; + reg = <0x0 0x80000000 0x0 0x40000000>; + }; + ddrc_cache_hi: memory@1040000000 { + device_type = "memory"; + reg = <0x10 0x40000000 0x0 0x40000000>; + }; +}; + +&can0 { + status = "okay"; +}; + +&i2c0 { + status = "okay"; +}; + +&i2c1 { + status = "okay"; +}; + +&gpio0 { + ngpios = <14>; + status = "okay"; + + pmic-irq-hog { + gpio-hog; + gpios = <13 0>; + input; + }; +}; + +&gpio2 { + status = "okay"; +}; + +&mac0 { + status = "okay"; + phy-mode = "gmii"; + phy-handle = <&phy0>; + phy0: ethernet-phy@0 { + reg = <0>; + }; +}; + +&mac1 { + status = "okay"; + phy-mode = "gmii"; + phy-handle = <&phy1>; + phy1: ethernet-phy@0 { + reg = <0>; + }; +}; + +&mbox { + status = "okay"; +}; + +&mmuart1 { + status = "okay"; +}; + +&mmuart2 { + status = "okay"; +}; + +&mmuart3 { + status = "okay"; +}; + +&mmuart4 { + status = "okay"; +}; + +&pcie { + status = "okay"; +}; + +&qspi { + status = "okay"; +}; + +&refclk { + clock-frequency = <125000000>; +}; + +&rtc { + status = "okay"; +}; + +&spi0 { + status = "okay"; +}; + +&spi1 { + status = "okay"; +}; + +&syscontroller { + status = "okay"; +}; + +&usb { + status = "okay"; + dr_mode = "host"; +}; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-m100pfsevp-emmc.dts linux-microchip/arch/riscv/boot/dts/microchip/mpfs-m100pfsevp-emmc.dts --- linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-m100pfsevp-emmc.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/arch/riscv/boot/dts/microchip/mpfs-m100pfsevp-emmc.dts 2024-11-26 14:02:36.078468260 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Original all-in-one devicetree: + * Copyright (C) 2021-2023 - Wolfgang Grandegger <wg@aries-embedded.de> + * Rewritten to use includes: + * Copyright (C) 2022 - Conor Dooley <conor.dooley@microchip.com> + */ +/dts-v1/; + +#include "mpfs-m100pfsevp.dtsi" + +&gpio0 { + /* Set to low for eMMC, high for SD-card */ + mmc-sel-hog { + gpio-hog; + gpios = <12 0>; + output-low; + }; +}; + +&mmc { + max-frequency = <50000000>; + bus-width = <8>; + cap-mmc-highspeed; + no-1-8-v; + non-removable; + disable-wp; + status = "okay"; +}; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-m100pfsevp-sdcard.dts linux-microchip/arch/riscv/boot/dts/microchip/mpfs-m100pfsevp-sdcard.dts --- linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-m100pfsevp-sdcard.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/arch/riscv/boot/dts/microchip/mpfs-m100pfsevp-sdcard.dts 2024-11-26 14:02:36.078468260 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Original all-in-one devicetree: + * Copyright (C) 2021-2023 - Wolfgang Grandegger <wg@aries-embedded.de> + * Rewritten to use includes: + * Copyright (C) 2022 - Conor Dooley <conor.dooley@microchip.com> + */ +/dts-v1/; + +#include "mpfs-m100pfsevp.dtsi" + +/* Clock frequency (in Hz) of the rtcclk */ +#define MTIMER_FREQ 1000000 + +&gpio0 { + /* Set to low for eMMC, high for SD-card */ + mmc-sel-hog { + gpio-hog; + gpios = <12 0>; + output-high; + }; +}; + +&mmc { + max-frequency = <50000000>; + bus-width = <4>; + cap-sd-highspeed; + no-1-8-v; + sd-uhs-sdr12; + sd-uhs-sdr25; + sd-uhs-sdr50; + sd-uhs-sdr104; + disable-wp; + status = "okay"; +}; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-m100pfs-fabric.dtsi linux-microchip/arch/riscv/boot/dts/microchip/mpfs-m100pfs-fabric.dtsi --- linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-m100pfs-fabric.dtsi 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/riscv/boot/dts/microchip/mpfs-m100pfs-fabric.dtsi 2024-11-26 14:02:36.078468260 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:17 @ clock-frequency = <125000000>; }; - pcie: pcie@2000000000 { - compatible = "microchip,pcie-host-1.0"; - #address-cells = <0x3>; - #interrupt-cells = <0x1>; - #size-cells = <0x2>; - device_type = "pci"; - reg = <0x20 0x0 0x0 0x8000000>, <0x0 0x43000000 0x0 0x10000>; - reg-names = "cfg", "apb"; - bus-range = <0x0 0x7f>; - interrupt-parent = <&plic>; - interrupts = <119>; - interrupt-map = <0 0 0 1 &pcie_intc 0>, - <0 0 0 2 &pcie_intc 1>, - <0 0 0 3 &pcie_intc 2>, - <0 0 0 4 &pcie_intc 3>; - interrupt-map-mask = <0 0 0 7>; - clocks = <&fabric_clk1>, <&fabric_clk3>; - clock-names = "fic0", "fic3"; - ranges = <0x3000000 0x0 0x8000000 0x20 0x8000000 0x0 0x80000000>; - msi-parent = <&pcie>; - msi-controller; - status = "disabled"; - pcie_intc: interrupt-controller { - #address-cells = <0>; - #interrupt-cells = <1>; - interrupt-controller; + fabric-pci-bus@2000000000 { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges = <0x0 0x40000000 0x0 0x40000000 0x0 0x20000000>, + <0x20 0x0 0x20 0x0 0x10 0x0>; + + pcie: pcie@3000000000 { + compatible = "microchip,pcie-host-1.0"; + #address-cells = <0x3>; + #interrupt-cells = <0x1>; + #size-cells = <0x2>; + device_type = "pci"; + reg = <0x30 0x0 0x0 0x8000000>, <0x0 0x43008000 0x0 0x2000>, <0x0 0x4300a000 0x0 0x2000>; + reg-names = "cfg", "bridge", "ctrl"; + bus-range = <0x0 0x7f>; + interrupt-parent = <&plic>; + interrupts = <119>; + interrupt-map = <0 0 0 1 &pcie_intc 0>, + <0 0 0 2 &pcie_intc 1>, + <0 0 0 3 &pcie_intc 2>, + <0 0 0 4 &pcie_intc 3>; + interrupt-map-mask = <0 0 0 7>; + clocks = <&fabric_clk1>, <&fabric_clk3>; + clock-names = "fic0", "fic3"; + ranges = <0x3000000 0x0 0x8000000 0x20 0x8000000 0x0 0x80000000>; + dma-ranges = <0x3000000 0x0 0x80000000 0x0 0x80000000 0x0 0x40000000>; + msi-parent = <&pcie>; + msi-controller; + status = "disabled"; + + pcie_intc: interrupt-controller { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + }; }; }; }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-polarberry.dts linux-microchip/arch/riscv/boot/dts/microchip/mpfs-polarberry.dts --- linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-polarberry.dts 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/riscv/boot/dts/microchip/mpfs-polarberry.dts 2024-11-26 14:02:36.078468260 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:16 @ model = "Sundance PolarBerry"; compatible = "sundance,polarberry", "microchip,mpfs"; + soc { + dma-ranges = <0 0 0 0 0x40 0>; + }; + aliases { ethernet0 = &mac1; serial0 = &mmuart0; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-polarberry-fabric.dtsi linux-microchip/arch/riscv/boot/dts/microchip/mpfs-polarberry-fabric.dtsi --- linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-polarberry-fabric.dtsi 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/riscv/boot/dts/microchip/mpfs-polarberry-fabric.dtsi 2024-11-26 14:02:36.078468260 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:17 @ clock-frequency = <125000000>; }; - pcie: pcie@2000000000 { - compatible = "microchip,pcie-host-1.0"; - #address-cells = <0x3>; - #interrupt-cells = <0x1>; - #size-cells = <0x2>; - device_type = "pci"; - reg = <0x20 0x0 0x0 0x8000000>, <0x0 0x43000000 0x0 0x10000>; - reg-names = "cfg", "apb"; - bus-range = <0x0 0x7f>; - interrupt-parent = <&plic>; - interrupts = <119>; - interrupt-map = <0 0 0 1 &pcie_intc 0>, - <0 0 0 2 &pcie_intc 1>, - <0 0 0 3 &pcie_intc 2>, - <0 0 0 4 &pcie_intc 3>; - interrupt-map-mask = <0 0 0 7>; - clocks = <&fabric_clk1>, <&fabric_clk3>; - clock-names = "fic0", "fic3"; - ranges = <0x3000000 0x0 0x8000000 0x20 0x8000000 0x0 0x80000000>; - msi-parent = <&pcie>; - msi-controller; - status = "disabled"; - pcie_intc: interrupt-controller { - #address-cells = <0>; - #interrupt-cells = <1>; - interrupt-controller; + fabric-pci-bus@2000000000 { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges = <0x0 0x40000000 0x0 0x40000000 0x0 0x20000000>, + <0x20 0x0 0x20 0x0 0x10 0x0>; + + pcie: pcie@3000000000 { + compatible = "microchip,pcie-host-1.0"; + #address-cells = <0x3>; + #interrupt-cells = <0x1>; + #size-cells = <0x2>; + device_type = "pci"; + reg = <0x30 0x0 0x0 0x8000000>, <0x0 0x43008000 0x0 0x2000>, <0x0 0x4300a000 0x0 0x2000>; + reg-names = "cfg", "bridge", "ctrl"; + bus-range = <0x0 0x7f>; + interrupt-parent = <&plic>; + interrupts = <119>; + interrupt-map = <0 0 0 1 &pcie_intc 0>, + <0 0 0 2 &pcie_intc 1>, + <0 0 0 3 &pcie_intc 2>, + <0 0 0 4 &pcie_intc 3>; + interrupt-map-mask = <0 0 0 7>; + clocks = <&fabric_clk1>, <&fabric_clk3>; + clock-names = "fic0", "fic3"; + ranges = <0x3000000 0x0 0x8000000 0x20 0x8000000 0x0 0x80000000>; + dma-ranges = <0x3000000 0x0 0x80000000 0x0 0x80000000 0x0 0x40000000>; + msi-parent = <&pcie>; + msi-controller; + status = "disabled"; + + pcie_intc: interrupt-controller { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + }; }; }; }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-sev-kit.dts linux-microchip/arch/riscv/boot/dts/microchip/mpfs-sev-kit.dts --- linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-sev-kit.dts 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/riscv/boot/dts/microchip/mpfs-sev-kit.dts 1970-01-01 01:00:00.000000000 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1 @ -// SPDX-License-Identifier: (GPL-2.0 OR MIT) -/* Copyright (c) 2022 Microchip Technology Inc */ - -/dts-v1/; - -#include "mpfs.dtsi" -#include "mpfs-sev-kit-fabric.dtsi" - -/* Clock frequency (in Hz) of the rtcclk */ -#define MTIMER_FREQ 1000000 - -/ { - #address-cells = <2>; - #size-cells = <2>; - model = "Microchip PolarFire-SoC SEV Kit"; - compatible = "microchip,mpfs-sev-kit", "microchip,mpfs"; - - aliases { - ethernet0 = &mac1; - serial0 = &mmuart0; - serial1 = &mmuart1; - serial2 = &mmuart2; - serial3 = &mmuart3; - serial4 = &mmuart4; - }; - - chosen { - stdout-path = "serial1:115200n8"; - }; - - cpus { - timebase-frequency = <MTIMER_FREQ>; - }; - - reserved-memory { - #address-cells = <2>; - #size-cells = <2>; - ranges; - - fabricbuf0ddrc: buffer@80000000 { - compatible = "shared-dma-pool"; - reg = <0x0 0x80000000 0x0 0x2000000>; - }; - - fabricbuf1ddrnc: buffer@c4000000 { - compatible = "shared-dma-pool"; - reg = <0x0 0xc4000000 0x0 0x4000000>; - }; - - fabricbuf2ddrncwcb: buffer@d4000000 { - compatible = "shared-dma-pool"; - reg = <0x0 0xd4000000 0x0 0x4000000>; - }; - }; - - ddrc_cache: memory@1000000000 { - device_type = "memory"; - reg = <0x10 0x0 0x0 0x76000000>; - }; -}; - -&i2c0 { - status = "okay"; -}; - -&gpio2 { - interrupts = <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>; - status = "okay"; -}; - -&mac0 { - status = "okay"; - phy-mode = "sgmii"; - phy-handle = <&phy0>; - phy1: ethernet-phy@9 { - reg = <9>; - }; - phy0: ethernet-phy@8 { - reg = <8>; - }; -}; - -&mac1 { - status = "okay"; - phy-mode = "sgmii"; - phy-handle = <&phy1>; -}; - -&mbox { - status = "okay"; -}; - -&mmc { - status = "okay"; - bus-width = <4>; - disable-wp; - cap-sd-highspeed; - cap-mmc-highspeed; - mmc-ddr-1_8v; - mmc-hs200-1_8v; - sd-uhs-sdr12; - sd-uhs-sdr25; - sd-uhs-sdr50; - sd-uhs-sdr104; -}; - -&mmuart1 { - status = "okay"; -}; - -&mmuart2 { - status = "okay"; -}; - -&mmuart3 { - status = "okay"; -}; - -&mmuart4 { - status = "okay"; -}; - -&refclk { - clock-frequency = <125000000>; -}; - -&rtc { - status = "okay"; -}; - -&syscontroller { - status = "okay"; -}; - -&usb { - status = "okay"; - dr_mode = "otg"; -}; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-sev-kit-fabric.dtsi linux-microchip/arch/riscv/boot/dts/microchip/mpfs-sev-kit-fabric.dtsi --- linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-sev-kit-fabric.dtsi 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/riscv/boot/dts/microchip/mpfs-sev-kit-fabric.dtsi 1970-01-01 01:00:00.000000000 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1 @ -// SPDX-License-Identifier: (GPL-2.0 OR MIT) -/* Copyright (c) 2022 Microchip Technology Inc */ - -/ { - fabric_clk3: fabric-clk3 { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <0>; - }; - - fabric_clk1: fabric-clk1 { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <125000000>; - }; -}; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-tysom-m.dts linux-microchip/arch/riscv/boot/dts/microchip/mpfs-tysom-m.dts --- linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-tysom-m.dts 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/riscv/boot/dts/microchip/mpfs-tysom-m.dts 2024-11-26 14:02:36.078468260 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:21 @ model = "Aldec TySOM-M-MPFS250T-REV2"; compatible = "aldec,tysom-m-mpfs250t-rev2", "microchip,mpfs"; + soc { + dma-ranges = <0 0 0 0 0x40 0>; + }; + aliases { ethernet0 = &mac0; ethernet1 = &mac1; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:48 @ ddrc_cache_lo: memory@80000000 { device_type = "memory"; reg = <0x0 0x80000000 0x0 0x30000000>; - status = "okay"; }; ddrc_cache_hi: memory@1000000000 { device_type = "memory"; - reg = <0x10 0x00000000 0x0 0x40000000>; - status = "okay"; + reg = <0x10 0x0 0x0 0x40000000>; }; leds { compatible = "gpio-leds"; - status = "okay"; - - led0 { + led-0 { gpios = <&gpio1 23 1>; default-state = "on"; linux,default-trigger = "heartbeat"; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:72 @ &i2c1 { status = "okay"; hwmon: hwmon@45 { - status = "okay"; compatible = "ti,ina219"; reg = <0x45>; shunt-resistor = <2000>; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:79 @ }; &gpio1 { - interrupts = <27>, <28>, <29>, <30>, - <31>, <32>, <33>, <47>, - <35>, <36>, <37>, <38>, - <39>, <40>, <41>, <42>, - <43>, <44>, <45>, <46>, - <47>, <48>, <49>, <50>; status = "okay"; }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:146 @ status = "okay"; flash@0 { compatible = "micron,n25q128a11", "jedec,spi-nor"; - reg = <0x0>; + status = "okay"; spi-max-frequency = <10000000>; + reg = <0x0>; }; }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-video-kit.dts linux-microchip/arch/riscv/boot/dts/microchip/mpfs-video-kit.dts --- linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-video-kit.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/arch/riscv/boot/dts/microchip/mpfs-video-kit.dts 2024-11-26 14:02:36.078468260 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* Copyright (c) 2022 Microchip Technology Inc */ + +/dts-v1/; + +#include "mpfs.dtsi" +#include "mpfs-video-kit-fabric.dtsi" + +/* Clock frequency (in Hz) of the rtcclk */ +#define MTIMER_FREQ 1000000 + +/ { + #address-cells = <2>; + #size-cells = <2>; + model = "Microchip PolarFire-SoC VIDEO Kit"; + compatible = "microchip,mpfs-video-kit", "microchip,mpfs-sev-kit", "microchip,mpfs"; + + soc { + dma-ranges = <0x14 0x0 0x0 0x80000000 0x0 0x4000000>, + <0x14 0x4000000 0x0 0xc4000000 0x0 0x6000000>, + <0x14 0xa000000 0x0 0x8a000000 0x0 0x8000000>, + <0x14 0x12000000 0x14 0x12000000 0x0 0x10000000>, + <0x14 0x22000000 0x10 0x22000000 0x0 0x5e000000>; + }; + + aliases { + ethernet0 = &mac1; + serial0 = &mmuart0; + serial1 = &mmuart1; + serial2 = &mmuart2; + serial3 = &mmuart3; + serial4 = &mmuart4; + }; + + chosen { + stdout-path = "serial1:115200n8"; + }; + + cpus { + timebase-frequency = <MTIMER_FREQ>; + }; + + kernel: memory@80000000 { + device_type = "memory"; + reg = <0x0 0x80000000 0x0 0x4000000>; + }; + + ddr_cached_low: memory@8a000000 { + device_type = "memory"; + reg = <0x0 0x8a000000 0x0 0x8000000>; + }; + + ddr_non_cached_low: memory@c4000000 { + device_type = "memory"; + reg = <0x0 0xc4000000 0x0 0x6000000>; + }; + + ddr_cached_high: memory@1022000000 { + device_type = "memory"; + reg = <0x10 0x22000000 0x0 0x5e000000>; + }; + + ddr_non_cached_high: memory@1412000000 { + device_type = "memory"; + reg = <0x14 0x12000000 0x0 0x10000000>; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + hss: hss-buffer@103fc00000 { + compatible = "shared-dma-pool"; + reg = <0x10 0x3fc00000 0x0 0x400000>; + no-map; + }; + + dma_non_cached_low: non-cached-low-buffer { + compatible = "shared-dma-pool"; + size = <0x0 0x2000000>; + no-map; + alloc-ranges = <0x0 0xc4000000 0x0 0x2000000>; + }; + + dma_non_cached_high: non-cached-high-buffer { + compatible = "shared-dma-pool"; + size = <0x0 0x10000000>; + no-map; + linux,dma-default; + alloc-ranges = <0x14 0x12000000 0x0 0x10000000>; + }; + + fabricbuf0ddrc: buffer@88000000 { + compatible = "shared-dma-pool"; + reg = <0x0 0x88000000 0x0 0x2000000>; + no-map; + }; + + fabricbuf1ddrnc: buffer@c8000000 { + compatible = "shared-dma-pool"; + reg = <0x0 0xc8000000 0x0 0x2000000>; + no-map; + }; + + fabricbuf2ddrncwcb: buffer@d8000000 { + compatible = "shared-dma-pool"; + reg = <0x0 0xd8000000 0x0 0x2000000>; + no-map; + }; + }; + + udmabuf0 { + compatible = "ikwzm,u-dma-buf"; + device-name = "udmabuf-ddr-c0"; + minor-number = <0>; + size = <0x0 0x2000000>; + memory-region = <&fabricbuf0ddrc>; + sync-mode = <3>; + }; + + udmabuf1 { + compatible = "ikwzm,u-dma-buf"; + device-name = "udmabuf-ddr-nc0"; + minor-number = <1>; + size = <0x0 0x2000000>; + memory-region = <&fabricbuf1ddrnc>; + sync-mode = <3>; + }; + + udmabuf2 { + compatible = "ikwzm,u-dma-buf"; + device-name = "udmabuf-ddr-nc-wcb0"; + minor-number = <2>; + size = <0x0 0x2000000>; + memory-region = <&fabricbuf2ddrncwcb>; + sync-mode = <3>; + }; +}; + +&crypto { + status = "okay"; +}; + +&i2c0 { + status = "okay"; +}; + +&gpio2 { + status = "okay"; +}; + +&mac0 { + dma-noncoherent; + status = "okay"; + phy-mode = "sgmii"; + phy-handle = <&phy0>; + phy1: ethernet-phy@9 { + reg = <9>; + }; + phy0: ethernet-phy@8 { + reg = <8>; + }; +}; + +&mac1 { + dma-noncoherent; + status = "okay"; + phy-mode = "sgmii"; + phy-handle = <&phy1>; +}; + +&mbox { + status = "okay"; +}; + +&mmc { + dma-noncoherent; + status = "okay"; + bus-width = <4>; + disable-wp; + cap-sd-highspeed; + cap-mmc-highspeed; + mmc-ddr-1_8v; + mmc-hs200-1_8v; + sd-uhs-sdr12; + sd-uhs-sdr25; + sd-uhs-sdr50; + sd-uhs-sdr104; +}; + +&mmuart1 { + status = "okay"; +}; + +&mmuart2 { + status = "okay"; +}; + +&mmuart3 { + status = "okay"; +}; + +&mmuart4 { + status = "okay"; +}; + +&refclk { + clock-frequency = <125000000>; +}; + +&rtc { + status = "okay"; +}; + +&syscontroller { + microchip,bitstream-flash = <&sys_ctrl_flash>; + status = "okay"; +}; + +&syscontroller_qspi { + status = "okay"; + + sys_ctrl_flash: flash@0 { + compatible = "jedec,spi-nor"; + #address-cells = <1>; + #size-cells = <1>; + spi-max-frequency = <20000000>; + spi-rx-bus-width = <1>; + reg = <0>; + }; +}; + +&usb { + dma-noncoherent; + status = "okay"; + dr_mode = "otg"; +}; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-video-kit-fabric.dtsi linux-microchip/arch/riscv/boot/dts/microchip/mpfs-video-kit-fabric.dtsi --- linux-6.6.51/arch/riscv/boot/dts/microchip/mpfs-video-kit-fabric.dtsi 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/arch/riscv/boot/dts/microchip/mpfs-video-kit-fabric.dtsi 2024-11-26 14:02:36.078468260 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* Copyright (c) 2022 Microchip Technology Inc */ + +/ { + + fabric_bus: fabric-bus@40000000 { + compatible = "simple-bus"; + #address-cells = <2>; + #size-cells = <2>; + ranges = <0x0 0x40000000 0x0 0x40000000 0x0 0x20000000>, /* FIC3-FAB */ + <0x0 0x60000000 0x0 0x60000000 0x0 0x20000000>, /* FIC0, LO */ + <0x0 0xe0000000 0x0 0xe0000000 0x0 0x20000000>, /* FIC1, LO */ + <0x20 0x0 0x20 0x0 0x10 0x0>, /* FIC0,HI */ + <0x30 0x0 0x30 0x0 0x10 0x0>; /* FIC1,HI */ + }; +}; + diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/riscv/boot/dts/microchip/pic64gx-curiosity-kit.dts linux-microchip/arch/riscv/boot/dts/microchip/pic64gx-curiosity-kit.dts --- linux-6.6.51/arch/riscv/boot/dts/microchip/pic64gx-curiosity-kit.dts 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/arch/riscv/boot/dts/microchip/pic64gx-curiosity-kit.dts 2024-11-26 14:02:36.078468260 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Device Tree Source for the PIC64GX Curiosity Kit + * + * Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries + * + * Author: Pierre-Henry Moussay <pierre-henry.moussay@microchip.com> + * + * The Curiosity-GX10000 (PIC64GX SoC Curiosity Kit) is a compact SoC + * prototyping board featuring a Microchip PIC64GX SoC + * PIC64GX-1000. Features include: + * - 1 GB DDR4 SDRAM + * - Gigabit Ethernet + * - microSD-card slot + * + * https://www.microchip.com/en-us/development-tool/curiosity-pic64gx1000-kit-es + */ + +/dts-v1/; + +#include "pic64gx.dtsi" + +/* Clock frequency (in Hz) of the rtcclk */ +#define RTCCLK_FREQ 1000000 + +/ { + #address-cells = <2>; + #size-cells = <2>; + model = "Microchip PIC64GX Curiosity Kit"; + compatible = "microchip,pic64gx-curiosity-kit", "microchip,pic64gx"; + + aliases { + ethernet0 = &mac0; + serial1 = &mmuart1; + serial2 = &mmuart2; + }; + + chosen { + stdout-path = "serial1:115200n8"; + }; + + cpus { + timebase-frequency = <RTCCLK_FREQ>; + }; + + memory@80000000 { + device_type = "memory"; + reg = <0x0 0x80000000 0x0 0x40000000>; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + hss: hss-buffer@bfc00000 { + compatible = "shared-dma-pool"; + reg = <0x0 0xbfc00000 0x0 0x400000>; + no-map; + }; + }; +}; + +&gpio0 { + status ="okay"; + gpio-line-names = + "", "", "", "", "", "", "", "", + "", "", "", "", "MIPI_CAM_RESET", "MIPI_CAM_STANDBY"; +}; + +&gpio1 { + status ="okay"; + gpio-line-names = + "", "", "LED1", "LED2", "LED3", "LED4", "LED5", "LED6", + "LED7", "LED8", "", "", "", "", "", "", + "", "", "", "", "HDMI_HPD", "", "", "GPIO_1_23"; +}; + +&gpio2 { + status ="okay"; + gpio-line-names = + "", "", "", "", "", "", "SWITCH2", "USR_IO12", + "DIP1", "DIP2", "", "DIP3", "USR_IO1", "USR_IO2", "USR_IO7", "USR_IO8", + "USR_IO3", "USR_IO4", "USR_IO5", "USR_IO6", "", "", "USR_IO9", "USR_IO10", + "DIP4", "USR_IO11", "", "", "SWITCH1", "", "", ""; +}; + +&mac0 { + status = "okay"; + phy-mode = "sgmii"; + phy-handle = <&phy0>; + + phy0: ethernet-phy@b { + reg = <0xb>; + }; +}; + +&mbox { + status = "okay"; +}; + +&mmc { + bus-width = <4>; + disable-wp; + cap-sd-highspeed; + cap-mmc-highspeed; + sdhci-caps-mask = <0x00000007 0x00000000>; + status = "okay"; +}; + +&mmuart1 { + status = "okay"; +}; + +&mmuart2 { + status = "okay"; +}; + +&refclk { + clock-frequency = <125000000>; +}; + +&rtc { + status = "okay"; +}; + +&syscontroller { + status = "okay"; +}; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/riscv/boot/dts/microchip/pic64gx.dtsi linux-microchip/arch/riscv/boot/dts/microchip/pic64gx.dtsi --- linux-6.6.51/arch/riscv/boot/dts/microchip/pic64gx.dtsi 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/arch/riscv/boot/dts/microchip/pic64gx.dtsi 2024-11-26 14:02:36.078468260 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Device Tree Source for the PIC64GX SoCs + * + * Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries + * + * Author: Pierre-Henry Moussay <pierre-henry.moussay@microchip.com> + * + * PIC64GX is a series RISC-V multicore SoCs: + * https://www.microchip.com/en-us/products/microprocessors/64-bit-mpus/pic64gx + */ + +/dts-v1/; +#include "dt-bindings/clock/microchip,mpfs-clock.h" + +/ { + #address-cells = <2>; + #size-cells = <2>; + model = "Microchip PIC64GX SoC"; + compatible = "microchip,pic64gx"; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu0: cpu@0 { + compatible = "sifive,e51", "sifive,rocket0", "riscv"; + device_type = "cpu"; + i-cache-block-size = <64>; + i-cache-sets = <128>; + i-cache-size = <16384>; + reg = <0>; + riscv,isa = "rv64imac"; + riscv,isa-base = "rv64i"; + riscv,isa-extensions = "i", "m", "a", "c", "zicntr", "zicsr", + "zifencei", "zihpm"; + clocks = <&clkcfg CLK_CPU>; + status = "disabled"; + + cpu0_intc: interrupt-controller { + #interrupt-cells = <1>; + compatible = "riscv,cpu-intc"; + interrupt-controller; + }; + }; + + cpu1: cpu@1 { + compatible = "sifive,u54-mc", "sifive,rocket0", "riscv"; + d-cache-block-size = <64>; + d-cache-sets = <64>; + d-cache-size = <32768>; + d-tlb-sets = <1>; + d-tlb-size = <32>; + device_type = "cpu"; + i-cache-block-size = <64>; + i-cache-sets = <64>; + i-cache-size = <32768>; + i-tlb-sets = <1>; + i-tlb-size = <32>; + mmu-type = "riscv,sv39"; + reg = <1>; + riscv,isa = "rv64imafdc"; + riscv,isa-base = "rv64i"; + riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "zicntr", + "zicsr", "zifencei", "zihpm"; + clocks = <&clkcfg CLK_CPU>; + tlb-split; + next-level-cache = <&cctrllr>; + status = "okay"; + + cpu1_intc: interrupt-controller { + #interrupt-cells = <1>; + compatible = "riscv,cpu-intc"; + interrupt-controller; + }; + }; + + cpu2: cpu@2 { + compatible = "sifive,u54-mc", "sifive,rocket0", "riscv"; + d-cache-block-size = <64>; + d-cache-sets = <64>; + d-cache-size = <32768>; + d-tlb-sets = <1>; + d-tlb-size = <32>; + device_type = "cpu"; + i-cache-block-size = <64>; + i-cache-sets = <64>; + i-cache-size = <32768>; + i-tlb-sets = <1>; + i-tlb-size = <32>; + mmu-type = "riscv,sv39"; + reg = <2>; + riscv,isa = "rv64imafdc"; + riscv,isa-base = "rv64i"; + riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "zicntr", + "zicsr", "zifencei", "zihpm"; + clocks = <&clkcfg CLK_CPU>; + tlb-split; + next-level-cache = <&cctrllr>; + status = "okay"; + + cpu2_intc: interrupt-controller { + #interrupt-cells = <1>; + compatible = "riscv,cpu-intc"; + interrupt-controller; + }; + }; + + cpu3: cpu@3 { + compatible = "sifive,u54-mc", "sifive,rocket0", "riscv"; + d-cache-block-size = <64>; + d-cache-sets = <64>; + d-cache-size = <32768>; + d-tlb-sets = <1>; + d-tlb-size = <32>; + device_type = "cpu"; + i-cache-block-size = <64>; + i-cache-sets = <64>; + i-cache-size = <32768>; + i-tlb-sets = <1>; + i-tlb-size = <32>; + mmu-type = "riscv,sv39"; + reg = <3>; + riscv,isa = "rv64imafdc"; + riscv,isa-base = "rv64i"; + riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "zicntr", + "zicsr", "zifencei", "zihpm"; + clocks = <&clkcfg CLK_CPU>; + tlb-split; + next-level-cache = <&cctrllr>; + status = "okay"; + + cpu3_intc: interrupt-controller { + #interrupt-cells = <1>; + compatible = "riscv,cpu-intc"; + interrupt-controller; + }; + }; + + cpu4: cpu@4 { + compatible = "sifive,u54-mc", "sifive,rocket0", "riscv"; + d-cache-block-size = <64>; + d-cache-sets = <64>; + d-cache-size = <32768>; + d-tlb-sets = <1>; + d-tlb-size = <32>; + device_type = "cpu"; + i-cache-block-size = <64>; + i-cache-sets = <64>; + i-cache-size = <32768>; + i-tlb-sets = <1>; + i-tlb-size = <32>; + mmu-type = "riscv,sv39"; + reg = <4>; + riscv,isa = "rv64imafdc"; + riscv,isa-base = "rv64i"; + riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "zicntr", + "zicsr", "zifencei", "zihpm"; + clocks = <&clkcfg CLK_CPU>; + tlb-split; + next-level-cache = <&cctrllr>; + status = "okay"; + cpu4_intc: interrupt-controller { + #interrupt-cells = <1>; + compatible = "riscv,cpu-intc"; + interrupt-controller; + }; + }; + + cpu-map { + cluster0 { + core0 { + cpu = <&cpu0>; + }; + + core1 { + cpu = <&cpu1>; + }; + + core2 { + cpu = <&cpu2>; + }; + + core3 { + cpu = <&cpu3>; + }; + + core4 { + cpu = <&cpu4>; + }; + }; + }; + }; + + scbclk: clock-80000000 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <80000000>; + }; + + refclk: mssrefclk { + compatible = "fixed-clock"; + #clock-cells = <0>; + }; + + syscontroller: syscontroller { + compatible = "microchip,pic64gx-sys-controller"; + mboxes = <&mbox 0>; + }; + + soc { + #address-cells = <2>; + #size-cells = <2>; + compatible = "simple-bus"; + ranges; + + clint: clint@2000000 { + compatible = "microchip,pic64gx-clint", "sifive,clint0"; + reg = <0x0 0x2000000 0x0 0xC000>; + interrupts-extended = <&cpu0_intc 0xffffffff>, <&cpu0_intc 0xffffffff>, + <&cpu1_intc 3>, <&cpu1_intc 7>, + <&cpu2_intc 3>, <&cpu2_intc 7>, + <&cpu3_intc 3>, <&cpu3_intc 7>, + <&cpu4_intc 3>, <&cpu4_intc 7>; + }; + + cctrllr: cache-controller@2010000 { + compatible = "microchip,pic64gx-ccache", "microchip,mpfs-ccache", + "sifive,fu540-c000-ccache", "cache"; + reg = <0x0 0x2010000 0x0 0x1000>; + cache-block-size = <64>; + cache-level = <2>; + cache-sets = <1024>; + cache-size = <2097152>; + cache-unified; + interrupt-parent = <&plic>; + interrupts = <1>, <3>, <4>, <2>; + }; + + pdma: dma-controller@3000000 { + compatible = "microchip,pic64gx-pdma", "microchip,mpfs-pdma", + "sifive,fu540-c000-pdma"; + reg = <0x0 0x3000000 0x0 0x8000>; + interrupt-parent = <&plic>; + interrupts = <5 6>, <7 8>, <9 10>, <11 12>; + #dma-cells = <1>; + }; + + plic: interrupt-controller@c000000 { + compatible = "microchip,pic64gx-plic", "sifive,plic-1.0.0"; + reg = <0x0 0xc000000 0x0 0x4000000>; + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + interrupts-extended = <&cpu0_intc 0xffffffff>, + <&cpu1_intc 0xffffffff>, <&cpu1_intc 9>, + <&cpu2_intc 0xffffffff>, <&cpu2_intc 9>, + <&cpu3_intc 0xffffffff>, <&cpu3_intc 9>, + <&cpu4_intc 0xffffffff>, <&cpu4_intc 9>; + riscv,ndev = <186>; + }; + + mmuart0: serial@20000000 { + compatible = "ns16550a"; + reg = <0x0 0x20000000 0x0 0x400>; + reg-io-width = <4>; + reg-shift = <2>; + interrupt-parent = <&plic>; + interrupts = <90>; + current-speed = <115200>; + clocks = <&clkcfg CLK_MMUART0>; + status = "disabled"; /* Reserved for the HSS */ + }; + + mss_top_sysreg: syscon@20002000 { + compatible = "microchip,pic64gx-mss-top-sysreg", + "microchip,mpfs-mss-top-sysreg", + "syscon", "simple-mfd"; + reg = <0x0 0x20002000 0x0 0x1000>; + #reset-cells = <1>; + #address-cells = <1>; + #size-cells = <1>; + + irqmux: interrupt-controller@54 { + compatible = "microchip,pic64gx-gpio-irq-mux", + "microchip,mpfs-gpio-irq-mux"; + reg = <0x54 0x4>; + interrupt-parent = <&plic>; + interrupt-controller; + #interrupt-cells = <1>; + interrupts = <13>, <14>, <15>, <16>, + <17>, <18>, <19>, <20>, + <21>, <22>, <23>, <24>, + <25>, <26>, <27>, <28>, + <29>, <30>, <31>, <32>, + <33>, <34>, <35>, <36>, + <37>, <38>, <39>, <40>, + <41>, <42>, <43>, <44>, + <45>, <46>, <47>, <48>, + <49>, <50>, <51>, <52>, + <53>; + }; + }; + + sysreg_scb: syscon@20003000 { + compatible = "microchip,pic64gx-sysreg-scb", + "microchip,mpfs-sysreg-scb", + "syscon"; + reg = <0x0 0x20003000 0x0 0x1000>; + }; + + /* Common node entry for emmc/sd */ + mmc: mmc@20008000 { + compatible = "microchip,pic64gx-sd4hc", "cdns,sd4hc"; + reg = <0x0 0x20008000 0x0 0x1000>; + interrupt-parent = <&plic>; + interrupts = <88>; + clocks = <&clkcfg CLK_MMC>; + max-frequency = <200000000>; + status = "disabled"; + }; + + mmuart1: serial@20100000 { + compatible = "ns16550a"; + reg = <0x0 0x20100000 0x0 0x400>; + reg-io-width = <4>; + reg-shift = <2>; + interrupt-parent = <&plic>; + interrupts = <91>; + current-speed = <115200>; + clocks = <&clkcfg CLK_MMUART1>; + status = "disabled"; + }; + + mmuart2: serial@20102000 { + compatible = "ns16550a"; + reg = <0x0 0x20102000 0x0 0x400>; + reg-io-width = <4>; + reg-shift = <2>; + interrupt-parent = <&plic>; + interrupts = <92>; + current-speed = <115200>; + clocks = <&clkcfg CLK_MMUART2>; + status = "disabled"; + }; + + mmuart3: serial@20104000 { + compatible = "ns16550a"; + reg = <0x0 0x20104000 0x0 0x400>; + reg-io-width = <4>; + reg-shift = <2>; + interrupt-parent = <&plic>; + interrupts = <93>; + current-speed = <115200>; + clocks = <&clkcfg CLK_MMUART3>; + status = "disabled"; + }; + + mmuart4: serial@20106000 { + compatible = "ns16550a"; + reg = <0x0 0x20106000 0x0 0x400>; + reg-io-width = <4>; + reg-shift = <2>; + interrupt-parent = <&plic>; + interrupts = <94>; + clocks = <&clkcfg CLK_MMUART4>; + current-speed = <115200>; + status = "disabled"; + }; + + spi0: spi@20108000 { + compatible = "microchip,pic64gx-spi", "microchip,mpfs-spi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x20108000 0x0 0x1000>; + interrupt-parent = <&plic>; + interrupts = <54>; + clocks = <&clkcfg CLK_SPI0>; + status = "disabled"; + }; + + spi1: spi@20109000 { + compatible = "microchip,pic64gx-spi", "microchip,mpfs-spi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x20109000 0x0 0x1000>; + interrupt-parent = <&plic>; + interrupts = <55>; + clocks = <&clkcfg CLK_SPI1>; + status = "disabled"; + }; + + i2c0: i2c@2010a000 { + compatible = "microchip,pic64gx-i2c", "microchip,corei2c-rtl-v7"; + reg = <0x0 0x2010a000 0x0 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + interrupt-parent = <&plic>; + interrupts = <58>; + clocks = <&clkcfg CLK_I2C0>; + clock-frequency = <100000>; + status = "disabled"; + }; + + i2c1: i2c@2010b000 { + compatible = "microchip,pic64gx-i2c", "microchip,corei2c-rtl-v7"; + reg = <0x0 0x2010b000 0x0 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + interrupt-parent = <&plic>; + interrupts = <61>; + clocks = <&clkcfg CLK_I2C1>; + clock-frequency = <100000>; + status = "disabled"; + }; + + can0: can@2010c000 { + compatible = "microchip,pic64gx-can", "microchip,mpfs-can"; + reg = <0x0 0x2010c000 0x0 0x1000>; + clocks = <&clkcfg CLK_CAN0>, <&clkcfg CLK_MSSPLL3>; + interrupt-parent = <&plic>; + interrupts = <56>; + resets = <&mss_top_sysreg CLK_CAN0>; + status = "disabled"; + }; + + can1: can@2010d000 { + compatible = "microchip,pic64gx-can", "microchip,mpfs-can"; + reg = <0x0 0x2010d000 0x0 0x1000>; + clocks = <&clkcfg CLK_CAN1>, <&clkcfg CLK_MSSPLL3>; + interrupt-parent = <&plic>; + interrupts = <57>; + resets = <&mss_top_sysreg CLK_CAN1>; + status = "disabled"; + }; + + mac0: ethernet@20110000 { + compatible = "microchip,pic64gx-macb", "microchip,mpfs-macb", + "cdns,macb"; + reg = <0x0 0x20110000 0x0 0x2000>; + #address-cells = <1>; + #size-cells = <0>; + interrupt-parent = <&plic>; + interrupts = <64>, <65>, <66>, <67>, <68>, <69>; + /* Filled in by a bootloader */ + local-mac-address = [00 00 00 00 00 00]; + clocks = <&clkcfg CLK_MAC0>, <&clkcfg CLK_AHB>; + clock-names = "pclk", "hclk"; + resets = <&mss_top_sysreg CLK_MAC0>; + status = "disabled"; + }; + + mac1: ethernet@20112000 { + compatible = "microchip,pic64gx-macb", "microchip,mpfs-macb", + "cdns,macb"; + reg = <0x0 0x20112000 0x0 0x2000>; + #address-cells = <1>; + #size-cells = <0>; + interrupt-parent = <&plic>; + interrupts = <70>, <71>, <72>, <73>, <74>, <75>; + /* Filled in by a bootloader */ + local-mac-address = [00 00 00 00 00 00]; + clocks = <&clkcfg CLK_MAC1>, <&clkcfg CLK_AHB>; + clock-names = "pclk", "hclk"; + resets = <&mss_top_sysreg CLK_MAC1>; + status = "disabled"; + }; + + gpio0: gpio@20120000 { + compatible = "microchip,pic64gx-gpio", "microchip,mpfs-gpio"; + reg = <0x0 0x20120000 0x0 0x1000>; + interrupt-parent = <&irqmux>; + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <0>, <1>, <2>, <3>, + <4>, <5>, <6>, <7>, + <8>, <9>, <10>, <11>, + <12>, <13>; + clocks = <&clkcfg CLK_GPIO0>; + gpio-controller; + #gpio-cells = <2>; + ngpios = <14>; + status = "disabled"; + }; + + gpio1: gpio@20121000 { + compatible = "microchip,pic64gx-gpio", "microchip,mpfs-gpio"; + reg = <0x0 0x20121000 0x0 0x1000>; + interrupt-parent = <&irqmux>; + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <32>, <33>, <34>, <35>, + <36>, <37>, <38>, <39>, + <40>, <41>, <42>, <43>, + <44>, <45>, <46>, <47>, + <48>, <49>, <50>, <51>, + <52>, <53>, <54>, <55>; + clocks = <&clkcfg CLK_GPIO1>; + gpio-controller; + #gpio-cells = <2>; + ngpios = <24>; + status = "disabled"; + }; + + gpio2: gpio@20122000 { + compatible = "microchip,pic64gx-gpio", "microchip,mpfs-gpio"; + reg = <0x0 0x20122000 0x0 0x1000>; + interrupt-parent = <&irqmux>; + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <64>, <65>, <66>, <67>, + <68>, <69>, <70>, <71>, + <72>, <73>, <74>, <75>, + <76>, <77>, <78>, <79>, + <80>, <81>, <82>, <83>, + <84>, <85>, <86>, <87>, + <88>, <89>, <90>, <91>, + <92>, <93>, <94>, <95>; + clocks = <&clkcfg CLK_GPIO2>; + gpio-controller; + #gpio-cells = <2>; + ngpios = <32>; + status = "disabled"; + }; + + rtc: rtc@20124000 { + compatible = "microchip,pic64gx-rtc", "microchip,mpfs-rtc"; + reg = <0x0 0x20124000 0x0 0x1000>; + interrupt-parent = <&plic>; + interrupts = <80>, <81>; + clocks = <&clkcfg CLK_RTC>, <&clkcfg CLK_RTCREF>; + clock-names = "rtc", "rtcref"; + status = "disabled"; + }; + + usb: usb@20201000 { + compatible = "microchip,pic64gx-musb", "microchip,mpfs-musb"; + reg = <0x0 0x20201000 0x0 0x1000>; + interrupt-parent = <&plic>; + interrupts = <86>, <87>; + clocks = <&clkcfg CLK_USB>; + interrupt-names = "dma", "mc"; + status = "disabled"; + }; + + qspi: spi@21000000 { + compatible = "microchip,pic64gx-qspi", "microchip,coreqspi-rtl-v2"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x21000000 0x0 0x1000>; + interrupt-parent = <&plic>; + interrupts = <85>; + clocks = <&clkcfg CLK_QSPI>; + status = "disabled"; + }; + + control_scb: syscon@37020000 { + compatible = "microchip,pic64gx-control-scb", + "microchip,mpfs-control-scb", + "syscon", "simple-mfd"; + reg = <0x0 0x37020000 0x0 0x100>; + }; + + syscontroller_qspi: spi@37020100 { + compatible = "microchip,pic64gx-qspi", "microchip,coreqspi-rtl-v2"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x37020100 0x0 0x100>; + interrupt-parent = <&plic>; + interrupts = <110>; + clocks = <&scbclk>; + status = "disabled"; + }; + + mbox: mailbox@37020800 { + compatible = "microchip,pic64gx-mailbox", "microchip,mpfs-mailbox"; + reg = <0x0 0x37020800 0x0 0x100>; + interrupt-parent = <&plic>; + interrupts = <96>; + #mbox-cells = <1>; + status = "disabled"; + }; + + ccc_se: clock-controller@38010000 { + compatible = "microchip,pic64gx-ccc", "microchip,mpfs-ccc"; + reg = <0x0 0x38010000 0x0 0x1000>, <0x0 0x38020000 0x0 0x1000>, + <0x0 0x39010000 0x0 0x1000>, <0x0 0x39020000 0x0 0x1000>; + #clock-cells = <1>; + status = "disabled"; + }; + + ccc_ne: clock-controller@38040000 { + compatible = "microchip,pic64gx-ccc", "microchip,mpfs-ccc"; + reg = <0x0 0x38040000 0x0 0x1000>, <0x0 0x38080000 0x0 0x1000>, + <0x0 0x39040000 0x0 0x1000>, <0x0 0x39080000 0x0 0x1000>; + #clock-cells = <1>; + status = "disabled"; + }; + + ccc_nw: clock-controller@38100000 { + compatible = "microchip,pic64gx-ccc", "microchip,mpfs-ccc"; + reg = <0x0 0x38100000 0x0 0x1000>, <0x0 0x38200000 0x0 0x1000>, + <0x0 0x39100000 0x0 0x1000>, <0x0 0x39200000 0x0 0x1000>; + #clock-cells = <1>; + status = "disabled"; + }; + + ccc_sw: clock-controller@38400000 { + compatible = "microchip,pic64gx-ccc", "microchip,mpfs-ccc"; + reg = <0x0 0x38400000 0x0 0x1000>, <0x0 0x38800000 0x0 0x1000>, + <0x0 0x39400000 0x0 0x1000>, <0x0 0x39800000 0x0 0x1000>; + #clock-cells = <1>; + status = "disabled"; + }; + + clkcfg: clkcfg@3e001000 { + compatible = "microchip,pic64gx-clkcfg", "microchip,mpfs-clkcfg"; + reg = <0x0 0x3e001000 0x0 0x1000>; + clocks = <&refclk>; + #clock-cells = <1>; + }; + }; +}; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/riscv/configs/mpfs_defconfig linux-microchip/arch/riscv/configs/mpfs_defconfig --- linux-6.6.51/arch/riscv/configs/mpfs_defconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/arch/riscv/configs/mpfs_defconfig 2024-11-26 14:04:18.560303308 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_NO_HZ_IDLE=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_BPF_SYSCALL=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_SCHED=y +CONFIG_CFS_BANDWIDTH=y +CONFIG_CGROUP_BPF=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_EXPERT=y +CONFIG_KALLSYMS_ALL=y +CONFIG_SOC_MICROCHIP_POLARFIRE=y +CONFIG_SMP=y +CONFIG_CMDLINE="earlycon uio_pdrv_genirq.of_id=generic-uio" +CONFIG_JUMP_LABEL=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_BLK_DEV_ZONED=y +CONFIG_SPARSEMEM_MANUAL=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_CAN=y +CONFIG_PCI=y +CONFIG_PCI_HOST_GENERIC=y +CONFIG_PCIE_MICROCHIP_HOST=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_FW_LOADER_USER_HELPER=y +CONFIG_SIFIVE_CCACHE=y +CONFIG_POLARFIRE_SOC_AUTO_UPDATE=y +CONFIG_MTD=y +CONFIG_MTD_SPI_NAND=y +CONFIG_MTD_SPI_NOR=y +CONFIG_OF_OVERLAY=y +CONFIG_OF_CONFIGFS=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_NVME=y +CONFIG_MPFS_DMA_PROXY=y +CONFIG_BLK_DEV_SD=y +CONFIG_ATA=y +CONFIG_NETDEVICES=y +CONFIG_MACB=y +CONFIG_MICREL_PHY=y +CONFIG_MICROSEMI_PHY=y +CONFIG_CAN_POLARFIRE_SOC=y +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_RPMSG_TTY=m +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_POLARFIRE_SOC=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MICROCHIP_CORE=y +CONFIG_SPI=y +CONFIG_SPI_MICROCHIP_CORE=y +CONFIG_SPI_MICROCHIP_CORE_QSPI=y +CONFIG_SPI_SPIDEV=m +CONFIG_GPIOLIB=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_POLARFIRE_SOC=y +CONFIG_POWER_RESET=y +CONFIG_PMBUS=m +CONFIG_MFD_SYSCON=y +CONFIG_MEDIA_SUPPORT=m +CONFIG_MEDIA_SUPPORT_FILTER=y +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_USB_VIDEO_CLASS=m +CONFIG_USB=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_PLATFORM=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PLATFORM=y +CONFIG_USB_ACM=m +CONFIG_USB_STORAGE=m +CONFIG_USB_MUSB_HDRC=y +CONFIG_USB_MUSB_POLARFIRE_SOC=y +CONFIG_USB_INVENTRA_DMA=y +CONFIG_USB_SERIAL=m +CONFIG_NOP_USB_XCEIV=y +CONFIG_MMC=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_CADENCE=y +CONFIG_MMC_SPI=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_GPIO=y +CONFIG_LEDS_TRIGGERS=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_POLARFIRE_SOC=y +CONFIG_DMADEVICES=y +CONFIG_SF_PDMA=y +CONFIG_UIO=y +CONFIG_UIO_PDRV_GENIRQ=y +CONFIG_UIO_DMEM_GENIRQ=y +CONFIG_UIO_MICROCHIP_DMA=y +CONFIG_MAILBOX=y +CONFIG_POLARFIRE_SOC_MAILBOX=y +CONFIG_MCHP_SBI_IPC_MBOX=y +CONFIG_MIV_IHC=y +CONFIG_REMOTEPROC=y +CONFIG_REMOTEPROC_CDEV=y +CONFIG_MIV_REMOTEPROC=y +CONFIG_MCHP_IPC_REMOTEPROC=y +CONFIG_RPMSG_CHAR=m +CONFIG_RPMSG_CTRL=m +CONFIG_RPMSG_VIRTIO=y +CONFIG_POLARFIRE_SOC_SYS_CTRL=y +CONFIG_POLARFIRE_SOC_GENERIC_SERVICE=m +CONFIG_IIO=m +CONFIG_IIO_SW_DEVICE=m +CONFIG_IIO_SW_TRIGGER=m +CONFIG_PAC1934=m +CONFIG_PWM=y +CONFIG_PWM_MICROCHIP_CORE=y +CONFIG_RESET_CONTROLLER=y +CONFIG_FPGA=y +CONFIG_FPGA_BRIDGE=y +CONFIG_FPGA_REGION=y +CONFIG_FPGA_MGR_MICROCHIP_SPI=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_FANOTIFY=y +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=m +CONFIG_FAT_DEFAULT_IOCHARSET="ascii" +CONFIG_EXFAT_FS=m +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_NFS_FS=m +CONFIG_NFS_V4=m +CONFIG_NFS_V4_1=y +CONFIG_NFS_V4_2=y +CONFIG_NLS_CODEPAGE_437=m +CONFIG_NLS_CODEPAGE_850=m +CONFIG_NLS_ASCII=m +CONFIG_NLS_ISO8859_1=m +CONFIG_NLS_UTF8=m +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_FS=y +CONFIG_SCHED_STACK_END_CHECK=y +CONFIG_SOFTLOCKUP_DETECTOR=y +CONFIG_WQ_WATCHDOG=y +CONFIG_SCHEDSTATS=y +CONFIG_SAMPLES=y +CONFIG_SAMPLE_RPMSG_CLIENT=m +CONFIG_STRICT_DEVMEM=y +CONFIG_IO_STRICT_DEVMEM=y diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/riscv/include/asm/vendorid_list.h linux-microchip/arch/riscv/include/asm/vendorid_list.h --- linux-6.6.51/arch/riscv/include/asm/vendorid_list.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/riscv/include/asm/vendorid_list.h 2024-11-26 14:02:36.082468331 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:9 @ #define ASM_VENDOR_LIST_H #define ANDESTECH_VENDOR_ID 0x31e +#define MICROCHIP_VENDOR_ID 0x029 #define SIFIVE_VENDOR_ID 0x489 #define THEAD_VENDOR_ID 0x5b7 diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/riscv/Kconfig.socs linux-microchip/arch/riscv/Kconfig.socs --- linux-6.6.51/arch/riscv/Kconfig.socs 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/riscv/Kconfig.socs 2024-11-26 14:02:36.078468260 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:8 @ config SOC_MICROCHIP_POLARFIRE bool "Microchip PolarFire SoCs" + select DMA_GLOBAL_POOL + select RISCV_NONSTANDARD_CACHE_OPS help This enables support for Microchip PolarFire SoC platforms. diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/arch/riscv/kernel/smp.c linux-microchip/arch/riscv/kernel/smp.c --- linux-6.6.51/arch/riscv/kernel/smp.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/arch/riscv/kernel/smp.c 2024-11-26 14:02:36.086468404 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:42 @ unsigned long __cpuid_to_hartid_map[NR_CPUS] __ro_after_init = { [0 ... NR_CPUS-1] = INVALID_HARTID }; +EXPORT_SYMBOL_GPL(__cpuid_to_hartid_map); void __init smp_setup_processor_id(void) { diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/ABI/testing/sysfs-bus-iio-adc-mcp3564 linux-microchip/Documentation/ABI/testing/sysfs-bus-iio-adc-mcp3564 --- linux-6.6.51/Documentation/ABI/testing/sysfs-bus-iio-adc-mcp3564 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/ABI/testing/sysfs-bus-iio-adc-mcp3564 2024-11-26 14:02:35.338455004 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +What: /sys/bus/iio/devices/iio:deviceX/boost_current_gain +KernelVersion: 6.4 +Contact: linux-iio@vger.kernel.org +Description: + This attribute is used to set the gain of the biasing current + circuit of the Delta-Sigma modulator. The different BOOST + settings are applied to the entire modulator circuit, including + the voltage reference buffers. + +What: /sys/bus/iio/devices/iio:deviceX/boost_current_gain_available +KernelVersion: 6.4 +Contact: linux-iio@vger.kernel.org +Description: + Reading returns a list with the possible gain values for + the current biasing circuit of the Delta-Sigma modulator. + +What: /sys/bus/iio/devices/iio:deviceX/auto_zeroing_mux_enable +KernelVersion: 6.4 +Contact: linux-iio@vger.kernel.org +Description: + This attribute is used to enable the analog input multiplexer + auto-zeroing algorithm (the input multiplexer and the ADC + include an offset cancellation algorithm that cancels the offset + contribution of the ADC). When the offset cancellation algorithm + is enabled, ADC takes two conversions, one with the differential + input as VIN+/VIN-, one with VIN+/VIN- inverted. In this case the + conversion time is multiplied by two compared to the default + case where the algorithm is disabled. This technique allows the + cancellation of the ADC offset error and the achievement of + ultra-low offset without any digital calibration. The resulting + offset is the residue of the difference between the two + conversions, which is on the order of magnitude of the noise + floor. This offset is effectively canceled at every conversion, + so the residual offset error temperature drift is extremely low. + Write '1' to enable it, write '0' to disable it. + +What: /sys/bus/iio/devices/iio:deviceX/auto_zeroing_ref_enable +KernelVersion: 6.4 +Contact: linux-iio@vger.kernel.org +Description: + This attribute is used to enable the chopping algorithm for the + internal voltage reference buffer. This setting has no effect + when external voltage reference is selected. + Internal voltage reference buffer injects a certain quantity of + 1/f noise into the system that can be modulated with the + incoming input signals and can limit the SNR performance at + higher Oversampling Ratio values (over 256). To overcome this + limitation, the buffer includes an auto-zeroing algorithm that + greatly reduces (cancels out) the 1/f noise and cancels the + offset value of the reference buffer. As a result, the SNR of + the system is not affected by this 1/f noise component of the + reference buffer, even at maximum oversampling ratio values. + Write '1' to enable it, write '0' to disable it. diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/ABI/testing/sysfs-bus-iio-adc-pac1934 linux-microchip/Documentation/ABI/testing/sysfs-bus-iio-adc-pac1934 --- linux-6.6.51/Documentation/ABI/testing/sysfs-bus-iio-adc-pac1934 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/ABI/testing/sysfs-bus-iio-adc-pac1934 2024-11-26 14:02:35.338455004 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +What: /sys/bus/iio/devices/iio:deviceX/in_shunt_resistorY +KernelVersion: 6.7 +Contact: linux-iio@vger.kernel.org +Description: + The value of the shunt resistor may be known only at runtime + and set by a client application. This attribute allows to + set its value in micro-ohms. X is the IIO index of the device. + Y is the channel number. The value is used to calculate + current, power and accumulated energy. diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/arm/atmel-at91.yaml linux-microchip/Documentation/devicetree/bindings/arm/atmel-at91.yaml --- linux-6.6.51/Documentation/devicetree/bindings/arm/atmel-at91.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/arm/atmel-at91.yaml 2024-11-26 14:02:35.390455935 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:82 @ - const: atmel,sama5d2 - const: atmel,sama5 + - description: Microchip SAMA5D29 Curiosity + items: + - const: microchip,sama5d29-curiosity + - const: atmel,sama5d29 + - const: atmel,sama5d2 + - const: atmel,sama5 + - items: - const: atmel,sama5d27 - const: atmel,sama5d2 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:109 @ - const: microchip,sam9x60 - const: atmel,at91sam9 + - description: SAM9X75-EB board + items: + - const: microchip,sam9x75eb + - const: microchip,sam9x7 + - const: atmel,at91sam9 + - description: Nattis v2 board with Natte v2 power board items: - const: axentia,nattis-2 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:188 @ - const: microchip,sama7g5 - const: microchip,sama7 + - description: Microchip SAMA7G54 Curiosity Board + items: + - const: microchip,sama7g54-curiosity + - const: microchip,sama7g5 + - const: microchip,sama7 + - description: Microchip LAN9662 Evaluation Boards. items: - enum: diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/arm/atmel-sysregs.txt linux-microchip/Documentation/devicetree/bindings/arm/atmel-sysregs.txt --- linux-6.6.51/Documentation/devicetree/bindings/arm/atmel-sysregs.txt 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/arm/atmel-sysregs.txt 2024-11-26 14:02:35.390455935 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ Atmel system registers Chipid required properties: -- compatible: Should be "atmel,sama5d2-chipid" or "microchip,sama7g5-chipid" +- compatible: Should be "atmel,sama5d2-chipid" or + "microchip,sama7g5-chipid" or + "microchip,sama7d65-chipid". - reg : Should contain registers location and length PIT Timer required properties: diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/auxdisplay/raspberrypi,sensehat-display.yaml linux-microchip/Documentation/devicetree/bindings/auxdisplay/raspberrypi,sensehat-display.yaml --- linux-6.6.51/Documentation/devicetree/bindings/auxdisplay/raspberrypi,sensehat-display.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/auxdisplay/raspberrypi,sensehat-display.yaml 2024-11-26 14:04:18.556303237 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/auxdisplay/raspberrypi,sensehat-display.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Raspberry Pi Sensehat Display + +maintainers: + - Charles Mirabile <cmirabil@redhat.com> + - Joel Savitz <jsavitz@redhat.com> + +description: + This device is part of the sensehat multi function device. + For more information see ../mfd/raspberrypi,sensehat.yaml. + + This device features a programmable 8x8 RGB LED matrix. + +properties: + compatible: + const: raspberrypi,sensehat-display + +required: + - compatible + +additionalProperties: false diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/cache/sifive,ccache0.yaml linux-microchip/Documentation/devicetree/bindings/cache/sifive,ccache0.yaml --- linux-6.6.51/Documentation/devicetree/bindings/cache/sifive,ccache0.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/cache/sifive,ccache0.yaml 2024-11-26 14:02:35.394456007 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:48 @ - const: microchip,mpfs-ccache - const: sifive,fu540-c000-ccache - const: cache + - items: + - const: microchip,pic64gx-ccache + - const: microchip,mpfs-ccache + - const: sifive,fu540-c000-ccache + - const: cache cache-block-size: const: 64 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:98 @ - sifive,fu740-c000-ccache - starfive,jh7110-ccache - microchip,mpfs-ccache + - microchip,pic64gx-ccache then: properties: diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/clock/microchip,mpfs-ccc.yaml linux-microchip/Documentation/devicetree/bindings/clock/microchip,mpfs-ccc.yaml --- linux-6.6.51/Documentation/devicetree/bindings/clock/microchip,mpfs-ccc.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/clock/microchip,mpfs-ccc.yaml 2024-11-26 14:02:35.398456078 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:20 @ properties: compatible: - const: microchip,mpfs-ccc + oneOf: + - items: + - const: microchip,pic64gx-ccc + - const: microchip,mpfs-ccc + - const: microchip,mpfs-ccc reg: items: diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/clock/microchip,mpfs-clkcfg.yaml linux-microchip/Documentation/devicetree/bindings/clock/microchip,mpfs-clkcfg.yaml --- linux-6.6.51/Documentation/devicetree/bindings/clock/microchip,mpfs-clkcfg.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/clock/microchip,mpfs-clkcfg.yaml 2024-11-26 14:02:35.398456078 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:22 @ properties: compatible: - const: microchip,mpfs-clkcfg + oneOf: + - items: + - const: microchip,pic64gx-clkcfg + - const: microchip,mpfs-clkcfg + - const: microchip,mpfs-clkcfg reg: - items: - - description: | - clock config registers: - These registers contain enable, reset & divider tables for the, cpu, - axi, ahb and rtc/mtimer reference clocks as well as enable and reset - for the peripheral clocks. - - description: | - mss pll dri registers: - Block of registers responsible for dynamic reconfiguration of the mss - pll + oneOf: + - items: + - description: | + clock config registers: + These registers contain enable, reset & divider tables for the, cpu, + axi, ahb and rtc/mtimer reference clocks as well as enable and reset + for the peripheral clocks. + - description: | + mss pll dri registers: + Block of registers responsible for dynamic reconfiguration of the mss + pll + - items: + - description: | + mss pll dri registers: + Block of registers responsible for dynamic reconfiguration of the mss + pll + clocks: maxItems: 1 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:83 @ - | #include <dt-bindings/clock/microchip,mpfs-clock.h> soc { - #address-cells = <2>; - #size-cells = <2>; - clkcfg: clock-controller@20002000 { + #address-cells = <1>; + #size-cells = <1>; + + clkcfg: clock-controller@3E001000 { compatible = "microchip,mpfs-clkcfg"; - reg = <0x0 0x20002000 0x0 0x1000>, <0x0 0x3E001000 0x0 0x1000>; + reg = <0x3E001000 0x1000>; clocks = <&ref>; #clock-cells = <1>; }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/crypto/microchip,mpfs-crypto.yaml linux-microchip/Documentation/devicetree/bindings/crypto/microchip,mpfs-crypto.yaml --- linux-6.6.51/Documentation/devicetree/bindings/crypto/microchip,mpfs-crypto.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/crypto/microchip,mpfs-crypto.yaml 2024-11-26 14:02:35.406456222 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/crypto/microchip,mpfs-crypto.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip PolarFire SoC (MPFS) MSS (microprocessor subsystem) User Crypto + +maintainers: + - Padmarao Begari <padmarao.begari@microchip.com> + +properties: + compatible: + const: microchip,mpfs-crypto + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: AHB peripheral clock + - description: Crypto clock + +required: + - compatible + - reg + - clocks + - interrupts + +additionalProperties: false + +examples: + - | + #include "dt-bindings/clock/microchip,mpfs-clock.h" + crypto@22000000 { + compatible = "microchip,mpfs-crypto"; + reg = <0x22000000 0x10000>; + clocks = <&clkcfg 0>, <&clkcfg 1>; + interrupt-parent = <&plic>; + interrupts = <112>; + }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/display/bridge/microchip,sam9x75-mipi-dsi.yaml linux-microchip/Documentation/devicetree/bindings/display/bridge/microchip,sam9x75-mipi-dsi.yaml --- linux-6.6.51/Documentation/devicetree/bindings/display/bridge/microchip,sam9x75-mipi-dsi.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/display/bridge/microchip,sam9x75-mipi-dsi.yaml 2024-11-26 14:02:35.406456222 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/bridge/microchip,sam9x75-mipi-dsi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip SAM9X75 MIPI DSI Controller + +maintainers: + - Manikandan Muralidharan <manikandan.m@microchip.com> + +description: + Microchip specific extensions or wrapper to the Synopsys Designware MIPI DSI. + The MIPI Display Serial Interface (DSI) Host Controller implements all + protocol functions defined in the MIPI DSI Specification. The DSI Host + provides an interface between the LCD Controller (LCDC) and the MIPI D-PHY, + allowing communication with a DSI-compliant display. + +allOf: + - $ref: /schemas/display/dsi-controller.yaml# + +properties: + compatible: + const: microchip,sam9x75-mipi-dsi + + reg: + maxItems: 1 + + clocks: + description: + MIPI DSI must have two clocks to function correctly.Peripheral clock + 'pclk' for the hardware block functionality and Generic clock 'refclk' to + drive the D-PHY PLL block. + minItems: 2 + + clock-names: + items: + - const: pclk + - const: refclk + + microchip,sfr: + $ref: /schemas/types.yaml#/definitions/phandle + description: + phandle to Special Function Register (SFR) node.To enable the DSI/CSI + selection bit in SFR's ISS Configuration Register. + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: + DSI Input port node, connected to the LCDC RGB output port. + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + properties: + remote-endpoint: true + + port@1: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: + DSI Output port node, connected to a panel or a bridge input port. + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + properties: + remote-endpoint: true + +required: + - compatible + - reg + - clocks + - clock-names + - ports + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/at91.h> + #include <dt-bindings/gpio/gpio.h> + + dsi: dsi@f8054000 { + compatible = "microchip,sam9x75-mipi-dsi"; + reg = <0xf8054000 0x200>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 54>, <&pmc PMC_TYPE_GCK 55>; + clock-names = "pclk", "refclk"; + microchip,sfr = <&sfr>; + + #address-cells = <1>; + #size-cells = <0>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + dsi_in: endpoint { + remote-endpoint = <&hlcdc_panel_output>; + }; + }; + + port@1 { + reg = <1>; + dsi_out: endpoint { + remote-endpoint = <&mipi_in_panel>; + }; + }; + }; + + panel@0 { + compatible = "hannstar,hsd060bhw4", "himax,hx8394"; + reg = <0>; + vcc-supply = <&mic23150_reg>; + iovcc-supply = <&mic23150_reg>; + reset-gpios = <&pioC 24 GPIO_ACTIVE_LOW>; + backlight = <&backlight>; + + port { + mipi_in_panel: endpoint { + remote-endpoint = <&dsi_out>; + }; + }; + }; + }; + +... diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/display/bridge/microchip,sam9x7-lvds.yaml linux-microchip/Documentation/devicetree/bindings/display/bridge/microchip,sam9x7-lvds.yaml --- linux-6.6.51/Documentation/devicetree/bindings/display/bridge/microchip,sam9x7-lvds.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/display/bridge/microchip,sam9x7-lvds.yaml 2024-11-26 14:02:35.406456222 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/bridge/microchip,sam9x7-lvds.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip SAM9X7 LVDS Controller + +maintainers: + - Dharma Balasubiramani <dharma.b@microchip.com> + +description: | + The Low Voltage Differential Signaling Controller (LVDSC) manages data + format conversion from the LCD Controller internal DPI bus to OpenLDI + LVDS output signals. LVDSC functions include bit mapping, balanced mode + management, and serializer. + +properties: + compatible: + const: microchip,sam9x7-lvds + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: Peripheral Bus Clock + + clock-names: + items: + - const: pclk + - const: gclk + minItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/at91.h> + #include <dt-bindings/dma/at91.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + + lvds-controller@f8060000 { + compatible = "microchip,sam9x7-lvds"; + reg = <0xf8060000 0x100>; + interrupts = <56 IRQ_TYPE_LEVEL_HIGH 0>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 56>; + clock-names = "pclk"; + }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/display/panel/himax,hx8394.yaml linux-microchip/Documentation/devicetree/bindings/display/panel/himax,hx8394.yaml --- linux-6.6.51/Documentation/devicetree/bindings/display/panel/himax,hx8394.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/display/panel/himax,hx8394.yaml 2024-11-26 14:02:35.410456293 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:26 @ items: - enum: - hannstar,hsd060bhw4 + - microchip,ac40t08a-mipi-panel - const: himax,hx8394 reg: true @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:46 @ required: - compatible - reg - - reset-gpios - backlight - port - vcc-supply diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/display/panel/panel-simple.yaml linux-microchip/Documentation/devicetree/bindings/display/panel/panel-simple.yaml --- linux-6.6.51/Documentation/devicetree/bindings/display/panel/panel-simple.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/display/panel/panel-simple.yaml 2024-11-26 14:02:35.410456293 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:315 @ - sharp,ls020b1dd01d # Shelly SCA07010-BFN-LNN 7.0" WVGA TFT LCD panel - shelly,sca07010-bfn-lnn + # Sitronix ST7262 1200CH 5" WVGA (800x480) LVDS TFT LCD panel + - sitronix,st7262 # Starry KR070PE2T 7" WVGA TFT LCD panel - starry,kr070pe2t # Starry 12.2" (1920x1200 pixels) TFT LCD panel diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/dma/atmel-xdma.txt linux-microchip/Documentation/devicetree/bindings/dma/atmel-xdma.txt --- linux-6.6.51/Documentation/devicetree/bindings/dma/atmel-xdma.txt 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/dma/atmel-xdma.txt 2024-11-26 14:02:35.414456366 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:19 @ interface identifier, - bit 30-24: PERID, peripheral identifier. +Optional properties: +- dma-channels: Specify total DMA channels per XDMAC. It is required when the + number of channels could not be got from register XDMAC_GTYPE + (accessing from non-secure world for some devices). + Example: dma1: dma-controller@f0004000 { diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/dma/microchip,mpfs-fpga-dma.yaml linux-microchip/Documentation/devicetree/bindings/dma/microchip,mpfs-fpga-dma.yaml --- linux-6.6.51/Documentation/devicetree/bindings/dma/microchip,mpfs-fpga-dma.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/dma/microchip,mpfs-fpga-dma.yaml 2024-11-26 14:02:35.414456366 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/dma/microchip,mpfs-fpga-dma.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip FPGA soft DMA controller core + +maintainers: + - Conor Dooley <conor.dooley@microchip.com> + +allOf: + - $ref: dma-controller.yaml# + +properties: + "#dma-cells": + const: 1 + + compatible: + const: microchip,mpfs-fpga-dma + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + +required: + - "#dma-cells" + - compatible + - reg + - interrupts + +additionalProperties: false + +examples: + - | + dma: dma-controller@60020000 { + compatible = "microchip,mpfs-fpga-dma"; + reg = <0x60020000 0x1000>; + interrupt-parent = <&plic>; + interrupts = <120>; + status = "disabled"; + }; + +... diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/dma/sifive,fu540-c000-pdma.yaml linux-microchip/Documentation/devicetree/bindings/dma/sifive,fu540-c000-pdma.yaml --- linux-6.6.51/Documentation/devicetree/bindings/dma/sifive,fu540-c000-pdma.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/dma/sifive,fu540-c000-pdma.yaml 2024-11-26 14:02:35.414456366 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:30 @ properties: compatible: - items: - - enum: - - sifive,fu540-c000-pdma - - const: sifive,pdma0 + oneOf: + - items: + - const: microchip,mpfs-pdma # Microchip out of order DMA transfer + - const: sifive,fu540-c000-pdma # Sifive in-order DMA transfer + - items: + - const: microchip,pic64gx-pdma + - const: microchip,mpfs-pdma + - const: sifive,fu540-c000-pdma + - items: + - enum: + - sifive,fu540-c000-pdma + - const: sifive,pdma0 description: Should be "sifive,<chip>-pdma" and "sifive,pdma<version>". Supported compatible strings are - diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml linux-microchip/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml --- linux-6.6.51/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml 2024-11-26 14:02:35.418456436 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:14 @ properties: compatible: - items: - - enum: - - microchip,mpfs-gpio + oneOf: + - items: + - const: microchip,pic64gx-gpio + - const: microchip,mpfs-gpio + - items: + - enum: + - microchip,mpfs-gpio + - microchip,coregpio-rtl-v3 reg: maxItems: 1 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:29 @ interrupts: description: Interrupt mapping, one per GPIO. Maximum 32 GPIOs. - minItems: 1 + minItems: 14 maxItems: 32 interrupt-controller: true @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:41 @ const: 2 "#interrupt-cells": - const: 1 + const: 2 ngpios: description: The number of GPIOs available. - minimum: 1 - maximum: 32 - default: 32 + enum: [14, 16, 20, 24, 32] gpio-controller: true + gpio-line-names: true patternProperties: "^.+-hog(-[0-9]+)?$": @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:69 @ - gpio-hog - gpios +allOf: + - if: + properties: + compatible: + contains: + const: microchip,mpfs-gpio + then: + required: + - interrupts + - "#interrupt-cells" + - interrupt-controller + required: - compatible - reg - - interrupts - - "#interrupt-cells" - - interrupt-controller - "#gpio-cells" - gpio-controller + - ngpios - clocks additionalProperties: false @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:97 @ compatible = "microchip,mpfs-gpio"; reg = <0x20122000 0x1000>; clocks = <&clkcfg 25>; - interrupt-parent = <&plic>; + interrupt-parent = <&irqmux>; gpio-controller; #gpio-cells = <2>; + ngpios = <32>; interrupt-controller; - #interrupt-cells = <1>; - interrupts = <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>, - <53>, <53>, <53>, <53>; + #interrupt-cells = <2>; + interrupts = <64>, <65>, <66>, <67>, + <68>, <69>, <70>, <71>, + <72>, <73>, <74>, <75>, + <76>, <77>, <78>, <79>, + <80>, <81>, <82>, <83>, + <84>, <85>, <86>, <87>, + <88>, <89>, <90>, <91>, + <92>, <93>, <94>, <95>; }; ... diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/i2c/microchip,corei2c.yaml linux-microchip/Documentation/devicetree/bindings/i2c/microchip,corei2c.yaml --- linux-6.6.51/Documentation/devicetree/bindings/i2c/microchip,corei2c.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/i2c/microchip,corei2c.yaml 2024-11-26 14:02:35.422456509 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:19 @ compatible: oneOf: - items: - - const: microchip,mpfs-i2c # Microchip PolarFire SoC compatible SoCs + - enum: + - microchip,pic64gx-i2c + - microchip,mpfs-i2c - const: microchip,corei2c-rtl-v7 # Microchip Fabric based i2c IP core - const: microchip,corei2c-rtl-v7 # Microchip Fabric based i2c IP core diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/iio/adc/atmel,sama5d2-adc.yaml linux-microchip/Documentation/devicetree/bindings/iio/adc/atmel,sama5d2-adc.yaml --- linux-6.6.51/Documentation/devicetree/bindings/iio/adc/atmel,sama5d2-adc.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/iio/adc/atmel,sama5d2-adc.yaml 2024-11-26 14:02:35.426456580 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:14 @ properties: compatible: - enum: - - atmel,sama5d2-adc - - microchip,sam9x60-adc - - microchip,sama7g5-adc + oneOf: + - enum: + - atmel,sama5d2-adc + - atmel,sama5d3-adc + - atmel,at91sam9x5-adc + - microchip,sama7g5-adc + - items: + - enum: + - microchip,sam9x60-adc + - microchip,sam9x7-adc + - const: atmel,sama5d2-adc reg: maxItems: 1 diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/iio/adc/microchip,mcp3564.yaml linux-microchip/Documentation/devicetree/bindings/iio/adc/microchip,mcp3564.yaml --- linux-6.6.51/Documentation/devicetree/bindings/iio/adc/microchip,mcp3564.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/iio/adc/microchip,mcp3564.yaml 2024-11-26 14:02:35.426456580 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/microchip,mcp3564.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip MCP346X and MCP356X ADC Family + +maintainers: + - Marius Cristea <marius.cristea@microchip.com> + +description: | + Bindings for the Microchip family of 153.6 ksps, Low-Noise 16/24-Bit + Delta-Sigma ADCs with an SPI interface. Datasheet can be found here: + Datasheet for MCP3561, MCP3562, MCP3564 can be found here: + https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/MCP3561-2-4-Family-Data-Sheet-DS20006181C.pdf + Datasheet for MCP3561R, MCP3562R, MCP3564R can be found here: + https://ww1.microchip.com/downloads/aemDocuments/documents/APID/ProductDocuments/DataSheets/MCP3561_2_4R-Data-Sheet-DS200006391C.pdf + Datasheet for MCP3461, MCP3462, MCP3464 can be found here: + https://ww1.microchip.com/downloads/aemDocuments/documents/APID/ProductDocuments/DataSheets/MCP3461-2-4-Two-Four-Eight-Channel-153.6-ksps-Low-Noise-16-Bit-Delta-Sigma-ADC-Data-Sheet-20006180D.pdf + Datasheet for MCP3461R, MCP3462R, MCP3464R can be found here: + https://ww1.microchip.com/downloads/aemDocuments/documents/APID/ProductDocuments/DataSheets/MCP3461-2-4R-Family-Data-Sheet-DS20006404C.pdf + +properties: + compatible: + enum: + - microchip,mcp3461 + - microchip,mcp3462 + - microchip,mcp3464 + - microchip,mcp3461r + - microchip,mcp3462r + - microchip,mcp3464r + - microchip,mcp3561 + - microchip,mcp3562 + - microchip,mcp3564 + - microchip,mcp3561r + - microchip,mcp3562r + - microchip,mcp3564r + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 20000000 + + spi-cpha: true + + spi-cpol: true + + vdd-supply: true + + avdd-supply: true + + clocks: + description: + Phandle and clock identifier for external sampling clock. + If not specified, the internal crystal oscillator will be used. + maxItems: 1 + + interrupts: + description: IRQ line of the ADC + maxItems: 1 + + drive-open-drain: + description: + Whether to drive the IRQ signal as push-pull (default) or open-drain. Note + that the device requires this pin to become "high", otherwise it will stop + converting. + type: boolean + + vref-supply: + description: + Some devices have a specific reference voltage supplied on a different + pin to the other supplies. Needed to be able to establish channel scaling + unless there is also an internal reference available (e.g. mcp3564r). In + case of "r" devices (e. g. mcp3564r), if it does not exists the internal + reference will be used. + + microchip,hw-device-address: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 3 + description: + The address is set on a per-device basis by fuses in the factory, + configured on request. If not requested, the fuses are set for 0x1. + The device address is part of the device markings to avoid + potential confusion. This address is coded on two bits, so four possible + addresses are available when multiple devices are present on the same + SPI bus with only one Chip Select line for all devices. + Each device communication starts by a CS falling edge, followed by the + clocking of the device address (BITS[7:6] - top two bits of COMMAND BYTE + which is first one on the wire). + + "#io-channel-cells": + const: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + +patternProperties: + "^channel@([0-9]|([1-7][0-9]))$": + $ref: adc.yaml + type: object + unevaluatedProperties: false + description: Represents the external channels which are connected to the ADC. + + properties: + reg: + description: The channel number in single-ended and differential mode. + minimum: 0 + maximum: 79 + + required: + - reg + +dependencies: + spi-cpol: [ spi-cpha ] + spi-cpha: [ spi-cpol ] + +required: + - compatible + - reg + - microchip,hw-device-address + - spi-max-frequency + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + - # External vref, no internal reference + if: + properties: + compatible: + contains: + enum: + - microchip,mcp3461 + - microchip,mcp3462 + - microchip,mcp3464 + - microchip,mcp3561 + - microchip,mcp3562 + - microchip,mcp3564 + then: + required: + - vref-supply + +unevaluatedProperties: false + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "microchip,mcp3564r"; + reg = <0>; + vref-supply = <&vref_reg>; + spi-cpha; + spi-cpol; + spi-max-frequency = <10000000>; + microchip,hw-device-address = <1>; + + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + /* CH0 to AGND */ + reg = <0>; + label = "CH0"; + }; + + channel@1 { + /* CH1 to AGND */ + reg = <1>; + label = "CH1"; + }; + + /* diff-channels */ + channel@11 { + reg = <11>; + + /* CN0, CN1 */ + diff-channels = <0 1>; + label = "CH0_CH1"; + }; + + channel@22 { + reg = <0x22>; + + /* CN1, CN2 */ + diff-channels = <1 2>; + label = "CH1_CH3"; + }; + + channel@23 { + reg = <0x23>; + + /* CN1, CN3 */ + diff-channels = <1 3>; + label = "CH1_CH3"; + }; + }; + }; +... diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/iio/adc/microchip,pac1934.yaml linux-microchip/Documentation/devicetree/bindings/iio/adc/microchip,pac1934.yaml --- linux-6.6.51/Documentation/devicetree/bindings/iio/adc/microchip,pac1934.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/iio/adc/microchip,pac1934.yaml 2024-11-26 14:02:35.426456580 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/microchip,pac1934.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip PAC1934 Power Monitors with Accumulator + +maintainers: + - Marius Cristea <marius.cristea@microchip.com> + +description: | + This device is part of the Microchip family of Power Monitors with + Accumulator. + The datasheet for PAC1931, PAC1932, PAC1933 and PAC1934 can be found here: + https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/PAC1931-Family-Data-Sheet-DS20005850E.pdf + +properties: + compatible: + enum: + - microchip,pac1931 + - microchip,pac1932 + - microchip,pac1933 + - microchip,pac1934 + + reg: + maxItems: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + interrupts: + maxItems: 1 + + slow-io-gpios: + description: + A GPIO used to trigger a change is sampling rate (lowering the chip power + consumption). If configured in SLOW mode, if this pin is forced high, + sampling rate is forced to eight samples/second. When it is forced low, + the sampling rate is 1024 samples/second unless a different sample rate + has been programmed. + +patternProperties: + "^channel@[1-4]+$": + type: object + $ref: adc.yaml + description: + Represents the external channels which are connected to the ADC. + + properties: + reg: + items: + minimum: 1 + maximum: 4 + + shunt-resistor-micro-ohms: + description: + Value in micro Ohms of the shunt resistor connected between + the SENSE+ and SENSE- inputs, across which the current is measured. + Value is needed to compute the scaling of the measured current. + + required: + - reg + - shunt-resistor-micro-ohms + + unevaluatedProperties: false + +required: + - compatible + - reg + - "#address-cells" + - "#size-cells" + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + power-monitor@10 { + compatible = "microchip,pac1934"; + reg = <0x10>; + + #address-cells = <1>; + #size-cells = <0>; + + channel@1 { + reg = <0x1>; + shunt-resistor-micro-ohms = <24900000>; + label = "CPU"; + }; + + channel@2 { + reg = <0x2>; + shunt-resistor-micro-ohms = <49900000>; + label = "GPU"; + }; + + channel@3 { + reg = <0x3>; + shunt-resistor-micro-ohms = <75000000>; + label = "MEM"; + bipolar; + }; + + channel@4 { + reg = <0x4>; + shunt-resistor-micro-ohms = <100000000>; + label = "NET"; + bipolar; + }; + }; + }; + +... diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/input/atmel,ptc.txt linux-microchip/Documentation/devicetree/bindings/input/atmel,ptc.txt --- linux-6.6.51/Documentation/devicetree/bindings/input/atmel,ptc.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/input/atmel,ptc.txt 2024-11-26 14:02:35.430456652 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +Atmel PTC Subsystem + +The Atmel Peripheral Touch Controller subsystem offers built-in hardware +for capacitive touch measurement on sensors that function as buttons, sliders +and wheels. + +Required properties: +- compatible: Must be "atmel,sama5d2-ptc" +- reg: Address, length of the shared memory and ppp registers location + and length. +- clocks: Phandlers to the clocks. +- clock-names: Must be "ptc_clk", "ptc_int_osc", "slow_clk". + +Example: + ptc@fc060000 { + compatible = "atmel,sama5d2-ptc"; + reg = <0x00800000 0x10000 + 0xfc060000 0xcf>; + interrupts = <58 IRQ_TYPE_LEVEL_HIGH 7>; + clocks = <&ptc_clk>, <&main>, <&clk32k>; + clock-names = "ptc_clk", "ptc_int_osc", "slow_clk"; + }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/input/raspberrypi,sensehat-joystick.yaml linux-microchip/Documentation/devicetree/bindings/input/raspberrypi,sensehat-joystick.yaml --- linux-6.6.51/Documentation/devicetree/bindings/input/raspberrypi,sensehat-joystick.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/input/raspberrypi,sensehat-joystick.yaml 2024-11-26 14:04:18.556303237 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/raspberrypi,sensehat-joystick.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Raspberry Pi Sensehat Joystick + +maintainers: + - Charles Mirabile <cmirabil@redhat.com> + - Joel Savitz <jsavitz@redhat.com> + +description: + This device is part of the sensehat multi function device. + For more information see ../mfd/raspberrypi,sensehat.yaml. + + This device features a five button joystick (up, down,left, + right, click) + +properties: + compatible: + const: raspberrypi,sensehat-joystick + + interrupts: + maxItems: 1 + +required: + - compatible + - interrupts + +additionalProperties: false diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/interrupt-controller/microchip,mpfs-gpio-irq-mux.yaml linux-microchip/Documentation/devicetree/bindings/interrupt-controller/microchip,mpfs-gpio-irq-mux.yaml --- linux-6.6.51/Documentation/devicetree/bindings/interrupt-controller/microchip,mpfs-gpio-irq-mux.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/interrupt-controller/microchip,mpfs-gpio-irq-mux.yaml 2024-11-26 14:02:35.434456724 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interrupt-controller/microchip,mpfs-gpio-irq-mux.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip Polarfire SoC GPIO Interrupt Mux + +maintainers: + - Conor Dooley <conor.dooley@microchip.com> + +description: | + There are 3 GPIO controllers on this SoC, of which: + - GPIO controller 0 has 14 GPIOs + - GPIO controller 1 has 24 GPIOs + - GPIO controller 2 has 32 GPIOs + + All GPIOs are capable of generating interrupts, for a total of 70. + There are only 41 IRQs available however, so a configurable mux is used to + ensure all GPIOs can be used for interrupt generation. + 38 of the 41 interrupts are in what the documentation calls "direct mode", + as they provide an exclusive connection from a GPIO to the PLIC. + The 3 remaining interrupts are used to mux the interrupts which do not have + a exclusive connection, one for each GPIO controller. + +properties: + compatible: + oneOf: + - items: + - const: microchip,pic64gx-gpio-irq-mux + - const: microchip,mpfs-gpio-irq-mux + - const: microchip,mpfs-gpio-irq-mux + + reg: + maxItems: 1 + + interrupt-controller: true + + "#interrupt-cells": + const: 1 + + interrupts: + description: + The first 38 entries must be the "direct" interrupts, for exclusive + connections to the PLIC. The final 3 entries must be the + "non-direct"/muxed connections for each of GPIO controller 0, 1 & 2 + respectively. + maxItems: 41 + +allOf: + - $ref: /schemas/interrupt-controller.yaml# + +required: + - compatible + - reg + - interrupts + - "#interrupt-cells" + - interrupt-controller + +additionalProperties: false + +examples: + - | + irqmux: interrupt-controller@20002054 { + compatible = "microchip,mpfs-gpio-irq-mux"; + reg = <0x20002054 0x4>; + interrupt-parent = <&plic>; + interrupt-controller; + #interrupt-cells = <1>; + status = "okay"; + interrupts = <13>, <14>, <15>, <16>, + <17>, <18>, <19>, <20>, + <21>, <22>, <23>, <24>, + <25>, <26>, <27>, <28>, + <29>, <30>, <31>, <32>, + <33>, <34>, <35>, <36>, + <37>, <38>, <39>, <40>, + <41>, <42>, <43>, <44>, + <45>, <46>, <47>, <48>, + <49>, <50>, <51>, <52>, + <53>; + }; +... diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml linux-microchip/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml --- linux-6.6.51/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml 2024-11-26 14:02:35.434456724 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:61 @ - items: - enum: - canaan,k210-plic + - microchip,pic64gx-plic - sifive,fu540-c000-plic - starfive,jh7100-plic - starfive,jh7110-plic diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/mailbox/microchip,miv-ihc.yaml linux-microchip/Documentation/devicetree/bindings/mailbox/microchip,miv-ihc.yaml --- linux-6.6.51/Documentation/devicetree/bindings/mailbox/microchip,miv-ihc.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/mailbox/microchip,miv-ihc.yaml 2024-11-26 14:02:35.438456795 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mailbox/microchip,miv-ihc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip PolarFire Mi-V Inter-hart communication (IHC) driver + +maintainers: + - Valentina Fernandez <valentina.fernandezalanis@microchip.com> + - Conor Dooley <Conor Dooley <conor.dooley@microchip.com>> + +description: | + The Mi-V Inter-hart Communication (IHC) subsystem is used to exchange + data between harts in PolarFire SoC. It provides the ability to + send messages (ie. data, status and control) and coordinate between + harts through a non-blocking interrupt signaling mechanism. + +properties: + compatible: + const: microchip,miv-ihc + + interrupts: + description: | + Should contain the IHC interrupt associated with the lowest + hart ID in the local software context. + maxItems: 1 + + microchip,miv-ihc-remote-context-id: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [5, 6] + default: 6 + description: | + ID of the software context where data should be sent to. + Should be either a value of 5 (Context A) or 6 (Context B). + A default value of 6 should be used to match default IHC configuration + (Linux on context A and RTOS/BM on context B). + + "#mbox-cells": + const: 1 + +required: + - compatible + - interrupts + - "#mbox-cells" + - microchip,miv-ihc-remote-context-id + +additionalProperties: false + +examples: + - | + #include "dt-bindings/mailbox/miv-ihc.h" + ihc: mailbox { + compatible = "microchip,miv-ihc"; + interrupt-parent = <&plic>; + interrupts = <IHC_HART1_INT>; + microchip,miv-ihc-remote-context-id = <IHC_CONTEXT_B>; + #mbox-cells = <1>; + }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/mailbox/microchip,mpfs-mailbox.yaml linux-microchip/Documentation/devicetree/bindings/mailbox/microchip,mpfs-mailbox.yaml --- linux-6.6.51/Documentation/devicetree/bindings/mailbox/microchip,mpfs-mailbox.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/mailbox/microchip,mpfs-mailbox.yaml 2024-11-26 14:02:35.438456795 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:14 @ properties: compatible: - const: microchip,mpfs-mailbox + oneOf: + - items: + - const: microchip,pic64gx-mailbox + - const: microchip,mpfs-mailbox + - const: microchip,mpfs-mailbox reg: oneOf: - items: + - description: mailbox data registers + - items: - description: mailbox control & data registers - description: mailbox interrupt registers deprecated: true @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:32 @ - description: mailbox control registers - description: mailbox interrupt registers - description: mailbox data registers + deprecated: true interrupts: maxItems: 1 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:51 @ examples: - | soc { - #address-cells = <2>; - #size-cells = <2>; - mbox: mailbox@37020000 { + #address-cells = <1>; + #size-cells = <1>; + + mailbox@37020800 { compatible = "microchip,mpfs-mailbox"; - reg = <0x0 0x37020000 0x0 0x58>, <0x0 0x2000318C 0x0 0x40>, - <0x0 0x37020800 0x0 0x100>; + reg = <0x37020800 0x100>; interrupt-parent = <&L1>; interrupts = <96>; #mbox-cells = <1>; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/mailbox/microchip,sbi-ipc.yaml linux-microchip/Documentation/devicetree/bindings/mailbox/microchip,sbi-ipc.yaml --- linux-6.6.51/Documentation/devicetree/bindings/mailbox/microchip,sbi-ipc.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/mailbox/microchip,sbi-ipc.yaml 2024-11-26 14:02:35.438456795 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mailbox/microchip,sbi-ipc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip Inter-processor communication (IPC) mailbox controller + +maintainers: + - Valentina Fernandez <valentina.fernandezalanis@microchip.com> + +description: + The Microchip Inter-processor Communication (IPC) is used to exchange + data between processors. It provides the ability to send and receive + messages through a non-blocking interrupt signaling mechanism. + This SBI interface is compatible with the Mi-V Inter-hart + Communication (IHC) IP. + The microchip,sbi-ipc compatible string is inteded for use by software + running in s-mode. The SoC-specific compatibles are inteded for use + by the SBI implementation (m-mode). + +properties: + compatible: + enum: + - microchip,sbi-ipc + - microchip,miv-ihc-rtl-v2 + + reg: + maxItems: 1 + + interrupts: + minItems: 1 + maxItems: 5 + + interrupt-names: + minItems: 1 + maxItems: 5 + + "#mbox-cells": + description: + For the SBI "device", the cell represents the global "logical" channel IDs. + The meaning of channel IDs are platform firmware dependent. The + SoC-specific compatibles are intended for use by the SBI implementation, + rather than s-mode software. There the cell would represent the physical + channel and do not vary depending on platform firmware. + const: 1 + + microchip,ihc-chan-disabled-mask: + description: + Represents the enable/disable state of the bi-directional IHC channels + within the MIV-IHC IP configuration. The mask is a 16-bit value, but only + the first 15 bits are utilized.Each of the bits corresponds to + one of the 15 IHC channels. + A bit set to '1' indicates that the corresponding channel is disabled, + and any read or write operations to that channel will return zero. + A bit set to '0' indicates that the corresponding channel is enabled + and will be accessible through its dedicated address range registers. + The remaining bit of the 16-bit mask is reserved and should be ignored. + The actual enable/disable state of each channel is determined by the + IP block’s configuration. + $ref: /schemas/types.yaml#/definitions/uint16 + default: 0 + +required: + - compatible + - interrupts + - interrupt-names + - "#mbox-cells" + +additionalProperties: false + +allOf: + - if: + properties: + compatible: + contains: + const: microchip,sbi-ipc + then: + properties: + reg: false + else: + required: + - reg + + - if: + properties: + compatible: + contains: + const: microchip,miv-ihc-rtl-v2 + then: + properties: + interrupt-names: + items: + pattern: "^hart-[0-5]+$" + +examples: + - | + mailbox { + compatible = "microchip,sbi-ipc"; + interrupt-parent = <&plic>; + interrupts = <180>, <179>, <178>; + interrupt-names = "hart-1", "hart-2", "hart-3"; + #mbox-cells = <1>; + }; + - | + mailbox@50000000 { + compatible = "microchip,miv-ihc-rtl-v2"; + microchip,ihc-chan-disabled-mask= /bits/ 16 <0>; + reg = <0x50000000 0x1C000>; + interrupt-parent = <&plic>; + interrupts = <180>, <179>, <178>; + interrupt-names = "hart-1", "hart-2", "hart-3"; + #mbox-cells = <1>; + }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/media/microchip,fpga-dscmi.yaml linux-microchip/Documentation/devicetree/bindings/media/microchip,fpga-dscmi.yaml --- linux-6.6.51/Documentation/devicetree/bindings/media/microchip,fpga-dscmi.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/media/microchip,fpga-dscmi.yaml 2024-11-26 14:02:35.442456867 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/microchip,fpga-dscmi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip Digital Serial Camera Memory Interface + +maintainers: + - Shravan Chippa <shravan.chippa@microchip.com> + +description: + The Digital Serial Camera Memory Interface is a fabric-based video pipeline + used in Microchip's "Smart Embedded Vision" development kits. It captures raw + sRGB frames from a CSI2 interface, converts them to YUV422 & can convert to a + other formats, including H264 & MJPEG. + +properties: + compatible: + enum: + - microchip,fpga-dscmi + - microchip,fpga-dscmi-rtl-v2306 # Compatible hardware to enable/disable OSD feature + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + dmas: + maxItems: 1 + + dma-names: + items: + - const: rx + + reset-gpios: + maxItems: 1 + description: CSI2 module MIPI training reset + + memory-region: + description: + This memory region is used for DMA transfers to/from the streaming + interface. + maxItems: 1 + + port: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: + supports a single port node with CSI2 interface. + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + +required: + - compatible + - reg + - interrupts + - reset-gpios + - memory-region + - dmas + - dma-names + - port + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + mchp_dscmi_cam0: video-pipeline@40000000 { + compatible = "microchip,fpga-dscmi"; + reg = <0x40000000 0x2000>; + interrupt-parent = <&plic>; + interrupts = <118>; + reset-gpios = <&gpio2 4 GPIO_ACTIVE_HIGH>; + memory-region = <&cambuf0>; + dmas = <&pdma 0>; + dma-names = "rx"; + port { + mchp_dscmi_ep_cam0: endpoint { + remote-endpoint = <&imx334_ep_cam0>; + }; + }; + }; + +... diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/media/microchip,generic-video-pipeline-connector.yaml linux-microchip/Documentation/devicetree/bindings/media/microchip,generic-video-pipeline-connector.yaml --- linux-6.6.51/Documentation/devicetree/bindings/media/microchip,generic-video-pipeline-connector.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/media/microchip,generic-video-pipeline-connector.yaml 2024-11-26 14:02:35.442456867 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/microchip,generic-video-pipeline-connector.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip Generic Video Pipeline Connector (GVPC) + +maintainers: + - Shravan Chippa <shravan.chippa@microchip.com> + +description: | + The Microchip generic video pipeline connector is used for the FPGA + logic to convert the video format and stream the data through + AXI streaming or negative interface for the image processing. + +properties: + compatible: + const: microchip,generic-video-pipeline-connector + + reg: + maxItems: 1 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + port@0: + type: object + description: Input/sink port node to receive the video data. + + properties: + reg: + description: Input/sink port number. + const: 0 + + microchip,video-format: + description: Output/source video format. Possible values are as below. + 0 - MVCF_MONO_SENSOR + 1 - MVCF_YUV_444 + 2 - MVCF_YUV_422 + 3 - MVCF_YUV_420 + 4 - MVCF_RBG + 5 - MVCF_H264 + 6 - MVCF_MJPEG + 7 - MVCF_RGB + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2, 3, 4, 5, 6, 7] + + microchip,cfa-pattern: + description: Output/source video pattern. + $ref: /schemas/types.yaml#/definitions/string + enum: + - mono + - rggb + - bggr + - gbrg + - grbg + default: mono + + microchip,video-width: + description: Output/source video axi stream width. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [8, 10, 12, 16, 20, 32] + + endpoint: + type: object + + properties: + remote-endpoint: true + + required: + - remote-endpoint + + additionalProperties: false + + required: + - reg + - endpoint + - microchip,video-format + - microchip,video-width + allOf: + - if: + properties: + microchip,video-format: + contains: + const: 0 + then: + required: + - microchip,cfa-pattern + + additionalProperties: false + + patternProperties: + "port@1": + type: object + description: Output/source port node to transmit the video data. + properties: + reg: + description: Output/source port number. + + microchip,video-format: + description: + Output/source video format. + Possible values are as below - + 0 - MVCF_MONO_SENSOR + 1 - MVCF_YUV_444 + 2 - MVCF_YUV_422 + 3 - MVCF_YUV_420 + 4 - MVCF_RBG + 5 - MVCF_H264 + 6 - MVCF_MJPEG + 7 - MVCF_RGB + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2, 3, 4, 5, 6, 7] + + microchip,cfa-pattern: + description: Output/source video pattern. + $ref: /schemas/types.yaml#/definitions/string + enum: + - mono + - rggb + - bggr + - gbrg + - grbg + default: mono + + microchip,video-width: + description: Output/source video axi stream width. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [8, 10, 12, 16, 20, 32] + + endpoint: + type: object + + properties: + remote-endpoint: true + + required: + - remote-endpoint + + additionalProperties: false + + required: + - reg + - endpoint + - microchip,video-format + - microchip,video-width + allOf: + - if: + properties: + microchip,video-format: + contains: + const: 0 + then: + required: + - microchip,cfa-pattern + + additionalProperties: false + + required: + - "#address-cells" + - "#size-cells" + - port@0 + - port@1 + + additionalProperties: false + +required: + - compatible + - reg + - ports + +additionalProperties: false + +examples: + - | + #include <dt-bindings/media/microchip-common.h> + + mgvpc_0: gvpc-video-0@50020000 { + compatible = "microchip,generic-video-pipeline-connector"; + reg = <0x50020000 0x10000>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + /* Sink port */ + reg = <0>; + microchip,video-format = <MVCF_MONO_SENSOR>; + microchip,cfa-pattern = "rggb"; + microchip,video-width = <8>; + gvpc_0_in: endpoint { + remote-endpoint = <&mipi_csi2_0_out>; + }; + }; + port@1 { + /* Source port */ + reg = <1>; + microchip,video-format = <MVCF_YUV_420>; + microchip,video-width = <8>; + gvpc_0_out: endpoint { + remote-endpoint = <&h264_in>; + }; + }; + }; + }; +... diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/media/microchip,image-enhancement.yaml linux-microchip/Documentation/devicetree/bindings/media/microchip,image-enhancement.yaml --- linux-6.6.51/Documentation/devicetree/bindings/media/microchip,image-enhancement.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/media/microchip,image-enhancement.yaml 2024-11-26 14:02:35.442456867 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/microchip,image-enhancement.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip Video Image Enhancement + +maintainers: + - Shravan Chippa <shravan.chippa@microchip.com> + +description: | + The Microchip video image enhancement IP is used to control + the contrast, brightness etc of the video image and stream + the video data through AXI streaming interface or native + interface for image processing. + +properties: + compatible: + enum: + - microchip,image-enhancement-rtl-v1 + + reg: + maxItems: 1 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + port@0: + type: object + description: The Input/sink port node properties. + properties: + reg: + description: Input/sink port number. + const: 0 + + endpoint: + type: object + + properties: + remote-endpoint: true + + required: + - remote-endpoint + + additionalProperties: false + + required: + - reg + - endpoint + + additionalProperties: false + + patternProperties: + "port@1": + type: object + description: The Output/source port node properties. + properties: + reg: + description: Output/source port number. + + endpoint: + type: object + + properties: + remote-endpoint: true + + required: + - remote-endpoint + + additionalProperties: false + + required: + - reg + - endpoint + + additionalProperties: false + + required: + - "#address-cells" + - "#size-cells" + - port@0 + - port@1 + + additionalProperties: false + +required: + - compatible + - reg + - ports + +additionalProperties: false + +examples: + - | + #include <dt-bindings/media/microchip-common.h> + image-enhancement@50020000 { + compatible = "microchip,image-enhancement-rtl-v1"; + reg = <0x50020000 0x10000>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + /* Sink port */ + reg = <0>; + image_enhancement_0_in: endpoint { + remote-endpoint = <&monotorgb_0_out>; + }; + }; + port@1 { + /* Source port */ + reg = <1>; + image_enhancement_0_out: endpoint { + remote-endpoint = <&rgb_scaler_0_in>; + }; + }; + }; + }; +... diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/media/microchip,mipi-csi2rx.yaml linux-microchip/Documentation/devicetree/bindings/media/microchip,mipi-csi2rx.yaml --- linux-6.6.51/Documentation/devicetree/bindings/media/microchip,mipi-csi2rx.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/media/microchip,mipi-csi2rx.yaml 2024-11-26 14:02:35.442456867 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/microchip,mipi-csi2rx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip MIPI CSI-2 Receiver Subsystem + +maintainers: + - Shravan Chiappa <shravan.chippa@microchip.com> + +description: + The Microchip MIPI CSI-2 Receiver Subsystem is used to capture MIPI CSI-2 + traffic from compliant camera sensors and send the output as AXI4 Stream + video data for image processing. + The subsystem consists of a MIPI D-PHY in slave mode which captures the + data packets. This is passed along the MIPI CSI-2 Rx IP which extracts the + packet data. + +properties: + compatible: + items: + - enum: + - microchip,mipi-csi2-rx-rtl-v0 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: AXI Lite clock + - description: Video frame clock + + clock-names: + items: + - const: axi + - const: video + + microchip,csi-pxl-format: + description: | + This denotes the CSI Data type selected in hw design. + Possible values are as below - + 0x24 - RGB888 + 0x2a - RAW8 + 0x2b - RAW10 + 0x2c - RAW12 + 0x2d - RAW14 + 0x2e - RAW16 + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0x24, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e] + default: 0x2a + + microchip,csi-fixed-out-raw8: + type: boolean + description: Present when Video Format output is raw8 fixed + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/$defs/port-base + description: | + Input / sink port node, single endpoint describing the + CSI-2 transmitter. + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + description: | + This is required only in the sink port 0 endpoint which + connects to MIPI CSI-2 source like sensor. + The possible values are - + 1 - For 1 lane enabled in IP. + 1 2 - For 2 lanes enabled in IP. + 1 2 3 - For 3 lanes enabled in IP. + 1 2 3 4 - For 4 lanes enabled in IP. + items: + - const: 1 + - const: 2 + - const: 3 + - const: 4 + + required: + - data-lanes + + unevaluatedProperties: false + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: | + Output / source port node, endpoint describing modules + connected the CSI-2 receiver. + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - microchip,csi-pxl-format + - ports + +additionalProperties: false + +examples: + - | + mcsi2rx_0: csi2rx@50020000 { + compatible = "microchip,mipi-csi2-rx"; + reg = <0x50020000 0x10000>; + interrupt-parent = <&plic>; + interrupts = <120>; + microchip,csi-pxl-format = <0x2a>; + clock-names = "axi", "video"; + clocks = <&axi_clk_0>, <&video_clk_0>; + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + /* Sink port */ + reg = <0>; + csi_0_in: endpoint { + data-lanes = <1 2 3 4>; + /* MIPI CSI-2 Camera handle */ + remote-endpoint = <&camera_out>; + }; + }; + port@1 { + /* Source port */ + reg = <1>; + csi_0_out: endpoint { + remote-endpoint = <&vproc_in>; + }; + }; + }; + }; +... diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/media/microchip,osd.yaml linux-microchip/Documentation/devicetree/bindings/media/microchip,osd.yaml --- linux-6.6.51/Documentation/devicetree/bindings/media/microchip,osd.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/media/microchip,osd.yaml 2024-11-26 14:02:35.442456867 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/microchip,osd.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip Video On Screen Display (OSD) + +maintainers: + - Shravan Chippa <shravan.chippa@microchip.com> + +description: | + The Microchip video on screen display is a user-defined FPGA + logic to display text on the video frames and stream the video + data through the native interface for image processing. + +properties: + compatible: + enum: + - microchip,osd-rtl-v1 + + reg: + maxItems: 1 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + port@0: + type: object + description: The Input/sink port node properties. + properties: + reg: + description: Input/sink port number. + const: 0 + + endpoint: + type: object + + properties: + remote-endpoint: true + + required: + - remote-endpoint + + additionalProperties: false + + required: + - reg + - endpoint + + additionalProperties: false + + patternProperties: + "port@1": + type: object + description: The Output/source port node properties. + properties: + reg: + description: Output/source port number. + + endpoint: + type: object + + properties: + remote-endpoint: true + + required: + - remote-endpoint + + additionalProperties: false + + required: + - reg + - endpoint + + additionalProperties: false + + required: + - "#address-cells" + - "#size-cells" + - port@0 + - port@1 + + additionalProperties: false + +required: + - compatible + - reg + - ports + +additionalProperties: false + +examples: + - | + #include <dt-bindings/media/microchip-common.h> + osd-0@50020000 { + compatible = "microchip,osd-rtl-v1"; + reg = <0x50020000 0x10000>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + /* Sink port */ + reg = <0>; + osd_0_in: endpoint { + remote-endpoint = <&monotorgb_0_out>; + }; + }; + port@1 { + /* Source port */ + reg = <1>; + osd_0_out: endpoint { + remote-endpoint = <&rgb_scaler_0_in>; + }; + }; + }; + }; +... diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/media/microchip,rgb-scaler.yaml linux-microchip/Documentation/devicetree/bindings/media/microchip,rgb-scaler.yaml --- linux-6.6.51/Documentation/devicetree/bindings/media/microchip,rgb-scaler.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/media/microchip,rgb-scaler.yaml 2024-11-26 14:02:35.442456867 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/microchip,rgb-scaler.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip rgb scaler + +maintainers: + - Shravan Chippa <shravan.chippa@microchip.com> + +description: | + The Microchip video rgb scaler IP is used to scale up/down the + video image and stream the video data through AXI streaming + interface or native interface for the image processing. + +properties: + compatible: + enum: + - microchip,rgb-scaler-rtl-v1 + + reg: + maxItems: 1 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + port@0: + type: object + description: The Input/sink port node properties. + properties: + reg: + description: Input/sink port number. + const: 0 + + endpoint: + type: object + + properties: + remote-endpoint: true + + required: + - remote-endpoint + + additionalProperties: false + + required: + - reg + - endpoint + + additionalProperties: false + + patternProperties: + "port@1": + type: object + description: The Output/source port node properties. + properties: + reg: + description: Output/source port number. + + endpoint: + type: object + + properties: + remote-endpoint: true + + required: + - remote-endpoint + + additionalProperties: false + + required: + - reg + - endpoint + + additionalProperties: false + + required: + - "#address-cells" + - "#size-cells" + - port@0 + - port@1 + + additionalProperties: false + +required: + - compatible + - reg + - ports + +additionalProperties: false + +examples: + - | + #include <dt-bindings/media/microchip-common.h> + rgb-scaler-0@50020000 { + compatible = "microchip,rgb-scaler-rtl-v1"; + reg = <0x50020000 0x10000>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + /* Sink port */ + reg = <0>; + gvpc_0_in: endpoint { + remote-endpoint = <&image_enhancement_0_out>; + }; + }; + port@1 { + /* Source port */ + reg = <1>; + gvpc_0_out: endpoint { + remote-endpoint = <&osd_0_in>; + }; + }; + }; + }; +... diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/media/microchip,video-capture.yaml linux-microchip/Documentation/devicetree/bindings/media/microchip,video-capture.yaml --- linux-6.6.51/Documentation/devicetree/bindings/media/microchip,video-capture.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/media/microchip,video-capture.yaml 2024-11-26 14:02:35.442456867 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/microchip,video-capture.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip Video Capture + +maintainers: + - Shravan Chiappa <shravan.chippa@microchip.com> + +description: + Microchip Video Capture IP processes the video streams through one or more + video IP cores. The DT node of the video capture represents as a top level + node of the pipeline. + +properties: + compatible: + items: + - enum: + - microchip,video-dma-rtl-v0 + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: AXI Lite clock + - description: Video frame clock + + clock-names: + items: + - const: axi + - const: video + + memory-region: + description: Reserved memory for v4l2 buffers (optional) + maxItems: 1 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/$defs/port-base + description: | + Input / sink port node, endpoint describing modules connected video capture IP. + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - ports + +additionalProperties: false + +examples: + - | + video-cap@50020000 { + compatible = "microchip,video-dma-rtl-v0"; + reg = <0x50020000 0x10000>; + interrupt-parent = <&plic>; + interrupts = <121>; + clock-names = "axi", "video"; + clocks = <&axi_clk_0>, <&video_clk_0>; + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@1 { + /* Sink port */ + reg = <0>; + video_cap_in0: endpoint { + remote-endpoint = <&any_in>; + }; + }; + }; + }; + +... + diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/media/microchip,yuv2h264.yaml linux-microchip/Documentation/devicetree/bindings/media/microchip,yuv2h264.yaml --- linux-6.6.51/Documentation/devicetree/bindings/media/microchip,yuv2h264.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/media/microchip,yuv2h264.yaml 2024-11-26 14:02:35.442456867 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/microchip,yuv2h264.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip H.264 Encoder IP + +maintainers: + - Shravan Chippa <shravan.chippa@microchip.com> + +description: | + The Microchip H.264 encoder IP used for encode H.264 frame from + yuv422 frames and stream the video data through AXI streaming + or native interface to video DMA node. + +properties: + compatible: + items: + - enum: + - microchip,yuv2h264-rtl-v1 + + reg: + maxItems: 1 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + port@0: + type: object + description: The Input/sink port node properties. + properties: + reg: + description: Input/sink port number. + const: 0 + + endpoint: + type: object + + properties: + remote-endpoint: true + + required: + - remote-endpoint + + additionalProperties: false + + required: + - reg + - endpoint + + additionalProperties: false + + patternProperties: + "port@1": + type: object + description: The Output/source port node properties. + properties: + reg: + description: Output/source port number. + + endpoint: + type: object + + properties: + remote-endpoint: true + + required: + - remote-endpoint + + additionalProperties: false + + required: + - reg + - endpoint + + additionalProperties: false + + required: + - "#address-cells" + - "#size-cells" + - port@0 + - port@1 + + additionalProperties: false + +required: + - compatible + - reg + - ports + +additionalProperties: false + +examples: + - | + #include <dt-bindings/media/microchip-common.h> + + yuv2h264_0: h264-video-0@50020000 { + compatible = "microchip,yuv2h264-rtl-v1"; + reg = <0x50020000 0x10000>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + /* Sink port */ + reg = <0>; + h264_0_in: endpoint { + remote-endpoint = <&yuv420_0_out>; + }; + }; + port@1 { + /* Source port */ + reg = <1>; + h264_0_out: endpoint { + remote-endpoint = <&video_dma_in>; + }; + }; + }; + }; +... diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/media/snps,dw-csi.yaml linux-microchip/Documentation/devicetree/bindings/media/snps,dw-csi.yaml --- linux-6.6.51/Documentation/devicetree/bindings/media/snps,dw-csi.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/media/snps,dw-csi.yaml 2024-11-26 14:02:35.446456938 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/snps,dw-csi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Synopsys DesignWare CSI-2 Host controller (csi2host) + +maintainers: + - Eugen Hristev <eugen.hristev@microchip.com> + +description: + CSI2HOST is used to receive image coming from an MIPI CSI-2 compatible + camera. It will convert the incoming CSI-2 stream into a dedicated + interface called the Synopsys IDI (Image Data Interface). + This interface is a 32-bit SoC internal only, and can be assimilated + with a CSI-2 interface. + +properties: + compatible: + const: snps,dw-csi + + reg: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + description: + CSI2HOST can have two clocks connected. One clock is the + peripheral clock for the inside functionality of the hardware block. + This is named 'perclk'. The second clock can be the phy clock, + which is used to clock the phy via an internal link. + This clock is named 'phyclk', phy clock. + items: + - const: perclk + - const: phyclk + + phys: + maxItems: 1 + description: MIPI D-PHY + + phy-names: + items: + - const: dphy + + resets: + maxItems: 1 + + interrupts: + maxItems: 1 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: + Input port node, single endpoint describing the input port. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + description: Endpoint connected to input device + + properties: + bus-type: + const: 4 + + data-lanes: + minItems: 1 + maxItems: 4 + items: + maximum: 4 + + clock-lanes: + maxItems: 1 + + remote-endpoint: true + + port@1: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: + Output port node, single endpoint describing the output port. + + properties: + endpoint: + unevaluatedProperties: false + $ref: video-interfaces.yaml# + description: Endpoint connected to output device + + properties: + bus-type: + const: 4 + + remote-endpoint: true + + required: + - port@0 + - port@1 + +additionalProperties: false + +required: + - compatible + - ports + +examples: + - | + csi2: csi2@3000 { + compatible = "snps,dw-csi"; + reg = <0x03000 0x7FF>; + phys = <&mipi_dphy_rx>; + phy-names = "dphy"; + resets = <&dw_rst 1>; + interrupts = <2>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + csi_ep1: endpoint { + bus-type = <4>; /* MIPI CSI2 D-PHY */ + remote-endpoint = <&camera_1>; + data-lanes = <1 2>; + clock-lanes = <0>; + }; + }; + + port@1 { + reg = <1>; + + csi_ep2: endpoint { + remote-endpoint = <&idi_receiver>; + bus-type = <4>; + }; + }; + }; + }; + +... diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt linux-microchip/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt --- linux-6.6.51/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt 2024-11-26 14:02:35.446456938 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:14 @ "microchip,sam9x75-xlcdc" - reg: base address and size of the HLCDC device registers. - clock-names: the name of the 3 clocks requested by the HLCDC device. - Should contain "periph_clk", "sys_clk" and "slow_clk". + Should contain "periph_clk", "sys_clk" or "lvds_pll_clk" and "slow_clk". - clocks: should contain the 3 clocks requested by the HLCDC device. - interrupts: should contain the description of the HLCDC interrupt line diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/mfd/raspberrypi,sensehat.yaml linux-microchip/Documentation/devicetree/bindings/mfd/raspberrypi,sensehat.yaml --- linux-6.6.51/Documentation/devicetree/bindings/mfd/raspberrypi,sensehat.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/mfd/raspberrypi,sensehat.yaml 2024-11-26 14:04:18.556303237 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/raspberrypi,sensehat.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Raspberry Pi Sensehat + +maintainers: + - Charles Mirabile <cmirabil@redhat.com> + - Joel Savitz <jsavitz@redhat.com> + +description: + The Raspberry Pi Sensehat is an addon board originally developed + for the Raspberry Pi that has a joystick and an 8x8 RGB LED display + as well as several environmental sensors. It connects via i2c and + a gpio for irq. + +properties: + compatible: + const: raspberrypi,sensehat + + reg: + maxItems: 1 + + joystick: + $ref: /schemas/input/raspberrypi,sensehat-joystick.yaml# + + display: + $ref: /schemas/auxdisplay/raspberrypi,sensehat-display.yaml# + +required: + - compatible + - reg + - joystick + - display + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + i2c { + #address-cells = <1>; + #size-cells = <0>; + hat@46 { + compatible = "raspberrypi,sensehat"; + reg = <0x46>; + display { + compatible = "raspberrypi,sensehat-display"; + }; + joystick { + compatible = "raspberrypi,sensehat-joystick"; + interrupts = <23 IRQ_TYPE_EDGE_RISING>; + }; + }; + }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/mfd/syscon-smc.yaml linux-microchip/Documentation/devicetree/bindings/mfd/syscon-smc.yaml --- linux-6.6.51/Documentation/devicetree/bindings/mfd/syscon-smc.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/mfd/syscon-smc.yaml 2024-11-26 14:02:35.450457011 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/syscon-smc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: System Controller Registers R/W via SMC Device Tree Bindings + +description: | + System controller SMC node represents a register region containing a set + of miscellaneous registers accessed through a secure monitor. + The typical use-case is the same as the syscon one but when running with a + secure monitor. + +maintainers: + - Lee Jones <lee.jones@linaro.org> + +properties: + compatible: + anyOf: + - items: + - enum: + - atmel,sama5d2-sfr + + - const: syscon-smc + + - contains: + const: syscon-smc + minItems: 2 + maxItems: 4 # Should be enough + + arm,smc-id: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + The ATF smc function id used by the firmware. + + reg-io-width: + description: | + The size (in bytes) of the IO accesses that should be performed + on the device. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [1, 2, 4, 8] + +required: + - compatible + - arm,smc-id + +additionalProperties: false + +examples: + - | + sfr { + compatible = "atmel,sama5d2-sfr", "syscon-smc"; + arm,smc-id = <0x02000300>; + }; + +... diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/mfd/syscon.yaml linux-microchip/Documentation/devicetree/bindings/mfd/syscon.yaml --- linux-6.6.51/Documentation/devicetree/bindings/mfd/syscon.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/mfd/syscon.yaml 2024-11-26 14:02:35.450457011 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:28 @ contains: enum: - syscon - required: - compatible @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:55 @ - mediatek,mt8135-pctl-a-syscfg - mediatek,mt8135-pctl-b-syscfg - mediatek,mt8365-syscfg + - microchip,mpfs-sysreg-scb + - microchip,pic64gx-sysreg-scb - microchip,lan966x-cpu-syscon - microchip,sparx5-cpu-syscon - mstar,msc313-pmsleep diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/misc/microchip,mpfs-dma-proxy.yaml linux-microchip/Documentation/devicetree/bindings/misc/microchip,mpfs-dma-proxy.yaml --- linux-6.6.51/Documentation/devicetree/bindings/misc/microchip,mpfs-dma-proxy.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/misc/microchip,mpfs-dma-proxy.yaml 2024-11-26 14:02:35.450457011 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/misc/microchip,mpfs-dma-proxy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip PolarFire SoC DMA proxy + +description: + Userspace DMA interface, Exposing the platform DMA on PolarFire SoC + +maintainers: + - Shravan Chippa <shravan.chippa@microchip.com> + +properties: + compatible: + const: microchip,mpfs-dma-proxy + + dmas: + minItems: 1 + maxItems: 4 + + dma-names: + items: + - const: dma-proxy0 + - const: dma-proxy1 + - const: dma-proxy2 + - const: dma-proxy3 + minItems: 1 + +required: + - compatible + - dmas + - dma-names + +additionalProperties: false + +examples: + - | + + mpfs_dma_proxy: mpfs-dma-proxy { + compatible = "microchip,mpfs-dma-proxy"; + dmas = <&pdma 3>; + dma-names = "dma-proxy0"; + }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml linux-microchip/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml --- linux-6.6.51/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml 2024-11-26 14:02:35.450457011 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:18 @ - enum: - amd,pensando-elba-sd4hc - microchip,mpfs-sd4hc + - microchip,pic64gx-sd4hc - socionext,uniphier-sd4hc - const: cdns,sd4hc @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:117 @ minimum: 0 maximum: 0x7f + dma-noncoherent: true + required: - compatible - reg @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:126 @ - clocks allOf: - - $ref: mmc-controller.yaml + - $ref: sdhci-common.yaml# - if: properties: compatible: diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/mtd/atmel-nand.txt linux-microchip/Documentation/devicetree/bindings/mtd/atmel-nand.txt --- linux-6.6.51/Documentation/devicetree/bindings/mtd/atmel-nand.txt 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/mtd/atmel-nand.txt 2024-11-26 14:02:35.454457082 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:56 @ Required properties: - compatible: should be one of the following "atmel,at91sam9g45-pmecc" + "microchip,sam9x7-pmecc" "atmel,sama5d4-pmecc" "atmel,sama5d2-pmecc" "microchip,sam9x60-pmecc" diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/net/can/microchip,mpfs-can.yaml linux-microchip/Documentation/devicetree/bindings/net/can/microchip,mpfs-can.yaml --- linux-6.6.51/Documentation/devicetree/bindings/net/can/microchip,mpfs-can.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/net/can/microchip,mpfs-can.yaml 2024-11-26 14:02:35.454457082 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:18 @ properties: compatible: - const: microchip,mpfs-can + oneOf: + - items: + - const: microchip,pic64gx-can + - const: microchip,mpfs-can + - const: microchip,mpfs-can reg: maxItems: 1 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:31 @ maxItems: 1 clocks: + items: + - description: AHB peripheral clock + - description: CAN bus clock + + resets: maxItems: 1 required: @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:51 @ can@2010c000 { compatible = "microchip,mpfs-can"; reg = <0x2010c000 0x1000>; - clocks = <&clkcfg 17>; + clocks = <&clkcfg 17>, <&clkcfg 37>; interrupt-parent = <&plic>; interrupts = <56>; }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/net/cdns,macb.yaml linux-microchip/Documentation/devicetree/bindings/net/cdns,macb.yaml --- linux-6.6.51/Documentation/devicetree/bindings/net/cdns,macb.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/net/cdns,macb.yaml 2024-11-26 14:02:35.454457082 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:41 @ - cdns,sam9x60-macb # Microchip sam9x60 SoC - microchip,mpfs-macb # Microchip PolarFire SoC - const: cdns,macb # Generic - + - items: + - const: microchip,pic64gx-macb # Microchip PIC64GX SoC + - const: microchip,mpfs-macb # Microchip PolarFire SoC + - const: cdns,macb # Generic - items: - enum: - atmel,sama5d3-macb # 10/100Mbit IP on Atmel sama5d3 SoCs @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:65 @ - cdns,gem # Generic - cdns,macb # Generic + - items: + - enum: + - microchip,sam9x7-gem # Microchip SAM9X7 gigabit ethernet interface + - const: microchip,sama7g5-gem # Microchip SAMA7G5 gigabit ethernet interface + reg: minItems: 1 items: @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:94 @ - enum: [ rx_clk, tsu_clk ] - const: tsu_clk + dma-noncoherent: true + local-mac-address: true phy-mode: true diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml linux-microchip/Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml --- linux-6.6.51/Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/pci/microchip,pcie-host.yaml 2024-11-26 14:02:35.470457369 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:21 @ const: microchip,pcie-host-1.0 # PolarFire reg: - maxItems: 2 + maxItems: 3 reg-names: items: - const: cfg - - const: apb + - const: bridge + - const: ctrl clocks: description: @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:56 @ items: pattern: '^fic[0-3]$' + dma-noncoherent: true + interrupts: minItems: 1 items: @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:71 @ - const: msi ranges: - maxItems: 1 + minItems: 1 + maxItems: 3 dma-ranges: minItems: 1 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:121 @ pcie0: pcie@2030000000 { compatible = "microchip,pcie-host-1.0"; reg = <0x0 0x70000000 0x0 0x08000000>, - <0x0 0x43000000 0x0 0x00010000>; - reg-names = "cfg", "apb"; + <0x0 0x43008000 0x0 0x00002000>, + <0x0 0x4300a000 0x0 0x00002000>; + reg-names = "cfg", "bridge", "ctrl"; device_type = "pci"; #address-cells = <3>; #size-cells = <2>; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/phy/microchip,sama7-usb-phy.yaml linux-microchip/Documentation/devicetree/bindings/phy/microchip,sama7-usb-phy.yaml --- linux-6.6.51/Documentation/devicetree/bindings/phy/microchip,sama7-usb-phy.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/phy/microchip,sama7-usb-phy.yaml 2024-11-26 14:02:35.474457440 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/microchip,sama7-usb-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip SAMA7 USB PHY + +maintainers: + - Cristian Birsan <cristian.birsan@microchip.com> + +properties: + "#phy-cells": + const: 0 + + compatible: + enum: + - microchip,sama7g5-usb-phy + + reg: + items: + - description: PHY id + + clocks: + maxItems: 1 + description: USB PHY bus clock + + clock-names: + const: utmi_clk + + sfr-phandle: + maxItems: 1 + description: phandle to Special Function Registers (SFR) node + +required: + - "#phy-cells" + - compatible + - clocks + - clock-names + - reg + - sfr-phandle + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/at91.h> + + utmi { + #address-cells = <1>; + #size-cells = <0>; + + usb_phy0: phy@0 { + compatible = "microchip,sama7g5-usb-phy"; + sfr-phandle = <&sfr>; + reg = <0>; + clocks = <&utmi_clk UTMI1>; + clock-names = "utmi_clk"; + status = "disabled"; + #phy-cells = <0>; + }; + }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/phy/microchip,sama7-utmi-clk.yaml linux-microchip/Documentation/devicetree/bindings/phy/microchip,sama7-utmi-clk.yaml --- linux-6.6.51/Documentation/devicetree/bindings/phy/microchip,sama7-utmi-clk.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/phy/microchip,sama7-utmi-clk.yaml 2024-11-26 14:02:35.474457440 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/microchip,sama7-utmi-clk.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip SAMA7 UTMI Clock + +maintainers: + - Cristian Birsan <cristian.birsan@microchip.com> + +properties: + "#clock-cells": + const: 1 + + compatible: + enum: + - microchip,sama7g5-utmi-clk + + clocks: + maxItems: 1 + + clock-names: + maxItems: 1 + + sfr-phandle: + maxItems: 1 + description: phandle to Special Function Registers (SFR) node + + resets: + maxItems: 3 + minItems: 3 + + reset-names: + maxItems: 3 + minItems: 3 + +required: + - "#clock-cells" + - compatible + - sfr-phandle + - clocks + - clock-names + - resets + - reset-names + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/at91.h> + #include <dt-bindings/reset/sama7g5-reset.h> + + utmi_clk: utmi-clk { + compatible = "microchip,sama7g5-utmi-clk"; + sfr-phandle = <&sfr>; + #clock-cells = <1>; + clocks = <&pmc PMC_TYPE_CORE PMC_UTMI>; + 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"; + }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/phy/snps,dw-dphy-rx.txt linux-microchip/Documentation/devicetree/bindings/phy/snps,dw-dphy-rx.txt --- linux-6.6.51/Documentation/devicetree/bindings/phy/snps,dw-dphy-rx.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/phy/snps,dw-dphy-rx.txt 2024-11-26 14:02:35.478457512 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +Synopsys DesignWare MIPI Rx D-PHY block details + +Description +----------- + +The Synopsys MIPI D-PHY controller supports MIPI-DPHY in receiver mode. +Please refer to phy-bindings.txt for more information. + +Required properties: +- compatible : Shall be "snps,dw-dphy-rx". +- #phy-cells : Must be 1. +- bus-width : Size of the test interface data bus (8 bits->8 or + 12bits->12). +- snps,dphy-frequency : Frequency at which D-PHY should start, configurable. + Check Synopsys databook. (-kHz) +- reg : Test interface register. This correspondes to the + physical base address of the controller and size of + the device memory mapped registers; Check Synopsys + databook. + +Example: + + mipi_dphy_rx1: dphy@d00003040 { + compatible = "snps,dw-dphy-rx"; + #phy-cells = <1>; + bus-width = <12>; + snps,dphy-frequency = <300000>; + reg = <0xd0003040 0x20>; + }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/pinctrl/atmel,at91-pio4-pinctrl.txt linux-microchip/Documentation/devicetree/bindings/pinctrl/atmel,at91-pio4-pinctrl.txt --- linux-6.6.51/Documentation/devicetree/bindings/pinctrl/atmel,at91-pio4-pinctrl.txt 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/pinctrl/atmel,at91-pio4-pinctrl.txt 2024-11-26 14:02:35.478457512 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:9 @ Required properties: - compatible: "atmel,sama5d2-pinctrl" + "microchip,sama7d65-pinctrl" "microchip,sama7g5-pinctrl" - reg: base address and length of the PIO controller. - interrupts: interrupt outputs from the controller, one for each bank. diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/power/reset/atmel,sama5d2-shdwc.yaml linux-microchip/Documentation/devicetree/bindings/power/reset/atmel,sama5d2-shdwc.yaml --- linux-6.6.51/Documentation/devicetree/bindings/power/reset/atmel,sama5d2-shdwc.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/power/reset/atmel,sama5d2-shdwc.yaml 2024-11-26 14:02:35.486457655 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:51 @ description: enable real-time timer wake-up type: boolean + microchip,lpm-connection: + description: list of phandles to devices which are connected to SHDWC's LPM pin + $ref: /schemas/types.yaml#/definitions/phandle + patternProperties: "^input@[0-15]$": description: @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:95 @ properties: atmel,wakeup-rtt-timer: false + - if: + properties: + compatible: + contains: + enum: + - atmel,sama5d2-shdwc + - microchip,sam9x60-shdwc + then: + properties: + microchip,lpm-connection: false + additionalProperties: false examples: diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/remoteproc/microchip,ipc-remoteproc.yaml linux-microchip/Documentation/devicetree/bindings/remoteproc/microchip,ipc-remoteproc.yaml --- linux-6.6.51/Documentation/devicetree/bindings/remoteproc/microchip,ipc-remoteproc.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/remoteproc/microchip,ipc-remoteproc.yaml 2024-11-26 14:02:35.494457798 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/remoteproc/microchip,ipc-remoteproc.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Microchip IPC Remote Processor + +description: + Microchip family of RISC-V SoCs typically have one or more + clusters. These clusters can be configured to run in an Asymmetric + Multi Processing (AMP) mode where clusters are split in independent + software contexts. + + This document defines the binding for the remoteproc component that + loads and boots firmwares on remote clusters. + + This SBI interface is compatible with the Mi-V Inter-hart + Communication (IHC) IP. + +maintainers: + - Valentina Fernandez <valentina.fernandezalanis@microchip.com> + +properties: + compatible: + const: microchip,ipc-remoteproc + + mboxes: + description: + This property is required only if the rpmsg/virtio functionality is used. + Microchip IPC mailbox specifier. To be used for communication with a + remote cluster. The specifier format is as per the bindings, + Documentation/devicetree/bindings/mailbox/microchip,sbi-ipc.yaml + maxItems: 1 + + microchip,auto-boot: + $ref: /schemas/types.yaml#/definitions/flag + description: + If defined, when remoteproc is probed, it loads the default firmware and + starts the remote processor. + + memory-region: + description: + If present, a phandle for a reserved memory area that used for vdev buffer, + resource table, vring region and others used by remote cluster. + +required: + - compatible + +additionalProperties: false + +examples: + - | + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + + contextb: contextb_reserved@81000000 { + reg = <0x81000000 0x400000>; + no-map; + }; + }; + + soc { + #address-cells = <2>; + #size-cells = <2>; + + rproc-contextb { + compatible = "microchip,ipc-remoteproc"; + memory-region = <&contextb>; + mboxes= <&ihc 8>; + }; + }; + +... diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/remoteproc/microchip,miv-remoteproc.yaml linux-microchip/Documentation/devicetree/bindings/remoteproc/microchip,miv-remoteproc.yaml --- linux-6.6.51/Documentation/devicetree/bindings/remoteproc/microchip,miv-remoteproc.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/remoteproc/microchip,miv-remoteproc.yaml 2024-11-26 14:02:35.494457798 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/remoteproc/microchip,miv-remoteproc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip Mi-V Remoteproc + +description: + Microchip's SoCs using the Mi-V ecosystem typically have one or more + harts (cores) within a coreplex. This coreplex can be configured to run in + an Asymmetric Multi Processing (AMP) mode where harts (cores) are split in + independent software contexts. + + This document defines the binding for the remoteproc component that loads and + boots firmwares on the remote AMP context. + +maintainers: + - Valentina Fernandez <valentina.fernandezalanis@microchip.com> + - Conor Dooley <conor.dooley@microchip.com> + +properties: + compatible: + const: microchip,miv-remoteproc + + mboxes: + description: + This property is required only if the rpmsg/virtio functionality is used. + Mi-V IHC mailbox specifier. To be used for communication with a + remote AMP context. The specifier format is as per the bindings, + Documentation/devicetree/bindings/mailbox/microchip,miv-ihc.yaml + minItems: 1 + + microchip,auto-boot: + $ref: /schemas/types.yaml#/definitions/flag + description: + If defined, when remoteproc is probed, it loads the default firmware and + starts the remote processor. + +allOf: + - $ref: /schemas/reserved-memory/memory-region.yaml + +required: + - compatible + +unevaluatedProperties: false + +examples: + - | + + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + + contextb: contextb_reserved@81000000 { + reg = <0x81000000 0x400000>; + no-map; + }; + }; + + soc { + #address-cells = <2>; + #size-cells = <2>; + + remote-context { + compatible = "microchip,miv-remoteproc"; + memory-region = <&contextb>; + mboxes= <&ihc 0>; + }; + }; + +... diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/riscv/microchip.yaml linux-microchip/Documentation/devicetree/bindings/riscv/microchip.yaml --- linux-6.6.51/Documentation/devicetree/bindings/riscv/microchip.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/riscv/microchip.yaml 2024-11-26 14:02:35.494457798 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:7 @ $id: http://devicetree.org/schemas/riscv/microchip.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Microchip PolarFire SoC-based boards +title: Microchip SoC-based boards maintainers: - Conor Dooley <conor.dooley@microchip.com> - Daire McNamara <daire.mcnamara@microchip.com> description: - Microchip PolarFire SoC-based boards + Microchip SoC-based boards properties: $nodename: @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:25 @ - enum: - microchip,mpfs-icicle-reference-rtlv2203 - microchip,mpfs-icicle-reference-rtlv2210 + - microchip,mpfs-icicle-reference-rtl-v2406 - const: microchip,mpfs-icicle-kit - const: microchip,mpfs + - items: + - const: microchip,mpfs-video-kit + - const: microchip,mpfs-sev-kit + - const: microchip,mpfs - items: - enum: - aldec,tysom-m-mpfs250t-rev2 - aries,m100pfsevp + - beagle,beaglev-fire + - microchip,mpfs-disco-kit - microchip,mpfs-sev-kit - sundance,polarberry - const: microchip,mpfs + - items: + - const: microchip,pic64gx-curiosity-kit + - const: microchip,pic64gx additionalProperties: true diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/rtc/microchip,mfps-rtc.yaml linux-microchip/Documentation/devicetree/bindings/rtc/microchip,mfps-rtc.yaml --- linux-6.6.51/Documentation/devicetree/bindings/rtc/microchip,mfps-rtc.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/rtc/microchip,mfps-rtc.yaml 2024-11-26 14:02:35.498457870 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:19 @ properties: compatible: - enum: - - microchip,mpfs-rtc + oneOf: + - items: + - const: microchip,pic64gx-rtc + - const: microchip,mpfs-rtc + - const: microchip,mpfs-rtc reg: maxItems: 1 diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/soc/microchip/atmel,at91rm9200-tcb.yaml linux-microchip/Documentation/devicetree/bindings/soc/microchip/atmel,at91rm9200-tcb.yaml --- linux-6.6.51/Documentation/devicetree/bindings/soc/microchip/atmel,at91rm9200-tcb.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/soc/microchip/atmel,at91rm9200-tcb.yaml 2024-11-26 14:02:35.502457941 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:23 @ - atmel,at91rm9200-tcb - atmel,at91sam9x5-tcb - atmel,sama5d2-tcb + - microchip,sam9x60-tcb - const: simple-mfd - const: syscon diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-control-scb.yaml linux-microchip/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-control-scb.yaml --- linux-6.6.51/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-control-scb.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-control-scb.yaml 2024-11-26 14:02:35.502457941 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/microchip/microchip,mpfs-control-scb.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip PolarFire SoC System Controller Bus (SCB) Control Register region + +maintainers: + - Conor Dooley <conor.dooley@microchip.com> + +description: + An assortment of system controller related registers, including voltage and + temperature sensors and the status/control registers for the system + controller's mailbox. + +properties: + compatible: + oneOf: + - items: + - const: microchip,pic64gx-control-scb + - const: microchip,mpfs-control-scb + - const: syscon + - const: simple-mfd + - items: + - const: microchip,mpfs-control-scb + - const: syscon + - const: simple-mfd + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + soc { + #address-cells = <1>; + #size-cells = <1>; + + syscon@37020000 { + compatible = "microchip,mpfs-control-scb", "syscon", "simple-mfd"; + reg = <0x37020000 0x100>; + }; + }; + diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-mss-top-sysreg.yaml linux-microchip/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-mss-top-sysreg.yaml --- linux-6.6.51/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-mss-top-sysreg.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-mss-top-sysreg.yaml 2024-11-26 14:02:35.502457941 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/microchip/microchip,mpfs-mss-top-sysreg.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip PolarFire SoC Microprocessor Subsystem (MSS) sysreg Register region + +maintainers: + - Conor Dooley <conor.dooley@microchip.com> + +description: + An wide assortment of registers that control elements of the MSS on PolarFire + SoC, including pinmuxing, resets and clocks among others. + +properties: + compatible: + oneOf: + - items: + - const: microchip,pic64gx-mss-top-sysreg + - const: microchip,mpfs-mss-top-sysreg + - const: syscon + - const: simple-mfd + - items: + - const: microchip,mpfs-mss-top-sysreg + - const: syscon + - const: simple-mfd + + reg: + maxItems: 1 + + '#reset-cells': + description: | + The AHB/AXI peripherals on the PolarFire SoC have reset support, so + from CLK_ENVM to CLK_CFM. The reset consumer should specify the + desired peripheral via the clock ID in its "resets" phandle cell. + See include/dt-bindings/clock/microchip,mpfs-clock.h for the full list + of PolarFire clock/reset IDs. + const: 1 + + '#address-cells': + const: 1 + + '#size-cells': + const: 1 + + interrupt-controller@54: + type: object + description: | + This interrupt controller is the GPIO interrupt multiplexer. + +required: + - compatible + - reg + - '#reset-cells' + +additionalProperties: false + +examples: + - | + soc { + #address-cells = <1>; + #size-cells = <1>; + + syscon@20002000 { + compatible = "microchip,mpfs-mss-top-sysreg", "syscon", "simple-mfd"; + reg = <0x20002000 0x1000>; + #reset-cells = <1>; + }; + }; + diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-sys-controller.yaml linux-microchip/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-sys-controller.yaml --- linux-6.6.51/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-sys-controller.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-sys-controller.yaml 2024-11-26 14:02:35.502457941 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:27 @ maxItems: 1 compatible: - const: microchip,mpfs-sys-controller + enum: + - microchip,mpfs-sys-controller + - microchip,pic64gx-sys-controller + + microchip,bitstream-flash: + $ref: /schemas/types.yaml#/definitions/phandle + description: + The SPI flash connected to the system controller's QSPI controller. + The system controller may retrieve FPGA bitstreams from this flash to + perform In-Application Programming (IAP) or during device initialisation + for Auto Update. The MSS and system controller have separate QSPI + controllers and this flash is connected to both. Software running in the + MSS can write bitstreams to the flash. required: - compatible diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/sound/microchip,asrc-card.yaml linux-microchip/Documentation/devicetree/bindings/sound/microchip,asrc-card.yaml --- linux-6.6.51/Documentation/devicetree/bindings/sound/microchip,asrc-card.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/sound/microchip,asrc-card.yaml 2024-11-26 14:02:35.506458014 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/microchip,asrc-card.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sound Card driver for Microchip's Asynchronous Sample Rate Converter + +maintainers: + - Codrin Ciubotariu <codrin.ciubotariu@microchip.com> + +description: + Sound Card driver to be used with Microchip's ASRC. It is able to register + the ASRC as Front-End and makes the rest of the audio IPs behave as + Back-ends and as normal PCMs as well. The ASRC use is optional though. + +definitions: + model: + description: User specified audio sound card name. + $ref: /schemas/types.yaml#/definitions/string + + audio-routing: + description: | + A list of the connections between audio components. + Each entry is a pair of strings, the first being the + connection's sink, the second being the connection's source. + $ref: /schemas/types.yaml#/definitions/non-unique-string-array + + dai-tdm-slot-num: + description: see tdm-slot.txt. + $ref: /schemas/types.yaml#/definitions/uint32 + + dai-tdm-slot-width: + description: see tdm-slot.txt. + $ref: /schemas/types.yaml#/definitions/uint32 + + frame-master: + description: Indicates dai-link frame master. + $ref: /schemas/types.yaml#/definitions/phandle + + bitclock-master: + description: Indicates dai-link bit clock master + $ref: /schemas/types.yaml#/definitions/phandle + + label: + maxItems: 1 + + format: + description: audio format. + items: + enum: + - i2s + - right_j + - left_j + - dsp_a + - dsp_b + - pdm + + convert-rate: + description: Front-End to Back-End rate convert. + $ref: /schemas/types.yaml#/definitions/uint32 + + convert-channels: + description: Front-end to Back-end channels convert. + $ref: /schemas/types.yaml#/definitions/uint32 + + convert-sample-format: + description: Front-end to Back-end sample format convert. + $ref: /schemas/types.yaml#/definitions/uint32 + + dai: + type: object + properties: + sound-dai: + maxItems: 1 + + # common properties + frame-master: + $ref: /schemas/types.yaml#/definitions/flag + bitclock-master: + $ref: /schemas/types.yaml#/definitions/flag + dai-tdm-slot-num: + $ref: "#/definitions/dai-tdm-slot-num" + dai-tdm-slot-width: + $ref: "#/definitions/dai-tdm-slot-width" + required: + - sound-dai +properties: + compatible: + const: microchip,sama7g5-asrc-card + + "#address-cells": + const: 1 + "#size-cells": + const: 0 + + label: + $ref: "#/definitions/label" + +patternProperties: + "^microchip,dai-link(@[0-9a-f]+)?$": + description: | + Container for dai-link level properties and the CPU and CODEC sub-nodes. + This container may be omitted when the card has only one DAI link. + type: object + properties: + + # common properties + microchip,frame-master: + $ref: "#/definitions/frame-master" + microchip,bitclock-master: + $ref: "#/definitions/bitclock-master" + microchip,format: + $ref: "#/definitions/format" + microchip,convert-rate: + $ref: "#/definitions/convert-rate" + microchip,convert-channels: + $ref: "#/definitions/convert-channels" + microchip,convert-sample-format: + $ref: "#/definitions/convert-sample-format" + + patternProperties: + "^cpu(@[0-9a-f]+)?": + $ref: "#/definitions/dai" + "^codec(@[0-9a-f]+)?": + $ref: "#/definitions/dai" + additionalProperties: false + +required: + - compatible + - microchip,model + +additionalProperties: false + +examples: + - | + #include <dt-bindings/sound/microchip,asrc-card.h> + + sound: sound { + #address-cells = <1>; + #size-cells = <0>; + compatible = "microchip,asrc-card"; + microchip,model = "mchp-asrc-card @ sama5g7 EK"; + + microchip,audio-asrc = <&asrc 0>, <&asrc 1>, <&asrc 2>, <&asrc 3>; + + microchip,dai-link@0 { + reg = <0>; + microchip,format = "i2s"; + microchip,frame-master = <&dailink_master>; + microchip,bitclock-master = <&dailink_master>; + microchip,convert-channels = <2>; + microchip,convert-rate = <48000>; + microchip,convert-sample-format = <MCHP_ASRC_PCM_FORMAT_S24_LE>; + dailink_master: cpu { + sound-dai = <&i2s0>; + }; + codec { + sound-dai = <&wm8731>; + }; + }; + }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/sound/microchip,asrc.yaml linux-microchip/Documentation/devicetree/bindings/sound/microchip,asrc.yaml --- linux-6.6.51/Documentation/devicetree/bindings/sound/microchip,asrc.yaml 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/bindings/sound/microchip,asrc.yaml 2024-11-26 14:02:35.506458014 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/microchip,asrc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Asynchronous Sample Rate Converter + +maintainers: + - Codrin Ciubotariu <codrin.ciubotariu@microchip.com> + +description: + The Asynchronous Sample Rate Converter (ASRC) converts the sample rate of an + incoming audio frame without affecting quality. It supports input and output + sampling rates up to 192 kHz. It is made of 4 independent digital signal + processing modules (DSP) + +properties: + compatible: + const: microchip,sama7g5-asrc + + reg: + maxItems: 1 + + "#sound-dai-cells": + const: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: Peripheral Bus Clock + - description: Generic Clock + + clock-names: + items: + - const: pclk + - const: gclk + + dmas: + description: RX and TX DMA Channels + minItems: 8 + maxItems: 8 + + dma-names: + items: + - const: rx0 + - const: tx0 + - const: rx1 + - const: tx1 + - const: rx2 + - const: tx2 + - const: rx3 + - const: tx3 + + microchip,triggers: + $ref: /schemas/types.yaml#/definitions/phandle-array + minItems: 1 + maxItems: 16 + description: | + List of phandles pointing to other audio peripherals that can generate + a trigger to ASRC. The trigger is used by the ASRC to compute the needed + sampling rate. + + microchip,trigger-indexes: + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 16 + description: | + An array of external (to ASRC) trigger indexes. Position of an entry + determines to which trigger phandle (from the 'trigger' property) the + value reffers to. e.g. the first value in 'trigger-indexes' will + correspond to the first phande in 'triggers', the second value from + 'trigger-indexes' will correspond to the second phandle in 'triggers', + etc. + +required: + - compatible + - reg + - "#sound-dai-cells" + - interrupts + - clocks + - clock-names + - dmas + - dma-names + - microchip,triggers + - microchip,trigger-indexes + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/at91.h> + #include <dt-bindings/dma/at91.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + + asrc: sound@e1610000 { + compatible = "microchip,sama7g5-asrc"; + reg = <0xe1610000 0x1000>; + interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>; + #sound-dai-cells = <1>; + dmas = <&dma0 AT91_XDMAC_DT_PERID(55)>, + <&dma0 AT91_XDMAC_DT_PERID(56)>, + <&dma0 AT91_XDMAC_DT_PERID(57)>, + <&dma0 AT91_XDMAC_DT_PERID(58)>, + <&dma0 AT91_XDMAC_DT_PERID(59)>, + <&dma0 AT91_XDMAC_DT_PERID(60)>, + <&dma0 AT91_XDMAC_DT_PERID(61)>, + <&dma0 AT91_XDMAC_DT_PERID(62)>; + dma-names = "rx0", "tx0", "rx1", "tx1", "rx2", "tx2", "rx3", "tx3"; + clocks = <&pmc PMC_TYPE_PERIPHERAL 30>, <&pmc PMC_TYPE_GCK 30>; + clock-names = "pclk", "gclk"; + microchip,triggers = <&i2s0>, <&i2s1>, <&pdmc0>, <&pdmc1>, <&ssc0>, + <&ssc1>, <&spdiftx>, <&spdifrx>; + microchip,trigger-indexes = <1>, <2>, <3>, <4>, <5>, <8>, <9>, <10>; + }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/sound/microchip,sama7g5-i2smcc.yaml linux-microchip/Documentation/devicetree/bindings/sound/microchip,sama7g5-i2smcc.yaml --- linux-6.6.51/Documentation/devicetree/bindings/sound/microchip,sama7g5-i2smcc.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/sound/microchip,sama7g5-i2smcc.yaml 2024-11-26 14:02:35.506458014 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:50 @ - const: gclk minItems: 1 + sound-name-prefix: + pattern: "^I2SMCC[0-9]$" + $ref: /schemas/types.yaml#/definitions/string + description: + used as prefix for sink/source names of the component. Must be a + unique string among multiple instances of the same component. + The name can be "I2SMCC0" or "I2SMCC1" ... "I2SMCCx", where x depends + on the maximum available instances on a Microchip SoC. + dmas: items: - description: TX DMA Channel diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/sound/microchip,sama7g5-pdmc.yaml linux-microchip/Documentation/devicetree/bindings/sound/microchip,sama7g5-pdmc.yaml --- linux-6.6.51/Documentation/devicetree/bindings/sound/microchip,sama7g5-pdmc.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/sound/microchip,sama7g5-pdmc.yaml 2024-11-26 14:02:35.506458014 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:72 @ enabling the PDMC microphones to avoid unwanted noise due to microphones not being ready. + sound-name-prefix: + pattern: "^PDMC[0-9]$" + $ref: /schemas/types.yaml#/definitions/string + description: + used as prefix for sink/source names of the component. Must be a + unique string among multiple instances of the same component. + The name can be "PDMC0" or "PDMC1" ... "PDMCx", where x depends + on the maximum available instances on a Microchip SoC. + required: - compatible - reg diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/sound/microchip,sama7g5-spdifrx.yaml linux-microchip/Documentation/devicetree/bindings/sound/microchip,sama7g5-spdifrx.yaml --- linux-6.6.51/Documentation/devicetree/bindings/sound/microchip,sama7g5-spdifrx.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/sound/microchip,sama7g5-spdifrx.yaml 2024-11-26 14:02:35.506458014 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:46 @ dma-names: const: rx + sound-name-prefix: + pattern: "^SPDIFRX[0-9]$" + $ref: /schemas/types.yaml#/definitions/string + description: + used as prefix for sink/source names of the component. Must be a + unique string among multiple instances of the same component. + The name can be "SPDIFRX0" or "SPDIFRX1" ... "SPDIFRXx", where x depends + on the maximum available instances on a Microchip SoC. + required: - "#sound-dai-cells" - compatible diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/sound/microchip,sama7g5-spdiftx.yaml linux-microchip/Documentation/devicetree/bindings/sound/microchip,sama7g5-spdiftx.yaml --- linux-6.6.51/Documentation/devicetree/bindings/sound/microchip,sama7g5-spdiftx.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/sound/microchip,sama7g5-spdiftx.yaml 2024-11-26 14:02:35.506458014 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:49 @ dma-names: const: tx + sound-name-prefix: + pattern: "^SPDIFTX[0-9]$" + $ref: /schemas/types.yaml#/definitions/string + description: + used as prefix for sink/source names of the component. Must be a + unique string among multiple instances of the same component. + The name can be "SPDIFTX0" or "SPDIFTX1" ... "SPDIFTXx", where x depends + on the maximum available instances on a Microchip SoC. + required: - "#sound-dai-cells" - compatible diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/spi/atmel,quadspi.yaml linux-microchip/Documentation/devicetree/bindings/spi/atmel,quadspi.yaml --- linux-6.6.51/Documentation/devicetree/bindings/spi/atmel,quadspi.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/spi/atmel,quadspi.yaml 2024-11-26 14:02:35.514458156 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:22 @ - microchip,sam9x60-qspi - microchip,sama7g5-qspi - microchip,sama7g5-ospi + - microchip,sam9x7-ospi reg: items: diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/spi/microchip,mpfs-spi.yaml linux-microchip/Documentation/devicetree/bindings/spi/microchip,mpfs-spi.yaml --- linux-6.6.51/Documentation/devicetree/bindings/spi/microchip,mpfs-spi.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/spi/microchip,mpfs-spi.yaml 2024-11-26 14:02:35.514458156 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:16 @ maintainers: - Conor Dooley <conor.dooley@microchip.com> -allOf: - - $ref: spi-controller.yaml# - properties: compatible: oneOf: - items: - - const: microchip,mpfs-qspi + - enum: + - microchip,mpfs-qspi + - microchip,pic64gx-qspi - const: microchip,coreqspi-rtl-v2 - const: microchip,coreqspi-rtl-v2 # FPGA QSPI + - items: + - const: microchip,pic64gx-spi + - const: microchip,mpfs-spi - const: microchip,mpfs-spi reg: @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:48 @ - interrupts - clocks +allOf: + - $ref: spi-controller.yaml# + + - if: + properties: + compatible: + contains: + const: microchip,mpfs-spi + then: + properties: + num-cs: + default: 1 + + - if: + properties: + compatible: + contains: + const: microchip,mpfs-spi + not: + required: + - cs-gpios + then: + properties: + num-cs: + maximum: 1 + unevaluatedProperties: false examples: diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/timer/sifive,clint.yaml linux-microchip/Documentation/devicetree/bindings/timer/sifive,clint.yaml --- linux-6.6.51/Documentation/devicetree/bindings/timer/sifive,clint.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/timer/sifive,clint.yaml 2024-11-26 14:02:35.518458229 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:33 @ - items: - enum: - canaan,k210-clint # Canaan Kendryte K210 + - microchip,pic64gx-clint # Microchip PIC64GX - sifive,fu540-c000-clint # SiFive FU540 - starfive,jh7100-clint # StarFive JH7100 - starfive,jh7110-clint # StarFive JH7110 diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/usb/generic-ehci.yaml linux-microchip/Documentation/devicetree/bindings/usb/generic-ehci.yaml --- linux-6.6.51/Documentation/devicetree/bindings/usb/generic-ehci.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/usb/generic-ehci.yaml 2024-11-26 14:02:35.518458229 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:107 @ - if a USB DRD channel: first clock should be host and second one should be peripheral + clock-names: + minItems: 1 + maxItems: 4 + power-domains: maxItems: 1 diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/bindings/usb/microchip,mpfs-musb.yaml linux-microchip/Documentation/devicetree/bindings/usb/microchip,mpfs-musb.yaml --- linux-6.6.51/Documentation/devicetree/bindings/usb/microchip,mpfs-musb.yaml 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Documentation/devicetree/bindings/usb/microchip,mpfs-musb.yaml 2024-11-26 14:02:35.518458229 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:17 @ properties: compatible: - enum: - - microchip,mpfs-musb + oneOf: + - items: + - const: microchip,pic64gx-musb + - const: microchip,mpfs-musb + - const: microchip,mpfs-musb dr_mode: true reg: maxItems: 1 + dma-noncoherent: true + interrupts: minItems: 2 maxItems: 2 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:42 @ clocks: maxItems: 1 + microchip,ext-vbus-drv: + description: + Some ULPI USB PHYs do not support an internal VBUS supply and driving + the CPEN pin requires the configuration of the ulpi UPLI_USE__EXTVBUS + bit in ULPI_BUSCONTROL. + $ref: /schemas/types.yaml#/definitions/flag + required: - compatible - reg diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Documentation/devicetree/configfs-overlays.txt linux-microchip/Documentation/devicetree/configfs-overlays.txt --- linux-6.6.51/Documentation/devicetree/configfs-overlays.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/Documentation/devicetree/configfs-overlays.txt 2024-11-26 14:04:18.556303237 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +Howto use the configfs overlay interface. + +A device-tree configfs entry is created in /config/device-tree/overlays +and and it is manipulated using standard file system I/O. +Note that this is a debug level interface, for use by developers and +not necessarily something accessed by normal users due to the +security implications of having direct access to the kernel's device tree. + +* To create an overlay you mkdir the directory: + + # mkdir /config/device-tree/overlays/foo + +* Either you echo the overlay firmware file to the path property file. + + # echo foo.dtbo >/config/device-tree/overlays/foo/path + +* Or you cat the contents of the overlay to the dtbo file + + # cat foo.dtbo >/config/device-tree/overlays/foo/dtbo + +The overlay file will be applied, and devices will be created/destroyed +as required. + +To remove it simply rmdir the directory. + + # rmdir /config/device-tree/overlays/foo + +The rationalle of the dual interface (firmware & direct copy) is that each is +better suited to different use patterns. The firmware interface is what's +intended to be used by hardware managers in the kernel, while the copy interface +make sense for developers (since it avoids problems with namespaces). diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/auxdisplay/Kconfig linux-microchip/drivers/auxdisplay/Kconfig --- linux-6.6.51/drivers/auxdisplay/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/auxdisplay/Kconfig 2024-11-26 14:04:18.560303308 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:206 @ line and the Linux version on the second line, but that's still useful. +config SENSEHAT_DISPLAY + tristate "Raspberry Pi Sense HAT display driver" + depends on I2C + select MFD_SIMPLE_MFD_I2C + help + This is a driver for the Raspberry Pi Sensehat 8x8 RBG-LED matrix + you can access it as a misc device at /dev/sense-hat + menuconfig PARPORT_PANEL tristate "Parallel port LCD/Keypad Panel support" depends on PARPORT diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/auxdisplay/Makefile linux-microchip/drivers/auxdisplay/Makefile --- linux-6.6.51/drivers/auxdisplay/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/auxdisplay/Makefile 2024-11-26 14:04:18.560303308 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:17 @ obj-$(CONFIG_PARPORT_PANEL) += panel.o obj-$(CONFIG_LCD2S) += lcd2s.o obj-$(CONFIG_LINEDISP) += line-display.o +obj-$(CONFIG_SENSEHAT_DISPLAY) += sensehat-display.o diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/auxdisplay/sensehat-display.c linux-microchip/drivers/auxdisplay/sensehat-display.c --- linux-6.6.51/drivers/auxdisplay/sensehat-display.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/auxdisplay/sensehat-display.c 2024-11-26 14:04:18.560303308 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Raspberry Pi Sense HAT 8x8 LED matrix display driver + * http://raspberrypi.org + * + * Copyright (C) 2015 Raspberry Pi + * Copyright (C) 2021 Charles Mirabile, Joel Savitz + * + * Original Author: Serge Schneider + * Revised for upstream Linux by: Charles Mirabile, Joel Savitz + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/mod_devicetable.h> +#include <linux/miscdevice.h> +#include <linux/regmap.h> +#include <linux/property.h> + +#define DISPLAY_SMB_REG 0x00 +#define RGB_555_MASK 0x1f +#define NUM_LEDS 8 +#define NUM_CHANNELS 3 + +struct sensehat_display { + struct platform_device *pdev; + struct miscdevice mdev; + struct mutex rw_mtx; + u8 vmem[NUM_LEDS][NUM_LEDS][NUM_CHANNELS]; + struct regmap *regmap; +}; + +#define VMEM_SIZE sizeof_field(struct sensehat_display, vmem) + + +static int sensehat_update_display(struct sensehat_display *display) +{ + int i, j, k, ret; + u8 buff[NUM_LEDS][NUM_CHANNELS][NUM_LEDS]; + + for (i = 0; i < NUM_LEDS; ++i) + for (j = 0; j < NUM_LEDS; ++j) + for (k = 0; k < NUM_CHANNELS; ++k) + buff[i][k][j] = + display->vmem[i][j][k] & RGB_555_MASK; + + ret = regmap_bulk_write(display->regmap, DISPLAY_SMB_REG, buff, + VMEM_SIZE); + if (ret < 0) + dev_err(&display->pdev->dev, + "Update to 8x8 LED matrix display failed"); + return ret; +} + +static loff_t sensehat_display_llseek(struct file *filp, loff_t offset, + int whence) +{ + return fixed_size_llseek(filp, offset, whence, VMEM_SIZE); +} + +static ssize_t sensehat_display_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) +{ + struct sensehat_display *sensehat_display = + container_of(filp->private_data, struct sensehat_display, mdev); + ssize_t ret = -EFAULT; + + if (*f_pos < 0 || *f_pos >= VMEM_SIZE) + return 0; + count = min_t(size_t, count, VMEM_SIZE - *f_pos); + + if (mutex_lock_interruptible(&sensehat_display->rw_mtx)) + return -ERESTARTSYS; + if (copy_to_user(buf, *f_pos + (u8 *)sensehat_display->vmem, count)) + goto out; + *f_pos += count; + ret = count; +out: + mutex_unlock(&sensehat_display->rw_mtx); + return ret; +} + +static ssize_t sensehat_display_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + struct sensehat_display *sensehat_display = + container_of(filp->private_data, struct sensehat_display, mdev); + int ret = -EFAULT; + + if (*f_pos < 0 || *f_pos >= VMEM_SIZE) + return -EFBIG; + count = min_t(size_t, count, VMEM_SIZE - *f_pos); + + if (mutex_lock_interruptible(&sensehat_display->rw_mtx)) + return -ERESTARTSYS; + if (copy_from_user(*f_pos + (u8 *)sensehat_display->vmem, buf, count)) + goto out; + ret = sensehat_update_display(sensehat_display); + if (ret < 0) { + ret = -EIO; + goto out; + } + *f_pos += count; + ret = count; +out: + mutex_unlock(&sensehat_display->rw_mtx); + return ret; +} + +static const struct file_operations sensehat_display_fops = { + .owner = THIS_MODULE, + .llseek = sensehat_display_llseek, + .read = sensehat_display_read, + .write = sensehat_display_write, +}; + +static int sensehat_display_probe(struct platform_device *pdev) +{ + int ret; + + struct sensehat_display *sensehat_display = + devm_kmalloc(&pdev->dev, sizeof(*sensehat_display), GFP_KERNEL); + if (!sensehat_display) + return -ENOMEM; + + sensehat_display->pdev = pdev; + + dev_set_drvdata(&pdev->dev, sensehat_display); + + sensehat_display->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!sensehat_display->regmap) { + dev_err(&pdev->dev, + "unable to get sensehat regmap"); + return -ENODEV; + } + + memset(sensehat_display->vmem, 0, VMEM_SIZE); + + mutex_init(&sensehat_display->rw_mtx); + + ret = sensehat_update_display(sensehat_display); + if (ret < 0) { + dev_err(&pdev->dev, + "Could not communicate with sensehat"); + return ret; + } + + sensehat_display->mdev = (struct miscdevice){ + .minor = MISC_DYNAMIC_MINOR, + .name = "sense-hat", + .mode = 0666, + .fops = &sensehat_display_fops, + }; + + ret = misc_register(&sensehat_display->mdev); + if (ret < 0) { + dev_err(&pdev->dev, + "Could not register 8x8 LED matrix display."); + return ret; + } + + dev_info(&pdev->dev, + "8x8 LED matrix display registered with minor number %i", + sensehat_display->mdev.minor); + + return 0; +} + +static int sensehat_display_remove(struct platform_device *pdev) +{ + struct sensehat_display *sensehat_display = dev_get_drvdata(&pdev->dev); + + misc_deregister(&sensehat_display->mdev); + return 0; +} + +static const struct of_device_id sensehat_display_device_id[] = { + { .compatible = "raspberrypi,sensehat-display" }, + {}, +}; +MODULE_DEVICE_TABLE(of, sensehat_display_device_id); + +static struct platform_driver sensehat_display_driver = { + .probe = sensehat_display_probe, + .remove = sensehat_display_remove, + .driver = { + .name = "sensehat-display", + .of_match_table = sensehat_display_device_id, + }, +}; + +module_platform_driver(sensehat_display_driver); + +MODULE_DESCRIPTION("Raspberry Pi Sense HAT 8x8 LED matrix display driver"); +MODULE_AUTHOR("Charles Mirabile <cmirabil@redhat.com>"); +MODULE_AUTHOR("Serge Schneider <serge@raspberrypi.org>"); +MODULE_LICENSE("GPL"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/base/firmware_loader/sysfs_upload.c linux-microchip/drivers/base/firmware_loader/sysfs_upload.c --- linux-6.6.51/drivers/base/firmware_loader/sysfs_upload.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/base/firmware_loader/sysfs_upload.c 2024-11-26 14:02:36.314472487 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:30 @ [FW_UPLOAD_ERR_INVALID_SIZE] = "invalid-file-size", [FW_UPLOAD_ERR_RW_ERROR] = "read-write-error", [FW_UPLOAD_ERR_WEAROUT] = "flash-wearout", + [FW_UPLOAD_ERR_FW_INVALID] = "firmware-invalid", }; static const char *fw_upload_progress(struct device *dev, diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/base/regmap/Kconfig linux-microchip/drivers/base/regmap/Kconfig --- linux-6.6.51/drivers/base/regmap/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/base/regmap/Kconfig 2024-11-26 14:02:36.314472487 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:8 @ config REGMAP bool - default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_W1 || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ || REGMAP_SOUNDWIRE || REGMAP_SOUNDWIRE_MBQ || REGMAP_SCCB || REGMAP_I3C || REGMAP_SPI_AVMM || REGMAP_MDIO || REGMAP_FSI) + default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_W1 || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ || REGMAP_SOUNDWIRE || REGMAP_SOUNDWIRE_MBQ || REGMAP_SCCB || REGMAP_I3C || REGMAP_SPI_AVMM || REGMAP_MDIO || REGMAP_FSI || REGMAP_SMCCC) select IRQ_DOMAIN if REGMAP_IRQ select MDIO_BUS if REGMAP_MDIO help @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:94 @ config REGMAP_FSI tristate depends on FSI + +config REGMAP_SMCCC + default y if HAVE_ARM_SMCCC_DISCOVERY + tristate + depends on HAVE_ARM_SMCCC_DISCOVERY diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/base/regmap/Makefile linux-microchip/drivers/base/regmap/Makefile --- linux-6.6.51/drivers/base/regmap/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/base/regmap/Makefile 2024-11-26 14:02:36.314472487 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:25 @ obj-$(CONFIG_REGMAP_SPI_AVMM) += regmap-spi-avmm.o obj-$(CONFIG_REGMAP_MDIO) += regmap-mdio.o obj-$(CONFIG_REGMAP_FSI) += regmap-fsi.o +obj-$(CONFIG_REGMAP_SMCCC) += regmap-smccc.o diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/base/regmap/regmap-smccc.c linux-microchip/drivers/base/regmap/regmap-smccc.c --- linux-6.6.51/drivers/base/regmap/regmap-smccc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/base/regmap/regmap-smccc.c 2024-11-26 14:02:36.314472487 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2021 Bootlin + +#include <linux/arm-smccc.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/types.h> + +#define REGMAP_SMC_READ 0 +#define REGMAP_SMC_WRITE 1 + +struct regmap_smccc_ctx { + u32 regmap_smc_id; +}; + +static int regmap_smccc_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct regmap_smccc_ctx *ctx = context; + struct arm_smccc_res res; + + arm_smccc_1_1_invoke(ctx->regmap_smc_id, REGMAP_SMC_WRITE, reg, val, + &res); + + if (res.a0) + return -EACCES; + + return 0; +} + +static int regmap_smccc_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct regmap_smccc_ctx *ctx = context; + struct arm_smccc_res res; + + arm_smccc_1_1_invoke(ctx->regmap_smc_id, REGMAP_SMC_READ, reg, &res); + + if (res.a0) + return -EACCES; + + *val = res.a1; + + return 0; +} + +static struct regmap_bus regmap_smccc = { + .reg_write = regmap_smccc_reg_write, + .reg_read = regmap_smccc_reg_read, +}; + +static int regmap_smccc_bits_is_supported(int val_bits) +{ + switch (val_bits) { + case 8: + case 16: + case 32: + return 0; + case 64: + /* + * SMCs are using registers to pass information so if architecture is + * not using 64 bits registers, we won't be able to pass information + * transparently. + */ +#if !defined(CONFIG_64BIT) + return -EINVAL; +#else + return 0; +#endif + default: + return -EINVAL; + } +} + +static struct regmap_smccc_ctx *smccc_regmap_init_ctx( + const struct regmap_config *config, + u32 regmap_smc_id) +{ + int ret; + struct regmap_smccc_ctx *ctx; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return ERR_PTR(-ENOMEM); + + ret = regmap_smccc_bits_is_supported(config->val_bits); + if (ret) + return ERR_PTR(ret); + + ctx->regmap_smc_id = regmap_smc_id; + + return ctx; +} + +struct regmap *__regmap_init_smccc(struct device *dev, u32 regmap_smc_id, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name) +{ + struct regmap_smccc_ctx *ctx; + + ctx = smccc_regmap_init_ctx(config, regmap_smc_id); + if (IS_ERR(ctx)) + return ERR_CAST(ctx); + + return __regmap_init(dev, ®map_smccc, ctx, config, lock_key, + lock_name); +} +EXPORT_SYMBOL_GPL(__regmap_init_smccc); + +struct regmap *__devm_regmap_init_smccc(struct device *dev, u32 regmap_smc_id, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name) +{ + struct regmap_smccc_ctx *ctx; + + ctx = smccc_regmap_init_ctx(config, regmap_smc_id); + if (IS_ERR(ctx)) + return ERR_CAST(ctx); + + return __devm_regmap_init(dev, ®map_smccc, ctx, config, lock_key, + lock_name); +} +EXPORT_SYMBOL_GPL(__devm_regmap_init_smccc); + +MODULE_AUTHOR("Clément Léger <clement.leger@bootlin.com>"); +MODULE_DESCRIPTION("Regmap SMCCC Module"); +MODULE_LICENSE("GPL v2"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/cache/Kconfig linux-microchip/drivers/cache/Kconfig --- linux-6.6.51/drivers/cache/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/cache/Kconfig 2024-11-26 14:02:36.330472774 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:11 @ help Support for the L2 cache controller on Andes Technology AX45MP platforms. +config SIFIVE_CCACHE + bool "Sifive Composable Cache controller" + depends on ARCH_MICROCHIP_POLARFIRE || ARCH_SIFIVE || ARCH_STARFIVE + help + Support for the composable cache controller on SiFive platforms. + endmenu diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/cache/Makefile linux-microchip/drivers/cache/Makefile --- linux-6.6.51/drivers/cache/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/cache/Makefile 2024-11-26 14:02:36.330472774 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1 @ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_AX45MP_L2_CACHE) += ax45mp_cache.o +obj-$(CONFIG_AX45MP_L2_CACHE) += ax45mp_cache.o +obj-$(CONFIG_SIFIVE_CCACHE) += sifive_ccache.o diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/cache/sifive_ccache.c linux-microchip/drivers/cache/sifive_ccache.c --- linux-6.6.51/drivers/cache/sifive_ccache.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/cache/sifive_ccache.c 2024-11-26 14:02:36.330472774 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * SiFive composable cache controller Driver + * + * Copyright (C) 2018-2022 SiFive, Inc. + * + */ + +#define pr_fmt(fmt) "CCACHE: " fmt + +#include <linux/align.h> +#include <linux/debugfs.h> +#include <linux/interrupt.h> +#include <linux/of_irq.h> +#include <linux/of_address.h> +#include <linux/device.h> +#include <linux/bitfield.h> +#include <asm/cacheflush.h> +#include <asm/cacheinfo.h> +#include <asm/dma-noncoherent.h> +#include <soc/sifive/sifive_ccache.h> + +#define SIFIVE_CCACHE_DIRECCFIX_LOW 0x100 +#define SIFIVE_CCACHE_DIRECCFIX_HIGH 0x104 +#define SIFIVE_CCACHE_DIRECCFIX_COUNT 0x108 + +#define SIFIVE_CCACHE_DIRECCFAIL_LOW 0x120 +#define SIFIVE_CCACHE_DIRECCFAIL_HIGH 0x124 +#define SIFIVE_CCACHE_DIRECCFAIL_COUNT 0x128 + +#define SIFIVE_CCACHE_DATECCFIX_LOW 0x140 +#define SIFIVE_CCACHE_DATECCFIX_HIGH 0x144 +#define SIFIVE_CCACHE_DATECCFIX_COUNT 0x148 + +#define SIFIVE_CCACHE_DATECCFAIL_LOW 0x160 +#define SIFIVE_CCACHE_DATECCFAIL_HIGH 0x164 +#define SIFIVE_CCACHE_DATECCFAIL_COUNT 0x168 + +#define SIFIVE_CCACHE_CONFIG 0x00 +#define SIFIVE_CCACHE_CONFIG_BANK_MASK GENMASK_ULL(7, 0) +#define SIFIVE_CCACHE_CONFIG_WAYS_MASK GENMASK_ULL(15, 8) +#define SIFIVE_CCACHE_CONFIG_SETS_MASK GENMASK_ULL(23, 16) +#define SIFIVE_CCACHE_CONFIG_BLKS_MASK GENMASK_ULL(31, 24) + +#define SIFIVE_CCACHE_FLUSH64 0x200 +#define SIFIVE_CCACHE_FLUSH32 0x240 + +#define SIFIVE_CCACHE_WAYENABLE 0x08 +#define SIFIVE_CCACHE_ECCINJECTERR 0x40 + +#define SIFIVE_CCACHE_MAX_ECCINTR 4 +#define SIFIVE_CCACHE_LINE_SIZE 64 + +static void __iomem *ccache_base; +static int g_irq[SIFIVE_CCACHE_MAX_ECCINTR]; +static struct riscv_cacheinfo_ops ccache_cache_ops; +static int level; + +enum { + DIR_CORR = 0, + DATA_CORR, + DATA_UNCORR, + DIR_UNCORR, +}; + +enum { + QUIRK_NONSTANDARD_CACHE_OPS = BIT(0), + QUIRK_BROKEN_DATA_UNCORR = BIT(1), +}; + +#ifdef CONFIG_DEBUG_FS +static struct dentry *sifive_test; + +static ssize_t ccache_write(struct file *file, const char __user *data, + size_t count, loff_t *ppos) +{ + unsigned int val; + + if (kstrtouint_from_user(data, count, 0, &val)) + return -EINVAL; + if ((val < 0xFF) || (val >= 0x10000 && val < 0x100FF)) + writel(val, ccache_base + SIFIVE_CCACHE_ECCINJECTERR); + else + return -EINVAL; + return count; +} + +static const struct file_operations ccache_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .write = ccache_write +}; + +static void setup_sifive_debug(void) +{ + sifive_test = debugfs_create_dir("sifive_ccache_cache", NULL); + + debugfs_create_file("sifive_debug_inject_error", 0200, + sifive_test, NULL, &ccache_fops); +} +#endif + +static void ccache_config_read(void) +{ + u32 cfg; + + cfg = readl(ccache_base + SIFIVE_CCACHE_CONFIG); + pr_info("%llu banks, %llu ways, sets/bank=%llu, bytes/block=%llu\n", + FIELD_GET(SIFIVE_CCACHE_CONFIG_BANK_MASK, cfg), + FIELD_GET(SIFIVE_CCACHE_CONFIG_WAYS_MASK, cfg), + BIT_ULL(FIELD_GET(SIFIVE_CCACHE_CONFIG_SETS_MASK, cfg)), + BIT_ULL(FIELD_GET(SIFIVE_CCACHE_CONFIG_BLKS_MASK, cfg))); + + cfg = readl(ccache_base + SIFIVE_CCACHE_WAYENABLE); + pr_info("Index of the largest way enabled: %u\n", cfg); +} + +static const struct of_device_id sifive_ccache_ids[] = { + { .compatible = "sifive,fu540-c000-ccache" }, + { .compatible = "sifive,fu740-c000-ccache" }, + { .compatible = "starfive,jh7100-ccache", + .data = (void *)(QUIRK_NONSTANDARD_CACHE_OPS | QUIRK_BROKEN_DATA_UNCORR) }, + { .compatible = "microchip,mpfs-ccache", + .data = (void *)(QUIRK_NONSTANDARD_CACHE_OPS) }, + { .compatible = "sifive,ccache0" }, + { /* end of table */ } +}; + +static ATOMIC_NOTIFIER_HEAD(ccache_err_chain); + +int register_sifive_ccache_error_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&ccache_err_chain, nb); +} +EXPORT_SYMBOL_GPL(register_sifive_ccache_error_notifier); + +int unregister_sifive_ccache_error_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&ccache_err_chain, nb); +} +EXPORT_SYMBOL_GPL(unregister_sifive_ccache_error_notifier); + +#ifdef CONFIG_RISCV_NONSTANDARD_CACHE_OPS +static void ccache_flush_range(phys_addr_t start, size_t len) +{ + phys_addr_t end = start + len; + phys_addr_t line; + + if (!len) + return; + + mb(); + for (line = ALIGN_DOWN(start, SIFIVE_CCACHE_LINE_SIZE); line < end; + line += SIFIVE_CCACHE_LINE_SIZE) { +#ifdef CONFIG_32BIT + writel(line >> 4, ccache_base + SIFIVE_CCACHE_FLUSH32); +#else + writeq(line, ccache_base + SIFIVE_CCACHE_FLUSH64); +#endif + mb(); + } +} + +static const struct riscv_nonstd_cache_ops ccache_mgmt_ops __initconst = { + .wback = &ccache_flush_range, + .inv = &ccache_flush_range, + .wback_inv = &ccache_flush_range, +}; +#endif /* CONFIG_RISCV_NONSTANDARD_CACHE_OPS */ + +static int ccache_largest_wayenabled(void) +{ + return readl(ccache_base + SIFIVE_CCACHE_WAYENABLE) & 0xFF; +} + +static ssize_t number_of_ways_enabled_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%u\n", ccache_largest_wayenabled()); +} + +static DEVICE_ATTR_RO(number_of_ways_enabled); + +static struct attribute *priv_attrs[] = { + &dev_attr_number_of_ways_enabled.attr, + NULL, +}; + +static const struct attribute_group priv_attr_group = { + .attrs = priv_attrs, +}; + +static const struct attribute_group *ccache_get_priv_group(struct cacheinfo + *this_leaf) +{ + /* We want to use private group for composable cache only */ + if (this_leaf->level == level) + return &priv_attr_group; + else + return NULL; +} + +static irqreturn_t ccache_int_handler(int irq, void *device) +{ + unsigned int add_h, add_l; + + if (irq == g_irq[DIR_CORR]) { + add_h = readl(ccache_base + SIFIVE_CCACHE_DIRECCFIX_HIGH); + add_l = readl(ccache_base + SIFIVE_CCACHE_DIRECCFIX_LOW); + pr_err("DirError @ 0x%08X.%08X\n", add_h, add_l); + /* Reading this register clears the DirError interrupt sig */ + readl(ccache_base + SIFIVE_CCACHE_DIRECCFIX_COUNT); + atomic_notifier_call_chain(&ccache_err_chain, + SIFIVE_CCACHE_ERR_TYPE_CE, + "DirECCFix"); + } + if (irq == g_irq[DIR_UNCORR]) { + add_h = readl(ccache_base + SIFIVE_CCACHE_DIRECCFAIL_HIGH); + add_l = readl(ccache_base + SIFIVE_CCACHE_DIRECCFAIL_LOW); + /* Reading this register clears the DirFail interrupt sig */ + readl(ccache_base + SIFIVE_CCACHE_DIRECCFAIL_COUNT); + atomic_notifier_call_chain(&ccache_err_chain, + SIFIVE_CCACHE_ERR_TYPE_UE, + "DirECCFail"); + panic("CCACHE: DirFail @ 0x%08X.%08X\n", add_h, add_l); + } + if (irq == g_irq[DATA_CORR]) { + add_h = readl(ccache_base + SIFIVE_CCACHE_DATECCFIX_HIGH); + add_l = readl(ccache_base + SIFIVE_CCACHE_DATECCFIX_LOW); + pr_err("DataError @ 0x%08X.%08X\n", add_h, add_l); + /* Reading this register clears the DataError interrupt sig */ + readl(ccache_base + SIFIVE_CCACHE_DATECCFIX_COUNT); + atomic_notifier_call_chain(&ccache_err_chain, + SIFIVE_CCACHE_ERR_TYPE_CE, + "DatECCFix"); + } + if (irq == g_irq[DATA_UNCORR]) { + add_h = readl(ccache_base + SIFIVE_CCACHE_DATECCFAIL_HIGH); + add_l = readl(ccache_base + SIFIVE_CCACHE_DATECCFAIL_LOW); + pr_err("DataFail @ 0x%08X.%08X\n", add_h, add_l); + /* Reading this register clears the DataFail interrupt sig */ + readl(ccache_base + SIFIVE_CCACHE_DATECCFAIL_COUNT); + atomic_notifier_call_chain(&ccache_err_chain, + SIFIVE_CCACHE_ERR_TYPE_UE, + "DatECCFail"); + } + + return IRQ_HANDLED; +} + +static int __init sifive_ccache_init(void) +{ + struct device_node *np; + struct resource res; + int i, rc, intr_num; + const struct of_device_id *match; + unsigned long quirks; + + np = of_find_matching_node_and_match(NULL, sifive_ccache_ids, &match); + if (!np) + return -ENODEV; + + quirks = (uintptr_t)match->data; + + if (of_address_to_resource(np, 0, &res)) { + rc = -ENODEV; + goto err_node_put; + } + + ccache_base = ioremap(res.start, resource_size(&res)); + if (!ccache_base) { + rc = -ENOMEM; + goto err_node_put; + } + + if (of_property_read_u32(np, "cache-level", &level)) { + rc = -ENOENT; + goto err_unmap; + } + + intr_num = of_property_count_u32_elems(np, "interrupts"); + if (!intr_num) { + pr_err("No interrupts property\n"); + rc = -ENODEV; + goto err_unmap; + } + + for (i = 0; i < intr_num; i++) { + g_irq[i] = irq_of_parse_and_map(np, i); + + if (i == DATA_UNCORR && (quirks & QUIRK_BROKEN_DATA_UNCORR)) + continue; + + rc = request_irq(g_irq[i], ccache_int_handler, 0, "ccache_ecc", + NULL); + if (rc) { + pr_err("Could not request IRQ %d\n", g_irq[i]); + goto err_free_irq; + } + } + of_node_put(np); + +#ifdef CONFIG_RISCV_NONSTANDARD_CACHE_OPS + if (quirks & QUIRK_NONSTANDARD_CACHE_OPS) { + riscv_cbom_block_size = SIFIVE_CCACHE_LINE_SIZE; + riscv_noncoherent_supported(); + riscv_noncoherent_register_cache_ops(&ccache_mgmt_ops); + } +#endif + + ccache_config_read(); + + ccache_cache_ops.get_priv_group = ccache_get_priv_group; + riscv_set_cacheinfo_ops(&ccache_cache_ops); + +#ifdef CONFIG_DEBUG_FS + setup_sifive_debug(); +#endif + return 0; + +err_free_irq: + while (--i >= 0) + free_irq(g_irq[i], NULL); +err_unmap: + iounmap(ccache_base); +err_node_put: + of_node_put(np); + return rc; +} + +arch_initcall(sifive_ccache_init); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/at91/clk-master.c linux-microchip/drivers/clk/at91/clk-master.c --- linux-6.6.51/drivers/clk/at91/clk-master.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/at91/clk-master.c 2024-11-26 14:02:36.346473061 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:583 @ { struct clk_master *master = to_clk_master(hw); + if (master->div == MASTER_PRES_MAX) + return DIV_ROUND_CLOSEST_ULL(parent_rate, 3); + return DIV_ROUND_CLOSEST_ULL(parent_rate, (1 << master->div)); } diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/at91/clk-sam9x60-pll.c linux-microchip/drivers/clk/at91/clk-sam9x60-pll.c --- linux-6.6.51/drivers/clk/at91/clk-sam9x60-pll.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/at91/clk-sam9x60-pll.c 2024-11-26 14:04:18.560303308 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:26 @ #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_core { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:76 @ { struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw); struct sam9x60_frac *frac = to_sam9x60_frac(core); + unsigned long freq; - return parent_rate * (frac->mul + 1) + + freq = parent_rate * (frac->mul + 1) + DIV_ROUND_CLOSEST_ULL((u64)parent_rate * frac->frac, (1 << 22)); + + if (core->layout->div2) + freq >>= 1; + + return freq; } static int sam9x60_frac_pll_set(struct sam9x60_pll_core *core) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:200 @ unsigned long nmul = 0; unsigned long nfrac = 0; - if (rate < FCORE_MIN || rate > FCORE_MAX) + if (rate < core->characteristics->core_output[0].min || + rate > core->characteristics->core_output[0].max) return -ERANGE; /* @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:221 @ } /* Check if resulted rate is a valid. */ - if (tmprate < FCORE_MIN || tmprate > FCORE_MAX) + if (tmprate < core->characteristics->core_output[0].min || + tmprate > core->characteristics->core_output[0].max) return -ERANGE; if (update) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:441 @ return DIV_ROUND_CLOSEST_ULL(parent_rate, (div->div + 1)); } +static unsigned long sam9x60_fixed_div_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return parent_rate >> 1; +} + static long sam9x60_div_pll_compute_div(struct sam9x60_pll_core *core, unsigned long *parent_rate, unsigned long rate) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:621 @ .restore_context = sam9x60_div_pll_restore_context, }; +static const struct clk_ops sam9x60_fixed_div_pll_ops = { + .prepare = sam9x60_div_pll_prepare, + .unprepare = sam9x60_div_pll_unprepare, + .is_prepared = sam9x60_div_pll_is_prepared, + .recalc_rate = sam9x60_fixed_div_pll_recalc_rate, + .round_rate = sam9x60_div_pll_round_rate, + .save_context = sam9x60_div_pll_save_context, + .restore_context = sam9x60_div_pll_restore_context, +}; + struct clk_hw * __init sam9x60_clk_register_frac_pll(struct regmap *regmap, spinlock_t *lock, const char *name, const char *parent_name, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:693 @ goto free; } - ret = sam9x60_frac_pll_compute_mul_frac(&frac->core, FCORE_MIN, + ret = sam9x60_frac_pll_compute_mul_frac(&frac->core, + characteristics->core_output[0].min, parent_rate, true); if (ret < 0) { hw = ERR_PTR(ret); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:750 @ else init.parent_names = &parent_name; init.num_parents = 1; - if (flags & CLK_SET_RATE_GATE) - init.ops = &sam9x60_div_pll_ops; - else - init.ops = &sam9x60_div_pll_ops_chg; + + if (layout->div2) { + init.ops = &sam9x60_fixed_div_pll_ops; + } else { + if (flags & CLK_SET_RATE_GATE) + init.ops = &sam9x60_div_pll_ops; + else + init.ops = &sam9x60_div_pll_ops_chg; + } + init.flags = flags; div->core.id = id; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/at91/Makefile linux-microchip/drivers/clk/at91/Makefile --- linux-6.6.51/drivers/clk/at91/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/at91/Makefile 2024-11-26 14:02:36.342472989 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:23 @ obj-$(CONFIG_SOC_AT91SAM9) += at91sam9g45.o dt-compat.o obj-$(CONFIG_SOC_AT91SAM9) += at91sam9n12.o at91sam9x5.o dt-compat.o obj-$(CONFIG_SOC_SAM9X60) += sam9x60.o +obj-$(CONFIG_SOC_SAM9X7) += sam9x7.o obj-$(CONFIG_SOC_SAMA5D3) += sama5d3.o dt-compat.o obj-$(CONFIG_SOC_SAMA5D4) += sama5d4.o dt-compat.o obj-$(CONFIG_SOC_SAMA5D2) += sama5d2.o dt-compat.o diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/at91/pmc.h linux-microchip/drivers/clk/at91/pmc.h --- linux-6.6.51/drivers/clk/at91/pmc.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/at91/pmc.h 2024-11-26 14:04:18.560303308 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:67 @ u8 frac_shift; u8 div_shift; u8 endiv_shift; + u8 div2; }; extern const struct clk_pll_layout at91rm9200_pll_layout; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:79 @ struct clk_range input; int num_output; const struct clk_range *output; + const struct clk_range *core_output; u16 *icpll; u8 *out; u8 upll : 1; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/at91/sam9x60.c linux-microchip/drivers/clk/at91/sam9x60.c --- linux-6.6.51/drivers/clk/at91/sam9x60.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/at91/sam9x60.c 2024-11-26 14:04:18.560303308 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:29 @ { .min = 2343750, .max = 1200000000 }, }; +/* Fractional PLL core output range. */ +static const struct clk_range core_outputs[] = { + { .min = 600000000, .max = 1200000000 }, +}; + static const struct clk_pll_characteristics plla_characteristics = { .input = { .min = 12000000, .max = 48000000 }, .num_output = ARRAY_SIZE(plla_outputs), .output = plla_outputs, + .core_output = core_outputs, }; static const struct clk_range upll_outputs[] = { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:49 @ .input = { .min = 12000000, .max = 48000000 }, .num_output = ARRAY_SIZE(upll_outputs), .output = upll_outputs, + .core_output = core_outputs, .upll = true, }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/at91/sam9x7.c linux-microchip/drivers/clk/at91/sam9x7.c --- linux-6.6.51/drivers/clk/at91/sam9x7.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/clk/at91/sam9x7.c 2024-11-26 14:04:18.560303308 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * SAM9X7 PMC code. + * + * Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries + * + * Author: Varshini Rajendran <varshini.rajendran@microchip.com> + * + */ +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/mfd/syscon.h> +#include <linux/slab.h> + +#include <dt-bindings/clock/at91.h> + +#include "pmc.h" + +#define SAM9X7_INIT_TABLE(_table, _count) \ + do { \ + u8 _i; \ + for (_i = 0; _i < (_count); _i++) \ + (_table)[_i] = _i; \ + } while (0) + +#define SAM9X7_FILL_TABLE(_to, _from, _count) \ + do { \ + u8 _i; \ + for (_i = 0; _i < (_count); _i++) { \ + (_to)[_i] = (_from)[_i]; \ + } \ + } while (0) + +static DEFINE_SPINLOCK(pmc_pll_lock); +static DEFINE_SPINLOCK(mck_lock); + +/** + * enum pll_ids - PLL clocks identifiers + * @PLL_ID_PLLA: PLLA identifier + * @PLL_ID_UPLL: UPLL identifier + * @PLL_ID_AUDIO: Audio PLL identifier + * @PLL_ID_LVDS: LVDS PLL identifier + * @PLL_ID_PLLA_DIV2: PLLA DIV2 identifier + * @PLL_ID_MAX: Max PLL Identifier + */ +enum pll_ids { + PLL_ID_PLLA, + PLL_ID_UPLL, + PLL_ID_AUDIO, + PLL_ID_LVDS, + PLL_ID_PLLA_DIV2, + PLL_ID_MAX, +}; + +/** + * enum pll_type - PLL type identifiers + * @PLL_TYPE_FRAC: fractional PLL identifier + * @PLL_TYPE_DIV: divider PLL identifier + */ +enum pll_type { + PLL_TYPE_FRAC, + PLL_TYPE_DIV, +}; + +static const struct clk_master_characteristics mck_characteristics = { + .output = { .min = 32000000, .max = 266666667 }, + .divisors = { 1, 2, 4, 3, 5}, + .have_div3_pres = 1, +}; + +static const struct clk_master_layout sam9x7_master_layout = { + .mask = 0x373, + .pres_shift = 4, + .offset = 0x28, +}; + +/* Fractional PLL core output range. */ +static const struct clk_range plla_core_outputs[] = { + { .min = 375000000, .max = 1600000000 }, +}; + +static const struct clk_range upll_core_outputs[] = { + { .min = 600000000, .max = 1200000000 }, +}; + +static const struct clk_range lvdspll_core_outputs[] = { + { .min = 400000000, .max = 800000000 }, +}; + +static const struct clk_range audiopll_core_outputs[] = { + { .min = 400000000, .max = 800000000 }, +}; + +static const struct clk_range plladiv2_core_outputs[] = { + { .min = 375000000, .max = 1600000000 }, +}; + +/* Fractional PLL output range. */ +static const struct clk_range plla_outputs[] = { + { .min = 732421, .max = 800000000 }, +}; + +static const struct clk_range upll_outputs[] = { + { .min = 300000000, .max = 600000000 }, +}; + +static const struct clk_range lvdspll_outputs[] = { + { .min = 10000000, .max = 800000000 }, +}; + +static const struct clk_range audiopll_outputs[] = { + { .min = 10000000, .max = 800000000 }, +}; + +static const struct clk_range plladiv2_outputs[] = { + { .min = 366210, .max = 400000000 }, +}; + +/* PLL characteristics. */ +static const struct clk_pll_characteristics plla_characteristics = { + .input = { .min = 20000000, .max = 50000000 }, + .num_output = ARRAY_SIZE(plla_outputs), + .output = plla_outputs, + .core_output = plla_core_outputs, +}; + +static const struct clk_pll_characteristics upll_characteristics = { + .input = { .min = 20000000, .max = 50000000 }, + .num_output = ARRAY_SIZE(upll_outputs), + .output = upll_outputs, + .core_output = upll_core_outputs, + .upll = true, +}; + +static const struct clk_pll_characteristics lvdspll_characteristics = { + .input = { .min = 20000000, .max = 50000000 }, + .num_output = ARRAY_SIZE(lvdspll_outputs), + .output = lvdspll_outputs, + .core_output = lvdspll_core_outputs, +}; + +static const struct clk_pll_characteristics audiopll_characteristics = { + .input = { .min = 20000000, .max = 50000000 }, + .num_output = ARRAY_SIZE(audiopll_outputs), + .output = audiopll_outputs, + .core_output = audiopll_core_outputs, +}; + +static const struct clk_pll_characteristics plladiv2_characteristics = { + .input = { .min = 20000000, .max = 50000000 }, + .num_output = ARRAY_SIZE(plladiv2_outputs), + .output = plladiv2_outputs, + .core_output = plladiv2_core_outputs, +}; + +/* Layout for fractional PLL ID PLLA. */ +static const struct clk_pll_layout plla_frac_layout = { + .mul_mask = GENMASK(31, 24), + .frac_mask = GENMASK(21, 0), + .mul_shift = 24, + .frac_shift = 0, + .div2 = 1, +}; + +/* Layout for fractional PLLs. */ +static const struct clk_pll_layout pll_frac_layout = { + .mul_mask = GENMASK(31, 24), + .frac_mask = GENMASK(21, 0), + .mul_shift = 24, + .frac_shift = 0, +}; + +/* Layout for DIV PLLs. */ +static const struct clk_pll_layout pll_divpmc_layout = { + .div_mask = GENMASK(7, 0), + .endiv_mask = BIT(29), + .div_shift = 0, + .endiv_shift = 29, +}; + +/* Layout for DIV PLL ID PLLADIV2. */ +static const struct clk_pll_layout plladiv2_divpmc_layout = { + .div_mask = GENMASK(7, 0), + .endiv_mask = BIT(29), + .div_shift = 0, + .endiv_shift = 29, + .div2 = 1, +}; + +/* Layout for DIVIO dividers. */ +static const struct clk_pll_layout pll_divio_layout = { + .div_mask = GENMASK(19, 12), + .endiv_mask = BIT(30), + .div_shift = 12, + .endiv_shift = 30, +}; + +/* + * PLL clocks description + * @n: clock name + * @p: clock parent + * @l: clock layout + * @t: clock type + * @c: pll characteristics + * @f: true if clock is critical and cannot be disabled + * @eid: export index in sam9x7->chws[] array + */ +static const struct { + const char *n; + const char *p; + const struct clk_pll_layout *l; + u8 t; + const struct clk_pll_characteristics *c; + unsigned long f; + u8 eid; +} sam9x7_plls[][PLL_ID_MAX] = { + [PLL_ID_PLLA] = { + { + .n = "plla_fracck", + .p = "mainck", + .l = &plla_frac_layout, + .t = PLL_TYPE_FRAC, + /* + * This feeds plla_divpmcck which feeds CPU. It should + * not be disabled. + */ + .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE, + .c = &plla_characteristics, + }, + + { + .n = "plla_divpmcck", + .p = "plla_fracck", + .l = &pll_divpmc_layout, + .t = PLL_TYPE_DIV, + /* This feeds CPU. It should not be disabled */ + .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE, + .eid = PMC_PLLACK, + .c = &plla_characteristics, + }, + }, + + [PLL_ID_UPLL] = { + { + .n = "upll_fracck", + .p = "main_osc", + .l = &pll_frac_layout, + .t = PLL_TYPE_FRAC, + .f = CLK_SET_RATE_GATE, + .c = &upll_characteristics, + }, + + { + .n = "upll_divpmcck", + .p = "upll_fracck", + .l = &pll_divpmc_layout, + .t = PLL_TYPE_DIV, + .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | + CLK_SET_RATE_PARENT, + .eid = PMC_UTMI, + .c = &upll_characteristics, + }, + }, + + [PLL_ID_AUDIO] = { + { + .n = "audiopll_fracck", + .p = "main_osc", + .l = &pll_frac_layout, + .f = CLK_SET_RATE_GATE, + .c = &audiopll_characteristics, + .t = PLL_TYPE_FRAC, + }, + + { + .n = "audiopll_divpmcck", + .p = "audiopll_fracck", + .l = &pll_divpmc_layout, + .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | + CLK_SET_RATE_PARENT, + .c = &audiopll_characteristics, + .eid = PMC_AUDIOPMCPLL, + .t = PLL_TYPE_DIV, + }, + + { + .n = "audiopll_diviock", + .p = "audiopll_fracck", + .l = &pll_divio_layout, + .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | + CLK_SET_RATE_PARENT, + .c = &audiopll_characteristics, + .eid = PMC_AUDIOIOPLL, + .t = PLL_TYPE_DIV, + }, + }, + + [PLL_ID_LVDS] = { + { + .n = "lvdspll_fracck", + .p = "main_osc", + .l = &pll_frac_layout, + .f = CLK_SET_RATE_GATE, + .c = &lvdspll_characteristics, + .t = PLL_TYPE_FRAC, + }, + + { + .n = "lvdspll_divpmcck", + .p = "lvdspll_fracck", + .l = &pll_divpmc_layout, + .f = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | + CLK_SET_RATE_PARENT, + .c = &lvdspll_characteristics, + .eid = PMC_LVDSPLL, + .t = PLL_TYPE_DIV, + }, + }, + + [PLL_ID_PLLA_DIV2] = { + { + .n = "plla_div2pmcck", + .p = "plla_fracck", + .l = &plladiv2_divpmc_layout, + /* + * This may feed critical parts of the system like timers. + * It should not be disabled. + */ + .f = CLK_IS_CRITICAL | CLK_SET_RATE_GATE, + .c = &plladiv2_characteristics, + .eid = PMC_PLLADIV2, + .t = PLL_TYPE_DIV, + }, + }, +}; + +static const struct clk_programmable_layout sam9x7_programmable_layout = { + .pres_mask = 0xff, + .pres_shift = 8, + .css_mask = 0x1f, + .have_slck_mck = 0, + .is_pres_direct = 1, +}; + +static const struct clk_pcr_layout sam9x7_pcr_layout = { + .offset = 0x88, + .cmd = BIT(31), + .gckcss_mask = GENMASK(12, 8), + .pid_mask = GENMASK(6, 0), +}; + +static const struct { + char *n; + char *p; + u8 id; + unsigned long flags; +} sam9x7_systemck[] = { + /* + * ddrck feeds DDR controller and is enabled by bootloader thus we need + * to keep it enabled in case there is no Linux consumer for it. + */ + { .n = "ddrck", .p = "masterck_div", .id = 2, .flags = CLK_IS_CRITICAL }, + { .n = "uhpck", .p = "usbck", .id = 6 }, + { .n = "pck0", .p = "prog0", .id = 8 }, + { .n = "pck1", .p = "prog1", .id = 9 }, +}; + +/* + * Peripheral clocks description + * @n: clock name + * @f: true if clock is critical and cannot be disabled + * @id: peripheral id + */ +static const struct { + char *n; + unsigned long f; + u8 id; +} sam9x7_periphck[] = { + { .n = "pioA_clk", .id = 2, }, + { .n = "pioB_clk", .id = 3, }, + { .n = "pioC_clk", .id = 4, }, + { .n = "flex0_clk", .id = 5, }, + { .n = "flex1_clk", .id = 6, }, + { .n = "flex2_clk", .id = 7, }, + { .n = "flex3_clk", .id = 8, }, + { .n = "flex6_clk", .id = 9, }, + { .n = "flex7_clk", .id = 10, }, + { .n = "flex8_clk", .id = 11, }, + { .n = "sdmmc0_clk", .id = 12, }, + { .n = "flex4_clk", .id = 13, }, + { .n = "flex5_clk", .id = 14, }, + { .n = "flex9_clk", .id = 15, }, + { .n = "flex10_clk", .id = 16, }, + { .n = "tcb0_clk", .id = 17, }, + { .n = "pwm_clk", .id = 18, }, + { .n = "adc_clk", .id = 19, }, + { .n = "dma0_clk", .id = 20, }, + { .n = "uhphs_clk", .id = 22, }, + { .n = "udphs_clk", .id = 23, }, + { .n = "macb0_clk", .id = 24, }, + { .n = "lcd_clk", .id = 25, }, + { .n = "sdmmc1_clk", .id = 26, }, + { .n = "ssc_clk", .id = 28, }, + { .n = "can0_clk", .id = 29, }, + { .n = "can1_clk", .id = 30, }, + { .n = "flex11_clk", .id = 32, }, + { .n = "flex12_clk", .id = 33, }, + { .n = "i2s_clk", .id = 34, }, + { .n = "qspi_clk", .id = 35, }, + { .n = "gfx2d_clk", .id = 36, }, + { .n = "pit64b0_clk", .id = 37, }, + { .n = "trng_clk", .id = 38, }, + { .n = "aes_clk", .id = 39, }, + { .n = "tdes_clk", .id = 40, }, + { .n = "sha_clk", .id = 41, }, + { .n = "classd_clk", .id = 42, }, + { .n = "isi_clk", .id = 43, }, + { .n = "pioD_clk", .id = 44, }, + { .n = "tcb1_clk", .id = 45, }, + { .n = "dbgu_clk", .id = 47, }, + { .n = "pmecc_clk", .id = 48, }, + /* + * mpddr_clk feeds DDR controller and is enabled by bootloader thus we + * need to keep it enabled in case there is no Linux consumer for it. + */ + { .n = "mpddr_clk", .id = 49, .f = CLK_IS_CRITICAL }, + { .n = "csi2dc_clk", .id = 52, }, + { .n = "csi4l_clk", .id = 53, }, + { .n = "dsi4l_clk", .id = 54, }, + { .n = "lvdsc_clk", .id = 56, }, + { .n = "pit64b1_clk", .id = 58, }, + { .n = "puf_clk", .id = 59, }, + { .n = "gmactsu_clk", .id = 67, }, +}; + +/* + * Generic clock description + * @n: clock name + * @pp: PLL parents + * @pp_mux_table: PLL parents mux table + * @r: clock output range + * @pp_chg_id: id in parent array of changeable PLL parent + * @pp_count: PLL parents count + * @id: clock id + */ +static const struct { + const char *n; + const char *pp[8]; + const char pp_mux_table[8]; + struct clk_range r; + int pp_chg_id; + u8 pp_count; + u8 id; +} sam9x7_gck[] = { + { + .n = "flex0_gclk", + .id = 5, + .pp = { "plla_div2pmcck", }, + .pp_mux_table = { 8, }, + .pp_count = 1, + .pp_chg_id = INT_MIN, + }, + + { + .n = "flex1_gclk", + .id = 6, + .pp = { "plla_div2pmcck", }, + .pp_mux_table = { 8, }, + .pp_count = 1, + .pp_chg_id = INT_MIN, + }, + + { + .n = "flex2_gclk", + .id = 7, + .pp = { "plla_div2pmcck", }, + .pp_mux_table = { 8, }, + .pp_count = 1, + .pp_chg_id = INT_MIN, + }, + + { + .n = "flex3_gclk", + .id = 8, + .pp = { "plla_div2pmcck", }, + .pp_mux_table = { 8, }, + .pp_count = 1, + .pp_chg_id = INT_MIN, + }, + + { + .n = "flex6_gclk", + .id = 9, + .pp = { "plla_div2pmcck", }, + .pp_mux_table = { 8, }, + .pp_count = 1, + .pp_chg_id = INT_MIN, + }, + + { + .n = "flex7_gclk", + .id = 10, + .pp = { "plla_div2pmcck", }, + .pp_mux_table = { 8, }, + .pp_count = 1, + .pp_chg_id = INT_MIN, + }, + + { + .n = "flex8_gclk", + .id = 11, + .pp = { "plla_div2pmcck", }, + .pp_mux_table = { 8, }, + .pp_count = 1, + .pp_chg_id = INT_MIN, + }, + + { + .n = "sdmmc0_gclk", + .id = 12, + .r = { .max = 105000000 }, + .pp = { "audiopll_divpmcck", "plla_div2pmcck", }, + .pp_mux_table = { 6, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, + }, + + { + .n = "flex4_gclk", + .id = 13, + .pp = { "plla_div2pmcck", }, + .pp_mux_table = { 8, }, + .pp_count = 1, + .pp_chg_id = INT_MIN, + }, + + { + .n = "flex5_gclk", + .id = 14, + .pp = { "plla_div2pmcck", }, + .pp_mux_table = { 8, }, + .pp_count = 1, + .pp_chg_id = INT_MIN, + }, + + { + .n = "flex9_gclk", + .id = 15, + .pp = { "plla_div2pmcck", }, + .pp_mux_table = { 8, }, + .pp_count = 1, + .pp_chg_id = INT_MIN, + }, + + { + .n = "flex10_gclk", + .id = 16, + .pp = { "plla_div2pmcck", }, + .pp_mux_table = { 8, }, + .pp_count = 1, + .pp_chg_id = INT_MIN, + }, + + { + .n = "tcb0_gclk", + .id = 17, + .pp = { "audiopll_divpmcck", "plla_div2pmcck", }, + .pp_mux_table = { 6, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, + }, + + { + .n = "adc_gclk", + .id = 19, + .pp = { "upll_divpmcck", "plla_div2pmcck", }, + .pp_mux_table = { 5, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, + }, + + { + .n = "lcd_gclk", + .id = 25, + .r = { .max = 75000000 }, + .pp = { "audiopll_divpmcck", "plla_div2pmcck", }, + .pp_mux_table = { 6, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, + }, + + { + .n = "sdmmc1_gclk", + .id = 26, + .r = { .max = 105000000 }, + .pp = { "audiopll_divpmcck", "plla_div2pmcck", }, + .pp_mux_table = { 6, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, + }, + + { + .n = "mcan0_gclk", + .id = 29, + .r = { .max = 80000000 }, + .pp = { "upll_divpmcck", "plla_div2pmcck", }, + .pp_mux_table = { 5, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, + }, + + { + .n = "mcan1_gclk", + .id = 30, + .r = { .max = 80000000 }, + .pp = { "upll_divpmcck", "plla_div2pmcck", }, + .pp_mux_table = { 5, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, + }, + + { + .n = "flex11_gclk", + .id = 32, + .pp = { "plla_div2pmcck", }, + .pp_mux_table = { 8, }, + .pp_count = 1, + .pp_chg_id = INT_MIN, + }, + + { + .n = "flex12_gclk", + .id = 33, + .pp = { "plla_div2pmcck", }, + .pp_mux_table = { 8, }, + .pp_count = 1, + .pp_chg_id = INT_MIN, + }, + + { + .n = "i2s_gclk", + .id = 34, + .r = { .max = 100000000 }, + .pp = { "audiopll_divpmcck", "plla_div2pmcck", }, + .pp_mux_table = { 6, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, + }, + + { + .n = "qspi_gclk", + .id = 35, + .r = { .max = 200000000 }, + .pp = { "audiopll_divpmcck", "plla_div2pmcck", }, + .pp_mux_table = { 6, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, + }, + + { + .n = "pit64b0_gclk", + .id = 37, + .pp = { "plla_div2pmcck", }, + .pp_mux_table = { 8, }, + .pp_count = 1, + .pp_chg_id = INT_MIN, + }, + + { + .n = "classd_gclk", + .id = 42, + .r = { .max = 100000000 }, + .pp = { "audiopll_divpmcck", "plla_div2pmcck", }, + .pp_mux_table = { 6, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, + }, + + { + .n = "tcb1_gclk", + .id = 45, + .pp = { "audiopll_divpmcck", "plla_div2pmcck", }, + .pp_mux_table = { 6, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, + }, + + { + .n = "dbgu_gclk", + .id = 47, + .pp = { "plla_div2pmcck", }, + .pp_mux_table = { 8, }, + .pp_count = 1, + .pp_chg_id = INT_MIN, + }, + + { + .n = "mipiphy_gclk", + .id = 55, + .r = { .max = 27000000 }, + .pp = { "plla_div2pmcck", }, + .pp_mux_table = { 8, }, + .pp_count = 1, + .pp_chg_id = INT_MIN, + }, + + { + .n = "pit64b1_gclk", + .id = 58, + .pp = { "plla_div2pmcck", }, + .pp_mux_table = { 8, }, + .pp_count = 1, + .pp_chg_id = INT_MIN, + }, + + { + .n = "gmac_gclk", + .id = 67, + .pp = { "audiopll_divpmcck", "plla_div2pmcck", }, + .pp_mux_table = { 6, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, + }, +}; + +static void __init sam9x7_pmc_setup(struct device_node *np) +{ + struct clk_range range = CLK_RANGE(0, 0); + const char *td_slck_name, *md_slck_name, *mainxtal_name; + struct pmc_data *sam9x7_pmc; + const char *parent_names[9]; + void **clk_mux_buffer = NULL; + int clk_mux_buffer_size = 0; + struct clk_hw *main_osc_hw; + struct regmap *regmap; + struct clk_hw *hw; + int i, j; + + i = of_property_match_string(np, "clock-names", "td_slck"); + if (i < 0) + return; + + td_slck_name = of_clk_get_parent_name(np, i); + + i = of_property_match_string(np, "clock-names", "md_slck"); + if (i < 0) + return; + + md_slck_name = of_clk_get_parent_name(np, i); + + i = of_property_match_string(np, "clock-names", "main_xtal"); + if (i < 0) + return; + mainxtal_name = of_clk_get_parent_name(np, i); + + regmap = device_node_to_regmap(np); + if (IS_ERR(regmap)) + return; + + sam9x7_pmc = pmc_data_allocate(PMC_LVDSPLL + 1, + nck(sam9x7_systemck), + nck(sam9x7_periphck), + nck(sam9x7_gck), 8); + if (!sam9x7_pmc) + return; + + clk_mux_buffer = kmalloc(sizeof(void *) * + (ARRAY_SIZE(sam9x7_gck)), + GFP_KERNEL); + if (!clk_mux_buffer) + goto err_free; + + hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 12000000, + 50000000); + if (IS_ERR(hw)) + goto err_free; + + hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name, NULL, 0); + if (IS_ERR(hw)) + goto err_free; + main_osc_hw = hw; + + parent_names[0] = "main_rc_osc"; + parent_names[1] = "main_osc"; + hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, NULL, 2); + if (IS_ERR(hw)) + goto err_free; + + sam9x7_pmc->chws[PMC_MAIN] = hw; + + for (i = 0; i < PLL_ID_MAX; i++) { + for (j = 0; j < 3; j++) { + struct clk_hw *parent_hw; + + if (!sam9x7_plls[i][j].n) + continue; + + switch (sam9x7_plls[i][j].t) { + case PLL_TYPE_FRAC: + if (!strcmp(sam9x7_plls[i][j].p, "mainck")) + parent_hw = sam9x7_pmc->chws[PMC_MAIN]; + else if (!strcmp(sam9x7_plls[i][j].p, "main_osc")) + parent_hw = main_osc_hw; + else + parent_hw = __clk_get_hw(of_clk_get_by_name + (np, sam9x7_plls[i][j].p)); + + hw = sam9x60_clk_register_frac_pll(regmap, + &pmc_pll_lock, + sam9x7_plls[i][j].n, + sam9x7_plls[i][j].p, + parent_hw, i, + sam9x7_plls[i][j].c, + sam9x7_plls[i][j].l, + sam9x7_plls[i][j].f); + break; + + case PLL_TYPE_DIV: + hw = sam9x60_clk_register_div_pll(regmap, + &pmc_pll_lock, + sam9x7_plls[i][j].n, + sam9x7_plls[i][j].p, NULL, i, + sam9x7_plls[i][j].c, + sam9x7_plls[i][j].l, + sam9x7_plls[i][j].f, 0); + break; + + default: + continue; + } + + if (IS_ERR(hw)) + goto err_free; + + if (sam9x7_plls[i][j].eid) + sam9x7_pmc->chws[sam9x7_plls[i][j].eid] = hw; + } + } + + parent_names[0] = md_slck_name; + parent_names[1] = "mainck"; + parent_names[2] = "plla_divpmcck"; + parent_names[3] = "upll_divpmcck"; + hw = at91_clk_register_master_pres(regmap, "masterck_pres", 4, + parent_names, NULL, &sam9x7_master_layout, + &mck_characteristics, &mck_lock); + if (IS_ERR(hw)) + goto err_free; + + hw = at91_clk_register_master_div(regmap, "masterck_div", + "masterck_pres", NULL, &sam9x7_master_layout, + &mck_characteristics, &mck_lock, + CLK_SET_RATE_GATE, 0); + if (IS_ERR(hw)) + goto err_free; + + sam9x7_pmc->chws[PMC_MCK] = hw; + + parent_names[0] = "plla_divpmcck"; + parent_names[1] = "upll_divpmcck"; + parent_names[2] = "main_osc"; + hw = sam9x60_clk_register_usb(regmap, "usbck", parent_names, 3); + if (IS_ERR(hw)) + goto err_free; + + parent_names[0] = md_slck_name; + parent_names[1] = td_slck_name; + parent_names[2] = "mainck"; + parent_names[3] = "masterck_div"; + parent_names[4] = "plla_divpmcck"; + parent_names[5] = "upll_divpmcck"; + parent_names[6] = "audiopll_divpmcck"; + for (i = 0; i < 2; i++) { + char name[6]; + + snprintf(name, sizeof(name), "prog%d", i); + + hw = at91_clk_register_programmable(regmap, name, + parent_names, NULL, 7, i, + &sam9x7_programmable_layout, + NULL); + if (IS_ERR(hw)) + goto err_free; + + sam9x7_pmc->pchws[i] = hw; + } + + for (i = 0; i < ARRAY_SIZE(sam9x7_systemck); i++) { + hw = at91_clk_register_system(regmap, sam9x7_systemck[i].n, + sam9x7_systemck[i].p, NULL, + sam9x7_systemck[i].id, + sam9x7_systemck[i].flags); + if (IS_ERR(hw)) + goto err_free; + + sam9x7_pmc->shws[sam9x7_systemck[i].id] = hw; + } + + for (i = 0; i < ARRAY_SIZE(sam9x7_periphck); i++) { + hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, + &sam9x7_pcr_layout, + sam9x7_periphck[i].n, + "masterck_div", NULL, + sam9x7_periphck[i].id, + &range, INT_MIN, + sam9x7_periphck[i].f); + if (IS_ERR(hw)) + goto err_free; + + sam9x7_pmc->phws[sam9x7_periphck[i].id] = hw; + } + + parent_names[0] = md_slck_name; + parent_names[1] = td_slck_name; + parent_names[2] = "mainck"; + parent_names[3] = "masterck_div"; + for (i = 0; i < ARRAY_SIZE(sam9x7_gck); i++) { + u8 num_parents = 4 + sam9x7_gck[i].pp_count; + u32 *mux_table; + + mux_table = kmalloc_array(num_parents, sizeof(*mux_table), + GFP_KERNEL); + if (!mux_table) + goto err_free; + + SAM9X7_INIT_TABLE(mux_table, 4); + SAM9X7_FILL_TABLE(&mux_table[4], sam9x7_gck[i].pp_mux_table, + sam9x7_gck[i].pp_count); + SAM9X7_FILL_TABLE(&parent_names[4], sam9x7_gck[i].pp, + sam9x7_gck[i].pp_count); + + hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, + &sam9x7_pcr_layout, + sam9x7_gck[i].n, + parent_names, NULL, mux_table, + num_parents, + sam9x7_gck[i].id, + &sam9x7_gck[i].r, + sam9x7_gck[i].pp_chg_id); + if (IS_ERR(hw)) + goto err_free; + + sam9x7_pmc->ghws[sam9x7_gck[i].id] = hw; + clk_mux_buffer[clk_mux_buffer_size++] = mux_table; + } + + of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sam9x7_pmc); + kfree(clk_mux_buffer); + + return; + +err_free: + if (clk_mux_buffer) { + for (i = 0; i < clk_mux_buffer_size; i++) + kfree(clk_mux_buffer[i]); + kfree(clk_mux_buffer); + } + kfree(sam9x7_pmc); +} + +/* Some clks are used for a clocksource */ +CLK_OF_DECLARE(sam9x7_pmc, "microchip,sam9x7-pmc", sam9x7_pmc_setup); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/at91/sama7g5.c linux-microchip/drivers/clk/at91/sama7g5.c --- linux-6.6.51/drivers/clk/at91/sama7g5.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/at91/sama7g5.c 2024-11-26 14:04:18.560303308 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:119 @ { .min = 2343750, .max = 1200000000 }, }; +/* Fractional PLL core output range. */ +static const struct clk_range core_outputs[] = { + { .min = 600000000, .max = 1200000000 }, +}; + /* CPU PLL characteristics. */ static const struct clk_pll_characteristics cpu_pll_characteristics = { .input = { .min = 12000000, .max = 50000000 }, .num_output = ARRAY_SIZE(cpu_pll_outputs), .output = cpu_pll_outputs, + .core_output = core_outputs, }; /* PLL characteristics. */ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:137 @ .input = { .min = 12000000, .max = 50000000 }, .num_output = ARRAY_SIZE(pll_outputs), .output = pll_outputs, + .core_output = core_outputs, }; /* @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:591 @ .pp = { PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0), }, .pp_mux_table = { 9, }, .pp_count = 1, - .pp_chg_id = 3, }, + .pp_chg_id = INT_MIN, }, { .n = "csi_gclk", .id = 33, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:735 @ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0), }, .pp_mux_table = { 5, 9, }, .pp_count = 2, - .pp_chg_id = 4, }, + .pp_chg_id = INT_MIN, }, { .n = "i2smcc1_gclk", .id = 58, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:743 @ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0), }, .pp_mux_table = { 5, 9, }, .pp_count = 2, - .pp_chg_id = 4, }, + .pp_chg_id = INT_MIN, }, { .n = "mcan0_gclk", .id = 61, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:915 @ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0), }, .pp_mux_table = { 5, 9, }, .pp_count = 2, - .pp_chg_id = 4, }, + .pp_chg_id = INT_MIN, }, { .n = "spdiftx_gclk", .id = 85, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:923 @ .pp = { PLL_IDS_TO_ARR_ENTRY(SYS, DIV0), PLL_IDS_TO_ARR_ENTRY(AUDIO, DIV0), }, .pp_mux_table = { 5, 9, }, .pp_count = 2, - .pp_chg_id = 4, }, + .pp_chg_id = INT_MIN, }, { .n = "tcb0_ch0_gclk", .id = 88, diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/clk-regmap.c linux-microchip/drivers/clk/clk-regmap.c --- linux-6.6.51/drivers/clk/clk-regmap.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/clk/clk-regmap.c 2024-11-26 14:02:36.350473132 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018 BayLibre, SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> + */ + +#include <linux/module.h> +#include <linux/clk/clk-regmap.h> + +static int clk_regmap_gate_endisable(struct clk_hw *hw, int enable) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct clk_regmap_gate_data *gate = clk_get_regmap_gate_data(clk); + int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0; + + set ^= enable; + + return regmap_update_bits(clk->map, gate->offset, BIT(gate->bit_idx), + set ? BIT(gate->bit_idx) : 0); +} + +static int clk_regmap_gate_enable(struct clk_hw *hw) +{ + return clk_regmap_gate_endisable(hw, 1); +} + +static void clk_regmap_gate_disable(struct clk_hw *hw) +{ + clk_regmap_gate_endisable(hw, 0); +} + +static int clk_regmap_gate_is_enabled(struct clk_hw *hw) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct clk_regmap_gate_data *gate = clk_get_regmap_gate_data(clk); + unsigned int val; + + regmap_read(clk->map, gate->offset, &val); + if (gate->flags & CLK_GATE_SET_TO_DISABLE) + val ^= BIT(gate->bit_idx); + + val &= BIT(gate->bit_idx); + + return val ? 1 : 0; +} + +const struct clk_ops clk_regmap_gate_ops = { + .enable = clk_regmap_gate_enable, + .disable = clk_regmap_gate_disable, + .is_enabled = clk_regmap_gate_is_enabled, +}; +EXPORT_SYMBOL_GPL(clk_regmap_gate_ops); + +const struct clk_ops clk_regmap_gate_ro_ops = { + .is_enabled = clk_regmap_gate_is_enabled, +}; +EXPORT_SYMBOL_GPL(clk_regmap_gate_ro_ops); + +static unsigned long clk_regmap_div_recalc_rate(struct clk_hw *hw, + unsigned long prate) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk); + unsigned int val; + int ret; + + ret = regmap_read(clk->map, div->offset, &val); + if (ret) + /* Gives a hint that something is wrong */ + return 0; + + val >>= div->shift; + val &= clk_div_mask(div->width); + return divider_recalc_rate(hw, prate, val, div->table, div->flags, + div->width); +} + +static int clk_regmap_div_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk); + unsigned int val; + int ret; + + /* if read only, just return current value */ + if (div->flags & CLK_DIVIDER_READ_ONLY) { + ret = regmap_read(clk->map, div->offset, &val); + if (ret) + return ret; + + val >>= div->shift; + val &= clk_div_mask(div->width); + + return divider_ro_determine_rate(hw, req, div->table, + div->width, div->flags, val); + } + + return divider_determine_rate(hw, req, div->table, div->width, + div->flags); +} + +static int clk_regmap_div_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk); + unsigned int val; + int ret; + + ret = divider_get_val(rate, parent_rate, div->table, div->width, + div->flags); + if (ret < 0) + return ret; + + val = (unsigned int)ret << div->shift; + return regmap_update_bits(clk->map, div->offset, + clk_div_mask(div->width) << div->shift, val); +}; + +/* Would prefer clk_regmap_div_ro_ops but clashes with qcom */ + +const struct clk_ops clk_regmap_divider_ops = { + .recalc_rate = clk_regmap_div_recalc_rate, + .determine_rate = clk_regmap_div_determine_rate, + .set_rate = clk_regmap_div_set_rate, +}; +EXPORT_SYMBOL_GPL(clk_regmap_divider_ops); + +const struct clk_ops clk_regmap_divider_ro_ops = { + .recalc_rate = clk_regmap_div_recalc_rate, + .determine_rate = clk_regmap_div_determine_rate, +}; +EXPORT_SYMBOL_GPL(clk_regmap_divider_ro_ops); + +static u8 clk_regmap_mux_get_parent(struct clk_hw *hw) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk); + unsigned int val; + int ret; + + ret = regmap_read(clk->map, mux->offset, &val); + if (ret) + return ret; + + val >>= mux->shift; + val &= mux->mask; + return clk_mux_val_to_index(hw, mux->table, mux->flags, val); +} + +static int clk_regmap_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk); + unsigned int val = clk_mux_index_to_val(mux->table, mux->flags, index); + + return regmap_update_bits(clk->map, mux->offset, + mux->mask << mux->shift, + val << mux->shift); +} + +static int clk_regmap_mux_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_regmap *clk = to_clk_regmap(hw); + struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk); + + return clk_mux_determine_rate_flags(hw, req, mux->flags); +} + +const struct clk_ops clk_regmap_mux_ops = { + .get_parent = clk_regmap_mux_get_parent, + .set_parent = clk_regmap_mux_set_parent, + .determine_rate = clk_regmap_mux_determine_rate, +}; +EXPORT_SYMBOL_GPL(clk_regmap_mux_ops); + +const struct clk_ops clk_regmap_mux_ro_ops = { + .get_parent = clk_regmap_mux_get_parent, +}; +EXPORT_SYMBOL_GPL(clk_regmap_mux_ro_ops); + +MODULE_DESCRIPTION("Amlogic regmap backed clock driver"); +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_LICENSE("GPL v2"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/Kconfig linux-microchip/drivers/clk/Kconfig --- linux-6.6.51/drivers/clk/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/Kconfig 2024-11-26 14:02:36.342472989 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:36 @ if COMMON_CLK +config COMMON_CLK_REGMAP + bool + select REGMAP + config COMMON_CLK_WM831X tristate "Clock driver for WM831x/2x PMICs" depends on MFD_WM831X diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/Makefile linux-microchip/drivers/clk/Makefile --- linux-6.6.51/drivers/clk/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/Makefile 2024-11-26 14:02:36.342472989 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:13 @ obj-$(CONFIG_CLK_GATE_KUNIT_TEST) += clk-gate_test.o obj-$(CONFIG_COMMON_CLK) += clk-multiplier.o obj-$(CONFIG_COMMON_CLK) += clk-mux.o +obj-$(CONFIG_COMMON_CLK_REGMAP) += clk-regmap.o obj-$(CONFIG_COMMON_CLK) += clk-composite.o obj-$(CONFIG_COMMON_CLK) += clk-fractional-divider.o obj-$(CONFIG_COMMON_CLK) += clk-gpio.o diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/meson/a1-peripherals.c linux-microchip/drivers/clk/meson/a1-peripherals.c --- linux-6.6.51/drivers/clk/meson/a1-peripherals.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/meson/a1-peripherals.c 2024-11-26 14:02:36.362473347 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:15 @ #include <linux/platform_device.h> #include "a1-peripherals.h" #include "clk-dualdiv.h" -#include "clk-regmap.h" +#include <linux/clk/clk-regmap.h> #include "meson-clkc-utils.h" #include <dt-bindings/clock/amlogic,a1-peripherals-clkc.h> diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/meson/a1-pll.c linux-microchip/drivers/clk/meson/a1-pll.c --- linux-6.6.51/drivers/clk/meson/a1-pll.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/meson/a1-pll.c 2024-11-26 14:02:36.362473347 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:14 @ #include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include "a1-pll.h" -#include "clk-regmap.h" +#include <linux/clk/clk-regmap.h> #include "meson-clkc-utils.h" #include <dt-bindings/clock/amlogic,a1-pll-clkc.h> diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/meson/axg-aoclk.c linux-microchip/drivers/clk/meson/axg-aoclk.c --- linux-6.6.51/drivers/clk/meson/axg-aoclk.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/meson/axg-aoclk.c 2024-11-26 14:02:36.362473347 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:18 @ #include <linux/module.h> #include "meson-aoclk.h" -#include "clk-regmap.h" +#include <linux/clk/clk-regmap.h> #include "clk-dualdiv.h" #include <dt-bindings/clock/axg-aoclkc.h> diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/meson/axg-audio.c linux-microchip/drivers/clk/meson/axg-audio.c --- linux-6.6.51/drivers/clk/meson/axg-audio.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/meson/axg-audio.c 2024-11-26 14:02:36.362473347 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:20 @ #include "meson-clkc-utils.h" #include "axg-audio.h" -#include "clk-regmap.h" +#include <linux/clk/clk-regmap.h> #include "clk-phase.h" #include "sclk-div.h" diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/meson/axg.c linux-microchip/drivers/clk/meson/axg.c --- linux-6.6.51/drivers/clk/meson/axg.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/meson/axg.c 2024-11-26 14:02:36.362473347 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:18 @ #include <linux/platform_device.h> #include <linux/module.h> -#include "clk-regmap.h" +#include <linux/clk/clk-regmap.h> #include "clk-pll.h" #include "clk-mpll.h" #include "axg.h" diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/meson/clk-cpu-dyndiv.c linux-microchip/drivers/clk/meson/clk-cpu-dyndiv.c --- linux-6.6.51/drivers/clk/meson/clk-cpu-dyndiv.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/meson/clk-cpu-dyndiv.c 2024-11-26 14:02:36.362473347 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:10 @ #include <linux/clk-provider.h> #include <linux/module.h> -#include "clk-regmap.h" +#include <linux/clk/clk-regmap.h> #include "clk-cpu-dyndiv.h" static inline struct meson_clk_cpu_dyndiv_data * diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/meson/clk-dualdiv.c linux-microchip/drivers/clk/meson/clk-dualdiv.c --- linux-6.6.51/drivers/clk/meson/clk-dualdiv.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/meson/clk-dualdiv.c 2024-11-26 14:02:36.362473347 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:27 @ #include <linux/clk-provider.h> #include <linux/module.h> -#include "clk-regmap.h" +#include <linux/clk/clk-regmap.h> #include "clk-dualdiv.h" static inline struct meson_clk_dualdiv_data * diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/meson/clk-mpll.c linux-microchip/drivers/clk/meson/clk-mpll.c --- linux-6.6.51/drivers/clk/meson/clk-mpll.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/meson/clk-mpll.c 2024-11-26 14:02:36.362473347 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:18 @ #include <linux/module.h> #include <linux/spinlock.h> -#include "clk-regmap.h" +#include <linux/clk/clk-regmap.h> #include "clk-mpll.h" #define SDM_DEN 16384 diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/meson/clk-phase.c linux-microchip/drivers/clk/meson/clk-phase.c --- linux-6.6.51/drivers/clk/meson/clk-phase.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/meson/clk-phase.c 2024-11-26 14:02:36.362473347 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:10 @ #include <linux/clk-provider.h> #include <linux/module.h> -#include "clk-regmap.h" +#include <linux/clk/clk-regmap.h> #include "clk-phase.h" #define phase_step(_width) (360 / (1 << (_width))) diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/meson/clk-pll.c linux-microchip/drivers/clk/meson/clk-pll.c --- linux-6.6.51/drivers/clk/meson/clk-pll.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/meson/clk-pll.c 2024-11-26 14:02:36.362473347 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:36 @ #include <linux/math64.h> #include <linux/module.h> -#include "clk-regmap.h" +#include <linux/clk/clk-regmap.h> #include "clk-pll.h" static inline struct meson_clk_pll_data * diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/meson/clk-regmap.c linux-microchip/drivers/clk/meson/clk-regmap.c --- linux-6.6.51/drivers/clk/meson/clk-regmap.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/meson/clk-regmap.c 1970-01-01 01:00:00.000000000 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1 @ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2018 BayLibre, SAS. - * Author: Jerome Brunet <jbrunet@baylibre.com> - */ - -#include <linux/module.h> -#include "clk-regmap.h" - -static int clk_regmap_gate_endisable(struct clk_hw *hw, int enable) -{ - struct clk_regmap *clk = to_clk_regmap(hw); - struct clk_regmap_gate_data *gate = clk_get_regmap_gate_data(clk); - int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0; - - set ^= enable; - - return regmap_update_bits(clk->map, gate->offset, BIT(gate->bit_idx), - set ? BIT(gate->bit_idx) : 0); -} - -static int clk_regmap_gate_enable(struct clk_hw *hw) -{ - return clk_regmap_gate_endisable(hw, 1); -} - -static void clk_regmap_gate_disable(struct clk_hw *hw) -{ - clk_regmap_gate_endisable(hw, 0); -} - -static int clk_regmap_gate_is_enabled(struct clk_hw *hw) -{ - struct clk_regmap *clk = to_clk_regmap(hw); - struct clk_regmap_gate_data *gate = clk_get_regmap_gate_data(clk); - unsigned int val; - - regmap_read(clk->map, gate->offset, &val); - if (gate->flags & CLK_GATE_SET_TO_DISABLE) - val ^= BIT(gate->bit_idx); - - val &= BIT(gate->bit_idx); - - return val ? 1 : 0; -} - -const struct clk_ops clk_regmap_gate_ops = { - .enable = clk_regmap_gate_enable, - .disable = clk_regmap_gate_disable, - .is_enabled = clk_regmap_gate_is_enabled, -}; -EXPORT_SYMBOL_GPL(clk_regmap_gate_ops); - -const struct clk_ops clk_regmap_gate_ro_ops = { - .is_enabled = clk_regmap_gate_is_enabled, -}; -EXPORT_SYMBOL_GPL(clk_regmap_gate_ro_ops); - -static unsigned long clk_regmap_div_recalc_rate(struct clk_hw *hw, - unsigned long prate) -{ - struct clk_regmap *clk = to_clk_regmap(hw); - struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk); - unsigned int val; - int ret; - - ret = regmap_read(clk->map, div->offset, &val); - if (ret) - /* Gives a hint that something is wrong */ - return 0; - - val >>= div->shift; - val &= clk_div_mask(div->width); - return divider_recalc_rate(hw, prate, val, div->table, div->flags, - div->width); -} - -static int clk_regmap_div_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - struct clk_regmap *clk = to_clk_regmap(hw); - struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk); - unsigned int val; - int ret; - - /* if read only, just return current value */ - if (div->flags & CLK_DIVIDER_READ_ONLY) { - ret = regmap_read(clk->map, div->offset, &val); - if (ret) - return ret; - - val >>= div->shift; - val &= clk_div_mask(div->width); - - return divider_ro_determine_rate(hw, req, div->table, - div->width, div->flags, val); - } - - return divider_determine_rate(hw, req, div->table, div->width, - div->flags); -} - -static int clk_regmap_div_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) -{ - struct clk_regmap *clk = to_clk_regmap(hw); - struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk); - unsigned int val; - int ret; - - ret = divider_get_val(rate, parent_rate, div->table, div->width, - div->flags); - if (ret < 0) - return ret; - - val = (unsigned int)ret << div->shift; - return regmap_update_bits(clk->map, div->offset, - clk_div_mask(div->width) << div->shift, val); -}; - -/* Would prefer clk_regmap_div_ro_ops but clashes with qcom */ - -const struct clk_ops clk_regmap_divider_ops = { - .recalc_rate = clk_regmap_div_recalc_rate, - .determine_rate = clk_regmap_div_determine_rate, - .set_rate = clk_regmap_div_set_rate, -}; -EXPORT_SYMBOL_GPL(clk_regmap_divider_ops); - -const struct clk_ops clk_regmap_divider_ro_ops = { - .recalc_rate = clk_regmap_div_recalc_rate, - .determine_rate = clk_regmap_div_determine_rate, -}; -EXPORT_SYMBOL_GPL(clk_regmap_divider_ro_ops); - -static u8 clk_regmap_mux_get_parent(struct clk_hw *hw) -{ - struct clk_regmap *clk = to_clk_regmap(hw); - struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk); - unsigned int val; - int ret; - - ret = regmap_read(clk->map, mux->offset, &val); - if (ret) - return ret; - - val >>= mux->shift; - val &= mux->mask; - return clk_mux_val_to_index(hw, mux->table, mux->flags, val); -} - -static int clk_regmap_mux_set_parent(struct clk_hw *hw, u8 index) -{ - struct clk_regmap *clk = to_clk_regmap(hw); - struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk); - unsigned int val = clk_mux_index_to_val(mux->table, mux->flags, index); - - return regmap_update_bits(clk->map, mux->offset, - mux->mask << mux->shift, - val << mux->shift); -} - -static int clk_regmap_mux_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - struct clk_regmap *clk = to_clk_regmap(hw); - struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk); - - return clk_mux_determine_rate_flags(hw, req, mux->flags); -} - -const struct clk_ops clk_regmap_mux_ops = { - .get_parent = clk_regmap_mux_get_parent, - .set_parent = clk_regmap_mux_set_parent, - .determine_rate = clk_regmap_mux_determine_rate, -}; -EXPORT_SYMBOL_GPL(clk_regmap_mux_ops); - -const struct clk_ops clk_regmap_mux_ro_ops = { - .get_parent = clk_regmap_mux_get_parent, -}; -EXPORT_SYMBOL_GPL(clk_regmap_mux_ro_ops); - -MODULE_DESCRIPTION("Amlogic regmap backed clock driver"); -MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); -MODULE_LICENSE("GPL v2"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/meson/clk-regmap.h linux-microchip/drivers/clk/meson/clk-regmap.h --- linux-6.6.51/drivers/clk/meson/clk-regmap.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/meson/clk-regmap.h 1970-01-01 01:00:00.000000000 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1 @ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (c) 2018 BayLibre, SAS. - * Author: Jerome Brunet <jbrunet@baylibre.com> - */ - -#ifndef __CLK_REGMAP_H -#define __CLK_REGMAP_H - -#include <linux/clk-provider.h> -#include <linux/regmap.h> - -/** - * struct clk_regmap - regmap backed clock - * - * @hw: handle between common and hardware-specific interfaces - * @map: pointer to the regmap structure controlling the clock - * @data: data specific to the clock type - * - * Clock which is controlled by regmap backed registers. The actual type of - * of the clock is controlled by the clock_ops and data. - */ -struct clk_regmap { - struct clk_hw hw; - struct regmap *map; - void *data; -}; - -static inline struct clk_regmap *to_clk_regmap(struct clk_hw *hw) -{ - return container_of(hw, struct clk_regmap, hw); -} - -/** - * struct clk_regmap_gate_data - regmap backed gate specific data - * - * @offset: offset of the register controlling gate - * @bit_idx: single bit controlling gate - * @flags: hardware-specific flags - * - * Flags: - * Same as clk_gate except CLK_GATE_HIWORD_MASK which is ignored - */ -struct clk_regmap_gate_data { - unsigned int offset; - u8 bit_idx; - u8 flags; -}; - -static inline struct clk_regmap_gate_data * -clk_get_regmap_gate_data(struct clk_regmap *clk) -{ - return (struct clk_regmap_gate_data *)clk->data; -} - -extern const struct clk_ops clk_regmap_gate_ops; -extern const struct clk_ops clk_regmap_gate_ro_ops; - -/** - * struct clk_regmap_div_data - regmap backed adjustable divider specific data - * - * @offset: offset of the register controlling the divider - * @shift: shift to the divider bit field - * @width: width of the divider bit field - * @table: array of value/divider pairs, last entry should have div = 0 - * - * Flags: - * Same as clk_divider except CLK_DIVIDER_HIWORD_MASK which is ignored - */ -struct clk_regmap_div_data { - unsigned int offset; - u8 shift; - u8 width; - u8 flags; - const struct clk_div_table *table; -}; - -static inline struct clk_regmap_div_data * -clk_get_regmap_div_data(struct clk_regmap *clk) -{ - return (struct clk_regmap_div_data *)clk->data; -} - -extern const struct clk_ops clk_regmap_divider_ops; -extern const struct clk_ops clk_regmap_divider_ro_ops; - -/** - * struct clk_regmap_mux_data - regmap backed multiplexer clock specific data - * - * @hw: handle between common and hardware-specific interfaces - * @offset: offset of theregister controlling multiplexer - * @table: array of parent indexed register values - * @shift: shift to multiplexer bit field - * @mask: mask of mutliplexer bit field - * @flags: hardware-specific flags - * - * Flags: - * Same as clk_divider except CLK_MUX_HIWORD_MASK which is ignored - */ -struct clk_regmap_mux_data { - unsigned int offset; - u32 *table; - u32 mask; - u8 shift; - u8 flags; -}; - -static inline struct clk_regmap_mux_data * -clk_get_regmap_mux_data(struct clk_regmap *clk) -{ - return (struct clk_regmap_mux_data *)clk->data; -} - -extern const struct clk_ops clk_regmap_mux_ops; -extern const struct clk_ops clk_regmap_mux_ro_ops; - -#define __MESON_PCLK(_name, _reg, _bit, _ops, _pname) \ -struct clk_regmap _name = { \ - .data = &(struct clk_regmap_gate_data){ \ - .offset = (_reg), \ - .bit_idx = (_bit), \ - }, \ - .hw.init = &(struct clk_init_data) { \ - .name = #_name, \ - .ops = _ops, \ - .parent_hws = (const struct clk_hw *[]) { _pname }, \ - .num_parents = 1, \ - .flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), \ - }, \ -} - -#define MESON_PCLK(_name, _reg, _bit, _pname) \ - __MESON_PCLK(_name, _reg, _bit, &clk_regmap_gate_ops, _pname) - -#define MESON_PCLK_RO(_name, _reg, _bit, _pname) \ - __MESON_PCLK(_name, _reg, _bit, &clk_regmap_gate_ro_ops, _pname) -#endif /* __CLK_REGMAP_H */ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/meson/g12a-aoclk.c linux-microchip/drivers/clk/meson/g12a-aoclk.c --- linux-6.6.51/drivers/clk/meson/g12a-aoclk.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/meson/g12a-aoclk.c 2024-11-26 14:02:36.362473347 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:18 @ #include <linux/module.h> #include "meson-aoclk.h" -#include "clk-regmap.h" +#include <linux/clk/clk-regmap.h> #include "clk-dualdiv.h" #include <dt-bindings/clock/g12a-aoclkc.h> diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/meson/g12a.c linux-microchip/drivers/clk/meson/g12a.c --- linux-6.6.51/drivers/clk/meson/g12a.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/meson/g12a.c 2024-11-26 14:02:36.362473347 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:22 @ #include "clk-mpll.h" #include "clk-pll.h" -#include "clk-regmap.h" +#include <linux/clk/clk-regmap.h> #include "clk-cpu-dyndiv.h" #include "vid-pll-div.h" #include "meson-eeclk.h" diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/meson/gxbb-aoclk.c linux-microchip/drivers/clk/meson/gxbb-aoclk.c --- linux-6.6.51/drivers/clk/meson/gxbb-aoclk.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/meson/gxbb-aoclk.c 2024-11-26 14:02:36.362473347 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:11 @ #include <linux/module.h> #include "meson-aoclk.h" -#include "clk-regmap.h" +#include <linux/clk/clk-regmap.h> #include "clk-dualdiv.h" #include <dt-bindings/clock/gxbb-aoclkc.h> diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/meson/gxbb.c linux-microchip/drivers/clk/meson/gxbb.c --- linux-6.6.51/drivers/clk/meson/gxbb.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/meson/gxbb.c 2024-11-26 14:02:36.362473347 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:14 @ #include <linux/module.h> #include "gxbb.h" -#include "clk-regmap.h" +#include <linux/clk/clk-regmap.h> #include "clk-pll.h" #include "clk-mpll.h" #include "meson-eeclk.h" diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/meson/Kconfig linux-microchip/drivers/clk/meson/Kconfig --- linux-6.6.51/drivers/clk/meson/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/meson/Kconfig 2024-11-26 14:02:36.362473347 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:5 @ menu "Clock support for Amlogic platforms" depends on ARCH_MESON || COMPILE_TEST -config COMMON_CLK_MESON_REGMAP - tristate - select REGMAP - config COMMON_CLK_MESON_DUALDIV tristate - select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_REGMAP config COMMON_CLK_MESON_MPLL tristate - select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_REGMAP config COMMON_CLK_MESON_PHASE tristate - select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_REGMAP config COMMON_CLK_MESON_PLL tristate - select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_REGMAP config COMMON_CLK_MESON_SCLK_DIV tristate - select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_REGMAP config COMMON_CLK_MESON_VID_PLL_DIV tristate - select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_REGMAP config COMMON_CLK_MESON_CLKC_UTILS tristate config COMMON_CLK_MESON_AO_CLKC tristate - select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_REGMAP select COMMON_CLK_MESON_CLKC_UTILS select RESET_CONTROLLER config COMMON_CLK_MESON_EE_CLKC tristate - select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_REGMAP select COMMON_CLK_MESON_CLKC_UTILS config COMMON_CLK_MESON_CPU_DYNDIV tristate - select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_REGMAP config COMMON_CLK_MESON8B bool "Meson8 SoC Clock controller support" depends on ARM default y - select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_REGMAP select COMMON_CLK_MESON_CLKC_UTILS select COMMON_CLK_MESON_MPLL select COMMON_CLK_MESON_PLL @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:66 @ tristate "GXBB and GXL SoC clock controllers support" depends on ARM64 default y - select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_REGMAP select COMMON_CLK_MESON_DUALDIV select COMMON_CLK_MESON_VID_PLL_DIV select COMMON_CLK_MESON_MPLL @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:82 @ tristate "AXG SoC clock controllers support" depends on ARM64 default y - select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_REGMAP select COMMON_CLK_MESON_DUALDIV select COMMON_CLK_MESON_MPLL select COMMON_CLK_MESON_PLL @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:96 @ config COMMON_CLK_AXG_AUDIO tristate "Meson AXG Audio Clock Controller Driver" depends on ARM64 - select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_REGMAP select COMMON_CLK_MESON_PHASE select COMMON_CLK_MESON_SCLK_DIV select COMMON_CLK_MESON_CLKC_UTILS @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:108 @ config COMMON_CLK_A1_PLL tristate "Amlogic A1 SoC PLL controller support" depends on ARM64 - select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_REGMAP select COMMON_CLK_MESON_CLKC_UTILS select COMMON_CLK_MESON_PLL help @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:120 @ tristate "Amlogic A1 SoC Peripherals clock controller support" depends on ARM64 select COMMON_CLK_MESON_DUALDIV - select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_REGMAP select COMMON_CLK_MESON_CLKC_UTILS help Support for the Peripherals clock controller on Amlogic A113L based @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:131 @ tristate "G12 and SM1 SoC clock controllers support" depends on ARM64 default y - select COMMON_CLK_MESON_REGMAP + select COMMON_CLK_REGMAP select COMMON_CLK_MESON_DUALDIV select COMMON_CLK_MESON_MPLL select COMMON_CLK_MESON_PLL diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/meson/Makefile linux-microchip/drivers/clk/meson/Makefile --- linux-6.6.51/drivers/clk/meson/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/meson/Makefile 2024-11-26 14:02:36.362473347 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:12 @ obj-$(CONFIG_COMMON_CLK_MESON_MPLL) += clk-mpll.o obj-$(CONFIG_COMMON_CLK_MESON_PHASE) += clk-phase.o obj-$(CONFIG_COMMON_CLK_MESON_PLL) += clk-pll.o -obj-$(CONFIG_COMMON_CLK_MESON_REGMAP) += clk-regmap.o obj-$(CONFIG_COMMON_CLK_MESON_SCLK_DIV) += sclk-div.o obj-$(CONFIG_COMMON_CLK_MESON_VID_PLL_DIV) += vid-pll-div.o diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/meson/meson8b.c linux-microchip/drivers/clk/meson/meson8b.c --- linux-6.6.51/drivers/clk/meson/meson8b.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/meson/meson8b.c 2024-11-26 14:02:36.366473419 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:20 @ #include <linux/regmap.h> #include "meson8b.h" -#include "clk-regmap.h" +#include <linux/clk/clk-regmap.h> #include "meson-clkc-utils.h" #include "clk-pll.h" #include "clk-mpll.h" diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/meson/meson8-ddr.c linux-microchip/drivers/clk/meson/meson8-ddr.c --- linux-6.6.51/drivers/clk/meson/meson8-ddr.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/meson/meson8-ddr.c 2024-11-26 14:02:36.362473347 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:13 @ #include <linux/clk-provider.h> #include <linux/platform_device.h> -#include "clk-regmap.h" +#include <linux/clk/clk-regmap.h> #include "clk-pll.h" #define AM_DDR_PLL_CNTL 0x00 diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/meson/meson-aoclk.h linux-microchip/drivers/clk/meson/meson-aoclk.h --- linux-6.6.51/drivers/clk/meson/meson-aoclk.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/meson/meson-aoclk.h 2024-11-26 14:02:36.362473347 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:19 @ #include <linux/regmap.h> #include <linux/reset-controller.h> -#include "clk-regmap.h" +#include <linux/clk/clk-regmap.h> #include "meson-clkc-utils.h" struct meson_aoclk_data { diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/meson/meson-eeclk.c linux-microchip/drivers/clk/meson/meson-eeclk.c --- linux-6.6.51/drivers/clk/meson/meson-eeclk.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/meson/meson-eeclk.c 2024-11-26 14:02:36.362473347 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:14 @ #include <linux/regmap.h> #include <linux/module.h> -#include "clk-regmap.h" +#include <linux/clk/clk-regmap.h> #include "meson-eeclk.h" int meson_eeclkc_probe(struct platform_device *pdev) diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/meson/meson-eeclk.h linux-microchip/drivers/clk/meson/meson-eeclk.h --- linux-6.6.51/drivers/clk/meson/meson-eeclk.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/meson/meson-eeclk.h 2024-11-26 14:02:36.362473347 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:11 @ #define __MESON_CLKC_H #include <linux/clk-provider.h> -#include "clk-regmap.h" +#include <linux/clk/clk-regmap.h> #include "meson-clkc-utils.h" struct platform_device; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/meson/sclk-div.c linux-microchip/drivers/clk/meson/sclk-div.c --- linux-6.6.51/drivers/clk/meson/sclk-div.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/meson/sclk-div.c 2024-11-26 14:02:36.366473419 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:22 @ #include <linux/clk-provider.h> #include <linux/module.h> -#include "clk-regmap.h" +#include <linux/clk/clk-regmap.h> #include "sclk-div.h" static inline struct meson_sclk_div_data * diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/meson/vid-pll-div.c linux-microchip/drivers/clk/meson/vid-pll-div.c --- linux-6.6.51/drivers/clk/meson/vid-pll-div.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/meson/vid-pll-div.c 2024-11-26 14:02:36.366473419 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:10 @ #include <linux/clk-provider.h> #include <linux/module.h> -#include "clk-regmap.h" +#include <linux/clk/clk-regmap.h> #include "vid-pll-div.h" static inline struct meson_vid_pll_div_data * diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/microchip/clk-mpfs.c linux-microchip/drivers/clk/microchip/clk-mpfs.c --- linux-6.6.51/drivers/clk/microchip/clk-mpfs.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/microchip/clk-mpfs.c 2024-11-26 14:02:36.366473419 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:7 @ * * Copyright (C) 2020-2022 Microchip Technology Inc. All rights reserved. */ -#include <linux/auxiliary_bus.h> #include <linux/clk-provider.h> #include <linux/io.h> +#include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/platform_device.h> -#include <linux/slab.h> +#include <linux/regmap.h> #include <dt-bindings/clock/microchip,mpfs-clock.h> #include <soc/microchip/mpfs.h> +#include <linux/clk/clk-regmap.h> /* address offset of control registers */ #define REG_MSSPLL_REF_CR 0x08u -#define REG_MSSPLL_POSTDIV_CR 0x10u +#define REG_MSSPLL_POSTDIV01_CR 0x10u +#define REG_MSSPLL_POSTDIV23_CR 0x14u #define REG_MSSPLL_SSCG_2_CR 0x2Cu #define REG_CLOCK_CONFIG_CR 0x08u #define REG_RTC_CLOCK_CR 0x0Cu @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:31 @ #define MSSPLL_FBDIV_WIDTH 0x0Cu #define MSSPLL_REFDIV_SHIFT 0x08u #define MSSPLL_REFDIV_WIDTH 0x06u -#define MSSPLL_POSTDIV_SHIFT 0x08u +#define MSSPLL_POSTDIV02_SHIFT 0x08u +#define MSSPLL_POSTDIV13_SHIFT 0x18u #define MSSPLL_POSTDIV_WIDTH 0x07u #define MSSPLL_FIXED_DIV 4u +static const struct regmap_config clk_mpfs_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .val_format_endian = REGMAP_ENDIAN_LITTLE, + .max_register = REG_SUBBLK_CLOCK_CR, +}; + +/* + * This clock ID is defined here, rather than the binding headers, as it is an + * internal clock only, and therefore has no consumers in other peripheral + * blocks. + */ +#define CLK_MSSPLL_INTERNAL 38u + struct mpfs_clock_data { struct device *dev; + struct regmap *regmap; void __iomem *base; void __iomem *msspll_base; struct clk_hw_onecell_data hw_data; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:61 @ struct mpfs_msspll_hw_clock { void __iomem *base; + struct clk_hw hw; + struct clk_init_data init; unsigned int id; u32 reg_offset; u32 shift; u32 width; u32 flags; - struct clk_hw hw; - struct clk_init_data init; }; #define to_mpfs_msspll_clk(_hw) container_of(_hw, struct mpfs_msspll_hw_clock, hw) -struct mpfs_cfg_hw_clock { - struct clk_divider cfg; +struct mpfs_msspll_out_hw_clock { + void __iomem *base; + struct clk_divider output; struct clk_init_data init; unsigned int id; u32 reg_offset; }; +#define to_mpfs_msspll_out_clk(_hw) container_of(_hw, struct mpfs_msspll_out_hw_clock, hw) + +struct mpfs_cfg_hw_clock { + struct clk_regmap sigh; + struct clk_regmap_div_data cfg; + unsigned int id; +}; + struct mpfs_periph_hw_clock { - struct clk_gate periph; + struct clk_regmap sigh; + struct clk_regmap_gate_data periph; unsigned int id; }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:125 @ { 0, 0 } }; -static unsigned long mpfs_clk_msspll_recalc_rate(struct clk_hw *hw, unsigned long prate) -{ - struct mpfs_msspll_hw_clock *msspll_hw = to_mpfs_msspll_clk(hw); - void __iomem *mult_addr = msspll_hw->base + msspll_hw->reg_offset; - void __iomem *ref_div_addr = msspll_hw->base + REG_MSSPLL_REF_CR; - void __iomem *postdiv_addr = msspll_hw->base + REG_MSSPLL_POSTDIV_CR; - u32 mult, ref_div, postdiv; - - mult = readl_relaxed(mult_addr) >> MSSPLL_FBDIV_SHIFT; - mult &= clk_div_mask(MSSPLL_FBDIV_WIDTH); - ref_div = readl_relaxed(ref_div_addr) >> MSSPLL_REFDIV_SHIFT; - ref_div &= clk_div_mask(MSSPLL_REFDIV_WIDTH); - postdiv = readl_relaxed(postdiv_addr) >> MSSPLL_POSTDIV_SHIFT; - postdiv &= clk_div_mask(MSSPLL_POSTDIV_WIDTH); - - return prate * mult / (ref_div * MSSPLL_FIXED_DIV * postdiv); -} +/* + * MSS PLL internal clock + */ -static long mpfs_clk_msspll_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) +static unsigned long mpfs_clk_msspll_recalc_rate(struct clk_hw *hw, unsigned long prate) { struct mpfs_msspll_hw_clock *msspll_hw = to_mpfs_msspll_clk(hw); void __iomem *mult_addr = msspll_hw->base + msspll_hw->reg_offset; void __iomem *ref_div_addr = msspll_hw->base + REG_MSSPLL_REF_CR; u32 mult, ref_div; - unsigned long rate_before_ctrl; - - mult = readl_relaxed(mult_addr) >> MSSPLL_FBDIV_SHIFT; - mult &= clk_div_mask(MSSPLL_FBDIV_WIDTH); - ref_div = readl_relaxed(ref_div_addr) >> MSSPLL_REFDIV_SHIFT; - ref_div &= clk_div_mask(MSSPLL_REFDIV_WIDTH); - - rate_before_ctrl = rate * (ref_div * MSSPLL_FIXED_DIV) / mult; - - return divider_round_rate(hw, rate_before_ctrl, prate, NULL, MSSPLL_POSTDIV_WIDTH, - msspll_hw->flags); -} - -static int mpfs_clk_msspll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long prate) -{ - struct mpfs_msspll_hw_clock *msspll_hw = to_mpfs_msspll_clk(hw); - void __iomem *mult_addr = msspll_hw->base + msspll_hw->reg_offset; - void __iomem *ref_div_addr = msspll_hw->base + REG_MSSPLL_REF_CR; - void __iomem *postdiv_addr = msspll_hw->base + REG_MSSPLL_POSTDIV_CR; - u32 mult, ref_div, postdiv; - int divider_setting; - unsigned long rate_before_ctrl, flags; mult = readl_relaxed(mult_addr) >> MSSPLL_FBDIV_SHIFT; mult &= clk_div_mask(MSSPLL_FBDIV_WIDTH); ref_div = readl_relaxed(ref_div_addr) >> MSSPLL_REFDIV_SHIFT; ref_div &= clk_div_mask(MSSPLL_REFDIV_WIDTH); - rate_before_ctrl = rate * (ref_div * MSSPLL_FIXED_DIV) / mult; - divider_setting = divider_get_val(rate_before_ctrl, prate, NULL, MSSPLL_POSTDIV_WIDTH, - msspll_hw->flags); - - if (divider_setting < 0) - return divider_setting; - - spin_lock_irqsave(&mpfs_clk_lock, flags); - - postdiv = readl_relaxed(postdiv_addr); - postdiv &= ~(clk_div_mask(MSSPLL_POSTDIV_WIDTH) << MSSPLL_POSTDIV_SHIFT); - writel_relaxed(postdiv, postdiv_addr); - - spin_unlock_irqrestore(&mpfs_clk_lock, flags); - - return 0; + return prate * mult / (ref_div * MSSPLL_FIXED_DIV); } static const struct clk_ops mpfs_clk_msspll_ops = { .recalc_rate = mpfs_clk_msspll_recalc_rate, - .round_rate = mpfs_clk_msspll_round_rate, - .set_rate = mpfs_clk_msspll_set_rate, }; #define CLK_PLL(_id, _name, _parent, _shift, _width, _flags, _offset) { \ .id = _id, \ + .flags = _flags, \ .shift = _shift, \ .width = _width, \ .reg_offset = _offset, \ - .flags = _flags, \ .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, _parent, &mpfs_clk_msspll_ops, 0), \ } static struct mpfs_msspll_hw_clock mpfs_msspll_clks[] = { - CLK_PLL(CLK_MSSPLL, "clk_msspll", mpfs_ext_ref, MSSPLL_FBDIV_SHIFT, + CLK_PLL(CLK_MSSPLL_INTERNAL, "clk_msspll_internal", mpfs_ext_ref, MSSPLL_FBDIV_SHIFT, MSSPLL_FBDIV_WIDTH, 0, REG_MSSPLL_SSCG_2_CR), }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:175 @ ret = devm_clk_hw_register(dev, &msspll_hw->hw); if (ret) return dev_err_probe(dev, ret, "failed to register msspll id: %d\n", - CLK_MSSPLL); + CLK_MSSPLL_INTERNAL); data->hw_data.hws[msspll_hw->id] = &msspll_hw->hw; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:184 @ } /* + * MSS PLL output clocks + */ + +#define CLK_PLL_OUT(_id, _name, _parent, _flags, _shift, _width, _offset) { \ + .id = _id, \ + .output.shift = _shift, \ + .output.width = _width, \ + .output.table = NULL, \ + .reg_offset = _offset, \ + .output.flags = _flags, \ + .output.hw.init = CLK_HW_INIT(_name, _parent, &clk_divider_ops, 0), \ + .output.lock = &mpfs_clk_lock, \ +} + +static struct mpfs_msspll_out_hw_clock mpfs_msspll_out_clks[] = { + CLK_PLL_OUT(CLK_MSSPLL0, "clk_msspll", "clk_msspll_internal", CLK_DIVIDER_ONE_BASED, + MSSPLL_POSTDIV02_SHIFT, MSSPLL_POSTDIV_WIDTH, REG_MSSPLL_POSTDIV01_CR), + CLK_PLL_OUT(CLK_MSSPLL1, "clk_msspll1", "clk_msspll_internal", CLK_DIVIDER_ONE_BASED, + MSSPLL_POSTDIV13_SHIFT, MSSPLL_POSTDIV_WIDTH, REG_MSSPLL_POSTDIV01_CR), + CLK_PLL_OUT(CLK_MSSPLL2, "clk_msspll2", "clk_msspll_internal", CLK_DIVIDER_ONE_BASED, + MSSPLL_POSTDIV02_SHIFT, MSSPLL_POSTDIV_WIDTH, REG_MSSPLL_POSTDIV23_CR), + CLK_PLL_OUT(CLK_MSSPLL3, "clk_msspll3", "clk_msspll_internal", CLK_DIVIDER_ONE_BASED, + MSSPLL_POSTDIV13_SHIFT, MSSPLL_POSTDIV_WIDTH, REG_MSSPLL_POSTDIV23_CR), +}; + +static int mpfs_clk_register_msspll_outs(struct device *dev, + struct mpfs_msspll_out_hw_clock *msspll_out_hws, + unsigned int num_clks, struct mpfs_clock_data *data) +{ + unsigned int i; + int ret; + + for (i = 0; i < num_clks; i++) { + struct mpfs_msspll_out_hw_clock *msspll_out_hw = &msspll_out_hws[i]; + + msspll_out_hw->output.reg = data->msspll_base + msspll_out_hw->reg_offset; + ret = devm_clk_hw_register(dev, &msspll_out_hw->output.hw); + if (ret) + return dev_err_probe(dev, ret, "failed to register msspll out id: %d\n", + msspll_out_hw->id); + + data->hw_data.hws[msspll_out_hw->id] = &msspll_out_hw->output.hw; + } + + return 0; +} + +/* * "CFG" clocks */ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:240 @ .cfg.shift = _shift, \ .cfg.width = _width, \ .cfg.table = _table, \ - .reg_offset = _offset, \ + .cfg.offset = _offset, \ .cfg.flags = _flags, \ - .cfg.hw.init = CLK_HW_INIT(_name, _parent, &clk_divider_ops, 0), \ - .cfg.lock = &mpfs_clk_lock, \ + .sigh.hw.init = CLK_HW_INIT(_name, _parent, &clk_regmap_divider_ops, 0), \ } #define CLK_CPU_OFFSET 0u @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:262 @ .cfg.shift = 0, .cfg.width = 12, .cfg.table = mpfs_div_rtcref_table, - .reg_offset = REG_RTC_CLOCK_CR, + .cfg.offset = REG_RTC_CLOCK_CR, .cfg.flags = CLK_DIVIDER_ONE_BASED, - .cfg.hw.init = - CLK_HW_INIT_PARENTS_DATA("clk_rtcref", mpfs_ext_ref, &clk_divider_ops, 0), + .sigh.hw.init = + CLK_HW_INIT_PARENTS_DATA("clk_rtcref", mpfs_ext_ref, &clk_regmap_divider_ops, 0), } }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:278 @ for (i = 0; i < num_clks; i++) { struct mpfs_cfg_hw_clock *cfg_hw = &cfg_hws[i]; - cfg_hw->cfg.reg = data->base + cfg_hw->reg_offset; - ret = devm_clk_hw_register(dev, &cfg_hw->cfg.hw); + cfg_hw->sigh.map = data->regmap; + cfg_hw->sigh.data = &cfg_hw->cfg; + + ret = devm_clk_hw_register(dev, &cfg_hw->sigh.hw); if (ret) return dev_err_probe(dev, ret, "failed to register clock id: %d\n", cfg_hw->id); id = cfg_hw->id; - data->hw_data.hws[id] = &cfg_hw->cfg.hw; + data->hw_data.hws[id] = &cfg_hw->sigh.hw; } return 0; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:299 @ #define CLK_PERIPH(_id, _name, _parent, _shift, _flags) { \ .id = _id, \ + .periph.offset = REG_SUBBLK_CLOCK_CR, \ .periph.bit_idx = _shift, \ - .periph.hw.init = CLK_HW_INIT_HW(_name, _parent, &clk_gate_ops, \ - _flags), \ - .periph.lock = &mpfs_clk_lock, \ + .sigh.hw.init = CLK_HW_INIT_HW(_name, _parent, &clk_regmap_gate_ops, \ + _flags), \ } -#define PARENT_CLK(PARENT) (&mpfs_cfg_clks[CLK_##PARENT##_OFFSET].cfg.hw) +#define PARENT_CLK(PARENT) (&mpfs_cfg_clks[CLK_##PARENT##_OFFSET].sigh.hw) /* * Critical clocks: @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:362 @ for (i = 0; i < num_clks; i++) { struct mpfs_periph_hw_clock *periph_hw = &periph_hws[i]; - periph_hw->periph.reg = data->base + REG_SUBBLK_CLOCK_CR; - ret = devm_clk_hw_register(dev, &periph_hw->periph.hw); + periph_hw->sigh.map = data->regmap; + periph_hw->sigh.data = &periph_hw->periph; + ret = devm_clk_hw_register(dev, &periph_hw->sigh.hw); if (ret) return dev_err_probe(dev, ret, "failed to register clock id: %d\n", periph_hw->id); id = periph_hws[i].id; - data->hw_data.hws[id] = &periph_hw->periph.hw; + data->hw_data.hws[id] = &periph_hw->sigh.hw; } return 0; } -/* - * Peripheral clock resets - */ - -#if IS_ENABLED(CONFIG_RESET_CONTROLLER) - -u32 mpfs_reset_read(struct device *dev) -{ - struct mpfs_clock_data *clock_data = dev_get_drvdata(dev->parent); - - return readl_relaxed(clock_data->base + REG_SUBBLK_RESET_CR); -} -EXPORT_SYMBOL_NS_GPL(mpfs_reset_read, MCHP_CLK_MPFS); - -void mpfs_reset_write(struct device *dev, u32 val) -{ - struct mpfs_clock_data *clock_data = dev_get_drvdata(dev->parent); - - writel_relaxed(val, clock_data->base + REG_SUBBLK_RESET_CR); -} -EXPORT_SYMBOL_NS_GPL(mpfs_reset_write, MCHP_CLK_MPFS); - -static void mpfs_reset_unregister_adev(void *_adev) -{ - struct auxiliary_device *adev = _adev; - - auxiliary_device_delete(adev); - auxiliary_device_uninit(adev); -} - -static void mpfs_reset_adev_release(struct device *dev) -{ - struct auxiliary_device *adev = to_auxiliary_dev(dev); - - kfree(adev); -} - -static struct auxiliary_device *mpfs_reset_adev_alloc(struct mpfs_clock_data *clk_data) -{ - struct auxiliary_device *adev; - int ret; - - adev = kzalloc(sizeof(*adev), GFP_KERNEL); - if (!adev) - return ERR_PTR(-ENOMEM); - - adev->name = "reset-mpfs"; - adev->dev.parent = clk_data->dev; - adev->dev.release = mpfs_reset_adev_release; - adev->id = 666u; - - ret = auxiliary_device_init(adev); - if (ret) { - kfree(adev); - return ERR_PTR(ret); - } - - return adev; -} - -static int mpfs_reset_controller_register(struct mpfs_clock_data *clk_data) -{ - struct auxiliary_device *adev; - int ret; - - adev = mpfs_reset_adev_alloc(clk_data); - if (IS_ERR(adev)) - return PTR_ERR(adev); - - ret = auxiliary_device_add(adev); - if (ret) { - auxiliary_device_uninit(adev); - return ret; - } - - return devm_add_action_or_reset(clk_data->dev, mpfs_reset_unregister_adev, adev); -} - -#else /* !CONFIG_RESET_CONTROLLER */ - -static int mpfs_reset_controller_register(struct mpfs_clock_data *clk_data) -{ - return 0; -} - -#endif /* !CONFIG_RESET_CONTROLLER */ - static int mpfs_clk_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:384 @ int ret; /* CLK_RESERVED is not part of clock arrays, so add 1 */ - num_clks = ARRAY_SIZE(mpfs_msspll_clks) + ARRAY_SIZE(mpfs_cfg_clks) - + ARRAY_SIZE(mpfs_periph_clks) + 1; + num_clks = ARRAY_SIZE(mpfs_msspll_clks) + ARRAY_SIZE(mpfs_msspll_out_clks) + + ARRAY_SIZE(mpfs_cfg_clks) + ARRAY_SIZE(mpfs_periph_clks) + 1; clk_data = devm_kzalloc(dev, struct_size(clk_data, hw_data.hws, num_clks), GFP_KERNEL); if (!clk_data) return -ENOMEM; + clk_data->regmap = syscon_regmap_lookup_by_compatible("microchip,mpfs-mss-top-sysreg"); + if (IS_ERR(clk_data->regmap)) { + clk_data->regmap = NULL; + goto old_format; + } + + clk_data->msspll_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(clk_data->msspll_base)) + return PTR_ERR(clk_data->msspll_base); + + goto done; + +old_format: clk_data->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(clk_data->base)) return PTR_ERR(clk_data->base); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:412 @ if (IS_ERR(clk_data->msspll_base)) return PTR_ERR(clk_data->msspll_base); + clk_data->regmap = devm_regmap_init_mmio(dev, clk_data->base, &clk_mpfs_regmap_config); + if (IS_ERR(clk_data->regmap)) + return PTR_ERR(clk_data->regmap); + + ret = mpfs_reset_controller_register(dev, clk_data->base + REG_SUBBLK_RESET_CR); + if (ret) + return ret; +done: clk_data->hw_data.num = num_clks; clk_data->dev = dev; dev_set_drvdata(dev, clk_data); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:429 @ if (ret) return ret; - ret = mpfs_clk_register_cfgs(dev, mpfs_cfg_clks, ARRAY_SIZE(mpfs_cfg_clks), clk_data); + ret = mpfs_clk_register_msspll_outs(dev, mpfs_msspll_out_clks, + ARRAY_SIZE(mpfs_msspll_out_clks), + clk_data); if (ret) return ret; - ret = mpfs_clk_register_periphs(dev, mpfs_periph_clks, ARRAY_SIZE(mpfs_periph_clks), - clk_data); + ret = mpfs_clk_register_cfgs(dev, mpfs_cfg_clks, ARRAY_SIZE(mpfs_cfg_clks), clk_data); if (ret) return ret; - ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, &clk_data->hw_data); + ret = mpfs_clk_register_periphs(dev, mpfs_periph_clks, ARRAY_SIZE(mpfs_periph_clks), + clk_data); if (ret) return ret; - return mpfs_reset_controller_register(clk_data); + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, &clk_data->hw_data); } static const struct of_device_id mpfs_clk_of_match_table[] = { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:477 @ MODULE_AUTHOR("Padmarao Begari <padmarao.begari@microchip.com>"); MODULE_AUTHOR("Daire McNamara <daire.mcnamara@microchip.com>"); MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>"); +MODULE_IMPORT_NS(MCHP_CLK_MPFS); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clk/microchip/Kconfig linux-microchip/drivers/clk/microchip/Kconfig --- linux-6.6.51/drivers/clk/microchip/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clk/microchip/Kconfig 2024-11-26 14:02:36.366473419 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:9 @ config MCHP_CLK_MPFS bool "Clk driver for PolarFire SoC" depends on ARCH_MICROCHIP_POLARFIRE || COMPILE_TEST + depends on MFD_SYSCON default ARCH_MICROCHIP_POLARFIRE select AUXILIARY_BUS + select COMMON_CLK_REGMAP + select REGMAP_MMIO help Supports Clock Configuration for PolarFire SoC diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/clocksource/Kconfig linux-microchip/drivers/clocksource/Kconfig --- linux-6.6.51/drivers/clocksource/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/clocksource/Kconfig 2024-11-26 14:02:36.398473992 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:719 @ Support for the Operating System Timer of the Ingenic JZ SoCs. config MICROCHIP_PIT64B - bool "Microchip PIT64B support" + bool "Microchip PIT64B support" if COMPILE_TEST depends on OF && ARM select TIMER_OF help diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/crypto/atmel-aes.c linux-microchip/drivers/crypto/atmel-aes.c --- linux-6.6.51/drivers/crypto/atmel-aes.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/crypto/atmel-aes.c 2024-11-26 14:02:36.422474423 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:87 @ #define ATMEL_AES_DMA_THRESHOLD 256 - +/** + * struct atmel_aes_caps: at91 aes capabilities + * @has_dualbuff: dual buffer support + * @has_cfb64: cfb64 support + * @has_gcm: gcm support + * @has_xts: xts support + * @has_authenc: authentication support + * @max_burst_size: max DMA bust size + * @has_6words_limitation: some versions of IP have a 6 word headder limitation + */ struct atmel_aes_caps { bool has_dualbuff; bool has_cfb64; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:104 @ bool has_xts; bool has_authenc; u32 max_burst_size; + bool has_6words_limitation; }; struct atmel_aes_dev; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:163 @ struct atmel_aes_authenc_ctx { struct atmel_aes_base_ctx base; struct atmel_sha_authenc_ctx *auth; + + struct crypto_aead *fallback; }; #endif @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2135 @ memcpy(ctx->base.key, keys.enckey, keys.enckeylen); memzero_explicit(&keys, sizeof(keys)); + + if (ctx->base.dd->caps.has_6words_limitation) { + crypto_aead_clear_flags(ctx->fallback, CRYPTO_TFM_REQ_MASK); + crypto_aead_set_flags(ctx->fallback, + crypto_aead_get_flags(tfm) & + CRYPTO_TFM_REQ_MASK); + crypto_aead_setkey(ctx->fallback, key, keylen); + } return 0; badkey: @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2155 @ { struct atmel_aes_authenc_ctx *ctx = crypto_aead_ctx(tfm); unsigned int auth_reqsize = atmel_sha_authenc_get_reqsize(); + struct aead_alg *alg = crypto_aead_alg(tfm); struct atmel_aes_dev *dd; dd = atmel_aes_dev_alloc(&ctx->base); if (!dd) return -ENODEV; + if (dd->caps.has_6words_limitation) { + ctx->fallback = crypto_alloc_aead(alg->base.cra_name, 0, + CRYPTO_ALG_NEED_FALLBACK | CRYPTO_ALG_ASYNC); + if (IS_ERR(ctx->fallback)) { + dev_err(dd->dev, "fallback driver fail %s\n", alg->base.cra_name); + return PTR_ERR(ctx->fallback); + } + } ctx->auth = atmel_sha_authenc_spawn(auth_mode); - if (IS_ERR(ctx->auth)) + if (IS_ERR(ctx->auth)) { + if (dd->caps.has_6words_limitation) + crypto_free_aead(ctx->fallback); return PTR_ERR(ctx->auth); + } crypto_aead_set_reqsize(tfm, (sizeof(struct atmel_aes_authenc_reqctx) + auth_reqsize)); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2215 @ struct atmel_aes_authenc_ctx *ctx = crypto_aead_ctx(tfm); atmel_sha_authenc_free(ctx->auth); + if (ctx->base.dd->caps.has_6words_limitation) + crypto_free_aead(ctx->fallback); } static int atmel_aes_authenc_crypt(struct aead_request *req, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2225 @ struct atmel_aes_authenc_reqctx *rctx = aead_request_ctx(req); struct crypto_aead *tfm = crypto_aead_reqtfm(req); struct atmel_aes_base_ctx *ctx = crypto_aead_ctx(tfm); + struct atmel_aes_authenc_ctx *actx = crypto_aead_ctx(tfm); u32 authsize = crypto_aead_authsize(tfm); bool enc = (mode & AES_FLAGS_ENCRYPT); + bool limitation = ctx->dd->caps.has_6words_limitation; /* Compute text length. */ if (!enc && req->cryptlen < authsize) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2242 @ */ if (!rctx->textlen && !req->assoclen) return -EINVAL; + /* + *Check if data size triggers the HW limitation and + * run fallback functions + */ + if ((req->assoclen != 16) && limitation) { + struct aead_request *subreq = aead_request_ctx(req); + + aead_request_set_tfm(subreq, actx->fallback); + aead_request_set_callback(subreq, req->base.flags, + req->base.complete, req->base.data); + aead_request_set_crypt(subreq, req->src, req->dst, + req->cryptlen, req->iv); + aead_request_set_ad(subreq, req->assoclen); + + if (enc) + return crypto_aead_encrypt(subreq); + else + return crypto_aead_decrypt(subreq); + } rctx->base.mode = mode; ctx->block_size = AES_BLOCK_SIZE; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2558 @ dd->caps.has_xts = 0; dd->caps.has_authenc = 0; dd->caps.max_burst_size = 1; + dd->caps.has_6words_limitation = 0; /* keep only major version number */ switch (dd->hw_version & 0xff0) { case 0x700: case 0x600: + dd->caps.has_6words_limitation = 1; + fallthrough; + case 0x800: case 0x500: dd->caps.has_dualbuff = 1; dd->caps.has_cfb64 = 1; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/crypto/atmel-sha.c linux-microchip/drivers/crypto/atmel-sha.c --- linux-6.6.51/drivers/crypto/atmel-sha.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/crypto/atmel-sha.c 2024-11-26 14:02:36.422474423 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2537 @ /* keep only major version number */ switch (dd->hw_version & 0xff0) { + case 0x800: case 0x700: case 0x600: case 0x510: diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/crypto/Kconfig linux-microchip/drivers/crypto/Kconfig --- linux-6.6.51/drivers/crypto/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/crypto/Kconfig 2024-11-26 14:02:36.422474423 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:798 @ acceleration for cryptographic algorithms on these devices. source "drivers/crypto/aspeed/Kconfig" +source "drivers/crypto/microchip/Kconfig" source "drivers/crypto/starfive/Kconfig" endif # CRYPTO_HW diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/crypto/Makefile linux-microchip/drivers/crypto/Makefile --- linux-6.6.51/drivers/crypto/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/crypto/Makefile 2024-11-26 14:02:36.422474423 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:54 @ obj-$(CONFIG_CRYPTO_DEV_AMLOGIC_GXL) += amlogic/ obj-y += intel/ obj-y += starfive/ +obj-y += microchip/ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/crypto/microchip/Kconfig linux-microchip/drivers/crypto/microchip/Kconfig --- linux-6.6.51/drivers/crypto/microchip/Kconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/crypto/microchip/Kconfig 2024-11-26 14:02:36.442474781 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# +# Microchip Crypto drivers configuration +# + +config CRYPTO_DEV_POLARFIRE_SOC + tristate "Microchip PolarFire SoC User cryptographic engine driver" + depends on RISCV_SBI + depends on SOC_MICROCHIP_POLARFIRE || COMPILE_TEST + select CRYPTO_ENGINE + select CRYPTO_AES + select CRYPTO_AEAD + select CRYPTO_AKCIPHER + select CRYPTO_KPP + select CRYPTO_SKCIPHER + default y + help + Support for Microchip PolarFire SoC User crypto engine. + This module provides acceleration for skcipher functions. + + If you choose 'M' here, this module will be called microchip-crypto. + + If unsure, say N. diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/crypto/microchip/Makefile linux-microchip/drivers/crypto/microchip/Makefile --- linux-6.6.51/drivers/crypto/microchip/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/crypto/microchip/Makefile 2024-11-26 14:02:36.442474781 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1 @ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_CRYPTO_DEV_POLARFIRE_SOC) += mchp-crypto.o +mchp-crypto-objs := mchp-crypto-sbi.o mchp-crypto-sbi-aes.o diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/crypto/microchip/mchp-crypto-sbi-aes.c linux-microchip/drivers/crypto/microchip/mchp-crypto-sbi-aes.c --- linux-6.6.51/drivers/crypto/microchip/mchp-crypto-sbi-aes.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/crypto/microchip/mchp-crypto-sbi-aes.c 2024-11-26 14:02:36.442474781 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microchip PolarFire SoC (MPFS) User Crypto driver + * + * Copyright (c) 2023 Microchip Corporation. All rights reserved. + * + * Author: Padmarao Begari <padmarao.begari@microchip.com> + * + */ + +#include <crypto/engine.h> +#include <crypto/internal/skcipher.h> +#include <crypto/scatterwalk.h> +#include <linux/crypto.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <asm/sbi.h> +#include "mchp-crypto-sbi.h" + +#define MCHP_AES_DIR_DECRYPT 0x00 +#define MCHP_AES_DIR_ENCRYPT 0x01 + +#define MCHP_AES_MODE_ECB 0x0100 +#define MCHP_AES_MODE_CBC 0x0200 +#define MCHP_AES_MODE_CFB 0x0300 +#define MCHP_AES_MODE_OFB 0x0400 +#define MCHP_AES_MODE_CTR 0x0500 +#define MCHP_AES_MODE_GCM 0x0600 +#define MCHP_AES_MODE_CCM 0x0700 +#define MCHP_AES_MODE_GHASH 0x0800 +#define MCHP_AES_MODE_MASK 0x0F00 + +#define MCHP_AES_TYPE_128 0x010000 +#define MCHP_AES_TYPE_192 0x020000 +#define MCHP_AES_TYPE_256 0x030000 + +struct mchp_crypto_aes_algo { + u64 algonum; + struct skcipher_engine_alg algo; +}; + +struct mchp_crypto_aes_req { + u64 src; + u64 iv; + u64 key; + u64 dst; + u64 size; +}; + +static int mchp_aes_do_one_req(struct crypto_engine *engine, void *areq) +{ + struct skcipher_request *req = + container_of(areq, struct skcipher_request, base); + struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); + struct mchp_crypto_ctx *ctx = crypto_skcipher_ctx(tfm); + struct device *dev = ctx->cryp->dev; + struct mchp_crypto_aes_req *aes_req; + dma_addr_t dma_addr_data, dma_addr_aes_req; + unsigned int iv_size; + unsigned int data_size; + size_t dma_size; + char *kbuf; + int ret; + + switch (ctx->keylen) { + case AES_KEYSIZE_128: + ctx->flags |= MCHP_AES_TYPE_128; + break; + case AES_KEYSIZE_192: + ctx->flags |= MCHP_AES_TYPE_192; + break; + case AES_KEYSIZE_256: + ctx->flags |= MCHP_AES_TYPE_256; + break; + default: + return -EINVAL; + } + + iv_size = crypto_skcipher_ivsize(tfm); + + dma_size = req->cryptlen + ctx->keylen + iv_size; + + kbuf = dma_alloc_coherent(dev, dma_size, &dma_addr_data, GFP_KERNEL); + if (!kbuf) + return -ENOMEM; + + aes_req = dma_alloc_coherent(dev, sizeof(struct mchp_crypto_aes_req), + &dma_addr_aes_req, GFP_KERNEL); + if (!aes_req) { + dma_free_coherent(dev, dma_size, kbuf, dma_addr_data); + return -ENOMEM; + } + + sg_copy_to_buffer(req->src, sg_nents(req->src), + kbuf, req->cryptlen); + data_size = req->cryptlen; + memcpy(kbuf + data_size, req->iv, iv_size); + memcpy(kbuf + data_size + iv_size, ctx->key, ctx->keylen); + + aes_req->src = dma_addr_data; + aes_req->dst = dma_addr_data; + aes_req->iv = aes_req->src + data_size; + aes_req->size = data_size; + aes_req->key = aes_req->src + data_size + iv_size; + + ret = mchp_crypto_sbi_services(CRYPTO_SERVICE_AES, + dma_addr_aes_req, ctx->flags); + if (!ret) + sg_copy_from_buffer(req->dst, sg_nents(req->dst), + kbuf, data_size); + + memzero_explicit(kbuf, dma_size); + dma_free_coherent(dev, dma_size, kbuf, dma_addr_data); + + memzero_explicit(aes_req, sizeof(struct mchp_crypto_aes_req)); + dma_free_coherent(dev, sizeof(struct mchp_crypto_aes_req), + aes_req, dma_addr_aes_req); + + crypto_finalize_skcipher_request(engine, req, ret); + + return ret; +} + +static int mchp_aes_crypt(struct skcipher_request *req, unsigned long flags) +{ + struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req); + struct mchp_crypto_ctx *ctx = crypto_skcipher_ctx(tfm); + struct mchp_crypto_dev *cryp = ctx->cryp; + unsigned int blocksize_align = crypto_skcipher_blocksize(tfm) - 1; + + ctx->flags = flags; + + if ((ctx->flags & MCHP_AES_MODE_MASK) == MCHP_AES_MODE_ECB || + (ctx->flags & MCHP_AES_MODE_MASK) == MCHP_AES_MODE_CBC) + if (req->cryptlen & blocksize_align) + return -EINVAL; + + return crypto_transfer_skcipher_request_to_engine(cryp->engine, req); +} + +static int mchp_aes_ecb_encrypt(struct skcipher_request *req) +{ + return mchp_aes_crypt(req, MCHP_AES_MODE_ECB | MCHP_AES_DIR_ENCRYPT); +} + +static int mchp_aes_ecb_decrypt(struct skcipher_request *req) +{ + return mchp_aes_crypt(req, MCHP_AES_MODE_ECB); +} + +static int mchp_aes_cbc_encrypt(struct skcipher_request *req) +{ + return mchp_aes_crypt(req, MCHP_AES_MODE_CBC | MCHP_AES_DIR_ENCRYPT); +} + +static int mchp_aes_cbc_decrypt(struct skcipher_request *req) +{ + return mchp_aes_crypt(req, MCHP_AES_MODE_CBC); +} + +static int mchp_aes_cfb_encrypt(struct skcipher_request *req) +{ + return mchp_aes_crypt(req, MCHP_AES_MODE_CFB | MCHP_AES_DIR_ENCRYPT); +} + +static int mchp_aes_cfb_decrypt(struct skcipher_request *req) +{ + return mchp_aes_crypt(req, MCHP_AES_MODE_CFB); +} + +static int mchp_aes_ofb_encrypt(struct skcipher_request *req) +{ + return mchp_aes_crypt(req, MCHP_AES_MODE_OFB | MCHP_AES_DIR_ENCRYPT); +} + +static int mchp_aes_ofb_decrypt(struct skcipher_request *req) +{ + return mchp_aes_crypt(req, MCHP_AES_MODE_OFB); +} + +static int mchp_aes_ctr_encrypt(struct skcipher_request *req) +{ + return mchp_aes_crypt(req, MCHP_AES_MODE_CTR | MCHP_AES_DIR_ENCRYPT); +} + +static int mchp_aes_ctr_decrypt(struct skcipher_request *req) +{ + return mchp_aes_crypt(req, MCHP_AES_MODE_CTR); +} + +static int mchp_aes_setkey(struct crypto_skcipher *tfm, const u8 *key, + unsigned int keylen) +{ + struct mchp_crypto_ctx *ctx = crypto_skcipher_ctx(tfm); + + if (!key || !keylen) + return -EINVAL; + + if (keylen != AES_KEYSIZE_256 && + keylen != AES_KEYSIZE_192 && + keylen != AES_KEYSIZE_128) + return -EINVAL; + + memcpy(ctx->key, key, keylen); + ctx->keylen = keylen; + + return 0; +} + +static int mchp_aes_init_tfm(struct crypto_skcipher *tfm) +{ + struct mchp_crypto_ctx *ctx = crypto_skcipher_ctx(tfm); + + ctx->cryp = mchp_crypto_find_dev(ctx); + if (!ctx->cryp) + return -ENODEV; + + crypto_skcipher_set_reqsize(tfm, sizeof(struct mchp_crypto_ctx) + + sizeof(struct skcipher_request)); + + return 0; +} + +static struct mchp_crypto_aes_algo mchp_aes_algs[] = { +{ + .algonum = CRYPTO_ALG_AES_ECB, + .algo.base = { + .base.cra_name = "ecb(aes)", + .base.cra_driver_name = "microchip-ecb-aes", + .base.cra_priority = 300, + .base.cra_flags = CRYPTO_ALG_ASYNC, + .base.cra_blocksize = AES_BLOCK_SIZE, + .base.cra_ctxsize = sizeof(struct mchp_crypto_ctx), + .base.cra_alignmask = 0xf, + .base.cra_module = THIS_MODULE, + + .init = mchp_aes_init_tfm, + .setkey = mchp_aes_setkey, + .encrypt = mchp_aes_ecb_encrypt, + .decrypt = mchp_aes_ecb_decrypt, + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + }, + .algo.op = { + .do_one_request = mchp_aes_do_one_req + }, +}, { + .algonum = CRYPTO_ALG_AES_CBC, + .algo.base = { + .base.cra_name = "cbc(aes)", + .base.cra_driver_name = "microchip-cbc-aes", + .base.cra_priority = 300, + .base.cra_flags = CRYPTO_ALG_ASYNC, + .base.cra_blocksize = AES_BLOCK_SIZE, + .base.cra_ctxsize = sizeof(struct mchp_crypto_ctx), + .base.cra_alignmask = 0xf, + .base.cra_module = THIS_MODULE, + + .init = mchp_aes_init_tfm, + .setkey = mchp_aes_setkey, + .encrypt = mchp_aes_cbc_encrypt, + .decrypt = mchp_aes_cbc_decrypt, + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + }, + .algo.op = { + .do_one_request = mchp_aes_do_one_req + }, +}, { + .algonum = CRYPTO_ALG_AES_OFB, + .algo.base = { + .base.cra_name = "ofb(aes)", + .base.cra_driver_name = "microchip-ofb-aes", + .base.cra_priority = 300, + .base.cra_flags = CRYPTO_ALG_ASYNC, + .base.cra_blocksize = 1, + .base.cra_ctxsize = sizeof(struct mchp_crypto_ctx), + .base.cra_alignmask = 0xf, + .base.cra_module = THIS_MODULE, + + .init = mchp_aes_init_tfm, + .setkey = mchp_aes_setkey, + .encrypt = mchp_aes_ofb_encrypt, + .decrypt = mchp_aes_ofb_decrypt, + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + }, + .algo.op = { + .do_one_request = mchp_aes_do_one_req + }, +}, { + .algonum = CRYPTO_ALG_AES_CFB, + .algo.base = { + .base.cra_name = "cfb(aes)", + .base.cra_driver_name = "microchip-cfb-aes", + .base.cra_priority = 300, + .base.cra_flags = CRYPTO_ALG_ASYNC, + .base.cra_blocksize = 1, + .base.cra_ctxsize = sizeof(struct mchp_crypto_ctx), + .base.cra_alignmask = 0xf, + .base.cra_module = THIS_MODULE, + + .init = mchp_aes_init_tfm, + .setkey = mchp_aes_setkey, + .encrypt = mchp_aes_cfb_encrypt, + .decrypt = mchp_aes_cfb_decrypt, + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + }, + .algo.op = { + .do_one_request = mchp_aes_do_one_req + }, +}, { + .algonum = CRYPTO_ALG_AES_CTR, + .algo.base = { + .base.cra_name = "ctr(aes)", + .base.cra_driver_name = "microchip-ctr-aes", + .base.cra_priority = 300, + .base.cra_flags = CRYPTO_ALG_ASYNC, + .base.cra_blocksize = 1, + .base.cra_ctxsize = sizeof(struct mchp_crypto_ctx), + .base.cra_alignmask = 0xf, + .base.cra_module = THIS_MODULE, + + .init = mchp_aes_init_tfm, + .setkey = mchp_aes_setkey, + .encrypt = mchp_aes_ctr_encrypt, + .decrypt = mchp_aes_ctr_decrypt, + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + }, + .algo.op = { + .do_one_request = mchp_aes_do_one_req + }, +}, +}; + +int mchp_aes_register_algs(struct mchp_crypto_dev *cryp) +{ + for (int i = 0; i < ARRAY_SIZE(mchp_aes_algs); i++) { + u64 algonum = mchp_aes_algs[i].algonum; + int ret; + + if (!(algonum & cryp->crypto->cipher_algo)) + continue; + + ret = crypto_engine_register_skcipher(&mchp_aes_algs[i].algo); + if (ret) + return ret; + } + + return 0; +} + +void mchp_aes_unregister_algs(struct mchp_crypto_dev *cryp) +{ + for (int i = 0; i < ARRAY_SIZE(mchp_aes_algs); i++) { + u64 algonum = mchp_aes_algs[i].algonum; + + if (!(algonum & cryp->crypto->cipher_algo)) + continue; + + crypto_engine_unregister_skcipher(&mchp_aes_algs[i].algo); + } +} diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/crypto/microchip/mchp-crypto-sbi.c linux-microchip/drivers/crypto/microchip/mchp-crypto-sbi.c --- linux-6.6.51/drivers/crypto/microchip/mchp-crypto-sbi.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/crypto/microchip/mchp-crypto-sbi.c 2024-11-26 14:02:36.442474781 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microchip PolarFire SoC (MPFS) User Crypto driver + * + * Copyright (c) 2023 Microchip Corporation. All rights reserved. + * + * Author: Padmarao Begari <padmarao.begari@microchip.com> + * + */ + +#include <crypto/engine.h> +#include <linux/crypto.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <asm/sbi.h> +#include "mchp-crypto-sbi.h" + +#define MPFS_CRYPTO_DMA_BIT_MASK 32 + +static DEFINE_SPINLOCK(cryto_service_lock); + +static dma_addr_t dma_addr_crypto; +static struct mchp_crypto_info *crypto_info; + +struct mchp_dev_list { + struct list_head dev_list; + spinlock_t lock; /* protect dev_list */ +}; + +static struct mchp_dev_list dev_list = { + .dev_list = LIST_HEAD_INIT(dev_list.dev_list), + .lock = __SPIN_LOCK_UNLOCKED(dev_list.lock), +}; + +struct mchp_crypto_dev *mchp_crypto_find_dev(struct mchp_crypto_ctx *ctx) +{ + struct mchp_crypto_dev *cryp = NULL, *tmp; + + spin_lock(&dev_list.lock); + if (!ctx->cryp) { + list_for_each_entry(tmp, &dev_list.dev_list, list) { + cryp = tmp; + break; + } + ctx->cryp = cryp; + } else { + cryp = ctx->cryp; + } + + spin_unlock(&dev_list.lock); + + return cryp; +} + +int mchp_crypto_sbi_services(u32 service, u64 crypto_addr, u32 flags) +{ + struct sbiret ret; + + spin_lock(&cryto_service_lock); + ret = sbi_ecall(SBI_EXT_MICROCHIP_TECHNOLOGY, + MICROCHIP_SBI_EXT_CRYPTO_SERVICES, + service, crypto_addr, flags, 0, 0, 0); + spin_unlock(&cryto_service_lock); + if (ret.error) + return sbi_err_map_linux_errno(ret.error); + else + return ret.value; +} + +static int mchp_crypto_sbi_sevices_probe(u64 crypto_addr) +{ + struct sbiret ret; + + ret = sbi_ecall(SBI_EXT_MICROCHIP_TECHNOLOGY, + MICROCHIP_SBI_EXT_CRYPTO_SERVICES_PROBE, + crypto_addr, 0, 0, 0, 0, 0); + if (ret.error) + return sbi_err_map_linux_errno(ret.error); + else + return ret.value; +} + +static int mchp_crypto_sbi_init(bool enable) +{ + struct sbiret ret; + + ret = sbi_ecall(SBI_EXT_MICROCHIP_TECHNOLOGY, + MICROCHIP_SBI_EXT_CRYPTO_INIT, + enable, 0, 0, 0, 0, 0); + if (ret.error) + return sbi_err_map_linux_errno(ret.error); + else + return ret.value; +} + +static inline int mchp_crypto_sbi_startup(void) +{ + return mchp_crypto_sbi_init(true); +} + +static inline int mchp_crypto_sbi_shutdown(void) +{ + return mchp_crypto_sbi_init(false); +} + +static int mchp_crypto_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mchp_crypto_dev *cryp; + int ret; + + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(MPFS_CRYPTO_DMA_BIT_MASK)); + if (ret < 0) + return dev_err_probe(dev, ret, "No usable DMA configuration\n"); + + cryp = devm_kzalloc(dev, sizeof(*cryp), GFP_KERNEL); + if (!cryp) + return -ENOMEM; + + platform_set_drvdata(pdev, cryp); + + cryp->dev = dev; + + ret = sbi_probe_extension(SBI_EXT_MICROCHIP_TECHNOLOGY); + if (!ret) + return dev_err_probe(dev, ret, "SBI extension not detected\n"); + + crypto_info = dma_alloc_coherent(dev, sizeof(struct mchp_crypto_info), + &dma_addr_crypto, GFP_KERNEL); + if (!crypto_info) + return -ENOMEM; + + /* Initializes the crypto processor and enable clock. */ + ret = mchp_crypto_sbi_startup(); + if (ret < 0) + goto err_free_info; + + ret = mchp_crypto_sbi_sevices_probe(dma_addr_crypto); + if (ret < 0) + goto err_crypto_shutdown; + + cryp->crypto = crypto_info; + + spin_lock(&dev_list.lock); + list_add(&cryp->list, &dev_list.dev_list); + spin_unlock(&dev_list.lock); + + cryp->engine = crypto_engine_alloc_init(dev, 1); + if (!cryp->engine) { + ret = -ENOMEM; + goto err_list_del; + } + + ret = crypto_engine_start(cryp->engine); + if (ret) + goto err_engine_exit; + + if (cryp->crypto->services & CRYPTO_SERVICE_AES) { + ret = mchp_aes_register_algs(cryp); + if (ret) + goto err_engine_stop; + } + + return 0; + +err_engine_stop: + crypto_engine_stop(cryp->engine); +err_engine_exit: + crypto_engine_exit(cryp->engine); +err_list_del: + spin_lock(&dev_list.lock); + list_del(&cryp->list); + spin_unlock(&dev_list.lock); +err_crypto_shutdown: + mchp_crypto_sbi_shutdown(); +err_free_info: + dma_free_coherent(cryp->dev, sizeof(struct mchp_crypto_info), + crypto_info, dma_addr_crypto); + return ret; +} + +static int mchp_crypto_remove(struct platform_device *pdev) +{ + struct mchp_crypto_dev *cryp = platform_get_drvdata(pdev); + /* Disable crypto clock */ + mchp_crypto_sbi_shutdown(); + + if (cryp->crypto->services & CRYPTO_SERVICE_AES) + mchp_aes_unregister_algs(cryp); + + crypto_engine_stop(cryp->engine); + crypto_engine_exit(cryp->engine); + + spin_lock(&dev_list.lock); + list_del(&cryp->list); + spin_unlock(&dev_list.lock); + + dma_free_coherent(cryp->dev, sizeof(struct mchp_crypto_info), + crypto_info, dma_addr_crypto); + return 0; +} + +static const struct of_device_id mchp_crypto_dt_ids[] = { + { .compatible = "microchip,mpfs-crypto", }, + {} +}; +MODULE_DEVICE_TABLE(of, mchp_crypto_dt_ids); + +static struct platform_driver mchp_crypto_driver = { + .driver = { + .name = "microchip-crypto", + .of_match_table = mchp_crypto_dt_ids, + }, + .probe = mchp_crypto_probe, + .remove = mchp_crypto_remove, +}; +module_platform_driver(mchp_crypto_driver); + +MODULE_AUTHOR("Padmarao Begari <padmarao.begari@microchip.com>"); +MODULE_DESCRIPTION("Microchip PolarFire SoC User Crypto driver"); +MODULE_LICENSE("GPL"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/crypto/microchip/mchp-crypto-sbi.h linux-microchip/drivers/crypto/microchip/mchp-crypto-sbi.h --- linux-6.6.51/drivers/crypto/microchip/mchp-crypto-sbi.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/crypto/microchip/mchp-crypto-sbi.h 2024-11-26 14:02:36.442474781 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microchip PolarFire SoC (MPFS) User Crypto driver + * + * Copyright (c) 2023 Microchip Corporation. All rights reserved. + * + * Author: Padmarao Begari <padmarao.begari@microchip.com> + * + */ + +#include <asm/sbi.h> +#include <asm/vendorid_list.h> +#include <crypto/aes.h> +#include <linux/bits.h> + +#define SBI_EXT_MICROCHIP_TECHNOLOGY (SBI_EXT_VENDOR_START | \ + MICROCHIP_VENDOR_ID) + +#define MICROCHIP_SBI_EXT_CRYPTO_INIT 0x11 +#define MICROCHIP_SBI_EXT_CRYPTO_SERVICES_PROBE 0x12 +#define MICROCHIP_SBI_EXT_CRYPTO_SERVICES 0x13 + +struct mchp_crypto_info { + u32 services; +#define CRYPTO_SERVICE_AES BIT(0) +#define CRYPTO_SERVICE_AEAD BIT(1) +#define CRYPTO_SERVICE_HASH BIT(2) +#define CRYPTO_SERVICE_MAC BIT(3) +#define CRYPTO_SERVICE_RSA BIT(4) +#define CRYPTO_SERVICE_DSA BIT(5) +#define CRYPTO_SERVICE_ECDSA BIT(6) +#define CRYPTO_SERVICE_NRBG BIT(7) + u64 cipher_algo; +#define CRYPTO_ALG_AES_ECB BIT(0) +#define CRYPTO_ALG_AES_CBC BIT(1) +#define CRYPTO_ALG_AES_OFB BIT(2) +#define CRYPTO_ALG_AES_CFB BIT(3) +#define CRYPTO_ALG_AES_CTR BIT(4) + u32 aead_algo; +#define CRYPTO_ALG_AEAD_GCM BIT(0) +#define CRYPTO_ALG_AEAD_CCM BIT(1) + u32 hash_algo; +#define CRYPTO_ALG_HASH_SHA1 BIT(0) +#define CRYPTO_ALG_HASH_SHA224 BIT(1) +#define CRYPTO_ALG_HASH_SHA256 BIT(2) +#define CRYPTO_ALG_HASH_SHA384 BIT(3) +#define CRYPTO_ALG_HASH_SHA512 BIT(4) +#define CRYPTO_ALG_HASH_SHA512_224 BIT(5) +#define CRYPTO_ALG_HASH_SHA512_256 BIT(6) + u64 mac_algo; +#define CRYPTO_ALG_MAC_SHA1 BIT(0) +#define CRYPTO_ALG_MAC_SHA224 BIT(1) +#define CRYPTO_ALG_MAC_SHA256 BIT(2) +#define CRYPTO_ALG_MAC_SHA384 BIT(3) +#define CRYPTO_ALG_MAC_SHA512 BIT(4) +#define CRYPTO_ALG_MAC_SHA512_224 BIT(5) +#define CRYPTO_ALG_MAC_SHA512_256 BIT(6) +#define CRYPTO_ALG_MAC_AESCMAC128 BIT(10) +#define CRYPTO_ALG_MAC_AESCMAC192 BIT(11) +#define CRYPTO_ALG_MAC_AESCMAC256 BIT(12) +#define CRYPTO_ALG_MAC_AESGMAC BIT(13) + u64 akcipher_algo; +#define CRYPTO_ALG_DSA BIT(0) +#define CRYPTO_ALG_RSA BIT(1) +#define CRYPTO_ALG_DH BIT(2) +#define CRYPTO_ALG_ECDSA BIT(3) +#define CRYPTO_ALG_ECDH BIT(4) + u32 nrbg_algo; +}; + +struct mchp_crypto_ctx { + struct mchp_crypto_dev *cryp; + u8 key[AES_MAX_KEY_SIZE]; + int keylen; + unsigned long flags; +}; + +struct mchp_crypto_dev { + struct list_head list; + struct device *dev; + struct crypto_engine *engine; + struct mchp_crypto_info *crypto; +}; + +struct mchp_crypto_dev *mchp_crypto_find_dev(struct mchp_crypto_ctx *ctx); + +int mchp_crypto_sbi_services(u32 service, u64 crypto_addr, u32 flags); + +int mchp_aes_register_algs(struct mchp_crypto_dev *cryp); +void mchp_aes_unregister_algs(struct mchp_crypto_dev *cryp); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/dma/at_xdmac.c linux-microchip/drivers/dma/at_xdmac.c --- linux-6.6.51/drivers/dma/at_xdmac.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/dma/at_xdmac.c 2024-11-26 14:02:36.454474995 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2260 @ return clk_enable(atxdmac->clk); } +static inline int at_xdmac_get_channel_number(struct platform_device *pdev, + u32 reg, u32 *pchannels) +{ + int ret; + + if (reg) { + *pchannels = AT_XDMAC_NB_CH(reg); + return 0; + } + + ret = of_property_read_u32(pdev->dev.of_node, "dma-channels", pchannels); + if (ret) + dev_err(&pdev->dev, "can't get number of channels\n"); + + return ret; +} + static int at_xdmac_probe(struct platform_device *pdev) { struct at_xdmac *atxdmac; - int irq, nr_channels, i, ret; + int irq, ret; void __iomem *base; - u32 reg; + u32 nr_channels, i, reg; irq = platform_get_irq(pdev, 0); if (irq < 0) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2298 @ * of channels to do the allocation. */ reg = readl_relaxed(base + AT_XDMAC_GTYPE); - nr_channels = AT_XDMAC_NB_CH(reg); + ret = at_xdmac_get_channel_number(pdev, reg, &nr_channels); + if (ret) + return ret; + if (nr_channels > AT_XDMAC_MAX_CHAN) { dev_err(&pdev->dev, "invalid number of channels (%u)\n", nr_channels); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/dma/sf-pdma/sf-pdma.c linux-microchip/drivers/dma/sf-pdma/sf-pdma.c --- linux-6.6.51/drivers/dma/sf-pdma/sf-pdma.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/dma/sf-pdma/sf-pdma.c 2024-11-26 14:02:36.462475139 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:23 @ #include <linux/mod_devicetable.h> #include <linux/dma-mapping.h> #include <linux/of.h> +#include <linux/of_dma.h> +#include <linux/of_device.h> #include <linux/slab.h> #include "sf-pdma.h" @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:70 @ static void sf_pdma_fill_desc(struct sf_pdma_desc *desc, u64 dst, u64 src, u64 size) { - desc->xfer_type = PDMA_FULL_SPEED; + desc->xfer_type = desc->chan->pdma->transfer_type; desc->xfer_size = size; desc->dst_addr = dst; desc->src_addr = src; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:495 @ } } +static struct dma_chan *sf_pdma_of_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct sf_pdma *pdma = ofdma->of_dma_data; + struct device *dev = pdma->dma_dev.dev; + struct sf_pdma_chan *chan; + struct dma_chan *c; + u32 channel_id; + + if (dma_spec->args_count != 1) { + dev_err(dev, "Bad number of cells\n"); + return NULL; + } + + channel_id = dma_spec->args[0]; + + chan = &pdma->chans[channel_id]; + + c = dma_get_slave_channel(&chan->vchan.chan); + if (!c) { + dev_err(dev, "No more channels available\n"); + return NULL; + } + + return c; +} + static int sf_pdma_probe(struct platform_device *pdev) { + const struct sf_pdma_driver_platdata *ddata; struct sf_pdma *pdma; int ret, n_chans; const enum dma_slave_buswidth widths = @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:550 @ pdma->n_chans = n_chans; + pdma->transfer_type = PDMA_FULL_SPEED; + + ddata = of_device_get_match_data(&pdev->dev); + if (ddata) { + if (ddata->quirks & NO_STRICT_ORDERING) + pdma->transfer_type &= ~(NO_STRICT_ORDERING); + } + pdma->membase = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(pdma->membase)) return PTR_ERR(pdma->membase); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:604 @ return ret; } + ret = of_dma_controller_register(pdev->dev.of_node, + sf_pdma_of_xlate, pdma); + if (ret < 0) { + dev_err(&pdev->dev, + "Can't register SiFive Platform OF_DMA. (%d)\n", ret); + goto err_unregister; + } + return 0; + +err_unregister: + dma_async_device_unregister(&pdma->dma_dev); + + return ret; } static int sf_pdma_remove(struct platform_device *pdev) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:642 @ return 0; } +static const struct sf_pdma_driver_platdata mpfs_pdma = { + .quirks = NO_STRICT_ORDERING, +}; + static const struct of_device_id sf_pdma_dt_ids[] = { - { .compatible = "sifive,fu540-c000-pdma" }, - { .compatible = "sifive,pdma0" }, + { + .compatible = "sifive,fu540-c000-pdma", + }, { + .compatible = "sifive,pdma0", + }, { + .compatible = "microchip,mpfs-pdma", + .data = &mpfs_pdma, + }, {}, }; + MODULE_DEVICE_TABLE(of, sf_pdma_dt_ids); static struct platform_driver sf_pdma_driver = { diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/dma/sf-pdma/sf-pdma.h linux-microchip/drivers/dma/sf-pdma/sf-pdma.h --- linux-6.6.51/drivers/dma/sf-pdma/sf-pdma.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/dma/sf-pdma/sf-pdma.h 2024-11-26 14:02:36.462475139 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:52 @ /* Transfer Type */ #define PDMA_FULL_SPEED 0xFF000008 +#define NO_STRICT_ORDERING BIT(3) /* Error Recovery */ #define MAX_RETRY 1 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:116 @ struct dma_device dma_dev; void __iomem *membase; void __iomem *mappedbase; + u32 transfer_type; u32 n_chans; struct sf_pdma_chan chans[]; }; +struct sf_pdma_driver_platdata { + u32 quirks; +}; + #endif /* _SF_PDMA_H */ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/dma-buf/Kconfig linux-microchip/drivers/dma-buf/Kconfig --- linux-6.6.51/drivers/dma-buf/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/dma-buf/Kconfig 2024-11-26 14:04:18.560303308 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:42 @ A driver to let userspace turn memfd regions into dma-bufs. Qemu can use this to create host dmabufs for guest framebuffers. +config U_DMA_BUF + tristate "User space mappable DMA Buffer" + default y + depends on OF + help + Enable this to allow the udmabuf to be built. + udmabuf is a Linux device driver that allocates contiguous + memory blocks in the kernel space as DMA buffers and + makes them available from the user space. + + If you don't know what to do here, say N. + + config DMABUF_MOVE_NOTIFY bool "Move notify between drivers (EXPERIMENTAL)" default n diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/dma-buf/Makefile linux-microchip/drivers/dma-buf/Makefile --- linux-6.6.51/drivers/dma-buf/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/dma-buf/Makefile 2024-11-26 14:04:18.560303308 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:10 @ obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o obj-$(CONFIG_UDMABUF) += udmabuf.o obj-$(CONFIG_DMABUF_SYSFS_STATS) += dma-buf-sysfs-stats.o +obj-$(CONFIG_U_DMA_BUF) += u-dma-buf.o dmabuf_selftests-y := \ selftest.o \ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/dma-buf/u-dma-buf.c linux-microchip/drivers/dma-buf/u-dma-buf.c --- linux-6.6.51/drivers/dma-buf/u-dma-buf.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/dma-buf/u-dma-buf.c 2024-11-26 14:04:18.560303308 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +/********************************************************************************* + * + * Copyright (C) 2015-2023 Ichiro Kawazome + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ********************************************************************************/ +#include <linux/cdev.h> +#include <linux/clk.h> +#include <linux/dma-mapping.h> +#include <linux/fs.h> +#include <linux/idr.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/sched.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/sysctl.h> +#include <linux/types.h> +#include <linux/uaccess.h> +#include <linux/scatterlist.h> +#include <linux/pagemap.h> +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/version.h> +#include <asm/page.h> +#include <asm/byteorder.h> + +/** + * DOC: Udmabuf Constants. + */ + +MODULE_DESCRIPTION("User space mappable DMA buffer device driver"); +MODULE_AUTHOR("ikwzm"); +MODULE_LICENSE("Dual BSD/GPL"); + +#define DRIVER_VERSION "4.5.3" +#define DRIVER_NAME "u-dma-buf" +#define DEVICE_NAME_FORMAT "udmabuf%d" +#define DEVICE_MAX_NUM 256 +#define UDMABUF_DEBUG 1 +#define USE_QUIRK_MMAP 1 +#define IN_KERNEL_FUNCTIONS 1 + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) +#include <linux/dma-map-ops.h> +#define IS_DMA_COHERENT(dev) dev_is_dma_coherent(dev) +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)) +#include <linux/dma-noncoherent.h> +#define IS_DMA_COHERENT(dev) dev_is_dma_coherent(dev) +#elif ((LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)) && (defined(CONFIG_ARM) || defined(CONFIG_ARM64))) +#define IS_DMA_COHERENT(dev) is_device_dma_coherent(dev) +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)) +#define USE_DEV_GROUPS 1 +#else +#define USE_DEV_GROUPS 0 +#endif + +#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) && defined(CONFIG_OF)) +#define USE_OF_RESERVED_MEM 1 +#else +#define USE_OF_RESERVED_MEM 0 +#endif + +#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) && defined(CONFIG_OF)) +#define USE_OF_DMA_CONFIG 1 +#else +#define USE_OF_DMA_CONFIG 0 +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) +#define USE_DEV_PROPERTY 1 +#else +#define USE_DEV_PROPERTY 0 +#endif + +#if (USE_OF_RESERVED_MEM == 1) +#include <linux/of_reserved_mem.h> +#endif + +#ifndef U64_MAX +#define U64_MAX ((u64)~0ULL) +#endif + +/** + * DOC: Udmabuf Static Variables. + * + * * udmabuf_sys_class - udmabuf system class + * * init_enable - udmabuf install/uninstall infomation enable + * * dma_mask_bit - udmabuf dma mask bit + * * bind - udmabuf bind device name + * * quirk_mmap_mode - udmabuf default quirk mmap mode + */ + +/** + * udmabuf_sys_class - udmabuf system class + */ +static struct class* udmabuf_sys_class = NULL; + +/** + * info_enable module parameter + */ +static int info_enable = 1; +module_param( info_enable , int, S_IRUGO); +MODULE_PARM_DESC( info_enable , "udmabuf install/uninstall infomation enable"); +#define DMA_INFO_ENABLE (info_enable & 0x02) +#define CONFIG_INFO_ENABLE (info_enable & 0x04) + +/** + * dma_mask_bit module parameter + */ +static int dma_mask_bit = 32; +module_param( dma_mask_bit, int, S_IRUGO); +MODULE_PARM_DESC( dma_mask_bit, "udmabuf dma mask bit(default=32)"); + +/** + * bind module parameter + */ +static char* bind = NULL; +module_param( bind, charp, S_IRUGO); +MODULE_PARM_DESC( bind, "bind device name. exp pci/0000:00:20:0"); + +#if (USE_QUIRK_MMAP == 1) +/** + * quirk_mmap_mode - udmabuf default quirk mmap mode + */ +#define QUIRK_MMAP_MODE_UNDEFINED 0 +#define QUIRK_MMAP_MODE_ALWAYS_OFF 1 +#define QUIRK_MMAP_MODE_ALWAYS_ON 2 +#define QUIRK_MMAP_MODE_AUTO 3 +#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) +static int quirk_mmap_mode = QUIRK_MMAP_MODE_ALWAYS_ON; +#else +static int quirk_mmap_mode = QUIRK_MMAP_MODE_AUTO; +#endif +module_param( quirk_mmap_mode, int, S_IRUGO); +MODULE_PARM_DESC( quirk_mmap_mode, "udmabuf default quirk mmap mode(1:off,2:on,3:auto)(default=3)"); +#endif /* #if (USE_QUIRK_MMAP == 1) */ + +/** + * DOC: Udmabuf Object Data Structure. + * + * This section defines the structure of udmabuf device. + * + */ + +/** + * struct udmabuf_object - udmabuf object structure. + */ +struct udmabuf_object { + struct device* sys_dev; + struct device* dma_dev; + struct cdev cdev; + dev_t device_number; + struct mutex sem; + bool is_open; + size_t size; + size_t alloc_size; + void* virt_addr; + dma_addr_t phys_addr; + int sync_mode; + u64 sync_offset; + size_t sync_size; + int sync_direction; + bool sync_owner; + u64 sync_for_cpu; + u64 sync_for_device; +#if (USE_QUIRK_MMAP == 1) + int quirk_mmap_mode; +#endif +#if (USE_OF_RESERVED_MEM == 1) + bool of_reserved_mem; +#endif +#if ((UDMABUF_DEBUG == 1) && (USE_QUIRK_MMAP == 1)) + bool debug_vma; +#endif +}; + +#if ((UDMABUF_DEBUG == 1) && (USE_QUIRK_MMAP == 1)) +#define UDMABUF_VMA_DEBUG(this) (this->debug_vma) +#else +#define UDMABUF_VMA_DEBUG(this) (0) +#endif + +/** + * sync_mode(synchronous mode) value + */ +#define SYNC_MODE_INVALID (0x00) +#define SYNC_MODE_NONCACHED (0x01) +#define SYNC_MODE_WRITECOMBINE (0x02) +#define SYNC_MODE_DMACOHERENT (0x03) +#define SYNC_MODE_MASK (0x03) +#define SYNC_MODE_MIN (0x01) +#define SYNC_MODE_MAX (0x03) +#define SYNC_ALWAYS (0x04) + +/** + * DOC: Udmabuf System Class Device File Description. + * + * This section define the device file created in system class when udmabuf is + * loaded into the kernel. + * + * The device file created in system class is as follows. + * + * * /sys/class/u-dma-buf/<device-name>/driver_version + * * /sys/class/u-dma-buf/<device-name>/phys_addr + * * /sys/class/u-dma-buf/<device-name>/size + * * /sys/class/u-dma-buf/<device-name>/sync_mode + * * /sys/class/u-dma-buf/<device-name>/sync_offset + * * /sys/class/u-dma-buf/<device-name>/sync_size + * * /sys/class/u-dma-buf/<device-name>/sync_direction + * * /sys/class/u-dma-buf/<device-name>/sync_owner + * * /sys/class/u-dma-buf/<device-name>/sync_for_cpu + * * /sys/class/u-dma-buf/<device-name>/sync_for_device + * * /sys/class/u-dma-buf/<device-name>/dma_coherent + * * /sys/class/u-dma-buf/<device-name>/quirk_mmap_mode + * * + */ + +#define SYNC_COMMAND_DIR_MASK (0x000000000000000C) +#define SYNC_COMMAND_DIR_SHIFT (2) +#define SYNC_COMMAND_SIZE_MASK (0x00000000FFFFFFF0) +#define SYNC_COMMAND_SIZE_SHIFT (0) +#define SYNC_COMMAND_OFFSET_MASK (0xFFFFFFFF00000000) +#define SYNC_COMMAND_OFFSET_SHIFT (32) +#define SYNC_COMMAND_ARGMENT_MASK (0xFFFFFFFFFFFFFFFE) +/** + * udmabuf_sync_command_argments() - get argment for dma_sync_single_for_cpu() or dma_sync_single_for_device() + * + * @this: Pointer to the udmabuf object. + * @command sync command (this->sync_for_cpu or this->sync_for_device) + * @phys_addr Pointer to the phys_addr for dma_sync_single_for_...() + * @size Pointer to the size for dma_sync_single_for_...() + * @direction Pointer to the direction for dma_sync_single_for_...() + * Return: Success(=0) or error status(<0). + */ +static int udmabuf_sync_command_argments( + struct udmabuf_object *this , + u64 command , + dma_addr_t *phys_addr, + size_t *size , + enum dma_data_direction *direction +) { + u64 sync_offset ; + size_t sync_size ; + int sync_direction; + if ((command & SYNC_COMMAND_ARGMENT_MASK) != 0) { + sync_offset = (u64 )((command & SYNC_COMMAND_OFFSET_MASK) >> SYNC_COMMAND_OFFSET_SHIFT); + sync_size = (size_t)((command & SYNC_COMMAND_SIZE_MASK ) >> SYNC_COMMAND_SIZE_SHIFT ); + sync_direction = (int )((command & SYNC_COMMAND_DIR_MASK ) >> SYNC_COMMAND_DIR_SHIFT ); + } else { + sync_offset = this->sync_offset; + sync_size = this->sync_size; + sync_direction = this->sync_direction; + } + if (sync_offset + sync_size > this->size) + return -EINVAL; + switch(sync_direction) { + case 1 : *direction = DMA_TO_DEVICE ; break; + case 2 : *direction = DMA_FROM_DEVICE ; break; + default: *direction = DMA_BIDIRECTIONAL; break; + } + *phys_addr = this->phys_addr + sync_offset; + *size = sync_size; + return 0; +} + +/** + * udmabuf_sync_for_cpu() - call dma_sync_single_for_cpu() when (sync_for_cpu != 0) + * @this: Pointer to the udmabuf object. + * Return: Success(=0) or error status(<0). + */ +static int udmabuf_sync_for_cpu(struct udmabuf_object* this) +{ + int status = 0; + + if (this->sync_for_cpu) { + dma_addr_t phys_addr; + size_t size; + enum dma_data_direction direction; + status = udmabuf_sync_command_argments(this, this->sync_for_cpu, &phys_addr, &size, &direction); + if (status == 0) { + dma_sync_single_for_cpu(this->dma_dev, phys_addr, size, direction); + this->sync_for_cpu = 0; + this->sync_owner = 0; + } + } + return status; +} + +/** + * udmabuf_sync_for_device() - call dma_sync_single_for_device() when (sync_for_device != 0) + * @this: Pointer to the udmabuf object. + * Return: Success(=0) or error status(<0). + */ +static int udmabuf_sync_for_device(struct udmabuf_object* this) +{ + int status = 0; + + if (this->sync_for_device) { + dma_addr_t phys_addr; + size_t size; + enum dma_data_direction direction; + status = udmabuf_sync_command_argments(this, this->sync_for_device, &phys_addr, &size, &direction); + if (status == 0) { + dma_sync_single_for_device(this->dma_dev, phys_addr, size, direction); + this->sync_for_device = 0; + this->sync_owner = 1; + } + } + return status; +} + +#define DEF_ATTR_SHOW(__attr_name, __format, __value) \ +static ssize_t udmabuf_show_ ## __attr_name(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + ssize_t status; \ + struct udmabuf_object* this = dev_get_drvdata(dev); \ + if (mutex_lock_interruptible(&this->sem) != 0) \ + return -ERESTARTSYS; \ + status = sprintf(buf, __format, (__value)); \ + mutex_unlock(&this->sem); \ + return status; \ +} + +static inline int NO_ACTION(struct udmabuf_object* this){return 0;} + +#define DEF_ATTR_SET(__attr_name, __min, __max, __pre_action, __post_action) \ +static ssize_t udmabuf_set_ ## __attr_name(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) \ +{ \ + ssize_t status; \ + u64 value; \ + struct udmabuf_object* this = dev_get_drvdata(dev); \ + if (0 != mutex_lock_interruptible(&this->sem)){return -ERESTARTSYS;} \ + if (0 != (status = kstrtoull(buf, 0, &value))){ goto failed;} \ + if ((value < __min) || (__max < value)) {status = -EINVAL; goto failed;} \ + if (0 != (status = __pre_action(this))) { goto failed;} \ + this->__attr_name = value; \ + if (0 != (status = __post_action(this))) { goto failed;} \ + status = size; \ + failed: \ + mutex_unlock(&this->sem); \ + return status; \ +} + +DEF_ATTR_SHOW(driver_version , "%s\n" , DRIVER_VERSION ); +DEF_ATTR_SHOW(size , "%zu\n" , this->size ); +DEF_ATTR_SHOW(phys_addr , "%pad\n" , &this->phys_addr ); +DEF_ATTR_SHOW(sync_mode , "%d\n" , this->sync_mode ); +DEF_ATTR_SET( sync_mode , 0, 7, NO_ACTION, NO_ACTION ); +DEF_ATTR_SHOW(sync_offset , "0x%llx\n", this->sync_offset ); +DEF_ATTR_SET( sync_offset , 0, U64_MAX, NO_ACTION, NO_ACTION ); +DEF_ATTR_SHOW(sync_size , "%zu\n" , this->sync_size ); +DEF_ATTR_SET( sync_size , 0, SIZE_MAX, NO_ACTION, NO_ACTION ); +DEF_ATTR_SHOW(sync_direction , "%d\n" , this->sync_direction ); +DEF_ATTR_SET( sync_direction , 0, 2, NO_ACTION, NO_ACTION ); +DEF_ATTR_SHOW(sync_owner , "%d\n" , this->sync_owner ); +DEF_ATTR_SHOW(sync_for_cpu , "%llu\n" , this->sync_for_cpu ); +DEF_ATTR_SET( sync_for_cpu , 0, U64_MAX, NO_ACTION, udmabuf_sync_for_cpu ); +DEF_ATTR_SHOW(sync_for_device, "%llu\n" , this->sync_for_device ); +DEF_ATTR_SET( sync_for_device , 0, U64_MAX, NO_ACTION, udmabuf_sync_for_device); +#if (USE_QUIRK_MMAP == 1) +DEF_ATTR_SHOW(quirk_mmap_mode, "%d\n" , this->quirk_mmap_mode ); +#endif +#if defined(IS_DMA_COHERENT) +DEF_ATTR_SHOW(dma_coherent , "%d\n" , IS_DMA_COHERENT(this->dma_dev) ); +#endif +#if ((UDMABUF_DEBUG == 1) && (USE_QUIRK_MMAP == 1)) +DEF_ATTR_SHOW(debug_vma , "%d\n" , this->debug_vma ); +DEF_ATTR_SET( debug_vma , 0, 1, NO_ACTION, NO_ACTION ); +#endif + +static struct device_attribute udmabuf_device_attrs[] = { + __ATTR(driver_version , 0444, udmabuf_show_driver_version , NULL ), + __ATTR(size , 0444, udmabuf_show_size , NULL ), + __ATTR(phys_addr , 0444, udmabuf_show_phys_addr , NULL ), + __ATTR(sync_mode , 0664, udmabuf_show_sync_mode , udmabuf_set_sync_mode ), + __ATTR(sync_offset , 0664, udmabuf_show_sync_offset , udmabuf_set_sync_offset ), + __ATTR(sync_size , 0664, udmabuf_show_sync_size , udmabuf_set_sync_size ), + __ATTR(sync_direction , 0664, udmabuf_show_sync_direction , udmabuf_set_sync_direction ), + __ATTR(sync_owner , 0444, udmabuf_show_sync_owner , NULL ), + __ATTR(sync_for_cpu , 0664, udmabuf_show_sync_for_cpu , udmabuf_set_sync_for_cpu ), + __ATTR(sync_for_device, 0664, udmabuf_show_sync_for_device , udmabuf_set_sync_for_device), +#if (USE_QUIRK_MMAP == 1) + __ATTR(quirk_mmap_mode, 0444, udmabuf_show_quirk_mmap_mode , NULL ), +#endif +#if defined(IS_DMA_COHERENT) + __ATTR(dma_coherent , 0444, udmabuf_show_dma_coherent , NULL ), +#endif +#if ((UDMABUF_DEBUG == 1) && (USE_QUIRK_MMAP == 1)) + __ATTR(debug_vma , 0664, udmabuf_show_debug_vma , udmabuf_set_debug_vma ), +#endif + __ATTR_NULL, +}; + +#if (USE_DEV_GROUPS == 1) + +#define udmabuf_device_attrs_size (sizeof(udmabuf_device_attrs)/sizeof(udmabuf_device_attrs[0])) + +static struct attribute* udmabuf_attrs[udmabuf_device_attrs_size] = { + NULL +}; +static struct attribute_group udmabuf_attr_group = { + .attrs = udmabuf_attrs +}; +static const struct attribute_group* udmabuf_attr_groups[] = { + &udmabuf_attr_group, + NULL +}; + +static inline void udmabuf_sys_class_set_attributes(void) +{ + int i; + for (i = 0 ; i < udmabuf_device_attrs_size-1 ; i++) { + udmabuf_attrs[i] = &(udmabuf_device_attrs[i].attr); + } + udmabuf_attrs[i] = NULL; + udmabuf_sys_class->dev_groups = udmabuf_attr_groups; +} +#else + +static inline void udmabuf_sys_class_set_attributes(void) +{ + udmabuf_sys_class->dev_attrs = udmabuf_device_attrs; +} + +#endif + +#if (USE_QUIRK_MMAP == 1) +/** + * DOC: Udmabuf Object VM Area Operations for quirk-mmap. + * + * This section defines the operation of vm when mmap-ed the udmabuf object. + * + * * udmabuf_mmap_vma_open() - udmabuf object quirk-mmap vm area open operation. + * * udmabuf_mmap_vma_close() - udmabuf object quirk-mmap vm area close operation. + * * udmabuf_mmap_vma_fault() - udmabuf object quirk-mmap vm area fault operation. + * * udmabuf_mmap_vm_ops - udmabuf object quirk-mmap vm operation table. + * * udmabuf_set_quirk_mmap_mode() - set quirk-mmap in udmabuf object. + * * udmabuf_quirk_mmap_enable() - check if udmabuf object can use quirk-mmap. + */ +/** + * udmabuf_mmap_vma_open() - udmabuf device file mmap vm area open operation. + * @vma: Pointer to the vm area structure. + * Return: None + */ +static void udmabuf_mmap_vma_open(struct vm_area_struct* vma) +{ + struct udmabuf_object* this = vma->vm_private_data; + if (UDMABUF_VMA_DEBUG(this)) + dev_info(this->dma_dev, "vma_open(virt_addr=0x%lx, offset=0x%lx)\n", vma->vm_start, vma->vm_pgoff<<PAGE_SHIFT); +} + +/** + * udmabuf_mmap_vma_close() - udmabuf device file mmap vm area close operation. + * @vma: Pointer to the vm area structure. + * Return: None + */ +static void udmabuf_mmap_vma_close(struct vm_area_struct* vma) +{ + struct udmabuf_object* this = vma->vm_private_data; + if (UDMABUF_VMA_DEBUG(this)) + dev_info(this->dma_dev, "vma_close()\n"); +} + +/** + * VM_FAULT_RETURN_TYPE - Type of udmabuf_mmap_vma_fault() return value. + */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)) +typedef vm_fault_t VM_FAULT_RETURN_TYPE; +#else +typedef int VM_FAULT_RETURN_TYPE; +#endif + +/** + * _udmabuf_mmap_vma_fault() - udmabuf device file mmap vm area fault operation. + * @vma: Pointer to the vm area structure. + * @vfm: Pointer to the vm fault structure. + * Return: VM_FAULT_RETURN_TYPE (Success(=0) or error status(!=0)). + */ +static inline VM_FAULT_RETURN_TYPE _udmabuf_mmap_vma_fault(struct vm_area_struct* vma, struct vm_fault* vmf) +{ + struct udmabuf_object* this = vma->vm_private_data; + unsigned long offset = vmf->pgoff << PAGE_SHIFT; + unsigned long phys_addr = this->phys_addr + offset; + unsigned long page_frame_num = phys_addr >> PAGE_SHIFT; + unsigned long request_size = 1 << PAGE_SHIFT; + unsigned long available_size = this->alloc_size -offset; + unsigned long virt_addr; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) + virt_addr = vmf->address; +#else + virt_addr = (unsigned long)vmf->virtual_address; +#endif + + if (UDMABUF_VMA_DEBUG(this)) + dev_info(this->dma_dev, + "vma_fault(virt_addr=%pad, phys_addr=%pad)\n", &virt_addr, &phys_addr + ); + + if (request_size > available_size) + return VM_FAULT_SIGBUS; + + if (!pfn_valid(page_frame_num)) + return VM_FAULT_SIGBUS; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)) + return vmf_insert_pfn(vma, virt_addr, page_frame_num); +#else + { + int err = vm_insert_pfn(vma, virt_addr, page_frame_num); + if (err == -ENOMEM) + return VM_FAULT_OOM; + if (err < 0 && err != -EBUSY) + return VM_FAULT_SIGBUS; + + return VM_FAULT_NOPAGE; + } +#endif +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)) +/** + * udmabuf_mmap_vma_fault() - udmabuf device file mmap vm area fault operation. + * @vfm: Pointer to the vm fault structure. + * Return: VM_FAULT_RETURN_TYPE (Success(=0) or error status(!=0)). + */ +static VM_FAULT_RETURN_TYPE udmabuf_mmap_vma_fault(struct vm_fault* vmf) +{ + return _udmabuf_mmap_vma_fault(vmf->vma, vmf); +} +#else +/** + * udmabuf_mmap_vma_fault() - udmabuf device file mmap vm area fault operation. + * @vma: Pointer to the vm area structure. + * @vfm: Pointer to the vm fault structure. + * Return: VM_FAULT_RETURN_TYPE (Success(=0) or error status(!=0)). + */ +static VM_FAULT_RETURN_TYPE udmabuf_mmap_vma_fault(struct vm_area_struct* vma, struct vm_fault* vmf) +{ + return _udmabuf_mmap_vma_fault(vma, vmf); +} +#endif + +/** + * udmabuf device file mmap vm operation table. + */ +static const struct vm_operations_struct udmabuf_mmap_vm_ops = { + .open = udmabuf_mmap_vma_open , + .close = udmabuf_mmap_vma_close, + .fault = udmabuf_mmap_vma_fault, +}; + +/** + * udmabuf_set_quirk_mmap_mode() - set quirk-mmap in udmabuf object. + * @this: Pointer to the udmabuf object. + * @value: quirk-mmap mode. + * Return: Success(=0) or error status(<0). + */ +static inline int udmabuf_set_quirk_mmap_mode(struct udmabuf_object* this, int value) +{ + if (!this) + return -ENODEV; + + if ((value == QUIRK_MMAP_MODE_ALWAYS_OFF) || + (value == QUIRK_MMAP_MODE_ALWAYS_ON ) || + (value == QUIRK_MMAP_MODE_AUTO )) { + this->quirk_mmap_mode = value; + return 0; + } else { + return -EINVAL; + } +} + +/** + * udmabuf_quirk_mmap_enable() - check if udmabuf object can use quirk-mmap. + * @this: Pointer to the udmabuf object. + * Return: Enable(=True) or Disable(=False). + */ +static bool udmabuf_quirk_mmap_enable(struct udmabuf_object* this) +{ + if (!this) + return true; + + if (this->quirk_mmap_mode == QUIRK_MMAP_MODE_ALWAYS_OFF) + return false; + if (this->quirk_mmap_mode == QUIRK_MMAP_MODE_ALWAYS_ON ) + return true; +#if defined(IS_DMA_COHERENT) + if (this->quirk_mmap_mode == QUIRK_MMAP_MODE_AUTO ) + return !IS_DMA_COHERENT(this->dma_dev); +#endif + return true; +} +#endif /* #if (USE_QUIRK_MMAP == 1) */ + +/** + * DOC: Udmabuf Object Memory Map Operation. + */ +/** + * _PGPROT_NONCACHED - vm_page_prot value when sync_mode is SYNC_MODE_NONCACHED + * _PGPROT_WRITECOMBINE - vm_page_prot value when sync_mode is SYNC_MODE_WRITECOMBINE + * _PGPROT_DMACOHERENT - vm_page_prot value when sync_mode is SYNC_MODE_DMACOHERENT + */ +#if defined(CONFIG_ARM) +#define _PGPROT_NONCACHED(vm_page_prot) pgprot_noncached(vm_page_prot) +#define _PGPROT_WRITECOMBINE(vm_page_prot) pgprot_writecombine(vm_page_prot) +#define _PGPROT_DMACOHERENT(vm_page_prot) pgprot_dmacoherent(vm_page_prot) +#elif defined(CONFIG_ARM64) +#define _PGPROT_NONCACHED(vm_page_prot) pgprot_noncached(vm_page_prot) +#define _PGPROT_WRITECOMBINE(vm_page_prot) pgprot_writecombine(vm_page_prot) +#define _PGPROT_DMACOHERENT(vm_page_prot) pgprot_writecombine(vm_page_prot) +#else +#define _PGPROT_NONCACHED(vm_page_prot) pgprot_noncached(vm_page_prot) +#define _PGPROT_WRITECOMBINE(vm_page_prot) pgprot_writecombine(vm_page_prot) +#define _PGPROT_DMACOHERENT(vm_page_prot) pgprot_writecombine(vm_page_prot) +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)) +static inline void vm_flags_set(struct vm_area_struct* vma, vm_flags_t flags) +{ + vma->vm_flags |= flags; +} +#endif + +/** + * udmabuf_object_mmap() - udmabuf object memory map operation. + * @this: Pointer to the udmabuf object. + * @vma: Pointer to the vm area structure. + * @force_sync Force sync flag. + * Return: Success(=0) or error status(<0). + */ +static int udmabuf_object_mmap(struct udmabuf_object* this, struct vm_area_struct* vma, bool force_sync) +{ + if (vma->vm_pgoff + vma_pages(vma) > (this->alloc_size >> PAGE_SHIFT)) + return -EINVAL; + + if ((force_sync == true) | (this->sync_mode & SYNC_ALWAYS)) { + switch (this->sync_mode & SYNC_MODE_MASK) { + case SYNC_MODE_NONCACHED : + vm_flags_set(vma, VM_IO); + vma->vm_page_prot = _PGPROT_NONCACHED(vma->vm_page_prot); + break; + case SYNC_MODE_WRITECOMBINE : + vm_flags_set(vma, VM_IO); + vma->vm_page_prot = _PGPROT_WRITECOMBINE(vma->vm_page_prot); + break; + case SYNC_MODE_DMACOHERENT : + vm_flags_set(vma, VM_IO); + vma->vm_page_prot = _PGPROT_DMACOHERENT(vma->vm_page_prot); + break; + default : + break; + } + } + +#if (USE_QUIRK_MMAP == 1) + if (udmabuf_quirk_mmap_enable(this)) + { + unsigned long page_frame_num = (this->phys_addr >> PAGE_SHIFT) + vma->vm_pgoff; + if (pfn_valid(page_frame_num)) { + vm_flags_set(vma, VM_PFNMAP); + vma->vm_ops = &udmabuf_mmap_vm_ops; + vma->vm_private_data = this; + udmabuf_mmap_vma_open(vma); + return 0; + } + } +#endif + + return dma_mmap_coherent(this->dma_dev, vma, this->virt_addr, this->phys_addr, this->alloc_size); +} + +/** + * DOC: Udmabuf Device File Operations. + * + * This section defines the operation of the udmabuf device file. + * + * * udmabuf_device_file_open() - udmabuf device file open operation. + * * udmabuf_device_file_release() - udmabuf device file release operation. + * * udmabuf_device_file_mmap() - udmabuf device file memory map operation. + * * udmabuf_device_file_read() - udmabuf device file read operation. + * * udmabuf_device_file_write() - udmabuf device file write operation. + * * udmabuf_device_file_llseek() - udmabuf device file llseek operation. + * * udmabuf_device_file_ops - udmabuf device file operation table. + */ + +/** + * udmabuf_device_file_open() - udmabuf device file open operation. + * @inode: Pointer to the inode structure of this device. + * @file: to the file structure. + * Return: Success(=0) or error status(<0). + */ +static int udmabuf_device_file_open(struct inode *inode, struct file *file) +{ + struct udmabuf_object* this; + int status = 0; + + this = container_of(inode->i_cdev, struct udmabuf_object, cdev); + file->private_data = this; + this->is_open = 1; + + return status; +} + +/** + * udmabuf_device_file_release() - udmabuf device file release operation. + * @inode: Pointer to the inode structure of this device. + * @file: Pointer to the file structure. + * Return: Success(=0) or error status(<0). + */ +static int udmabuf_device_file_release(struct inode *inode, struct file *file) +{ + struct udmabuf_object* this = file->private_data; + + this->is_open = 0; + + return 0; +} + +/** + * udmabuf_device_file_mmap() - udmabuf device file memory map operation. + * @file: Pointer to the file structure. + * @vma: Pointer to the vm area structure. + * Return: Success(=0) or error status(<0). + */ +static int udmabuf_device_file_mmap(struct file *file, struct vm_area_struct* vma) +{ + struct udmabuf_object* this = file->private_data; + bool force_sync = ((file->f_flags & O_SYNC) != 0); + + return udmabuf_object_mmap(this, vma, force_sync); +} + +/** + * udmabuf_device_file_read() - udmabuf device file read operation. + * @file: Pointer to the file structure. + * @buff: Pointer to the user buffer. + * @count: The number of bytes to be read. + * @ppos: Pointer to the offset value. + * Return: Transferd size. + */ +static ssize_t udmabuf_device_file_read(struct file* file, char __user* buff, size_t count, loff_t* ppos) +{ + struct udmabuf_object* this = file->private_data; + int result = 0; + size_t xfer_size; + size_t remain_size; + dma_addr_t phys_addr; + void* virt_addr; + + if (mutex_lock_interruptible(&this->sem)) + return -ERESTARTSYS; + + if (*ppos >= this->size) { + result = 0; + goto return_unlock; + } + + phys_addr = this->phys_addr + *ppos; + virt_addr = this->virt_addr + *ppos; + xfer_size = (*ppos + count >= this->size) ? this->size - *ppos : count; + + if ((file->f_flags & O_SYNC) | (this->sync_mode & SYNC_ALWAYS)) + dma_sync_single_for_cpu(this->dma_dev, phys_addr, xfer_size, DMA_FROM_DEVICE); + + if ((remain_size = copy_to_user(buff, virt_addr, xfer_size)) != 0) { + result = 0; + goto return_unlock; + } + + if ((file->f_flags & O_SYNC) | (this->sync_mode & SYNC_ALWAYS)) + dma_sync_single_for_device(this->dma_dev, phys_addr, xfer_size, DMA_FROM_DEVICE); + + *ppos += xfer_size; + result = xfer_size; + return_unlock: + mutex_unlock(&this->sem); + return result; +} + +/** + * udmabuf_device_file_write() - udmabuf device file write operation. + * @file: Pointer to the file structure. + * @buff: Pointer to the user buffer. + * @count: The number of bytes to be written. + * @ppos: Pointer to the offset value + * Return: Transferd size. + */ +static ssize_t udmabuf_device_file_write(struct file* file, const char __user* buff, size_t count, loff_t* ppos) +{ + struct udmabuf_object* this = file->private_data; + int result = 0; + size_t xfer_size; + size_t remain_size; + dma_addr_t phys_addr; + void* virt_addr; + + if (mutex_lock_interruptible(&this->sem)) + return -ERESTARTSYS; + + if (*ppos >= this->size) { + result = 0; + goto return_unlock; + } + + phys_addr = this->phys_addr + *ppos; + virt_addr = this->virt_addr + *ppos; + xfer_size = (*ppos + count >= this->size) ? this->size - *ppos : count; + + if ((file->f_flags & O_SYNC) | (this->sync_mode & SYNC_ALWAYS)) + dma_sync_single_for_cpu(this->dma_dev, phys_addr, xfer_size, DMA_TO_DEVICE); + + if ((remain_size = copy_from_user(virt_addr, buff, xfer_size)) != 0) { + result = 0; + goto return_unlock; + } + + if ((file->f_flags & O_SYNC) | (this->sync_mode & SYNC_ALWAYS)) + dma_sync_single_for_device(this->dma_dev, phys_addr, xfer_size, DMA_TO_DEVICE); + + *ppos += xfer_size; + result = xfer_size; + return_unlock: + mutex_unlock(&this->sem); + return result; +} + +/** + * udmabuf_device_file_llseek() - udmabuf device file llseek operation. + * @file: Pointer to the file structure. + * @offset: File offset to seek. + * @whence: Type of seek. + * Return: The new position. + */ +static loff_t udmabuf_device_file_llseek(struct file* file, loff_t offset, int whence) +{ + struct udmabuf_object* this = file->private_data; + loff_t new_pos; + + switch (whence) { + case 0 : /* SEEK_SET */ + new_pos = offset; + break; + case 1 : /* SEEK_CUR */ + new_pos = file->f_pos + offset; + break; + case 2 : /* SEEK_END */ + new_pos = this->size + offset; + break; + default: + return -EINVAL; + } + if (new_pos < 0 ){return -EINVAL;} + if (new_pos > this->size){return -EINVAL;} + file->f_pos = new_pos; + return new_pos; +} + +/** + * udmabuf device file operation table. + */ +static const struct file_operations udmabuf_device_file_ops = { + .owner = THIS_MODULE, + .open = udmabuf_device_file_open, + .release = udmabuf_device_file_release, + .mmap = udmabuf_device_file_mmap, + .read = udmabuf_device_file_read, + .write = udmabuf_device_file_write, + .llseek = udmabuf_device_file_llseek, +}; + +/** + * DOC: Udmabuf Object Operations. + * + * This section defines the operation of udmabuf object. + * + * * udmabuf_device_ida - Udmabuf Object Device Minor Number allocator variable. + * * udmabuf_device_number - Udmabuf Object Device Major Number. + * * udmabuf_object_create() - Create udmabuf object. + * * udmabuf_object_setup() - Setup the udmabuf object. + * * udmabuf_object_info() - Print infomation the udmabuf object. + * * udmabuf_object_destroy() - Destroy the udmabuf object. + */ +static DEFINE_IDA(udmabuf_device_ida); +static dev_t udmabuf_device_number = 0; + +/** + * udmabuf_object_create() - Create udmabuf object. + * @name: device name or NULL. + * @parent: parent device or NULL. + * @minor: minor_number or -1 or -2. + * Return: Pointer to the udmabuf object or NULL. + */ +static struct udmabuf_object* udmabuf_object_create(const char* name, struct device* parent, int minor) +{ + struct udmabuf_object* this = NULL; + unsigned int done = 0; + const unsigned int DONE_ALLOC_MINOR = (1 << 0); + const unsigned int DONE_CHRDEV_ADD = (1 << 1); + const unsigned int DONE_DEVICE_CREATE = (1 << 3); + const unsigned int DONE_SET_DMA_DEV = (1 << 4); + /* + * allocate device minor number + */ + { + if ((0 <= minor) && (minor < DEVICE_MAX_NUM)) { + if (ida_simple_get(&udmabuf_device_ida, minor, minor+1, GFP_KERNEL) < 0) { + pr_err(DRIVER_NAME ": couldn't allocate minor number(=%d).\n", minor); + goto failed; + } + } else if(minor < 0) { + if ((minor = ida_simple_get(&udmabuf_device_ida, 0, DEVICE_MAX_NUM, GFP_KERNEL)) < 0) { + pr_err(DRIVER_NAME ": couldn't allocate new minor number. return=%d.\n", minor); + goto failed; + } + } else { + pr_err(DRIVER_NAME ": invalid minor number(=%d), valid range is 0 to %d\n", minor, DEVICE_MAX_NUM-1); + goto failed; + } + done |= DONE_ALLOC_MINOR; + } + /* + * create (udmabuf_object*) this. + */ + { + this = kzalloc(sizeof(*this), GFP_KERNEL); + if (IS_ERR_OR_NULL(this)) { + int retval = PTR_ERR(this); + this = NULL; + pr_err(DRIVER_NAME ": kzalloc() failed. return=%d\n", retval); + goto failed; + } + } + /* + * set device_number + */ + { + this->device_number = MKDEV(MAJOR(udmabuf_device_number), minor); + } + /* + * register /sys/class/u-dma-buf/<name> + */ + { + if (name == NULL) { + this->sys_dev = device_create(udmabuf_sys_class, + parent, + this->device_number, + (void *)this, + DEVICE_NAME_FORMAT, MINOR(this->device_number)); + } else { + this->sys_dev = device_create(udmabuf_sys_class, + parent, + this->device_number, + (void *)this, + "%s", name); + } + if (IS_ERR_OR_NULL(this->sys_dev)) { + int retval = PTR_ERR(this->sys_dev); + this->sys_dev = NULL; + pr_err(DRIVER_NAME ": device_create() failed. return=%d\n", retval); + goto failed; + } + done |= DONE_DEVICE_CREATE; + } + /* + * add chrdev. + */ + { + int retval; + cdev_init(&this->cdev, &udmabuf_device_file_ops); + this->cdev.owner = THIS_MODULE; + if ((retval = cdev_add(&this->cdev, this->device_number, 1)) != 0) { + dev_err(this->sys_dev, "cdev_add() failed. return=%d\n", retval); + goto failed; + } + done |= DONE_CHRDEV_ADD; + } + /* + * set dma_dev + */ + { + if (parent != NULL) + this->dma_dev = get_device(parent); + else + this->dma_dev = get_device(this->sys_dev); + /* + * set this->dma_dev->dma_mask + */ + if (this->dma_dev->dma_mask == NULL) { + this->dma_dev->dma_mask = &this->dma_dev->coherent_dma_mask; + } + /* + * set *this->dma_dev->dma_mask and this->dma_dev->coherent_dma_mask + * Executing dma_set_mask_and_coherent() before of_dma_configure() may fail. + * Because dma_set_mask_and_coherent() will fail unless dev->dma_ops is set. + * When dma_set_mask_and_coherent() fails, it is forcefuly setting the dma-mask value. + */ + if (*this->dma_dev->dma_mask == 0) { + int retval = dma_set_mask_and_coherent(this->dma_dev, DMA_BIT_MASK(dma_mask_bit)); + if (retval != 0) { + dev_warn(this->sys_dev,"dma_set_mask_and_coherent(DMA_BIT_MASK(%d)) failed. return=(%d)\n", dma_mask_bit, retval); + *this->dma_dev->dma_mask = DMA_BIT_MASK(dma_mask_bit); + this->dma_dev->coherent_dma_mask = DMA_BIT_MASK(dma_mask_bit); + } + } + done |= DONE_SET_DMA_DEV; + } + /* + * initialize other variables. + */ + { + this->size = 0; + this->alloc_size = 0; + this->sync_mode = SYNC_MODE_NONCACHED; + this->sync_offset = 0; + this->sync_size = 0; + this->sync_direction = 0; + this->sync_owner = 0; + this->sync_for_cpu = 0; + this->sync_for_device = 0; + } +#if (USE_OF_RESERVED_MEM == 1) + { + this->of_reserved_mem = 0; + } +#endif +#if (USE_QUIRK_MMAP == 1) + { + this->quirk_mmap_mode = quirk_mmap_mode; + } +#endif +#if ((UDMABUF_DEBUG == 1) && (USE_QUIRK_MMAP == 1)) + { + this->debug_vma = 0; + } +#endif + mutex_init(&this->sem); + + return this; + + failed: + if (done & DONE_SET_DMA_DEV ) { put_device(this->dma_dev);} + if (done & DONE_CHRDEV_ADD ) { cdev_del(&this->cdev); } + if (done & DONE_DEVICE_CREATE) { device_destroy(udmabuf_sys_class, this->device_number);} + if (done & DONE_ALLOC_MINOR ) { ida_simple_remove(&udmabuf_device_ida, minor);} + if (this != NULL) { kfree(this); } + return NULL; +} + +/** + * udmabuf_object_setup() - Setup the udmabuf object. + * @this: Pointer to the udmabuf object. + * Return: Success(=0) or error status(<0). + */ +static int udmabuf_object_setup(struct udmabuf_object* this) +{ + if (!this) + return -ENODEV; + /* + * setup buffer size and allocation size + */ + this->alloc_size = ((this->size + ((1 << PAGE_SHIFT) - 1)) >> PAGE_SHIFT) << PAGE_SHIFT; + /* + * dma buffer allocation + */ + this->virt_addr = dma_alloc_coherent(this->dma_dev, this->alloc_size, &this->phys_addr, GFP_KERNEL); + if (IS_ERR_OR_NULL(this->virt_addr)) { + int retval = PTR_ERR(this->virt_addr); + dev_err(this->sys_dev, "dma_alloc_coherent(size=%zu) failed. return(%d)\n", this->alloc_size, retval); + this->virt_addr = NULL; + return (retval == 0) ? -ENOMEM : retval; + } + return 0; +} + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 11)) +/** + * dev_bus_name() - Return a device's bus/class name, if at all possible. + * @dev: struct device to get the bus/class name of + * + * Will return the name of the bus/class the device is attached to. + * If it is not attached to a bus/class, an empty string will be returned. + */ +static inline const char* dev_bus_name(const struct device* dev) +{ + return dev->bus ? dev->bus->name : (dev->class ? dev->class->name : ""); +} +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)) +#include <linux/iommu.h> +static char* get_iommu_domain_type(struct device* dev) +{ + struct iommu_domain* domain = iommu_get_domain_for_dev(dev); + if (!domain) + return "NONE"; + else if (domain->type == IOMMU_DOMAIN_BLOCKED) + return "BLOCKED"; + else if (domain->type == IOMMU_DOMAIN_IDENTITY) + return "IDENTITY"; + else if (domain->type == IOMMU_DOMAIN_UNMANAGED) + return "UNMANAGED"; + else if (domain->type == IOMMU_DOMAIN_DMA) + return "DMA"; + else + return "UNKNOWN"; +} +#define GET_IOMMU_DOMAIN_TYPE(dev) get_iommu_domain_type(dev) +#endif + +/** + * udmabuf_object_info() - Print infomation the udmabuf object. + * @this: Pointer to the udmabuf object. + */ +static void udmabuf_object_info(struct udmabuf_object* this) +{ + dev_info(this->sys_dev, "driver version = %s\n" , DRIVER_VERSION); + dev_info(this->sys_dev, "major number = %d\n" , MAJOR(this->device_number)); + dev_info(this->sys_dev, "minor number = %d\n" , MINOR(this->device_number)); + dev_info(this->sys_dev, "phys address = %pad\n", &this->phys_addr); + dev_info(this->sys_dev, "buffer size = %zu\n" , this->alloc_size); + if (DMA_INFO_ENABLE) { + dev_info(this->sys_dev, "dma device = %s\n" , dev_name(this->dma_dev)); + dev_info(this->sys_dev, "dma bus = %s\n" , dev_bus_name(this->dma_dev)); +#if defined(IS_DMA_COHERENT) + dev_info(this->sys_dev, "dma coherent = %d\n" , IS_DMA_COHERENT(this->dma_dev)); +#endif + dev_info(this->sys_dev, "dma mask = 0x%016llx\n", dma_get_mask(this->dma_dev)); +#if defined(GET_IOMMU_DOMAIN_TYPE) + dev_info(this->sys_dev, "iommu domain = %s\n" , GET_IOMMU_DOMAIN_TYPE(this->dma_dev)); +#endif +#if (USE_QUIRK_MMAP == 1) + dev_info(this->sys_dev, "quirk mmap = %d\n" , udmabuf_quirk_mmap_enable(this)); +#endif + } +} + +/** + * udmabuf_object_destroy() - Destroy the udmabuf object. + * @this: Pointer to the udmabuf object. + * Return: Success(=0) or error status(<0). + * + * Unregister the device after releasing the resources. + */ +static int udmabuf_object_destroy(struct udmabuf_object* this) +{ + if (!this) + return -ENODEV; + + if (this->virt_addr != NULL) { + dma_free_coherent(this->dma_dev, this->alloc_size, this->virt_addr, this->phys_addr); + this->virt_addr = NULL; + } + put_device(this->dma_dev); + cdev_del(&this->cdev); + device_destroy(udmabuf_sys_class, this->device_number); + ida_simple_remove(&udmabuf_device_ida, MINOR(this->device_number)); + kfree(this); + return 0; +} + +/** + * DOC: Udmabuf Device List section. + * + * This section defines the udmabuf device list. + * + * * struct udmabuf_device_entry - udmabuf device entry structure. + * * udmabuf_device_list - list of udmabuf device entry structure. + * * udmabuf_device_list_sem - semaphore of udmabuf device list. + * * udmabuf_device_list_create_entry() - Create udmabuf device entry and add to list. + * * udmabuf_device_list_delete_entry() - Delete udmabuf device entry from list. + * * udmabuf_device_list_remove_entry() - Remove udmabuf device entry from list with remove functions. + * * udmabuf_device_list_cleanup() - Remove all udmabuf device entry from list. + * * udmabuf_device_list_search() - Search udmabuf device entry from list by name or number. + * * udmabuf_get_device_name_property() - Get "device-name" property from udmabuf device. + * * udmabuf_get_size_property() - Get "buffer-size" property from udmabuf device. + * * udmabuf_get_minor_number_property() - Get "minor-number" property from udmabuf device. + */ + +#if (USE_DEV_PROPERTY != 0) +#include <linux/property.h> +#endif + +/** + * struct udmabuf_device_entry - udmabuf device entry structure. + */ +struct udmabuf_device_entry { + struct device* dev; + void (*prep_remove)(struct device* dev); + void (*post_remove)(struct device* dev); +#if (USE_DEV_PROPERTY == 0) + const char* device_name; + u32 minor_number; + u64 buffer_size; +#endif + struct list_head list; +}; + +/** + * udmabuf_device_list - list of udmabuf device entry structure. + * udmabuf_device_list_sem - semaphore of udmabuf device list. + */ +static struct list_head udmabuf_device_list; +static struct mutex udmabuf_device_list_sem; + +/** + * udmabuf_get_device_name_property() - Get "device-name" property from udmabuf device. + * @dev: handle to the device structure. + * @name: address of device name. + * @lock: use mutex_lock()/mutex_unlock() + * Return: Success(=0) or error status(<0). + */ +static int udmabuf_get_device_name_property(struct device *dev, const char** name, bool lock) +{ +#if (USE_DEV_PROPERTY == 0) + int status = -1; + struct udmabuf_device_entry* entry; + + if (lock) + mutex_lock(&udmabuf_device_list_sem); + list_for_each_entry(entry, &udmabuf_device_list, list) { + if (entry->dev == dev) { + if (entry->device_name == NULL) { + status = -1; + } else { + *name = entry->device_name; + status = 0; + } + break; + } + } + if (lock) + mutex_unlock(&udmabuf_device_list_sem); + return status; +#else + return device_property_read_string(dev, "device-name", name); +#endif +} + +/** + * udmabuf_get_size_property() - Get "buffer-size" property from udmabuf device. + * @dev: handle to the device structure. + * @value: address of buffer size value. + * @lock: use mutex_lock()/mutex_unlock() + * Return: Success(=0) or error status(<0). + */ +static int udmabuf_get_size_property(struct device *dev, u64* value, bool lock) +{ +#if (USE_DEV_PROPERTY == 0) + int status = -1; + struct udmabuf_device_entry* entry; + + if (lock) + mutex_lock(&udmabuf_device_list_sem); + list_for_each_entry(entry, &udmabuf_device_list, list) { + if (entry->dev == dev) { + *value = entry->buffer_size; + status = 0; + break; + } + } + if (lock) + mutex_unlock(&udmabuf_device_list_sem); + return status; +#else + return device_property_read_u64(dev, "size", value); +#endif +} + +/** + * udmabuf_get_minor_number_property() - Get "minor-number" property from udmabuf device. + * @dev: handle to the device structure. + * @value: address of minor number value. + * @lock: use mutex_lock()/mutex_unlock() + * Return: Success(=0) or error status(<0). + */ + +static int udmabuf_get_minor_number_property(struct device *dev, u32* value, bool lock) +{ +#if (USE_DEV_PROPERTY == 0) + int status = -1; + struct udmabuf_device_entry* entry; + + if (lock) + mutex_lock(&udmabuf_device_list_sem); + list_for_each_entry(entry, &udmabuf_device_list, list) { + if (entry->dev == dev) { + *value = entry->minor_number; + status = 0; + break; + } + } + if (lock) + mutex_unlock(&udmabuf_device_list_sem); + return status; +#else + return device_property_read_u32(dev, "minor-number", value); +#endif +} + +/** + * udmabuf_device_list_search() - Search udmabuf device entry from list by name or number. + * @dev: handle to the device structure or NULL. + * @name: device name or NULL. + * @id: device id or negative integer. + * Return: Pointer to the found udmabuf device entry or NULL. + */ +static struct udmabuf_device_entry* udmabuf_device_list_search(struct device *dev, const char* name, int id) +{ + struct udmabuf_device_entry* entry; + struct udmabuf_device_entry* found_entry = NULL; + mutex_lock(&udmabuf_device_list_sem); + list_for_each_entry(entry, &udmabuf_device_list, list) { + bool found_by_dev = true; + bool found_by_name = true; + bool found_by_id = true; + if (dev != NULL) { + found_by_dev = false; + if (dev == entry->dev) + found_by_dev = true; + } + if (name != NULL) { + const char* device_name; + found_by_name = false; + if (udmabuf_get_device_name_property(entry->dev, &device_name, false) == 0) + if (strcmp(name, device_name) == 0) + found_by_name = true; + } + if (id >= 0) { + u32 minor_number; + found_by_id = false; + if (udmabuf_get_minor_number_property(entry->dev, &minor_number, false) == 0) + if (id == minor_number) + found_by_id = true; + } + if ((found_by_dev == true) && (found_by_name == true) && (found_by_id == true)) + found_entry = entry; + } + mutex_unlock(&udmabuf_device_list_sem); + return found_entry; +} + +/** + * udmabuf_device_list_create_entry() - Create udmabuf device entry and add to list. + * @dev: handle to the device structure. + * @name: device name or NULL. + * @id: device id or negative integer. + * @size: buffer size. + * @prep_remove prepare function when remove entry from udmabuf device list or NULL. + * @post_remove post function when remove entry from udmabuf device list or NULL. + * Return: pointer to the udmabuf device entry or NULL. + */ +static struct udmabuf_device_entry* udmabuf_device_list_create_entry(struct device *dev, const char* name, int id, unsigned int size, void (*prep_remove)(struct device*), void (*post_remove)(struct device*)) +{ + struct udmabuf_device_entry* exist_entry; + struct udmabuf_device_entry* entry = NULL; + int retval = 0; + + exist_entry = udmabuf_device_list_search(NULL, name, id); + if (!IS_ERR_OR_NULL(exist_entry)) { + pr_err(DRIVER_NAME ": device name(%s) or id(%d) is already exists\n", (name)?name:"NULL", id); + retval = -EINVAL; + goto failed; + } + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (IS_ERR_OR_NULL(entry)) { + retval = PTR_ERR(entry); + entry = NULL; + pr_err(DRIVER_NAME ": kzalloc() failed. return=%d\n", retval); + goto failed; + } + +#if (USE_DEV_PROPERTY == 0) + { + entry->device_name = (name != NULL) ? kstrdup(name, GFP_KERNEL) : NULL; + entry->minor_number = id; + entry->buffer_size = size; + } +#else + { + struct property_entry props_list[] = { + PROPERTY_ENTRY_STRING("device-name" , name), + PROPERTY_ENTRY_U64( "size" , size), + PROPERTY_ENTRY_U32( "minor-number", id ), + {}, + }; + struct property_entry* props = (name != NULL) ? &props_list[0] : &props_list[1]; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0)) + { + retval = device_create_managed_software_node(dev, props, NULL); + if (retval != 0) { + pr_err(DRIVER_NAME ": device_create_managed_software_node failed. return=%d\n", retval); + goto failed; + } + } +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) + { + retval = device_add_properties(dev, props); + if (retval != 0) { + pr_err(DRIVER_NAME ": device_add_properties failed. return=%d\n", retval); + goto failed; + } + } +#else + { + const struct property_set pset = { + .properties = props, + }; + retval = device_add_property_set(dev, &pset); + if (retval != 0) { + pr_err(DRIVER_NAME ": device_add_propertiy_set failed. return=%d\n", retval); + goto failed; + } + } +#endif + } +#endif + + entry->dev = dev; + entry->prep_remove = prep_remove; + entry->post_remove = post_remove; + + mutex_lock(&udmabuf_device_list_sem); + list_add_tail(&entry->list, &udmabuf_device_list); + mutex_unlock(&udmabuf_device_list_sem); + + return entry; + + failed: + if (entry != NULL) { +#if (USE_DEV_PROPERTY == 0) + if (entry->device_name != NULL) + kfree(entry->device_name); +#endif + kfree(entry); + } + return ERR_PTR(retval); +} + +/** + * udmabuf_device_list_delete_entry() - Delete udmabuf device entry from list. + * @entry: Pointer to the udmabuf device entry. + */ +static void udmabuf_device_list_delete_entry(struct udmabuf_device_entry* entry) +{ + mutex_lock(&udmabuf_device_list_sem); + list_del(&entry->list); + mutex_unlock(&udmabuf_device_list_sem); +#if (USE_DEV_PROPERTY == 0) + if (entry->device_name != NULL) + kfree(entry->device_name); +#endif + kfree(entry); +} + +/** + * udmabuf_device_list_remove_entry() - Remove udmabuf device entry from list with remove functions. + * @entry: Pointer to the udmabuf device entry. + */ +static void udmabuf_device_list_remove_entry(struct udmabuf_device_entry* entry) +{ + struct device* dev = entry->dev; + void (*prep_remove)(struct device* dev) = entry->prep_remove; + void (*post_remove)(struct device* dev) = entry->post_remove; + if (prep_remove) + prep_remove(dev); + udmabuf_device_list_delete_entry(entry); + if (post_remove) + post_remove(dev); +} + +/** + * udmabuf_device_list_cleanup() - Remove all udmabuf device entry from list. + */ +static void udmabuf_device_list_cleanup(void) +{ + struct udmabuf_device_entry* entry; + while(!list_empty(&udmabuf_device_list)) { + entry = list_first_entry(&udmabuf_device_list, typeof(*(entry)), list); + udmabuf_device_list_remove_entry(entry); + } +} + +/** + * DOC: Udmabuf Platform Device section. + * + * This section defines the udmabuf platform device. + * + * * udmabuf_platform_device_create() - Create udmabuf platform device and add to device list. + * * udmabuf_platform_device_del() - Delete udmabuf platform device before remove from device list. + * * udmabuf_platform_device_put() - Put udmabuf platform device after remove from device list. + * * udmabuf_platform_device_probe() - Probe call for the platform device driver. + * * udmabuf_platform_device_remove() - Remove call for the platform device driver. + */ + +/** + * udmabuf_platform_device_del() - Delete udmabuf platform device before remove from device list. + * @dev: handle to the device structure. + */ +static void udmabuf_platform_device_del(struct device* dev) +{ + /* + * platform_device_del() calls udmabuf_platform_driver_remove() + */ + platform_device_del(to_platform_device(dev)); +} + +/** + * udmabuf_platform_device_put() - Put udmabuf platform device after remove from device list. + * @dev: handle to the device structure. + */ +static void udmabuf_platform_device_put(struct device* dev) +{ + platform_device_put(to_platform_device(dev)); +} + +/** + * udmabuf_platform_device_create() - Create udmabuf platform device and add to list. + * @name: device name or NULL. + * @id: device id or negative integer. + * @size: buffer size. + * @dma_mask: dma mask or 0. + * Return: Success(=0) or error status(<0). + */ +static int udmabuf_platform_device_create(const char* name, int id, unsigned int size, u64 dma_mask) +{ + struct platform_device* pdev = NULL; + struct udmabuf_device_entry* entry = NULL; + int retval = 0; + + if (size == 0) + return -EINVAL; + + pdev = platform_device_alloc(DRIVER_NAME, id); + if (IS_ERR_OR_NULL(pdev)) { + retval = PTR_ERR(pdev); + pdev = NULL; + pr_err(DRIVER_NAME ": platform_device_alloc(%s,%d) failed. return=%d\n", DRIVER_NAME, id, retval); + goto failed; + } + + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; + + if (dma_mask != 0) { + pdev->dev.coherent_dma_mask = dma_mask; + *pdev->dev.dma_mask = dma_mask; + } else { + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(dma_mask_bit); + *pdev->dev.dma_mask = DMA_BIT_MASK(dma_mask_bit); + } + + entry = udmabuf_device_list_create_entry(&pdev->dev, + name, + id, + size, + udmabuf_platform_device_del, + udmabuf_platform_device_put); + if (IS_ERR_OR_NULL(entry)) { + retval = PTR_ERR(entry); + entry = NULL; + pr_err(DRIVER_NAME ": device create entry failed. return=%d\n", retval); + goto failed; + } + + /* + * platform_device_add() calls udmabuf_platform_driver_probe() + */ + retval = platform_device_add(pdev); + if (retval != 0) { + pr_err(DRIVER_NAME ": platform_device_add failed. return=%d\n", retval); + goto failed; + } + + if (dev_get_drvdata(&pdev->dev) == NULL) { + pr_err(DRIVER_NAME ": object of %s is none.", dev_name(&pdev->dev)); + platform_device_del(pdev); + retval = -ENODEV; + goto failed; + } + + return 0; + + failed: + if (entry != NULL) { + udmabuf_device_list_delete_entry(entry); + } + if (pdev != NULL) { + platform_device_put(pdev); + } + return retval; +} + +/** + * udmabuf_platform_device_remove() - Remove call for the platform device driver. + * @dev: handle to the device structure. + * @obj: Pointer to the udmabuf object. + * Return: Success(=0) or error status(<0). + */ +static int udmabuf_platform_device_remove(struct device *dev, struct udmabuf_object *obj) +{ + int retval = 0; + + if (obj != NULL) { +#if (USE_OF_RESERVED_MEM == 1) + bool of_reserved_mem = obj->of_reserved_mem; +#endif + retval = udmabuf_object_destroy(obj); + dev_set_drvdata(dev, NULL); +#if (USE_OF_RESERVED_MEM == 1) + if (of_reserved_mem) { + of_reserved_mem_device_release(dev); + } +#endif + } else { + retval = -ENODEV; + } + return retval; +} + +/** + * of_property_read_ulong() - Find and read a unsigned long intger from a property. + * @node: device node which the property value is to be read. + * @propname: name of property to be searched. + * @out_value: pointer to return value, modified only if return value is 0. + * Return: Success(=0) or error status(<0). + */ +static int of_property_read_ulong(const struct device_node* node, const char* propname, u64* out_value) +{ + u32 u32_value; + u64 u64_value; + int retval; + + if ((retval = of_property_read_u64(node, propname, &u64_value)) == 0) { + *out_value = u64_value; + return 0; + } + + if ((retval = of_property_read_u32(node, propname, &u32_value)) == 0) { + *out_value = (u64)u32_value; + return 0; + } + + return retval; +} + +/** + * udmabuf_platform_device_probe() - Probe call for the platform device driver. + * @dev: handle to the device structure. + * Return: Success(=0) or error status(<0). + * + * It does all the memory allocation and registration for the device. + */ +static int udmabuf_platform_device_probe(struct device *dev) +{ + int retval = 0; + int prop_status = 0; + u32 u32_value = 0; + u64 u64_value = 0; + size_t size = 0; + int minor_number = -1; + struct udmabuf_object* obj = NULL; + const char* device_name = NULL; + + /* + * size property + */ + if ((prop_status = udmabuf_get_size_property(dev, &u64_value, true)) == 0) { + size = u64_value; + } else if ((prop_status = of_property_read_ulong(dev->of_node, "size", &u64_value)) == 0) { + size = u64_value; + } else { + dev_err(dev, "invalid property size. status=%d\n", prop_status); + retval = -ENODEV; + goto failed; + } + if (size <= 0) { + dev_err(dev, "invalid size, size=%zu\n", size); + retval = -ENODEV; + goto failed; + } + /* + * minor-number property + */ + if (udmabuf_get_minor_number_property(dev, &u32_value, true) == 0) { + minor_number = u32_value; + } else if (of_property_read_u32(dev->of_node, "minor-number", &u32_value) == 0) { + minor_number = u32_value; + } else { + minor_number = -1; + } + /* + * device-name property + */ + if (udmabuf_get_device_name_property(dev, &device_name, true) != 0) + device_name = of_get_property(dev->of_node, "device-name", NULL); + if (IS_ERR_OR_NULL(device_name)) { + if (minor_number < 0) + device_name = dev_name(dev); + else + device_name = NULL; + } + /* + * udmabuf_object_create() + */ + obj = udmabuf_object_create(device_name, dev, minor_number); + if (IS_ERR_OR_NULL(obj)) { + retval = PTR_ERR(obj); + dev_err(dev, "object create failed. return=%d\n", retval); + obj = NULL; + retval = (retval == 0) ? -EINVAL : retval; + goto failed; + } + /* + * mutex_lock() then dev_set_drvdata() + */ + mutex_lock(&obj->sem); + dev_set_drvdata(dev, obj); + /* + * set size + */ + obj->size = size; + /* + * dma-mask property + * If you want to set dma-mask, do it before of_dma_configure(). + * Because of_dma_configure() needs the value of dev->coherent_dma_mask. + * However, executing dma_set_mask_and_coherent() before of_dma_configure() may fail. + * Because dma_set_mask_and_coherent() will fail unless dev->dma_ops is set. + * When dma_set_mask_and_coherent() fails, it is forcefuly setting the dma-mask value. + */ + if (of_property_read_u32(dev->of_node, "dma-mask", &u32_value) == 0) { + if ((u32_value > 64) || (u32_value < 12)) { + dev_err(dev, "invalid dma-mask property value=%d\n", u32_value); + goto failed_with_unlock; + } + retval = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(u32_value)); + if (retval != 0) { + dev_info(dev, "dma_set_mask_and_coherent(DMA_BIT_MASK(%d)) failed. return=%d\n", u32_value, retval); + retval = 0; + *dev->dma_mask = DMA_BIT_MASK(u32_value); + dev->coherent_dma_mask = DMA_BIT_MASK(u32_value); + } + } + /* + * of_reserved_mem_device_init() + */ +#if (USE_OF_RESERVED_MEM == 1) + if (dev->of_node != NULL) { + retval = of_reserved_mem_device_init(dev); + if (retval == 0) { + obj->of_reserved_mem = 1; + } else if (retval != -ENODEV) { + dev_err(dev, "of_reserved_mem_device_init failed. return=%d\n", retval); + goto failed_with_unlock; + } + } +#endif +#if (USE_OF_DMA_CONFIG == 1) + /* + * of_dma_configure() + * - set pdev->dev->dma_mask + * - set pdev->dev->coherent_dma_mask + * - call of_dma_is_coherent() + * - call arch_setup_dma_ops() + */ +#if ((USE_OF_RESERVED_MEM == 1) && (LINUX_VERSION_CODE < KERNEL_VERSION(5, 1, 0))) + /* + * Under less than Linux Kernel 5.1, if "memory-region" property is specified, + * of_dma_configure() will not be executed. + * Because in that case, it is already executed in of_reserved_mem_device_init(). + */ + if (obj->of_reserved_mem == 0) +#endif + { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)) + retval = of_dma_configure(dev, dev->of_node, true); +#else + retval = of_dma_configure(dev, dev->of_node); +#endif + if (retval != 0) { + dev_err(dev, "of_dma_configure failed. return=%d\n", retval); + goto failed_with_unlock; + } +#else + of_dma_configure(dev, dev->of_node); +#endif + } +#endif +#if (USE_QUIRK_MMAP == 1) + /* + * quirk-mmap-on property + */ + if (of_property_read_bool(dev->of_node, "quirk-mmap-on")) { + udmabuf_set_quirk_mmap_mode(obj, QUIRK_MMAP_MODE_ALWAYS_ON); + } + /* + * quirk-mmap-off property + */ + if (of_property_read_bool(dev->of_node, "quirk-mmap-off")) { + udmabuf_set_quirk_mmap_mode(obj, QUIRK_MMAP_MODE_ALWAYS_OFF); + } + /* + * quirk-mmap-auto property + */ + if (of_property_read_bool(dev->of_node, "quirk-mmap-auto")) { + udmabuf_set_quirk_mmap_mode(obj, QUIRK_MMAP_MODE_AUTO); + } +#endif + /* + * sync-mode property + */ + if (of_property_read_u32(dev->of_node, "sync-mode", &u32_value) == 0) { + if ((u32_value < SYNC_MODE_MIN) || (u32_value > SYNC_MODE_MAX)) { + dev_err(dev, "invalid sync-mode property value=%d\n", u32_value); + goto failed_with_unlock; + } + obj->sync_mode &= ~SYNC_MODE_MASK; + obj->sync_mode |= (int)u32_value; + } + /* + * sync-always property + */ + if (of_property_read_bool(dev->of_node, "sync-always")) { + obj->sync_mode |= SYNC_ALWAYS; + } + /* + * sync-direction property + */ + if (of_property_read_u32(dev->of_node, "sync-direction", &u32_value) == 0) { + if (u32_value > 2) { + dev_err(dev, "invalid sync-direction property value=%d\n", u32_value); + goto failed_with_unlock; + } + obj->sync_direction = (int)u32_value; + } + /* + * sync-offset property + */ + if (of_property_read_ulong(dev->of_node, "sync-offset", &u64_value) == 0) { + if (u64_value >= obj->size) { + dev_err(dev, "invalid sync-offset property value=%llu\n", u64_value); + goto failed_with_unlock; + } + obj->sync_offset = (int)u64_value; + } + /* + * sync-size property + */ + if (of_property_read_ulong(dev->of_node, "sync-size", &u64_value) == 0) { + if (obj->sync_offset + u64_value > obj->size) { + dev_err(dev, "invalid sync-size property value=%llu\n", u64_value); + goto failed_with_unlock; + } + obj->sync_size = (size_t)u64_value; + } else { + obj->sync_size = obj->size; + } + /* + * udmabuf_object_setup() + */ + retval = udmabuf_object_setup(obj); + if (retval) { + dev_err(dev, "object setup failed. return=%d\n", retval); + goto failed_with_unlock; + } + + mutex_unlock(&obj->sem); + + if (info_enable) { + udmabuf_object_info(obj); + } + + return 0; + + failed_with_unlock: + mutex_unlock(&obj->sem); + failed: + if (obj != NULL) { + udmabuf_platform_device_remove(dev, obj); + } else { + dev_set_drvdata(dev, NULL); + } + + return retval; +} + +/** + * DOC: Udmabuf Child Device section. + * + * This section defines the udmabuf sub device. + * + * * udmabuf_child_device_create() - Create udmabuf child device and add to device list. + * * udmabuf_child_device_delete() - Delete udmabuf child device after remove from device list. + */ + +/** + * udmabuf_child_device_delete() - Delete udmabuf child device after remove from device list. + * @dev: handle to the device structure. + */ +static void udmabuf_child_device_delete(struct device* dev) +{ + char* device_name = kstrdup(dev_name(dev), GFP_KERNEL); + + udmabuf_object_destroy(dev_get_drvdata(dev)); + + if (info_enable) { + pr_info(DRIVER_NAME ": %s removed.\n", ((device_name) ? device_name: "")); + } + if (device_name) + kfree(device_name); +} + +/** + * udmabuf_child_device_create() - Create udmabuf child device and add to list. + * @name: device name or NULL. + * @id: device id or negative integer. + * @size: buffer size. + * @parent: parent device. + * Return: Success(=0) or error status(<0). + */ +static int udmabuf_child_device_create(const char* name, int id, unsigned int size, struct device* parent) +{ + const char* device_name = NULL; + struct udmabuf_object* obj = NULL; + struct udmabuf_device_entry* entry = NULL; + int retval = 0; + + pr_debug(DRIVER_NAME ": child device create start.\n"); + + if (size == 0) + return -EINVAL; + + /* + * device-name property + */ + if ((name == NULL) && (id < 0)) + device_name = DRIVER_NAME; + else + device_name = name; + /* + * udmabuf_object_create() + */ + obj = udmabuf_object_create(device_name, parent, id); + if (IS_ERR_OR_NULL(obj)) { + retval = PTR_ERR(obj); + pr_err(DRIVER_NAME ": object create failed. return=%d\n", retval); + obj = NULL; + retval = (retval == 0) ? -EINVAL : retval; + goto failed; + } + /* + * mutex_lock() + */ + mutex_lock(&obj->sem); + /* + * set size + */ + obj->size = size; + /* + * create entry + */ + entry = udmabuf_device_list_create_entry(obj->sys_dev, + name, + id, + size, + NULL, + udmabuf_child_device_delete); + if (IS_ERR_OR_NULL(entry)) { + retval = PTR_ERR(entry); + entry = NULL; + dev_err(obj->sys_dev, "device create entry failed. return=%d\n", retval); + goto failed_with_unlock; + } + /* + * udmabuf_object_setup() + */ + retval = udmabuf_object_setup(obj); + if (retval) { + dev_err(obj->sys_dev, "object setup failed. return=%d\n", retval); + goto failed_with_unlock; + } + + mutex_unlock(&obj->sem); + + if (info_enable) { + udmabuf_object_info(obj); + } + + if (info_enable) { + pr_info(DRIVER_NAME ": %s installed.\n", dev_name(obj->sys_dev)); + } + return 0; + + failed_with_unlock: + mutex_unlock(&obj->sem); + failed: + if (entry != NULL) { + udmabuf_device_list_delete_entry(entry); + } + if (obj != NULL) { + udmabuf_object_destroy(obj); + } + return retval; +} + +/** + * DOC: Udmabuf Static Devices section. + * + * This section defines the udmabuf device to be created with arguments when loaded + * into ther kernel with insmod. + * + * * udmabuf_available_bus_type_list[] - List of bus_type available for udmabuf static device. + * * udmabuf_find_available_bus_type() - Find available bus_type by name. + * * udmabuf_static_parent_device - Parent device of udmabuf static device or NULL or ERR_PTR. + * * udmabuf_static_device_create() - Create udmabuf static device and add to list. + */ +/** + * * udmabuf_available_bus_type_list[] - List of bus_type available for udmabuf static device. + */ +#if defined(CONFIG_ARM_AMBA) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)) +extern struct bus_type amba_bustype; +#define AMBA_BUS_TYPE &amba_bustype, +#else +#define AMBA_BUS_TYPE +#endif +#if defined(CONFIG_PCI) +extern struct bus_type pci_bus_type; +#define PCI_BUS_TYPE &pci_bus_type, +#else +#define PCI_BUS_TYPE +#endif +#if defined(CONFIG_PCIEPORTBUS) && (LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)) +extern struct bus_type pcie_port_bus_type; +#define PCIE_PORT_BUS_TYPE &pcie_port_bus_type, +#else +#define PCIE_PORT_BUS_TYPE +#endif + +static struct bus_type* udmabuf_available_bus_type_list[] = { + AMBA_BUS_TYPE + PCI_BUS_TYPE + PCIE_PORT_BUS_TYPE + NULL +}; + +/** + * udmabuf_find_available_bus_type() - Find available bus_type by name. + * @name: bus name string. + * @name_len: length of @name. + * Return: pointer to the bus_type or NULL. + */ +static struct bus_type* udmabuf_find_available_bus_type(char* name, int name_len) +{ + int i; + if ((name == NULL) || (name_len == 0)) + return NULL; + for (i = 0; udmabuf_available_bus_type_list[i] != NULL; i++) { + const char* bus_name; + if (udmabuf_available_bus_type_list[i] == NULL) + break; + bus_name = udmabuf_available_bus_type_list[i]->name; + if (name_len != strlen(bus_name)) + continue; + if (strncmp(name, bus_name, name_len) == 0) + break; + } + return udmabuf_available_bus_type_list[i]; +} + +/** + * udmabuf_static_bind_parse() - Parse bind string to get bus_type and device_name. + * @bind: string to parse. + * @bus_type: pointer to store bus_type found. + * @device_name: pointer to store device_name found. + * Return: Success(=0) or error status(<0). + */ +static int udmabuf_static_parse_bind(char* bind, struct bus_type** bus_type, char** device_name) +{ + int retval = 0; + char* next_ptr = strchr(bind, '/'); + + if (!next_ptr) { + *bus_type = &platform_bus_type; + *device_name = bind; + retval = 0; + } else { + char* name = bind; + int name_len = next_ptr - bind; + struct bus_type* found_bus_type = udmabuf_find_available_bus_type(name, name_len); + if (found_bus_type == NULL) { + retval = -EINVAL; + } else { + *bus_type = found_bus_type; + *device_name = next_ptr+1; + retval = 0; + } + } + return retval; +} + +/** + * udmabuf_static_parent_device - Parent device of udmabuf static device or NULL or ERR_PTR. + */ +static struct device* udmabuf_static_parent_device = NULL; + +/** + * udmabuf_static_device_create() - Create udmabuf static device and add to list. + * @name: device name or NULL. + * @id: device id or negative integer. + * @size: buffer size. + * Return: Success(=0) or error status(<0). + */ +static void udmabuf_static_device_create(const char* name, int id, unsigned int size) +{ + if ((bind != NULL) && (udmabuf_static_parent_device == NULL)) { + struct device* parent = NULL; + struct bus_type* bus_type = NULL; + char* device_name = NULL; + int retval; + retval = udmabuf_static_parse_bind(bind, &bus_type, &device_name); + if (retval) { + udmabuf_static_parent_device = ERR_PTR(-EINVAL); + pr_err(DRIVER_NAME ": bind error: %s is not support bus\n", bind); + return; + } + parent = bus_find_device_by_name(bus_type, NULL, device_name); + if (IS_ERR_OR_NULL(parent)) { + udmabuf_static_parent_device = (parent == NULL)? ERR_PTR(-EINVAL) : parent; + pr_err(DRIVER_NAME ": bind error: device(%s) not found in bus(%s)\n", device_name, bus_type->name); + return; + } else { + udmabuf_static_parent_device = parent; + } + } + + if (IS_ERR(udmabuf_static_parent_device)) + return; + + if (udmabuf_static_parent_device) + udmabuf_child_device_create(name, id, size, udmabuf_static_parent_device); + else + udmabuf_platform_device_create(name, id, size, 0); +} + +#define DEFINE_UDMABUF_STATIC_DEVICE_PARAM(__num) \ + static ulong udmabuf ## __num = 0; \ + module_param( udmabuf ## __num, ulong, S_IRUGO); \ + MODULE_PARM_DESC(udmabuf ## __num, DRIVER_NAME #__num " buffer size"); + +#define CALL_UDMABUF_STATIC_DEVICE_CREATE(__num) \ + if (udmabuf ## __num != 0) { \ + ida_simple_remove(&udmabuf_device_ida, __num); \ + udmabuf_static_device_create(NULL, __num, udmabuf ## __num); \ + } + +#define CALL_UDMABUF_STATIC_DEVICE_RESERVE_MINOR_NUMBER(__num) \ + if (udmabuf ## __num != 0) { \ + ida_simple_get(&udmabuf_device_ida, __num, __num+1, GFP_KERNEL); \ + } + +DEFINE_UDMABUF_STATIC_DEVICE_PARAM(0); +DEFINE_UDMABUF_STATIC_DEVICE_PARAM(1); +DEFINE_UDMABUF_STATIC_DEVICE_PARAM(2); +DEFINE_UDMABUF_STATIC_DEVICE_PARAM(3); +DEFINE_UDMABUF_STATIC_DEVICE_PARAM(4); +DEFINE_UDMABUF_STATIC_DEVICE_PARAM(5); +DEFINE_UDMABUF_STATIC_DEVICE_PARAM(6); +DEFINE_UDMABUF_STATIC_DEVICE_PARAM(7); + +/** + * udmabuf_static_device_reserve_minor_number_all() - Reserve udmabuf static device's minor-number. + */ +static void udmabuf_static_device_reserve_minor_number_all(void) +{ + CALL_UDMABUF_STATIC_DEVICE_RESERVE_MINOR_NUMBER(0); + CALL_UDMABUF_STATIC_DEVICE_RESERVE_MINOR_NUMBER(1); + CALL_UDMABUF_STATIC_DEVICE_RESERVE_MINOR_NUMBER(2); + CALL_UDMABUF_STATIC_DEVICE_RESERVE_MINOR_NUMBER(3); + CALL_UDMABUF_STATIC_DEVICE_RESERVE_MINOR_NUMBER(4); + CALL_UDMABUF_STATIC_DEVICE_RESERVE_MINOR_NUMBER(5); + CALL_UDMABUF_STATIC_DEVICE_RESERVE_MINOR_NUMBER(6); + CALL_UDMABUF_STATIC_DEVICE_RESERVE_MINOR_NUMBER(7); +} + +/** + * udmabuf_static_device_create_all() - Create udmabuf static devices. + */ +static int udmabuf_static_device_create_all(void) +{ + CALL_UDMABUF_STATIC_DEVICE_CREATE(0); + CALL_UDMABUF_STATIC_DEVICE_CREATE(1); + CALL_UDMABUF_STATIC_DEVICE_CREATE(2); + CALL_UDMABUF_STATIC_DEVICE_CREATE(3); + CALL_UDMABUF_STATIC_DEVICE_CREATE(4); + CALL_UDMABUF_STATIC_DEVICE_CREATE(5); + CALL_UDMABUF_STATIC_DEVICE_CREATE(6); + CALL_UDMABUF_STATIC_DEVICE_CREATE(7); + return (IS_ERR(udmabuf_static_parent_device))? PTR_ERR(udmabuf_static_parent_device) : 0; +} + +/** + * DOC: Udmabuf Platform Driver section. + * + * This section defines the udmabuf platform driver. + * + * * udmabuf_platform_driver_probe() - Probe call for platform_device_add(). + * * udmabuf_platform_driver_remove() - Remove call for platform_device_del(). + * * udmabuf_of_match - Open Firmware Device Identifier Matching Table. + * * udmabuf_platform_driver - Platform Driver Structure. + */ + +/** + * udmabuf_platform_driver_probe() - Probe call for the device. + * @pdev: Handle to the platform device structure. + * Return: Success(=0) or error status(<0). + * + * It does all the memory allocation and registration for the device. + */ +static int udmabuf_platform_driver_probe(struct platform_device *pdev) +{ + int retval = 0; + + dev_dbg(&pdev->dev, "driver probe start.\n"); + + retval = udmabuf_platform_device_probe(&pdev->dev); + + if (retval != 0) { + dev_err(&pdev->dev, "driver probe failed. return=%d\n", retval); + } else if (info_enable) { + dev_info(&pdev->dev, "driver installed.\n"); + } + return retval; +} +/** + * udmabuf_platform_driver_remove() - Remove call for the device. + * @pdev: Handle to the platform device structure. + * Return: Success(=0) or error status(<0). + * + * Unregister the device after releasing the resources. + */ +static int udmabuf_platform_driver_remove(struct platform_device *pdev) +{ + struct udmabuf_object* this = dev_get_drvdata(&pdev->dev); + int retval = 0; + + dev_dbg(&pdev->dev, "driver remove start.\n"); + + retval = udmabuf_platform_device_remove(&pdev->dev, this); + + if (retval != 0) { + dev_err(&pdev->dev, "driver remove failed. return=%d\n", retval); + } else if (info_enable) { + dev_info(&pdev->dev, "driver removed.\n"); + } + return retval; +} + +/** + * Open Firmware Device Identifier Matching Table + */ +static struct of_device_id udmabuf_of_match[] = { + { .compatible = "ikwzm,u-dma-buf", }, + { /* end of table */} +}; +MODULE_DEVICE_TABLE(of, udmabuf_of_match); + +/** + * Platform Driver Structure + */ +static struct platform_driver udmabuf_platform_driver = { + .probe = udmabuf_platform_driver_probe, + .remove = udmabuf_platform_driver_remove, + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .of_match_table = udmabuf_of_match, + }, +}; + +/** + * DOC: u-dma-buf Device In-Kernel Interface. + * + * * u_dma_buf_device_search() - Search u-dma-buf device by name or id. + * * u_dma_buf_device_create() - Create u-dma-buf device for in-kernel. + * * u_dma_buf_device_remove() - Remove u-dma-buf device for in-kernel. + * * u_dma_buf_device_getmap() - Get mapping information from u-dma-buf device for in-kernel. + * * u_dma_buf_device_sync() - Sync for CPU/Device u-dma-buf device for in-kernel. + * * u_dma_buf_find_available_bus_type() - Find available bus_type by name. + * * u_dma_buf_available_bus_type_list[] - List of bus_type available by u-dma-buf. + */ +/** + * u_dma_buf_device_search() - Search u-dma-buf device by name or id. + * @name: device name or NULL. + * @id: device id or negative integer. + * Return: handle to u-dma-buf device structure(>=0) or error status(<0). + */ +#if (IN_KERNEL_FUNCTIONS == 1) +struct device* u_dma_buf_device_search(const char* name, int id) +{ + struct udmabuf_device_entry* entry = udmabuf_device_list_search(NULL, name, id); + + if (entry == NULL) + return ERR_PTR(-ENODEV); + else + return entry->dev; +} +EXPORT_SYMBOL(u_dma_buf_device_search); +#endif + +/** + * u_dma_buf_device_option_dma_mask_size() - Get dma mask size from create device option. + * + * @option: option. dma_mask=option[7:0] + */ +#if (IN_KERNEL_FUNCTIONS == 1) +#define DEFINE_U_DMA_BUF_OPTION(name,type,lo,hi) \ +static inline type u_dma_buf_device_option_ ## name(u64 option) \ +{ \ + const u64 mask = ((1 << ((hi)-(lo)+1))-1); \ + return (type)((option >> (lo)) & mask); \ +} +DEFINE_U_DMA_BUF_OPTION(dma_mask_size ,u64, 0, 7) +DEFINE_U_DMA_BUF_OPTION(quirk_mmap_mode,int,10,11) +#endif + +/** + * u_dma_buf_device_create() - Create u-dma-buf device for in-kernel. + * @name: device name or NULL. + * @id: device id or negative integer. + * @size: buffer size. + * @option: option. dma_mask=option[7:0], quirk_mmap_mode=option[11:10] + * @parent: parent device or NULL. + * Return: handle to u-dma-buf device structure(>=0) or error status(<0). + */ +#if (IN_KERNEL_FUNCTIONS == 1) +struct device* u_dma_buf_device_create(const char* name, int id, size_t size, u64 option, struct device* parent) +{ + int result = 0; + struct device* dev; + + if (parent) { + result = udmabuf_child_device_create(name, id, size, parent); + } else { + u64 dma_mask = DMA_BIT_MASK(u_dma_buf_device_option_dma_mask_size(option)); + result = udmabuf_platform_device_create(name, id, size, dma_mask); + } + + if (result) + return ERR_PTR(result); + + dev = u_dma_buf_device_search(name, id); +#if (USE_QUIRK_MMAP == 1) + if (!IS_ERR_OR_NULL(dev)) { + int quirk_mmap_mode = u_dma_buf_device_option_quirk_mmap_mode(option); + udmabuf_set_quirk_mmap_mode(dev_get_drvdata(dev), quirk_mmap_mode); + } +#endif + return dev; +} +EXPORT_SYMBOL(u_dma_buf_device_create); +#endif + +/** + * u_dma_buf_device_remove() - Remove u-dma-buf device for in-kernel. + * @dev: handle to the u-dma-buf device structure. + * Return: Success(=0) or error status(<0). + */ +#if (IN_KERNEL_FUNCTIONS == 1) +int u_dma_buf_device_remove(struct device *dev) +{ + struct udmabuf_device_entry* entry = udmabuf_device_list_search(dev, NULL, -1); + if (entry == NULL) + return -EINVAL; + + udmabuf_device_list_remove_entry(entry); + return 0; +} +EXPORT_SYMBOL(u_dma_buf_device_remove); +#endif + +/** + * u_dma_buf_device_getmap() - Get mapping information from u-dma-buf device for in-kernel. + * @dev: handle to the u-dma-buf device structure. + * @size Pointer to the buffer size for output. + * @virt_addr Pointer to the virtual address for output. + * @phys_addr Pointer to the physical address for output. + * Return: Success(=0) or error status(<0). + */ +#if (IN_KERNEL_FUNCTIONS == 1) +int u_dma_buf_device_getmap(struct device *dev, size_t* size, void** virt_addr, dma_addr_t* phys_addr) +{ + struct udmabuf_device_entry* entry; + struct udmabuf_object* this; + + entry = udmabuf_device_list_search(dev, NULL, -1); + if (entry == NULL) + return -EINVAL; + + this = dev_get_drvdata(entry->dev); + if (this == NULL) + return -ENODEV; + + if (!mutex_trylock(&this->sem)) + return -EBUSY; + + if (size != NULL) {*size = this->size ;} + if (virt_addr != NULL) {*virt_addr = this->virt_addr;} + if (phys_addr != NULL) {*phys_addr = this->phys_addr;} + + mutex_unlock(&this->sem); + return 0; +} +EXPORT_SYMBOL(u_dma_buf_device_getmap); +#endif + +/** + * u_dma_buf_device_sync() - Sync for CPU/Device u-dma-buf device for in-kernel. + * @dev: handle to the u-dma-buf device structure. + * @command sync command (no_op=0, sync_for_cpu=1, sync_for_device=2) + * @direction sync direction (0 = DMA_BIDIRECTIONAL, 1 = DMA_TO_DEVICE, 2 = DMA_FROM_DEVICE) + * @offset sync offset. + * @size sync size. + * Return: Success(=0) or error status(<0). + */ +#if (IN_KERNEL_FUNCTIONS == 1) +int u_dma_buf_device_sync(struct device *dev, int command, int direction, u64 offset, ssize_t size) +{ + struct udmabuf_device_entry* entry; + struct udmabuf_object* this; + int result = 0; + + entry = udmabuf_device_list_search(dev, NULL, -1); + if (entry == NULL) + return -EINVAL; + + this = dev_get_drvdata(entry->dev); + if (this == NULL) + return -ENODEV; + + if (!mutex_trylock(&this->sem)) + return -EBUSY; + + switch(direction) { + case 0 : this->sync_direction = 0; break; + case 1 : this->sync_direction = 1; break; + case 2 : this->sync_direction = 2; break; + default : /* none */ break; + } + if (offset >= 0) {this->sync_offset = offset;} + if (size > 0) {this->sync_size = size ;} + + switch (command) { + case 0 : + result = 0; + break; + case 1 : + this->sync_for_cpu = 1; + result = udmabuf_sync_for_cpu(this); + break; + case 2 : + this->sync_for_device = 1; + result = udmabuf_sync_for_device(this); + break; + default: + result = -EINVAL; + break; + } + + mutex_unlock(&this->sem); + return result; +} +EXPORT_SYMBOL(u_dma_buf_device_sync); +#endif + +/** + * u_dma_buf_find_available_bus_type() - Find available bus_type by name. + * @name: bus name string. + * @name_len: length of @name. + * Return: pointer to the bus_type or NULL. + */ +#if (IN_KERNEL_FUNCTIONS == 1) +struct bus_type* u_dma_buf_find_available_bus_type(char* name, int name_len) +{ + return udmabuf_find_available_bus_type(name, name_len); +} +EXPORT_SYMBOL(u_dma_buf_find_available_bus_type); +#endif + +/** + * u_dma_buf_available_bus_type_list[] - List of bus_type available by u-dma-buf. + */ +#if (IN_KERNEL_FUNCTIONS == 1) +struct bus_type** u_dma_buf_available_bus_type_list = &udmabuf_available_bus_type_list[0]; +EXPORT_SYMBOL(u_dma_buf_available_bus_type_list); +#endif + +/** + * DOC: u-dma-buf Kernel Module Operations. + * + * * u_dma_buf_cleanup() + * * u_dma_buf_init() + * * u_dma_buf_exit() + */ + +static bool udmabuf_platform_driver_registerd = false; + +/** + * u_dma_buf_cleanup() + */ +static void u_dma_buf_cleanup(void) +{ + udmabuf_device_list_cleanup(); + if (udmabuf_platform_driver_registerd){platform_driver_unregister(&udmabuf_platform_driver);} + if (udmabuf_sys_class != NULL ){class_destroy(udmabuf_sys_class);} + if (udmabuf_device_number != 0 ){unregister_chrdev_region(udmabuf_device_number, 0);} + ida_destroy(&udmabuf_device_ida); +} + +/** + * u_dma_buf_init() + */ +static int __init u_dma_buf_init(void) +{ + int retval = 0; + + if (CONFIG_INFO_ENABLE) { + #define TO_STR(x) #x + #define NUM_TO_STR(x) TO_STR(x) + pr_info(DRIVER_NAME ": " + "DEVICE_MAX_NUM=" NUM_TO_STR(DEVICE_MAX_NUM) "," + "UDMABUF_DEBUG=" NUM_TO_STR(UDMABUF_DEBUG) "," + "USE_QUIRK_MMAP=" NUM_TO_STR(USE_QUIRK_MMAP) "," + #if defined(IS_DMA_COHERENT) + "IS_DMA_COHERENT=1," + #endif + "USE_DEV_GROUPS=" NUM_TO_STR(USE_DEV_GROUPS) "," + "USE_OF_RESERVED_MEM=" NUM_TO_STR(USE_OF_RESERVED_MEM) "," + "USE_OF_DMA_CONFIG=" NUM_TO_STR(USE_OF_DMA_CONFIG) "," + "USE_DEV_PROPERTY=" NUM_TO_STR(USE_DEV_PROPERTY) "," + "IN_KERNEL_FUNCTIONS=" NUM_TO_STR(IN_KERNEL_FUNCTIONS) ); + } + + ida_init(&udmabuf_device_ida); + INIT_LIST_HEAD(&udmabuf_device_list); + mutex_init(&udmabuf_device_list_sem); + + retval = alloc_chrdev_region(&udmabuf_device_number, 0, 0, DRIVER_NAME); + if (retval != 0) { + pr_err(DRIVER_NAME ": couldn't allocate device major number. return=%d\n", retval); + udmabuf_device_number = 0; + goto failed; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0) + udmabuf_sys_class = class_create(THIS_MODULE, DRIVER_NAME); +#else + udmabuf_sys_class = class_create(DRIVER_NAME); +#endif + if (IS_ERR_OR_NULL(udmabuf_sys_class)) { + retval = PTR_ERR(udmabuf_sys_class); + udmabuf_sys_class = NULL; + pr_err(DRIVER_NAME ": couldn't create sys class. return=%d\n", retval); + retval = (retval == 0) ? -ENOMEM : retval; + goto failed; + } + + udmabuf_sys_class_set_attributes(); + + udmabuf_static_device_reserve_minor_number_all(); + + retval = platform_driver_register(&udmabuf_platform_driver); + if (retval) { + pr_err(DRIVER_NAME ": couldn't register platform driver. return=%d\n", retval); + udmabuf_platform_driver_registerd = false; + goto failed; + } else { + udmabuf_platform_driver_registerd = true; + } + + retval = udmabuf_static_device_create_all(); + if (retval) { + pr_err(DRIVER_NAME ": couldn't create static devices. return=%d\n", retval); + goto failed; + } + + return 0; + + failed: + u_dma_buf_cleanup(); + return retval; +} + +/** + * u_dma_buf_exit() + */ +static void __exit u_dma_buf_exit(void) +{ + u_dma_buf_cleanup(); +} + +module_init(u_dma_buf_init); +module_exit(u_dma_buf_exit); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/firmware/Kconfig linux-microchip/drivers/firmware/Kconfig --- linux-6.6.51/drivers/firmware/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/firmware/Kconfig 2024-11-26 14:02:36.474475353 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:314 @ source "drivers/firmware/efi/Kconfig" source "drivers/firmware/imx/Kconfig" source "drivers/firmware/meson/Kconfig" +source "drivers/firmware/microchip/Kconfig" source "drivers/firmware/psci/Kconfig" source "drivers/firmware/smccc/Kconfig" source "drivers/firmware/tegra/Kconfig" diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/firmware/Makefile linux-microchip/drivers/firmware/Makefile --- linux-6.6.51/drivers/firmware/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/firmware/Makefile 2024-11-26 14:02:36.474475353 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:34 @ obj-y += broadcom/ obj-y += cirrus/ obj-y += meson/ +obj-y += microchip/ obj-$(CONFIG_GOOGLE_FIRMWARE) += google/ obj-y += efi/ obj-y += imx/ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/firmware/microchip/Kconfig linux-microchip/drivers/firmware/microchip/Kconfig --- linux-6.6.51/drivers/firmware/microchip/Kconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/firmware/microchip/Kconfig 2024-11-26 14:02:36.482475497 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: GPL-2.0-only + +config POLARFIRE_SOC_AUTO_UPDATE + tristate "Microchip PolarFire SoC AUTO UPDATE" + depends on POLARFIRE_SOC_SYS_CTRL + select FW_LOADER + select FW_UPLOAD + help + Support for reprogramming PolarFire SoC from within Linux, using the + Auto Upgrade feature of the system controller. + + If built as a module, it will be called mpfs-auto-update. diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/firmware/microchip/Makefile linux-microchip/drivers/firmware/microchip/Makefile --- linux-6.6.51/drivers/firmware/microchip/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/firmware/microchip/Makefile 2024-11-26 14:02:36.482475497 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1 @ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_POLARFIRE_SOC_AUTO_UPDATE) += mpfs-auto-update.o diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/firmware/microchip/mpfs-auto-update.c linux-microchip/drivers/firmware/microchip/mpfs-auto-update.c --- linux-6.6.51/drivers/firmware/microchip/mpfs-auto-update.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/firmware/microchip/mpfs-auto-update.c 2024-11-26 14:04:18.560303308 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microchip Polarfire SoC "Auto Update" FPGA reprogramming. + * + * Documentation of this functionality is available in the "PolarFire® FPGA and + * PolarFire SoC FPGA Programming" User Guide. + * + * Copyright (c) 2022-2023 Microchip Corporation. All rights reserved. + * + * Author: Conor Dooley <conor.dooley@microchip.com> + */ +#include <linux/debugfs.h> +#include <linux/firmware.h> +#include <linux/math.h> +#include <linux/module.h> +#include <linux/mtd/mtd.h> +#include <linux/platform_device.h> +#include <linux/sizes.h> + +#include <soc/microchip/mpfs.h> + +#define AUTO_UPDATE_DEFAULT_MBOX_OFFSET 0u +#define AUTO_UPDATE_DEFAULT_RESP_OFFSET 0u + +#define AUTO_UPDATE_FEATURE_CMD_OPCODE 0x05u +#define AUTO_UPDATE_FEATURE_CMD_DATA_SIZE 0u +#define AUTO_UPDATE_FEATURE_RESP_SIZE 33u +#define AUTO_UPDATE_FEATURE_CMD_DATA NULL +#define AUTO_UPDATE_FEATURE_ENABLED BIT(5) + +#define AUTO_UPDATE_AUTHENTICATE_CMD_OPCODE 0x22u +#define AUTO_UPDATE_AUTHENTICATE_CMD_DATA_SIZE 0u +#define AUTO_UPDATE_AUTHENTICATE_RESP_SIZE 1u +#define AUTO_UPDATE_AUTHENTICATE_CMD_DATA NULL + +#define AUTO_UPDATE_PROGRAM_CMD_OPCODE 0x46u +#define AUTO_UPDATE_PROGRAM_CMD_DATA_SIZE 0u +#define AUTO_UPDATE_PROGRAM_RESP_SIZE 1u +#define AUTO_UPDATE_PROGRAM_CMD_DATA NULL + +/* + * SPI Flash layout example: + * |------------------------------| 0x0000000 + * | 1 KiB | + * | SPI "directories" | + * |------------------------------| 0x0000400 + * | 1 MiB | + * | Reserved area | + * | Used for bitstream info | + * |------------------------------| 0x0100400 + * | 20 MiB | + * | Golden Image | + * |------------------------------| 0x1500400 + * | 20 MiB | + * | Auto Upgrade Image | + * |------------------------------| 0x2900400 + * | 20 MiB | + * | Reserved for multi-image IAP | + * | Unused for Auto Upgrade | + * |------------------------------| 0x3D00400 + * | ? B | + * | Unused | + * |------------------------------| 0x? + */ +#define AUTO_UPDATE_DIRECTORY_BASE 0u +#define AUTO_UPDATE_DIRECTORY_WIDTH 4u +#define AUTO_UPDATE_GOLDEN_INDEX 0u +#define AUTO_UPDATE_UPGRADE_INDEX 1u +#define AUTO_UPDATE_BLANK_INDEX 2u +#define AUTO_UPDATE_GOLDEN_DIRECTORY (AUTO_UPDATE_DIRECTORY_WIDTH * AUTO_UPDATE_GOLDEN_INDEX) +#define AUTO_UPDATE_UPGRADE_DIRECTORY (AUTO_UPDATE_DIRECTORY_WIDTH * AUTO_UPDATE_UPGRADE_INDEX) +#define AUTO_UPDATE_BLANK_DIRECTORY (AUTO_UPDATE_DIRECTORY_WIDTH * AUTO_UPDATE_BLANK_INDEX) +#define AUTO_UPDATE_DIRECTORY_SIZE SZ_1K +#define AUTO_UPDATE_INFO_BASE AUTO_UPDATE_DIRECTORY_SIZE +#define AUTO_UPDATE_INFO_SIZE SZ_1M +#define AUTO_UPDATE_BITSTREAM_BASE (AUTO_UPDATE_DIRECTORY_SIZE + AUTO_UPDATE_INFO_SIZE) + +#define AUTO_UPDATE_TIMEOUT_MS 60000 + +struct mpfs_auto_update_priv { + struct mpfs_sys_controller *sys_controller; + struct device *dev; + struct mtd_info *flash; + struct fw_upload *fw_uploader; + struct completion programming_complete; + size_t size_per_bitstream; + bool cancel_request; +}; + +static bool mpfs_auto_update_is_bitstream_info(const u8 *data, u32 size) +{ + if (size < 4) + return false; + + if (data[0] == 0x4d && data[1] == 0x43 && data[2] == 0x48 && data[3] == 0x50) + return true; + + return false; +} + +static enum fw_upload_err mpfs_auto_update_prepare(struct fw_upload *fw_uploader, const u8 *data, + u32 size) +{ + struct mpfs_auto_update_priv *priv = fw_uploader->dd_handle; + size_t erase_size = AUTO_UPDATE_DIRECTORY_SIZE; + + /* + * Verifying the Golden Image is idealistic. It will be evaluated + * against the currently programmed image and thus may fail - due to + * either rollback protection (if its an older version than that in use) + * or if the version is the same as that of the in-use image. + * Extracting the information as to why a failure occurred is not + * currently possible due to limitations of the system controller + * driver. If those are fixed, verification of the Golden Image should + * be added here. + */ + + priv->flash = mpfs_sys_controller_get_flash(priv->sys_controller); + if (!priv->flash) + return FW_UPLOAD_ERR_HW_ERROR; + + erase_size = round_up(erase_size, (u64)priv->flash->erasesize); + + /* + * We need to calculate if we have enough space in the flash for the + * new image. + * First, chop off the first 1 KiB as it's reserved for the directory. + * The 1 MiB reserved for design info needs to be ignored also. + * All that remains is carved into 3 & rounded down to the erasesize. + * If this is smaller than the image size, we abort. + * There's also no need to consume more than 20 MiB per image. + */ + priv->size_per_bitstream = priv->flash->size - SZ_1K - SZ_1M; + priv->size_per_bitstream = round_down(priv->size_per_bitstream / 3, erase_size); + if (priv->size_per_bitstream > 20 * SZ_1M) + priv->size_per_bitstream = 20 * SZ_1M; + + if (priv->size_per_bitstream < size) { + dev_err(priv->dev, + "flash device has insufficient capacity to store this bitstream\n"); + return FW_UPLOAD_ERR_INVALID_SIZE; + } + + priv->cancel_request = false; + + return FW_UPLOAD_ERR_NONE; +} + +static void mpfs_auto_update_cancel(struct fw_upload *fw_uploader) +{ + struct mpfs_auto_update_priv *priv = fw_uploader->dd_handle; + + priv->cancel_request = true; +} + +static enum fw_upload_err mpfs_auto_update_poll_complete(struct fw_upload *fw_uploader) +{ + struct mpfs_auto_update_priv *priv = fw_uploader->dd_handle; + int ret; + + /* + * There is no meaningful way to get the status of the programming while + * it is in progress, so attempting anything other than waiting for it + * to complete would be misplaced. + */ + ret = wait_for_completion_timeout(&priv->programming_complete, + msecs_to_jiffies(AUTO_UPDATE_TIMEOUT_MS)); + if (!ret) + return FW_UPLOAD_ERR_TIMEOUT; + + return FW_UPLOAD_ERR_NONE; +} + +static int mpfs_auto_update_verify_image(struct fw_upload *fw_uploader) +{ + struct mpfs_auto_update_priv *priv = fw_uploader->dd_handle; + struct mpfs_mss_response *response; + struct mpfs_mss_msg *message; + u32 *response_msg; + int ret; + + response_msg = devm_kzalloc(priv->dev, AUTO_UPDATE_FEATURE_RESP_SIZE * sizeof(response_msg), + GFP_KERNEL); + if (!response_msg) + return -ENOMEM; + + response = devm_kzalloc(priv->dev, sizeof(struct mpfs_mss_response), GFP_KERNEL); + if (!response) { + ret = -ENOMEM; + goto free_response_msg; + } + + message = devm_kzalloc(priv->dev, sizeof(struct mpfs_mss_msg), GFP_KERNEL); + if (!message) { + ret = -ENOMEM; + goto free_response; + } + + /* + * The system controller can verify that an image in the flash is valid. + * Rather than duplicate the check in this driver, call the relevant + * service from the system controller instead. + * This service has no command data and no response data. It overloads + * mbox_offset with the image index in the flash's SPI directory where + * the bitstream is located. + */ + response->resp_msg = response_msg; + response->resp_size = AUTO_UPDATE_AUTHENTICATE_RESP_SIZE; + message->cmd_opcode = AUTO_UPDATE_AUTHENTICATE_CMD_OPCODE; + message->cmd_data_size = AUTO_UPDATE_AUTHENTICATE_CMD_DATA_SIZE; + message->response = response; + message->cmd_data = AUTO_UPDATE_AUTHENTICATE_CMD_DATA; + message->mbox_offset = AUTO_UPDATE_UPGRADE_INDEX; + message->resp_offset = AUTO_UPDATE_DEFAULT_RESP_OFFSET; + + dev_info(priv->dev, "Running verification of Upgrade Image\n"); + ret = mpfs_blocking_transaction(priv->sys_controller, message); + if (ret | response->resp_status) { + dev_warn(priv->dev, "Verification of Upgrade Image failed!\n"); + ret = ret ? ret : -EBADMSG; + } + + dev_info(priv->dev, "Verification of Upgrade Image passed!\n"); + + devm_kfree(priv->dev, message); +free_response: + devm_kfree(priv->dev, response); +free_response_msg: + devm_kfree(priv->dev, response_msg); + + return ret; +} + +static int mpfs_auto_update_set_image_address(struct mpfs_auto_update_priv *priv, char *buffer, + u32 image_address, loff_t directory_address) +{ + struct erase_info erase; + size_t erase_size = AUTO_UPDATE_DIRECTORY_SIZE; + size_t bytes_written = 0, bytes_read = 0; + int ret; + + erase_size = round_up(erase_size, (u64)priv->flash->erasesize); + + erase.addr = AUTO_UPDATE_DIRECTORY_BASE; + erase.len = erase_size; + + /* + * We need to write the "SPI DIRECTORY" to the first 1 KiB, telling + * the system controller where to find the actual bitstream. Since + * this is spi-nor, we have to read the first eraseblock, erase that + * portion of the flash, modify the data and then write it back. + * There's no need to do this though if things are already the way they + * should be, so check and save the write in that case. + */ + ret = mtd_read(priv->flash, AUTO_UPDATE_DIRECTORY_BASE, erase_size, &bytes_read, + (u_char *)buffer); + if (ret) + return ret; + + if (bytes_read != erase_size) + return -EIO; + + if ((*(u32 *)(buffer + AUTO_UPDATE_UPGRADE_DIRECTORY) == image_address) && + !(*(u32 *)(buffer + AUTO_UPDATE_BLANK_DIRECTORY))) + return 0; + + ret = mtd_erase(priv->flash, &erase); + if (ret) + return ret; + + /* + * Populate the image address and then zero out the next directory so + * that the system controller doesn't complain if in "Single Image" + * mode. + */ + memcpy(buffer + AUTO_UPDATE_UPGRADE_DIRECTORY, &image_address, + AUTO_UPDATE_DIRECTORY_WIDTH); + memset(buffer + AUTO_UPDATE_BLANK_DIRECTORY, 0x0, AUTO_UPDATE_DIRECTORY_WIDTH); + + dev_info(priv->dev, "Writing the image address (%x) to the flash directory (%llx)\n", + image_address, directory_address); + + ret = mtd_write(priv->flash, 0x0, erase_size, &bytes_written, (u_char *)buffer); + if (ret) + return ret; + + if (bytes_written != erase_size) + return ret; + + return 0; +} + +static int mpfs_auto_update_write_bitstream(struct fw_upload *fw_uploader, const u8 *data, + u32 offset, u32 size, u32 *written) +{ + struct mpfs_auto_update_priv *priv = fw_uploader->dd_handle; + struct erase_info erase; + char *buffer; + loff_t directory_address = AUTO_UPDATE_UPGRADE_DIRECTORY; + size_t erase_size = AUTO_UPDATE_DIRECTORY_SIZE; + size_t bytes_written = 0; + bool is_info = mpfs_auto_update_is_bitstream_info(data, size); + u32 image_address; + int ret; + + erase_size = round_up(erase_size, (u64)priv->flash->erasesize); + + if (is_info) + image_address = AUTO_UPDATE_INFO_BASE; + else + image_address = AUTO_UPDATE_BITSTREAM_BASE + + AUTO_UPDATE_UPGRADE_INDEX * priv->size_per_bitstream; + + buffer = devm_kzalloc(priv->dev, erase_size, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + /* + * For bitstream info, the descriptor is written to a fixed offset, + * so there is no need to set the image address. + */ + if (!is_info) { + ret = mpfs_auto_update_set_image_address(priv, buffer, image_address, directory_address); + if (ret) { + dev_err(priv->dev, "failed to set image address in the SPI directory: %d\n", ret); + return ret; + } + } else { + if (size > AUTO_UPDATE_INFO_SIZE) { + dev_err(priv->dev, "bitstream info exceeds permitted size\n"); + return -ENOSPC; + } + } + + /* + * Now the .spi image itself can be written to the flash. Preservation + * of contents here is not important here, unlike the spi "directory" + * which must be RMWed. + */ + erase.len = round_up(size, (size_t)priv->flash->erasesize); + erase.addr = image_address; + + dev_info(priv->dev, "Erasing the flash at address (%x)\n", image_address); + ret = mtd_erase(priv->flash, &erase); + if (ret) + goto out; + + /* + * No parsing etc of the bitstream is required. The system controller + * will do all of that itself - including verifying that the bitstream + * is valid. + */ + dev_info(priv->dev, "Writing the image to the flash at address (%x)\n", image_address); + ret = mtd_write(priv->flash, (loff_t)image_address, size, &bytes_written, data); + if (ret) + goto out; + + if (bytes_written != size) { + ret = -EIO; + goto out; + } + + *written = bytes_written; + dev_info(priv->dev, "Wrote 0x%zx bytes to the flash\n", bytes_written); + +out: + devm_kfree(priv->dev, buffer); + return ret; +} + +static enum fw_upload_err mpfs_auto_update_write(struct fw_upload *fw_uploader, const u8 *data, + u32 offset, u32 size, u32 *written) +{ + struct mpfs_auto_update_priv *priv = fw_uploader->dd_handle; + enum fw_upload_err err = FW_UPLOAD_ERR_NONE; + int ret; + + reinit_completion(&priv->programming_complete); + + ret = mpfs_auto_update_write_bitstream(fw_uploader, data, offset, size, written); + if (ret) { + err = FW_UPLOAD_ERR_RW_ERROR; + goto out; + } + + if (priv->cancel_request) { + err = FW_UPLOAD_ERR_CANCELED; + goto out; + } + + if (mpfs_auto_update_is_bitstream_info(data, size)) + goto out; + + ret = mpfs_auto_update_verify_image(fw_uploader); + if (ret) + err = FW_UPLOAD_ERR_FW_INVALID; + +out: + complete(&priv->programming_complete); + + return err; +} + +static const struct fw_upload_ops mpfs_auto_update_ops = { + .prepare = mpfs_auto_update_prepare, + .write = mpfs_auto_update_write, + .poll_complete = mpfs_auto_update_poll_complete, + .cancel = mpfs_auto_update_cancel, +}; + +static int mpfs_auto_update_available(struct mpfs_auto_update_priv *priv) +{ + struct mpfs_mss_response *response; + struct mpfs_mss_msg *message; + u32 *response_msg; + int ret; + + response_msg = devm_kzalloc(priv->dev, AUTO_UPDATE_FEATURE_RESP_SIZE * sizeof(response_msg), + GFP_KERNEL); + if (!response_msg) + return -ENOMEM; + + response = devm_kzalloc(priv->dev, sizeof(struct mpfs_mss_response), GFP_KERNEL); + if (!response) + return -ENOMEM; + + message = devm_kzalloc(priv->dev, sizeof(struct mpfs_mss_msg), GFP_KERNEL); + if (!message) + return -ENOMEM; + + /* + * To verify that Auto Update is possible, the "Query Security Service + * Request" is performed. + * This service has no command data & does not overload mbox_offset. + */ + response->resp_msg = response_msg; + response->resp_size = AUTO_UPDATE_FEATURE_RESP_SIZE; + message->cmd_opcode = AUTO_UPDATE_FEATURE_CMD_OPCODE; + message->cmd_data_size = AUTO_UPDATE_FEATURE_CMD_DATA_SIZE; + message->response = response; + message->cmd_data = AUTO_UPDATE_FEATURE_CMD_DATA; + message->mbox_offset = AUTO_UPDATE_DEFAULT_MBOX_OFFSET; + message->resp_offset = AUTO_UPDATE_DEFAULT_RESP_OFFSET; + + ret = mpfs_blocking_transaction(priv->sys_controller, message); + if (ret) + return ret; + + /* + * Currently, the system controller's firmware does not generate any + * interrupts for failed services, so mpfs_blocking_transaction() should + * time out & therefore return an error. + * Hitting this check is highly unlikely at present, but if the system + * controller's behaviour changes so that it does generate interrupts + * for failed services, it will be required. + */ + if (response->resp_status) + return -EIO; + + /* + * Bit 5 of byte 1 is "UL_Auto Update" & if it is set, Auto Update is + * not possible. + */ + if (response_msg[1] & AUTO_UPDATE_FEATURE_ENABLED) + return -EPERM; + + return 0; +} + +static int mpfs_auto_update_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mpfs_auto_update_priv *priv; + struct fw_upload *fw_uploader; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->sys_controller = mpfs_sys_controller_get(dev); + if (IS_ERR(priv->sys_controller)) + return dev_err_probe(dev, PTR_ERR(priv->sys_controller), + "Could not register as a sub device of the system controller\n"); + + priv->dev = dev; + platform_set_drvdata(pdev, priv); + + ret = mpfs_auto_update_available(priv); + if (ret) + return dev_err_probe(dev, ret, + "The current bitstream does not support auto-update\n"); + + init_completion(&priv->programming_complete); + + fw_uploader = firmware_upload_register(THIS_MODULE, dev, "mpfs-auto-update", + &mpfs_auto_update_ops, priv); + if (IS_ERR(fw_uploader)) + return dev_err_probe(dev, PTR_ERR(fw_uploader), + "Failed to register the bitstream uploader\n"); + + priv->fw_uploader = fw_uploader; + + return 0; +} + +static void mpfs_auto_update_remove(struct platform_device *pdev) +{ + struct mpfs_auto_update_priv *priv = platform_get_drvdata(pdev); + + firmware_upload_unregister(priv->fw_uploader); +} + +static struct platform_driver mpfs_auto_update_driver = { + .driver = { + .name = "mpfs-auto-update", + }, + .probe = mpfs_auto_update_probe, + .remove_new = mpfs_auto_update_remove, +}; +module_platform_driver(mpfs_auto_update_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>"); +MODULE_DESCRIPTION("PolarFire SoC Auto Update FPGA reprogramming"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/gpio/gpio-mpfs.c linux-microchip/drivers/gpio/gpio-mpfs.c --- linux-6.6.51/drivers/gpio/gpio-mpfs.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/gpio/gpio-mpfs.c 2024-11-26 14:02:36.490475641 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: (GPL-2.0) +/* + * Microchip PolarFire SoC (MPFS) GPIO controller driver + * + * Copyright (c) 2018-2022 Microchip Technology Inc. and its subsidiaries + * + * Author: Lewis Hanly <lewis.hanly@microchip.com> + */ + +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/gpio/driver.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_irq.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> + +#define MPFS_GPIO_CTRL(i) (0x4 * (i)) +#define MAX_NUM_GPIO 32 +#define MPFS_GPIO_EN_INT 3 +#define MPFS_GPIO_EN_OUT_BUF BIT(2) +#define MPFS_GPIO_EN_IN BIT(1) +#define MPFS_GPIO_EN_OUT BIT(0) + +#define MPFS_GPIO_TYPE_INT_EDGE_BOTH 0x80 +#define MPFS_GPIO_TYPE_INT_EDGE_NEG 0x60 +#define MPFS_GPIO_TYPE_INT_EDGE_POS 0x40 +#define MPFS_GPIO_TYPE_INT_LEVEL_LOW 0x20 +#define MPFS_GPIO_TYPE_INT_LEVEL_HIGH 0x00 +#define MPFS_GPIO_TYPE_INT_MASK GENMASK(7, 5) +#define MPFS_IRQ_REG 0x80 + +#define MPFS_INP_REG 0x84 +#define COREGPIO_INP_REG 0x90 +#define MPFS_OUTP_REG 0x88 +#define COREGPIO_OUTP_REG 0xA0 + +struct mpfs_gpio_reg_offsets { + u8 inp; + u8 outp; +}; + +struct mpfs_gpio_chip { + void __iomem *base; + const struct mpfs_gpio_reg_offsets *regs; + struct clk *clk; + raw_spinlock_t lock; + struct gpio_chip gc; + u8 irq_data[MAX_NUM_GPIO]; +}; + +static void mpfs_gpio_assign_bit(void __iomem *addr, unsigned int bit_offset, bool value) +{ + unsigned long reg = readl(addr); + + __assign_bit(bit_offset, ®, value); + writel(reg, addr); +} + +static int mpfs_gpio_direction_input(struct gpio_chip *gc, unsigned int gpio_index) +{ + struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); + u32 gpio_cfg; + unsigned long flags; + + raw_spin_lock_irqsave(&mpfs_gpio->lock, flags); + + gpio_cfg = readl(mpfs_gpio->base + MPFS_GPIO_CTRL(gpio_index)); + gpio_cfg |= MPFS_GPIO_EN_IN; + gpio_cfg &= ~(MPFS_GPIO_EN_OUT | MPFS_GPIO_EN_OUT_BUF); + writel(gpio_cfg, mpfs_gpio->base + MPFS_GPIO_CTRL(gpio_index)); + + raw_spin_unlock_irqrestore(&mpfs_gpio->lock, flags); + + return 0; +} + +static int mpfs_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio_index, int value) +{ + struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); + u32 gpio_cfg; + unsigned long flags; + + raw_spin_lock_irqsave(&mpfs_gpio->lock, flags); + + gpio_cfg = readl(mpfs_gpio->base + MPFS_GPIO_CTRL(gpio_index)); + gpio_cfg |= MPFS_GPIO_EN_OUT | MPFS_GPIO_EN_OUT_BUF; + gpio_cfg &= ~MPFS_GPIO_EN_IN; + writel(gpio_cfg, mpfs_gpio->base + MPFS_GPIO_CTRL(gpio_index)); + + mpfs_gpio_assign_bit(mpfs_gpio->base + mpfs_gpio->regs->outp, gpio_index, value); + + raw_spin_unlock_irqrestore(&mpfs_gpio->lock, flags); + + return 0; +} + +static int mpfs_gpio_get_direction(struct gpio_chip *gc, + unsigned int gpio_index) +{ + struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); + u32 gpio_cfg; + + gpio_cfg = readl(mpfs_gpio->base + MPFS_GPIO_CTRL(gpio_index)); + if (gpio_cfg & MPFS_GPIO_EN_IN) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; +} + +static int mpfs_gpio_get(struct gpio_chip *gc, + unsigned int gpio_index) +{ + struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); + + if (mpfs_gpio_get_direction(gc, gpio_index) == GPIO_LINE_DIRECTION_OUT) { + return !!(readl(mpfs_gpio->base + mpfs_gpio->regs->outp) & BIT(gpio_index)); + } + + return !!(readl(mpfs_gpio->base + mpfs_gpio->regs->inp) & BIT(gpio_index)); +} + +static void mpfs_gpio_set(struct gpio_chip *gc, unsigned int gpio_index, int value) +{ + struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); + unsigned long flags; + + raw_spin_lock_irqsave(&mpfs_gpio->lock, flags); + + mpfs_gpio_assign_bit(mpfs_gpio->base + mpfs_gpio->regs->outp, + gpio_index, value); + + raw_spin_unlock_irqrestore(&mpfs_gpio->lock, flags); +} + +static int mpfs_gpio_irq_set_type(struct irq_data *data, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(data); + struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); + int gpio_index = irqd_to_hwirq(data) % 32; + u32 interrupt_type; + u32 gpio_cfg; + unsigned long flags; + + switch (type) { + case IRQ_TYPE_EDGE_BOTH: + interrupt_type = MPFS_GPIO_TYPE_INT_EDGE_BOTH; + break; + case IRQ_TYPE_EDGE_FALLING: + interrupt_type = MPFS_GPIO_TYPE_INT_EDGE_NEG; + break; + case IRQ_TYPE_EDGE_RISING: + interrupt_type = MPFS_GPIO_TYPE_INT_EDGE_POS; + break; + case IRQ_TYPE_LEVEL_HIGH: + interrupt_type = MPFS_GPIO_TYPE_INT_LEVEL_HIGH; + break; + case IRQ_TYPE_LEVEL_LOW: + interrupt_type = MPFS_GPIO_TYPE_INT_LEVEL_LOW; + break; + } + + raw_spin_lock_irqsave(&mpfs_gpio->lock, flags); + + gpio_cfg = readl(mpfs_gpio->base + MPFS_GPIO_CTRL(gpio_index)); + gpio_cfg &= ~MPFS_GPIO_TYPE_INT_MASK; + gpio_cfg |= interrupt_type; + writel(gpio_cfg, mpfs_gpio->base + MPFS_GPIO_CTRL(gpio_index)); + + raw_spin_unlock_irqrestore(&mpfs_gpio->lock, flags); + + return 0; +} + +static void mpfs_gpio_irq_unmask(struct irq_data *data) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(data); + struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); + int gpio_index = irqd_to_hwirq(data) % 32; + + gpiochip_enable_irq(gc, gpio_index); + mpfs_gpio_direction_input(gc, gpio_index); + mpfs_gpio_assign_bit(mpfs_gpio->base + MPFS_GPIO_CTRL(gpio_index), + MPFS_GPIO_EN_INT, 1); +} + +static void mpfs_gpio_irq_mask(struct irq_data *data) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(data); + struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); + int gpio_index = irqd_to_hwirq(data) % 32; + + mpfs_gpio_assign_bit(mpfs_gpio->base + MPFS_GPIO_CTRL(gpio_index), + MPFS_GPIO_EN_INT, 0); + gpiochip_disable_irq(gc, gpio_index); +} + +const struct irq_chip mpfs_gpio_irqchip = { + .name = "MPFS GPIO", + .irq_set_type = mpfs_gpio_irq_set_type, + .irq_mask = mpfs_gpio_irq_mask, + .irq_unmask = mpfs_gpio_irq_unmask, + .flags = IRQCHIP_IMMUTABLE | IRQCHIP_MASK_ON_SUSPEND, + GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static void mpfs_gpio_irq_handler(struct irq_desc *desc) +{ + struct irq_chip *irqchip = irq_desc_get_chip(desc); + void *handler_data = irq_desc_get_handler_data(desc); + struct gpio_chip *gc = irq_data_get_irq_chip_data(&desc->irq_data); + struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc); + u8 gpio_index = *((u8 *)handler_data); + unsigned long status; + + /* + * Since the parent may be a muxed/"non-direct" interrupt, this + * interrupt may not be for us. + */ + status = readl(mpfs_gpio->base + MPFS_IRQ_REG); + if (!(status & BIT(gpio_index))) + return; + + chained_irq_enter(irqchip, desc); + + generic_handle_irq(irq_find_mapping(mpfs_gpio->gc.irq.domain, gpio_index)); + mpfs_gpio_assign_bit(mpfs_gpio->base + MPFS_IRQ_REG, gpio_index, 1); + + chained_irq_exit(irqchip, desc); +} + +static int mpfs_gpio_probe(struct platform_device *pdev) +{ + struct clk *clk; + struct device *dev = &pdev->dev; + struct device_node *node = pdev->dev.of_node; + void **irq_data = NULL; + struct mpfs_gpio_chip *mpfs_gpio; + struct gpio_irq_chip *girq; + int i, ret, ngpios, nirqs; + + mpfs_gpio = devm_kzalloc(dev, sizeof(*mpfs_gpio), GFP_KERNEL); + if (!mpfs_gpio) + return -ENOMEM; + + mpfs_gpio->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(mpfs_gpio->base)) + return dev_err_probe(dev, PTR_ERR(mpfs_gpio->base), + "failed to ioremap memory resource\n"); + + clk = devm_clk_get(dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "devm_clk_get failed\n"); + + ret = clk_prepare_enable(clk); + if (ret) + return dev_err_probe(dev, ret, "failed to enable clock\n"); + + mpfs_gpio->regs = of_device_get_match_data(&pdev->dev); + + mpfs_gpio->clk = clk; + + ngpios = MAX_NUM_GPIO; + device_property_read_u32(dev, "ngpios", &ngpios); + if (ngpios > MAX_NUM_GPIO) + ngpios = MAX_NUM_GPIO; + + raw_spin_lock_init(&mpfs_gpio->lock); + mpfs_gpio->gc.direction_input = mpfs_gpio_direction_input; + mpfs_gpio->gc.direction_output = mpfs_gpio_direction_output; + mpfs_gpio->gc.get_direction = mpfs_gpio_get_direction; + mpfs_gpio->gc.get = mpfs_gpio_get; + mpfs_gpio->gc.set = mpfs_gpio_set; + mpfs_gpio->gc.base = -1; + mpfs_gpio->gc.ngpio = ngpios; + mpfs_gpio->gc.label = dev_name(dev); + mpfs_gpio->gc.parent = dev; + mpfs_gpio->gc.owner = THIS_MODULE; + + nirqs = of_irq_count(node); + if (nirqs > MAX_NUM_GPIO) { + ret = -ENXIO; + goto cleanup_clock; + } + + girq = &mpfs_gpio->gc.irq; + girq->num_parents = nirqs; + + if (girq->num_parents) { + gpio_irq_chip_set_chip(girq, &mpfs_gpio_irqchip); + girq->parent_handler = mpfs_gpio_irq_handler; + + girq->parents = devm_kcalloc(&pdev->dev, girq->num_parents, + sizeof(*girq->parents), GFP_KERNEL); + irq_data = devm_kmalloc_array(&pdev->dev, girq->num_parents, + sizeof(*irq_data), GFP_KERNEL); + + if (!girq->parents || !irq_data) { + ret = -ENOMEM; + goto cleanup_clock; + } + + for (i = 0; i < girq->num_parents; i++) { + ret = platform_get_irq(pdev, i); + if (ret < 0) + goto cleanup_clock; + + girq->parents[i] = ret; + mpfs_gpio->irq_data[i] = i; + irq_data[i] = &mpfs_gpio->irq_data[i]; + + irq_set_chip_data(ret, &mpfs_gpio->gc); + irq_set_handler(ret, handle_simple_irq); + } + + girq->parent_handler_data_array = irq_data; + girq->per_parent_data = true; + girq->handler = handle_simple_irq; + girq->default_type = IRQ_TYPE_NONE; + } + + ret = gpiochip_add_data(&mpfs_gpio->gc, mpfs_gpio); + if (ret) + goto cleanup_clock; + + platform_set_drvdata(pdev, mpfs_gpio); + + return 0; + +cleanup_clock: + clk_disable_unprepare(mpfs_gpio->clk); + return ret; +} + +static int mpfs_gpio_remove(struct platform_device *pdev) +{ + struct mpfs_gpio_chip *mpfs_gpio = platform_get_drvdata(pdev); + + gpiochip_remove(&mpfs_gpio->gc); + clk_disable_unprepare(mpfs_gpio->clk); + + return 0; +} + +static const struct mpfs_gpio_reg_offsets mpfs_reg_offsets = { + .inp = MPFS_INP_REG, + .outp = MPFS_OUTP_REG, +}; + +static const struct mpfs_gpio_reg_offsets coregpio_reg_offsets = { + .inp = COREGPIO_INP_REG, + .outp = COREGPIO_OUTP_REG, +}; +static const struct of_device_id mpfs_gpio_of_ids[] = { + { + .compatible = "microchip,mpfs-gpio", + .data = &mpfs_reg_offsets, + }, { + .compatible = "microchip,coregpio-rtl-v3", + .data = &coregpio_reg_offsets, + }, + { /* end of list */ } +}; + +static struct platform_driver mpfs_gpio_driver = { + .probe = mpfs_gpio_probe, + .driver = { + .name = "microchip,mpfs-gpio", + .of_match_table = mpfs_gpio_of_ids, + }, + .remove = mpfs_gpio_remove, +}; +builtin_platform_driver(mpfs_gpio_driver); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/gpio/Kconfig linux-microchip/drivers/gpio/Kconfig --- linux-6.6.51/drivers/gpio/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/gpio/Kconfig 2024-11-26 14:02:36.486475568 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:506 @ help Say yes here to support the PrimeCell PL061 GPIO device. +config GPIO_POLARFIRE_SOC + bool "Microchip FPGA GPIO support" + depends on OF_GPIO + select GPIOLIB_IRQCHIP + help + Say yes here to support the GPIO device on Microchip FPGAs. + config GPIO_PXA bool "PXA GPIO support" depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/gpio/Makefile linux-microchip/drivers/gpio/Makefile --- linux-6.6.51/drivers/gpio/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/gpio/Makefile 2024-11-26 14:02:36.486475568 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:131 @ obj-$(CONFIG_GPIO_PISOSR) += gpio-pisosr.o obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o obj-$(CONFIG_GPIO_PMIC_EIC_SPRD) += gpio-pmic-eic-sprd.o +obj-$(CONFIG_GPIO_POLARFIRE_SOC) += gpio-mpfs.o obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o obj-$(CONFIG_GPIO_RASPBERRYPI_EXP) += gpio-raspberrypi-exp.o obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c linux-microchip/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c --- linux-6.6.51/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c 2024-11-26 14:02:37.158487607 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:33 @ * * @base: base CRTC state * @output_mode: RGBXXX output mode + * @dpi: output DPI mode */ struct atmel_hlcdc_crtc_state { struct drm_crtc_state base; unsigned int output_mode; + u8 dpi; }; static inline struct atmel_hlcdc_crtc_state * @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:84 @ unsigned int mask = ATMEL_HLCDC_CLKDIV_MASK | ATMEL_HLCDC_CLKPOL; unsigned int cfg = 0; int div, ret; + bool is_xlcdc = crtc->dc->desc->is_xlcdc; /* get encoder from crtc */ drm_for_each_encoder(en_iter, ddev) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:103 @ drm_connector_list_iter_end(&iter); } - ret = clk_prepare_enable(crtc->dc->hlcdc->sys_clk); - if (ret) - return; + if (crtc->dc->hlcdc->lvds_pll_clk) { + ret = clk_prepare_enable(crtc->dc->hlcdc->lvds_pll_clk); + if (ret) + return; + } else { + ret = clk_prepare_enable(crtc->dc->hlcdc->sys_clk); + if (ret) + return; + } + + if (crtc->dc->hlcdc->lvds_pll_clk) { + cfg |= ATMEL_XLCDC_CLKBYP; + mask |= ATMEL_XLCDC_CLKBYP; + } else { + prate = clk_get_rate(crtc->dc->hlcdc->sys_clk); + mode_rate = adj->crtc_clock * 1000; + if (!crtc->dc->desc->fixed_clksrc) { + prate *= 2; + if (is_xlcdc) { + cfg |= ATMEL_XLCDC_CLKBYP; + mask |= ATMEL_XLCDC_CLKBYP; + } else { + cfg |= ATMEL_HLCDC_CLKSEL; + mask |= ATMEL_HLCDC_CLKSEL; + } + } + + div = DIV_ROUND_UP(prate, mode_rate); + if (div < 2) { + div = 2; + } else if (ATMEL_HLCDC_CLKDIV(div) & ~ATMEL_HLCDC_CLKDIV_MASK) { + /* The divider ended up too big, try a lower base rate. */ + if (is_xlcdc) + cfg &= ~ATMEL_XLCDC_CLKBYP; + else + cfg &= ~ATMEL_HLCDC_CLKSEL; + + prate /= 2; + div = DIV_ROUND_UP(prate, mode_rate); + if (ATMEL_HLCDC_CLKDIV(div) & ~ATMEL_HLCDC_CLKDIV_MASK) + div = ATMEL_HLCDC_CLKDIV_MASK; + } else { + int div_low = prate / mode_rate; + + /* + * Its better to use a higher Pixel clock + * frequency than requested, instead of a lower. + * So, go with that. + */ + + if (div_low >= 2 && + ((prate / div_low >= mode_rate) && + (prate / div < mode_rate))) { + div = div_low; + } else { + if (is_xlcdc) { + cfg |= ATMEL_XLCDC_CLKBYP; + mask |= ATMEL_XLCDC_CLKBYP; + } + } + } + cfg |= ATMEL_HLCDC_CLKDIV(div); + } + + if (connector && + connector->display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) + cfg |= ATMEL_HLCDC_CLKPOL; + + regmap_update_bits(regmap, ATMEL_HLCDC_CFG(0), mask, cfg); vm.vfront_porch = adj->crtc_vsync_start - adj->crtc_vdisplay; vm.vback_porch = adj->crtc_vtotal - adj->crtc_vsync_end; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:193 @ (adj->crtc_hdisplay - 1) | ((adj->crtc_vdisplay - 1) << 16)); - prate = clk_get_rate(crtc->dc->hlcdc->sys_clk); - mode_rate = adj->crtc_clock * 1000; - if (!crtc->dc->desc->fixed_clksrc) { - prate *= 2; - cfg |= ATMEL_HLCDC_CLKSEL; - mask |= ATMEL_HLCDC_CLKSEL; - } - - div = DIV_ROUND_UP(prate, mode_rate); - if (div < 2) { - div = 2; - } else if (ATMEL_HLCDC_CLKDIV(div) & ~ATMEL_HLCDC_CLKDIV_MASK) { - /* The divider ended up too big, try a lower base rate. */ - cfg &= ~ATMEL_HLCDC_CLKSEL; - prate /= 2; - div = DIV_ROUND_UP(prate, mode_rate); - if (ATMEL_HLCDC_CLKDIV(div) & ~ATMEL_HLCDC_CLKDIV_MASK) - div = ATMEL_HLCDC_CLKDIV_MASK; - } else { - int div_low = prate / mode_rate; - - if (div_low >= 2 && - (10 * (prate / div_low - mode_rate) < - (mode_rate - prate / div))) - /* - * At least 10 times better when using a higher - * frequency than requested, instead of a lower. - * So, go with that. - */ - div = div_low; - } - - cfg |= ATMEL_HLCDC_CLKDIV(div); - - if (connector && - connector->display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) - cfg |= ATMEL_HLCDC_CLKPOL; - - regmap_update_bits(regmap, ATMEL_HLCDC_CFG(0), mask, cfg); - state = drm_crtc_state_to_atmel_hlcdc_crtc_state(c->state); cfg = state->output_mode << 8; + if (is_xlcdc) + cfg |= state->dpi << 11; - if (adj->flags & DRM_MODE_FLAG_NVSYNC) + if (!is_xlcdc && (adj->flags & DRM_MODE_FLAG_NVSYNC)) cfg |= ATMEL_HLCDC_VSPOL; - if (adj->flags & DRM_MODE_FLAG_NHSYNC) + if (!is_xlcdc && (adj->flags & DRM_MODE_FLAG_NHSYNC)) cfg |= ATMEL_HLCDC_HSPOL; regmap_update_bits(regmap, ATMEL_HLCDC_CFG(5), @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:209 @ ATMEL_HLCDC_VSPDLYS | ATMEL_HLCDC_VSPDLYE | ATMEL_HLCDC_DISPPOL | ATMEL_HLCDC_DISPDLY | ATMEL_HLCDC_VSPSU | ATMEL_HLCDC_VSPHO | - ATMEL_HLCDC_GUARDTIME_MASK | ATMEL_HLCDC_MODE_MASK, + ATMEL_HLCDC_GUARDTIME_MASK | + (is_xlcdc ? ATMEL_XLCDC_MODE_MASK | + ATMEL_XLCDC_DPI : ATMEL_HLCDC_MODE_MASK), cfg); - clk_disable_unprepare(crtc->dc->hlcdc->sys_clk); + if (crtc->dc->hlcdc->lvds_pll_clk) + clk_disable_unprepare(crtc->dc->hlcdc->lvds_pll_clk); + else + clk_disable_unprepare(crtc->dc->hlcdc->sys_clk); } static enum drm_mode_status @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:241 @ pm_runtime_get_sync(dev->dev); + if (crtc->dc->desc->is_xlcdc) { + regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_XLCDC_CM); + if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status, + !(status & ATMEL_XLCDC_CM), + 10, 1000)) + dev_warn(dev->dev, "Atmel LCDC status register CMSTS timeout\n"); + + regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_XLCDC_SD); + if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status, + status & ATMEL_XLCDC_SD, + 10, 1000)) + dev_warn(dev->dev, "Atmel LCDC status register SDSTS timeout\n"); + } + regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_DISP); while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && (status & ATMEL_HLCDC_DISP)) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:270 @ (status & ATMEL_HLCDC_PIXEL_CLK)) cpu_relax(); - clk_disable_unprepare(crtc->dc->hlcdc->sys_clk); + if (crtc->dc->hlcdc->lvds_pll_clk) + clk_disable_unprepare(crtc->dc->hlcdc->lvds_pll_clk); + else + clk_disable_unprepare(crtc->dc->hlcdc->sys_clk); + pinctrl_pm_select_sleep_state(dev->dev); pm_runtime_allow(dev->dev); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:287 @ { struct drm_device *dev = c->dev; struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); + struct drm_display_mode *adj = &c->state->adjusted_mode; struct regmap *regmap = crtc->dc->hlcdc->regmap; unsigned int status; + int ret; pm_runtime_get_sync(dev->dev); pm_runtime_forbid(dev->dev); pinctrl_pm_select_default_state(dev->dev); - clk_prepare_enable(crtc->dc->hlcdc->sys_clk); + + if (crtc->dc->hlcdc->lvds_pll_clk) { + /* If the LVDS interface is used, fetch the pixel clock + * from the panel and set the clock rate. + * Here LVDS PLL clock is 7 times the pixel clock. + */ + ret = clk_set_rate(crtc->dc->hlcdc->lvds_pll_clk, + (adj->clock * 7 * 1000)); + if (ret) { + dev_err(c->dev->dev, "failed to set clk rate for lvds pll: %d\n", ret); + return; + } + + ret = clk_prepare_enable(crtc->dc->hlcdc->lvds_pll_clk); + if (ret) + return; + } else { + ret = clk_prepare_enable(crtc->dc->hlcdc->sys_clk); + if (ret) + return; + } regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_PIXEL_CLK); while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:335 @ !(status & ATMEL_HLCDC_DISP)) cpu_relax(); + if (crtc->dc->desc->is_xlcdc) { + regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_XLCDC_CM); + if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status, + status & ATMEL_XLCDC_CM, + 10, 1000)) + dev_warn(dev->dev, "Atmel LCDC status register CMSTS timeout\n"); + + regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_XLCDC_SD); + if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status, + !(status & ATMEL_XLCDC_SD), + 10, 1000)) + dev_warn(dev->dev, "Atmel LCDC status register SDSTS timeout\n"); + } + pm_runtime_put_sync(dev->dev); } -#define ATMEL_HLCDC_RGB444_OUTPUT BIT(0) -#define ATMEL_HLCDC_RGB565_OUTPUT BIT(1) -#define ATMEL_HLCDC_RGB666_OUTPUT BIT(2) -#define ATMEL_HLCDC_RGB888_OUTPUT BIT(3) -#define ATMEL_HLCDC_OUTPUT_MODE_MASK GENMASK(3, 0) +#define ATMEL_HLCDC_RGB444_OUTPUT BIT(0) +#define ATMEL_HLCDC_RGB565_OUTPUT BIT(1) +#define ATMEL_HLCDC_RGB666_OUTPUT BIT(2) +#define ATMEL_HLCDC_RGB888_OUTPUT BIT(3) +#define ATMEL_HLCDC_DPI_RGB565C1_OUTPUT BIT(4) +#define ATMEL_HLCDC_DPI_RGB565C2_OUTPUT BIT(5) +#define ATMEL_HLCDC_DPI_RGB565C3_OUTPUT BIT(6) +#define ATMEL_HLCDC_DPI_RGB666C1_OUTPUT BIT(7) +#define ATMEL_HLCDC_DPI_RGB666C2_OUTPUT BIT(8) +#define ATMEL_HLCDC_DPI_RGB888_OUTPUT BIT(9) +#define ATMEL_HLCDC_OUTPUT_MODE_MASK GENMASK(3, 0) +#define ATMEL_XLCDC_OUTPUT_MODE_MASK GENMASK(9, 0) static int atmel_hlcdc_connector_output_mode(struct drm_connector_state *state) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:378 @ if (!encoder) encoder = connector->encoder; - switch (atmel_hlcdc_encoder_get_bus_fmt(encoder)) { - case 0: - break; - case MEDIA_BUS_FMT_RGB444_1X12: - return ATMEL_HLCDC_RGB444_OUTPUT; - case MEDIA_BUS_FMT_RGB565_1X16: - return ATMEL_HLCDC_RGB565_OUTPUT; - case MEDIA_BUS_FMT_RGB666_1X18: - return ATMEL_HLCDC_RGB666_OUTPUT; - case MEDIA_BUS_FMT_RGB888_1X24: - return ATMEL_HLCDC_RGB888_OUTPUT; - default: - return -EINVAL; - } - - for (j = 0; j < info->num_bus_formats; j++) { - switch (info->bus_formats[j]) { - case MEDIA_BUS_FMT_RGB444_1X12: - supported_fmts |= ATMEL_HLCDC_RGB444_OUTPUT; + switch (encoder->encoder_type) { + case DRM_MODE_ENCODER_DSI: + /* + * atmel-hlcdc to support DSI formats with DSI video pipeline + * when DRM_MODE_ENCODER_DSI type is set by + * connector driver component. + */ + switch (atmel_hlcdc_encoder_get_bus_fmt(encoder)) { + case 0: break; case MEDIA_BUS_FMT_RGB565_1X16: - supported_fmts |= ATMEL_HLCDC_RGB565_OUTPUT; - break; + return ATMEL_HLCDC_DPI_RGB565C1_OUTPUT; case MEDIA_BUS_FMT_RGB666_1X18: - supported_fmts |= ATMEL_HLCDC_RGB666_OUTPUT; - break; + return ATMEL_HLCDC_DPI_RGB666C1_OUTPUT; + case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: + return ATMEL_HLCDC_DPI_RGB666C2_OUTPUT; case MEDIA_BUS_FMT_RGB888_1X24: - supported_fmts |= ATMEL_HLCDC_RGB888_OUTPUT; + return ATMEL_HLCDC_DPI_RGB888_OUTPUT; + default: + return -EINVAL; + } + + for (j = 0; j < info->num_bus_formats; j++) { + switch (info->bus_formats[j]) { + case MEDIA_BUS_FMT_RGB565_1X16: + supported_fmts |= + ATMEL_HLCDC_DPI_RGB565C1_OUTPUT; + break; + case MEDIA_BUS_FMT_RGB666_1X18: + supported_fmts |= + ATMEL_HLCDC_DPI_RGB666C1_OUTPUT; + break; + case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: + supported_fmts |= + ATMEL_HLCDC_DPI_RGB666C2_OUTPUT; + break; + case MEDIA_BUS_FMT_RGB888_1X24: + supported_fmts |= + ATMEL_HLCDC_DPI_RGB888_OUTPUT; + break; + default: + break; + } + } + break; + case DRM_MODE_ENCODER_LVDS: + switch (atmel_hlcdc_encoder_get_bus_fmt(encoder)) { + case 0: break; + case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: + case MEDIA_BUS_FMT_RGB666_1X18: + return ATMEL_HLCDC_RGB666_OUTPUT; + case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: + case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: default: + return ATMEL_HLCDC_RGB888_OUTPUT; + } + + for (j = 0; j < info->num_bus_formats; j++) { + switch (info->bus_formats[j]) { + case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: + case MEDIA_BUS_FMT_RGB666_1X18: + supported_fmts |= ATMEL_HLCDC_RGB666_OUTPUT; + break; + case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: + case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: + default: + supported_fmts |= ATMEL_HLCDC_RGB888_OUTPUT; + break; + } + } + break; + default: + switch (atmel_hlcdc_encoder_get_bus_fmt(encoder)) { + case 0: break; + case MEDIA_BUS_FMT_RGB444_1X12: + return ATMEL_HLCDC_RGB444_OUTPUT; + case MEDIA_BUS_FMT_RGB565_1X16: + return ATMEL_HLCDC_RGB565_OUTPUT; + case MEDIA_BUS_FMT_RGB666_1X18: + return ATMEL_HLCDC_RGB666_OUTPUT; + case MEDIA_BUS_FMT_RGB888_1X24: + return ATMEL_HLCDC_RGB888_OUTPUT; + default: + return -EINVAL; } - } + for (j = 0; j < info->num_bus_formats; j++) { + switch (info->bus_formats[j]) { + case MEDIA_BUS_FMT_RGB444_1X12: + supported_fmts |= ATMEL_HLCDC_RGB444_OUTPUT; + break; + case MEDIA_BUS_FMT_RGB565_1X16: + supported_fmts |= ATMEL_HLCDC_RGB565_OUTPUT; + break; + case MEDIA_BUS_FMT_RGB666_1X18: + supported_fmts |= ATMEL_HLCDC_RGB666_OUTPUT; + break; + case MEDIA_BUS_FMT_RGB888_1X24: + supported_fmts |= ATMEL_HLCDC_RGB888_OUTPUT; + break; + default: + break; + } + } + break; + } return supported_fmts; } static int atmel_hlcdc_crtc_select_output_mode(struct drm_crtc_state *state) { - unsigned int output_fmts = ATMEL_HLCDC_OUTPUT_MODE_MASK; + unsigned int output_fmts; struct atmel_hlcdc_crtc_state *hstate; struct drm_connector_state *cstate; struct drm_connector *connector; - struct atmel_hlcdc_crtc *crtc; + struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(state->crtc); int i; + bool is_xlcdc = crtc->dc->desc->is_xlcdc; - crtc = drm_crtc_to_atmel_hlcdc_crtc(state->crtc); + output_fmts = is_xlcdc ? ATMEL_XLCDC_OUTPUT_MODE_MASK : + ATMEL_HLCDC_OUTPUT_MODE_MASK; for_each_new_connector_in_state(state->state, connector, cstate, i) { unsigned int supported_fmts = 0; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:521 @ hstate = drm_crtc_state_to_atmel_hlcdc_crtc_state(state); hstate->output_mode = fls(output_fmts) - 1; - + if (is_xlcdc) { + /* check if MIPI DPI bit needs to be set */ + if (fls(output_fmts) > 3) { + hstate->output_mode -= 4; + hstate->dpi = 1; + } else { + hstate->dpi = 0; + } + } return 0; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:633 @ atmel_hlcdc_crtc_duplicate_state(struct drm_crtc *crtc) { struct atmel_hlcdc_crtc_state *state, *cur; + struct atmel_hlcdc_crtc *c = drm_crtc_to_atmel_hlcdc_crtc(crtc); if (WARN_ON(!crtc->state)) return NULL; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:645 @ cur = drm_crtc_state_to_atmel_hlcdc_crtc_state(crtc->state); state->output_mode = cur->output_mode; + if (c->dc->desc->is_xlcdc) + state->dpi = cur->dpi; return &state->base; } diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c linux-microchip/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c --- linux-6.6.51/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c 2024-11-26 14:02:37.158487607 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:61 @ .conflicting_output_formats = true, .nlayers = ARRAY_SIZE(atmel_hlcdc_at91sam9n12_layers), .layers = atmel_hlcdc_at91sam9n12_layers, + .ops = &atmel_hlcdc_ops, }; static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9x5_layers[] = { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:155 @ .conflicting_output_formats = true, .nlayers = ARRAY_SIZE(atmel_hlcdc_at91sam9x5_layers), .layers = atmel_hlcdc_at91sam9x5_layers, + .ops = &atmel_hlcdc_ops, }; static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:274 @ .conflicting_output_formats = true, .nlayers = ARRAY_SIZE(atmel_hlcdc_sama5d3_layers), .layers = atmel_hlcdc_sama5d3_layers, + .ops = &atmel_hlcdc_ops, }; static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d4_layers[] = { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:370 @ .max_hpw = 0x3ff, .nlayers = ARRAY_SIZE(atmel_hlcdc_sama5d4_layers), .layers = atmel_hlcdc_sama5d4_layers, + .ops = &atmel_hlcdc_ops, }; static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sam9x60_layers[] = { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:467 @ .fixed_clksrc = true, .nlayers = ARRAY_SIZE(atmel_hlcdc_sam9x60_layers), .layers = atmel_hlcdc_sam9x60_layers, + .ops = &atmel_hlcdc_ops, +}; + +static const struct atmel_hlcdc_layer_desc atmel_xlcdc_sam9x75_layers[] = { + { + .name = "base", + .formats = &atmel_hlcdc_plane_rgb_formats, + .regs_offset = 0x60, + .id = 0, + .type = ATMEL_HLCDC_BASE_LAYER, + .cfgs_offset = 0x1c, + .layout = { + .xstride = { 2 }, + .default_color = 3, + .general_config = 4, + .disc_pos = 5, + .disc_size = 6, + }, + .clut_offset = 0x700, + }, + { + .name = "overlay1", + .formats = &atmel_hlcdc_plane_rgb_formats, + .regs_offset = 0x160, + .id = 1, + .type = ATMEL_HLCDC_OVERLAY_LAYER, + .cfgs_offset = 0x1c, + .layout = { + .pos = 2, + .size = 3, + .xstride = { 4 }, + .pstride = { 5 }, + .default_color = 6, + .chroma_key = 7, + .chroma_key_mask = 8, + .general_config = 9, + }, + .clut_offset = 0xb00, + }, + { + .name = "overlay2", + .formats = &atmel_hlcdc_plane_rgb_formats, + .regs_offset = 0x260, + .id = 2, + .type = ATMEL_HLCDC_OVERLAY_LAYER, + .cfgs_offset = 0x1c, + .layout = { + .pos = 2, + .size = 3, + .xstride = { 4 }, + .pstride = { 5 }, + .default_color = 6, + .chroma_key = 7, + .chroma_key_mask = 8, + .general_config = 9, + }, + .clut_offset = 0xf00, + }, + { + .name = "high-end-overlay", + .formats = &atmel_hlcdc_plane_rgb_and_yuv_formats, + .regs_offset = 0x360, + .id = 3, + .type = ATMEL_HLCDC_OVERLAY_LAYER, + .cfgs_offset = 0x30, + .layout = { + .pos = 2, + .size = 3, + .memsize = 4, + .xstride = { 5, 7 }, + .pstride = { 6, 8 }, + .default_color = 9, + .chroma_key = 10, + .chroma_key_mask = 11, + .general_config = 12, + .csc = 16, + .scaler_config = 23, + .vxs_config = 30, + .hxs_config = 31, + }, + .clut_offset = 0x1300, + }, +}; + +static const struct atmel_hlcdc_dc_desc atmel_xlcdc_dc_sam9x75 = { + .min_width = 0, + .min_height = 0, + .max_width = 2048, + .max_height = 2048, + .max_spw = 0xff, + .max_vpw = 0xff, + .max_hpw = 0x3ff, + .fixed_clksrc = true, + .is_xlcdc = true, + .nlayers = ARRAY_SIZE(atmel_xlcdc_sam9x75_layers), + .layers = atmel_xlcdc_sam9x75_layers, + .ops = &atmel_xlcdc_ops, +}; + +static const struct atmel_hlcdc_layer_desc atmel_xlcdc_sama7d65_layers[] = { + { + .name = "base", + .formats = &atmel_hlcdc_plane_rgb_formats, + .regs_offset = 0x60, + .id = 0, + .type = ATMEL_HLCDC_BASE_LAYER, + .cfgs_offset = 0x1c, + .layout = { + .xstride = { 2 }, + .default_color = 3, + .general_config = 4, + .disc_pos = 5, + .disc_size = 6, + }, + .clut_offset = 0x700, + }, + { + .name = "overlay1", + .formats = &atmel_hlcdc_plane_rgb_formats, + .regs_offset = 0x160, + .id = 1, + .type = ATMEL_HLCDC_OVERLAY_LAYER, + .cfgs_offset = 0x1c, + .layout = { + .pos = 2, + .size = 3, + .xstride = { 4 }, + .pstride = { 5 }, + .default_color = 6, + .chroma_key = 7, + .chroma_key_mask = 8, + .general_config = 9, + }, + .clut_offset = 0xb00, + }, + { + .name = "high-end-overlay", + .formats = &atmel_hlcdc_plane_rgb_and_yuv_formats, + .regs_offset = 0x360, + .id = 2, + .type = ATMEL_HLCDC_OVERLAY_LAYER, + .cfgs_offset = 0x30, + .layout = { + .pos = 2, + .size = 3, + .memsize = 4, + .xstride = { 5, 7 }, + .pstride = { 6, 8 }, + .default_color = 9, + .chroma_key = 10, + .chroma_key_mask = 11, + .general_config = 12, + .csc = 16, + .scaler_config = 23, + .vxs_config = 30, + .hxs_config = 31, + }, + .clut_offset = 0x1300, + }, +}; + +static const struct atmel_hlcdc_dc_desc atmel_xlcdc_dc_sama7d65 = { + .min_width = 0, + .min_height = 0, + .max_width = 2048, + .max_height = 2048, + .max_spw = 0x3ff, + .max_vpw = 0x3ff, + .max_hpw = 0x3ff, + .fixed_clksrc = false, + .is_xlcdc = true, + .nlayers = ARRAY_SIZE(atmel_xlcdc_sama7d65_layers), + .layers = atmel_xlcdc_sama7d65_layers, + .ops = &atmel_xlcdc_ops, }; static const struct of_device_id atmel_hlcdc_of_match[] = { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:668 @ .compatible = "microchip,sam9x60-hlcdc", .data = &atmel_hlcdc_dc_sam9x60, }, + { + .compatible = "microchip,sam9x75-xlcdc", + .data = &atmel_xlcdc_dc_sam9x75, + }, + { + .compatible = "microchip,sama7d65-xlcdc", + .data = &atmel_xlcdc_dc_sama7d65, + }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, atmel_hlcdc_of_match); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:951 @ drm_fbdev_dma_setup(ddev, 24); + dev_info(ddev->dev, "DRM device successfully registered\n"); + return 0; err_unload: diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h linux-microchip/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h --- linux-6.6.51/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h 2024-11-26 14:02:37.158487607 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:16 @ #include <linux/regmap.h> +#include <drm/drm_crtc.h> +#include <drm/drm_encoder.h> +#include <drm/drm_modes.h> #include <drm/drm_plane.h> +/* LCD controller common registers */ #define ATMEL_HLCDC_LAYER_CHER 0x0 #define ATMEL_HLCDC_LAYER_CHDR 0x4 #define ATMEL_HLCDC_LAYER_CHSR 0x8 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:135 @ #define ATMEL_HLCDC_MAX_LAYERS 6 +/* XLCDC controller specific registers */ +#define ATMEL_XLCDC_LAYER_ENR 0x10 +#define ATMEL_XLCDC_LAYER_EN BIT(0) + +#define ATMEL_XLCDC_LAYER_IER 0x0 +#define ATMEL_XLCDC_LAYER_IDR 0x4 +#define ATMEL_XLCDC_LAYER_ISR 0xc +#define ATMEL_XLCDC_LAYER_OVR_IRQ(p) BIT(2 + (8 * (p))) + +#define ATMEL_XLCDC_LAYER_PLANE_ADDR(p) (((p) * 0x4) + 0x18) + +#define ATMEL_XLCDC_LAYER_DMA_CFG 0 + +#define ATMEL_XLCDC_LAYER_DMA BIT(0) +#define ATMEL_XLCDC_LAYER_REP BIT(1) +#define ATMEL_XLCDC_LAYER_DISCEN BIT(4) + +#define ATMEL_XLCDC_LAYER_SFACTC_A0_MULT_AS (4 << 6) +#define ATMEL_XLCDC_LAYER_SFACTA_ONE BIT(9) +#define ATMEL_XLCDC_LAYER_DFACTC_M_A0_MULT_AS (6 << 11) +#define ATMEL_XLCDC_LAYER_DFACTA_ONE BIT(14) + +#define ATMEL_XLCDC_LAYER_A0_SHIFT 16 +#define ATMEL_XLCDC_LAYER_A0(x) \ + ((x) << ATMEL_XLCDC_LAYER_A0_SHIFT) + +#define ATMEL_XLCDC_LAYER_VSCALER_LUMA_ENABLE BIT(0) +#define ATMEL_XLCDC_LAYER_VSCALER_CHROMA_ENABLE BIT(1) +#define ATMEL_XLCDC_LAYER_HSCALER_LUMA_ENABLE BIT(4) +#define ATMEL_XLCDC_LAYER_HSCALER_CHROMA_ENABLE BIT(5) + +#define ATMEL_XLCDC_LAYER_VXSYCFG_ONE BIT(0) +#define ATMEL_XLCDC_LAYER_VXSYTAP2_ENABLE BIT(4) +#define ATMEL_XLCDC_LAYER_VXSCCFG_ONE BIT(16) +#define ATMEL_XLCDC_LAYER_VXSCTAP2_ENABLE BIT(20) + +#define ATMEL_XLCDC_LAYER_HXSYCFG_ONE BIT(0) +#define ATMEL_XLCDC_LAYER_HXSYTAP2_ENABLE BIT(4) +#define ATMEL_XLCDC_LAYER_HXSCCFG_ONE BIT(16) +#define ATMEL_XLCDC_LAYER_HXSCTAP2_ENABLE BIT(20) + /** * Atmel HLCDC Layer registers layout structure * @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:204 @ * @disc_pos: discard area position register * @disc_size: discard area size register * @csc: color space conversion register + * @vxs_config: vertical scalar filter taps control register + * @hxs_config: horizontal scalar filter taps control register */ struct atmel_hlcdc_layer_cfg_layout { int xstride[ATMEL_HLCDC_LAYER_MAX_PLANES]; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:225 @ int disc_pos; int disc_size; int csc; + int vxs_config; + int hxs_config; }; +struct atmel_hlcdc_plane_state; +struct atmel_hlcdc_dc; + /** * Atmel HLCDC DMA descriptor structure * @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:344 @ } /** + * struct atmel_lcdc_dc_ops - describes atmel_lcdc ops group + * to differentiate HLCDC and XLCDC IP code support. + * @plane_setup_scaler: update the vertical and horizontal scaling factors + * @update_lcdc_buffers: update the each LCDC layers DMA registers. + * @lcdc_atomic_disable: disable LCDC interrupts and layers + * @lcdc_update_general_settings: update each LCDC layers general + * confiugration register. + * @lcdc_atomic_update: enable the LCDC layers and interrupts. + * @lcdc_csc_init: update the color space conversion co-efficient of + * High-end overlay register. + * @lcdc_irq_dbg: to raise alert incase of interrupt overrun in any LCDC layer. + */ +struct atmel_lcdc_dc_ops { + void (*plane_setup_scaler)(struct atmel_hlcdc_plane *plane, + struct atmel_hlcdc_plane_state *state); + void (*update_lcdc_buffers)(struct atmel_hlcdc_plane *plane, + struct atmel_hlcdc_plane_state *state, + u32 sr, int i); + void (*lcdc_atomic_disable)(struct atmel_hlcdc_plane *plane, + struct atmel_hlcdc_dc *dc); + void (*lcdc_update_general_settings)(struct atmel_hlcdc_plane *plane, + struct atmel_hlcdc_plane_state *state); + void (*lcdc_atomic_update)(struct atmel_hlcdc_plane *plane, + struct atmel_hlcdc_dc *dc); + void (*lcdc_csc_init)(struct atmel_hlcdc_plane *plane, + const struct atmel_hlcdc_layer_desc *desc); + void (*lcdc_irq_dbg)(struct atmel_hlcdc_plane *plane, + const struct atmel_hlcdc_layer_desc *desc); +}; + +/** * Atmel HLCDC Display Controller description structure. * * This structure describes the HLCDC IP capabilities and depends on the @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:390 @ * @conflicting_output_formats: true if RGBXXX output formats conflict with * each other. * @fixed_clksrc: true if clock source is fixed + * @is_xlcdc: true if XLCDC IP is supported * @layers: a layer description table describing available layers * @nlayers: layer description table size + * @ops: atmel lcdc dc ops */ struct atmel_hlcdc_dc_desc { int min_width; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:405 @ int max_hpw; bool conflicting_output_formats; bool fixed_clksrc; + bool is_xlcdc; const struct atmel_hlcdc_layer_desc *layers; int nlayers; + const struct atmel_lcdc_dc_ops *ops; }; /** @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:435 @ } suspend; }; +extern const struct atmel_lcdc_dc_ops atmel_hlcdc_ops; +extern const struct atmel_lcdc_dc_ops atmel_xlcdc_ops; + extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats; extern struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c linux-microchip/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c --- linux-6.6.51/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c 2024-11-26 14:02:37.158487607 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:285 @ coeff_tab[i]); } -static void atmel_hlcdc_plane_setup_scaler(struct atmel_hlcdc_plane *plane, - struct atmel_hlcdc_plane_state *state) +static +void atmel_hlcdc_plane_setup_scaler(struct atmel_hlcdc_plane *plane, + struct atmel_hlcdc_plane_state *state) { const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; u32 xfactor, yfactor; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:334 @ yfactor)); } +static +void atmel_xlcdc_plane_setup_scaler(struct atmel_hlcdc_plane *plane, + struct atmel_hlcdc_plane_state *state) +{ + const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; + u32 xfactor, yfactor; + + if (!desc->layout.scaler_config) + return; + + if (state->crtc_w == state->src_w && state->crtc_h == state->src_h) { + atmel_hlcdc_layer_write_cfg(&plane->layer, + desc->layout.scaler_config, 0); + return; + } + + /* xfactor = round[(2^20 * XMEMSIZE)/XSIZE)] */ + xfactor = (u32)(((1 << 20) * state->src_w) / state->crtc_w); + + /* yfactor = round[(2^20 * YMEMSIZE)/YSIZE)] */ + yfactor = (u32)(((1 << 20) * state->src_h) / state->crtc_h); + + atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config, + ATMEL_XLCDC_LAYER_VSCALER_LUMA_ENABLE | + ATMEL_XLCDC_LAYER_VSCALER_CHROMA_ENABLE | + ATMEL_XLCDC_LAYER_HSCALER_LUMA_ENABLE | + ATMEL_XLCDC_LAYER_HSCALER_CHROMA_ENABLE); + + atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config + 1, + yfactor); + atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config + 3, + xfactor); + + /* + * With YCbCr 4:2:0 window resampling, configuration register + * LCDC_HEOCFG25.VXSCFACT and LCDC_HEOCFG27.HXSCFACT values are half + * the value of yfactor and xfactor. + * + * On the other hand, with YCbCr 4:2:2 window resampling, only the + * configuration register LCDC_HEOCFG27.HXSCFACT value is half the value + * of the xfactor; the value of LCDC_HEOCFG25.VXSCFACT is yfactor (no + * division by 2). + */ + switch (state->base.fb->format->format) { + /* YCbCr 4:2:2 */ + case DRM_FORMAT_YUYV: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_YVYU: + case DRM_FORMAT_VYUY: + case DRM_FORMAT_YUV422: + case DRM_FORMAT_NV61: + xfactor /= 2; + break; + + /* YCbCr 4:2:0 */ + case DRM_FORMAT_YUV420: + case DRM_FORMAT_NV21: + yfactor /= 2; + xfactor /= 2; + break; + default: + break; + } + + atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config + 2, + yfactor); + atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config + 4, + xfactor); +} + static void atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane, struct atmel_hlcdc_plane_state *state) { const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; + struct atmel_hlcdc_dc *dc = plane->base.dev->dev_private; if (desc->layout.size) atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.size, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:427 @ ATMEL_HLCDC_LAYER_POS(state->crtc_x, state->crtc_y)); - atmel_hlcdc_plane_setup_scaler(plane, state); + dc->desc->ops->plane_setup_scaler(plane, state); } -static void -atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane, - struct atmel_hlcdc_plane_state *state) +static +void atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane, + struct atmel_hlcdc_plane_state *state) { unsigned int cfg = ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 | state->ahb_id; const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:468 @ cfg); } +static +void atmel_xlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane, + struct atmel_hlcdc_plane_state *state) +{ + const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; + const struct drm_format_info *format = state->base.fb->format; + unsigned int cfg; + + atmel_hlcdc_layer_write_cfg(&plane->layer, ATMEL_XLCDC_LAYER_DMA_CFG, + ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 | state->ahb_id); + + cfg = ATMEL_XLCDC_LAYER_DMA | ATMEL_XLCDC_LAYER_REP; + + if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) { + /* + * Alpha Blending bits specific to SAM9X7 SoC + */ + cfg |= ATMEL_XLCDC_LAYER_SFACTC_A0_MULT_AS | + ATMEL_XLCDC_LAYER_SFACTA_ONE | + ATMEL_XLCDC_LAYER_DFACTC_M_A0_MULT_AS | + ATMEL_XLCDC_LAYER_DFACTA_ONE; + if (format->has_alpha) + cfg |= ATMEL_XLCDC_LAYER_A0(0xff); + else + cfg |= ATMEL_XLCDC_LAYER_A0(state->base.alpha); + } + + if (state->disc_h && state->disc_w) + cfg |= ATMEL_XLCDC_LAYER_DISCEN; + + atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.general_config, + cfg); +} + static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane, struct atmel_hlcdc_plane_state *state) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:546 @ } } +static void update_hlcdc_buffers(struct atmel_hlcdc_plane *plane, + struct atmel_hlcdc_plane_state *state, + u32 sr, int i) +{ + atmel_hlcdc_layer_write_reg(&plane->layer, + ATMEL_HLCDC_LAYER_PLANE_HEAD(i), + state->dscrs[i]->self); + + if (sr & ATMEL_HLCDC_LAYER_EN) + return; + + atmel_hlcdc_layer_write_reg(&plane->layer, + ATMEL_HLCDC_LAYER_PLANE_ADDR(i), + state->dscrs[i]->addr); + atmel_hlcdc_layer_write_reg(&plane->layer, + ATMEL_HLCDC_LAYER_PLANE_CTRL(i), + state->dscrs[i]->ctrl); + atmel_hlcdc_layer_write_reg(&plane->layer, + ATMEL_HLCDC_LAYER_PLANE_NEXT(i), + state->dscrs[i]->self); +} + +static void update_xlcdc_buffers(struct atmel_hlcdc_plane *plane, + struct atmel_hlcdc_plane_state *state, + u32 sr, int i) +{ + atmel_hlcdc_layer_write_reg(&plane->layer, + ATMEL_XLCDC_LAYER_PLANE_ADDR(i), + state->dscrs[i]->addr); +} + static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane, - struct atmel_hlcdc_plane_state *state) + struct atmel_hlcdc_plane_state *state) { const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; + struct atmel_hlcdc_dc *dc = plane->base.dev->dev_private; struct drm_framebuffer *fb = state->base.fb; u32 sr; int i; - sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR); + if (!dc->desc->is_xlcdc) + sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR); for (i = 0; i < state->nplanes; i++) { struct drm_gem_dma_object *gem = drm_fb_dma_get_gem_obj(fb, i); state->dscrs[i]->addr = gem->dma_addr + state->offsets[i]; - atmel_hlcdc_layer_write_reg(&plane->layer, - ATMEL_HLCDC_LAYER_PLANE_HEAD(i), - state->dscrs[i]->self); - - if (!(sr & ATMEL_HLCDC_LAYER_EN)) { - atmel_hlcdc_layer_write_reg(&plane->layer, - ATMEL_HLCDC_LAYER_PLANE_ADDR(i), - state->dscrs[i]->addr); - atmel_hlcdc_layer_write_reg(&plane->layer, - ATMEL_HLCDC_LAYER_PLANE_CTRL(i), - state->dscrs[i]->ctrl); - atmel_hlcdc_layer_write_reg(&plane->layer, - ATMEL_HLCDC_LAYER_PLANE_NEXT(i), - state->dscrs[i]->self); - } + dc->desc->ops->update_lcdc_buffers(plane, state, sr, i); if (desc->layout.xstride[i]) atmel_hlcdc_layer_write_cfg(&plane->layer, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:840 @ return 0; } -static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p, - struct drm_atomic_state *state) +static void hlcdc_atomic_disable(struct atmel_hlcdc_plane *plane, + struct atmel_hlcdc_dc *dc) { - struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); - /* Disable interrupts */ atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IDR, 0xffffffff); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:857 @ atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR); } +static void xlcdc_atomic_disable(struct atmel_hlcdc_plane *plane, + struct atmel_hlcdc_dc *dc) +{ + /* Disable interrupts */ + atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_XLCDC_LAYER_IDR, + 0xffffffff); + + /* Disable the layer */ + atmel_hlcdc_layer_write_reg(&plane->layer, + ATMEL_XLCDC_LAYER_ENR, 0); + + /* + * Updating XLCDC_xxxCFGx, XLCDC_xxxFBA and XLCDC_xxxEN, + * (where xxx indicates each layer) requires writing one to the + * Update Attribute field for each layer in LCDC_ATTRE register for SAM9X7. + */ + regmap_write(dc->hlcdc->regmap, ATMEL_XLCDC_ATTRE, ATMEL_XLCDC_BASE_UPDATE | + ATMEL_XLCDC_OVR1_UPDATE | ATMEL_XLCDC_OVR3_UPDATE | + ATMEL_XLCDC_HEO_UPDATE); + + /* Clear all pending interrupts */ + atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_XLCDC_LAYER_ISR); +} + +static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p, + struct drm_atomic_state *state) +{ + struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); + struct atmel_hlcdc_dc *dc = plane->base.dev->dev_private; + + dc->desc->ops->lcdc_atomic_disable(plane, dc); +} + +static void hlcdc_atomic_update(struct atmel_hlcdc_plane *plane, + struct atmel_hlcdc_dc *dc) +{ + u32 sr; + + /* Enable the overrun interrupts. */ + atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IER, + ATMEL_HLCDC_LAYER_OVR_IRQ(0) | + ATMEL_HLCDC_LAYER_OVR_IRQ(1) | + ATMEL_HLCDC_LAYER_OVR_IRQ(2)); + + /* Apply the new config at the next SOF event. */ + sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR); + atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHER, + ATMEL_HLCDC_LAYER_UPDATE | + (sr & ATMEL_HLCDC_LAYER_EN ? + ATMEL_HLCDC_LAYER_A2Q : ATMEL_HLCDC_LAYER_EN)); +} + +static void xlcdc_atomic_update(struct atmel_hlcdc_plane *plane, + struct atmel_hlcdc_dc *dc) +{ + /* Enable the overrun interrupts. */ + atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_XLCDC_LAYER_IER, + ATMEL_XLCDC_LAYER_OVR_IRQ(0) | + ATMEL_XLCDC_LAYER_OVR_IRQ(1) | + ATMEL_XLCDC_LAYER_OVR_IRQ(2)); + + atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_XLCDC_LAYER_ENR, + ATMEL_XLCDC_LAYER_EN); + + /* + * Updating XLCDC_xxxCFGx, XLCDC_xxxFBA and XLCDC_xxxEN, + * (where xxx indicates each layer) requires writing one to the + * Update Attribute field for each layer in LCDC_ATTRE register for SAM9X7. + */ + regmap_write(dc->hlcdc->regmap, ATMEL_XLCDC_ATTRE, ATMEL_XLCDC_BASE_UPDATE | + ATMEL_XLCDC_OVR1_UPDATE | ATMEL_XLCDC_OVR3_UPDATE | + ATMEL_XLCDC_HEO_UPDATE); +} + static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p, struct drm_atomic_state *state) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:939 @ struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p); struct atmel_hlcdc_plane_state *hstate = drm_plane_state_to_atmel_hlcdc_plane_state(new_s); - u32 sr; + struct atmel_hlcdc_dc *dc = p->dev->dev_private; if (!new_s->crtc || !new_s->fb) return; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:950 @ } atmel_hlcdc_plane_update_pos_and_size(plane, hstate); - atmel_hlcdc_plane_update_general_settings(plane, hstate); + dc->desc->ops->lcdc_update_general_settings(plane, hstate); atmel_hlcdc_plane_update_format(plane, hstate); atmel_hlcdc_plane_update_clut(plane, hstate); atmel_hlcdc_plane_update_buffers(plane, hstate); atmel_hlcdc_plane_update_disc_area(plane, hstate); - /* Enable the overrun interrupts. */ - atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IER, - ATMEL_HLCDC_LAYER_OVR_IRQ(0) | - ATMEL_HLCDC_LAYER_OVR_IRQ(1) | - ATMEL_HLCDC_LAYER_OVR_IRQ(2)); + dc->desc->ops->lcdc_atomic_update(plane, dc); +} - /* Apply the new config at the next SOF event. */ - sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR); - atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHER, - ATMEL_HLCDC_LAYER_UPDATE | - (sr & ATMEL_HLCDC_LAYER_EN ? - ATMEL_HLCDC_LAYER_A2Q : ATMEL_HLCDC_LAYER_EN)); +static void hlcdc_csc_init(struct atmel_hlcdc_plane *plane, + const struct atmel_hlcdc_layer_desc *desc) +{ + /* + * TODO: declare a "yuv-to-rgb-conv-factors" property to let + * userspace modify these factors (using a BLOB property ?). + */ + static const u32 hlcdc_csc_coeffs[] = { + 0x4c900091, + 0x7a5f5090, + 0x40040890 + }; + + for (int i = 0; i < ARRAY_SIZE(hlcdc_csc_coeffs); i++) { + atmel_hlcdc_layer_write_cfg(&plane->layer, + desc->layout.csc + i, + hlcdc_csc_coeffs[i]); + } +} + +static void xlcdc_csc_init(struct atmel_hlcdc_plane *plane, + const struct atmel_hlcdc_layer_desc *desc) +{ + /* + * yuv-to-rgb-conv-factors are now defined from LCDC_HEOCFG16 to + * LCDC_HEOCFG21 registers in SAM9X7. + */ + static const u32 xlcdc_csc_coeffs[] = { + 0x00000488, + 0x00000648, + 0x1EA00480, + 0x00001D28, + 0x08100480, + 0x00000000, + 0x00000007 + }; + + for (int i = 0; i < ARRAY_SIZE(xlcdc_csc_coeffs); i++) { + atmel_hlcdc_layer_write_cfg(&plane->layer, + desc->layout.csc + i, + xlcdc_csc_coeffs[i]); + } + + if (desc->layout.vxs_config && desc->layout.hxs_config) { + /* + * Updating vxs.config and hxs.config fixes the + * Green Color Issue in SAM9X7 EGT Video Player App + */ + atmel_hlcdc_layer_write_cfg(&plane->layer, + desc->layout.vxs_config, + ATMEL_XLCDC_LAYER_VXSYCFG_ONE | + ATMEL_XLCDC_LAYER_VXSYTAP2_ENABLE | + ATMEL_XLCDC_LAYER_VXSCCFG_ONE | + ATMEL_XLCDC_LAYER_VXSCTAP2_ENABLE); + + atmel_hlcdc_layer_write_cfg(&plane->layer, + desc->layout.hxs_config, + ATMEL_XLCDC_LAYER_HXSYCFG_ONE | + ATMEL_XLCDC_LAYER_HXSYTAP2_ENABLE | + ATMEL_XLCDC_LAYER_HXSCCFG_ONE | + ATMEL_XLCDC_LAYER_HXSCTAP2_ENABLE); + } } static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane) { const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; + struct atmel_hlcdc_dc *dc = plane->base.dev->dev_private; if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER || desc->type == ATMEL_HLCDC_CURSOR_LAYER) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1050 @ return ret; } - if (desc->layout.csc) { - /* - * TODO: decare a "yuv-to-rgb-conv-factors" property to let - * userspace modify these factors (using a BLOB property ?). - */ - atmel_hlcdc_layer_write_cfg(&plane->layer, - desc->layout.csc, - 0x4c900091); - atmel_hlcdc_layer_write_cfg(&plane->layer, - desc->layout.csc + 1, - 0x7a5f5090); - atmel_hlcdc_layer_write_cfg(&plane->layer, - desc->layout.csc + 2, - 0x40040890); - } + if (desc->layout.csc) + dc->desc->ops->lcdc_csc_init(plane, desc); return 0; } -void atmel_hlcdc_plane_irq(struct atmel_hlcdc_plane *plane) +static void hlcdc_irq_dbg(struct atmel_hlcdc_plane *plane, + const struct atmel_hlcdc_layer_desc *desc) { - const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; - u32 isr; - - isr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR); + u32 isr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR); /* * There's not much we can do in case of overrun except informing @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1073 @ desc->name); } +static void xlcdc_irq_dbg(struct atmel_hlcdc_plane *plane, + const struct atmel_hlcdc_layer_desc *desc) +{ + u32 isr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_XLCDC_LAYER_ISR); + + /* + * There's not much we can do in case of overrun except informing + * the user. However, we are in interrupt context here, hence the + * use of dev_dbg(). + */ + if (isr & + (ATMEL_XLCDC_LAYER_OVR_IRQ(0) | ATMEL_XLCDC_LAYER_OVR_IRQ(1) | + ATMEL_XLCDC_LAYER_OVR_IRQ(2))) + dev_dbg(plane->base.dev->dev, "overrun on plane %s\n", + desc->name); +} + +void atmel_hlcdc_plane_irq(struct atmel_hlcdc_plane *plane) +{ + const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc; + struct atmel_hlcdc_dc *dc = plane->base.dev->dev_private; + + dc->desc->ops->lcdc_irq_dbg(plane, desc); +} + +const struct atmel_lcdc_dc_ops atmel_hlcdc_ops = { + .plane_setup_scaler = atmel_hlcdc_plane_setup_scaler, + .update_lcdc_buffers = update_hlcdc_buffers, + .lcdc_atomic_disable = hlcdc_atomic_disable, + .lcdc_update_general_settings = atmel_hlcdc_plane_update_general_settings, + .lcdc_atomic_update = hlcdc_atomic_update, + .lcdc_csc_init = hlcdc_csc_init, + .lcdc_irq_dbg = hlcdc_irq_dbg, +}; + +const struct atmel_lcdc_dc_ops atmel_xlcdc_ops = { + .plane_setup_scaler = atmel_xlcdc_plane_setup_scaler, + .update_lcdc_buffers = update_xlcdc_buffers, + .lcdc_atomic_disable = xlcdc_atomic_disable, + .lcdc_update_general_settings = atmel_xlcdc_plane_update_general_settings, + .lcdc_atomic_update = xlcdc_atomic_update, + .lcdc_csc_init = xlcdc_csc_init, + .lcdc_irq_dbg = xlcdc_irq_dbg, +}; + static const struct drm_plane_helper_funcs atmel_hlcdc_layer_plane_helper_funcs = { .atomic_check = atmel_hlcdc_plane_atomic_check, .atomic_update = atmel_hlcdc_plane_atomic_update, diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/gpu/drm/bridge/dw-mipi-dsi-mchp.c linux-microchip/drivers/gpu/drm/bridge/dw-mipi-dsi-mchp.c --- linux-6.6.51/drivers/gpu/drm/bridge/dw-mipi-dsi-mchp.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/gpu/drm/bridge/dw-mipi-dsi-mchp.c 2024-11-26 14:02:37.162487678 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries + * + * Author: Manikandan Muralidharan <manikandan.m@microchip.com> + * + */ + +#include <drm/bridge/dw_mipi_dsi.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_print.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/mfd/syscon.h> +#include <linux/mod_devicetable.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define DSI_PLL_REF_CLK 24000000 + +#define DSI_PWR_UP 0x04 +#define HOST_RESET BIT(0) +#define HOST_PWRUP 0 + +#define DSI_PHY_RSTZ 0xa0 +#define PHY_SHUTDOWNZ 0 + +#define DSI_PHY_TST_CTRL0 0xb4 +#define PHY_TESTCLK BIT(1) +#define PHY_UNTESTCLK 0 +#define PHY_TESTCLR BIT(0) +#define PHY_UNTESTCLR 0 + +#define DSI_PHY_TST_CTRL1 0xb8 +#define PHY_TESTEN BIT(16) +#define PHY_UNTESTEN 0 +#define PHY_TESTDOUT(n) (((n) & 0xff) << 8) +#define PHY_TESTDIN(n) (((n) & 0xff) << 0) + +#define BYPASS_VCO_RANGE BIT(7) +#define VCO_RANGE_CON_SEL(val) (((val) & 0x7) << 3) +#define VCO_IN_CAP_CON_LOW BIT(1) + +#define CP_CURRENT_0 0x2 +#define CP_CURRENT_1 0x4 +#define CP_CURRENT_2 0x5 +#define CP_CURRENT_3 0x6 +#define CP_CURRENT_4 0x7 +#define CP_CURRENT_5 0x8 +#define CP_CURRENT_6 0xc +#define CP_CURRENT_SEL(val) ((val) & 0xf) +#define CP_PROGRAM_EN BIT(7) + +#define LPF_RESISTORS_18KOHM 0x0 +#define LPF_RESISTORS_15_6KOHM 0x1 +#define LPF_RESISTORS_15KOHM 0x2 +#define LPF_RESISTORS_14_4KOHM 0x4 +#define LPF_RESISTORS_12_8KOHM 0x8 +#define LPF_RESISTORS_11_4KOHM 0x10 +#define LPF_RESISTORS_10_5KOHM 0x20 +#define LPF_PROGRAM_EN BIT(6) +#define LPF_RESISTORS_SEL(val) ((val) & 0x3f) + +#define HSFREQRANGE_SEL(val) (((val) & 0x3f) << 1) + +#define INPUT_DIVIDER(val) (((val) - 1) & 0x7f) +#define LOW_PROGRAM_EN 0 +#define HIGH_PROGRAM_EN BIT(7) +#define LOOP_DIV_LOW_SEL(val) (((val) - 1) & 0x1f) +#define LOOP_DIV_HIGH_SEL(val) ((((val) - 1) >> 5) & 0xf) +#define PLL_LOOP_DIV_EN BIT(5) +#define PLL_INPUT_DIV_EN BIT(4) + +#define PLL_BIAS_CUR_SEL_CAP_VCO_CONTROL 0x10 +#define PLL_CP_CONTROL_PLL_LOCK_BYPASS 0x11 +#define PLL_LPF_AND_CP_CONTROL 0x12 +#define PLL_INPUT_DIVIDER_RATIO 0x17 +#define PLL_LOOP_DIVIDER_RATIO 0x18 +#define PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL 0x19 + +#define SFR_ISS_CFG 0x240 +#define ISS_CFG_DSI_MODE 1 + +struct dw_mipi_dsi_mchp_chip_data { + unsigned int max_data_lanes; + struct dw_mipi_dsi_phy_ops *phy_ops; + bool has_sfr; +}; + +struct dw_mipi_dsi_mchp { + struct device *dev; + void __iomem *base; + struct dw_mipi_dsi_plat_data pdata; + struct dw_mipi_dsi *dsi; + + /* needed for PLL config */ + unsigned int lane_mbps; + u16 input_div; + u16 feedback_div; + u32 format; + + struct clk *pclk; + struct clk *pllref_clk; +}; + +struct dphy_pll_parameter_map { + unsigned int max_mbps; + u8 hsfreqrange; + u8 icpctrl; + u8 lpfctrl; +}; + +static const struct dphy_pll_parameter_map dppa_map[] = { + { 89, 0x00, CP_CURRENT_1, LPF_RESISTORS_11_4KOHM }, + { 99, 0x20, CP_CURRENT_1, LPF_RESISTORS_11_4KOHM }, + { 109, 0x40, CP_CURRENT_1, LPF_RESISTORS_11_4KOHM }, + { 129, 0x02, CP_CURRENT_5, LPF_RESISTORS_12_8KOHM }, + { 139, 0x22, CP_CURRENT_5, LPF_RESISTORS_12_8KOHM }, + { 149, 0x42, CP_CURRENT_5, LPF_RESISTORS_12_8KOHM }, + { 169, 0x04, CP_CURRENT_6, LPF_RESISTORS_12_8KOHM }, + { 179, 0x24, CP_CURRENT_6, LPF_RESISTORS_12_8KOHM }, + { 199, 0x44, CP_CURRENT_6, LPF_RESISTORS_12_8KOHM }, + { 219, 0x06, CP_CURRENT_6, LPF_RESISTORS_12_8KOHM }, + { 239, 0x26, CP_CURRENT_6, LPF_RESISTORS_12_8KOHM }, + { 249, 0x46, CP_CURRENT_6, LPF_RESISTORS_12_8KOHM }, + { 269, 0x08, CP_CURRENT_0, LPF_RESISTORS_12_8KOHM }, + { 299, 0x28, CP_CURRENT_0, LPF_RESISTORS_12_8KOHM }, + { 329, 0x0a, CP_CURRENT_2, LPF_RESISTORS_12_8KOHM }, + { 359, 0x2a, CP_CURRENT_2, LPF_RESISTORS_12_8KOHM }, + { 399, 0x4a, CP_CURRENT_2, LPF_RESISTORS_12_8KOHM }, + { 449, 0x0C, CP_CURRENT_2, LPF_RESISTORS_15_6KOHM }, + { 499, 0x2c, CP_CURRENT_2, LPF_RESISTORS_15_6KOHM }, + { 549, 0x0e, CP_CURRENT_3, LPF_RESISTORS_11_4KOHM }, + { 599, 0x2e, CP_CURRENT_3, LPF_RESISTORS_11_4KOHM }, + { 649, 0x10, CP_CURRENT_3, LPF_RESISTORS_14_4KOHM }, + { 699, 0x30, CP_CURRENT_3, LPF_RESISTORS_14_4KOHM }, + { 749, 0x12, CP_CURRENT_3, LPF_RESISTORS_14_4KOHM }, + { 799, 0x32, CP_CURRENT_3, LPF_RESISTORS_14_4KOHM }, + { 849, 0x52, CP_CURRENT_3, LPF_RESISTORS_14_4KOHM }, + { 899, 0x72, CP_CURRENT_3, LPF_RESISTORS_14_4KOHM }, + { 949, 0x14, CP_CURRENT_4, LPF_RESISTORS_11_4KOHM }, + {1000, 0x34, CP_CURRENT_4, LPF_RESISTORS_11_4KOHM } +}; + +struct hstt { + unsigned int maxfreq; + struct dw_mipi_dsi_dphy_timing timing; +}; + +#define HSTT(_maxfreq, _c_lp2hs, _c_hs2lp, _d_lp2hs, _d_hs2lp) \ +{ \ + .maxfreq = _maxfreq, \ + .timing = { \ + .clk_lp2hs = _c_lp2hs, \ + .clk_hs2lp = _c_hs2lp, \ + .data_lp2hs = _d_lp2hs, \ + .data_hs2lp = _d_hs2lp, \ + } \ +} + +struct hstt hstt_table[] = { + HSTT(90, 32, 20, 26, 13), + HSTT(100, 35, 23, 28, 14), + HSTT(110, 32, 22, 26, 13), + HSTT(130, 31, 20, 27, 13), + HSTT(140, 33, 22, 26, 14), + HSTT(150, 33, 21, 26, 14), + HSTT(170, 32, 20, 27, 13), + HSTT(180, 36, 23, 30, 15), + HSTT(200, 40, 22, 33, 15), + HSTT(220, 40, 22, 33, 15), + HSTT(240, 44, 24, 36, 16), + HSTT(250, 48, 24, 38, 17), + HSTT(270, 48, 24, 38, 17), + HSTT(300, 50, 27, 41, 18), + HSTT(330, 56, 28, 45, 18), + HSTT(360, 59, 28, 48, 19), + HSTT(400, 61, 30, 50, 20), + HSTT(450, 67, 31, 55, 21), + HSTT(500, 73, 31, 59, 22), + HSTT(550, 79, 36, 63, 24), + HSTT(600, 83, 37, 68, 25), + HSTT(650, 90, 38, 73, 27), + HSTT(700, 95, 40, 77, 28), + HSTT(750, 102, 40, 84, 28), + HSTT(800, 106, 42, 87, 30), + HSTT(850, 113, 44, 93, 31), + HSTT(900, 118, 47, 98, 32), + HSTT(950, 124, 47, 102, 34), + HSTT(1000, 130, 49, 107, 35), +}; + +static int max_mbps_to_parameter(unsigned int max_mbps) +{ + int index; + + for (index = 0; index < ARRAY_SIZE(dppa_map); index++) + if (dppa_map[index].max_mbps >= max_mbps) + return index; + + return -EINVAL; +} + +static inline void dsi_write(struct dw_mipi_dsi_mchp *dsi, u32 reg, u32 val) +{ + writel(val, dsi->base + reg); +} + +static inline u32 dsi_read(struct dw_mipi_dsi_mchp *dsi, u32 reg) +{ + return readl(dsi->base + reg); +} + +static void dw_mipi_dsi_phy_write(struct dw_mipi_dsi_mchp *dsi, + u8 test_code, + u8 test_data) +{ + /* + * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content + * is latched internally as the current test code. Test data is + * programmed internally by rising edge on TESTCLK. + */ + + /* General DPHY control operation */ + + dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR); + dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_TESTEN | PHY_TESTDOUT(1) | + PHY_TESTDIN(test_code)); + dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLK | PHY_UNTESTCLR); + dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_UNTESTEN | PHY_TESTDOUT(0) | + PHY_TESTDIN(test_data)); + dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR); + dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLK | PHY_UNTESTCLR); +} + +static int dw_mipi_dsi_mchp_init(void *priv_data) +{ + struct dw_mipi_dsi_mchp *dsi = priv_data; + int ret, index, vco; + + /* + * Get vco from frequency(lane_mbps) + * vco frequency table + * 000 - between 80 and 200 MHz + * 001 - between 200 and 300 MHz + * 010 - between 300 and 500 MHz + * 011 - between 500 and 700 MHz + * 100 - between 700 and 900 MHz + * 101 - between 900 and 1000 MHz + */ + vco = (dsi->lane_mbps < 200) ? 0 : (dsi->lane_mbps + 100) / 200; + + index = max_mbps_to_parameter(dsi->lane_mbps); + if (index < 0) { + dev_err(dsi->dev, + "failed to get parameter for %dmbps clock\n", + dsi->lane_mbps); + return index; + } + + /* D-PHY in Shutdown mode */ + dsi_write(dsi, DSI_PHY_RSTZ, PHY_SHUTDOWNZ); + + dw_mipi_dsi_phy_write(dsi, PLL_BIAS_CUR_SEL_CAP_VCO_CONTROL, + BYPASS_VCO_RANGE | + VCO_RANGE_CON_SEL(vco) | + VCO_IN_CAP_CON_LOW); + + dw_mipi_dsi_phy_write(dsi, PLL_CP_CONTROL_PLL_LOCK_BYPASS, + CP_CURRENT_SEL(dppa_map[index].icpctrl)); + + dw_mipi_dsi_phy_write(dsi, PLL_LPF_AND_CP_CONTROL, + CP_PROGRAM_EN | LPF_PROGRAM_EN | + LPF_RESISTORS_SEL(dppa_map[index].lpfctrl)); + + dw_mipi_dsi_phy_write(dsi, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL, + PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN); + + dw_mipi_dsi_phy_write(dsi, PLL_INPUT_DIVIDER_RATIO, + INPUT_DIVIDER(dsi->input_div)); + + dw_mipi_dsi_phy_write(dsi, PLL_LOOP_DIVIDER_RATIO, + LOOP_DIV_LOW_SEL(dsi->feedback_div) | + LOW_PROGRAM_EN); + + dw_mipi_dsi_phy_write(dsi, PLL_LOOP_DIVIDER_RATIO, + LOOP_DIV_HIGH_SEL(dsi->feedback_div) | + HIGH_PROGRAM_EN); + + return ret; +} + +static int dw_mipi_dsi_mchp_get_lane_mbps(void *priv_data, + const struct drm_display_mode *mode, + unsigned long mode_flags, u32 lanes, + u32 format, unsigned int *lane_mbps) +{ + struct dw_mipi_dsi_mchp *dsi = priv_data; + unsigned int mpclk, target_mbps, desired_mbps; + unsigned int max_mbps = dppa_map[ARRAY_SIZE(dppa_map) - 1].max_mbps; + unsigned long best_freq, fvco_min, fvco_max, fin, fout; + unsigned long min_delta = ULONG_MAX, delta; + int bpp, min_prediv, max_prediv, _fbdiv, best_fbdiv, _prediv, best_prediv; + u64 freq_factor; + + dsi->format = format; + bpp = mipi_dsi_pixel_format_to_bpp(dsi->format); + if (bpp < 0) { + dev_err(dsi->dev, + "failed to get bpp for pixel format %d\n", + dsi->format); + return bpp; + } + + mpclk = DIV_ROUND_UP(mode->clock, MSEC_PER_SEC); + if (mpclk) { + /* take 1/0.8, since mbps must be bigger than bandwidth of RGB */ + desired_mbps = mpclk * (bpp / lanes) * 10 / 8; + if (desired_mbps < max_mbps) { + target_mbps = desired_mbps; + } else { + dev_err(dsi->dev, + "DPHY clock frequency is out of range\n"); + return -ERANGE; + } + } + + fin = clk_get_rate(dsi->pllref_clk); + fout = target_mbps * USEC_PER_SEC; + + /* constraint: 5Mhz <= Fref / N <= 40MHz */ + min_prediv = DIV_ROUND_UP(fin, 40 * USEC_PER_SEC); + max_prediv = fin / (5 * USEC_PER_SEC); + + /* constraint: 80MHz <= Fvco <= 1000Mhz */ + fvco_min = 80 * USEC_PER_SEC; + fvco_max = 1000 * USEC_PER_SEC; + + best_freq = 0; + for (_prediv = min_prediv; _prediv <= max_prediv; _prediv++) { + /* Fvco = Fref * M / N */ + freq_factor = (uint64_t)fout * _prediv; + do_div(freq_factor, fin); + _fbdiv = freq_factor; + /* + * Due to the use of a "by 2 pre-scaler," the range of the + * feedback multiplication value M is limited to even division + * numbers, and m must be greater than 6, not bigger than 512. + */ + if (_fbdiv < 6 || _fbdiv > 512) + continue; + + _fbdiv += _fbdiv % 2; + + freq_factor = (uint64_t)_fbdiv * fin; + do_div(freq_factor, _prediv); + if (freq_factor < fvco_min || freq_factor > fvco_max) + continue; + + delta = abs(fout - freq_factor); + if (delta < min_delta) { + best_prediv = _prediv; + best_fbdiv = _fbdiv; + min_delta = delta; + best_freq = freq_factor; + } + } + + if (best_freq) { + dsi->lane_mbps = DIV_ROUND_UP(best_freq, USEC_PER_SEC); + *lane_mbps = dsi->lane_mbps; + dsi->input_div = best_prediv; + dsi->feedback_div = best_fbdiv; + } else { + dev_err(dsi->dev, "Can not find best_freq for DPHY\n"); + return -EINVAL; + } + + return 0; +} + +static int dw_mipi_dsi_mchp_get_timing(void *priv_data, unsigned int lane_mbps, + struct dw_mipi_dsi_dphy_timing *timing) +{ + int index; + + for (index = 0; index < ARRAY_SIZE(hstt_table); index++) + if (lane_mbps < hstt_table[index].maxfreq) + break; + + if (index == ARRAY_SIZE(hstt_table)) + index--; + + *timing = hstt_table[index].timing; + + return 0; +} + +static void dw_mipi_dsi_mchp_power_on(void *priv_data) +{ + struct dw_mipi_dsi_mchp *dsi = priv_data; + + /* Enable the DSI wrapper */ + dsi_write(dsi, DSI_PWR_UP, HOST_PWRUP); +} + +static void dw_mipi_dsi_mchp_power_off(void *priv_data) +{ + struct dw_mipi_dsi_mchp *dsi = priv_data; + + /* Disable the DSI wrapper */ + dsi_write(dsi, DSI_PWR_UP, HOST_RESET); +} + +struct dw_mipi_dsi_phy_ops dw_mipi_dsi_mchp_phy_ops = { + .init = dw_mipi_dsi_mchp_init, + .power_on = dw_mipi_dsi_mchp_power_on, + .power_off = dw_mipi_dsi_mchp_power_off, + .get_lane_mbps = dw_mipi_dsi_mchp_get_lane_mbps, + .get_timing = dw_mipi_dsi_mchp_get_timing, +}; + +static int dw_mipi_dsi_mchp_probe(struct platform_device *pdev) +{ + struct dw_mipi_dsi_mchp *dsi; + struct resource *res; + struct regmap *sfr; + const struct dw_mipi_dsi_mchp_chip_data *cdata; + int ret; + + dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL); + if (!dsi) + return -ENOMEM; + + dsi->dev = &pdev->dev; + cdata = of_device_get_match_data(dsi->dev); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dsi->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dsi->base)) { + ret = PTR_ERR(dsi->base); + dev_err(dsi->dev, "Unable to get DSI Base address: %d\n", ret); + return ret; + } + + dsi->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(dsi->pclk)) { + ret = PTR_ERR(dsi->pclk); + dev_err(dsi->dev, "Unable to get pclk: %d\n", ret); + return ret; + } + + dsi->pllref_clk = devm_clk_get(&pdev->dev, "refclk"); + if (IS_ERR(dsi->pllref_clk)) { + ret = PTR_ERR(dsi->pllref_clk); + dev_err(dsi->dev, "Unable to get DSI PHY PLL reference clock: %d\n", + ret); + return ret; + } + + clk_set_rate(dsi->pllref_clk, DSI_PLL_REF_CLK); + if (clk_get_rate(dsi->pllref_clk) != DSI_PLL_REF_CLK) { + dev_err(dsi->dev, "Failed to set DSI PHY PLL reference clock to: %d\n", + DSI_PLL_REF_CLK); + return -1; + } + + ret = clk_prepare_enable(dsi->pllref_clk); + if (ret) { + dev_err(dsi->dev, "Failed to enable DSI PHY PLL reference clock: %d\n", + ret); + return ret; + } + + if (cdata->has_sfr) { + sfr = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "microchip,sfr"); + if (IS_ERR_OR_NULL(sfr)) { + ret = PTR_ERR(sfr); + dev_err(dsi->dev, "Failed to get handle on Special Function Register: %d\n", + ret); + goto err_dsi_probe; + } + + /* Select DSI in SFR's ISS Configuration Register */ + ret = regmap_write(sfr, SFR_ISS_CFG, ISS_CFG_DSI_MODE); + if (ret) { + dev_err(dsi->dev, "Failed to enable DSI in SFR ISS configuration register: %d\n", + ret); + goto err_dsi_probe; + } + } + dsi->pdata.base = dsi->base; + dsi->pdata.max_data_lanes = cdata->max_data_lanes; + dsi->pdata.phy_ops = cdata->phy_ops; + dsi->pdata.priv_data = dsi; + platform_set_drvdata(pdev, dsi); + + /* call synopsis probe */ + dsi->dsi = dw_mipi_dsi_probe(pdev, &dsi->pdata); + if (IS_ERR(dsi->dsi)) { + ret = PTR_ERR(dsi->dsi); + dev_err(dsi->dev, "Failed to initialize mipi dsi host: %d\n", ret); + goto err_dsi_probe; + } + + return 0; + +err_dsi_probe: + clk_disable_unprepare(dsi->pllref_clk); + + return ret; +} + +static int dw_mipi_dsi_mchp_remove(struct platform_device *pdev) +{ + struct dw_mipi_dsi_mchp *dsi = platform_get_drvdata(pdev); + + dw_mipi_dsi_remove(dsi->dsi); + clk_disable_unprepare(dsi->pllref_clk); + + return 0; +} + +static const struct dw_mipi_dsi_mchp_chip_data sam9x75_chip_data = { + .max_data_lanes = 4, + .phy_ops = &dw_mipi_dsi_mchp_phy_ops, + .has_sfr = true, +}; + +static const struct dw_mipi_dsi_mchp_chip_data sama7d65_chip_data = { + .max_data_lanes = 4, + .phy_ops = &dw_mipi_dsi_mchp_phy_ops, + .has_sfr = false, +}; + +static const struct of_device_id dw_mipi_dsi_mchp_dt_ids[] = { + { + .compatible = "microchip,sam9x75-mipi-dsi", + .data = &sam9x75_chip_data, + }, + { + .compatible = "microchip,sama7d65-mipi-dsi", + .data = &sama7d65_chip_data, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dw_mipi_dsi_mchp_dt_ids); + +struct platform_driver dw_mipi_dsi_mchp_driver = { + .probe = dw_mipi_dsi_mchp_probe, + .remove = dw_mipi_dsi_mchp_remove, + .driver = { + .of_match_table = dw_mipi_dsi_mchp_dt_ids, + .name = "dw-mipi-dsi-mchp", + }, +}; +module_platform_driver(dw_mipi_dsi_mchp_driver); + +MODULE_AUTHOR("Manikandan Muralidharan <manikandan.m@microchip.com>"); +MODULE_DESCRIPTION("Microchip DW MIPI DSI Host Controller Driver"); +MODULE_LICENSE("GPL"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/gpu/drm/bridge/Kconfig linux-microchip/drivers/gpu/drm/bridge/Kconfig --- linux-6.6.51/drivers/gpu/drm/bridge/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/gpu/drm/bridge/Kconfig 2024-11-26 14:02:37.158487607 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:176 @ to DP++. This is used with the i.MX6 imx-ldb driver. You are likely to say N here. +config DRM_MICROCHIP_LVDS_SERIALIZER + tristate "Microchip LVDS serailzer support" + depends on OF + depends on DRM_ATMEL_HLCDC + help + Support for Microchip's LVDS serializer. + +config DRM_MICROCHIP_DW_MIPI_DSI + tristate "Microchip specific extensions for Synopsys DW MIPI DSI" + depends on DRM_ATMEL_HLCDC + select DRM_DW_MIPI_DSI + help + This selects support for Microchip's SoC specific extensions + for the Synopsys DesignWare dsi driver. + config DRM_NWL_MIPI_DSI tristate "Northwest Logic MIPI DSI Host controller" depends on DRM diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/gpu/drm/bridge/Makefile linux-microchip/drivers/gpu/drm/bridge/Makefile --- linux-6.6.51/drivers/gpu/drm/bridge/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/gpu/drm/bridge/Makefile 2024-11-26 14:02:37.158487607 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:14 @ obj-$(CONFIG_DRM_LONTIUM_LT9611UXC) += lontium-lt9611uxc.o obj-$(CONFIG_DRM_LVDS_CODEC) += lvds-codec.o obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o +obj-$(CONFIG_DRM_MICROCHIP_LVDS_SERIALIZER) += microchip-lvds.o +obj-$(CONFIG_DRM_MICROCHIP_DW_MIPI_DSI) += dw-mipi-dsi-mchp.o obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o obj-$(CONFIG_DRM_PARADE_PS8640) += parade-ps8640.o diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/gpu/drm/bridge/microchip-lvds.c linux-microchip/drivers/gpu/drm/bridge/microchip-lvds.c --- linux-6.6.51/drivers/gpu/drm/bridge/microchip-lvds.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/gpu/drm/bridge/microchip-lvds.c 2024-11-26 14:02:37.162487678 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2023 Microchip Technology Inc. and its subsidiaries + * + * Author: Manikandan Muralidharan <manikandan.m@microchip.com> + * Author: Dharma Balasubiramani <dharma.b@microchip.com> + * + */ + +#include <linux/clk.h> +#include <linux/component.h> +#include <linux/delay.h> +#include <linux/jiffies.h> +#include <linux/mfd/syscon.h> +#include <linux/of_graph.h> +#include <linux/pinctrl/devinfo.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/reset.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> +#include <drm/drm_of.h> +#include <drm/drm_panel.h> +#include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_simple_kms_helper.h> + +#define LVDS_POLL_TIMEOUT_MS 1000 + +/* LVDSC register offsets */ +#define LVDSC_CR 0x00 +#define LVDSC_CFGR 0x04 +#define LVDSC_SR 0x0C +#define LVDSC_WPMR 0xE4 + +/* Bitfields in LVDSC_CR (Control Register) */ +#define LVDSC_CR_SER_EN BIT(0) + +/* Bitfields in LVDSC_CFGR (Configuration Register) */ +#define LVDSC_CFGR_PIXSIZE_24BITS 0 +#define LVDSC_CFGR_DEN_POL_HIGH 0 +#define LVDSC_CFGR_DC_UNBALANCED 0 +#define LVDSC_CFGR_MAPPING_JEIDA BIT(6) + +/*Bitfields in LVDSC_SR */ +#define LVDSC_SR_CS BIT(0) + +/* Bitfields in LVDSC_WPMR (Write Protection Mode Register) */ +#define LVDSC_WPMR_WPKEY_MASK GENMASK(31, 8) +#define LVDSC_WPMR_WPKEY_PSSWD 0x4C5644 + +struct mchp_lvds { + struct device *dev; + void __iomem *regs; + struct clk *pclk; + int format; /* vesa or jeida format */ + struct drm_panel *panel; + struct drm_bridge bridge; + struct drm_bridge *panel_bridge; +}; + +static inline struct mchp_lvds *bridge_to_lvds(struct drm_bridge *bridge) +{ + return container_of(bridge, struct mchp_lvds, bridge); +} + +static inline u32 lvds_readl(struct mchp_lvds *lvds, u32 offset) +{ + return readl_relaxed(lvds->regs + offset); +} + +static inline void lvds_writel(struct mchp_lvds *lvds, u32 offset, u32 val) +{ + writel_relaxed(val, lvds->regs + offset); +} + +static void lvds_serialiser_on(struct mchp_lvds *lvds) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(LVDS_POLL_TIMEOUT_MS); + + /* The LVDSC registers can only be written if WPEN is cleared */ + lvds_writel(lvds, LVDSC_WPMR, (LVDSC_WPMR_WPKEY_PSSWD & + LVDSC_WPMR_WPKEY_MASK)); + + /* Wait for the status of configuration registers to be changed */ + while (lvds_readl(lvds, LVDSC_SR) & LVDSC_SR_CS) { + if (time_after(jiffies, timeout)) { + dev_err(lvds->dev, "%s: timeout error\n", __func__); + return; + } + usleep_range(1000, 2000); + } + + /* Configure the LVDSC */ + lvds_writel(lvds, LVDSC_CFGR, (LVDSC_CFGR_MAPPING_JEIDA | + LVDSC_CFGR_DC_UNBALANCED | + LVDSC_CFGR_DEN_POL_HIGH | + LVDSC_CFGR_PIXSIZE_24BITS)); + + /* Enable the LVDS serializer */ + lvds_writel(lvds, LVDSC_CR, LVDSC_CR_SER_EN); +} + +static int mchp_lvds_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct mchp_lvds *lvds = bridge_to_lvds(bridge); + + bridge->encoder->encoder_type = DRM_MODE_ENCODER_LVDS; + + return drm_bridge_attach(bridge->encoder, lvds->panel_bridge, + bridge, flags); +} + +static void mchp_lvds_enable(struct drm_bridge *bridge) +{ + struct mchp_lvds *lvds = bridge_to_lvds(bridge); + int ret; + + ret = clk_prepare_enable(lvds->pclk); + if (ret < 0) { + DRM_DEV_ERROR(lvds->dev, "failed to enable lvds pclk %d\n", ret); + return; + } + + ret = pm_runtime_get_sync(lvds->dev); + if (ret < 0) { + DRM_DEV_ERROR(lvds->dev, "failed to get pm runtime: %d\n", ret); + clk_disable_unprepare(lvds->pclk); + return; + } + + lvds_serialiser_on(lvds); +} + +static void mchp_lvds_disable(struct drm_bridge *bridge) +{ + struct mchp_lvds *lvds = bridge_to_lvds(bridge); + + pm_runtime_put(lvds->dev); + clk_disable_unprepare(lvds->pclk); +} + +static const struct drm_bridge_funcs mchp_lvds_bridge_funcs = { + .attach = mchp_lvds_attach, + .enable = mchp_lvds_enable, + .disable = mchp_lvds_disable, +}; + +static int mchp_lvds_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mchp_lvds *lvds; + struct resource *res; + struct device_node *port; + + if (!dev->of_node) + return -ENODEV; + + lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL); + if (!lvds) + return -ENOMEM; + + lvds->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + lvds->regs = devm_ioremap_resource(lvds->dev, res); + if (IS_ERR(lvds->regs)) + return PTR_ERR(lvds->regs); + + lvds->pclk = devm_clk_get(lvds->dev, "pclk"); + if (IS_ERR(lvds->pclk)) { + DRM_DEV_ERROR(lvds->dev, "could not get pclk_lvds\n"); + return PTR_ERR(lvds->pclk); + } + + port = of_graph_get_remote_node(dev->of_node, 1, 0); + if (!port) { + DRM_DEV_ERROR(dev, + "can't find port point, please init lvds panel port!\n"); + return -EINVAL; + } + + lvds->panel = of_drm_find_panel(port); + of_node_put(port); + + if (IS_ERR(lvds->panel)) { + DRM_DEV_ERROR(dev, "failed to find panel node\n"); + return -EPROBE_DEFER; + } + + lvds->panel_bridge = devm_drm_panel_bridge_add(dev, lvds->panel); + + if (IS_ERR(lvds->panel_bridge)) + return PTR_ERR(lvds->panel_bridge); + + lvds->bridge.of_node = dev->of_node; + lvds->bridge.type = DRM_MODE_CONNECTOR_LVDS; + lvds->bridge.funcs = &mchp_lvds_bridge_funcs; + + dev_set_drvdata(dev, lvds); + pm_runtime_enable(dev); + + drm_bridge_add(&lvds->bridge); + + return 0; +} + +static int mchp_lvds_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct of_device_id mchp_lvds_dt_ids[] = { + { .compatible = "microchip,sam9x7-lvds" }, + { .compatible = "microchip,sama7d65-lvds" }, + { /* sentinel */ }, +}; + +struct platform_driver mchp_lvds_driver = { + .probe = mchp_lvds_probe, + .remove = mchp_lvds_remove, + .driver = { + .name = "microchip-lvds", + .of_match_table = mchp_lvds_dt_ids, + }, +}; +module_platform_driver(mchp_lvds_driver); + +MODULE_AUTHOR("Manikandan Muralidharan <manikandan.m@microchip.com>"); +MODULE_AUTHOR("Dharma Balasubiramani <dharma.b@microchip.com>"); +MODULE_DESCRIPTION("Low Voltage Differential Signaling Controller Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:microchip-lvds"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/gpu/drm/drm_atomic_helper.c linux-microchip/drivers/gpu/drm/drm_atomic_helper.c --- linux-6.6.51/drivers/gpu/drm/drm_atomic_helper.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/gpu/drm/drm_atomic_helper.c 2024-11-26 14:02:37.170487821 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1628 @ EXPORT_SYMBOL(drm_atomic_helper_wait_for_fences); /** - * drm_atomic_helper_wait_for_vblanks - wait for vblank on CRTCs + * drm_atomic_helper_framebuffer_changed - check if framebuffer has changed + * @dev: DRM device + * @old_state: atomic state object with old state structures + * @crtc: DRM crtc + * + * Checks whether the framebuffer used for this CRTC changes as a result of + * the atomic update. This is useful for drivers which cannot use + * drm_atomic_helper_wait_for_vblanks() and need to reimplement its + * functionality. + * + * Returns: + * true if the framebuffer changed. + */ +bool drm_atomic_helper_framebuffer_changed(struct drm_device *dev, + struct drm_atomic_state *old_state, + struct drm_crtc *crtc) +{ + struct drm_plane *plane; + struct drm_plane_state *old_plane_state; + int i; + + for_each_old_plane_in_state(old_state, plane, old_plane_state, i) { + if (plane->state->crtc != crtc && + old_plane_state->crtc != crtc) + continue; + + if (plane->state->fb != old_plane_state->fb) + return true; + } + + return false; +} +EXPORT_SYMBOL(drm_atomic_helper_framebuffer_changed); + +/** + * drm_atomic_helper_wait_for_vblanks - wait for vblank on crtcs * @dev: DRM device * @old_state: atomic state object with old state structures * @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1697 @ if (!new_crtc_state->active) continue; + if (!drm_atomic_helper_framebuffer_changed(dev, + old_state, crtc)) + continue; ret = drm_crtc_vblank_get(crtc); if (ret != 0) continue; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/gpu/drm/panel/panel-himax-hx8394.c linux-microchip/drivers/gpu/drm/panel/panel-himax-hx8394.c --- linux-6.6.51/drivers/gpu/drm/panel/panel-himax-hx8394.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/gpu/drm/panel/panel-himax-hx8394.c 2024-11-26 14:02:37.286489899 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:55 @ #define HX8394_CMD_SETGIP1 0xd5 #define HX8394_CMD_SETGIP2 0xd6 #define HX8394_CMD_SETGPO 0xd6 +#define HX8394_CMD_SETGIP3 0xd8 #define HX8394_CMD_SETSCALING 0xdd #define HX8394_CMD_SETIDLE 0xdf #define HX8394_CMD_SETGAMMA 0xe0 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:207 @ .init_sequence = hsd060bhw4_init_sequence, }; +static int mchp_ac40t08a_init_sequence(struct hx8394 *ctx) +{ + struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); + + /* DCS commands do not seem to be sent correclty with out this delay */ + msleep(20); + + /* 5.19.8 SETEXTC: Set extension command (B9h) */ + mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETEXTC, + 0xff, 0x83, 0x94); + + /* 5.19.9 SETMIPI: Set MIPI control (BAh) */ + mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETMIPI, + 0x63, 0x03, 0x68, 0x6b, 0xb2, 0xc0); + + /* 5.19.2 SETPOWER: Set power (B1h) */ + mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETPOWER, + 0x48, 0x12, 0x72, 0x09, 0x32, 0x54, + 0x71, 0x71, 0x57, 0x47); + + /* 5.19.3 SETDISP: Set display related register (B2h) */ + mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETDISP, + 0x00, 0x80, 0x64, 0x0c, 0x0d, 0x2f); + + /* 5.19.4 SETCYC: Set display waveform cycles (B4h) */ + mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETCYC, + 0x73, 0x74, 0x73, 0x74, 0x73, 0x74, + 0x01, 0x0C, 0x86, 0x75, 0x00, 0x3F, + 0x73, 0x74, 0x73, 0x74, 0x73, 0x74, + 0x01, 0x0C, 0x86); + + /* 5.19.5 SETVCOM: Set VCOM voltage (B6h) */ + mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETVCOM, + 0x6e, 0x6e); + + /* 5.19.19 SETGIP0: Set GIP Option0 (D3h) */ + mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETGIP0, + 0x00, 0x00, 0x07, 0x07, 0x40, 0x07, + 0x0c, 0x00, 0x08, 0x10, 0x08, 0x00, + 0x08, 0x54, 0x15, 0x0a, 0x05, 0x0a, + 0x02, 0x15, 0x06, 0x05, 0x06, 0x47, + 0x44, 0x0a, 0x0a, 0x4b, 0x10, 0x07, + 0x07, 0x0c, 0x40); + + /* 5.19.20 Set GIP Option1 (D5h) */ + mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETGIP1, + 0x1C, 0x1C, 0x1D, 0x1D, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x24, 0x25, + 0x18, 0x18, 0x26, 0x27, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x20, 0x21, 0x18, 0x18, + 0x18, 0x18); + + /* 5.19.21 Set GIP Option2 (D6h) */ + mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETGIP2, + 0x1C, 0x1C, 0x1D, 0x1D, 0x07, 0x06, + 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0x0B, 0x0A, 0x09, 0x08, 0x21, 0x20, + 0x18, 0x18, 0x27, 0x26, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x25, 0x24, 0x18, 0x18, + 0x18, 0x18); + + /* 5.19.25 SETGAMMA: Set gamma curve related setting (E0h) */ + mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETGAMMA, + 0x00, 0x0A, 0x15, 0x1B, 0x1E, 0x21, + 0x24, 0x22, 0x47, 0x56, 0x65, 0x66, + 0x6E, 0x82, 0x88, 0x8B, 0x9A, 0x9D, + 0x98, 0xA8, 0xB9, 0x5D, 0x5C, 0x61, + 0x66, 0x6A, 0x6F, 0x7F, 0x7F, 0x00, + 0x0A, 0x15, 0x1B, 0x1E, 0x21, 0x24, + 0x22, 0x47, 0x56, 0x65, 0x65, 0x6E, + 0x81, 0x87, 0x8B, 0x98, 0x9D, 0x99, + 0xA8, 0xBA, 0x5D, 0x5D, 0x62, 0x67, + 0x6B, 0x72, 0x7F, 0x7F); + + /* Unknown command, not listed in the HX8394-F datasheet (C0H) */ + mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_UNKNOWN1, + 0x1f, 0x73); + + /* Set CABC control (C9h)*/ + mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETCABC, + 0x76, 0x00, 0x30); + + /* 5.19.17 SETPANEL (CCh) */ + mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETPANEL, + 0x0b); + + /* Unknown command, not listed in the HX8394-F datasheet (D4h) */ + mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_UNKNOWN3, + 0x02); + + /* 5.19.11 Set register bank (BDh) */ + mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETREGBANK, + 0x02); + + /* 5.19.11 Set register bank (D8h) */ + mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETGIP3, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF); + + /* 5.19.11 Set register bank (BDh) */ + mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETREGBANK, + 0x00); + + /* 5.19.11 Set register bank (BDh) */ + mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETREGBANK, + 0x01); + + /* 5.19.2 SETPOWER: Set power (B1h) */ + mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETPOWER, + 0x00); + + /* 5.19.11 Set register bank (BDh) */ + mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_SETREGBANK, + 0x00); + + /* Unknown command, not listed in the HX8394-F datasheet (C6h) */ + mipi_dsi_dcs_write_seq(dsi, HX8394_CMD_UNKNOWN2, + 0xed); + + return 0; +} + +static const struct drm_display_mode mchp_ac40t08a_mode = { + .hdisplay = 720, + .hsync_start = 720 + 12, + .hsync_end = 720 + 12 + 24, + .htotal = 720 + 12 + 12 + 24, + .vdisplay = 1280, + .vsync_start = 1280 + 13, + .vsync_end = 1280 + 14, + .vtotal = 1280 + 14 + 13, + .clock = 60226, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, + .width_mm = 76, + .height_mm = 132, +}; + +static const struct hx8394_panel_desc mchp_ac40t08a_desc = { + .mode = &mchp_ac40t08a_mode, + .lanes = 4, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST, + .format = MIPI_DSI_FMT_RGB888, + .init_sequence = mchp_ac40t08a_init_sequence, +}; + static int hx8394_enable(struct drm_panel *panel) { struct hx8394 *ctx = panel_to_hx8394(panel); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:506 @ if (!ctx) return -ENOMEM; - ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(ctx->reset_gpio)) return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), "Failed to get reset gpio\n"); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:584 @ static const struct of_device_id hx8394_of_match[] = { { .compatible = "hannstar,hsd060bhw4", .data = &hsd060bhw4_desc }, + { .compatible = "microchip,ac40t08a-mipi-panel", .data = &mchp_ac40t08a_desc }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, hx8394_of_match); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/gpu/drm/panel/panel-simple.c linux-microchip/drivers/gpu/drm/panel/panel-simple.c --- linux-6.6.51/drivers/gpu/drm/panel/panel-simple.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/gpu/drm/panel/panel-simple.c 2024-11-26 14:02:37.290489972 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4090 @ .connector_type = DRM_MODE_CONNECTOR_LVDS, }; +static const struct drm_display_mode sitronix_st7262_lvds_mode = { + .clock = 25000, + .hdisplay = 800, + .hsync_start = 800 + 88, + .hsync_end = 800 + 88 + 5, + .htotal = 800 + 88 + 5 + 40, + .vdisplay = 480, + .vsync_start = 480 + 23, + .vsync_end = 480 + 23 + 5, + .vtotal = 480 + 23 + 5 + 1, +}; + +static const struct panel_desc sitronix_st7262_lvds = { + .modes = &sitronix_st7262_lvds_mode, + .num_modes = 1, + .bpc = 8, + .size = { + .width = 108, + .height = 65, + }, + .bus_flags = DRM_BUS_FLAG_DE_HIGH, + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, + .connector_type = DRM_MODE_CONNECTOR_LVDS, +}; + static const struct drm_display_mode arm_rtsm_mode[] = { { .clock = 65000, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4551 @ .compatible = "yes-optoelectronics,ytc700tlag-05-201c", .data = &yes_optoelectronics_ytc700tlag_05_201c, }, { + .compatible = "sitronix,st7262", + .data = &sitronix_st7262_lvds, + }, { /* Must be the last entry */ .compatible = "panel-dpi", .data = &panel_dpi, diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/hwmon/Kconfig linux-microchip/drivers/hwmon/Kconfig --- linux-6.6.51/drivers/hwmon/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/hwmon/Kconfig 2024-11-26 14:02:37.354491117 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2147 @ This driver can also be built as a module. If so, the module will be called tmp513. +config SENSORS_POLARFIRE_SOC_TVS + tristate "PolarFire SoC (MPFS) temperature and voltage sensor" + depends on POLARFIRE_SOC_MAILBOX + help + This driver adds support for the PolarFire SoC (MPFS) Temperature and + Voltage Sensor. + + To compile this driver as a module, choose M here. the + module will be called tvs-mpfs. + + If unsure, say N. + config SENSORS_VEXPRESS tristate "Versatile Express" depends on VEXPRESS_CONFIG diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/hwmon/Makefile linux-microchip/drivers/hwmon/Makefile --- linux-6.6.51/drivers/hwmon/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/hwmon/Makefile 2024-11-26 14:02:37.354491117 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:212 @ obj-$(CONFIG_SENSORS_TMP421) += tmp421.o obj-$(CONFIG_SENSORS_TMP464) += tmp464.o obj-$(CONFIG_SENSORS_TMP513) += tmp513.o +obj-$(CONFIG_SENSORS_POLARFIRE_SOC_TVS) += tvs-mpfs.o obj-$(CONFIG_SENSORS_VEXPRESS) += vexpress-hwmon.o obj-$(CONFIG_SENSORS_VIA_CPUTEMP)+= via-cputemp.o obj-$(CONFIG_SENSORS_VIA686A) += via686a.o diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/hwmon/tvs-mpfs.c linux-microchip/drivers/hwmon/tvs-mpfs.c --- linux-6.6.51/drivers/hwmon/tvs-mpfs.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/hwmon/tvs-mpfs.c 2024-11-26 14:02:37.370491404 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0+ +/* + * + * Author: Lars Randers <lranders@mail.dk> + * + */ + +#include <linux/err.h> +#include <linux/freezer.h> +#include <linux/kthread.h> +#include <linux/hwmon.h> +#include <linux/io.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define PFSOC_CONTROL_SCB_TVS_CONTROL 0x08 +#define PFSOC_CONTROL_SCB_TVS_OUTPUT0 0x24 +#define PFSOC_CONTROL_SCB_TVS_OUTPUT1 0x28 + +#define CTRL_POWEROFF BIT(5) +#define CTRL_ABORT BIT(4) +#define CTRL_TEMP BIT(3) +#define CTRL_2P5 BIT(2) +#define CTRL_1P8 BIT(1) +#define CTRL_1P05 BIT(0) + +#define OUTPUT0_U1P8_MASK GENMASK(30, 16) +#define OUTPUT0_U1P8_OFF 16 +#define OUTPUT0_U1P0_MASK GENMASK(14, 0) +#define OUTPUT0_U1P0_OFF 0 +#define OUTPUT1_TEMP_MASK GENMASK(31, 16) +#define OUTPUT1_TEMP_OFF 16 +#define OUTPUT1_U2P5_MASK GENMASK(14, 0) +#define OUTPUT1_U2P5_OFF 0 + +#define MPFS_TVS_MIN_POLL_INTERVAL_IN_MILLIS 2000 + +/* The following constant is 273.5 in (16.4) fixedpoint notation */ +#define MPFS_TVS_MIN_TEMP_IN_K 0x1112 + +typedef struct { + long min; + long actual; + long max; +} mpfs_tvs_sensor_t; + +typedef enum { + SN_V1P05 = 0, + SN_V1P8, + SN_V2P5, + SN_TEMP, + + SN_MAX +} mpfs_tvs_sn_t; + +static const char *mpfs_tvs_voltage_labels[] = { "U1P05", "U1P8", "U2P5" }; + +struct mpfs_tvs { + struct device *dev; + struct device *hwmon_dev; + struct task_struct *poll_task; + struct regmap *regmap; + bool kthread_running; + long update_interval; /* in milli-seconds */ + mpfs_tvs_sensor_t sensors[SN_MAX]; +}; + +static int mpfs_tvs_update_sensors(struct mpfs_tvs *data) { + u32 temp; + u32 work; + int ret; + + ret = regmap_read(data->regmap, PFSOC_CONTROL_SCB_TVS_OUTPUT1, &temp); + if (ret) + return ret; + + work = temp; + + temp = (temp & OUTPUT1_TEMP_MASK) >> OUTPUT1_TEMP_OFF; + temp = clamp_val(temp, MPFS_TVS_MIN_TEMP_IN_K, INT_MAX); + temp = temp - MPFS_TVS_MIN_TEMP_IN_K; /* Kelvin to Celsius */ + temp = (1000 * temp) >> 4; /* fixed point (10.4) to millicentigrade */ + data->sensors[SN_TEMP].actual = temp; + data->sensors[SN_TEMP].max = + max(data->sensors[SN_TEMP].actual, data->sensors[SN_TEMP].max); + data->sensors[SN_TEMP].min = + min(data->sensors[SN_TEMP].min, data->sensors[SN_TEMP].actual); + + work &= OUTPUT1_U2P5_MASK; + /* fixed point (11.3) adjust; value is already millivolts */ + work = (1 * work) >> 3; + data->sensors[SN_V2P5].actual = work; + data->sensors[SN_V2P5].max = + max(data->sensors[SN_V2P5].actual, data->sensors[SN_V2P5].max); + data->sensors[SN_V2P5].min = + min(data->sensors[SN_V2P5].min, data->sensors[SN_V2P5].actual); + + ret = regmap_read(data->regmap, PFSOC_CONTROL_SCB_TVS_OUTPUT0, &temp); + if (ret) + return ret; + + work = temp; + temp = (OUTPUT0_U1P8_MASK & temp) >> OUTPUT0_U1P8_OFF; + /* fixed point (11.3) adjust; value is already millivolts */ + temp = (1 * temp) >> 3; + data->sensors[SN_V1P8].actual = temp; + data->sensors[SN_V1P8].max = + max(data->sensors[SN_V1P8].actual, data->sensors[SN_V1P8].max); + data->sensors[SN_V2P5].min = + min(data->sensors[SN_V1P8].min, data->sensors[SN_V1P8].actual); + + work &= OUTPUT0_U1P0_MASK; + /* fixed point (11.3) adjust; value is already millivolts */ + work = (1 * work) >> 3; + data->sensors[SN_V1P05].actual = work; + data->sensors[SN_V1P05].max = + max(data->sensors[SN_V1P05].actual, data->sensors[SN_V1P05].max); + data->sensors[SN_V1P05].min = + min(data->sensors[SN_V1P05].min, data->sensors[SN_V1P05].actual); + + return 0; +} + + +static int mpfs_tvs_chip_read(struct mpfs_tvs *data, long *val) +{ + *val = data->update_interval; + return 0; +} + +static int mpfs_tvs_temp_read(struct mpfs_tvs *data, u32 attr, + int channel, long *val) +{ + switch(attr) { + case hwmon_temp_input: + *val = data->sensors[SN_TEMP].actual; + break; + + case hwmon_temp_max: + *val = data->sensors[SN_TEMP].max; + break; + + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int mpfs_tvs_voltage_read(struct mpfs_tvs *data, u32 attr, + int channel, long *val) +{ + dev_dbg(data->dev, "read voltage chan %d\n", channel); + switch(attr) { + case hwmon_in_input: + *val = data->sensors[channel].actual; + break; + + case hwmon_in_max: + *val = data->sensors[channel].max; + break; + + default: + return -EOPNOTSUPP; + } + return 0; +} + + +static ssize_t mpfs_tvs_interval_write(struct mpfs_tvs *data, long val) +{ + data->update_interval = + clamp_val(val, MPFS_TVS_MIN_POLL_INTERVAL_IN_MILLIS, INT_MAX); + return 0; +} + + +static umode_t mpfs_tvs_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if(type == hwmon_chip && attr == hwmon_chip_update_interval) + return 0644; + + if(type == hwmon_temp) { + switch(attr) { + case hwmon_temp_input: + case hwmon_temp_max: + case hwmon_temp_label: + return 0444; + + default: + return 0; + } + } else if(type == hwmon_in) { + switch(attr) { + case hwmon_in_input: + case hwmon_in_label: + return 0444; + + default: + return 0; + } + } + return 0; +} + +static int mpfs_tvs_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct mpfs_tvs *data = dev_get_drvdata(dev); + + switch(type) { + case hwmon_temp: + return mpfs_tvs_temp_read(data, attr, channel, val); + case hwmon_in: + return mpfs_tvs_voltage_read(data, attr, channel, val); + case hwmon_chip: + return mpfs_tvs_chip_read(data, val); + + default: + return -EOPNOTSUPP; + } +} + +static int mpfs_tvs_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct mpfs_tvs *data = dev_get_drvdata(dev); + + switch(type) { + case hwmon_chip: + return mpfs_tvs_interval_write(data, val); + default: + return -EOPNOTSUPP; + } +} + +static int mpfs_tvs_read_labels(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, + const char **str) +{ + switch(type) { + case hwmon_temp: + *str = "CPU Temp"; + return 0; + case hwmon_in: + *str = mpfs_tvs_voltage_labels[channel]; + return 0; + default: + return -EOPNOTSUPP; + } +} + + +static const struct hwmon_ops mpfs_tvs_ops = { + .is_visible = mpfs_tvs_is_visible, + .read_string = mpfs_tvs_read_labels, + .read = mpfs_tvs_read, + .write = mpfs_tvs_write, +}; + +static const struct hwmon_channel_info *mpfs_tvs_info[] = { + HWMON_CHANNEL_INFO(chip, + HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_MIN | + HWMON_T_MAX | HWMON_T_LABEL), + HWMON_CHANNEL_INFO(in, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL), + NULL +}; + +static const struct hwmon_chip_info mpfs_tvs_chip_info = { + .ops = &mpfs_tvs_ops, + .info = mpfs_tvs_info, +}; + + +static int mpfs_tvs_poll_task(void *ptr) +{ + struct mpfs_tvs *data = ptr; + int ret = 0; + + data->kthread_running = true; + + set_freezable(); + + while(!kthread_should_stop()) { + schedule_timeout_interruptible(data->update_interval); + try_to_freeze(); + ret = mpfs_tvs_update_sensors(data); + if(ret) + break; + } + + data->kthread_running = false; + return ret; +} + +static int mpfs_tvs_probe(struct platform_device *pdev) +{ + struct device *hwmon_dev; + struct mpfs_tvs *data; + struct task_struct *task; + int err; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if(!data) + return -ENOMEM; + + data->dev = &pdev->dev; + + data->regmap = device_node_to_regmap(pdev->dev.parent->of_node); + if (IS_ERR(data->regmap)) + dev_err_probe(data->dev, PTR_ERR(data->regmap), "Failed to find syscon regmap\n"); + + data->kthread_running = false; + + hwmon_dev = devm_hwmon_device_register_with_info(data->dev, "mpfs_tvs", + data, + &mpfs_tvs_chip_info, + NULL); + + if(IS_ERR(hwmon_dev)) { + err = PTR_ERR(hwmon_dev); + dev_err(data->dev, "Class registration failed (%d)\n", err); + return err; + } + + /* enable HW sensor */ + err = regmap_write(data->regmap, PFSOC_CONTROL_SCB_TVS_CONTROL, + CTRL_1P05 | CTRL_1P8 | CTRL_2P5 | CTRL_TEMP); + + data->hwmon_dev = hwmon_dev; + data->sensors[SN_TEMP].max = 0; + data->sensors[SN_V1P05].min = + data->sensors[SN_V1P8].min = + data->sensors[SN_V2P5].min = 20000; + data->sensors[SN_V1P05].max = + data->sensors[SN_V1P8].max = + data->sensors[SN_V2P5].max = 0; + data->update_interval = MPFS_TVS_MIN_POLL_INTERVAL_IN_MILLIS; + mpfs_tvs_update_sensors(data); + + task = kthread_run(mpfs_tvs_poll_task, data, "tvs-mpfs-kthread"); + if (IS_ERR(task)) { + err = PTR_ERR(task); + dev_err(data->dev, "Unable to run kthread err %d\n", err); + return err; + } + + data->poll_task = task; + + return 0; +} + +static struct platform_driver mpfs_tvs_driver = { + .probe = mpfs_tvs_probe, + .driver = { + .name = "mpfs-tvs", + }, +}; +module_platform_driver(mpfs_tvs_driver); + +MODULE_AUTHOR("Lars Randers <lranders@mail.dk>"); +MODULE_DESCRIPTION("PolarFire SoC temperature & voltage sensor driver"); +MODULE_LICENSE("GPL"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/i2c/busses/i2c-at91-core.c linux-microchip/drivers/i2c/busses/i2c-at91-core.c --- linux-6.6.51/drivers/i2c/busses/i2c-at91-core.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/i2c/busses/i2c-at91-core.c 2024-11-26 14:02:37.374491476 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:122 @ .clk_offset = 4, .has_hold_field = true, .has_dig_filtr = true, + .has_adv_dig_filtr = true, }; static struct at91_twi_pdata sama5d2_config = { diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/i2c/busses/i2c-at91-master.c linux-microchip/drivers/i2c/busses/i2c-at91-master.c --- linux-6.6.51/drivers/i2c/busses/i2c-at91-master.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/i2c/busses/i2c-at91-master.c 2024-11-26 14:02:37.374491476 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:40 @ /* FIFO should be enabled immediately after the software reset */ if (dev->fifo_size) at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_FIFOEN); - at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_MSEN); + at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_MSDIS); at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SVDIS); at91_twi_write(dev, AT91_TWI_CWGR, dev->twi_cwgr_reg); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:595 @ if (time_left == 0) { dev->transfer_status |= at91_twi_read(dev, AT91_TWI_SR); dev_err(dev->dev, "controller timed out\n"); - at91_init_twi_bus(dev); ret = -ETIMEDOUT; goto error; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:640 @ AT91_TWI_THRCLR | AT91_TWI_LOCKCLR); } - /* - * some faulty I2C slave devices might hold SDA down; - * we can send a bus clear command, hoping that the pins will be - * released - */ - i2c_recover_bus(&dev->adapter); - return ret; } +static void at91_twi_disable(struct at91_twi_dev *dev) +{ + /* return if previous operation is completed */ + if (!(at91_twi_read(dev, AT91_TWI_SR) & AT91_TWI_TXCOMP)) { + /* wait for previous command to complete before disabling the controller */ + dev_dbg(dev->dev, "wait for command to complete...\n"); + reinit_completion(&dev->cmd_complete); + dev->transfer_status = 0; + at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_TXCOMP); + wait_for_completion_timeout(&dev->cmd_complete, dev->adapter.timeout); + if (!(dev->transfer_status & AT91_TWI_TXCOMP)) { + dev_dbg(dev->dev, "IP still busy, resetting the controller...\n"); + at91_init_twi_bus(dev); + return; + } + } + + at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_MSDIS); +} + static int at91_twi_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num) { struct at91_twi_dev *dev = i2c_get_adapdata(adap); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:678 @ if (ret < 0) goto out; + at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_MSEN); + if (num == 2) { int internal_address = 0; int i; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:732 @ ret = at91_do_twi_transfer(dev); i2c_put_dma_safe_msg_buf(dma_buf, m_start, !ret); - ret = (ret < 0) ? ret : num; + if (ret < 0) { + /* disable controller before using GPIO recovery */ + if (!dev->pdata->has_clear_cmd) + at91_twi_disable(dev); + + /* + * some faulty I2C slave devices might hold SDA down; + * we can send a bus clear command, hoping that the pins will be + * released + */ + i2c_recover_bus(&dev->adapter); + + /* disable controller if not disabled before */ + if (dev->pdata->has_clear_cmd) + at91_twi_disable(dev); + } else { + at91_twi_disable(dev); + ret = num; + } + out: pm_runtime_mark_last_busy(dev->dev); pm_runtime_put_autosuspend(dev->dev); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/i2c/busses/i2c-microchip-corei2c.c linux-microchip/drivers/i2c/busses/i2c-microchip-corei2c.c --- linux-6.6.51/drivers/i2c/busses/i2c-microchip-corei2c.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/i2c/busses/i2c-microchip-corei2c.c 2024-11-26 14:02:37.378491548 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:109 @ void __iomem *base; struct device *dev; struct clk *i2c_clk; + struct i2c_msg *msg_queue; u8 *buf; struct completion msg_complete; struct i2c_adapter adapter; int msg_err; + int total_num; + int current_num; u32 bus_clk_rate; u32 isr_status; u16 msg_len; u8 addr; + bool repeated_send_needed; }; static void mchp_corei2c_core_disable(struct mchp_corei2c_dev *idev) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:229 @ return 0; } +static void mchp_corei2c_next_msg(struct mchp_corei2c_dev *idev) +{ + + if (idev->current_num < idev->total_num) { + struct i2c_msg *this_msg = ++(idev->msg_queue); + u8 ctrl; + + if (idev->current_num < (idev->total_num - 1)) { + struct i2c_msg *next_msg = idev->msg_queue; + + idev->repeated_send_needed = next_msg->flags & I2C_M_RD; + } else { + idev->repeated_send_needed = false; + } + + idev->addr = i2c_8bit_addr_from_msg(this_msg); + idev->msg_len = this_msg->len; + idev->buf = this_msg->buf; + + ctrl = readb(idev->base + CORE_I2C_CTRL); + ctrl |= CTRL_STA; + writeb(ctrl, idev->base + CORE_I2C_CTRL); + + idev->current_num++; + } else { + complete(&idev->msg_complete); + } +} + static irqreturn_t mchp_corei2c_handle_isr(struct mchp_corei2c_dev *idev) { u32 status = idev->isr_status; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:283 @ break; case STATUS_M_SLAW_ACK: case STATUS_M_TX_DATA_ACK: - if (idev->msg_len > 0) + if (idev->msg_len > 0) { mchp_corei2c_fill_tx(idev); - else - last_byte = true; + } else { + if (idev->repeated_send_needed) { + finished = true; + } else { + last_byte = true; + } + } break; case STATUS_M_TX_DATA_NACK: case STATUS_M_SLAR_NACK: @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:328 @ mchp_corei2c_stop(idev); if (last_byte || finished) - complete(&idev->msg_complete); + mchp_corei2c_next_msg(idev); return IRQ_HANDLED; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:352 @ return ret; } -static int mchp_corei2c_xfer_msg(struct mchp_corei2c_dev *idev, - struct i2c_msg *msg) +static int mchp_corei2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) { - u8 ctrl; + struct mchp_corei2c_dev *idev = i2c_get_adapdata(adap); + struct i2c_msg *this_msg = msgs; unsigned long time_left; + u8 ctrl; + + mchp_corei2c_core_enable(idev); - idev->addr = i2c_8bit_addr_from_msg(msg); - idev->msg_len = msg->len; - idev->buf = msg->buf; + /* + * The isr controls the flow of a transfer, this info needs to be saved + * to a location that it can access the queue information from. + */ + idev->repeated_send_needed = false; + idev->msg_queue = msgs; + idev->total_num = num; + idev->current_num = 0; + + /* + * But the first entry to the isr is triggered by the start in this + * function, so the first message needs to be "dequeued". + */ + idev->addr = i2c_8bit_addr_from_msg(this_msg); + idev->msg_len = this_msg->len; + idev->buf = this_msg->buf; idev->msg_err = 0; - reinit_completion(&idev->msg_complete); + if (idev->total_num > 1) { + struct i2c_msg *next_msg = msgs + 1; - mchp_corei2c_core_enable(idev); + idev->repeated_send_needed = next_msg->flags & I2C_M_RD; + } + + idev->current_num++; + reinit_completion(&idev->msg_complete); + + /* + * Send the first start to pass control to the interrupt handler + */ ctrl = readb(idev->base + CORE_I2C_CTRL); ctrl |= CTRL_STA; writeb(ctrl, idev->base + CORE_I2C_CTRL); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:402 @ if (!time_left) return -ETIMEDOUT; - return idev->msg_err; -} - -static int mchp_corei2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, - int num) -{ - struct mchp_corei2c_dev *idev = i2c_get_adapdata(adap); - int i, ret; - - for (i = 0; i < num; i++) { - ret = mchp_corei2c_xfer_msg(idev, msgs++); - if (ret) - return ret; - } + /* + * TODO: fix error handing with new xfer based lifecycle as this will + * only report the last error received after all are sent + */ + if (idev->msg_err) + return idev->msg_err; return num; } diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/iio/adc/at91-sama5d2_adc.c linux-microchip/drivers/iio/adc/at91-sama5d2_adc.c --- linux-6.6.51/drivers/iio/adc/at91-sama5d2_adc.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/iio/adc/at91-sama5d2_adc.c 2024-11-26 14:02:37.394491835 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:16 @ #include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/dmaengine.h> +#include <linux/genalloc.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/module.h> @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:36 @ #include <linux/pinctrl/consumer.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> +#include <linux/sram.h> #include <dt-bindings/iio/adc/at91-sama5d2_adc.h> @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:335 @ #define AT91_HWFIFO_MAX_SIZE 128 #define AT91_SAMA5D2_CHAN_SINGLE(index, num, addr) \ + AT91_SAMA_CHAN_SINGLE(index, num, addr, 14) + +#define AT91_SAMA7G5_CHAN_SINGLE(index, num, addr) \ + AT91_SAMA_CHAN_SINGLE(index, num, addr, 16) + +#define AT91_SAMA_CHAN_SINGLE(index, num, addr, rbits) \ { \ .type = IIO_VOLTAGE, \ .channel = num, \ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:348 @ .scan_index = index, \ .scan_type = { \ .sign = 'u', \ - .realbits = 14, \ + .realbits = rbits, \ .storagebits = 16, \ }, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:362 @ } #define AT91_SAMA5D2_CHAN_DIFF(index, num, num2, addr) \ + AT91_SAMA_CHAN_DIFF(index, num, num2, addr, 14) + +#define AT91_SAMA7G5_CHAN_DIFF(index, num, num2, addr) \ + AT91_SAMA_CHAN_DIFF(index, num, num2, addr, 16) + +#define AT91_SAMA_CHAN_DIFF(index, num, num2, addr, rbits) \ { \ .type = IIO_VOLTAGE, \ .differential = 1, \ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:377 @ .scan_index = index, \ .scan_type = { \ .sign = 's', \ - .realbits = 14, \ + .realbits = rbits, \ .storagebits = 16, \ }, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:449 @ #define at91_adc_writel(st, reg, val) \ writel_relaxed(val, (st)->base + (st)->soc_info.platform->layout->reg) +struct at91_adc_state; +static int at91_adc_temp_sensor_init(struct at91_adc_state *st, + struct device *dev); +static int at91_sama7d65_adc_temp_sensor_init(struct at91_adc_state *st, + struct device *dev); + /** * struct at91_adc_platform - at91-sama5d2 platform information struct * @layout: pointer to the reg layout struct @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:474 @ * @chan_realbits: realbits for registered channels * @temp_chan: temperature channel index * @temp_sensor: temperature sensor supported + * @temp_init: callback function to initialize the temperature sensor + * with its calibration data */ struct at91_adc_platform { const struct at91_adc_reg_layout *layout; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:493 @ unsigned int chan_realbits; unsigned int temp_chan; bool temp_sensor; + int (*temp_init)(struct at91_adc_state *st, struct device *dev); }; /** @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:523 @ AT91_ADC_TS_CLB_IDX_MAX = 19, }; +/** + * enum at91_sama7d65_adc_ts_clb_idx - calibration indexes in NVMEM buffer + * @AT91_SAMA7D65_ADC_TS_CLB_IDX_P1: index for FT1_TEMP equivalent to P1 * (10 ^ 6) + * @AT91_SAMA7D65_ADC_TS_CLB_IDX_P4: index for FT1_VPAT equivalent to P4 + * @AT91_SAMA7D65_ADC_TS_CLB_IDX_P6: index for FT2_VBG equivalent to P6 + * @AT91_SAMA7D65_ADC_TS_CLB_IDX_MAX: max index for temperature calibration packet in OTP + */ +enum at91_sama7d65_adc_ts_clb_idx { + AT91_SAMA7D65_ADC_TS_CLB_IDX_P1 = 2, + AT91_SAMA7D65_ADC_TS_CLB_IDX_P4 = 1, + AT91_SAMA7D65_ADC_TS_CLB_IDX_P6 = 4, + AT91_SAMA7D65_ADC_TS_CLB_IDX_MAX = 10, +}; + /* Temperature sensor calibration - Vtemp voltage sensitivity to temperature. */ #define AT91_ADC_TS_VTEMP_DT (2080U) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:706 @ }; static const struct iio_chan_spec at91_sama7g5_adc_channels[] = { - AT91_SAMA5D2_CHAN_SINGLE(0, 0, 0x60), - AT91_SAMA5D2_CHAN_SINGLE(1, 1, 0x64), - AT91_SAMA5D2_CHAN_SINGLE(2, 2, 0x68), - AT91_SAMA5D2_CHAN_SINGLE(3, 3, 0x6c), - AT91_SAMA5D2_CHAN_SINGLE(4, 4, 0x70), - AT91_SAMA5D2_CHAN_SINGLE(5, 5, 0x74), - AT91_SAMA5D2_CHAN_SINGLE(6, 6, 0x78), - AT91_SAMA5D2_CHAN_SINGLE(7, 7, 0x7c), - AT91_SAMA5D2_CHAN_SINGLE(8, 8, 0x80), - AT91_SAMA5D2_CHAN_SINGLE(9, 9, 0x84), - AT91_SAMA5D2_CHAN_SINGLE(10, 10, 0x88), - AT91_SAMA5D2_CHAN_SINGLE(11, 11, 0x8c), - AT91_SAMA5D2_CHAN_SINGLE(12, 12, 0x90), - AT91_SAMA5D2_CHAN_SINGLE(13, 13, 0x94), - AT91_SAMA5D2_CHAN_SINGLE(14, 14, 0x98), - AT91_SAMA5D2_CHAN_SINGLE(15, 15, 0x9c), - AT91_SAMA5D2_CHAN_DIFF(16, 0, 1, 0x60), - AT91_SAMA5D2_CHAN_DIFF(17, 2, 3, 0x68), - AT91_SAMA5D2_CHAN_DIFF(18, 4, 5, 0x70), - AT91_SAMA5D2_CHAN_DIFF(19, 6, 7, 0x78), - AT91_SAMA5D2_CHAN_DIFF(20, 8, 9, 0x80), - AT91_SAMA5D2_CHAN_DIFF(21, 10, 11, 0x88), - AT91_SAMA5D2_CHAN_DIFF(22, 12, 13, 0x90), - AT91_SAMA5D2_CHAN_DIFF(23, 14, 15, 0x98), + AT91_SAMA7G5_CHAN_SINGLE(0, 0, 0x60), + AT91_SAMA7G5_CHAN_SINGLE(1, 1, 0x64), + AT91_SAMA7G5_CHAN_SINGLE(2, 2, 0x68), + AT91_SAMA7G5_CHAN_SINGLE(3, 3, 0x6c), + AT91_SAMA7G5_CHAN_SINGLE(4, 4, 0x70), + AT91_SAMA7G5_CHAN_SINGLE(5, 5, 0x74), + AT91_SAMA7G5_CHAN_SINGLE(6, 6, 0x78), + AT91_SAMA7G5_CHAN_SINGLE(7, 7, 0x7c), + AT91_SAMA7G5_CHAN_SINGLE(8, 8, 0x80), + AT91_SAMA7G5_CHAN_SINGLE(9, 9, 0x84), + AT91_SAMA7G5_CHAN_SINGLE(10, 10, 0x88), + AT91_SAMA7G5_CHAN_SINGLE(11, 11, 0x8c), + AT91_SAMA7G5_CHAN_SINGLE(12, 12, 0x90), + AT91_SAMA7G5_CHAN_SINGLE(13, 13, 0x94), + AT91_SAMA7G5_CHAN_SINGLE(14, 14, 0x98), + AT91_SAMA7G5_CHAN_SINGLE(15, 15, 0x9c), + AT91_SAMA7G5_CHAN_DIFF(16, 0, 1, 0x60), + AT91_SAMA7G5_CHAN_DIFF(17, 2, 3, 0x68), + AT91_SAMA7G5_CHAN_DIFF(18, 4, 5, 0x70), + AT91_SAMA7G5_CHAN_DIFF(19, 6, 7, 0x78), + AT91_SAMA7G5_CHAN_DIFF(20, 8, 9, 0x80), + AT91_SAMA7G5_CHAN_DIFF(21, 10, 11, 0x88), + AT91_SAMA7G5_CHAN_DIFF(22, 12, 13, 0x90), + AT91_SAMA7G5_CHAN_DIFF(23, 14, 15, 0x98), IIO_CHAN_SOFT_TIMESTAMP(24), AT91_SAMA5D2_CHAN_TEMP(AT91_SAMA7G5_ADC_TEMP_CHANNEL, "temp", 0xdc), }; +static const struct iio_chan_spec at91_sama7d65_adc_channels[] = { + AT91_SAMA7G5_CHAN_SINGLE(0, 0, 0x60), + AT91_SAMA7G5_CHAN_SINGLE(1, 1, 0x64), + AT91_SAMA7G5_CHAN_SINGLE(2, 2, 0x68), + AT91_SAMA7G5_CHAN_SINGLE(3, 3, 0x6c), + AT91_SAMA7G5_CHAN_SINGLE(4, 4, 0x70), + AT91_SAMA7G5_CHAN_SINGLE(5, 5, 0x74), + AT91_SAMA7G5_CHAN_SINGLE(6, 6, 0x78), + AT91_SAMA7G5_CHAN_SINGLE(7, 7, 0x7c), + AT91_SAMA7G5_CHAN_SINGLE(8, 8, 0x80), + AT91_SAMA7G5_CHAN_SINGLE(9, 9, 0x84), + AT91_SAMA7G5_CHAN_SINGLE(10, 10, 0x88), + AT91_SAMA7G5_CHAN_SINGLE(11, 11, 0x8c), + AT91_SAMA7G5_CHAN_SINGLE(12, 12, 0x90), + AT91_SAMA7G5_CHAN_SINGLE(13, 13, 0x94), + AT91_SAMA7G5_CHAN_SINGLE(14, 14, 0x98), + AT91_SAMA7G5_CHAN_SINGLE(15, 15, 0x9c), + /* + * This is commented due to a driver limitation. The driver currently + * does not support repetitive scan index. This is the case only for + * sama7d65 as it has more than 15 single channels + */ +#if 0 + AT91_SAMA7G5_CHAN_SINGLE(16, 16, 0xa0), + AT91_SAMA7G5_CHAN_SINGLE(17, 17, 0xa4), + AT91_SAMA7G5_CHAN_SINGLE(18, 18, 0xa8), + AT91_SAMA7G5_CHAN_SINGLE(19, 19, 0xac), + AT91_SAMA7G5_CHAN_SINGLE(20, 20, 0xb0), + AT91_SAMA7G5_CHAN_SINGLE(21, 21, 0xb4), + AT91_SAMA7G5_CHAN_SINGLE(22, 22, 0xb8), + AT91_SAMA7G5_CHAN_SINGLE(23, 23, 0xbc), + AT91_SAMA7G5_CHAN_SINGLE(24, 24, 0xc0), + AT91_SAMA7G5_CHAN_SINGLE(25, 25, 0xc4), + AT91_SAMA7G5_CHAN_SINGLE(26, 26, 0xc8), + AT91_SAMA7G5_CHAN_SINGLE(27, 27, 0xcc), + AT91_SAMA7G5_CHAN_SINGLE(28, 28, 0xd0), + AT91_SAMA7G5_CHAN_SINGLE(29, 29, 0xd4), +#endif + AT91_SAMA7G5_CHAN_DIFF(16, 0, 1, 0x60), + AT91_SAMA7G5_CHAN_DIFF(17, 2, 3, 0x68), + AT91_SAMA7G5_CHAN_DIFF(18, 4, 5, 0x70), + AT91_SAMA7G5_CHAN_DIFF(19, 6, 7, 0x78), + AT91_SAMA7G5_CHAN_DIFF(20, 8, 9, 0x80), + AT91_SAMA7G5_CHAN_DIFF(21, 10, 11, 0x88), + AT91_SAMA7G5_CHAN_DIFF(22, 12, 13, 0x90), + AT91_SAMA7G5_CHAN_DIFF(23, 14, 15, 0x98), + AT91_SAMA7G5_CHAN_DIFF(24, 16, 17, 0xa0), + AT91_SAMA7G5_CHAN_DIFF(25, 18, 19, 0xa8), + AT91_SAMA7G5_CHAN_DIFF(26, 20, 21, 0xb0), + AT91_SAMA7G5_CHAN_DIFF(27, 22, 23, 0xb8), + AT91_SAMA7G5_CHAN_DIFF(28, 24, 25, 0xc0), + AT91_SAMA7G5_CHAN_DIFF(29, 26, 27, 0xc8), + AT91_SAMA7G5_CHAN_DIFF(30, 28, 29, 0xd0), + AT91_SAMA5D2_CHAN_TEMP(AT91_SAMA7G5_ADC_TEMP_CHANNEL, "temp", 0xdc), +}; + static const struct at91_adc_platform sama5d2_platform = { .layout = &sama5d2_layout, .adc_channels = &at91_sama5d2_adc_channels, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:813 @ .oversampling_avail = { 1, 4, 16, }, .oversampling_avail_no = 3, .chan_realbits = 14, + .temp_init = at91_adc_temp_sensor_init, }; static const struct at91_adc_platform sama7g5_platform = { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:838 @ .chan_realbits = 16, .temp_sensor = true, .temp_chan = AT91_SAMA7G5_ADC_TEMP_CHANNEL, + .temp_init = at91_adc_temp_sensor_init, +}; + +static const struct at91_adc_platform sama7d65_platform = { + .layout = &sama7g5_layout, + .adc_channels = &at91_sama7d65_adc_channels, +#define AT91_SAMA7D65_SINGLE_CHAN_CNT 30 +#define AT91_SAMA7D65_DIFF_CHAN_CNT 15 +#define AT91_SAMA7D65_TEMP_CHAN_CNT 1 + .nr_channels = AT91_SAMA7D65_SINGLE_CHAN_CNT + + AT91_SAMA7D65_DIFF_CHAN_CNT + + AT91_SAMA7D65_TEMP_CHAN_CNT, +#define AT91_SAMA7D65_MAX_CHAN_IDX (AT91_SAMA7D65_SINGLE_CHAN_CNT + \ + AT91_SAMA7D65_DIFF_CHAN_CNT + \ + AT91_SAMA7D65_TEMP_CHAN_CNT) + .max_channels = ARRAY_SIZE(at91_sama7d65_adc_channels), + .max_index = AT91_SAMA7D65_MAX_CHAN_IDX, +#define AT91_SAMA7G5_HW_TRIG_CNT 3 + .hw_trig_cnt = AT91_SAMA7G5_HW_TRIG_CNT, + .osr_mask = GENMASK(18, 16), + .oversampling_avail = { 1, 4, 16, 64, 256, }, + .oversampling_avail_no = 5, + .chan_realbits = 16, + .temp_sensor = true, + .temp_chan = AT91_SAMA7G5_ADC_TEMP_CHANNEL, + /* + * The calibration data has a TAG to recognize the packet + * The tag has a constant value "ACST" with the ASCII + * equivalent 0x41435354. This is used to validate the + * calibration data. + */ +#define AT91_SAMA7D65_TEMP_CALIB_TAG 0x41435354 + .temp_init = at91_sama7d65_adc_temp_sensor_init, }; static int at91_adc_chan_xlate(struct iio_dev *indio_dev, int chan) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2433 @ return ret; } +static int at91_sama7d65_adc_temp_sensor_init(struct at91_adc_state *st, + struct device *dev) +{ + struct at91_adc_temp_sensor_clb *clb = &st->soc_info.temp_sensor_clb; + struct device_node *np = dev->of_node; + struct gen_pool *sram_ts_clb_pool_code = NULL; + unsigned long sram_ts_clb_virt_addr = 0; + struct nvmem_cell *temp_calib; + u32 *buf = NULL; + size_t len; + int ret = 0; + + if (!st->soc_info.platform->temp_sensor) + return 0; + + if (device_property_read_bool(dev, "microchip,calib-on-sram")) { + /* Get the calibration data from the OTP copy on SRAM0. */ + sram_ts_clb_pool_code = of_gen_pool_get(np, "sram", 0); + if (!sram_ts_clb_pool_code) { + dev_err(dev, "Failed to get temp calib data from SRAM!\n"); + return -ENODEV; + } + + sram_ts_clb_virt_addr = gen_pool_alloc(sram_ts_clb_pool_code, + AT91_SAMA7D65_ADC_TS_CLB_IDX_MAX + * sizeof(u32)); + if (!sram_ts_clb_virt_addr) { + dev_err(dev, "Failed to read calibration data from SRAM\n"); + return -ENOMEM; + } + + buf = kzalloc((AT91_SAMA7D65_ADC_TS_CLB_IDX_MAX * sizeof(u32)), + GFP_KERNEL); + if (IS_ERR(buf)) { + dev_err(dev, "Cannot allocate memory for Calibration data!\n"); + return PTR_ERR(buf); + } + + memcpy(buf, (void *)sram_ts_clb_virt_addr, + (AT91_SAMA7D65_ADC_TS_CLB_IDX_MAX * sizeof(u32))); + + if (buf[0] != AT91_SAMA7D65_TEMP_CALIB_TAG) { + dev_err(dev, "Invalid calibration data!\n"); + ret = -EINVAL; + goto free_buf; + } + } else { + /* Get the calibration data from NVMEM. */ + temp_calib = devm_nvmem_cell_get(dev, "temperature_calib"); + if (IS_ERR(temp_calib)) { + ret = PTR_ERR(temp_calib); + if (ret != -ENOENT) + dev_err(dev, "Failed to get temperature_calib cell!\n"); + return ret; + } + + buf = nvmem_cell_read(temp_calib, &len); + if (IS_ERR(buf)) { + dev_err(dev, "Failed to read calibration data!\n"); + return PTR_ERR(buf); + } + + if (len < AT91_SAMA7D65_ADC_TS_CLB_IDX_MAX * sizeof(u32)) { + dev_err(dev, "Invalid calibration data!\n"); + ret = -EINVAL; + goto free_buf; + } + } + + /* Store calibration data for later use. */ + clb->p1 = buf[AT91_SAMA7D65_ADC_TS_CLB_IDX_P1]; + clb->p4 = buf[AT91_SAMA7D65_ADC_TS_CLB_IDX_P4]; + clb->p6 = buf[AT91_SAMA7D65_ADC_TS_CLB_IDX_P6]; + + /* + * We prepare here the conversion to milli from micro to avoid + * doing it on hotpath. + */ + clb->p1 = clb->p1 / 1000; + +free_buf: + if (sram_ts_clb_virt_addr) { + gen_pool_free(sram_ts_clb_pool_code, + sram_ts_clb_virt_addr, + AT91_SAMA7D65_ADC_TS_CLB_IDX_MAX * sizeof(u32)); + } + kfree(buf); + return ret; +} + static int at91_adc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2541 @ st->soc_info.platform = device_get_match_data(dev); - ret = at91_adc_temp_sensor_init(st, &pdev->dev); + ret = st->soc_info.platform->temp_init(st, &pdev->dev); /* Don't register temperature channel if initialization failed. */ if (ret) num_channels = st->soc_info.platform->max_channels - 1; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2840 @ .compatible = "microchip,sama7g5-adc", .data = (const void *)&sama7g5_platform, }, { + .compatible = "microchip,sama7d65-adc", + .data = (const void *)&sama7d65_platform, + }, { /* sentinel */ } }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/iio/adc/Kconfig linux-microchip/drivers/iio/adc/Kconfig --- linux-6.6.51/drivers/iio/adc/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/iio/adc/Kconfig 2024-11-26 14:02:37.390491762 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:782 @ This driver can also be built as a module. If so, the module will be called mcp3422. +config MCP3564 + tristate "Microchip Technology MCP3461/2/4/R, MCP3561/2/4/R driver" + depends on SPI + help + Say yes here to build support for Microchip Technology's MCP3461, + MCP3462, MCP3464, MCP3461R, MCP3462R, MCP3464R, MCP3561, MCP3562, + MCP3564, MCP3561R, MCP3562R and MCP3564R analog to digital + converters. + + This driver can also be built as a module. If so, the module will be + called mcp3564. + config MCP3911 tristate "Microchip Technology MCP3911 driver" depends on SPI @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:893 @ This driver can also be built as a module. If so, the module will be called npcm_adc. +config PAC1934 + tristate "Microchip Technology PAC1934 driver" + depends on I2C + help + Say yes here to build support for Microchip Technology's PAC1931, + PAC1932, PAC1933, PAC1934 Single/Multi-Channel Power Monitor with + Accumulator. + + This driver can also be built as a module. If so, the module + will be called pac1934. + config PALMAS_GPADC tristate "TI Palmas General Purpose ADC" depends on MFD_PALMAS diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/iio/adc/Makefile linux-microchip/drivers/iio/adc/Makefile --- linux-6.6.51/drivers/iio/adc/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/iio/adc/Makefile 2024-11-26 14:02:37.390491762 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:74 @ obj-$(CONFIG_MAX9611) += max9611.o obj-$(CONFIG_MCP320X) += mcp320x.o obj-$(CONFIG_MCP3422) += mcp3422.o +obj-$(CONFIG_MCP3564) += mcp3564.o obj-$(CONFIG_MCP3911) += mcp3911.o obj-$(CONFIG_MEDIATEK_MT6360_ADC) += mt6360-adc.o obj-$(CONFIG_MEDIATEK_MT6370_ADC) += mt6370-adc.o @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:85 @ obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o obj-$(CONFIG_NAU7802) += nau7802.o obj-$(CONFIG_NPCM_ADC) += npcm_adc.o +obj-$(CONFIG_PAC1934) += pac1934.o obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o obj-$(CONFIG_QCOM_SPMI_ADC5) += qcom-spmi-adc5.o obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/iio/adc/mcp3564.c linux-microchip/drivers/iio/adc/mcp3564.c --- linux-6.6.51/drivers/iio/adc/mcp3564.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/iio/adc/mcp3564.c 2024-11-26 14:02:37.394491835 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0+ +/* + * IIO driver for MCP356X/MCP356XR and MCP346X/MCP346XR series ADC chip family + * + * Copyright (C) 2022-2024 Microchip Technology Inc. and its subsidiaries + * + * Author: Marius Cristea <marius.cristea@microchip.com> + * + * Datasheet for MCP3561, MCP3562, MCP3564 can be found here: + * https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/MCP3561-2-4-Family-Data-Sheet-DS20006181C.pdf + * Datasheet for MCP3561R, MCP3562R, MCP3564R can be found here: + * https://ww1.microchip.com/downloads/aemDocuments/documents/APID/ProductDocuments/DataSheets/MCP3561_2_4R-Data-Sheet-DS200006391C.pdf + * Datasheet for MCP3461, MCP3462, MCP3464 can be found here: + * https://ww1.microchip.com/downloads/aemDocuments/documents/APID/ProductDocuments/DataSheets/MCP3461-2-4-Two-Four-Eight-Channel-153.6-ksps-Low-Noise-16-Bit-Delta-Sigma-ADC-Data-Sheet-20006180D.pdf + * Datasheet for MCP3461R, MCP3462R, MCP3464R can be found here: + * https://ww1.microchip.com/downloads/aemDocuments/documents/APID/ProductDocuments/DataSheets/MCP3461-2-4R-Family-Data-Sheet-DS20006404C.pdf + */ + +#include <linux/bitfield.h> +#include <linux/iopoll.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> +#include <linux/units.h> +#include <linux/util_macros.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#define MCP3564_ADCDATA_REG 0x00 + +#define MCP3564_CONFIG0_REG 0x01 +#define MCP3564_CONFIG0_ADC_MODE_MASK GENMASK(1, 0) +/* Current Source/Sink Selection Bits for Sensor Bias */ +#define MCP3564_CONFIG0_CS_SEL_MASK GENMASK(3, 2) +/* Internal clock is selected and AMCLK is present on the analog master clock output pin */ +#define MCP3564_CONFIG0_USE_INT_CLK_OUTPUT_EN 0x03 +/* Internal clock is selected and no clock output is present on the CLK pin */ +#define MCP3564_CONFIG0_USE_INT_CLK 0x02 +/* External digital clock */ +#define MCP3564_CONFIG0_USE_EXT_CLK 0x01 +/* External digital clock (default) */ +#define MCP3564_CONFIG0_USE_EXT_CLK_DEFAULT 0x00 +#define MCP3564_CONFIG0_CLK_SEL_MASK GENMASK(5, 4) +#define MCP3456_CONFIG0_BIT6_DEFAULT BIT(6) +#define MCP3456_CONFIG0_VREF_MASK BIT(7) + +#define MCP3564_CONFIG1_REG 0x02 +#define MCP3564_CONFIG1_OVERSPL_RATIO_MASK GENMASK(5, 2) + +#define MCP3564_CONFIG2_REG 0x03 +#define MCP3564_CONFIG2_AZ_REF_MASK BIT(1) +#define MCP3564_CONFIG2_AZ_MUX_MASK BIT(2) + +#define MCP3564_CONFIG2_HARDWARE_GAIN_MASK GENMASK(5, 3) +#define MCP3564_DEFAULT_HARDWARE_GAIN 0x01 +#define MCP3564_CONFIG2_BOOST_CURRENT_MASK GENMASK(7, 6) + +#define MCP3564_CONFIG3_REG 0x04 +#define MCP3464_CONFIG3_EN_GAINCAL_MASK BIT(0) +#define MCP3464_CONFIG3_EN_OFFCAL_MASK BIT(1) +#define MCP3464_CONFIG3_EN_CRCCOM_MASK BIT(2) +#define MCP3464_CONFIG3_CRC_FORMAT_MASK BIT(3) +/* + * ADC Output Data Format 32-bit (25-bit right justified data + Channel ID): + * CHID[3:0] + SGN extension (4 bits) + 24-bit ADC data. + * It allows overrange with the SGN extension. + */ +#define MCP3464_CONFIG3_DATA_FMT_32B_WITH_CH_ID 3 +/* + * ADC Output Data Format 32-bit (25-bit right justified data): + * SGN extension (8-bit) + 24-bit ADC data. + * It allows overrange with the SGN extension. + */ +#define MCP3464_CONFIG3_DATA_FMT_32B_SGN_EXT 2 +/* + * ADC Output Data Format 32-bit (24-bit left justified data): + * 24-bit ADC data + 0x00 (8-bit). + * It does not allow overrange (ADC code locked to 0xFFFFFF or 0x800000). + */ +#define MCP3464_CONFIG3_DATA_FMT_32B_LEFT_JUSTIFIED 1 +/* + * ADC Output Data Format 24-bit (default ADC coding): + * 24-bit ADC data. + * It does not allow overrange (ADC code locked to 0xFFFFFF or 0x800000). + */ +#define MCP3464_CONFIG3_DATA_FMT_24B 0 +#define MCP3464_CONFIG3_DATA_FORMAT_MASK GENMASK(5, 4) + +/* Continuous Conversion mode or continuous conversion cycle in SCAN mode. */ +#define MCP3464_CONFIG3_CONV_MODE_CONTINUOUS 3 +/* + * One-shot conversion or one-shot cycle in SCAN mode. It sets ADC_MODE[1:0] to ‘10’ + * (standby) at the end of the conversion or at the end of the conversion cycle in SCAN mode. + */ +#define MCP3464_CONFIG3_CONV_MODE_ONE_SHOT_STANDBY 2 +/* + * One-shot conversion or one-shot cycle in SCAN mode. It sets ADC_MODE[1:0] to ‘0x’ (ADC + * Shutdown) at the end of the conversion or at the end of the conversion cycle in SCAN + * mode (default). + */ +#define MCP3464_CONFIG3_CONV_MODE_ONE_SHOT_SHUTDOWN 0 +#define MCP3464_CONFIG3_CONV_MODE_MASK GENMASK(7, 6) + +#define MCP3564_IRQ_REG 0x05 +#define MCP3464_EN_STP_MASK BIT(0) +#define MCP3464_EN_FASTCMD_MASK BIT(1) +#define MCP3464_IRQ_MODE_0_MASK BIT(2) +#define MCP3464_IRQ_MODE_1_MASK BIT(3) +#define MCP3564_POR_STATUS_MASK BIT(4) +#define MCP3564_CRCCFG_STATUS_MASK BIT(5) +#define MCP3564_DATA_READY_MASK BIT(6) + +#define MCP3564_MUX_REG 0x06 +#define MCP3564_MUX_VIN_P_MASK GENMASK(7, 4) +#define MCP3564_MUX_VIN_N_MASK GENMASK(3, 0) +#define MCP3564_MUX_SET(x, y) (FIELD_PREP(MCP3564_MUX_VIN_P_MASK, (x)) | \ + FIELD_PREP(MCP3564_MUX_VIN_N_MASK, (y))) + +#define MCP3564_SCAN_REG 0x07 +#define MCP3564_SCAN_CH_SEL_MASK GENMASK(15, 0) +#define MCP3564_SCAN_CH_SEL_SET(x) FIELD_PREP(MCP3564_SCAN_CH_SEL_MASK, (x)) +#define MCP3564_SCAN_DELAY_TIME_MASK GENMASK(23, 21) +#define MCP3564_SCAN_DELAY_TIME_SET(x) FIELD_PREP(MCP3564_SCAN_DELAY_TIME_MASK, (x)) +#define MCP3564_SCAN_DEFAULT_VALUE 0 + +#define MCP3564_TIMER_REG 0x08 +#define MCP3564_TIMER_DEFAULT_VALUE 0 + +#define MCP3564_OFFSETCAL_REG 0x09 +#define MCP3564_DEFAULT_OFFSETCAL 0 + +#define MCP3564_GAINCAL_REG 0x0A +#define MCP3564_DEFAULT_GAINCAL 0x00800000 + +#define MCP3564_RESERVED_B_REG 0x0B + +#define MCP3564_RESERVED_C_REG 0x0C +#define MCP3564_C_REG_DEFAULT 0x50 +#define MCP3564R_C_REG_DEFAULT 0x30 + +#define MCP3564_LOCK_REG 0x0D +#define MCP3564_LOCK_WRITE_ACCESS_PASSWORD 0xA5 +#define MCP3564_RESERVED_E_REG 0x0E +#define MCP3564_CRCCFG_REG 0x0F + +#define MCP3564_CMD_HW_ADDR_MASK GENMASK(7, 6) +#define MCP3564_CMD_ADDR_MASK GENMASK(5, 2) + +#define MCP3564_HW_ADDR_MASK GENMASK(1, 0) + +#define MCP3564_FASTCMD_START 0x0A +#define MCP3564_FASTCMD_RESET 0x0E + +#define MCP3461_HW_ID 0x0008 +#define MCP3462_HW_ID 0x0009 +#define MCP3464_HW_ID 0x000B + +#define MCP3561_HW_ID 0x000C +#define MCP3562_HW_ID 0x000D +#define MCP3564_HW_ID 0x000F +#define MCP3564_HW_ID_MASK GENMASK(3, 0) + +#define MCP3564R_INT_VREF_MV 2400 + +#define MCP3564_DATA_READY_TIMEOUT_MS 2000 + +#define MCP3564_MAX_PGA 8 +#define MCP3564_MAX_BURNOUT_IDX 4 +#define MCP3564_MAX_CHANNELS 66 + +enum mcp3564_ids { + mcp3461, + mcp3462, + mcp3464, + mcp3561, + mcp3562, + mcp3564, + mcp3461r, + mcp3462r, + mcp3464r, + mcp3561r, + mcp3562r, + mcp3564r, +}; + +enum mcp3564_delay_time { + MCP3564_NO_DELAY, + MCP3564_DELAY_8_DMCLK, + MCP3564_DELAY_16_DMCLK, + MCP3564_DELAY_32_DMCLK, + MCP3564_DELAY_64_DMCLK, + MCP3564_DELAY_128_DMCLK, + MCP3564_DELAY_256_DMCLK, + MCP3564_DELAY_512_DMCLK +}; + +enum mcp3564_adc_conversion_mode { + MCP3564_ADC_MODE_DEFAULT, + MCP3564_ADC_MODE_SHUTDOWN, + MCP3564_ADC_MODE_STANDBY, + MCP3564_ADC_MODE_CONVERSION +}; + +enum mcp3564_adc_bias_current { + MCP3564_BOOST_CURRENT_x0_50, + MCP3564_BOOST_CURRENT_x0_66, + MCP3564_BOOST_CURRENT_x1_00, + MCP3564_BOOST_CURRENT_x2_00 +}; + +enum mcp3564_burnout { + MCP3564_CONFIG0_CS_SEL_0_0_uA, + MCP3564_CONFIG0_CS_SEL_0_9_uA, + MCP3564_CONFIG0_CS_SEL_3_7_uA, + MCP3564_CONFIG0_CS_SEL_15_uA +}; + +enum mcp3564_channel_names { + MCP3564_CH0, + MCP3564_CH1, + MCP3564_CH2, + MCP3564_CH3, + MCP3564_CH4, + MCP3564_CH5, + MCP3564_CH6, + MCP3564_CH7, + MCP3564_AGND, + MCP3564_AVDD, + MCP3564_RESERVED, /* do not use */ + MCP3564_REFIN_POZ, + MCP3564_REFIN_NEG, + MCP3564_TEMP_DIODE_P, + MCP3564_TEMP_DIODE_M, + MCP3564_INTERNAL_VCM, +}; + +enum mcp3564_oversampling { + MCP3564_OVERSAMPLING_RATIO_32, + MCP3564_OVERSAMPLING_RATIO_64, + MCP3564_OVERSAMPLING_RATIO_128, + MCP3564_OVERSAMPLING_RATIO_256, + MCP3564_OVERSAMPLING_RATIO_512, + MCP3564_OVERSAMPLING_RATIO_1024, + MCP3564_OVERSAMPLING_RATIO_2048, + MCP3564_OVERSAMPLING_RATIO_4096, + MCP3564_OVERSAMPLING_RATIO_8192, + MCP3564_OVERSAMPLING_RATIO_16384, + MCP3564_OVERSAMPLING_RATIO_20480, + MCP3564_OVERSAMPLING_RATIO_24576, + MCP3564_OVERSAMPLING_RATIO_40960, + MCP3564_OVERSAMPLING_RATIO_49152, + MCP3564_OVERSAMPLING_RATIO_81920, + MCP3564_OVERSAMPLING_RATIO_98304 +}; + +static const unsigned int mcp3564_oversampling_avail[] = { + [MCP3564_OVERSAMPLING_RATIO_32] = 32, + [MCP3564_OVERSAMPLING_RATIO_64] = 64, + [MCP3564_OVERSAMPLING_RATIO_128] = 128, + [MCP3564_OVERSAMPLING_RATIO_256] = 256, + [MCP3564_OVERSAMPLING_RATIO_512] = 512, + [MCP3564_OVERSAMPLING_RATIO_1024] = 1024, + [MCP3564_OVERSAMPLING_RATIO_2048] = 2048, + [MCP3564_OVERSAMPLING_RATIO_4096] = 4096, + [MCP3564_OVERSAMPLING_RATIO_8192] = 8192, + [MCP3564_OVERSAMPLING_RATIO_16384] = 16384, + [MCP3564_OVERSAMPLING_RATIO_20480] = 20480, + [MCP3564_OVERSAMPLING_RATIO_24576] = 24576, + [MCP3564_OVERSAMPLING_RATIO_40960] = 40960, + [MCP3564_OVERSAMPLING_RATIO_49152] = 49152, + [MCP3564_OVERSAMPLING_RATIO_81920] = 81920, + [MCP3564_OVERSAMPLING_RATIO_98304] = 98304 +}; + +/* + * Current Source/Sink Selection Bits for Sensor Bias (source on VIN+/sink on VIN-) + */ +static const int mcp3564_burnout_avail[][2] = { + [MCP3564_CONFIG0_CS_SEL_0_0_uA] = { 0, 0 }, + [MCP3564_CONFIG0_CS_SEL_0_9_uA] = { 0, 900 }, + [MCP3564_CONFIG0_CS_SEL_3_7_uA] = { 0, 3700 }, + [MCP3564_CONFIG0_CS_SEL_15_uA] = { 0, 15000 } +}; + +/* + * BOOST[1:0]: ADC Bias Current Selection + */ +static const char * const mcp3564_boost_current_avail[] = { + [MCP3564_BOOST_CURRENT_x0_50] = "0.5", + [MCP3564_BOOST_CURRENT_x0_66] = "0.66", + [MCP3564_BOOST_CURRENT_x1_00] = "1", + [MCP3564_BOOST_CURRENT_x2_00] = "2", +}; + +/* + * Calibration bias values + */ +static const int mcp3564_calib_bias[] = { + -8388608, /* min: -2^23 */ + 1, /* step: 1 */ + 8388607 /* max: 2^23 - 1 */ +}; + +/* + * Calibration scale values + * The Gain Error Calibration register (GAINCAL) is an + * unsigned 24-bit register that holds the digital gain error + * calibration value, GAINCAL which could be calculated by + * GAINCAL (V/V) = (GAINCAL[23:0])/8388608 + * The gain error calibration value range in equivalent voltage is [0; 2-2^(-23)] + */ +static const unsigned int mcp3564_calib_scale[] = { + 0, /* min: 0 */ + 1, /* step: 1/8388608 */ + 16777215 /* max: 2 - 2^(-23) */ +}; + +/* Programmable hardware gain x1/3, x1, x2, x4, x8, x16, x32, x64 */ +static const int mcp3564_hwgain_frac[] = { + 3, 10, + 1, 1, + 2, 1, + 4, 1, + 8, 1, + 16, 1, + 32, 1, + 64, 1 +}; + +static const char *mcp3564_channel_labels[2] = { + "burnout_current", "temperature", +}; + +/** + * struct mcp3564_chip_info - chip specific data + * @name: device name + * @num_channels: number of channels + * @resolution: ADC resolution + * @have_vref: does the hardware have an internal voltage reference? + */ +struct mcp3564_chip_info { + const char *name; + unsigned int num_channels; + unsigned int resolution; + bool have_vref; +}; + +/** + * struct mcp3564_state - working data for a ADC device + * @chip_info: chip specific data + * @spi: SPI device structure + * @vref: the regulator device used as a voltage reference in case + * external voltage reference is used + * @vref_mv: voltage reference value in miliVolts + * @lock: synchronize access to driver's state members + * @dev_addr: hardware device address + * @oversampling: the index inside oversampling list of the ADC + * @hwgain: the index inside hardware gain list of the ADC + * @scale_tbls: table with precalculated scale + * @calib_bias: calibration bias value + * @calib_scale: calibration scale value + * @current_boost_mode: the index inside current boost list of the ADC + * @burnout_mode: the index inside current bias list of the ADC + * @auto_zeroing_mux: set if ADC auto-zeroing algorithm is enabled + * @auto_zeroing_ref: set if ADC auto-Zeroing Reference Buffer Setting is enabled + * @have_vref: does the ADC have an internal voltage reference? + * @labels: table with channels labels + */ +struct mcp3564_state { + const struct mcp3564_chip_info *chip_info; + struct spi_device *spi; + struct regulator *vref; + unsigned short vref_mv; + struct mutex lock; /* Synchronize access to driver's state members */ + u8 dev_addr; + enum mcp3564_oversampling oversampling; + unsigned int hwgain; + unsigned int scale_tbls[MCP3564_MAX_PGA][2]; + int calib_bias; + int calib_scale; + unsigned int current_boost_mode; + enum mcp3564_burnout burnout_mode; + bool auto_zeroing_mux; + bool auto_zeroing_ref; + bool have_vref; + const char *labels[MCP3564_MAX_CHANNELS]; +}; + +static inline u8 mcp3564_cmd_write(u8 chip_addr, u8 reg) +{ + return FIELD_PREP(MCP3564_CMD_HW_ADDR_MASK, chip_addr) | + FIELD_PREP(MCP3564_CMD_ADDR_MASK, reg) | + BIT(1); +} + +static inline u8 mcp3564_cmd_read(u8 chip_addr, u8 reg) +{ + return FIELD_PREP(MCP3564_CMD_HW_ADDR_MASK, chip_addr) | + FIELD_PREP(MCP3564_CMD_ADDR_MASK, reg) | + BIT(0); +} + +static int mcp3564_read_8bits(struct mcp3564_state *adc, u8 reg, u8 *val) +{ + int ret; + u8 tx_buf; + u8 rx_buf; + + tx_buf = mcp3564_cmd_read(adc->dev_addr, reg); + + ret = spi_write_then_read(adc->spi, &tx_buf, sizeof(tx_buf), + &rx_buf, sizeof(rx_buf)); + *val = rx_buf; + + return ret; +} + +static int mcp3564_read_16bits(struct mcp3564_state *adc, u8 reg, u16 *val) +{ + int ret; + u8 tx_buf; + __be16 rx_buf; + + tx_buf = mcp3564_cmd_read(adc->dev_addr, reg); + + ret = spi_write_then_read(adc->spi, &tx_buf, sizeof(tx_buf), + &rx_buf, sizeof(rx_buf)); + *val = be16_to_cpu(rx_buf); + + return ret; +} + +static int mcp3564_read_32bits(struct mcp3564_state *adc, u8 reg, u32 *val) +{ + int ret; + u8 tx_buf; + __be32 rx_buf; + + tx_buf = mcp3564_cmd_read(adc->dev_addr, reg); + + ret = spi_write_then_read(adc->spi, &tx_buf, sizeof(tx_buf), + &rx_buf, sizeof(rx_buf)); + *val = be32_to_cpu(rx_buf); + + return ret; +} + +static int mcp3564_write_8bits(struct mcp3564_state *adc, u8 reg, u8 val) +{ + u8 tx_buf[2]; + + tx_buf[0] = mcp3564_cmd_write(adc->dev_addr, reg); + tx_buf[1] = val; + + return spi_write_then_read(adc->spi, tx_buf, sizeof(tx_buf), NULL, 0); +} + +static int mcp3564_write_24bits(struct mcp3564_state *adc, u8 reg, u32 val) +{ + __be32 val_be; + + val |= (mcp3564_cmd_write(adc->dev_addr, reg) << 24); + val_be = cpu_to_be32(val); + + return spi_write_then_read(adc->spi, &val_be, sizeof(val_be), NULL, 0); +} + +static int mcp3564_fast_cmd(struct mcp3564_state *adc, u8 fast_cmd) +{ + u8 val; + + val = FIELD_PREP(MCP3564_CMD_HW_ADDR_MASK, adc->dev_addr) | + FIELD_PREP(MCP3564_CMD_ADDR_MASK, fast_cmd); + + return spi_write_then_read(adc->spi, &val, 1, NULL, 0); +} + +static int mcp3564_update_8bits(struct mcp3564_state *adc, u8 reg, u32 mask, u8 val) +{ + u8 tmp; + int ret; + + val &= mask; + + ret = mcp3564_read_8bits(adc, reg, &tmp); + if (ret < 0) + return ret; + + tmp &= ~mask; + tmp |= val; + + return mcp3564_write_8bits(adc, reg, tmp); +} + +static int mcp3564_set_current_boost_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct mcp3564_state *adc = iio_priv(indio_dev); + int ret; + + dev_dbg(&indio_dev->dev, "%s: %d\n", __func__, mode); + + mutex_lock(&adc->lock); + ret = mcp3564_update_8bits(adc, MCP3564_CONFIG2_REG, MCP3564_CONFIG2_BOOST_CURRENT_MASK, + FIELD_PREP(MCP3564_CONFIG2_BOOST_CURRENT_MASK, mode)); + + if (ret) + dev_err(&indio_dev->dev, "Failed to configure CONFIG2 register\n"); + else + adc->current_boost_mode = mode; + + mutex_unlock(&adc->lock); + + return ret; +} + +static int mcp3564_get_current_boost_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct mcp3564_state *adc = iio_priv(indio_dev); + + return adc->current_boost_mode; +} + +static const struct iio_enum mcp3564_current_boost_mode_enum = { + .items = mcp3564_boost_current_avail, + .num_items = ARRAY_SIZE(mcp3564_boost_current_avail), + .set = mcp3564_set_current_boost_mode, + .get = mcp3564_get_current_boost_mode, +}; + +static const struct iio_chan_spec_ext_info mcp3564_ext_info[] = { + IIO_ENUM("boost_current_gain", IIO_SHARED_BY_ALL, &mcp3564_current_boost_mode_enum), + { + .name = "boost_current_gain_available", + .shared = IIO_SHARED_BY_ALL, + .read = iio_enum_available_read, + .private = (uintptr_t)&mcp3564_current_boost_mode_enum, + }, + { } +}; + +static ssize_t mcp3564_auto_zeroing_mux_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct mcp3564_state *adc = iio_priv(indio_dev); + + return sysfs_emit(buf, "%d\n", adc->auto_zeroing_mux); +} + +static ssize_t mcp3564_auto_zeroing_mux_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct mcp3564_state *adc = iio_priv(indio_dev); + bool auto_zero; + int ret; + + ret = kstrtobool(buf, &auto_zero); + if (ret) + return ret; + + mutex_lock(&adc->lock); + ret = mcp3564_update_8bits(adc, MCP3564_CONFIG2_REG, MCP3564_CONFIG2_AZ_MUX_MASK, + FIELD_PREP(MCP3564_CONFIG2_AZ_MUX_MASK, auto_zero)); + + if (ret) + dev_err(&indio_dev->dev, "Failed to update CONFIG2 register\n"); + else + adc->auto_zeroing_mux = auto_zero; + + mutex_unlock(&adc->lock); + + return ret ? ret : len; +} + +static ssize_t mcp3564_auto_zeroing_ref_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct mcp3564_state *adc = iio_priv(indio_dev); + + return sysfs_emit(buf, "%d\n", adc->auto_zeroing_ref); +} + +static ssize_t mcp3564_auto_zeroing_ref_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct mcp3564_state *adc = iio_priv(indio_dev); + bool auto_zero; + int ret; + + ret = kstrtobool(buf, &auto_zero); + if (ret) + return ret; + + mutex_lock(&adc->lock); + ret = mcp3564_update_8bits(adc, MCP3564_CONFIG2_REG, MCP3564_CONFIG2_AZ_REF_MASK, + FIELD_PREP(MCP3564_CONFIG2_AZ_REF_MASK, auto_zero)); + + if (ret) + dev_err(&indio_dev->dev, "Failed to update CONFIG2 register\n"); + else + adc->auto_zeroing_ref = auto_zero; + + mutex_unlock(&adc->lock); + + return ret ? ret : len; +} + +static const struct iio_chan_spec mcp3564_channel_template = { + .type = IIO_VOLTAGE, + .indexed = 1, + .differential = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .ext_info = mcp3564_ext_info, +}; + +static const struct iio_chan_spec mcp3564_temp_channel_template = { + .type = IIO_TEMP, + .channel = 0, + .address = ((MCP3564_TEMP_DIODE_P << 4) | MCP3564_TEMP_DIODE_M), + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), +}; + +static const struct iio_chan_spec mcp3564_burnout_channel_template = { + .type = IIO_CURRENT, + .output = true, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), +}; + +/* + * Number of channels could be calculated: + * num_channels = single_ended_input + differential_input + temperature + burnout + * Eg. for MCP3561 (only 2 channels available: CH0 and CH1) + * single_ended_input = (CH0 - GND), (CH1 - GND) = 2 + * differential_input = (CH0 - CH1), (CH0 - CH0) = 2 + * num_channels = 2 + 2 + 2 + * Generic formula is: + * num_channels = P^R(Number_of_single_ended_channels, 2) + 2 (temperature + burnout channels) + * P^R(Number_of_single_ended_channels, 2) is Permutations with Replacement of + * Number_of_single_ended_channels taken by 2 + */ +static const struct mcp3564_chip_info mcp3564_chip_infos_tbl[] = { + [mcp3461] = { + .name = "mcp3461", + .num_channels = 6, + .resolution = 16, + .have_vref = false, + }, + [mcp3462] = { + .name = "mcp3462", + .num_channels = 18, + .resolution = 16, + .have_vref = false, + }, + [mcp3464] = { + .name = "mcp3464", + .num_channels = 66, + .resolution = 16, + .have_vref = false, + }, + [mcp3561] = { + .name = "mcp3561", + .num_channels = 6, + .resolution = 24, + .have_vref = false, + }, + [mcp3562] = { + .name = "mcp3562", + .num_channels = 18, + .resolution = 24, + .have_vref = false, + }, + [mcp3564] = { + .name = "mcp3564", + .num_channels = 66, + .resolution = 24, + .have_vref = false, + }, + [mcp3461r] = { + .name = "mcp3461r", + .num_channels = 6, + .resolution = 16, + .have_vref = false, + }, + [mcp3462r] = { + .name = "mcp3462r", + .num_channels = 18, + .resolution = 16, + .have_vref = true, + }, + [mcp3464r] = { + .name = "mcp3464r", + .num_channels = 66, + .resolution = 16, + .have_vref = true, + }, + [mcp3561r] = { + .name = "mcp3561r", + .num_channels = 6, + .resolution = 24, + .have_vref = true, + }, + [mcp3562r] = { + .name = "mcp3562r", + .num_channels = 18, + .resolution = 24, + .have_vref = true, + }, + [mcp3564r] = { + .name = "mcp3564r", + .num_channels = 66, + .resolution = 24, + .have_vref = true, + }, +}; + +static int mcp3564_read_single_value(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, + int *val) +{ + struct mcp3564_state *adc = iio_priv(indio_dev); + int ret; + u8 tmp; + int ret_read = 0; + + ret = mcp3564_write_8bits(adc, MCP3564_MUX_REG, channel->address); + if (ret) + return ret; + + /* Start ADC Conversion using fast command (overwrites ADC_MODE[1:0] = 11) */ + ret = mcp3564_fast_cmd(adc, MCP3564_FASTCMD_START); + if (ret) + return ret; + + /* + * Check if the conversion is ready. If not, wait a little bit, and + * in case of timeout exit with an error. + */ + ret = read_poll_timeout(mcp3564_read_8bits, ret_read, + ret_read || !(tmp & MCP3564_DATA_READY_MASK), + 20000, MCP3564_DATA_READY_TIMEOUT_MS * 1000, true, + adc, MCP3564_IRQ_REG, &tmp); + + /* failed to read status register */ + if (ret_read) + return ret_read; + + if (ret) + return ret; + + if (tmp & MCP3564_DATA_READY_MASK) + /* failing to finish conversion */ + return -EBUSY; + + return mcp3564_read_32bits(adc, MCP3564_ADCDATA_REG, val); +} + +static int mcp3564_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, + const int **vals, int *type, + int *length, long mask) +{ + struct mcp3564_state *adc = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!channel->output) + return -EINVAL; + + *vals = mcp3564_burnout_avail[0]; + *length = ARRAY_SIZE(mcp3564_burnout_avail) * 2; + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = mcp3564_oversampling_avail; + *length = ARRAY_SIZE(mcp3564_oversampling_avail); + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SCALE: + *vals = (int *)adc->scale_tbls; + *length = ARRAY_SIZE(adc->scale_tbls) * 2; + *type = IIO_VAL_INT_PLUS_NANO; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_CALIBBIAS: + *vals = mcp3564_calib_bias; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + case IIO_CHAN_INFO_CALIBSCALE: + *vals = mcp3564_calib_scale; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + default: + return -EINVAL; + } +} + +static int mcp3564_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, + int *val, int *val2, long mask) +{ + struct mcp3564_state *adc = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (channel->output) { + mutex_lock(&adc->lock); + *val = mcp3564_burnout_avail[adc->burnout_mode][0]; + *val2 = mcp3564_burnout_avail[adc->burnout_mode][1]; + mutex_unlock(&adc->lock); + return IIO_VAL_INT_PLUS_MICRO; + } + + ret = mcp3564_read_single_value(indio_dev, channel, val); + if (ret) + return -EINVAL; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + mutex_lock(&adc->lock); + *val = adc->scale_tbls[adc->hwgain][0]; + *val2 = adc->scale_tbls[adc->hwgain][1]; + mutex_unlock(&adc->lock); + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = mcp3564_oversampling_avail[adc->oversampling]; + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBBIAS: + *val = adc->calib_bias; + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBSCALE: + *val = adc->calib_scale; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int mcp3564_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_RAW: + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_CALIBBIAS: + case IIO_CHAN_INFO_CALIBSCALE: + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } +} + +static int mcp3564_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int val, + int val2, long mask) +{ + struct mcp3564_state *adc = iio_priv(indio_dev); + int tmp; + unsigned int hwgain; + enum mcp3564_burnout burnout; + int ret = 0; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (!channel->output) + return -EINVAL; + + for (burnout = 0; burnout < MCP3564_MAX_BURNOUT_IDX; burnout++) + if (val == mcp3564_burnout_avail[burnout][0] && + val2 == mcp3564_burnout_avail[burnout][1]) + break; + + if (burnout == MCP3564_MAX_BURNOUT_IDX) + return -EINVAL; + + if (burnout == adc->burnout_mode) + return ret; + + mutex_lock(&adc->lock); + ret = mcp3564_update_8bits(adc, MCP3564_CONFIG0_REG, + MCP3564_CONFIG0_CS_SEL_MASK, + FIELD_PREP(MCP3564_CONFIG0_CS_SEL_MASK, burnout)); + + if (ret) + dev_err(&indio_dev->dev, "Failed to configure burnout current\n"); + else + adc->burnout_mode = burnout; + mutex_unlock(&adc->lock); + return ret; + case IIO_CHAN_INFO_CALIBBIAS: + if (val < mcp3564_calib_bias[0] && val > mcp3564_calib_bias[2]) + return -EINVAL; + + mutex_lock(&adc->lock); + ret = mcp3564_write_24bits(adc, MCP3564_OFFSETCAL_REG, val); + if (!ret) + adc->calib_bias = val; + mutex_unlock(&adc->lock); + return ret; + case IIO_CHAN_INFO_CALIBSCALE: + if (val < mcp3564_calib_scale[0] && val > mcp3564_calib_scale[2]) + return -EINVAL; + + if (adc->calib_scale == val) + return ret; + + mutex_lock(&adc->lock); + ret = mcp3564_write_24bits(adc, MCP3564_GAINCAL_REG, val); + if (!ret) + adc->calib_scale = val; + mutex_unlock(&adc->lock); + return ret; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + if (val < 0) + return -EINVAL; + + tmp = find_closest(val, mcp3564_oversampling_avail, + ARRAY_SIZE(mcp3564_oversampling_avail)); + + if (adc->oversampling == tmp) + return ret; + + mutex_lock(&adc->lock); + ret = mcp3564_update_8bits(adc, MCP3564_CONFIG1_REG, + MCP3564_CONFIG1_OVERSPL_RATIO_MASK, + FIELD_PREP(MCP3564_CONFIG1_OVERSPL_RATIO_MASK, + adc->oversampling)); + if (!ret) + adc->oversampling = tmp; + mutex_unlock(&adc->lock); + return ret; + case IIO_CHAN_INFO_SCALE: + for (hwgain = 0; hwgain < MCP3564_MAX_PGA; hwgain++) + if (val == adc->scale_tbls[hwgain][0] && + val2 == adc->scale_tbls[hwgain][1]) + break; + + if (hwgain == MCP3564_MAX_PGA) + return -EINVAL; + + if (hwgain == adc->hwgain) + return ret; + + mutex_lock(&adc->lock); + ret = mcp3564_update_8bits(adc, MCP3564_CONFIG2_REG, + MCP3564_CONFIG2_HARDWARE_GAIN_MASK, + FIELD_PREP(MCP3564_CONFIG2_HARDWARE_GAIN_MASK, hwgain)); + if (!ret) + adc->hwgain = hwgain; + + mutex_unlock(&adc->lock); + return ret; + default: + return -EINVAL; + } +} + +static int mcp3564_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, char *label) +{ + struct mcp3564_state *adc = iio_priv(indio_dev); + + return sprintf(label, "%s\n", adc->labels[chan->scan_index]); +} + +static int mcp3564_parse_fw_children(struct iio_dev *indio_dev) +{ + struct mcp3564_state *adc = iio_priv(indio_dev); + struct device *dev = &adc->spi->dev; + struct iio_chan_spec *channels; + struct fwnode_handle *child; + struct iio_chan_spec chanspec = mcp3564_channel_template; + struct iio_chan_spec temp_chanspec = mcp3564_temp_channel_template; + struct iio_chan_spec burnout_chanspec = mcp3564_burnout_channel_template; + int chan_idx = 0; + unsigned int num_ch; + u32 inputs[2]; + const char *node_name; + const char *label; + int ret; + + num_ch = device_get_child_node_count(dev); + if (num_ch == 0) + return dev_err_probe(&indio_dev->dev, -ENODEV, + "FW has no channels defined\n"); + + /* Reserve space for burnout and temperature channel */ + num_ch += 2; + + if (num_ch > adc->chip_info->num_channels) + return dev_err_probe(dev, -EINVAL, "Too many channels %d > %d\n", + num_ch, adc->chip_info->num_channels); + + channels = devm_kcalloc(dev, num_ch, sizeof(*channels), GFP_KERNEL); + if (!channels) + return dev_err_probe(dev, -ENOMEM, "Can't allocate memory\n"); + + device_for_each_child_node(dev, child) { + node_name = fwnode_get_name(child); + + if (fwnode_property_present(child, "diff-channels")) { + ret = fwnode_property_read_u32_array(child, + "diff-channels", + inputs, + ARRAY_SIZE(inputs)); + chanspec.differential = 1; + } else { + ret = fwnode_property_read_u32(child, "reg", &inputs[0]); + + chanspec.differential = 0; + inputs[1] = MCP3564_AGND; + } + if (ret) { + fwnode_handle_put(child); + return ret; + } + + if (inputs[0] > MCP3564_INTERNAL_VCM || + inputs[1] > MCP3564_INTERNAL_VCM) { + fwnode_handle_put(child); + return dev_err_probe(&indio_dev->dev, -EINVAL, + "Channel index > %d, for %s\n", + MCP3564_INTERNAL_VCM + 1, + node_name); + } + + chanspec.address = (inputs[0] << 4) | inputs[1]; + chanspec.channel = inputs[0]; + chanspec.channel2 = inputs[1]; + chanspec.scan_index = chan_idx; + + if (fwnode_property_present(child, "label")) { + fwnode_property_read_string(child, "label", &label); + adc->labels[chan_idx] = label; + } + + channels[chan_idx] = chanspec; + chan_idx++; + } + + /* Add burnout current channel */ + burnout_chanspec.scan_index = chan_idx; + channels[chan_idx] = burnout_chanspec; + adc->labels[chan_idx] = mcp3564_channel_labels[0]; + chanspec.scan_index = chan_idx; + chan_idx++; + + /* Add temperature channel */ + temp_chanspec.scan_index = chan_idx; + channels[chan_idx] = temp_chanspec; + adc->labels[chan_idx] = mcp3564_channel_labels[1]; + chan_idx++; + + indio_dev->num_channels = chan_idx; + indio_dev->channels = channels; + + return 0; +} + +static void mcp3564_disable_reg(void *reg) +{ + regulator_disable(reg); +} + +static void mcp3564_fill_scale_tbls(struct mcp3564_state *adc) +{ + unsigned int pow = adc->chip_info->resolution - 1; + int ref; + unsigned int i; + int tmp0; + u64 tmp1; + + for (i = 0; i < MCP3564_MAX_PGA; i++) { + ref = adc->vref_mv; + tmp1 = ((u64)ref * NANO) >> pow; + div_u64_rem(tmp1, NANO, &tmp0); + + tmp1 = tmp1 * mcp3564_hwgain_frac[(2 * i) + 1]; + tmp0 = (int)div_u64(tmp1, mcp3564_hwgain_frac[2 * i]); + + adc->scale_tbls[i][1] = tmp0; + } +} + +static int mcp3564_config(struct iio_dev *indio_dev) +{ + struct mcp3564_state *adc = iio_priv(indio_dev); + struct device *dev = &adc->spi->dev; + const struct spi_device_id *dev_id; + u8 tmp_reg; + u16 tmp_u16; + enum mcp3564_ids ids; + int ret = 0; + unsigned int tmp = 0x01; + bool err = true; + + /* + * The address is set on a per-device basis by fuses in the factory, + * configured on request. If not requested, the fuses are set for 0x1. + * The device address is part of the device markings to avoid + * potential confusion. This address is coded on two bits, so four possible + * addresses are available when multiple devices are present on the same + * SPI bus with only one Chip Select line for all devices. + */ + device_property_read_u32(dev, "microchip,hw-device-address", &tmp); + + if (tmp > 3) { + dev_err_probe(dev, tmp, + "invalid device address. Must be in range 0-3.\n"); + return -EINVAL; + } + + adc->dev_addr = FIELD_GET(MCP3564_HW_ADDR_MASK, tmp); + + dev_dbg(dev, "use HW device address %i\n", adc->dev_addr); + + ret = mcp3564_read_8bits(adc, MCP3564_RESERVED_C_REG, &tmp_reg); + if (ret < 0) + return ret; + + switch (tmp_reg) { + case MCP3564_C_REG_DEFAULT: + adc->have_vref = false; + break; + case MCP3564R_C_REG_DEFAULT: + adc->have_vref = true; + break; + default: + dev_info(dev, "Unknown chip found: %d\n", tmp_reg); + err = true; + } + + if (!err) { + ret = mcp3564_read_16bits(adc, MCP3564_RESERVED_E_REG, &tmp_u16); + if (ret < 0) + return ret; + + switch (tmp_u16 & MCP3564_HW_ID_MASK) { + case MCP3461_HW_ID: + if (adc->have_vref) + ids = mcp3461r; + else + ids = mcp3461; + break; + case MCP3462_HW_ID: + if (adc->have_vref) + ids = mcp3462r; + else + ids = mcp3462; + break; + case MCP3464_HW_ID: + if (adc->have_vref) + ids = mcp3464r; + else + ids = mcp3464; + break; + case MCP3561_HW_ID: + if (adc->have_vref) + ids = mcp3561r; + else + ids = mcp3561; + break; + case MCP3562_HW_ID: + if (adc->have_vref) + ids = mcp3562r; + else + ids = mcp3562; + break; + case MCP3564_HW_ID: + if (adc->have_vref) + ids = mcp3564r; + else + ids = mcp3564; + break; + default: + dev_info(dev, "Unknown chip found: %d\n", tmp_u16); + err = true; + } + } + + if (err) { + /* + * If failed to identify the hardware based on internal registers, + * try using fallback compatible in device tree to deal with some newer part number. + */ + adc->chip_info = spi_get_device_match_data(adc->spi); + if (!adc->chip_info) { + dev_id = spi_get_device_id(adc->spi); + adc->chip_info = (const struct mcp3564_chip_info *)dev_id->driver_data; + } + + adc->have_vref = adc->chip_info->have_vref; + } else { + adc->chip_info = &mcp3564_chip_infos_tbl[ids]; + } + + dev_dbg(dev, "Found %s chip\n", adc->chip_info->name); + + adc->vref = devm_regulator_get_optional(dev, "vref"); + if (IS_ERR(adc->vref)) { + if (PTR_ERR(adc->vref) != -ENODEV) + return dev_err_probe(dev, PTR_ERR(adc->vref), + "failed to get regulator\n"); + + /* Check if chip has internal vref */ + if (!adc->have_vref) + return dev_err_probe(dev, PTR_ERR(adc->vref), + "Unknown Vref\n"); + adc->vref = NULL; + dev_dbg(dev, "%s: Using internal Vref\n", __func__); + } else { + ret = regulator_enable(adc->vref); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, mcp3564_disable_reg, + adc->vref); + if (ret) + return ret; + + dev_dbg(dev, "%s: Using External Vref\n", __func__); + + ret = regulator_get_voltage(adc->vref); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to read vref regulator\n"); + + adc->vref_mv = ret / MILLI; + } + + ret = mcp3564_parse_fw_children(indio_dev); + if (ret) + return ret; + + /* + * Command sequence that ensures a recovery with the desired settings + * in any cases of loss-of-power scenario (Full Chip Reset): + * - Write LOCK register to 0xA5 + * - Write IRQ register to 0x03 + * - Send "Device Full Reset" fast command + * - Wait 1ms for "Full Reset" to complete + */ + ret = mcp3564_write_8bits(adc, MCP3564_LOCK_REG, MCP3564_LOCK_WRITE_ACCESS_PASSWORD); + if (ret) + return ret; + + ret = mcp3564_write_8bits(adc, MCP3564_IRQ_REG, 0x03); + if (ret) + return ret; + + ret = mcp3564_fast_cmd(adc, MCP3564_FASTCMD_RESET); + if (ret) + return ret; + + /* + * After Full reset wait some time to be able to fully reset the part and place + * it back in a default configuration. + * From datasheet: POR (Power On Reset Time) is ~1us + * 1ms should be enough. + */ + mdelay(1); + + /* set a gain of 1x for GAINCAL */ + ret = mcp3564_write_24bits(adc, MCP3564_GAINCAL_REG, MCP3564_DEFAULT_GAINCAL); + if (ret) + return ret; + + adc->calib_scale = MCP3564_DEFAULT_GAINCAL; + + ret = mcp3564_write_24bits(adc, MCP3564_OFFSETCAL_REG, MCP3564_DEFAULT_OFFSETCAL); + if (ret) + return ret; + + ret = mcp3564_write_24bits(adc, MCP3564_TIMER_REG, MCP3564_TIMER_DEFAULT_VALUE); + if (ret) + return ret; + + ret = mcp3564_write_24bits(adc, MCP3564_SCAN_REG, + MCP3564_SCAN_DELAY_TIME_SET(MCP3564_NO_DELAY) | + MCP3564_SCAN_CH_SEL_SET(MCP3564_SCAN_DEFAULT_VALUE)); + if (ret) + return ret; + + ret = mcp3564_write_8bits(adc, MCP3564_MUX_REG, MCP3564_MUX_SET(MCP3564_CH0, MCP3564_CH1)); + if (ret) + return ret; + + ret = mcp3564_write_8bits(adc, MCP3564_IRQ_REG, + FIELD_PREP(MCP3464_EN_FASTCMD_MASK, 1) | + FIELD_PREP(MCP3464_EN_STP_MASK, 1)); + if (ret) + return ret; + + tmp_reg = FIELD_PREP(MCP3464_CONFIG3_CONV_MODE_MASK, + MCP3464_CONFIG3_CONV_MODE_ONE_SHOT_STANDBY); + tmp_reg |= FIELD_PREP(MCP3464_CONFIG3_DATA_FORMAT_MASK, + MCP3464_CONFIG3_DATA_FMT_32B_SGN_EXT); + tmp_reg |= MCP3464_CONFIG3_EN_OFFCAL_MASK; + tmp_reg |= MCP3464_CONFIG3_EN_GAINCAL_MASK; + + ret = mcp3564_write_8bits(adc, MCP3564_CONFIG3_REG, tmp_reg); + if (ret) + return ret; + + tmp_reg = FIELD_PREP(MCP3564_CONFIG2_BOOST_CURRENT_MASK, MCP3564_BOOST_CURRENT_x1_00); + tmp_reg |= FIELD_PREP(MCP3564_CONFIG2_HARDWARE_GAIN_MASK, 0x01); + tmp_reg |= FIELD_PREP(MCP3564_CONFIG2_AZ_MUX_MASK, 1); + + ret = mcp3564_write_8bits(adc, MCP3564_CONFIG2_REG, tmp_reg); + if (ret) + return ret; + + adc->hwgain = 0x01; + adc->auto_zeroing_mux = true; + adc->auto_zeroing_ref = false; + adc->current_boost_mode = MCP3564_BOOST_CURRENT_x1_00; + + tmp_reg = FIELD_PREP(MCP3564_CONFIG1_OVERSPL_RATIO_MASK, MCP3564_OVERSAMPLING_RATIO_98304); + + ret = mcp3564_write_8bits(adc, MCP3564_CONFIG1_REG, tmp_reg); + if (ret) + return ret; + + adc->oversampling = MCP3564_OVERSAMPLING_RATIO_98304; + + tmp_reg = FIELD_PREP(MCP3564_CONFIG0_ADC_MODE_MASK, MCP3564_ADC_MODE_STANDBY); + tmp_reg |= FIELD_PREP(MCP3564_CONFIG0_CS_SEL_MASK, MCP3564_CONFIG0_CS_SEL_0_0_uA); + tmp_reg |= FIELD_PREP(MCP3564_CONFIG0_CLK_SEL_MASK, MCP3564_CONFIG0_USE_INT_CLK); + tmp_reg |= MCP3456_CONFIG0_BIT6_DEFAULT; + + if (!adc->vref) { + tmp_reg |= FIELD_PREP(MCP3456_CONFIG0_VREF_MASK, 1); + adc->vref_mv = MCP3564R_INT_VREF_MV; + } + + ret = mcp3564_write_8bits(adc, MCP3564_CONFIG0_REG, tmp_reg); + + adc->burnout_mode = MCP3564_CONFIG0_CS_SEL_0_0_uA; + + return ret; +} + +static IIO_DEVICE_ATTR(auto_zeroing_ref_enable, 0644, + mcp3564_auto_zeroing_ref_show, + mcp3564_auto_zeroing_ref_store, 0); + +static IIO_DEVICE_ATTR(auto_zeroing_mux_enable, 0644, + mcp3564_auto_zeroing_mux_show, + mcp3564_auto_zeroing_mux_store, 0); + +static struct attribute *mcp3564_attributes[] = { + &iio_dev_attr_auto_zeroing_mux_enable.dev_attr.attr, + NULL +}; + +static struct attribute *mcp3564r_attributes[] = { + &iio_dev_attr_auto_zeroing_mux_enable.dev_attr.attr, + &iio_dev_attr_auto_zeroing_ref_enable.dev_attr.attr, + NULL +}; + +static struct attribute_group mcp3564_attribute_group = { + .attrs = mcp3564_attributes, +}; + +static struct attribute_group mcp3564r_attribute_group = { + .attrs = mcp3564r_attributes, +}; + +static const struct iio_info mcp3564_info = { + .read_raw = mcp3564_read_raw, + .read_avail = mcp3564_read_avail, + .write_raw = mcp3564_write_raw, + .write_raw_get_fmt = mcp3564_write_raw_get_fmt, + .read_label = mcp3564_read_label, + .attrs = &mcp3564_attribute_group, +}; + +static const struct iio_info mcp3564r_info = { + .read_raw = mcp3564_read_raw, + .read_avail = mcp3564_read_avail, + .write_raw = mcp3564_write_raw, + .write_raw_get_fmt = mcp3564_write_raw_get_fmt, + .read_label = mcp3564_read_label, + .attrs = &mcp3564r_attribute_group, +}; + +static int mcp3564_probe(struct spi_device *spi) +{ + int ret; + struct iio_dev *indio_dev; + struct mcp3564_state *adc; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->spi = spi; + + dev_dbg(&spi->dev, "%s: probe(spi = 0x%p)\n", __func__, spi); + + /* + * Do any chip specific initialization, e.g: + * read/write some registers + * enable/disable certain channels + * change the sampling rate to the requested value + */ + ret = mcp3564_config(indio_dev); + if (ret) + return dev_err_probe(&spi->dev, ret, + "Can't configure MCP356X device\n"); + + dev_dbg(&spi->dev, "%s: Vref (mV): %d\n", __func__, adc->vref_mv); + + mcp3564_fill_scale_tbls(adc); + + indio_dev->name = adc->chip_info->name; + indio_dev->modes = INDIO_DIRECT_MODE; + + if (!adc->vref) + indio_dev->info = &mcp3564r_info; + else + indio_dev->info = &mcp3564_info; + + mutex_init(&adc->lock); + + ret = devm_iio_device_register(&spi->dev, indio_dev); + if (ret) + return dev_err_probe(&spi->dev, ret, + "Can't register IIO device\n"); + + return 0; +} + +static const struct of_device_id mcp3564_dt_ids[] = { + { .compatible = "microchip,mcp3461", .data = &mcp3564_chip_infos_tbl[mcp3461] }, + { .compatible = "microchip,mcp3462", .data = &mcp3564_chip_infos_tbl[mcp3462] }, + { .compatible = "microchip,mcp3464", .data = &mcp3564_chip_infos_tbl[mcp3464] }, + { .compatible = "microchip,mcp3561", .data = &mcp3564_chip_infos_tbl[mcp3561] }, + { .compatible = "microchip,mcp3562", .data = &mcp3564_chip_infos_tbl[mcp3562] }, + { .compatible = "microchip,mcp3564", .data = &mcp3564_chip_infos_tbl[mcp3564] }, + { .compatible = "microchip,mcp3461r", .data = &mcp3564_chip_infos_tbl[mcp3461r] }, + { .compatible = "microchip,mcp3462r", .data = &mcp3564_chip_infos_tbl[mcp3462r] }, + { .compatible = "microchip,mcp3464r", .data = &mcp3564_chip_infos_tbl[mcp3464r] }, + { .compatible = "microchip,mcp3561r", .data = &mcp3564_chip_infos_tbl[mcp3561r] }, + { .compatible = "microchip,mcp3562r", .data = &mcp3564_chip_infos_tbl[mcp3562r] }, + { .compatible = "microchip,mcp3564r", .data = &mcp3564_chip_infos_tbl[mcp3564r] }, + { } +}; +MODULE_DEVICE_TABLE(of, mcp3564_dt_ids); + +static const struct spi_device_id mcp3564_id[] = { + { "mcp3461", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3461] }, + { "mcp3462", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3462] }, + { "mcp3464", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3464] }, + { "mcp3561", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3561] }, + { "mcp3562", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3562] }, + { "mcp3564", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3564] }, + { "mcp3461r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3461r] }, + { "mcp3462r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3462r] }, + { "mcp3464r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3464r] }, + { "mcp3561r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3561r] }, + { "mcp3562r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3562r] }, + { "mcp3564r", (kernel_ulong_t)&mcp3564_chip_infos_tbl[mcp3564r] }, + { } +}; +MODULE_DEVICE_TABLE(spi, mcp3564_id); + +static struct spi_driver mcp3564_driver = { + .driver = { + .name = "mcp3564", + .of_match_table = mcp3564_dt_ids, + }, + .probe = mcp3564_probe, + .id_table = mcp3564_id, +}; + +module_spi_driver(mcp3564_driver); + +MODULE_AUTHOR("Marius Cristea <marius.cristea@microchip.com>"); +MODULE_DESCRIPTION("Microchip MCP346x/MCP346xR and MCP356x/MCP346xR ADCs"); +MODULE_LICENSE("GPL v2"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/iio/adc/pac1934.c linux-microchip/drivers/iio/adc/pac1934.c --- linux-6.6.51/drivers/iio/adc/pac1934.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/iio/adc/pac1934.c 2024-11-26 14:02:37.394491835 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0+ +/* + * IIO driver for PAC1934 Multi-Channel DC Power/Energy Monitor + * + * Copyright (C) 2017-2024 Microchip Technology Inc. and its subsidiaries + * + * Author: Bogdan Bolocan <bogdan.bolocan@microchip.com> + * Author: Victor Tudose + * Author: Marius Cristea <marius.cristea@microchip.com> + * + * Datasheet for PAC1931, PAC1932, PAC1933 and PAC1934 can be found here: + * https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/PAC1931-Family-Data-Sheet-DS20005850E.pdf + */ + +#include <linux/acpi.h> +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <asm/unaligned.h> + +/* + * maximum accumulation time should be (17 * 60 * 1000) around 17 minutes@1024 sps + * till PAC1934 accumulation registers starts to saturate + */ +#define PAC1934_MAX_RFSH_LIMIT_MS 60000 +/* 50msec is the timeout for validity of the cached registers */ +#define PAC1934_MIN_POLLING_TIME_MS 50 +/* + * 1000usec is the minimum wait time for normal conversions when sample + * rate doesn't change + */ +#define PAC1934_MIN_UPDATE_WAIT_TIME_US 1000 + +/* 32000mV */ +#define PAC1934_VOLTAGE_MILLIVOLTS_MAX 32000 +/* voltage bits resolution when set for unsigned values */ +#define PAC1934_VOLTAGE_U_RES 16 +/* voltage bits resolution when set for signed values */ +#define PAC1934_VOLTAGE_S_RES 15 + +/* + * max signed value that can be stored on 32 bits and 8 digits fractional value + * (2^31 - 1) * 10^8 + 99999999 + */ +#define PAC_193X_MAX_POWER_ACC 214748364799999999LL +/* + * min signed value that can be stored on 32 bits and 8 digits fractional value + * -(2^31) * 10^8 - 99999999 + */ +#define PAC_193X_MIN_POWER_ACC -214748364899999999LL + +#define PAC1934_MAX_NUM_CHANNELS 4 + +#define PAC1934_MEAS_REG_LEN 76 +#define PAC1934_CTRL_REG_LEN 12 + +#define PAC1934_DEFAULT_CHIP_SAMP_SPEED_HZ 1024 + +/* I2C address map */ +#define PAC1934_REFRESH_REG_ADDR 0x00 +#define PAC1934_CTRL_REG_ADDR 0x01 +#define PAC1934_ACC_COUNT_REG_ADDR 0x02 +#define PAC1934_VPOWER_ACC_1_ADDR 0x03 +#define PAC1934_VPOWER_ACC_2_ADDR 0x04 +#define PAC1934_VPOWER_ACC_3_ADDR 0x05 +#define PAC1934_VPOWER_ACC_4_ADDR 0x06 +#define PAC1934_VBUS_1_ADDR 0x07 +#define PAC1934_VBUS_2_ADDR 0x08 +#define PAC1934_VBUS_3_ADDR 0x09 +#define PAC1934_VBUS_4_ADDR 0x0A +#define PAC1934_VSENSE_1_ADDR 0x0B +#define PAC1934_VSENSE_2_ADDR 0x0C +#define PAC1934_VSENSE_3_ADDR 0x0D +#define PAC1934_VSENSE_4_ADDR 0x0E +#define PAC1934_VBUS_AVG_1_ADDR 0x0F +#define PAC1934_VBUS_AVG_2_ADDR 0x10 +#define PAC1934_VBUS_AVG_3_ADDR 0x11 +#define PAC1934_VBUS_AVG_4_ADDR 0x12 +#define PAC1934_VSENSE_AVG_1_ADDR 0x13 +#define PAC1934_VSENSE_AVG_2_ADDR 0x14 +#define PAC1934_VSENSE_AVG_3_ADDR 0x15 +#define PAC1934_VSENSE_AVG_4_ADDR 0x16 +#define PAC1934_VPOWER_1_ADDR 0x17 +#define PAC1934_VPOWER_2_ADDR 0x18 +#define PAC1934_VPOWER_3_ADDR 0x19 +#define PAC1934_VPOWER_4_ADDR 0x1A +#define PAC1934_REFRESH_V_REG_ADDR 0x1F +#define PAC1934_CTRL_STAT_REGS_ADDR 0x1C +#define PAC1934_PID_REG_ADDR 0xFD +#define PAC1934_MID_REG_ADDR 0xFE +#define PAC1934_RID_REG_ADDR 0xFF + +/* PRODUCT ID REGISTER + MANUFACTURER ID REGISTER + REVISION ID REGISTER */ +#define PAC1934_ID_REG_LEN 3 +#define PAC1934_PID_IDX 0 +#define PAC1934_MID_IDX 1 +#define PAC1934_RID_IDX 2 + +#define PAC1934_ACPI_GET_NAMES_AND_MOHMS_VALS 1 +#define PAC1934_ACPI_GET_UOHMS_VALS 2 +#define PAC1934_ACPI_GET_BIPOLAR_SETTINGS 4 +#define PAC1934_ACPI_GET_SAMP 5 + +#define PAC1934_SAMPLE_RATE_SHIFT 6 + +#define PAC1934_VBUS_SENSE_REG_LEN 2 +#define PAC1934_ACC_REG_LEN 3 +#define PAC1934_VPOWER_REG_LEN 4 +#define PAC1934_VPOWER_ACC_REG_LEN 6 +#define PAC1934_MAX_REGISTER_LENGTH 6 + +#define PAC1934_CUSTOM_ATTR_FOR_CHANNEL 1 + +/* + * relative offsets when using multi-byte reads/writes even though these + * bytes are read one after the other, they are not at adjacent memory + * locations within the I2C memory map. The chip can skip some addresses + */ +#define PAC1934_CHANNEL_DIS_REG_OFF 0 +#define PAC1934_NEG_PWR_REG_OFF 1 + +/* + * when reading/writing multiple bytes from offset PAC1934_CHANNEL_DIS_REG_OFF, + * the chip jumps over the 0x1E (REFRESH_G) and 0x1F (REFRESH_V) offsets + */ +#define PAC1934_SLOW_REG_OFF 2 +#define PAC1934_CTRL_ACT_REG_OFF 3 +#define PAC1934_CHANNEL_DIS_ACT_REG_OFF 4 +#define PAC1934_NEG_PWR_ACT_REG_OFF 5 +#define PAC1934_CTRL_LAT_REG_OFF 6 +#define PAC1934_CHANNEL_DIS_LAT_REG_OFF 7 +#define PAC1934_NEG_PWR_LAT_REG_OFF 8 +#define PAC1934_PID_REG_OFF 9 +#define PAC1934_MID_REG_OFF 10 +#define PAC1934_REV_REG_OFF 11 +#define PAC1934_CTRL_STATUS_INFO_LEN 12 + +#define PAC1934_MID 0x5D +#define PAC1931_PID 0x58 +#define PAC1932_PID 0x59 +#define PAC1933_PID 0x5A +#define PAC1934_PID 0x5B + +/* Scale constant = (10^3 * 3.2 * 10^9 / 2^28) for mili Watt-second */ +#define PAC1934_SCALE_CONSTANT 11921 + +#define PAC1934_MAX_VPOWER_RSHIFTED_BY_28B 11921 +#define PAC1934_MAX_VSENSE_RSHIFTED_BY_16B 1525 + +#define PAC1934_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr) + +#define PAC1934_CRTL_SAMPLE_RATE_MASK GENMASK(7, 6) +#define PAC1934_CHAN_SLEEP_MASK BIT(5) +#define PAC1934_CHAN_SLEEP_SET BIT(5) +#define PAC1934_CHAN_SINGLE_MASK BIT(4) +#define PAC1934_CHAN_SINGLE_SHOT_SET BIT(4) +#define PAC1934_CHAN_ALERT_MASK BIT(3) +#define PAC1934_CHAN_ALERT_EN BIT(3) +#define PAC1934_CHAN_ALERT_CC_MASK BIT(2) +#define PAC1934_CHAN_ALERT_CC_EN BIT(2) +#define PAC1934_CHAN_OVF_ALERT_MASK BIT(1) +#define PAC1934_CHAN_OVF_ALERT_EN BIT(1) +#define PAC1934_CHAN_OVF_MASK BIT(0) + +#define PAC1934_CHAN_DIS_CH1_OFF_MASK BIT(7) +#define PAC1934_CHAN_DIS_CH2_OFF_MASK BIT(6) +#define PAC1934_CHAN_DIS_CH3_OFF_MASK BIT(5) +#define PAC1934_CHAN_DIS_CH4_OFF_MASK BIT(4) +#define PAC1934_SMBUS_TIMEOUT_MASK BIT(3) +#define PAC1934_SMBUS_BYTECOUNT_MASK BIT(2) +#define PAC1934_SMBUS_NO_SKIP_MASK BIT(1) + +#define PAC1934_NEG_PWR_CH1_BIDI_MASK BIT(7) +#define PAC1934_NEG_PWR_CH2_BIDI_MASK BIT(6) +#define PAC1934_NEG_PWR_CH3_BIDI_MASK BIT(5) +#define PAC1934_NEG_PWR_CH4_BIDI_MASK BIT(4) +#define PAC1934_NEG_PWR_CH1_BIDV_MASK BIT(3) +#define PAC1934_NEG_PWR_CH2_BIDV_MASK BIT(2) +#define PAC1934_NEG_PWR_CH3_BIDV_MASK BIT(1) +#define PAC1934_NEG_PWR_CH4_BIDV_MASK BIT(0) + +/* + * Universal Unique Identifier (UUID), + * 033771E0-1705-47B4-9535-D1BBE14D9A09, + * is reserved to Microchip for the PAC1934. + */ +#define PAC1934_DSM_UUID "033771E0-1705-47B4-9535-D1BBE14D9A09" + +enum pac1934_ids { + PAC1931, + PAC1932, + PAC1933, + PAC1934 +}; + +enum pac1934_samps { + PAC1934_SAMP_1024SPS, + PAC1934_SAMP_256SPS, + PAC1934_SAMP_64SPS, + PAC1934_SAMP_8SPS +}; + +/* + * these indexes are exactly describing the element order within a single + * PAC1934 phys channel IIO channel descriptor; see the static const struct + * iio_chan_spec pac1934_single_channel[] declaration + */ +enum pac1934_ch_idx { + PAC1934_CH_ENERGY, + PAC1934_CH_POWER, + PAC1934_CH_VOLTAGE, + PAC1934_CH_CURRENT, + PAC1934_CH_VOLTAGE_AVERAGE, + PAC1934_CH_CURRENT_AVERAGE +}; + +/** + * struct pac1934_features - features of a pac1934 instance + * @phys_channels: number of physical channels supported by the chip + * @name: chip's name + */ +struct pac1934_features { + u8 phys_channels; + const char *name; +}; + +struct samp_rate_mapping { + u16 samp_rate; + u8 shift2value; +}; + +static const unsigned int samp_rate_map_tbl[] = { + [PAC1934_SAMP_1024SPS] = 1024, + [PAC1934_SAMP_256SPS] = 256, + [PAC1934_SAMP_64SPS] = 64, + [PAC1934_SAMP_8SPS] = 8, +}; + +static const struct pac1934_features pac1934_chip_config[] = { + [PAC1931] = { + .phys_channels = 1, + .name = "pac1931", + }, + [PAC1932] = { + .phys_channels = 2, + .name = "pac1932", + }, + [PAC1933] = { + .phys_channels = 3, + .name = "pac1933", + }, + [PAC1934] = { + .phys_channels = 4, + .name = "pac1934", + }, +}; + +/** + * struct reg_data - data from the registers + * @meas_regs: snapshot of raw measurements registers + * @ctrl_regs: snapshot of control registers + * @energy_sec_acc: snapshot of energy values + * @vpower_acc: accumulated vpower values + * @vpower: snapshot of vpower registers + * @vbus: snapshot of vbus registers + * @vbus_avg: averages of vbus registers + * @vsense: snapshot of vsense registers + * @vsense_avg: averages of vsense registers + * @num_enabled_channels: count of how many chip channels are currently enabled + */ +struct reg_data { + u8 meas_regs[PAC1934_MEAS_REG_LEN]; + u8 ctrl_regs[PAC1934_CTRL_REG_LEN]; + s64 energy_sec_acc[PAC1934_MAX_NUM_CHANNELS]; + s64 vpower_acc[PAC1934_MAX_NUM_CHANNELS]; + s32 vpower[PAC1934_MAX_NUM_CHANNELS]; + s32 vbus[PAC1934_MAX_NUM_CHANNELS]; + s32 vbus_avg[PAC1934_MAX_NUM_CHANNELS]; + s32 vsense[PAC1934_MAX_NUM_CHANNELS]; + s32 vsense_avg[PAC1934_MAX_NUM_CHANNELS]; + u8 num_enabled_channels; +}; + +/** + * struct pac1934_chip_info - information about the chip + * @client: the i2c-client attached to the device + * @lock: synchronize access to driver's state members + * @work_chip_rfsh: work queue used for refresh commands + * @phys_channels: phys channels count + * @active_channels: array of values, true means that channel is active + * @enable_energy: array of values, true means that channel energy is measured + * @bi_dir: array of bools, true means that channel is bidirectional + * @chip_variant: chip variant + * @chip_revision: chip revision + * @shunts: shunts + * @chip_reg_data: chip reg data + * @sample_rate_value: sampling frequency + * @labels: table with channels labels + * @iio_info: iio_info + * @tstamp: chip's uptime + */ +struct pac1934_chip_info { + struct i2c_client *client; + struct mutex lock; /* synchronize access to driver's state members */ + struct delayed_work work_chip_rfsh; + u8 phys_channels; + bool active_channels[PAC1934_MAX_NUM_CHANNELS]; + bool enable_energy[PAC1934_MAX_NUM_CHANNELS]; + bool bi_dir[PAC1934_MAX_NUM_CHANNELS]; + u8 chip_variant; + u8 chip_revision; + u32 shunts[PAC1934_MAX_NUM_CHANNELS]; + struct reg_data chip_reg_data; + s32 sample_rate_value; + char *labels[PAC1934_MAX_NUM_CHANNELS]; + struct iio_info iio_info; + unsigned long tstamp; +}; + +#define TO_PAC1934_CHIP_INFO(d) container_of(d, struct pac1934_chip_info, work_chip_rfsh) + +#define PAC1934_VPOWER_ACC_CHANNEL(_index, _si, _address) { \ + .type = IIO_ENERGY, \ + .address = (_address), \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_ENABLE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = (_si), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 48, \ + .storagebits = 64, \ + .endianness = IIO_CPU, \ + } \ +} + +#define PAC1934_VBUS_CHANNEL(_index, _si, _address) { \ + .type = IIO_VOLTAGE, \ + .address = (_address), \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = (_si), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + } \ +} + +#define PAC1934_VBUS_AVG_CHANNEL(_index, _si, _address) { \ + .type = IIO_VOLTAGE, \ + .address = (_address), \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = (_si), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + } \ +} + +#define PAC1934_VSENSE_CHANNEL(_index, _si, _address) { \ + .type = IIO_CURRENT, \ + .address = (_address), \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = (_si), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + } \ +} + +#define PAC1934_VSENSE_AVG_CHANNEL(_index, _si, _address) { \ + .type = IIO_CURRENT, \ + .address = (_address), \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = (_si), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + } \ +} + +#define PAC1934_VPOWER_CHANNEL(_index, _si, _address) { \ + .type = IIO_POWER, \ + .address = (_address), \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = (_si), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 28, \ + .storagebits = 32, \ + .shift = 4, \ + .endianness = IIO_CPU, \ + } \ +} + +static const struct iio_chan_spec pac1934_single_channel[] = { + PAC1934_VPOWER_ACC_CHANNEL(0, 0, PAC1934_VPOWER_ACC_1_ADDR), + PAC1934_VPOWER_CHANNEL(0, 0, PAC1934_VPOWER_1_ADDR), + PAC1934_VBUS_CHANNEL(0, 0, PAC1934_VBUS_1_ADDR), + PAC1934_VSENSE_CHANNEL(0, 0, PAC1934_VSENSE_1_ADDR), + PAC1934_VBUS_AVG_CHANNEL(0, 0, PAC1934_VBUS_AVG_1_ADDR), + PAC1934_VSENSE_AVG_CHANNEL(0, 0, PAC1934_VSENSE_AVG_1_ADDR), +}; + +/* Low-level I2c functions used to transfer up to 76 bytes at once */ +static int pac1934_i2c_read(struct i2c_client *client, u8 reg_addr, + void *databuf, u8 len) +{ + int ret; + struct i2c_msg msgs[2] = { + { + .addr = client->addr, + .len = 1, + .buf = (u8 *)®_addr, + }, + { + .addr = client->addr, + .len = len, + .buf = databuf, + .flags = I2C_M_RD + } + }; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + return ret; + + return 0; +} + +static int pac1934_get_samp_rate_idx(struct pac1934_chip_info *info, + u32 new_samp_rate) +{ + int cnt; + + for (cnt = 0; cnt < ARRAY_SIZE(samp_rate_map_tbl); cnt++) + if (new_samp_rate == samp_rate_map_tbl[cnt]) + return cnt; + + /* not a valid sample rate value */ + return -EINVAL; +} + +static ssize_t pac1934_shunt_value_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct pac1934_chip_info *info = iio_priv(indio_dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + return sysfs_emit(buf, "%u\n", info->shunts[this_attr->address]); +} + +static ssize_t pac1934_shunt_value_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct pac1934_chip_info *info = iio_priv(indio_dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int sh_val; + + if (kstrtouint(buf, 10, &sh_val)) { + dev_err(dev, "Shunt value is not valid\n"); + return -EINVAL; + } + + scoped_guard(mutex, &info->lock) + info->shunts[this_attr->address] = sh_val; + + return count; +} + +static int pac1934_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, + const int **vals, int *type, int *length, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *type = IIO_VAL_INT; + *vals = samp_rate_map_tbl; + *length = ARRAY_SIZE(samp_rate_map_tbl); + return IIO_AVAIL_LIST; + } + + return -EINVAL; +} + +static int pac1934_send_refresh(struct pac1934_chip_info *info, + u8 refresh_cmd, u32 wait_time) +{ + /* this function only sends REFRESH or REFRESH_V */ + struct i2c_client *client = info->client; + int ret; + u8 bidir_reg; + bool revision_bug = false; + + if (info->chip_revision == 2 || info->chip_revision == 3) { + /* + * chip rev 2 and 3 bug workaround + * see: PAC1934 Family Data Sheet Errata DS80000836A.pdf + */ + revision_bug = true; + + bidir_reg = + FIELD_PREP(PAC1934_NEG_PWR_CH1_BIDI_MASK, info->bi_dir[0]) | + FIELD_PREP(PAC1934_NEG_PWR_CH2_BIDI_MASK, info->bi_dir[1]) | + FIELD_PREP(PAC1934_NEG_PWR_CH3_BIDI_MASK, info->bi_dir[2]) | + FIELD_PREP(PAC1934_NEG_PWR_CH4_BIDI_MASK, info->bi_dir[3]) | + FIELD_PREP(PAC1934_NEG_PWR_CH1_BIDV_MASK, info->bi_dir[0]) | + FIELD_PREP(PAC1934_NEG_PWR_CH2_BIDV_MASK, info->bi_dir[1]) | + FIELD_PREP(PAC1934_NEG_PWR_CH3_BIDV_MASK, info->bi_dir[2]) | + FIELD_PREP(PAC1934_NEG_PWR_CH4_BIDV_MASK, info->bi_dir[3]); + + ret = i2c_smbus_write_byte_data(client, + PAC1934_CTRL_STAT_REGS_ADDR + + PAC1934_NEG_PWR_REG_OFF, + bidir_reg); + if (ret) + return ret; + } + + ret = i2c_smbus_write_byte(client, refresh_cmd); + if (ret) { + dev_err(&client->dev, "%s - cannot send 0x%02X\n", + __func__, refresh_cmd); + return ret; + } + + if (revision_bug) { + /* + * chip rev 2 and 3 bug workaround - write again the same + * register write the updated registers back + */ + ret = i2c_smbus_write_byte_data(client, + PAC1934_CTRL_STAT_REGS_ADDR + + PAC1934_NEG_PWR_REG_OFF, bidir_reg); + if (ret) + return ret; + } + + /* register data retrieval timestamp */ + info->tstamp = jiffies; + + /* wait till the data is available */ + usleep_range(wait_time, wait_time + 100); + + return ret; +} + +static int pac1934_reg_snapshot(struct pac1934_chip_info *info, + bool do_refresh, u8 refresh_cmd, u32 wait_time) +{ + int ret; + struct i2c_client *client = info->client; + u8 samp_shift, ctrl_regs_tmp; + u8 *offset_reg_data_p; + u16 tmp_value; + u32 samp_rate, cnt, tmp; + s64 curr_energy, inc; + u64 tmp_energy; + struct reg_data *reg_data; + + guard(mutex)(&info->lock); + + if (do_refresh) { + ret = pac1934_send_refresh(info, refresh_cmd, wait_time); + if (ret < 0) { + dev_err(&client->dev, + "%s - cannot send refresh\n", + __func__); + return ret; + } + } + + ret = i2c_smbus_read_i2c_block_data(client, PAC1934_CTRL_STAT_REGS_ADDR, + PAC1934_CTRL_REG_LEN, + (u8 *)info->chip_reg_data.ctrl_regs); + if (ret < 0) { + dev_err(&client->dev, + "%s - cannot read ctrl/status registers\n", + __func__); + return ret; + } + + reg_data = &info->chip_reg_data; + + /* read the data registers */ + ret = pac1934_i2c_read(client, PAC1934_ACC_COUNT_REG_ADDR, + (u8 *)reg_data->meas_regs, PAC1934_MEAS_REG_LEN); + if (ret) { + dev_err(&client->dev, + "%s - cannot read ACC_COUNT register: %d:%d\n", + __func__, ret, PAC1934_MEAS_REG_LEN); + return ret; + } + + /* see how much shift is required by the sample rate */ + samp_rate = samp_rate_map_tbl[((reg_data->ctrl_regs[PAC1934_CTRL_LAT_REG_OFF]) >> 6)]; + samp_shift = get_count_order(samp_rate); + + ctrl_regs_tmp = reg_data->ctrl_regs[PAC1934_CHANNEL_DIS_LAT_REG_OFF]; + offset_reg_data_p = ®_data->meas_regs[PAC1934_ACC_REG_LEN]; + + /* start with VPOWER_ACC */ + for (cnt = 0; cnt < info->phys_channels; cnt++) { + /* check if the channel is active, skip all fields if disabled */ + if ((ctrl_regs_tmp << cnt) & 0x80) + continue; + + /* skip if the energy accumulation is disabled */ + if (info->enable_energy[cnt]) { + curr_energy = info->chip_reg_data.energy_sec_acc[cnt]; + + tmp_energy = get_unaligned_be48(offset_reg_data_p); + + if (info->bi_dir[cnt]) + reg_data->vpower_acc[cnt] = sign_extend64(tmp_energy, 47); + else + reg_data->vpower_acc[cnt] = tmp_energy; + + /* + * compute the scaled to 1 second accumulated energy value; + * energy accumulator scaled to 1sec = VPOWER_ACC/2^samp_shift + * the chip's sampling rate is 2^samp_shift samples/sec + */ + inc = (reg_data->vpower_acc[cnt] >> samp_shift); + + /* add the power_acc field */ + curr_energy += inc; + + clamp(curr_energy, PAC_193X_MIN_POWER_ACC, PAC_193X_MAX_POWER_ACC); + + reg_data->energy_sec_acc[cnt] = curr_energy; + } + + offset_reg_data_p += PAC1934_VPOWER_ACC_REG_LEN; + } + + /* continue with VBUS */ + for (cnt = 0; cnt < info->phys_channels; cnt++) { + if ((ctrl_regs_tmp << cnt) & 0x80) + continue; + + tmp_value = get_unaligned_be16(offset_reg_data_p); + + if (info->bi_dir[cnt]) + reg_data->vbus[cnt] = sign_extend32((u32)(tmp_value), 15); + else + reg_data->vbus[cnt] = tmp_value; + + offset_reg_data_p += PAC1934_VBUS_SENSE_REG_LEN; + } + + /* VSENSE */ + for (cnt = 0; cnt < info->phys_channels; cnt++) { + if ((ctrl_regs_tmp << cnt) & 0x80) + continue; + + tmp_value = get_unaligned_be16(offset_reg_data_p); + + if (info->bi_dir[cnt]) + reg_data->vsense[cnt] = sign_extend32((u32)(tmp_value), 15); + else + reg_data->vsense[cnt] = tmp_value; + + offset_reg_data_p += PAC1934_VBUS_SENSE_REG_LEN; + } + + /* VBUS_AVG */ + for (cnt = 0; cnt < info->phys_channels; cnt++) { + if ((ctrl_regs_tmp << cnt) & 0x80) + continue; + + tmp_value = get_unaligned_be16(offset_reg_data_p); + + if (info->bi_dir[cnt]) + reg_data->vbus_avg[cnt] = sign_extend32((u32)(tmp_value), 15); + else + reg_data->vbus_avg[cnt] = tmp_value; + + offset_reg_data_p += PAC1934_VBUS_SENSE_REG_LEN; + } + + /* VSENSE_AVG */ + for (cnt = 0; cnt < info->phys_channels; cnt++) { + if ((ctrl_regs_tmp << cnt) & 0x80) + continue; + + tmp_value = get_unaligned_be16(offset_reg_data_p); + + if (info->bi_dir[cnt]) + reg_data->vsense_avg[cnt] = sign_extend32((u32)(tmp_value), 15); + else + reg_data->vsense_avg[cnt] = tmp_value; + + offset_reg_data_p += PAC1934_VBUS_SENSE_REG_LEN; + } + + /* VPOWER */ + for (cnt = 0; cnt < info->phys_channels; cnt++) { + if ((ctrl_regs_tmp << cnt) & 0x80) + continue; + + tmp = get_unaligned_be32(offset_reg_data_p) >> 4; + + if (info->bi_dir[cnt]) + reg_data->vpower[cnt] = sign_extend32(tmp, 27); + else + reg_data->vpower[cnt] = tmp; + + offset_reg_data_p += PAC1934_VPOWER_REG_LEN; + } + + return 0; +} + +static int pac1934_retrieve_data(struct pac1934_chip_info *info, + u32 wait_time) +{ + int ret = 0; + + /* + * check if the minimal elapsed time has passed and if so, + * re-read the chip, otherwise the cached info is just fine + */ + if (time_after(jiffies, info->tstamp + msecs_to_jiffies(PAC1934_MIN_POLLING_TIME_MS))) { + ret = pac1934_reg_snapshot(info, true, PAC1934_REFRESH_REG_ADDR, + wait_time); + + /* + * Re-schedule the work for the read registers on timeout + * (to prevent chip registers saturation) + */ + mod_delayed_work(system_wq, &info->work_chip_rfsh, + msecs_to_jiffies(PAC1934_MAX_RFSH_LIMIT_MS)); + } + + return ret; +} + +static int pac1934_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct pac1934_chip_info *info = iio_priv(indio_dev); + s64 curr_energy; + int ret, channel = chan->channel - 1; + + /* + * For AVG the index should be between 5 to 8. + * To calculate PAC1934_CH_VOLTAGE_AVERAGE, + * respectively PAC1934_CH_CURRENT real index, we need + * to remove the added offset (PAC1934_MAX_NUM_CHANNELS). + */ + if (channel >= PAC1934_MAX_NUM_CHANNELS) + channel = channel - PAC1934_MAX_NUM_CHANNELS; + + ret = pac1934_retrieve_data(info, PAC1934_MIN_UPDATE_WAIT_TIME_US); + if (ret < 0) + return ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_VOLTAGE: + *val = info->chip_reg_data.vbus[channel]; + return IIO_VAL_INT; + case IIO_CURRENT: + *val = info->chip_reg_data.vsense[channel]; + return IIO_VAL_INT; + case IIO_POWER: + *val = info->chip_reg_data.vpower[channel]; + return IIO_VAL_INT; + case IIO_ENERGY: + curr_energy = info->chip_reg_data.energy_sec_acc[channel]; + *val = (u32)curr_energy; + *val2 = (u32)(curr_energy >> 32); + return IIO_VAL_INT_64; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_AVERAGE_RAW: + switch (chan->type) { + case IIO_VOLTAGE: + *val = info->chip_reg_data.vbus_avg[channel]; + return IIO_VAL_INT; + case IIO_CURRENT: + *val = info->chip_reg_data.vsense_avg[channel]; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->address) { + /* Voltages - scale for millivolts */ + case PAC1934_VBUS_1_ADDR: + case PAC1934_VBUS_2_ADDR: + case PAC1934_VBUS_3_ADDR: + case PAC1934_VBUS_4_ADDR: + case PAC1934_VBUS_AVG_1_ADDR: + case PAC1934_VBUS_AVG_2_ADDR: + case PAC1934_VBUS_AVG_3_ADDR: + case PAC1934_VBUS_AVG_4_ADDR: + *val = PAC1934_VOLTAGE_MILLIVOLTS_MAX; + if (chan->scan_type.sign == 'u') + *val2 = PAC1934_VOLTAGE_U_RES; + else + *val2 = PAC1934_VOLTAGE_S_RES; + return IIO_VAL_FRACTIONAL_LOG2; + /* + * Currents - scale for mA - depends on the + * channel's shunt value + * (100mV * 1000000) / (2^16 * shunt(uohm)) + */ + case PAC1934_VSENSE_1_ADDR: + case PAC1934_VSENSE_2_ADDR: + case PAC1934_VSENSE_3_ADDR: + case PAC1934_VSENSE_4_ADDR: + case PAC1934_VSENSE_AVG_1_ADDR: + case PAC1934_VSENSE_AVG_2_ADDR: + case PAC1934_VSENSE_AVG_3_ADDR: + case PAC1934_VSENSE_AVG_4_ADDR: + *val = PAC1934_MAX_VSENSE_RSHIFTED_BY_16B; + if (chan->scan_type.sign == 'u') + *val2 = info->shunts[channel]; + else + *val2 = info->shunts[channel] >> 1; + return IIO_VAL_FRACTIONAL; + /* + * Power - uW - it will use the combined scale + * for current and voltage + * current(mA) * voltage(mV) = power (uW) + */ + case PAC1934_VPOWER_1_ADDR: + case PAC1934_VPOWER_2_ADDR: + case PAC1934_VPOWER_3_ADDR: + case PAC1934_VPOWER_4_ADDR: + *val = PAC1934_MAX_VPOWER_RSHIFTED_BY_28B; + if (chan->scan_type.sign == 'u') + *val2 = info->shunts[channel]; + else + *val2 = info->shunts[channel] >> 1; + return IIO_VAL_FRACTIONAL; + case PAC1934_VPOWER_ACC_1_ADDR: + case PAC1934_VPOWER_ACC_2_ADDR: + case PAC1934_VPOWER_ACC_3_ADDR: + case PAC1934_VPOWER_ACC_4_ADDR: + /* + * expresses the 32 bit scale value here compute + * the scale for energy (miliWatt-second or miliJoule) + */ + *val = PAC1934_SCALE_CONSTANT; + + if (chan->scan_type.sign == 'u') + *val2 = info->shunts[channel]; + else + *val2 = info->shunts[channel] >> 1; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: + *val = info->sample_rate_value; + return IIO_VAL_INT; + case IIO_CHAN_INFO_ENABLE: + *val = info->enable_energy[channel]; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int pac1934_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct pac1934_chip_info *info = iio_priv(indio_dev); + struct i2c_client *client = info->client; + int ret = -EINVAL; + s32 old_samp_rate; + u8 ctrl_reg; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + ret = pac1934_get_samp_rate_idx(info, val); + if (ret < 0) + return ret; + + /* write the new sampling value and trigger a snapshot(incl refresh) */ + scoped_guard(mutex, &info->lock) { + ctrl_reg = FIELD_PREP(PAC1934_CRTL_SAMPLE_RATE_MASK, ret); + ret = i2c_smbus_write_byte_data(client, PAC1934_CTRL_REG_ADDR, ctrl_reg); + if (ret) { + dev_err(&client->dev, + "%s - can't update sample rate\n", + __func__); + return ret; + } + } + + old_samp_rate = info->sample_rate_value; + info->sample_rate_value = val; + + /* + * now, force a snapshot with refresh - call retrieve + * data in order to update the refresh timer + * alter the timestamp in order to force trigger a + * register snapshot and a timestamp update + */ + info->tstamp -= msecs_to_jiffies(PAC1934_MIN_POLLING_TIME_MS); + ret = pac1934_retrieve_data(info, (1024 / old_samp_rate) * 1000); + if (ret < 0) { + dev_err(&client->dev, + "%s - cannot snapshot ctrl and measurement regs\n", + __func__); + return ret; + } + + return 0; + case IIO_CHAN_INFO_ENABLE: + scoped_guard(mutex, &info->lock) { + info->enable_energy[chan->channel - 1] = val ? true : false; + if (!val) + info->chip_reg_data.energy_sec_acc[chan->channel - 1] = 0; + } + + return 0; + default: + return -EINVAL; + } +} + +static int pac1934_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, char *label) +{ + struct pac1934_chip_info *info = iio_priv(indio_dev); + + switch (chan->address) { + case PAC1934_VBUS_1_ADDR: + case PAC1934_VBUS_2_ADDR: + case PAC1934_VBUS_3_ADDR: + case PAC1934_VBUS_4_ADDR: + return sysfs_emit(label, "%s_VBUS_%d\n", + info->labels[chan->scan_index], + chan->scan_index + 1); + case PAC1934_VBUS_AVG_1_ADDR: + case PAC1934_VBUS_AVG_2_ADDR: + case PAC1934_VBUS_AVG_3_ADDR: + case PAC1934_VBUS_AVG_4_ADDR: + return sysfs_emit(label, "%s_VBUS_AVG_%d\n", + info->labels[chan->scan_index], + chan->scan_index + 1); + case PAC1934_VSENSE_1_ADDR: + case PAC1934_VSENSE_2_ADDR: + case PAC1934_VSENSE_3_ADDR: + case PAC1934_VSENSE_4_ADDR: + return sysfs_emit(label, "%s_IBUS_%d\n", + info->labels[chan->scan_index], + chan->scan_index + 1); + case PAC1934_VSENSE_AVG_1_ADDR: + case PAC1934_VSENSE_AVG_2_ADDR: + case PAC1934_VSENSE_AVG_3_ADDR: + case PAC1934_VSENSE_AVG_4_ADDR: + return sysfs_emit(label, "%s_IBUS_AVG_%d\n", + info->labels[chan->scan_index], + chan->scan_index + 1); + case PAC1934_VPOWER_1_ADDR: + case PAC1934_VPOWER_2_ADDR: + case PAC1934_VPOWER_3_ADDR: + case PAC1934_VPOWER_4_ADDR: + return sysfs_emit(label, "%s_POWER_%d\n", + info->labels[chan->scan_index], + chan->scan_index + 1); + case PAC1934_VPOWER_ACC_1_ADDR: + case PAC1934_VPOWER_ACC_2_ADDR: + case PAC1934_VPOWER_ACC_3_ADDR: + case PAC1934_VPOWER_ACC_4_ADDR: + return sysfs_emit(label, "%s_ENERGY_%d\n", + info->labels[chan->scan_index], + chan->scan_index + 1); + } + + return 0; +} + +static void pac1934_work_periodic_rfsh(struct work_struct *work) +{ + struct pac1934_chip_info *info = TO_PAC1934_CHIP_INFO((struct delayed_work *)work); + struct device *dev = &info->client->dev; + + dev_dbg(dev, "%s - Periodic refresh\n", __func__); + + /* do a REFRESH, then read */ + pac1934_reg_snapshot(info, true, PAC1934_REFRESH_REG_ADDR, + PAC1934_MIN_UPDATE_WAIT_TIME_US); + + schedule_delayed_work(&info->work_chip_rfsh, + msecs_to_jiffies(PAC1934_MAX_RFSH_LIMIT_MS)); +} + +static int pac1934_read_revision(struct pac1934_chip_info *info, u8 *buf) +{ + int ret; + struct i2c_client *client = info->client; + + ret = i2c_smbus_read_i2c_block_data(client, PAC1934_PID_REG_ADDR, + PAC1934_ID_REG_LEN, + buf); + if (ret < 0) { + dev_err(&client->dev, "cannot read revision\n"); + return ret; + } + + return 0; +} + +static int pac1934_chip_identify(struct pac1934_chip_info *info) +{ + u8 rev_info[PAC1934_ID_REG_LEN]; + struct device *dev = &info->client->dev; + int ret = 0; + + ret = pac1934_read_revision(info, (u8 *)rev_info); + if (ret) + return ret; + + info->chip_variant = rev_info[PAC1934_PID_IDX]; + info->chip_revision = rev_info[PAC1934_RID_IDX]; + + dev_dbg(dev, "Chip variant: 0x%02X\n", info->chip_variant); + dev_dbg(dev, "Chip revision: 0x%02X\n", info->chip_revision); + + switch (info->chip_variant) { + case PAC1934_PID: + return PAC1934; + case PAC1933_PID: + return PAC1933; + case PAC1932_PID: + return PAC1932; + case PAC1931_PID: + return PAC1931; + default: + return -EINVAL; + } +} + +/* + * documentation related to the ACPI device definition + * https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ApplicationNotes/ApplicationNotes/PAC1934-Integration-Notes-for-Microsoft-Windows-10-and-Windows-11-Driver-Support-DS00002534.pdf + */ +static bool pac1934_acpi_parse_channel_config(struct i2c_client *client, + struct pac1934_chip_info *info) +{ + acpi_handle handle; + union acpi_object *rez; + struct device *dev = &client->dev; + unsigned short bi_dir_mask; + int idx, i; + guid_t guid; + + handle = ACPI_HANDLE(dev); + + guid_parse(PAC1934_DSM_UUID, &guid); + + rez = acpi_evaluate_dsm(handle, &guid, 0, PAC1934_ACPI_GET_NAMES_AND_MOHMS_VALS, NULL); + if (!rez) + return false; + + for (i = 0; i < rez->package.count; i += 2) { + idx = i / 2; + info->labels[idx] = + devm_kmemdup(dev, rez->package.elements[i].string.pointer, + (size_t)rez->package.elements[i].string.length + 1, + GFP_KERNEL); + info->labels[idx][rez->package.elements[i].string.length] = '\0'; + info->shunts[idx] = rez->package.elements[i + 1].integer.value * 1000; + info->active_channels[idx] = (info->shunts[idx] != 0); + } + + ACPI_FREE(rez); + + rez = acpi_evaluate_dsm(handle, &guid, 1, PAC1934_ACPI_GET_UOHMS_VALS, NULL); + if (!rez) { + /* + * initializing with default values + * we assume all channels are unidirectional(the mask is zero) + * and assign the default sampling rate + */ + info->sample_rate_value = PAC1934_DEFAULT_CHIP_SAMP_SPEED_HZ; + return true; + } + + for (i = 0; i < rez->package.count; i++) { + idx = i; + info->shunts[idx] = rez->package.elements[i].integer.value; + info->active_channels[idx] = (info->shunts[idx] != 0); + } + + ACPI_FREE(rez); + + rez = acpi_evaluate_dsm(handle, &guid, 1, PAC1934_ACPI_GET_BIPOLAR_SETTINGS, NULL); + if (!rez) + return false; + + bi_dir_mask = rez->package.elements[0].integer.value; + info->bi_dir[0] = ((bi_dir_mask & (1 << 3)) | (bi_dir_mask & (1 << 7))) != 0; + info->bi_dir[1] = ((bi_dir_mask & (1 << 2)) | (bi_dir_mask & (1 << 6))) != 0; + info->bi_dir[2] = ((bi_dir_mask & (1 << 1)) | (bi_dir_mask & (1 << 5))) != 0; + info->bi_dir[3] = ((bi_dir_mask & (1 << 0)) | (bi_dir_mask & (1 << 4))) != 0; + + ACPI_FREE(rez); + + rez = acpi_evaluate_dsm(handle, &guid, 1, PAC1934_ACPI_GET_SAMP, NULL); + if (!rez) + return false; + + info->sample_rate_value = rez->package.elements[0].integer.value; + + ACPI_FREE(rez); + + return true; +} + +static bool pac1934_of_parse_channel_config(struct i2c_client *client, + struct pac1934_chip_info *info) +{ + struct fwnode_handle *node, *fwnode; + struct device *dev = &client->dev; + unsigned int current_channel; + int idx, ret; + + info->sample_rate_value = 1024; + current_channel = 1; + + fwnode = dev_fwnode(dev); + fwnode_for_each_available_child_node(fwnode, node) { + ret = fwnode_property_read_u32(node, "reg", &idx); + if (ret) { + dev_err_probe(dev, ret, + "reading invalid channel index\n"); + goto err_fwnode; + } + /* adjust idx to match channel index (1 to 4) from the datasheet */ + idx--; + + if (current_channel >= (info->phys_channels + 1) || + idx >= info->phys_channels || idx < 0) { + dev_err_probe(dev, -EINVAL, + "%s: invalid channel_index %d value\n", + fwnode_get_name(node), idx); + goto err_fwnode; + } + + /* enable channel */ + info->active_channels[idx] = true; + + ret = fwnode_property_read_u32(node, "shunt-resistor-micro-ohms", + &info->shunts[idx]); + if (ret) { + dev_err_probe(dev, ret, + "%s: invalid shunt-resistor value: %d\n", + fwnode_get_name(node), info->shunts[idx]); + goto err_fwnode; + } + + if (fwnode_property_present(node, "label")) { + ret = fwnode_property_read_string(node, "label", + (const char **)&info->labels[idx]); + if (ret) { + dev_err_probe(dev, ret, + "%s: invalid rail-name value\n", + fwnode_get_name(node)); + goto err_fwnode; + } + } + + info->bi_dir[idx] = fwnode_property_read_bool(node, "bipolar"); + + current_channel++; + } + + return true; + +err_fwnode: + fwnode_handle_put(node); + + return false; +} + +static void pac1934_cancel_delayed_work(void *dwork) +{ + cancel_delayed_work_sync(dwork); +} + +static int pac1934_chip_configure(struct pac1934_chip_info *info) +{ + int cnt, ret; + struct i2c_client *client = info->client; + u8 regs[PAC1934_CTRL_STATUS_INFO_LEN], idx, ctrl_reg; + u32 wait_time; + + info->chip_reg_data.num_enabled_channels = 0; + for (cnt = 0; cnt < info->phys_channels; cnt++) { + if (info->active_channels[cnt]) + info->chip_reg_data.num_enabled_channels++; + } + + /* + * read whatever information was gathered before the driver was loaded + * establish which channels are enabled/disabled and then establish the + * information retrieval mode (using SKIP or no). + * Read the chip ID values + */ + ret = i2c_smbus_read_i2c_block_data(client, PAC1934_CTRL_STAT_REGS_ADDR, + ARRAY_SIZE(regs), + (u8 *)regs); + if (ret < 0) { + dev_err_probe(&client->dev, ret, + "%s - cannot read regs from 0x%02X\n", + __func__, PAC1934_CTRL_STAT_REGS_ADDR); + return ret; + } + + /* write the CHANNEL_DIS and the NEG_PWR registers */ + regs[PAC1934_CHANNEL_DIS_REG_OFF] = + FIELD_PREP(PAC1934_CHAN_DIS_CH1_OFF_MASK, info->active_channels[0] ? 0 : 1) | + FIELD_PREP(PAC1934_CHAN_DIS_CH2_OFF_MASK, info->active_channels[1] ? 0 : 1) | + FIELD_PREP(PAC1934_CHAN_DIS_CH3_OFF_MASK, info->active_channels[2] ? 0 : 1) | + FIELD_PREP(PAC1934_CHAN_DIS_CH4_OFF_MASK, info->active_channels[3] ? 0 : 1) | + FIELD_PREP(PAC1934_SMBUS_TIMEOUT_MASK, 0) | + FIELD_PREP(PAC1934_SMBUS_BYTECOUNT_MASK, 0) | + FIELD_PREP(PAC1934_SMBUS_NO_SKIP_MASK, 0); + + regs[PAC1934_NEG_PWR_REG_OFF] = + FIELD_PREP(PAC1934_NEG_PWR_CH1_BIDI_MASK, info->bi_dir[0]) | + FIELD_PREP(PAC1934_NEG_PWR_CH2_BIDI_MASK, info->bi_dir[1]) | + FIELD_PREP(PAC1934_NEG_PWR_CH3_BIDI_MASK, info->bi_dir[2]) | + FIELD_PREP(PAC1934_NEG_PWR_CH4_BIDI_MASK, info->bi_dir[3]) | + FIELD_PREP(PAC1934_NEG_PWR_CH1_BIDV_MASK, info->bi_dir[0]) | + FIELD_PREP(PAC1934_NEG_PWR_CH2_BIDV_MASK, info->bi_dir[1]) | + FIELD_PREP(PAC1934_NEG_PWR_CH3_BIDV_MASK, info->bi_dir[2]) | + FIELD_PREP(PAC1934_NEG_PWR_CH4_BIDV_MASK, info->bi_dir[3]); + + /* no SLOW triggered REFRESH, clear POR */ + regs[PAC1934_SLOW_REG_OFF] = 0; + + ret = i2c_smbus_write_block_data(client, PAC1934_CTRL_STAT_REGS_ADDR, + ARRAY_SIZE(regs), (u8 *)regs); + if (ret) + return ret; + + /* Default sampling rate */ + ctrl_reg = FIELD_PREP(PAC1934_CRTL_SAMPLE_RATE_MASK, PAC1934_SAMP_1024SPS); + + ret = i2c_smbus_write_byte_data(client, PAC1934_CTRL_REG_ADDR, ctrl_reg); + if (ret) + return ret; + + /* + * send a REFRESH to the chip, so the new settings take place + * as well as resetting the accumulators + */ + ret = i2c_smbus_write_byte(client, PAC1934_REFRESH_REG_ADDR); + if (ret) { + dev_err(&client->dev, + "%s - cannot send 0x%02X\n", + __func__, PAC1934_REFRESH_REG_ADDR); + return ret; + } + + /* + * get the current(in the chip) sampling speed and compute the + * required timeout based on its value + * the timeout is 1/sampling_speed + */ + idx = regs[PAC1934_CTRL_ACT_REG_OFF] >> PAC1934_SAMPLE_RATE_SHIFT; + wait_time = (1024 / samp_rate_map_tbl[idx]) * 1000; + + /* + * wait the maximum amount of time to be on the safe side + * the maximum wait time is for 8sps + */ + usleep_range(wait_time, wait_time + 100); + + INIT_DELAYED_WORK(&info->work_chip_rfsh, pac1934_work_periodic_rfsh); + /* Setup the latest moment for reading the regs before saturation */ + schedule_delayed_work(&info->work_chip_rfsh, + msecs_to_jiffies(PAC1934_MAX_RFSH_LIMIT_MS)); + + return devm_add_action_or_reset(&client->dev, pac1934_cancel_delayed_work, + &info->work_chip_rfsh); +} + +static int pac1934_prep_iio_channels(struct pac1934_chip_info *info, struct iio_dev *indio_dev) +{ + struct iio_chan_spec *ch_sp; + int channel_size, attribute_count, cnt; + void *dyn_ch_struct, *tmp_data; + struct device *dev = &info->client->dev; + + /* find out dynamically how many IIO channels we need */ + attribute_count = 0; + channel_size = 0; + for (cnt = 0; cnt < info->phys_channels; cnt++) { + if (!info->active_channels[cnt]) + continue; + + /* add the size of the properties of one chip physical channel */ + channel_size += sizeof(pac1934_single_channel); + /* count how many enabled channels we have */ + attribute_count += ARRAY_SIZE(pac1934_single_channel); + dev_info(dev, ":%s: Channel %d active\n", + __func__, cnt + 1); + } + + dyn_ch_struct = devm_kzalloc(dev, channel_size, GFP_KERNEL); + if (!dyn_ch_struct) + return -EINVAL; + + tmp_data = dyn_ch_struct; + + /* populate the dynamic channels and make all the adjustments */ + for (cnt = 0; cnt < info->phys_channels; cnt++) { + if (!info->active_channels[cnt]) + continue; + + memcpy(tmp_data, pac1934_single_channel, sizeof(pac1934_single_channel)); + ch_sp = (struct iio_chan_spec *)tmp_data; + ch_sp[PAC1934_CH_ENERGY].channel = cnt + 1; + ch_sp[PAC1934_CH_ENERGY].scan_index = cnt; + ch_sp[PAC1934_CH_ENERGY].address = cnt + PAC1934_VPOWER_ACC_1_ADDR; + ch_sp[PAC1934_CH_POWER].channel = cnt + 1; + ch_sp[PAC1934_CH_POWER].scan_index = cnt; + ch_sp[PAC1934_CH_POWER].address = cnt + PAC1934_VPOWER_1_ADDR; + ch_sp[PAC1934_CH_VOLTAGE].channel = cnt + 1; + ch_sp[PAC1934_CH_VOLTAGE].scan_index = cnt; + ch_sp[PAC1934_CH_VOLTAGE].address = cnt + PAC1934_VBUS_1_ADDR; + ch_sp[PAC1934_CH_CURRENT].channel = cnt + 1; + ch_sp[PAC1934_CH_CURRENT].scan_index = cnt; + ch_sp[PAC1934_CH_CURRENT].address = cnt + PAC1934_VSENSE_1_ADDR; + + /* + * In order to be able to use labels for PAC1934_CH_VOLTAGE, and + * PAC1934_CH_VOLTAGE_AVERAGE,respectively PAC1934_CH_CURRENT + * and PAC1934_CH_CURRENT_AVERAGE we need to use different + * channel numbers. We will add +5 (+1 to maximum PAC channels). + */ + ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].channel = cnt + 5; + ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].scan_index = cnt; + ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].address = cnt + PAC1934_VBUS_AVG_1_ADDR; + ch_sp[PAC1934_CH_CURRENT_AVERAGE].channel = cnt + 5; + ch_sp[PAC1934_CH_CURRENT_AVERAGE].scan_index = cnt; + ch_sp[PAC1934_CH_CURRENT_AVERAGE].address = cnt + PAC1934_VSENSE_AVG_1_ADDR; + + /* + * now modify the parameters in all channels if the + * whole chip rail(channel) is bi-directional + */ + if (info->bi_dir[cnt]) { + ch_sp[PAC1934_CH_ENERGY].scan_type.sign = 's'; + ch_sp[PAC1934_CH_ENERGY].scan_type.realbits = 47; + ch_sp[PAC1934_CH_POWER].scan_type.sign = 's'; + ch_sp[PAC1934_CH_POWER].scan_type.realbits = 27; + ch_sp[PAC1934_CH_VOLTAGE].scan_type.sign = 's'; + ch_sp[PAC1934_CH_VOLTAGE].scan_type.realbits = 15; + ch_sp[PAC1934_CH_CURRENT].scan_type.sign = 's'; + ch_sp[PAC1934_CH_CURRENT].scan_type.realbits = 15; + ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].scan_type.sign = 's'; + ch_sp[PAC1934_CH_VOLTAGE_AVERAGE].scan_type.realbits = 15; + ch_sp[PAC1934_CH_CURRENT_AVERAGE].scan_type.sign = 's'; + ch_sp[PAC1934_CH_CURRENT_AVERAGE].scan_type.realbits = 15; + } + tmp_data += sizeof(pac1934_single_channel); + } + + /* + * send the updated dynamic channel structure information towards IIO + * prepare the required field for IIO class registration + */ + indio_dev->num_channels = attribute_count; + indio_dev->channels = (const struct iio_chan_spec *)dyn_ch_struct; + + return 0; +} + +static IIO_DEVICE_ATTR(in_shunt_resistor1, 0644, + pac1934_shunt_value_show, pac1934_shunt_value_store, 0); +static IIO_DEVICE_ATTR(in_shunt_resistor2, 0644, + pac1934_shunt_value_show, pac1934_shunt_value_store, 1); +static IIO_DEVICE_ATTR(in_shunt_resistor3, 0644, + pac1934_shunt_value_show, pac1934_shunt_value_store, 2); +static IIO_DEVICE_ATTR(in_shunt_resistor4, 0644, + pac1934_shunt_value_show, pac1934_shunt_value_store, 3); + +static int pac1934_prep_custom_attributes(struct pac1934_chip_info *info, + struct iio_dev *indio_dev) +{ + int i, active_channels_count = 0; + struct attribute **pac1934_custom_attr; + struct attribute_group *pac1934_group; + struct device *dev = &info->client->dev; + + for (i = 0 ; i < info->phys_channels; i++) + if (info->active_channels[i]) + active_channels_count++; + + pac1934_group = devm_kzalloc(dev, sizeof(*pac1934_group), GFP_KERNEL); + if (!pac1934_group) + return -ENOMEM; + + pac1934_custom_attr = devm_kzalloc(dev, + (PAC1934_CUSTOM_ATTR_FOR_CHANNEL * + active_channels_count) + * sizeof(*pac1934_group) + 1, + GFP_KERNEL); + if (!pac1934_custom_attr) + return -ENOMEM; + + i = 0; + if (info->active_channels[0]) + pac1934_custom_attr[i++] = PAC1934_DEV_ATTR(in_shunt_resistor1); + + if (info->active_channels[1]) + pac1934_custom_attr[i++] = PAC1934_DEV_ATTR(in_shunt_resistor2); + + if (info->active_channels[2]) + pac1934_custom_attr[i++] = PAC1934_DEV_ATTR(in_shunt_resistor3); + + if (info->active_channels[3]) + pac1934_custom_attr[i] = PAC1934_DEV_ATTR(in_shunt_resistor4); + + pac1934_group->attrs = pac1934_custom_attr; + info->iio_info.attrs = pac1934_group; + + return 0; +} + +static void pac1934_mutex_destroy(void *data) +{ + struct mutex *lock = data; + + mutex_destroy(lock); +} + +static const struct iio_info pac1934_info = { + .read_raw = pac1934_read_raw, + .write_raw = pac1934_write_raw, + .read_avail = pac1934_read_avail, + .read_label = pac1934_read_label, +}; + +static int pac1934_probe(struct i2c_client *client) +{ + struct pac1934_chip_info *info; + const struct pac1934_features *chip; + struct iio_dev *indio_dev; + int cnt, ret; + bool match = false; + struct device *dev = &client->dev; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + + info->client = client; + + /* always start with energy accumulation enabled */ + for (cnt = 0; cnt < PAC1934_MAX_NUM_CHANNELS; cnt++) + info->enable_energy[cnt] = true; + + ret = pac1934_chip_identify(info); + if (ret < 0) { + /* + * If failed to identify the hardware based on internal + * registers, try using fallback compatible in device tree + * to deal with some newer part number. + */ + chip = i2c_get_match_data(client); + if (!chip) + return -EINVAL; + + info->phys_channels = chip->phys_channels; + indio_dev->name = chip->name; + } else { + info->phys_channels = pac1934_chip_config[ret].phys_channels; + indio_dev->name = pac1934_chip_config[ret].name; + } + + if (acpi_match_device(dev->driver->acpi_match_table, dev)) + match = pac1934_acpi_parse_channel_config(client, info); + else + /* + * This makes it possible to use also ACPI PRP0001 for + * registering the device using device tree properties. + */ + match = pac1934_of_parse_channel_config(client, info); + + if (!match) + return dev_err_probe(dev, -EINVAL, + "parameter parsing returned an error\n"); + + mutex_init(&info->lock); + ret = devm_add_action_or_reset(dev, pac1934_mutex_destroy, + &info->lock); + if (ret < 0) + return ret; + + /* + * do now any chip specific initialization (e.g. read/write + * some registers), enable/disable certain channels, change the sampling + * rate to the requested value + */ + ret = pac1934_chip_configure(info); + if (ret < 0) + return ret; + + /* prepare the channel information */ + ret = pac1934_prep_iio_channels(info, indio_dev); + if (ret < 0) + return ret; + + info->iio_info = pac1934_info; + indio_dev->info = &info->iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = pac1934_prep_custom_attributes(info, indio_dev); + if (ret < 0) + return dev_err_probe(dev, ret, + "Can't configure custom attributes for PAC1934 device\n"); + + /* + * read whatever has been accumulated in the chip so far + * and reset the accumulators + */ + ret = pac1934_reg_snapshot(info, true, PAC1934_REFRESH_REG_ADDR, + PAC1934_MIN_UPDATE_WAIT_TIME_US); + if (ret < 0) + return ret; + + ret = devm_iio_device_register(dev, indio_dev); + if (ret < 0) + return dev_err_probe(dev, ret, + "Can't register IIO device\n"); + + return 0; +} + +static const struct i2c_device_id pac1934_id[] = { + { .name = "pac1931", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1931] }, + { .name = "pac1932", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1932] }, + { .name = "pac1933", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1933] }, + { .name = "pac1934", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1934] }, + {} +}; +MODULE_DEVICE_TABLE(i2c, pac1934_id); + +static const struct of_device_id pac1934_of_match[] = { + { + .compatible = "microchip,pac1931", + .data = &pac1934_chip_config[PAC1931] + }, + { + .compatible = "microchip,pac1932", + .data = &pac1934_chip_config[PAC1932] + }, + { + .compatible = "microchip,pac1933", + .data = &pac1934_chip_config[PAC1933] + }, + { + .compatible = "microchip,pac1934", + .data = &pac1934_chip_config[PAC1934] + }, + {} +}; +MODULE_DEVICE_TABLE(of, pac1934_of_match); + +/* + * using MCHP1930 to be compatible with BIOS ACPI. See example: + * https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ApplicationNotes/ApplicationNotes/PAC1934-Integration-Notes-for-Microsoft-Windows-10-and-Windows-11-Driver-Support-DS00002534.pdf + */ +static const struct acpi_device_id pac1934_acpi_match[] = { + { "MCHP1930", .driver_data = (kernel_ulong_t)&pac1934_chip_config[PAC1934] }, + {} +}; +MODULE_DEVICE_TABLE(acpi, pac1934_acpi_match); + +static struct i2c_driver pac1934_driver = { + .driver = { + .name = "pac1934", + .of_match_table = pac1934_of_match, + .acpi_match_table = pac1934_acpi_match + }, + .probe = pac1934_probe, + .id_table = pac1934_id, +}; + +module_i2c_driver(pac1934_driver); + +MODULE_AUTHOR("Bogdan Bolocan <bogdan.bolocan@microchip.com>"); +MODULE_AUTHOR("Victor Tudose"); +MODULE_AUTHOR("Marius Cristea <marius.cristea@microchip.com>"); +MODULE_DESCRIPTION("IIO driver for PAC1934 Multi-Channel DC Power/Energy Monitor"); +MODULE_LICENSE("GPL"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/input/misc/atmel_ptc.c linux-microchip/drivers/input/misc/atmel_ptc.c --- linux-6.6.51/drivers/input/misc/atmel_ptc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/input/misc/atmel_ptc.c 2024-11-26 14:02:37.470493196 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Atmel PTC subsystem driver for SAMA5D2 devices and compatible. + * + * Copyright (C) 2017 Microchip, + * 2017 Ludovic Desroches <ludovic.desroches@microchip.com> + * + */ + +#include <linux/cdev.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/firmware.h> +#include <linux/gpio.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/uaccess.h> + +#define ATMEL_PTC_MAX_NODES 64 +#define ATMEL_PTC_MAX_SCROLLERS 4 +#define ATMEL_PTC_MAX_X_LINES 8 +#define ATMEL_PTC_MAX_Y_LINES 8 +#define ATMEL_PTC_KEYCODE_BASE_OFFSET 0x100 + +/* ----- PPP ----- */ +#define ATMEL_PPP_FW_NAME "microchip/ptc_fw.bin" +#define ATMEL_PPP_FW_FOOTER_SIZE 16 + +#define ATMEL_PPP_CONFIG 0x20 +#define ATMEL_PPP_CTRL 0x24 +#define ATMEL_PPP_CMD 0x28 +#define ATMEL_PPP_CMD_STOP 0x1 +#define ATMEL_PPP_CMD_RESET 0x2 +#define ATMEL_PPP_CMD_RESTART 0x3 +#define ATMEL_PPP_CMD_ABORT 0x4 +#define ATMEL_PPP_CMD_RUN 0x5 +#define ATMEL_PPP_CMD_RUN_LOCKED 0x6 +#define ATMEL_PPP_CMD_RUN_OCD 0x7 +#define ATMEL_PPP_CMD_UNLOCK 0x8 +#define ATMEL_PPP_CMD_NMI 0x9 +#define ATMEL_PPP_CMD_HOST_OCD_RESUME 0xB +#define ATMEL_PPP_ISR 0x33 +#define ATMEL_PPP_IRQ_MASK GENMASK(7, 4) +#define ATMEL_PPP_IRQ0 BIT(4) +#define ATMEL_PPP_IRQ1 BIT(5) +#define ATMEL_PPP_IRQ2 BIT(6) +#define ATMEL_PPP_IRQ3 BIT(7) +#define ATMEL_PPP_NOTIFY_MASK GENMASK(3, 0) +#define ATMEL_PPP_NOTIFY0 BIT(0) +#define ATMEL_PPP_NOTIFY1 BIT(1) +#define ATMEL_PPP_NOTIFY2 BIT(2) +#define ATMEL_PPP_NOTIFY3 BIT(3) +#define ATMEL_PPP_IDR 0x34 +#define ATMEL_PPP_IER 0x35 + +#define atmel_ppp_readb(ptc, reg) readb_relaxed((ptc)->ppp_regs + (reg)) +#define atmel_ppp_writeb(ptc, reg, val) writeb_relaxed(val, (ptc)->ppp_regs + (reg)) +#define atmel_ppp_readl(ptc, reg) readl_relaxed((ptc)->ppp_regs + (reg)) +#define atmel_ppp_writel(ptc, reg, val) writel_relaxed(val, (ptc)->ppp_regs + (reg)) + +/* ----- QTM ----- */ +#define ATMEL_QTM_CONF_NAME "microchip/ptc_cfg.bin" + +#define ATMEL_QTM_MB_OFFSET 0x4000 +#define ATMEL_QTM_MB_SIZE 0x1000 + +#define ATMEL_QTM_CMD_FIRM_VERSION 8 +#define ATMEL_QTM_CMD_INIT 18 +#define ATMEL_QTM_CMD_RUN 19 +#define ATMEL_QTM_CMD_STOP 21 +#define ATMEL_QTM_CMD_SET_ACQ_MODE_TIMER 24 +#define ATMEL_QTM_SCROLLER_TYPE_SLIDER 0x0 +#define ATMEL_QTM_SCROLLER_TYPE_WHEEL 0x1 + +static char *firmware_file = ATMEL_PPP_FW_NAME; +static char *configuration_file = ATMEL_QTM_CONF_NAME; +static bool debug_mode; + +struct atmel_qtm_conf_header { + u8 header_version_major; + u8 header_version_minor; + u32 header_size; + char *fw_version; + char *tool_version; + char *date; + char *description; +}; + +/* Depends on firmware version */ +struct atmel_qtm_mailbox_map { + unsigned int cmd_offset; + unsigned int cmd_id_offset; + unsigned int cmd_addr_offset; + unsigned int cmd_data_offset; + unsigned int node_group_config_offset; + unsigned int node_group_config_count_offset; + unsigned int node_config_offset; + unsigned int node_config_size; + unsigned int node_config_mask_x_offset; + unsigned int node_config_mask_y_offset; + unsigned int scroller_group_config_offset; + unsigned int scroller_group_config_count_offset; + unsigned int scroller_config_offset; + unsigned int scroller_config_size; + unsigned int scroller_config_type_offset; + unsigned int scroller_config_key_start_offset; + unsigned int scroller_config_key_count_offset; + unsigned int scroller_config_resol_deadband_offset; + unsigned int scroller_data_offset; + unsigned int scroller_data_size; + unsigned int scroller_data_status_offset; + unsigned int scroller_data_position_offset; + unsigned int touch_events_key_event_id; + unsigned int touch_events_key_enable_state; + unsigned int touch_events_scroller_event_id; +}; + +static struct atmel_qtm_mailbox_map mailbox_map_v64 = { + .cmd_offset = 0x0, + .cmd_id_offset = 0, + .cmd_addr_offset = 2, + .cmd_data_offset = 4, + .node_group_config_offset = 0x100, + .node_group_config_count_offset = 0, + .node_config_offset = 0x106, + .node_config_size = 12, + .node_config_mask_x_offset = 0, + .node_config_mask_y_offset = 2, + .scroller_group_config_offset = 0x816, + .scroller_group_config_count_offset = 2, + .scroller_config_offset = 0x81a, + .scroller_config_size = 10, + .scroller_config_type_offset = 0, + .scroller_config_key_start_offset = 2, + .scroller_config_key_count_offset = 4, + .scroller_config_resol_deadband_offset = 5, + .scroller_data_offset = 0x842, + .scroller_data_size = 10, + .scroller_data_status_offset = 0, + .scroller_data_position_offset = 6, + .touch_events_key_event_id = 0x880, + .touch_events_key_enable_state = 0x888, + .touch_events_scroller_event_id = 0x890, +}; + +struct atmel_qtm_cmd { + u16 id; + u16 addr; + u32 data; +}; + +struct atmel_ptc_pin { + const char *name; + unsigned int id; +}; + +struct atmel_ptc_pins { + struct atmel_ptc_pin x_lines[ATMEL_PTC_MAX_X_LINES]; + struct atmel_ptc_pin y_lines[ATMEL_PTC_MAX_Y_LINES]; +}; + +struct atmel_ptc { + void __iomem *ppp_regs; + void __iomem *firmware; + void __iomem *qtm_mb; + struct clk *clk_per; + struct clk *clk_int_osc; + struct clk *clk_slow; + struct device *dev; + struct input_dev *buttons_input; + struct input_dev *scroller_input[ATMEL_PTC_MAX_SCROLLERS]; + struct atmel_qtm_conf_header conf; + struct atmel_qtm_mailbox_map *mb_map; + const struct atmel_ptc_pins *pins; + bool *x_lines_requested; + bool *y_lines_requested; + int irq; + u8 imr; + struct completion ppp_ack; + struct tasklet_struct tasklet; + unsigned int button_keycode[ATMEL_PTC_MAX_NODES]; + bool buttons_registered; + bool scroller_registered[ATMEL_PTC_MAX_SCROLLERS]; + u32 button_event[ATMEL_PTC_MAX_NODES / 32]; + u32 button_state[ATMEL_PTC_MAX_NODES / 32]; + u32 scroller_event; + bool scroller_tracking; + char fw_version[ATMEL_PPP_FW_FOOTER_SIZE]; +}; + +static void atmel_ppp_irq_enable(struct atmel_ptc *ptc, u8 mask) +{ + ptc->imr |= mask; + atmel_ppp_writeb(ptc, ATMEL_PPP_IER, mask & ATMEL_PPP_IRQ_MASK); +} + +static void atmel_ppp_irq_disable(struct atmel_ptc *ptc, u8 mask) +{ + ptc->imr &= ~mask; + atmel_ppp_writeb(ptc, ATMEL_PPP_IDR, mask & ATMEL_PPP_IRQ_MASK); +} + +static void atmel_ppp_notify(struct atmel_ptc *ptc, u8 mask) +{ + if (mask & ATMEL_PPP_NOTIFY_MASK) { + u8 notify = atmel_ppp_readb(ptc, ATMEL_PPP_ISR) + | (mask & ATMEL_PPP_NOTIFY_MASK); + + atmel_ppp_writeb(ptc, ATMEL_PPP_ISR, notify); + } +} + +static void atmel_ppp_irq_pending_clr(struct atmel_ptc *ptc, u8 mask) +{ + if (mask & ATMEL_PPP_IRQ_MASK) { + u8 irq = atmel_ppp_readb(ptc, ATMEL_PPP_ISR) & ~mask; + + atmel_ppp_writeb(ptc, ATMEL_PPP_ISR, irq); + } +} + +static void atmel_ppp_cmd_send(struct atmel_ptc *ptc, u32 cmd) +{ + atmel_ppp_writel(ptc, ATMEL_PPP_CMD, cmd); +} + +static void atmel_qtm_set_cmd_id(struct atmel_ptc *ptc, u16 val) +{ + void __iomem *addr = ptc->qtm_mb + + ptc->mb_map->cmd_offset + + ptc->mb_map->cmd_id_offset; + + writew_relaxed(val, addr); +} + +static void atmel_qtm_set_cmd_addr(struct atmel_ptc *ptc, u16 val) +{ + void __iomem *addr = ptc->qtm_mb + + ptc->mb_map->cmd_offset + + ptc->mb_map->cmd_addr_offset; + + writew_relaxed(val, addr); +} + +static u32 atmel_qtm_get_cmd_data(struct atmel_ptc *ptc) +{ + void __iomem *addr = ptc->qtm_mb + + ptc->mb_map->cmd_offset + + ptc->mb_map->cmd_data_offset; + + return readl_relaxed(addr); +} + +static void atmel_qtm_set_cmd_data(struct atmel_ptc *ptc, u32 val) +{ + void __iomem *addr = ptc->qtm_mb + + ptc->mb_map->cmd_offset + + ptc->mb_map->cmd_data_offset; + + writel_relaxed(val, addr); +} + +static u16 atmel_qtm_get_node_mask_x(struct atmel_ptc *ptc, + unsigned int index) +{ + void __iomem *addr = ptc->qtm_mb + + ptc->mb_map->node_config_offset + + ptc->mb_map->node_config_size * index + + ptc->mb_map->node_config_mask_x_offset; + + return readw_relaxed(addr); +} + +static u32 atmel_qtm_get_node_mask_y(struct atmel_ptc *ptc, + unsigned int index) +{ + void __iomem *addr = ptc->qtm_mb + + ptc->mb_map->node_config_offset + + ptc->mb_map->node_config_size * index + + ptc->mb_map->node_config_mask_y_offset; + + return readl_relaxed(addr); +} + +static u16 atmel_qtm_get_key_count(struct atmel_ptc *ptc) +{ + void __iomem *addr = ptc->qtm_mb + + ptc->mb_map->node_group_config_offset + + ptc->mb_map->node_group_config_count_offset; + + return readw_relaxed(addr); +} + +static u8 atmel_qtm_get_scroller_group_config_count(struct atmel_ptc *ptc) +{ + void __iomem *addr = ptc->qtm_mb + + ptc->mb_map->scroller_group_config_offset + + ptc->mb_map->scroller_group_config_count_offset; + + return readb_relaxed(addr); +} + +static u8 atmel_qtm_get_scroller_type(struct atmel_ptc *ptc, + unsigned int index) +{ + void __iomem *addr = ptc->qtm_mb + + ptc->mb_map->scroller_config_offset + + ptc->mb_map->scroller_config_size * index + + ptc->mb_map->scroller_config_type_offset; + + return readb_relaxed(addr); +} + +static u16 atmel_qtm_get_scroller_key_start(struct atmel_ptc *ptc, + unsigned int index) +{ + void __iomem *addr = ptc->qtm_mb + + ptc->mb_map->scroller_config_offset + + ptc->mb_map->scroller_config_size * index + + ptc->mb_map->scroller_config_key_start_offset; + + return readw_relaxed(addr); +} + +static u8 atmel_qtm_get_scroller_key_count(struct atmel_ptc *ptc, + unsigned int index) +{ + void __iomem *addr = ptc->qtm_mb + + ptc->mb_map->scroller_config_offset + + ptc->mb_map->scroller_config_size * index + + ptc->mb_map->scroller_config_key_count_offset; + + return readb_relaxed(addr); +} + +static u8 atmel_qtm_get_scroller_resolution(struct atmel_ptc *ptc, + unsigned int index) +{ + void __iomem *addr = ptc->qtm_mb + + ptc->mb_map->scroller_config_offset + + ptc->mb_map->scroller_config_size * index + + ptc->mb_map->scroller_config_resol_deadband_offset; + + return (1 << (readb_relaxed(addr) >> 4)); +} + +static u8 atmel_qtm_get_scroller_status(struct atmel_ptc *ptc, + unsigned int index) +{ + void __iomem *addr = ptc->qtm_mb + + ptc->mb_map->scroller_data_offset + + ptc->mb_map->scroller_data_size * index + + ptc->mb_map->scroller_data_status_offset; + + return readb_relaxed(addr); +} + +static u16 atmel_qtm_get_scroller_position(struct atmel_ptc *ptc, + unsigned int index) +{ + void __iomem *addr = ptc->qtm_mb + + ptc->mb_map->scroller_data_offset + + ptc->mb_map->scroller_data_size * index + + ptc->mb_map->scroller_data_position_offset; + + return readw_relaxed(addr); +} + +static u32 atmel_qtm_get_touch_events_key_event_id(struct atmel_ptc *ptc, + unsigned int index) +{ + void __iomem *addr = ptc->qtm_mb + + ptc->mb_map->touch_events_key_event_id + + sizeof(u32) * index; + + return readw_relaxed(addr); +} + +static u32 atmel_qtm_get_touch_events_key_enable_state(struct atmel_ptc *ptc, + unsigned int index) +{ + void __iomem *addr = ptc->qtm_mb + + ptc->mb_map->touch_events_key_enable_state + + sizeof(u32) * index; + + return readw_relaxed(addr); +} + +static u32 atmel_qtm_get_touch_events_scroller_event_id(struct atmel_ptc *ptc) +{ + void __iomem *addr = ptc->qtm_mb + + ptc->mb_map->touch_events_scroller_event_id; + + return readw_relaxed(addr); +} + +static void atmel_ptc_irq_scroller_event(struct atmel_ptc *ptc) +{ + unsigned int status, i; + + if (!ptc->scroller_event || ptc->scroller_tracking) + return; + + /* + * Report the touch event and let the tasklet tracking the position + * until the scrollers are no longer touched. + */ + for (i = 0 ; i < ATMEL_PTC_MAX_SCROLLERS; i++) { + if (!ptc->scroller_input[i]) + break; + + status = atmel_qtm_get_scroller_status(ptc, i); + + input_report_key(ptc->scroller_input[i], BTN_TOUCH, + status & 0x1); + input_sync(ptc->scroller_input[i]); + + if (status & 0x1) { + ptc->scroller_tracking = true; + tasklet_schedule(&ptc->tasklet); + } + } +} + +static void atmel_ptc_irq_button_event(struct atmel_ptc *ptc) +{ + unsigned long i; + + for_each_set_bit(i, (unsigned long *)&ptc->button_event, ATMEL_PTC_MAX_NODES) { + u32 state = ptc->button_state[i / 32] & BIT(i % 32); + + input_report_key(ptc->buttons_input, + ptc->button_keycode[i], !!state); + input_sync(ptc->buttons_input); + } +} + +static void atmel_ptc_irq_touch_event(struct atmel_ptc *ptc) +{ + if (ptc->scroller_input[0]) + atmel_ptc_irq_scroller_event(ptc); + + if (ptc->buttons_input) + atmel_ptc_irq_button_event(ptc); +} + +static irqreturn_t atmel_ptc_irq_handler(int irq, void *data) +{ + struct atmel_ptc *ptc = data; + u32 isr = atmel_ppp_readb(ptc, ATMEL_PPP_ISR) & ptc->imr; + + /* QTM CMD acknowledgment */ + if (isr & ATMEL_PPP_IRQ0) { + atmel_ppp_irq_disable(ptc, ATMEL_PPP_IRQ0); + atmel_ppp_irq_pending_clr(ptc, ATMEL_PPP_IRQ0); + complete(&ptc->ppp_ack); + } + /* QTM touch event */ + if (isr & ATMEL_PPP_IRQ1) { + int i; + + for (i = 0; i < ATMEL_PTC_MAX_NODES / 32; i++) { + ptc->button_event[i] = atmel_qtm_get_touch_events_key_event_id(ptc, i); + ptc->button_state[i] = atmel_qtm_get_touch_events_key_enable_state(ptc, i); + } + ptc->scroller_event = atmel_qtm_get_touch_events_scroller_event_id(ptc); + + atmel_ppp_irq_pending_clr(ptc, ATMEL_PPP_IRQ1); + + atmel_ptc_irq_touch_event(ptc); + } + /* Debug event */ + if (isr & ATMEL_PPP_IRQ2) + atmel_ppp_irq_pending_clr(ptc, ATMEL_PPP_IRQ2); + + return IRQ_HANDLED; +} + +static void atmel_ptc_free_pins(struct atmel_ptc *ptc) +{ + int i; + + for (i = 0; i < ATMEL_PTC_MAX_X_LINES; i++) { + if (ptc->x_lines_requested[i]) { + gpio_free(ptc->pins->x_lines[i].id); + ptc->x_lines_requested[i] = false; + } + } + + for (i = 0; i < ATMEL_PTC_MAX_Y_LINES; i++) { + if (ptc->y_lines_requested[i]) { + gpio_free(ptc->pins->y_lines[i].id); + ptc->y_lines_requested[i] = false; + } + } +} + +static int atmel_ptc_request_pins(struct atmel_ptc *ptc) +{ + u16 key_count = atmel_qtm_get_key_count(ptc); + int i, j, ret = 0; + + /* + * One line can be used by several nodes. For that reason, we have + * to take care about not requesting a GPIO more than once. + */ + for (i = 0; i < key_count; i++) { + u16 mask_x = atmel_qtm_get_node_mask_x(ptc, i); + u32 mask_y = atmel_qtm_get_node_mask_y(ptc, i); + + for_each_set_bit(j, (unsigned long *)&mask_x, 16) { + if (ptc->x_lines_requested[j]) + continue; + + if (gpio_request_one(ptc->pins->x_lines[j].id, GPIOF_DIR_IN, ptc->pins->x_lines[j].name)) { + dev_err(ptc->dev, "Can't get %s\n", ptc->pins->x_lines[j].name); + return -ENXIO; + } + ptc->x_lines_requested[j] = true; + } + + for_each_set_bit(j, (unsigned long *)&mask_y, 32) { + if (ptc->y_lines_requested[j]) + continue; + + if (gpio_request_one(ptc->pins->y_lines[j].id, GPIOF_DIR_IN, ptc->pins->y_lines[j].name)) { + dev_err(ptc->dev, "Can't get %s\n", ptc->pins->y_lines[j].name); + return -ENXIO; + } + ptc->y_lines_requested[j] = true; + } + } + + return ret; +} + +static int atmel_ptc_conf_input_devs(struct atmel_ptc *ptc) +{ + struct input_dev *input_buttons = ptc->buttons_input; + u16 key_count = atmel_qtm_get_key_count(ptc); + bool *buttons; + int i, j, ret = 0; + + buttons = kmalloc_array(key_count, sizeof(*buttons), GFP_KERNEL); + if (!buttons) + return -ENOMEM; + memset(buttons, true, key_count); + + for (i = 0; i < atmel_qtm_get_scroller_group_config_count(ptc); i++) { + struct input_dev *scroller = ptc->scroller_input[i]; + u16 key_start = atmel_qtm_get_scroller_key_start(ptc, i); + u8 key_count = atmel_qtm_get_scroller_key_count(ptc, i); + + if (key_count) { + /* If a key is part of a scroller, it's not a button. */ + for (j = key_start; j < key_start + key_count; j++) + buttons[j] = false; + + /* + * Prevent several allocations for the same scroller if + * atmel_ptc_conf_input_devs() is call more than + * once. + */ + if (!scroller) { + scroller = devm_input_allocate_device(ptc->dev); + if (!scroller) + return -ENOMEM; + ptc->scroller_input[i] = scroller; + scroller->dev.parent = ptc->dev; + } + + switch (atmel_qtm_get_scroller_type(ptc, i)) { + case ATMEL_QTM_SCROLLER_TYPE_SLIDER: + input_set_capability(scroller, EV_ABS, ABS_X); + input_set_capability(scroller, EV_KEY, BTN_TOUCH); + input_set_abs_params(scroller, ABS_X, 0, atmel_qtm_get_scroller_resolution(ptc, i), 0, 0); + break; + case ATMEL_QTM_SCROLLER_TYPE_WHEEL: + input_set_capability(scroller, EV_ABS, ABS_WHEEL); + input_set_capability(scroller, EV_KEY, BTN_TOUCH); + input_set_abs_params(scroller, ABS_WHEEL, 0, atmel_qtm_get_scroller_resolution(ptc, i), 0, 0); + break; + default: + ret = -EINVAL; + goto out; + } + } + } + + for (i = 0; i < key_count; i++) { + if (buttons[i]) { + int keycode = ATMEL_PTC_KEYCODE_BASE_OFFSET + i; + + if (!input_buttons) { + input_buttons = devm_input_allocate_device(ptc->dev); + if (!input_buttons) + return -ENOMEM; + + input_buttons->dev.parent = ptc->dev; + input_buttons->keycode = ptc->button_keycode; + input_buttons->keycodesize = sizeof(ptc->button_keycode[0]); + input_buttons->keycodemax = ATMEL_PTC_MAX_NODES; + ptc->buttons_input = input_buttons; + } + ptc->button_keycode[i] = keycode; + input_set_capability(input_buttons, EV_KEY, keycode); + } + } + +out: + kfree(buttons); + return ret; +} + +static void atmel_ptc_unregister_input_devices(struct atmel_ptc *ptc) +{ + int i; + + if (ptc->buttons_registered) { + input_unregister_device(ptc->buttons_input); + ptc->buttons_registered = false; + } + + for (i = 0; i < ATMEL_PTC_MAX_SCROLLERS; i++) { + struct input_dev *scroller = ptc->scroller_input[i]; + + if (!scroller || !ptc->scroller_registered[i]) + continue; + + input_unregister_device(scroller); + ptc->scroller_registered[i] = false; + } +} + +static int atmel_ptc_register_input_devices(struct atmel_ptc *ptc) +{ + int i, ret = 0, id = 0; + + if (ptc->buttons_input) { + struct input_dev *buttons = ptc->buttons_input; + buttons->name = devm_kasprintf(&buttons->dev, GFP_KERNEL, + "atmel_ptc%d", id++); + if (!buttons->name) + return -ENOMEM; + ret = input_register_device(buttons); + if (ret) { + dev_err(ptc->dev, "can't register input button device.\n"); + atmel_ptc_unregister_input_devices(ptc); + return ret; + } + + ptc->buttons_registered = true; + } + + for (i = 0; i < ATMEL_PTC_MAX_SCROLLERS; i++) { + struct input_dev *scroller = ptc->scroller_input[i]; + + if (!scroller) + continue; + + scroller->name = devm_kasprintf(&scroller->dev, GFP_KERNEL, + "atmel_ptc%d", id++); + if (!scroller->name) { + atmel_ptc_unregister_input_devices(ptc); + return -ENOMEM; + } + ret = input_register_device(scroller); + if (ret) { + dev_err(ptc->dev, "can't register input scroller device.\n"); + atmel_ptc_unregister_input_devices(ptc); + return ret; + } + + ptc->scroller_registered[i] = true; + } + + return ret; +} + +static int atmel_ptc_cmd_send(struct atmel_ptc *ptc, struct atmel_qtm_cmd *cmd) +{ + int ret; + + dev_dbg(ptc->dev, "%s: cmd=0x%x, addr=0x%x, data=0x%x\n", + __func__, cmd->id, cmd->addr, cmd->data); + + /* + * Configure and register input devices only when QTM is started + * since some information from the QTM configuration is needed. + * It could be done when loading the configuration file but if + * the debug mode is enabled, the configuration can be changed + * before sending the run command. + */ + if (cmd->id == ATMEL_QTM_CMD_RUN) { + ret = atmel_ptc_request_pins(ptc); + if (ret) + return ret; + + ret = atmel_ptc_conf_input_devs(ptc); + if (ret) + return ret; + + ret = atmel_ptc_register_input_devices(ptc); + if (ret) + return ret; + } + + atmel_qtm_set_cmd_id(ptc, cmd->id); + atmel_qtm_set_cmd_addr(ptc, cmd->addr); + atmel_qtm_set_cmd_data(ptc, cmd->data); + + /* Once command performed, we'll get an IRQ. */ + atmel_ppp_irq_enable(ptc, ATMEL_PPP_IRQ0); + /* Notify PPP that we have sent a command. */ + atmel_ppp_notify(ptc, ATMEL_PPP_NOTIFY0); + /* Wait for IRQ from PPP. */ + wait_for_completion(&ptc->ppp_ack); + + if (cmd->id == ATMEL_QTM_CMD_STOP) { + atmel_ptc_unregister_input_devices(ptc); + atmel_ptc_free_pins(ptc); + } + + cmd->data = atmel_qtm_get_cmd_data(ptc); + return 0; +} + +static void atmel_ptc_tasklet(unsigned long priv) +{ + struct atmel_ptc *ptc = (struct atmel_ptc *)priv; + unsigned int scroller_type, position, status, i; + + for (i = 0 ; i < ATMEL_PTC_MAX_SCROLLERS; i++) { + if (!ptc->scroller_input[i]) + break; + + scroller_type = atmel_qtm_get_scroller_type(ptc, i); + position = atmel_qtm_get_scroller_position(ptc, i); + status = atmel_qtm_get_scroller_status(ptc, i); + + if (status & 0x1) { + if (scroller_type == ATMEL_QTM_SCROLLER_TYPE_WHEEL) + input_report_abs(ptc->scroller_input[i], + ABS_WHEEL, position); + else + input_report_abs(ptc->scroller_input[i], + ABS_X, position); + input_sync(ptc->scroller_input[i]); + tasklet_schedule(&ptc->tasklet); + } else { + input_report_key(ptc->scroller_input[i], BTN_TOUCH, 0); + input_sync(ptc->scroller_input[i]); + + ptc->scroller_tracking = false; + } + } +} + +static int atmel_ptc_conf_load(struct atmel_ptc *ptc) +{ + const struct firmware *conf; + struct atmel_qtm_cmd cmd; + int ret; + u16 key_count; + char *dst; + + dev_info(ptc->dev, "Loading configuration: %s\n", configuration_file); + ret = request_firmware(&conf, configuration_file, ptc->dev); + if (ret) { + dev_err(ptc->dev, "Can't load configuration %s\n", configuration_file); + return ret; + } + + ptc->conf.header_version_major = conf->data[0]; + ptc->conf.header_version_minor = conf->data[1]; + switch (ptc->conf.header_version_major) { + case (1): + ptc->conf.header_size = 96; + ptc->conf.fw_version = (char *) conf->data + 16; + ptc->conf.tool_version = (char *) conf->data + 32; + ptc->conf.date = (char *) conf->data + 48; + ptc->conf.description = (char *) conf->data + 64; + break; + default: + release_firmware(conf); + dev_err(ptc->dev, "Unsupported header version: %u.%u\n", + ptc->conf.header_version_major, + ptc->conf.header_version_minor); + return -EINVAL; + }; + + dev_info(ptc->dev, "firmware version: %s, tool version: %s\n", + ptc->conf.fw_version, ptc->conf.tool_version); + dev_info(ptc->dev, "date: %s, description: %s\n", + ptc->conf.date, ptc->conf.description); + + /* + * TODO: check the version of the firmware loaded vs the version of the + * firmware needed by the configuration file. + */ + if (strcmp(ptc->fw_version, ptc->conf.fw_version)) + dev_warn(ptc->dev, "be careful the configuration requires firmware %s, current firmware is %s\n", + ptc->conf.fw_version, ptc->fw_version); + + atmel_ppp_irq_enable(ptc, ATMEL_PPP_IRQ1); + atmel_ppp_irq_disable(ptc, ATMEL_PPP_IRQ2 | ATMEL_PPP_IRQ3); + + cmd.id = ATMEL_QTM_CMD_STOP; + atmel_ptc_cmd_send(ptc, &cmd); + + dst = (char *)ptc->qtm_mb; + /* Need to use _memcpy_toio, otherwise configuration is not well loaded. */ + _memcpy_toio(dst, conf->data + ptc->conf.header_size, + conf->size - ptc->conf.header_size); + + key_count = atmel_qtm_get_key_count(ptc); + + /* Start QTM. */ + cmd.id = ATMEL_QTM_CMD_INIT; + cmd.data = key_count; + atmel_ptc_cmd_send(ptc, &cmd); + + cmd.id = ATMEL_QTM_CMD_SET_ACQ_MODE_TIMER; + cmd.data = 1; + atmel_ptc_cmd_send(ptc, &cmd); + + cmd.id = ATMEL_QTM_CMD_RUN; + cmd.data = key_count; + atmel_ptc_cmd_send(ptc, &cmd); + + cmd.id = ATMEL_QTM_CMD_FIRM_VERSION; + cmd.data = 0; + atmel_ptc_cmd_send(ptc, &cmd); + dev_dbg(ptc->dev, "firmware version: v%u.%u\n", + (cmd.data >> 16) & 0xffff, cmd.data & 0xffff); + + release_firmware(conf); + + return ret; +} + +static int atmel_ptc_fw_load(struct atmel_ptc *ptc) +{ + const struct firmware *fw; + int ret; + + dev_dbg(ptc->dev, "loading firmware: %s\n", firmware_file); + ret = request_firmware(&fw, firmware_file, ptc->dev); + if (ret) { + dev_err(ptc->dev, "error while requesting the firmware\n"); + return ret; + } + + strncpy(ptc->fw_version, fw->data + fw->size - ATMEL_PPP_FW_FOOTER_SIZE, + ATMEL_PPP_FW_FOOTER_SIZE); + dev_dbg(ptc->dev, "version: %s\n", ptc->fw_version); + + if (!strcmp(ptc->fw_version, "PPP_VER_006.004")) { + ptc->mb_map = &mailbox_map_v64; + } else { + dev_err(ptc->dev, "unsupported firmware version\n"); + ret = -EINVAL; + goto out; + } + + /* Memset needed to avoid firmware unexpected behavior. */ + memset(ptc->firmware, 0, ATMEL_QTM_MB_OFFSET + sizeof(*ptc->qtm_mb)); + ptc->imr = 0; + /* Command sequence to start from a clean state. */ + atmel_ppp_cmd_send(ptc, ATMEL_PPP_CMD_ABORT); + atmel_ppp_irq_pending_clr(ptc, ATMEL_PPP_IRQ_MASK); + atmel_ppp_cmd_send(ptc, ATMEL_PPP_CMD_RESET); + + dev_dbg(ptc->dev, "downloading %zu bytes\n", fw->size); + memcpy(ptc->firmware, fw->data, fw->size); + + atmel_ppp_cmd_send(ptc, ATMEL_PPP_CMD_RUN); + +out: + release_firmware(fw); + return ret; +} + +static void atmel_ptc_clk_disable_unprepare(void *data) +{ + struct atmel_ptc *ptc = data; + + clk_disable_unprepare(ptc->clk_slow); + clk_disable_unprepare(ptc->clk_per); + clk_disable_unprepare(ptc->clk_int_osc); +} + +static inline struct atmel_ptc *kobj_to_atmel_ptc(struct kobject *kobj) +{ + struct device *dev = kobj_to_dev(kobj); + + return dev->driver_data; +} + +static ssize_t atmel_qtm_mb_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct atmel_ptc *ptc = kobj_to_atmel_ptc(kobj); + char *qtm_mb = (char *)ptc->qtm_mb; + + dev_dbg(ptc->dev, "%s: off=0x%llx, count=%zu\n", __func__, off, count); + + memcpy_fromio(buf, qtm_mb + off, count); + + return count; +} + +static ssize_t atmel_qtm_mb_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct atmel_ptc *ptc = kobj_to_atmel_ptc(kobj); + char *qtm_mb = (char *)ptc->qtm_mb; + + dev_dbg(ptc->dev, "%s: off=0x%llx, count=%zu\n", __func__, off, count); + + if (off == 0 && count == sizeof(struct atmel_qtm_cmd)) + atmel_ptc_cmd_send(ptc, (struct atmel_qtm_cmd *)buf); + else + memcpy_toio(qtm_mb + off, buf, count); + + return count; +} + +static BIN_ATTR_RW(atmel_qtm_mb, ATMEL_QTM_MB_SIZE); + +static struct bin_attribute *atmel_ptc_qtm_mb_attrs[] = { + &bin_attr_atmel_qtm_mb, + NULL, +}; + +static const struct attribute_group atmel_ptc_qtm_mb_attr_group = { + .bin_attrs = atmel_ptc_qtm_mb_attrs, +}; + +static int atmel_ptc_probe(struct platform_device *pdev) +{ + struct atmel_ptc *ptc; + struct resource *res; + void *shared_memory; + int ret; + + ptc = devm_kzalloc(&pdev->dev, sizeof(*ptc), GFP_KERNEL); + if (!ptc) + return -ENOMEM; + + platform_set_drvdata(pdev, ptc); + ptc->dev = &pdev->dev; + ptc->dev->driver_data = ptc; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + shared_memory = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(shared_memory)) + return PTR_ERR(shared_memory); + + ptc->firmware = shared_memory; + ptc->qtm_mb = shared_memory + ATMEL_QTM_MB_OFFSET; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) + return -EINVAL; + + ptc->ppp_regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ptc->ppp_regs)) + return PTR_ERR(ptc->ppp_regs); + + ptc->irq = platform_get_irq(pdev, 0); + if (ptc->irq <= 0) { + if (!ptc->irq) + ptc->irq = -ENXIO; + + return ptc->irq; + } + + ptc->pins = of_device_get_match_data(&pdev->dev); + if (!ptc->pins) { + dev_err(ptc->dev, "can't retrieve pins\n"); + return -EINVAL; + } + + ptc->x_lines_requested = devm_kzalloc(ptc->dev, + ATMEL_PTC_MAX_X_LINES * sizeof(*ptc->x_lines_requested), + GFP_KERNEL); + if (!ptc->x_lines_requested) + return -ENOMEM; + + ptc->y_lines_requested = devm_kzalloc(ptc->dev, + ATMEL_PTC_MAX_Y_LINES * sizeof(*ptc->y_lines_requested), + GFP_KERNEL); + if (!ptc->y_lines_requested) + return -ENOMEM; + + ptc->clk_per = devm_clk_get(&pdev->dev, "ptc_clk"); + if (IS_ERR(ptc->clk_per)) + return PTR_ERR(ptc->clk_per); + + ptc->clk_int_osc = devm_clk_get(&pdev->dev, "ptc_int_osc"); + if (IS_ERR(ptc->clk_int_osc)) + return PTR_ERR(ptc->clk_int_osc); + + ptc->clk_slow = devm_clk_get(&pdev->dev, "slow_clk"); + if (IS_ERR(ptc->clk_slow)) + return PTR_ERR(ptc->clk_slow); + + ret = clk_prepare_enable(ptc->clk_int_osc); + if (ret) + return ret; + + ret = clk_prepare_enable(ptc->clk_per); + if (ret) { + clk_disable_unprepare(ptc->clk_int_osc); + return ret; + } + + ret = clk_prepare_enable(ptc->clk_slow); + if (ret) { + clk_disable_unprepare(ptc->clk_per); + clk_disable_unprepare(ptc->clk_int_osc); + return ret; + } + + ret = devm_add_action_or_reset(&pdev->dev, + atmel_ptc_clk_disable_unprepare, + ptc); + if (ret) + return ret; + + ret = devm_request_irq(&pdev->dev, ptc->irq, atmel_ptc_irq_handler, 0, + pdev->dev.driver->name, ptc); + if (ret) + return ret; + + init_completion(&ptc->ppp_ack); + + ret = atmel_ptc_fw_load(ptc); + if (ret) + return ret; + + ret = atmel_ptc_conf_load(ptc); + if (ret) + return ret; + + /* + * Expose a file to give an access to the QTM mailbox to a user space + * application in order to configure it or to send commands. + */ + if (debug_mode) + ret = sysfs_create_group(&ptc->dev->kobj, &atmel_ptc_qtm_mb_attr_group); + + tasklet_init(&ptc->tasklet, atmel_ptc_tasklet, (unsigned long)ptc); + + return ret; +} + +static int atmel_ptc_remove(struct platform_device *pdev) +{ + struct atmel_ptc *ptc = platform_get_drvdata(pdev); + + tasklet_kill(&ptc->tasklet); + atmel_ptc_unregister_input_devices(ptc); + atmel_ptc_free_pins(ptc); + + if (debug_mode) + sysfs_remove_group(&ptc->dev->kobj, &atmel_ptc_qtm_mb_attr_group); + + return 0; +} + +/* + * x_lines and y_lines have to be described in the ascending order i.e. + * PTC_X0 must be at index 0, PTC_X1 must be at index 1, etc. + */ +static struct atmel_ptc_pins atmel_ptc_pins_sama5d2 = { + .x_lines = { + { .name = "PTC_X0", .id = 99 /* PD3 */ }, + { .name = "PTC_X1", .id = 100 /* PD4 */ }, + { .name = "PTC_X2", .id = 101 /* PD5 */ }, + { .name = "PTC_X3", .id = 102 /* PD6 */ }, + { .name = "PTC_X4", .id = 103 /* PD7 */ }, + { .name = "PTC_X5", .id = 104 /* PD8 */ }, + { .name = "PTC_X6", .id = 105 /* PD9 */ }, + { .name = "PTC_X7", .id = 106 /* PD10 */ }, + }, + .y_lines = { + { .name = "PTC_Y0", .id = 107 /* PD11 */ }, + { .name = "PTC_Y1", .id = 108 /* PD12 */ }, + { .name = "PTC_Y2", .id = 109 /* PD13 */ }, + { .name = "PTC_Y3", .id = 110 /* PD14 */ }, + { .name = "PTC_Y4", .id = 111 /* PD15 */ }, + { .name = "PTC_Y5", .id = 112 /* PD16 */ }, + { .name = "PTC_Y6", .id = 113 /* PD17 */ }, + { .name = "PTC_Y7", .id = 114 /* PD18 */ }, + }, +}; + +static const struct of_device_id atmel_ptc_dt_match[] = { + { + .compatible = "atmel,sama5d2-ptc", + .data = &atmel_ptc_pins_sama5d2, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, atmel_ptc_dt_match); + +static struct platform_driver atmel_ptc_driver = { + .probe = atmel_ptc_probe, + .remove = atmel_ptc_remove, + .driver = { + .name = "atmel_ptc", + .of_match_table = of_match_ptr(atmel_ptc_dt_match), + }, +}; +module_platform_driver(atmel_ptc_driver) + +module_param(firmware_file, charp, 0444); +MODULE_PARM_DESC(firmware_file, "Name of the firmware file"); +module_param(configuration_file, charp, 0444); +MODULE_PARM_DESC(configuration_file, "Name of the configuration file"); +module_param(debug_mode, bool, 0444); +MODULE_PARM_DESC(debug_mode, "The debug mode provides an interface to the mailbox through sysfs"); +MODULE_AUTHOR("Ludovic Desroches <ludovic.desroches@microchip.com>"); +MODULE_DESCRIPTION("Atmel PTC subsystem"); +MODULE_LICENSE("GPL v2"); +MODULE_FIRMWARE(ATMEL_PPP_FW_NAME); +MODULE_FIRMWARE(ATMEL_QTM_CONF_NAME); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/input/misc/Kconfig linux-microchip/drivers/input/misc/Kconfig --- linux-6.6.51/drivers/input/misc/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/input/misc/Kconfig 2024-11-26 14:02:37.470493196 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:133 @ To compile this driver as a module, choose M here; the module will be called bbnsm_pwrkey. +config INPUT_ATMEL_PTC + tristate "Atmel PTC Driver" + depends on OF || COMPILE_TEST + depends on SOC_SAMA5D2 + help + Say Y to enable support for the Atmel PTC Subsystem. + + To compile this driver as a module, choose M here: the + module will be called atmel_ptc. + If you compile it as a built-in driver, you have to build the + firmware into the kernel or to use an initrd. + config INPUT_BMA150 tristate "BMA150/SMB380 acceleration sensor support" depends on I2C diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/input/misc/Makefile linux-microchip/drivers/input/misc/Makefile --- linux-6.6.51/drivers/input/misc/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/input/misc/Makefile 2024-11-26 14:02:37.470493196 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:25 @ obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o obj-$(CONFIG_INPUT_ATMEL_CAPTOUCH) += atmel_captouch.o obj-$(CONFIG_INPUT_BBNSM_PWRKEY) += nxp-bbnsm-pwrkey.o +obj-$(CONFIG_INPUT_ATMEL_PTC) += atmel_ptc.o obj-$(CONFIG_INPUT_BMA150) += bma150.o obj-$(CONFIG_INPUT_CM109) += cm109.o obj-$(CONFIG_INPUT_CMA3000) += cma3000_d0x.o diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/irqchip/irq-atmel-aic5.c linux-microchip/drivers/irqchip/irq-atmel-aic5.c --- linux-6.6.51/drivers/irqchip/irq-atmel-aic5.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/irqchip/irq-atmel-aic5.c 2024-11-26 14:02:37.498493698 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:323 @ { .compatible = "atmel,sama5d3", .data = sama5d3_aic_irq_fixup }, { .compatible = "atmel,sama5d4", .data = sama5d3_aic_irq_fixup }, { .compatible = "microchip,sam9x60", .data = sam9x60_aic_irq_fixup }, + { .compatible = "microchip,sam9x7", .data = sam9x60_aic_irq_fixup }, { /* sentinel */ }, }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:410 @ return aic5_of_init(node, parent, NR_SAM9X60_IRQS); } IRQCHIP_DECLARE(sam9x60_aic5, "microchip,sam9x60-aic", sam9x60_aic5_of_init); + +#define NR_SAM9X7_IRQS 70 + +static int __init sam9x7_aic5_of_init(struct device_node *node, + struct device_node *parent) +{ + return aic5_of_init(node, parent, NR_SAM9X7_IRQS); +} +IRQCHIP_DECLARE(sam9x7_aic5, "microchip,sam9x7-aic", sam9x7_aic5_of_init); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/irqchip/irq-mpfs-mux.c linux-microchip/drivers/irqchip/irq-mpfs-mux.c --- linux-6.6.51/drivers/irqchip/irq-mpfs-mux.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/irqchip/irq-mpfs-mux.c 2024-11-26 14:02:37.498493698 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microchip PolarFire SoC (MPFS) GPIO IRQ MUX + * + * Author: Conor Dooley <conor.dooley@microchip.com> + */ + +#define pr_fmt(fmt) "mpfs-irq-mux: " fmt + +#include <linux/bits.h> +#include <linux/interrupt.h> +#include <linux/irqchip.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/irqdomain.h> +#include <linux/mfd/syscon.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define MPFS_MUX_NUM_IRQS 41 +#define MPFS_MUX_NUM_DIRECT_IRQS 38 +#define MPFS_MUX_NUM_NON_DIRECT_IRQS 3 +#define MPFS_MAX_IRQS_PER_GPIO 32 +#define MPFS_NUM_IRQS_GPIO0 14 +#define MPFS_NUM_IRQS_GPIO1 24 +#define MPFS_NUM_IRQS_GPIO2 32 +#define MPFS_NUM_IRQS_GPIO02_SHIFT 0 +#define MPFS_NUM_IRQS_GPIO1_SHIFT 14 + +static const struct regmap_config mpfs_irq_mux_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .val_format_endian = REGMAP_ENDIAN_LITTLE, + .max_register = 1, +}; + +/* + * There are 3 GPIO controllers on this SoC, of which: + * - GPIO controller 0 has 14 GPIOs + * - GPIO controller 1 has 24 GPIOs + * - GPIO controller 2 has 32 GPIOs + * + * All GPIOs are capable of generating interrupts, for a total of 70. + * There are only 41 IRQs available however, so a configurable mux is used to + * ensure all GPIOs can be used for interrupt generation. + * 38 of the 41 interrupts are in what the documentation calls "direct mode", + * as they provide an exclusive connection from a GPIO to the PLIC. + * The 3 remaining interrupts are used to mux the interrupts which do not have + * a exclusive connection, one for each GPIO controller. + * A register is used to set this configuration of this mux, depending on how + * the "MSS Configurator" (FPGA configuration tool) has set things up. + * This is done by the platform's firmware, so access from Linux is read-only. + * + * Documentation also refers to GPIO controller 0 & 1 as "pad" GPIO controllers + * and GPIO controller 2 as the "fabric" GPIO controller. Despite that wording, + * all 3 are "hard" peripherals. + * + * The mux has a single register, where bits 0 to 13 mux between GPIO controller + * 1's 14 GPIOs and GPIO controller 2's first 14 GPIOs. The remaining bits mux + * between the first 18 GPIOs of controller 1 and the last 18 GPIOS of + * controller 2. If a bit in the mux's control register is set, the + * corresponding interrupt line for GPIO controller 0 or 1 will be put in + * "non-direct" mode. If cleared, the "fabric" controller's will. + * + * Register layout: + * GPIO 1 interrupt line 17 | mux bit 31 | GPIO 2 interrupt line 31 + * ... | ... | ... + * ... | ... | ... + * GPIO 1 interrupt line 0 | mux bit 14 | GPIO 2 interrupt line 14 + * GPIO 0 interrupt line 13 | mux bit 13 | GPIO 2 interrupt line 13 + * ... | ... | ... + * ... | ... | ... + * GPIO 0 interrupt line 0 | mux bit 0 | GPIO 2 interrupt line 0 + * + * That leaves 6 exclusive, or "direct", interrupts remaining. These are + * used by GPIO controller 1's lines 18 to 23. + */ + +struct mpfs_irq_mux_bank_config { + u32 mask; + u8 shift; +}; + +static const struct mpfs_irq_mux_bank_config mpfs_irq_mux_bank_configs[3] = { + {GENMASK(MPFS_NUM_IRQS_GPIO0 - 1, 0), MPFS_NUM_IRQS_GPIO02_SHIFT}, + {GENMASK(MPFS_NUM_IRQS_GPIO1 - 1, 0), MPFS_NUM_IRQS_GPIO1_SHIFT}, + {GENMASK(MPFS_NUM_IRQS_GPIO2 - 1, 0), MPFS_NUM_IRQS_GPIO02_SHIFT}, +}; + +struct mpfs_irq_mux_irqchip { + struct irq_domain *domain; + int bank; + int irq; + u8 offset; +}; + +struct mpfs_irq_mux { + struct regmap *regmap; + u32 offset; + u32 mux_config; + struct mpfs_irq_mux_irqchip nondirect_irqchips[MPFS_MUX_NUM_NON_DIRECT_IRQS]; + int parent_irqs[MPFS_MUX_NUM_DIRECT_IRQS]; +}; + +/* + * There is no "control" hardware in this mux, and as such there is no ability + * to mask at this level. As the irq has been disconnected from the hierarchy, + * there's no parent irqchip from which to use mask functions either. + */ +static void mpfs_irq_mux_irq_mask(struct irq_data *d) {} +static void mpfs_irq_mux_irq_unmask(struct irq_data *d) {} + +static struct irq_chip mpfs_irq_mux_nondirect_irq_chip = { + .name = "MPFS GPIO Interrupt Mux", + .irq_mask = mpfs_irq_mux_irq_mask, + .irq_unmask = mpfs_irq_mux_irq_unmask, + .flags = IRQCHIP_IMMUTABLE, +}; + +static struct irq_chip mpfs_irq_mux_irq_chip = { + .name = "MPFS GPIO Interrupt Mux", + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_eoi = irq_chip_eoi_parent, + .irq_set_type = irq_chip_set_type_parent, + .irq_set_affinity = irq_chip_set_affinity_parent, + .flags = IRQCHIP_IMMUTABLE, +}; + +/* + * Returns an unsigned long, where a set bit indicates the corresponding + * interrupt is in non-direct/muxed mode for that bank/GPIO controller. + */ +static inline unsigned long mpfs_irq_mux_get_muxed_irqs(struct mpfs_irq_mux *priv, + unsigned int bank) +{ + unsigned long mux_config = priv->mux_config, muxed_irqs = -1; + struct mpfs_irq_mux_bank_config bank_config = mpfs_irq_mux_bank_configs[bank]; + + /* + * If a bit is set in the mux, GPIO the corresponding interrupt from + * controller 2 is direct and that controllers 0 or 1 is muxed. + * Invert the bits in the configuration register, so that set bits + * equate to non-direct mode, for GPIO controller 2. + */ + if (bank == 2u) + mux_config = ~mux_config; + + muxed_irqs &= bank_config.mask; + muxed_irqs &= mux_config >> bank_config.shift; + + return muxed_irqs; +} + +static void mpfs_irq_mux_nondirect_handler(struct irq_desc *desc) +{ + struct mpfs_irq_mux_irqchip *irqchip_data = irq_desc_get_handler_data(desc); + struct mpfs_irq_mux *priv = container_of(irqchip_data, struct mpfs_irq_mux, + nondirect_irqchips[irqchip_data->bank]); + unsigned long muxed_irqs; + int pos; + + chained_irq_enter(irq_desc_get_chip(desc), desc); + + muxed_irqs = mpfs_irq_mux_get_muxed_irqs(priv, irqchip_data->bank); + + for_each_set_bit(pos, &muxed_irqs, MPFS_MAX_IRQS_PER_GPIO) + generic_handle_domain_irq(irqchip_data->domain, irqchip_data->offset + pos); + + chained_irq_exit(irq_desc_get_chip(desc), desc); +} + +static bool mpfs_irq_mux_is_direct(struct mpfs_irq_mux *priv, struct irq_fwspec *fwspec) +{ + unsigned int bank, line; + u32 muxed_irqs; + + bank = fwspec->param[0] / MPFS_MAX_IRQS_PER_GPIO; + line = fwspec->param[0] % MPFS_MAX_IRQS_PER_GPIO; + + muxed_irqs = mpfs_irq_mux_get_muxed_irqs(priv, bank); + + if (BIT(line) & muxed_irqs) + return false; + + return true; +} + +static int mpfs_irq_mux_translate(struct irq_domain *d, struct irq_fwspec *fwspec, + unsigned long *out_hwirq, unsigned int *out_type) +{ + if (!is_of_node(fwspec->fwnode)) + return -EINVAL; + + return irq_domain_translate_onecell(d, fwspec, out_hwirq, out_type); +} + +static int mpfs_irq_mux_nondirect_alloc(struct irq_domain *d, unsigned int virq, + struct irq_fwspec *fwspec, struct mpfs_irq_mux *priv) +{ + unsigned int bank = fwspec->param[0] / MPFS_MAX_IRQS_PER_GPIO; + + if (bank > 2) + return -EINVAL; + + priv->nondirect_irqchips[bank].domain = d; + + irq_domain_set_hwirq_and_chip(d, virq, fwspec->param[0], + &mpfs_irq_mux_nondirect_irq_chip, priv); + irq_set_chained_handler_and_data(virq, handle_untracked_irq, + &priv->nondirect_irqchips[bank]); + + return irq_domain_disconnect_hierarchy(d->parent, virq); +} + +static int mpfs_irq_mux_alloc(struct irq_domain *d, unsigned int virq, + unsigned int nr_irqs, void *arg) +{ + struct mpfs_irq_mux *priv = d->host_data; + struct irq_fwspec *fwspec = arg; + struct irq_fwspec parent_fwspec; + unsigned int bank, line, irq; + u64 mask; + + if (!mpfs_irq_mux_is_direct(priv, fwspec)) + return mpfs_irq_mux_nondirect_alloc(d, virq, fwspec, priv); + + bank = fwspec->param[0] / MPFS_MAX_IRQS_PER_GPIO; + line = fwspec->param[0] % MPFS_MAX_IRQS_PER_GPIO; + mask = mpfs_irq_mux_bank_configs[bank].mask; + irq = line + mpfs_irq_mux_bank_configs[bank].shift; + + parent_fwspec.fwnode = d->parent->fwnode; + parent_fwspec.param[0] = priv->parent_irqs[irq]; + parent_fwspec.param_count = 1; + + irq_domain_set_hwirq_and_chip(d, virq, fwspec->param[0], &mpfs_irq_mux_irq_chip, priv); + + return irq_domain_alloc_irqs_parent(d, virq, 1, &parent_fwspec); +} + +static const struct irq_domain_ops mpfs_irq_mux_domain_ops = { + .translate = mpfs_irq_mux_translate, + .alloc = mpfs_irq_mux_alloc, + .free = irq_domain_free_irqs_common, +}; + +static int mpfs_irq_mux_probe(struct platform_device *pdev) +{ + struct device_node *parent; + struct device *dev = &pdev->dev; + struct mpfs_irq_mux *priv; + struct irq_domain *hier_domain, *parent_domain; + void __iomem *base; + int i, ret = 0; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + if (!of_device_is_compatible(dev->parent->of_node, "microchip,mpfs-mss-top-sysreg")) + goto old_format; + + priv->regmap = device_node_to_regmap(dev->parent->of_node); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->offset = 0x54; + + goto done; + +old_format: + base = of_iomap(dev->of_node, 0); + if (IS_ERR(base)) { + goto out_unmap; + ret = PTR_ERR(base); + } + + priv->regmap = devm_regmap_init_mmio(dev, base, &mpfs_irq_mux_regmap_config); + if (IS_ERR(priv->regmap)) { + goto out_unmap; + ret = PTR_ERR(priv->regmap); + } + + priv->offset = 0; + +done: + ret = regmap_read(priv->regmap, priv->offset, &priv->mux_config); + if (ret) + goto out_unmap; + + for (i = 0; i < MPFS_MUX_NUM_DIRECT_IRQS; i++) { + struct of_phandle_args parent_irq; + int ret; + + ret = of_irq_parse_one(to_of_node(dev->fwnode), i, &parent_irq); + if (ret) + goto out_unmap; + + /* + * The parent irqs are saved off for the first 38 interrupts + * from the devicetree entry so that they can be used in the + * domains alloc callback to allocate irqs from the parent irq + * chip directly. + */ + priv->parent_irqs[i] = parent_irq.args[0]; + } + + parent = of_irq_find_parent(to_of_node(dev->fwnode)); + parent_domain = irq_find_host(parent); + hier_domain = irq_domain_add_hierarchy(parent_domain, 0, MPFS_MAX_IRQS_PER_GPIO * 3, + to_of_node(dev->fwnode), &mpfs_irq_mux_domain_ops, + priv); + if (!hier_domain) { + ret = -ENODEV; + dev_err(dev, "failed to add hierarchical domain\n"); + goto out_unmap; + } + + /* + * The last 3 interrupts must be the non-direct/muxed ones, per + * the dt-binding. + */ + for (i = 0; i < MPFS_MUX_NUM_NON_DIRECT_IRQS; i++) { + int irq_index = i + MPFS_MUX_NUM_DIRECT_IRQS; + + priv->nondirect_irqchips[i].bank = i; + priv->nondirect_irqchips[i].irq = irq_of_parse_and_map(to_of_node(dev->fwnode), + irq_index); + priv->nondirect_irqchips[i].offset = i * MPFS_MAX_IRQS_PER_GPIO; + irq_set_chained_handler_and_data(priv->nondirect_irqchips[i].irq, + mpfs_irq_mux_nondirect_handler, + &priv->nondirect_irqchips[i]); + } + + dev_info(dev, "mux configuration %x\n", priv->mux_config); + + return 0; + +out_unmap: + iounmap(base); + return ret; +} + +static const struct of_device_id mpfs_irq_mux_match[] = { + { .compatible = "microchip,mpfs-gpio-irq-mux" }, + {} +}; + +static struct platform_driver mpfs_irq_mux_driver = { + .driver = { + .name = "mpfs_irq_mux", + .of_match_table = mpfs_irq_mux_match, + }, + .probe = mpfs_irq_mux_probe, +}; +builtin_platform_driver(mpfs_irq_mux_driver); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/irqchip/Kconfig linux-microchip/drivers/irqchip/Kconfig --- linux-6.6.51/drivers/irqchip/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/irqchip/Kconfig 2024-11-26 14:02:37.494493625 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:673 @ help Support for Microchip External Interrupt Controller. +config POLARFIRE_SOC_IRQ_MUX + bool "Microchip PolarFire SoC's GPIO IRQ Mux" + depends on SOC_MICROCHIP_POLARFIRE + select REGMAP + select REGMAP_MMIO + default y + help + Support for the interrupt mux on Polarfire SoC. It sits between + the GPIO controllers and the PLIC, as only 35 interrupts are shared + between 3 GPIO controllers with 32 interrupts each. Configuration of + the mux is done by the platform firmware, this driver is responsible + for reading that configuration and setting up correct routing. + config SUNPLUS_SP7021_INTC bool "Sunplus SP7021 interrupt controller" if COMPILE_TEST default SOC_SP7021 diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/irqchip/Makefile linux-microchip/drivers/irqchip/Makefile --- linux-6.6.51/drivers/irqchip/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/irqchip/Makefile 2024-11-26 14:02:37.494493625 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:122 @ obj-$(CONFIG_IRQ_IDT3243X) += irq-idt3243x.o obj-$(CONFIG_APPLE_AIC) += irq-apple-aic.o obj-$(CONFIG_MCHP_EIC) += irq-mchp-eic.o +obj-$(CONFIG_POLARFIRE_SOC_IRQ_MUX) += irq-mpfs-mux.o obj-$(CONFIG_SUNPLUS_SP7021_INTC) += irq-sp7021-intc.o diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mailbox/Kconfig linux-microchip/drivers/mailbox/Kconfig --- linux-6.6.51/drivers/mailbox/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/mailbox/Kconfig 2024-11-26 14:02:37.514493983 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:180 @ tristate "PolarFire SoC (MPFS) Mailbox" depends on HAS_IOMEM depends on ARCH_MICROCHIP_POLARFIRE || COMPILE_TEST + depends on MFD_SYSCON help This driver adds support for the PolarFire SoC (MPFS) mailbox controller. @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:189 @ If unsure, say N. +config MIV_IHC + tristate "Mi-V Inter-hart Communication (IHC) driver" + depends on RISCV_SBI + depends on SOC_MICROCHIP_POLARFIRE || COMPILE_TEST + help + This driver adds support for the inter-hart + communication mailbox controller driver. + + To compile this driver as a module, choose M here. the + module will be called mailbox-miv-ihc. + + If unsure, say N. + +config MCHP_SBI_IPC_MBOX + tristate "Microchip Inter-processor Communication (IPC) SBI driver" + depends on RISCV_SBI + help + Mailbox implementation for Microchip devices with an + Inter-process communication (IPC) controller. + + To compile this driver as a module, choose M here. the + module will be called mailbox-mchp-ipc-sbi. + + If unsure, say N. + config QCOM_APCS_IPC tristate "Qualcomm APCS IPC driver" depends on ARCH_QCOM || COMPILE_TEST diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mailbox/mailbox-mchp-ipc-sbi.c linux-microchip/drivers/mailbox/mailbox-mchp-ipc-sbi.c --- linux-6.6.51/drivers/mailbox/mailbox-mchp-ipc-sbi.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/mailbox/mailbox-mchp-ipc-sbi.c 2024-11-26 14:02:37.514493983 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microchip Inter-Processor communication (IPC) driver + * + * Copyright (c) 2021 - 2024 Microchip Technology Inc. All rights reserved. + * + * Author: Valentina Fernandez <valentina.fernandezalanis@microchip.com> + * + */ + +#include <linux/io.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/of_device.h> +#include <linux/interrupt.h> +#include <linux/dma-mapping.h> +#include <linux/platform_device.h> +#include <linux/mailbox/mchp-ipc.h> +#include <asm/sbi.h> +#include <asm/smp.h> +#include <asm/vendorid_list.h> + +#define IRQ_STATUS_BITS 12 +#define NUM_CHANS_PER_CLUSTER 5 +#define IPC_DMA_BIT_MASK 32 +#define SBI_EXT_MICROCHIP_TECHNOLOGY (SBI_EXT_VENDOR_START | \ + MICROCHIP_VENDOR_ID) + +enum { + SBI_EXT_IPC_PROBE = 0x100, + SBI_EXT_IPC_CH_INIT, + SBI_EXT_IPC_SEND, + SBI_EXT_IPC_RECEIVE, + SBI_EXT_IPC_STATUS, +}; + +enum ipc_hw { + MIV_IHC, + RESERVED1, + RESERVED2, +}; + +enum ipc_irq_type { + IPC_OPS_NOT_SUPPORTED = 1, + IPC_MP_IRQ = 2, + IPC_MC_IRQ = 4, +}; + +/** + * struct mchp_ipc_probe - IPC probe message format + * + * @hw_type: IPC implementation available in the hardware + * @num_channels: number of IPC channels available in the hardware + * + * Used to retrieve information on the IPC implementation + * using the SBI_EXT_IPC_PROBE SBI function id. + */ +struct mchp_ipc_probe { + enum ipc_hw hw_type; + u8 num_channels; +}; + +/** + * struct mchp_ipc_init - IPC channel init message format + * + * @max_msg_size: maxmimum message size in bytes of a given channel + * + * struct used by the SBI_EXT_IPC_CH_INIT SBI function id to get + * the max message size in bytes of the initialized channel. + */ +struct mchp_ipc_init { + u16 max_msg_size; +}; + +/** + * struct mchp_ipc_status - IPC status message format + * + * @status: interrupt status for all channels associated to a cluster + * @cluster: specifies the cluster instance that originated an irq + * + * struct used by the SBI_EXT_IPC_STATUS SBI function id to get + * the message present and message clear interrupt status for all the + * channels associated to a cluster. + */ +struct mchp_ipc_status { + u32 status; + u8 cluster; +}; + +/** + * struct mchp_ipc_sbi_msg - IPC SBI payload message + * + * @buf_addr: physical address where the received data should be copied to + * @size: maximum size(in bytes) that can be stored in the buffer pointed to by `buf` + * @irq_type: mask representing the irq types that triggered an irq + * + * struct used by the SBI_EXT_IPC_SEND/SBI_EXT_IPC_RECEIVE SBI function + * ids to send/receive a message from an associated processor using + * the IPC. + */ +struct mchp_ipc_sbi_msg { + u64 buf_addr; + u16 size; + u8 irq_type; +}; + +struct mchp_ipc_cluster_cfg { + void *buf_base; + dma_addr_t dma_addr; + int irq; +}; + +struct ipc_chan_info { + void *buf_base_tx; + void *buf_base_rx; + void *msg_buf_tx; + void *msg_buf_rx; + dma_addr_t dma_addr_tx; + dma_addr_t dma_addr_rx; + dma_addr_t msg_buf_dma_tx; + dma_addr_t msg_buf_dma_rx; + int chan_aggregated_irq; + int mp_irq; + int mc_irq; + u32 id; + u32 max_msg_size; +}; + +struct microchip_ipc { + struct device *dev; + struct mbox_chan *chans; + struct mchp_ipc_cluster_cfg *cluster_cfg; + struct ipc_chan_info *priv; + void *buf_base; + dma_addr_t dma_addr; + struct mbox_controller controller; + u8 num_channels; + enum ipc_hw hw_type; +}; + +static int mchp_ipc_sbi_chan_send(u32 command, u32 channel, dma_addr_t address) +{ + struct sbiret ret; + + ret = sbi_ecall(SBI_EXT_MICROCHIP_TECHNOLOGY, command, channel, + address, 0, 0, 0, 0); + + if (ret.error) + return sbi_err_map_linux_errno(ret.error); + else + return ret.value; +} + +static int mchp_ipc_sbi_send(u32 command, dma_addr_t address) +{ + struct sbiret ret; + + ret = sbi_ecall(SBI_EXT_MICROCHIP_TECHNOLOGY, command, address, + 0, 0, 0, 0, 0); + + if (ret.error) + return sbi_err_map_linux_errno(ret.error); + else + return ret.value; +} + +static struct microchip_ipc *to_mchp_ipc_mbox(struct mbox_controller *mbox) +{ + return container_of(mbox, struct microchip_ipc, controller); +} + +u32 mchp_ipc_get_chan_id(struct mbox_chan *chan) +{ + struct ipc_chan_info *chan_info = (struct ipc_chan_info *)chan->con_priv; + + return chan_info->id; +} +EXPORT_SYMBOL(mchp_ipc_get_chan_id); + +static inline void mchp_ipc_prepare_receive_req(struct mbox_chan *chan) +{ + struct ipc_chan_info *chan_info = (struct ipc_chan_info *)chan->con_priv; + struct mchp_ipc_sbi_msg request; + + request.buf_addr = chan_info->msg_buf_dma_rx; + request.size = chan_info->max_msg_size; + memcpy(chan_info->buf_base_rx, &request, sizeof(struct mchp_ipc_sbi_msg)); +} + +static inline void mchp_ipc_process_received_data(struct mbox_chan *chan, + struct mchp_ipc_msg *ipc_msg) +{ + struct ipc_chan_info *chan_info = (struct ipc_chan_info *)chan->con_priv; + struct mchp_ipc_sbi_msg sbi_msg; + + memcpy(&sbi_msg, chan_info->buf_base_rx, sizeof(struct mchp_ipc_sbi_msg)); + ipc_msg->buf = (u32 *)chan_info->msg_buf_rx; + ipc_msg->size = sbi_msg.size; +} + +static irqreturn_t mchp_ipc_cluster_aggr_isr(int irq, void *data) +{ + struct mbox_chan *chan; + struct ipc_chan_info *chan_info; + struct microchip_ipc *ipc = (struct microchip_ipc *)data; + struct mchp_ipc_msg ipc_msg; + struct mchp_ipc_status status_msg; + int ret; + unsigned long hartid; + u32 i, chan_index, chan_id; + + /* Find out the hart that originated the irq */ + for_each_online_cpu(i) { + hartid = cpuid_to_hartid_map(i); + if (irq == ipc->cluster_cfg[hartid].irq) + break; + } + + status_msg.cluster = hartid; + memcpy(ipc->cluster_cfg[hartid].buf_base, &status_msg, sizeof(struct mchp_ipc_status)); + + ret = mchp_ipc_sbi_send(SBI_EXT_IPC_STATUS, ipc->cluster_cfg[hartid].dma_addr); + if (ret < 0) { + dev_err_ratelimited(ipc->dev, "could not get IHC irq status ret=%d\n", ret); + return IRQ_HANDLED; + } + + memcpy(&status_msg, ipc->cluster_cfg[hartid].buf_base, sizeof(struct mchp_ipc_status)); + + /* + * Iterate over each bit set in the IHC interrupt status register (IRQ_STATUS) to identify + * the channel(s) that have a message to be processed/acknowledged. + * The bits are organized in alternating format, where each pair of bits represents + * the status of the message present and message clear interrupts for each cluster/hart + * (from hart 0 to hart 5). Each cluster can have up to 5 fixed channels associated. + */ + + for_each_set_bit(i, (unsigned long *)&status_msg.status, IRQ_STATUS_BITS) { + /* Find out the destination hart that triggered the interrupt */ + chan_index = i / 2; + + /* + * The IP has no loopback channels, so we need to decrement the index when + * the target hart has a greater index than our own + */ + if (chan_index >= status_msg.cluster) + chan_index--; + + /* + * Calculate the channel id given the hart and channel index. Channel IDs + * are unique across all clusters of an IPC, and iterate contiguously + * across all clusters. + */ + chan_id = status_msg.cluster * (NUM_CHANS_PER_CLUSTER + chan_index); + + chan = &ipc->chans[chan_id]; + chan_info = (struct ipc_chan_info *)chan->con_priv; + + if (i % 2 == 0) { + mchp_ipc_prepare_receive_req(chan); + ret = mchp_ipc_sbi_chan_send(SBI_EXT_IPC_RECEIVE, chan_id, + chan_info->dma_addr_rx); + if (ret < 0) + continue; + + mchp_ipc_process_received_data(chan, &ipc_msg); + mbox_chan_received_data(&ipc->chans[chan_id], (void *)&ipc_msg); + + } else { + ret = mchp_ipc_sbi_chan_send(SBI_EXT_IPC_RECEIVE, chan_id, + chan_info->dma_addr_rx); + mbox_chan_txdone(&ipc->chans[chan_id], ret); + } + } + return IRQ_HANDLED; +} + +static irqreturn_t mchp_ipc_isr(int irq, void *data) +{ + struct mbox_chan *chan = (struct mbox_chan *)data; + struct ipc_chan_info *chan_info = (struct ipc_chan_info *)chan->con_priv; + struct microchip_ipc *ipc = to_mchp_ipc_mbox(chan->mbox); + struct mchp_ipc_msg ipc_msg; + int ret; + + mchp_ipc_prepare_receive_req(chan); + + ret = mchp_ipc_sbi_chan_send(SBI_EXT_IPC_RECEIVE, chan_info->id, chan_info->dma_addr_rx); + if (ret < 0) { + dev_err_ratelimited(ipc->dev, + "failed to receive message on channel %d, ret = %d\n", + chan_info->id, + ret); + return IRQ_HANDLED; + } + + if (irq == chan_info->mp_irq) { + mchp_ipc_process_received_data(chan, &ipc_msg); + mbox_chan_received_data(&ipc->chans[chan_info->id], (void *)&ipc_msg); + } + + if (irq == chan_info->mc_irq) + mbox_chan_txdone(&ipc->chans[chan_info->id], ret); + + return IRQ_HANDLED; +} + +static irqreturn_t mchp_ipc_chan_aggr_isr(int irq, void *data) +{ + struct mbox_chan *chan = (struct mbox_chan *)data; + struct ipc_chan_info *chan_info = (struct ipc_chan_info *)chan->con_priv; + struct microchip_ipc *ipc = to_mchp_ipc_mbox(chan->mbox); + struct mchp_ipc_sbi_msg sbi_msg; + struct mchp_ipc_msg ipc_msg; + int ret; + + mchp_ipc_prepare_receive_req(chan); + + ret = mchp_ipc_sbi_chan_send(SBI_EXT_IPC_RECEIVE, chan_info->id, chan_info->dma_addr_rx); + if (ret < 0) { + dev_err_ratelimited(ipc->dev, + "failed to receive message on channel %d, ret = %d\n", + chan_info->id, + ret); + return IRQ_HANDLED; + } + + memcpy(&sbi_msg, chan_info->buf_base_rx, sizeof(struct mchp_ipc_sbi_msg)); + + if (sbi_msg.irq_type & IPC_MP_IRQ) { + ipc_msg.buf = (u32 *)chan_info->msg_buf_rx; + ipc_msg.size = sbi_msg.size; + mbox_chan_received_data(&ipc->chans[chan_info->id], (void *)&ipc_msg); + } + + if (sbi_msg.irq_type & IPC_MC_IRQ) + mbox_chan_txdone(&ipc->chans[chan_info->id], ret); + + return IRQ_HANDLED; +} + +static int mchp_ipc_get_irq(struct microchip_ipc *ipc, u32 id, const char *name) +{ + struct platform_device *pdev = to_platform_device(ipc->dev); + char *irq_name; + + irq_name = devm_kasprintf(ipc->dev, GFP_KERNEL, "ch%u-%s", id, name); + if (!irq_name) + return -ENOMEM; + + return platform_get_irq_byname_optional(pdev, irq_name); +} + +static int mchp_ipc_get_cluster_aggr_irq(struct microchip_ipc *ipc) +{ + struct platform_device *pdev = to_platform_device(ipc->dev); + char *irq_name; + int cpuid, ret; + unsigned long hartid; + bool irq_found = false; + + for_each_online_cpu(cpuid) { + hartid = cpuid_to_hartid_map(cpuid); + irq_name = devm_kasprintf(ipc->dev, GFP_KERNEL, "hart-%lu", hartid); + ret = platform_get_irq_byname_optional(pdev, irq_name); + if (ret <= 0) + continue; + + ipc->cluster_cfg[hartid].irq = ret; + ret = devm_request_irq(ipc->dev, ipc->cluster_cfg[hartid].irq, + mchp_ipc_cluster_aggr_isr, IRQF_SHARED, + "miv-ihc-irq", ipc); + if (ret) + return ret; + + ipc->cluster_cfg[hartid].buf_base = dmam_alloc_coherent(ipc->dev, + sizeof(struct mchp_ipc_status), + &ipc->cluster_cfg[hartid].dma_addr, + GFP_KERNEL); + + if (!ipc->cluster_cfg[hartid].buf_base) + return -ENOMEM; + + irq_found = true; + } + + return irq_found; +} + +static int mchp_ipc_get_chan_aggr_irq(struct microchip_ipc *ipc, u32 channel) +{ + struct platform_device *pdev = to_platform_device(ipc->dev); + char *irq_name; + + irq_name = devm_kasprintf(ipc->dev, GFP_KERNEL, "ch%u", channel); + return platform_get_irq_byname_optional(pdev, irq_name); +} + +static int mchp_ipc_send_data(struct mbox_chan *chan, void *data) +{ + struct ipc_chan_info *chan_info = (struct ipc_chan_info *)chan->con_priv; + const struct mchp_ipc_msg *msg = data; + struct mchp_ipc_sbi_msg sbi_payload; + + memcpy(chan_info->msg_buf_tx, msg->buf, msg->size); + sbi_payload.buf_addr = chan_info->msg_buf_dma_tx; + sbi_payload.size = msg->size; + memcpy(chan_info->buf_base_tx, &sbi_payload, sizeof(sbi_payload)); + + return mchp_ipc_sbi_chan_send(SBI_EXT_IPC_SEND, chan_info->id, chan_info->dma_addr_tx); +} + +static int mchp_ipc_startup(struct mbox_chan *chan) +{ + struct ipc_chan_info *chan_info = (struct ipc_chan_info *)chan->con_priv; + struct microchip_ipc *ipc = to_mchp_ipc_mbox(chan->mbox); + struct mchp_ipc_init ch_init_msg; + int ret; + + chan_info->buf_base_tx = dma_alloc_coherent(ipc->dev, sizeof(struct mchp_ipc_sbi_msg), + &chan_info->dma_addr_tx, GFP_KERNEL); + if (!chan_info->buf_base_tx) { + ret = -ENOMEM; + goto fail; + } + + chan_info->buf_base_rx = dma_alloc_coherent(ipc->dev, sizeof(struct mchp_ipc_sbi_msg), + &chan_info->dma_addr_rx, GFP_KERNEL); + if (!chan_info->buf_base_rx) { + ret = -ENOMEM; + goto fail_free_buf_base_tx; + } + + ret = mchp_ipc_sbi_chan_send(SBI_EXT_IPC_CH_INIT, chan_info->id, chan_info->dma_addr_tx); + if (ret < 0) { + dev_err(ipc->dev, "channel %u init failed\n", chan_info->id); + goto fail_free_buf_base_rx; + } + + memcpy(&ch_init_msg, chan_info->buf_base_tx, sizeof(struct mchp_ipc_init)); + chan_info->max_msg_size = ch_init_msg.max_msg_size; + + chan_info->msg_buf_tx = dma_alloc_coherent(ipc->dev, chan_info->max_msg_size, + &chan_info->msg_buf_dma_tx, GFP_KERNEL); + if (!chan_info->msg_buf_tx) { + ret = -ENOMEM; + goto fail_free_buf_base_rx; + } + + chan_info->msg_buf_rx = dma_alloc_coherent(ipc->dev, chan_info->max_msg_size, + &chan_info->msg_buf_dma_rx, GFP_KERNEL); + if (!chan_info->msg_buf_rx) { + ret = -ENOMEM; + goto fail_free_buf_msg_tx; + } + + switch (ipc->hw_type) { + case MIV_IHC: + return 0; + case RESERVED1: + ret = devm_request_irq(ipc->dev, chan_info->mp_irq, mchp_ipc_isr, IRQF_SHARED, + "ipc-irq-mp", &ipc->chans[chan_info->id]); + if (ret) + goto fail_free_buf_msg_rx; + ret = devm_request_irq(ipc->dev, chan_info->mc_irq, mchp_ipc_isr, IRQF_SHARED, + "ipc-irq-mc", &ipc->chans[chan_info->id]); + break; + case RESERVED2: + ret = devm_request_irq(ipc->dev, chan_info->chan_aggregated_irq, + mchp_ipc_chan_aggr_isr, IRQF_SHARED, "ipc-irq", + &ipc->chans[chan_info->id]); + break; + default: + goto fail_free_buf_msg_rx; + } + + if (ret) { + dev_err(ipc->dev, "failed to register interrupt(s)\n"); + goto fail_free_buf_msg_rx; + } + + return ret; + +fail_free_buf_msg_rx: + dma_free_coherent(ipc->dev, chan_info->max_msg_size, + chan_info->msg_buf_rx, chan_info->msg_buf_dma_rx); +fail_free_buf_msg_tx: + dma_free_coherent(ipc->dev, chan_info->max_msg_size, + chan_info->msg_buf_tx, chan_info->msg_buf_dma_tx); +fail_free_buf_base_rx: + dma_free_coherent(ipc->dev, sizeof(struct mchp_ipc_sbi_msg), + chan_info->buf_base_rx, chan_info->dma_addr_rx); +fail_free_buf_base_tx: + dma_free_coherent(ipc->dev, sizeof(struct mchp_ipc_sbi_msg), + chan_info->buf_base_tx, chan_info->dma_addr_tx); +fail: + return ret; +} + +static void mchp_ipc_shutdown(struct mbox_chan *chan) +{ + struct ipc_chan_info *chan_info = (struct ipc_chan_info *)chan->con_priv; + struct microchip_ipc *ipc = to_mchp_ipc_mbox(chan->mbox); + + switch (ipc->hw_type) { + case RESERVED1: + devm_free_irq(ipc->dev, chan_info->mp_irq, chan); + devm_free_irq(ipc->dev, chan_info->mc_irq, chan); + break; + case RESERVED2: + devm_free_irq(ipc->dev, chan_info->chan_aggregated_irq, chan); + break; + default: + break; + } + dma_free_coherent(ipc->dev, chan_info->max_msg_size, + chan_info->msg_buf_tx, chan_info->msg_buf_dma_tx); + + dma_free_coherent(ipc->dev, chan_info->max_msg_size, + chan_info->msg_buf_rx, chan_info->msg_buf_dma_rx); + + dma_free_coherent(ipc->dev, sizeof(struct mchp_ipc_sbi_msg), + chan_info->buf_base_tx, chan_info->dma_addr_tx); + + dma_free_coherent(ipc->dev, sizeof(struct mchp_ipc_sbi_msg), + chan_info->buf_base_rx, chan_info->dma_addr_rx); +} + +static const struct mbox_chan_ops mchp_ipc_ops = { + .startup = mchp_ipc_startup, + .send_data = mchp_ipc_send_data, + .shutdown = mchp_ipc_shutdown, +}; + +static struct mbox_chan *mchp_ipc_mbox_xlate(struct mbox_controller *controller, + const struct of_phandle_args *spec) +{ + struct microchip_ipc *ipc = to_mchp_ipc_mbox(controller); + unsigned int chan_id = spec->args[0]; + + if (chan_id >= ipc->num_channels) { + dev_err(ipc->dev, "invalid channel id %d\n", chan_id); + return ERR_PTR(-EINVAL); + } + + return &ipc->chans[chan_id]; +} + +static const struct of_device_id mchp_ipc_of_match[] = { + {.compatible = "microchip,sbi-ipc", }, + {} +}; + +static int mchp_ipc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mchp_ipc_probe ipc_info; + struct microchip_ipc *ipc; + struct ipc_chan_info *priv; + bool irq_avail = false; + int ret; + u32 chan_id; + + ret = sbi_probe_extension(SBI_EXT_MICROCHIP_TECHNOLOGY); + if (ret <= 0) + return dev_err_probe(dev, ret, "Microchip SBI extension not detected\n"); + + ipc = devm_kzalloc(dev, sizeof(*ipc), GFP_KERNEL); + if (!ipc) + return -ENOMEM; + + platform_set_drvdata(pdev, ipc); + + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(IPC_DMA_BIT_MASK)); + if (ret) + return dev_err_probe(dev, ret, "dma_set_mask_and_coherent failed\n"); + + ipc->buf_base = dmam_alloc_coherent(dev, sizeof(u32), &ipc->dma_addr, GFP_KERNEL); + + if (!ipc->buf_base) + return -ENOMEM; + + ret = mchp_ipc_sbi_send(SBI_EXT_IPC_PROBE, ipc->dma_addr); + if (ret < 0) + return dev_err_probe(dev, ret, "could not probe IPC SBI service\n"); + + memcpy(&ipc_info, ipc->buf_base, sizeof(struct mchp_ipc_probe)); + ipc->num_channels = ipc_info.num_channels; + ipc->hw_type = ipc_info.hw_type; + + ipc->chans = devm_kcalloc(dev, ipc->num_channels, sizeof(*ipc->chans), GFP_KERNEL); + if (!ipc->chans) + return -ENOMEM; + + ipc->dev = dev; + ipc->controller.txdone_irq = true; + ipc->controller.dev = ipc->dev; + ipc->controller.ops = &mchp_ipc_ops; + ipc->controller.chans = ipc->chans; + ipc->controller.num_chans = ipc->num_channels; + ipc->controller.of_xlate = mchp_ipc_mbox_xlate; + + for (chan_id = 0; chan_id < ipc->num_channels; chan_id++) { + priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + ipc->chans[chan_id].con_priv = priv; + priv->id = chan_id; + + if (ipc->hw_type == RESERVED1) { + priv->mp_irq = mchp_ipc_get_irq(ipc, chan_id, "mp"); + priv->mc_irq = mchp_ipc_get_irq(ipc, chan_id, "mc"); + + if (priv->mp_irq > 0 && priv->mc_irq > 0) + irq_avail = true; + } + + if (ipc->hw_type == RESERVED2) { + priv->chan_aggregated_irq = mchp_ipc_get_chan_aggr_irq(ipc, chan_id); + if (priv->chan_aggregated_irq > 0) + irq_avail = true; + } + } + + if (ipc->hw_type == MIV_IHC) { + ipc->cluster_cfg = devm_kcalloc(dev, num_online_cpus(), + sizeof(struct mchp_ipc_cluster_cfg), + GFP_KERNEL); + if (!ipc->cluster_cfg) + return -ENOMEM; + + if (mchp_ipc_get_cluster_aggr_irq(ipc)) + irq_avail = true; + } + + if (!irq_avail) + return dev_err_probe(dev, -ENODEV, "missing interrupt property\n"); + + ret = devm_mbox_controller_register(dev, &ipc->controller); + if (ret) + return dev_err_probe(dev, ret, + "Inter-Processor communication (IPC) registration failed\n"); + + return 0; +} + +MODULE_DEVICE_TABLE(of, mchp_ipc_of_match); + +static struct platform_driver mchp_ipc_driver = { + .driver = { + .name = "microchip_ipc", + .of_match_table = of_match_ptr(mchp_ipc_of_match), + }, + .probe = mchp_ipc_probe, +}; + +module_platform_driver(mchp_ipc_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Valentina Fernandez <valentina.fernandezalanis@microchip.com>"); +MODULE_DESCRIPTION("Microchip Inter-Processor Communication (IPC) driver"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mailbox/mailbox-miv-ihc.c linux-microchip/drivers/mailbox/mailbox-miv-ihc.c --- linux-6.6.51/drivers/mailbox/mailbox-miv-ihc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/mailbox/mailbox-miv-ihc.c 2024-11-26 14:02:37.514493983 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Mi-V Inter-hart communication (IHC) driver + * + * Copyright (c) 2021 - 2022 Microchip Technology Inc. All rights reserved. + * + * Author: Valentina Fernandez <valentina.fernandezalanis@microchip.com> + * + */ + +#include <linux/io.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/dmapool.h> +#include <linux/interrupt.h> +#include <linux/dma-mapping.h> +#include <linux/platform_device.h> +#include <linux/mailbox/miv_ihc.h> +#include <asm/sbi.h> + +#define SBI_EXT_VENDOR_START 0x09000000 +#define MICROCHIP_TECHNOLOGY_MVENDOR_ID 0x029 + +#define SBI_EXT_MICROCHIP_TECHNOLOGY (SBI_EXT_VENDOR_START | \ + MICROCHIP_TECHNOLOGY_MVENDOR_ID) + +enum { + IHC_MP_IRQ, + IHC_ACK_IRQ, +}; + +struct ihc_sbi_msg { + struct miv_ihc_msg ihc_msg; + u8 irq_type; +}; + +static int ihc_sbi_send(u32 command, u32 remote_context_id, dma_addr_t address) +{ + struct sbiret ret; + + ret = sbi_ecall(SBI_EXT_MICROCHIP_TECHNOLOGY, command, remote_context_id, + address, 0, 0, 0, 0); + + if (ret.error) + return sbi_err_map_linux_errno(ret.error); + else + return ret.value; +} + +static int ihc_sbi_init(u32 remote_context_id) +{ + struct sbiret ret; + + ret = sbi_ecall(SBI_EXT_MICROCHIP_TECHNOLOGY, SBI_EXT_IHC_INIT, remote_context_id, + 0, 0, 0, 0, 0); + + if (ret.error) + return sbi_err_map_linux_errno(ret.error); + else + return ret.value; +} + +static struct miv_ihc *mbox_chan_to_ihc(struct mbox_chan *chan) +{ + if (!chan || !chan->con_priv) + return NULL; + + return (struct miv_ihc *)chan->con_priv; +} + +static irqreturn_t ihc_isr(int irq, void *data) +{ + struct ihc_sbi_msg sbi_rx_msg; + struct mbox_chan *chan = (struct mbox_chan *)data; + struct miv_ihc *ihc = mbox_chan_to_ihc(chan); + int ret; + + ret = ihc_sbi_send(SBI_EXT_IHC_RX, ihc->remote_context_id, ihc->dma_addr); + + if (unlikely(ret < 0)) { + dev_warn_ratelimited(ihc->dev, "incorrect remote context ID\n"); + return IRQ_NONE; + } + + memcpy(&sbi_rx_msg, ihc->buf_base, sizeof(sbi_rx_msg)); + + if (sbi_rx_msg.irq_type == IHC_MP_IRQ) + mbox_chan_received_data(&ihc->channel, &sbi_rx_msg.ihc_msg); + else + mbox_chan_txdone(&ihc->channel, 0); + + return IRQ_HANDLED; +} + +static int ihc_send_data(struct mbox_chan *chan, void *data) +{ + struct miv_ihc *ihc = mbox_chan_to_ihc(chan); + struct miv_ihc_msg *message = data; + int ret; + + memcpy(ihc->buf_base, message, sizeof(*message)); + + ret = ihc_sbi_send(SBI_EXT_IHC_TX, ihc->remote_context_id, ihc->dma_addr); + + return ret; +} + +static int ihc_startup(struct mbox_chan *chan) +{ + struct miv_ihc *ihc = mbox_chan_to_ihc(chan); + int ret; + + ret = devm_request_irq(ihc->dev, ihc->irq, ihc_isr, IRQF_SHARED, + "miv-ihc-irq", &ihc->channel); + + if (ret) + dev_err(ihc->dev, "failed to register interrupt:%d\n", ret); + + return ret; +} + +static void ihc_shutdown(struct mbox_chan *chan) +{ + struct miv_ihc *ihc = mbox_chan_to_ihc(chan); + + devm_free_irq(ihc->dev, ihc->irq, chan); +} + +static const struct mbox_chan_ops miv_ihc_ops = { + .startup = ihc_startup, + .send_data = ihc_send_data, + .shutdown = ihc_shutdown, +}; + +static int ihc_probe(struct platform_device *pdev) +{ + struct miv_ihc *ihc; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + int ret; + + ret = sbi_probe_extension(SBI_EXT_MICROCHIP_TECHNOLOGY); + if (ret <= 0) + return dev_err_probe(dev, ret, "SBI IHC extension not detected\n"); + + ihc = devm_kzalloc(dev, sizeof(*ihc), GFP_KERNEL); + if (!ihc) + return -ENOMEM; + platform_set_drvdata(pdev, ihc); + + ret = of_property_read_u32(np, "microchip,miv-ihc-remote-context-id", + &ihc->remote_context_id); + if (ret) { + return dev_err_probe(dev, ret, + "missing microchip,miv-ihc-remote-context-id property\n"); + } + + ret = ihc_sbi_init(ihc->remote_context_id); + if (ret < 0) + return dev_err_probe(dev, ret, "Context init failed\n"); + + ihc->channel.con_priv = ihc; + ihc->dev = dev; + + ihc->controller.txdone_irq = true; + ihc->controller.dev = ihc->dev; + ihc->controller.ops = &miv_ihc_ops; + ihc->controller.chans = &ihc->channel; + ihc->controller.num_chans = 1; + + ihc->irq = platform_get_irq(pdev, 0); + if (ihc->irq < 0) + return dev_err_probe(dev, ihc->irq, "unable to get IRQ\n"); + + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(39)); + if (ret) + return dev_err_probe(dev, ret, "dma_set_mask_and_coherent failed\n"); + + ihc->pool = dma_pool_create("ihc", dev, IHC_MAX_MESSAGE_SIZE, 0, 0); + if (!ihc->pool) + return dev_err_probe(dev, PTR_ERR(ihc->pool), "failed to create dma pool\n"); + + ihc->buf_base = dma_pool_alloc(ihc->pool, GFP_KERNEL, &ihc->dma_addr); + if (!ihc->buf_base) { + dma_pool_destroy(ihc->pool); + return dev_err_probe(dev, PTR_ERR(ihc->buf_base), + "failed to allocate memory for dma pool\n"); + } + + ret = devm_mbox_controller_register(dev, &ihc->controller); + if (ret) { + dma_pool_free(ihc->pool, ihc->buf_base, ihc->dma_addr); + dma_pool_destroy(ihc->pool); + return dev_err_probe(dev, ret, + "Mi-V inter-hart communication (IHC) registration failed\n"); + } + + dev_info(&pdev->dev, "Mi-V inter-hart communication (IHC) registered\n"); + return 0; +} + +static int ihc_remove(struct platform_device *pdev) +{ + struct miv_ihc *ihc = platform_get_drvdata(pdev); + + dma_pool_free(ihc->pool, ihc->buf_base, ihc->dma_addr); + dma_pool_destroy(ihc->pool); + + return 0; +} + +static const struct of_device_id ihc_of_match[] = { + { .compatible = "microchip,miv-ihc", }, + {} +}; +MODULE_DEVICE_TABLE(of, ihc_of_match); + +static struct platform_driver miv_ihc_driver = { + .driver = { + .name = "miv_ihc", + .of_match_table = ihc_of_match + }, + .probe = ihc_probe, + .remove = ihc_remove, +}; + +module_platform_driver(miv_ihc_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Valentina Fernandez <valentina.fernandezalanis@microchip.com>"); +MODULE_DESCRIPTION("Mi-V Inter-Hart Communication (IHC) driver"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mailbox/mailbox-mpfs.c linux-microchip/drivers/mailbox/mailbox-mpfs.c --- linux-6.6.51/drivers/mailbox/mailbox-mpfs.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/mailbox/mailbox-mpfs.c 2024-11-26 14:02:37.514493983 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:16 @ #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> +#include <linux/regmap.h> #include <linux/interrupt.h> +#include <linux/mfd/syscon.h> #include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include <linux/mailbox_controller.h> #include <soc/microchip/mpfs.h> +#define MESSAGE_INT_OFFSET 0x18cu #define SERVICES_CR_OFFSET 0x50u #define SERVICES_SR_OFFSET 0x54u #define MAILBOX_REG_OFFSET 0x800u @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:74 @ void __iomem *int_reg; struct mbox_chan chans[1]; struct mpfs_mss_response *response; + struct regmap *sysreg_scb, *control_scb; u16 resp_offset; }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:82 @ { u32 status; - status = readl_relaxed(mbox->ctrl_base + SERVICES_SR_OFFSET); + if (mbox->control_scb) + regmap_read(mbox->control_scb, SERVICES_SR_OFFSET, &status); + else + status = readl_relaxed(mbox->ctrl_base + SERVICES_SR_OFFSET); return status & SCB_STATUS_BUSY_MASK; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:105 @ * Failed services are intended to generated interrupts, but in reality * this does not happen, so the status must be checked here. */ - val = readl_relaxed(mbox->ctrl_base + SERVICES_SR_OFFSET); + if (mbox->control_scb) + regmap_read(mbox->control_scb, SERVICES_SR_OFFSET, &val); + else + val = readl_relaxed(mbox->ctrl_base + SERVICES_SR_OFFSET); + response->resp_status = (val & SCB_STATUS_MASK) >> SCB_STATUS_POS; return true; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:157 @ tx_trigger = (opt_sel << SCB_CTRL_POS) & SCB_CTRL_MASK; tx_trigger |= SCB_CTRL_REQ_MASK | SCB_STATUS_NOTIFY_MASK; - writel_relaxed(tx_trigger, mbox->ctrl_base + SERVICES_CR_OFFSET); + + if (mbox->control_scb) + regmap_write(mbox->control_scb, SERVICES_CR_OFFSET, tx_trigger); + else + writel_relaxed(tx_trigger, mbox->ctrl_base + SERVICES_CR_OFFSET); + return 0; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:204 @ struct mbox_chan *chan = data; struct mpfs_mbox *mbox = (struct mpfs_mbox *)chan->con_priv; - writel_relaxed(0, mbox->int_reg); + if (mbox->control_scb) + regmap_write(mbox->sysreg_scb, MESSAGE_INT_OFFSET, 0); + else + writel_relaxed(0, mbox->int_reg); mpfs_mbox_rx_data(chan); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:253 @ if (!mbox) return -ENOMEM; + mbox->control_scb = syscon_regmap_lookup_by_compatible("microchip,mpfs-control-scb"); + if (IS_ERR(mbox->control_scb)) { + mbox->control_scb = NULL; + goto old_format; + } + + mbox->sysreg_scb = syscon_regmap_lookup_by_compatible("microchip,mpfs-sysreg-scb"); + if (IS_ERR(mbox->sysreg_scb)) + return PTR_ERR(mbox->sysreg_scb); + + mbox->mbox_base = devm_platform_get_and_ioremap_resource(pdev, 0, ®s); + if (IS_ERR(mbox->ctrl_base)) + return PTR_ERR(mbox->mbox_base); + + goto done; + +old_format: mbox->ctrl_base = devm_platform_get_and_ioremap_resource(pdev, 0, ®s); if (IS_ERR(mbox->ctrl_base)) return PTR_ERR(mbox->ctrl_base); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:282 @ if (IS_ERR(mbox->mbox_base)) // account for the old dt-binding w/ 2 regs mbox->mbox_base = mbox->ctrl_base + MAILBOX_REG_OFFSET; +done: mbox->irq = platform_get_irq(pdev, 0); if (mbox->irq < 0) return mbox->irq; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:302 @ dev_err(&pdev->dev, "Registering MPFS mailbox controller failed\n"); return ret; } + dev_info(&pdev->dev, "Registered MPFS mailbox controller driver\n"); return 0; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mailbox/Makefile linux-microchip/drivers/mailbox/Makefile --- linux-6.6.51/drivers/mailbox/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/mailbox/Makefile 2024-11-26 14:02:37.514493983 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:46 @ obj-$(CONFIG_POLARFIRE_SOC_MAILBOX) += mailbox-mpfs.o +obj-$(CONFIG_MIV_IHC) += mailbox-miv-ihc.o + +obj-$(CONFIG_MCHP_SBI_IPC_MBOX) += mailbox-mchp-ipc-sbi.o + obj-$(CONFIG_QCOM_APCS_IPC) += qcom-apcs-ipc-mailbox.o obj-$(CONFIG_TEGRA_HSP_MBOX) += tegra-hsp.o diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/media/i2c/imx334.c linux-microchip/drivers/media/i2c/imx334.c --- linux-6.6.51/drivers/media/i2c/imx334.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/media/i2c/imx334.c 2024-11-26 14:02:37.570494987 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:59 @ #define IMX334_REG_MIN 0x00 #define IMX334_REG_MAX 0xfffff +/* Test Pattern Control */ +#define IMX334_REG_TP 0x329e +#define IMX334_TP_COLOR_HBARS 0xA +#define IMX334_TP_COLOR_VBARS 0xB +#define IMX334_TP_BLACK 0x0 +#define IMX334_TP_WHITE 0x1 +#define IMX334_TP_PINK 0x5 +#define IMX334_TP_BLUE 0x6 +#define IMX334_TP_RED 0x7 +#define IMX334_TP_BLACK_GREY 0xC + + +#define IMX334_TPG_EN_DOUT 0x329c +#define IMX334_TP_ENABLE 0x1 +#define IMX334_TP_DISABLE 0x0 + +#define IMX334_TPG_COLORW 0x32a0 +#define IMX334_TPG_COLORW_120P 0x13 + +#define IMX334_TP_CLK_EN 0x3148 +#define IMX334_TP_CLK_EN_VAL 0x10 +#define IMX334_TP_CLK_DIS_VAL 0x0 + +#define IMX334_DIG_CLP_MODE 0x3280 + /** * struct imx334_reg - imx334 sensor register * @address: Register address @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:179 @ IMX334_LINK_FREQ_445M, }; +/* Sensor mode registers for 640x480@30fps */ +static const struct imx334_reg mode_640x480_regs[] = { + {0x3000, 0x01}, + {0x3018, 0x04}, + {0x3030, 0xca}, + {0x3031, 0x08}, + {0x3032, 0x00}, + {0x3034, 0x4c}, + {0x3035, 0x04}, + {0x302c, 0x70}, + {0x302d, 0x06}, + {0x302e, 0x80}, + {0x302f, 0x02}, + {0x3074, 0x48}, + {0x3075, 0x07}, + {0x308e, 0x49}, + {0x308f, 0x07}, + {0x3076, 0xe0}, + {0x3077, 0x01}, + {0x3090, 0xe0}, + {0x3091, 0x01}, + {0x3308, 0xe0}, + {0x3309, 0x01}, + {0x30d8, 0x30}, + {0x30d9, 0x0b}, + {0x30C6, 0x00}, + {0x30c7, 0x00}, + {0x30ce, 0x00}, + {0x30cf, 0x00}, + {0x304c, 0x00}, + {0x304e, 0x00}, + {0x304f, 0x00}, + {0x3050, 0x00}, + {0x30b6, 0x00}, + {0x30b7, 0x00}, + {0x3116, 0x08}, + {0x3117, 0x00}, + {0x31a0, 0x20}, + {0x31a1, 0x0f}, + {0x300c, 0x3b}, + {0x300d, 0x29}, + {0x314c, 0x29}, + {0x314d, 0x01}, + {0x315a, 0x0a}, + {0x3168, 0xa0}, + {0x316a, 0x7e}, + {0x319e, 0x02}, + {0x3199, 0x00}, + {0x319d, 0x00}, + {0x31dd, 0x03}, + {0x3300, 0x00}, + {0x341c, 0xff}, + {0x341d, 0x01}, + {0x3a01, 0x03}, + {0x3a18, 0x7f}, + {0x3a19, 0x00}, + {0x3a1a, 0x37}, + {0x3a1b, 0x00}, + {0x3a1c, 0x37}, + {0x3a1d, 0x00}, + {0x3a1e, 0xf7}, + {0x3a1f, 0x00}, + {0x3a20, 0x3f}, + {0x3a21, 0x00}, + {0x3a20, 0x6f}, + {0x3a21, 0x00}, + {0x3a20, 0x3f}, + {0x3a21, 0x00}, + {0x3a20, 0x5f}, + {0x3a21, 0x00}, + {0x3a20, 0x2f}, + {0x3a21, 0x00}, + {0x3078, 0x02}, + {0x3079, 0x00}, + {0x307a, 0x00}, + {0x307b, 0x00}, + {0x3080, 0x02}, + {0x3081, 0x00}, + {0x3082, 0x00}, + {0x3083, 0x00}, + {0x3088, 0x02}, + {0x3094, 0x00}, + {0x3095, 0x00}, + {0x3096, 0x00}, + {0x309b, 0x02}, + {0x309c, 0x00}, + {0x309d, 0x00}, + {0x309e, 0x00}, + {0x30a4, 0x00}, + {0x30a5, 0x00}, + {0x3288, 0x21}, + {0x328a, 0x02}, + {0x3414, 0x05}, + {0x3416, 0x18}, + {0x35Ac, 0x0e}, + {0x3648, 0x01}, + {0x364a, 0x04}, + {0x364c, 0x04}, + {0x3678, 0x01}, + {0x367c, 0x31}, + {0x367e, 0x31}, + {0x3708, 0x02}, + {0x3714, 0x01}, + {0x3715, 0x02}, + {0x3716, 0x02}, + {0x3717, 0x02}, + {0x371c, 0x3d}, + {0x371d, 0x3f}, + {0x372c, 0x00}, + {0x372d, 0x00}, + {0x372e, 0x46}, + {0x372f, 0x00}, + {0x3730, 0x89}, + {0x3731, 0x00}, + {0x3732, 0x08}, + {0x3733, 0x01}, + {0x3734, 0xfe}, + {0x3735, 0x05}, + {0x375d, 0x00}, + {0x375e, 0x00}, + {0x375f, 0x61}, + {0x3760, 0x06}, + {0x3768, 0x1b}, + {0x3769, 0x1b}, + {0x376a, 0x1a}, + {0x376b, 0x19}, + {0x376c, 0x18}, + {0x376d, 0x14}, + {0x376e, 0x0f}, + {0x3776, 0x00}, + {0x3777, 0x00}, + {0x3778, 0x46}, + {0x3779, 0x00}, + {0x377a, 0x08}, + {0x377b, 0x01}, + {0x377c, 0x45}, + {0x377d, 0x01}, + {0x377e, 0x23}, + {0x377f, 0x02}, + {0x3780, 0xd9}, + {0x3781, 0x03}, + {0x3782, 0xf5}, + {0x3783, 0x06}, + {0x3784, 0xa5}, + {0x3788, 0x0f}, + {0x378a, 0xd9}, + {0x378b, 0x03}, + {0x378c, 0xeb}, + {0x378d, 0x05}, + {0x378e, 0x87}, + {0x378f, 0x06}, + {0x3790, 0xf5}, + {0x3792, 0x43}, + {0x3794, 0x7a}, + {0x3796, 0xa1}, + {0x37b0, 0x37}, + {0x3e04, 0x0e}, + {0x30e8, 0x50}, + {0x30e9, 0x00}, + {0x3e04, 0x0e}, + {0x3002, 0x00}, +}; + +/* Sensor mode registers for 1280x720@30fps */ +static const struct imx334_reg mode_1280x720_regs[] = { + {0x3000, 0x01}, + {0x3018, 0x04}, + {0x3030, 0xca}, + {0x3031, 0x08}, + {0x3032, 0x00}, + {0x3034, 0x4c}, + {0x3035, 0x04}, + {0x302c, 0x30}, + {0x302d, 0x05}, + {0x302e, 0x00}, + {0x302f, 0x05}, + {0x3074, 0x84}, + {0x3075, 0x03}, + {0x308e, 0x85}, + {0x308f, 0x03}, + {0x3076, 0xd0}, + {0x3077, 0x02}, + {0x3090, 0xd0}, + {0x3091, 0x02}, + {0x3308, 0xd0}, + {0x3309, 0x02}, + {0x30d8, 0x30}, + {0x30d9, 0x0b}, + {0x30C6, 0x00}, + {0x30c7, 0x00}, + {0x30ce, 0x00}, + {0x30cf, 0x00}, + {0x304c, 0x00}, + {0x304e, 0x00}, + {0x304f, 0x00}, + {0x3050, 0x00}, + {0x30b6, 0x00}, + {0x30b7, 0x00}, + {0x3116, 0x08}, + {0x3117, 0x00}, + {0x31a0, 0x20}, + {0x31a1, 0x0f}, + {0x300c, 0x3b}, + {0x300d, 0x29}, + {0x314c, 0x29}, + {0x314d, 0x01}, + {0x315a, 0x0a}, + {0x3168, 0xa0}, + {0x316a, 0x7e}, + {0x319e, 0x02}, + {0x3199, 0x00}, + {0x319d, 0x00}, + {0x31dd, 0x03}, + {0x3300, 0x00}, + {0x341c, 0xff}, + {0x341d, 0x01}, + {0x3a01, 0x03}, + {0x3a18, 0x7f}, + {0x3a19, 0x00}, + {0x3a1a, 0x37}, + {0x3a1b, 0x00}, + {0x3a1c, 0x37}, + {0x3a1d, 0x00}, + {0x3a1e, 0xf7}, + {0x3a1f, 0x00}, + {0x3a20, 0x3f}, + {0x3a21, 0x00}, + {0x3a20, 0x6f}, + {0x3a21, 0x00}, + {0x3a20, 0x3f}, + {0x3a21, 0x00}, + {0x3a20, 0x5f}, + {0x3a21, 0x00}, + {0x3a20, 0x2f}, + {0x3a21, 0x00}, + {0x3078, 0x02}, + {0x3079, 0x00}, + {0x307a, 0x00}, + {0x307b, 0x00}, + {0x3080, 0x02}, + {0x3081, 0x00}, + {0x3082, 0x00}, + {0x3083, 0x00}, + {0x3088, 0x02}, + {0x3094, 0x00}, + {0x3095, 0x00}, + {0x3096, 0x00}, + {0x309b, 0x02}, + {0x309c, 0x00}, + {0x309d, 0x00}, + {0x309e, 0x00}, + {0x30a4, 0x00}, + {0x30a5, 0x00}, + {0x3288, 0x21}, + {0x328a, 0x02}, + {0x3414, 0x05}, + {0x3416, 0x18}, + {0x35Ac, 0x0e}, + {0x3648, 0x01}, + {0x364a, 0x04}, + {0x364c, 0x04}, + {0x3678, 0x01}, + {0x367c, 0x31}, + {0x367e, 0x31}, + {0x3708, 0x02}, + {0x3714, 0x01}, + {0x3715, 0x02}, + {0x3716, 0x02}, + {0x3717, 0x02}, + {0x371c, 0x3d}, + {0x371d, 0x3f}, + {0x372c, 0x00}, + {0x372d, 0x00}, + {0x372e, 0x46}, + {0x372f, 0x00}, + {0x3730, 0x89}, + {0x3731, 0x00}, + {0x3732, 0x08}, + {0x3733, 0x01}, + {0x3734, 0xfe}, + {0x3735, 0x05}, + {0x375d, 0x00}, + {0x375e, 0x00}, + {0x375f, 0x61}, + {0x3760, 0x06}, + {0x3768, 0x1b}, + {0x3769, 0x1b}, + {0x376a, 0x1a}, + {0x376b, 0x19}, + {0x376c, 0x18}, + {0x376d, 0x14}, + {0x376e, 0x0f}, + {0x3776, 0x00}, + {0x3777, 0x00}, + {0x3778, 0x46}, + {0x3779, 0x00}, + {0x377a, 0x08}, + {0x377b, 0x01}, + {0x377c, 0x45}, + {0x377d, 0x01}, + {0x377e, 0x23}, + {0x377f, 0x02}, + {0x3780, 0xd9}, + {0x3781, 0x03}, + {0x3782, 0xf5}, + {0x3783, 0x06}, + {0x3784, 0xa5}, + {0x3788, 0x0f}, + {0x378a, 0xd9}, + {0x378b, 0x03}, + {0x378c, 0xeb}, + {0x378d, 0x05}, + {0x378e, 0x87}, + {0x378f, 0x06}, + {0x3790, 0xf5}, + {0x3792, 0x43}, + {0x3794, 0x7a}, + {0x3796, 0xa1}, + {0x37b0, 0x37}, + {0x3e04, 0x0e}, + {0x30e8, 0x50}, + {0x30e9, 0x00}, + {0x3e04, 0x0e}, + {0x3002, 0x00}, +}; + /* Sensor mode registers for 1920x1080@30fps */ static const struct imx334_reg mode_1920x1080_regs[] = { {0x3000, 0x01}, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:548 @ {0x300d, 0x29}, {0x314c, 0x29}, {0x314d, 0x01}, - {0x315a, 0x06}, + {0x315a, 0x0a}, {0x3168, 0xa0}, {0x316a, 0x7e}, {0x319e, 0x02}, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:784 @ {0x3a29, 0x00}, }; +static const char * const imx334_test_pattern_menu[] = { + "Disabled", + "Color Bars Ver", + "Color Bars Hor", + "Black and Grey Bars", + "Black Color", + "White Color", + "Pink Color", + "Blue Color", + "Red Color", +}; + +static const int imx334_test_pattern_val[] = { + IMX334_TP_DISABLE, + IMX334_TP_COLOR_HBARS, + IMX334_TP_COLOR_VBARS, + IMX334_TP_BLACK_GREY, + IMX334_TP_BLACK, + IMX334_TP_WHITE, + IMX334_TP_PINK, + IMX334_TP_BLUE, + IMX334_TP_RED, +}; + static const struct imx334_reg raw10_framefmt_regs[] = { {0x3050, 0x00}, {0x319d, 0x00}, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:855 @ .num_of_regs = ARRAY_SIZE(mode_1920x1080_regs), .regs = mode_1920x1080_regs, }, + }, { + .width = 1280, + .height = 720, + .hblank = 2480, + .vblank = 1170, + .vblank_min = 45, + .vblank_max = 132840, + .pclk = 297000000, + .link_freq_idx = 1, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1280x720_regs), + .regs = mode_1280x720_regs, + }, + }, { + .width = 640, + .height = 480, + .hblank = 2480, + .vblank = 1170, + .vblank_min = 45, + .vblank_max = 132840, + .pclk = 297000000, + .link_freq_idx = 1, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_640x480_regs), + .regs = mode_640x480_regs, + }, }, }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1120 @ case V4L2_CID_HBLANK: ret = 0; break; + case V4L2_CID_TEST_PATTERN: + if (ctrl->val) { + imx334_write_reg(imx334, IMX334_TP_CLK_EN, 1, + IMX334_TP_CLK_EN_VAL); + imx334_write_reg(imx334, IMX334_DIG_CLP_MODE, 1, 0x0); + imx334_write_reg(imx334, IMX334_TPG_COLORW, 1, + IMX334_TPG_COLORW_120P); + imx334_write_reg(imx334, IMX334_REG_TP, 1, + imx334_test_pattern_val[ctrl->val]); + imx334_write_reg(imx334, IMX334_TPG_EN_DOUT, 1, + IMX334_TP_ENABLE); + } else { + imx334_write_reg(imx334, IMX334_DIG_CLP_MODE, 1, 0x1); + imx334_write_reg(imx334, IMX334_TP_CLK_EN, 1, + IMX334_TP_CLK_DIS_VAL); + imx334_write_reg(imx334, IMX334_TPG_EN_DOUT, 1, + IMX334_TP_DISABLE); + } + ret = 0; + break; default: dev_err(imx334->dev, "Invalid control %d", ctrl->id); ret = -EINVAL; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1646 @ u32 lpfr; int ret; - ret = v4l2_ctrl_handler_init(ctrl_hdlr, 6); + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 7); if (ret) return ret; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1706 @ if (imx334->hblank_ctrl) imx334->hblank_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx334_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(imx334_test_pattern_menu) - 1, + 0, 0, imx334_test_pattern_menu); + if (ctrl_hdlr->error) { dev_err(imx334->dev, "control init failed: %d", ctrl_hdlr->error); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/media/platform/dwc/dw-csi-plat.c linux-microchip/drivers/media/platform/dwc/dw-csi-plat.c --- linux-6.6.51/drivers/media/platform/dwc/dw-csi-plat.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/media/platform/dwc/dw-csi-plat.c 2024-11-26 14:02:37.602495560 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates. + * + * Synopsys DesignWare MIPI CSI-2 Host controller driver. + * Platform driver + * + * Author: Luis Oliveira <luis.oliveira@synopsys.com> + */ + +#include <media/dwc/dw-csi-data.h> +#include <media/dwc/dw-dphy-data.h> + +#include "dw-csi-plat.h" +#include <linux/clk.h> + +const struct mipi_dt csi_dt[] = { + { + .hex = CSI_2_YUV420_8, + .name = "YUV420_8bits", + }, { + .hex = CSI_2_YUV420_10, + .name = "YUV420_10bits", + }, { + .hex = CSI_2_YUV420_8_LEG, + .name = "YUV420_8bits_LEGACY", + }, { + .hex = CSI_2_YUV420_8_SHIFT, + .name = "YUV420_8bits_SHIFT", + }, { + .hex = CSI_2_YUV420_10_SHIFT, + .name = "YUV420_10bits_SHIFT", + }, { + .hex = CSI_2_YUV422_8, + .name = "YUV442_8bits", + }, { + .hex = CSI_2_YUV422_10, + .name = "YUV442_10bits", + }, { + .hex = CSI_2_RGB444, + .name = "RGB444", + }, { + .hex = CSI_2_RGB555, + .name = "RGB555", + }, { + .hex = CSI_2_RGB565, + .name = "RGB565", + }, { + .hex = CSI_2_RGB666, + .name = "RGB666", + }, { + .hex = CSI_2_RGB888, + .name = "RGB888", + }, { + .hex = CSI_2_RAW6, + .name = "RAW6", + }, { + .hex = CSI_2_RAW7, + .name = "RAW7", + }, { + .hex = CSI_2_RAW8, + .name = "RAW8", + }, { + .hex = CSI_2_RAW10, + .name = "RAW10", + }, { + .hex = CSI_2_RAW12, + .name = "RAW12", + }, { + .hex = CSI_2_RAW14, + .name = "RAW14", + }, { + .hex = CSI_2_RAW16, + .name = "RAW16", + }, +}; + +static struct mipi_fmt * +find_dw_mipi_csi_format(struct v4l2_mbus_framefmt *mf) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(dw_mipi_csi_formats); i++) + if (mf->code == dw_mipi_csi_formats[i].mbus_code) + return &dw_mipi_csi_formats[i]; + + return NULL; +} + +static int dw_mipi_csi_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(dw_mipi_csi_formats)) + return -EINVAL; + + code->code = dw_mipi_csi_formats[code->index].mbus_code; + return 0; +} + +static struct mipi_fmt * +dw_mipi_csi_try_format(struct v4l2_mbus_framefmt *mf) +{ + struct mipi_fmt *fmt; + + fmt = find_dw_mipi_csi_format(mf); + if (!fmt) + fmt = &dw_mipi_csi_formats[0]; + + mf->code = fmt->mbus_code; + + return fmt; +} + +static struct v4l2_mbus_framefmt * +dw_mipi_csi_get_format(struct dw_csi *dev, struct v4l2_subdev_state *sd_state, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return sd_state->pads ? v4l2_subdev_get_try_format(&dev->sd, + sd_state, + 0) : NULL; + dev_dbg(dev->dev, + "%s got v4l2_mbus_pixelcode. 0x%x\n", __func__, + dev->format.code); + dev_dbg(dev->dev, + "%s got width. 0x%x\n", __func__, + dev->format.width); + dev_dbg(dev->dev, + "%s got height. 0x%x\n", __func__, + dev->format.height); + return &dev->format; +} + +static int +dw_mipi_csi_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct dw_csi *dev = sd_to_mipi_csi_dev(sd); + struct mipi_fmt *dev_fmt; + struct v4l2_mbus_framefmt *mf = dw_mipi_csi_get_format(dev, sd_state, + fmt->which); + int i; + + dev_fmt = dw_mipi_csi_try_format(&fmt->format); + + if (dev_fmt) { + *mf = fmt->format; + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + dev->fmt = dev_fmt; + dw_mipi_csi_set_ipi_fmt(dev); + } + + if (fmt->format.width > 0 && fmt->format.height > 0) { + dw_mipi_csi_fill_timings(dev, fmt); + } else { + dev_vdbg(dev->dev, "%s unacceptable values 0x%x.\n", + __func__, fmt->format.width); + dev_vdbg(dev->dev, "%s unacceptable values 0x%x.\n", + __func__, fmt->format.height); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(csi_dt); i++) + if (csi_dt[i].hex == dev->ipi_dt) { + dev_vdbg(dev->dev, "Using data type %s\n", + csi_dt[i].name); + } + return 0; +} + +static int +dw_mipi_csi_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct dw_csi *dev = sd_to_mipi_csi_dev(sd); + struct v4l2_mbus_framefmt *mf; + + mf = dw_mipi_csi_get_format(dev, sd_state, fmt->which); + if (!mf) + return -EINVAL; + + mutex_lock(&dev->lock); + fmt->format = *mf; + mutex_unlock(&dev->lock); + + return 0; +} + +static int +dw_mipi_csi_log_status(struct v4l2_subdev *sd) +{ + struct dw_csi *dev = sd_to_mipi_csi_dev(sd); + + dw_mipi_csi_dump(dev); + + return 0; +} + +#if IS_ENABLED(CONFIG_VIDEO_ADV_DEBUG) +static int +dw_mipi_csi_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) +{ + struct dw_csi *dev = sd_to_mipi_csi_dev(sd); + + dev_vdbg(dev->dev, "%s: reg=%llu\n", __func__, reg->reg); + reg->val = dw_mipi_csi_read(dev, reg->reg); + + return 0; +} +#endif + +static int dw_mipi_csi_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + struct v4l2_mbus_framefmt *format = + v4l2_subdev_get_try_format(sd, sd_state, 0); + + format->colorspace = V4L2_COLORSPACE_SRGB; + format->code = MEDIA_BUS_FMT_RGB888_1X24; + format->field = V4L2_FIELD_NONE; + + return 0; +} + +static int dw_mipi_csi_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct dw_csi *dev = sd_to_mipi_csi_dev(sd); + int ret = v4l2_subdev_call(dev->input_sd, video, s_stream, enable); + + if (enable) { + dw_mipi_csi_hw_stdby(dev); + dw_mipi_csi_start(dev); + } else { + phy_power_off(dev->phy); + dw_mipi_csi_mask_irq_power_off(dev); + /* reset data type */ + dev->ipi_dt = 0x0; + } + return ret; +} + +static const struct v4l2_subdev_core_ops dw_mipi_csi_core_ops = { + .log_status = dw_mipi_csi_log_status, +#if IS_ENABLED(CONFIG_VIDEO_ADV_DEBUG) + .g_register = dw_mipi_csi_g_register, +#endif +}; + +static int dw_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad, + struct v4l2_mbus_config *cfg) +{ + cfg->bus.mipi_csi2.num_data_lanes = 2; + cfg->type = V4L2_MBUS_CSI2_DPHY; + + return 0; +} + +static int dw_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index) + return -EINVAL; + + fse->min_width = 16; + fse->max_width = 4000; + fse->min_height = 16; + fse->max_height = 3000; + + return 0; +} + +static struct v4l2_subdev_pad_ops dw_mipi_csi_pad_ops = { + .init_cfg = dw_mipi_csi_init_cfg, + .enum_mbus_code = dw_mipi_csi_enum_mbus_code, + .enum_frame_size = dw_enum_frame_size, + .get_fmt = dw_mipi_csi_get_fmt, + .set_fmt = dw_mipi_csi_set_fmt, + .get_mbus_config = dw_get_mbus_config, +}; + +static const struct v4l2_subdev_video_ops dw_mipi_csi_video_ops = { + .s_stream = dw_mipi_csi_s_stream, +}; + +static const struct v4l2_subdev_ops dw_mipi_csi_subdev_ops = { + .core = &dw_mipi_csi_core_ops, + .pad = &dw_mipi_csi_pad_ops, + .video = &dw_mipi_csi_video_ops, +}; + +static irqreturn_t dw_mipi_csi_irq1(int irq, void *dev_id) +{ + struct dw_csi *csi_dev = dev_id; + + dw_mipi_csi_irq_handler(csi_dev); + + return IRQ_HANDLED; +} + +static int dw_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *asc) +{ + struct dw_csi *dw = container_of(notifier, + struct dw_csi, notifier); + int ret; + int pad; + + dev_dbg(dw->dev, "async bound\n"); + dw->input_sd = subdev; + + pad = media_entity_get_fwnode_pad(&subdev->entity, + asc->match.fwnode, + MEDIA_PAD_FL_SOURCE); + if (pad < 0) { + dev_err(dw->dev, "Failed to find pad for %s\n", + dw->sd.name); + return pad; + } + + dw->remote_pad = pad; + + ret = media_create_pad_link(&dw->input_sd->entity, dw->remote_pad, + &dw->sd.entity, 0, MEDIA_LNK_FL_ENABLED); + + if (ret < 0) { + dev_err(dw->dev, + "Failed to create pad link: %s to %s\n", + dw->input_sd->entity.name, dw->sd.entity.name); + return ret; + } + + dev_dbg(dw->dev, "link with %s pad: %d\n", + dw->input_sd->name, dw->remote_pad); + + return ret; +} + +static const struct v4l2_async_notifier_operations csi2host_async_ops = { + .bound = dw_async_bound, +}; + +static int +dw_mipi_csi_parse_dt(struct platform_device *pdev, struct dw_csi *dev) +{ + struct device_node *of_node = pdev->dev.of_node; + struct fwnode_handle *input_fwnode, *output_fwnode; + struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY }; + struct v4l2_fwnode_endpoint ep2 = { }; + struct v4l2_async_connection *asc; + int ret = 0; + + if (of_property_read_u32(of_node, "snps,output-type", + &dev->hw.output)) + dev->hw.output = 2; + + input_fwnode = fwnode_graph_get_next_endpoint(of_fwnode_handle(of_node), + NULL); + if (!input_fwnode) { + dev_err(&pdev->dev, + "missing port node at %pOF, input node is mandatory.\n", + of_node); + return -EINVAL; + } + + /* Get port node and validate MIPI-CSI channel id. */ + ret = v4l2_fwnode_endpoint_parse(input_fwnode, &ep); + if (ret) + goto err; + + dev->index = ep.base.port - 1; + if (dev->index >= CSI_MAX_ENTITIES) { + ret = -ENXIO; + goto err; + } + dev->hw.num_lanes = ep.bus.mipi_csi2.num_data_lanes; + + output_fwnode = fwnode_graph_get_next_endpoint + (of_fwnode_handle(of_node), input_fwnode); + + if (output_fwnode) { + ret = v4l2_fwnode_endpoint_parse(output_fwnode, + &ep2); + + fwnode_handle_put(output_fwnode); + } + + if (!output_fwnode || ret) { + dev_info(&pdev->dev, + "missing output node at %pOF\n", of_node); + } + + v4l2_async_subdev_nf_init(&dev->notifier, &dev->sd); + asc = v4l2_async_nf_add_fwnode_remote(&dev->notifier, input_fwnode, + struct v4l2_async_connection); + + if (IS_ERR(asc)) { + ret = PTR_ERR(asc); + goto err; + } + + if (ret) { + dev_err(&pdev->dev, "failed to add async notifier.\n"); + goto err; + } + + dev->notifier.ops = &csi2host_async_ops; + + ret = v4l2_async_nf_register(&dev->notifier); + + if (ret) { + dev_err(&pdev->dev, "fail to register async notifier.\n"); + goto err; + } + +err: + of_node_put(of_node); + return ret; +} + +static const struct of_device_id dw_mipi_csi_of_match[]; + +static int dw_csi_probe(struct platform_device *pdev) +{ + const struct of_device_id *of_id = NULL; + struct dw_csih_pdata *pdata = NULL; + struct device *dev = &pdev->dev; + struct resource *res = NULL; + struct dw_csi *csi; + struct v4l2_subdev *sd; + int ret; + + if (!IS_ENABLED(CONFIG_OF)) + pdata = pdev->dev.platform_data; + + dev_dbg(dev, "Probing started\n"); + + /* Resource allocation */ + csi = devm_kzalloc(dev, sizeof(*csi), GFP_KERNEL); + if (!csi) + return -ENOMEM; + + mutex_init(&csi->lock); + spin_lock_init(&csi->slock); + csi->dev = dev; + + csi->perclk = devm_clk_get(dev, "perclk"); + if (IS_ERR(csi->perclk)) { + ret = PTR_ERR(csi->perclk); + dev_err(dev, "failed to get perclk: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(csi->perclk); + if (ret) { + dev_err(dev, "failed to enable perclk: %d\n", ret); + return ret; + } + + csi->phyclk = devm_clk_get(dev, "phyclk"); + if (IS_ERR(csi->perclk)) { + ret = PTR_ERR(csi->phyclk); + dev_err(dev, "failed to get phyclk: %d\n", ret); + goto csi2host_phyclk_err; + } + + ret = clk_prepare_enable(csi->phyclk); + if (ret) { + dev_err(dev, "failed to enable phyclk: %d\n", ret); + goto csi2host_phyclk_err; + } + + if (dev->of_node) { + of_id = of_match_node(dw_mipi_csi_of_match, dev->of_node); + if (!of_id) { + ret = -EINVAL; + goto csi2host_reg_err; + } + + ret = dw_mipi_csi_parse_dt(pdev, csi); + if (ret < 0) + goto csi2host_reg_err; + + csi->phy = devm_of_phy_get(dev, dev->of_node, NULL); + if (IS_ERR(csi->phy)) { + dev_dbg(dev, "No DPHY available\n"); + ret = -EPROBE_DEFER; /* attempt to defer */ + goto csi2host_defer_err; + } + } else { + if (!pdata) + goto csi2host_reg_err; + + csi->phy = devm_phy_get(dev, phys[pdata->id].name); + if (IS_ERR(csi->phy)) { + dev_err(dev, "No '%s' DPHY available\n", + phys[pdata->id].name); + return PTR_ERR(csi->phy); + } + dev_vdbg(dev, "got D-PHY %s with id %d\n", phys[pdata->id].name, + csi->phy->id); + } + /* Registers mapping */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENXIO; + goto csi2host_defer_err; + } + + csi->base_address = devm_ioremap_resource(dev, res); + if (IS_ERR(csi->base_address)) { + dev_err(dev, "Base address not set.\n"); + ret = PTR_ERR(csi->base_address); + goto csi2host_defer_err; + } + + csi->ctrl_irq_number = platform_get_irq(pdev, 0); + if (csi->ctrl_irq_number < 0) { + dev_err(dev, "irq number %d not set.\n", csi->ctrl_irq_number); + ret = csi->ctrl_irq_number; + goto end; + } + + csi->rst = devm_reset_control_get_optional_shared(dev, NULL); + if (IS_ERR(csi->rst)) { + dev_err(dev, "error getting reset control %d\n", ret); + ret = PTR_ERR(csi->rst); + goto end; + } + + ret = devm_request_irq(dev, csi->ctrl_irq_number, + dw_mipi_csi_irq1, IRQF_SHARED, + dev_name(dev), csi); + if (ret) { + if (dev->of_node) + dev_err(dev, "irq csi %s failed\n", of_id->name); + else + dev_err(dev, "irq csi %d failed\n", pdata->id); + + goto end; + } + + sd = &csi->sd; + v4l2_subdev_init(sd, &dw_mipi_csi_subdev_ops); + csi->sd.owner = THIS_MODULE; + csi->sd.fwnode = of_fwnode_handle(dev->of_node); + + if (dev->of_node) { + snprintf(sd->name, sizeof(sd->name), "%s.%d", + "dw-csi", csi->index); + + csi->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + } else { + strlcpy(sd->name, dev_name(dev), sizeof(sd->name)); + } + csi->fmt = &dw_mipi_csi_formats[0]; + csi->format.code = dw_mipi_csi_formats[0].mbus_code; + + sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; + + if (dev->of_node) { + csi->pads[CSI_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + csi->pads[CSI_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&csi->sd.entity, + CSI_PADS_NUM, csi->pads); + if (ret < 0) { + dev_err(dev, "media entity init failed\n"); + goto end; + } + } else { + csi->hw.num_lanes = pdata->lanes; + csi->hw.pclk = pdata->pclk; + csi->hw.fps = pdata->fps; + csi->hw.dphy_freq = pdata->hs_freq; + } + v4l2_set_subdevdata(&csi->sd, pdev); + platform_set_drvdata(pdev, &csi->sd); + dev_set_drvdata(dev, sd); + + if (csi->rst) + reset_control_deassert(csi->rst); + +#if IS_ENABLED(CONFIG_DWC_MIPI_TC_DPHY_GEN3) + dw_csi_create_capabilities_sysfs(pdev); +#endif + dw_mipi_csi_get_version(csi); + dw_mipi_csi_specific_mappings(csi); + dw_mipi_csi_mask_irq_power_off(csi); + + dev_info(dev, "DW MIPI CSI-2 Host registered successfully HW v%u.%u\n", + csi->hw_version_major, csi->hw_version_minor); + + phy_init(csi->phy); + + ret = v4l2_async_register_subdev(&csi->sd); + + if (ret) + dev_dbg(csi->dev, "failed to register the subdevice\n"); + + return ret; +end: +#if IS_ENABLED(CONFIG_OF) + media_entity_cleanup(&csi->sd.entity); +#endif +csi2host_defer_err: + v4l2_async_nf_unregister(&csi->notifier); + v4l2_async_nf_cleanup(&csi->notifier); + +csi2host_reg_err: + clk_disable_unprepare(csi->phyclk); +csi2host_phyclk_err: + clk_disable_unprepare(csi->perclk); + return ret; +} + +/** + * @short Exit routine - Exit point of the driver + * @param[in] pdev pointer to the platform device structure + * @return 0 on success + */ +static int dw_csi_remove(struct platform_device *pdev) +{ + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *mipi_csi = sd_to_mipi_csi_dev(sd); + + dev_dbg(&pdev->dev, "Removing DW MIPI CSI-2 Host module\n"); + + if (mipi_csi->rst) + reset_control_assert(mipi_csi->rst); +#if IS_ENABLED(CONFIG_OF) + media_entity_cleanup(&mipi_csi->sd.entity); +#endif + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id dw_mipi_csi_of_match[] = { + { .compatible = "snps,dw-csi" }, + {}, +}; + +MODULE_DEVICE_TABLE(of, dw_mipi_csi_of_match); +#endif + +static struct platform_driver dw_mipi_csi_driver = { + .remove = dw_csi_remove, + .probe = dw_csi_probe, + .driver = { + .name = "dw-csi", + .owner = THIS_MODULE, +#if IS_ENABLED(CONFIG_OF) + .of_match_table = of_match_ptr(dw_mipi_csi_of_match), +#endif + }, +}; + +module_platform_driver(dw_mipi_csi_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Luis Oliveira <luis.oliveira@synopsys.com>"); +MODULE_DESCRIPTION("Synopsys DesignWare MIPI CSI-2 Host Platform driver"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/media/platform/dwc/dw-csi-plat.h linux-microchip/drivers/media/platform/dwc/dw-csi-plat.h --- linux-6.6.51/drivers/media/platform/dwc/dw-csi-plat.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/media/platform/dwc/dw-csi-plat.h 2024-11-26 14:02:37.602495560 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018 Synopsys, Inc. + * + * Synopsys DesignWare MIPI CSI-2 Host controller driver. + * Supported bus formats + * + * Author: Luis Oliveira <Luis.Oliveira@synopsys.com> + */ + +#ifndef _DW_CSI_PLAT_H__ +#define _DW_CSI_PLAT_H__ + +#include "dw-mipi-csi.h" + +/* Video formats supported by the MIPI CSI-2 */ +struct mipi_fmt dw_mipi_csi_formats[] = { + { + /* RAW 8 */ + .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, + .depth = 8, + }, { + /* RAW 10 */ + .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10, + .depth = 10, + }, { + /* RAW 10 */ + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, + .depth = 10, + + }, { + /* RAW 12 */ + .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12, + .depth = 12, + }, { + /* RAW 14 */ + .mbus_code = MEDIA_BUS_FMT_SBGGR14_1X14, + .depth = 14, + }, { + /* RAW 16 */ + .mbus_code = MEDIA_BUS_FMT_SBGGR16_1X16, + .depth = 16, + }, { + /* RGB 666 */ + .mbus_code = MEDIA_BUS_FMT_RGB666_1X18, + .depth = 18, + }, { + /* RGB 565 */ + .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_BE, + .depth = 16, + }, { + /* BGR 565 */ + .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .depth = 16, + }, { + /* RGB 555 */ + .mbus_code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, + .depth = 16, + }, { + /* BGR 555 */ + .mbus_code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, + .depth = 16, + }, { + /* RGB 444 */ + .mbus_code = MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE, + .depth = 16, + }, { + /* RGB 444 */ + .mbus_code = MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE, + .depth = 16, + }, { + /* RGB 888 */ + .mbus_code = MEDIA_BUS_FMT_RGB888_2X12_LE, + .depth = 24, + }, { + /* BGR 888 */ + .mbus_code = MEDIA_BUS_FMT_RGB888_2X12_BE, + .depth = 24, + }, { + /* BGR 888 */ + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, + .depth = 24, + }, { + /* YUV 422 8-bit */ + .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16, + .depth = 16, + }, { + /* YUV 422 10-bit */ + .mbus_code = MEDIA_BUS_FMT_VYUY10_2X10, + .depth = 20, + }, { + /* YUV 420 8-bit LEGACY */ + .mbus_code = MEDIA_BUS_FMT_Y8_1X8, + .depth = 8, + }, { + /* YUV 420 10-bit */ + .mbus_code = MEDIA_BUS_FMT_Y10_1X10, + .depth = 10, + }, +}; + +#endif /* _DW_CSI_PLAT_H__ */ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/media/platform/dwc/dw-csi-sysfs.c linux-microchip/drivers/media/platform/dwc/dw-csi-sysfs.c --- linux-6.6.51/drivers/media/platform/dwc/dw-csi-sysfs.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/media/platform/dwc/dw-csi-sysfs.c 2024-11-26 14:02:37.602495560 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates. + * + * Synopsys DesignWare MIPI CSI-2 Host controller driver. + * SysFS components for the platform driver + * + * Author: Luis Oliveira <Luis.Oliveira@synopsys.com> + */ + +#include "dw-mipi-csi.h" + +static ssize_t core_version_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *csi_dev = sd_to_mipi_csi_dev(sd); + + char buffer[10]; + + snprintf(buffer, 10, "v.%d.%d*\n", csi_dev->hw_version_major, + csi_dev->hw_version_minor); + + return strlcpy(buf, buffer, PAGE_SIZE); +} + +static ssize_t n_lanes_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + unsigned long lanes; + + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *csi_dev = sd_to_mipi_csi_dev(sd); + + ret = kstrtoul(buf, 10, &lanes); + if (ret < 0) + return ret; + + if (lanes > 8) { + dev_err(dev, "Invalid number of lanes %lu\n", lanes); + return count; + } + + dev_info(dev, "Lanes %lu\n", lanes); + csi_dev->hw.num_lanes = lanes; + + return count; +} + +static ssize_t n_lanes_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *csi_dev = sd_to_mipi_csi_dev(sd); + + char buffer[10]; + + snprintf(buffer, 10, "%d\n", csi_dev->hw.num_lanes); + + return strlcpy(buf, buffer, PAGE_SIZE); +} + +static ssize_t core_reset_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *csi_dev = sd_to_mipi_csi_dev(sd); + + char buffer[10]; + + /* Reset Controller and DPHY */ + phy_reset(csi_dev->phy); + dw_mipi_csi_reset(csi_dev); + + snprintf(buffer, 10, "Reset\n"); + + return strlcpy(buf, buffer, PAGE_SIZE); +} + +static ssize_t data_type_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + unsigned long dt; + + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *csi_dev = sd_to_mipi_csi_dev(sd); + + ret = kstrtoul(buf, 16, &dt); + if (ret < 0) + return ret; + + if (dt < 0x18 || dt > 0x2F) { + dev_err(dev, "Invalid data type %lx\n", dt); + return count; + } + + dev_info(dev, "Data type 0x%lx\n", dt); + csi_dev->ipi_dt = dt; + + return count; +} + +static ssize_t data_type_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *csi_dev = sd_to_mipi_csi_dev(sd); + + char buffer[10]; + + snprintf(buffer, 10, "%x\n", csi_dev->ipi_dt); + + return strlcpy(buf, buffer, PAGE_SIZE); +} + +static ssize_t hsa_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + unsigned long hsa; + + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *csi_dev = sd_to_mipi_csi_dev(sd); + + ret = kstrtoul(buf, 16, &hsa); + if (ret < 0) + return ret; + + if (hsa > 0xFFF) { + dev_err(dev, "Invalid HSA time %lx\n", hsa); + return count; + } + + dev_info(dev, "HSA time 0x%lx\n", hsa); + csi_dev->hw.hsa = hsa; + + return count; +} + +static ssize_t hsa_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *csi_dev = sd_to_mipi_csi_dev(sd); + + char buffer[10]; + + snprintf(buffer, 10, "%x\n", csi_dev->hw.hsa); + + return strlcpy(buf, buffer, PAGE_SIZE); +} + +static ssize_t hbp_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + unsigned long hbp; + + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *csi_dev = sd_to_mipi_csi_dev(sd); + + ret = kstrtoul(buf, 16, &hbp); + if (ret < 0) + return ret; + + if (hbp > 0xFFF) { + dev_err(dev, "Invalid HBP time %lx\n", hbp); + return count; + } + + dev_info(dev, "HBP time 0x%lx\n", hbp); + csi_dev->hw.hbp = hbp; + + return count; +} + +static ssize_t hbp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *csi_dev = sd_to_mipi_csi_dev(sd); + + char buffer[10]; + + snprintf(buffer, 10, "%x\n", csi_dev->hw.hbp); + + return strlcpy(buf, buffer, PAGE_SIZE); +} + +static ssize_t hsd_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + unsigned long hsd; + + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *csi_dev = sd_to_mipi_csi_dev(sd); + + ret = kstrtoul(buf, 16, &hsd); + if (ret < 0) + return ret; + + if (hsd > 0xFF) { + dev_err(dev, "Invalid HSD time %lx\n", hsd); + return count; + } + + dev_info(dev, "HSD time 0x%lx\n", hsd); + csi_dev->hw.hsd = hsd; + + return count; +} + +static ssize_t hsd_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *csi_dev = sd_to_mipi_csi_dev(sd); + + char buffer[10]; + + snprintf(buffer, 10, "%x\n", csi_dev->hw.hsd); + + return strlcpy(buf, buffer, PAGE_SIZE); +} + +static ssize_t vsa_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + unsigned long vsa; + + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *csi_dev = sd_to_mipi_csi_dev(sd); + + ret = kstrtoul(buf, 16, &vsa); + if (ret < 0) + return ret; + + if (vsa > 0x3FF) { + dev_err(dev, "Invalid VSA period %lx\n", vsa); + return count; + } + + dev_info(dev, "VSA period 0x%lx\n", vsa); + csi_dev->hw.vsa = vsa; + + return count; +} + +static ssize_t vsa_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *csi_dev = sd_to_mipi_csi_dev(sd); + + char buffer[10]; + + snprintf(buffer, 10, "%x\n", csi_dev->hw.vsa); + + return strlcpy(buf, buffer, PAGE_SIZE); +} + +static ssize_t vbp_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + unsigned long vbp; + + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *csi_dev = sd_to_mipi_csi_dev(sd); + + ret = kstrtoul(buf, 16, &vbp); + if (ret < 0) + return ret; + + if (vbp > 0x2FF) { + dev_err(dev, "Invalid VBP period %lx\n", vbp); + return count; + } + + dev_info(dev, "VBP period 0x%lx\n", vbp); + csi_dev->hw.vbp = vbp; + + return count; +} + +static ssize_t vbp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *csi_dev = sd_to_mipi_csi_dev(sd); + + char buffer[10]; + + snprintf(buffer, 10, "%x\n", csi_dev->hw.vbp); + + return strlcpy(buf, buffer, PAGE_SIZE); +} + +static ssize_t vfp_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + unsigned long vfp; + + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *csi_dev = sd_to_mipi_csi_dev(sd); + + ret = kstrtoul(buf, 16, &vfp); + if (ret < 0) + return ret; + + if (vfp > 0x3ff) { + dev_err(dev, "Invalid VFP period %lx\n", vfp); + return count; + } + + dev_info(dev, "VFP period 0x%lx\n", vfp); + csi_dev->hw.vfp = vfp; + + return count; +} + +static ssize_t vfp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *csi_dev = sd_to_mipi_csi_dev(sd); + + char buffer[10]; + + snprintf(buffer, 10, "%x\n", csi_dev->hw.vfp); + + return strlcpy(buf, buffer, PAGE_SIZE); +} + +static ssize_t virtual_channel_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + unsigned long virtual_ch; + + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *csi_dev = sd_to_mipi_csi_dev(sd); + + ret = kstrtoul(buf, 10, &virtual_ch); + if (ret < 0) + return ret; + + if ((signed int)virtual_ch < 0 || (signed int)virtual_ch > 8) { + dev_err(dev, "Invalid Virtual Channel %lu\n", virtual_ch); + return count; + } + + dev_info(dev, "Virtual Channel %lu\n", virtual_ch); + csi_dev->hw.virtual_ch = virtual_ch; + + return count; +} + +static ssize_t virtual_channel_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *csi_dev = sd_to_mipi_csi_dev(sd); + + char buffer[10]; + + snprintf(buffer, 10, "%d\n", csi_dev->hw.virtual_ch); + + return strlcpy(buf, buffer, PAGE_SIZE); +} + +static ssize_t ipi_color_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + unsigned long ipi_color_mode; + + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *csi_dev = sd_to_mipi_csi_dev(sd); + + ret = kstrtoul(buf, 10, &ipi_color_mode); + if (ret < 0) + return ret; + + if ((signed int)ipi_color_mode < 0 || (signed int)ipi_color_mode > 1) { + dev_err(dev, + "Wrong Color Mode %lu, (48 bits -> 0 or 16 bits -> 1\n", + ipi_color_mode); + return count; + } + + dev_info(dev, "IPI Color mode %lu\n", ipi_color_mode); + csi_dev->hw.ipi_color_mode = ipi_color_mode; + + return count; +} + +static ssize_t ipi_color_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *csi_dev = sd_to_mipi_csi_dev(sd); + + char buffer[10]; + + snprintf(buffer, 10, "%d\n", csi_dev->hw.ipi_color_mode); + + return strlcpy(buf, buffer, PAGE_SIZE); +} + +static ssize_t ipi_auto_flush_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + unsigned long ipi_auto_flush; + + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *csi_dev = sd_to_mipi_csi_dev(sd); + + ret = kstrtoul(buf, 10, &ipi_auto_flush); + if (ret < 0) + return ret; + + if ((signed int)ipi_auto_flush < 0 || (signed int)ipi_auto_flush > 1) { + dev_err(dev, + "Invalid Auto Flush Mode %lu, (No -> 0 or Yes -> 1\n", + ipi_auto_flush); + return count; + } + + dev_info(dev, "IPI Auto Flush %lu\n", ipi_auto_flush); + csi_dev->hw.ipi_auto_flush = ipi_auto_flush; + + return count; +} + +static ssize_t ipi_auto_flush_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *csi_dev = sd_to_mipi_csi_dev(sd); + + char buffer[10]; + + snprintf(buffer, 10, "%d\n", csi_dev->hw.ipi_auto_flush); + + return strlcpy(buf, buffer, PAGE_SIZE); +} + +static ssize_t ipi_timings_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + unsigned long ipi_mode; + + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *csi_dev = sd_to_mipi_csi_dev(sd); + + ret = kstrtoul(buf, 10, &ipi_mode); + if (ret < 0) + return ret; + + if ((signed int)ipi_mode < 0 || (signed int)ipi_mode > 1) { + dev_err(dev, + "Invalid Timing Source %lu (Camera:0|Controller:1)\n", + ipi_mode); + return count; + } + + dev_info(dev, "IPI Color mode %lu\n", ipi_mode); + csi_dev->hw.ipi_mode = ipi_mode; + + return count; +} + +static ssize_t ipi_timings_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *csi_dev = sd_to_mipi_csi_dev(sd); + + char buffer[10]; + + snprintf(buffer, 10, "%d\n", csi_dev->hw.ipi_mode); + + return strlcpy(buf, buffer, PAGE_SIZE); +} + +static ssize_t output_type_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + unsigned long output; + + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *csi_dev = sd_to_mipi_csi_dev(sd); + + ret = kstrtoul(buf, 10, &output); + if (ret < 0) + return ret; + + if ((signed int)output < 0 || (signed int)output > 1) { + dev_err(dev, + "Invalid Core output %lu to be used (IPI-> 0 or IDI->1 or BOTH- 2\n", + output); + return count; + } + + dev_info(dev, "IPI Color mode %lu\n", output); + csi_dev->hw.output = output; + + return count; +} + +static ssize_t output_type_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct v4l2_subdev *sd = platform_get_drvdata(pdev); + struct dw_csi *csi_dev = sd_to_mipi_csi_dev(sd); + + char buffer[10]; + + snprintf(buffer, 10, "%d\n", csi_dev->hw.output); + + return strlcpy(buf, buffer, PAGE_SIZE); +} + +static DEVICE_ATTR_RO(core_version); +static DEVICE_ATTR_RO(core_reset); +static DEVICE_ATTR_RW(n_lanes); +static DEVICE_ATTR_RW(data_type); +static DEVICE_ATTR_RW(hsa); +static DEVICE_ATTR_RW(hbp); +static DEVICE_ATTR_RW(hsd); +static DEVICE_ATTR_RW(vsa); +static DEVICE_ATTR_RW(vbp); +static DEVICE_ATTR_RW(vfp); +static DEVICE_ATTR_RW(virtual_channel); +static DEVICE_ATTR_RW(ipi_color_mode); +static DEVICE_ATTR_RW(ipi_auto_flush); +static DEVICE_ATTR_RW(ipi_timings_mode); +static DEVICE_ATTR_RW(output_type); + +int dw_csi_create_capabilities_sysfs(struct platform_device *pdev) +{ + device_create_file(&pdev->dev, &dev_attr_core_version); + device_create_file(&pdev->dev, &dev_attr_core_reset); + device_create_file(&pdev->dev, &dev_attr_n_lanes); + device_create_file(&pdev->dev, &dev_attr_data_type); + device_create_file(&pdev->dev, &dev_attr_hsa); + device_create_file(&pdev->dev, &dev_attr_hbp); + device_create_file(&pdev->dev, &dev_attr_hsd); + device_create_file(&pdev->dev, &dev_attr_vsa); + device_create_file(&pdev->dev, &dev_attr_vbp); + device_create_file(&pdev->dev, &dev_attr_vfp); + device_create_file(&pdev->dev, &dev_attr_virtual_channel); + device_create_file(&pdev->dev, &dev_attr_ipi_color_mode); + device_create_file(&pdev->dev, &dev_attr_ipi_auto_flush); + device_create_file(&pdev->dev, &dev_attr_ipi_timings_mode); + device_create_file(&pdev->dev, &dev_attr_output_type); + + return 0; +} diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/media/platform/dwc/dw-dphy-plat.c linux-microchip/drivers/media/platform/dwc/dw-dphy-plat.c --- linux-6.6.51/drivers/media/platform/dwc/dw-dphy-plat.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/media/platform/dwc/dw-dphy-plat.c 2024-11-26 14:02:37.602495560 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates. + * + * Synopsys DesignWare MIPI D-PHY controller driver. + * Platform driver + * + * Author: Luis Oliveira <luis.oliveira@synopsys.com> + */ + +#include <media/dwc/dw-dphy-data.h> +#include <media/dwc/dw-csi-data.h> + +#include "dw-dphy-rx.h" + +static struct phy_ops dw_dphy_ops = { + .init = dw_dphy_init, + .reset = dw_dphy_reset, + .power_on = dw_dphy_power_on, + .power_off = dw_dphy_power_off, + .owner = THIS_MODULE, +}; + +static struct phy_provider *phy_provider; + +#if IS_ENABLED(CONFIG_DWC_MIPI_TC_DPHY_GEN3) +static u8 get_config_8l(struct device *dev, struct dw_dphy_rx *dphy) +{ + if (IS_ENABLED(CONFIG_OF) && dev->of_node) { + struct gpio_desc *conf; + + conf = devm_fwnode_gpiod_get_index(dev, dev->fwnode, "config", + 0, GPIOD_IN, + fwnode_get_name(dev->fwnode)); + if (IS_ERR(conf)) { + dev_warn(dev, "8L config parse err, default is 4L\n"); + dphy->config_8l = CTRL_4_LANES; + } + } else { + struct dw_phy_pdata *pdata = dev->platform_data; + + dphy->config_8l = pdata->config_8l; + } + return dphy->config_8l; +} +#endif +static int get_resources(struct device *dev, struct dw_dphy_rx *dphy) +{ + int ret = 0; + + if (IS_ENABLED(CONFIG_OF) && dev->of_node) { + if (of_property_read_u32(dev->of_node, "snps,dphy-frequency", + &dphy->dphy_freq)) { + dev_err(dev, "failed to find dphy frequency\n"); + ret = -EINVAL; + } + if (of_property_read_u32(dev->of_node, "bus-width", + &dphy->dphy_te_len)) { + dev_err(dev, "failed to find dphy te length\n"); + ret = -EINVAL; + } + if (of_property_read_u32(dev->of_node, "snps,phy_type", + &dphy->phy_type)) { + dev_err(dev, "failed to find dphy type\n"); + ret = -EINVAL; + } + } else { + struct dw_phy_pdata *pdata = dev->platform_data; + + dphy->dphy_freq = pdata->dphy_frequency; + dphy->dphy_te_len = pdata->dphy_te_len; + dphy->dphy_gen = pdata->dphy_gen; + } + dev_set_drvdata(dev, dphy); + + return ret; +} + +static int phy_register(struct device *dev) +{ + int ret = 0; + + if (IS_ENABLED(CONFIG_OF) && dev->of_node) { + phy_provider = devm_of_phy_provider_register(dev, + dw_dphy_xlate); + if (IS_ERR(phy_provider)) { + dev_err(dev, "error getting phy provider\n"); + ret = PTR_ERR(phy_provider); + } + } else { + struct dw_phy_pdata *pdata = dev->platform_data; + struct dw_dphy_rx *dphy = dev_get_drvdata(dev); + + ret = phy_create_lookup(dphy->phy, + phys[pdata->id].name, + csis[pdata->id].name); + if (ret) + dev_err(dev, "Failed to create dphy lookup\n"); + else + dev_warn(dev, + "Created dphy lookup [%s] --> [%s]\n", + phys[pdata->id].name, csis[pdata->id].name); + } + return ret; +} + +static void phy_unregister(struct device *dev) +{ + if (!dev->of_node) { + struct dw_dphy_rx *dphy = dev_get_drvdata(dev); + + phy_remove_lookup(dphy->phy, "dw-dphy", "dw-csi"); + } +} + +static int dw_dphy_rx_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dw_dphy_rx *dphy; + struct resource *res; + + dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL); + if (!dphy) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dphy->base_address = devm_ioremap(&pdev->dev, + res->start, resource_size(res)); + if (IS_ERR(dphy->base_address)) { + dev_err(&pdev->dev, "error requesting base address\n"); + return PTR_ERR(dphy->base_address); + } + +#if IS_ENABLED(CONFIG_DWC_MIPI_TC_DPHY_GEN3) + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + + dphy->dphy1_if_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dphy->dphy1_if_addr)) { + dev_err(&pdev->dev, "error requesting dphy 1 if regbank\n"); + return PTR_ERR(dphy->dphy1_if_addr); + } + + dphy->max_lanes = + dw_dphy_if_read_msk(dphy, DPHYID, DPHY_ID_LANE_SUPPORT, 4); + + dphy->dphy_gen = dw_dphy_if_read_msk(dphy, DPHYID, DPHY_ID_GEN, 4); + + dev_info(&pdev->dev, "DPHY GEN %s with maximum %s lanes\n", + dphy->dphy_gen == GEN3 ? "3" : "2", + dphy->max_lanes == CTRL_8_LANES ? "8" : "4"); + + if (dphy->max_lanes == CTRL_8_LANES) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + dphy->dphy2_if_addr = + devm_ioremap(&pdev->dev, + res->start, resource_size(res)); + + if (IS_ERR(dphy->dphy2_if_addr)) { + dev_err(&pdev->dev, + "error requesting dphy 2 if regbank\n"); + return PTR_ERR(dphy->dphy2_if_addr); + } + dphy->config_8l = get_config_8l(&pdev->dev, dphy); + } +#endif + if (get_resources(dev, dphy)) { + dev_err(dev, "failed to parse PHY resources\n"); + return -EINVAL; + } + + dphy->phy = devm_phy_create(dev, NULL, &dw_dphy_ops); + if (IS_ERR(dphy->phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(dphy->phy); + } + + platform_set_drvdata(pdev, dphy); + phy_set_drvdata(dphy->phy, dphy); + + if (phy_register(dev)) { + dev_err(dev, "failed to register PHY\n"); + return -EINVAL; + } + + dphy->lp_time = 1000; /* 1000 ns */ + dphy->lanes_config = dw_dphy_setup_config(dphy); + + dev_info(dev, "Probing dphy finished\n"); +#if IS_ENABLED(CONFIG_DWC_MIPI_TC_DPHY_GEN3) + dw_dphy_create_capabilities_sysfs(pdev); +#endif + + return 0; +} + +static int dw_dphy_rx_remove(struct platform_device *pdev) +{ + phy_unregister(&pdev->dev); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id dw_dphy_rx_of_match[] = { + { .compatible = "snps,dw-dphy-rx" }, + {}, +}; + +MODULE_DEVICE_TABLE(of, dw_dphy_rx_of_match); +#endif + +static struct platform_driver dw_dphy_rx_driver = { + .probe = dw_dphy_rx_probe, + .remove = dw_dphy_rx_remove, + .driver = { +#if IS_ENABLED(CONFIG_OF) + .of_match_table = of_match_ptr(dw_dphy_rx_of_match), +#endif + .name = "dw-dphy", + .owner = THIS_MODULE, + } +}; +module_platform_driver(dw_dphy_rx_driver); + +MODULE_DESCRIPTION("Synopsys DesignWare MIPI DPHY Rx driver"); +MODULE_AUTHOR("Luis Oliveira <lolivei@synopsys.com>"); +MODULE_LICENSE("GPL v2"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/media/platform/dwc/dw-dphy-rx.c linux-microchip/drivers/media/platform/dwc/dw-dphy-rx.c --- linux-6.6.51/drivers/media/platform/dwc/dw-dphy-rx.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/media/platform/dwc/dw-dphy-rx.c 2024-11-26 14:02:37.602495560 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates. + * + * Synopsys DesignWare MIPI D-PHY controller driver + * Core functions + * + * Author: Luis Oliveira <luis.oliveira@synopsys.com> + */ + +#include "dw-dphy-rx.h" + +struct range_dphy_gen2 { + u32 freq; + u8 hsfregrange; +}; + +struct range_dphy_gen2 range_gen2[] = { + { 80, 0x00 }, { 90, 0x10 }, { 100, 0x20 }, { 110, 0x30 }, + { 120, 0x01 }, { 130, 0x11 }, { 140, 0x21 }, { 150, 0x31 }, + { 160, 0x02 }, { 170, 0x12 }, { 180, 0x22 }, { 190, 0x32 }, + { 205, 0x03 }, { 220, 0x13 }, { 235, 0x23 }, { 250, 0x33 }, + { 275, 0x04 }, { 300, 0x14 }, { 325, 0x05 }, { 350, 0x15 }, + { 400, 0x25 }, { 450, 0x06 }, { 500, 0x16 }, { 550, 0x07 }, + { 600, 0x17 }, { 650, 0x08 }, { 700, 0x18 }, { 750, 0x09 }, + { 800, 0x19 }, { 850, 0x29 }, { 900, 0x39 }, { 950, 0x0A }, + { 1000, 0x1A }, { 1050, 0x2A }, { 1100, 0x3A }, { 1150, 0x0B }, + { 1200, 0x1B }, { 1250, 0x2B }, { 1300, 0x3B }, { 1350, 0x0C }, + { 1400, 0x1C }, { 1450, 0x2C }, { 1500, 0x3C }, { 1550, 0x0D }, + { 1600, 0x1D }, { 1650, 0x2D }, { 1700, 0x0E }, { 1750, 0x1E }, + { 1800, 0x2E }, { 1850, 0x3E }, { 1900, 0x0F }, { 1950, 0x1F }, + { 2000, 0x2F }, +}; + +struct range_dphy_gen3 { + u32 freq; + u8 hsfregrange; + u32 osc_freq_target; +}; + +struct range_dphy_gen3 range_gen3[] = { + { 80, 0x00, 0x1B6 }, { 90, 0x10, 0x1B6 }, { 100, 0x20, 0x1B6 }, + { 110, 0x30, 0x1B6 }, { 120, 0x01, 0x1B6 }, { 130, 0x11, 0x1B6 }, + { 140, 0x21, 0x1B6 }, { 150, 0x31, 0x1B6 }, { 160, 0x02, 0x1B6 }, + { 170, 0x12, 0x1B6 }, { 180, 0x22, 0x1B6 }, { 190, 0x32, 0x1B6 }, + { 205, 0x03, 0x1B6 }, { 220, 0x13, 0x1B6 }, { 235, 0x23, 0x1B6 }, + { 250, 0x33, 0x1B6 }, { 275, 0x04, 0x1B6 }, { 300, 0x14, 0x1B6 }, + { 325, 0x25, 0x1B6 }, { 350, 0x35, 0x1B6 }, { 400, 0x05, 0x1B6 }, + { 450, 0x16, 0x1B6 }, { 500, 0x26, 0x1B6 }, { 550, 0x37, 0x1B6 }, + { 600, 0x07, 0x1B6 }, { 650, 0x18, 0x1B6 }, { 700, 0x28, 0x1B6 }, + { 750, 0x39, 0x1B6 }, { 800, 0x09, 0x1B6 }, { 850, 0x19, 0x1B6 }, + { 900, 0x29, 0x1B6 }, { 950, 0x3A, 0x1B6 }, { 1000, 0x0A, 0x1B6 }, + { 1050, 0x1A, 0x1B6 }, { 1100, 0x2A, 0x1B6 }, { 1150, 0x3B, 0x1B6 }, + { 1200, 0x0B, 0x1B6 }, { 1250, 0x1B, 0x1B6 }, { 1300, 0x2B, 0x1B6 }, + { 1350, 0x3C, 0x1B6 }, { 1400, 0x0C, 0x1B6 }, { 1450, 0x1C, 0x1B6 }, + { 1500, 0x2C, 0x1B6 }, { 1550, 0x3D, 0x10F }, { 1600, 0x0D, 0x118 }, + { 1650, 0x1D, 0x121 }, { 1700, 0x2E, 0x12A }, { 1750, 0x3E, 0x132 }, + { 1800, 0x0E, 0x13B }, { 1850, 0x1E, 0x144 }, { 1900, 0x2F, 0x14D }, + { 1950, 0x3F, 0x155 }, { 2000, 0x0F, 0x15E }, { 2050, 0x40, 0x167 }, + { 2100, 0x41, 0x170 }, { 2150, 0x42, 0x178 }, { 2200, 0x43, 0x181 }, + { 2250, 0x44, 0x18A }, { 2300, 0x45, 0x193 }, { 2350, 0x46, 0x19B }, + { 2400, 0x47, 0x1A4 }, { 2450, 0x48, 0x1AD }, { 2500, 0x49, 0x1B6 } +}; + +u8 dw_dphy_setup_config(struct dw_dphy_rx *dphy) +{ +#if IS_ENABLED(CONFIG_DWC_MIPI_TC_DPHY_GEN3) + int ret; + struct gpio_desc *config_gpio; + + if (dphy->max_lanes == CTRL_4_LANES) { + dev_vdbg(&dphy->phy->dev, "CONFIG 4L\n"); + return CTRL_4_LANES; + } + if (IS_ENABLED(CONFIG_OF)) { + config_gpio = devm_gpiod_get(&dphy->phy->dev, "config", + GPIOD_IN); + if (IS_ERR(config_gpio)) { + dev_vdbg(&dphy->phy->dev, + "8L config parse err, default 4L"); + return CTRL_4_LANES; + } + ret = gpiod_get_value(config_gpio); + } else { + ret = dphy->config_8l; + } + + dev_vdbg(&dphy->phy->dev, + "Booting in [%s] mode\n", + ret == CTRL_8_LANES ? "8L" : "4+4L"); + return ret; + +#endif /* CONFIG_DWC_MIPI_TC_DPHY_GEN3 */ + return CTRL_4_LANES; +} + +#if IS_ENABLED(CONFIG_DWC_MIPI_TC_DPHY_GEN3) +void dw_dphy_if_write(struct dw_dphy_rx *dphy, u32 address, u32 data) +{ + writel(data, dphy->dphy1_if_addr + address); + + if (dphy->lanes_config == CTRL_4_LANES) + return; + + iowrite32(data, dphy->dphy2_if_addr + address); +} + +u32 dw_dphy_if_read(struct dw_dphy_rx *dphy, u32 address) +{ + u32 if1 = 0, if2 = 0; + + if1 = readl(dphy->dphy1_if_addr + address); + + if (dphy->lanes_config == CTRL_4_LANES) + goto end; + + if (dphy->lanes_config == DPHYID) + goto end; + + if2 = readl(dphy->dphy2_if_addr + address); + + if (if1 != if2) + dev_vdbg(&dphy->phy->dev, + "Values read different for each interface\n"); +end: + return if1; +} +#endif + +void dw_dphy_write(struct dw_dphy_rx *dphy, u32 address, u32 data) +{ + iowrite32(data, dphy->base_address + address); + + if (dphy->lanes_config == CTRL_4_LANES) + return; + + if (address == R_CSI2_DPHY_TST_CTRL0) + iowrite32(data, dphy->base_address + R_CSI2_DPHY2_TST_CTRL0); + else if (address == R_CSI2_DPHY_TST_CTRL1) + iowrite32(data, dphy->base_address + R_CSI2_DPHY2_TST_CTRL1); +} + +u32 dw_dphy_read(struct dw_dphy_rx *dphy, u32 address) +{ + int dphy1 = 0, dphy2 = 0; + + dphy1 = ioread32(dphy->base_address + address); + + if (dphy->lanes_config == CTRL_4_LANES) + goto end; + + if (address == R_CSI2_DPHY_TST_CTRL0) + dphy2 = ioread32(dphy->base_address + R_CSI2_DPHY2_TST_CTRL0); + else if (address == R_CSI2_DPHY_TST_CTRL1) + dphy2 = ioread32(dphy->base_address + R_CSI2_DPHY2_TST_CTRL1); + else + return -ENODEV; +end: + return dphy1; +} + +void dw_dphy_write_msk(struct dw_dphy_rx *dev, u32 address, u32 data, u8 shift, + u8 width) +{ + u32 temp = dw_dphy_read(dev, address); + u32 mask = (1 << width) - 1; + + temp &= ~(mask << shift); + temp |= (data & mask) << shift; + dw_dphy_write(dev, address, temp); +} + +static void dw_dphy_te_12b_write(struct dw_dphy_rx *dphy, u16 addr, u8 data) +{ + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0, PHY_TESTEN, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 1, PHY_TESTEN, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0x00, PHY_TESTDIN, 8); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0, PHY_TESTEN, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, (u8)(addr >> 8), + PHY_TESTDIN, 8); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 1, PHY_TESTEN, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, (u8)addr, PHY_TESTDIN, + 8); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0, PHY_TESTEN, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, (u8)data, PHY_TESTDIN, + 8); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1); +} + +static void dw_dphy_te_8b_write(struct dw_dphy_rx *dphy, u8 addr, u8 data) +{ + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1); + dw_dphy_write(dphy, R_CSI2_DPHY_TST_CTRL1, addr); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 1, PHY_TESTEN, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0, PHY_TESTEN, 1); + dw_dphy_write(dphy, R_CSI2_DPHY_TST_CTRL1, data); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1); +} + +static void dw_dphy_te_write(struct dw_dphy_rx *dphy, u16 addr, u8 data) +{ + if (dphy->dphy_te_len == BIT12) + dw_dphy_te_12b_write(dphy, addr, data); + else + dw_dphy_te_8b_write(dphy, addr, data); +} + +static int dw_dphy_te_12b_read(struct dw_dphy_rx *dphy, u32 addr) +{ + u8 ret; + + dw_dphy_write(dphy, R_CSI2_DPHY_SHUTDOWNZ, 0); + dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 0); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0, PHY_TESTEN, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 1, PHY_TESTEN, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0x00, PHY_TESTDIN, 8); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0, PHY_TESTEN, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, (u8)(addr >> 8), + PHY_TESTDIN, 8); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 1, PHY_TESTEN, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, (u8)addr, PHY_TESTDIN, + 8); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0x00, 0, PHY_TESTDIN); + ret = dw_dphy_read_msk(dphy, R_CSI2_DPHY_TST_CTRL1, PHY_TESTDOUT, 8); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0, PHY_TESTEN, 1); + dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 1); + dw_dphy_write(dphy, R_CSI2_DPHY_SHUTDOWNZ, 1); + + return ret; +} + +static int dw_dphy_te_8b_read(struct dw_dphy_rx *dphy, u32 addr) +{ + u8 ret; + + dw_dphy_write(dphy, R_CSI2_DPHY_SHUTDOWNZ, 0); + dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 0); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 1, PHY_TESTEN, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, addr, PHY_TESTDIN, 8); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0, PHY_TESTEN, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL1, 0, PHY_TESTDIN, 8); + ret = dw_dphy_read_msk(dphy, R_CSI2_DPHY_TST_CTRL1, PHY_TESTDOUT, 8); + dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 1); + dw_dphy_write(dphy, R_CSI2_DPHY_SHUTDOWNZ, 1); + + return ret; +} + +int dw_dphy_te_read(struct dw_dphy_rx *dphy, u32 addr) +{ + int ret; + + if (dphy->dphy_te_len == BIT12) + ret = dw_dphy_te_12b_read(dphy, addr); + else + ret = dw_dphy_te_8b_read(dphy, addr); + + return ret; +} + +#if IS_ENABLED(CONFIG_DWC_MIPI_TC_DPHY_GEN3) +static void dw_dphy_if_init(struct dw_dphy_rx *dphy) +{ + dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RESET); + dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, TX_PHY); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLR, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLR, 1); + dw_dphy_if_write(dphy, DPHYZCALCTRL, 0); + dw_dphy_if_write(dphy, DPHYZCALCTRL, 1); + dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RESET); + dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, GLUELOGIC); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLR, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLR, 1); + dw_dphy_if_write(dphy, DPHYZCALCTRL, 0); + dw_dphy_if_write(dphy, DPHYZCALCTRL, 1); + dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RESET); + dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RX_PHY); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLR, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLR, 1); + dw_dphy_if_write(dphy, DPHYZCALCTRL, 0); + dw_dphy_if_write(dphy, DPHYZCALCTRL, 1); +} +#endif + +static void dw_dphy_gen3_12bit_tc_power_up(struct dw_dphy_rx *dphy) +{ +#if IS_ENABLED(CONFIG_DWC_MIPI_TC_DPHY_GEN3) + dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RESET); + dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, GLUELOGIC); +#endif + dw_dphy_te_write(dphy, CFGCLKFREQRANGE_TX, 0x1C); + + /* CLKSEL | UPDATEPLL | SHADOW_CLEAR | SHADOW_CTRL | FORCEPLL */ + dw_dphy_te_write(dphy, BYPASS, 0x3F); + + /* IO_DS3 | IO_DS2 | IO_DS1 | IO_DS0 */ + if (dphy->dphy_freq > 1500) + dw_dphy_te_write(dphy, IO_DS, 0x0F); +#if IS_ENABLED(CONFIG_DWC_MIPI_TC_DPHY_GEN3) + dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RESET); + dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RX_PHY); +#endif +} + +static void dw_dphy_gen3_8bit_tc_power_up(struct dw_dphy_rx *dphy) +{ + u32 input_freq = dphy->dphy_freq / 1000; +#if IS_ENABLED(CONFIG_DWC_MIPI_TC_DPHY_GEN3) + dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RESET); + dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, GLUELOGIC); + dw_dphy_te_write(dphy, CFGCLKFREQRANGE_RX, 0x1C); + dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RESET); + dw_dphy_if_write(dphy, DPHYGLUEIFTESTER, RX_PHY); +#endif + dw_dphy_te_write(dphy, OSC_FREQ_TARGET_RX0_MSB, 0x03); + dw_dphy_te_write(dphy, OSC_FREQ_TARGET_RX0_LSB, 0x02); + dw_dphy_te_write(dphy, OSC_FREQ_TARGET_RX1_MSB, 0x03); + dw_dphy_te_write(dphy, OSC_FREQ_TARGET_RX1_LSB, 0x02); + dw_dphy_te_write(dphy, OSC_FREQ_TARGET_RX2_MSB, 0x03); + dw_dphy_te_write(dphy, OSC_FREQ_TARGET_RX2_LSB, 0x02); + dw_dphy_te_write(dphy, OSC_FREQ_TARGET_RX3_MSB, 0x03); + dw_dphy_te_write(dphy, OSC_FREQ_TARGET_RX3_LSB, 0x02); + dw_dphy_te_write(dphy, BANDGAP_CTRL, 0x80); + + if (input_freq < 2000) + dw_dphy_te_write(dphy, HS_RX_CTRL_LANE0, 0xC0); + + if (input_freq < 1000) { + dw_dphy_te_write(dphy, HS_RX_CTRL_LANE1, 0xC0); + dw_dphy_te_write(dphy, HS_RX_CTRL_LANE2, 0xC0); + dw_dphy_te_write(dphy, HS_RX_CTRL_LANE3, 0xC0); + } +} + +int dw_dphy_g118_settle(struct dw_dphy_rx *dphy) +{ + u32 input_freq, total_settle, settle_time, byte_clk, lp_time; + + lp_time = dphy->lp_time; + input_freq = dphy->dphy_freq / 1000; + + settle_time = (8 * (1000000 / (input_freq))) + 115000; + byte_clk = (8000000 / (input_freq)); + total_settle = (settle_time + lp_time * 1000) / byte_clk; + + if (total_settle > 0xFF) + total_settle = 0xFF; + + return total_settle; +} + +static void dw_dphy_pwr_down(struct dw_dphy_rx *dphy) +{ + dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 0); + + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLK, 1); + if (dphy->lanes_config == CTRL_8_LANES) + dw_dphy_write_msk(dphy, R_CSI2_DPHY2_TST_CTRL0, 0, PHY_TESTCLK, + 1); + + dw_dphy_write(dphy, R_CSI2_DPHY_SHUTDOWNZ, 0); +} + +static void dw_dphy_pwr_up(struct dw_dphy_rx *dphy) +{ + u32 state = 0, wait = 0; + + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLK, 1); + if (dphy->lanes_config == CTRL_8_LANES) + dw_dphy_write_msk(dphy, R_CSI2_DPHY2_TST_CTRL0, 1, PHY_TESTCLK, + 1); + dev_vdbg(&dphy->phy->dev, "DPHY power up.\n"); + dw_dphy_write(dphy, R_CSI2_DPHY_SHUTDOWNZ, 1); + usleep_range(100, 500); + dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 1); + + while (state != 0x10003) { + state = dw_dphy_read(dphy, R_CSI2_DPHY_STOPSTATE); + usleep_range(100, 500); + wait++; + if (wait > 10) + break; + } + if (state != 0x10003) + dev_warn(&dphy->phy->dev, "PHY Stop state not reached %x\n", + state); +} + +static int dw_dphy_gen3_12bit_configure(struct dw_dphy_rx *dphy) +{ + u32 input_freq = dphy->dphy_freq; + u8 range = 0; + + dev_vdbg(&dphy->phy->dev, "12bit: PHY GEN 3: Freq: %u\n", input_freq); + for (range = 0; (range < ARRAY_SIZE(range_gen3) - 1) && + ((input_freq / 1000) > range_gen3[range].freq); + range++) + ; + + dw_dphy_gen3_12bit_tc_power_up(dphy); + dw_dphy_te_write(dphy, RX_SYS_1, range_gen3[range].hsfregrange); + dw_dphy_te_write(dphy, RX_SYS_0, 0x20); + dw_dphy_te_write(dphy, RX_RX_STARTUP_OVR_2, + (u8)range_gen3[range].osc_freq_target); + dw_dphy_te_write(dphy, RX_RX_STARTUP_OVR_3, + (u8)(range_gen3[range].osc_freq_target >> 8)); + dw_dphy_te_write(dphy, RX_RX_STARTUP_OVR_4, 0x01); + + if (dphy->phy_type) { + dw_dphy_te_write(dphy, RX_RX_STARTUP_OVR_1, 0x01); + dw_dphy_te_write(dphy, RX_RX_STARTUP_OVR_0, 0x80); + } + + if (dphy->phy_type || input_freq <= 1500) + dw_dphy_te_write(dphy, RX_SYS_7, 0x38); + + return 0; +} + +static int dw_dphy_gen3_8bit_configure(struct dw_dphy_rx *dphy) +{ + u32 input_freq = dphy->dphy_freq; + u8 data; + u8 range = 0; + + dev_vdbg(&dphy->phy->dev, "8bit: PHY GEN 3: Freq: %u\n", input_freq); + for (range = 0; (range < ARRAY_SIZE(range_gen3) - 1) && + ((input_freq / 1000) > range_gen3[range].freq); + range++) + ; + + dw_dphy_te_write(dphy, RX_SKEW_CAL, dw_dphy_g118_settle(dphy)); + data = 1 << 7 | range_gen3[range].hsfregrange; + dw_dphy_te_write(dphy, HSFREQRANGE_8BIT, data); + dw_dphy_gen3_8bit_tc_power_up(dphy); + + return 0; +} + +static int dw_dphy_gen2_configure(struct dw_dphy_rx *dphy) +{ + u32 input_freq = dphy->dphy_freq; + u8 data; + u8 range = 0; + + /* provide an initial active-high test clear pulse in TESTCLR */ + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 1, PHY_TESTCLR, 1); + dw_dphy_write_msk(dphy, R_CSI2_DPHY_TST_CTRL0, 0, PHY_TESTCLR, 1); + + dev_vdbg(&dphy->phy->dev, "PHY GEN 2: Freq: %u\n", input_freq); + for (range = 0; (range < ARRAY_SIZE(range_gen2) - 1) && + ((input_freq / 1000) > range_gen2[range].freq); range++) + ; + + data = range_gen2[range].hsfregrange << 1; + dw_dphy_te_write(dphy, HSFREQRANGE_8BIT, data); + + return 0; +} + +static int dw_dphy_configure(struct dw_dphy_rx *dphy) +{ + dw_dphy_pwr_down(dphy); + + if (dphy->dphy_gen == GEN3) { +#if IS_ENABLED(CONFIG_DWC_MIPI_TC_DPHY_GEN3) + dw_dphy_if_init(dphy); +#endif + if (dphy->dphy_te_len == BIT12) + dw_dphy_gen3_12bit_configure(dphy); + else + dw_dphy_gen3_8bit_configure(dphy); + } else { + dw_dphy_gen2_configure(dphy); + } + dw_dphy_pwr_up(dphy); + + return 0; +} + +#if IS_ENABLED(CONFIG_DWC_MIPI_TC_DPHY_GEN3) +int dw_dphy_if_set_idelay(struct dw_dphy_rx *dphy, u8 dly, u8 cells) +{ + u32 val = 0; + + dw_dphy_if_write(dphy, IDLYCFG, 0); + dw_dphy_if_write(dphy, IDLYSEL, cells); + dw_dphy_if_write(dphy, IDLYCNTINVAL, dly); + + /* Pulse Value Set */ + dw_dphy_if_write(dphy, IDLYCFG, 1); + usleep_range(10, 20); + dw_dphy_if_write(dphy, IDLYCFG, 0); + + /* Pulse IDELAY CTRL Reset */ + dw_dphy_if_write(dphy, DPHY1REGRSTN, 0); + usleep_range(10, 20); + dw_dphy_if_write(dphy, DPHY1REGRSTN, 1); + + /* Get Value*/ + val = dw_dphy_if_read(dphy, IDLYCNTOUTVAL); + + if (val != dly) { + dev_vdbg(&dphy->phy->dev, + "odelay config failed, set %d get %d", dly, val); + return -EINVAL; + } + + return 0; +} + +int dw_dphy_if_get_idelay(struct dw_dphy_rx *dphy) +{ + return dw_dphy_if_read(dphy, IDLYCNTOUTVAL); +} + +int dw_dphy_if_set_idelay_lane(struct dw_dphy_rx *dphy, u8 dly, u8 lane) +{ + int cell; + + switch (lane) { + case 0: + for (cell = 3; cell <= 10; cell++) + dw_dphy_if_set_idelay(dphy, dly, cell); + break; + case 1: + for (cell = 14; cell <= 21; cell++) + dw_dphy_if_set_idelay(dphy, dly, cell); + break; + case 2: + for (cell = 24; cell <= 31; cell++) + dw_dphy_if_set_idelay(dphy, dly, cell); + break; + case 3: + for (cell = 34; cell <= 41; cell++) + dw_dphy_if_set_idelay(dphy, dly, cell); + break; + case 4: /* ALL */ + dw_dphy_if_set_idelay(dphy, dly, 0x7F); + break; + default: + dev_err(&dphy->phy->dev, "Lane Value not recognized\n"); + return -1; + } + return 0; +} +#endif + +int dw_dphy_init(struct phy *phy) +{ + struct dw_dphy_rx *dphy = phy_get_drvdata(phy); + + dev_vdbg(&dphy->phy->dev, "Init DPHY.\n"); + + dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 0); + dw_dphy_write(dphy, R_CSI2_DPHY_SHUTDOWNZ, 0); + + return 0; +} + +static int dw_dphy_set_phy_state(struct dw_dphy_rx *dphy, u32 on) +{ + u8 hs_freq; + + dphy->lanes_config = dw_dphy_setup_config(dphy); + + if (dphy->dphy_te_len == BIT12) + hs_freq = RX_SYS_1; + else + hs_freq = HSFREQRANGE_8BIT; + + if (on) { + dw_dphy_configure(dphy); + dev_vdbg(&dphy->phy->dev, + "HS Code: 0X%x\n", dw_dphy_te_read(dphy, hs_freq)); + } else { + dw_dphy_write(dphy, R_CSI2_DPHY_SHUTDOWNZ, 0); + dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 0); + } + + return 0; +} + +int dw_dphy_power_on(struct phy *phy) +{ + struct dw_dphy_rx *dphy = phy_get_drvdata(phy); + + return dw_dphy_set_phy_state(dphy, 1); +} + +int dw_dphy_power_off(struct phy *phy) +{ + struct dw_dphy_rx *dphy = phy_get_drvdata(phy); + + return dw_dphy_set_phy_state(dphy, 0); +} + +int dw_dphy_reset(struct phy *phy) +{ + struct dw_dphy_rx *dphy = phy_get_drvdata(phy); + + dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 0); + usleep_range(100, 200); + dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 1); + + return 0; +} diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/media/platform/dwc/dw-dphy-rx.h linux-microchip/drivers/media/platform/dwc/dw-dphy-rx.h --- linux-6.6.51/drivers/media/platform/dwc/dw-dphy-rx.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/media/platform/dwc/dw-dphy-rx.h 2024-11-26 14:02:37.602495560 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates. + * + * Synopsys DesignWare MIPI D-PHY controller driver + * + * Author: Luis Oliveira <luis.oliveira@synopsys.com> + */ + +#ifndef __PHY_SNPS_DPHY_RX_H__ +#define __PHY_SNPS_DPHY_RX_H__ + +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_gpio.h> +#include <linux/phy/phy.h> +#include <linux/phy/phy-mipi-dphy.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> + +/* DPHY interface register bank*/ + +#define R_CSI2_DPHY_SHUTDOWNZ 0x0 +#define R_CSI2_DPHY_RSTZ 0x4 +#define R_CSI2_DPHY_RX 0x8 +#define R_CSI2_DPHY_STOPSTATE 0xC +#define R_CSI2_DPHY_TST_CTRL0 0x10 +#define R_CSI2_DPHY_TST_CTRL1 0x14 +#define R_CSI2_DPHY2_TST_CTRL0 0x18 +#define R_CSI2_DPHY2_TST_CTRL1 0x1C + +enum dphy_id_mask { + DPHY_ID_LANE_SUPPORT = 0, + DPHY_ID_IF = 4, + DPHY_ID_GEN = 8, +}; + +enum dphy_gen_values { + GEN1, + GEN2, + GEN3, +}; + +enum dphy_interface_length { + BIT8 = 8, + BIT12 = 12, +}; + +enum tst_ctrl0 { + PHY_TESTCLR, + PHY_TESTCLK, +}; + +enum tst_ctrl1 { + PHY_TESTDIN = 0, + PHY_TESTDOUT = 8, + PHY_TESTEN = 16, +}; + +enum lanes_config_values { + CTRL_4_LANES, + CTRL_8_LANES, +}; + +enum dphy_tc { + CFGCLKFREQRANGE_TX = 0x02, + CFGCLKFREQRANGE_RX = 0x05, + BYPASS = 0x20, + IO_DS = 0x30, +}; + +enum dphy_8bit_interface_addr { + BANDGAP_CTRL = 0x24, + HS_RX_CTRL_LANE0 = 0x42, + HSFREQRANGE_8BIT = 0x44, + OSC_FREQ_TARGET_RX0_LSB = 0x4e, + OSC_FREQ_TARGET_RX0_MSB = 0x4f, + HS_RX_CTRL_LANE1 = 0x52, + OSC_FREQ_TARGET_RX1_LSB = 0x5e, + OSC_FREQ_TARGET_RX1_MSB = 0x5f, + RX_SKEW_CAL = 0x7e, + HS_RX_CTRL_LANE2 = 0x82, + OSC_FREQ_TARGET_RX2_LSB = 0x8e, + OSC_FREQ_TARGET_RX2_MSB = 0x8f, + HS_RX_CTRL_LANE3 = 0x92, + OSC_FREQ_TARGET_RX3_LSB = 0x9e, + OSC_FREQ_TARGET_RX3_MSB = 0x9f, +}; + +enum dphy_12bit_interface_addr { + RX_SYS_0 = 0x01, + RX_SYS_1 = 0x02, + RX_SYS_7 = 0x08, + RX_RX_STARTUP_OVR_0 = 0xe0, + RX_RX_STARTUP_OVR_1 = 0xe1, + RX_RX_STARTUP_OVR_2 = 0xe2, + RX_RX_STARTUP_OVR_3 = 0xe3, + RX_RX_STARTUP_OVR_4 = 0xe4, +}; + +#if IS_ENABLED(CONFIG_DWC_MIPI_TC_DPHY_GEN3) +/* Testchip interface register bank */ +#define IDLYCFG 0x00 +#define IDLYSEL 0x04 +#define IDLYCNTINVAL 0x08 +#define IDLYCNTOUTVAL 0x0c +#define DPHY1REGRSTN 0x10 +#define DPHYZCALSTAT 0x14 +#define DPHYZCALCTRL 0x18 +#define DPHYLANE0STAT 0x1c +#define DPHYLANE1STAT 0x20 +#define DPHYLANE2STAT 0x24 +#define DPHYLANE3STAT 0x28 +#define DPHYCLKSTAT 0x2c +#define DPHYZCLKCTRL 0x30 +#define TCGENPURPOSOUT 0x34 +#define TCGENPURPOSIN 0x38 +#define DPHYGENERICOUT 0x3c +#define DPHYGENERICIN 0x40 +#define DPHYGLUEIFTESTER 0x44 +#define DPHYID 0x100 + +#define DPHY_DEFAULT_FREQ 300000 + +enum glueiftester { + RESET = 0x0, + TX_PHY = 0x1, + RX_PHY = 0x2, + GLUELOGIC = 0x4, +}; +#endif + +/** + * struct phy specifies associated phy component + * struct cfg to pass mipi dphy specific configurations + * @lanes_config lanes configuration + * @dphy_freq operating frequency of the d-phy (mbps) + * @phy_type dphy can be of two types, passed here + * @dphy_gen dphy can be of three generations, passed here + * @dphy_te_len bus width + * @max_lanes maximum number of lanes + * @lp_time time in low-power + * @base_address memmory address of dphy test interface + * @dphy1_if_addr gluelogic dphy 1 memmory address of interface + * @dphy2_if_addr gluelogic dphy 2 memmory address of interface + * @config_8l eight lanes configuration + */ + +struct dw_dphy_rx { + struct phy *phy; + struct phy_configure_opts_mipi_dphy *cfg; + u32 lanes_config; + u32 dphy_freq; + u32 phy_type; + u32 dphy_gen; + u32 dphy_te_len; + u32 max_lanes; + u32 lp_time; + void __iomem *base_address; +#if IS_ENABLED(CONFIG_DWC_MIPI_TC_DPHY_GEN3) + void __iomem *dphy1_if_addr; + void __iomem *dphy2_if_addr; + u8 config_8l; + u8 (*get_config_8l)(struct device *dev, struct dw_dphy_rx *dphy); +#endif + u8 (*phy_register)(struct device *dev); + void (*phy_unregister)(struct device *dev); +}; + +int dw_dphy_init(struct phy *phy); +int dw_dphy_reset(struct phy *phy); +int dw_dphy_power_off(struct phy *phy); +int dw_dphy_power_on(struct phy *phy); +u8 dw_dphy_setup_config(struct dw_dphy_rx *dphy); +void dw_dphy_write(struct dw_dphy_rx *dphy, u32 address, u32 data); +u32 dw_dphy_read(struct dw_dphy_rx *dphy, u32 address); +int dw_dphy_te_read(struct dw_dphy_rx *dphy, u32 addr); + +#if IS_ENABLED(CONFIG_DWC_MIPI_TC_DPHY_GEN3) +u32 dw_dphy_if_read(struct dw_dphy_rx *dphy, u32 address); +int dw_dphy_if_get_idelay(struct dw_dphy_rx *dphy); +int dw_dphy_if_set_idelay_lane(struct dw_dphy_rx *dphy, u8 dly, u8 lane); +int dw_dphy_create_capabilities_sysfs(struct platform_device *pdev); + +static inline +u32 dw_dphy_if_read_msk(struct dw_dphy_rx *dphy, + u32 address, u8 shift, u8 width) +{ + return (dw_dphy_if_read(dphy, address) >> shift) & ((1 << width) - 1); +} +#endif /*IS_ENABLED(CONFIG_DWC_MIPI_TC_DPHY_GEN3)*/ + +static inline struct phy *dw_dphy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct dw_dphy_rx *dphy = dev_get_drvdata(dev); + + return dphy->phy; +} + +static inline +u32 dw_dphy_read_msk(struct dw_dphy_rx *dev, u32 address, u8 shift, u8 width) +{ + return (dw_dphy_read(dev, address) >> shift) & ((1 << width) - 1); +} +#endif /*__PHY_SNPS_DPHY_RX_H__*/ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/media/platform/dwc/dw-dphy-sysfs.c linux-microchip/drivers/media/platform/dwc/dw-dphy-sysfs.c --- linux-6.6.51/drivers/media/platform/dwc/dw-dphy-sysfs.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/media/platform/dwc/dw-dphy-sysfs.c 2024-11-26 14:02:37.602495560 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates. + * + * Synopsys DesignWare MIPI D-PHY controller driver. + * SysFS components for the platform driver + * + * Author: Luis Oliveira <luis.oliveira@synopsys.com> + */ + +#include "dw-dphy-rx.h" + +static ssize_t dphy_reset_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dw_dphy_rx *dphy = platform_get_drvdata(pdev); + char buffer[15]; + + dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 0); + usleep_range(100, 200); + dw_dphy_write(dphy, R_CSI2_DPHY_RSTZ, 1); + + return strlcpy(buf, buffer, PAGE_SIZE); +} + +static ssize_t dphy_freq_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int ret; + unsigned long freq; + + struct platform_device *pdev = to_platform_device(dev); + struct dw_dphy_rx *dphy = platform_get_drvdata(pdev); + + ret = kstrtoul(buf, 10, &freq); + if (ret < 0) + return ret; + + if (freq > 2500) { + dev_info(dev, "Freq must be under 2500 Mhz\n"); + return count; + } + if (freq < 80) { + dev_info(dev, "Freq must be over 80 Mhz\n"); + return count; + } + + dev_vdbg(dev, "Data Rate %lu Mbps\n", freq); + dphy->dphy_freq = freq; + + return count; +} + +static ssize_t dphy_freq_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dw_dphy_rx *dphy = platform_get_drvdata(pdev); + char buffer[15]; + + snprintf(buffer, + sizeof(buffer), + "Freq %d\n", dphy->dphy_freq); + + return strlcpy(buf, buffer, PAGE_SIZE); +} + +static ssize_t dphy_addr_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dw_dphy_rx *dphy = platform_get_drvdata(pdev); + unsigned long val; + u8 addr, payload; + int ret; + + ret = kstrtoul(buf, 32, &val); + if (ret < 0) + return ret; + + payload = (u16)val; + addr = (u16)(val >> 16); + + dev_vdbg(dev, "addr 0x%lX\n", val); + dev_vdbg(dev, "payload: 0x%X\n", addr); + dev_vdbg(dev, "Addr [0x%x] -> 0x%x\n", (unsigned int)addr, + dw_dphy_te_read(dphy, addr)); + + return count; +} + +#if IS_ENABLED(CONFIG_DWC_MIPI_TC_DPHY_GEN3) +static ssize_t idelay_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dw_dphy_rx *dphy = platform_get_drvdata(pdev); + char buffer[15]; + + snprintf(buffer, + sizeof(buffer), "idelay %d\n", dw_dphy_if_get_idelay(dphy)); + + return strlcpy(buf, buffer, PAGE_SIZE); +} + +static ssize_t idelay_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dw_dphy_rx *dphy = platform_get_drvdata(pdev); + unsigned long val; + u8 lane, delay; + int ret; + + ret = kstrtoul(buf, 16, &val); + if (ret < 0) + return ret; + + lane = (u8)val; + delay = (u8)(val >> 8); + + dev_vdbg(dev, "Lanes %u\n", lane); + dev_vdbg(dev, "Delay %u\n", delay); + + dw_dphy_if_set_idelay_lane(dphy, delay, lane); + + return count; +} +#endif + +static ssize_t len_config_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dw_dphy_rx *dphy = platform_get_drvdata(pdev); + unsigned long length; + int ret; + + ret = kstrtoul(buf, 10, &length); + if (ret < 0) + return ret; + + if (length == BIT8) + dev_vdbg(dev, "Configured for 8-bit interface\n"); + else if (length == BIT12) + dev_vdbg(dev, "Configured for 12-bit interface\n"); + else + return count; + + dphy->dphy_te_len = length; + + return count; +} + +static ssize_t len_config_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dw_dphy_rx *dphy = platform_get_drvdata(pdev); + char buffer[20]; + + snprintf(buffer, sizeof(buffer), "Length %d\n", dphy->dphy_te_len); + + return strlcpy(buf, buffer, PAGE_SIZE); +} + +static ssize_t dw_dphy_g118_settle_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dw_dphy_rx *dphy = platform_get_drvdata(pdev); + unsigned long lp_time; + int ret; + + ret = kstrtoul(buf, 10, &lp_time); + if (ret < 0) + return ret; + + if (lp_time > 1 && lp_time < 10000) { + dphy->lp_time = lp_time; + } else { + dev_vdbg(dev, "Invalid Value configuring for 1000 ns\n"); + dphy->lp_time = 1000; + } + + dphy->lp_time = lp_time; + + return count; +} + +static ssize_t dw_dphy_g118_settle_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dw_dphy_rx *dphy = platform_get_drvdata(pdev); + char buffer[10]; + + snprintf(buffer, sizeof(buffer), "Settle %d ns\n", dphy->lp_time); + + return strlcpy(buf, buffer, PAGE_SIZE); +} + +static DEVICE_ATTR_RO(dphy_reset); +static DEVICE_ATTR_RW(dphy_freq); +static DEVICE_ATTR_WO(dphy_addr); +#if IS_ENABLED(CONFIG_DWC_MIPI_TC_DPHY_GEN3) +static DEVICE_ATTR_RW(idelay); +#endif +static DEVICE_ATTR_RW(len_config); +static DEVICE_ATTR_RW(dw_dphy_g118_settle); + +int dw_dphy_create_capabilities_sysfs(struct platform_device *pdev) +{ + device_create_file(&pdev->dev, &dev_attr_dphy_reset); + device_create_file(&pdev->dev, &dev_attr_dphy_freq); + device_create_file(&pdev->dev, &dev_attr_dphy_addr); +#if IS_ENABLED(CONFIG_DWC_MIPI_TC_DPHY_GEN3) + device_create_file(&pdev->dev, &dev_attr_idelay); +#endif + device_create_file(&pdev->dev, &dev_attr_len_config); + device_create_file(&pdev->dev, &dev_attr_dw_dphy_g118_settle); + return 0; +} diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/media/platform/dwc/dw-mipi-csi.c linux-microchip/drivers/media/platform/dwc/dw-mipi-csi.c --- linux-6.6.51/drivers/media/platform/dwc/dw-mipi-csi.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/media/platform/dwc/dw-mipi-csi.c 2024-11-26 14:02:37.602495560 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates. + * + * Synopsys DesignWare MIPI CSI-2 Host controller driver + * Core MIPI CSI-2 functions + * + * Author: Luis Oliveira <Luis.Oliveira@synopsys.com> + */ + +#include "dw-mipi-csi.h" + +static struct R_CSI2 reg = { + .VERSION = 0x00, + .N_LANES = 0x04, + .CTRL_RESETN = 0x08, + .INTERRUPT = 0x0C, + .DATA_IDS_1 = 0x10, + .DATA_IDS_2 = 0x14, + .IPI_MODE = 0x80, + .IPI_VCID = 0x84, + .IPI_DATA_TYPE = 0x88, + .IPI_MEM_FLUSH = 0x8C, + .IPI_HSA_TIME = 0x90, + .IPI_HBP_TIME = 0x94, + .IPI_HSD_TIME = 0x98, + .IPI_HLINE_TIME = 0x9C, + .IPI_SOFTRSTN = 0xA0, + .IPI_ADV_FEATURES = 0xAC, + .IPI_VSA_LINES = 0xB0, + .IPI_VBP_LINES = 0xB4, + .IPI_VFP_LINES = 0xB8, + .IPI_VACTIVE_LINES = 0xBC, + .INT_PHY_FATAL = 0xe0, + .MASK_INT_PHY_FATAL = 0xe4, + .FORCE_INT_PHY_FATAL = 0xe8, + .INT_PKT_FATAL = 0xf0, + .MASK_INT_PKT_FATAL = 0xf4, + .FORCE_INT_PKT_FATAL = 0xf8, + .INT_PHY = 0x110, + .MASK_INT_PHY = 0x114, + .FORCE_INT_PHY = 0x118, + .INT_LINE = 0x130, + .MASK_INT_LINE = 0x134, + .FORCE_INT_LINE = 0x138, + .INT_IPI = 0x140, + .MASK_INT_IPI = 0x144, + .FORCE_INT_IPI = 0x148, +}; + +struct interrupt_type csi_int = { + .PHY_FATAL = BIT(0), + .PKT_FATAL = BIT(1), + .PHY = BIT(16), +}; + +#define dw_print(VAR) \ + dev_info(csi_dev->dev, "%s: 0x%x: %X\n", "#VAR#",\ + VAR, dw_mipi_csi_read(csi_dev, VAR)) + +void dw_mipi_csi_write_part(struct dw_csi *dev, u32 address, u32 data, + u8 shift, u8 width) +{ + u32 mask = (1 << width) - 1; + u32 temp = dw_mipi_csi_read(dev, address); + + temp &= ~(mask << shift); + temp |= (data & mask) << shift; + dw_mipi_csi_write(dev, address, temp); +} + +void dw_mipi_csi_reset(struct dw_csi *csi_dev) +{ + dw_mipi_csi_write(csi_dev, reg.CTRL_RESETN, 0); + usleep_range(100, 200); + dw_mipi_csi_write(csi_dev, reg.CTRL_RESETN, 1); +} + +int dw_mipi_csi_mask_irq_power_off(struct dw_csi *csi_dev) +{ + if (csi_dev->hw_version_major == 1) { + /* set only one lane (lane 0) as active (ON) */ + dw_mipi_csi_write(csi_dev, reg.N_LANES, 0); + dw_mipi_csi_write(csi_dev, reg.MASK_INT_PHY_FATAL, 0); + dw_mipi_csi_write(csi_dev, reg.MASK_INT_PKT_FATAL, 0); + dw_mipi_csi_write(csi_dev, reg.MASK_INT_PHY, 0); + dw_mipi_csi_write(csi_dev, reg.MASK_INT_LINE, 0); + dw_mipi_csi_write(csi_dev, reg.MASK_INT_IPI, 0); + + /* only for version 1.30 */ + if (csi_dev->hw_version_minor == 30) + dw_mipi_csi_write(csi_dev, + reg.MASK_INT_FRAME_FATAL, 0); + + dw_mipi_csi_write(csi_dev, reg.CTRL_RESETN, 0); + + /* only for version 1.40 */ + if (csi_dev->hw_version_minor == 40) { + dw_mipi_csi_write(csi_dev, + reg.MSK_BNDRY_FRAME_FATAL, 0); + dw_mipi_csi_write(csi_dev, + reg.MSK_SEQ_FRAME_FATAL, 0); + dw_mipi_csi_write(csi_dev, + reg.MSK_CRC_FRAME_FATAL, 0); + dw_mipi_csi_write(csi_dev, reg.MSK_PLD_CRC_FATAL, 0); + dw_mipi_csi_write(csi_dev, reg.MSK_DATA_ID, 0); + dw_mipi_csi_write(csi_dev, reg.MSK_ECC_CORRECT, 0); + } + } + + return 0; +} + +int dw_mipi_csi_hw_stdby(struct dw_csi *csi_dev) +{ + if (csi_dev->hw_version_major == 1) { + /* set only one lane (lane 0) as active (ON) */ + dw_mipi_csi_reset(csi_dev); + dw_mipi_csi_write(csi_dev, reg.N_LANES, 0); + phy_init(csi_dev->phy); + + /* only for version 1.30 */ + if (csi_dev->hw_version_minor == 30) + dw_mipi_csi_write(csi_dev, + reg.MASK_INT_FRAME_FATAL, + GENMASK(31, 0)); + + /* common */ + dw_mipi_csi_write(csi_dev, reg.MASK_INT_PHY_FATAL, + GENMASK(8, 0)); + dw_mipi_csi_write(csi_dev, reg.MASK_INT_PKT_FATAL, + GENMASK(1, 0)); + dw_mipi_csi_write(csi_dev, reg.MASK_INT_PHY, GENMASK(23, 0)); + dw_mipi_csi_write(csi_dev, reg.MASK_INT_LINE, GENMASK(23, 0)); + dw_mipi_csi_write(csi_dev, reg.MASK_INT_IPI, GENMASK(5, 0)); + + /* only for version 1.40 */ + if (csi_dev->hw_version_minor == 40) { + dw_mipi_csi_write(csi_dev, + reg.MSK_BNDRY_FRAME_FATAL, + GENMASK(31, 0)); + dw_mipi_csi_write(csi_dev, + reg.MSK_SEQ_FRAME_FATAL, + GENMASK(31, 0)); + dw_mipi_csi_write(csi_dev, + reg.MSK_CRC_FRAME_FATAL, + GENMASK(31, 0)); + dw_mipi_csi_write(csi_dev, + reg.MSK_PLD_CRC_FATAL, + GENMASK(31, 0)); + dw_mipi_csi_write(csi_dev, + reg.MSK_DATA_ID, GENMASK(31, 0)); + dw_mipi_csi_write(csi_dev, + reg.MSK_ECC_CORRECT, GENMASK(31, 0)); + } + } + return 0; +} + +void dw_mipi_csi_set_ipi_fmt(struct dw_csi *csi_dev) +{ + struct device *dev = csi_dev->dev; + + if (csi_dev->ipi_dt) { + dw_mipi_csi_write(csi_dev, reg.IPI_DATA_TYPE, csi_dev->ipi_dt); + switch (csi_dev->ipi_dt) { + case CSI_2_YUV420_8: + case CSI_2_YUV420_8_LEG: + case CSI_2_YUV420_8_SHIFT: + break; + case CSI_2_YUV420_10: + case CSI_2_YUV420_10_SHIFT: + break; + } + } else { + switch (csi_dev->fmt->mbus_code) { + /* RGB 666 */ + case MEDIA_BUS_FMT_RGB666_1X18: + csi_dev->ipi_dt = CSI_2_RGB666; + break; + /* RGB 565 */ + case MEDIA_BUS_FMT_RGB565_2X8_BE: + case MEDIA_BUS_FMT_RGB565_2X8_LE: + csi_dev->ipi_dt = CSI_2_RGB565; + break; + /* RGB 555 */ + case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE: + case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: + csi_dev->ipi_dt = CSI_2_RGB555; + break; + /* RGB 444 */ + case MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE: + case MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE: + csi_dev->ipi_dt = CSI_2_RGB444; + break; + /* RGB 888 */ + break; + case MEDIA_BUS_FMT_RGB888_2X12_LE: + case MEDIA_BUS_FMT_RGB888_2X12_BE: + csi_dev->ipi_dt = CSI_2_RGB888; + break; + /* RAW 10 */ + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_BE: + csi_dev->ipi_dt = CSI_2_RAW10; + break; + /* RAW 12 */ + case MEDIA_BUS_FMT_SBGGR12_1X12: + csi_dev->ipi_dt = CSI_2_RAW12; + break; + /* RAW 14 */ + case MEDIA_BUS_FMT_SBGGR14_1X14: + csi_dev->ipi_dt = CSI_2_RAW14; + break; + /* RAW 16 */ + case MEDIA_BUS_FMT_SBGGR16_1X16: + csi_dev->ipi_dt = CSI_2_RAW16; + break; + /* RAW 8 */ + case MEDIA_BUS_FMT_SBGGR8_1X8: + csi_dev->ipi_dt = CSI_2_RAW8; + break; + /* YUV 422 8-bit */ + case MEDIA_BUS_FMT_YVYU8_2X8: + csi_dev->ipi_dt = CSI_2_RAW8; + break; + /* YUV 422 10-bit */ + case MEDIA_BUS_FMT_VYUY8_1X16: + csi_dev->ipi_dt = CSI_2_YUV422_8; + break; + /* YUV 420 8-bit LEGACY */ + case MEDIA_BUS_FMT_Y8_1X8: + csi_dev->ipi_dt = CSI_2_RAW8; + break; + /* YUV 420 10-bit */ + case MEDIA_BUS_FMT_Y10_1X10: + csi_dev->ipi_dt = CSI_2_RAW8; + break; + default: + break; + } + dw_mipi_csi_write(csi_dev, reg.DATA_IDS_1, csi_dev->ipi_dt); + } + dev_dbg(dev, "Selected IPI Data Type 0x%X\n", csi_dev->ipi_dt); +} + +void dw_mipi_csi_fill_timings(struct dw_csi *dev, + struct v4l2_subdev_format *fmt) +{ + /* expected values */ + dev->hw.virtual_ch = 0; + dev->hw.ipi_color_mode = COLOR48; + dev->hw.ipi_auto_flush = 1; + dev->hw.ipi_mode = CAMERA_TIMING; + dev->hw.ipi_cut_through = CTINACTIVE; + dev->hw.ipi_adv_features = LINE_EVENT_SELECTION(EVSELAUTO); + dev->hw.htotal = fmt->format.width + dev->hw.hsa + + dev->hw.hbp + dev->hw.hsd; + dev->hw.vactive = fmt->format.height; + dev->hw.output = 2; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + dev_dbg(dev->dev, "*********** timings *********\n"); + dev_dbg(dev->dev, "Horizontal Sync Active: %d\n", dev->hw.hsa); + dev_dbg(dev->dev, "Horizontal Back Porch: %d\n", dev->hw.hbp); + dev_dbg(dev->dev, "Horizontal Width: %d\n", fmt->format.width); + dev_dbg(dev->dev, "Horizontal Total: %d\n", dev->hw.htotal); + dev_dbg(dev->dev, "Vertical Sync Active: %d\n", dev->hw.vsa); + dev_dbg(dev->dev, "Vertical Back Porch: %d\n", dev->hw.vbp); + dev_dbg(dev->dev, "Vertical Front Porch: %d\n", dev->hw.vfp); + dev_dbg(dev->dev, "Vertical Active: %d\n", dev->hw.vactive); + } +} + +void dw_mipi_csi_start(struct dw_csi *csi_dev) +{ + struct device *dev = csi_dev->dev; + + dw_mipi_csi_write(csi_dev, reg.N_LANES, (csi_dev->hw.num_lanes - 1)); + dev_dbg(dev, "number of lanes: %d\n", csi_dev->hw.num_lanes); + + /* IPI Related Configuration */ + if (csi_dev->hw.output == IPI_OUT || csi_dev->hw.output == BOTH_OUT) { + if (csi_dev->hw_version_major >= 1) { + if (csi_dev->hw_version_minor >= 20) + dw_mipi_csi_write(csi_dev, + reg.IPI_ADV_FEATURES, + csi_dev->hw.ipi_adv_features); + if (csi_dev->hw_version_minor >= 30) + dw_mipi_csi_write(csi_dev, + reg.IPI_SOFTRSTN, 0x1); + } + /* address | data, | shift | width */ + dw_mipi_csi_write_part(csi_dev, reg.IPI_MODE, 1, 24, 1); + dw_mipi_csi_write_part(csi_dev, + reg.IPI_MODE, + csi_dev->hw.ipi_mode, + 0, 1); + if (csi_dev->hw.ipi_mode == CAMERA_TIMING) { + dw_mipi_csi_write(csi_dev, + reg.IPI_ADV_FEATURES, + LINE_EVENT_SELECTION(EVSELPROG) | + EN_VIDEO | + EN_LINE_START | + EN_NULL | + EN_BLANKING | + EN_EMBEDDED); + } + dw_mipi_csi_write_part(csi_dev, + reg.IPI_MODE, + csi_dev->hw.ipi_color_mode, + 8, 1); + dw_mipi_csi_write_part(csi_dev, + reg.IPI_MODE, + csi_dev->hw.ipi_cut_through, + 16, 1); + dw_mipi_csi_write_part(csi_dev, + reg.IPI_VCID, + csi_dev->hw.virtual_ch, + 0, 2); + dw_mipi_csi_write_part(csi_dev, + reg.IPI_MEM_FLUSH, + csi_dev->hw.ipi_auto_flush, + 8, 1); + + dev_vdbg(dev, "*********** config *********\n"); + dev_vdbg(dev, "IPI enable: %s\n", + csi_dev->hw.output ? "YES" : "NO"); + dev_vdbg(dev, "video mode transmission type: %s timming\n", + csi_dev->hw.ipi_mode ? "controller" : "camera"); + dev_vdbg(dev, "Color Mode: %s\n", + csi_dev->hw.ipi_color_mode ? "16 bits" : "48 bits"); + dev_vdbg(dev, "Cut Through Mode: %s\n", + csi_dev->hw.ipi_cut_through ? "enable" : "disable"); + dev_vdbg(dev, "Virtual Channel: %d\n", + csi_dev->hw.virtual_ch); + dev_vdbg(dev, "Auto-flush: %d\n", + csi_dev->hw.ipi_auto_flush); + dw_mipi_csi_write(csi_dev, reg.IPI_SOFTRSTN, 1); + + if (csi_dev->hw.ipi_mode == AUTO_TIMING) + phy_power_on(csi_dev->phy); + + dw_mipi_csi_write(csi_dev, + reg.IPI_HSA_TIME, csi_dev->hw.hsa); + dw_mipi_csi_write(csi_dev, + reg.IPI_HBP_TIME, csi_dev->hw.hbp); + dw_mipi_csi_write(csi_dev, + reg.IPI_HSD_TIME, csi_dev->hw.hsd); + dw_mipi_csi_write(csi_dev, + reg.IPI_HLINE_TIME, csi_dev->hw.htotal); + dw_mipi_csi_write(csi_dev, + reg.IPI_VSA_LINES, csi_dev->hw.vsa); + dw_mipi_csi_write(csi_dev, + reg.IPI_VBP_LINES, csi_dev->hw.vbp); + dw_mipi_csi_write(csi_dev, + reg.IPI_VFP_LINES, csi_dev->hw.vfp); + dw_mipi_csi_write(csi_dev, + reg.IPI_VACTIVE_LINES, csi_dev->hw.vactive); + } + phy_power_on(csi_dev->phy); +} + +int dw_mipi_csi_irq_handler(struct dw_csi *csi_dev) +{ + struct device *dev = csi_dev->dev; + u32 global_int_status, i_sts; + unsigned long flags; + + spin_lock_irqsave(&csi_dev->slock, flags); + global_int_status = dw_mipi_csi_read(csi_dev, reg.INTERRUPT); + + if (global_int_status & csi_int.PHY_FATAL) { + i_sts = dw_mipi_csi_read(csi_dev, reg.INT_PHY_FATAL); + dev_err_ratelimited(dev, "int %08X: PHY FATAL: %08X\n", + reg.INT_PHY_FATAL, i_sts); + } + + if (global_int_status & csi_int.PKT_FATAL) { + i_sts = dw_mipi_csi_read(csi_dev, reg.INT_PKT_FATAL); + dev_err_ratelimited(dev, "int %08X: PKT FATAL: %08X\n", + reg.INT_PKT_FATAL, i_sts); + } + + if (global_int_status & csi_int.FRAME_FATAL && + csi_dev->hw_version_major == 1 && + csi_dev->hw_version_minor == 30) { + i_sts = dw_mipi_csi_read(csi_dev, reg.INT_FRAME_FATAL); + dev_err_ratelimited(dev, "int %08X: FRAME FATAL: %08X\n", + reg.INT_FRAME_FATAL, i_sts); + } + + if (global_int_status & csi_int.PHY) { + i_sts = dw_mipi_csi_read(csi_dev, reg.INT_PHY); + dev_err_ratelimited(dev, "int %08X: PHY: %08X\n", + reg.INT_PHY, i_sts); + } + + if (global_int_status & csi_int.PKT && + csi_dev->hw_version_major == 1 && + csi_dev->hw_version_minor <= 30) { + i_sts = dw_mipi_csi_read(csi_dev, reg.INT_PKT); + dev_err_ratelimited(dev, "int %08X: PKT: %08X\n", + reg.INT_PKT, i_sts); + } + + if (global_int_status & csi_int.LINE) { + i_sts = dw_mipi_csi_read(csi_dev, reg.INT_LINE); + dev_err_ratelimited(dev, "int %08X: LINE: %08X\n", + reg.INT_LINE, i_sts); + } + + if (global_int_status & csi_int.IPI) { + i_sts = dw_mipi_csi_read(csi_dev, reg.INT_IPI); + dev_err_ratelimited(dev, "int %08X: IPI: %08X\n", + reg.INT_IPI, i_sts); + } + + if (global_int_status & csi_int.BNDRY_FRAME_FATAL) { + i_sts = dw_mipi_csi_read(csi_dev, reg.ST_BNDRY_FRAME_FATAL); + dev_err_ratelimited(dev, + "int %08X: ST_BNDRY_FRAME_FATAL: %08X\n", + reg.ST_BNDRY_FRAME_FATAL, i_sts); + } + + if (global_int_status & csi_int.SEQ_FRAME_FATAL) { + i_sts = dw_mipi_csi_read(csi_dev, reg.ST_SEQ_FRAME_FATAL); + dev_err_ratelimited(dev, + "int %08X: ST_SEQ_FRAME_FATAL: %08X\n", + reg.ST_SEQ_FRAME_FATAL, i_sts); + } + + if (global_int_status & csi_int.CRC_FRAME_FATAL) { + i_sts = dw_mipi_csi_read(csi_dev, reg.ST_CRC_FRAME_FATAL); + dev_err_ratelimited(dev, + "int %08X: ST_CRC_FRAME_FATAL: %08X\n", + reg.ST_CRC_FRAME_FATAL, i_sts); + } + + if (global_int_status & csi_int.PLD_CRC_FATAL) { + i_sts = dw_mipi_csi_read(csi_dev, reg.ST_PLD_CRC_FATAL); + dev_err_ratelimited(dev, + "int %08X: ST_PLD_CRC_FATAL: %08X\n", + reg.ST_PLD_CRC_FATAL, i_sts); + } + + if (global_int_status & csi_int.DATA_ID) { + i_sts = dw_mipi_csi_read(csi_dev, reg.ST_DATA_ID); + dev_err_ratelimited(dev, "int %08X: ST_DATA_ID: %08X\n", + reg.ST_DATA_ID, i_sts); + } + + if (global_int_status & csi_int.ECC_CORRECTED) { + i_sts = dw_mipi_csi_read(csi_dev, reg.ST_ECC_CORRECT); + dev_err_ratelimited(dev, "int %08X: ST_ECC_CORRECT: %08X\n", + reg.ST_ECC_CORRECT, i_sts); + } + + spin_unlock_irqrestore(&csi_dev->slock, flags); + + return 1; +} + +void dw_mipi_csi_get_version(struct dw_csi *csi_dev) +{ + u32 hw_version; + + hw_version = dw_mipi_csi_read(csi_dev, reg.VERSION); + csi_dev->hw_version_major = (u8)((hw_version >> 24) - '0'); + csi_dev->hw_version_minor = (u8)((hw_version >> 16) - '0'); + csi_dev->hw_version_minor = csi_dev->hw_version_minor * 10; + csi_dev->hw_version_minor += (u8)((hw_version >> 8) - '0'); +} + +int dw_mipi_csi_specific_mappings(struct dw_csi *csi_dev) +{ + struct device *dev = csi_dev->dev; + + if (csi_dev->hw_version_major == 1) { + if (csi_dev->hw_version_minor == 30) { + /* + * Hardware registers that were + * exclusive to version < 1.40 + */ + reg.INT_FRAME_FATAL = 0x100; + reg.MASK_INT_FRAME_FATAL = 0x104; + reg.FORCE_INT_FRAME_FATAL = 0x108; + reg.INT_PKT = 0x120; + reg.MASK_INT_PKT = 0x124; + reg.FORCE_INT_PKT = 0x128; + + /* interrupt source present until this release */ + csi_int.PKT = BIT(17); + csi_int.LINE = BIT(18); + csi_int.IPI = BIT(19); + csi_int.FRAME_FATAL = BIT(2); + + } else if (csi_dev->hw_version_minor == 40) { + /* + * HW registers that were added + * to version 1.40 + */ + reg.ST_BNDRY_FRAME_FATAL = 0x280; + reg.MSK_BNDRY_FRAME_FATAL = 0x284; + reg.FORCE_BNDRY_FRAME_FATAL = 0x288; + reg.ST_SEQ_FRAME_FATAL = 0x290; + reg.MSK_SEQ_FRAME_FATAL = 0x294; + reg.FORCE_SEQ_FRAME_FATAL = 0x298; + reg.ST_CRC_FRAME_FATAL = 0x2a0; + reg.MSK_CRC_FRAME_FATAL = 0x2a4; + reg.FORCE_CRC_FRAME_FATAL = 0x2a8; + reg.ST_PLD_CRC_FATAL = 0x2b0; + reg.MSK_PLD_CRC_FATAL = 0x2b4; + reg.FORCE_PLD_CRC_FATAL = 0x2b8; + reg.ST_DATA_ID = 0x2c0; + reg.MSK_DATA_ID = 0x2c4; + reg.FORCE_DATA_ID = 0x2c8; + reg.ST_ECC_CORRECT = 0x2d0; + reg.MSK_ECC_CORRECT = 0x2d4; + reg.FORCE_ECC_CORRECT = 0x2d8; + reg.DATA_IDS_VC_1 = 0x0; + reg.DATA_IDS_VC_2 = 0x0; + reg.VC_EXTENSION = 0x0; + + /* interrupts map were changed */ + csi_int.LINE = BIT(17); + csi_int.IPI = BIT(18); + csi_int.BNDRY_FRAME_FATAL = BIT(2); + csi_int.SEQ_FRAME_FATAL = BIT(3); + csi_int.CRC_FRAME_FATAL = BIT(4); + csi_int.PLD_CRC_FATAL = BIT(5); + csi_int.DATA_ID = BIT(6); + csi_int.ECC_CORRECTED = BIT(7); + + } else { + dev_info(dev, "Version minor not supported."); + } + } else { + dev_info(dev, "Version major not supported."); + } + return 0; +} + +void dw_mipi_csi_dump(struct dw_csi *csi_dev) +{ + dw_print(reg.VERSION); + dw_print(reg.N_LANES); + dw_print(reg.CTRL_RESETN); + dw_print(reg.INTERRUPT); + dw_print(reg.DATA_IDS_1); + dw_print(reg.DATA_IDS_2); + dw_print(reg.IPI_MODE); + dw_print(reg.IPI_VCID); + dw_print(reg.IPI_DATA_TYPE); + dw_print(reg.IPI_MEM_FLUSH); + dw_print(reg.IPI_HSA_TIME); + dw_print(reg.IPI_HBP_TIME); + dw_print(reg.IPI_HSD_TIME); + dw_print(reg.IPI_HLINE_TIME); + dw_print(reg.IPI_SOFTRSTN); + dw_print(reg.IPI_ADV_FEATURES); + dw_print(reg.IPI_VSA_LINES); + dw_print(reg.IPI_VBP_LINES); + dw_print(reg.IPI_VFP_LINES); + dw_print(reg.IPI_VACTIVE_LINES); + dw_print(reg.IPI_DATA_TYPE); + dw_print(reg.VERSION); + dw_print(reg.IPI_ADV_FEATURES); +} diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/media/platform/dwc/dw-mipi-csi.h linux-microchip/drivers/media/platform/dwc/dw-mipi-csi.h --- linux-6.6.51/drivers/media/platform/dwc/dw-mipi-csi.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/media/platform/dwc/dw-mipi-csi.h 2024-11-26 14:02:37.602495560 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates. + * + * Synopsys DesignWare MIPI CSI-2 Host controller driver + * + * Author: Luis Oliveira <Luis.Oliveira@synopsys.com> + */ + +#ifndef _DW_MIPI_CSI_H__ +#define _DW_MIPI_CSI_H__ + +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/phy/phy.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/platform_device.h> +#include <linux/ratelimit.h> +#include <linux/reset.h> +#include <linux/videodev2.h> +#include <linux/wait.h> +#include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> +#include <media/dwc/dw-mipi-csi-pltfrm.h> + +/* Advanced features */ +#define IPI_DT_OVERWRITE BIT(0) +#define DATA_TYPE_OVERWRITE(dt) (((dt) & GENMASK(5, 0)) << 8) +#define LINE_EVENT_SELECTION(n) ((n) << 16) + +enum line_event { + EVSELAUTO = 0, + EVSELPROG = 1, +}; + +#define EN_VIDEO BIT(17) +#define EN_LINE_START BIT(18) +#define EN_NULL BIT(19) +#define EN_BLANKING BIT(20) +#define EN_EMBEDDED BIT(21) +#define IPI_SYNC_EVENT_MODE(n) ((n) << 24) + +enum sync_event { + SYNCEVFSN = 0, + SYNCEVFS = 1, +}; + +/* DW MIPI CSI-2 register addresses*/ + +struct R_CSI2 { + u16 VERSION; + u16 N_LANES; + u16 CTRL_RESETN; + u16 INTERRUPT; + u16 DATA_IDS_1; + u16 DATA_IDS_2; + u16 DATA_IDS_VC_1; + u16 DATA_IDS_VC_2; + u16 IPI_MODE; + u16 IPI_VCID; + u16 IPI_DATA_TYPE; + u16 IPI_MEM_FLUSH; + u16 IPI_HSA_TIME; + u16 IPI_HBP_TIME; + u16 IPI_HSD_TIME; + u16 IPI_HLINE_TIME; + u16 IPI_SOFTRSTN; + u16 IPI_ADV_FEATURES; + u16 IPI_VSA_LINES; + u16 IPI_VBP_LINES; + u16 IPI_VFP_LINES; + u16 IPI_VACTIVE_LINES; + u16 VC_EXTENSION; + u16 INT_PHY_FATAL; + u16 MASK_INT_PHY_FATAL; + u16 FORCE_INT_PHY_FATAL; + u16 INT_PKT_FATAL; + u16 MASK_INT_PKT_FATAL; + u16 FORCE_INT_PKT_FATAL; + u16 INT_FRAME_FATAL; + u16 MASK_INT_FRAME_FATAL; + u16 FORCE_INT_FRAME_FATAL; + u16 INT_PHY; + u16 MASK_INT_PHY; + u16 FORCE_INT_PHY; + u16 INT_PKT; + u16 MASK_INT_PKT; + u16 FORCE_INT_PKT; + u16 INT_LINE; + u16 MASK_INT_LINE; + u16 FORCE_INT_LINE; + u16 INT_IPI; + u16 MASK_INT_IPI; + u16 FORCE_INT_IPI; + u16 ST_BNDRY_FRAME_FATAL; + u16 MSK_BNDRY_FRAME_FATAL; + u16 FORCE_BNDRY_FRAME_FATAL; + u16 ST_SEQ_FRAME_FATAL; + u16 MSK_SEQ_FRAME_FATAL; + u16 FORCE_SEQ_FRAME_FATAL; + u16 ST_CRC_FRAME_FATAL; + u16 MSK_CRC_FRAME_FATAL; + u16 FORCE_CRC_FRAME_FATAL; + u16 ST_PLD_CRC_FATAL; + u16 MSK_PLD_CRC_FATAL; + u16 FORCE_PLD_CRC_FATAL; + u16 ST_DATA_ID; + u16 MSK_DATA_ID; + u16 FORCE_DATA_ID; + u16 ST_ECC_CORRECT; + u16 MSK_ECC_CORRECT; + u16 FORCE_ECC_CORRECT; +}; + +/* Interrupt Masks */ +struct interrupt_type { + u32 PHY_FATAL; + u32 PKT_FATAL; + u32 FRAME_FATAL; + u32 PHY; + u32 PKT; + u32 LINE; + u32 IPI; + u32 BNDRY_FRAME_FATAL; + u32 SEQ_FRAME_FATAL; + u32 CRC_FRAME_FATAL; + u32 PLD_CRC_FATAL; + u32 DATA_ID; + u32 ECC_CORRECTED; +}; + +/* IPI Data Types */ +enum data_type { + CSI_2_YUV420_8 = 0x18, + CSI_2_YUV420_10 = 0x19, + CSI_2_YUV420_8_LEG = 0x1A, + CSI_2_YUV420_8_SHIFT = 0x1C, + CSI_2_YUV420_10_SHIFT = 0x1D, + CSI_2_YUV422_8 = 0x1E, + CSI_2_YUV422_10 = 0x1F, + CSI_2_RGB444 = 0x20, + CSI_2_RGB555 = 0x21, + CSI_2_RGB565 = 0x22, + CSI_2_RGB666 = 0x23, + CSI_2_RGB888 = 0x24, + CSI_2_RAW6 = 0x28, + CSI_2_RAW7 = 0x29, + CSI_2_RAW8 = 0x2A, + CSI_2_RAW10 = 0x2B, + CSI_2_RAW12 = 0x2C, + CSI_2_RAW14 = 0x2D, + CSI_2_RAW16 = 0x2E, + CSI_2_RAW20 = 0x2F, + USER_DEFINED_1 = 0x30, + USER_DEFINED_2 = 0x31, + USER_DEFINED_3 = 0x32, + USER_DEFINED_4 = 0x33, + USER_DEFINED_5 = 0x34, + USER_DEFINED_6 = 0x35, + USER_DEFINED_7 = 0x36, + USER_DEFINED_8 = 0x37, +}; + +/* DWC MIPI CSI-2 output types */ +enum output { + IPI_OUT = 0, + IDI_OUT = 1, + BOTH_OUT = 2 +}; + +/* IPI color components */ +enum color_mode { + COLOR48 = 0, + COLOR16 = 1 +}; + +/* IPI cut through */ +enum cut_through { + CTINACTIVE = 0, + CTACTIVE = 1 +}; + +/* IPI output types */ +enum ipi_output { + CAMERA_TIMING = 0, + AUTO_TIMING = 1 +}; + +/* Format template */ +struct mipi_fmt { + u32 mbus_code; + u8 depth; +}; + +struct mipi_dt { + u32 hex; + char *name; +}; + +/* CSI specific configuration */ +struct csi_data { + u32 num_lanes; + u32 dphy_freq; + u32 pclk; + u32 fps; + u32 bpp; + u32 output; + u32 ipi_mode; + u32 ipi_adv_features; + u32 ipi_cut_through; + u32 ipi_color_mode; + u32 ipi_auto_flush; + u32 virtual_ch; + u32 hsa; + u32 hbp; + u32 hsd; + u32 htotal; + u32 vsa; + u32 vbp; + u32 vfp; + u32 vactive; +}; + +/* Structure to embed device driver information */ +struct dw_csi { + struct v4l2_subdev sd; + struct video_device vdev; + struct v4l2_device v4l2_dev; + struct device *dev; + struct clk *perclk, *phyclk; + struct media_pad pads[CSI_PADS_NUM]; + struct mipi_fmt *fmt; + struct v4l2_mbus_framefmt format; + void __iomem *base_address; + void __iomem *demo; + void __iomem *csc; + int ctrl_irq_number; + int demosaic_irq; + struct csi_data hw; + struct reset_control *rst; + struct phy *phy; + struct dw_csih_pdata *config; + struct mutex lock; /* protect resources sharing */ + spinlock_t slock; /* interrupt handling lock */ + u8 ipi_dt; + u8 index; + u8 hw_version_major; + u16 hw_version_minor; + + struct v4l2_async_notifier notifier; + + u32 remote_pad; + + struct v4l2_subdev *input_sd; +}; + +static inline struct dw_csi *sd_to_mipi_csi_dev(struct v4l2_subdev *sdev) +{ + return container_of(sdev, struct dw_csi, sd); +} + +void dw_mipi_csi_reset(struct dw_csi *csi_dev); +int dw_mipi_csi_mask_irq_power_off(struct dw_csi *csi_dev); +int dw_mipi_csi_hw_stdby(struct dw_csi *csi_dev); +void dw_mipi_csi_set_ipi_fmt(struct dw_csi *csi_dev); +void dw_mipi_csi_start(struct dw_csi *csi_dev); +int dw_mipi_csi_irq_handler(struct dw_csi *csi_dev); +void dw_mipi_csi_get_version(struct dw_csi *csi_dev); +int dw_mipi_csi_specific_mappings(struct dw_csi *csi_dev); +void dw_mipi_csi_fill_timings(struct dw_csi *dev, + struct v4l2_subdev_format *fmt); +void dw_mipi_csi_dump(struct dw_csi *csi_dev); + +#if IS_ENABLED(CONFIG_DWC_MIPI_TC_DPHY_GEN3) +int dw_csi_create_capabilities_sysfs(struct platform_device *pdev); +#endif + +static inline void dw_mipi_csi_write(struct dw_csi *dev, + u32 address, u32 data) +{ + writel(data, dev->base_address + address); +} + +static inline u32 dw_mipi_csi_read(struct dw_csi *dev, u32 address) +{ + return readl(dev->base_address + address); +} + +#endif /*_DW_MIPI_CSI_H__ */ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/media/platform/dwc/Kconfig linux-microchip/drivers/media/platform/dwc/Kconfig --- linux-6.6.51/drivers/media/platform/dwc/Kconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/media/platform/dwc/Kconfig 2024-11-26 14:02:37.602495560 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: GPL-2.0 +# +# Synopsys DWC Platform drivers +# Drivers here are currently for MIPI CSI-2 and MIPI DPHY support + +config DWC_MIPI_CSI2_HOST + tristate "Synopsys DesignWare CSI-2 Host Controller support" + select VIDEO_DEV + select VIDEO_V4L2 + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + This selects the DesignWare MIPI CSI-2 host controller support. This + controller gives access to control a CSI-2 receiver acting as a V4L2 + subdevice. + + If you have a controller with this interface, say Y. + + If unsure, say N. + +config DWC_MIPI_DPHY_GEN3 + tristate "DesignWare platform support using a Gen3 D-PHY" + select GENERIC_PHY + help + Synopsys MIPI D-PHY Generation 3 reference driver. This driver supports + all Generation 3 D-PHYs. Choose Y or M if you have a platform with this + block. + + If unsure, say N. + +if DWC_MIPI_DPHY_GEN3 + +config DWC_MIPI_TC_DPHY_GEN3 + bool "Platform support using a Synopsys Test Chip" + help + Synopsys Test Chip is for prototyping purposes. This enables extra + features that exist only in prototyping and/or for debug purposes. + + If unsure, say N. + +endif # DWC_MIPI_DPHY_GEN3 diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/media/platform/dwc/Makefile linux-microchip/drivers/media/platform/dwc/Makefile --- linux-6.6.51/drivers/media/platform/dwc/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/media/platform/dwc/Makefile 2024-11-26 14:02:37.602495560 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for Synopsys DWC Platform drivers +# +dw-csi-objs := dw-csi-plat.o dw-mipi-csi.o +ifeq ($(CONFIG_DWC_MIPI_TC_DPHY_GEN3),y) + dw-csi-objs += dw-csi-sysfs.o +endif +obj-$(CONFIG_DWC_MIPI_CSI2_HOST) += dw-csi.o + +dw-dphy-objs := dw-dphy-plat.o dw-dphy-rx.o +ifeq ($(CONFIG_DWC_MIPI_TC_DPHY_GEN3),y) + dw-dphy-objs += dw-dphy-sysfs.o +endif +obj-$(CONFIG_DWC_MIPI_DPHY_GEN3) += dw-dphy.o diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/media/platform/Kconfig linux-microchip/drivers/media/platform/Kconfig --- linux-6.6.51/drivers/media/platform/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/media/platform/Kconfig 2024-11-26 14:02:37.598495489 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:72 @ source "drivers/media/platform/atmel/Kconfig" source "drivers/media/platform/cadence/Kconfig" source "drivers/media/platform/chips-media/Kconfig" +source "drivers/media/platform/dwc/Kconfig" source "drivers/media/platform/intel/Kconfig" source "drivers/media/platform/marvell/Kconfig" source "drivers/media/platform/mediatek/Kconfig" diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/media/platform/Makefile linux-microchip/drivers/media/platform/Makefile --- linux-6.6.51/drivers/media/platform/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/media/platform/Makefile 2024-11-26 14:02:37.598495489 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:15 @ obj-y += atmel/ obj-y += cadence/ obj-y += chips-media/ +obj-y += dwc/ obj-y += intel/ obj-y += marvell/ obj-y += mediatek/ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:38 @ # (e. g. LC_ALL=C sort Makefile) obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o obj-$(CONFIG_VIDEO_MUX) += video-mux.o + diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/media/platform/microchip/Kconfig linux-microchip/drivers/media/platform/microchip/Kconfig --- linux-6.6.51/drivers/media/platform/microchip/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/media/platform/microchip/Kconfig 2024-11-26 14:02:37.606495632 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:62 @ To compile this driver as a module, choose M here: the module will be called microchip-csi2dc. + +config VIDEO_MCHP_DSCMI + tristate "Microchip Digital Serial Camera Memory Interface Driver" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && OF + select VIDEOBUF2_DMA_CONTIG + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + help + Microchip Digital Serial Camera Memory Interface Driver Support For Smart Embedded Vision Kit. + +config VIDEO_MICROCHIP_COMMON + tristate + default n + help + Microchip common code base for FPAG Soft IP drivers. + +config VIDEO_MICROCHIP_GVPC + tristate "Microchip GVPC (Generic Video Pipeline Connector) Driver" + depends on VIDEO_DEV && OF + select CONFIG_VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + select VIDEO_MICROCHIP_COMMON + help + Microchip GVPC (Generic Video Pipeline Connector) Driver Support. + +config VIDEO_MICROCHIP_IMAGE_ENHANCEMENT + tristate "Microchip Video image enhancement" + depends on VIDEO_DEV && OF + select CONFIG_VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + select VIDEO_MICROCHIP_COMMON + help + Driver for the Microchip Video image enhancement + +config VIDEO_MICROCHIP_MIPI_CSI2 + tristate "Microchip MIPI CSI2 Driver" + depends on VIDEO_DEV && OF + select CONFIG_VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + select VIDEO_MICROCHIP_COMMON + help + Microchip MIPI CSI2 FPGA Soft IP Driver Support. + +config VIDEO_MICROCHIP_OSD + tristate "Microchip Video On Screen Display" + depends on VIDEO_DEV && OF + select CONFIG_VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + select VIDEO_MICROCHIP_COMMON + help + Driver for the Microchip Video OSD (On Screen Display) + +config VIDEO_MICROCHIP_RGB_SCALER + tristate "Microchip Video RGB Scaler" + depends on VIDEO_DEV && OF + select CONFIG_VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + select VIDEO_MICROCHIP_COMMON + help + Driver for the Microchip Video RGB Scaler + +config VIDEO_MICROCHIP_VIDEO_CAPTURE + tristate "Microchip Video Capture Driver" + depends on VIDEO_DEV && OF + select CONFIG_VIDEO_V4L2_SUBDEV_API + select VIDEOBUF2_DMA_CONTIG + select V4L2_FWNODE + select VIDEO_MICROCHIP_COMMON + help + Microchip Video Capture Driver Support For FPGA Soft IP. + +config VIDEO_MICROCHIP_YUV2H264 + tristate "Microchip Video H264 Encoder Driver" + depends on VIDEO_DEV && OF + select CONFIG_VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE + select VIDEO_MICROCHIP_COMMON + help + Microchip Video H264 Encoder Driver Support. + diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/media/platform/microchip/Makefile linux-microchip/drivers/media/platform/microchip/Makefile --- linux-6.6.51/drivers/media/platform/microchip/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/media/platform/microchip/Makefile 2024-11-26 14:02:37.606495632 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:10 @ obj-$(CONFIG_VIDEO_MICROCHIP_ISC) += microchip-isc.o obj-$(CONFIG_VIDEO_MICROCHIP_XISC) += microchip-xisc.o obj-$(CONFIG_VIDEO_MICROCHIP_CSI2DC) += microchip-csi2dc.o +obj-$(CONFIG_VIDEO_MCHP_DSCMI) += mchp-dscmi.o +obj-$(CONFIG_VIDEO_MICROCHIP_COMMON) += microchip-common.o +obj-$(CONFIG_VIDEO_MICROCHIP_GVPC) += microchip-gvpc.o +obj-$(CONFIG_VIDEO_MICROCHIP_IMAGE_ENHANCEMENT) += microchip-image-enhancement.o +obj-$(CONFIG_VIDEO_MICROCHIP_MIPI_CSI2) += microchip-mipi-csi2rx.o +obj-$(CONFIG_VIDEO_MICROCHIP_OSD) += microchip-osd.o +obj-$(CONFIG_VIDEO_MICROCHIP_RGB_SCALER) += microchip-rgb-scaler.o +obj-$(CONFIG_VIDEO_MICROCHIP_VIDEO_CAPTURE) += microchip-vcpp.o +obj-$(CONFIG_VIDEO_MICROCHIP_YUV2H264) += microchip-yuv2h264.o diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/media/platform/microchip/mchp-dscmi.c linux-microchip/drivers/media/platform/microchip/mchp-dscmi.c --- linux-6.6.51/drivers/media/platform/microchip/mchp-dscmi.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/media/platform/microchip/mchp-dscmi.c 2024-11-26 14:02:37.610495703 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microchip Digital Serial Camera Memory Interface Driver. + * + * Copyright (C) 2021-2022 Microchip Technology Inc. and its subsidiaries + * Author: Shravan Chippa <shavan.chippa@microchip.com> + * + * Driver based on stm32-dcmi.c + */ + +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/dmaengine.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kmod.h> +#include <linux/mutex.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/videodev2.h> +#include <linux/of_device.h> + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-subdev.h> +#include <media/videobuf2-v4l2.h> +#include <media/videobuf2-dma-contig.h> + +#define MCHP_DSCMI_DRV_NAME "mchp-dscmi" +#define MCHP_DSCMI_DRV_VERSION "0.1" + +/* Offset address to control Red, Green, Blue gain etc */ +#define MCHP_DSCMI_R_CONSTRAINT 0x1004 +#define MCHP_DSCMI_G_CONSTRAINT 0x1008 +#define MCHP_DSCMI_B_CONSTRAINT 0x100C +#define MCHP_DSCMI_SECOND_CONSTRAINT 0x1010 +#define MCHP_DSCMI_RGB_SUM 0x1038 + +/* Offset address to control space */ +#define MCHP_DSCMI_FRAME_Q_FACTOR 0x1074 +#define MCHP_DSCMI_FRAME_WIDTH 0x1078 +#define MCHP_DSCMI_FRAME_HIGHT 0x107C +#define MCHP_DSCMI_FRAME_SIZE_REG 0x1080 +#define MCHP_DSCMI_FRAME_START_REG 0x1084 +#define MCHP_DSCMI_STREAM_ADDR_LOW 0x1088 +#define MCHP_DSCMI_STREAM_ADDR_HIGH 0x108C +#define MCHP_DSCMI_H264_RATIO 0x1108 +#define MCHP_DSCMI_R_FRAME_WIDTH 0x110C +#define MCHP_DSCMI_R_FRAME_HIGHT 0x1110 +#define MCHP_DSCMI_OSD_X_Y_POS 0x1100 +#define MCHP_DSCMI_OSD_COLOR 0x1104 +#define MCHP_DSCMI_OSD_EN 0x1114 + +/* Offset address to get video capability */ +#define MCHP_DSCMI_CAPABILITIES_V4L2 0x1500 + +#define MCHP_DSCMI_FRAME_START 0x1 +#define MCHP_DSCMI_FRAME_STOP 0x0 + +#define MCHP_DSCMI_H264_NUM_CTRLS 11 +#define MCHP_DSCMI_NUM_CTRLS 7 + +#define MCHP_DSCMI_CAM_POWER_ON 1 +#define MCHP_DSCMI_CAM_POWER_OFF 0 + +#define MCHP_DSCMI_CAM_START 1 +#define MCHP_DSCMI_CAM_STOP 0 + +#define MCHP_DSCMI_FRAME_SIZE_MASK GENMASK(27, 0) +#define MCHP_DSCMI_FRAME_INDEX_MASK GENMASK(29, 28) + +#define MCHP_DSCMI_MAX_FRAMES 4 +#define MCHP_DSCMI_FRAME_S_NEXT (1024 * 1024) +#define MCHP_DSCMI_FRAME_MAX_SIZE (1024 * 1024) + +/* Video capabilities */ +#define MCHP_DSCMI_CAPABILITIES_H264 0x48323634 +#define MCHP_DSCMI_CAPABILITIES_MJPEG 0x4D4A5047 +#define MCHP_DSCMI_CAPABILITIES_YUV 0x59555956 + +/* User defined v4l2 control */ +#define MCHP_DSCMI_CID_RED_GAIN (V4L2_CID_USER_BASE | 0x1001) +#define MCHP_DSCMI_CID_GREEN_GAIN (V4L2_CID_USER_BASE | 0x1002) +#define MCHP_DSCMI_CID_BLUE_GAIN (V4L2_CID_USER_BASE | 0x1003) +#define MCHP_DSCMI_CID_Q_FACTOR (V4L2_CID_USER_BASE | 0x1004) +#define MCHP_DSCMI_CID_OSD_X_POS (V4L2_CID_USER_BASE | 0x1005) +#define MCHP_DSCMI_CID_OSD_Y_POS (V4L2_CID_USER_BASE | 0x1006) +#define MCHP_DSCMI_CID_OSD_ENABLE (V4L2_CID_USER_BASE | 0x1007) +#define MCHP_DSCMI_CID_OSD_COLOR (V4L2_CID_USER_BASE | 0x1008) + +/* Default resolution */ +#define MCHP_DSCMI_FIXED_WIDTH 1280 +#define MCHP_DSCMI_FIXED_HEIGHT 720 + +#define MCHP_DSCMI_MAX_WIDTH 1920 +#define MCHP_DSCMI_MAX_HEIGHT 1080 + +#define MCHP_DSCMI_GAIN_AVERAGE 125 +#define MCHP_DSCMI_GAIN_MIN 5 +#define MCHP_DSCMI_GAIN_INIT 80 +#define MCHP_DSCMI_HYSTERESIS_GAIN 4 +#define MCHP_DSCMI_GAIN_CTL_DEFAULT 112 +#define MCHP_DSCMI_GAIN_CTL_MAX 255 +#define MCHP_DSCMI_CTL_MAX 255 +#define MCHP_DSCMI_CTL_MIN 0 +#define MCHP_DSCMI_CTL_STEP 1 +#define MCHP_DSCMI_Q_FACTOR_CTL_MAX 52 +#define MCHP_DSCMI_Q_FACTOR_CTL_MIN 25 +#define MCHP_DSCMI_Q_FACTOR_CTL_DEFAULT 30 + +/* Text overlay (On Screen Display) */ +#define MCHP_DSCMI_OSD_X_Y_POS_MAX 4096 +#define MCHP_DSCMI_OSD_X_Y_POS_MIN 4 +#define MCHP_DSCMI_OSD_X_Y_POS_DEFAULT 4 +#define MCHP_DSCMI_OSD_X_Y_POS_STEP 2 +#define MCHP_DSCMI_OSD_ENABLE_MAX 1 +#define MCHP_DSCMI_OSD_ENABLE_MIN 0 +#define MCHP_DSCMI_OSD_ENABLE_DEFAULT 1 +#define MCHP_DSCMI_OSD_ENABLE_STEP 1 +#define MCHP_DSCMI_OSD_DISABLE_MAX_PIX 0x2000 +#define MCHP_DSCMI_OSD_X_POS_SHIFT 16 +#define MCHP_DSCMI_OSD_COLOR_MAX 0xFFFFFF +#define MCHP_DSCMI_OSD_COLOR_MIN 0x000000 +#define MCHP_DSCMI_OSD_COLOR_DEFAULT 0xFFFFFF +#define MCHP_DSCMI_OSD_COLOR_STEP 1 + +/* The compression ratio is calculated for every 60 frames */ +#define MCHP_DSCMI_OSD_MAX_FRAMES_RESET_COUNT 60 +#define MCHP_DSCMI_OSD_MAX_FRAMES_INIT 50 +#define MCHP_DSCMI_OSD_MAX_ARRAY 6 + +/* Auto gain check delay in msecs*/ +#define MCHP_DSCMI_DELAYED_WORK_TIME_M_SEC 150 + +/* Minimum wait time for camera to stabilize */ +#define MCHP_DSCMI_DELAYED_CAM_M_SEC 100 + +#define MCHP_DSCMI_OSD_EN_FPGA_RTL BIT(0) + +enum mchp_dscmi_state { + STOPPED = 0, + WAIT_FOR_BUFFER, + RUNNING, +}; + +enum mchp_dscmi_capabilities { + H264 = 0, + MJPEG, + YUV_RAW, +}; + +/** + * struct mchp_dscmi_framesize - size of the frame + * @width: frame width + * @height: frame height + */ +struct mchp_dscmi_framesize { + u32 width; + u32 height; +}; + +struct mchp_dscmi_driver_platdata { + u32 quirks; +}; + +/** + * struct mchp_dscmi_compression_ratio - for compression calculation + * @frame_size: accumulated frames size + * @frame_size_index: accumulated present frames size index + * @frame_count: present frame count + */ +struct mchp_dscmi_compression_ratio { + u32 frame_size[MCHP_DSCMI_OSD_MAX_ARRAY]; + u32 frame_size_index; + u32 frame_count; +}; + +/** + * struct mchp_dscmi_buffer - buffer for one video frame + * @vb: video buffer information struct vb2_v4l2_buffer + * @prepared: status of buffer + * @paddr: physical address of buffer + * @size: size of buffer + * @list: list of all requested buffers from user space + */ +struct mchp_dscmi_buffer { + struct vb2_v4l2_buffer vb; + bool prepared; + dma_addr_t paddr; + size_t size; + struct list_head list; +}; + +/** + * struct mchp_dscmi_cam_buffer - camera dma buffers and size + * @paddr: camera stream base address + * @size: size of buffer + */ +struct mchp_dscmi_cam_buffer { + dma_addr_t paddr; + size_t size; +}; + +/** + * struct mchp_dscmi_format - media bus format information + * @fourcc: Fourcc code for this format + * @mbus_code: V4L2 media bus format code + * @bpp: Bytes per pixel (when stored in memory) + */ +struct mchp_dscmi_format { + u32 fourcc; + u32 mbus_code; + u8 bpp; +}; + +/** + * struct mchp_dscmi_fpga - V4L2 device context + * @base: pointer to control register + * @pdev: platform device + * @dev: device + * @active: current buffer in queue + * @current_subdev: current subdevice: the camera sensor + * @reset_gpio: sensor fabric rest + * @dma_chan: DMA engine channel + * @auto_gain_wq: auto gain control work queue struct + * @auto_gain_dw: auto gain delayed work struct + * @v4l2_dev: top-level v4l2 device struct + * @vdev: video node structure + * @fmt: current v4l2 format + * @queue: vb2 video capture queue + * @lock: mutex lock for vb2 buffs + * @qlock: spinlock controlling access to buf_list and sequence + * @buf_list: list of buffers queued for DMA + * @subdev_entities: subdevice list + * @ctrl_handler: control handler structure + * @dma_cookie: DMA engine cookie + * @dma_lock: DMA mutex lock for serializing dma use + * @cambuf: struct cam_buffer + * @h264_ratio: struct compression_ratio + * @state: state of buffers + * @capabilities: capabilities to identify the fabric v4l2 support + * @s_buff_index: current streaming buf index + * @s_buff_size: current streaming buf size + * @irq: external IRQ for new frame + * @contrast: contrast value + * @brightness: brightness value + * @sequence: frame sequence counter + * @drop_count: total frame drop counter + * @horizontal_pos: overlay horizontal position + * @vertical_pos: overlay vertical position + * @has_hw_osd_enable: osd enable feture status + */ +struct mchp_dscmi_fpga { + void __iomem *base; + struct platform_device *pdev; + struct device *dev; + struct mchp_dscmi_buffer *active; + struct mchp_dscmi_subdev_entity *current_subdev; + struct gpio_desc *reset_gpio; + struct dma_chan *dma_chan; + struct workqueue_struct *auto_gain_wq; + struct delayed_work auto_gain_dw; + struct v4l2_device v4l2_dev; + struct video_device vdev; + struct v4l2_format fmt; + struct vb2_queue queue; + struct mutex lock; /* vb2 buffer lock */ + spinlock_t qlock; + struct list_head buf_list; + struct list_head subdev_entities; + struct v4l2_ctrl_handler ctrl_handler; + struct mutex dma_lock; /* mutex for dma operations */ + struct mchp_dscmi_cam_buffer cambuf; + struct mchp_dscmi_compression_ratio h264_ratio; + enum mchp_dscmi_state state; + enum mchp_dscmi_capabilities capabilities; + dma_cookie_t dma_cookie; + int s_buff_index; + int s_buff_size; + int irq; + int contrast; + int brightness; + int sequence; + int drop_count; + int horizontal_pos; + int vertical_pos; + bool has_hw_osd_enable; +}; + +struct mchp_dscmi_subdev_entity { + struct v4l2_subdev *subdev; + struct v4l2_async_connection *asc; + struct device_node *epn; + struct v4l2_async_notifier notifier; + struct list_head list; +}; + +static struct mchp_dscmi_framesize framesize_list[] = { + { + .width = 1920, + .height = 1072, + }, + { + .width = 1280, + .height = 720, + }, + { + .width = 960, + .height = 544, + }, + { + .width = 640, + .height = 480, + }, + { + .width = 432, + .height = 240, + }, + {}, +}; + +static struct mchp_dscmi_format mchp_dscmi_formats[] = { + { + .fourcc = V4L2_PIX_FMT_H264, + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, + }, { + .fourcc = V4L2_PIX_FMT_MJPEG, + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, + }, { + .fourcc = V4L2_PIX_FMT_YUYV, + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10, + .bpp = 2, + }, +}; + +static inline void mchp_dscmi_reg_write(struct mchp_dscmi_fpga *mchp_dscmi, + u32 reg, u32 val) +{ + writel_relaxed(val, mchp_dscmi->base + reg); +} + +static inline u32 mchp_dscmi_reg_read(struct mchp_dscmi_fpga *mchp_dscmi, + u32 reg) +{ + return readl_relaxed(mchp_dscmi->base + reg); +} + +/* + * Compression ratio is the percentage of compressed image over the actual image. + * The YUV420 requires 3 bytes per 2 pixels, or 1.5 bytes per pixel, because + * groups of pixels share a single color value. + * The compression ratio can be calculated as: + * (width * height * accumulated-frame-count * bytes-per-pixel) / accumulated-frame-size + */ +static u32 compression_ratio_calc(u32 hres, u32 vres, u32 accumulated_frame_size) +{ + return ((hres * vres * MCHP_DSCMI_OSD_MAX_FRAMES_RESET_COUNT * 3) / + (accumulated_frame_size)) / 2; +} + +/* + * The compression ratio is calculated by the driver, but overlaid by + * the FPGA logic. It is displayed as a percentage, with the 4 least + * significant bits of MCHP_DSCMI_H264_RATIO containing the units, + * bits 4 to 7 containing the tens etc to be displayed. + */ + +static void mchp_dscmi_osd_text(struct mchp_dscmi_fpga *mchp_dscmi, + u32 compression_ratio) +{ + u32 compression_ratio_osd = ((compression_ratio % 10) | + ((compression_ratio / 10 % 10) << 4) | + ((compression_ratio / 100) << 8)); + + mchp_dscmi_reg_write(mchp_dscmi, MCHP_DSCMI_H264_RATIO, compression_ratio_osd); +} + +static void mchp_dscmi_buffer_done(struct mchp_dscmi_fpga *mchp_dscmi, + struct mchp_dscmi_buffer *buf, + size_t bytesused, + int err) +{ + struct vb2_v4l2_buffer *vbuf; + + if (!buf) + return; + + list_del_init(&buf->list); + vbuf = &buf->vb; + vbuf->sequence = mchp_dscmi->sequence++; + vbuf->field = V4L2_FIELD_NONE; + vbuf->vb2_buf.timestamp = ktime_get_ns(); + + vb2_set_plane_payload(&vbuf->vb2_buf, 0, bytesused); + + vb2_buffer_done(&vbuf->vb2_buf, + err ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + + dev_dbg(mchp_dscmi->dev, "Buffer[%d] done seq=%d, bytesused=%zu\n", + vbuf->vb2_buf.index, vbuf->sequence, bytesused); + + mchp_dscmi->active = NULL; +} + +static void mchp_dscmi_dma_callback(void *param) +{ + struct mchp_dscmi_fpga *mchp_dscmi = param; + struct mchp_dscmi_buffer *buf = mchp_dscmi->active; + struct dma_tx_state state; + enum dma_status status; + + spin_lock_irq(&mchp_dscmi->qlock); + + status = dmaengine_tx_status(mchp_dscmi->dma_chan, + mchp_dscmi->dma_cookie, &state); + + switch (status) { + case DMA_ERROR: + dev_err(mchp_dscmi->dev, "Received DMA_ERROR\n"); + mchp_dscmi_buffer_done(mchp_dscmi, buf, 0, -EIO); + break; + case DMA_COMPLETE: + dev_dbg(mchp_dscmi->dev, "Received DMA_COMPLETE\n"); + mchp_dscmi_buffer_done(mchp_dscmi, buf, + mchp_dscmi->s_buff_size, 0); + break; + + default: + dev_err(mchp_dscmi->dev, "Received unknown status\n"); + break; + } + + spin_unlock_irq(&mchp_dscmi->qlock); +} + +static int mchp_dscmi_start_dma(struct mchp_dscmi_fpga *mchp_dscmi, + struct mchp_dscmi_buffer *buf) +{ + struct dma_async_tx_descriptor *desc = NULL; + struct dma_slave_config config; + struct mchp_dscmi_compression_ratio *h264_ratio = &mchp_dscmi->h264_ratio; + int ret, buf_size, i; + int *frame_size_index = &h264_ratio->frame_size_index; + + memset(&config, 0, sizeof(config)); + + spin_lock_irq(&mchp_dscmi->qlock); + config.src_addr = mchp_dscmi->cambuf.paddr + + (MCHP_DSCMI_FRAME_S_NEXT * mchp_dscmi->s_buff_index); + + buf_size = mchp_dscmi->s_buff_size; + spin_unlock_irq(&mchp_dscmi->qlock); + + h264_ratio->frame_count++; + h264_ratio->frame_size[*frame_size_index] += buf_size; + + if (h264_ratio->frame_count % 10 == 0) + (*frame_size_index)++; + + if (*frame_size_index == MCHP_DSCMI_OSD_MAX_ARRAY) + *frame_size_index = 0; + + if (h264_ratio->frame_count == MCHP_DSCMI_OSD_MAX_FRAMES_RESET_COUNT) { + u32 accumulated_frame_size = 0, compression_ratio, hres, vres; + + for (i = 0; i < MCHP_DSCMI_OSD_MAX_ARRAY; i++) + accumulated_frame_size += h264_ratio->frame_size[i]; + + hres = mchp_dscmi_reg_read(mchp_dscmi, MCHP_DSCMI_R_FRAME_WIDTH); + vres = mchp_dscmi_reg_read(mchp_dscmi, MCHP_DSCMI_R_FRAME_HIGHT); + + compression_ratio = compression_ratio_calc(hres, vres, + accumulated_frame_size); + + mchp_dscmi_osd_text(mchp_dscmi, compression_ratio); + + h264_ratio->frame_count = MCHP_DSCMI_OSD_MAX_FRAMES_INIT; + h264_ratio->frame_size[*frame_size_index] = 0; + } + + config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + config.dst_maxburst = 4; + + ret = dmaengine_slave_config(mchp_dscmi->dma_chan, &config); + if (ret) { + dev_err(mchp_dscmi->dev, + "DMA channel config failed (%d)\n", ret); + return ret; + } + + mutex_lock(&mchp_dscmi->dma_lock); + + desc = dmaengine_prep_dma_memcpy(mchp_dscmi->dma_chan, buf->paddr, + config.src_addr, buf_size, + DMA_CTRL_ACK | DMA_PREP_INTERRUPT); + if (!desc) { + dev_err(mchp_dscmi->dev, + "DMA failed for buffer src=%pad dst=%pad size=%d\n", + &config.src_addr, &buf->paddr, buf_size); + mutex_unlock(&mchp_dscmi->dma_lock); + return -EINVAL; + } + + desc->callback = mchp_dscmi_dma_callback; + desc->callback_param = mchp_dscmi; + + mchp_dscmi->dma_cookie = dmaengine_submit(desc); + + ret = dma_submit_error(mchp_dscmi->dma_cookie); + mutex_unlock(&mchp_dscmi->dma_lock); + if (ret) { + dev_err(mchp_dscmi->dev, "DMA submission failed %d\n", ret); + return -ENXIO; + } + + dma_async_issue_pending(mchp_dscmi->dma_chan); + + return 0; +} + +static int mchp_dscmi_restart_capture(struct mchp_dscmi_fpga *mchp_dscmi) +{ + struct mchp_dscmi_buffer *buf; + + spin_lock_irq(&mchp_dscmi->qlock); + + if (list_empty(&mchp_dscmi->buf_list)) { + dev_dbg(mchp_dscmi->dev, + "Capture restart is deferred to next buffer queue\n"); + mchp_dscmi->state = WAIT_FOR_BUFFER; + mchp_dscmi->drop_count++; + spin_unlock_irq(&mchp_dscmi->qlock); + return 0; + } + + buf = list_entry(mchp_dscmi->buf_list.next, + struct mchp_dscmi_buffer, list); + + mchp_dscmi->active = buf; + + spin_unlock_irq(&mchp_dscmi->qlock); + + return mchp_dscmi_start_dma(mchp_dscmi, buf); +} + +static irqreturn_t mchp_dscmi_irq_ext(int irq, void *dev_id) +{ + struct mchp_dscmi_fpga *mchp_dscmi = dev_id; + int frame_info; + unsigned long flags; + + frame_info = mchp_dscmi_reg_read(mchp_dscmi, MCHP_DSCMI_FRAME_SIZE_REG); + + spin_lock_irqsave(&mchp_dscmi->qlock, flags); + + if (mchp_dscmi->state != RUNNING) { + spin_unlock_irqrestore(&mchp_dscmi->qlock, flags); + mchp_dscmi->drop_count++; + return IRQ_HANDLED; + } + + mchp_dscmi->s_buff_index = FIELD_GET(MCHP_DSCMI_FRAME_INDEX_MASK, frame_info); + + mchp_dscmi->s_buff_size = frame_info & MCHP_DSCMI_FRAME_SIZE_MASK; + + if (mchp_dscmi->s_buff_size >= MCHP_DSCMI_FRAME_S_NEXT || + mchp_dscmi->s_buff_size == 0) { + spin_unlock_irqrestore(&mchp_dscmi->qlock, flags); + mchp_dscmi->drop_count++; + return IRQ_HANDLED; + } + + spin_unlock_irqrestore(&mchp_dscmi->qlock, flags); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t mchp_dscmi_irq_thread_fn(int irq, void *dev_id) +{ + struct mchp_dscmi_fpga *mchp_dscmi = dev_id; + int ret; + + ret = mchp_dscmi_restart_capture(mchp_dscmi); + if (ret) + dev_warn_once(mchp_dscmi->dev, + "failed to mchp restart capture %d\n", ret); + + return IRQ_HANDLED; +} + +static int mchp_dscmi_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct mchp_dscmi_fpga *mchp_dscmi = vb2_get_drv_priv(vq); + unsigned int size; + + if (*nbuffers > MCHP_DSCMI_MAX_FRAMES) { + dev_dbg(mchp_dscmi->dev, + "output frame count too high (%d), cut to %d\n", + *nbuffers, MCHP_DSCMI_MAX_FRAMES); + *nbuffers = MCHP_DSCMI_MAX_FRAMES; + } + + size = mchp_dscmi->fmt.fmt.pix.sizeimage; + + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; + + *nplanes = 1; + sizes[0] = size; + + dev_dbg(mchp_dscmi->dev, "Setup queue, count=%d, size=%d\n", + *nbuffers, size); + + return 0; +} + +static int mchp_dscmi_buffer_prepare(struct vb2_buffer *vb) +{ + struct mchp_dscmi_fpga *mchp_dscmi = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct mchp_dscmi_buffer *buf = + container_of(vbuf, struct mchp_dscmi_buffer, vb); + unsigned long size; + + size = mchp_dscmi->fmt.fmt.pix.sizeimage; + + if (vb2_plane_size(vb, 0) < size) { + dev_err(mchp_dscmi->dev, + "data will not fit into plane (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + if (!buf->prepared) { + buf->paddr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + buf->size = vb2_plane_size(&buf->vb.vb2_buf, 0); + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->size); + buf->prepared = true; + } + + return 0; +} + +static void mchp_dscmi_buffer_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct mchp_dscmi_fpga *mchp_dscmi = vb2_get_drv_priv(vb->vb2_queue); + struct mchp_dscmi_buffer *buf = + container_of(vbuf, struct mchp_dscmi_buffer, vb); + + spin_lock_irq(&mchp_dscmi->qlock); + list_add_tail(&buf->list, &mchp_dscmi->buf_list); + + if (mchp_dscmi->state == WAIT_FOR_BUFFER) { + mchp_dscmi->state = RUNNING; + mchp_dscmi->active = buf; + } + + spin_unlock_irq(&mchp_dscmi->qlock); +} + +static void return_all_buffers(struct mchp_dscmi_fpga *mchp_dscmi, + enum vb2_buffer_state state) +{ + struct mchp_dscmi_buffer *buf, *node; + + list_for_each_entry_safe(buf, node, &mchp_dscmi->buf_list, list) { + vb2_buffer_done(&buf->vb.vb2_buf, state); + list_del(&buf->list); + } + mchp_dscmi->active = NULL; +} + +static int mchp_dscmi_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct mchp_dscmi_fpga *mchp_dscmi = vb2_get_drv_priv(vq); + struct v4l2_subdev *subdev = mchp_dscmi->current_subdev->subdev; + struct mchp_dscmi_compression_ratio *h264_ratio = &mchp_dscmi->h264_ratio; + int ret; + + v4l2_subdev_call(subdev, core, s_power, MCHP_DSCMI_CAM_POWER_ON); + + ret = v4l2_subdev_call(subdev, video, s_stream, MCHP_DSCMI_CAM_START); + if (ret && ret != -ENOIOCTLCMD) { + dev_err(mchp_dscmi->dev, + "stream enable failed in subdev %d\n", ret); + goto err_free_buffers; + } + + mchp_dscmi_reg_write(mchp_dscmi, MCHP_DSCMI_FRAME_START_REG, MCHP_DSCMI_FRAME_STOP); + mdelay(MCHP_DSCMI_DELAYED_CAM_M_SEC); + mchp_dscmi_reg_write(mchp_dscmi, MCHP_DSCMI_FRAME_START_REG, + MCHP_DSCMI_FRAME_START); + + mchp_dscmi->sequence = 0; + mchp_dscmi->drop_count = 0; + mchp_dscmi->s_buff_index = 0; + mchp_dscmi->s_buff_size = 0; + + h264_ratio->frame_size_index = 0; + h264_ratio->frame_count = 0; + + ret = request_threaded_irq(mchp_dscmi->irq, mchp_dscmi_irq_ext, + mchp_dscmi_irq_thread_fn, IRQF_NO_SUSPEND, + KBUILD_MODNAME, mchp_dscmi); + + if (ret) { + dev_err(mchp_dscmi->dev, "request threaded irq failed %d\n", + ret); + goto err_free_buffers; + } + + spin_lock_irq(&mchp_dscmi->qlock); + + if (list_empty(&mchp_dscmi->buf_list)) { + dev_dbg(mchp_dscmi->dev, + "Start streaming is deferred to next buffer queue\n"); + mchp_dscmi->state = WAIT_FOR_BUFFER; + spin_unlock_irq(&mchp_dscmi->qlock); + return 0; + } + + mchp_dscmi->state = RUNNING; + + spin_unlock_irq(&mchp_dscmi->qlock); + + return 0; + +err_free_buffers: + spin_lock_irq(&mchp_dscmi->qlock); + return_all_buffers(mchp_dscmi, VB2_BUF_STATE_QUEUED); + spin_unlock_irq(&mchp_dscmi->qlock); + return ret; +} + +static void mchp_dscmi_stop_streaming(struct vb2_queue *vq) +{ + struct mchp_dscmi_fpga *mchp_dscmi = vb2_get_drv_priv(vq); + struct v4l2_subdev *subdev = mchp_dscmi->current_subdev->subdev; + + free_irq(mchp_dscmi->irq, mchp_dscmi); + + spin_lock_irq(&mchp_dscmi->qlock); + + return_all_buffers(mchp_dscmi, VB2_BUF_STATE_QUEUED); + + mchp_dscmi->active = NULL; + mchp_dscmi->state = STOPPED; + + spin_unlock_irq(&mchp_dscmi->qlock); + + mchp_dscmi_reg_write(mchp_dscmi, MCHP_DSCMI_FRAME_START_REG, MCHP_DSCMI_FRAME_STOP); + + v4l2_subdev_call(subdev, video, s_stream, MCHP_DSCMI_CAM_STOP); + + v4l2_subdev_call(subdev, core, s_power, MCHP_DSCMI_CAM_POWER_OFF); + + dev_dbg(mchp_dscmi->dev, "Capture frame count %d & drop count %d\n", + mchp_dscmi->sequence, mchp_dscmi->drop_count); +} + +static const struct vb2_ops mchp_dscmi_qops = { + .queue_setup = mchp_dscmi_queue_setup, + .buf_prepare = mchp_dscmi_buffer_prepare, + .buf_queue = mchp_dscmi_buffer_queue, + .start_streaming = mchp_dscmi_start_streaming, + .stop_streaming = mchp_dscmi_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int mchp_dscmi_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, MCHP_DSCMI_DRV_NAME, sizeof(cap->driver)); + strscpy(cap->card, "MCHP Camera Memory Interface", sizeof(cap->card)); + strscpy(cap->bus_info, "platform: mchp-dscmi", sizeof(cap->bus_info)); + + return 0; +} + +static int mchp_dscmi_try_fmt(struct mchp_dscmi_fpga *mchp_dscmi, + struct v4l2_format *fmt) +{ + struct v4l2_pix_format *pix = &fmt->fmt.pix; + struct v4l2_pix_format *pix_present = &mchp_dscmi->fmt.fmt.pix; + struct v4l2_subdev *subdev = mchp_dscmi->current_subdev->subdev; + struct mchp_dscmi_framesize *framessize; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + int ret; + + v4l2_fill_mbus_format(&format.format, pix, + mchp_dscmi_formats[mchp_dscmi->capabilities].mbus_code); + + format.format.width = MCHP_DSCMI_MAX_WIDTH; + format.format.height = MCHP_DSCMI_MAX_HEIGHT; + + ret = v4l2_subdev_call(subdev, pad, set_fmt, NULL, &format); + if (ret < 0) + return ret; + + framessize = v4l2_find_nearest_size(framesize_list, + ARRAY_SIZE(framesize_list), + width, height, + pix->width, pix->height); + + mchp_dscmi_reg_write(mchp_dscmi, MCHP_DSCMI_FRAME_WIDTH, framessize->width); + mchp_dscmi_reg_write(mchp_dscmi, MCHP_DSCMI_FRAME_HIGHT, framessize->height); + + /* Driver supports only fixed format */ + pix->pixelformat = mchp_dscmi_formats[mchp_dscmi->capabilities].fourcc; + pix->width = framessize->width; + pix->height = framessize->height; + pix->field = pix_present->field; + pix->colorspace = pix_present->colorspace; + pix->ycbcr_enc = pix_present->ycbcr_enc; + pix->quantization = pix_present->quantization; + pix->xfer_func = pix_present->xfer_func; + pix->sizeimage = pix_present->sizeimage; + + return 0; +} + +static int mchp_dscmi_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct mchp_dscmi_fpga *mchp_dscmi = video_drvdata(file); + + mchp_dscmi_try_fmt(mchp_dscmi, fmt); + + return 0; +} + +static int mchp_dscmi_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct mchp_dscmi_fpga *mchp_dscmi = video_drvdata(file); + + if (mchp_dscmi->capabilities == H264) + return mchp_dscmi_try_fmt_vid_cap(file, priv, fmt); + + if (vb2_is_streaming(&mchp_dscmi->queue)) + return -EBUSY; + + return mchp_dscmi_try_fmt_vid_cap(file, priv, fmt); +} + +static int mchp_dscmi_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct mchp_dscmi_fpga *mchp_dscmi = video_drvdata(file); + + *fmt = mchp_dscmi->fmt; + + return 0; +} + +static int mchp_dscmi_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + struct mchp_dscmi_fpga *mchp_dscmi = video_drvdata(file); + + if (fmt->index > 0) + return -EINVAL; + + fmt->pixelformat = mchp_dscmi_formats[mchp_dscmi->capabilities].fourcc; + return 0; +} + +static int mchp_dscmi_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + if (input->index != 0) + return -EINVAL; + + input->type = V4L2_INPUT_TYPE_CAMERA; + strscpy(input->name, "Camera", sizeof(input->name)); + return 0; +} + +static int mchp_dscmi_s_input(struct file *file, void *priv, unsigned int index) +{ + if (index > 0) + return -EINVAL; + + return 0; +} + +static int mchp_dscmi_g_input(struct file *file, void *priv, unsigned int *index) +{ + *index = 0; + return 0; +} + +static int mchp_dscmi_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct mchp_dscmi_fpga *mchp_dscmi = video_drvdata(file); + + if (fsize->index > 0) + return -EINVAL; + + if (fsize->pixel_format != + mchp_dscmi_formats[mchp_dscmi->capabilities].fourcc) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = MCHP_DSCMI_FIXED_WIDTH; + fsize->discrete.height = MCHP_DSCMI_FIXED_HEIGHT; + + return 0; +} + +static int mchp_dscmi_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *parm) +{ + struct mchp_dscmi_fpga *mchp_dscmi = video_drvdata(file); + struct v4l2_subdev *subdev = mchp_dscmi->current_subdev->subdev; + + return v4l2_g_parm_cap(video_devdata(file), subdev, parm); +} + +static int mchp_dscmi_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *p) +{ + struct mchp_dscmi_fpga *mchp_dscmi = video_drvdata(file); + struct v4l2_subdev *subdev = mchp_dscmi->current_subdev->subdev; + + return v4l2_s_parm_cap(video_devdata(file), subdev, p); +} + +static int mchp_dscmi_enum_frameintervals(struct file *file, void *fh, + struct v4l2_frmivalenum *fival) +{ + struct mchp_dscmi_fpga *mchp_dscmi = video_drvdata(file); + struct v4l2_subdev *subdev = mchp_dscmi->current_subdev->subdev; + struct v4l2_subdev_frame_interval_enum fie = { + .index = fival->index, + .width = fival->width, + .height = fival->height, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + int ret; + + if (fival->pixel_format != + mchp_dscmi_formats[mchp_dscmi->capabilities].fourcc) + return -EINVAL; + + fie.code = mchp_dscmi_formats[mchp_dscmi->capabilities].mbus_code; + + ret = v4l2_subdev_call(subdev, pad, enum_frame_interval, NULL, &fie); + if (ret) + return ret; + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete = fie.interval; + + return 0; +} + +/* + * The calculated parameters are used by Image Enhancement IP UG0646 running + * in the FPGA logic. The equations used for calculation are explained in + * UG0646 page number 2 in the below link + * + * (https://ww1.microchip.com/downloads/aemDocuments/documents/FPGA/ProductDocuments/UserGuides/microsemi_image_enhancement_ip_user_guide_ug0646_v3.pdf) + * + * V4l2 controls like brightness, contrast, r_gain, g_gain and b_gain are + * calulated based on two functions second_constraint_cal() and + * contrast_scale_cal() + */ + +static inline int second_constraint_cal(int brightness, int contrast_scale) +{ + return (128 * ((brightness) - ((128 * (contrast_scale)) / 10))); +} + +static inline int contrast_scale_cal(int contrast) +{ + return ((325 * (contrast + 128) / (387 - contrast)) >> 5u); +} + +/* + * x and y co-ordinates of the OSD, where x is lsb and y is msb + */ + +static void update_osd_coordinates(struct mchp_dscmi_fpga *mchp_dscmi, bool enable) +{ + u32 val; + + if (enable) { + val = mchp_dscmi->vertical_pos | + (mchp_dscmi->horizontal_pos << MCHP_DSCMI_OSD_X_POS_SHIFT); + } else { + val = MCHP_DSCMI_OSD_DISABLE_MAX_PIX; + } + mchp_dscmi_reg_write(mchp_dscmi, MCHP_DSCMI_OSD_X_Y_POS, val); +} + +static int mchp_dscmi_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mchp_dscmi_fpga *mchp_dscmi; + u32 contrast_scale, second_constraint, r_gain, g_gain, b_gain; + + mchp_dscmi = container_of(ctrl->handler, + struct mchp_dscmi_fpga, ctrl_handler); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + mchp_dscmi->brightness = ctrl->val; + contrast_scale = contrast_scale_cal(mchp_dscmi->contrast); + second_constraint = second_constraint_cal(ctrl->val, + contrast_scale); + mchp_dscmi_reg_write(mchp_dscmi, MCHP_DSCMI_SECOND_CONSTRAINT, + second_constraint); + break; + case V4L2_CID_CONTRAST: + mchp_dscmi->contrast = ctrl->val; + contrast_scale = contrast_scale_cal(ctrl->val); + second_constraint = second_constraint_cal(mchp_dscmi->brightness, + contrast_scale); + + mchp_dscmi_reg_write(mchp_dscmi, MCHP_DSCMI_SECOND_CONSTRAINT, + second_constraint); + + break; + case MCHP_DSCMI_CID_RED_GAIN: + contrast_scale = contrast_scale_cal(mchp_dscmi->contrast); + r_gain = ((ctrl->val * contrast_scale) / 10); + mchp_dscmi_reg_write(mchp_dscmi, MCHP_DSCMI_R_CONSTRAINT, r_gain); + break; + case MCHP_DSCMI_CID_GREEN_GAIN: + contrast_scale = contrast_scale_cal(mchp_dscmi->contrast); + g_gain = ((ctrl->val * contrast_scale) / 10); + mchp_dscmi_reg_write(mchp_dscmi, MCHP_DSCMI_G_CONSTRAINT, g_gain); + break; + case MCHP_DSCMI_CID_BLUE_GAIN: + contrast_scale = contrast_scale_cal(mchp_dscmi->contrast); + b_gain = ((ctrl->val * contrast_scale) / 10); + mchp_dscmi_reg_write(mchp_dscmi, MCHP_DSCMI_B_CONSTRAINT, b_gain); + break; + case V4L2_CID_GAIN: + contrast_scale = contrast_scale_cal(mchp_dscmi->contrast); + r_gain = ((ctrl->val * contrast_scale) / 10); + mchp_dscmi_reg_write(mchp_dscmi, MCHP_DSCMI_R_CONSTRAINT, r_gain); + mchp_dscmi_reg_write(mchp_dscmi, MCHP_DSCMI_G_CONSTRAINT, r_gain); + mchp_dscmi_reg_write(mchp_dscmi, MCHP_DSCMI_B_CONSTRAINT, r_gain); + break; + case MCHP_DSCMI_CID_Q_FACTOR: + mchp_dscmi_reg_write(mchp_dscmi, MCHP_DSCMI_FRAME_Q_FACTOR, + ctrl->val); + break; + case MCHP_DSCMI_CID_OSD_X_POS: + mchp_dscmi->horizontal_pos = ctrl->val; + update_osd_coordinates(mchp_dscmi, true); + break; + case MCHP_DSCMI_CID_OSD_Y_POS: + mchp_dscmi->vertical_pos = ctrl->val; + update_osd_coordinates(mchp_dscmi, true); + break; + case MCHP_DSCMI_CID_OSD_ENABLE: + if (mchp_dscmi->has_hw_osd_enable) + mchp_dscmi_reg_write(mchp_dscmi, MCHP_DSCMI_OSD_EN, ctrl->val); + else + update_osd_coordinates(mchp_dscmi, ctrl->val); + break; + case MCHP_DSCMI_CID_OSD_COLOR: + mchp_dscmi_reg_write(mchp_dscmi, MCHP_DSCMI_OSD_COLOR, ctrl->val); + break; + case V4L2_CID_AUTOGAIN: + if (ctrl->val) + queue_delayed_work(mchp_dscmi->auto_gain_wq, + &mchp_dscmi->auto_gain_dw, + msecs_to_jiffies(MCHP_DSCMI_DELAYED_WORK_TIME_M_SEC)); + else + cancel_delayed_work(&mchp_dscmi->auto_gain_dw); + + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ctrl_ops mchp_dscmi_ctrl_ops = { + .s_ctrl = mchp_dscmi_s_ctrl, +}; + +static const struct v4l2_ioctl_ops mchp_dscmi_ioctl_ops = { + .vidioc_querycap = mchp_dscmi_querycap, + .vidioc_try_fmt_vid_cap = mchp_dscmi_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = mchp_dscmi_s_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = mchp_dscmi_g_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = mchp_dscmi_enum_fmt_vid_cap, + + .vidioc_enum_input = mchp_dscmi_enum_input, + .vidioc_g_input = mchp_dscmi_g_input, + .vidioc_s_input = mchp_dscmi_s_input, + + .vidioc_g_parm = mchp_dscmi_g_parm, + .vidioc_s_parm = mchp_dscmi_s_parm, + .vidioc_enum_framesizes = mchp_dscmi_enum_framesizes, + .vidioc_enum_frameintervals = mchp_dscmi_enum_frameintervals, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct v4l2_file_operations mchp_dscmi_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, +}; + +static struct v4l2_format default_fmt[] = { + { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .fmt.pix = { + .width = MCHP_DSCMI_FIXED_WIDTH, + .height = MCHP_DSCMI_FIXED_HEIGHT, + .pixelformat = V4L2_PIX_FMT_H264, + .bytesperline = 0, + .sizeimage = MCHP_DSCMI_FRAME_MAX_SIZE, + .field = V4L2_FIELD_NONE, + .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT, + .colorspace = V4L2_COLORSPACE_RAW, + .xfer_func = V4L2_XFER_FUNC_NONE, + }, + }, { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .fmt.pix = { + .width = MCHP_DSCMI_FIXED_WIDTH, + .height = MCHP_DSCMI_FIXED_HEIGHT, + .pixelformat = V4L2_PIX_FMT_MJPEG, + .bytesperline = 0, + .sizeimage = MCHP_DSCMI_FRAME_MAX_SIZE, + .field = V4L2_FIELD_NONE, + .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT, + .colorspace = V4L2_COLORSPACE_RAW, + .xfer_func = V4L2_XFER_FUNC_NONE, + }, + }, +}; + +static const struct v4l2_ctrl_config mchp_dscmi_gain_ctrls[] = { + { + .ops = &mchp_dscmi_ctrl_ops, + .id = MCHP_DSCMI_CID_RED_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Red", + .min = MCHP_DSCMI_CTL_MIN, + .max = MCHP_DSCMI_GAIN_CTL_MAX, + .def = MCHP_DSCMI_GAIN_CTL_DEFAULT, + .step = MCHP_DSCMI_CTL_STEP, + }, { + .ops = &mchp_dscmi_ctrl_ops, + .id = MCHP_DSCMI_CID_GREEN_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Green", + .min = MCHP_DSCMI_CTL_MIN, + .max = MCHP_DSCMI_GAIN_CTL_MAX, + .def = MCHP_DSCMI_GAIN_CTL_DEFAULT, + .step = MCHP_DSCMI_CTL_STEP, + }, { + .ops = &mchp_dscmi_ctrl_ops, + .id = MCHP_DSCMI_CID_BLUE_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Blue", + .min = MCHP_DSCMI_CTL_MIN, + .max = MCHP_DSCMI_GAIN_CTL_MAX, + .def = MCHP_DSCMI_GAIN_CTL_DEFAULT, + .step = MCHP_DSCMI_CTL_STEP, + }, { + .ops = &mchp_dscmi_ctrl_ops, + .id = MCHP_DSCMI_CID_Q_FACTOR, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Quality Factor", + .min = MCHP_DSCMI_Q_FACTOR_CTL_MIN, + .max = MCHP_DSCMI_Q_FACTOR_CTL_MAX, + .def = MCHP_DSCMI_Q_FACTOR_CTL_DEFAULT, + .step = MCHP_DSCMI_CTL_STEP, + }, { + .ops = &mchp_dscmi_ctrl_ops, + .id = MCHP_DSCMI_CID_OSD_X_POS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "OSDx Position", + .min = MCHP_DSCMI_OSD_X_Y_POS_MIN, + .max = MCHP_DSCMI_OSD_X_Y_POS_MAX, + .def = MCHP_DSCMI_OSD_X_Y_POS_DEFAULT, + .step = MCHP_DSCMI_OSD_X_Y_POS_STEP, + }, { + .ops = &mchp_dscmi_ctrl_ops, + .id = MCHP_DSCMI_CID_OSD_Y_POS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "OSDy Position", + .min = MCHP_DSCMI_OSD_X_Y_POS_MIN, + .max = MCHP_DSCMI_OSD_X_Y_POS_MAX, + .def = MCHP_DSCMI_OSD_X_Y_POS_DEFAULT, + .step = MCHP_DSCMI_OSD_X_Y_POS_STEP, + }, { + .ops = &mchp_dscmi_ctrl_ops, + .id = MCHP_DSCMI_CID_OSD_ENABLE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "OSD enable", + .min = MCHP_DSCMI_OSD_ENABLE_MIN, + .max = MCHP_DSCMI_OSD_ENABLE_MAX, + .def = MCHP_DSCMI_OSD_ENABLE_DEFAULT, + .step = MCHP_DSCMI_OSD_ENABLE_STEP, + }, { + .ops = &mchp_dscmi_ctrl_ops, + .id = MCHP_DSCMI_CID_OSD_COLOR, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "OSD color", + .min = MCHP_DSCMI_OSD_COLOR_MIN, + .max = MCHP_DSCMI_OSD_COLOR_MAX, + .def = MCHP_DSCMI_OSD_COLOR_DEFAULT, + .step = MCHP_DSCMI_OSD_COLOR_STEP, + }, +}; + +static int mchp_dscmi_formats_init(struct mchp_dscmi_fpga *mchp_dscmi) +{ + struct v4l2_subdev *subdev = mchp_dscmi->current_subdev->subdev; + struct v4l2_subdev_mbus_code_enum mbus_code = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .index = 0, + }; + + /* + * Read all mbus codes supported by the camera sensor and return error + * if it is not matching with mbus codes supported by the driver + */ + + while (!v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL, &mbus_code)) { + dev_dbg(mchp_dscmi->dev, "mbus_code.index %d\n", mbus_code.index); + + if (mchp_dscmi_formats[0].mbus_code == mbus_code.code) + return 0; + mbus_code.index++; + } + + return -ENXIO; +} + +static int mchp_dscmi_graph_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *asc) +{ + struct mchp_dscmi_subdev_entity *subdev_entity = + container_of(notifier, struct mchp_dscmi_subdev_entity, notifier); + + subdev_entity->subdev = subdev; + + return 0; +} + +static void mchp_dscmi_graph_notify_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *asc) +{ + struct mchp_dscmi_fpga *mchp_dscmi = container_of(notifier->v4l2_dev, + struct mchp_dscmi_fpga, v4l2_dev); + + video_unregister_device(&mchp_dscmi->vdev); + v4l2_ctrl_handler_free(&mchp_dscmi->ctrl_handler); +} + +static int mchp_dscmi_graph_notify_complete(struct v4l2_async_notifier *notifier) +{ + struct mchp_dscmi_fpga *mchp_dscmi = container_of(notifier->v4l2_dev, + struct mchp_dscmi_fpga, + v4l2_dev); + struct vb2_queue *vb2_q; + struct v4l2_ctrl_handler *ctrl_hdlr; + struct video_device *vdev; + int ret; + + ret = v4l2_device_register_subdev_nodes(&mchp_dscmi->v4l2_dev); + if (ret < 0) { + dev_err(mchp_dscmi->dev, "Failed to register subdev nodes\n"); + return ret; + } + + mchp_dscmi->current_subdev = container_of(notifier, + struct mchp_dscmi_subdev_entity, + notifier); + + ctrl_hdlr = &mchp_dscmi->ctrl_handler; + + if (mchp_dscmi->capabilities == H264) + v4l2_ctrl_handler_init(ctrl_hdlr, MCHP_DSCMI_H264_NUM_CTRLS); + else + v4l2_ctrl_handler_init(ctrl_hdlr, MCHP_DSCMI_NUM_CTRLS); + + v4l2_ctrl_new_std(ctrl_hdlr, &mchp_dscmi_ctrl_ops, + V4L2_CID_BRIGHTNESS, MCHP_DSCMI_CTL_MIN, MCHP_DSCMI_CTL_MAX, + MCHP_DSCMI_CTL_STEP, MCHP_DSCMI_CTL_MAX / 2); + v4l2_ctrl_new_std(ctrl_hdlr, &mchp_dscmi_ctrl_ops, + V4L2_CID_CONTRAST, MCHP_DSCMI_CTL_MIN, MCHP_DSCMI_CTL_MAX, + MCHP_DSCMI_CTL_STEP, MCHP_DSCMI_CTL_MAX / 2); + v4l2_ctrl_new_std(ctrl_hdlr, &mchp_dscmi_ctrl_ops, + V4L2_CID_GAIN, MCHP_DSCMI_CTL_MIN, MCHP_DSCMI_CTL_MAX, + MCHP_DSCMI_CTL_STEP, MCHP_DSCMI_GAIN_CTL_DEFAULT); + + v4l2_ctrl_new_std(ctrl_hdlr, &mchp_dscmi_ctrl_ops, + V4L2_CID_AUTOGAIN, MCHP_DSCMI_CTL_MIN, 1, MCHP_DSCMI_CTL_STEP, 0); + + v4l2_ctrl_new_custom(ctrl_hdlr, &mchp_dscmi_gain_ctrls[0], NULL); + v4l2_ctrl_new_custom(ctrl_hdlr, &mchp_dscmi_gain_ctrls[1], NULL); + v4l2_ctrl_new_custom(ctrl_hdlr, &mchp_dscmi_gain_ctrls[2], NULL); + v4l2_ctrl_new_custom(ctrl_hdlr, &mchp_dscmi_gain_ctrls[4], NULL); + mchp_dscmi->horizontal_pos = MCHP_DSCMI_OSD_X_Y_POS_MIN; + + v4l2_ctrl_new_custom(ctrl_hdlr, &mchp_dscmi_gain_ctrls[5], NULL); + mchp_dscmi->vertical_pos = MCHP_DSCMI_OSD_X_Y_POS_MIN; + + v4l2_ctrl_new_custom(ctrl_hdlr, &mchp_dscmi_gain_ctrls[6], NULL); + v4l2_ctrl_new_custom(ctrl_hdlr, &mchp_dscmi_gain_ctrls[7], NULL); + + if (mchp_dscmi->capabilities == H264) + v4l2_ctrl_new_custom(ctrl_hdlr, &mchp_dscmi_gain_ctrls[3], NULL); + + if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; + return ret; + } + mchp_dscmi->v4l2_dev.ctrl_handler = ctrl_hdlr; + + ret = mchp_dscmi_formats_init(mchp_dscmi); + if (ret) { + dev_err(mchp_dscmi->dev, "No supported mediabus format found\n"); + goto free_ctrl_hdlr; + } + + mchp_dscmi->fmt = default_fmt[mchp_dscmi->capabilities]; + + vb2_q = &mchp_dscmi->queue; + vb2_q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vb2_q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; + vb2_q->dev = mchp_dscmi->dev; + vb2_q->drv_priv = mchp_dscmi; + vb2_q->buf_struct_size = sizeof(struct mchp_dscmi_buffer); + vb2_q->ops = &mchp_dscmi_qops; + vb2_q->mem_ops = &vb2_dma_contig_memops; + vb2_q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + vb2_q->min_buffers_needed = 2; + vb2_q->lock = &mchp_dscmi->lock; + + ret = vb2_queue_init(vb2_q); + if (ret) { + dev_err(mchp_dscmi->dev, "vb2 queue init failed %d\n", ret); + goto free_ctrl_hdlr; + } + + INIT_LIST_HEAD(&mchp_dscmi->buf_list); + spin_lock_init(&mchp_dscmi->qlock); + + vdev = &mchp_dscmi->vdev; + strscpy(vdev->name, KBUILD_MODNAME, sizeof(vdev->name)); + vdev->release = video_device_release_empty; + vdev->fops = &mchp_dscmi_fops, + vdev->ioctl_ops = &mchp_dscmi_ioctl_ops, + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + vdev->lock = &mchp_dscmi->lock; + vdev->queue = vb2_q; + vdev->v4l2_dev = &mchp_dscmi->v4l2_dev; + video_set_drvdata(vdev, mchp_dscmi); + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + dev_err(mchp_dscmi->dev, "video register device failed %d\n", + ret); + goto vb2_queue_free; + } + + dev_dbg(mchp_dscmi->dev, "Async_complete\n"); + + return 0; + +vb2_queue_free: + vb2_queue_release(vb2_q); +free_ctrl_hdlr: + v4l2_ctrl_handler_free(ctrl_hdlr); + + return ret; +} + +static const struct v4l2_async_notifier_operations mchp_dscmi_v4l2_async_ops = { + .bound = mchp_dscmi_graph_notify_bound, + .unbind = mchp_dscmi_graph_notify_unbind, + .complete = mchp_dscmi_graph_notify_complete, +}; + +static void mchp_dscmi_subdev_cleanup(struct mchp_dscmi_fpga *mchp_dscmi) +{ + struct mchp_dscmi_subdev_entity *subdev_entity; + + list_for_each_entry(subdev_entity, &mchp_dscmi->subdev_entities, list) { + v4l2_async_nf_unregister(&subdev_entity->notifier); + v4l2_async_nf_cleanup(&subdev_entity->notifier); + } + + INIT_LIST_HEAD(&mchp_dscmi->subdev_entities); +} + +static void mchp_dscmi_gain_cal(struct mchp_dscmi_fpga *mchp_dscmi, + uint32_t total_average) +{ + struct v4l2_subdev *subdev = mchp_dscmi->current_subdev->subdev; + struct v4l2_control ctrl; + const u16 hs_threshold_high = (MCHP_DSCMI_GAIN_AVERAGE + MCHP_DSCMI_HYSTERESIS_GAIN); + const u16 hs_threshold_low = (MCHP_DSCMI_GAIN_AVERAGE - MCHP_DSCMI_HYSTERESIS_GAIN); + static u16 in_gain = MCHP_DSCMI_GAIN_INIT; + static u16 last_step; + u16 step; + + /* + * The total_average feed back value from fabric is less than threshold + * value then the gain will be step up by one, if the value is more than + * the threshold value then the gain will be step down by one. + */ + + if (total_average < hs_threshold_low) + step = 1; + else + if (total_average > hs_threshold_high) + step = -1; + else + step = 0; + + in_gain = in_gain + step; + + if (in_gain < MCHP_DSCMI_GAIN_MIN) + in_gain = MCHP_DSCMI_GAIN_MIN; + + if (in_gain >= MCHP_DSCMI_GAIN_AVERAGE) + in_gain = MCHP_DSCMI_GAIN_AVERAGE; + + if (last_step != step && step != 0) { + dev_dbg(mchp_dscmi->dev, "average=%d in_gain=%d step=%d\n", + total_average, in_gain, step); + } + + last_step = step; + if (step != 0) { + memset(&ctrl, 0, sizeof(ctrl)); + ctrl.id = V4L2_CID_ANALOGUE_GAIN; + ctrl.value = in_gain; + v4l2_s_ctrl(NULL, subdev->ctrl_handler, &ctrl); + } +} + +static void mchp_dscmi_work_auto_analog_gain(struct work_struct *work) +{ + struct mchp_dscmi_fpga *mchp_dscmi = container_of(work, + struct mchp_dscmi_fpga, + auto_gain_dw.work); + int div = MCHP_DSCMI_MAX_WIDTH * MCHP_DSCMI_MAX_HEIGHT * 2; + u32 total_sum, total_average; + + total_sum = mchp_dscmi_reg_read(mchp_dscmi, MCHP_DSCMI_RGB_SUM); + total_average = total_sum / div; + mchp_dscmi_gain_cal(mchp_dscmi, total_average); + queue_delayed_work(mchp_dscmi->auto_gain_wq, + &mchp_dscmi->auto_gain_dw, + msecs_to_jiffies(MCHP_DSCMI_DELAYED_WORK_TIME_M_SEC)); +} + +static int mchp_dscmi_graph_parse_dt(struct device *dev, + struct mchp_dscmi_fpga *mchp_dscmi) +{ + struct device_node *np = dev->of_node; + struct device_node *epn; + struct mchp_dscmi_subdev_entity *subdev_entity; + struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 }; + int ret; + + INIT_LIST_HEAD(&mchp_dscmi->subdev_entities); + + epn = of_graph_get_next_endpoint(np, NULL); + if (!epn) + return 0; + + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn), + &v4l2_epn); + if (ret) { + dev_err(dev, "Could not parse the endpoint\n"); + of_node_put(epn); + return -EINVAL; + } + + subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity), + GFP_KERNEL); + if (!subdev_entity) { + of_node_put(epn); + return -ENOMEM; + } + + subdev_entity->epn = epn; + + list_add_tail(&subdev_entity->list, + &mchp_dscmi->subdev_entities); + + of_node_put(epn); + + return 0; +} + +static int mchp_dscmi_read_capabilities(struct platform_device *pdev, + struct mchp_dscmi_fpga *mchp_dscmi) +{ + u32 capabilities; + int ret = 0; + + capabilities = mchp_dscmi_reg_read(mchp_dscmi, MCHP_DSCMI_CAPABILITIES_V4L2); + + switch (capabilities) { + case MCHP_DSCMI_CAPABILITIES_H264: + dev_info(&pdev->dev, "Found H.264 video capabilities\n"); + mchp_dscmi->capabilities = H264; + break; + case MCHP_DSCMI_CAPABILITIES_MJPEG: + dev_info(&pdev->dev, "Found mJPEG video capabilities\n"); + mchp_dscmi->capabilities = MJPEG; + break; + default: + dev_err(&pdev->dev, "capabilities not available 0x%x 0x%x\n", + capabilities, + mchp_dscmi_reg_read(mchp_dscmi, MCHP_DSCMI_CAPABILITIES_V4L2 + 4)); + ret = -ENODEV; + break; + } + + return ret; +} + +static int mchp_dscmi_probe(struct platform_device *pdev) +{ + struct mchp_dscmi_fpga *mchp_dscmi; + struct mchp_dscmi_subdev_entity *subdev_entity; + struct device_node *np; + struct dma_chan *chan; + struct resource *res, r; + int ret; + const struct mchp_dscmi_driver_platdata *ddata; + + mchp_dscmi = devm_kzalloc(&pdev->dev, + sizeof(struct mchp_dscmi_fpga), GFP_KERNEL); + if (!mchp_dscmi) + return dev_err_probe(&pdev->dev, PTR_ERR(mchp_dscmi), + "kzalloc failed\n"); + + mchp_dscmi->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(mchp_dscmi->base)) + return dev_err_probe(&pdev->dev, PTR_ERR(mchp_dscmi->base), + "could not get mem resource\n"); + + ret = mchp_dscmi_read_capabilities(pdev, mchp_dscmi); + if (ret < 0) + return ret; + + ddata = of_device_get_match_data(&pdev->dev); + if (ddata) { + if (ddata->quirks & MCHP_DSCMI_OSD_EN_FPGA_RTL) + mchp_dscmi->has_hw_osd_enable = true; + else + mchp_dscmi->has_hw_osd_enable = false; + } + + np = of_parse_phandle(pdev->dev.of_node, "memory-region", 0); + if (!np) + return dev_err_probe(&pdev->dev, PTR_ERR(np), + "No memory-region specified\n"); + + ret = of_address_to_resource(np, 0, &r); + of_node_put(np); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "No memory address assigned to the region\n"); + + mchp_dscmi->cambuf.paddr = r.start; + mchp_dscmi->cambuf.size = r.end - r.start; + + mchp_dscmi_reg_write(mchp_dscmi, MCHP_DSCMI_STREAM_ADDR_LOW, + lower_32_bits(r.start)); + + mchp_dscmi_reg_write(mchp_dscmi, MCHP_DSCMI_STREAM_ADDR_HIGH, + upper_32_bits(r.start)); + + mchp_dscmi->irq = platform_get_irq(pdev, 0); + if (mchp_dscmi->irq <= 0) + return dev_err_probe(&pdev->dev, mchp_dscmi->irq, + "could not get irq\n"); + + mchp_dscmi->reset_gpio = devm_gpiod_get_optional(&pdev->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(mchp_dscmi->reset_gpio)) + return dev_err_probe(&pdev->dev, PTR_ERR(mchp_dscmi->reset_gpio), + "Failed to get reset gpio"); + + if (mchp_dscmi->reset_gpio) + gpiod_set_value_cansleep(mchp_dscmi->reset_gpio, 1); + + mchp_dscmi->s_buff_index = 0; + mchp_dscmi->s_buff_size = 0; + + chan = dma_request_chan(&pdev->dev, "rx"); + if (IS_ERR(chan)) + return dev_err_probe(&pdev->dev, PTR_ERR(chan), + "Failed to request DMA channel\n"); + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (ret) { + dev_err_probe(&pdev->dev, ret, "DMA set mask failed"); + goto dma_free; + } + + mutex_init(&mchp_dscmi->dma_lock); + mutex_init(&mchp_dscmi->lock); + mchp_dscmi->dma_chan = chan; + + mchp_dscmi->pdev = pdev; + mchp_dscmi->dev = &pdev->dev; + + ret = v4l2_device_register(&pdev->dev, &mchp_dscmi->v4l2_dev); + if (ret) + goto dma_free; + + platform_set_drvdata(pdev, mchp_dscmi); + + mchp_dscmi->auto_gain_wq = create_workqueue("auto gain"); + INIT_DELAYED_WORK(&mchp_dscmi->auto_gain_dw, + mchp_dscmi_work_auto_analog_gain); + + ret = mchp_dscmi_graph_parse_dt(&pdev->dev, mchp_dscmi); + if (ret) { + dev_err_probe(&pdev->dev, ret, "Fail to parse device tree\n"); + goto v4l2_unregister; + } + + if (list_empty(&mchp_dscmi->subdev_entities)) { + ret = -ENODEV; + dev_err_probe(&pdev->dev, ret, "No subdev found\n"); + goto v4l2_unregister; + } + + list_for_each_entry(subdev_entity, &mchp_dscmi->subdev_entities, list) { + struct v4l2_async_connection *asc; + + v4l2_async_nf_init(&subdev_entity->notifier, &mchp_dscmi->v4l2_dev); + + asc = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier, + of_fwnode_handle(subdev_entity + ->epn), + struct v4l2_async_connection); + of_node_put(subdev_entity->epn); + subdev_entity->epn = NULL; + + if (IS_ERR(asc)) { + ret = PTR_ERR(asc); + goto cleanup_subdev; + } + + subdev_entity->notifier.ops = &mchp_dscmi_v4l2_async_ops; + + ret = v4l2_async_nf_register(&subdev_entity->notifier); + if (ret) { + dev_err_probe(&pdev->dev, ret, + "Fail to register async notifier\n"); + goto cleanup_subdev; + } + } + + dev_info(&pdev->dev, "Version %s loaded\n", MCHP_DSCMI_DRV_VERSION); + return 0; + +cleanup_subdev: + mchp_dscmi_subdev_cleanup(mchp_dscmi); +v4l2_unregister: + v4l2_device_unregister(&mchp_dscmi->v4l2_dev); +dma_free: + dma_release_channel(mchp_dscmi->dma_chan); + return ret; +} + +static int mchp_dscmi_remove(struct platform_device *pdev) +{ + struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); + struct mchp_dscmi_fpga *mchp_dscmi = container_of(v4l2_dev, + struct mchp_dscmi_fpga, + v4l2_dev); + + mutex_destroy(&mchp_dscmi->dma_lock); + mutex_destroy(&mchp_dscmi->lock); + cancel_delayed_work(&mchp_dscmi->auto_gain_dw); + flush_workqueue(mchp_dscmi->auto_gain_wq); + destroy_workqueue(mchp_dscmi->auto_gain_wq); + v4l2_async_nf_unregister(&mchp_dscmi->current_subdev->notifier); + v4l2_async_nf_cleanup(&mchp_dscmi->current_subdev->notifier); + v4l2_device_unregister(&mchp_dscmi->v4l2_dev); + dma_release_channel(mchp_dscmi->dma_chan); + + return 0; +} + +static const struct mchp_dscmi_driver_platdata mpfs_osd = { + .quirks = MCHP_DSCMI_OSD_EN_FPGA_RTL, +}; + +static const struct of_device_id mchp_dscmi_of_match[] = { + { + .compatible = "microchip,fpga-dscmi", + }, { + .compatible = "microchip,fpga-dscmi-rtl-v2306", + .data = &mpfs_osd, + }, + {} +}; + +MODULE_DEVICE_TABLE(of, mchp_dscmi_of_match); + +static struct platform_driver mchp_dscmi_driver = { + .probe = mchp_dscmi_probe, + .remove = mchp_dscmi_remove, + .driver = { + .name = MCHP_DSCMI_DRV_NAME, + .of_match_table = mchp_dscmi_of_match, + }, +}; + +module_platform_driver(mchp_dscmi_driver); + +MODULE_DESCRIPTION("Microchip Digital Serial Camera Memory Interface Driver"); +MODULE_AUTHOR("Shravan Chippa <shravan.chippa@microchip.com>"); +MODULE_LICENSE("GPL"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/media/platform/microchip/microchip-common.c linux-microchip/drivers/media/platform/microchip/microchip-common.c --- linux-6.6.51/drivers/media/platform/microchip/microchip-common.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/media/platform/microchip/microchip-common.c 2024-11-26 14:02:37.610495703 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microchip Video Common Driver. + * + * Copyright (C) 2023 Microchip Technology Inc. and its subsidiaries + * Author: Shravan Chippa <shavan.chippa@microchip.com> + * + */ + +#include <linux/clk.h> +#include <linux/export.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include "microchip-common.h" + +#include <dt-bindings/media/microchip-common.h> + +static const struct mvideo_format mvideo_formats[] = { + { MVCF_YUV_420, 8, NULL, MEDIA_BUS_FMT_UYVY8_2X8, 1, + V4L2_PIX_FMT_NV12, 2, 1, 1, 2 }, + { MVCF_H264, 8, NULL, MEDIA_BUS_FMT_H264_1X8, 1, + V4L2_PIX_FMT_H264, 1, 1, 1, 1 }, + { MVCF_YUV_422, 8, NULL, MEDIA_BUS_FMT_UYVY8_1X16, 2, + V4L2_PIX_FMT_YUYV, 1, 1, 1, 1 }, + { MVCF_YUV_444, 8, NULL, MEDIA_BUS_FMT_VUY8_1X24, 3, + V4L2_PIX_FMT_YUV444, 1, 1, 1, 1 }, + { MVCF_RBG, 8, NULL, MEDIA_BUS_FMT_RBG888_1X24, 3, + V4L2_PIX_FMT_RGB24, 1, 1, 1, 1 }, + { MVCF_RGB, 8, NULL, MEDIA_BUS_FMT_RGB888_1X24, 3, + V4L2_PIX_FMT_RGB24, 1, 1, 1, 1 }, + { MVCF_MJPEG, 8, NULL, MEDIA_BUS_FMT_JPEG_1X8, 3, + V4L2_PIX_FMT_MJPEG, 1, 1, 1, 1 }, + { MVCF_MONO_SENSOR, 8, "mono", MEDIA_BUS_FMT_Y8_1X8, 1, + V4L2_PIX_FMT_GREY, 1, 1, 1, 1 }, + { MVCF_MONO_SENSOR, 8, "rggb", MEDIA_BUS_FMT_SRGGB8_1X8, 1, + V4L2_PIX_FMT_SRGGB8, 1, 1, 1, 1 }, + { MVCF_MONO_SENSOR, 8, "grbg", MEDIA_BUS_FMT_SGRBG8_1X8, 1, + V4L2_PIX_FMT_SGRBG8, 1, 1, 1, 1 }, + { MVCF_MONO_SENSOR, 8, "gbrg", MEDIA_BUS_FMT_SGBRG8_1X8, 1, + V4L2_PIX_FMT_SGBRG8, 1, 1, 1, 1 }, + { MVCF_MONO_SENSOR, 8, "bggr", MEDIA_BUS_FMT_SBGGR8_1X8, 1, + V4L2_PIX_FMT_SBGGR8, 1, 1, 1, 1 }, + { MVCF_MONO_SENSOR, 10, "rggb", MEDIA_BUS_FMT_SRGGB10_1X10, 2, + V4L2_PIX_FMT_SRGGB10, 1, 1, 1, 1 }, + { MVCF_MONO_SENSOR, 10, "grbg", MEDIA_BUS_FMT_SGRBG10_1X10, 2, + V4L2_PIX_FMT_SGRBG10, 1, 1, 1, 1 }, + { MVCF_MONO_SENSOR, 10, "gbrg", MEDIA_BUS_FMT_SGBRG10_1X10, 2, + V4L2_PIX_FMT_SGBRG10, 1, 1, 1, 1 }, + { MVCF_MONO_SENSOR, 10, "bggr", MEDIA_BUS_FMT_SBGGR10_1X10, 2, + V4L2_PIX_FMT_SBGGR10, 1, 1, 1, 1 }, + { MVCF_MONO_SENSOR, 12, "rggb", MEDIA_BUS_FMT_SRGGB12_1X12, 2, + V4L2_PIX_FMT_SRGGB12, 1, 1, 1, 1 }, + { MVCF_MONO_SENSOR, 12, "grbg", MEDIA_BUS_FMT_SGRBG12_1X12, 2, + V4L2_PIX_FMT_SGRBG12, 1, 1, 1, 1 }, + { MVCF_MONO_SENSOR, 12, "gbrg", MEDIA_BUS_FMT_SGBRG12_1X12, 2, + V4L2_PIX_FMT_SGBRG12, 1, 1, 1, 1 }, + { MVCF_MONO_SENSOR, 12, "bggr", MEDIA_BUS_FMT_SBGGR12_1X12, 2, + V4L2_PIX_FMT_SBGGR12, 1, 1, 1, 1 }, + { MVCF_MONO_SENSOR, 16, "rggb", MEDIA_BUS_FMT_SRGGB16_1X16, 2, + V4L2_PIX_FMT_SRGGB16, 1, 1, 1, 1 }, + { MVCF_MONO_SENSOR, 16, "grbg", MEDIA_BUS_FMT_SGRBG16_1X16, 2, + V4L2_PIX_FMT_SGRBG16, 1, 1, 1, 1 }, + { MVCF_MONO_SENSOR, 16, "gbrg", MEDIA_BUS_FMT_SGBRG16_1X16, 2, + V4L2_PIX_FMT_SGBRG16, 1, 1, 1, 1 }, + { MVCF_MONO_SENSOR, 16, "bggr", MEDIA_BUS_FMT_SBGGR16_1X16, 2, + V4L2_PIX_FMT_SBGGR16, 1, 1, 1, 1 }, +}; + +const struct mvideo_format *mvc_get_format_by_code(unsigned int code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mvideo_formats); ++i) { + const struct mvideo_format *format = &mvideo_formats[i]; + + if (format->code == code) + return format; + } + + return ERR_PTR(-EINVAL); +} +EXPORT_SYMBOL_GPL(mvc_get_format_by_code); + +const struct mvideo_format *mvc_get_format_by_vf_code(unsigned int vf_code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mvideo_formats); ++i) { + const struct mvideo_format *format = &mvideo_formats[i]; + + if (format->vf_code == vf_code) + return format; + } + + return ERR_PTR(-EINVAL); +} +EXPORT_SYMBOL_GPL(mvc_get_format_by_vf_code); + +const struct mvideo_format *mvc_get_format_by_fourcc(u32 fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mvideo_formats); ++i) { + const struct mvideo_format *format = &mvideo_formats[i]; + + if (format->fourcc == fourcc) + return format; + } + + return &mvideo_formats[0]; +} +EXPORT_SYMBOL_GPL(mvc_get_format_by_fourcc); + +const struct mvideo_format *mvc_of_get_format(struct device_node *node) +{ + const char *pattern = "mono"; + unsigned int vf_code; + unsigned int i; + u32 width; + int ret; + + ret = of_property_read_u32(node, "microchip,video-format", &vf_code); + if (ret < 0) + return ERR_PTR(ret); + + ret = of_property_read_u32(node, "microchip,video-width", &width); + if (ret < 0) + return ERR_PTR(ret); + + if (vf_code == MVCF_MONO_SENSOR) + of_property_read_string(node, "microchip,cfa-pattern", &pattern); + + for (i = 0; i < ARRAY_SIZE(mvideo_formats); ++i) { + const struct mvideo_format *format = &mvideo_formats[i]; + + if (format->vf_code != vf_code || format->width != width) + continue; + + if (vf_code == MVCF_MONO_SENSOR && + strcmp(pattern, format->pattern)) + continue; + + return format; + } + + return ERR_PTR(-EINVAL); +} +EXPORT_SYMBOL_GPL(mvc_of_get_format); + +void mvc_set_format_size(struct v4l2_mbus_framefmt *format, + const struct v4l2_subdev_format *fmt) +{ + format->width = clamp_t(unsigned int, fmt->format.width, + MVC_MIN_WIDTH, MVC_MAX_WIDTH); + format->height = clamp_t(unsigned int, fmt->format.height, + MVC_MIN_HEIGHT, MVC_MAX_HEIGHT); +} +EXPORT_SYMBOL_GPL(mvc_set_format_size); + +int mvc_init_resources(struct mvc_device *mvc) +{ + struct platform_device *pdev = to_platform_device(mvc->dev); + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mvc->iomem = devm_ioremap_resource(mvc->dev, res); + if (IS_ERR(mvc->iomem)) + return PTR_ERR(mvc->iomem); + + mvc->clk = devm_clk_get(mvc->dev, NULL); + if (IS_ERR(mvc->clk)) + return PTR_ERR(mvc->clk); + + clk_prepare_enable(mvc->clk); + return 0; +} +EXPORT_SYMBOL_GPL(mvc_init_resources); + +void mvc_cleanup_resources(struct mvc_device *mvc) +{ + clk_disable_unprepare(mvc->clk); +} +EXPORT_SYMBOL_GPL(mvc_cleanup_resources); + +int mvc_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct v4l2_mbus_framefmt *format; + + if (code->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + if (code->index) + return -EINVAL; + + format = v4l2_subdev_get_try_format(subdev, sd_state, code->pad); + + code->code = format->code; + + return 0; +} +EXPORT_SYMBOL_GPL(mvc_enum_mbus_code); + +int mvc_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct v4l2_mbus_framefmt *format; + + if (fse->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + format = v4l2_subdev_get_try_format(subdev, sd_state, fse->pad); + + if (fse->index || fse->code != format->code) + return -EINVAL; + + if (fse->pad == MVC_PAD_SINK) { + fse->min_width = MVC_MIN_WIDTH; + fse->max_width = MVC_MAX_WIDTH; + fse->min_height = MVC_MIN_HEIGHT; + fse->max_height = MVC_MAX_HEIGHT; + } else { + fse->min_width = format->width; + fse->max_width = format->width; + fse->min_height = format->height; + fse->max_height = format->height; + } + + return 0; +} +EXPORT_SYMBOL_GPL(mvc_enum_frame_size); + +MODULE_DESCRIPTION("Microchip Video Common Driver"); +MODULE_AUTHOR("Shravan Chippa <shravan.chippa@microchip.com>"); +MODULE_LICENSE("GPL"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/media/platform/microchip/microchip-common.h linux-microchip/drivers/media/platform/microchip/microchip-common.h --- linux-6.6.51/drivers/media/platform/microchip/microchip-common.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/media/platform/microchip/microchip-common.h 2024-11-26 14:02:37.610495703 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries + * Author: Shravan Chippa <shavan.chippa@microchip.com> + * + */ + +#ifndef __MCHP_COMMAN_H__ +#define __MCHP_COMMAN_H__ + +#include <linux/bitops.h> +#include <linux/io.h> +#include <media/v4l2-subdev.h> + +#define MVC_PAD_SINK 0 +#define MVC_PAD_SOURCE 1 + +/* User defined v4l2 control */ +#define MCHP_CID_RED_GAIN (V4L2_CID_USER_BASE | 0x1001) +#define MCHP_CID_GREEN_GAIN (V4L2_CID_USER_BASE | 0x1002) +#define MCHP_CID_BLUE_GAIN (V4L2_CID_USER_BASE | 0x1003) +#define MCHP_CID_Q_FACTOR (V4L2_CID_USER_BASE | 0x1004) +#define MCHP_CID_OSD_X_POS (V4L2_CID_USER_BASE | 0x1005) +#define MCHP_CID_OSD_Y_POS (V4L2_CID_USER_BASE | 0x1006) +#define MCHP_CID_OSD_ENABLE (V4L2_CID_USER_BASE | 0x1007) +#define MCHP_CID_OSD_COLOR (V4L2_CID_USER_BASE | 0x1008) +#define MCHP_CID_P_COUNT (V4L2_CID_USER_BASE | 0x1009) +#define MCHP_CID_COMPRESSION_RATIO (V4L2_CID_USER_BASE | 0x100A) +#define MCHP_CID_OSD_NUM (V4L2_CID_USER_BASE | 0x100B) +#define MCHP_CID_RGB_SUM (V4L2_CID_USER_BASE | 0x100C) + +/** + * struct mvc_device - Microchip Video Capture device structure + * @subdev: V4L2 subdevice + * @dev: (OF) device + * @iomem: device I/O register space remapped to kernel virtual memory + * @clk: video core clock + */ +struct mvc_device { + struct v4l2_subdev subdev; + struct device *dev; + void __iomem *iomem; + struct clk *clk; +}; + +/** + * struct mvideo_format - Microchip video format description + * @vf_code: AXI4 video format code + * @width: AXI4 format width in bits per component + * @pattern: CFA pattern for Mono/Sensor formats + * @code: media bus format code + * @bpl_factor: Bytes per line factor + * @bpp: bits per pixel + * @fourcc: V4L2 pixel format FCC identifier + * @num_planes: number of planes w.r.t. color format + * @buffers: number of buffers per format + * @hsub: Horizontal sampling factor of Chroma + * @vsub: Vertical sampling factor of Chroma + */ +struct mvideo_format { + unsigned int vf_code; + unsigned int width; + const char *pattern; + unsigned int code; + unsigned int bpp; + u32 fourcc; + u8 num_planes; + u8 buffers; + u8 hsub; + u8 vsub; +}; + +const struct mvideo_format *mvc_get_format_by_code(unsigned int code); +const struct mvideo_format *mvc_get_format_by_vf_code(unsigned int vf_code); +const struct mvideo_format *mvc_get_format_by_fourcc(u32 fourcc); +const struct mvideo_format *mvc_of_get_format(struct device_node *node); +void mvc_set_format_size(struct v4l2_mbus_framefmt *format, + const struct v4l2_subdev_format *fmt); +int mvc_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code); +int mvc_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse); + +int mvc_init_resources(struct mvc_device *mvc); +void mvc_cleanup_resources(struct mvc_device *mvc); + +#endif /* __MICROCHIP_COMMAN_H__ */ + diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/media/platform/microchip/microchip-gvpc.c linux-microchip/drivers/media/platform/microchip/microchip-gvpc.c --- linux-6.6.51/drivers/media/platform/microchip/microchip-gvpc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/media/platform/microchip/microchip-gvpc.c 2024-11-26 14:02:37.610495703 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microchip GVPC (Generic Video Pipeline Connector) Driver. + * + * Copyright (C) 2022-2023 Microchip Technology Inc. and its subsidiaries + * Author: Shravan Chippa <shavan.chippa@microchip.com> + * + */ + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <media/v4l2-async.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> + +#include "microchip-common.h" + +#define MVC_DEF_HEIGHT 1080 +#define MVC_DEF_WIDTH 1920 + +/** + * struct mchp_gvpc_device - Microchip GVPC Core device structure + * @dev: device + * @subdev: The v4l2 subdev structure + * @iomem: Base address of subsystem + * @pads: media pads + * @formats: active V4L2 media bus formats at the sink and source pads + * @default_formats: default V4L2 media bus formats + * @vip_formats: format information corresponding to the pads active formats + */ +struct mchp_gvpc_device { + struct device *dev; + struct v4l2_subdev subdev; + void __iomem *iomem; + + struct media_pad pads[2]; + + struct v4l2_mbus_framefmt formats[2]; + struct v4l2_mbus_framefmt default_formats[2]; + const struct mvideo_format *vip_formats[2]; +}; + +static inline struct mchp_gvpc_device *to_gvpc(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct mchp_gvpc_device, subdev); +} + +static int mchp_gvpc_s_stream(struct v4l2_subdev *subdev, int enable) +{ + /* + * struct mchp_gvpc_device *mchp_gvpc = to_gvpc(subdev); + * struct v4l2_mbus_framefmt *format = &mchp_gvpc->formats[MVC_PAD_SINK]; + */ + + return 0; +} + +static struct v4l2_mbus_framefmt * +__mchp_gvpc_get_pad_format(struct mchp_gvpc_device *mchp_gvpc, + struct v4l2_subdev_state *sd_state, + unsigned int pad, u32 which) +{ + struct v4l2_mbus_framefmt *format; + + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + format = v4l2_subdev_get_try_format(&mchp_gvpc->subdev, + sd_state, pad); + break; + case V4L2_SUBDEV_FORMAT_ACTIVE: + format = &mchp_gvpc->formats[pad]; + break; + default: + format = NULL; + break; + } + + return format; +} + +static int mchp_gvpc_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct mchp_gvpc_device *mchp_gvpc = to_gvpc(subdev); + struct v4l2_mbus_framefmt *format; + + format = __mchp_gvpc_get_pad_format(mchp_gvpc, sd_state, fmt->pad, fmt->which); + if (!format) + return -EINVAL; + + fmt->format = *format; + + return 0; +} + +static int mchp_gvpc_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct mchp_gvpc_device *mchp_gvpc = to_gvpc(subdev); + struct v4l2_mbus_framefmt *format; + + format = __mchp_gvpc_get_pad_format(mchp_gvpc, sd_state, fmt->pad, fmt->which); + if (!format) + return -EINVAL; + + if (fmt->pad == MVC_PAD_SOURCE) { + fmt->format = *format; + return 0; + } + + mvc_set_format_size(format, fmt); + + fmt->format = *format; + + format = __mchp_gvpc_get_pad_format(mchp_gvpc, sd_state, MVC_PAD_SOURCE, + fmt->which); + if (!format) + return -EINVAL; + + mvc_set_format_size(format, fmt); + + return 0; +} + +static int mchp_gvpc_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + struct mchp_gvpc_device *mchp_gvpc = to_gvpc(subdev); + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_get_try_format(subdev, fh->state, MVC_PAD_SINK); + *format = mchp_gvpc->default_formats[MVC_PAD_SINK]; + + format = v4l2_subdev_get_try_format(subdev, fh->state, MVC_PAD_SOURCE); + *format = mchp_gvpc->default_formats[MVC_PAD_SOURCE]; + + return 0; +} + +static int mchp_gvpc_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + return 0; +} + +static const struct v4l2_subdev_video_ops mchp_gvpc_video_ops = { + .s_stream = mchp_gvpc_s_stream, +}; + +static const struct v4l2_subdev_pad_ops mchp_gvpc_pad_ops = { + .enum_mbus_code = mvc_enum_mbus_code, + .get_fmt = mchp_gvpc_get_format, + .set_fmt = mchp_gvpc_set_format, + .link_validate = v4l2_subdev_link_validate_default, +}; + +static const struct v4l2_subdev_ops mchp_gvpc_ops = { + .video = &mchp_gvpc_video_ops, + .pad = &mchp_gvpc_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops mchp_gvpc_internal_ops = { + .open = mchp_gvpc_open, + .close = mchp_gvpc_close, +}; + +static const struct media_entity_operations mchp_gvpc_media_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static void mchp_gvpc_init_formats(struct mchp_gvpc_device *mchp_gvpc) +{ + struct v4l2_mbus_framefmt *format; + + /* Initialize default and active formats */ + format = &mchp_gvpc->default_formats[MVC_PAD_SINK]; + format->code = mchp_gvpc->vip_formats[MVC_PAD_SINK]->code; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + format->width = MVC_DEF_WIDTH; + format->height = MVC_DEF_HEIGHT; + + mchp_gvpc->formats[MVC_PAD_SINK] = *format; + + format = &mchp_gvpc->default_formats[MVC_PAD_SOURCE]; + *format = mchp_gvpc->default_formats[MVC_PAD_SINK]; + format->code = mchp_gvpc->vip_formats[MVC_PAD_SOURCE]->code; + + mchp_gvpc->formats[MVC_PAD_SOURCE] = *format; +} + +static int mchp_gvpc_parse_of(struct mchp_gvpc_device *mchp_gvpc) +{ + struct device *dev = mchp_gvpc->dev; + struct device_node *node = mchp_gvpc->dev->of_node; + struct device_node *ports; + struct device_node *port; + u32 port_id; + int ret; + + ports = of_get_child_by_name(node, "ports"); + if (!ports) + ports = node; + + for_each_child_of_node(ports, port) { + if (port->name && (of_node_cmp(port->name, "port") == 0)) { + const struct mvideo_format *vip_format; + + vip_format = mvc_of_get_format(port); + if (IS_ERR(vip_format)) + return dev_err_probe(dev, IS_ERR(vip_format), + "invalid format in DT"); + + ret = of_property_read_u32(port, "reg", &port_id); + if (ret < 0) { + dev_err(dev, "no reg in DT"); + return ret; + } + + if (port_id != 0 && port_id != 1) { + dev_err(dev, "invalid reg in DT"); + return -EINVAL; + } + + mchp_gvpc->vip_formats[port_id] = vip_format; + } + } + + return 0; +} + +static int mchp_gvpc_probe(struct platform_device *pdev) +{ + struct v4l2_subdev *subdev; + struct mchp_gvpc_device *mchp_gvpc; + struct resource *res; + int ret; + + mchp_gvpc = devm_kzalloc(&pdev->dev, sizeof(*mchp_gvpc), GFP_KERNEL); + if (!mchp_gvpc) + return -ENOMEM; + + mchp_gvpc->dev = &pdev->dev; + + ret = mchp_gvpc_parse_of(mchp_gvpc); + if (ret < 0) + return ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mchp_gvpc->iomem = devm_ioremap_resource(mchp_gvpc->dev, res); + if (IS_ERR(mchp_gvpc->iomem)) + return dev_err_probe(&pdev->dev, PTR_ERR(mchp_gvpc->iomem), + "could not get mem resource\n"); + + /* Initialize V4L2 subdevice and media entity */ + subdev = &mchp_gvpc->subdev; + v4l2_subdev_init(subdev, &mchp_gvpc_ops); + subdev->dev = &pdev->dev; + subdev->internal_ops = &mchp_gvpc_internal_ops; + strscpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name)); + v4l2_set_subdevdata(subdev, mchp_gvpc); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + mchp_gvpc_init_formats(mchp_gvpc); + + mchp_gvpc->pads[MVC_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + mchp_gvpc->pads[MVC_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + subdev->entity.ops = &mchp_gvpc_media_ops; + ret = media_entity_pads_init(&subdev->entity, 2, mchp_gvpc->pads); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, mchp_gvpc); + + ret = v4l2_async_register_subdev(subdev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register subdev\n"); + goto error; + } + + return 0; + +error: + media_entity_cleanup(&subdev->entity); + return ret; +} + +static int mchp_gvpc_remove(struct platform_device *pdev) +{ + struct mchp_gvpc_device *mchp_gvpc = platform_get_drvdata(pdev); + struct v4l2_subdev *subdev = &mchp_gvpc->subdev; + + v4l2_async_unregister_subdev(subdev); + media_entity_cleanup(&subdev->entity); + + return 0; +} + +static const struct of_device_id mchp_gvpc_of_id_table[] = { + { .compatible = "microchip,generic-video-pipeline-connector" }, + { } +}; +MODULE_DEVICE_TABLE(of, mchp_gvpc_of_id_table); + +static struct platform_driver mchp_gvpc_driver = { + .driver = { + .name = "microchip-gvpc", + .of_match_table = mchp_gvpc_of_id_table, + }, + .probe = mchp_gvpc_probe, + .remove = mchp_gvpc_remove, +}; + +module_platform_driver(mchp_gvpc_driver); + +MODULE_AUTHOR("Shravan Chippa <shravan.chippa@microchip.com>"); +MODULE_DESCRIPTION("Microchip Generic Video Pipeline Connector Driver"); +MODULE_LICENSE("GPL"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/media/platform/microchip/microchip-image-enhancement.c linux-microchip/drivers/media/platform/microchip/microchip-image-enhancement.c --- linux-6.6.51/drivers/media/platform/microchip/microchip-image-enhancement.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/media/platform/microchip/microchip-image-enhancement.c 2024-11-26 14:02:37.610495703 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microchip Video Image Enhancement Driver. + * + * Copyright (C) 2021-2022 Microchip Technology Inc. and its subsidiaries + * Author: Shravan Chippa <shavan.chippa@microchip.com> + * + */ + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <media/v4l2-async.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> + +#include <dt-bindings/media/microchip-common.h> + +#include "microchip-common.h" + +#define MCHP_IMAGE_ENHANCEMENT_IP_VER 0x00 +#define MCHP_IMAGE_ENHANCEMENT_CTRL 0x04 +#define MCHP_IMAGE_ENHANCEMENT_CTRL_START BIT(0) +#define MCHP_IMAGE_ENHANCEMENT_CTRL_RESET BIT(1) + +#define MCHP_IMAGE_ENHANCEMENT_R_CONSTRAINT 0x08 +#define MCHP_IMAGE_ENHANCEMENT_G_CONSTRAINT 0x0C +#define MCHP_IMAGE_ENHANCEMENT_B_CONSTRAINT 0x10 +#define MCHP_IMAGE_ENHANCEMENT_SECOND_CONSTRAINT 0x14 +#define MCHP_IMAGE_ENHANCEMENT_RGB_SUM 0x18 + +#define MCHP_IMAGE_ENHANCEMENT_GAIN_CTL_DEFAULT 112 +#define MCHP_IMAGE_ENHANCEMENT_GAIN_CTL_MAX 255 +#define MCHP_IMAGE_ENHANCEMENT_CTL_MIN 0 +#define MCHP_IMAGE_ENHANCEMENT_CTL_MAX 255 +#define MCHP_IMAGE_ENHANCEMENT_CTL_STEP 1 + +#define MCHP_IMAGE_ENHANCEMENT_NUM_CTRLS 6 + +#define MCHP_IMAGE_ENHANCEMENT_DEF_FORMAT MVCF_RGB + +#define MCHP_IMAGE_ENHANCEMENT_RGB_SUM_MAX 0xF27CB22F +#define MCHP_IMAGE_ENHANCEMENT_RGB_SUM_MIN 0x100000 +#define MCHP_IMAGE_ENHANCEMENT_RGB_SUM_DEF 0x127CB22F + +/** + * struct mchp_image_enhancement - Microchip image enhancement device structure + * @dev: device + * @subdev: The v4l2 subdev structure + * @iomem: Base address of subsystem + * @pads: media pads + * @mutex : mutex lock for hardware reg access + * @formats: active V4L2 media bus formats at the sink and source pads + * @default_formats: default V4L2 media bus formats + * @vip_formats: format information corresponding to the pads active formats + * @ctrl_handler: control handler + */ +struct mchp_image_enhancement { + struct device *dev; + struct v4l2_subdev subdev; + void __iomem *iomem; + + struct media_pad pads[2]; + + struct mutex lock; + struct v4l2_mbus_framefmt formats[2]; + struct v4l2_mbus_framefmt default_formats[2]; + const struct mvideo_format *vip_formats[2]; + + struct v4l2_ctrl_handler ctrl_handler; + + int contrast; + int brightness; + int r_gain; + int b_gain; + int g_gain; +}; + +static inline struct mchp_image_enhancement * +to_image_enhancement(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct mchp_image_enhancement, subdev); +} + +static inline u32 +mchp_image_enhancement_reg_read(struct mchp_image_enhancement *image_enhancement, + u32 addr) +{ + return ioread32(image_enhancement->iomem + addr); +} + +static inline void +mchp_image_enhancement_reg_write(struct mchp_image_enhancement *image_enhancement, + u32 addr, u32 value) +{ + iowrite32(value, image_enhancement->iomem + addr); +} + +static inline void +mchp_image_enhancement_clr(struct mchp_image_enhancement *image_enhancement, + u32 addr, u32 clr) +{ + u32 val; + + val = mchp_image_enhancement_reg_read(image_enhancement, addr) & ~clr; + mchp_image_enhancement_reg_write(image_enhancement, addr, val); +} + +static inline void +mchp_image_enhancement_set(struct mchp_image_enhancement *image_enhancement, + u32 addr, u32 set) +{ + u32 val; + + val = mchp_image_enhancement_reg_read(image_enhancement, addr) | set; + mchp_image_enhancement_reg_write(image_enhancement, addr, val); +} + +static int mchp_image_enhancement_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct mchp_image_enhancement *image_enhancement = to_image_enhancement(subdev); + int ret; + + if (!enable) { + mchp_image_enhancement_clr(image_enhancement, MCHP_IMAGE_ENHANCEMENT_CTRL, + MCHP_IMAGE_ENHANCEMENT_CTRL_START); + return 0; + } + + mchp_image_enhancement_set(image_enhancement, MCHP_IMAGE_ENHANCEMENT_CTRL, + MCHP_IMAGE_ENHANCEMENT_CTRL_RESET); + + ret = __v4l2_ctrl_handler_setup(&image_enhancement->ctrl_handler); + if (ret) + return ret; + + mchp_image_enhancement_set(image_enhancement, MCHP_IMAGE_ENHANCEMENT_CTRL, + MCHP_IMAGE_ENHANCEMENT_CTRL_START); + + return 0; +} + +static struct v4l2_mbus_framefmt * +__mchp_image_enhancement_get_pad_format(struct mchp_image_enhancement *image_enhancement, + struct v4l2_subdev_state *sd_state, + unsigned int pad, u32 which) +{ + struct v4l2_mbus_framefmt *format; + + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + format = v4l2_subdev_get_try_format(&image_enhancement->subdev, + sd_state, pad); + break; + case V4L2_SUBDEV_FORMAT_ACTIVE: + format = &image_enhancement->formats[pad]; + break; + default: + format = NULL; + break; + } + + return format; +} + +static int mchp_image_enhancement_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct mchp_image_enhancement *image_enhancement = to_image_enhancement(subdev); + struct v4l2_mbus_framefmt *format; + + format = __mchp_image_enhancement_get_pad_format(image_enhancement, sd_state, + fmt->pad, fmt->which); + if (!format) + return -EINVAL; + + fmt->format = *format; + + return 0; +} + +static int mchp_image_enhancement_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct mchp_image_enhancement *image_enhancement = to_image_enhancement(subdev); + struct v4l2_mbus_framefmt *format; + + format = __mchp_image_enhancement_get_pad_format(image_enhancement, sd_state, + fmt->pad, fmt->which); + if (!format) + return -EINVAL; + + if (fmt->pad == MVC_PAD_SOURCE) { + fmt->format = *format; + return 0; + } + + mvc_set_format_size(format, fmt); + + fmt->format = *format; + + format = __mchp_image_enhancement_get_pad_format(image_enhancement, sd_state, + MVC_PAD_SOURCE, + fmt->which); + if (!format) + return -EINVAL; + + mvc_set_format_size(format, fmt); + + return 0; +} + +static int mchp_image_enhancement_open(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh) +{ + struct mchp_image_enhancement *image_enhancement = to_image_enhancement(subdev); + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_get_try_format(subdev, fh->state, MVC_PAD_SINK); + *format = image_enhancement->default_formats[MVC_PAD_SINK]; + + format = v4l2_subdev_get_try_format(subdev, fh->state, MVC_PAD_SOURCE); + *format = image_enhancement->default_formats[MVC_PAD_SOURCE]; + + return 0; +} + +static int mchp_image_enhancement_close(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh) +{ + return 0; +} + +/* + * The calculated parameters are used by Image Enhancement IP UG0646 running + * in the FPGA logic. The equations used for calculation are explained in + * UG0646 page number 2 in the below link + * + * (https://ww1.microchip.com/downloads/aemDocuments/documents/FPGA/ProductDocuments/UserGuides/microsemi_image_enhancement_ip_user_guide_ug0646_v3.pdf) + * + * V4l2 controls like brightness, contrast, r_gain, g_gain and b_gain are + * calulated based on two functions second_constraint_cal() and + * contrast_scale_cal() + */ + +static inline int second_constraint_cal(int brightness, int contrast_scale) +{ + return (128 * ((brightness) - ((128 * (contrast_scale)) / 10))); +} + +static inline int contrast_scale_cal(int contrast) +{ + return ((325 * (contrast + 128) / (387 - contrast)) >> 5u); +} + +static void mchp_image_enhancement_update_ctrls(struct mchp_image_enhancement *image_enhancement) +{ + u32 contrast_scale, second_constraint, r_gain_val, g_gain_val, b_gain_val; + + contrast_scale = contrast_scale_cal(image_enhancement->contrast); + second_constraint = second_constraint_cal(image_enhancement->brightness, + contrast_scale); + r_gain_val = ((image_enhancement->r_gain * contrast_scale) / 10); + g_gain_val = ((image_enhancement->g_gain * contrast_scale) / 10); + b_gain_val = ((image_enhancement->b_gain * contrast_scale) / 10); + + mutex_lock(&image_enhancement->lock); + + mchp_image_enhancement_reg_write(image_enhancement, + MCHP_IMAGE_ENHANCEMENT_R_CONSTRAINT, + r_gain_val); + + mchp_image_enhancement_reg_write(image_enhancement, + MCHP_IMAGE_ENHANCEMENT_G_CONSTRAINT, + g_gain_val); + + mchp_image_enhancement_reg_write(image_enhancement, + MCHP_IMAGE_ENHANCEMENT_B_CONSTRAINT, + b_gain_val); + + mchp_image_enhancement_reg_write(image_enhancement, + MCHP_IMAGE_ENHANCEMENT_SECOND_CONSTRAINT, + second_constraint); + mutex_unlock(&image_enhancement->lock); +} + +static int mchp_image_enhancement_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mchp_image_enhancement *image_enhancement = + container_of(ctrl->handler, struct mchp_image_enhancement, + ctrl_handler); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + image_enhancement->brightness = ctrl->val; + mchp_image_enhancement_update_ctrls(image_enhancement); + break; + case V4L2_CID_CONTRAST: + image_enhancement->contrast = ctrl->val; + mchp_image_enhancement_update_ctrls(image_enhancement); + break; + case MCHP_CID_RED_GAIN: + image_enhancement->r_gain = ctrl->val; + mchp_image_enhancement_update_ctrls(image_enhancement); + break; + case MCHP_CID_GREEN_GAIN: + image_enhancement->g_gain = ctrl->val; + mchp_image_enhancement_update_ctrls(image_enhancement); + break; + case MCHP_CID_BLUE_GAIN: + image_enhancement->b_gain = ctrl->val; + mchp_image_enhancement_update_ctrls(image_enhancement); + break; + case MCHP_CID_RGB_SUM: + ctrl->val = mchp_image_enhancement_reg_read(image_enhancement, + MCHP_IMAGE_ENHANCEMENT_RGB_SUM); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ctrl_ops mchp_image_enhancement_ctrl_ops = { + .s_ctrl = mchp_image_enhancement_s_ctrl, +}; + +static const struct v4l2_subdev_video_ops mchp_image_enhancement_video_ops = { + .s_stream = mchp_image_enhancement_s_stream, +}; + +static const struct v4l2_subdev_pad_ops mchp_image_enhancement_pad_ops = { + .enum_mbus_code = mvc_enum_mbus_code, + .get_fmt = mchp_image_enhancement_get_format, + .set_fmt = mchp_image_enhancement_set_format, + .link_validate = v4l2_subdev_link_validate_default, +}; + +static const struct v4l2_subdev_ops mchp_image_enhancement_ops = { + .video = &mchp_image_enhancement_video_ops, + .pad = &mchp_image_enhancement_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops mchp_image_enhancement_internal_ops = { + .open = mchp_image_enhancement_open, + .close = mchp_image_enhancement_close, +}; + +static const struct media_entity_operations mchp_image_enhancement_media_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static struct v4l2_ctrl_config mchp_image_enhancement_ctrls[] = { + { + .ops = &mchp_image_enhancement_ctrl_ops, + .id = MCHP_CID_RED_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Red", + .min = MCHP_IMAGE_ENHANCEMENT_CTL_MIN, + .max = MCHP_IMAGE_ENHANCEMENT_GAIN_CTL_MAX, + .def = MCHP_IMAGE_ENHANCEMENT_GAIN_CTL_DEFAULT, + .step = MCHP_IMAGE_ENHANCEMENT_CTL_STEP, + }, { + .ops = &mchp_image_enhancement_ctrl_ops, + .id = MCHP_CID_GREEN_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Green", + .min = MCHP_IMAGE_ENHANCEMENT_CTL_MIN, + .max = MCHP_IMAGE_ENHANCEMENT_GAIN_CTL_MAX, + .def = MCHP_IMAGE_ENHANCEMENT_GAIN_CTL_DEFAULT, + .step = MCHP_IMAGE_ENHANCEMENT_CTL_STEP, + }, { + .ops = &mchp_image_enhancement_ctrl_ops, + .id = MCHP_CID_BLUE_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Blue", + .min = MCHP_IMAGE_ENHANCEMENT_CTL_MIN, + .max = MCHP_IMAGE_ENHANCEMENT_GAIN_CTL_MAX, + .def = MCHP_IMAGE_ENHANCEMENT_GAIN_CTL_DEFAULT, + .step = MCHP_IMAGE_ENHANCEMENT_CTL_STEP, + }, { + .ops = &mchp_image_enhancement_ctrl_ops, + .id = MCHP_CID_RGB_SUM, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Rgb, Sum", + .min = MCHP_IMAGE_ENHANCEMENT_RGB_SUM_MIN, + .max = MCHP_IMAGE_ENHANCEMENT_RGB_SUM_MAX, + .def = MCHP_IMAGE_ENHANCEMENT_RGB_SUM_DEF, + .step = MCHP_IMAGE_ENHANCEMENT_CTL_STEP, + }, +}; + +static int mchp_image_enhancement_init_controls(struct mchp_image_enhancement *image_enhancement) +{ + struct v4l2_ctrl_handler *ctrl_hdlr = &image_enhancement->ctrl_handler; + int ret; + + ret = v4l2_ctrl_handler_init(ctrl_hdlr, MCHP_IMAGE_ENHANCEMENT_NUM_CTRLS); + if (ret) + return ret; + + v4l2_ctrl_new_std(ctrl_hdlr, &mchp_image_enhancement_ctrl_ops, + V4L2_CID_BRIGHTNESS, MCHP_IMAGE_ENHANCEMENT_CTL_MIN, + MCHP_IMAGE_ENHANCEMENT_CTL_MAX, MCHP_IMAGE_ENHANCEMENT_CTL_STEP, + MCHP_IMAGE_ENHANCEMENT_CTL_MAX / 2); + + v4l2_ctrl_new_std(ctrl_hdlr, &mchp_image_enhancement_ctrl_ops, + V4L2_CID_CONTRAST, MCHP_IMAGE_ENHANCEMENT_CTL_MIN, + MCHP_IMAGE_ENHANCEMENT_CTL_MAX, MCHP_IMAGE_ENHANCEMENT_CTL_STEP, + MCHP_IMAGE_ENHANCEMENT_CTL_MAX / 2); + + v4l2_ctrl_new_custom(ctrl_hdlr, &mchp_image_enhancement_ctrls[0], NULL); + v4l2_ctrl_new_custom(ctrl_hdlr, &mchp_image_enhancement_ctrls[1], NULL); + v4l2_ctrl_new_custom(ctrl_hdlr, &mchp_image_enhancement_ctrls[2], NULL); + v4l2_ctrl_new_custom(ctrl_hdlr, &mchp_image_enhancement_ctrls[3], NULL); + + if (ctrl_hdlr->error) { + dev_err(image_enhancement->dev, "control init failed: %d", + ctrl_hdlr->error); + v4l2_ctrl_handler_free(ctrl_hdlr); + return ctrl_hdlr->error; + } + + image_enhancement->subdev.ctrl_handler = ctrl_hdlr; + + return 0; +} + +static int mchp_image_enhancement_init_formats(struct mchp_image_enhancement *image_enhancement) +{ + struct device *dev = image_enhancement->dev; + struct v4l2_mbus_framefmt *format; + const struct mvideo_format *vip_format; + + vip_format = mvc_get_format_by_vf_code(MCHP_IMAGE_ENHANCEMENT_DEF_FORMAT); + if (IS_ERR(vip_format)) + return dev_err_probe(dev, PTR_ERR(vip_format), "invalid format"); + + image_enhancement->vip_formats[MVC_PAD_SINK] = vip_format; + image_enhancement->vip_formats[MVC_PAD_SOURCE] = vip_format; + + /* Initialize default and active formats */ + format = &image_enhancement->default_formats[MVC_PAD_SINK]; + format->code = image_enhancement->vip_formats[MVC_PAD_SINK]->code; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + format->width = MVC_MAX_WIDTH; + format->height = MVC_MAX_HEIGHT; + + image_enhancement->formats[MVC_PAD_SINK] = *format; + + format = &image_enhancement->default_formats[MVC_PAD_SOURCE]; + *format = image_enhancement->default_formats[MVC_PAD_SINK]; + format->code = image_enhancement->vip_formats[MVC_PAD_SOURCE]->code; + + image_enhancement->formats[MVC_PAD_SOURCE] = *format; + + return 0; +} + +static int mchp_image_enhancement_probe(struct platform_device *pdev) +{ + struct v4l2_subdev *subdev; + struct mchp_image_enhancement *image_enhancement; + int ret; + + image_enhancement = devm_kzalloc(&pdev->dev, sizeof(*image_enhancement), GFP_KERNEL); + if (!image_enhancement) + return -ENOMEM; + + image_enhancement->dev = &pdev->dev; + + image_enhancement->iomem = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(image_enhancement->iomem)) + return PTR_ERR(image_enhancement->iomem); + + mutex_init(&image_enhancement->lock); + + /* Initialize V4L2 subdevice and media entity */ + subdev = &image_enhancement->subdev; + v4l2_subdev_init(subdev, &mchp_image_enhancement_ops); + subdev->dev = &pdev->dev; + subdev->internal_ops = &mchp_image_enhancement_internal_ops; + strscpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name)); + v4l2_set_subdevdata(subdev, image_enhancement); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + ret = mchp_image_enhancement_init_formats(image_enhancement); + if (ret < 0) + return ret; + + mchp_image_enhancement_init_controls(image_enhancement); + + mchp_image_enhancement_set(image_enhancement, MCHP_IMAGE_ENHANCEMENT_CTRL, + MCHP_IMAGE_ENHANCEMENT_CTRL_RESET); + + image_enhancement->pads[MVC_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + image_enhancement->pads[MVC_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + subdev->entity.ops = &mchp_image_enhancement_media_ops; + ret = media_entity_pads_init(&subdev->entity, 2, image_enhancement->pads); + if (ret < 0) + goto error; + + platform_set_drvdata(pdev, image_enhancement); + + ret = v4l2_async_register_subdev(subdev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register subdev\n"); + goto error_media; + } + + return 0; + +error_media: + media_entity_cleanup(&subdev->entity); +error: + v4l2_ctrl_handler_free(&image_enhancement->ctrl_handler); + + return ret; +} + +static int mchp_image_enhancement_remove(struct platform_device *pdev) +{ + struct mchp_image_enhancement *image_enhancement = platform_get_drvdata(pdev); + struct v4l2_subdev *subdev = &image_enhancement->subdev; + + v4l2_async_unregister_subdev(subdev); + v4l2_ctrl_handler_free(&image_enhancement->ctrl_handler); + media_entity_cleanup(&subdev->entity); + + return 0; +} + +static const struct of_device_id mchp_image_enhancement_of_id_table[] = { + { .compatible = "microchip,image-enhancement-rtl-v1" }, + { } +}; +MODULE_DEVICE_TABLE(of, mchp_image_enhancement_of_id_table); + +static struct platform_driver mchp_image_enhancement_driver = { + .driver = { + .name = "microchip-image-enhancement", + .of_match_table = mchp_image_enhancement_of_id_table, + }, + .probe = mchp_image_enhancement_probe, + .remove = mchp_image_enhancement_remove, +}; + +module_platform_driver(mchp_image_enhancement_driver); + +MODULE_AUTHOR("Shravan Chippa <shravan.chippa@microchip.com>"); +MODULE_DESCRIPTION("Microchip Video Image Enhancement Driver"); +MODULE_LICENSE("GPL"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/media/platform/microchip/microchip-mipi-csi2rx.c linux-microchip/drivers/media/platform/microchip/microchip-mipi-csi2rx.c --- linux-6.6.51/drivers/media/platform/microchip/microchip-mipi-csi2rx.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/media/platform/microchip/microchip-mipi-csi2rx.c 2024-11-26 14:02:37.610495703 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Microchip MIPI CSI-2 Rx + * + * Copyright (C) 2023 Microchip Technology Inc. and its subsidiaries + * Author: Shravan Chippa <shavan.chippa@microchip.com> + * + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <linux/v4l2-subdev.h> +#include <media/media-entity.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> +#include <media/mipi-csi2.h> +#include "microchip-common.h" + +#define MIPI_CSI_MEDIA_PADS 2 +#define MIPI_CSI_DEFAULT_WIDTH 1920 +#define MIPI_CSI_DEFAULT_HEIGHT 1080 + +#define MIPI_CSI_MEDIA_PADS 2 + +#define MIPI_CSI_IP_VER 0x00 + +#define MIPI_CSI_CTRL 0x04 +#define MIPI_CSI_CTRL_START BIT(0) +#define MIPI_CSI_CTRL_RESET BIT(1) + +#define MIPI_CSI_LANE_CONFIG 0x08 +#define MIPI_CSI_LANE_CONFIG_VAL(x) ((x) >> 4) + +#define MIPI_CSI_DATA_WIDTH 0x0C +#define MIPI_CSI_NO_OF_PIXELS 0x10 +#define MIPI_CSI_NO_OF_VC 0x14 +#define MIPI_CSI_INPUT_DATA_INVERT 0x18 +#define MIPI_CSI_FIFO_SIZE 0x1C +#define MIPI_CSI_FRAME_RESOLUTION 0x20 +#define MIPI_CSI_FRAME_WIDTH_MASK GENMASK(31, 16) +#define MIPI_CSI_FRAME_HIGHT_MASK GENMASK(15, 0) + +#define MIPI_CSI_GLOBAL_INTERRUPT 0x24 +#define MIPI_CSI_GLOBAL_IRQ_EN BIT(0) + +#define MIPI_CSI_INTERRUPT_STATUS 0x28 +#define MIPI_CSI_ISR_FR BIT(0) +#define MIPI_CSI_ISR_ACTIVE_LANE_ERR BIT(1) +#define MIPI_CSI_ISR_SOT_ERR BIT(2) +#define MIPI_CSI_ISR_SKEW_ERR BIT(3) +#define MIPI_CSI_ISR_ECC_ERR BIT(4) +#define MIPI_CSI_ISR_CRC_ERR BIT(5) +#define MIPI_CSI_ISR_DATAID_ERR BIT(6) + +#define MIPI_CSI_INTERRUPT_EN 0x2C +#define MIPI_CSI_INTERRUPT_EN_MASK 0x7F + +#define MIPI_CSI_CLK_STATUS 0x30 +#define MIPI_CSI_CLK_STATUS_STOPED 0x00 +#define MIPI_CSI_CLK_STATUS_RUNNING 0x01 + +#define MIPI_CSI_DATA_LANE_STATUS 0x34 +#define MIPI_CSI_DATA_LANE_STATUS_CLR 0x54 +#define MIPI_CSI_WORD_COUNT 0x58 +#define MIPI_CSI_CAM_DATA_TYPE 0x5C +#define MIPI_CSI_CAM_LANES_CONFIG 0x60 + +/** + * struct mipi_csi2rx_event - Event log structure + * @mask: Event mask + * @name: Name of the event + */ +struct mipi_csi2rx_event { + u32 mask; + const char *name; +}; + +static const struct mipi_csi2rx_event mipi_csi2rx_events[] = { + { MIPI_CSI_ISR_FR, "Frame Received" }, + { MIPI_CSI_ISR_ACTIVE_LANE_ERR, "Active lane mismatch Error" }, + { MIPI_CSI_ISR_SOT_ERR, "Start-of-Transmission Error" }, + { MIPI_CSI_ISR_SKEW_ERR, "IOD training failure error" }, + { MIPI_CSI_ISR_ECC_ERR, "ECC Error" }, + { MIPI_CSI_ISR_CRC_ERR, "CRC Error" }, + { MIPI_CSI_ISR_DATAID_ERR, "Data Id Error" }, +}; + +#define MIPI_CSI_NUM_EVENTS ARRAY_SIZE(mipi_csi2rx_events) +#define MIPI_CSI2_MAX_RAW_FORMATS 4 + +/* + * This table provides a mapping between CSI-2 Data type + * and media bus formats + */ +static const u32 mipi_csi2dt_mbus_lut[][2] = { + { MIPI_CSI2_DT_RAW8, MEDIA_BUS_FMT_SRGGB8_1X8 }, + { MIPI_CSI2_DT_RAW8, MEDIA_BUS_FMT_SBGGR8_1X8 }, + { MIPI_CSI2_DT_RAW8, MEDIA_BUS_FMT_SGBRG8_1X8 }, + { MIPI_CSI2_DT_RAW8, MEDIA_BUS_FMT_SGRBG8_1X8 }, + { MIPI_CSI2_DT_RAW10, MEDIA_BUS_FMT_SRGGB10_1X10 }, + { MIPI_CSI2_DT_RAW10, MEDIA_BUS_FMT_SBGGR10_1X10 }, + { MIPI_CSI2_DT_RAW10, MEDIA_BUS_FMT_SGBRG10_1X10 }, + { MIPI_CSI2_DT_RAW10, MEDIA_BUS_FMT_SGRBG10_1X10 }, + { MIPI_CSI2_DT_RAW12, MEDIA_BUS_FMT_SRGGB12_1X12 }, + { MIPI_CSI2_DT_RAW12, MEDIA_BUS_FMT_SBGGR12_1X12 }, + { MIPI_CSI2_DT_RAW12, MEDIA_BUS_FMT_SGBRG12_1X12 }, + { MIPI_CSI2_DT_RAW12, MEDIA_BUS_FMT_SGRBG12_1X12 }, + { MIPI_CSI2_DT_RAW16, MEDIA_BUS_FMT_SRGGB16_1X16 }, + { MIPI_CSI2_DT_RAW16, MEDIA_BUS_FMT_SBGGR16_1X16 }, + { MIPI_CSI2_DT_RAW16, MEDIA_BUS_FMT_SGBRG16_1X16 }, + { MIPI_CSI2_DT_RAW16, MEDIA_BUS_FMT_SGRBG16_1X16 }, + { MIPI_CSI2_DT_RGB888, MEDIA_BUS_FMT_RGB888_1X24 }, + { MIPI_CSI2_DT_RAW20, 0 }, +}; + +/** + * struct mipi_csi2rx_state - CSI-2 Rx Subsystem device structure + * @subdev: The v4l2 subdev structure + * @format: Active V4L2 formats on each pad + * @default_format: Default V4L2 format + * @events: counter for events + * @dev: Platform structure + * @clks: array of clocks + * @iomem: Base address of subsystem + * @max_num_lanes: Maximum number of lanes present + * @datatype: Data type filter + * @lock: mutex for accessing this structure + * @pads: media pads + * @streaming: Flag for storing streaming state + * @csi_fixed_out_raw8: If out put format is fixed to raw8 + * + * This structure contains the device driver related parameters + */ +struct mipi_csi2rx_state { + struct v4l2_subdev subdev; + struct v4l2_mbus_framefmt format[2]; + struct v4l2_mbus_framefmt default_format[2]; + struct device *dev; + struct clk_bulk_data *clks; + void __iomem *iomem; + u32 max_num_lanes; + u32 datatype; + u32 events[MIPI_CSI_NUM_EVENTS]; + /* used to protect access to this struct */ + struct mutex lock; + struct media_pad pads[MIPI_CSI_MEDIA_PADS]; + bool streaming; + bool csi_fixed_out_raw8; +}; + +static const struct clk_bulk_data mipi_csi2rx_clks[] = { + { .id = "axi" }, + { .id = "video" }, +}; + +static inline struct mipi_csi2rx_state * +to_csi2rxstate(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct mipi_csi2rx_state, subdev); +} + +/* + * Register related operations + */ +static inline u32 mipi_csi2rx_read(struct mipi_csi2rx_state *csi2rx, u32 addr) +{ + return ioread32(csi2rx->iomem + addr); +} + +static inline void mipi_csi2rx_write(struct mipi_csi2rx_state *csi2rx, u32 addr, + u32 value) +{ + iowrite32(value, csi2rx->iomem + addr); +} + +static inline void mipi_csi2rx_clr(struct mipi_csi2rx_state *csi2rx, u32 addr, + u32 clr) +{ + mipi_csi2rx_write(csi2rx, addr, + mipi_csi2rx_read(csi2rx, addr) & ~clr); +} + +static inline void mipi_csi2rx_set(struct mipi_csi2rx_state *csi2rx, u32 addr, + u32 set) +{ + mipi_csi2rx_write(csi2rx, addr, mipi_csi2rx_read(csi2rx, addr) | set); +} + +static u32 mipi_csi2rx_get_nth_mbus(u32 dt, u32 n) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mipi_csi2dt_mbus_lut); i++) { + if (mipi_csi2dt_mbus_lut[i][0] == dt) { + if (n-- == 0) + return mipi_csi2dt_mbus_lut[i][1]; + } + } + + return 0; +} + +static u32 mipi_csi2rx_get_dt(u32 mbus) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mipi_csi2dt_mbus_lut); i++) { + if (mipi_csi2dt_mbus_lut[i][1] == mbus) + return mipi_csi2dt_mbus_lut[i][0]; + } + + return 0; +} + +static int mipi_csi2rx_soft_reset(struct mipi_csi2rx_state *state) +{ + mipi_csi2rx_set(state, MIPI_CSI_CTRL, MIPI_CSI_CTRL_RESET); + + return 0; +} + +static void mipi_csi2rx_reset_event_counters(struct mipi_csi2rx_state *state) +{ + unsigned int i; + + for (i = 0; i < MIPI_CSI_NUM_EVENTS; i++) + state->events[i] = 0; +} + +static void mipi_csi2rx_log_counters(struct mipi_csi2rx_state *state) +{ + struct device *dev = state->dev; + unsigned int i; + + for (i = 0; i < MIPI_CSI_NUM_EVENTS; i++) { + if (state->events[i] > 0) { + dev_info(dev, "%s events: %d\n", + mipi_csi2rx_events[i].name, + state->events[i]); + } + } +} + +static int mipi_csi2rx_log_status(struct v4l2_subdev *sd) +{ + struct mipi_csi2rx_state *csi2rx = to_csi2rxstate(sd); + struct device *dev = csi2rx->dev; + u32 data; + + mutex_lock(&csi2rx->lock); + + mipi_csi2rx_log_counters(csi2rx); + + dev_info(dev, "***** Core Status *****\n"); + data = mipi_csi2rx_read(csi2rx, MIPI_CSI_CTRL); + dev_info(dev, "Core Status is %s\n", + data & MIPI_CSI_CTRL_START ? "enable" : "disable"); + data = mipi_csi2rx_read(csi2rx, MIPI_CSI_GLOBAL_INTERRUPT); + dev_info(dev, "MIPI_CSI_GLOBAL_IRQ_EN is %s\n", + data & MIPI_CSI_GLOBAL_IRQ_EN ? "enable" : "disable"); + + data = mipi_csi2rx_read(csi2rx, MIPI_CSI_INTERRUPT_EN); + dev_info(dev, "MIPI_CSI_INTERRUPT_EN value = %x\n", data); + + data = mipi_csi2rx_read(csi2rx, MIPI_CSI_LANE_CONFIG); + dev_info(dev, "No of Lanes configured = %x\n", (data & 0xF)); + dev_info(dev, "Max Lanes supported = %x\n", (data >> 4)); + + data = mipi_csi2rx_read(csi2rx, MIPI_CSI_DATA_WIDTH); + dev_info(dev, "DATA_WIDTH configured = %x\n", data); + + data = mipi_csi2rx_read(csi2rx, MIPI_CSI_NO_OF_PIXELS); + dev_info(dev, "No of output pixels per pixel clock = %x\n", data); + + data = mipi_csi2rx_read(csi2rx, MIPI_CSI_NO_OF_VC); + dev_info(dev, "No of Virtual channels supported = %x\n", data & 0xF); + dev_info(dev, "Max Virtual channels supported = %x\n", data >> 4); + + data = mipi_csi2rx_read(csi2rx, MIPI_CSI_INPUT_DATA_INVERT); + dev_info(dev, "Input data is inverted = %s\n", data ? "true" : "false"); + + data = mipi_csi2rx_read(csi2rx, MIPI_CSI_FIFO_SIZE); + dev_info(dev, "FIFO size = %x\n", data); + + data = mipi_csi2rx_read(csi2rx, MIPI_CSI_CLK_STATUS); + dev_info(dev, "MIPI clock state = %s\n", data ? "Running" : "Stopped"); + + data = mipi_csi2rx_read(csi2rx, MIPI_CSI_WORD_COUNT); + dev_info(dev, "total packet recvied = %x\n", data); + + data = mipi_csi2rx_read(csi2rx, MIPI_CSI_CAM_DATA_TYPE); + dev_info(dev, "MIPI_CAM_DATA_TYPE = %x\n", data); + + data = mipi_csi2rx_read(csi2rx, MIPI_CSI_CAM_LANES_CONFIG); + dev_info(dev, "MIPI_CAM_LANES_CONFIG = %x\n", data); + + mutex_unlock(&csi2rx->lock); + + return 0; +} + +static int mipi_csi2rx_start_stream(struct mipi_csi2rx_state *state) +{ + int ret = 0; + + ret = mipi_csi2rx_soft_reset(state); + if (ret) { + state->streaming = false; + return ret; + } + + mipi_csi2rx_set(state, MIPI_CSI_GLOBAL_INTERRUPT, + MIPI_CSI_GLOBAL_IRQ_EN); + mipi_csi2rx_set(state, MIPI_CSI_INTERRUPT_EN, + MIPI_CSI_INTERRUPT_EN_MASK); + mipi_csi2rx_set(state, MIPI_CSI_CTRL, MIPI_CSI_CTRL_START); + + state->streaming = true; + + return ret; +} + +static void mipi_csi2rx_stop_stream(struct mipi_csi2rx_state *state) +{ + mipi_csi2rx_clr(state, MIPI_CSI_GLOBAL_INTERRUPT, MIPI_CSI_GLOBAL_IRQ_EN); + mipi_csi2rx_clr(state, MIPI_CSI_INTERRUPT_EN, MIPI_CSI_INTERRUPT_EN_MASK); + mipi_csi2rx_clr(state, MIPI_CSI_CTRL, MIPI_CSI_CTRL_START); + + state->streaming = false; +} + +static irqreturn_t mipi_csi2rx_irq_handler(int irq, void *data) +{ + struct mipi_csi2rx_state *state = (struct mipi_csi2rx_state *)data; + struct device *dev = state->dev; + u32 status; + + status = mipi_csi2rx_read(state, MIPI_CSI_INTERRUPT_STATUS); + mipi_csi2rx_set(state, MIPI_CSI_INTERRUPT_STATUS, status); + + if (status) { + unsigned int i; + + for (i = 0; i < MIPI_CSI_NUM_EVENTS; i++) { + if (!(status & mipi_csi2rx_events[i].mask)) + continue; + state->events[i]++; + dev_dbg_ratelimited(dev, "%s: %u\n", + mipi_csi2rx_events[i].name, + state->events[i]); + } + } + + return IRQ_HANDLED; +} + +static int mipi_csi2rx_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct mipi_csi2rx_state *csi2rx = to_csi2rxstate(sd); + int ret = 0; + + mutex_lock(&csi2rx->lock); + + if (enable == csi2rx->streaming) + goto stream_done; + + if (enable) { + mipi_csi2rx_reset_event_counters(csi2rx); + ret = mipi_csi2rx_start_stream(csi2rx); + } else { + mipi_csi2rx_stop_stream(csi2rx); + } + +stream_done: + mutex_unlock(&csi2rx->lock); + + return ret; +} + +static struct v4l2_mbus_framefmt * +__mipi_csi2rx_get_pad_format(struct mipi_csi2rx_state *csi2rx, + struct v4l2_subdev_state *sd_state, + unsigned int pad, u32 which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&csi2rx->subdev, + sd_state, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &csi2rx->format[pad]; + default: + return NULL; + } +} + +static int mipi_csi2rx_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + struct mipi_csi2rx_state *csi2rx = to_csi2rxstate(sd); + struct v4l2_mbus_framefmt *format; + unsigned int i; + + mutex_lock(&csi2rx->lock); + for (i = 0; i < MIPI_CSI_MEDIA_PADS; i++) { + format = v4l2_subdev_get_try_format(sd, sd_state, i); + *format = csi2rx->default_format[i]; + } + mutex_unlock(&csi2rx->lock); + + return 0; +} + +static int mipi_csi2rx_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct mipi_csi2rx_state *csi2rx = to_csi2rxstate(sd); + + mutex_lock(&csi2rx->lock); + fmt->format = *__mipi_csi2rx_get_pad_format(csi2rx, sd_state, + fmt->pad, + fmt->which); + mutex_unlock(&csi2rx->lock); + + return 0; +} + +static int mipi_csi2rx_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct mipi_csi2rx_state *csi2rx = to_csi2rxstate(sd); + struct v4l2_mbus_framefmt *__format, *__format_sync; + u32 dt; + + mutex_lock(&csi2rx->lock); + + __format = __mipi_csi2rx_get_pad_format(csi2rx, sd_state, + fmt->pad, fmt->which); + + if (fmt->pad == MVC_PAD_SOURCE) { + if (csi2rx->csi_fixed_out_raw8) { + dev_info(csi2rx->dev, "csi_fixed_out_raw8 is enabled"); + switch (__format->code) { + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SBGGR12_1X12: + case MEDIA_BUS_FMT_SBGGR14_1X14: + case MEDIA_BUS_FMT_SBGGR16_1X16: + __format->code = MEDIA_BUS_FMT_SBGGR8_1X8; + break; + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SGBRG14_1X14: + case MEDIA_BUS_FMT_SGBRG16_1X16: + __format->code = MEDIA_BUS_FMT_SGBRG8_1X8; + break; + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SGRBG14_1X14: + case MEDIA_BUS_FMT_SGRBG16_1X16: + __format->code = MEDIA_BUS_FMT_SGRBG8_1X8; + break; + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SRGGB12_1X12: + case MEDIA_BUS_FMT_SRGGB14_1X14: + case MEDIA_BUS_FMT_SRGGB16_1X16: + __format->code = MEDIA_BUS_FMT_SRGGB8_1X8; + break; + } + } + + __format_sync = __mipi_csi2rx_get_pad_format(csi2rx, sd_state, 0, fmt->which); + + __format->width = __format_sync->width; + __format->height = __format_sync->height; + fmt->format = *__format; + + mutex_unlock(&csi2rx->lock); + + return 0; + } + + dt = mipi_csi2rx_get_dt(fmt->format.code); + if (dt != csi2rx->datatype && dt != MIPI_CSI2_DT_RAW8) { + dev_info(csi2rx->dev, "Unsupported media bus format"); + /* set the default format for the data type */ + fmt->format.code = mipi_csi2rx_get_nth_mbus(csi2rx->datatype, + 0); + } + + *__format = fmt->format; + mutex_unlock(&csi2rx->lock); + + return 0; +} + +static int mipi_csi2rx_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct mipi_csi2rx_state *state = to_csi2rxstate(sd); + u32 dt, n; + int ret = 0; + + if (code->index < MIPI_CSI2_MAX_RAW_FORMATS) { + n = code->index; + dt = MIPI_CSI2_DT_RAW8; + } else if (state->datatype != MIPI_CSI2_DT_RAW8) { + n = code->index - MIPI_CSI2_MAX_RAW_FORMATS; + dt = state->datatype; + } else { + return -EINVAL; + } + + code->code = mipi_csi2rx_get_nth_mbus(dt, n); + if (!code->code) + ret = -EINVAL; + + return ret; +} + +/* ----------------------------------------------------------------------------- + * Media Operations + */ + +static const struct media_entity_operations mipi_csi2rx_media_ops = { + .link_validate = v4l2_subdev_link_validate +}; + +static const struct v4l2_subdev_core_ops mipi_csi2rx_core_ops = { + .log_status = mipi_csi2rx_log_status, +}; + +static const struct v4l2_subdev_video_ops mipi_csi2rx_video_ops = { + .s_stream = mipi_csi2rx_s_stream +}; + +static const struct v4l2_subdev_pad_ops mipi_csi2rx_pad_ops = { + .init_cfg = mipi_csi2rx_init_cfg, + .get_fmt = mipi_csi2rx_get_format, + .set_fmt = mipi_csi2rx_set_format, + .enum_mbus_code = mipi_csi2rx_enum_mbus_code, + .link_validate = v4l2_subdev_link_validate_default, +}; + +static const struct v4l2_subdev_ops mipi_csi2rx_ops = { + .core = &mipi_csi2rx_core_ops, + .video = &mipi_csi2rx_video_ops, + .pad = &mipi_csi2rx_pad_ops +}; + +static int mipi_csi2rx_parse_of(struct mipi_csi2rx_state *csi2rx) +{ + struct device *dev = csi2rx->dev; + struct device_node *node = dev->of_node; + + struct fwnode_handle *ep; + struct v4l2_fwnode_endpoint vep = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + int ret; + + ret = of_property_read_u32(node, "microchip,csi-pxl-format", + &csi2rx->datatype); + if (ret) + return dev_err_probe(dev, ret, + "missing microchip,csi-pxl-format property\n"); + + switch (csi2rx->datatype) { + case MIPI_CSI2_DT_RAW8: + case MIPI_CSI2_DT_RAW10: + case MIPI_CSI2_DT_RAW12: + case MIPI_CSI2_DT_RAW14: + case MIPI_CSI2_DT_RAW16: + csi2rx->csi_fixed_out_raw8 = + of_property_read_bool(node, "microchip,csi-fixed-out-raw8"); + break; + case MIPI_CSI2_DT_RGB888: + break; + default: + return dev_err_probe(dev, -EINVAL, "invalid csi-pxl-format property!\n"); + } + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + MVC_PAD_SINK, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!ep) + return dev_err_probe(dev, -EINVAL, "no sink port found"); + + ret = v4l2_fwnode_endpoint_parse(ep, &vep); + fwnode_handle_put(ep); + if (ret) + return dev_err_probe(dev, ret, "error parsing sink port"); + + dev_dbg(dev, "mipi number lanes = %d\n", + vep.bus.mipi_csi2.num_data_lanes); + + csi2rx->max_num_lanes = vep.bus.mipi_csi2.num_data_lanes; + + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), + MVC_PAD_SOURCE, 0, + FWNODE_GRAPH_ENDPOINT_NEXT); + if (!ep) + return dev_err_probe(dev, -EINVAL, "no source port found"); + + fwnode_handle_put(ep); + + dev_dbg(dev, "%u data lanes, data type 0x%02x\n", + csi2rx->max_num_lanes, + csi2rx->datatype); + + return 0; +} + +static int mipi_csi2rx_probe(struct platform_device *pdev) +{ + struct v4l2_subdev *subdev; + struct mipi_csi2rx_state *csi2rx; + int num_clks = ARRAY_SIZE(mipi_csi2rx_clks); + struct device *dev = &pdev->dev; + int irq, ret; + + csi2rx = devm_kzalloc(dev, sizeof(*csi2rx), GFP_KERNEL); + if (!csi2rx) + return -ENOMEM; + + csi2rx->dev = dev; + + csi2rx->clks = devm_kmemdup(dev, mipi_csi2rx_clks, + sizeof(mipi_csi2rx_clks), GFP_KERNEL); + if (!csi2rx->clks) + return -ENOMEM; + + ret = mipi_csi2rx_parse_of(csi2rx); + if (ret < 0) + return ret; + + csi2rx->iomem = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(csi2rx->iomem)) + return PTR_ERR(csi2rx->iomem); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(dev, irq, NULL, + mipi_csi2rx_irq_handler, IRQF_ONESHOT, + dev_name(dev), csi2rx); + if (ret) + return dev_err_probe(dev, ret, "Unable to allocate irq\n"); + + ret = clk_bulk_get(dev, num_clks, csi2rx->clks); + if (ret) + return ret; + + ret = clk_bulk_prepare_enable(num_clks, csi2rx->clks); + if (ret) + goto err_clk_put; + + mutex_init(&csi2rx->lock); + + mipi_csi2rx_soft_reset(csi2rx); + + csi2rx->pads[MVC_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + csi2rx->pads[MVC_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + /* Initialize the default format */ + csi2rx->default_format[MVC_PAD_SINK].code = + mipi_csi2rx_get_nth_mbus(csi2rx->datatype, 0); + csi2rx->default_format[MVC_PAD_SINK].field = V4L2_FIELD_NONE; + csi2rx->default_format[MVC_PAD_SINK].colorspace = V4L2_COLORSPACE_SRGB; + csi2rx->default_format[MVC_PAD_SINK].width = MIPI_CSI_DEFAULT_WIDTH; + csi2rx->default_format[MVC_PAD_SINK].height = MIPI_CSI_DEFAULT_HEIGHT; + csi2rx->format[MVC_PAD_SINK] = csi2rx->default_format[MVC_PAD_SINK]; + + csi2rx->default_format[MVC_PAD_SOURCE].code = + mipi_csi2rx_get_nth_mbus(csi2rx->datatype, 0); + csi2rx->default_format[MVC_PAD_SOURCE].field = V4L2_FIELD_NONE; + csi2rx->default_format[MVC_PAD_SOURCE].colorspace = V4L2_COLORSPACE_SRGB; + csi2rx->default_format[MVC_PAD_SOURCE].width = MIPI_CSI_DEFAULT_WIDTH; + csi2rx->default_format[MVC_PAD_SOURCE].height = MIPI_CSI_DEFAULT_HEIGHT; + csi2rx->format[MVC_PAD_SOURCE] = csi2rx->default_format[MVC_PAD_SOURCE]; + + /* Initialize V4L2 subdevice and media entity */ + subdev = &csi2rx->subdev; + v4l2_subdev_init(subdev, &mipi_csi2rx_ops); + subdev->dev = dev; + strscpy(subdev->name, dev_name(dev), sizeof(subdev->name)); + subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; + subdev->entity.ops = &mipi_csi2rx_media_ops; + v4l2_set_subdevdata(subdev, csi2rx); + + ret = media_entity_pads_init(&subdev->entity, MIPI_CSI_MEDIA_PADS, + csi2rx->pads); + if (ret < 0) + goto error; + + platform_set_drvdata(pdev, csi2rx); + + ret = v4l2_async_register_subdev(subdev); + if (ret < 0) { + dev_err(dev, "failed to register subdev\n"); + goto error; + } + + return 0; +error: + media_entity_cleanup(&subdev->entity); + mutex_destroy(&csi2rx->lock); + clk_bulk_disable_unprepare(num_clks, csi2rx->clks); +err_clk_put: + clk_bulk_put(num_clks, csi2rx->clks); + return ret; +} + +static int mipi_csi2rx_remove(struct platform_device *pdev) +{ + struct mipi_csi2rx_state *csi2rx = platform_get_drvdata(pdev); + struct v4l2_subdev *subdev = &csi2rx->subdev; + int num_clks = ARRAY_SIZE(mipi_csi2rx_clks); + + v4l2_async_unregister_subdev(subdev); + media_entity_cleanup(&subdev->entity); + mutex_destroy(&csi2rx->lock); + clk_bulk_disable_unprepare(num_clks, csi2rx->clks); + clk_bulk_put(num_clks, csi2rx->clks); + + return 0; +} + +static const struct of_device_id mipi_csi2rx_of_id_table[] = { + { .compatible = "microchip,mipi-csi2-rx-rtl-v0", }, + { } +}; + +MODULE_DEVICE_TABLE(of, mipi_csi2rx_of_id_table); + +static struct platform_driver mipi_csi2rx_driver = { + .driver = { + .name = "microchip-csi2rxss", + .of_match_table = mipi_csi2rx_of_id_table, + }, + .probe = mipi_csi2rx_probe, + .remove = mipi_csi2rx_remove, +}; + +module_platform_driver(mipi_csi2rx_driver); + +MODULE_AUTHOR("Shravan Chippa <shravan.chippa@microchip.com>"); +MODULE_DESCRIPTION("Microchip MIPI CSI-2 Rx Subsystem Driver"); +MODULE_LICENSE("GPL"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/media/platform/microchip/microchip-osd.c linux-microchip/drivers/media/platform/microchip/microchip-osd.c --- linux-6.6.51/drivers/media/platform/microchip/microchip-osd.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/media/platform/microchip/microchip-osd.c 2024-11-26 14:02:37.610495703 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microchip Video On Screen Display Driver. + * + * Copyright (C) 2021-2022 Microchip Technology Inc. and its subsidiaries + * Author: Shravan Chippa <shavan.chippa@microchip.com> + * + */ + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <media/v4l2-async.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> + +#include <dt-bindings/media/microchip-common.h> + +#include "microchip-common.h" + +#define MCHP_OSD_IP_VER 0x00 +#define MCHP_OSD_CTRL 0x04 +#define MCHP_OSD_CTRL_START BIT(0) +#define MCHP_OSD_CTRL_RESET BIT(1) + +#define MCHP_OSD_X_Y_POS 0x08 +#define MCHP_OSD_COLOR 0x0C +#define MCHP_OSD_NUM 0x10 +#define MCHP_OSD_H_RES 0x14 +#define MCHP_OSD_V_RES 0x18 + +/* Text overlay (On Screen Display) */ +#define MCHP_OSD_X_Y_POS_MAX 4096 +#define MCHP_OSD_X_Y_POS_MIN 4 +#define MCHP_OSD_X_Y_POS_DEFAULT 4 +#define MCHP_OSD_X_Y_POS_STEP 2 +#define MCHP_OSD_ENABLE_MAX 1 +#define MCHP_OSD_ENABLE_MIN 0 +#define MCHP_OSD_ENABLE_DEFAULT 1 +#define MCHP_OSD_ENABLE_STEP 1 +#define MCHP_OSD_DISABLE_MAX_PIX 0x2000 +#define MCHP_OSD_X_POS_SHIFT 16 +#define MCHP_OSD_COLOR_MAX 0xFFFFFF +#define MCHP_OSD_COLOR_MIN 0x000000 +#define MCHP_OSD_COLOR_DEFAULT 0xFFFFFF +#define MCHP_OSD_COLOR_STEP 1 +#define MCHP_OSD_NUM_MAX 200 +#define MCHP_OSD_NUM_MIN 1 +#define MCHP_OSD_NUM_DEFAULT 30 +#define MCHP_OSD_NUM_STEP 1 + +#define MCHP_OSD_NUM_CTRLS 5 + +#define MCHP_OSD_DEF_FORMAT MVCF_RGB + +/** + * struct mchp_osd_device - Microchip On Screen Display device structure + * @dev: device + * @subdev: The v4l2 subdev structure + * @iomem: Base address of subsystem + * @pads: media pads + * @formats: active V4L2 media bus formats at the sink and source pads + * @default_formats: default V4L2 media bus formats + * @vip_formats: format information corresponding to the pads active formats + * @ctrl_handler: control handler + * @horizontal_pos: overlay horizontal position + * @vertical_pos: overlay vertical position + */ +struct mchp_osd_device { + struct device *dev; + struct v4l2_subdev subdev; + void __iomem *iomem; + + struct media_pad pads[2]; + + struct v4l2_mbus_framefmt formats[2]; + struct v4l2_mbus_framefmt default_formats[2]; + const struct mvideo_format *vip_formats[2]; + + struct v4l2_ctrl_handler ctrl_handler; + + int horizontal_pos; + int vertical_pos; +}; + +static inline struct mchp_osd_device *to_osd(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct mchp_osd_device, subdev); +} + +static inline u32 mchp_osd_reg_read(struct mchp_osd_device *osd, + u32 addr) +{ + return readl(osd->iomem + addr); +} + +static inline void mchp_osd_reg_write(struct mchp_osd_device *osd, + u32 addr, u32 value) +{ + writel(value, osd->iomem + addr); +} + +static inline void mchp_osd_clr(struct mchp_osd_device *osd, + u32 addr, u32 clr) +{ + mchp_osd_reg_write(osd, addr, mchp_osd_reg_read(osd, addr) & ~clr); +} + +static inline void mchp_osd_set(struct mchp_osd_device *osd, + u32 addr, u32 set) +{ + mchp_osd_reg_write(osd, addr, mchp_osd_reg_read(osd, addr) | set); +} + +static int mchp_osd_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct mchp_osd_device *osd = to_osd(subdev); + struct v4l2_mbus_framefmt *format = &osd->formats[MVC_PAD_SINK]; + + if (!enable) { + mchp_osd_clr(osd, MCHP_OSD_CTRL, MCHP_OSD_CTRL_START); + return 0; + } + + mchp_osd_reg_write(osd, MCHP_OSD_V_RES, format->height); + mchp_osd_reg_write(osd, MCHP_OSD_H_RES, format->width); + + mchp_osd_set(osd, MCHP_OSD_CTRL, MCHP_OSD_CTRL_START); + + return 0; +} + +static struct v4l2_mbus_framefmt * +__mchp_osd_get_pad_format(struct mchp_osd_device *osd, + struct v4l2_subdev_state *sd_state, + unsigned int pad, u32 which) +{ + struct v4l2_mbus_framefmt *format; + + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + format = v4l2_subdev_get_try_format(&osd->subdev, + sd_state, pad); + break; + case V4L2_SUBDEV_FORMAT_ACTIVE: + format = &osd->formats[pad]; + break; + default: + format = NULL; + break; + } + + return format; +} + +static int mchp_osd_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct mchp_osd_device *osd = to_osd(subdev); + struct v4l2_mbus_framefmt *format; + + format = __mchp_osd_get_pad_format(osd, sd_state, + fmt->pad, fmt->which); + if (!format) + return -EINVAL; + + fmt->format = *format; + + return 0; +} + +static int mchp_osd_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct mchp_osd_device *osd = to_osd(subdev); + struct v4l2_mbus_framefmt *format; + + format = __mchp_osd_get_pad_format(osd, sd_state, fmt->pad, fmt->which); + if (!format) + return -EINVAL; + + if (fmt->pad == MVC_PAD_SOURCE) { + fmt->format = *format; + return 0; + } + + mvc_set_format_size(format, fmt); + + fmt->format = *format; + + format = __mchp_osd_get_pad_format(osd, sd_state, + MVC_PAD_SOURCE, fmt->which); + if (!format) + return -EINVAL; + + mvc_set_format_size(format, fmt); + + mchp_osd_reg_write(osd, MCHP_OSD_V_RES, format->height); + mchp_osd_reg_write(osd, MCHP_OSD_H_RES, format->width); + + return 0; +} + +static int mchp_osd_open(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh) +{ + struct mchp_osd_device *osd = to_osd(subdev); + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_get_try_format(subdev, fh->state, MVC_PAD_SINK); + *format = osd->default_formats[MVC_PAD_SINK]; + + format = v4l2_subdev_get_try_format(subdev, fh->state, MVC_PAD_SOURCE); + *format = osd->default_formats[MVC_PAD_SOURCE]; + + return 0; +} + +static int mchp_osd_close(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh) +{ + return 0; +} + +/* + * The compression ratio is calculated by the vDMA driver, but overlaid by + * the OSD FPGA logic. It is displayed on the streaming video, with the + * 4 least significant bits of MCHP_OSD_NUM containing the units, bits 4 to 7 + * containing the tens etc to be displayed. + */ + +static void mchp_dscmi_osd_text(struct mchp_osd_device *osd, + u32 compression_ratio) +{ + u32 compression_ratio_osd = ((compression_ratio % 10) | + ((compression_ratio / 10 % 10) << 4) | + ((compression_ratio / 100) << 8)); + + mchp_osd_reg_write(osd, MCHP_OSD_NUM, compression_ratio_osd); +} + +static int mchp_osd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mchp_osd_device *osd = + container_of(ctrl->handler, struct mchp_osd_device, + ctrl_handler); + u32 val; + + switch (ctrl->id) { + case MCHP_CID_OSD_X_POS: + osd->horizontal_pos = ctrl->val; + val = osd->vertical_pos | (osd->horizontal_pos << MCHP_OSD_X_POS_SHIFT); + mchp_osd_reg_write(osd, MCHP_OSD_X_Y_POS, val); + break; + case MCHP_CID_OSD_Y_POS: + osd->vertical_pos = ctrl->val; + val = osd->vertical_pos | (osd->horizontal_pos << MCHP_OSD_X_POS_SHIFT); + mchp_osd_reg_write(osd, MCHP_OSD_X_Y_POS, val); + break; + case MCHP_CID_OSD_ENABLE: + if (ctrl->val) + mchp_osd_set(osd, MCHP_OSD_CTRL, MCHP_OSD_CTRL_START); + else + mchp_osd_clr(osd, MCHP_OSD_CTRL, MCHP_OSD_CTRL_START); + break; + case MCHP_CID_OSD_COLOR: + mchp_osd_reg_write(osd, MCHP_OSD_COLOR, ctrl->val); + break; + case MCHP_CID_OSD_NUM: + mchp_dscmi_osd_text(osd, ctrl->val); + break; + } + + return 0; +} + +static const struct v4l2_ctrl_ops mchp_osd_ctrl_ops = { + .s_ctrl = mchp_osd_s_ctrl, +}; + +static const struct v4l2_subdev_video_ops mchp_osd_video_ops = { + .s_stream = mchp_osd_s_stream, +}; + +static const struct v4l2_subdev_pad_ops mchp_osd_pad_ops = { + .enum_mbus_code = mvc_enum_mbus_code, + .get_fmt = mchp_osd_get_format, + .set_fmt = mchp_osd_set_format, + .link_validate = v4l2_subdev_link_validate_default, +}; + +static const struct v4l2_subdev_ops mchp_osd_ops = { + .video = &mchp_osd_video_ops, + .pad = &mchp_osd_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops mchp_osd_internal_ops = { + .open = mchp_osd_open, + .close = mchp_osd_close, +}; + +static const struct media_entity_operations mchp_osd_media_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static struct v4l2_ctrl_config mchp_osd_ctrls[] = { + { + .ops = &mchp_osd_ctrl_ops, + .id = MCHP_CID_OSD_X_POS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "OSDx Position", + .min = MCHP_OSD_X_Y_POS_MIN, + .max = MCHP_OSD_X_Y_POS_MAX, + .def = MCHP_OSD_X_Y_POS_DEFAULT, + .step = MCHP_OSD_X_Y_POS_STEP, + }, { + .ops = &mchp_osd_ctrl_ops, + .id = MCHP_CID_OSD_Y_POS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "OSDy Position", + .min = MCHP_OSD_X_Y_POS_MIN, + .max = MCHP_OSD_X_Y_POS_MAX, + .def = MCHP_OSD_X_Y_POS_DEFAULT, + .step = MCHP_OSD_X_Y_POS_STEP, + }, { + .ops = &mchp_osd_ctrl_ops, + .id = MCHP_CID_OSD_ENABLE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "OSD enable", + .min = MCHP_OSD_ENABLE_MIN, + .max = MCHP_OSD_ENABLE_MAX, + .def = MCHP_OSD_ENABLE_DEFAULT, + .step = MCHP_OSD_ENABLE_STEP, + }, { + .ops = &mchp_osd_ctrl_ops, + .id = MCHP_CID_OSD_COLOR, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "OSD color", + .min = MCHP_OSD_COLOR_MIN, + .max = MCHP_OSD_COLOR_MAX, + .def = MCHP_OSD_COLOR_DEFAULT, + .step = MCHP_OSD_COLOR_STEP, + }, { + .ops = &mchp_osd_ctrl_ops, + .id = MCHP_CID_OSD_NUM, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "OSD num", + .min = MCHP_OSD_NUM_MIN, + .max = MCHP_OSD_NUM_MAX, + .def = MCHP_OSD_NUM_DEFAULT, + .step = MCHP_OSD_NUM_STEP, + }, +}; + +static int mchp_osd_init_controls(struct mchp_osd_device *osd) +{ + struct v4l2_ctrl_handler *ctrl_hdlr = &osd->ctrl_handler; + int ret; + + ret = v4l2_ctrl_handler_init(ctrl_hdlr, MCHP_OSD_NUM_CTRLS); + if (ret) + return ret; + + v4l2_ctrl_new_custom(ctrl_hdlr, &mchp_osd_ctrls[0], NULL); + osd->horizontal_pos = MCHP_OSD_X_Y_POS_MIN; + + v4l2_ctrl_new_custom(ctrl_hdlr, &mchp_osd_ctrls[1], NULL); + osd->vertical_pos = MCHP_OSD_X_Y_POS_MIN; + + v4l2_ctrl_new_custom(ctrl_hdlr, &mchp_osd_ctrls[2], NULL); + v4l2_ctrl_new_custom(ctrl_hdlr, &mchp_osd_ctrls[3], NULL); + v4l2_ctrl_new_custom(ctrl_hdlr, &mchp_osd_ctrls[4], NULL); + + if (ctrl_hdlr->error) { + dev_err(osd->dev, "control init failed: %d", + ctrl_hdlr->error); + v4l2_ctrl_handler_free(ctrl_hdlr); + return ctrl_hdlr->error; + } + + osd->subdev.ctrl_handler = ctrl_hdlr; + + return 0; +} + +static int mchp_osd_init_formats(struct mchp_osd_device *osd) +{ + struct device *dev = osd->dev; + struct v4l2_mbus_framefmt *format; + const struct mvideo_format *vip_format; + + vip_format = mvc_get_format_by_vf_code(MCHP_OSD_DEF_FORMAT); + if (IS_ERR(vip_format)) + return dev_err_probe(dev, PTR_ERR(vip_format), "invalid format"); + + osd->vip_formats[MVC_PAD_SINK] = vip_format; + osd->vip_formats[MVC_PAD_SOURCE] = vip_format; + + /* Initialize default and active formats */ + format = &osd->default_formats[MVC_PAD_SINK]; + format->code = osd->vip_formats[MVC_PAD_SINK]->code; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + format->width = MVC_MAX_WIDTH; + format->height = MVC_MAX_HEIGHT; + + osd->formats[MVC_PAD_SINK] = *format; + + format = &osd->default_formats[MVC_PAD_SOURCE]; + *format = osd->default_formats[MVC_PAD_SINK]; + format->code = osd->vip_formats[MVC_PAD_SOURCE]->code; + + osd->formats[MVC_PAD_SOURCE] = *format; + + return 0; +} + +static int mchp_osd_probe(struct platform_device *pdev) +{ + struct v4l2_subdev *subdev; + struct mchp_osd_device *osd; + int ret; + + osd = devm_kzalloc(&pdev->dev, sizeof(*osd), GFP_KERNEL); + if (!osd) + return -ENOMEM; + + osd->dev = &pdev->dev; + + osd->iomem = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(osd->iomem)) + return PTR_ERR(osd->iomem); + + mchp_osd_set(osd, MCHP_OSD_CTRL, MCHP_OSD_CTRL_RESET); + + /* Initialize V4L2 subdevice and media entity */ + subdev = &osd->subdev; + v4l2_subdev_init(subdev, &mchp_osd_ops); + subdev->dev = &pdev->dev; + subdev->internal_ops = &mchp_osd_internal_ops; + strscpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name)); + v4l2_set_subdevdata(subdev, osd); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + ret = mchp_osd_init_formats(osd); + if (ret < 0) + return ret; + + mchp_osd_init_controls(osd); + + osd->pads[MVC_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + osd->pads[MVC_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + subdev->entity.ops = &mchp_osd_media_ops; + ret = media_entity_pads_init(&subdev->entity, 2, osd->pads); + if (ret < 0) + goto error; + + platform_set_drvdata(pdev, osd); + + ret = v4l2_async_register_subdev(subdev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register subdev\n"); + goto error_media; + } + + return 0; + +error_media: + media_entity_cleanup(&subdev->entity); +error: + v4l2_ctrl_handler_free(&osd->ctrl_handler); + + return ret; +} + +static int mchp_osd_remove(struct platform_device *pdev) +{ + struct mchp_osd_device *osd = platform_get_drvdata(pdev); + struct v4l2_subdev *subdev = &osd->subdev; + + v4l2_async_unregister_subdev(subdev); + v4l2_ctrl_handler_free(&osd->ctrl_handler); + media_entity_cleanup(&subdev->entity); + + return 0; +} + +static const struct of_device_id mchp_osd_of_id_table[] = { + { .compatible = "microchip,osd-rtl-v1" }, + { } +}; +MODULE_DEVICE_TABLE(of, mchp_osd_of_id_table); + +static struct platform_driver mchp_osd_driver = { + .driver = { + .name = "microchip-osd", + .of_match_table = mchp_osd_of_id_table, + }, + .probe = mchp_osd_probe, + .remove = mchp_osd_remove, +}; + +module_platform_driver(mchp_osd_driver); + +MODULE_AUTHOR("Shravan Chippa <shravan.chippa@microchip.com>"); +MODULE_DESCRIPTION("Microchip Video On Screen Display Driver"); +MODULE_LICENSE("GPL"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/media/platform/microchip/microchip-rgb-scaler.c linux-microchip/drivers/media/platform/microchip/microchip-rgb-scaler.c --- linux-6.6.51/drivers/media/platform/microchip/microchip-rgb-scaler.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/media/platform/microchip/microchip-rgb-scaler.c 2024-11-26 14:02:37.610495703 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microchip RGB Scaler Driver. + * + * Copyright (C) 2022-2023 Microchip Technology Inc. and its subsidiaries + * Author: Shravan Chippa <shavan.chippa@microchip.com> + * + */ + +#include <linux/device.h> +#include <linux/fixp-arith.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <media/v4l2-async.h> +#include <media/v4l2-subdev.h> + +#include <dt-bindings/media/microchip-common.h> + +#include "microchip-common.h" + +#define MCHP_RGB_SCALER_MIN_WIDTH 32 +#define MCHP_RGB_SCALER_MAX_WIDTH 4096 +#define MCHP_RGB_SCALER_MIN_HEIGHT 32 +#define MCHP_RGB_SCALER_MAX_HEIGHT 4096 + +#define MCHP_RGB_SCALER_DEFAULT_WIDTH 1920 +#define MCHP_RGB_SCALER_DEFAULT_HEIGHT 1080 + +#define MCHP_RGB_SCALER_IP_VER 0x00 +#define MCHP_RGB_SCALER_CTRL 0x04 +#define MCHP_RGB_SCALER_CTRL_START BIT(0) +#define MCHP_RGB_SCALER_CTRL_RESET BIT(1) + +#define MCHP_RGB_SCALER_INPUT_HRES 0x08 +#define MCHP_RGB_SCALER_INPUT_VRES 0x0C +#define MCHP_RGB_SCALER_OUTPUT_HRES 0x10 +#define MCHP_RGB_SCALER_OUTPUT_VRES 0x14 +#define MCHP_RGB_SCALER_FACTOR_H 0x18 +#define MCHP_RGB_SCALER_FACTOR_V 0x1C + +#define MCHP_RGB_SCALER_DEF_FORMAT MVCF_RGB + +/** + * struct mchp_rgb_scaler_device - Microchip RGB Scaler device structure + * @dev: device + * @subdev: The v4l2 subdev structure + * @iomem: Base address of subsystem + * @pads: media pads + * @mutex : mutex lock for hardware reg access + * @formats: V4L2 media bus formats at the sink and source pads + * @default_formats: default V4L2 media bus formats + * @vip_formats: Microchi Video IP format + * @crop: Active crop rectangle for the sink pad + */ +struct mchp_rgb_scaler_device { + struct device *dev; + struct v4l2_subdev subdev; + void __iomem *iomem; + + struct media_pad pads[2]; + + struct mutex lock; + struct v4l2_mbus_framefmt formats[2]; + struct v4l2_mbus_framefmt default_formats[2]; + const struct mvideo_format *vip_formats[2]; + + struct v4l2_rect crop; +}; + +static inline struct mchp_rgb_scaler_device *to_scaler(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct mchp_rgb_scaler_device, subdev); +} + +static inline u32 mchp_rgb_scaler_read(struct mchp_rgb_scaler_device *rgb_scaler, u32 addr) +{ + return readl(rgb_scaler->iomem + addr); +} + +static inline void mchp_rgb_scaler_write(struct mchp_rgb_scaler_device *rgb_scaler, + u32 addr, u32 value) +{ + writel(value, rgb_scaler->iomem + addr); +} + +static inline void mchp_rgb_scaler_clr(struct mchp_rgb_scaler_device *rgb_scaler, + u32 addr, u32 clr) +{ + mchp_rgb_scaler_write(rgb_scaler, addr, + mchp_rgb_scaler_read(rgb_scaler, addr) & ~clr); +} + +static inline void mchp_rgb_scaler_set(struct mchp_rgb_scaler_device *rgb_scaler, + u32 addr, u32 set) +{ + mchp_rgb_scaler_write(rgb_scaler, addr, mchp_rgb_scaler_read(rgb_scaler, addr) | set); +} + +static void mchp_set_scale_factor(struct mchp_rgb_scaler_device *rgb_scaler) +{ + u32 in_width, in_height, out_width, out_height; + u32 scale_factor_v, scale_factor_h; + + in_width = rgb_scaler->formats[MVC_PAD_SINK].width; + in_height = rgb_scaler->formats[MVC_PAD_SINK].height; + out_width = rgb_scaler->formats[MVC_PAD_SOURCE].width; + out_height = rgb_scaler->formats[MVC_PAD_SOURCE].height; + + scale_factor_h = (((in_width - 1) * 1024) / out_width); + scale_factor_v = (((in_height - 1) * 1024) / out_height); + + mutex_lock(&rgb_scaler->lock); + + mchp_rgb_scaler_write(rgb_scaler, MCHP_RGB_SCALER_INPUT_HRES, in_width); + mchp_rgb_scaler_write(rgb_scaler, MCHP_RGB_SCALER_INPUT_VRES, in_height); + mchp_rgb_scaler_write(rgb_scaler, MCHP_RGB_SCALER_OUTPUT_HRES, out_width); + mchp_rgb_scaler_write(rgb_scaler, MCHP_RGB_SCALER_OUTPUT_VRES, out_height); + mchp_rgb_scaler_write(rgb_scaler, MCHP_RGB_SCALER_FACTOR_H, scale_factor_h); + mchp_rgb_scaler_write(rgb_scaler, MCHP_RGB_SCALER_FACTOR_V, scale_factor_v); + + mutex_unlock(&rgb_scaler->lock); +} + +static int mchp_rgb_scaler_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct mchp_rgb_scaler_device *rgb_scaler = to_scaler(subdev); + + if (!enable) { + mchp_rgb_scaler_clr(rgb_scaler, MCHP_RGB_SCALER_CTRL, MCHP_RGB_SCALER_CTRL_START); + return 0; + } + + mchp_rgb_scaler_set(rgb_scaler, MCHP_RGB_SCALER_CTRL, MCHP_RGB_SCALER_CTRL_RESET); + + mchp_set_scale_factor(rgb_scaler); + + mchp_rgb_scaler_set(rgb_scaler, MCHP_RGB_SCALER_CTRL, MCHP_RGB_SCALER_CTRL_START); + + return 0; +} + +static int mchp_rgb_scaler_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct v4l2_mbus_framefmt *format; + + format = v4l2_subdev_get_try_format(subdev, sd_state, fse->pad); + + if (fse->index || fse->code != format->code) + return -EINVAL; + + fse->min_width = MCHP_RGB_SCALER_MIN_WIDTH; + fse->max_width = MCHP_RGB_SCALER_MAX_WIDTH; + fse->min_height = MCHP_RGB_SCALER_MIN_HEIGHT; + fse->max_height = MCHP_RGB_SCALER_MAX_HEIGHT; + + return 0; +} + +static struct v4l2_mbus_framefmt * +__mchp_rgb_scaler_get_pad_format(struct mchp_rgb_scaler_device *rgb_scaler, + struct v4l2_subdev_state *sd_state, + unsigned int pad, u32 which) +{ + struct v4l2_mbus_framefmt *format; + + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + format = v4l2_subdev_get_try_format(&rgb_scaler->subdev, + sd_state, pad); + break; + case V4L2_SUBDEV_FORMAT_ACTIVE: + format = &rgb_scaler->formats[pad]; + break; + default: + format = NULL; + break; + } + + return format; +} + +static struct v4l2_rect * +__mchp_rgb_scaler_get_crop(struct mchp_rgb_scaler_device *rgb_scaler, + struct v4l2_subdev_state *sd_state, u32 which) +{ + struct v4l2_rect *crop; + + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + crop = v4l2_subdev_get_try_crop(&rgb_scaler->subdev, + sd_state, + MVC_PAD_SINK); + break; + case V4L2_SUBDEV_FORMAT_ACTIVE: + crop = &rgb_scaler->crop; + break; + default: + crop = NULL; + break; + } + + return crop; +} + +static int mchp_rgb_scaler_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct mchp_rgb_scaler_device *rgb_scaler = to_scaler(subdev); + struct v4l2_mbus_framefmt *format; + + format = __mchp_rgb_scaler_get_pad_format(rgb_scaler, sd_state, fmt->pad, + fmt->which); + if (!format) + return -EINVAL; + + fmt->format = *format; + + return 0; +} + +static void mchp_rgb_scaler_try_crop(const struct v4l2_mbus_framefmt *sink, + struct v4l2_rect *crop) +{ + crop->left = min_t(u32, crop->left, sink->width - MCHP_RGB_SCALER_MIN_WIDTH); + crop->top = min_t(u32, crop->top, sink->height - MCHP_RGB_SCALER_MIN_HEIGHT); + crop->width = clamp_t(u32, crop->width, MCHP_RGB_SCALER_MIN_WIDTH, + sink->width - crop->left); + crop->height = clamp_t(u32, crop->height, MCHP_RGB_SCALER_MIN_HEIGHT, + sink->height - crop->top); +} + +static int mchp_rgb_scaler_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct mchp_rgb_scaler_device *rgb_scaler = to_scaler(subdev); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + format = __mchp_rgb_scaler_get_pad_format(rgb_scaler, sd_state, fmt->pad, + fmt->which); + if (!format) + return -EINVAL; + + format->width = clamp_t(unsigned int, fmt->format.width, + MCHP_RGB_SCALER_MIN_WIDTH, MCHP_RGB_SCALER_MAX_WIDTH); + format->height = clamp_t(unsigned int, fmt->format.height, + MCHP_RGB_SCALER_MIN_HEIGHT, MCHP_RGB_SCALER_MAX_HEIGHT); + + fmt->format = *format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + if (fmt->pad == MVC_PAD_SINK) { + /* Set the crop rectangle to the full frame */ + crop = __mchp_rgb_scaler_get_crop(rgb_scaler, sd_state, fmt->which); + if (!crop) + return -EINVAL; + crop->left = 0; + crop->top = 0; + crop->width = fmt->format.width; + crop->height = fmt->format.height; + rgb_scaler->formats[MVC_PAD_SOURCE].width = crop->width; + rgb_scaler->formats[MVC_PAD_SOURCE].height = crop->height; + } + + return 0; +} + +static int mchp_rgb_scaler_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + struct mchp_rgb_scaler_device *rgb_scaler = to_scaler(subdev); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + if (sel->pad != MVC_PAD_SINK) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + format = __mchp_rgb_scaler_get_pad_format(rgb_scaler, sd_state, + MVC_PAD_SINK, + sel->which); + if (!format) + return -EINVAL; + + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = format->width; + sel->r.height = format->height; + return 0; + case V4L2_SEL_TGT_CROP: + crop = __mchp_rgb_scaler_get_crop(rgb_scaler, sd_state, sel->which); + if (!crop) + return -EINVAL; + sel->r = *crop; + return 0; + default: + return -EINVAL; + } +} + +static int mchp_rgb_scaler_set_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + struct mchp_rgb_scaler_device *rgb_scaler = to_scaler(subdev); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + if (sel->target != V4L2_SEL_TGT_CROP || sel->pad != MVC_PAD_SINK) + return -EINVAL; + + format = __mchp_rgb_scaler_get_pad_format(rgb_scaler, sd_state, MVC_PAD_SINK, + sel->which); + if (!format) + return -EINVAL; + + mchp_rgb_scaler_try_crop(format, &sel->r); + crop = __mchp_rgb_scaler_get_crop(rgb_scaler, sd_state, sel->which); + if (!crop) + return -EINVAL; + + *crop = sel->r; + + rgb_scaler->formats[MVC_PAD_SOURCE].width = crop->width; + rgb_scaler->formats[MVC_PAD_SOURCE].height = crop->height; + + mchp_set_scale_factor(rgb_scaler); + + return 0; +} + +static int mchp_rgb_scaler_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + struct mchp_rgb_scaler_device *rgb_scaler = to_scaler(subdev); + struct v4l2_mbus_framefmt *format; + + /* Initialize with default formats */ + format = v4l2_subdev_get_try_format(subdev, fh->state, MVC_PAD_SINK); + *format = rgb_scaler->default_formats[MVC_PAD_SINK]; + + format = v4l2_subdev_get_try_format(subdev, fh->state, MVC_PAD_SOURCE); + *format = rgb_scaler->default_formats[MVC_PAD_SOURCE]; + + return 0; +} + +static int mchp_rgb_scaler_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + return 0; +} + +static const struct v4l2_subdev_video_ops mchp_rgb_scaler_video_ops = { + .s_stream = mchp_rgb_scaler_s_stream, +}; + +static const struct v4l2_subdev_pad_ops mchp_rgb_scaler_pad_ops = { + .enum_mbus_code = mvc_enum_mbus_code, + .enum_frame_size = mchp_rgb_scaler_enum_frame_size, + .get_fmt = mchp_rgb_scaler_get_format, + .set_fmt = mchp_rgb_scaler_set_format, + .get_selection = mchp_rgb_scaler_get_selection, + .set_selection = mchp_rgb_scaler_set_selection, +}; + +static const struct v4l2_subdev_ops mchp_rgb_scaler_ops = { + .video = &mchp_rgb_scaler_video_ops, + .pad = &mchp_rgb_scaler_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops mchp_rgb_scaler_internal_ops = { + .open = mchp_rgb_scaler_open, + .close = mchp_rgb_scaler_close, +}; + +static const struct media_entity_operations mchp_rgb_scaler_media_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static int mchp_rgb_scaler_init_formats(struct mchp_rgb_scaler_device *rgb_scaler) +{ + struct device *dev = rgb_scaler->dev; + const struct mvideo_format *vip_format; + + vip_format = mvc_get_format_by_vf_code(MCHP_RGB_SCALER_DEF_FORMAT); + if (IS_ERR(vip_format)) + return dev_err_probe(dev, PTR_ERR(vip_format), "invalid format"); + + rgb_scaler->vip_formats[MVC_PAD_SINK] = vip_format; + rgb_scaler->vip_formats[MVC_PAD_SOURCE] = vip_format; + + return 0; +} + +static int mchp_rgb_scaler_probe(struct platform_device *pdev) +{ + struct mchp_rgb_scaler_device *rgb_scaler; + struct v4l2_subdev *subdev; + struct v4l2_mbus_framefmt *default_format; + int ret; + + rgb_scaler = devm_kzalloc(&pdev->dev, sizeof(*rgb_scaler), GFP_KERNEL); + if (!rgb_scaler) + return -ENOMEM; + + rgb_scaler->dev = &pdev->dev; + + ret = mchp_rgb_scaler_init_formats(rgb_scaler); + if (ret < 0) + return ret; + + rgb_scaler->iomem = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rgb_scaler->iomem)) + return PTR_ERR(rgb_scaler->iomem); + + mutex_init(&rgb_scaler->lock); + mchp_rgb_scaler_set(rgb_scaler, MCHP_RGB_SCALER_CTRL, MCHP_RGB_SCALER_CTRL_RESET); + + /* Initialize V4L2 subdevice and media entity */ + subdev = &rgb_scaler->subdev; + v4l2_subdev_init(subdev, &mchp_rgb_scaler_ops); + subdev->dev = &pdev->dev; + subdev->internal_ops = &mchp_rgb_scaler_internal_ops; + strscpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name)); + v4l2_set_subdevdata(subdev, rgb_scaler); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + /* Initialize default and active formats */ + default_format = &rgb_scaler->default_formats[MVC_PAD_SINK]; + default_format->code = rgb_scaler->vip_formats[MVC_PAD_SINK]->code; + default_format->field = V4L2_FIELD_NONE; + default_format->colorspace = V4L2_COLORSPACE_SRGB; + default_format->width = MCHP_RGB_SCALER_DEFAULT_WIDTH; + default_format->height = MCHP_RGB_SCALER_DEFAULT_HEIGHT; + + rgb_scaler->formats[MVC_PAD_SINK] = *default_format; + + default_format = &rgb_scaler->default_formats[MVC_PAD_SOURCE]; + *default_format = rgb_scaler->default_formats[MVC_PAD_SINK]; + default_format->width = MCHP_RGB_SCALER_DEFAULT_WIDTH; + default_format->height = MCHP_RGB_SCALER_DEFAULT_HEIGHT; + + rgb_scaler->formats[MVC_PAD_SOURCE] = *default_format; + + rgb_scaler->pads[MVC_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + rgb_scaler->pads[MVC_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + subdev->entity.ops = &mchp_rgb_scaler_media_ops; + + ret = media_entity_pads_init(&subdev->entity, 2, rgb_scaler->pads); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, rgb_scaler); + + ret = v4l2_async_register_subdev(subdev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register subdev\n"); + goto error; + } + + return 0; + +error: + media_entity_cleanup(&subdev->entity); + + return ret; +} + +static int mchp_rgb_scaler_remove(struct platform_device *pdev) +{ + struct mchp_rgb_scaler_device *rgb_scaler = platform_get_drvdata(pdev); + struct v4l2_subdev *subdev = &rgb_scaler->subdev; + + v4l2_async_unregister_subdev(subdev); + media_entity_cleanup(&subdev->entity); + + return 0; +} + +static const struct of_device_id mchp_rgb_scaler_of_id_table[] = { + { .compatible = "microchip,rgb-scaler-rtl-v1" }, + { } +}; +MODULE_DEVICE_TABLE(of, mchp_rgb_scaler_of_id_table); + +static struct platform_driver mchp_rgb_scaler_driver = { + .driver = { + .name = "microchip-rgb-scaler", + .of_match_table = mchp_rgb_scaler_of_id_table, + }, + .probe = mchp_rgb_scaler_probe, + .remove = mchp_rgb_scaler_remove, +}; + +module_platform_driver(mchp_rgb_scaler_driver); + +MODULE_DESCRIPTION("Microchip RGB Scaler Driver"); +MODULE_LICENSE("GPL"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/media/platform/microchip/microchip-vcpp.c linux-microchip/drivers/media/platform/microchip/microchip-vcpp.c --- linux-6.6.51/drivers/media/platform/microchip/microchip-vcpp.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/media/platform/microchip/microchip-vcpp.c 2024-11-26 14:02:37.610495703 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microchip Video Capture Pipeline Processing (vcpp) Driver. + * + * Copyright (C) 2022-2023 Microchip Technology Inc. and its subsidiaries + * Author: Shravan Chippa <shavan.chippa@microchip.com> + * + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dma-map-ops.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kmod.h> +#include <linux/mutex.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/of_address.h> +#include <linux/of_reserved_mem.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/videodev2.h> + +#include <media/v4l2-ctrls.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-subdev.h> +#include <media/v4l2-mc.h> +#include <media/videobuf2-v4l2.h> +#include <media/videobuf2-dma-contig.h> + +#include "microchip-common.h" + +#define MCHP_VCPP_DRV_NAME "mchp-vcpp" + +#define MCHP_VCPP_DEF_WIDTH 1920 +#define MCHP_VCPP_DEF_HEIGHT 1080 + +/* Minimum and maximum widths are expressed in bytes */ +#define MCHP_VCPP_MIN_WIDTH 256U +#define MCHP_VCPP_MAX_WIDTH 2048U +#define MCHP_VCPP_MIN_HEIGHT 256U +#define MCHP_VCPP_MAX_HEIGHT 1080U +#define MCHP_VCPP_DEF_FORMAT V4L2_PIX_FMT_YUYV + +#define MCHP_VCPP_IP_VER 0x00 +#define MCHP_VCPP_CTRL_REG 0x04 +#define MCHP_VCPP_CORE_ENABLE BIT(0) +#define MCHP_VCPP_CORE_RESET BIT(1) +#define MCHP_VCPP_FLASH_FIFO BIT(2) + +#define MCHP_VCPP_GLBL_INT_EN 0x08 +#define MCHP_VCPP_GLBL_INT_EN_BIT BIT(0) + +#define MCHP_VCPP_INT_STATUS 0x0C +#define MCHP_VCPP_INT_STATUS_EOF BIT(0) + +#define MCHP_VCPP_INT_EN 0x10 +#define MCHP_VCPP_INT_EN_EOF BIT(0) +#define MCHP_VCPP_INT_EN_FIFO_MT (2 << 0) +#define MCHP_VCPP_INT_EN_FIFO_FULL (3 << 0) + +#define MCHP_VCPP_FRAME_RESOLUTION 0x14 +#define MCHP_VCPP_LINE_GAP 0x18 +#define MCHP_VCPP_BUFF_ADDR_FIFO_DATA 0x1C +#define MCHP_VCPP_BUFF_ADDR_FIFO_RDATA_COUNT 0x20 +#define MCHP_VCPP_FRAME_SIZE_FIFO_DATA_RD 0x24 +#define MCHP_VCPP_MEDIA_PIPE_START 0x28 +#define MCHP_VCPP_MEDIA_PIPE_START_0 BIT(0) + +#define MCHP_VCPP_FRAME_START 0x1 +#define MCHP_VCPP_FRAME_STOP 0x0 + +#define MCHP_VCPP_MAX_FRAMES 8 + +/* The compression ratio is calculated for every 60 frames */ +#define MCHP_VCPP_CR_MAX_FRAMES_RESET_COUNT 60 +#define MCHP_VCPP_CR_MAX_FRAMES_INIT 50 +#define MCHP_VCPP_CR_MAX_ARRAY 6 + +#define MCHP_VCPP_NUM_CTRLS 1 + +/* Minimum wait time for camera to stabilize */ +#define MCHP_VCPP_DELAYED_CAM_M_SEC 100 + +enum mchp_vcpp_state { + VCPP_STOPPED = 0, + VCPP_WAIT_FOR_BUFFER, + VCPP_RUNNING, +}; + +/** + * struct mchp_vcpp_buffer - buffer for one video frame + * @vb: video buffer information struct vb2_v4l2_buffer + * @list: list of all requested buffers from user space + * @paddr: physical address of buffer + * @size: size of buffer + * @prepared: status of buffer + */ +struct mchp_vcpp_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; + dma_addr_t paddr; + size_t size; + bool prepared; +}; + +/** + * struct mchp_vcpp_compression_ratio - for compression calculation + * @frame_size: accumulated frames size + * @frame_size_index: accumulated present frames size index + * @frame_count: present frame count + */ +struct mchp_vcpp_compression_ratio { + u32 frame_size[MCHP_VCPP_CR_MAX_ARRAY]; + u32 frame_size_index; + u32 frame_count; +}; + +/** + * struct mchp_vcpp_fpga - V4L2 device context + * @base: pointer to control register + * @pdev: platform device + * @dev: device + * @active: current buffer in queue + * @reset_gpio: sensor fabric rest + * @v4l2_dev: top-level v4l2 device struct + * @vdev: video node structure + * @format: active V4L2 pixel format + * @fmtinfo: format information corresponding to the active @format + * @queue: vb2 video capture queue + * @lock: mutex lock for vb2 buffs + * @qlock: spinlock controlling access to buf_list and sequence + * @buf_list: list of buffers queued for DMA + * @subdev_entities: subdevice list + * @cambuf: struct cam_buffer + * @notifier: V4L2 asynchronous subdevs notifier + * @mdev: media device + * @vid_cap_pad: media pad for the video device entity + * @h264_ratio struct mchp_vcpp_compression + * @ctrl_handler: control handler structure + * @state: state of buffers + * @irq: external IRQ for new frame + * @sequence: frame sequence counter + */ +struct mchp_vcpp_fpga { + void __iomem *base; + struct platform_device *pdev; + struct device *dev; + struct mchp_vcpp_buffer *active; + struct gpio_desc *reset_gpio; + struct clk_bulk_data *clks; + struct v4l2_device v4l2_dev; + struct video_device vdev; + struct v4l2_format format; + const struct mvideo_format *fmtinfo; + struct vb2_queue queue; + struct mutex lock; /* vb2 buffer lock */ + spinlock_t qlock; + struct list_head buf_list; + struct list_head subdev_entities; + struct v4l2_async_notifier notifier; + struct media_device mdev; + struct media_pad vid_cap_pad; + struct mchp_vcpp_compression_ratio h264_ratio; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *compression_ratio_ctrl; + enum mchp_vcpp_state state; + int irq; + int sequence; +}; + +struct mchp_vcpp_graph_entity { + struct v4l2_async_connection asc; + struct media_entity *entity; + struct v4l2_subdev *subdev; +}; + +static const struct clk_bulk_data mchp_vcpp_clks[] = { + { .id = "axi" }, + { .id = "video" }, +}; + +static inline struct mchp_vcpp_graph_entity * +to_mchp_vcpp_entity(struct v4l2_async_connection *asc) +{ + return container_of(asc, struct mchp_vcpp_graph_entity, asc); +} + +static struct mchp_vcpp_graph_entity * +mchp_vcpp_graph_find_entity(struct mchp_vcpp_fpga *mchp_vcpp, + const struct fwnode_handle *fwnode) +{ + struct mchp_vcpp_graph_entity *entity; + struct v4l2_async_connection *asc; + struct list_head *lists[] = { + &mchp_vcpp->notifier.done_list, + &mchp_vcpp->notifier.waiting_list + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(lists); i++) { + list_for_each_entry(asc, lists[i], asc_entry) { + entity = to_mchp_vcpp_entity(asc); + if (entity->asc.match.fwnode == fwnode) + return entity; + } + } + + return NULL; +} + +static struct v4l2_subdev * +mchp_vcpp_remote_subdev(struct media_pad *local, u32 *pad) +{ + struct media_pad *remote; + + remote = media_pad_remote_pad_first(local); + if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) + return NULL; + + if (pad) + *pad = remote->index; + + return media_entity_to_v4l2_subdev(remote->entity); +} + +static int mchp_vcpp_verify_format(struct mchp_vcpp_fpga *mchp_vcpp) +{ + struct v4l2_subdev_format fmt= { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_subdev *subdev; + int ret; + + subdev = mchp_vcpp_remote_subdev(&mchp_vcpp->vid_cap_pad, &fmt.pad); + if (!subdev) + return -EPIPE; + + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); + if (ret < 0) + return ret == -ENOIOCTLCMD ? -EINVAL : ret; + + if (mchp_vcpp->fmtinfo->code != fmt.format.code || + mchp_vcpp->format.fmt.pix.height != fmt.format.height || + mchp_vcpp->format.fmt.pix.width != fmt.format.width) + return -EINVAL; + + return 0; +} + +static void mchp_vcpp_buffer_done(struct mchp_vcpp_fpga *mchp_vcpp, + struct mchp_vcpp_buffer *buf, + size_t bytesused, + int err) +{ + struct vb2_v4l2_buffer *vbuf; + + if (!buf) + return; + + list_del_init(&buf->list); + vbuf = &buf->vb; + vbuf->sequence = mchp_vcpp->sequence++; + vbuf->field = V4L2_FIELD_NONE; + vbuf->vb2_buf.timestamp = ktime_get_ns(); + + vb2_set_plane_payload(&vbuf->vb2_buf, 0, bytesused); + + vb2_buffer_done(&vbuf->vb2_buf, + err ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + + dev_dbg(mchp_vcpp->dev, "Buffer[%d] done seq=%d, bytesused=%zu\n", + vbuf->vb2_buf.index, vbuf->sequence, bytesused); + + mchp_vcpp->active = NULL; +} + +/* + * Compression ratio is the percentage of compressed image over the actual image. + * The YUV420 requires 3 bytes per 2 pixels, or 1.5 bytes per pixel, because + * groups of pixels share a single color value. + * The compression ratio can be calculated as: + * (width * height * accumulated-frame-count * bytes-per-pixel) / accumulated-frame-size + */ +static u32 compression_ratio_calc(u32 hres, u32 vres, u32 accumulated_frame_size) +{ + return ((hres * vres * MCHP_VCPP_CR_MAX_FRAMES_RESET_COUNT * 3) / + (accumulated_frame_size)) / 2; +} + +static irqreturn_t mchp_vcpp_irq_ext(int irq, void *dev_id) +{ + struct mchp_vcpp_fpga *mchp_vcpp = dev_id; + u32 irq_status; + unsigned long flags; + + irq_status = readl_relaxed(mchp_vcpp->base + MCHP_VCPP_INT_STATUS); + + spin_lock_irqsave(&mchp_vcpp->qlock, flags); + + if (!(irq_status & MCHP_VCPP_INT_STATUS_EOF)) { + writel_relaxed(0xFF, mchp_vcpp->base + MCHP_VCPP_INT_STATUS); + spin_unlock_irqrestore(&mchp_vcpp->qlock, flags); + return IRQ_NONE; + } else { + writel_relaxed(MCHP_VCPP_INT_EN_EOF, mchp_vcpp->base + MCHP_VCPP_INT_STATUS); + } + + if (mchp_vcpp->state != VCPP_RUNNING) { + spin_unlock_irqrestore(&mchp_vcpp->qlock, flags); + return IRQ_HANDLED; + } + + spin_unlock_irqrestore(&mchp_vcpp->qlock, flags); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t mchp_vcpp_irq_thread_fn(int irq, void *dev_id) +{ + struct mchp_vcpp_fpga *mchp_vcpp = dev_id; + struct mchp_vcpp_buffer *buf; + struct v4l2_pix_format *pix = &mchp_vcpp->format.fmt.pix; + struct mchp_vcpp_compression_ratio *h264_ratio = &mchp_vcpp->h264_ratio; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_subdev *subdev; + int ret, buf_size, i; + int *frame_size_index = &h264_ratio->frame_size_index; + + spin_lock_irq(&mchp_vcpp->qlock); + + if (list_empty(&mchp_vcpp->buf_list)) { + dev_dbg(mchp_vcpp->dev, + "Capture restart is deferred to next buffer queue\n"); + mchp_vcpp->state = VCPP_WAIT_FOR_BUFFER; + spin_unlock_irq(&mchp_vcpp->qlock); + return IRQ_HANDLED; + } + + buf = list_entry(mchp_vcpp->buf_list.next, + struct mchp_vcpp_buffer, list); + + mchp_vcpp->active = buf; + + spin_unlock_irq(&mchp_vcpp->qlock); + + if (mchp_vcpp->fmtinfo->fourcc == V4L2_PIX_FMT_H264) { + buf_size = readl_relaxed(mchp_vcpp->base + MCHP_VCPP_FRAME_SIZE_FIFO_DATA_RD); + spin_lock_irq(&mchp_vcpp->qlock); + mchp_vcpp_buffer_done(mchp_vcpp, buf, buf_size, 0); + spin_unlock_irq(&mchp_vcpp->qlock); + h264_ratio->frame_count++; + h264_ratio->frame_size[*frame_size_index] += buf_size; + + if (h264_ratio->frame_count % 10 == 0) + (*frame_size_index)++; + + if (*frame_size_index == MCHP_VCPP_CR_MAX_ARRAY) + *frame_size_index = 0; + + if (h264_ratio->frame_count == MCHP_VCPP_CR_MAX_FRAMES_RESET_COUNT) { + u32 accumulated_frame_size = 0, compression_ratio, hres, vres; + + for (i = 0; i < MCHP_VCPP_CR_MAX_ARRAY; i++) + accumulated_frame_size += h264_ratio->frame_size[i]; + + subdev = mchp_vcpp_remote_subdev(&mchp_vcpp->vid_cap_pad, &fmt.pad); + if (!subdev) + return -EPIPE; + + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); + if (ret < 0) + return ret == -ENOIOCTLCMD ? -EINVAL : ret; + + hres = fmt.format.width; + vres = fmt.format.height; + + compression_ratio = compression_ratio_calc(hres, vres, + accumulated_frame_size); + __v4l2_ctrl_s_ctrl(mchp_vcpp->compression_ratio_ctrl, compression_ratio); + + h264_ratio->frame_count = MCHP_VCPP_CR_MAX_FRAMES_INIT; + h264_ratio->frame_size[*frame_size_index] = 0; + } + } else { + spin_lock_irq(&mchp_vcpp->qlock); + mchp_vcpp_buffer_done(mchp_vcpp, buf, + pix->width * pix->height * mchp_vcpp->fmtinfo->bpp, 0); + spin_unlock_irq(&mchp_vcpp->qlock); + } + + spin_lock_irq(&mchp_vcpp->qlock); + + if (list_empty(&mchp_vcpp->buf_list)) { + dev_dbg(mchp_vcpp->dev, + "Capture restart is deferred to next buffer queue\n"); + mchp_vcpp->state = VCPP_WAIT_FOR_BUFFER; + spin_unlock_irq(&mchp_vcpp->qlock); + return IRQ_HANDLED; + } + + spin_unlock_irq(&mchp_vcpp->qlock); + + return IRQ_HANDLED; +} + +static int mchp_vcpp_pipeline_set_stream(struct mchp_vcpp_fpga *mchp_vcpp, bool state) +{ + struct media_entity *entity = &mchp_vcpp->vdev.entity; + struct v4l2_subdev *subdev; + struct media_pad *pad; + int ret; + + /* Start/stop all entities within pipeline */ + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_subdev_call(subdev, video, s_stream, state); + if (ret < 0 && ret != -ENOIOCTLCMD) { + dev_err(mchp_vcpp->dev, "%s: \"%s\" failed to %s streaming (%d)\n", + __func__, subdev->name, + state ? "start" : "stop", ret); + return ret; + } + + dev_dbg(mchp_vcpp->dev, "\"%s\" is %s\n", + subdev->name, state ? "started" : "stopped"); + } + + return 0; +} + +static int mchp_vcpp_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct mchp_vcpp_fpga *mchp_vcpp = vb2_get_drv_priv(vq); + unsigned int size; + + if (*nbuffers > MCHP_VCPP_MAX_FRAMES) { + dev_dbg(mchp_vcpp->dev, + "output frame count too high (%d), cut to %d\n", + *nbuffers, MCHP_VCPP_MAX_FRAMES); + *nbuffers = MCHP_VCPP_MAX_FRAMES; + } + + size = mchp_vcpp->format.fmt.pix.sizeimage; + + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; + + *nplanes = 1; + sizes[0] = size; + + dev_dbg(mchp_vcpp->dev, "Setup queue, count=%d, size=%d\n", + *nbuffers, size); + + return 0; +} + +static int mchp_vcpp_buffer_prepare(struct vb2_buffer *vb) +{ + struct mchp_vcpp_fpga *mchp_vcpp = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct mchp_vcpp_buffer *buf = + container_of(vbuf, struct mchp_vcpp_buffer, vb); + unsigned int size; + + size = mchp_vcpp->format.fmt.pix.sizeimage; + + if (vb2_plane_size(vb, 0) < size) { + dev_err(mchp_vcpp->dev, + "data will not fit into plane (%lu < %u)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + if (!buf->prepared) { + buf->paddr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + buf->size = vb2_plane_size(&buf->vb.vb2_buf, 0); + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->size); + buf->prepared = true; + } + + return 0; +} + +static void mchp_vcpp_buffer_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct mchp_vcpp_fpga *mchp_vcpp = vb2_get_drv_priv(vb->vb2_queue); + struct mchp_vcpp_buffer *buf = + container_of(vbuf, struct mchp_vcpp_buffer, vb); + + spin_lock_irq(&mchp_vcpp->qlock); + list_add_tail(&buf->list, &mchp_vcpp->buf_list); + if (mchp_vcpp->state == VCPP_WAIT_FOR_BUFFER) { + mchp_vcpp->state = VCPP_RUNNING; + mchp_vcpp->active = buf; + } + + writel_relaxed(buf->paddr >> 6, mchp_vcpp->base + MCHP_VCPP_BUFF_ADDR_FIFO_DATA); + + spin_unlock_irq(&mchp_vcpp->qlock); +} + +static void mchp_return_all_buffers(struct mchp_vcpp_fpga *mchp_vcpp, + enum vb2_buffer_state state) +{ + struct mchp_vcpp_buffer *buf, *node; + + list_for_each_entry_safe(buf, node, &mchp_vcpp->buf_list, list) { + vb2_buffer_done(&buf->vb.vb2_buf, state); + list_del(&buf->list); + } + + mchp_vcpp->active = NULL; +} + +static int mchp_vcpp_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct mchp_vcpp_fpga *mchp_vcpp = vb2_get_drv_priv(vq); + int ret; + + ret = mchp_vcpp_verify_format(mchp_vcpp); + if (ret < 0) { + dev_err(mchp_vcpp->dev, "%s: Failed to mchp_vcpp_verify_format (%d)\n", + __func__, ret); + goto err_free_buffers; + } + + ret = v4l2_pipeline_pm_get(&mchp_vcpp->vdev.entity); + if (ret) { + dev_err(mchp_vcpp->dev, "open cif pipeline failed %d\n", ret); + return ret; + } + + ret = mchp_vcpp_pipeline_set_stream(mchp_vcpp, true); + if (ret < 0) { + dev_err(mchp_vcpp->dev, "%s: Failed to mchp_vcpp_pipeline_start (%d)\n", + __func__, ret); + return ret; + } + + writel_relaxed(MCHP_VCPP_MEDIA_PIPE_START_0, mchp_vcpp->base + MCHP_VCPP_MEDIA_PIPE_START); + writel_relaxed(MCHP_VCPP_GLBL_INT_EN_BIT, mchp_vcpp->base + MCHP_VCPP_GLBL_INT_EN); + writel_relaxed(MCHP_VCPP_INT_EN_EOF, mchp_vcpp->base + MCHP_VCPP_INT_EN); + + writel_relaxed(mchp_vcpp->format.fmt.pix.width, mchp_vcpp->base + MCHP_VCPP_LINE_GAP); + + writel_relaxed(MCHP_VCPP_FRAME_STOP, mchp_vcpp->base + MCHP_VCPP_CTRL_REG); + mdelay(MCHP_VCPP_DELAYED_CAM_M_SEC); + writel_relaxed(MCHP_VCPP_FRAME_START, mchp_vcpp->base + MCHP_VCPP_CTRL_REG); + + mchp_vcpp->sequence = 0; + + ret = request_threaded_irq(mchp_vcpp->irq, mchp_vcpp_irq_ext, + mchp_vcpp_irq_thread_fn, IRQF_NO_SUSPEND, + KBUILD_MODNAME, mchp_vcpp); + + if (ret) { + dev_err(mchp_vcpp->dev, "request threaded irq failed %d\n", + ret); + goto err_free_buffers; + } + + spin_lock_irq(&mchp_vcpp->qlock); + + if (list_empty(&mchp_vcpp->buf_list)) { + dev_dbg(mchp_vcpp->dev, + "Start streaming is deferred to next buffer queue\n"); + mchp_vcpp->state = VCPP_WAIT_FOR_BUFFER; + spin_unlock_irq(&mchp_vcpp->qlock); + return 0; + } + + mchp_vcpp->state = VCPP_RUNNING; + + spin_unlock_irq(&mchp_vcpp->qlock); + + return 0; + +err_free_buffers: + spin_lock_irq(&mchp_vcpp->qlock); + mchp_return_all_buffers(mchp_vcpp, VB2_BUF_STATE_QUEUED); + spin_unlock_irq(&mchp_vcpp->qlock); + + return ret; +} + +static void mchp_vcpp_stop_streaming(struct vb2_queue *vq) +{ + struct mchp_vcpp_fpga *mchp_vcpp = vb2_get_drv_priv(vq); + + writel_relaxed(MCHP_VCPP_FRAME_STOP, mchp_vcpp->base + MCHP_VCPP_CTRL_REG); + + free_irq(mchp_vcpp->irq, mchp_vcpp); + + spin_lock_irq(&mchp_vcpp->qlock); + + mchp_return_all_buffers(mchp_vcpp, VB2_BUF_STATE_QUEUED); + + mchp_vcpp->active = NULL; + mchp_vcpp->state = VCPP_STOPPED; + + spin_unlock_irq(&mchp_vcpp->qlock); + + writel_relaxed(MCHP_VCPP_CORE_RESET, mchp_vcpp->base + MCHP_VCPP_CTRL_REG); + + mchp_vcpp_pipeline_set_stream(mchp_vcpp, false); +} + +static const struct vb2_ops mchp_vcpp_qops = { + .queue_setup = mchp_vcpp_queue_setup, + .buf_prepare = mchp_vcpp_buffer_prepare, + .buf_queue = mchp_vcpp_buffer_queue, + .start_streaming = mchp_vcpp_start_streaming, + .stop_streaming = mchp_vcpp_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static int mchp_vcpp_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, MCHP_VCPP_DRV_NAME, sizeof(cap->driver)); + strscpy(cap->card, "MCHP Camera Media Pipeline", sizeof(cap->card)); + strscpy(cap->bus_info, "platform:mchp-vcpp", sizeof(cap->bus_info)); + + return 0; +} + +static void __mchp_vcpp_try_format(struct mchp_vcpp_fpga *mchp_vcpp, + struct v4l2_pix_format *pix, + const struct mvideo_format **fmtinfo) +{ + const struct mvideo_format *info; + struct v4l2_subdev_format v4l_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_subdev *subdev; + unsigned int ret; + + subdev = mchp_vcpp_remote_subdev(&mchp_vcpp->vid_cap_pad, &v4l_fmt.pad); + if (!subdev) + return; + + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &v4l_fmt); + if (ret < 0) + return; + + info = mvc_get_format_by_code(v4l_fmt.format.code); + if (IS_ERR(info)) + return; + + pix->pixelformat = info->fourcc; + pix->field = V4L2_FIELD_NONE; + + pix->width = clamp(pix->width, MCHP_VCPP_MIN_WIDTH, MCHP_VCPP_MAX_WIDTH); + pix->height = clamp(pix->height, MCHP_VCPP_MIN_HEIGHT, MCHP_VCPP_MAX_HEIGHT); + + pix->bytesperline = info->bpp * pix->width; + pix->sizeimage = pix->bytesperline * pix->height; + pix->colorspace = mchp_vcpp->format.fmt.pix.colorspace; + pix->ycbcr_enc = mchp_vcpp->format.fmt.pix.ycbcr_enc; + pix->quantization = mchp_vcpp->format.fmt.pix.quantization; + + if (fmtinfo) + *fmtinfo = info; +} + +static int mchp_vcpp_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *format) +{ + struct mchp_vcpp_fpga *mchp_vcpp = video_drvdata(file); + + __mchp_vcpp_try_format(mchp_vcpp, &format->fmt.pix, NULL); + + return 0; +} + +static int mchp_vcpp_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *format) +{ + struct mchp_vcpp_fpga *mchp_vcpp = video_drvdata(file); + const struct mvideo_format *info; + + if (vb2_is_streaming(&mchp_vcpp->queue)) + return -EBUSY; + + __mchp_vcpp_try_format(mchp_vcpp, &format->fmt.pix, &info); + + mchp_vcpp->format.fmt.pix.height = format->fmt.pix.height; + mchp_vcpp->format.fmt.pix.width = format->fmt.pix.width; + mchp_vcpp->format.fmt.pix.sizeimage = format->fmt.pix.sizeimage; + mchp_vcpp->format.fmt.pix.bytesperline = format->fmt.pix.bytesperline; + mchp_vcpp->format.fmt.pix.pixelformat = format->fmt.pix.pixelformat; + mchp_vcpp->format.fmt.pix.quantization = 0; + mchp_vcpp->fmtinfo = info; + + return 0; +} + +static int mchp_vcpp_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *format) +{ + struct mchp_vcpp_fpga *mchp_vcpp = video_drvdata(file); + + *format = mchp_vcpp->format; + + return 0; +} + +static int mchp_vcpp_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + struct mchp_vcpp_fpga *mchp_vcpp = video_drvdata(file); + struct v4l2_subdev *subdev; + struct v4l2_subdev_format v4l_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + const struct mvideo_format *mvideo_fmt; + int ret; + + /* Establish media pad format */ + subdev = mchp_vcpp_remote_subdev(&mchp_vcpp->vid_cap_pad, &v4l_fmt.pad); + if (!subdev) + return -EPIPE; + + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &v4l_fmt); + if (ret < 0) + return ret == -ENOIOCTLCMD ? -EINVAL : ret; + + if (fmt->index > 0) + return -EINVAL; + + mvideo_fmt = mvc_get_format_by_code(v4l_fmt.format.code); + if (IS_ERR(mvideo_fmt)) + return PTR_ERR(mvideo_fmt); + + fmt->pixelformat = mvideo_fmt->fourcc; + + fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + return 0; +} + +static int mchp_vcpp_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + if (input->index != 0) + return -EINVAL; + + input->type = V4L2_INPUT_TYPE_CAMERA; + strscpy(input->name, "Camera", sizeof(input->name)); + + return 0; +} + +static int mchp_vcpp_s_input(struct file *file, void *priv, unsigned int index) +{ + if (index > 0) + return -EINVAL; + + return 0; +} + +static int mchp_vcpp_g_input(struct file *file, void *priv, unsigned int *index) +{ + *index = 0; + + return 0; +} + +static int mchp_vb2_ioctl_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct mchp_vcpp_fpga *mchp_vcpp = video_drvdata(file); + + if (!dev_is_dma_coherent(mchp_vcpp->dev)) + p->flags |= V4L2_MEMORY_FLAG_NON_COHERENT; + + return vb2_ioctl_reqbufs(file, priv, p); +} + +static int mchp_vcpp_s_ctrl(struct v4l2_ctrl *ctrl) +{ + if (ctrl->id != MCHP_CID_COMPRESSION_RATIO) + return -EINVAL; + + return 0; +} + +static const struct v4l2_ctrl_ops mchp_vcpp_ctrl_ops = { + .s_ctrl = mchp_vcpp_s_ctrl, +}; + +static const struct v4l2_ctrl_config mchp_vcpp_ctrls[] = { + { + .ops = &mchp_vcpp_ctrl_ops, + .id = MCHP_CID_COMPRESSION_RATIO, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "compression ratio", + .min = 0, + .max = 200, + .def = 30, + .step = 1, + }, +}; + +static const struct v4l2_ioctl_ops mchp_vcpp_ioctl_ops = { + .vidioc_querycap = mchp_vcpp_querycap, + .vidioc_try_fmt_vid_cap = mchp_vcpp_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = mchp_vcpp_s_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = mchp_vcpp_g_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = mchp_vcpp_enum_fmt_vid_cap, + + .vidioc_enum_input = mchp_vcpp_enum_input, + .vidioc_g_input = mchp_vcpp_g_input, + .vidioc_s_input = mchp_vcpp_s_input, + + .vidioc_reqbufs = mchp_vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct v4l2_file_operations mchp_vcpp_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, +}; + +static int mchp_vcpp_graph_notify_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_connection *asc) +{ + struct mchp_vcpp_graph_entity *entity = to_mchp_vcpp_entity(asc); + + entity->entity = &subdev->entity; + entity->subdev = subdev; + + return 0; +} + +static int mchp_vcpp_graph_build_one(struct mchp_vcpp_fpga *mchp_vcpp, + struct mchp_vcpp_graph_entity *entity) +{ + u32 link_flags = MEDIA_LNK_FL_ENABLED; + struct media_entity *local = entity->entity; + struct media_entity *remote; + struct media_pad *local_pad; + struct media_pad *remote_pad; + struct mchp_vcpp_graph_entity *graph_entity; + struct v4l2_fwnode_link link; + struct fwnode_handle *ep_fw_handle = NULL; + int ret = 0; + + dev_dbg(mchp_vcpp->dev, "creating links for entity %s\n", local->name); + + while (1) { + ep_fw_handle = fwnode_graph_get_next_endpoint(entity->asc.match.fwnode, + ep_fw_handle); + if (!ep_fw_handle) + break; + + ret = v4l2_fwnode_parse_link(ep_fw_handle, &link); + if (ret < 0) { + dev_err(mchp_vcpp->dev, + "failed to parse link for %p\n", ep_fw_handle); + continue; + } + + if (link.local_port >= local->num_pads) { + dev_err(mchp_vcpp->dev, + "invalid port number %u for %p\n", + link.local_port, link.local_node); + v4l2_fwnode_put_link(&link); + ret = -EINVAL; + break; + } + + local_pad = &local->pads[link.local_port]; + + if (local_pad->flags & MEDIA_PAD_FL_SINK) { + dev_dbg(mchp_vcpp->dev, "skipping sink port %p:%u\n", + link.local_node, link.local_port); + v4l2_fwnode_put_link(&link); + continue; + } + + if (link.remote_node == of_fwnode_handle(mchp_vcpp->dev->of_node)) { + dev_dbg(mchp_vcpp->dev, "skipping DMA %p:%u\n", + link.local_node, link.local_port); + v4l2_fwnode_put_link(&link); + continue; + } + + graph_entity = mchp_vcpp_graph_find_entity(mchp_vcpp, link.remote_node); + if (!graph_entity) { + dev_dbg(mchp_vcpp->dev, "no entity found for %p\n", + link.remote_node); + v4l2_fwnode_put_link(&link); + ret = -ENODEV; + break; + } + + remote = graph_entity->entity; + + if (link.remote_port >= remote->num_pads) { + dev_err(mchp_vcpp->dev, "invalid port number %u on %p\n", + link.remote_port, link.remote_node); + v4l2_fwnode_put_link(&link); + ret = -EINVAL; + break; + } + + remote_pad = &remote->pads[link.remote_port]; + + v4l2_fwnode_put_link(&link); + + dev_info(mchp_vcpp->dev, "creating %s:%u -> %s:%u link\n", + local->name, local_pad->index, + remote->name, remote_pad->index); + + ret = media_create_pad_link(local, local_pad->index, + remote, remote_pad->index, + link_flags); + if (ret < 0) { + dev_err(mchp_vcpp->dev, + "failed to create %s:%u -> %s:%u link\n", + local->name, local_pad->index, + remote->name, remote_pad->index); + break; + } + } + + fwnode_handle_put(ep_fw_handle); + + return ret; +} + +static int mchp_vcpp_graph_build_dma(struct mchp_vcpp_fpga *mchp_vcpp) +{ + struct device_node *node = mchp_vcpp->dev->of_node; + struct media_entity *source; + struct media_entity *sink; + struct media_pad *source_pad; + struct media_pad *sink_pad; + struct mchp_vcpp_graph_entity *g_entity; + struct v4l2_fwnode_link link; + struct device_node *ep_node; + int ret = 0; + u32 link_flags = MEDIA_LNK_FL_ENABLED; + + ep_node = of_graph_get_next_endpoint(node, NULL); + if (!ep_node) + goto done; + + ret = v4l2_fwnode_parse_link(of_fwnode_handle(ep_node), &link); + if (ret < 0) { + dev_err(mchp_vcpp->dev, "failed to parse link for %pOF\n", + ep_node); + goto done; + } + + dev_dbg(mchp_vcpp->dev, "creating link for DMA engine %s\n", + mchp_vcpp->vdev.name); + + g_entity = mchp_vcpp_graph_find_entity(mchp_vcpp, link.remote_node); + if (!g_entity) { + dev_err(mchp_vcpp->dev, "no entity found for %pOF\n", + to_of_node(link.remote_node)); + v4l2_fwnode_put_link(&link); + ret = -ENODEV; + goto done; + } + + if (link.remote_port >= g_entity->entity->num_pads) { + dev_err(mchp_vcpp->dev, "invalid port number %u on %pOF\n", + link.remote_port, to_of_node(link.remote_node)); + v4l2_fwnode_put_link(&link); + ret = -EINVAL; + goto done; + } + + source = g_entity->entity; + source_pad = &source->pads[link.remote_port]; + sink = &mchp_vcpp->vdev.entity; + sink_pad = &mchp_vcpp->vid_cap_pad; + + v4l2_fwnode_put_link(&link); + + dev_dbg(mchp_vcpp->dev, "creating %s:%u -> %s:%u link\n", + source->name, source_pad->index, sink->name, sink_pad->index); + + ret = media_create_pad_link(source, source_pad->index, sink, + sink_pad->index, link_flags); + if (ret < 0) { + dev_err(mchp_vcpp->dev, + "failed to create %s:%u -> %s:%u link\n", source->name, + source_pad->index, sink->name, sink_pad->index); + } + +done: + return ret; +} + +static int mchp_vcpp_graph_notify_complete(struct v4l2_async_notifier *notifier) +{ + struct mchp_vcpp_fpga *mchp_vcpp = container_of(notifier->v4l2_dev, + struct mchp_vcpp_fpga, + v4l2_dev); + struct mchp_vcpp_graph_entity *entity; + struct v4l2_async_connection *asc; + int ret; + + dev_info(mchp_vcpp->dev, "notify complete, all subdevices registered\n"); + + list_for_each_entry(asc, &mchp_vcpp->notifier.done_list, asc_entry) { + entity = to_mchp_vcpp_entity(asc); + ret = mchp_vcpp_graph_build_one(mchp_vcpp, entity); + if (ret < 0) + return ret; + } + + ret = mchp_vcpp_graph_build_dma(mchp_vcpp); + if (ret < 0) + return ret; + + ret = v4l2_device_register_subdev_nodes(&mchp_vcpp->v4l2_dev); + if (ret < 0) + dev_err(mchp_vcpp->dev, "failed to register subdev nodes\n"); + + return media_device_register(&mchp_vcpp->mdev); +} + +static const struct v4l2_async_notifier_operations mchp_vcpp_v4l2_async_ops = { + .bound = mchp_vcpp_graph_notify_bound, + .complete = mchp_vcpp_graph_notify_complete, +}; + +static void mchp_vcpp_set_default_format(struct mchp_vcpp_fpga *mchp_vcpp) +{ + struct v4l2_pix_format *pix; + + mchp_vcpp->fmtinfo = mvc_get_format_by_fourcc(MCHP_VCPP_DEF_FORMAT); + mchp_vcpp->format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + pix = &mchp_vcpp->format.fmt.pix; + pix->pixelformat = mchp_vcpp->fmtinfo->fourcc; + pix->colorspace = V4L2_COLORSPACE_SRGB; + pix->field = V4L2_FIELD_NONE; + pix->width = MCHP_VCPP_DEF_WIDTH; + pix->height = MCHP_VCPP_DEF_HEIGHT; + pix->xfer_func = V4L2_XFER_FUNC_NONE; + pix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + pix->quantization = 0; + pix->bytesperline = pix->width * mchp_vcpp->fmtinfo->bpp; + pix->sizeimage = + pix->width * pix->height * mchp_vcpp->fmtinfo->bpp; +} + +static void mchp_vcpp_graph_cleanup(struct mchp_vcpp_fpga *mchp_vcpp) +{ + v4l2_async_nf_unregister(&mchp_vcpp->notifier); + v4l2_async_nf_cleanup(&mchp_vcpp->notifier); +} + +static int mchp_vcpp_graph_parse_one(struct mchp_vcpp_fpga *mchp_vcpp, + struct fwnode_handle *fwnode) +{ + struct fwnode_handle *remote; + struct fwnode_handle *ep_fw_handle = NULL; + int ret = 0; + + dev_dbg(mchp_vcpp->dev, "parsing node %p\n", fwnode); + + while (1) { + struct mchp_vcpp_graph_entity *graph_entity; + + ep_fw_handle = fwnode_graph_get_next_endpoint(fwnode, ep_fw_handle); + if (!ep_fw_handle) + break; + + remote = fwnode_graph_get_remote_port_parent(ep_fw_handle); + if (!remote) { + ret = -EINVAL; + goto err_notifier_cleanup; + } + + fwnode_handle_put(ep_fw_handle); + + if (remote == of_fwnode_handle(mchp_vcpp->dev->of_node) || + mchp_vcpp_graph_find_entity(mchp_vcpp, remote)) { + fwnode_handle_put(remote); + continue; + } + + graph_entity = v4l2_async_nf_add_fwnode(&mchp_vcpp->notifier, + remote, + struct mchp_vcpp_graph_entity); + fwnode_handle_put(remote); + if (IS_ERR(graph_entity)) { + ret = PTR_ERR(graph_entity); + goto err_notifier_cleanup; + } + } + + return 0; + +err_notifier_cleanup: + v4l2_async_nf_cleanup(&mchp_vcpp->notifier); + fwnode_handle_put(ep_fw_handle); + return ret; +} + +static int mchp_vcpp_graph_parse(struct mchp_vcpp_fpga *mchp_vcpp) +{ + struct mchp_vcpp_graph_entity *entity; + struct v4l2_async_connection *asc; + int ret; + + ret = mchp_vcpp_graph_parse_one(mchp_vcpp, of_fwnode_handle(mchp_vcpp->dev->of_node)); + if (ret < 0) { + dev_err(mchp_vcpp->dev, "mchp_vcpp_graph_parse_one error %d\n", ret); + return 0; + } + + list_for_each_entry(asc, &mchp_vcpp->notifier.waiting_list, asc_entry) { + entity = to_mchp_vcpp_entity(asc); + ret = mchp_vcpp_graph_parse_one(mchp_vcpp, entity->asc.match.fwnode); + if (ret < 0) { + v4l2_async_nf_cleanup(&mchp_vcpp->notifier); + break; + } + } + + return ret; +} + +static int mchp_vcpp_graph_init(struct mchp_vcpp_fpga *mchp_vcpp) +{ + int ret; + + /* Parse the graph to extract a list of subdevice DT nodes. */ + ret = mchp_vcpp_graph_parse(mchp_vcpp); + if (ret < 0) { + dev_err(mchp_vcpp->dev, "graph parsing failed\n"); + goto err_graph_cleanup; + } + + if (list_empty(&mchp_vcpp->notifier.waiting_list)) { + dev_err(mchp_vcpp->dev, "no subdev found in graph\n"); + goto err_graph_cleanup; + } + + mchp_vcpp->notifier.ops = &mchp_vcpp_v4l2_async_ops; + + ret = v4l2_async_nf_register(&mchp_vcpp->notifier); + if (ret < 0) { + dev_err(mchp_vcpp->dev, "Failed to register notifier\n"); + goto err_graph_cleanup; + } + + ret = 0; + +err_graph_cleanup: + if (ret < 0) + mchp_vcpp_graph_cleanup(mchp_vcpp); + + return ret; +} + +static int mchp_vcpp_probe(struct platform_device *pdev) +{ + struct mchp_vcpp_fpga *mchp_vcpp; + struct vb2_queue *vb2_q; + struct resource *res; + struct video_device *vdev; + struct v4l2_ctrl_handler *ctrl_hdlr; + int num_clks = ARRAY_SIZE(mchp_vcpp_clks); + int ret; + + mchp_vcpp = devm_kzalloc(&pdev->dev, + sizeof(struct mchp_vcpp_fpga), GFP_KERNEL); + if (!mchp_vcpp) + return dev_err_probe(&pdev->dev, PTR_ERR(mchp_vcpp), + "kzalloc failed\n"); + + mchp_vcpp->clks = devm_kmemdup(&pdev->dev, mchp_vcpp_clks, + sizeof(mchp_vcpp_clks), GFP_KERNEL); + if (!mchp_vcpp->clks) + return dev_err_probe(&pdev->dev, PTR_ERR(mchp_vcpp->clks), + "Failed to get clocks\n"); + + mchp_vcpp->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(mchp_vcpp->base)) + return dev_err_probe(&pdev->dev, PTR_ERR(mchp_vcpp->base), + "could not get mem resource\n"); + + mchp_vcpp->irq = platform_get_irq(pdev, 0); + if (mchp_vcpp->irq <= 0) + return dev_err_probe(&pdev->dev, mchp_vcpp->irq, + "could not get irq\n"); + + ret = clk_bulk_get(&pdev->dev, num_clks, mchp_vcpp->clks); + if (ret) + return ret; + + ret = clk_bulk_prepare_enable(num_clks, mchp_vcpp->clks); + if (ret) + goto err_clk_put; + + mchp_vcpp->reset_gpio = devm_gpiod_get_optional(&pdev->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(mchp_vcpp->reset_gpio)) + return dev_err_probe(&pdev->dev, PTR_ERR(mchp_vcpp->reset_gpio), + "Failed to get reset gpio"); + + if (mchp_vcpp->reset_gpio) + gpiod_set_value_cansleep(mchp_vcpp->reset_gpio, 1); + + writel_relaxed(MCHP_VCPP_CORE_RESET, mchp_vcpp->base + MCHP_VCPP_CTRL_REG); + + mutex_init(&mchp_vcpp->lock); + + mchp_vcpp->pdev = pdev; + mchp_vcpp->dev = &pdev->dev; + + mchp_vcpp_set_default_format(mchp_vcpp); + + ret = v4l2_device_register(&pdev->dev, &mchp_vcpp->v4l2_dev); + if (ret) + return ret; + + ctrl_hdlr = &mchp_vcpp->ctrl_handler; + + v4l2_ctrl_handler_init(ctrl_hdlr, MCHP_VCPP_NUM_CTRLS); + + mchp_vcpp->compression_ratio_ctrl = v4l2_ctrl_new_custom(ctrl_hdlr, + &mchp_vcpp_ctrls[0], + NULL); + if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; + goto v4l2_unregister; + } + + mchp_vcpp->v4l2_dev.ctrl_handler = ctrl_hdlr; + + INIT_LIST_HEAD(&mchp_vcpp->buf_list); + spin_lock_init(&mchp_vcpp->qlock); + + vdev = &mchp_vcpp->vdev; + strscpy(vdev->name, KBUILD_MODNAME, sizeof(vdev->name)); + vdev->release = video_device_release_empty; + vdev->fops = &mchp_vcpp_fops, + vdev->ioctl_ops = &mchp_vcpp_ioctl_ops, + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + vdev->lock = &mchp_vcpp->lock; + vdev->queue = &mchp_vcpp->queue; + vdev->v4l2_dev = &mchp_vcpp->v4l2_dev; + vdev->vfl_dir = VFL_DIR_RX; + vdev->vfl_type = VFL_TYPE_VIDEO; + + /* Initialize media device */ + strscpy(mchp_vcpp->mdev.model, MCHP_VCPP_DRV_NAME, sizeof(mchp_vcpp->mdev.model)); + snprintf(mchp_vcpp->mdev.bus_info, sizeof(mchp_vcpp->mdev.bus_info), + "platform:%s", MCHP_VCPP_DRV_NAME); + + mchp_vcpp->mdev.dev = &mchp_vcpp->pdev->dev; + media_device_init(&mchp_vcpp->mdev); + mchp_vcpp->v4l2_dev.mdev = &mchp_vcpp->mdev; + + /* Media entity pads */ + mchp_vcpp->vid_cap_pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&mchp_vcpp->vdev.entity, + 1, &mchp_vcpp->vid_cap_pad); + if (ret) { + dev_err(mchp_vcpp->dev, "Failed to init media entity pad\n"); + goto v4l2_unregister; + } + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + dev_err(mchp_vcpp->dev, "video register device failed %d\n", + ret); + goto v4l2_unregister; + } + + vb2_q = &mchp_vcpp->queue; + vb2_q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vb2_q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ | VB2_USERPTR; + vb2_q->dev = mchp_vcpp->dev; + vb2_q->drv_priv = mchp_vcpp; + vb2_q->buf_struct_size = sizeof(struct mchp_vcpp_buffer); + vb2_q->ops = &mchp_vcpp_qops; + vb2_q->mem_ops = &vb2_dma_contig_memops; + vb2_q->allow_cache_hints = 1; + vb2_q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + vb2_q->min_buffers_needed = 2; + vb2_q->lock = &mchp_vcpp->lock; + + ret = vb2_queue_init(vb2_q); + if (ret) { + dev_err(mchp_vcpp->dev, "vb2 queue init failed %d\n", ret); + goto v4l2_unregister; + } + + v4l2_async_nf_init(&mchp_vcpp->notifier, &mchp_vcpp->v4l2_dev); + + ret = mchp_vcpp_graph_init(mchp_vcpp); + if (ret < 0) { + dev_err(mchp_vcpp->dev, "mchp dscmi graph init failed %d\n", ret); + goto v4l2_unregister; + } + + ret = of_reserved_mem_device_init(&pdev->dev); + if (ret) + dev_dbg(&pdev->dev, "of_reserved_mem_device_init: %d\n", ret); + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (ret) { + dev_err(&pdev->dev, "dma_set_mask_and_coherent: %d\n", ret); + goto v4l2_unregister; + } + + platform_set_drvdata(pdev, mchp_vcpp); + + video_set_drvdata(vdev, mchp_vcpp); + + return 0; + +v4l2_unregister: + mutex_destroy(&mchp_vcpp->lock); + v4l2_device_unregister(&mchp_vcpp->v4l2_dev); +err_clk_put: + clk_bulk_put(num_clks, mchp_vcpp->clks); + + return ret; +} + +static int mchp_vcpp_remove(struct platform_device *pdev) +{ + struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); + struct mchp_vcpp_fpga *mchp_vcpp = container_of(v4l2_dev, + struct mchp_vcpp_fpga, + v4l2_dev); + int num_clks = ARRAY_SIZE(mchp_vcpp_clks); + + mutex_destroy(&mchp_vcpp->lock); + v4l2_async_nf_unregister(&mchp_vcpp->notifier); + v4l2_async_nf_cleanup(&mchp_vcpp->notifier); + v4l2_device_unregister(&mchp_vcpp->v4l2_dev); + clk_bulk_disable_unprepare(num_clks, mchp_vcpp->clks); + clk_bulk_put(num_clks, mchp_vcpp->clks); + + return 0; +} + +static const struct of_device_id mchp_vcpp_of_match[] = { + { .compatible = "microchip,video-dma-rtl-v0" }, + {} +}; + +MODULE_DEVICE_TABLE(of, mchp_vcpp_of_match); + +static struct platform_driver mchp_vcpp_driver = { + .probe = mchp_vcpp_probe, + .remove = mchp_vcpp_remove, + .driver = { + .name = MCHP_VCPP_DRV_NAME, + .of_match_table = mchp_vcpp_of_match, + }, +}; + +module_platform_driver(mchp_vcpp_driver); + +MODULE_DESCRIPTION("Microchip Video Capture Pipeline Processing (Video DMA) Driver"); +MODULE_AUTHOR("Shravan Chippa <shravan.chippa@microchip.com>"); +MODULE_LICENSE("GPL"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/media/platform/microchip/microchip-yuv2h264.c linux-microchip/drivers/media/platform/microchip/microchip-yuv2h264.c --- linux-6.6.51/drivers/media/platform/microchip/microchip-yuv2h264.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/media/platform/microchip/microchip-yuv2h264.c 2024-11-26 14:02:37.610495703 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microchip H264 Encoder Driver. + * + * Copyright (C) 2022-2023 Microchip Technology Inc. and its subsidiaries + * Author: Shravan Chippa <shavan.chippa@microchip.com> + * + */ + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +#include <media/v4l2-async.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> + +#include "microchip-common.h" + +#include <dt-bindings/media/microchip-common.h> + +#define MCHP_YUV2H264_MIN_WIDTH 32 +#define MCHP_YUV2H264_MAX_WIDTH 4096 +#define MCHP_YUV2H264_MIN_HEIGHT 32 +#define MCHP_YUV2H264_MAX_HEIGHT 4096 + +#define MCHP_YUV2H264_IP_VER 0x00 +#define MCHP_YUV2H264_CTRL 0x04 +#define MCHP_YUV2H264_CTRL_START BIT(0) +#define MCHP_YUV2H264_CTRL_RESET BIT(1) + +#define MCHP_YUV2H264_IP_TYPE 0x08 +#define MCHP_YUV2H264_Q_FACTOR 0x0C +#define MCHP_YUV2H264_I_P_FRAME_GAP 0x14 +#define MCHP_YUV2H264_H_RES 0x18 +#define MCHP_YUV2H264_V_RES 0x1C + +#define MCHP_CTL_STEP 1 +#define MCHP_Q_FACTOR_CTL_MAX 52 +#define MCHP_Q_FACTOR_CTL_MIN 25 +#define MCHP_Q_FACTOR_CTL_DEFAULT 30 + +#define MCHP_P_COUNT_CTL_MAX 255 +#define MCHP_P_COUNT_CTL_MIN 0 +#define MCHP_P_COUNT_CTL_DEFAULT 0 + +#define MCHP_YUV2H264_DEFAULT_WIDTH 1920 +#define MCHP_YUV2H264_DEFAULT_HEIGHT 1080 + +#define MCHP_YUV2H264_DEF_IN_FORMAT MVCF_YUV_422 +#define MCHP_YUV2H264_DEF_OUT_FORMAT MVCF_H264 + +/** + * struct mchp_yuv2h264_device - Microchip YUV2H264 device structure + * @dev: device + * @subdev: The v4l2 subdev structure + * @iomem: Base address of subsystem + * @pads: media pads + * @formats: V4L2 media bus formats at the sink and source pads + * @default_formats: default V4L2 media bus formats + * @vip_formats: Microchip Video IP formats + * @q_factor: Quality factor + * @p_count: P-frame count + * @ctrl_handler: control handler + */ +struct mchp_yuv2h264_device { + struct device *dev; + struct v4l2_subdev subdev; + void __iomem *iomem; + + struct media_pad pads[2]; + + struct v4l2_mbus_framefmt formats[2]; + struct v4l2_mbus_framefmt default_formats[2]; + const struct mvideo_format *vip_formats[2]; + struct v4l2_ctrl *q_factor; + struct v4l2_ctrl *p_count; + + struct v4l2_ctrl_handler ctrl_handler; +}; + +static inline struct mchp_yuv2h264_device *to_mchp_yuv2h264(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct mchp_yuv2h264_device, subdev); +} + +static inline u32 mchp_yuv2h264_read(struct mchp_yuv2h264_device *yuv2h264, + u32 addr) +{ + return readl(yuv2h264->iomem + addr); +} + +static inline void mchp_yuv2h264_write(struct mchp_yuv2h264_device *yuv2h264, + u32 addr, u32 value) +{ + writel(value, yuv2h264->iomem + addr); +} + +static inline void mchp_yuv2h264_clr(struct mchp_yuv2h264_device *yuv2h264, + u32 addr, u32 clr) +{ + mchp_yuv2h264_write(yuv2h264, addr, + mchp_yuv2h264_read(yuv2h264, addr) & ~clr); +} + +static inline void mchp_yuv2h264_set(struct mchp_yuv2h264_device *yuv2h264, + u32 addr, u32 set) +{ + mchp_yuv2h264_write(yuv2h264, addr, mchp_yuv2h264_read(yuv2h264, addr) | set); +} + +static int mchp_yuv2h264_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct mchp_yuv2h264_device *yuv2h264 = to_mchp_yuv2h264(subdev); + struct v4l2_mbus_framefmt *format; + + mchp_yuv2h264_set(yuv2h264, MCHP_YUV2H264_CTRL, MCHP_YUV2H264_CTRL_RESET); + + if (!enable) { + mchp_yuv2h264_clr(yuv2h264, MCHP_YUV2H264_CTRL, MCHP_YUV2H264_CTRL_START); + return 0; + } + + format = &yuv2h264->formats[MVC_PAD_SINK]; + + mchp_yuv2h264_write(yuv2h264, MCHP_YUV2H264_H_RES, format->width); + mchp_yuv2h264_write(yuv2h264, MCHP_YUV2H264_V_RES, format->height); + mchp_yuv2h264_write(yuv2h264, MCHP_YUV2H264_Q_FACTOR, yuv2h264->q_factor->val); + mchp_yuv2h264_write(yuv2h264, MCHP_YUV2H264_I_P_FRAME_GAP, yuv2h264->p_count->val); + + mchp_yuv2h264_set(yuv2h264, MCHP_YUV2H264_CTRL, MCHP_YUV2H264_CTRL_START); + + return 0; +} + +static struct v4l2_mbus_framefmt * +__mchp_yuv2h264_get_pad_format(struct mchp_yuv2h264_device *yuv2h264, + struct v4l2_subdev_state *sd_state, + unsigned int pad, u32 which) +{ + struct v4l2_mbus_framefmt *format; + + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + format = v4l2_subdev_get_try_format(&yuv2h264->subdev, + sd_state, pad); + break; + case V4L2_SUBDEV_FORMAT_ACTIVE: + format = &yuv2h264->formats[pad]; + break; + default: + format = NULL; + break; + } + + return format; +} + +static int mchp_yuv2h264_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct mchp_yuv2h264_device *yuv2h264 = to_mchp_yuv2h264(subdev); + struct v4l2_mbus_framefmt *format; + + format = __mchp_yuv2h264_get_pad_format(yuv2h264, sd_state, fmt->pad, + fmt->which); + if (!format) + return -EINVAL; + + fmt->format = *format; + + return 0; +} + +static int mchp_yuv2h264_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *req_fmt) +{ + struct mchp_yuv2h264_device *yuv2h264 = to_mchp_yuv2h264(subdev); + struct v4l2_mbus_framefmt *format; + + format = __mchp_yuv2h264_get_pad_format(yuv2h264, sd_state, req_fmt->pad, + req_fmt->which); + if (!format) + return -EINVAL; + + if (req_fmt->pad == MVC_PAD_SOURCE) { + req_fmt->format = *format; + return 0; + } + + /* Propagate the format to the source pad. */ + format = __mchp_yuv2h264_get_pad_format(yuv2h264, sd_state, MVC_PAD_SOURCE, + req_fmt->which); + + format->width = req_fmt->format.width; + format->height = req_fmt->format.height; + + v4l_bound_align_image(&format->width, + 16, MCHP_YUV2H264_MAX_WIDTH, 0, + &format->height, + 16, MCHP_YUV2H264_MAX_HEIGHT, 0, 0); + + if (req_fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + yuv2h264->formats[MVC_PAD_SINK].width = format->width; + yuv2h264->formats[MVC_PAD_SINK].height = format->height; + yuv2h264->formats[MVC_PAD_SOURCE].width = format->width; + yuv2h264->formats[MVC_PAD_SOURCE].height = format->height; + + mchp_yuv2h264_write(yuv2h264, MCHP_YUV2H264_H_RES, format->width); + mchp_yuv2h264_write(yuv2h264, MCHP_YUV2H264_V_RES, format->height); + + return 0; +} + +static int mchp_yuv2h264_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + struct mchp_yuv2h264_device *yuv2h264 = to_mchp_yuv2h264(subdev); + struct v4l2_mbus_framefmt *format; + + /* Initialize with default formats */ + format = v4l2_subdev_get_try_format(subdev, fh->state, MVC_PAD_SINK); + *format = yuv2h264->default_formats[MVC_PAD_SINK]; + + format = v4l2_subdev_get_try_format(subdev, fh->state, MVC_PAD_SOURCE); + *format = yuv2h264->default_formats[MVC_PAD_SOURCE]; + + return 0; +} + +static int mchp_yuv2h264_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) +{ + return 0; +} + +static int mchp_yuv2h264_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mchp_yuv2h264_device *yuv2h264 = + container_of(ctrl->handler, struct mchp_yuv2h264_device, + ctrl_handler); + + switch (ctrl->id) { + case MCHP_CID_Q_FACTOR: + mchp_yuv2h264_write(yuv2h264, MCHP_YUV2H264_Q_FACTOR, ctrl->val); + return 0; + case MCHP_CID_P_COUNT: + mchp_yuv2h264_write(yuv2h264, MCHP_YUV2H264_I_P_FRAME_GAP, ctrl->val); + return 0; + } + + return -EINVAL; +} + +static const struct v4l2_ctrl_ops mchp_yuv2h264_ctrl_ops = { + .s_ctrl = mchp_yuv2h264_s_ctrl, +}; + +static const struct v4l2_subdev_video_ops mchp_yuv2h264_video_ops = { + .s_stream = mchp_yuv2h264_s_stream, +}; + +static const struct v4l2_subdev_pad_ops mchp_yuv2h264_pad_ops = { + .enum_mbus_code = mvc_enum_mbus_code, + .enum_frame_size = mvc_enum_frame_size, + .get_fmt = mchp_yuv2h264_get_format, + .set_fmt = mchp_yuv2h264_set_format, +}; + +static const struct v4l2_subdev_ops mchp_yuv2h264_ops = { + .video = &mchp_yuv2h264_video_ops, + .pad = &mchp_yuv2h264_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops mchp_yuv2h264_internal_ops = { + .open = mchp_yuv2h264_open, + .close = mchp_yuv2h264_close, +}; + +/* + * Control Configs + */ +static struct v4l2_ctrl_config mchp_yuv2h264_ctrls[] = { + { + .ops = &mchp_yuv2h264_ctrl_ops, + .id = MCHP_CID_Q_FACTOR, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Quality Factor", + .min = MCHP_Q_FACTOR_CTL_MIN, + .max = MCHP_Q_FACTOR_CTL_MAX, + .def = MCHP_Q_FACTOR_CTL_DEFAULT, + .step = MCHP_CTL_STEP, + }, { + .ops = &mchp_yuv2h264_ctrl_ops, + .id = MCHP_CID_P_COUNT, + .name = "P frame count", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = MCHP_P_COUNT_CTL_MIN, + .max = MCHP_P_COUNT_CTL_MAX, + .def = MCHP_P_COUNT_CTL_DEFAULT, + .step = MCHP_CTL_STEP, + }, +}; + +static const struct media_entity_operations mchp_yuv2h264_media_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static int mchp_yuv2h264_init_formats(struct mchp_yuv2h264_device *yuv2h264) +{ + struct device *dev = yuv2h264->dev; + const struct mvideo_format *vip_format; + + vip_format = mvc_get_format_by_vf_code(MCHP_YUV2H264_DEF_IN_FORMAT); + if (IS_ERR(vip_format)) + return dev_err_probe(dev, PTR_ERR(vip_format), "invalid format"); + + yuv2h264->vip_formats[MVC_PAD_SINK] = vip_format; + + vip_format = mvc_get_format_by_vf_code(MCHP_YUV2H264_DEF_OUT_FORMAT); + if (IS_ERR(vip_format)) + return dev_err_probe(dev, PTR_ERR(vip_format), "invalid format"); + + yuv2h264->vip_formats[MVC_PAD_SOURCE] = vip_format; + + return 0; +} + +static int mchp_yuv2h264_probe(struct platform_device *pdev) +{ + struct mchp_yuv2h264_device *yuv2h264; + struct v4l2_subdev *subdev; + struct v4l2_mbus_framefmt *default_format; + int ret; + + yuv2h264 = devm_kzalloc(&pdev->dev, sizeof(*yuv2h264), GFP_KERNEL); + if (!yuv2h264) + return -ENOMEM; + + yuv2h264->dev = &pdev->dev; + + ret = mchp_yuv2h264_init_formats(yuv2h264); + if (ret < 0) + return ret; + + yuv2h264->iomem = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(yuv2h264->iomem)) + return dev_err_probe(&pdev->dev, PTR_ERR(yuv2h264->iomem), + "could not get mem resource\n"); + + /* Initialize V4L2 subdevice and media entity */ + subdev = &yuv2h264->subdev; + v4l2_subdev_init(subdev, &mchp_yuv2h264_ops); + subdev->dev = &pdev->dev; + subdev->internal_ops = &mchp_yuv2h264_internal_ops; + strscpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name)); + v4l2_set_subdevdata(subdev, yuv2h264); + subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + /* Initialize default and active formats */ + default_format = &yuv2h264->default_formats[MVC_PAD_SINK]; + default_format->code = yuv2h264->vip_formats[MVC_PAD_SINK]->code; + default_format->field = V4L2_FIELD_NONE; + default_format->colorspace = V4L2_COLORSPACE_SRGB; + default_format->width = MCHP_YUV2H264_DEFAULT_WIDTH; + default_format->height = MCHP_YUV2H264_DEFAULT_HEIGHT; + + yuv2h264->formats[MVC_PAD_SINK] = *default_format; + + default_format = &yuv2h264->default_formats[MVC_PAD_SOURCE]; + *default_format = yuv2h264->default_formats[MVC_PAD_SINK]; + default_format->code = yuv2h264->vip_formats[MVC_PAD_SOURCE]->code; + + yuv2h264->formats[MVC_PAD_SOURCE] = *default_format; + + yuv2h264->pads[MVC_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + yuv2h264->pads[MVC_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + subdev->entity.ops = &mchp_yuv2h264_media_ops; + ret = media_entity_pads_init(&subdev->entity, 2, yuv2h264->pads); + if (ret < 0) + return ret; + + v4l2_ctrl_handler_init(&yuv2h264->ctrl_handler, 2); + + yuv2h264->q_factor = v4l2_ctrl_new_custom(&yuv2h264->ctrl_handler, + &mchp_yuv2h264_ctrls[0], NULL); + + yuv2h264->p_count = v4l2_ctrl_new_custom(&yuv2h264->ctrl_handler, + &mchp_yuv2h264_ctrls[1], NULL); + + if (yuv2h264->ctrl_handler.error) { + dev_err(&pdev->dev, "failed to add controls\n"); + ret = yuv2h264->ctrl_handler.error; + goto media_error; + } + subdev->ctrl_handler = &yuv2h264->ctrl_handler; + + platform_set_drvdata(pdev, yuv2h264); + + ret = v4l2_async_register_subdev(subdev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register subdev\n"); + goto error; + } + + return 0; + +error: + v4l2_ctrl_handler_free(&yuv2h264->ctrl_handler); +media_error: + media_entity_cleanup(&subdev->entity); + return ret; +} + +static int mchp_yuv2h264_remove(struct platform_device *pdev) +{ + struct mchp_yuv2h264_device *yuv2h264 = platform_get_drvdata(pdev); + struct v4l2_subdev *subdev = &yuv2h264->subdev; + + v4l2_async_unregister_subdev(subdev); + v4l2_ctrl_handler_free(&yuv2h264->ctrl_handler); + media_entity_cleanup(&subdev->entity); + + return 0; +} + +static const struct of_device_id mchp_yuv2h264_of_id_table[] = { + { .compatible = "microchip,yuv2h264-rtl-v1" }, + { } +}; +MODULE_DEVICE_TABLE(of, mchp_yuv2h264_of_id_table); + +static struct platform_driver mchp_yuv2h264_driver = { + .driver = { + .name = "microchip-yuv2h264", + .of_match_table = mchp_yuv2h264_of_id_table, + }, + .probe = mchp_yuv2h264_probe, + .remove = mchp_yuv2h264_remove, +}; + +module_platform_driver(mchp_yuv2h264_driver); + +MODULE_AUTHOR("Shravan Chippa <shravan.chippa@microchip.com>"); +MODULE_DESCRIPTION("Microchip H264 Encoder Driver"); +MODULE_LICENSE("GPL"); + diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mfd/atmel-hlcdc.c linux-microchip/drivers/mfd/atmel-hlcdc.c --- linux-6.6.51/drivers/mfd/atmel-hlcdc.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/mfd/atmel-hlcdc.c 2024-11-26 14:02:37.686497064 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:48 @ ret = readl_poll_timeout_atomic(hregmap->regs + ATMEL_HLCDC_SR, status, !(status & ATMEL_HLCDC_SIP), - 1, 100); + 1, 400); if (ret) { dev_err(hregmap->dev, "Timeout! Clock domain synchronization is in progress!\n"); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:111 @ return PTR_ERR(hlcdc->periph_clk); } + /* + * Obtain one of the essential clocks (GCK / LVDS PLL) necessary for + * the LCD to function. GCK for PDA and MIPI Display, LVDS PLL for LVDS + * Display. DT is expected to have any one of the clocks, not both. + */ + hlcdc->sys_clk = NULL; + hlcdc->lvds_pll_clk = NULL; hlcdc->sys_clk = devm_clk_get(dev, "sys_clk"); if (IS_ERR(hlcdc->sys_clk)) { - dev_err(dev, "failed to get system clock\n"); - return PTR_ERR(hlcdc->sys_clk); + dev_dbg(dev, "failed to get sys_clk\n"); + hlcdc->sys_clk = NULL; + hlcdc->lvds_pll_clk = devm_clk_get(dev, "lvds_pll_clk"); + if (IS_ERR(hlcdc->lvds_pll_clk)) { + dev_err(dev, "failed to get GCK and LVDS_PLL\n"); + return PTR_ERR(hlcdc->lvds_pll_clk); + } } hlcdc->slow_clk = devm_clk_get(dev, "slow_clk"); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:154 @ { .compatible = "atmel,sama5d3-hlcdc" }, { .compatible = "atmel,sama5d4-hlcdc" }, { .compatible = "microchip,sam9x60-hlcdc" }, + { .compatible = "microchip,sam9x75-xlcdc" }, + { .compatible = "microchip,sama7d65-xlcdc" }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, atmel_hlcdc_match); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mfd/simple-mfd-i2c.c linux-microchip/drivers/mfd/simple-mfd-i2c.c --- linux-6.6.51/drivers/mfd/simple-mfd-i2c.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/mfd/simple-mfd-i2c.c 2024-11-26 14:04:18.560303308 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:91 @ { .compatible = "silergy,sy7636a", .data = &silergy_sy7636a}, { .compatible = "maxim,max5970", .data = &maxim_max5970}, { .compatible = "maxim,max5978", .data = &maxim_max5970}, + { .compatible = "raspberrypi,sensehat" }, {} }; MODULE_DEVICE_TABLE(of, simple_mfd_i2c_of_match); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mfd/syscon.c linux-microchip/drivers/mfd/syscon.c --- linux-6.6.51/drivers/mfd/syscon.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/mfd/syscon.c 2024-11-26 14:02:37.694497208 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:45 @ .reg_stride = 4, }; -static struct syscon *of_syscon_register(struct device_node *np, bool check_res) +static void syscon_add(struct syscon *syscon) +{ + spin_lock(&syscon_list_slock); + list_add_tail(&syscon->list, &syscon_list); + spin_unlock(&syscon_list_slock); +} + +static struct syscon *of_syscon_register_mmio(struct device_node *np, + bool check_res) { struct clk *clk; struct syscon *syscon; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:159 @ syscon->regmap = regmap; syscon->np = np; - spin_lock(&syscon_list_slock); - list_add_tail(&syscon->list, &syscon_list); - spin_unlock(&syscon_list_slock); - return syscon; err_reset: @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:175 @ return ERR_PTR(ret); } +#ifdef CONFIG_REGMAP_SMCCC +static struct syscon *of_syscon_register_smccc(struct device_node *np) +{ + struct syscon *syscon; + struct regmap *regmap; + u32 reg_io_width = 4, smc_id; + int ret; + struct regmap_config syscon_config = syscon_regmap_config; + + ret = of_property_read_u32(np, "arm,smc-id", &smc_id); + if (ret) + return ERR_PTR(-ENODEV); + + syscon = kzalloc(sizeof(*syscon), GFP_KERNEL); + if (!syscon) + return ERR_PTR(-ENOMEM); + + of_property_read_u32(np, "reg-io-width", ®_io_width); + + syscon_config.name = kasprintf(GFP_KERNEL, "%pOFn@smc%x", np, smc_id); + syscon_config.val_bits = reg_io_width * 8; + + regmap = regmap_init_smccc(NULL, smc_id, &syscon_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + goto err_regmap; + } + + syscon->regmap = regmap; + syscon->np = np; + + return syscon; + +err_regmap: + kfree(syscon_config.name); + kfree(syscon); + + return ERR_PTR(ret); +} +#endif + static struct regmap *device_node_get_regmap(struct device_node *np, - bool check_res) + bool check_clk, bool use_smccc) { struct syscon *entry, *syscon = NULL; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:231 @ spin_unlock(&syscon_list_slock); - if (!syscon) - syscon = of_syscon_register(np, check_res); + if (!syscon) { + if (use_smccc) +#ifdef CONFIG_REGMAP_SMCCC + syscon = of_syscon_register_smccc(np); +#else + syscon = NULL; +#endif + else + syscon = of_syscon_register_mmio(np, check_clk); + + if (!IS_ERR(syscon)) + syscon_add(syscon); + } if (IS_ERR(syscon)) return ERR_CAST(syscon); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:253 @ struct regmap *device_node_to_regmap(struct device_node *np) { - return device_node_get_regmap(np, false); + return device_node_get_regmap(np, false, false); } EXPORT_SYMBOL_GPL(device_node_to_regmap); struct regmap *syscon_node_to_regmap(struct device_node *np) { - if (!of_device_is_compatible(np, "syscon")) - return ERR_PTR(-EINVAL); + if (of_device_is_compatible(np, "syscon")) + return device_node_get_regmap(np, true, false); + + if (of_device_is_compatible(np, "syscon-smc")) + return device_node_get_regmap(np, true, true); - return device_node_get_regmap(np, true); + return ERR_PTR(-EINVAL); } EXPORT_SYMBOL_GPL(syscon_node_to_regmap); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:355 @ } EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_phandle_optional); -static int syscon_probe(struct platform_device *pdev) +struct syscon_driver_data { + int (*probe_func)(struct platform_device *pdev, struct device *dev, + struct syscon *syscon); +}; + +static int syscon_probe_mmio(struct platform_device *pdev, + struct device *dev, + struct syscon *syscon) { - struct device *dev = &pdev->dev; - struct syscon_platform_data *pdata = dev_get_platdata(dev); - struct syscon *syscon; struct regmap_config syscon_config = syscon_regmap_config; struct resource *res; void __iomem *base; - syscon = devm_kzalloc(dev, sizeof(*syscon), GFP_KERNEL); - if (!syscon) - return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -ENOENT; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:377 @ return -ENOMEM; syscon_config.max_register = resource_size(res) - 4; - if (pdata) - syscon_config.name = pdata->label; + syscon->regmap = devm_regmap_init_mmio(dev, base, &syscon_config); if (IS_ERR(syscon->regmap)) { dev_err(dev, "regmap init failed\n"); return PTR_ERR(syscon->regmap); } - platform_set_drvdata(pdev, syscon); + dev_dbg(dev, "regmap_mmio %pR registered\n", res); + + return 0; +} + +static const struct syscon_driver_data syscon_mmio_data = { + .probe_func = &syscon_probe_mmio, +}; + +#ifdef CONFIG_REGMAP_SMCCC + +static int syscon_probe_smc(struct platform_device *pdev, + struct device *dev, + struct syscon *syscon) +{ + struct regmap_config syscon_config = syscon_regmap_config; + int smc_id, ret; + + ret = of_property_read_u32(dev->of_node, "arm,smc-id", &smc_id); + if (!ret) + return -ENODEV; + + syscon->regmap = devm_regmap_init_smccc(dev, smc_id, &syscon_config); + if (IS_ERR(syscon->regmap)) { + dev_err(dev, "regmap init failed\n"); + return PTR_ERR(syscon->regmap); + } - dev_dbg(dev, "regmap %pR registered\n", res); + dev_dbg(dev, "regmap_smccc %x registered\n", smc_id); + + return 0; +} + +static const struct syscon_driver_data syscon_smc_data = { + .probe_func = &syscon_probe_smc, +}; +#endif + +static int syscon_probe(struct platform_device *pdev) +{ + int ret; + struct device *dev = &pdev->dev; + struct syscon_platform_data *pdata = dev_get_platdata(dev); + struct regmap_config syscon_config = syscon_regmap_config; + struct syscon *syscon; + const struct syscon_driver_data *driver_data; + + if (pdata) + syscon_config.name = pdata->label; + + syscon = devm_kzalloc(dev, sizeof(*syscon), GFP_KERNEL); + if (!syscon) + return -ENOMEM; + + driver_data = (const struct syscon_driver_data *) + platform_get_device_id(pdev)->driver_data; + + ret = driver_data->probe_func(pdev, dev, syscon); + if (ret) + return ret; + + platform_set_drvdata(pdev, syscon); return 0; } static const struct platform_device_id syscon_ids[] = { - { "syscon", }, + { .name = "syscon", .driver_data = (kernel_ulong_t)&syscon_mmio_data}, +#ifdef CONFIG_REGMAP_SMCCC + { .name = "syscon-smc", .driver_data = (kernel_ulong_t)&syscon_smc_data}, +#endif { } }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/misc/eeprom/at24.c linux-microchip/drivers/misc/eeprom/at24.c --- linux-6.6.51/drivers/misc/eeprom/at24.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/misc/eeprom/at24.c 2024-11-26 14:02:37.702497352 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:123 @ u32 byte_len; u8 flags; u8 bank_addr_shift; + u8 adjoff; void (*read_post)(unsigned int off, char *buf, size_t count); }; -#define AT24_CHIP_DATA(_name, _len, _flags) \ +#define AT24_CHIP_DATA(_name, _len, _flags, _adjoff) \ static const struct at24_chip_data _name = { \ - .byte_len = _len, .flags = _flags, \ + .byte_len = _len, .flags = _flags, .adjoff = _adjoff, \ } -#define AT24_CHIP_DATA_CB(_name, _len, _flags, _read_post) \ +#define AT24_CHIP_DATA_CB(_name, _len, _flags, _adjoff, _read_post) \ static const struct at24_chip_data _name = { \ .byte_len = _len, .flags = _flags, \ + .adjoff = _adjoff, \ .read_post = _read_post, \ } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:166 @ } /* needs 8 addresses as A0-A2 are ignored */ -AT24_CHIP_DATA(at24_data_24c00, 128 / 8, AT24_FLAG_TAKE8ADDR); +AT24_CHIP_DATA(at24_data_24c00, 128 / 8, AT24_FLAG_TAKE8ADDR, 0); /* old variants can't be handled with this generic entry! */ -AT24_CHIP_DATA(at24_data_24c01, 1024 / 8, 0); +AT24_CHIP_DATA(at24_data_24c01, 1024 / 8, 0, 0); AT24_CHIP_DATA(at24_data_24cs01, 16, - AT24_FLAG_SERIAL | AT24_FLAG_READONLY); -AT24_CHIP_DATA(at24_data_24c02, 2048 / 8, 0); + AT24_FLAG_SERIAL | AT24_FLAG_READONLY, 0); +AT24_CHIP_DATA(at24_data_24c02, 2048 / 8, 0, 0); AT24_CHIP_DATA(at24_data_24cs02, 16, - AT24_FLAG_SERIAL | AT24_FLAG_READONLY); + AT24_FLAG_SERIAL | AT24_FLAG_READONLY, 0); AT24_CHIP_DATA(at24_data_24mac402, 48 / 8, - AT24_FLAG_MAC | AT24_FLAG_READONLY); + AT24_FLAG_MAC | AT24_FLAG_READONLY, 1); AT24_CHIP_DATA(at24_data_24mac602, 64 / 8, - AT24_FLAG_MAC | AT24_FLAG_READONLY); + AT24_FLAG_MAC | AT24_FLAG_READONLY, 1); +AT24_CHIP_DATA(at24_data_24mac02e4, 48 / 8, + AT24_FLAG_MAC | AT24_FLAG_READONLY, 0); +AT24_CHIP_DATA(at24_data_24mac02e6, 64 / 8, + AT24_FLAG_MAC | AT24_FLAG_READONLY, 0); /* spd is a 24c02 in memory DIMMs */ AT24_CHIP_DATA(at24_data_spd, 2048 / 8, - AT24_FLAG_READONLY | AT24_FLAG_IRUGO); + AT24_FLAG_READONLY | AT24_FLAG_IRUGO, 0); /* 24c02_vaio is a 24c02 on some Sony laptops */ AT24_CHIP_DATA_CB(at24_data_24c02_vaio, 2048 / 8, - AT24_FLAG_READONLY | AT24_FLAG_IRUGO, + AT24_FLAG_READONLY | AT24_FLAG_IRUGO, 0, at24_read_post_vaio); -AT24_CHIP_DATA(at24_data_24c04, 4096 / 8, 0); +AT24_CHIP_DATA(at24_data_24c04, 4096 / 8, 0, 0); AT24_CHIP_DATA(at24_data_24cs04, 16, - AT24_FLAG_SERIAL | AT24_FLAG_READONLY); + AT24_FLAG_SERIAL | AT24_FLAG_READONLY, 0); /* 24rf08 quirk is handled at i2c-core */ -AT24_CHIP_DATA(at24_data_24c08, 8192 / 8, 0); +AT24_CHIP_DATA(at24_data_24c08, 8192 / 8, 0, 0); AT24_CHIP_DATA(at24_data_24cs08, 16, - AT24_FLAG_SERIAL | AT24_FLAG_READONLY); -AT24_CHIP_DATA(at24_data_24c16, 16384 / 8, 0); + AT24_FLAG_SERIAL | AT24_FLAG_READONLY, 0); +AT24_CHIP_DATA(at24_data_24c16, 16384 / 8, 0, 0); AT24_CHIP_DATA(at24_data_24cs16, 16, - AT24_FLAG_SERIAL | AT24_FLAG_READONLY); -AT24_CHIP_DATA(at24_data_24c32, 32768 / 8, AT24_FLAG_ADDR16); + AT24_FLAG_SERIAL | AT24_FLAG_READONLY, 0); +AT24_CHIP_DATA(at24_data_24c32, 32768 / 8, AT24_FLAG_ADDR16, 0); AT24_CHIP_DATA(at24_data_24cs32, 16, - AT24_FLAG_ADDR16 | AT24_FLAG_SERIAL | AT24_FLAG_READONLY); -AT24_CHIP_DATA(at24_data_24c64, 65536 / 8, AT24_FLAG_ADDR16); + AT24_FLAG_ADDR16 | AT24_FLAG_SERIAL | AT24_FLAG_READONLY, 0); +AT24_CHIP_DATA(at24_data_24c64, 65536 / 8, AT24_FLAG_ADDR16, 0); AT24_CHIP_DATA(at24_data_24cs64, 16, - AT24_FLAG_ADDR16 | AT24_FLAG_SERIAL | AT24_FLAG_READONLY); -AT24_CHIP_DATA(at24_data_24c128, 131072 / 8, AT24_FLAG_ADDR16); -AT24_CHIP_DATA(at24_data_24c256, 262144 / 8, AT24_FLAG_ADDR16); -AT24_CHIP_DATA(at24_data_24c512, 524288 / 8, AT24_FLAG_ADDR16); -AT24_CHIP_DATA(at24_data_24c1024, 1048576 / 8, AT24_FLAG_ADDR16); + AT24_FLAG_ADDR16 | AT24_FLAG_SERIAL | AT24_FLAG_READONLY, 0); +AT24_CHIP_DATA(at24_data_24c128, 131072 / 8, AT24_FLAG_ADDR16, 0); +AT24_CHIP_DATA(at24_data_24c256, 262144 / 8, AT24_FLAG_ADDR16, 0); +AT24_CHIP_DATA(at24_data_24c512, 524288 / 8, AT24_FLAG_ADDR16, 0); +AT24_CHIP_DATA(at24_data_24c1024, 1048576 / 8, AT24_FLAG_ADDR16, 0); +AT24_CHIP_DATA(at24_data_24c2048, 2097152 / 8, AT24_FLAG_ADDR16, 0); AT24_CHIP_DATA_BS(at24_data_24c1025, 1048576 / 8, AT24_FLAG_ADDR16, 2); -AT24_CHIP_DATA(at24_data_24c2048, 2097152 / 8, AT24_FLAG_ADDR16); /* identical to 24c08 ? */ -AT24_CHIP_DATA(at24_data_INT3499, 8192 / 8, 0); +AT24_CHIP_DATA(at24_data_INT3499, 8192 / 8, 0, 0); static const struct i2c_device_id at24_ids[] = { { "24c00", (kernel_ulong_t)&at24_data_24c00 }, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:221 @ { "24c02", (kernel_ulong_t)&at24_data_24c02 }, { "24cs02", (kernel_ulong_t)&at24_data_24cs02 }, { "24mac402", (kernel_ulong_t)&at24_data_24mac402 }, + { "24mac02e4", (kernel_ulong_t)&at24_data_24mac02e4 }, { "24mac602", (kernel_ulong_t)&at24_data_24mac602 }, + { "24mac02e6", (kernel_ulong_t)&at24_data_24mac02e6 }, { "spd", (kernel_ulong_t)&at24_data_spd }, { "24c02-vaio", (kernel_ulong_t)&at24_data_24c02_vaio }, { "24c04", (kernel_ulong_t)&at24_data_24c04 }, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:254 @ { .compatible = "atmel,24c02", .data = &at24_data_24c02 }, { .compatible = "atmel,24cs02", .data = &at24_data_24cs02 }, { .compatible = "atmel,24mac402", .data = &at24_data_24mac402 }, + { .compatible = "atmel,24mac02e4", .data = &at24_data_24mac02e4 }, { .compatible = "atmel,24mac602", .data = &at24_data_24mac602 }, + { .compatible = "atmel,24mac02e6", .data = &at24_data_24mac02e6 }, { .compatible = "atmel,spd", .data = &at24_data_spd }, { .compatible = "atmel,24c04", .data = &at24_data_24c04 }, { .compatible = "atmel,24cs04", .data = &at24_data_24cs04 }, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:726 @ at24->read_post = cdata->read_post; at24->bank_addr_shift = cdata->bank_addr_shift; at24->num_addresses = num_addresses; - at24->offset_adj = at24_get_offset_adj(flags, byte_len); + at24->offset_adj = cdata->adjoff ? + at24_get_offset_adj(flags, byte_len) : 0; at24->client_regmaps[0] = regmap; at24->vcc_reg = devm_regulator_get(dev, "vcc"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/misc/Kconfig linux-microchip/drivers/misc/Kconfig --- linux-6.6.51/drivers/misc/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/misc/Kconfig 2024-11-26 14:02:37.698497279 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:256 @ This driver provides IRQ handling for Hi6421v600, used on some Kirin chipsets, like the one at Hikey 970. +config MPFS_DMA_PROXY + tristate "Microchip dma proxy driver" + depends on OF + help + This driver will give DMA interface to userspace + user can configure src, dst and size to initiate transfer + proxy dma driver will use platform dma + config HP_ILO tristate "Channel interface driver for the HP iLO processor" depends on PCI diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/misc/Makefile linux-microchip/drivers/misc/Makefile --- linux-6.6.51/drivers/misc/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/misc/Makefile 2024-11-26 14:02:37.698497279 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:70 @ obj-$(CONFIG_TMR_INJECT) += xilinx_tmr_inject.o obj-$(CONFIG_TPS6594_ESM) += tps6594-esm.o obj-$(CONFIG_TPS6594_PFSM) += tps6594-pfsm.o +obj-$(CONFIG_MPFS_DMA_PROXY) += mpfs-dma-proxy.o diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/misc/mpfs-dma-proxy.c linux-microchip/drivers/misc/mpfs-dma-proxy.c --- linux-6.6.51/drivers/misc/mpfs-dma-proxy.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/misc/mpfs-dma-proxy.c 2024-11-26 14:02:37.706497423 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microchip PolarFire SoC DMA proxy driver + * + * Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries + * + * Author: Shravan Chippa <shravan.chippa@microchip.com> + * + * Driver based on Xilinx DMA proxy driver + */ + +#include <linux/cdev.h> +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/ioctl.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_dma.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/version.h> +#include <uapi/misc/mpfs-dma-proxy.h> + +#define MPFS_DMA_PROXY_DRIVER_NAME "mpfs_dma_proxy" +#define MPFS_DMA_PROXY_XFER_TIMEOUT_M_SEC 3000 +#define MPFS_DMA_PROXY_MAXBURST_SIZE 8 + +/** + * struct mpfs_dma_proxy_channel - one channel resource + * @channel_config: dma config data pointer + * @proxy_device: device struct proxy dma + * @dma_device: device struct dma + * @class: struct class proxy dma + * @channel: dma chan pointer + * @cdev: struct cdev for char device + * @proxy_status: status of dma xfer + * @complete: struct completion + * @cookie: transaction cookie + * @dev: device node + */ +struct mpfs_dma_proxy_channel { + struct mpfs_dma_proxy_channel_config *channel_config; + struct device *proxy_device; + struct device *dma_device; + struct class *class; + struct dma_chan *channel; + struct cdev cdev; + enum mpfs_dma_proxy_status proxy_status; + struct completion complete; + dma_cookie_t cookie; + dev_t dev; +}; + +/** + * struct mpfs_dma_proxy - dma proxy platform data for all channels + * @channels: pointer for all channels + * @names: array of channel names + * @channel_count: channel count + * @active_channels: active channel counter for cleanup class + */ +struct mpfs_dma_proxy { + struct mpfs_dma_proxy_channel *channels; + char **names; + int channel_count; + int active_channels; +}; + +static void mpfs_dma_proxy_sync_callback(void *done) +{ + complete(done); +} + +static int mpfs_dma_proxy_start_transfer(struct mpfs_dma_proxy_channel *channel) +{ + struct mpfs_dma_proxy_channel_config *channel_config = channel->channel_config; + struct dma_async_tx_descriptor *desc; + struct dma_slave_config config; + int ret; + + memset(&config, 0, sizeof(config)); + + config.src_addr_width = DMA_SLAVE_BUSWIDTH_8_BYTES; + config.dst_addr_width = DMA_SLAVE_BUSWIDTH_8_BYTES; + config.dst_maxburst = MPFS_DMA_PROXY_MAXBURST_SIZE; + + ret = dmaengine_slave_config(channel->channel, &config); + if (ret < 0) { + dev_err(channel->dma_device, "DMA channel config failed (%d)\n", ret); + return ret; + } + + desc = dmaengine_prep_dma_memcpy(channel->channel, channel_config->dst, + channel_config->src, channel_config->length, + DMA_CTRL_ACK | DMA_PREP_INTERRUPT); + if (!desc) { + dev_err(channel->dma_device, "DMA dmaengine_prep_slave_single failed\n"); + return -EINVAL; + } + + desc->callback = mpfs_dma_proxy_sync_callback; + desc->callback_param = &channel->complete; + init_completion(&channel->complete); + + channel->cookie = dmaengine_submit(desc); + if (dma_submit_error(channel->cookie)) { + dev_err(channel->dma_device, "DMA submission failed\n"); + return -ENXIO; + } + + dma_async_issue_pending(channel->channel); + + return 0; +} + +static void mpfs_dma_proxy_wait_for_xfer(struct mpfs_dma_proxy_channel *channel) +{ + unsigned long timeout = msecs_to_jiffies(MPFS_DMA_PROXY_XFER_TIMEOUT_M_SEC); + enum dma_status status; + + timeout = wait_for_completion_timeout(&channel->complete, timeout); + status = dma_async_is_tx_complete(channel->channel, channel->cookie, NULL, NULL); + + if (timeout == 0) { + dev_err(channel->dma_device, "DMA timed out\n"); + channel->proxy_status = PROXY_TIMEOUT; + } else if (status != DMA_COMPLETE) { + channel->proxy_status = PROXY_ERROR; + dev_err(channel->dma_device, + "DMA returned completion callback status of: %s\n", + status == DMA_ERROR ? "error" : "in progress"); + } else { + channel->proxy_status = PROXY_SUCCESS; + } +} + +static int mpfs_dma_proxy_local_open(struct inode *ino, struct file *file) +{ + file->private_data = container_of(ino->i_cdev, + struct mpfs_dma_proxy_channel, cdev); + + return 0; +} + +static int mpfs_dma_proxy_release(struct inode *ino, struct file *file) +{ + return 0; +} + +static long mpfs_dma_proxy_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct mpfs_dma_proxy_channel *channel = + (struct mpfs_dma_proxy_channel *)file->private_data; + + void __user *uarg = (void __user *)arg; + int ret; + + switch (cmd) { + case MPFS_DMA_PROXY_START_XFER: + if (copy_from_user(channel->channel_config, uarg, + sizeof(struct mpfs_dma_proxy_channel_config))) + return -EFAULT; + + ret = mpfs_dma_proxy_start_transfer(channel); + if (ret) + return ret; + + break; + + case MPFS_DMA_PROXY_FINISH_XFER: + mpfs_dma_proxy_wait_for_xfer(channel); + if (copy_to_user(uarg, &channel->proxy_status, + sizeof(enum mpfs_dma_proxy_status))) + return -EFAULT; + + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static const struct file_operations dm_fops = { + .open = mpfs_dma_proxy_local_open, + .release = mpfs_dma_proxy_release, + .unlocked_ioctl = mpfs_dma_proxy_ioctl, +}; + +static int mpfs_dma_proxy_cdevice_init(struct mpfs_dma_proxy_channel *channel, + char *name) +{ + static struct class *local_class; + int ret; + + ret = alloc_chrdev_region(&channel->dev, 0, 1, MPFS_DMA_PROXY_DRIVER_NAME); + + if (ret) + return dev_err_probe(channel->dma_device, ret, + "unable to get a char device number\n"); + + cdev_init(&channel->cdev, &dm_fops); + channel->cdev.owner = THIS_MODULE; + ret = cdev_add(&channel->cdev, channel->dev, 1); + + if (ret) { + dev_err_probe(channel->dma_device, ret, "unable to add char device\n"); + goto error_cdev_add; + } + + if (!local_class) { + local_class = class_create(MPFS_DMA_PROXY_DRIVER_NAME); + + if (IS_ERR(channel->dma_device->class)) { + ret = dev_err_probe(channel->dma_device, + PTR_ERR(channel->dma_device->class), + "unable to create class\n"); + goto error_class_create; + } + } + + channel->class = local_class; + + channel->proxy_device = device_create(channel->class, NULL, + channel->dev, NULL, name); + + if (IS_ERR(channel->proxy_device)) { + ret = dev_err_probe(channel->dma_device, PTR_ERR(channel->proxy_device), + "unable to create the device\n"); + goto error_device_create; + } + + return 0; + +error_device_create: +error_class_create: + cdev_del(&channel->cdev); + +error_cdev_add: + unregister_chrdev_region(channel->dev, 1); + + return ret; +} + +static void mpfs_dma_proxy_cdevice_exit(struct mpfs_dma_proxy *dma_proxy, int channel_no) +{ + struct mpfs_dma_proxy_channel *channel = &dma_proxy->channels[channel_no]; + + if (channel->proxy_device) { + device_destroy(channel->class, channel->dev); + + /* If the active channels count is one we can destroy the class */ + if (dma_proxy->active_channels == 1) + class_destroy(channel->class); + + cdev_del(&channel->cdev); + unregister_chrdev_region(channel->dev, 1); + } +} + +static int mpfs_dma_proxy_create_channel(struct platform_device *pdev, + struct mpfs_dma_proxy_channel *channel, + char *name, u32 direction) +{ + int ret; + + channel->dma_device = &pdev->dev; + channel->channel = dma_request_slave_channel(&pdev->dev, name); + if (IS_ERR(channel->channel)) { + return dev_err_probe(channel->dma_device, PTR_ERR(channel->channel), + "Failed to request DMA channel\n"); + } + + channel->channel_config = + devm_kmalloc(&pdev->dev, sizeof(struct mpfs_dma_proxy_channel_config), GFP_KERNEL); + if (IS_ERR(channel->channel_config)) { + ret = dev_err_probe(channel->dma_device, PTR_ERR(channel->channel_config), + "Could not allocate channel config\n"); + goto free_channel; + } + + ret = mpfs_dma_proxy_cdevice_init(channel, name); + if (ret) + goto free_channel; + + return 0; + +free_channel: + dma_release_channel(channel->channel); + + return ret; +} + +static void mpfs_dma_proxy_cleanup_channels(struct mpfs_dma_proxy *dma_proxy) +{ + struct mpfs_dma_proxy_channel *channels = dma_proxy->channels; + int i; + + for (i = 0; i < dma_proxy->channel_count; i++) { + if (channels[i].proxy_device) + mpfs_dma_proxy_cdevice_exit(dma_proxy, i); + + if (channels[i].channel) { + channels[i].channel->device->device_terminate_all(channels[i].channel); + dma_release_channel(channels[i].channel); + } + + dma_proxy->active_channels--; + } +} + +static int mpfs_dma_proxy_probe(struct platform_device *pdev) +{ + struct mpfs_dma_proxy *dma_proxy; + struct device *dev = &pdev->dev; + int ret, i; + + dma_proxy = devm_kmalloc(dev, sizeof(struct mpfs_dma_proxy), GFP_KERNEL); + if (IS_ERR(dma_proxy)) + return dev_err_probe(dev, PTR_ERR(dma_proxy), "Could not allocate proxy device\n"); + + dev_set_drvdata(dev, dma_proxy); + + dma_proxy->channel_count = + device_property_read_string_array(dev, "dma-names", NULL, 0); + + if (dma_proxy->channel_count <= 0) + return dev_err_probe(dev, dma_proxy->channel_count, + "Could not get DMA channel count\n"); + + dma_proxy->names = devm_kmalloc_array(&pdev->dev, dma_proxy->channel_count, + sizeof(char *), GFP_KERNEL); + if (IS_ERR(dma_proxy->names)) + return dev_err_probe(dev, PTR_ERR(dma_proxy->names), + "Could not allocate name array\n"); + + ret = device_property_read_string_array(&pdev->dev, "dma-names", + (const char **)dma_proxy->names, + dma_proxy->channel_count); + if (ret < 0) + return dev_err_probe(dev, ret, "Could not get the dma-names"); + + dma_proxy->channels = + devm_kmalloc(dev, + sizeof(struct mpfs_dma_proxy_channel) * dma_proxy->channel_count, + GFP_KERNEL); + if (IS_ERR(dma_proxy->channels)) + return dev_err_probe(dev, PTR_ERR(dma_proxy->channels), + "Could not allocate channels\n"); + + for (i = 0; i < dma_proxy->channel_count; i++) { + ret = mpfs_dma_proxy_create_channel(pdev, &dma_proxy->channels[i], + dma_proxy->names[i], DMA_MEM_TO_MEM); + + if (ret) { + dev_err_probe(dev, ret, "Fail to create channel\n"); + dma_proxy->channel_count = dma_proxy->active_channels; + goto cleanup_channels; + } + + dma_proxy->active_channels++; + } + + dev_info(dev, "proxy dma %d channels initialized\n", dma_proxy->channel_count); + + return 0; + +cleanup_channels: + mpfs_dma_proxy_cleanup_channels(dma_proxy); + + return ret; +} + +static int mpfs_dma_proxy_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mpfs_dma_proxy *dma_proxy = dev_get_drvdata(dev); + + mpfs_dma_proxy_cleanup_channels(dma_proxy); + + return 0; +} + +static const struct of_device_id mpfs_dma_proxy_of_ids[] = { + { .compatible = "microchip,mpfs-dma-proxy",}, + {} +}; + +static struct platform_driver mpfs_dma_proxy_driver = { + .driver = { + .name = MPFS_DMA_PROXY_DRIVER_NAME, + .of_match_table = mpfs_dma_proxy_of_ids, + }, + .probe = mpfs_dma_proxy_probe, + .remove = mpfs_dma_proxy_remove, +}; + +static int __init mpfs_dma_proxy_init(void) +{ + return platform_driver_register(&mpfs_dma_proxy_driver); +} + +static void __exit mpfs_dma_proxy_exit(void) +{ + platform_driver_unregister(&mpfs_dma_proxy_driver); +} + +module_init(mpfs_dma_proxy_init); +module_exit(mpfs_dma_proxy_exit); + +MODULE_AUTHOR("Shravan.chippa@microchip.com"); +MODULE_DESCRIPTION("Microchip PolarFire SoC proxy DMA driver"); +MODULE_LICENSE("GPL"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mmc/core/card.h linux-microchip/drivers/mmc/core/card.h --- linux-6.6.51/drivers/mmc/core/card.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/mmc/core/card.h 2024-11-26 14:02:37.710497495 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:152 @ .of_compatible = _compatible, \ } +#define MMC_FIXUP_COMPATIBLE(_compatible, _fixup, _data) \ + SDIO_FIXUP_COMPATIBLE(_compatible, _fixup, _data) + #define cid_rev(hwrev, fwrev, year, month) \ (((u64) hwrev) << 40 | \ ((u64) fwrev) << 32 | \ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mmc/core/debugfs.c linux-microchip/drivers/mmc/core/debugfs.c --- linux-6.6.51/drivers/mmc/core/debugfs.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/mmc/core/debugfs.c 2024-11-26 14:02:37.710497495 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:15 @ #include <linux/slab.h> #include <linux/stat.h> #include <linux/fault-inject.h> +#include <linux/time.h> #include <linux/mmc/card.h> #include <linux/mmc/host.h> +#include <linux/mmc/mmc.h> +#include <linux/mmc/sd.h> #include "core.h" #include "card.h" @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:304 @ .release = single_release, }; +static int mmc_caps_get(void *data, u64 *val) +{ + *val = *(u32 *)data; + return 0; +} + +static int mmc_caps_set(void *data, u64 val) +{ + u32 *caps = data; + u32 diff = *caps ^ val; + u32 allowed = MMC_CAP_AGGRESSIVE_PM | + MMC_CAP_SD_HIGHSPEED | + MMC_CAP_MMC_HIGHSPEED | + MMC_CAP_UHS | + MMC_CAP_DDR; + + if (diff & ~allowed) + return -EINVAL; + + *caps = val; + + return 0; +} + +static int mmc_caps2_set(void *data, u64 val) +{ + u32 allowed = MMC_CAP2_HSX00_1_8V | MMC_CAP2_HSX00_1_2V; + u32 *caps = data; + u32 diff = *caps ^ val; + + if (diff & ~allowed) + return -EINVAL; + + *caps = val; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(mmc_caps_fops, mmc_caps_get, mmc_caps_set, + "0x%08llx\n"); +DEFINE_DEBUGFS_ATTRIBUTE(mmc_caps2_fops, mmc_caps_get, mmc_caps2_set, + "0x%08llx\n"); + void mmc_add_host_debugfs(struct mmc_host *host) { struct dentry *root; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:355 @ host->debugfs_root = root; debugfs_create_file("ios", S_IRUSR, root, host, &mmc_ios_fops); - debugfs_create_x32("caps", S_IRUSR, root, &host->caps); - debugfs_create_x32("caps2", S_IRUSR, root, &host->caps2); + debugfs_create_file("caps", 0600, root, &host->caps, &mmc_caps_fops); + debugfs_create_file("caps2", 0600, root, &host->caps2, + &mmc_caps2_fops); debugfs_create_file_unsafe("clock", S_IRUSR | S_IWUSR, root, host, &mmc_clock_fops); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mmc/core/Kconfig linux-microchip/drivers/mmc/core/Kconfig --- linux-6.6.51/drivers/mmc/core/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/mmc/core/Kconfig 2024-11-26 14:02:37.710497495 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:18 @ config PWRSEQ_SD8787 tristate "HW reset support for SD8787 BT + Wifi module" - depends on OF && (MWIFIEX != n || BT_MRVL_SDIO != n || LIBERTAS_SDIO != n || WILC1000_SDIO != n) + depends on OF && (MWIFIEX != n || BT_MRVL_SDIO != n || LIBERTAS_SDIO != n || WILC_SDIO != n) help This selects hardware reset support for the SD8787 BT + Wifi module. By default this option is set to n. diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mmc/core/mmc.c linux-microchip/drivers/mmc/core/mmc.c --- linux-6.6.51/drivers/mmc/core/mmc.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/mmc/core/mmc.c 2024-11-26 14:02:37.714497566 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:139 @ mmc_init_erase(card); } + +static void mmc_set_wp_grp_size(struct mmc_card *card) +{ + if (card->ext_csd.erase_group_def & 1) + card->wp_grp_size = card->ext_csd.hc_erase_size * + card->ext_csd.raw_hc_erase_gap_size; + else + card->wp_grp_size = card->csd.erase_size * + (card->csd.wp_grp_size + 1); +} + /* * Given a 128-bit response, decode to our card CSD structure. */ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:200 @ b = UNSTUFF_BITS(resp, 37, 5); csd->erase_size = (a + 1) * (b + 1); csd->erase_size <<= csd->write_blkbits - 9; + csd->wp_grp_size = UNSTUFF_BITS(resp, 32, 5); } return 0; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:434 @ card->ext_csd.strobe_support = ext_csd[EXT_CSD_STROBE_SUPPORT]; card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE]; - mmc_select_card_type(card); card->ext_csd.raw_s_a_timeout = ext_csd[EXT_CSD_S_A_TIMEOUT]; card->ext_csd.raw_erase_timeout_mult = @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:628 @ } else { card->ext_csd.data_tag_unit_size = 0; } - - card->ext_csd.max_packed_writes = - ext_csd[EXT_CSD_MAX_PACKED_WRITES]; - card->ext_csd.max_packed_reads = - ext_csd[EXT_CSD_MAX_PACKED_READS]; } else { card->ext_csd.data_sector_size = 512; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:800 @ MMC_DEV_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year); MMC_DEV_ATTR(erase_size, "%u\n", card->erase_size << 9); MMC_DEV_ATTR(preferred_erase_size, "%u\n", card->pref_erase << 9); +MMC_DEV_ATTR(wp_grp_size, "%u\n", card->wp_grp_size << 9); MMC_DEV_ATTR(ffu_capable, "%d\n", card->ext_csd.ffu_capable); MMC_DEV_ATTR(hwrev, "0x%x\n", card->cid.hwrev); MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:861 @ &dev_attr_date.attr, &dev_attr_erase_size.attr, &dev_attr_preferred_erase_size.attr, + &dev_attr_wp_grp_size.attr, &dev_attr_fwrev.attr, &dev_attr_ffu_capable.attr, &dev_attr_hwrev.attr, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1553 @ if (!mmc_can_ext_csd(card)) goto bus_speed; + if (card->quirks & MMC_QUIRK_HS_CLK_REVERSED) + mmc_set_bus_speed(card); + if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400ES) { err = mmc_select_hs400es(card); goto out; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1680 @ memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); } + /* Fixup init methods */ + mmc_fixup_device(card, mmc_init_methods); + /* * Call the optional HC's init_card function to handle quirks. */ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1751 @ mmc_set_erase_size(card); } + /* + * Reselect the card type since host caps could have been changed when + * debugging even if the card is not new. + */ + mmc_select_card_type(card); + /* Enable ERASE_GRP_DEF. This bit is lost after a reset or power off. */ if (card->ext_csd.rev >= 3) { err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1784 @ mmc_set_erase_size(card); } } - + mmc_set_wp_grp_size(card); /* * Ensure eMMC user default partition is enabled */ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mmc/core/mmc_test.c linux-microchip/drivers/mmc/core/mmc_test.c --- linux-6.6.51/drivers/mmc/core/mmc_test.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/mmc/core/mmc_test.c 2024-11-26 14:02:37.714497566 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1907 @ } static int mmc_test_rnd_perf(struct mmc_test_card *test, int write, int print, - unsigned long sz) + unsigned long sz, int secs, int force_retuning) { unsigned int dev_addr, cnt, rnd_addr, range1, range2, last_ea = 0, ea; unsigned int ssz; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1924 @ for (cnt = 0; cnt < UINT_MAX; cnt++) { ktime_get_ts64(&ts2); ts = timespec64_sub(ts2, ts1); - if (ts.tv_sec >= 10) + if (ts.tv_sec >= secs) break; ea = mmc_test_rnd_num(range1); if (ea == last_ea) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1932 @ last_ea = ea; dev_addr = rnd_addr + test->card->pref_erase * ea + ssz * mmc_test_rnd_num(range2); + if (force_retuning) + mmc_retune_needed(test->card->host); ret = mmc_test_area_io(test, sz, dev_addr, write, 0, 0); if (ret) return ret; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1958 @ */ if (write) { next = rnd_next; - ret = mmc_test_rnd_perf(test, write, 0, sz); + ret = mmc_test_rnd_perf(test, write, 0, sz, 10, 0); if (ret) return ret; rnd_next = next; } - ret = mmc_test_rnd_perf(test, write, 1, sz); + ret = mmc_test_rnd_perf(test, write, 1, sz, 10, 0); if (ret) return ret; } sz = t->max_tfr; if (write) { next = rnd_next; - ret = mmc_test_rnd_perf(test, write, 0, sz); + ret = mmc_test_rnd_perf(test, write, 0, sz, 10, 0); if (ret) return ret; rnd_next = next; } - return mmc_test_rnd_perf(test, write, 1, sz); + return mmc_test_rnd_perf(test, write, 1, sz, 10, 0); +} + +static int mmc_test_retuning(struct mmc_test_card *test) +{ + if (!mmc_can_retune(test->card->host)) { + pr_info("%s: No retuning - test skipped\n", + mmc_hostname(test->card->host)); + return RESULT_UNSUP_HOST; + } + + return mmc_test_rnd_perf(test, 0, 0, 8192, 30, 1); } /* @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2937 @ .run = mmc_test_cmds_during_write_cmd23_nonblock, .cleanup = mmc_test_area_cleanup, }, + + { + .name = "Re-tuning reliability", + .prepare = mmc_test_area_prepare, + .run = mmc_test_retuning, + .cleanup = mmc_test_area_cleanup, + }, + }; static DEFINE_MUTEX(mmc_test_lock); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mmc/core/queue.c linux-microchip/drivers/mmc/core/queue.c --- linux-6.6.51/drivers/mmc/core/queue.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/mmc/core/queue.c 2024-11-26 14:02:37.714497566 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:263 @ } break; case MMC_ISSUE_ASYNC: - /* - * For MMC host software queue, we only allow 2 requests in - * flight to avoid a long latency. - */ - if (host->hsq_enabled && mq->in_flight[issue_type] > 2) { + if (host->hsq_enabled && mq->in_flight[issue_type] > host->hsq_depth) { spin_unlock_irq(&mq->lock); return BLK_STS_RESOURCE; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:354 @ if (mmc_can_erase(card)) mmc_queue_setup_discard(mq->queue, card); - if (!mmc_dev(host)->dma_mask || !*mmc_dev(host)->dma_mask) - blk_queue_bounce_limit(mq->queue, BLK_BOUNCE_HIGH); blk_queue_max_hw_sectors(mq->queue, min(host->max_blk_count, host->max_req_size / 512)); if (host->can_dma_map_merge) diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mmc/core/quirks.h linux-microchip/drivers/mmc/core/quirks.h --- linux-6.6.51/drivers/mmc/core/quirks.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/mmc/core/quirks.h 2024-11-26 14:02:37.714497566 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:194 @ END_FIXUP }; +static const struct mmc_fixup __maybe_unused mmc_init_methods[] = { + MMC_FIXUP_COMPATIBLE("microchip,sama7g5-sdhci", add_quirk, + MMC_QUIRK_HS_CLK_REVERSED), + + END_FIXUP +}; + static inline bool mmc_fixup_of_compatible_match(struct mmc_card *card, const char *compatible) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:212 @ return true; } } + if (of_device_is_compatible(mmc_dev(card->host)->of_node, compatible)) + return true; return false; } diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mmc/host/atmel-mci.c linux-microchip/drivers/mmc/host/atmel-mci.c --- linux-6.6.51/drivers/mmc/host/atmel-mci.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/mmc/host/atmel-mci.c 2024-11-26 14:02:37.714497566 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:40 @ #include <asm/cacheflush.h> #include <asm/io.h> #include <asm/unaligned.h> +#include "../core/pwrseq.h" #define ATMCI_MAX_NR_SLOTS 2 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:231 @ /** * struct mci_platform_data - board-specific MMC/SDcard configuration * @dma_slave: DMA slave interface to use in data transfers. + * @dma_filter: Filtering function to filter the DMA channel * @slot: Per-slot configuration data. */ struct mci_platform_data { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:679 @ "cd", GPIOD_IN, "cd-gpios"); err = PTR_ERR_OR_ZERO(pdata->slot[slot_id].detect_pin); if (err) { - if (err != -ENOENT) + if (err != -ENOENT) { + of_node_put(cnp); return ERR_PTR(err); + } pdata->slot[slot_id].detect_pin = NULL; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:694 @ "wp", GPIOD_IN, "wp-gpios"); err = PTR_ERR_OR_ZERO(pdata->slot[slot_id].wp_pin); if (err) { - if (err != -ENOENT) + if (err != -ENOENT) { + of_node_put(cnp); return ERR_PTR(err); + } pdata->slot[slot_id].wp_pin = NULL; } } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2344 @ host->slot[id] = slot; mmc_regulator_get_supply(mmc); + mmc_pwrseq_alloc(mmc); ret = mmc_add_host(mmc); if (ret) { + mmc_pwrseq_free(mmc); mmc_free_host(mmc); return ret; } diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mmc/host/mmc_hsq.c linux-microchip/drivers/mmc/host/mmc_hsq.c --- linux-6.6.51/drivers/mmc/host/mmc_hsq.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/mmc/host/mmc_hsq.c 2024-11-26 14:02:37.714497566 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:24 @ mmc->ops->request(mmc, hsq->mrq); } +static void mmc_hsq_modify_threshold(struct mmc_hsq *hsq) +{ + struct mmc_host *mmc = hsq->mmc; + struct mmc_request *mrq; + unsigned int tag, need_change = 0; + + mmc->hsq_depth = HSQ_NORMAL_DEPTH; + for (tag = 0; tag < HSQ_NUM_SLOTS; tag++) { + mrq = hsq->slot[tag].mrq; + if (mrq && mrq->data && + (mrq->data->blksz * mrq->data->blocks == 4096) && + (mrq->data->flags & MMC_DATA_WRITE) && + (++need_change == 2)) { + mmc->hsq_depth = HSQ_PERFORMANCE_DEPTH; + break; + } + } +} + static void mmc_hsq_pump_requests(struct mmc_hsq *hsq) { struct mmc_host *mmc = hsq->mmc; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:64 @ return; } + mmc_hsq_modify_threshold(hsq); + slot = &hsq->slot[hsq->next_tag]; hsq->mrq = slot->mrq; hsq->qcnt--; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:361 @ hsq->mmc = mmc; hsq->mmc->cqe_private = hsq; mmc->cqe_ops = &mmc_hsq_ops; + mmc->hsq_depth = HSQ_NORMAL_DEPTH; for (i = 0; i < HSQ_NUM_SLOTS; i++) hsq->tag_slot[i] = HSQ_INVALID_TAG; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mmc/host/mmc_hsq.h linux-microchip/drivers/mmc/host/mmc_hsq.h --- linux-6.6.51/drivers/mmc/host/mmc_hsq.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/mmc/host/mmc_hsq.h 2024-11-26 14:02:37.714497566 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:8 @ #define HSQ_NUM_SLOTS 64 #define HSQ_INVALID_TAG HSQ_NUM_SLOTS +/* + * For MMC host software queue, we only allow 2 requests in + * flight to avoid a long latency. + */ +#define HSQ_NORMAL_DEPTH 2 +/* + * For 4k random writes, we allow hsq_depth to increase to 5 + * for better performance. + */ +#define HSQ_PERFORMANCE_DEPTH 5 + struct hsq_slot { struct mmc_request *mrq; }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mmc/host/mmc_spi.c linux-microchip/drivers/mmc/host/mmc_spi.c --- linux-6.6.51/drivers/mmc/host/mmc_spi.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/mmc/host/mmc_spi.c 2024-11-26 14:02:37.718497639 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1213 @ * that's the only reason not to use a few MHz for f_min (until * the upper layer reads the target frequency from the CSD). */ - mmc->f_min = 400000; + if (spi->controller->min_speed_hz > 400000) + dev_warn(&spi->dev,"Controller unable to reduce bus clock to 400 KHz\n"); + + mmc->f_min = max(spi->controller->min_speed_hz, 400000); mmc->f_max = spi->max_speed_hz; host = mmc_priv(mmc); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mmc/host/sdhci.c linux-microchip/drivers/mmc/host/sdhci.c --- linux-6.6.51/drivers/mmc/host/sdhci.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/mmc/host/sdhci.c 2024-11-26 14:02:37.722497710 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2296 @ case MMC_TIMING_UHS_SDR104: case MMC_TIMING_UHS_DDR50: case MMC_TIMING_MMC_DDR52: +#ifdef SDHCI_QUIRK2_AT91_HS400_PRESET + case MMC_TIMING_MMC_HS400: +#endif return true; } return false; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2850 @ } EXPORT_SYMBOL_GPL(sdhci_send_tuning); -static int __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) +int __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) { int i; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2888 @ sdhci_reset_tuning(host); return -EAGAIN; } +EXPORT_SYMBOL_GPL(__sdhci_execute_tuning); int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) { diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mmc/host/sdhci.h linux-microchip/drivers/mmc/host/sdhci.h --- linux-6.6.51/drivers/mmc/host/sdhci.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/mmc/host/sdhci.h 2024-11-26 14:02:37.722497710 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:273 @ #define SDHCI_PRESET_FOR_SDR104 0x6C #define SDHCI_PRESET_FOR_DDR50 0x6E #define SDHCI_PRESET_FOR_HS400 0x74 /* Non-standard */ +#define SDHCI_PRESET_FOR_HS400_AT91 0x244 /* AT91 specific */ #define SDHCI_PRESET_DRV_MASK GENMASK(15, 14) #define SDHCI_PRESET_CLKGEN_SEL BIT(10) #define SDHCI_PRESET_SDCLK_FREQ_MASK GENMASK(9, 0) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:490 @ #define SDHCI_QUIRK2_USE_32BIT_BLK_CNT (1<<18) /* Issue CMD and DATA reset together */ #define SDHCI_QUIRK2_ISSUE_CMD_DAT_RESET_TOGETHER (1<<19) +/* + * AT91 specific HS400 preset register + */ +#define SDHCI_QUIRK2_AT91_HS400_PRESET (1<<20) int irq; /* Device IRQ */ void __iomem *ioaddr; /* Mapped address */ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:802 @ void sdhci_reset(struct sdhci_host *host, u8 mask); void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing); int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode); +int __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode); void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios); int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mmc/host/sdhci-of-at91.c linux-microchip/drivers/mmc/host/sdhci-of-at91.c --- linux-6.6.51/drivers/mmc/host/sdhci-of-at91.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/mmc/host/sdhci-of-at91.c 2024-11-26 14:02:37.718497639 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:28 @ #define SDMMC_MC1R 0x204 #define SDMMC_MC1R_DDR BIT(3) +#define SDMMC_MC1R_RSTN BIT(6) #define SDMMC_MC1R_FCD BIT(7) +#define SDMMC_MC3R 0x206 +#define SDMMC_MC3R_HS400EN BIT(0) +#define SDMMC_MC3R_ESMEN BIT(1) #define SDMMC_CACR 0x230 #define SDMMC_CACR_CAPWREN BIT(0) #define SDMMC_CACR_KEY (0x46 << 8) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:42 @ #define SDHCI_AT91_PRESET_COMMON_CONF 0x400 /* drv type B, programmable clock mode */ +/* drv type A, programmable clock mode */ +#define SDHCI_AT91_PRESET_DRVA_CONF (SDHCI_AT91_PRESET_COMMON_CONF \ + | 0x4000) struct sdhci_at91_soc_data { const struct sdhci_pltfm_data *pdata; bool baseclk_is_generated_internally; unsigned int divider_for_baseclk; + unsigned int max_sdr104_clk; + bool pm_runtime_disable_clks; + u32 quirks2; }; struct sdhci_at91_priv { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:113 @ static void sdhci_at91_set_uhs_signaling(struct sdhci_host *host, unsigned int timing) { - u8 mc1r; + u16 clk; + u8 mc3r, mc1r; - if (timing == MMC_TIMING_MMC_DDR52) { - mc1r = sdhci_readb(host, SDMMC_MC1R); + mc1r = readb(host->ioaddr + SDMMC_MC1R); + mc3r = readb(host->ioaddr + SDMMC_MC3R); + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + + /* SDCLK must be disabled while changing the mode */ + if (clk & SDHCI_CLOCK_CARD_EN) + sdhci_writew(host, clk & ~SDHCI_CLOCK_CARD_EN, + SDHCI_CLOCK_CONTROL); + + if (timing == MMC_TIMING_MMC_DDR52 || timing == MMC_TIMING_MMC_HS400) mc1r |= SDMMC_MC1R_DDR; - sdhci_writeb(host, mc1r, SDMMC_MC1R); - } + else + mc1r &= ~SDMMC_MC1R_DDR; + + sdhci_writeb(host, mc1r, SDMMC_MC1R); + + if (timing == MMC_TIMING_MMC_HS400) + mc3r |= SDMMC_MC3R_HS400EN; + else + mc3r &= ~SDMMC_MC3R_HS400EN; + + writeb(mc3r, host->ioaddr + SDMMC_MC3R); + sdhci_set_uhs_signaling(host, timing); + + /* reenable SDCLK */ + if (clk & SDHCI_CLOCK_CARD_EN) { + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + sdhci_writew(host, clk | SDHCI_CLOCK_CARD_EN, SDHCI_CLOCK_CONTROL); + } } static void sdhci_at91_reset(struct sdhci_host *host, u8 mask) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:153 @ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_at91_priv *priv = sdhci_pltfm_priv(pltfm_host); unsigned int tmp; + u16 clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + + /* SDCLK must be disabled while resetting the HW block */ + if (clk & SDHCI_CLOCK_CARD_EN) + sdhci_writew(host, clk & ~SDHCI_CLOCK_CARD_EN, + SDHCI_CLOCK_CONTROL); sdhci_reset(host, mask); + /* reenable SDCLK */ + if (clk & SDHCI_CLOCK_CARD_EN) { + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + sdhci_writew(host, clk | SDHCI_CLOCK_CARD_EN, SDHCI_CLOCK_CONTROL); + } + if ((host->mmc->caps & MMC_CAP_NONREMOVABLE) || mmc_gpio_get_cd(host->mmc) >= 0) sdhci_at91_set_force_card_detect(host); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:184 @ } } +static void sdhci_at91_hw_reset(struct sdhci_host *host) +{ + u8 mc1r; + + mc1r = readb(host->ioaddr + SDMMC_MC1R); + + mc1r |= SDMMC_MC1R_RSTN; + writeb(mc1r, host->ioaddr + SDMMC_MC1R); + + udelay(10); + + mc1r &= ~SDMMC_MC1R_RSTN; + writeb(mc1r, host->ioaddr + SDMMC_MC1R); + + /* JEDEC specifies a minimum of 200us for tRSCA (reset to command) */ + usleep_range(200, 500); +} + static const struct sdhci_ops sdhci_at91_sama5d2_ops = { .set_clock = sdhci_at91_set_clock, .set_bus_width = sdhci_set_bus_width, .reset = sdhci_at91_reset, .set_uhs_signaling = sdhci_at91_set_uhs_signaling, .set_power = sdhci_set_power_and_bus_voltage, + .hw_reset = sdhci_at91_hw_reset, }; static const struct sdhci_pltfm_data sdhci_sama5d2_pdata = { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:218 @ static const struct sdhci_at91_soc_data soc_data_sama5d2 = { .pdata = &sdhci_sama5d2_pdata, .baseclk_is_generated_internally = false, + .max_sdr104_clk = 120000000, + .pm_runtime_disable_clks = true, + .quirks2 = SDHCI_QUIRK2_BROKEN_HS200, }; static const struct sdhci_at91_soc_data soc_data_sam9x60 = { .pdata = &sdhci_sama5d2_pdata, .baseclk_is_generated_internally = true, .divider_for_baseclk = 2, + .pm_runtime_disable_clks = true, +}; + +static const struct sdhci_at91_soc_data soc_data_sama7g5 = { + .pdata = &sdhci_sama5d2_pdata, + .baseclk_is_generated_internally = true, + .divider_for_baseclk = 2, + .max_sdr104_clk = 200000000, + .quirks2 = SDHCI_QUIRK2_AT91_HS400_PRESET, }; static const struct of_device_id sdhci_at91_dt_match[] = { { .compatible = "atmel,sama5d2-sdhci", .data = &soc_data_sama5d2 }, { .compatible = "microchip,sam9x60-sdhci", .data = &soc_data_sam9x60 }, + { .compatible = "microchip,sama7g5-sdhci", .data = &soc_data_sama7g5 }, {} }; MODULE_DEVICE_TABLE(of, sdhci_at91_dt_match); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:260 @ caps0 = readl(host->ioaddr + SDHCI_CAPABILITIES); caps1 = readl(host->ioaddr + SDHCI_CAPABILITIES_1); + /* + * We experience some issues with SDR104. If the SD clock is higher + * than 100 MHz, we can get data corruption. With a 100 MHz clock, + * the tuning procedure may fail. For those reasons, it is useless to + * advertise that we can use SDR104 mode, so remove it from + * the capabilities. + */ + writel(SDMMC_CACR_KEY | SDMMC_CACR_CAPWREN, host->ioaddr + SDMMC_CACR); + caps1 &= (~SDHCI_SUPPORT_SDR104); + writel(caps1, host->ioaddr + SDHCI_CAPABILITIES_1); + writel(0, host->ioaddr + SDMMC_CACR); + gck_rate = clk_get_rate(priv->gck); if (priv->soc_data->baseclk_is_generated_internally) clk_base_rate = gck_rate / priv->soc_data->divider_for_baseclk; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:310 @ preset_div = DIV_ROUND_UP(gck_rate, 100000000) - 1; writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div, host->ioaddr + SDHCI_PRESET_FOR_SDR50); - preset_div = DIV_ROUND_UP(gck_rate, 120000000) - 1; - writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div, - host->ioaddr + SDHCI_PRESET_FOR_SDR104); + if (priv->soc_data->max_sdr104_clk) { + preset_div = DIV_ROUND_UP(gck_rate, + priv->soc_data->max_sdr104_clk) - 1; + writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div, + host->ioaddr + SDHCI_PRESET_FOR_SDR104); + } preset_div = DIV_ROUND_UP(gck_rate, 50000000) - 1; writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div, host->ioaddr + SDHCI_PRESET_FOR_DDR50); + if (priv->soc_data->max_sdr104_clk) { + preset_div = DIV_ROUND_UP(gck_rate, + priv->soc_data->max_sdr104_clk) - 1; + writew(SDHCI_AT91_PRESET_DRVA_CONF | preset_div, + host->ioaddr + SDHCI_PRESET_FOR_HS400); + } clk_prepare_enable(priv->mainck); clk_prepare_enable(priv->gck); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:361 @ if (host->tuning_mode != SDHCI_TUNING_MODE_3) mmc_retune_needed(host->mmc); - clk_disable_unprepare(priv->gck); - clk_disable_unprepare(priv->hclock); - clk_disable_unprepare(priv->mainck); + if (priv->soc_data->pm_runtime_disable_clks) { + clk_disable_unprepare(priv->gck); + clk_disable_unprepare(priv->hclock); + clk_disable_unprepare(priv->mainck); + } return ret; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:386 @ goto out; } + if (!priv->soc_data->pm_runtime_disable_clks) + goto out; + ret = clk_prepare_enable(priv->mainck); if (ret) { dev_err(dev, "can't enable mainck\n"); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:419 @ NULL) }; +static void at91_sdhci_hs400_enhanced_strobe(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct sdhci_host *host = mmc_priv(mmc); + u8 mc3r; + + mc3r = readb(host->ioaddr + SDMMC_MC3R); + if (ios->enhanced_strobe) + mc3r |= SDMMC_MC3R_ESMEN; + else + mc3r &= ~SDMMC_MC3R_ESMEN; + + writeb(mc3r, host->ioaddr + SDMMC_MC3R); +} + static int sdhci_at91_probe(struct platform_device *pdev) { const struct sdhci_at91_soc_data *soc_data; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:453 @ priv = sdhci_pltfm_priv(pltfm_host); priv->soc_data = soc_data; + /* Perform a software reset before using the IP */ + sdhci_at91_reset(host, SDHCI_RESET_ALL); + /* Perform a hardware reset before using the IP */ + sdhci_at91_hw_reset(host); + priv->mainck = devm_clk_get(&pdev->dev, "baseclk"); if (IS_ERR(priv->mainck)) { if (soc_data->baseclk_is_generated_internally) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:509 @ pm_runtime_set_autosuspend_delay(&pdev->dev, 50); pm_runtime_use_autosuspend(&pdev->dev); - /* HS200 is broken at this moment */ - host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200; + host->quirks2 |= priv->soc_data->quirks2; ret = sdhci_add_host(host); if (ret) goto pm_runtime_disable; + host->mmc_host_ops.hs400_enhanced_strobe = at91_sdhci_hs400_enhanced_strobe; + /* * When calling sdhci_runtime_suspend_host(), the sdhci layer makes * the assumption that all the clocks of the controller are disabled. diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mtd/nand/raw/atmel/pmecc.c linux-microchip/drivers/mtd/nand/raw/atmel/pmecc.c --- linux-6.6.51/drivers/mtd/nand/raw/atmel/pmecc.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/mtd/nand/raw/atmel/pmecc.c 2024-11-26 14:02:37.730497853 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:44 @ * to expose the needed lib/bch.c helpers/functions and re-use them here. */ +#include <linux/clk.h> #include <linux/genalloc.h> #include <linux/iopoll.h> #include <linux/module.h> @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:147 @ int nstrengths; int el_offset; bool correct_erased_chunks; + bool pmc_clk_ctrl; }; struct atmel_pmecc { struct device *dev; const struct atmel_pmecc_caps *caps; + struct clk *clk; struct { void __iomem *base; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:840 @ { struct device *dev = &pdev->dev; struct atmel_pmecc *pmecc; + void *err; + int ret; pmecc = devm_kzalloc(dev, sizeof(*pmecc), GFP_KERNEL); if (!pmecc) return ERR_PTR(-ENOMEM); + /* Enable clk if controlled by pmc */ + if (caps->pmc_clk_ctrl) { + pmecc->clk = devm_clk_get(dev, NULL); + if (IS_ERR(pmecc->clk)) { + dev_err(dev, "failed to get pmecc clk\n"); + return ERR_CAST(pmecc->clk); + } + + ret = clk_prepare_enable(pmecc->clk); + if (ret) { + dev_err(dev, "failed to enable pmecc clk, ret = %d\n", ret); + return ERR_PTR(ret); + } + } + pmecc->caps = caps; pmecc->dev = dev; mutex_init(&pmecc->lock); pmecc->regs.base = devm_platform_ioremap_resource(pdev, pmecc_res_idx); - if (IS_ERR(pmecc->regs.base)) - return ERR_CAST(pmecc->regs.base); + if (IS_ERR(pmecc->regs.base)) { + err = ERR_CAST(pmecc->regs.base); + goto clk_disable; + } pmecc->regs.errloc = devm_platform_ioremap_resource(pdev, errloc_res_idx); - if (IS_ERR(pmecc->regs.errloc)) - return ERR_CAST(pmecc->regs.errloc); + if (IS_ERR(pmecc->regs.errloc)) { + err = ERR_CAST(pmecc->regs.errloc); + goto clk_disable; + } /* Disable all interrupts before registering the PMECC handler. */ writel(0xffffffff, pmecc->regs.base + ATMEL_PMECC_IDR); atmel_pmecc_reset(pmecc); return pmecc; + +clk_disable: + if (caps->pmc_clk_ctrl) + clk_disable_unprepare(pmecc->clk); + + return err; } static void devm_atmel_pmecc_put(struct device *dev, void *res) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:939 @ .el_offset = 0x8c, }; +static struct atmel_pmecc_caps sam9x7_caps = { + .strengths = atmel_pmecc_strengths, + .nstrengths = 5, + .el_offset = 0x8c, + .pmc_clk_ctrl = true, +}; + static struct atmel_pmecc_caps sama5d4_caps = { .strengths = atmel_pmecc_strengths, .nstrengths = 5, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1015 @ { .compatible = "atmel,at91sam9g45-pmecc", &at91sam9g45_caps }, { .compatible = "atmel,sama5d4-pmecc", &sama5d4_caps }, { .compatible = "atmel,sama5d2-pmecc", &sama5d2_caps }, + { .compatible = "microchip,sam9x7-pmecc", &sam9x7_caps }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, atmel_pmecc_match); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1041 @ return 0; } +static int atmel_pmecc_remove(struct platform_device *pdev) +{ + struct atmel_pmecc *pmecc = platform_get_drvdata(pdev); + + if (pmecc->caps->pmc_clk_ctrl) + clk_disable_unprepare(pmecc->clk); + + return 0; +} + static struct platform_driver atmel_pmecc_driver = { .driver = { .name = "atmel-pmecc", .of_match_table = atmel_pmecc_match, }, .probe = atmel_pmecc_probe, + .remove = atmel_pmecc_remove, }; module_platform_driver(atmel_pmecc_driver); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mtd/spi-nor/atmel.c linux-microchip/drivers/mtd/spi-nor/atmel.c --- linux-6.6.51/drivers/mtd/spi-nor/atmel.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/mtd/spi-nor/atmel.c 2024-11-26 14:02:37.738497997 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:187 @ FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) NO_SFDP_FLAGS(SECT_4K) .fixups = &atmel_nor_global_protection_fixups }, + { "at25ff321a", INFO(0x1f4708, 0, 64 * 1024, 64) + FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) + PARSE_SFDP + .fixups = &atmel_nor_global_protection_fixups }, { "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128) FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) NO_SFDP_FLAGS(SECT_4K) diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mtd/spi-nor/core.c linux-microchip/drivers/mtd/spi-nor/core.c --- linux-6.6.51/drivers/mtd/spi-nor/core.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/mtd/spi-nor/core.c 2024-11-26 14:02:37.738497997 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:109 @ op->dummy.dtr = true; op->data.dtr = true; + if (spi_nor_protocol_is_dtr_bswap16(proto)) + op->data.dtr_bswap16 = true; + /* 2 bytes per clock cycle in DTR mode. */ op->dummy.nbytes *= 2; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:459 @ if (nor->spimem) { struct spi_mem_op op = SPI_NOR_RDSR_OP(sr); - if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) { + if (spi_nor_protocol_is_octal_dtr(nor->reg_proto)) { op.addr.nbytes = nor->params->rdsr_addr_nbytes; op.dummy.nbytes = nor->params->rdsr_dummy; /* @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2697 @ { if (nor->params->addr_nbytes) { nor->addr_nbytes = nor->params->addr_nbytes; - } else if (nor->read_proto == SNOR_PROTO_8_8_8_DTR) { + } else if (spi_nor_protocol_is_octal_dtr(nor->read_proto)) { /* * In 8D-8D-8D mode, one byte takes half a cycle to transfer. So * in this protocol an odd addr_nbytes cannot be used because @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2779 @ { struct spi_nor_flash_parameter *params = nor->params; struct spi_nor_erase_map *map = ¶ms->erase_map; - const u8 no_sfdp_flags = nor->info->no_sfdp_flags; + const u16 no_sfdp_flags = nor->info->no_sfdp_flags; u8 i, erase_mask; if (no_sfdp_flags & SPI_NOR_DUAL_READ) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2820 @ SPINOR_OP_PP, SNOR_PROTO_8_8_8_DTR); } + if (no_sfdp_flags & SPI_NOR_DTR_BSWAP16) + nor->flags |= SNOR_F_DTR_BSWAP16; + /* * Sector Erase settings. Sort Erase Types in ascending order, with the * smallest erase size starting at BIT(0). @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2900 @ if (fixup_flags & SPI_NOR_IO_MODE_EN_VOLATILE) nor->flags |= SNOR_F_IO_MODE_EN_VOLATILE; + + if (fixup_flags & SPI_NOR_SOFT_RESET) + nor->flags |= SNOR_F_SOFT_RESET; +} + +static void spi_nor_set_dtr_bswap16_ops(struct spi_nor *nor) +{ + struct spi_nor_flash_parameter *params = nor->params; + u32 mask = SNOR_HWCAPS_READ_8_8_8_DTR | SNOR_HWCAPS_PP_8_8_8_DTR; + + if ((params->hwcaps.mask & mask) == mask) { + params->reads[SNOR_CMD_READ_8_8_8_DTR].proto |= + SNOR_PROTO_IS_DTR_BSWAP16; + params->page_programs[SNOR_CMD_PP_8_8_8_DTR].proto |= + SNOR_PROTO_IS_DTR_BSWAP16; + } } /** @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2951 @ spi_nor_init_flags(nor); spi_nor_init_fixup_flags(nor); + if (nor->flags & SNOR_F_DTR_BSWAP16) + spi_nor_set_dtr_bswap16_ops(nor); + /* * NOR protection support. When locking_ops are not provided, we pick * the default ones. @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:3139 @ if (!nor->params->set_octal_dtr) return 0; - if (!(nor->read_proto == SNOR_PROTO_8_8_8_DTR && - nor->write_proto == SNOR_PROTO_8_8_8_DTR)) + if (!(spi_nor_protocol_is_octal_dtr(nor->read_proto) && + spi_nor_protocol_is_octal_dtr(nor->write_proto))) return 0; if (!(nor->flags & SNOR_F_IO_MODE_EN_VOLATILE)) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:3235 @ spi_nor_try_unlock_all(nor); if (nor->addr_nbytes == 4 && - nor->read_proto != SNOR_PROTO_8_8_8_DTR && + !spi_nor_protocol_is_octal_dtr(nor->read_proto) && !(nor->flags & SNOR_F_4B_OPCODES)) { /* * If the RESET# pin isn't hooked up properly, or the system diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mtd/spi-nor/core.h linux-microchip/drivers/mtd/spi-nor/core.h --- linux-6.6.51/drivers/mtd/spi-nor/core.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/mtd/spi-nor/core.h 2024-11-26 14:02:37.738497997 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:136 @ SNOR_F_RWW = BIT(14), SNOR_F_ECC = BIT(15), SNOR_F_NO_WP = BIT(16), + SNOR_F_DTR_BSWAP16 = BIT(17), }; struct spi_nor_read_command { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:487 @ * SPI_NOR_OCTAL_READ: flash supports Octal Read. * SPI_NOR_OCTAL_DTR_READ: flash supports octal DTR Read. * SPI_NOR_OCTAL_DTR_PP: flash supports Octal DTR Page Program. + * SPI_NOR_DTR_BSWAP16: the byte order of 16-bit words is swapped when + * read or written in DTR mode compared to STR mode. * * @fixup_flags: flags that indicate support that can be discovered via SFDP * ideally, but can not be discovered for this particular flash @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:501 @ * memory size above 128Mib. * SPI_NOR_IO_MODE_EN_VOLATILE: flash enables the best available I/O mode * via a volatile bit. + * SPI_NOR_SOFT_RESET: flash supports software reset enable, reset + * sequence. * @mfr_flags: manufacturer private flags. Used in the manufacturer fixup * hooks to differentiate support between flashes of the same * manufacturer. @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:533 @ #define SPI_NOR_QUAD_PP BIT(9) #define SPI_NOR_RWW BIT(10) - u8 no_sfdp_flags; + u16 no_sfdp_flags; #define SPI_NOR_SKIP_SFDP BIT(0) #define SECT_4K BIT(1) #define SPI_NOR_DUAL_READ BIT(3) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:541 @ #define SPI_NOR_OCTAL_READ BIT(5) #define SPI_NOR_OCTAL_DTR_READ BIT(6) #define SPI_NOR_OCTAL_DTR_PP BIT(7) +#define SPI_NOR_DTR_BSWAP16 BIT(8) u8 fixup_flags; #define SPI_NOR_4B_OPCODES BIT(0) #define SPI_NOR_IO_MODE_EN_VOLATILE BIT(1) +#define SPI_NOR_SOFT_RESET BIT(2) u8 mfr_flags; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mtd/spi-nor/macronix.c linux-microchip/drivers/mtd/spi-nor/macronix.c --- linux-6.6.51/drivers/mtd/spi-nor/macronix.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/mtd/spi-nor/macronix.c 2024-11-26 14:02:37.738497997 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:11 @ #include "core.h" +#define SPINOR_OP_READ_CR2 0x71 /* Read Configuration Register 2 */ +#define SPINOR_OP_WRITE_CR2 0x72 /* Write Configuration Register 2 */ +#define SPINOR_OP_MX_DTR_RD 0xee /* Octa DTR Read Opcode */ + +#define SPINOR_REG_CR2_MODE_ADDR 0 /* Address of Mode Enable in CR2 */ +#define SPINOR_REG_CR2_DTR_OPI_ENABLE BIT(1) /* DTR OPI Enable */ +#define SPINOR_REG_CR2_SPI 0 /* SPI Enable */ + +/** + * Macronix SPI NOR flash operations. + */ +#define SPI_NOR_MX_READ_CR2_OP(ndummy, buf, ndata) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_READ_CR2, 0), \ + SPI_MEM_OP_ADDR(4, SPINOR_REG_CR2_MODE_ADDR, 0), \ + SPI_MEM_OP_DUMMY(ndummy, 0), \ + SPI_MEM_OP_DATA_IN(ndata, buf, 0)) + +#define SPI_NOR_MX_WRITE_CR2_OP(buf, ndata) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRITE_CR2, 0), \ + SPI_MEM_OP_ADDR(4, SPINOR_REG_CR2_MODE_ADDR, 0), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_DATA_OUT(ndata, buf, 0)) + +static int spi_nor_macronix_read_cr2(struct spi_nor *nor, u8 ndummy, void *sr, + unsigned int nbytes) +{ + struct spi_mem_op op = SPI_NOR_MX_READ_CR2_OP(ndummy, sr, nbytes); + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + return spi_mem_exec_op(nor->spimem, &op); +} + +static int spi_nor_macronix_write_cr2(struct spi_nor *nor, const void *sr, + unsigned int nbytes) +{ + struct spi_mem_op op = SPI_NOR_MX_WRITE_CR2_OP(sr, nbytes); + int ret; + + spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); + ret = spi_nor_write_enable(nor); + if (ret) + return ret; + return spi_mem_exec_op(nor->spimem, &op); +} + +static int spi_nor_macronix_octal_dtr_en(struct spi_nor *nor) +{ + u8 *buf = nor->bouncebuf; + int i, ret; + + buf[0] = SPINOR_REG_CR2_DTR_OPI_ENABLE; + ret = spi_nor_macronix_write_cr2(nor, buf, 1); + if (ret) + return ret; + + /* Read flash ID to make sure the switch was successful. */ + ret = spi_nor_read_id(nor, 4, 4, buf, SNOR_PROTO_8_8_8_DTR); + if (ret) + return ret; + + for (i = 0; i < nor->info->id_len; i++) + if (buf[i * 2] != nor->info->id[i]) + return -EINVAL; + return 0; +} + +static int spi_nor_macronix_octal_dtr_dis(struct spi_nor *nor) +{ + u8 *buf = nor->bouncebuf; + int ret; + + /* + * One byte transactions are not allowed in 8D-8D-8D mode. mx66lm1g45g + * requires that undefined register addresses to keep their value + * unchanged. Its second CR2 byte value is not defined. Read the second + * byte value of CR2 so that we can write it back when disabling + * Octal DTR mode. + */ + ret = spi_nor_macronix_read_cr2(nor, 4, buf, 2); + if (ret) + return ret; + /* Keep the value of buf[1] unchanged.*/ + buf[0] = SPINOR_REG_CR2_SPI; + + ret = spi_nor_macronix_write_cr2(nor, buf, 2); + if (ret) + return ret; + + ret = spi_nor_read_id(nor, 0, 0, buf, SNOR_PROTO_1_1_1); + if (ret) + return ret; + + if (memcmp(buf, nor->info->id, nor->info->id_len)) { + dev_dbg(nor->dev, "Failed to disable 8D-8D-8D mode.\n"); + return -EINVAL; + } + return 0; +} + +static int spi_nor_macronix_set_octal_dtr(struct spi_nor *nor, bool enable) +{ + return enable ? spi_nor_macronix_octal_dtr_en(nor) : + spi_nor_macronix_octal_dtr_dis(nor); +} + +static int mx66lm1g45g_late_init(struct spi_nor *nor) +{ + nor->params->set_octal_dtr = spi_nor_macronix_set_octal_dtr; + + /* Set the Fast Read settings. */ + nor->params->hwcaps.mask |= SNOR_HWCAPS_READ_8_8_8_DTR; + spi_nor_set_read_settings(&nor->params->reads[SNOR_CMD_READ_8_8_8_DTR], + 0, 20, SPINOR_OP_MX_DTR_RD, + SNOR_PROTO_8_8_8_DTR); + + nor->cmd_ext_type = SPI_NOR_EXT_INVERT; + nor->params->rdsr_dummy = 4; + nor->params->rdsr_addr_nbytes = 4; + + return 0; +} + +static struct spi_nor_fixups mx66lm1g45g_fixups = { + .late_init = mx66lm1g45g_late_init, +}; + static int mx25l25635_post_bfpt_fixups(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:232 @ { "mx66u2g45g", INFO(0xc2253c, 0, 64 * 1024, 4096) NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) FIXUP_FLAGS(SPI_NOR_4B_OPCODES) }, + { "mx66lm1g45g", INFO(0xc2853b, 0, 64 * 1024, 2048) + NO_SFDP_FLAGS(SPI_NOR_SKIP_SFDP | SECT_4K | + SPI_NOR_OCTAL_DTR_READ | SPI_NOR_OCTAL_DTR_PP | + SPI_NOR_DTR_BSWAP16) + FIXUP_FLAGS(SPI_NOR_4B_OPCODES | SPI_NOR_IO_MODE_EN_VOLATILE | + SPI_NOR_SOFT_RESET) + .fixups = &mx66lm1g45g_fixups, + }, }; static void macronix_nor_default_init(struct spi_nor *nor) diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mtd/spi-nor/sfdp.c linux-microchip/drivers/mtd/spi-nor/sfdp.c --- linux-6.6.51/drivers/mtd/spi-nor/sfdp.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/mtd/spi-nor/sfdp.c 2024-11-26 14:02:37.738497997 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:653 @ return -EOPNOTSUPP; } + if (bfpt.dwords[SFDP_DWORD(18)] & BFPT_DWORD18_BYTE_ORDER_SWAPPED) + nor->flags |= SNOR_F_DTR_BSWAP16; + return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt); } diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mtd/spi-nor/sfdp.h linux-microchip/drivers/mtd/spi-nor/sfdp.h --- linux-6.6.51/drivers/mtd/spi-nor/sfdp.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/mtd/spi-nor/sfdp.h 2024-11-26 14:02:37.738497997 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:126 @ #define BFPT_DWORD18_CMD_EXT_INV (0x1UL << 29) /* Invert */ #define BFPT_DWORD18_CMD_EXT_RES (0x2UL << 29) /* Reserved */ #define BFPT_DWORD18_CMD_EXT_16B (0x3UL << 29) /* 16-bit opcode */ +#define BFPT_DWORD18_BYTE_ORDER_SWAPPED BIT(31) struct sfdp_parameter_header { u8 id_lsb; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/mtd/spi-nor/sst.c linux-microchip/drivers/mtd/spi-nor/sst.c --- linux-6.6.51/drivers/mtd/spi-nor/sst.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/mtd/spi-nor/sst.c 2024-11-26 14:02:37.738497997 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:115 @ NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "sst26vf016b", INFO(0xbf2641, 0, 64 * 1024, 32) - NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ) }, + FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) + NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ) + .fixups = &sst26vf_nor_fixups }, { "sst26vf032b", INFO(0xbf2642, 0, 0, 0) FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) PARSE_SFDP diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/can/Kconfig linux-microchip/drivers/net/can/Kconfig --- linux-6.6.51/drivers/net/can/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/net/can/Kconfig 2024-11-26 14:02:37.746498139 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:171 @ Kvaser Mini PCI Express 1xCAN v3 Kvaser Mini PCI Express 2xCAN v3 +config CAN_POLARFIRE_SOC + tristate "Microchip Polarfire SoC CAN" + depends on SOC_MICROCHIP_POLARFIRE || COMPILE_TEST + help + Microchip Polarfire SoC CAN driver. + config CAN_SLCAN tristate "Serial / USB serial CAN Adaptors (slcan)" depends on TTY diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/can/Makefile linux-microchip/drivers/net/can/Makefile --- linux-6.6.51/drivers/net/can/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/net/can/Makefile 2024-11-26 14:02:37.746498139 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:30 @ obj-$(CONFIG_CAN_MSCAN) += mscan/ obj-$(CONFIG_CAN_M_CAN) += m_can/ obj-$(CONFIG_CAN_PEAK_PCIEFD) += peak_canfd/ +obj-$(CONFIG_CAN_POLARFIRE_SOC) += mpfs_can.o obj-$(CONFIG_CAN_SJA1000) += sja1000/ obj-$(CONFIG_CAN_SUN4I) += sun4i_can.o obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/can/mpfs_can.c linux-microchip/drivers/net/can/mpfs_can.c --- linux-6.6.51/drivers/net/can/mpfs_can.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/net/can/mpfs_can.c 2024-11-26 14:02:37.746498139 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: (GPL-2.0) +/* + * Microchip Polarfire SoC MSS CAN controller driver + * + * Copyright (C) 2023 Microchip Technology Inc. and its subsidiaries + * + * Author: Naga Sureshkumar Relli <nagasuresh.relli@microchip.com> + * + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/can/dev.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/reset.h> + +#define DRIVER_NAME "mpfs_can" + +/* CAN registers set */ +enum mpfs_can_reg { + MPFS_CAN_ISR_OFFSET = 0x00, + MPFS_CAN_IER_OFFSET = 0x04, + MPFS_CAN_RXBUF_STAT_OFFSET = 0x08, + MPFS_CAN_TXBUF_STAT_OFFSET = 0x0C, + MPFS_CAN_ESR_OFFSET = 0x10, + MPFS_CAN_COMMAND_OFFSET = 0x14, + MPFS_CAN_CONFIG_OFFSET = 0x18, + MPFS_CAN_TX_MSG_CTRL_CMD_BASE = 0x20, + MPFS_CAN_TX_MSG_ID_BASE = 0x24, + MPFS_CAN_TX_MSG_DATAL_BASE = 0x28, + MPFS_CAN_TX_MSG_DATAH_BASE = 0x2C, + MPFS_CAN_RX_MSG_CTRL_CMD_BASE = 0x220, + MPFS_CAN_RX_MSG_ID_BASE = 0x224, + MPFS_CAN_RX_MSG_DATAL_BASE = 0x228, + MPFS_CAN_RX_MSG_DATAH_BASE = 0x22C, + MPFS_CAN_RX_MSG_AMR_BASE = 0x230, + MPFS_CAN_RX_MSG_ACR_BASE = 0x234, + MPFS_CAN_RX_MSG_AMR_DATA_BASE = 0x238, + MPFS_CAN_RX_MSG_ACR_DATA_BASE = 0x23C, +}; + +/* CAN ISR Register masks */ +#define MPFS_CAN_ISR_ARB_LOSS_MASK BIT(2) +#define MPFS_CAN_ISR_OVR_LOAD_MASK BIT(3) +#define MPFS_CAN_ISR_BIT_ERR_MASK BIT(4) +#define MPFS_CAN_ISR_STUFF_ERR_MASK BIT(5) +#define MPFS_CAN_ISR_ACK_ERR_MASK BIT(6) +#define MPFS_CAN_ISR_FORM_ERR_MASK BIT(7) +#define MPFS_CAN_ISR_CRC_ERR_MASK BIT(8) +#define MPFS_CAN_ISR_BUS_OFF_MASK BIT(9) +#define MPFS_CAN_ISR_RXMSG_LOS_MASK BIT(10) +#define MPFS_CAN_ISR_TXMSG_SNT_MASK BIT(11) +#define MPFS_CAN_ISR_RXMSG_SNT_MASK BIT(12) +#define MPFS_CAN_ISR_RTR_SNT_MASK BIT(13) +#define MPFS_CAN_ISR_STUKAT_0_MASK BIT(14) +#define MPFS_CAN_ISR_SST_FAIL_MASK BIT(15) + +/* CAN IER Register masks */ +#define MPFS_CAN_IER_GLOBAL_MASK BIT(0) +#define MPFS_CAN_IER_ARB_LOSS_MASK BIT(2) +#define MPFS_CAN_IER_OVR_LOAD_MASK BIT(3) +#define MPFS_CAN_IER_BIT_ERR_MASK BIT(4) +#define MPFS_CAN_IER_STUFF_ERR_MASK BIT(5) +#define MPFS_CAN_IER_ACK_ERR_MASK BIT(6) +#define MPFS_CAN_IER_FORM_ERR_MASK BIT(7) +#define MPFS_CAN_IER_CRC_ERR_MASK BIT(8) +#define MPFS_CAN_IER_BUS_OFF_MASK BIT(9) +#define MPFS_CAN_IER_RXMSG_LOS_MASK BIT(10) +#define MPFS_CAN_IER_TXMSG_SNT_MASK BIT(11) +#define MPFS_CAN_IER_RXMSG_SNT_MASK BIT(12) +#define MPFS_CAN_IER_RTR_SNT_MASK BIT(13) +#define MPFS_CAN_IER_STUKAT_0_MASK BIT(14) +#define MPFS_CAN_IER_SST_FAIL_MASK BIT(15) + +/* CAN ERR Register masks */ +#define MPFS_CAN_ERR_TXCNT_MASK GENMASK(7, 0) +#define MPFS_CAN_ERR_RXCNT_MASK GENMASK(15, 8) +#define MPFS_CAN_ERR_STATE_MASK GENMASK(17, 16) +#define MPFS_CAN_ERR_TX96_MASK BIT(18) +#define MPFS_CAN_ERR_RX96_MASK BIT(19) +#define MPFS_CAN_ERR_RXCNT_SHIFT 8 + +/* CAN CMD Register masks */ +#define MPFS_CAN_CMD_NORMAL_MASK BIT(0) +#define MPFS_CAN_CMD_LISTEN_MASK GENMASK(1, 0) +#define MPFS_CAN_CMD_LOOP_MASK GENMASK(2, 0) +#define MPFS_CAN_CMD_SRAM_TEST_MASK BIT(3) +#define MPFS_CAN_CMD_SW_REST_MASK BIT(4) +#define MPFS_CAN_CMD_REV_CTRL_MASK GENMASK(31, 16) + +/* CAN CONFIG Register masks */ +#define MPFS_CAN_CONFIG_EDGE_MODE_MASK BIT(0) +#define MPFS_CAN_CONFIG_SAMPLE_MODE_MASK BIT(1) +#define MPFS_CAN_CONFIG_CFG_SJW_MASK GENMASK(3, 2) +#define MPFS_CAN_CONFIG_ATO_RSRT_MASK BIT(4) +#define MPFS_CAN_CONFIG_TSEG2_MASK GENMASK(7, 5) +#define MPFS_CAN_CONFIG_TSEG1_MASK GENMASK(11, 8) +#define MPFS_CAN_CONFIG_ARBITR_MASK BIT(12) +#define MPFS_CAN_CONFIG_SWP_ENDIN_MASK BIT(13) +#define MPFS_CAN_CONFIG_ECR_MASK BIT(14) +#define MPFS_CAN_CONFIG_BITR_MASK GENMASK(30, 16) + +/* CAN ECR Register masks */ +#define MPFS_CAN_ECR_STAT_MASK BIT(0) +#define MPFS_CAN_ECR_ERR_TYPE_MASK GENMASK(3, 1) +#define MPFS_CAN_ECR_TXMODE_MASK BIT(4) +#define MPFS_CAN_ECR_RXMODE_MASK BIT(5) +#define MPFS_CAN_ECR_BITNR_MASK GENMASK(11, 6) +#define MPFS_CAN_ECR_FILED_MASK GENMASK(16, 12) + +/* CAN TXMSG CTRL Register masks */ +#define MPFS_CAN_TXMSG_CTRL_CMD_TXREQ_MASK BIT(0) +#define MPFS_CAN_TXMSG_CTRL_CMD_TXABORT_MASK BIT(1) +#define MPFS_CAN_TXMSG_CTRL_CMD_TXINTEN_MASK BIT(2) +#define MPFS_CAN_TXMSG_CTRL_CMD_WPNA_MASK BIT(3) +#define MPFS_CAN_TXMSG_CTRL_CMD_DLC_MASK GENMASK(19, 16) +#define MPFS_CAN_TXMSG_CTRL_CMD_IDE_MASK BIT(20) +#define MPFS_CAN_TXMSG_CTRL_CMD_RTR_MASK BIT(21) +#define MPFS_CAN_TXMSG_CTRL_CMD_WPNB_MASK BIT(23) + +/* CAN TXMSG Id/Data Register masks */ +#define MPFS_CAN_TXMSG_ID_MASK GENMASK(31, 3) +#define MPFS_CAN_TXMSG_DATAH_MASK GENMASK(31, 0) +#define MPFS_CAN_TXMSG_DATAL_MASK GENMASK(31, 0) + +/* CAN RXMSG CTRL Register masks */ +#define MPFS_CAN_RXMSG_CTRL_CMD_AVAIL_MASK BIT(0) +#define MPFS_CAN_RXMSG_CTRL_CMD_RTRP_MASK BIT(1) +#define MPFS_CAN_RXMSG_CTRL_CMD_RTRABRT_MASK BIT(2) +#define MPFS_CAN_RXMSG_CTRL_CMD_BUFEN_MASK BIT(3) +#define MPFS_CAN_RXMSG_CTRL_CMD_RTRRPLY_MASK BIT(4) +#define MPFS_CAN_RXMSG_CTRL_CMD_RXINTEN_MASK BIT(5) +#define MPFS_CAN_RXMSG_CTRL_CMD_LF_MASK BIT(6) +#define MPFS_CAN_RXMSG_CTRL_CMD_WPNL_MASK BIT(7) +#define MPFS_CAN_RXMSG_CTRL_CMD_DLC_MASK GENMASK(19, 16) +#define MPFS_CAN_RXMSG_CTRL_CMD_IDE_MASK BIT(20) +#define MPFS_CAN_RXMSG_CTRL_CMD_RTR_MASK BIT(21) +#define MPFS_CAN_RXMSG_CTRL_CMD_WPNH_MASK BIT(23) + +/* CAN RXMSG ID/Data Register masks */ +#define MPFS_CAN_RXMSG_ID_MASK GENMASK(31, 3) +#define MPFS_CAN_RXMSG_DATAH_MASK GENMASK(31, 0) +#define MPFS_CAN_RXMSG_DATAL_MASK GENMASK(31, 0) +#define MPFS_CAN_RXMSG_AMR_MASK GENMASK(31, 0) +#define MPFS_CAN_RXMSG_ACR_MASK GENMASK(31, 0) +#define MPFS_CAN_RXMSG_AMR_DATA GENMASK(31, 0) +#define MPFS_CAN_RXMSG_ACR_DATA GENMASK(31, 0) + +/* CAN Bit Timing masks */ +#define MPFS_CAN_BTR_SJW_MASK GENMASK(3, 2) +#define MPFS_CAN_BTR_MASK GENMASK(30, 16) +#define MPFS_CAN_DLC_MASK GENMASK(19, 16) +#define MPFS_CAN_TS1_MASK GENMASK(11, 8) +#define MPFS_CAN_TS2_MASK GENMASK(7, 5) +#define MPFS_CAN_STDID_SHIFT 21 +#define MPFS_CAN_EXTID_SHIFT 3 +#define MPFS_CAN_ESR_REC_SHIFT 8 + +/* CAN TX/RX register fifo offsets */ +#define TX_REG_LEN 16 +#define RX_REG_LEN 32 +#define TX_MSG_BASE(base, fifono) ((base) + ((fifono) * TX_REG_LEN)) +#define RX_MSG_BASE(base, fifono) ((base) + ((fifono) * RX_REG_LEN)) + +#define MPFS_CAN_TX_MSG_CTR_OFFSET(fifono) TX_MSG_BASE(MPFS_CAN_TX_MSG_CTRL_CMD_BASE, fifono) +#define MPFS_CAN_TX_MSG_ID_OFFSET(fifono) TX_MSG_BASE(MPFS_CAN_TX_MSG_ID_BASE, fifono) +#define MPFS_CAN_TX_DATAL_OFFSET(fifono) TX_MSG_BASE(MPFS_CAN_TX_MSG_DATAL_BASE, fifono) +#define MPFS_CAN_TX_DATAH_OFFSET(fifono) TX_MSG_BASE(MPFS_CAN_TX_MSG_DATAH_BASE, fifono) + +#define MPFS_CAN_RX_MSG_CTR_OFFSET(fifono) RX_MSG_BASE(MPFS_CAN_RX_MSG_CTRL_CMD_BASE, fifono) +#define MPFS_CAN_RX_MSG_ID_OFFSET(fifono) RX_MSG_BASE(MPFS_CAN_RX_MSG_ID_BASE, fifono) +#define MPFS_CAN_RX_DATAL_OFFSET(fifono) RX_MSG_BASE(MPFS_CAN_RX_MSG_DATAL_BASE, fifono) +#define MPFS_CAN_RX_DATAH_OFFSET(fifono) RX_MSG_BASE(MPFS_CAN_RX_MSG_DATAH_BASE, fifono) +#define MPFS_CAN_AMR_OFFSET(fifono) RX_MSG_BASE(MPFS_CAN_RX_MSG_AMR_BASE, fifono) +#define MPFS_CAN_ACR_OFFSET(fifono) RX_MSG_BASE(MPFS_CAN_RX_MSG_ACR_BASE, fifono) +#define MPFS_CAN_AMR_DATA_OFFSET(fifono) RX_MSG_BASE(MPFS_CAN_RX_MSG_AMR_DATA_BASE, fifono) +#define MPFS_CAN_ACR_DATA_OFFSET(fifono) RX_MSG_BASE(MPFS_CAN_RX_MSG_ACR_DATA_BASE, fifono) + +#define MPFS_CAN_TX_BUFFERS 32 +#define MPFS_CAN_RX_BUFFERS 32 +#define MPFS_CAN_AMR_MASK 0xFFFFFFFF +#define MPFS_CAN_AMR_DATA_MASK 0xFFFFFFFF + +/* CAN Error and Interrupt masks */ +#define MPFS_CAN_ERR_MASK (MPFS_CAN_ISR_ARB_LOSS_MASK | \ + MPFS_CAN_ISR_BIT_ERR_MASK | \ + MPFS_CAN_ISR_STUFF_ERR_MASK | \ + MPFS_CAN_ISR_ACK_ERR_MASK | \ + MPFS_CAN_ISR_FORM_ERR_MASK | \ + MPFS_CAN_ISR_CRC_ERR_MASK | \ + MPFS_CAN_ISR_BUS_OFF_MASK) + +#define MPFS_CAN_IER_MASK (MPFS_CAN_IER_ARB_LOSS_MASK | \ + MPFS_CAN_IER_BIT_ERR_MASK | \ + MPFS_CAN_IER_STUFF_ERR_MASK | \ + MPFS_CAN_IER_ACK_ERR_MASK | \ + MPFS_CAN_IER_FORM_ERR_MASK | \ + MPFS_CAN_IER_CRC_ERR_MASK | \ + MPFS_CAN_IER_BUS_OFF_MASK | \ + MPFS_CAN_IER_RXMSG_LOS_MASK | \ + MPFS_CAN_IER_TXMSG_SNT_MASK | \ + MPFS_CAN_IER_RXMSG_SNT_MASK | \ + MPFS_CAN_IER_RTR_SNT_MASK | \ + MPFS_CAN_IER_GLOBAL_MASK) + +/** + * struct mpfs_can_priv - CAN controller private data + * @can: CAN private data structure. + * @tx_lock: Lock for synchronizing TX interrupt handling + * @tx_head: Tx CAN packets ready to send on the queue + * @tx_tail: Tx CAN packets successfully sended on the queue + * @tx_max: Maximum number packets the driver can send + * @napi: NAPI structure + * @dev: Network device data structure + * @reg: Ioremapped address to registers + * @irq_flags: For request_irq() + * @pclk: Pointer to struct clk + * @can_clk: Pointer to struct clk + */ +struct mpfs_can_priv { + struct can_priv can; + spinlock_t tx_lock; + unsigned int tx_head; + unsigned int tx_tail; + unsigned int tx_max; + struct napi_struct napi; + struct device *dev; + void __iomem *reg; + unsigned long irq_flags; + struct clk_bulk_data *clks; + int num_clocks; + const struct can_bittiming_const *bittiming_const; +}; + +static const struct can_bittiming_const mpfs_can_bittiming_const = { + .name = DRIVER_NAME, + .tseg1_min = 2, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 0, + .brp_max = 32767, + .brp_inc = 1, +}; + +static void mpfs_can_write(const struct mpfs_can_priv *priv, enum mpfs_can_reg reg, + u32 val) +{ + writel(val, priv->reg + reg); +} + +static u32 mpfs_can_read(const struct mpfs_can_priv *priv, enum mpfs_can_reg reg) +{ + return readl(priv->reg + reg); +} + +static int mpfs_can_reset(struct net_device *ndev) +{ + struct mpfs_can_priv *priv = netdev_priv(ndev); + int ret; + + /* There is no separate register in the MSS CAN to do the soft reset + * hence do the full device reset and this is needed to bring back the + * bus to normal state in error cases. + */ + ret = device_reset(priv->dev); + if (ret) + return ret; + + priv->tx_head = 0; + priv->tx_tail = 0; + + return ret; +} + +static int mpfs_can_set_bittiming(struct net_device *ndev) +{ + struct mpfs_can_priv *priv = netdev_priv(ndev); + struct can_bittiming *bt = &priv->can.bittiming; + u32 config, can_started; + + can_started = mpfs_can_read(priv, MPFS_CAN_COMMAND_OFFSET) & MPFS_CAN_CMD_NORMAL_MASK; + if (can_started) { + netdev_alert(ndev, + "BUG! Cannot set bittiming - CAN is already running\n"); + return -EPERM; + } + + /* The TS1, TS2 and buad rate values written in the Config register are + * added to 1 to compute the prescalar. + * TS1 configuration for the mpfs can includes both propagation seg and phase seg1 + */ + config = FIELD_PREP(MPFS_CAN_BTR_MASK, bt->brp - 1) | + FIELD_PREP(MPFS_CAN_TS1_MASK, bt->prop_seg + bt->phase_seg1 - 1) | + FIELD_PREP(MPFS_CAN_TS2_MASK, bt->phase_seg2 - 1) | + FIELD_PREP(MPFS_CAN_BTR_SJW_MASK, bt->sjw - 1); + + mpfs_can_write(priv, MPFS_CAN_CONFIG_OFFSET, config); + + return 0; +} + +static int mpfs_can_start(struct net_device *ndev) +{ + struct mpfs_can_priv *priv = netdev_priv(ndev); + u32 reg_msr; + int ret, buf; + + ret = mpfs_can_reset(ndev); + if (ret) + return ret; + + ret = mpfs_can_set_bittiming(ndev); + if (ret < 0) + return ret; + + /* Clear pending interrupts */ + mpfs_can_write(priv, MPFS_CAN_ISR_OFFSET, 0x0); + + /* Enable interrupts */ + mpfs_can_write(priv, MPFS_CAN_IER_OFFSET, MPFS_CAN_IER_MASK); + + /* Check whether it is loopback mode or normal mode */ + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) + reg_msr = MPFS_CAN_CMD_LOOP_MASK; + else + reg_msr = MPFS_CAN_CMD_NORMAL_MASK; + + mpfs_can_write(priv, MPFS_CAN_COMMAND_OFFSET, reg_msr); + + for (buf = 0; buf < MPFS_CAN_RX_BUFFERS; buf++) { + mpfs_can_write(priv, MPFS_CAN_AMR_OFFSET(buf), MPFS_CAN_AMR_MASK); + mpfs_can_write(priv, MPFS_CAN_ACR_OFFSET(buf), 0x0); + mpfs_can_write(priv, MPFS_CAN_AMR_DATA_OFFSET(buf), MPFS_CAN_AMR_DATA_MASK); + mpfs_can_write(priv, MPFS_CAN_ACR_DATA_OFFSET(buf), 0x0); + + /* Enable the link flag for the buffers, so that the receive data will store + * in sequential order. + */ + mpfs_can_write(priv, MPFS_CAN_RX_MSG_CTR_OFFSET(buf), + (MPFS_CAN_RXMSG_CTRL_CMD_LF_MASK | + MPFS_CAN_RXMSG_CTRL_CMD_WPNL_MASK | + MPFS_CAN_RXMSG_CTRL_CMD_WPNH_MASK | + MPFS_CAN_RXMSG_CTRL_CMD_BUFEN_MASK | + MPFS_CAN_RXMSG_CTRL_CMD_RXINTEN_MASK)); + + /* Link flag is not needed for the last buffer */ + if (buf == (MPFS_CAN_RX_BUFFERS - 1)) + mpfs_can_write(priv, MPFS_CAN_RX_MSG_CTR_OFFSET(buf), + (MPFS_CAN_RXMSG_CTRL_CMD_WPNL_MASK | + MPFS_CAN_RXMSG_CTRL_CMD_WPNH_MASK | + MPFS_CAN_RXMSG_CTRL_CMD_BUFEN_MASK | + MPFS_CAN_RXMSG_CTRL_CMD_RXINTEN_MASK)); + } + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + return 0; +} + +static int mpfs_can_do_set_mode(struct net_device *ndev, enum can_mode mode) +{ + int ret = 0; + + switch (mode) { + case CAN_MODE_START: + ret = mpfs_can_start(ndev); + if (ret < 0) { + netdev_err(ndev, "mpfs_can_start failed!\n"); + return ret; + } + netif_wake_queue(ndev); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + +static void mpfs_can_tx(struct net_device *ndev, struct sk_buff *skb, + int buf_off) +{ + struct can_frame *frame = (struct can_frame *)skb->data; + struct mpfs_can_priv *priv = netdev_priv(ndev); + u32 id = 0, data[2] = {0, 0}, ctlr = 0; + + if (frame->can_id & CAN_EFF_FLAG) { + ctlr = MPFS_CAN_TXMSG_CTRL_CMD_IDE_MASK; + id = (frame->can_id << MPFS_CAN_EXTID_SHIFT); + } else { + id = (frame->can_id << MPFS_CAN_STDID_SHIFT); + } + + if (frame->can_id & CAN_RTR_FLAG) + ctlr |= MPFS_CAN_TXMSG_CTRL_CMD_RTR_MASK; + + ctlr |= FIELD_PREP(MPFS_CAN_DLC_MASK, frame->len) | + MPFS_CAN_TXMSG_CTRL_CMD_WPNA_MASK | MPFS_CAN_TXMSG_CTRL_CMD_WPNB_MASK | + MPFS_CAN_TXMSG_CTRL_CMD_TXREQ_MASK | MPFS_CAN_TXMSG_CTRL_CMD_TXINTEN_MASK; + can_put_echo_skb(skb, ndev, priv->tx_head % priv->tx_max, 0); + + priv->tx_head++; + mpfs_can_write(priv, MPFS_CAN_TX_MSG_ID_OFFSET(buf_off), id); + + if (frame->len > 0) + data[0] = be32_to_cpup((__be32 *)(frame->data + 0)); + if (frame->len > 4) + data[1] = be32_to_cpup((__be32 *)(frame->data + 4)); + + if (!(frame->can_id & CAN_RTR_FLAG)) { + mpfs_can_write(priv, MPFS_CAN_TX_DATAL_OFFSET(buf_off), data[0]); + mpfs_can_write(priv, MPFS_CAN_TX_DATAH_OFFSET(buf_off), data[1]); + } + + mpfs_can_write(priv, MPFS_CAN_TX_MSG_CTR_OFFSET(buf_off), ctlr); +} + +static int mpfs_can_get_txmsg_index(struct mpfs_can_priv *priv) +{ + int available_txreq; + + for (int buf_index = 0; buf_index < MPFS_CAN_TX_BUFFERS; buf_index++) { + available_txreq = mpfs_can_read(priv, MPFS_CAN_TX_MSG_CTR_OFFSET(buf_index)); + + if (available_txreq & MPFS_CAN_TXMSG_CTRL_CMD_TXREQ_MASK) + continue; + + return buf_index; + } + + return -ENOBUFS; +} + +static int mpfs_can_get_rxmsg_index(struct mpfs_can_priv *priv) +{ + int available_rxreq; + + for (int buf_index = 0; buf_index < MPFS_CAN_RX_BUFFERS; buf_index++) { + available_rxreq = mpfs_can_read(priv, MPFS_CAN_RX_MSG_CTR_OFFSET(buf_index)); + + if (!(available_rxreq & MPFS_CAN_RXMSG_CTRL_CMD_AVAIL_MASK)) + continue; + + return buf_index; + } + + return -ENOBUFS; +} + +static netdev_tx_t mpfs_can_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct mpfs_can_priv *priv = netdev_priv(ndev); + int buf_off; + unsigned long flags; + + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + + buf_off = mpfs_can_get_txmsg_index(priv); + if (buf_off < 0) { + netdev_err(ndev, "BUG!, TX full when queue awake!\n"); + netif_stop_queue(ndev); + return NETDEV_TX_BUSY; + } + + spin_lock_irqsave(&priv->tx_lock, flags); + mpfs_can_tx(ndev, skb, buf_off); + + if ((priv->tx_head - priv->tx_tail) == priv->tx_max) + netif_stop_queue(ndev); + + netif_stop_queue(ndev); + + spin_unlock_irqrestore(&priv->tx_lock, flags); + + return NETDEV_TX_OK; +} + +static int mpfs_can_rx(struct net_device *ndev, int buf_off) +{ + struct mpfs_can_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *frame; + struct sk_buff *skb; + u32 id, data[2] = {0, 0}, ctrl; + + skb = alloc_can_skb(ndev, &frame); + if (unlikely(!skb)) { + stats->rx_dropped++; + return 0; + } + + id = mpfs_can_read(priv, MPFS_CAN_RX_MSG_ID_OFFSET(buf_off)); + ctrl = mpfs_can_read(priv, MPFS_CAN_RX_MSG_CTR_OFFSET(buf_off)); + + if (ctrl & MPFS_CAN_RXMSG_CTRL_CMD_IDE_MASK) { + frame->can_id = CAN_EFF_FLAG; + frame->can_id |= (id >> MPFS_CAN_EXTID_SHIFT); + } else { + frame->can_id = (id >> MPFS_CAN_STDID_SHIFT); + } + + if (ctrl & MPFS_CAN_RXMSG_CTRL_CMD_RTR_MASK) + frame->can_id |= CAN_RTR_FLAG; + + frame->len = (ctrl & MPFS_CAN_DLC_MASK) >> 16; + + data[0] = mpfs_can_read(priv, MPFS_CAN_RX_DATAL_OFFSET(buf_off)); + data[1] = mpfs_can_read(priv, MPFS_CAN_RX_DATAH_OFFSET(buf_off)); + + if (!(frame->can_id & CAN_RTR_FLAG)) { + if (frame->len > 0) + *(__be32 *)(frame->data) = cpu_to_be32(data[0]); + if (frame->len > 4) + *(__be32 *)(frame->data + 4) = cpu_to_be32(data[1]); + } + + /* Notify the controller that the rx message is read by setting message + * available bit. + */ + ctrl = mpfs_can_read(priv, MPFS_CAN_RX_MSG_CTR_OFFSET(buf_off)); + ctrl |= MPFS_CAN_RXMSG_CTRL_CMD_AVAIL_MASK; + mpfs_can_write(priv, MPFS_CAN_RX_MSG_CTR_OFFSET(buf_off), ctrl); + + stats->rx_bytes += frame->len; + stats->rx_packets++; + netif_receive_skb(skb); + + return 1; +} + +static enum can_state mpfs_can_current_error_state(struct net_device *ndev) +{ + struct mpfs_can_priv *priv = netdev_priv(ndev); + u32 status = mpfs_can_read(priv, MPFS_CAN_ESR_OFFSET); + + if (status & MPFS_CAN_ERR_STATE_MASK) + return CAN_STATE_ERROR_PASSIVE; + else if (status & (MPFS_CAN_ERR_TX96_MASK | MPFS_CAN_ERR_RX96_MASK)) + return CAN_STATE_ERROR_WARNING; + else + return CAN_STATE_ERROR_ACTIVE; +} + +static void mpfs_can_set_error_state(struct net_device *ndev, enum can_state new_state, + struct can_frame *frame) +{ + struct mpfs_can_priv *priv = netdev_priv(ndev); + u32 ecr = mpfs_can_read(priv, MPFS_CAN_ESR_OFFSET); + u32 txerr = ecr & MPFS_CAN_ERR_TXCNT_MASK; + u32 rxerr = (ecr & MPFS_CAN_ERR_RXCNT_MASK) >> MPFS_CAN_ERR_RXCNT_SHIFT; + enum can_state tx_state = txerr >= rxerr ? new_state : 0; + enum can_state rx_state = txerr <= rxerr ? new_state : 0; + + if (new_state > CAN_STATE_ERROR_PASSIVE) { + frame->can_id |= CAN_ERR_CRTL; + frame->data[1] = (txerr > rxerr) ? + CAN_ERR_CRTL_TX_PASSIVE : CAN_ERR_CRTL_RX_PASSIVE; + } else { + can_change_state(ndev, frame, tx_state, rx_state); + + if (frame) { + frame->data[6] = txerr; + frame->data[7] = rxerr; + } + } +} + +static void mpfs_can_update_error_state(struct net_device *ndev) +{ + struct mpfs_can_priv *priv = netdev_priv(ndev); + enum can_state old_state = priv->can.state; + enum can_state new_state; + + if (old_state != CAN_STATE_ERROR_WARNING && + old_state != CAN_STATE_ERROR_PASSIVE) + return; + + new_state = mpfs_can_current_error_state(ndev); + + if (new_state != old_state) { + struct sk_buff *skb; + struct can_frame *cf; + + skb = alloc_can_err_skb(ndev, &cf); + + mpfs_can_set_error_state(ndev, new_state, skb ? cf : NULL); + + if (skb) { + struct net_device_stats *stats = &ndev->stats; + + stats->rx_packets++; + stats->rx_bytes += cf->len; + netif_rx(skb); + } + } +} + +static void mpfs_can_err_interrupt(struct net_device *ndev, u32 int_status) +{ + struct mpfs_can_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame cf = { }; + u32 err_status; + + err_status = mpfs_can_read(priv, MPFS_CAN_ESR_OFFSET); + mpfs_can_write(priv, MPFS_CAN_ESR_OFFSET, err_status); + + if (int_status & MPFS_CAN_ISR_BUS_OFF_MASK) { + priv->can.state = CAN_STATE_BUS_OFF; + priv->can.can_stats.bus_off++; + can_bus_off(ndev); + cf.can_id |= CAN_ERR_BUSOFF; + } else { + enum can_state new_state = mpfs_can_current_error_state(ndev); + + if (new_state != priv->can.state) + mpfs_can_set_error_state(ndev, new_state, &cf); + } + + if (int_status & MPFS_CAN_ISR_ARB_LOSS_MASK) { + priv->can.can_stats.arbitration_lost++; + cf.can_id |= CAN_ERR_LOSTARB; + cf.data[0] = CAN_ERR_LOSTARB_UNSPEC; + } + + if (int_status & MPFS_CAN_ERR_MASK) { + priv->can.can_stats.bus_error++; + cf.can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + + if (int_status & MPFS_CAN_ISR_ACK_ERR_MASK) { + stats->tx_errors++; + cf.can_id |= CAN_ERR_ACK; + cf.data[3] = CAN_ERR_PROT_LOC_ACK; + } + + if (int_status & MPFS_CAN_ISR_BIT_ERR_MASK) { + stats->tx_errors++; + cf.can_id |= CAN_ERR_PROT; + cf.data[2] |= CAN_ERR_PROT_BIT; + } + + if (int_status & MPFS_CAN_ISR_STUFF_ERR_MASK) { + stats->rx_errors++; + cf.can_id |= CAN_ERR_PROT; + cf.data[2] |= CAN_ERR_PROT_STUFF; + } + + if (err_status & MPFS_CAN_ISR_FORM_ERR_MASK) { + stats->rx_errors++; + cf.can_id |= CAN_ERR_PROT; + cf.data[2] |= CAN_ERR_PROT_FORM; + } + + if (err_status & MPFS_CAN_ISR_CRC_ERR_MASK) { + stats->rx_errors++; + cf.can_id |= CAN_ERR_PROT; + cf.data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ; + } + + if (err_status & MPFS_CAN_ISR_OVR_LOAD_MASK) { + stats->rx_over_errors++; + stats->rx_errors++; + cf.can_id |= CAN_ERR_PROT; + cf.data[3] |= CAN_ERR_PROT_OVERLOAD; + } + } + + if (cf.can_id) { + struct can_frame *frame; + struct sk_buff *skb = alloc_can_err_skb(ndev, &frame); + + if (skb) { + frame->can_id |= cf.can_id; + memcpy(frame->data, cf.data, CAN_ERR_DLC); + stats->rx_packets++; + stats->rx_bytes += CAN_ERR_DLC; + netif_rx(skb); + } + } + + netdev_dbg(ndev, "%s: error status register:0x%x\n", + __func__, mpfs_can_read(priv, MPFS_CAN_ESR_OFFSET)); +} + +static int mpfs_can_rx_poll(struct napi_struct *napi, int quota) +{ + struct net_device *ndev = napi->dev; + struct mpfs_can_priv *priv = netdev_priv(ndev); + u32 int_enable; + int processed = 0, buf_off; + + while ((buf_off = mpfs_can_get_rxmsg_index(priv)) >= 0 && (processed < quota)) + processed += mpfs_can_rx(ndev, buf_off); + + if (processed) + mpfs_can_update_error_state(ndev); + + if (processed < quota) { + if (napi_complete_done(napi, processed)) { + int_enable = mpfs_can_read(priv, MPFS_CAN_IER_OFFSET); + int_enable |= (MPFS_CAN_IER_RXMSG_SNT_MASK); + mpfs_can_write(priv, MPFS_CAN_IER_OFFSET, int_enable); + } + } + + return processed; +} + +static void mpfs_can_tx_interrupt(struct net_device *ndev) +{ + struct mpfs_can_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + unsigned int frames_in_fifo; + unsigned long flags; + int bytes = 0; + + spin_lock_irqsave(&priv->tx_lock, flags); + frames_in_fifo = priv->tx_head - priv->tx_tail; + + while (frames_in_fifo--) { + bytes = can_get_echo_skb(ndev, priv->tx_tail % priv->tx_max, NULL); + stats->tx_bytes += bytes; + priv->tx_tail++; + stats->tx_packets++; + } + + netif_wake_queue(ndev); + spin_unlock_irqrestore(&priv->tx_lock, flags); + mpfs_can_update_error_state(ndev); +} + +static irqreturn_t mpfs_can_interrupt(int irq, void *dev_id) +{ + struct net_device *ndev = (struct net_device *)dev_id; + struct mpfs_can_priv *priv = netdev_priv(ndev); + u32 isr, ier; + + isr = mpfs_can_read(priv, MPFS_CAN_ISR_OFFSET); + if (!isr) + return IRQ_NONE; + + mpfs_can_write(priv, MPFS_CAN_ISR_OFFSET, isr); + + if (isr & MPFS_CAN_ISR_TXMSG_SNT_MASK) + mpfs_can_tx_interrupt(ndev); + + if (isr & MPFS_CAN_ERR_MASK) + mpfs_can_err_interrupt(ndev, isr); + + if (isr & MPFS_CAN_ISR_RXMSG_SNT_MASK) { + ier = mpfs_can_read(priv, MPFS_CAN_IER_OFFSET); + ier &= ~(MPFS_CAN_IER_RXMSG_SNT_MASK | MPFS_CAN_IER_RTR_SNT_MASK); + mpfs_can_write(priv, MPFS_CAN_IER_OFFSET, ier); + napi_schedule(&priv->napi); + } + + return IRQ_HANDLED; +} + +static int mpfs_can_chip_stop(struct net_device *ndev) +{ + struct mpfs_can_priv *priv = netdev_priv(ndev); + int ret; + + ret = mpfs_can_reset(ndev); + if (ret) + return ret; + + priv->can.state = CAN_STATE_STOPPED; + + return 0; +} + +static int mpfs_can_open(struct net_device *ndev) +{ + struct mpfs_can_priv *priv = netdev_priv(ndev); + int ret; + + ret = request_irq(ndev->irq, mpfs_can_interrupt, priv->irq_flags, + ndev->name, ndev); + if (ret < 0) { + netdev_err(ndev, "irq allocation for CAN failed\n"); + return ret; + } + + ret = mpfs_can_reset(ndev); + if (ret) + goto err_irq; + + ret = open_candev(ndev); + if (ret) + goto err_irq; + + ret = mpfs_can_start(ndev); + if (ret < 0) { + netdev_err(ndev, "mpfs_can_start failed!\n"); + goto err_candev; + } + + napi_enable(&priv->napi); + netif_start_queue(ndev); + + return 0; + +err_candev: + close_candev(ndev); +err_irq: + free_irq(ndev->irq, ndev); + + return ret; +} + +static int mpfs_can_close(struct net_device *ndev) +{ + struct mpfs_can_priv *priv = netdev_priv(ndev); + int ret; + + netif_stop_queue(ndev); + napi_disable(&priv->napi); + ret = mpfs_can_chip_stop(ndev); + free_irq(ndev->irq, ndev); + close_candev(ndev); + + return ret; +} + +static int mpfs_can_get_berr_counter(const struct net_device *ndev, + struct can_berr_counter *bec) +{ + struct mpfs_can_priv *priv = netdev_priv(ndev); + int err_cnt; + + err_cnt = mpfs_can_read(priv, MPFS_CAN_ESR_OFFSET); + bec->txerr = err_cnt & MPFS_CAN_ERR_TXCNT_MASK; + bec->rxerr = (err_cnt & MPFS_CAN_ERR_RXCNT_MASK) >> MPFS_CAN_ERR_RXCNT_SHIFT; + + return 0; +} + +static const struct net_device_ops mpfs_can_netdev_ops = { + .ndo_open = mpfs_can_open, + .ndo_stop = mpfs_can_close, + .ndo_start_xmit = mpfs_can_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +static int mpfs_can_probe(struct platform_device *pdev) +{ + struct net_device *ndev; + struct mpfs_can_priv *priv; + struct resource *res; + int ret; + + ndev = alloc_candev(sizeof(struct mpfs_can_priv), MPFS_CAN_TX_BUFFERS); + if (!ndev) + return -ENOMEM; + + priv = netdev_priv(ndev); + priv->dev = &pdev->dev; + priv->can.bittiming_const = &mpfs_can_bittiming_const; + priv->can.do_set_mode = mpfs_can_do_set_mode; + priv->can.do_get_berr_counter = mpfs_can_get_berr_counter; + priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_BERR_REPORTING; + priv->tx_max = MPFS_CAN_TX_BUFFERS; + + priv->reg = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(priv->reg)) { + ret = PTR_ERR(priv->reg); + goto err; + } + + spin_lock_init(&priv->tx_lock); + + ndev->irq = platform_get_irq(pdev, 0); + ndev->flags |= IFF_ECHO; + ndev->netdev_ops = &mpfs_can_netdev_ops; + platform_set_drvdata(pdev, ndev); + SET_NETDEV_DEV(ndev, &pdev->dev); + + ret = clk_bulk_get_all(&pdev->dev, &priv->clks); + if (ret < 2) + goto err; + + priv->num_clocks = ret; + ret = clk_bulk_prepare_enable(priv->num_clocks, priv->clks); + if (ret) + goto err; + + /* Retrieve the CAN clock rate */ + priv->can.clock.freq = clk_get_rate(priv->clks[1].clk); + + netif_napi_add_weight(ndev, &priv->napi, mpfs_can_rx_poll, MPFS_CAN_RX_BUFFERS); + + ret = register_candev(ndev); + if (ret) { + ret = dev_err_probe(&pdev->dev, ret, "fail to register can device\n"); + goto err; + } + + netdev_dbg(ndev, "reg=0x%p irq=%d clock=%d, max tx buffers %d\n", + priv->reg, ndev->irq, priv->can.clock.freq, + priv->tx_max); + + return 0; + +err: + free_candev(ndev); + netif_napi_del(&priv->napi); + + return ret; +} + +static int mpfs_can_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct mpfs_can_priv *priv = netdev_priv(ndev); + + unregister_candev(ndev); + netif_napi_del(&priv->napi); + free_candev(ndev); + + return 0; +} + +static const struct of_device_id mpfs_can_of_match[] = { + { .compatible = "microchip,mpfs-can", .data = NULL }, + { /* end of list */ }, +}; + +MODULE_DEVICE_TABLE(of, mpfs_can_of_match); + +static struct platform_driver mpfs_can_driver = { + .probe = mpfs_can_probe, + .remove = mpfs_can_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = mpfs_can_of_match, + }, +}; + +module_platform_driver(mpfs_can_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Microchip Polarfire SoC MSS CAN controller driver"); +MODULE_AUTHOR("Naga Sureshkumar Relli <nagasuresh.relli@microchip.com"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/dsa/microchip/ksz_common.c linux-microchip/drivers/net/dsa/microchip/ksz_common.c --- linux-6.6.51/drivers/net/dsa/microchip/ksz_common.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/net/dsa/microchip/ksz_common.c 2024-11-26 14:04:18.560303308 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:514 @ regmap_reg_range(0x111a, 0x111d), regmap_reg_range(0x1122, 0x1127), regmap_reg_range(0x112a, 0x112b), - regmap_reg_range(0x1136, 0x1139), - regmap_reg_range(0x113e, 0x113f), + regmap_reg_range(0x1134, 0x113f), regmap_reg_range(0x1400, 0x1401), regmap_reg_range(0x1403, 0x1403), regmap_reg_range(0x1410, 0x1417), @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:543 @ regmap_reg_range(0x211a, 0x211d), regmap_reg_range(0x2122, 0x2127), regmap_reg_range(0x212a, 0x212b), - regmap_reg_range(0x2136, 0x2139), - regmap_reg_range(0x213e, 0x213f), + regmap_reg_range(0x2134, 0x213f), regmap_reg_range(0x2400, 0x2401), regmap_reg_range(0x2403, 0x2403), regmap_reg_range(0x2410, 0x2417), diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/ethernet/cadence/macb.h linux-microchip/drivers/net/ethernet/cadence/macb.h --- linux-6.6.51/drivers/net/ethernet/cadence/macb.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/net/ethernet/cadence/macb.h 2024-11-26 14:02:37.798499071 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1168 @ int (*get_ts_info)(struct net_device *dev, struct ethtool_ts_info *info); int (*get_hwtst)(struct net_device *netdev, - struct ifreq *ifr); + struct kernel_hwtstamp_config *tstamp_config); int (*set_hwtst)(struct net_device *netdev, - struct ifreq *ifr, int cmd); + struct kernel_hwtstamp_config *tstamp_config, + struct netlink_ext_ack *extack); }; struct macb_pm_data { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1318 @ struct ptp_clock *ptp_clock; struct ptp_clock_info ptp_clock_info; struct tsu_incr tsu_incr; - struct hwtstamp_config tstamp_config; + struct kernel_hwtstamp_config tstamp_config; /* RX queue filer rule set*/ struct ethtool_rx_fs_list rx_fs_list; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1367 @ gem_ptp_rxstamp(bp, skb, desc); } -int gem_get_hwtst(struct net_device *dev, struct ifreq *rq); -int gem_set_hwtst(struct net_device *dev, struct ifreq *ifr, int cmd); + +int gem_get_hwtst(struct net_device *dev, + struct kernel_hwtstamp_config *tstamp_config); +int gem_set_hwtst(struct net_device *dev, + struct kernel_hwtstamp_config *tstamp_config, + struct netlink_ext_ack *extack); #else static inline void gem_ptp_init(struct net_device *ndev) { } static inline void gem_ptp_remove(struct net_device *ndev) { } diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/ethernet/cadence/macb_main.c linux-microchip/drivers/net/ethernet/cadence/macb_main.c --- linux-6.6.51/drivers/net/ethernet/cadence/macb_main.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/net/ethernet/cadence/macb_main.c 2024-11-26 14:02:37.798499071 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:871 @ bp->phylink_config.dev = &dev->dev; bp->phylink_config.type = PHYLINK_NETDEV; - bp->phylink_config.mac_managed_pm = true; + if (!of_phy_is_fixed_link(bp->pdev->dev.of_node)) + bp->phylink_config.mac_managed_pm = true; if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) { bp->phylink_config.poll_fixed_state = true; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:3777 @ if (!netif_running(dev)) return -EINVAL; - if (bp->ptp_info) { - switch (cmd) { - case SIOCSHWTSTAMP: - return bp->ptp_info->set_hwtst(dev, rq, cmd); - case SIOCGHWTSTAMP: - return bp->ptp_info->get_hwtst(dev, rq); - } - } - return phylink_mii_ioctl(bp->phylink, rq, cmd); } +static int macb_hwtstamp_get(struct net_device *dev, + struct kernel_hwtstamp_config *cfg) +{ + struct macb *bp = netdev_priv(dev); + + if (!netif_running(dev)) + return -EINVAL; + + if (!bp->ptp_info) + return -EOPNOTSUPP; + + return bp->ptp_info->get_hwtst(dev, cfg); +} + +static int macb_hwtstamp_set(struct net_device *dev, + struct kernel_hwtstamp_config *cfg, + struct netlink_ext_ack *extack) +{ + struct macb *bp = netdev_priv(dev); + + if (!netif_running(dev)) + return -EINVAL; + + if (!bp->ptp_info) + return -EOPNOTSUPP; + + return bp->ptp_info->set_hwtst(dev, cfg, extack); +} + static inline void macb_set_txcsum_feature(struct macb *bp, netdev_features_t features) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:3908 @ #endif .ndo_set_features = macb_set_features, .ndo_features_check = macb_features_check, + .ndo_hwtstamp_set = macb_hwtstamp_set, + .ndo_hwtstamp_get = macb_hwtstamp_get, }; /* Configure peripheral capabilities according to device tree @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4565 @ #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = at91ether_poll_controller, #endif + .ndo_hwtstamp_set = macb_hwtstamp_set, + .ndo_hwtstamp_get = macb_hwtstamp_get, }; static int at91ether_clk_init(struct platform_device *pdev, struct clk **pclk, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4900 @ .jumbo_max_len = 4040, }; +static const struct macb_config sam9x7_gem_config = { + .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_CLK_HW_CHG | + MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII | MACB_CAPS_USRIO_HAS_CLKEN | + MACB_CAPS_MIIONRGMII | MACB_CAPS_GEM_HAS_PTP, + .dma_burst_length = 16, + .clk_init = macb_clk_init, + .init = macb_init, + .usrio = &sama7g5_usrio, +}; + static const struct macb_config sama7g5_gem_config = { .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_CLK_HW_CHG | MACB_CAPS_MIIONRGMII | MACB_CAPS_GEM_HAS_PTP, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4957 @ { .compatible = "cdns,zynq-gem", .data = &zynq_config }, /* deprecated */ { .compatible = "sifive,fu540-c000-gem", .data = &fu540_c000_config }, { .compatible = "microchip,mpfs-macb", .data = &mpfs_config }, + { .compatible = "microchip,sam9x7-gem", .data = &sam9x7_gem_config }, { .compatible = "microchip,sama7g5-gem", .data = &sama7g5_gem_config }, { .compatible = "microchip,sama7g5-emac", .data = &sama7g5_emac_config }, { .compatible = "xlnx,zynqmp-gem", .data = &zynqmp_config}, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:5195 @ return err; } -static int macb_remove(struct platform_device *pdev) +static void macb_remove(struct platform_device *pdev) { struct net_device *dev; struct macb *bp; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:5220 @ phylink_destroy(bp->phylink); free_netdev(dev); } - - return 0; } static int __maybe_unused macb_suspend(struct device *dev) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:5387 @ macb_restore_features(bp); rtnl_lock(); + if (!of_phy_is_fixed_link(bp->pdev->dev.of_node)) + phylink_init_phydev(bp->phylink); phylink_start(bp->phylink); rtnl_unlock(); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:5437 @ static struct platform_driver macb_driver = { .probe = macb_probe, - .remove = macb_remove, + .remove_new = macb_remove, .driver = { .name = "macb", .of_match_table = of_match_ptr(macb_dt_ids), diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/ethernet/cadence/macb_ptp.c linux-microchip/drivers/net/ethernet/cadence/macb_ptp.c --- linux-6.6.51/drivers/net/ethernet/cadence/macb_ptp.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/net/ethernet/cadence/macb_ptp.c 2024-11-26 14:02:37.798499071 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:377 @ return 0; } -int gem_get_hwtst(struct net_device *dev, struct ifreq *rq) +int gem_get_hwtst(struct net_device *dev, + struct kernel_hwtstamp_config *tstamp_config) { - struct hwtstamp_config *tstamp_config; struct macb *bp = netdev_priv(dev); - tstamp_config = &bp->tstamp_config; + *tstamp_config = bp->tstamp_config; if ((bp->hw_dma_cap & HW_DMA_CAP_PTP) == 0) return -EOPNOTSUPP; - if (copy_to_user(rq->ifr_data, tstamp_config, sizeof(*tstamp_config))) - return -EFAULT; - else - return 0; + return 0; } static void gem_ptp_set_one_step_sync(struct macb *bp, u8 enable) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:401 @ macb_writel(bp, NCR, reg_val & ~MACB_BIT(OSSMODE)); } -int gem_set_hwtst(struct net_device *dev, struct ifreq *ifr, int cmd) +int gem_set_hwtst(struct net_device *dev, + struct kernel_hwtstamp_config *tstamp_config, + struct netlink_ext_ack *extack) { enum macb_bd_control tx_bd_control = TSTAMP_DISABLED; enum macb_bd_control rx_bd_control = TSTAMP_DISABLED; - struct hwtstamp_config *tstamp_config; struct macb *bp = netdev_priv(dev); u32 regval; - tstamp_config = &bp->tstamp_config; if ((bp->hw_dma_cap & HW_DMA_CAP_PTP) == 0) return -EOPNOTSUPP; - if (copy_from_user(tstamp_config, ifr->ifr_data, - sizeof(*tstamp_config))) - return -EFAULT; - switch (tstamp_config->tx_type) { case HWTSTAMP_TX_OFF: break; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:459 @ return -ERANGE; } + bp->tstamp_config = *tstamp_config; + if (gem_ptp_set_ts_mode(bp, tx_bd_control, rx_bd_control) != 0) return -ERANGE; - if (copy_to_user(ifr->ifr_data, tstamp_config, sizeof(*tstamp_config))) - return -EFAULT; - else - return 0; + return 0; } diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/phy/phylink.c linux-microchip/drivers/net/phy/phylink.c --- linux-6.6.51/drivers/net/phy/phylink.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/net/phy/phylink.c 2024-11-26 14:02:38.010502868 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2729 @ } EXPORT_SYMBOL_GPL(phylink_ethtool_set_eee); +/** + * phylink_init_phydev() - initialize phydev associated to phylink + * @pl: a pointer to a &struct phylink returned from phylink_create() + */ +int phylink_init_phydev(struct phylink *pl) +{ + return phy_init_hw(pl->phydev); +} +EXPORT_SYMBOL_GPL(phylink_init_phydev); + /* This emulates MII registers for a fixed-mode phy operating as per the * passed in state. "aneg" defines if we report negotiation is possible. * diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/wireless/microchip/Makefile linux-microchip/drivers/net/wireless/microchip/Makefile --- linux-6.6.51/drivers/net/wireless/microchip/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/net/wireless/microchip/Makefile 2024-11-26 14:02:38.118504803 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1 @ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_WILC1000) += wilc1000/ +obj-$(CONFIG_WILC) += wilc1000/ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/wireless/microchip/wilc1000/bt.c linux-microchip/drivers/net/wireless/microchip/wilc1000/bt.c --- linux-6.6.51/drivers/net/wireless/microchip/wilc1000/bt.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/net/wireless/microchip/wilc1000/bt.c 2024-11-26 14:02:38.118504803 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries. + * All rights reserved. + */ + +#include <linux/fs.h> +#include <linux/device.h> +#include <linux/cdev.h> +#include <linux/types.h> +#include <linux/kdev_t.h> +#include <linux/firmware.h> + +#include <linux/mmc/sdio.h> +#include <linux/mmc/sdio_func.h> +#include <linux/mmc/card.h> +#include <linux/mmc/sdio_ids.h> +#include <linux/mmc/sdio.h> +#include <linux/mmc/host.h> + +#include "netdev.h" + +static struct wilc *wilc_bt; +static dev_t chc_dev_no; /* Global variable for the first device number */ +static struct cdev str_chc_dev; /* Global variable for the character */ +static struct device *dev; +static struct class *chc_dev_class; /* Global variable for the device class */ +static bool device_created; +static int bt_init_done; + +typedef void (wilc_cmd_handler)(char *); + +static void handle_cmd_bt_enable(char *param); +static void handle_cmd_pwr_up(char *param); +static void handle_cmd_pwr_down(char *param); +static void handle_cmd_chip_wake_up(char *param); +static void handle_cmd_chip_allow_sleep(char *param); +static void handle_cmd_download_fw(char *param); +static void handle_cmd_cca_thrshld(char *param); + +static void wilc_bt_firmware_download(struct wilc *); +static void wilc_bt_start(struct wilc *); +static int wilc_bt_dev_open(struct inode *i, struct file *f); +static int wilc_bt_dev_close(struct inode *i, struct file *f); +static ssize_t wilc_bt_dev_read(struct file *f, char __user *buf, size_t len, + loff_t *off); +static ssize_t wilc_bt_dev_write(struct file *f, const char __user *buff, + size_t len, loff_t *off); + +static const struct file_operations pugs_fops = { + .owner = THIS_MODULE, + .open = wilc_bt_dev_open, + .release = wilc_bt_dev_close, + .read = wilc_bt_dev_read, + .write = wilc_bt_dev_write +}; + +struct cmd_entry { + const char *str; + wilc_cmd_handler *wilc_handle_cmd; +}; + +static const struct cmd_entry cmd_table[] = { + {"BT_DOWNLOAD_FW", handle_cmd_download_fw}, + {"BT_POWER_UP", handle_cmd_pwr_up}, + {"BT_POWER_DOWN", handle_cmd_pwr_down}, + {"BT_FW_CHIP_WAKEUP", handle_cmd_chip_wake_up}, + {"BT_FW_CHIP_ALLOW_SLEEP", handle_cmd_chip_allow_sleep}, + {"BT_ENABLE", handle_cmd_bt_enable}, + {"CCA_THRESHOLD", handle_cmd_cca_thrshld}, + /* Keep the NULL handler at the end of the table */ + {(const char *)NULL, NULL}, +}; + +static int wilc_bt_dev_open(struct inode *i, struct file *f) +{ + pr_info("at_pwr_dev: open()\n"); + return 0; +} + +static int wilc_bt_dev_close(struct inode *i, struct file *f) +{ + pr_info("at_pwr_dev: close()\n"); + return 0; +} + +static ssize_t wilc_bt_dev_read(struct file *f, char __user *buf, size_t len, + loff_t *off) +{ + pr_debug("at_pwr_dev: read()\n"); + return 0; +} + +static ssize_t wilc_bt_dev_write(struct file *f, const char __user *buff, + size_t len, loff_t *off) +{ + struct cmd_entry *cmd; + char *usr_str; + + + if (len == 0) { + pr_debug("received invalid size <=0: %zu\n", len); + return len; + } + + usr_str = kmalloc(len, GFP_KERNEL); + + if (copy_from_user(usr_str, buff, len)) + return -EIO; + + pr_debug("received %s, len %zu\n", usr_str, len); + /* call the appropriate command handler */ + cmd = (struct cmd_entry *)cmd_table; + while (cmd->wilc_handle_cmd != NULL) { + if (strncmp(cmd->str, usr_str, strlen(cmd->str)) == 0) { + pr_debug("param len: %zu, string: %s\n", + len - strlen(cmd->str), usr_str); + cmd->wilc_handle_cmd(usr_str + strlen(cmd->str)); + break; + } + cmd++; + } + + kfree(usr_str); + return len; +} + +static void wilc_bt_create_device(void) +{ + int ret = 0; + + if (device_created) + return; + + ret = alloc_chrdev_region(&chc_dev_no, 0, 1, "atmel"); + if (ret < 0) + return; +#if KERNEL_VERSION(6, 4, 0) <= LINUX_VERSION_CODE + chc_dev_class = class_create("atmel"); +#else + chc_dev_class = class_create(THIS_MODULE, "atmel"); +#endif + if (IS_ERR(chc_dev_class)) { + unregister_chrdev_region(chc_dev_no, 1); + return; + } + dev = device_create(chc_dev_class, NULL, chc_dev_no, NULL, + "wilc_bt"); + if (IS_ERR(dev)) { + class_destroy(chc_dev_class); + unregister_chrdev_region(chc_dev_no, 1); + return; + } + + cdev_init(&str_chc_dev, &pugs_fops); + ret = cdev_add(&str_chc_dev, chc_dev_no, 1); + if (ret < 0) { + device_destroy(chc_dev_class, chc_dev_no); + class_destroy(chc_dev_class); + unregister_chrdev_region(chc_dev_no, 1); + return; + } + mutex_init(&wilc_bt->cs); + device_created = 1; +} + +static void handle_cmd_cca_thrshld(char *param) +{ + int carrier_thrshld, noise_thrshld; + unsigned int carr_thrshld_frac, noise_thrshld_frac, carr_thrshld_int, + noise_thrshld_int, reg; + + if (param == NULL) { + pr_err("Invalid parameter\n"); + return; + } + + if (sscanf(param, " %d %d", &noise_thrshld, &carrier_thrshld) != 2) { + pr_err("Failed to parse input parameters. Usage:\n"); + pr_err("echo CCA_THRESHOLD NOISE_THRESHOLD CARRIER_THRESHOLD > /dev/at_pwr_dev\n"); + pr_err("where threshold values are in dB * 10\n"); + pr_err("e.g. echo CCA_THRESHOLD -625 -826 > /dev/at_pwr_dev to set thresholds to -62.5 and -82.6\n\n"); + return; + } + + pr_info("Changing CCA noise threshold to %d and carrier thresholds to %d\n", + noise_thrshld, carrier_thrshld); + + carr_thrshld_int = carrier_thrshld/10; + if (carrier_thrshld < 0) + carr_thrshld_frac = (carr_thrshld_int * 10) - carrier_thrshld; + else + carr_thrshld_frac = carrier_thrshld - (carr_thrshld_int * 10); + + noise_thrshld_int = noise_thrshld/10; + if (noise_thrshld < 0) + noise_thrshld_frac = (noise_thrshld_int * 10) - noise_thrshld; + else + noise_thrshld_frac = noise_thrshld - (noise_thrshld_int * 10); + + wilc_bt->hif_func->hif_read_reg(wilc_bt, CCA_CTL_2, ®); + reg &= ~(0x7FF0000); + reg |= ((noise_thrshld_frac & 0x7) | ((noise_thrshld_int & 0x1FF) + << 3)) << 16; + wilc_bt->hif_func->hif_write_reg(wilc_bt, CCA_CTL_2, reg); + + wilc_bt->hif_func->hif_read_reg(wilc_bt, CCA_CTL_7, ®); + reg &= ~(0x7FF0000); + reg |= ((carr_thrshld_frac & 0x7) | ((carr_thrshld_int & 0x1FF) << 3)) + << 16; + wilc_bt->hif_func->hif_write_reg(wilc_bt, CCA_CTL_7, reg); +} + +int wilc_bt_power_down(struct wilc *wilc, int source) +{ + const struct wilc_hif_func *hif_func = wilc->hif_func; + int ret; + + if (source == DEV_BT) { + u32 reg; + + pr_info("AT PWR: bt_power_down\n"); + + /* Adjust coexistence module. This should be done from the FW + * in the future + */ + acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_BT); + + ret = hif_func->hif_read_reg(wilc, GLOBAL_MODE_CONTROL, + ®); + if (ret) { + pr_err("[wilc start]: fail read reg %x\n", + GLOBAL_MODE_CONTROL); + release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT); + return ret; + } + /* Clear BT mode*/ + reg &= ~BIT(1); + ret = hif_func->hif_write_reg(wilc, GLOBAL_MODE_CONTROL, + reg); + if (ret) { + pr_err("[wilc start]: fail write reg %x\n", + GLOBAL_MODE_CONTROL); + release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT); + return ret; + } + + + /*TicketId1115*/ + /*Disable awake coex null frames*/ + ret = hif_func->hif_read_reg(wilc, COE_AUTO_PS_ON_NULL_PKT, + ®); + if (ret) { + pr_err("[wilc start]: fail read reg %x\n", + COE_AUTO_PS_ON_NULL_PKT); + release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT); + return ret; + } + reg &= ~BIT(30); + ret = hif_func->hif_write_reg(wilc, COE_AUTO_PS_ON_NULL_PKT, + reg); + if (ret) { + pr_err("[wilc start]: fail write reg %x\n", + COE_AUTO_PS_ON_NULL_PKT); + release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT); + return ret; + } + + /*TicketId1115*/ + /*Disable doze coex null frames*/ + ret = hif_func->hif_read_reg(wilc, COE_AUTO_PS_OFF_NULL_PKT, + ®); + if (ret) { + pr_err("[wilc start]: fail read reg %x\n", + COE_AUTO_PS_OFF_NULL_PKT); + release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT); + return ret; + } + reg &= ~BIT(30); + ret = hif_func->hif_write_reg(wilc, COE_AUTO_PS_OFF_NULL_PKT, + reg); + if (ret) { + pr_err("[wilc start]: fail write reg %x\n", + COE_AUTO_PS_OFF_NULL_PKT); + release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT); + return ret; + } + /* Disable BT wakeup */ + ret = hif_func->hif_read_reg(wilc, PWR_SEQ_MISC_CTRL, + ®); + if (ret) { + pr_err("[wilc start]: fail read reg %x\n", + PWR_SEQ_MISC_CTRL); + release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT); + return ret; + } + reg &= ~BIT(29); + ret = hif_func->hif_write_reg(wilc, PWR_SEQ_MISC_CTRL, + reg); + if (ret) { + pr_err("[wilc start]: fail write reg %x\n", + PWR_SEQ_MISC_CTRL); + release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT); + return ret; + } + + release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT); + + bt_init_done = 0; + } + + mutex_lock(&wilc->cs); + + pr_info("source: %s, current bus status Wifi: %d, BT: %d\n", + (source == DEV_WIFI ? "Wifi" : "BT"), + wilc->power.status[DEV_WIFI], + wilc->power.status[DEV_BT]); + + if (wilc->power.status[source] == false) { + pr_err("power down request for already powered down source %s\n", + (source == DEV_WIFI ? "Wifi" : "BT")); + } else if (((source == DEV_WIFI) && + (wilc->power.status[DEV_BT] == true)) || + ((source == DEV_BT) && + (wilc->power.status[DEV_WIFI] == true))) { + pr_warn("Another device is preventing power down. request source is %s\n", + (source == DEV_WIFI ? "Wifi" : "BT")); + } else { + acquire_bus(wilc, WILC_BUS_ACQUIRE_ONLY, source); + ret = wilc->hif_func->hif_deinit(wilc); + release_bus(wilc, WILC_BUS_RELEASE_ONLY, source); + if (ret) { + mutex_unlock(&wilc->cs); + return ret; + } + } + wilc->power.status[source] = false; + + mutex_unlock(&wilc->cs); + + return 0; +} + +int wilc_bt_power_up(struct wilc *wilc, int source) +{ + int count = 0; + int ret; + int reg; + const struct wilc_hif_func *hif_func = wilc->hif_func; + + mutex_lock(&wilc->cs); + + pr_debug("source: %s, current bus status Wifi: %d, BT: %d\n", + (source == DEV_WIFI ? "Wifi" : "BT"), + wilc->power.status[DEV_WIFI], + wilc->power.status[DEV_BT]); + + if (wilc->power.status[source] == true) { + pr_err("power up request for already powered up source %s\n", + (source == DEV_WIFI ? "Wifi" : "BT")); + } else { + /*Bug 215*/ + /*Avoid overlapping between BT and Wifi intialization*/ + if (wilc->power.status[DEV_WIFI] == true) { + while (!wilc->initialized) { + msleep(100); + if (++count > 20) { + pr_warn("Wifi initialize timeout\n"); + break; + } + } + } else if (wilc->power.status[DEV_BT] == true) { + while (!bt_init_done) { + msleep(200); + if (++count > 30) { + pr_warn("BT initialize timeout\n"); + break; + } + } + /* An additional wait to give BT firmware time to do + * CPLL update as the time measured since the start of + * BT Fw till the end of function "rf_nmi_init_tuner" + * was 71.2 ms + */ + msleep(100); + } + } + + if ((wilc->power.status[DEV_WIFI] == true) || + (wilc->power.status[DEV_BT] == true)) { + pr_info("Device already up. request source is %s\n", + (source == DEV_WIFI ? "Wifi" : "BT")); + } else { + pr_info("WILC POWER UP\n"); + } + wilc->power.status[source] = true; + mutex_unlock(&wilc->cs); + + if (source == DEV_BT) { + /*TicketId1092*/ + /*If WiFi is off, force BT*/ + if (wilc->power.status[DEV_WIFI] == false) { + acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_BT); + + /*TicketId1115*/ + /*Disable awake coex null frames*/ + ret = hif_func->hif_read_reg(wilc, + COE_AUTO_PS_ON_NULL_PKT, + ®); + if (ret) { + pr_err("[wilc start]: fail read reg %x\n", + COE_AUTO_PS_ON_NULL_PKT); + goto fail; + } + reg &= ~BIT(30); + ret = hif_func->hif_write_reg(wilc, + COE_AUTO_PS_ON_NULL_PKT, + reg); + if (ret) { + pr_err("[wilc start]: fail write reg %x\n", + COE_AUTO_PS_ON_NULL_PKT); + goto fail; + } + + /*TicketId1115*/ + /*Disable doze coex null frames*/ + ret = hif_func->hif_read_reg(wilc, + COE_AUTO_PS_OFF_NULL_PKT, + ®); + if (ret) { + pr_err("[wilc start]: fail read reg %x\n", + COE_AUTO_PS_OFF_NULL_PKT); + goto fail; + } + reg &= ~BIT(30); + ret = hif_func->hif_write_reg(wilc, + COE_AUTO_PS_OFF_NULL_PKT, + reg); + if (ret) { + pr_err("[wilc start]: fail write reg %x\n", + COE_AUTO_PS_OFF_NULL_PKT); + goto fail; + } + + release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT); + } + + /* Enable BT wakeup */ + acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_BT); + + ret = hif_func->hif_read_reg(wilc, PWR_SEQ_MISC_CTRL, + ®); + if (ret) { + pr_err("[wilc start]: fail read reg %x ...\n", + PWR_SEQ_MISC_CTRL); + goto fail; + } + reg |= BIT(29); + ret = hif_func->hif_write_reg(wilc, PWR_SEQ_MISC_CTRL, + reg); + if (ret) { + pr_err("[wilc start]: fail write reg %x ...\n", + PWR_SEQ_MISC_CTRL); + goto fail; + } + + release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT); + } + + return 0; + +fail: + release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT); + wilc_bt_power_down(wilc, DEV_BT); + return ret; +} + +static void wilc_bt_firmware_download(struct wilc *wilc) +{ + u32 offset; + u32 addr, size, size2, blksz; + u8 *dma_buffer; + const struct firmware *wilc_bt_firmware; + const u8 *buffer; + size_t buffer_size; + int ret = 0; + u32 reg; + const struct wilc_hif_func *hif_func; + + hif_func = wilc->hif_func; + + pr_info("Bluetooth firmware: %s\n", FW_WILC3000_BLE); + if (request_firmware(&wilc_bt_firmware, FW_WILC3000_BLE, dev) != 0) { + pr_err("%s - firmare not available. Skip!\n", FW_WILC3000_BLE); + ret = -1; + goto fail_1; + } + + buffer = wilc_bt_firmware->data; + buffer_size = (size_t)wilc_bt_firmware->size; + if (buffer_size <= 0) { + pr_err("Firmware size = 0!\n"); + ret = -1; + goto fail_1; + } + + acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_BT); + + ret = hif_func->hif_write_reg(wilc, 0x4f0000, 0x71); + if (ret) { + pr_err("[wilc start]: fail write reg 0x4f0000 ...\n"); + release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT); + goto fail_1; + } + + /* + * Avoid booting from BT boot ROM. Make sure that Drive IRQN + * [SDIO platform] or SD_DAT3 [SPI platform] to ?1? + */ + /* Set cortus reset register to register control. */ + ret = hif_func->hif_read_reg(wilc, 0x3b0090, ®); + if (ret) { + pr_err("[wilc start]: fail read reg 0x3b0090 ...\n"); + release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT); + goto fail_1; + } + + reg |= (1 << 0); + ret = hif_func->hif_write_reg(wilc, 0x3b0090, reg); + if (ret) { + pr_err("[wilc start]: fail write reg 0x3b0090 ...\n"); + release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT); + goto fail_1; + } + + hif_func->hif_read_reg(wilc, 0x3B0400, ®); + + if (reg & (1ul << 2)) { + reg &= ~(1ul << 2); + } else { + reg |= (1ul << 2); + hif_func->hif_write_reg(wilc, 0x3B0400, reg); + reg &= ~(1ul << 2); + } + hif_func->hif_write_reg(wilc, 0x3B0400, reg); + + release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT); + + /* blocks of sizes > 512 causes the wifi to hang! */ + blksz = (1ul << 9); + /* Allocate a DMA coherent buffer. */ + dma_buffer = kmalloc(blksz, GFP_KERNEL); + if (dma_buffer == NULL) { + ret = -5; + pr_err("Can't allocate buffer for BT firmware download IO error\n"); + goto fail_1; + } + pr_info("Downloading BT firmware size = %zu ...\n", buffer_size); + + offset = 0; + addr = 0x400000; + size = buffer_size; + addr = cpu_to_le32(addr); + size = cpu_to_le32(size); + offset = 0; + + while (((int)size) && (offset < buffer_size)) { + if (size <= blksz) + size2 = size; + else + size2 = blksz; + + /* Copy firmware into a DMA coherent buffer */ + memcpy(dma_buffer, &buffer[offset], size2); + + acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_BT); + + ret = hif_func->hif_block_tx(wilc, addr, dma_buffer, size2); + + release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT); + + if (ret) + break; + + addr += size2; + offset += size2; + size -= size2; + } + + if (ret) { + ret = -5; + pr_err("Can't download BT firmware IO error\n"); + goto fail; + } + +fail: + kfree(dma_buffer); +fail_1: + pr_debug("Freeing BT FW buffer ...\n"); + pr_debug("Releasing BT firmware\n"); + release_firmware(wilc_bt_firmware); +} + +static void wilc_bt_start(struct wilc *wilc) +{ + u32 val32 = 0; + + acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_BT); + + pr_info("Starting BT firmware\n"); + /* + * Write the firmware download complete magic value 0x10ADD09E at + * location 0xFFFF000C (Cortus map) or C000C (AHB map). + * This will let the boot-rom code execute from RAM. + */ + wilc->hif_func->hif_write_reg(wilc, 0x4F000c, 0x10add09e); + + wilc->hif_func->hif_read_reg(wilc, 0x3B0400, &val32); + val32 &= ~((1ul << 2) | (1ul << 3)); + wilc->hif_func->hif_write_reg(wilc, 0x3B0400, val32); + + msleep(100); + + val32 |= ((1ul << 2) | (1ul << 3)); + + wilc->hif_func->hif_write_reg(wilc, 0x3B0400, val32); + + pr_info("BT Start Succeeded\n"); + + release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_BT); +} + +static void handle_cmd_pwr_up(char *param) +{ + pr_info("AT PWR: bt_power_up\n"); + bt_init_done = 0; + + if (!wilc_bt->initialized && !wilc_bt->hif_func->hif_is_init(wilc_bt)) { + acquire_bus(wilc_bt, WILC_BUS_ACQUIRE_ONLY, DEV_BT); + if (wilc_bt->hif_func->hif_init(wilc_bt, false)) { + release_bus(wilc_bt, WILC_BUS_RELEASE_ONLY, DEV_BT); + return; + } + release_bus(wilc_bt, WILC_BUS_RELEASE_ONLY, DEV_BT); + } + + wilc_bt_power_up(wilc_bt, DEV_BT); +} + +static void handle_cmd_pwr_down(char *param) +{ + wilc_bt_power_down(wilc_bt, DEV_BT); +} + +static void handle_cmd_chip_wake_up(char *param) +{ + chip_wakeup(wilc_bt, DEV_BT); +} + +static void handle_cmd_chip_allow_sleep(char *param) +{ + bt_init_done = 1; + chip_allow_sleep(wilc_bt, DEV_BT); +} + +static void handle_cmd_download_fw(char *param) +{ + pr_info("AT PWR: bt_download_fw\n"); + + wilc_bt_firmware_download(wilc_bt); + wilc_bt_start(wilc_bt); +} + +static void handle_cmd_bt_enable(char *param) +{ + wilc_bt_power_up(wilc_bt, DEV_BT); + wilc_bt_firmware_download(wilc_bt); + wilc_bt_start(wilc_bt); +} + +void wilc_bt_init(struct wilc *wilc) +{ + wilc_bt = wilc; + pr_debug("at_pwr_dev: init\n"); + wilc_bt_create_device(); +} + +void wilc_bt_deinit(void) +{ + pr_info("at_pwr_dev: deinit\n"); + + mutex_destroy(&wilc_bt->cs); + + cdev_del(&str_chc_dev); + device_created = 0; + device_destroy(chc_dev_class, chc_dev_no); + class_destroy(chc_dev_class); + unregister_chrdev_region(chc_dev_no, 1); + pr_info("at_pwr_dev: unregistered\n"); +} diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/wireless/microchip/wilc1000/cfg80211.c linux-microchip/drivers/net/wireless/microchip/wilc1000/cfg80211.c --- linux-6.6.51/drivers/net/wireless/microchip/wilc1000/cfg80211.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/net/wireless/microchip/wilc1000/cfg80211.c 2024-11-26 14:04:18.564303380 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:8 @ */ #include "cfg80211.h" +#include "netdev.h" #define GO_NEG_REQ 0x00 #define GO_NEG_RSP 0x01 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:49 @ BIT(IEEE80211_STYPE_DISASSOC >> 4) | BIT(IEEE80211_STYPE_AUTH >> 4) | BIT(IEEE80211_STYPE_DEAUTH >> 4) - } + }, }; #ifdef CONFIG_PM @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:109 @ } __packed; static void cfg_scan_result(enum scan_event scan_event, - struct wilc_rcvd_net_info *info, void *user_void) + struct wilc_rcvd_net_info *info, + struct wilc_priv *priv) { - struct wilc_priv *priv = user_void; - - if (!priv->cfg_scanning) + if (!priv || !priv->cfg_scanning) return; if (scan_event == SCAN_EVENT_NETWORK_FOUND) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:130 @ if (!channel) return; + PRINT_D(priv->dev, CFG80211_DBG, + "Network Info:: CHANNEL Frequency: %d, RSSI: %d,\n", + freq, ((s32)info->rssi * 100)); + bss = cfg80211_inform_bss_frame(wiphy, channel, info->mgmt, info->frame_len, (s32)info->rssi * 100, GFP_KERNEL); cfg80211_put_bss(wiphy, bss); } else if (scan_event == SCAN_EVENT_DONE) { + PRINT_INFO(priv->dev, CFG80211_DBG, "Scan Done[%p]\n", + priv->dev); mutex_lock(&priv->scan_req_lock); if (priv->scan_req) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:157 @ } else if (scan_event == SCAN_EVENT_ABORTED) { mutex_lock(&priv->scan_req_lock); + PRINT_INFO(priv->dev, CFG80211_DBG, "Scan Aborted\n"); if (priv->scan_req) { struct cfg80211_scan_info info = { .aborted = false, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:172 @ } static void cfg_connect_result(enum conn_event conn_disconn_evt, u8 mac_status, - void *priv_data) + struct wilc_priv *priv) { - struct wilc_priv *priv = priv_data; struct net_device *dev = priv->dev; struct wilc_vif *vif = netdev_priv(dev); struct wilc *wl = vif->wilc; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:186 @ if (conn_disconn_evt == CONN_DISCONN_EVENT_CONN_RESP) { u16 connect_status = conn_info->status; + PRINT_INFO(vif->ndev, CFG80211_DBG, + "Connection response received=%d connect_stat[%d]\n", + mac_status, connect_status); if (mac_status == WILC_MAC_STATUS_DISCONNECTED && connect_status == WLAN_STATUS_SUCCESS) { connect_status = WLAN_STATUS_UNSPECIFIED_FAILURE; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:200 @ netdev_err(dev, "Unspecified failure\n"); } - if (connect_status == WLAN_STATUS_SUCCESS) + if (connect_status == WLAN_STATUS_SUCCESS) { + PRINT_INFO(vif->ndev, CFG80211_DBG, + "Connection Successful: BSSID: %x%x%x%x%x%x\n", + conn_info->bssid[0], conn_info->bssid[1], + conn_info->bssid[2], conn_info->bssid[3], + conn_info->bssid[4], conn_info->bssid[5]); memcpy(priv->associated_bss, conn_info->bssid, ETH_ALEN); + } + PRINT_INFO(vif->ndev, CFG80211_DBG, + "Association request info elements length = %zu\n", + conn_info->req_ies_len); + PRINT_INFO(vif->ndev, CFG80211_DBG, + "Association response info elements length = %d\n", + conn_info->resp_ies_len); cfg80211_ref_bss(wiphy, vif->bss); cfg80211_connect_bss(dev, conn_info->bssid, vif->bss, conn_info->req_ies, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:229 @ } else if (conn_disconn_evt == CONN_DISCONN_EVENT_DISCONN_NOTIF) { u16 reason = 0; + PRINT_INFO(vif->ndev, CFG80211_DBG, + "Received WILC_MAC_STATUS_DISCONNECTED dev [%p]\n", + priv->dev); eth_zero_addr(priv->associated_bss); wilc_wlan_set_bssid(priv->dev, NULL, WILC_STATION_MODE); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:276 @ } channelnum = ieee80211_frequency_to_channel(chandef->chan->center_freq); + PRINT_INFO(vif->ndev, CFG80211_DBG, + "Setting channel %d with frequency %d\n", + channelnum, chandef->chan->center_freq); wl->op_ch = channelnum; result = wilc_set_mac_chnl_num(vif, channelnum); if (result) - netdev_err(vif->ndev, "Error in setting channel\n"); + netdev_err(vif->ndev, "Error in setting channel %d\n", + channelnum); srcu_read_unlock(&wl->srcu, srcu_idx); return result; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:310 @ u16 freq = request->channels[i]->center_freq; scan_ch_list[i] = ieee80211_frequency_to_channel(freq); + PRINT_D(vif->ndev, CFG80211_DBG, + "ScanChannel List[%d] = %d", + i, scan_ch_list[i]); } + PRINT_INFO(vif->ndev, CFG80211_DBG, "Requested num of channel %d\n", + request->n_channels); + PRINT_INFO(vif->ndev, CFG80211_DBG, "Scan Request IE len = %zu\n", + request->ie_len); + PRINT_INFO(vif->ndev, CFG80211_DBG, "Number of SSIDs %d\n", + request->n_ssids); + + PRINT_INFO(vif->ndev, CFG80211_DBG, + "Trigger Scan Request\n"); + if (request->n_ssids) scan_type = WILC_FW_ACTIVE_SCAN; else scan_type = WILC_FW_PASSIVE_SCAN; - ret = wilc_scan(vif, WILC_FW_USER_SCAN, scan_type, scan_ch_list, - request->n_channels, cfg_scan_result, (void *)priv, - request); + ret = wilc_scan(vif, WILC_FW_USER_SCAN, scan_type, + scan_ch_list, cfg_scan_result, request); if (ret) { priv->scan_req = NULL; priv->cfg_scanning = false; + PRINT_WRN(vif->ndev, CFG80211_DBG, + "Device is busy: Error(%d)\n", ret); } return ret; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:361 @ vif->connecting = true; + PRINT_INFO(vif->ndev, CFG80211_DBG, + "Connecting to SSID [%s] on netdev [%p] host if [%px]\n", + sme->ssid, dev, priv->hif_drv); + + if (sme->auth_type == NL80211_AUTHTYPE_SAE && + vif->wilc->chip == WILC_3000){ + pr_err("WILC3000: WPA3 not supported\n"); + ret = -ENOTSUPP; + goto out_error; + } + + if (vif->iftype == WILC_CLIENT_MODE) + PRINT_INFO(vif->ndev, CFG80211_DBG, + "Connected to Direct network,OBSS disabled\n"); + + PRINT_D(vif->ndev, CFG80211_DBG, "Required SSID= %s\n, AuthType= %d\n", + sme->ssid, sme->auth_type); + + PRINT_D(vif->ndev, CFG80211_DBG, "sme->crypto.wpa_versions=%x\n", + sme->crypto.wpa_versions); + PRINT_D(vif->ndev, CFG80211_DBG, "sme->crypto.cipher_group=%x\n", + sme->crypto.cipher_group); + PRINT_D(vif->ndev, CFG80211_DBG, "sme->crypto.n_ciphers_pairwise=%d\n", + sme->crypto.n_ciphers_pairwise); + for (i = 0; i < sme->crypto.n_ciphers_pairwise; i++) + PRINT_D(vif->ndev, CORECONFIG_DBG, + "sme->crypto.ciphers_pairwise[%d]=%x\n", i, + sme->crypto.ciphers_pairwise[i]); + cipher_group = sme->crypto.cipher_group; if (cipher_group != 0) { + PRINT_INFO(vif->ndev, CORECONFIG_DBG, + ">> sme->crypto.wpa_versions: %x\n", + sme->crypto.wpa_versions); if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) { if (cipher_group == WLAN_CIPHER_SUITE_TKIP) security = WILC_FW_SEC_WPA2_TKIP; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:425 @ } } + PRINT_INFO(vif->ndev, CFG80211_DBG, "Adding key with cipher group %x\n", + cipher_group); + + PRINT_INFO(vif->ndev, CFG80211_DBG, "Authentication Type = %d\n", + sme->auth_type); switch (sme->auth_type) { case NL80211_AUTHTYPE_OPEN_SYSTEM: + PRINT_INFO(vif->ndev, CFG80211_DBG, "In OPEN SYSTEM\n"); auth_type = WILC_FW_AUTH_OPEN_SYSTEM; break; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:447 @ break; default: + PRINT_INFO(vif->ndev, CFG80211_DBG, + "Automatic Authentication type= %d\n", + sme->auth_type); break; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:490 @ } ch = ieee80211_frequency_to_channel(bss->channel->center_freq); + PRINT_D(vif->ndev, CFG80211_DBG, "Required Channel = %d\n", ch); vif->wilc->op_ch = ch; if (vif->iftype != WILC_CLIENT_MODE) vif->wilc->sta_ch = ch; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:499 @ wfi_drv->conn_info.security = security; wfi_drv->conn_info.auth_type = auth_type; - wfi_drv->conn_info.ch = ch; wfi_drv->conn_info.conn_result = cfg_connect_result; - wfi_drv->conn_info.arg = priv; + wfi_drv->conn_info.priv = priv; wfi_drv->conn_info.param = join_params; if (sme->mfp == NL80211_MFP_OPTIONAL) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:540 @ struct wilc_vif *vif = netdev_priv(dev); struct wilc_priv *priv = &vif->priv; struct wilc *wilc = vif->wilc; + struct host_if_drv *wfi_drv; int ret; vif->connecting = false; if (!wilc) return -EIO; - - if (wilc->close) { - /* already disconnected done */ - cfg80211_disconnected(dev, 0, NULL, 0, true, GFP_KERNEL); - return 0; - } - + wfi_drv = (struct host_if_drv *)priv->hif_drv; if (vif->iftype != WILC_CLIENT_MODE) wilc->sta_ch = WILC_INVALID_CHANNEL; wilc_wlan_set_bssid(priv->dev, NULL, WILC_STATION_MODE); - priv->hif_drv->p2p_timeout = 0; + PRINT_INFO(vif->ndev, CFG80211_DBG, + "Disconnecting with reason code(%d)\n", reason_code); + wfi_drv->p2p_timeout = 0; ret = wilc_disconnect(vif); if (ret != 0) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:637 @ struct wilc_priv *priv = &vif->priv; struct wilc_wfi_key *key; + PRINT_INFO(vif->ndev, CFG80211_DBG, + "Adding key with cipher suite = %x\n", params->cipher); + PRINT_INFO(vif->ndev, CFG80211_DBG, "%px %px %d\n", wiphy, + netdev, key_index); + PRINT_INFO(vif->ndev, CFG80211_DBG, "key %x %x %x\n", params->key[0], + params->key[1], + params->key[2]); switch (params->cipher) { case WLAN_CIPHER_SUITE_TKIP: case WLAN_CIPHER_SUITE_CCMP: @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:672 @ key = priv->wilc_gtk[key_index]; } else { + PRINT_D(vif->ndev, CFG80211_DBG, + "STA Address: %x%x%x%x%x\n", + mac_addr[0], mac_addr[1], mac_addr[2], + mac_addr[3], mac_addr[4]); if (params->cipher == WLAN_CIPHER_SUITE_TKIP) mode = WILC_FW_SEC_WPA_TKIP; else @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:805 @ key_params.seq_len = priv->wilc_gtk[key_index]->seq_len; } } else { + PRINT_INFO(vif->ndev, CFG80211_DBG, "Getting pairwise key\n"); key_params.key = priv->wilc_ptk[key_index]->key; key_params.cipher = priv->wilc_ptk[key_index]->cipher; key_params.key_len = priv->wilc_ptk[key_index]->key_len; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:845 @ u32 inactive_time = 0; if (vif->iftype == WILC_AP_MODE || vif->iftype == WILC_GO_MODE) { + PRINT_INFO(vif->ndev, HOSTAPD_DBG, + "Getting station parameters\n"); for (i = 0; i < NUM_STA_ASSOCIATED; i++) { if (!(memcmp(mac, priv->assoc_stainfo.sta_associated_bss[i], @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:865 @ wilc_get_inactive_time(vif, mac, &inactive_time); sinfo->inactive_time = 1000 * inactive_time; + PRINT_INFO(vif->ndev, CFG80211_DBG, "Inactive time %d\n", + sinfo->inactive_time); } else if (vif->iftype == WILC_STATION_MODE) { struct rf_info stats; - if (!wilc->initialized) + if (!wilc->initialized) { + PRINT_INFO(vif->ndev, CFG80211_DBG, + "driver not initialized\n"); return -EBUSY; + } wilc_get_statistics(vif, &stats); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:895 @ wilc_enable_tcp_ack_filter(vif, true); else if (stats.link_speed != DEFAULT_LINK_SPEED) wilc_enable_tcp_ack_filter(vif, false); + + PRINT_INFO(vif->ndev, CORECONFIG_DBG, + "*** stats[%d][%d][%d][%d][%d]\n", sinfo->signal, + sinfo->rx_packets, sinfo->tx_packets, + sinfo->tx_failed, sinfo->txrate.legacy); } return 0; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:907 @ static int change_bss(struct wiphy *wiphy, struct net_device *dev, struct bss_parameters *params) { + PRINT_INFO(dev, CFG80211_DBG, "Changing Bss parametrs\n"); return 0; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:926 @ goto out; priv = &vif->priv; + cfg_param_val.flag = 0; + PRINT_INFO(vif->ndev, CFG80211_DBG, "Setting Wiphy params\n"); if (changed & WIPHY_PARAM_RETRY_SHORT) { netdev_dbg(vif->ndev, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:972 @ } } + PRINT_INFO(vif->ndev, CFG80211_DBG, + "Setting CFG params in the host interface\n"); ret = wilc_hif_set_cfg(vif, &cfg_param_val); if (ret) netdev_err(priv->dev, "Error in setting WIPHY PARAMS\n"); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:992 @ int ret = 0; u8 flag = 0; + PRINT_INFO(vif->ndev, CFG80211_DBG, "Setting PMKSA\n"); + for (i = 0; i < priv->pmkid_list.numpmkid; i++) { if (!memcmp(pmksa->bssid, priv->pmkid_list.pmkidlist[i].bssid, ETH_ALEN)) { flag = PMKID_FOUND; + PRINT_INFO(vif->ndev, CFG80211_DBG, + "PMKID already exists\n"); break; } } if (i < WILC_MAX_NUM_PMKIDS) { + PRINT_INFO(vif->ndev, CFG80211_DBG, + "Setting PMKID in private structure\n"); memcpy(priv->pmkid_list.pmkidlist[i].bssid, pmksa->bssid, ETH_ALEN); memcpy(priv->pmkid_list.pmkidlist[i].pmkid, pmksa->pmkid, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1017 @ ret = -EINVAL; } - if (!ret) + if (!ret) { + PRINT_INFO(vif->ndev, CFG80211_DBG, + "Setting pmkid in the host interface\n"); ret = wilc_set_pmkid_info(vif, &priv->pmkid_list); - + } return ret; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1032 @ struct wilc_vif *vif = netdev_priv(netdev); struct wilc_priv *priv = &vif->priv; + PRINT_INFO(netdev, CFG80211_DBG, "Deleting PMKSA keys\n"); + for (i = 0; i < priv->pmkid_list.numpmkid; i++) { if (!memcmp(pmksa->bssid, priv->pmkid_list.pmkidlist[i].bssid, ETH_ALEN)) { + PRINT_INFO(netdev, CFG80211_DBG, + "Resetting PMKID values\n"); memset(&priv->pmkid_list.pmkidlist[i], 0, sizeof(struct wilc_pmkid)); break; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1065 @ { struct wilc_vif *vif = netdev_priv(netdev); + PRINT_INFO(netdev, CFG80211_DBG, "Flushing PMKID key values\n"); memset(&vif->priv.pmkid_list, 0, sizeof(struct wilc_pmkid_attr)); return 0; } +static inline void wilc_wfi_cfg_parse_p2p_intent_attr(u8 *buf, u32 len, + bool p2p_mode) +{ + struct wilc_attr_entry *e; + u32 index = 0; + + while (index + sizeof(*e) <= len) { + e = (struct wilc_attr_entry *)&buf[index]; + if (e->attr_type == IEEE80211_P2P_ATTR_GO_INTENT) { + if (p2p_mode == WILC_P2P_ROLE_GO) + e->val[0] = (e->val[0] & 0x01) | (0x0f << 1); + else + e->val[0] = (e->val[0] & 0x01) | (0x00 << 1); + return; + } + index += le16_to_cpu(e->attr_len) + sizeof(*e); + } +} + static inline void wilc_wfi_cfg_parse_ch_attr(u8 *buf, u32 len, u8 sta_ch) { struct wilc_attr_entry *e; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1169 @ return cfg80211_rx_mgmt(&priv->wdev, freq, 0, buff, size, 0); } -void wilc_wfi_p2p_rx(struct wilc_vif *vif, u8 *buff, u32 size) +bool wilc_wfi_p2p_rx(struct wilc_vif *vif, u8 *buff, u32 size) { struct wilc *wl = vif->wilc; struct wilc_priv *priv = &vif->priv; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1181 @ const u8 *vendor_ie; u32 header, pkt_offset; s32 freq; + int ret; header = get_unaligned_le32(buff - HOST_HDR_OFFSET); pkt_offset = FIELD_GET(WILC_PKT_HDR_OFFSET_FIELD, header); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1196 @ cfg80211_mgmt_tx_status(&priv->wdev, priv->tx_cookie, buff, size, ack, GFP_KERNEL); - return; + return true; } freq = ieee80211_channel_to_frequency(wl->op_ch, NL80211_BAND_2GHZ); mgmt = (struct ieee80211_mgmt *)buff; + PRINT_D(vif->ndev, GENERIC_DBG, "Rx Frame Type:%x\n", + mgmt->frame_control); if (!ieee80211_is_action(mgmt->frame_control)) goto out_rx_mgmt; if (priv->cfg_scanning && time_after_eq(jiffies, (unsigned long)wfi_drv->p2p_timeout)) { netdev_dbg(vif->ndev, "Receiving action wrong ch\n"); - return; + return false; } if (!ieee80211_is_public_action((struct ieee80211_hdr *)buff, size)) goto out_rx_mgmt; d = (struct wilc_p2p_pub_act_frame *)(&mgmt->u.action); + PRINT_D(vif->ndev, GENERIC_DBG, + "Rx Action action: %x category %x oui type %x sub_type[%d]\n", + d->action, d->category, d->oui_type, d->oui_subtype); + if (d->oui_subtype != GO_NEG_REQ && d->oui_subtype != GO_NEG_RSP && d->oui_subtype != P2P_INV_REQ && d->oui_subtype != P2P_INV_RSP) goto out_rx_mgmt; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1231 @ goto out_rx_mgmt; p = (struct wilc_vendor_specific_ie *)vendor_ie; + /* use p2p mode invert value to treat other p2p device + * opposite of mode set on this device. + */ + wilc_wfi_cfg_parse_p2p_intent_attr(p->attr, p->tag_len - 4, + !vif->wilc->attr_sysfs.p2p_mode); wilc_wfi_cfg_parse_ch_attr(p->attr, p->tag_len - 4, vif->wilc->sta_ch); out_rx_mgmt: - cfg80211_rx_mgmt(&priv->wdev, freq, 0, buff, size, 0); + ret = cfg80211_rx_mgmt(&priv->wdev, freq, 0, buff, size, 0); + return ret; } static void wilc_wfi_mgmt_tx_complete(void *priv, int status) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1251 @ kfree(pv_data); } -static void wilc_wfi_remain_on_channel_expired(void *data, u64 cookie) +static void wilc_wfi_remain_on_channel_expired(struct wilc_vif *vif, u64 cookie) { - struct wilc_vif *vif = data; struct wilc_priv *priv = &vif->priv; struct wilc_wfi_p2p_listen_params *params = &priv->remain_on_ch_params; - if (cookie != params->listen_cookie) + if (cookie != priv->remain_on_ch_params.listen_cookie) { + PRINT_INFO(priv->dev, GENERIC_DBG, + "Received cookies didn't match received[%llu] Expected[%llu]\n", + cookie, priv->remain_on_ch_params.listen_cookie); return; + } - priv->p2p_listen_state = false; + vif->p2p_listen_state = false; - cfg80211_remain_on_channel_expired(&priv->wdev, params->listen_cookie, + cfg80211_remain_on_channel_expired(&vif->priv.wdev, cookie, params->listen_ch, GFP_KERNEL); } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1288 @ if (id == 0) id = ++priv->inc_roc_cookie; - ret = wilc_remain_on_channel(vif, id, duration, chan->hw_value, - wilc_wfi_remain_on_channel_expired, - (void *)vif); + ret = wilc_remain_on_channel(vif, id, chan->hw_value, + wilc_wfi_remain_on_channel_expired); if (ret) return ret; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1298 @ priv->remain_on_ch_params.listen_ch = chan; priv->remain_on_ch_params.listen_cookie = id; *cookie = id; - priv->p2p_listen_state = true; + vif->p2p_listen_state = true; priv->remain_on_ch_params.listen_duration = duration; cfg80211_ready_on_channel(wdev, *cookie, chan, duration, GFP_KERNEL); mod_timer(&vif->hif_drv->remain_on_ch_timer, jiffies + msecs_to_jiffies(duration + 1000)); + PRINT_INFO(vif->ndev, GENERIC_DBG, + "Remaining on duration [%d] [%llu] op_ch[%d]\n", + duration, priv->remain_on_ch_params.listen_cookie, + vif->wilc->op_ch); return ret; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1319 @ struct wilc_vif *vif = netdev_priv(wdev->netdev); struct wilc_priv *priv = &vif->priv; + PRINT_INFO(vif->ndev, CFG80211_DBG, + "cookie received[%llu] expected[%llu]\n", + cookie, priv->remain_on_ch_params.listen_cookie); if (cookie != priv->remain_on_ch_params.listen_cookie) return -ENOENT; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1352 @ priv->tx_cookie = *cookie; mgmt = (const struct ieee80211_mgmt *)buf; - if (!ieee80211_is_mgmt(mgmt->frame_control)) + if (!ieee80211_is_mgmt(mgmt->frame_control)) { + PRINT_INFO(vif->ndev, GENERIC_DBG, + "This function transmits only management frames\n"); goto out; + } mgmt_tx = kmalloc(sizeof(*mgmt_tx), GFP_KERNEL); if (!mgmt_tx) { - ret = -ENOMEM; - goto out; + PRINT_ER(vif->ndev, + "%s failed to allocate memory for structure\n", + __func__); + return -ENOMEM; } mgmt_tx->buff = kmemdup(buf, len, GFP_KERNEL); if (!mgmt_tx->buff) { ret = -ENOMEM; + PRINT_ER(vif->ndev, + "%s Failed to allocate memory buff\n", + __func__); kfree(mgmt_tx); goto out; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1379 @ mgmt_tx->size = len; if (ieee80211_is_probe_resp(mgmt->frame_control)) { + PRINT_INFO(vif->ndev, GENERIC_DBG, "TX: Probe Response\n"); + PRINT_INFO(vif->ndev, GENERIC_DBG, "Setting channel: %d\n", + chan->hw_value); wilc_set_mac_chnl_num(vif, chan->hw_value); vif->wilc->op_ch = chan->hw_value; goto out_txq_add_pkt; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1396 @ goto out_set_timeout; } + PRINT_INFO(vif->ndev, GENERIC_DBG, "ACTION FRAME:%x\n", + (u16)mgmt->frame_control); + d = (struct wilc_p2p_pub_act_frame *)(&mgmt->u.action); if (d->oui_type != WLAN_OUI_TYPE_WFA_P2P || d->oui_subtype != GO_NEG_CONF) { + PRINT_INFO(vif->ndev, GENERIC_DBG, "Setting channel: %d\n", + chan->hw_value); wilc_set_mac_chnl_num(vif, chan->hw_value); vif->wilc->op_ch = chan->hw_value; } - if (d->oui_subtype != P2P_INV_REQ && d->oui_subtype != P2P_INV_RSP) + if (d->oui_subtype != GO_NEG_REQ && d->oui_subtype != GO_NEG_RSP && + d->oui_subtype != P2P_INV_REQ && d->oui_subtype != P2P_INV_RSP) goto out_set_timeout; vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1419 @ goto out_set_timeout; p = (struct wilc_vendor_specific_ie *)vendor_ie; - wilc_wfi_cfg_parse_ch_attr(p->attr, p->tag_len - 4, vif->wilc->sta_ch); - + wilc_wfi_cfg_parse_p2p_intent_attr(p->attr, p->tag_len - 4, + vif->wilc->attr_sysfs.p2p_mode); + /* + * Update only the go_intent value and don't modify the channel list + * attributes values for GO_REQ and GO_Response to retain + * previous logic. For mgmt_tx only INVITATION_REQ and INVITATION_RES + * frame update the channel list attribute. + */ + + if (d->oui_subtype == P2P_INV_REQ && d->oui_subtype == P2P_INV_RSP) + wilc_wfi_cfg_parse_ch_attr(p->attr, p->tag_len - 4, + vif->wilc->sta_ch); + + PRINT_INFO(vif->ndev, GENERIC_DBG, + "TX: ACTION FRAME Type:%x : Chan:%d\n", d->action, + chan->hw_value); out_set_timeout: wfi_drv->p2p_timeout = (jiffies + msecs_to_jiffies(wait)); out_txq_add_pkt: - - wilc_wlan_txq_add_mgmt_pkt(wdev->netdev, mgmt_tx, + wilc_wlan_txq_add_mgmt_pkt(priv->wdev.netdev, mgmt_tx, mgmt_tx->buff, mgmt_tx->size, wilc_wfi_mgmt_tx_complete); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1458 @ wfi_drv->p2p_timeout = jiffies; - if (!priv->p2p_listen_state) { + if (!vif->p2p_listen_state) { struct wilc_wfi_p2p_listen_params *params; params = &priv->remain_on_ch_params; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1519 @ static int set_cqm_rssi_config(struct wiphy *wiphy, struct net_device *dev, s32 rssi_thold, u32 rssi_hyst) { + PRINT_INFO(dev, CFG80211_DBG, "Setting CQM RSSi Function\n"); return 0; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1532 @ if (idx != 0) return -ENOENT; + PRINT_INFO(vif->ndev, CFG80211_DBG, "Dumping station information\n"); + ret = wilc_get_rssi(vif, &sinfo->signal); if (ret) return ret; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1549 @ struct wilc_vif *vif = netdev_priv(dev); struct wilc_priv *priv = &vif->priv; - if (!priv->hif_drv) + PRINT_INFO(vif->ndev, GENERIC_DBG, "dev [%s]\n", dev->name); + if (!priv->hif_drv) { + PRINT_ER(dev, "hif driver is NULL\n"); return -EIO; + } + + PRINT_INFO(vif->ndev, CFG80211_DBG, + " Power save Enabled= %d , TimeOut = %d\n", enabled, + timeout); wilc_set_power_mgmt(vif, enabled, timeout); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1572 @ struct wilc_vif *vif = netdev_priv(dev); struct wilc_priv *priv = &vif->priv; + PRINT_INFO(vif->ndev, HOSTAPD_DBG, + "In Change virtual interface function\n"); + PRINT_INFO(vif->ndev, HOSTAPD_DBG, + "Wireless interface name =%s\n", dev->name); + switch (type) { case NL80211_IFTYPE_STATION: vif->connecting = false; + PRINT_INFO(vif->ndev, HOSTAPD_DBG, + "Interface type = NL80211_IFTYPE_STATION\n"); dev->ieee80211_ptr->iftype = type; priv->wdev.iftype = type; vif->monitor_flag = 0; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1599 @ case NL80211_IFTYPE_P2P_CLIENT: vif->connecting = false; + PRINT_INFO(vif->ndev, HOSTAPD_DBG, + "Interface type = NL80211_IFTYPE_P2P_CLIENT\n"); dev->ieee80211_ptr->iftype = type; priv->wdev.iftype = type; vif->monitor_flag = 0; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1612 @ break; case NL80211_IFTYPE_AP: + PRINT_INFO(vif->ndev, HOSTAPD_DBG, + "Interface type = NL80211_IFTYPE_AP\n"); dev->ieee80211_ptr->iftype = type; priv->wdev.iftype = type; vif->iftype = WILC_AP_MODE; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1624 @ break; case NL80211_IFTYPE_P2P_GO: + PRINT_INFO(vif->ndev, HOSTAPD_DBG, + "Interface type = NL80211_IFTYPE_GO\n"); + PRINT_INFO(vif->ndev, GENERIC_DBG, "start duringIP timer\n"); + dev->ieee80211_ptr->iftype = type; priv->wdev.iftype = type; vif->iftype = WILC_GO_MODE; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1636 @ wilc_set_operation_mode(vif, wilc_get_vif_idx(vif), WILC_AP_MODE, vif->idx); break; + case NL80211_IFTYPE_MONITOR: + PRINT_INFO(vif->ndev, HOSTAPD_DBG, + "Interface type = NL80211_IFTYPE_MONITOR\n"); + dev->ieee80211_ptr->iftype = type; + dev->type = ARPHRD_IEEE80211_RADIOTAP; + priv->wdev.iftype = type; + vif->iftype = WILC_MONITOR_MODE; + + if (wl->initialized) + wilc_set_operation_mode(vif, wilc_get_vif_idx(vif), + WILC_MONITOR_MODE, vif->idx); + break; default: netdev_err(dev, "Unknown interface type= %d\n", type); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1663 @ struct wilc_vif *vif = netdev_priv(dev); int ret; + PRINT_INFO(vif->ndev, HOSTAPD_DBG, "Starting ap\n"); ret = set_channel(wiphy, &settings->chandef); if (ret != 0) netdev_err(dev, "Error in setting channel\n"); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1679 @ { struct wilc_vif *vif = netdev_priv(dev); + PRINT_INFO(vif->ndev, HOSTAPD_DBG, "Setting beacon\n"); + return wilc_add_beacon(vif, 0, 0, beacon); } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1690 @ int ret; struct wilc_vif *vif = netdev_priv(dev); + PRINT_INFO(vif->ndev, CFG80211_DBG, "Deleting beacon\n"); + wilc_wlan_set_bssid(dev, NULL, WILC_AP_MODE); ret = wilc_del_beacon(vif); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1710 @ struct wilc_priv *priv = &vif->priv; if (vif->iftype == WILC_AP_MODE || vif->iftype == WILC_GO_MODE) { + + PRINT_INFO(vif->ndev, CFG80211_DBG, + "Adding station parameters %d\n", params->aid); + PRINT_INFO(vif->ndev, HOSTAPD_DBG, "ASSOC ID = %d\n", + params->aid); + PRINT_INFO(vif->ndev, HOSTAPD_DBG, + "Number of supported rates = %d\n", + params->link_sta_params.supported_rates_len); + + PRINT_INFO(vif->ndev, CFG80211_DBG, "IS HT supported = %d\n", + (!params->link_sta_params.ht_capa) ? false : true); + + if (params->link_sta_params.ht_capa) { + PRINT_INFO(vif->ndev, CFG80211_DBG, + "Capability Info = %d\n", + params->link_sta_params.ht_capa->cap_info); + PRINT_INFO(vif->ndev, CFG80211_DBG, + "AMPDU Params = %d\n", + params->link_sta_params.ht_capa->ampdu_params_info); + PRINT_INFO(vif->ndev, CFG80211_DBG, + "HT Extended params= %d\n", + params->link_sta_params.ht_capa->extended_ht_cap_info); + PRINT_INFO(vif->ndev, CFG80211_DBG, + "Tx Beamforming Cap= %d\n", + params->link_sta_params.ht_capa->tx_BF_cap_info); + PRINT_INFO(vif->ndev, CFG80211_DBG, + "Antenna selection info = %d\n", + params->link_sta_params.ht_capa->antenna_selection_info); + } + + PRINT_INFO(vif->ndev, CFG80211_DBG, "Flag Mask = %d\n", + params->sta_flags_mask); + PRINT_INFO(vif->ndev, CFG80211_DBG, "Flag Set = %d\n", + params->sta_flags_set); + memcpy(priv->assoc_stainfo.sta_associated_bss[params->aid], mac, ETH_ALEN); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1768 @ if (!(vif->iftype == WILC_AP_MODE || vif->iftype == WILC_GO_MODE)) return ret; + PRINT_INFO(vif->ndev, CFG80211_DBG, "Deleting station\n"); + info = &priv->assoc_stainfo; - if (!mac) + if (!mac) { + PRINT_INFO(vif->ndev, CFG80211_DBG, + "All associated stations\n"); ret = wilc_del_allstation(vif, info->sta_associated_bss); + } else { + PRINT_INFO(vif->ndev, CFG80211_DBG, + "With mac address: %x%x%x%x%x%x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + } ret = wilc_del_station(vif, mac); if (ret) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1794 @ int ret = 0; struct wilc_vif *vif = netdev_priv(dev); + PRINT_D(vif->ndev, CFG80211_DBG, "Change station parameters\n"); + if (vif->iftype == WILC_AP_MODE || vif->iftype == WILC_GO_MODE) { + PRINT_INFO(vif->ndev, CFG80211_DBG, "BSSID = %x%x%x%x%x%x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + PRINT_INFO(vif->ndev, CFG80211_DBG, "ASSOC ID = %d\n", + params->aid); + PRINT_INFO(vif->ndev, CFG80211_DBG, + "Number of supported rates = %d\n", + params->link_sta_params.supported_rates_len); + PRINT_INFO(vif->ndev, CFG80211_DBG, "IS HT supported = %d\n", + (!params->link_sta_params.ht_capa) ? false : true); + if (params->link_sta_params.ht_capa) { + PRINT_INFO(vif->ndev, CFG80211_DBG, + "Capability Info = %d\n", + params->link_sta_params.ht_capa->cap_info); + PRINT_INFO(vif->ndev, CFG80211_DBG, + "AMPDU Params = %d\n", + params->link_sta_params.ht_capa->ampdu_params_info); + PRINT_INFO(vif->ndev, CFG80211_DBG, + "HT Extended params= %d\n", + params->link_sta_params.ht_capa->extended_ht_cap_info); + PRINT_INFO(vif->ndev, CFG80211_DBG, + "Tx Beamforming Cap= %d\n", + params->link_sta_params.ht_capa->tx_BF_cap_info); + PRINT_INFO(vif->ndev, CFG80211_DBG, + "Antenna selection info = %d\n", + params->link_sta_params.ht_capa->antenna_selection_info); + } + PRINT_INFO(vif->ndev, CFG80211_DBG, "Flag Mask = %d\n", + params->sta_flags_mask); + PRINT_INFO(vif->ndev, CFG80211_DBG, "Flag Set = %d\n", + params->sta_flags_set); ret = wilc_edit_station(vif, mac, params); if (ret) netdev_err(dev, "Host edit station fail\n"); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1834 @ return ret; } -static struct wilc_vif *wilc_get_vif_from_type(struct wilc *wl, int type) +struct wilc_vif *wilc_get_vif_from_type(struct wilc *wl, int type) { struct wilc_vif *vif; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1875 @ srcu_read_unlock(&wl->srcu, srcu_idx); goto validate_interface; } - - ndev = wilc_wfi_init_mon_interface(wl, name, vif->ndev); + PRINT_INFO(vif->ndev, CFG80211_DBG, + "Initializing mon ifc virtual device driver\n"); + PRINT_INFO(vif->ndev, CFG80211_DBG, + "Adding monitor interface[%p]\n", vif->ndev); + ndev = wilc_wfi_init_mon_interface(vif->wilc, name, vif->ndev); if (ndev) { + PRINT_INFO(vif->ndev, CFG80211_DBG, + "Setting monitor flag in private structure\n"); vif->monitor_flag = 1; } else { srcu_read_unlock(&wl->srcu, srcu_idx); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1910 @ case NL80211_IFTYPE_AP: iftype = WILC_AP_MODE; break; + case NL80211_IFTYPE_MONITOR: + iftype = WILC_MONITOR_MODE; + break; default: return ERR_PTR(-EOPNOTSUPP); } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1929 @ struct wilc *wl = wiphy_priv(wiphy); struct wilc_vif *vif; + if (wdev->iftype == NL80211_IFTYPE_MONITOR) { + wilc_wfi_deinit_mon_interface(wl, true); + return 0; + } + if (wdev->iftype == NL80211_IFTYPE_AP || wdev->iftype == NL80211_IFTYPE_P2P_GO) wilc_wfi_deinit_mon_interface(wl, true); vif = netdev_priv(wdev->netdev); - cfg80211_stop_iface(wiphy, wdev, GFP_KERNEL); cfg80211_unregister_netdevice(vif->ndev); vif->monitor_flag = 0; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1951 @ static int wilc_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow) { - struct wilc *wl = wiphy_priv(wiphy); - - if (!wow && wilc_wlan_get_num_conn_ifcs(wl)) - wl->suspend_event = true; - else - wl->suspend_event = false; - return 0; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1996 @ return -EINVAL; } - netdev_info(vif->ndev, "Setting tx power %d\n", tx_power); + PRINT_INFO(vif->ndev, CFG80211_DBG, "Setting tx power %d\n", tx_power); if (tx_power < 0) tx_power = 0; else if (tx_power > 18) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2024 @ if (ret) netdev_err(vif->ndev, "Failed to get tx power\n"); + PRINT_INFO(vif->ndev, CFG80211_DBG, "Got tx power %d\n", *dbm); + + return ret; +} + +static int set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant) +{ + int ret; + struct wilc *wl = wiphy_priv(wiphy); + struct wilc_vif *vif; + int srcu_idx; + + srcu_idx = srcu_read_lock(&wl->srcu); + vif = wilc_get_wl_to_vif(wl); + if (IS_ERR(vif)) { + srcu_read_unlock(&wl->srcu, srcu_idx); + return -EINVAL; + } + + PRINT_INFO(vif->ndev, CFG80211_DBG, "Select antenna mode %d\n", tx_ant); + if (!tx_ant || !rx_ant) { + srcu_read_unlock(&wl->srcu, srcu_idx); + return -EINVAL; + } + + ret = wilc_set_antenna(vif, (u8)(tx_ant-1)); + if (ret) + PRINT_ER(vif->ndev, "Failed to set tx antenna\n"); + srcu_read_unlock(&wl->srcu, srcu_idx); + return ret; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2099 @ .set_wakeup = wilc_set_wakeup, .set_tx_power = set_tx_power, .get_tx_power = get_tx_power, - + .set_antenna = set_antenna, }; static void wlan_init_locks(struct wilc *wl) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2109 @ mutex_init(&wl->cfg_cmd_lock); mutex_init(&wl->vif_mutex); mutex_init(&wl->deinit_lock); + mutex_init(&wl->cs); spin_lock_init(&wl->txq_spinlock); mutex_init(&wl->txq_add_to_head_cs); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2118 @ init_completion(&wl->cfg_event); init_completion(&wl->sync_event); init_completion(&wl->txq_thread_started); + init_completion(&wl->debug_thread_started); init_srcu_struct(&wl->srcu); } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2129 @ mutex_destroy(&wilc->cfg_cmd_lock); mutex_destroy(&wilc->txq_add_to_head_cs); mutex_destroy(&wilc->vif_mutex); + mutex_destroy(&wilc->cs); mutex_destroy(&wilc->deinit_lock); cleanup_srcu_struct(&wilc->srcu); } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2137 @ int wilc_cfg80211_init(struct wilc **wilc, struct device *dev, int io_type, const struct wilc_hif_func *ops) { + int i, ret; struct wilc *wl; struct wilc_vif *vif; - int ret, i; wl = wilc_create_wiphy(dev); if (!wl) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2151 @ if (ret) goto free_wl; +#ifdef WILC_DEBUGFS + wilc_debugfs_init(); +#endif *wilc = wl; wl->io_type = io_type; wl->hif_func = ops; - for (i = 0; i < NQUEUES; i++) INIT_LIST_HEAD(&wl->txq[i].txq_head.list); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2176 @ goto free_hq; } + wilc_sysfs_init(wl); + return 0; free_hq: destroy_workqueue(wl->hif_workqueue); free_cfg: +#ifdef WILC_DEBUGFS + wilc_debugfs_remove(); +#endif wilc_wlan_cfg_deinit(wl); - free_wl: wlan_deinit_locks(wl); wiphy_unregister(wl->wiphy); wiphy_free(wl->wiphy); return ret; } -EXPORT_SYMBOL_GPL(wilc_cfg80211_init); struct wilc *wilc_create_wiphy(struct device *dev) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2233 @ sizeof(wilc_cipher_suites)); wiphy->cipher_suites = wl->cipher_suites; wiphy->n_cipher_suites = ARRAY_SIZE(wilc_cipher_suites); + wiphy->available_antennas_tx = 0x3; + wiphy->available_antennas_rx = 0x3; wiphy->mgmt_stypes = wilc_wfi_cfg80211_mgmt_types; wiphy->max_remain_on_channel_duration = 500; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2261 @ struct wilc_vif *vif = netdev_priv(net); struct wilc_priv *priv = &vif->priv; - priv->p2p_listen_state = false; + PRINT_INFO(net, INIT_DBG, "Host[%p][%p]\n", net, net->ieee80211_ptr); + timer_setup(&priv->eap_buff_timer, eap_buff_timeout, 0); + + vif->p2p_listen_state = false; mutex_init(&priv->scan_req_lock); ret = wilc_init(net, &priv->hif_drv); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2280 @ struct wilc_vif *vif = netdev_priv(net); struct wilc_priv *priv = &vif->priv; - priv->p2p_listen_state = false; + vif->p2p_listen_state = false; flush_workqueue(vif->wilc->hif_workqueue); mutex_destroy(&priv->scan_req_lock); ret = wilc_deinit(vif); + del_timer_sync(&priv->eap_buff_timer); + if (ret) netdev_err(net, "Error while deinitializing host interface\n"); } diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/wireless/microchip/wilc1000/cfg80211.h linux-microchip/drivers/net/wireless/microchip/wilc1000/cfg80211.h --- linux-6.6.51/drivers/net/wireless/microchip/wilc1000/cfg80211.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/net/wireless/microchip/wilc1000/cfg80211.h 2024-11-26 14:02:38.122504875 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:24 @ void wilc_update_mgmt_frame_registrations(struct wiphy *wiphy, struct wireless_dev *wdev, struct mgmt_frame_regs *upd); +void wilc_sysfs_init(struct wilc *wilc); +void wilc_sysfs_exit(void); +struct wilc_vif *wilc_get_vif_from_type(struct wilc *wl, int type); struct wilc_vif *wilc_get_wl_to_vif(struct wilc *wl); void wlan_deinit_locks(struct wilc *wilc); +struct wilc_vif *wilc_get_vif_from_type(struct wilc *wl, int type); #endif diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/wireless/microchip/wilc1000/debugfs.c linux-microchip/drivers/net/wireless/microchip/wilc1000/debugfs.c --- linux-6.6.51/drivers/net/wireless/microchip/wilc1000/debugfs.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/net/wireless/microchip/wilc1000/debugfs.c 2024-11-26 14:02:38.122504875 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries. + * All rights reserved. + */ + +#include <linux/module.h> +#include <linux/debugfs.h> + +#include "debugfs.h" + +atomic_t WILC_DEBUG_REGION = ATOMIC_INIT(INIT_DBG | GENERIC_DBG | + CFG80211_DBG | HOSTAPD_DBG | + PWRDEV_DBG); + +#if defined(WILC_DEBUGFS) +static struct dentry *wilc_dir; + +static ssize_t wilc_debug_region_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + char buf[128]; + int res = 0; + + /* only allow read from start */ + if (*ppos > 0) + return 0; + + res = scnprintf(buf, sizeof(buf), "Debug Region: (0x%08x)\n", + atomic_read(&WILC_DEBUG_REGION)); + + return simple_read_from_buffer(userbuf, count, ppos, buf, res); +} + +static ssize_t wilc_debug_region_write(struct file *filp, + const char __user *buf, size_t count, + loff_t *ppos) +{ + int flag = 0; + int ret; + + ret = kstrtouint_from_user(buf, count, 16, &flag); + if (ret) + return ret; + + if (flag > DBG_REGION_ALL) { + pr_err("%s, value (0x%08x) is out of range, stay previous flag (0x%08x)\n", + __func__, flag, atomic_read(&WILC_DEBUG_REGION)); + pr_err("allowed bits are 0 to 15\n"); + return -EINVAL; + } + + atomic_set(&WILC_DEBUG_REGION, (int)flag); + + pr_info("Debug region set to %x\n", atomic_read(&WILC_DEBUG_REGION)); + + return count; +} + +#define FOPS(_open, _read, _write, _poll) { \ + .owner = THIS_MODULE, \ + .open = (_open), \ + .read = (_read), \ + .write = (_write), \ + .poll = (_poll), \ +} + +struct wilc_debugfs_info_t { + const char *name; + int perm; + unsigned int data; + const struct file_operations fops; +}; + +static struct wilc_debugfs_info_t debugfs_info[] = { + { + "wilc_debug_region", + 0666, + 0, + FOPS(NULL, wilc_debug_region_read, wilc_debug_region_write, + NULL), + }, +}; + +int wilc_debugfs_init(void) +{ + int i; + struct wilc_debugfs_info_t *info; + + wilc_dir = debugfs_create_dir("wilc", NULL); + if (wilc_dir == NULL) { + pr_err("Error creating debugfs\n"); + return -EFAULT; + } + for (i = 0; i < ARRAY_SIZE(debugfs_info); i++) { + info = &debugfs_info[i]; + debugfs_create_file(info->name, + info->perm, + wilc_dir, + &info->data, + &info->fops); + } + return 0; +} + +void wilc_debugfs_remove(void) +{ + debugfs_remove_recursive(wilc_dir); +} + +#endif diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/wireless/microchip/wilc1000/debugfs.h linux-microchip/drivers/net/wireless/microchip/wilc1000/debugfs.h --- linux-6.6.51/drivers/net/wireless/microchip/wilc1000/debugfs.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/net/wireless/microchip/wilc1000/debugfs.h 2024-11-26 14:02:38.122504875 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries. + * All rights reserved. + */ + +#ifndef WILC_DEBUGFS_H +#define WILC_DEBUGFS_H + +#include <linux/kern_levels.h> +#include <linux/version.h> + +#define GENERIC_DBG BIT(0) +#define HOSTAPD_DBG BIT(1) +#define HOSTINF_DBG BIT(2) +#define CORECONFIG_DBG BIT(3) +#define CFG80211_DBG BIT(4) +#define INT_DBG BIT(5) +#define TX_DBG BIT(6) +#define RX_DBG BIT(7) +#define TCP_ENH BIT(8) +#define INIT_DBG BIT(9) +#define PWRDEV_DBG BIT(10) +#define DBG_REGION_ALL (BIT(11)-1) + +extern atomic_t WILC_DEBUG_REGION; + +#define PRINT_D(netdev, region, format, ...) do { \ + if (atomic_read(&WILC_DEBUG_REGION)&(region))\ + netdev_dbg(netdev, "DBG [%s: %d] "format, __func__, __LINE__,\ + ##__VA_ARGS__); } \ + while (0) + +#define PRINT_INFO(netdev, region, format, ...) do { \ + if (atomic_read(&WILC_DEBUG_REGION)&(region))\ + netdev_info(netdev, "INFO [%s]"format, __func__, \ + ##__VA_ARGS__); } \ + while (0) + +#define PRINT_WRN(netdev, region, format, ...) do { \ + if (atomic_read(&WILC_DEBUG_REGION)&(region))\ + netdev_warn(netdev, "WRN [%s: %d]"format, __func__, __LINE__,\ + ##__VA_ARGS__); } \ + while (0) + +#define PRINT_ER(netdev, format, ...) netdev_err(netdev, "ERR [%s:%d] "format,\ + __func__, __LINE__, ##__VA_ARGS__) + +#ifdef WILC_DEBUGFS +int wilc_debugfs_init(void); +void wilc_debugfs_remove(void); +#endif + +#endif /* WILC_DEBUGFS_H */ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/wireless/microchip/wilc1000/hif.c linux-microchip/drivers/net/wireless/microchip/wilc1000/hif.c --- linux-6.6.51/drivers/net/wireless/microchip/wilc1000/hif.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/net/wireless/microchip/wilc1000/hif.c 2024-11-26 14:04:18.564303380 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:7 @ * All rights reserved. */ +#include "cfg80211.h" #include "netdev.h" #define WILC_HIF_SCAN_TIMEOUT_MS 5000 #define WILC_HIF_CONNECT_TIMEOUT_MS 9500 #define WILC_FALSE_FRMWR_CHANNEL 100 +struct send_buffered_eap { + void (*deliver_to_stack)(struct wilc_vif *vif, u8 *buff, u32 size, + u32 pkt_offset, u8 status); + void (*eap_buf_param)(void *priv); + u8 *buff; + unsigned int size; + unsigned int pkt_offset; + void *user_arg; +}; #define WILC_SCAN_WID_LIST_SIZE 6 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:40 @ u8 wowlan_trigger; }; +struct host_if_set_ant { + u8 mode; + u8 antenna1; + u8 antenna2; + u8 gpio_mode; +}; + +struct tx_power { + u8 tx_pwr; +}; + +struct power_mgmt_param { + bool enabled; + u32 timeout; +}; + struct wilc_del_all_sta { u8 assoc_sta; u8 mac[WILC_MAX_NUM_STA][ETH_ALEN]; }; +struct add_sta_param { + u8 bssid[ETH_ALEN]; + u16 aid; + u8 supported_rates_len; + const u8 *supported_rates; + bool ht_supported; + struct ieee80211_ht_cap ht_capa; + u16 flags_mask; + u16 flags_set; +}; + union wilc_message_body { struct wilc_rcvd_net_info net_info; struct wilc_rcvd_mac_info mac_info; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:79 @ struct wilc_remain_ch remain_on_ch; char *data; struct host_if_wowlan_trigger wow_trigger; + struct send_buffered_eap send_buff_eap; + struct host_if_set_ant set_ant; + struct tx_power tx_power; + struct power_mgmt_param pwr_mgmt_info; + struct add_sta_param add_sta_info; + struct add_sta_param edit_sta_info; }; struct host_if_msg { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:161 @ return NULL; } -static int handle_scan_done(struct wilc_vif *vif, enum scan_event evt) +int handle_scan_done(struct wilc_vif *vif, enum scan_event evt) { int result = 0; u8 abort_running_scan; struct wid wid; struct host_if_drv *hif_drv = vif->hif_drv; struct wilc_user_scan_req *scan_req; + u8 null_bssid[6] = {0}; - if (evt == SCAN_EVENT_ABORTED) { + PRINT_INFO(vif->ndev, HOSTINF_DBG, "handling scan done\n"); + + if (!hif_drv) { + PRINT_ER(vif->ndev, "hif driver is NULL\n"); + return result; + } + + if (evt == SCAN_EVENT_DONE) { + if (memcmp(hif_drv->assoc_bssid, null_bssid, ETH_ALEN) == 0) + hif_drv->hif_state = HOST_IF_IDLE; + else + hif_drv->hif_state = HOST_IF_CONNECTED; + } else if (evt == SCAN_EVENT_ABORTED) { + PRINT_INFO(vif->ndev, GENERIC_DBG, "Abort running scan\n"); abort_running_scan = 1; wid.id = WID_ABORT_RUNNING_SCAN; wid.type = WID_CHAR; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:192 @ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); if (result) { - netdev_err(vif->ndev, "Failed to set abort running\n"); + PRINT_ER(vif->ndev, "Failed to set abort running\n"); result = -EFAULT; } } - if (!hif_drv) { - netdev_err(vif->ndev, "%s: hif driver is NULL\n", __func__); - return result; - } - scan_req = &hif_drv->usr_scan_req; if (scan_req->scan_result) { - scan_req->scan_result(evt, NULL, scan_req->arg); + scan_req->scan_result(evt, NULL, scan_req->priv); scan_req->scan_result = NULL; } return result; } -int wilc_scan(struct wilc_vif *vif, u8 scan_source, u8 scan_type, - u8 *ch_freq_list, u8 ch_list_len, +static void handle_send_buffered_eap(struct work_struct *work) +{ + struct host_if_msg *msg = container_of(work, struct host_if_msg, work); + struct wilc_vif *vif = msg->vif; + struct send_buffered_eap *hif_buff_eap = &msg->body.send_buff_eap; + + PRINT_INFO(vif->ndev, HOSTINF_DBG, "Sending bufferd eapol to WPAS\n"); + if (!hif_buff_eap->buff) + goto out; + + if (hif_buff_eap->deliver_to_stack) + hif_buff_eap->deliver_to_stack(vif, hif_buff_eap->buff, + hif_buff_eap->size, + hif_buff_eap->pkt_offset, + PKT_STATUS_BUFFERED); + if (hif_buff_eap->eap_buf_param) + hif_buff_eap->eap_buf_param(hif_buff_eap->user_arg); + + if (hif_buff_eap->buff != NULL) { + kfree(hif_buff_eap->buff); + hif_buff_eap->buff = NULL; + } + +out: + kfree(msg); +} + +int wilc_scan(struct wilc_vif *vif, u8 scan_source, + u8 scan_type, u8 *ch_freq_list, void (*scan_result_fn)(enum scan_event, - struct wilc_rcvd_net_info *, void *), - void *user_arg, struct cfg80211_scan_request *request) + struct wilc_rcvd_net_info *, + struct wilc_priv *), + struct cfg80211_scan_request *request) { int result = 0; struct wid wid_list[WILC_SCAN_WID_LIST_SIZE]; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:247 @ u8 *buffer; u8 valuesize = 0; u8 *search_ssid_vals = NULL; + const u8 ch_list_len = request->n_channels; struct host_if_drv *hif_drv = vif->hif_drv; + struct wilc_vif *vif_tmp; + int srcu_idx; - if (hif_drv->hif_state >= HOST_IF_SCANNING && - hif_drv->hif_state < HOST_IF_CONNECTED) { - netdev_err(vif->ndev, "Already scan\n"); - result = -EBUSY; - goto error; + PRINT_INFO(vif->ndev, HOSTINF_DBG, "Setting SCAN params\n"); + PRINT_INFO(vif->ndev, HOSTINF_DBG, "Scanning: In [%d] state\n", + hif_drv->hif_state); + + srcu_idx = srcu_read_lock(&vif->wilc->srcu); + list_for_each_entry_rcu(vif_tmp, &vif->wilc->vif_list, list) { + struct host_if_drv *hif_drv_tmp; + + if (vif_tmp == NULL || vif_tmp->hif_drv == NULL) + continue; + + hif_drv_tmp = vif_tmp->hif_drv; + + if (hif_drv_tmp->hif_state != HOST_IF_IDLE && + hif_drv_tmp->hif_state != HOST_IF_CONNECTED) { + PRINT_INFO(vif_tmp->ndev, GENERIC_DBG, + "Abort scan. In state [%d]\n", + hif_drv_tmp->hif_state); + result = -EBUSY; + srcu_read_unlock(&vif->wilc->srcu, srcu_idx); + goto error; + } } + srcu_read_unlock(&vif->wilc->srcu, srcu_idx); if (vif->connecting) { - netdev_err(vif->ndev, "Don't do obss scan\n"); + PRINT_INFO(vif->ndev, GENERIC_DBG, + "Don't do scan in (CONNECTING) state\n"); result = -EBUSY; goto error; } + PRINT_INFO(vif->ndev, HOSTINF_DBG, "Setting SCAN params\n"); hif_drv->usr_scan_req.ch_cnt = 0; if (request->n_ssids) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:299 @ *buffer++ = request->n_ssids; + PRINT_INFO(vif->ndev, HOSTINF_DBG, + "In Handle_ProbeRequest number of ssid %d\n", + request->n_ssids); for (i = 0; i < request->n_ssids; i++) { *buffer++ = request->ssids[i].ssid_len; memcpy(buffer, request->ssids[i].ssid, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:358 @ index++; hif_drv->usr_scan_req.scan_result = scan_result_fn; - hif_drv->usr_scan_req.arg = user_arg; + hif_drv->usr_scan_req.priv = &vif->priv; result = wilc_send_config_pkt(vif, WILC_SET_CFG, wid_list, index); if (result) { netdev_err(vif->ndev, "Failed to send scan parameters\n"); goto error; + } else { + hif_drv->scan_timer_vif = vif; + PRINT_INFO(vif->ndev, HOSTINF_DBG, + ">> Starting the SCAN timer\n"); + mod_timer(&hif_drv->scan_timer, + jiffies + msecs_to_jiffies(scan_timeout)); } - hif_drv->scan_timer_vif = vif; - mod_timer(&hif_drv->scan_timer, - jiffies + msecs_to_jiffies(scan_timeout)); - error: kfree(search_ssid_vals); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:387 @ struct host_if_drv *hif_drv = vif->hif_drv; struct wilc_conn_info *conn_attr = &hif_drv->conn_info; struct wilc_join_bss_param *bss_param = conn_attr->param; + struct wilc_vif *vif_tmp; + int srcu_idx; + + srcu_idx = srcu_read_lock(&vif->wilc->srcu); + list_for_each_entry_rcu(vif_tmp, &vif->wilc->vif_list, list) { + struct host_if_drv *hif_drv_tmp; + + if (vif_tmp == NULL || vif_tmp->hif_drv == NULL) + continue; + + hif_drv_tmp = vif_tmp->hif_drv; + + if (hif_drv_tmp->hif_state == HOST_IF_SCANNING) { + PRINT_INFO(vif_tmp->ndev, GENERIC_DBG, + "Abort connect in state [%d]\n", + hif_drv_tmp->hif_state); + result = -EBUSY; + srcu_read_unlock(&vif->wilc->srcu, srcu_idx); + goto error; + } + } + srcu_read_unlock(&vif->wilc->srcu, srcu_idx); wid_list[wid_cnt].id = WID_SET_MFP; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:429 @ wid_list[wid_cnt].val = (s8 *)&conn_attr->security; wid_cnt++; + PRINT_D(vif->ndev, HOSTINF_DBG, "Encrypt Mode = %x\n", + conn_attr->security); wid_list[wid_cnt].id = WID_AUTH_TYPE; wid_list[wid_cnt].type = WID_CHAR; wid_list[wid_cnt].size = sizeof(char); wid_list[wid_cnt].val = (s8 *)&conn_attr->auth_type; wid_cnt++; + PRINT_D(vif->ndev, HOSTINF_DBG, "Authentication Type = %x\n", + conn_attr->auth_type); + wid_list[wid_cnt].id = WID_JOIN_REQ_EXTENDED; wid_list[wid_cnt].type = WID_STR; wid_list[wid_cnt].size = sizeof(*bss_param); wid_list[wid_cnt].val = (u8 *)bss_param; wid_cnt++; + PRINT_INFO(vif->ndev, GENERIC_DBG, "send HOST_IF_WAITING_CONN_RESP\n"); + result = wilc_send_config_pkt(vif, WILC_SET_CFG, wid_list, wid_cnt); if (result) { netdev_err(vif->ndev, "failed to send config packet\n"); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:488 @ if (hif_drv->conn_info.conn_result) { hif_drv->conn_info.conn_result(CONN_DISCONN_EVENT_CONN_RESP, WILC_MAC_STATUS_DISCONNECTED, - hif_drv->conn_info.arg); + hif_drv->conn_info.priv); } else { netdev_err(vif->ndev, "%s: conn_result is NULL\n", __func__); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:499 @ wid.val = (s8 *)&dummy_reason_code; wid.size = sizeof(char); + PRINT_INFO(vif->ndev, HOSTINF_DBG, "Sending disconnect request\n"); result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); if (result) netdev_err(vif->ndev, "Failed to send disconnect\n"); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:512 @ kfree(msg); } -void *wilc_parse_join_bss_param(struct cfg80211_bss *bss, - struct cfg80211_crypto_settings *crypto) +struct wilc_join_bss_param * +wilc_parse_join_bss_param(struct cfg80211_bss *bss, + struct cfg80211_crypto_settings *crypto) { const u8 *ies_data, *tim_elm, *ssid_elm, *rates_ie, *supp_rates_ie; const u8 *ht_ie, *wpa_ie, *wmm_ie, *rsn_ie; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:676 @ { struct host_if_msg *msg = container_of(work, struct host_if_msg, work); struct wilc_rcvd_net_info *rcvd_info = &msg->body.net_info; - struct wilc_user_scan_req *scan_req = &msg->vif->hif_drv->usr_scan_req; + struct host_if_drv *hif_drv; + struct wilc_user_scan_req *scan_req; const u8 *ch_elm; u8 *ies; int ies_len; size_t offset; + PRINT_D(msg->vif->ndev, HOSTINF_DBG, + "Handling received network info\n"); + if (ieee80211_is_probe_resp(rcvd_info->mgmt->frame_control)) offset = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); else if (ieee80211_is_beacon(rcvd_info->mgmt->frame_control)) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:698 @ if (ies_len <= 0) goto done; + PRINT_INFO(msg->vif->ndev, HOSTINF_DBG, "New network found\n"); + /* extract the channel from received mgmt frame */ ch_elm = cfg80211_find_ie(WLAN_EID_DS_PARAMS, ies, ies_len); if (ch_elm && ch_elm[1] > 0) rcvd_info->ch = ch_elm[2]; + if (!msg->vif || !msg->vif->hif_drv) + goto done; + + hif_drv = msg->vif->hif_drv; + scan_req = &hif_drv->usr_scan_req; if (scan_req->scan_result) scan_req->scan_result(SCAN_EVENT_NETWORK_FOUND, rcvd_info, - scan_req->arg); + scan_req->priv); done: kfree(rcvd_info->mgmt); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:778 @ WILC_MAX_ASSOC_RESP_FRAME_SIZE, &assoc_resp_info_len); + PRINT_D(vif->ndev, HOSTINF_DBG, + "Received association response = %d\n", + assoc_resp_info_len); if (assoc_resp_info_len != 0) { s32 err = 0; + PRINT_INFO(vif->ndev, HOSTINF_DBG, + "Parsing association response\n"); err = wilc_parse_assoc_resp_info(hif_drv->assoc_resp, assoc_resp_info_len, conn_info); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:798 @ del_timer(&hif_drv->connect_timer); conn_info->conn_result(CONN_DISCONN_EVENT_CONN_RESP, mac_status, - hif_drv->conn_info.arg); + hif_drv->conn_info.priv); if (mac_status == WILC_MAC_STATUS_CONNECTED && conn_info->status == WLAN_STATUS_SUCCESS) { + PRINT_INFO(vif->ndev, HOSTINF_DBG, + "MAC status : CONNECTED and Connect Status : Successful\n"); ether_addr_copy(hif_drv->assoc_bssid, conn_info->bssid); hif_drv->hif_state = HOST_IF_CONNECTED; } else { + PRINT_INFO(vif->ndev, HOSTINF_DBG, + "MAC status : %d and Connect Status : %d\n", + mac_status, conn_info->status); hif_drv->hif_state = HOST_IF_IDLE; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:826 @ { struct host_if_drv *hif_drv = vif->hif_drv; + PRINT_INFO(vif->ndev, HOSTINF_DBG, + "Received WILC_MAC_STATUS_DISCONNECTED from the FW\n"); if (hif_drv->usr_scan_req.scan_result) { + PRINT_INFO(vif->ndev, HOSTINF_DBG, + "\n\n<< Abort the running OBSS Scan >>\n\n"); del_timer(&hif_drv->scan_timer); handle_scan_done(vif, SCAN_EVENT_ABORTED); } if (hif_drv->conn_info.conn_result) hif_drv->conn_info.conn_result(CONN_DISCONN_EVENT_DISCONN_NOTIF, - 0, hif_drv->conn_info.arg); + 0, hif_drv->conn_info.priv); eth_zero_addr(hif_drv->assoc_bssid); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:859 @ goto free_msg; } + PRINT_INFO(vif->ndev, GENERIC_DBG, + "Current State = %d,Received state = %d\n", + hif_drv->hif_state, mac_info->status); + if (!hif_drv->conn_info.conn_result) { netdev_err(vif->ndev, "%s: conn_result is NULL\n", __func__); goto free_msg; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:879 @ if (hif_drv->hif_state == HOST_IF_CONNECTED) { wilc_handle_disconnect(vif); } else if (hif_drv->usr_scan_req.scan_result) { + PRINT_WRN(vif->ndev, HOSTINF_DBG, + "Received WILC_MAC_STATUS_DISCONNECTED. Abort the running Scan"); del_timer(&hif_drv->scan_timer); handle_scan_done(vif, SCAN_EVENT_ABORTED); } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:898 @ struct wilc_conn_info *conn_info; int result; u16 dummy_reason_code = 0; + struct wilc_vif *vif_tmp; + int srcu_idx; + + srcu_idx = srcu_read_lock(&vif->wilc->srcu); + list_for_each_entry_rcu(vif_tmp, &vif->wilc->vif_list, list) { + struct host_if_drv *hif_drv_tmp; + + if (vif_tmp == NULL || vif_tmp->hif_drv == NULL) + continue; + + hif_drv_tmp = vif_tmp->hif_drv; + + if (hif_drv_tmp->hif_state == HOST_IF_SCANNING) { + PRINT_INFO(vif_tmp->ndev, GENERIC_DBG, + "Abort scan from disconnect. state [%d]\n", + hif_drv_tmp->hif_state); + del_timer(&hif_drv_tmp->scan_timer); + handle_scan_done(vif_tmp, SCAN_EVENT_ABORTED); + } + } + srcu_read_unlock(&vif->wilc->srcu, srcu_idx); wid.id = WID_DISCONNECT; wid.type = WID_CHAR; wid.val = (s8 *)&dummy_reason_code; wid.size = sizeof(char); + PRINT_INFO(vif->ndev, HOSTINF_DBG, "Sending disconnect request\n"); + result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); if (result) { netdev_err(vif->ndev, "Failed to send disconnect\n"); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:938 @ if (scan_req->scan_result) { del_timer(&hif_drv->scan_timer); - scan_req->scan_result(SCAN_EVENT_ABORTED, NULL, scan_req->arg); + scan_req->scan_result(SCAN_EVENT_ABORTED, NULL, scan_req->priv); scan_req->scan_result = NULL; } if (conn_info->conn_result) { if (hif_drv->hif_state == HOST_IF_WAITING_CONN_RESP || - hif_drv->hif_state == HOST_IF_EXTERNAL_AUTH) + hif_drv->hif_state == HOST_IF_EXTERNAL_AUTH) { del_timer(&hif_drv->connect_timer); - - conn_info->conn_result(CONN_DISCONN_EVENT_DISCONN_NOTIF, 0, - conn_info->arg); + conn_info->conn_result(CONN_DISCONN_EVENT_CONN_RESP, + WILC_MAC_STATUS_DISCONNECTED, + conn_info->priv); + } else if (hif_drv->hif_state == HOST_IF_CONNECTED) { + conn_info->conn_result(CONN_DISCONN_EVENT_DISCONN_NOTIF, + WILC_MAC_STATUS_DISCONNECTED, + conn_info->priv); + } } else { netdev_err(vif->ndev, "%s: conn_result is NULL\n", __func__); } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:965 @ conn_info->req_ies_len = 0; kfree(conn_info->req_ies); conn_info->req_ies = NULL; + conn_info->conn_result = NULL; return 0; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1012 @ } if (stats->link_speed > TCP_ACK_FILTER_LINK_SPEED_THRESH && - stats->link_speed != DEFAULT_LINK_SPEED) + stats->link_speed != DEFAULT_LINK_SPEED) { + PRINT_INFO(vif->ndev, HOSTINF_DBG, "Enable TCP filter\n"); wilc_enable_tcp_ack_filter(vif, true); - else if (stats->link_speed != DEFAULT_LINK_SPEED) + } else if (stats->link_speed != DEFAULT_LINK_SPEED) { + PRINT_INFO(vif->ndev, HOSTINF_DBG, "Disable TCP filter %d\n", + stats->link_speed); wilc_enable_tcp_ack_filter(vif, false); + } return result; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1035 @ kfree(msg); } -static void wilc_hif_pack_sta_param(u8 *cur_byte, const u8 *mac, - struct station_parameters *params) +static void wilc_hif_pack_sta_param(u8 *cur_byte, struct add_sta_param *params) { - ether_addr_copy(cur_byte, mac); + ether_addr_copy(cur_byte, params->bssid); cur_byte += ETH_ALEN; put_unaligned_le16(params->aid, cur_byte); cur_byte += 2; - *cur_byte++ = params->link_sta_params.supported_rates_len; - if (params->link_sta_params.supported_rates_len > 0) - memcpy(cur_byte, params->link_sta_params.supported_rates, - params->link_sta_params.supported_rates_len); - cur_byte += params->link_sta_params.supported_rates_len; + *cur_byte++ = params->supported_rates_len; + if (params->supported_rates_len > 0) + memcpy(cur_byte, params->supported_rates, + params->supported_rates_len); + cur_byte += params->supported_rates_len; - if (params->link_sta_params.ht_capa) { + if (params->ht_supported) { *cur_byte++ = true; - memcpy(cur_byte, params->link_sta_params.ht_capa, + memcpy(cur_byte, ¶ms->ht_capa, sizeof(struct ieee80211_ht_cap)); } else { *cur_byte++ = false; } cur_byte += sizeof(struct ieee80211_ht_cap); - put_unaligned_le16(params->sta_flags_mask, cur_byte); + put_unaligned_le16(params->flags_mask, cur_byte); cur_byte += 2; - put_unaligned_le16(params->sta_flags_set, cur_byte); + put_unaligned_le16(params->flags_set, cur_byte); } static int handle_remain_on_chan(struct wilc_vif *vif, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1070 @ u8 remain_on_chan_flag; struct wid wid; struct host_if_drv *hif_drv = vif->hif_drv; + struct wilc_vif *vif_tmp; + int srcu_idx; - if (hif_drv->usr_scan_req.scan_result) - return -EBUSY; + if (!hif_drv) { + PRINT_ER(vif->ndev, "Driver is null\n"); + return -EFAULT; + } - if (hif_drv->hif_state == HOST_IF_WAITING_CONN_RESP) - return -EBUSY; + srcu_idx = srcu_read_lock(&vif->wilc->srcu); + list_for_each_entry_rcu(vif_tmp, &vif->wilc->vif_list, list) { + struct host_if_drv *hif_drv_tmp; + + if (vif_tmp == NULL || vif_tmp->hif_drv == NULL) + continue; + + hif_drv_tmp = vif_tmp->hif_drv; + + if (hif_drv_tmp->hif_state == HOST_IF_SCANNING) { + PRINT_INFO(vif_tmp->ndev, GENERIC_DBG, + "IFC busy scanning. WLAN_IFC state %d\n", + hif_drv_tmp->hif_state); + srcu_read_unlock(&vif->wilc->srcu, srcu_idx); + return -EBUSY; + } else if (hif_drv_tmp->hif_state != HOST_IF_IDLE && + hif_drv_tmp->hif_state != HOST_IF_CONNECTED) { + PRINT_INFO(vif_tmp->ndev, GENERIC_DBG, + "IFC busy connecting. WLAN_IFC %d\n", + hif_drv_tmp->hif_state); + srcu_read_unlock(&vif->wilc->srcu, srcu_idx); + return -EBUSY; + } + } + srcu_read_unlock(&vif->wilc->srcu, srcu_idx); - if (vif->connecting) + if (vif->connecting) { + PRINT_INFO(vif->ndev, GENERIC_DBG, + "Don't do scan in (CONNECTING) state\n"); return -EBUSY; + } remain_on_chan_flag = true; wid.id = WID_REMAIN_ON_CHAN; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1123 @ result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); kfree(wid.val); - if (result) + if (result) { + PRINT_ER(vif->ndev, "Failed to set remain on channel\n"); return -EBUSY; + } - hif_drv->remain_on_ch.arg = hif_remain_ch->arg; + hif_drv->remain_on_ch.vif = hif_remain_ch->vif; hif_drv->remain_on_ch.expired = hif_remain_ch->expired; hif_drv->remain_on_ch.ch = hif_remain_ch->ch; hif_drv->remain_on_ch.cookie = hif_remain_ch->cookie; + hif_drv->hif_state = HOST_IF_P2P_LISTEN; + hif_drv->remain_on_ch_timer_vif = vif; - return 0; + return result; } static int wilc_handle_roc_expired(struct wilc_vif *vif, u64 cookie) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1145 @ struct wid wid; int result; struct host_if_drv *hif_drv = vif->hif_drv; + u8 null_bssid[6] = {0}; - if (vif->priv.p2p_listen_state) { + if (hif_drv->hif_state == HOST_IF_P2P_LISTEN) { remain_on_chan_flag = false; wid.id = WID_REMAIN_ON_CHAN; wid.type = WID_STR; wid.size = 2; wid.val = kmalloc(wid.size, GFP_KERNEL); - if (!wid.val) + if (!wid.val) { + PRINT_ER(vif->ndev, "Failed to allocate memory\n"); return -ENOMEM; + } wid.val[0] = remain_on_chan_flag; wid.val[1] = WILC_FALSE_FRMWR_CHANNEL; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1169 @ return -EINVAL; } - if (hif_drv->remain_on_ch.expired) { - hif_drv->remain_on_ch.expired(hif_drv->remain_on_ch.arg, + if (hif_drv->remain_on_ch.expired) + hif_drv->remain_on_ch.expired(hif_drv->remain_on_ch.vif, cookie); - } + + if (memcmp(hif_drv->assoc_bssid, null_bssid, ETH_ALEN) == 0) + hif_drv->hif_state = HOST_IF_IDLE; + else + hif_drv->hif_state = HOST_IF_CONNECTED; } else { netdev_dbg(vif->ndev, "Not in listen state\n"); } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1187 @ static void wilc_handle_listen_state_expired(struct work_struct *work) { struct host_if_msg *msg = container_of(work, struct host_if_msg, work); + struct wilc_vif *vif = msg->vif; + struct wilc_remain_ch *hif_remain_ch = &msg->body.remain_on_ch; + + PRINT_INFO(vif->ndev, HOSTINF_DBG, "CANCEL REMAIN ON CHAN\n"); + + wilc_handle_roc_expired(vif, hif_remain_ch->cookie); - wilc_handle_roc_expired(msg->vif, msg->body.remain_on_ch.cookie); kfree(msg); } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1229 @ struct wid wid; u8 *cur_byte; + PRINT_INFO(vif->ndev, HOSTINF_DBG, "Setup Multicast Filter\n"); + wid.id = WID_SETUP_MULTICAST_FILTER; wid.type = WID_BIN; wid.size = sizeof(struct wilc_set_multicast) + (set_mc->cnt * ETH_ALEN); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1274 @ ret = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); if (ret) - pr_err("Failed to send wowlan trigger config packet\n"); + PRINT_ER(vif->ndev, + "Failed to send wowlan trigger config packet\n"); } int wilc_set_external_auth_param(struct wilc_vif *vif, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1307 @ { struct host_if_msg *msg = container_of(work, struct host_if_msg, work); + if(!msg->vif || !msg->vif->wilc || msg->vif->wilc->close) + return; + handle_scan_done(msg->vif, SCAN_EVENT_ABORTED); kfree(msg); } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1319 @ struct host_if_msg *msg = container_of(work, struct host_if_msg, work); del_timer(&msg->vif->hif_drv->scan_timer); + PRINT_INFO(msg->vif->ndev, HOSTINF_DBG, "scan completed\n"); handle_scan_done(msg->vif, SCAN_EVENT_DONE); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1359 @ kfree(msg); } +signed int wilc_send_buffered_eap(struct wilc_vif *vif, + void (*deliver_to_stack)(struct wilc_vif *, + u8 *, u32, u32, u8), + void (*eap_buf_param)(void *), u8 *buff, + unsigned int size, unsigned int pkt_offset, + void *user_arg) +{ + int result; + struct host_if_msg *msg; + + if (!vif || !deliver_to_stack || !eap_buf_param) + return -EFAULT; + + msg = wilc_alloc_work(vif, handle_send_buffered_eap, false); + if (IS_ERR(msg)) + return PTR_ERR(msg); + msg->body.send_buff_eap.deliver_to_stack = deliver_to_stack; + msg->body.send_buff_eap.eap_buf_param = eap_buf_param; + msg->body.send_buff_eap.size = size; + msg->body.send_buff_eap.pkt_offset = pkt_offset; + msg->body.send_buff_eap.buff = kmalloc(size + pkt_offset, + GFP_ATOMIC); + memcpy(msg->body.send_buff_eap.buff, buff, size + pkt_offset); + msg->body.send_buff_eap.user_arg = user_arg; + + result = wilc_enqueue_work(msg); + if (result) { + PRINT_ER(vif->ndev, "enqueue work failed\n"); + kfree(msg->body.send_buff_eap.buff); + kfree(msg); + } + return result; +} + int wilc_add_ptk(struct wilc_vif *vif, const u8 *ptk, u8 ptk_key_len, const u8 *mac_addr, const u8 *rx_mic, const u8 *tx_mic, u8 mode, u8 cipher_mode, u8 index) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1410 @ wid_list[0].val = (s8 *)&cipher_mode; key_buf = kzalloc(sizeof(*key_buf) + t_key_len, GFP_KERNEL); - if (!key_buf) + if (!key_buf) { + PRINT_ER(vif->ndev, + "NO buffer to keep Key buffer - AP\n"); return -ENOMEM; - + } ether_addr_copy(key_buf->mac_addr, mac_addr); key_buf->index = index; key_buf->key_len = t_key_len; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1440 @ struct wilc_sta_wpa_ptk *key_buf; key_buf = kzalloc(sizeof(*key_buf) + t_key_len, GFP_KERNEL); - if (!key_buf) + if (!key_buf) { + PRINT_ER(vif->ndev, + "No buffer to keep Key buffer - Station\n"); return -ENOMEM; + } ether_addr_copy(key_buf->mac_addr, mac_addr); key_buf->key_len = t_key_len; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1509 @ int t_key_len = gtk_key_len + WILC_RX_MIC_KEY_LEN + WILC_TX_MIC_KEY_LEN; gtk_key = kzalloc(sizeof(*gtk_key) + t_key_len, GFP_KERNEL); - if (!gtk_key) + if (!gtk_key) { + PRINT_ER(vif->ndev, "No buffer to send GTK Key\n"); return -ENOMEM; + } /* fill bssid value only in station mode */ if (mode == WILC_STATION_MODE && @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1625 @ } result = wilc_send_connect_wid(vif); - if (result) + if (result) { + PRINT_ER(vif->ndev, "Failed to send connect wid\n"); goto free_ies; + } hif_drv->connect_timer_vif = vif; mod_timer(&hif_drv->connect_timer, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1690 @ wid.type = WID_STR; wid.size = ETH_ALEN; wid.val = kzalloc(wid.size, GFP_KERNEL); - if (!wid.val) + if (!wid.val) { + PRINT_ER(vif->ndev, "Failed to allocate buffer\n"); return -ENOMEM; + } ether_addr_copy(wid.val, mac); result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1711 @ if (result) netdev_err(vif->ndev, "Failed to get inactive time\n"); + PRINT_INFO(vif->ndev, CFG80211_DBG, "Getting inactive time : %d\n", + *out_val); + return result; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1743 @ int result; struct host_if_msg *msg; + PRINT_INFO(vif->ndev, HOSTINF_DBG, " getting async statistics\n"); msg = wilc_alloc_work(vif, handle_get_statistics, false); if (IS_ERR(msg)) return PTR_ERR(msg); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1817 @ struct host_if_drv *hif_drv; struct wilc_vif *vif = netdev_priv(dev); - hif_drv = kzalloc(sizeof(*hif_drv), GFP_KERNEL); - if (!hif_drv) + hif_drv = kzalloc(sizeof(*hif_drv), GFP_KERNEL); + if (!hif_drv) { + PRINT_ER(dev, "hif driver is NULL\n"); return -ENOMEM; - + } *hif_drv_handler = hif_drv; - vif->hif_drv = hif_drv; timer_setup(&vif->periodic_rssi, get_periodic_rssi, 0); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1851 @ mutex_lock(&vif->wilc->deinit_lock); +#if KERNEL_VERSION(6, 4, 0) <= LINUX_VERSION_CODE timer_shutdown_sync(&hif_drv->scan_timer); timer_shutdown_sync(&hif_drv->connect_timer); - del_timer_sync(&vif->periodic_rssi); timer_shutdown_sync(&hif_drv->remain_on_ch_timer); - +#else + /* to support compilation on v6.1, will be removed later */ + del_timer_sync(&hif_drv->scan_timer); + del_timer_sync(&hif_drv->connect_timer); + del_timer_sync(&hif_drv->remain_on_ch_timer); +#endif + del_timer_sync(&vif->periodic_rssi); if (hif_drv->usr_scan_req.scan_result) { hif_drv->usr_scan_req.scan_result(SCAN_EVENT_ABORTED, NULL, - hif_drv->usr_scan_req.arg); + hif_drv->usr_scan_req.priv); hif_drv->usr_scan_req.scan_result = NULL; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1883 @ int id; struct host_if_drv *hif_drv; struct wilc_vif *vif; + int srcu_idx; id = get_unaligned_le32(&buffer[length - 4]); + srcu_idx = srcu_read_lock(&wilc->srcu); vif = wilc_get_vif_from_idx(wilc, id); if (!vif) - return; - hif_drv = vif->hif_drv; + goto out; + hif_drv = vif->hif_drv; if (!hif_drv) { netdev_err(vif->ndev, "driver not init[%p]\n", hif_drv); - return; + goto out; } msg = wilc_alloc_work(vif, handle_rcvd_ntwrk_info, false); if (IS_ERR(msg)) - return; + goto out; msg->body.net_info.frame_len = get_unaligned_le16(&buffer[6]) - 1; msg->body.net_info.rssi = buffer[8]; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1908 @ GFP_KERNEL); if (!msg->body.net_info.mgmt) { kfree(msg); - return; + goto out; } result = wilc_enqueue_work(msg); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1917 @ kfree(msg->body.net_info.mgmt); kfree(msg); } +out: + srcu_read_unlock(&wilc->srcu, srcu_idx); } void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *buffer, u32 length) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1928 @ int id; struct host_if_drv *hif_drv; struct wilc_vif *vif; + int srcu_idx; mutex_lock(&wilc->deinit_lock); id = get_unaligned_le32(&buffer[length - 4]); + srcu_idx = srcu_read_lock(&wilc->srcu); vif = wilc_get_vif_from_idx(wilc, id); - if (!vif) { - mutex_unlock(&wilc->deinit_lock); - return; - } + if (!vif) + goto out; + + PRINT_INFO(vif->ndev, HOSTINF_DBG, + "General asynchronous info packet received\n"); hif_drv = vif->hif_drv; if (!hif_drv) { - mutex_unlock(&wilc->deinit_lock); - return; + pr_err("hif driver is NULL\n"); + goto out; } if (!hif_drv->conn_info.conn_result) { - netdev_err(vif->ndev, "%s: conn_result is NULL\n", __func__); - mutex_unlock(&wilc->deinit_lock); - return; + pr_err("there is no current Connect Request\n"); + goto out; } msg = wilc_alloc_work(vif, handle_rcvd_gnrl_async_info, false); - if (IS_ERR(msg)) { - mutex_unlock(&wilc->deinit_lock); - return; - } + if (IS_ERR(msg)) + goto out; msg->body.mac_info.status = buffer[7]; + PRINT_INFO(vif->ndev, HOSTINF_DBG, + "Received MAC status= %d Reason= %d Info = %d\n", + buffer[7], buffer[8], buffer[9]); result = wilc_enqueue_work(msg); if (result) { netdev_err(vif->ndev, "%s: enqueue work failed\n", __func__); kfree(msg); } - +out: mutex_unlock(&wilc->deinit_lock); + srcu_read_unlock(&wilc->srcu, srcu_idx); } void wilc_scan_complete_received(struct wilc *wilc, u8 *buffer, u32 length) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1977 @ int id; struct host_if_drv *hif_drv; struct wilc_vif *vif; + int srcu_idx; id = get_unaligned_le32(&buffer[length - 4]); + srcu_idx = srcu_read_lock(&wilc->srcu); vif = wilc_get_vif_from_idx(wilc, id); if (!vif) - return; - hif_drv = vif->hif_drv; + goto out; - if (!hif_drv) - return; + PRINT_INFO(vif->ndev, GENERIC_DBG, "Scan notification received\n"); + + hif_drv = vif->hif_drv; + if (!hif_drv) { + pr_err("hif driver is NULL\n"); + goto out; + } if (hif_drv->usr_scan_req.scan_result) { struct host_if_msg *msg; msg = wilc_alloc_work(vif, handle_scan_complete, false); if (IS_ERR(msg)) - return; + goto out; result = wilc_enqueue_work(msg); if (result) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2007 @ kfree(msg); } } +out: + srcu_read_unlock(&wilc->srcu, srcu_idx); } -int wilc_remain_on_channel(struct wilc_vif *vif, u64 cookie, - u32 duration, u16 chan, - void (*expired)(void *, u64), - void *user_arg) +int wilc_remain_on_channel(struct wilc_vif *vif, u64 cookie, u16 chan, + void (*expired)(struct wilc_vif *, u64)) { struct wilc_remain_ch roc; int result; + PRINT_INFO(vif->ndev, CFG80211_DBG, "called\n"); roc.ch = chan; roc.expired = expired; - roc.arg = user_arg; - roc.duration = duration; + roc.vif = vif; roc.cookie = cookie; result = handle_remain_on_chan(vif, &roc); if (result) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2060 @ switch (frame_type) { case IEEE80211_STYPE_ACTION: + PRINT_INFO(vif->ndev, HOSTINF_DBG, "ACTION\n"); reg_frame.reg_id = WILC_FW_ACTION_FRM_IDX; break; case IEEE80211_STYPE_PROBE_REQ: + PRINT_INFO(vif->ndev, HOSTINF_DBG, "PROBE REQ\n"); reg_frame.reg_id = WILC_FW_PROBE_REQ_IDX; break; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2074 @ break; default: + PRINT_INFO(vif->ndev, HOSTINF_DBG, "Not valid frame type\n"); break; } reg_frame.frame_type = cpu_to_le16(frame_type); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2090 @ int result; u8 *cur_byte; + PRINT_INFO(vif->ndev, HOSTINF_DBG, + "Setting adding beacon\n"); + wid.id = WID_ADD_BEACON; wid.type = WID_BIN; wid.size = params->head_len + params->tail_len + 16; wid.val = kzalloc(wid.size, GFP_KERNEL); - if (!wid.val) + if (!wid.val) { + PRINT_ER(vif->ndev, "Failed to allocate buffer\n"); return -ENOMEM; + } cur_byte = wid.val; put_unaligned_le32(interval, cur_byte); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2135 @ struct wid wid; u8 del_beacon = 0; + PRINT_INFO(vif->ndev, HOSTINF_DBG, + "Setting deleting beacon message queue params\n"); + wid.id = WID_DEL_BEACON; wid.type = WID_CHAR; wid.size = sizeof(char); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2150 @ return result; } -int wilc_add_station(struct wilc_vif *vif, const u8 *mac, - struct station_parameters *params) +static void handle_add_station(struct work_struct *work) { - struct wid wid; + struct host_if_msg *msg = container_of(work, struct host_if_msg, work); + struct wilc_vif *vif = msg->vif; + struct add_sta_param *params = &msg->body.add_sta_info; int result; - u8 *cur_byte; + struct wid wid; wid.id = WID_ADD_STA; wid.type = WID_BIN; - wid.size = WILC_ADD_STA_LENGTH + - params->link_sta_params.supported_rates_len; + wid.size = WILC_ADD_STA_LENGTH + params->supported_rates_len; + PRINT_INFO(vif->ndev, HOSTINF_DBG, "Handling add station\n"); wid.val = kmalloc(wid.size, GFP_KERNEL); if (!wid.val) - return -ENOMEM; + goto error; - cur_byte = wid.val; - wilc_hif_pack_sta_param(cur_byte, mac, params); + wilc_hif_pack_sta_param(wid.val, params); result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); - if (result != 0) - netdev_err(vif->ndev, "Failed to send add station\n"); + if (result) + PRINT_ER(vif->ndev, "Failed to send add station\n"); kfree(wid.val); +error: + kfree(params->supported_rates); + kfree(msg); +} + +int wilc_add_station(struct wilc_vif *vif, const u8 *mac, + struct station_parameters *params) +{ + int result; + struct host_if_msg *msg; + struct add_sta_param *sta_params; + + PRINT_INFO(vif->ndev, HOSTINF_DBG, + "Setting adding station message queue params\n"); + + msg = wilc_alloc_work(vif, handle_add_station, false); + if (IS_ERR(msg)) + return PTR_ERR(msg); + + sta_params = &msg->body.add_sta_info; + memcpy(sta_params->bssid, mac, ETH_ALEN); + sta_params->aid = params->aid; + if (!params->link_sta_params.ht_capa) { + sta_params->ht_supported = false; + } else { + sta_params->ht_supported = true; + memcpy(&sta_params->ht_capa, params->link_sta_params.ht_capa, + sizeof(struct ieee80211_ht_cap)); + } + sta_params->flags_mask = params->sta_flags_mask; + sta_params->flags_set = params->sta_flags_set; + + sta_params->supported_rates_len = params->link_sta_params.supported_rates_len; + if (params->link_sta_params.supported_rates_len > 0) { + sta_params->supported_rates = kmemdup(params->link_sta_params.supported_rates, + params->link_sta_params.supported_rates_len, + GFP_KERNEL); + if (!sta_params->supported_rates) { + kfree(msg); + return -ENOMEM; + } + } + + result = wilc_enqueue_work(msg); + if (result) { + PRINT_ER(vif->ndev, "enqueue work failed\n"); + kfree(sta_params->supported_rates); + kfree(msg); + } return result; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2231 @ struct wid wid; int result; + PRINT_INFO(vif->ndev, HOSTINF_DBG, + "Setting deleting station message queue params\n"); + wid.id = WID_REMOVE_STA; wid.type = WID_BIN; wid.size = ETH_ALEN; wid.val = kzalloc(wid.size, GFP_KERNEL); - if (!wid.val) + if (!wid.val) { + PRINT_ER(vif->ndev, "Failed to allocate buffer\n"); return -ENOMEM; + } if (!mac_addr) eth_broadcast_addr(wid.val); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2265 @ u8 assoc_sta = 0; struct wilc_del_all_sta del_sta; + PRINT_INFO(vif->ndev, HOSTINF_DBG, + "Setting deauthenticating station message queue params\n"); memset(&del_sta, 0x0, sizeof(del_sta)); for (i = 0; i < WILC_MAX_NUM_STA; i++) { if (!is_zero_ether_addr(mac_addr[i])) { + PRINT_INFO(vif->ndev, + CFG80211_DBG, "BSSID = %x%x%x%x%x%x\n", + mac_addr[i][0], mac_addr[i][1], + mac_addr[i][2], mac_addr[i][3], + mac_addr[i][4], mac_addr[i][5]); assoc_sta++; ether_addr_copy(del_sta.mac[i], mac_addr[i]); } } - if (!assoc_sta) + if (!assoc_sta) { + PRINT_INFO(vif->ndev, CFG80211_DBG, "NO ASSOCIATED STAS\n"); return 0; - + } del_sta.assoc_sta = assoc_sta; wid.id = WID_DEL_ALL_STA; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2298 @ return result; } -int wilc_edit_station(struct wilc_vif *vif, const u8 *mac, - struct station_parameters *params) +static void handle_edit_station(struct work_struct *work) { - struct wid wid; + struct host_if_msg *msg = container_of(work, struct host_if_msg, work); + struct wilc_vif *vif = msg->vif; + struct add_sta_param *params = &msg->body.edit_sta_info; int result; - u8 *cur_byte; + struct wid wid; wid.id = WID_EDIT_STA; wid.type = WID_BIN; - wid.size = WILC_ADD_STA_LENGTH + - params->link_sta_params.supported_rates_len; + wid.size = WILC_ADD_STA_LENGTH + params->supported_rates_len; + PRINT_INFO(vif->ndev, HOSTINF_DBG, "Handling edit station\n"); wid.val = kmalloc(wid.size, GFP_KERNEL); if (!wid.val) - return -ENOMEM; + goto error; - cur_byte = wid.val; - wilc_hif_pack_sta_param(cur_byte, mac, params); + wilc_hif_pack_sta_param(wid.val, params); result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); if (result) - netdev_err(vif->ndev, "Failed to send edit station\n"); + PRINT_ER(vif->ndev, "Failed to send edit station\n"); kfree(wid.val); +error: + kfree(params->supported_rates); + kfree(msg); +} + +int wilc_edit_station(struct wilc_vif *vif, const u8 *mac, + struct station_parameters *params) +{ + int result; + struct host_if_msg *msg; + struct add_sta_param *sta_params; + + PRINT_INFO(vif->ndev, HOSTINF_DBG, + "Setting editing station message queue params\n"); + + msg = wilc_alloc_work(vif, handle_edit_station, false); + if (IS_ERR(msg)) + return PTR_ERR(msg); + + sta_params = &msg->body.edit_sta_info; + memcpy(sta_params->bssid, mac, ETH_ALEN); + sta_params->aid = params->aid; + if (!params->link_sta_params.ht_capa) { + sta_params->ht_supported = false; + } else { + sta_params->ht_supported = true; + memcpy(&sta_params->ht_capa, params->link_sta_params.ht_capa, + sizeof(struct ieee80211_ht_cap)); + } + sta_params->flags_mask = params->sta_flags_mask; + sta_params->flags_set = params->sta_flags_set; + + sta_params->supported_rates_len = params->link_sta_params.supported_rates_len; + if (params->link_sta_params.supported_rates_len > 0) { + sta_params->supported_rates = kmemdup(params->link_sta_params.supported_rates, + params->link_sta_params.supported_rates_len, + GFP_KERNEL); + if (!sta_params->supported_rates) { + kfree(msg); + return -ENOMEM; + } + } + + result = wilc_enqueue_work(msg); + if (result) { + PRINT_ER(vif->ndev, "enqueue work failed\n"); + kfree(sta_params->supported_rates); + kfree(msg); + } + return result; } -int wilc_set_power_mgmt(struct wilc_vif *vif, bool enabled, u32 timeout) +static void handle_power_management(struct work_struct *work) { - struct wilc *wilc = vif->wilc; - struct wid wid; + struct host_if_msg *msg = container_of(work, struct host_if_msg, work); + struct wilc_vif *vif = msg->vif; + struct power_mgmt_param *pm_param = &msg->body.pwr_mgmt_info; int result; + struct wid wid; s8 power_mode; - if (enabled) + wid.id = WID_POWER_MANAGEMENT; + + if (pm_param->enabled) power_mode = WILC_FW_MIN_FAST_PS; else power_mode = WILC_FW_NO_POWERSAVE; - - wid.id = WID_POWER_MANAGEMENT; + PRINT_INFO(vif->ndev, HOSTINF_DBG, "Handling power mgmt to %d\n", + power_mode); wid.val = &power_mode; wid.size = sizeof(char); + + PRINT_INFO(vif->ndev, HOSTINF_DBG, "Handling Power Management\n"); result = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); if (result) netdev_err(vif->ndev, "Failed to send power management\n"); - else - wilc->power_save_mode = enabled; + kfree(msg); +} + +int wilc_set_power_mgmt(struct wilc_vif *vif, bool enabled, u32 timeout) +{ + int result; + struct host_if_msg *msg; + + PRINT_INFO(vif->ndev, HOSTINF_DBG, "\n\n>> Setting PS to %d <<\n\n", + enabled); + msg = wilc_alloc_work(vif, handle_power_management, false); + if (IS_ERR(msg)) + return PTR_ERR(msg); + + msg->body.pwr_mgmt_info.enabled = enabled; + msg->body.pwr_mgmt_info.timeout = timeout; + + result = wilc_enqueue_work(msg); + if (result) { + PRINT_ER(vif->ndev, "enqueue work failed\n"); + kfree(msg); + } return result; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2430 @ int result; struct host_if_msg *msg; + PRINT_INFO(vif->ndev, HOSTINF_DBG, + "Setting Multicast Filter params\n"); msg = wilc_alloc_work(vif, handle_set_mcast_filter, false); if (IS_ERR(msg)) return PTR_ERR(msg); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2460 @ return wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); } -int wilc_get_tx_power(struct wilc_vif *vif, u8 *tx_power) +static void handle_get_tx_pwr(struct work_struct *work) { + struct host_if_msg *msg = container_of(work, struct host_if_msg, work); + struct wilc_vif *vif = msg->vif; + u8 *tx_pwr = &msg->body.tx_power.tx_pwr; + int ret; struct wid wid; wid.id = WID_TX_POWER; wid.type = WID_CHAR; - wid.val = tx_power; + wid.val = tx_pwr; wid.size = sizeof(char); - return wilc_send_config_pkt(vif, WILC_GET_CFG, &wid, 1); + ret = wilc_send_config_pkt(vif, WILC_GET_CFG, &wid, 1); + if (ret) + PRINT_ER(vif->ndev, "Failed to get TX PWR\n"); + + complete(&msg->work_comp); +} + +int wilc_get_tx_power(struct wilc_vif *vif, u8 *tx_power) +{ + int ret; + struct host_if_msg *msg; + + msg = wilc_alloc_work(vif, handle_get_tx_pwr, true); + if (IS_ERR(msg)) + return PTR_ERR(msg); + + ret = wilc_enqueue_work(msg); + if (ret) { + PRINT_ER(vif->ndev, "enqueue work failed\n"); + } else { + wait_for_completion(&msg->work_comp); + *tx_power = msg->body.tx_power.tx_pwr; + } + + /* free 'msg' after copying data */ + kfree(msg); + return ret; } int wilc_set_default_mgmt_key_index(struct wilc_vif *vif, u8 index) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2518 @ return result; } + +static bool is_valid_gpio(struct wilc_vif *vif, u8 gpio) +{ + switch (vif->wilc->chip) { + case WILC_1000: + if (gpio == 0 || gpio == 1 || gpio == 4 || gpio == 6) + return true; + else + return false; + case WILC_3000: + if (gpio == 0 || gpio == 3 || gpio == 4 || + (gpio >= 17 && gpio <= 20)) + return true; + else + return false; + default: + return false; + } +} + +int wilc_set_antenna(struct wilc_vif *vif, u8 mode) +{ + struct wid wid; + int ret; + struct sysfs_attr_group *attr_sysfs_p = &vif->wilc->attr_sysfs; + struct host_if_set_ant set_ant; + + set_ant.mode = mode; + + if (attr_sysfs_p->ant_swtch_mode == ANT_SWTCH_INVALID_GPIO_CTRL) { + pr_err("Ant switch GPIO mode is invalid.\n"); + pr_err("Set it using /sys/wilc/ant_swtch_mode\n"); + return -EINVAL; + } + + if (is_valid_gpio(vif, attr_sysfs_p->antenna1)) { + set_ant.antenna1 = attr_sysfs_p->antenna1; + } else { + pr_err("Invalid GPIO %d\n", attr_sysfs_p->antenna1); + return -EINVAL; + } + + if (attr_sysfs_p->ant_swtch_mode == ANT_SWTCH_DUAL_GPIO_CTRL) { + if (attr_sysfs_p->antenna2 != attr_sysfs_p->antenna1 && + is_valid_gpio(vif, attr_sysfs_p->antenna2)) { + set_ant.antenna2 = attr_sysfs_p->antenna2; + } else { + pr_err("Invalid GPIO %d\n", attr_sysfs_p->antenna2); + return -EINVAL; + } + } + + set_ant.gpio_mode = attr_sysfs_p->ant_swtch_mode; + + wid.id = WID_ANTENNA_SELECTION; + wid.type = WID_BIN; + wid.val = (u8 *)&set_ant; + wid.size = sizeof(struct host_if_set_ant); + if (attr_sysfs_p->ant_swtch_mode == ANT_SWTCH_SNGL_GPIO_CTRL) + PRINT_INFO(vif->ndev, CFG80211_DBG, + "set antenna %d on GPIO %d\n", set_ant.mode, + set_ant.antenna1); + else if (attr_sysfs_p->ant_swtch_mode == ANT_SWTCH_DUAL_GPIO_CTRL) + PRINT_INFO(vif->ndev, CFG80211_DBG, + "set antenna %d on GPIOs %d and %d\n", + set_ant.mode, set_ant.antenna1, + set_ant.antenna2); + + ret = wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); + if (ret) + PRINT_ER(vif->ndev, "Failed to set antenna mode\n"); + + return ret; +} + +void wilc_set_fw_debug_level(struct wilc *wl, u8 dbg_level) +{ + struct wilc_vif *vif = wilc_get_wl_to_vif(wl); + struct wid wid; + + wid.id = WID_FW_PRINT_LEVEL; + wid.type = WID_CHAR; + wid.size = sizeof(char); + wid.val = &dbg_level; + wilc_send_config_pkt(vif, WILC_SET_CFG, &wid, 1); +} diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/wireless/microchip/wilc1000/hif.h linux-microchip/drivers/net/wireless/microchip/wilc1000/hif.h --- linux-6.6.51/drivers/net/wireless/microchip/wilc1000/hif.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/net/wireless/microchip/wilc1000/hif.h 2024-11-26 14:04:18.564303380 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:17 @ WILC_AP_MODE = 0x1, WILC_STATION_MODE = 0x2, WILC_GO_MODE = 0x3, - WILC_CLIENT_MODE = 0x4 + WILC_CLIENT_MODE = 0x4, + WILC_MONITOR_MODE = 0x5 }; #define WILC_MAX_NUM_PROBED_SSID 10 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:34 @ WILC_GET_CFG }; +extern uint32_t cfg_packet_timeout; + struct rf_info { u8 link_speed; s8 rssi; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:85 @ enum { WILC_HIF_SDIO = 0, - WILC_HIF_SPI = BIT(0) + WILC_HIF_SPI = BIT(0), + WILC_HIF_SDIO_GPIO_IRQ = BIT(1) }; enum { + WILC_MAC_STATUS_PRE_INIT = -2, WILC_MAC_STATUS_INIT = -1, WILC_MAC_STATUS_DISCONNECTED = 0, WILC_MAC_STATUS_CONNECTED = 1 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:103 @ struct ieee80211_mgmt *mgmt; }; +struct wilc_priv; struct wilc_user_scan_req { void (*scan_result)(enum scan_event evt, - struct wilc_rcvd_net_info *info, void *priv); - void *arg; + struct wilc_rcvd_net_info *info, + struct wilc_priv *priv); + struct wilc_priv *priv; u32 ch_cnt; }; +struct wilc_join_bss_param; struct wilc_conn_info { u8 bssid[ETH_ALEN]; u8 security; enum authtype auth_type; enum mfptype mfp_type; - u8 ch; u8 *req_ies; size_t req_ies_len; u8 *resp_ies; u16 resp_ies_len; u16 status; - void (*conn_result)(enum conn_event evt, u8 status, void *priv_data); - void *arg; - void *param; + void (*conn_result)(enum conn_event evt, u8 status, + struct wilc_priv *priv); + struct wilc_priv *priv; + struct wilc_join_bss_param *param; }; +struct wilc_vif; struct wilc_remain_ch { u16 ch; - u32 duration; - void (*expired)(void *priv, u64 cookie); - void *arg; + void (*expired)(struct wilc_vif *vif, u64 cookie); + struct wilc_vif *vif; u64 cookie; }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:162 @ }; struct wilc_vif; + +signed int wilc_send_buffered_eap(struct wilc_vif *vif, + void (*deliver_to_stack)(struct wilc_vif *, + u8 *, u32, u32, u8), + void (*eap_buf_param)(void *), u8 *buff, + unsigned int size, unsigned int pkt_offset, + void *user_arg); int wilc_add_ptk(struct wilc_vif *vif, const u8 *ptk, u8 ptk_key_len, const u8 *mac_addr, const u8 *rx_mic, const u8 *tx_mic, u8 mode, u8 cipher_mode, u8 index); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:189 @ int wilc_disconnect(struct wilc_vif *vif); int wilc_set_mac_chnl_num(struct wilc_vif *vif, u8 channel); int wilc_get_rssi(struct wilc_vif *vif, s8 *rssi_level); -int wilc_scan(struct wilc_vif *vif, u8 scan_source, u8 scan_type, - u8 *ch_freq_list, u8 ch_list_len, +int wilc_scan(struct wilc_vif *vif, u8 scan_source, + u8 scan_type, u8 *ch_freq_list, void (*scan_result_fn)(enum scan_event, - struct wilc_rcvd_net_info *, void *), - void *user_arg, struct cfg80211_scan_request *request); + struct wilc_rcvd_net_info *, + struct wilc_priv *), + struct cfg80211_scan_request *request); int wilc_hif_set_cfg(struct wilc_vif *vif, struct cfg_param_attr *cfg_param); int wilc_init(struct net_device *dev, struct host_if_drv **hif_drv_handler); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:211 @ int wilc_set_power_mgmt(struct wilc_vif *vif, bool enabled, u32 timeout); int wilc_setup_multicast_filter(struct wilc_vif *vif, u32 enabled, u32 count, u8 *mc_list); -int wilc_remain_on_channel(struct wilc_vif *vif, u64 cookie, - u32 duration, u16 chan, - void (*expired)(void *, u64), - void *user_arg); +int wilc_remain_on_channel(struct wilc_vif *vif, u64 cookie, u16 chan, + void (*expired)(struct wilc_vif *, u64)); int wilc_listen_state_expired(struct wilc_vif *vif, u64 cookie); void wilc_frame_register(struct wilc_vif *vif, u16 frame_type, bool reg); int wilc_set_operation_mode(struct wilc_vif *vif, int index, u8 mode, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:224 @ void wilc_set_wowlan_trigger(struct wilc_vif *vif, bool enabled); int wilc_set_external_auth_param(struct wilc_vif *vif, struct cfg80211_external_auth_params *param); +/* 0 select antenna 1 , 2 select antenna mode , 2 allow the firmware to choose + * the best antenna + */ +int wilc_set_antenna(struct wilc_vif *vif, u8 mode); +int handle_scan_done(struct wilc_vif *vif, enum scan_event evt); void wilc_scan_complete_received(struct wilc *wilc, u8 *buffer, u32 length); void wilc_network_info_received(struct wilc *wilc, u8 *buffer, u32 length); void wilc_gnrl_async_info_received(struct wilc *wilc, u8 *buffer, u32 length); -void *wilc_parse_join_bss_param(struct cfg80211_bss *bss, - struct cfg80211_crypto_settings *crypto); +struct wilc_join_bss_param * +wilc_parse_join_bss_param(struct cfg80211_bss *bss, + struct cfg80211_crypto_settings *crypto); int wilc_set_default_mgmt_key_index(struct wilc_vif *vif, u8 index); -void wilc_handle_disconnect(struct wilc_vif *vif); +void wilc_handle_disconnect(struct wilc_vif *vif); +int wilc_of_parse_power_pins(struct wilc *wilc); +void wilc_wlan_power(struct wilc *wilc, bool on); +void wilc_set_fw_debug_level(struct wilc *wl,u8 dbg_level); #endif diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/wireless/microchip/wilc1000/Kconfig linux-microchip/drivers/net/wireless/microchip/wilc1000/Kconfig --- linux-6.6.51/drivers/net/wireless/microchip/wilc1000/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/net/wireless/microchip/wilc1000/Kconfig 2024-11-26 14:02:38.118504803 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ # SPDX-License-Identifier: GPL-2.0 -config WILC1000 +config WILC tristate - help - Add support for the Atmel WILC1000 802.11 b/g/n SoC. - This provides Wi-FI over an SDIO or SPI interface, and - is usually found in IoT devices. - - This module only support IEEE 802.11n WiFi. -config WILC1000_SDIO - tristate "Atmel WILC1000 SDIO (WiFi only)" +config WILC_SDIO + tristate "WILC SDIO" depends on CFG80211 && INET && MMC - select WILC1000 + select WILC help This module adds support for the SDIO interface of adapters using - WILC1000 chipset. The Atmel WILC1000 SDIO is a full speed interface. + WILC1000 & WILC3000 chipset. The Atmel WILC1000 SDIO is a full speed interface. It meets SDIO card specification version 2.0. The interface supports the 1-bit/4-bit SD transfer mode at the clock range of 0-50 MHz. The host can use this interface to read and write from any register within the chip as well as configure the WILC1000 for data DMA. To use this interface, pin9 (SDIO_SPI_CFG) must be grounded. Select this if your platform is using the SDIO bus. + WILC3000 additionally supports BT 4.0 and BLE modes. -config WILC1000_SPI - tristate "Atmel WILC1000 SPI (WiFi only)" +config WILC_SPI + tristate "WILC SPI" depends on CFG80211 && INET && SPI - select WILC1000 + select WILC select CRC7 select CRC_ITU_T help This module adds support for the SPI interface of adapters using - WILC1000 chipset. The Atmel WILC1000 has a Serial Peripheral + WILC1000 & WILC3000 chipset. The Atmel WILC1000 has a Serial Peripheral Interface (SPI) that operates as a SPI slave. This SPI interface can be used for control and for serial I/O of 802.11 data. The SPI is a full-duplex slave synchronous serial interface that is available immediately following reset when pin 9 (SDIO_SPI_CFG) is tied to VDDIO. Select this if your platform is using the SPI bus. + WILC3000 additionally supports BT 4.0 and BLE modes. -config WILC1000_HW_OOB_INTR - bool "WILC1000 out of band interrupt" - depends on WILC1000_SDIO +config WILC_HW_OOB_INTR + bool "WILC out of band interrupt" + depends on WILC_SDIO + default n help - This option enables out-of-band interrupt support for the WILC1000 - chipset. This OOB interrupt is intended to provide a faster interrupt - mechanism for SDIO host controllers that don't support SDIO interrupt. - Select this option If the SDIO host controller in your platform - doesn't support SDIO time division interrupt. + This option enables out-of-band interrupt support for the WILC1000 & + WILC3000 chipset. This OOB interrupt is intended to provide a faster + interrupt mechanism for SDIO host controllers that don't support SDIO + interrupt. Select this option If the SDIO host controller in your + platform doesn't support SDIO time division interrupt. diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/wireless/microchip/wilc1000/Makefile linux-microchip/drivers/net/wireless/microchip/wilc1000/Makefile --- linux-6.6.51/drivers/net/wireless/microchip/wilc1000/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/net/wireless/microchip/wilc1000/Makefile 2024-11-26 14:02:38.118504803 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_WILC1000) += wilc1000.o +ccflags-y += -I$(src)/ -DWILC_DEBUGFS -wilc1000-objs := cfg80211.o netdev.o mon.o \ - hif.o wlan_cfg.o wlan.o +wilc-objs := cfg80211.o netdev.o mon.o \ + hif.o wlan_cfg.o wlan.o sysfs.o power.o bt.o debugfs.o wilcs02_loopback.o -obj-$(CONFIG_WILC1000_SDIO) += wilc1000-sdio.o -wilc1000-sdio-objs += sdio.o +obj-$(CONFIG_WILC_SDIO) += wilc-sdio.o +wilc-sdio-objs += $(wilc-objs) +wilc-sdio-objs += sdio.o -obj-$(CONFIG_WILC1000_SPI) += wilc1000-spi.o -wilc1000-spi-objs += spi.o +obj-$(CONFIG_WILC_SPI) += wilc-spi.o +wilc-spi-objs += $(wilc-objs) +wilc-spi-objs += spi.o diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/wireless/microchip/wilc1000/mon.c linux-microchip/drivers/net/wireless/microchip/wilc1000/mon.c --- linux-6.6.51/drivers/net/wireless/microchip/wilc1000/mon.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/net/wireless/microchip/wilc1000/mon.c 2024-11-26 14:02:38.122504875 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:34 @ if (!mon_dev) return; - if (!netif_running(mon_dev)) + if (!netif_running(mon_dev)) { + PRINT_D(mon_dev, HOSTAPD_DBG, + "Monitor interface already RUNNING\n"); return; + } /* Get WILC header */ header = get_unaligned_le32(buff - HOST_HDR_OFFSET); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:52 @ /* hostapd callback mgmt frame */ skb = dev_alloc_skb(size + sizeof(*cb_hdr)); - if (!skb) + if (!skb) { + PRINT_D(mon_dev, HOSTAPD_DBG, + "Monitor if : No memory to allocate skb"); return; - + } skb_put_data(skb, buff, size); cb_hdr = skb_push(skb, sizeof(*cb_hdr)); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:88 @ memset(hdr, 0, sizeof(struct wilc_wfi_radiotap_hdr)); hdr->hdr.it_version = 0; /* PKTHDR_RADIOTAP_VERSION; */ hdr->hdr.it_len = cpu_to_le16(sizeof(*hdr)); + PRINT_D(mon_dev, HOSTAPD_DBG, + "Radiotap len %d\n", hdr->hdr.it_len); hdr->hdr.it_present = cpu_to_le32 - (1 << IEEE80211_RADIOTAP_RATE); + (1 << IEEE80211_RADIOTAP_RATE); /* | */ + PRINT_D(mon_dev, HOSTAPD_DBG, "Presentflags %d\n", + hdr->hdr.it_present); hdr->rate = 5; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:203 @ } skb->dev = mon_priv->real_ndev; + PRINT_D(dev, HOSTAPD_DBG, "Skipping the radiotap header\n"); + PRINT_D(dev, HOSTAPD_DBG, "SKB netdevice name = %s\n", skb->dev->name); + PRINT_D(dev, HOSTAPD_DBG, "MONITOR real dev name = %s\n", + mon_priv->real_ndev->name); ether_addr_copy(srcadd, &skb->data[10]); ether_addr_copy(bssid, &skb->data[16]); /* @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:266 @ if (!wl->monitor_dev) return; + PRINT_INFO(wl->monitor_dev, HOSTAPD_DBG, + "In Deinit monitor interface\n"); + PRINT_INFO(wl->monitor_dev, HOSTAPD_DBG, "Unregister monitor netdev\n"); if (rtnl_locked) unregister_netdevice(wl->monitor_dev); else unregister_netdev(wl->monitor_dev); + PRINT_INFO(wl->monitor_dev, HOSTAPD_DBG, + "Deinit monitor interface done\n"); wl->monitor_dev = NULL; } diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/wireless/microchip/wilc1000/netdev.c linux-microchip/drivers/net/wireless/microchip/wilc1000/netdev.c --- linux-6.6.51/drivers/net/wireless/microchip/wilc1000/netdev.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/net/wireless/microchip/wilc1000/netdev.c 2024-11-26 14:04:18.564303380 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:22 @ /* latest API version supported */ #define WILC1000_API_VER 1 -#define WILC1000_FW_PREFIX "atmel/wilc1000_wifi_firmware-" +#define WILC1000_FW_PREFIX "mchp/wilc1000_wifi_firmware" #define __WILC1000_FW(api) WILC1000_FW_PREFIX #api ".bin" #define WILC1000_FW(api) __WILC1000_FW(api) +#define WILC3000_API_VER 1 + +#define WILC3000_FW_PREFIX "mchp/wilc3000_wifi_firmware" +#define __WILC3000_FW(api) WILC3000_FW_PREFIX #api ".bin" +#define WILC3000_FW(api) __WILC3000_FW(api) + +#define WILCS02_FW_PREFIX "mchp/wilcs02_wifi_firmware" +#define __WILCS02_FW(api) WILCS02_FW_PREFIX #api ".bin" +#define WILCS02_FW(api) __WILCS02_FW(api) + +#define WILC_FIRMWARE_START_TIMEOUT 500 +#define WILC_S02_FIRMWARE_START_TIMEOUT 10000 + +static int wilc_mac_open(struct net_device *ndev); +static int wilc_mac_close(struct net_device *ndev); + + +#ifdef WILC_S02_TEST_BUS_INTERFACE +static int frame_size= WILC_S02_TEST_FRAME_SIZE; /* configure the frame size of loopback */ +module_param(frame_size, int, 0644); +MODULE_PARM_DESC(frame_size, + "use to configure frame size, default 1546"); + +bool is_test_mode= false; /* enable loopback test */ +module_param(is_test_mode, bool, 0644); +MODULE_PARM_DESC(is_test_mode, + "use to enable loopback test mode, is_loopback_mode = 1 means loopback is enable, default disabled"); +#endif /* WILCS02_TEST_BUS_INTERFACE */ + +static int debug_running; +static int recovery_on; +int wait_for_recovery; +static int debug_thread(void *arg) +{ + struct wilc *wl = arg; + struct wilc_vif *vif; + signed long timeout; + struct host_if_drv *hif_drv; + int i = 0; + + complete(&wl->debug_thread_started); + + while (1) { + int srcu_idx; + int ret; + + if (!wl->initialized && !kthread_should_stop()) { + msleep(1000); + continue; + } else if (!wl->initialized) { + break; + } + ret = wait_for_completion_interruptible_timeout( + &wl->debug_thread_started, msecs_to_jiffies(6000)); + if (ret > 0) { + while (!kthread_should_stop()) + schedule(); + pr_info("Exit debug thread\n"); + return 0; + } + if (!debug_running || ret == -ERESTARTSYS) + continue; + + pr_debug("%s *** Debug Thread Running ***cnt[%d]\n", __func__, + cfg_packet_timeout); + + if (cfg_packet_timeout < 5) + continue; + + pr_info("%s <Recover>\n", __func__); + cfg_packet_timeout = 0; + timeout = 10; + recovery_on = 1; + wait_for_recovery = 1; + + srcu_idx = srcu_read_lock(&wl->srcu); + list_for_each_entry_rcu(vif, &wl->vif_list, list) { + /* close the interface only if it was open */ + if (vif->mac_opened) { + wilc_mac_close(vif->ndev); + vif->restart = 1; + } + } + + /* For Spi, clear 'is_init' flag so that protocol offset + * register can be send to FW to setup required crc mode after + * chip reset + */ + if (wl->io_type == WILC_HIF_SPI) + wl->hif_func->hif_clear_init(wl); + + //TODO://Need to find way to call them in reverse + i = 0; + list_for_each_entry_rcu(vif, &wl->vif_list, list) { + struct wilc_conn_info *info; + + /* Only open the interface manually closed earlier */ + if (!vif->restart) + continue; + i++; + hif_drv = vif->priv.hif_drv; + while (wilc_mac_open(vif->ndev) && --timeout) + msleep(100); + + if (timeout == 0) + PRINT_WRN(vif->ndev, GENERIC_DBG, + "Couldn't restart ifc %d\n", i); + + if (hif_drv->hif_state == HOST_IF_CONNECTED) { + info = &hif_drv->conn_info; + PRINT_INFO(vif->ndev, GENERIC_DBG, + "notify the user with the Disconnection\n"); + if (hif_drv->usr_scan_req.scan_result) { + PRINT_INFO(vif->ndev, GENERIC_DBG, + "Abort the running OBSS Scan\n"); + del_timer(&hif_drv->scan_timer); + handle_scan_done(vif, + SCAN_EVENT_ABORTED); + } + if (info->conn_result) { + info->conn_result(CONN_DISCONN_EVENT_DISCONN_NOTIF, + 0, info->priv); + } else { + PRINT_ER(vif->ndev, + "Connect result NULL\n"); + } + eth_zero_addr(hif_drv->assoc_bssid); + info->req_ies_len = 0; + kfree(info->req_ies); + info->req_ies = NULL; + hif_drv->hif_state = HOST_IF_IDLE; + } + vif->restart = 0; + } + srcu_read_unlock(&wl->srcu, srcu_idx); + recovery_on = 0; + } + return 0; +} + +static void wilc_disable_irq(struct wilc *wilc, int wait) +{ + if (wait) { + pr_info("%s Disabling IRQ ...\n", __func__); + disable_irq(wilc->dev_irq_num); + } else { + pr_info("%s Disabling IRQ ...\n", __func__); + disable_irq_nosync(wilc->dev_irq_num); + } +} + +static irqreturn_t host_wakeup_isr(int irq, void *user_data) +{ + return IRQ_HANDLED; +} + static irqreturn_t isr_uh_routine(int irq, void *user_data) { struct wilc *wilc = user_data; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:211 @ { struct wilc_vif *vif = netdev_priv(dev); struct wilc *wl = vif->wilc; - int ret; - ret = request_threaded_irq(wl->dev_irq_num, isr_uh_routine, - isr_bh_routine, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - dev->name, wl); - if (ret) { - netdev_err(dev, "Failed to request IRQ [%d]\n", ret); - return ret; + if (wl->dev_irq_num <= 0) + return 0; + + if (wl->io_type == WILC_HIF_SPI || + wl->io_type == WILC_HIF_SDIO_GPIO_IRQ) { + if (request_threaded_irq(wl->dev_irq_num, isr_uh_routine, + isr_bh_routine, + IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND, + dev->name, wl) < 0) { + pr_err("Failed to request IRQ [%d]\n", wl->dev_irq_num); + return -EINVAL; + } + } else { + if (request_irq(wl->dev_irq_num, host_wakeup_isr, + IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND, + dev->name, wl) < 0) { + pr_err("Failed to request IRQ [%d]\n", wl->dev_irq_num); + return -EINVAL; + } } - netdev_dbg(dev, "IRQ request succeeded IRQ-NUM= %d\n", wl->dev_irq_num); + PRINT_INFO(dev, GENERIC_DBG, "IRQ request succeeded IRQ-NUM= %d\n", + wl->dev_irq_num); + enable_irq_wake(wl->dev_irq_num); return 0; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:262 @ } } -static struct net_device *get_if_handler(struct wilc *wilc, u8 *mac_header) +static void free_eap_buff_params(void *vp) { - struct net_device *ndev = NULL; - struct wilc_vif *vif; - struct ieee80211_hdr *h = (struct ieee80211_hdr *)mac_header; + struct wilc_priv *priv; - list_for_each_entry_rcu(vif, &wilc->vif_list, list) { - if (vif->iftype == WILC_STATION_MODE) - if (ether_addr_equal_unaligned(h->addr2, vif->bssid)) { - ndev = vif->ndev; - goto out; - } - if (vif->iftype == WILC_AP_MODE) - if (ether_addr_equal_unaligned(h->addr1, vif->bssid)) { - ndev = vif->ndev; - goto out; - } + priv = (struct wilc_priv *)vp; + + if (priv->buffered_eap) { + kfree(priv->buffered_eap->buff); + priv->buffered_eap->buff = NULL; + + kfree(priv->buffered_eap); + priv->buffered_eap = NULL; + } +} + +void eap_buff_timeout(struct timer_list *t) +{ + u8 null_bssid[ETH_ALEN] = {0}; + u8 *assoc_bss; + static u8 timeout = 5; + int status = -1; + struct wilc_priv *priv = from_timer(priv, t, eap_buff_timer); + struct wilc_vif *vif = netdev_priv(priv->dev); + + assoc_bss = priv->associated_bss; + if (!(memcmp(assoc_bss, null_bssid, ETH_ALEN)) && (timeout-- > 0)) { + mod_timer(&priv->eap_buff_timer, + (jiffies + msecs_to_jiffies(10))); + return; } -out: - return ndev; + del_timer(&priv->eap_buff_timer); + timeout = 5; + + status = wilc_send_buffered_eap(vif, wilc_frmw_to_host, + free_eap_buff_params, + priv->buffered_eap->buff, + priv->buffered_eap->size, + priv->buffered_eap->pkt_offset, + (void *)priv); + if (status) + PRINT_ER(vif->ndev, "Failed so send buffered eap\n"); } void wilc_wlan_set_bssid(struct net_device *wilc_netdev, const u8 *bssid, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:341 @ complete(&wl->txq_thread_started); while (1) { - wait_for_completion(&wl->txq_event); + struct wilc_vif *vif = wilc_get_wl_to_vif(wl); + struct net_device *ndev = vif->ndev; + PRINT_INFO(ndev, TX_DBG, "txq_task Taking a nap\n"); + if (wait_for_completion_interruptible(&wl->txq_event)) + continue; + PRINT_INFO(ndev, TX_DBG, "txq_task Who waked me up\n"); if (wl->close) { complete(&wl->txq_thread_started); while (!kthread_should_stop()) schedule(); + PRINT_INFO(ndev, TX_DBG, "TX thread stopped\n"); break; } + PRINT_INFO(ndev, TX_DBG, "handle the tx packet\n"); do { ret = wilc_wlan_handle_txq(wl, &txq_count); if (txq_count < FLOW_CONTROL_LOWER_THRESHOLD) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:366 @ srcu_idx = srcu_read_lock(&wl->srcu); list_for_each_entry_rcu(ifc, &wl->vif_list, list) { - if (ifc->mac_opened && ifc->ndev) + if (ifc->mac_opened && + netif_queue_stopped(ifc->ndev)) netif_wake_queue(ifc->ndev); } srcu_read_unlock(&wl->srcu, srcu_idx); } - } while (ret == WILC_VMM_ENTRY_FULL_RETRY && !wl->close); + if (ret != WILC_VMM_ENTRY_FULL_RETRY) + break; + /* Back off TX task from sending packets for some time. + * msleep_interruptible will allow RX task to run and + * free buffers. TX task will be in TASK_INTERRUPTIBLE + * state which will put the thread back to CPU running + * queue when it's signaled even if the timeout isn't + * elapsed. This gives faster chance for reserved SK + * buffers to be free. + */ + msleep_interruptible(TX_BACKOFF_WEIGHT_MS); + } while (!wl->close); } return 0; } -static int wilc_wlan_get_firmware(struct net_device *dev) +static int wilc_wlan_get_firmware(struct wilc *wilc) { - struct wilc_vif *vif = netdev_priv(dev); - struct wilc *wilc = vif->wilc; - int chip_id; const struct firmware *wilc_fw; + char *firmware; int ret; - chip_id = wilc_get_chipid(wilc, false); - - netdev_info(dev, "ChipID [%x] loading firmware [%s]\n", chip_id, - WILC1000_FW(WILC1000_API_VER)); + if (wilc->chip == WILC_3000) { + pr_info("Detect chip WILC3000\n"); + firmware = WILC3000_FW(); + } else if (wilc->chip == WILC_1000) { + pr_info("Detect chip WILC1000\n"); + firmware = WILC1000_FW(); + } else if (wilc->chip == WILC_S02) { + dev_dbg(wilc->dev, "Detect chip WILCS02\n"); + firmware = WILCS02_FW(); + } else { + return -EINVAL; + } + pr_info("loading firmware %s\n", firmware); - ret = request_firmware(&wilc_fw, WILC1000_FW(WILC1000_API_VER), - wilc->dev); + ret = request_firmware(&wilc_fw, firmware, wilc->dev); if (ret != 0) { - netdev_err(dev, "%s - firmware not available\n", - WILC1000_FW(WILC1000_API_VER)); + if (wilc->chip != WILC_S02) { + pr_err("%s - firmware not available\n", firmware); + } else { + pr_err("Use pre-installed firmware for WILC-SO2 LinkController\n"); + } return -EINVAL; } wilc->firmware = wilc_fw; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:427 @ struct wilc_vif *vif = netdev_priv(dev); struct wilc *wilc = vif->wilc; int ret = 0; + unsigned int m; - ret = wilc_wlan_start(wilc); - if (ret) + PRINT_INFO(vif->ndev, INIT_DBG, "Starting Firmware ...\n"); + + if (wilc->chip == WILC_S02) { + ret = wilc_wlan_start_wilcs02_fw(wilc); + m = WILC_S02_FIRMWARE_START_TIMEOUT; + } else { + ret = wilc_wlan_start(wilc); + m = WILC_FIRMWARE_START_TIMEOUT; + } + if (ret < 0) { + PRINT_ER(dev, "Failed to start Firmware\n"); return ret; + } - if (!wait_for_completion_timeout(&wilc->sync_event, - msecs_to_jiffies(5000))) + PRINT_INFO(vif->ndev, INIT_DBG, "Waiting for FW to get ready ...\n"); +#ifdef WILC_S02_TEST_BUS_INTERFACE + if (is_test_mode) + msleep(5000); + if (!is_test_mode) +#endif + { + if (!wait_for_completion_timeout(&wilc->sync_event, + msecs_to_jiffies(m))) { + PRINT_INFO(vif->ndev, INIT_DBG, "Firmware start timed out\n"); return -ETIME; + } + if (wilc->mac_status == WILC_MAC_STATUS_PRE_INIT) + return -ETIME; + } + PRINT_INFO(vif->ndev, INIT_DBG, "Firmware successfully started\n"); + wilc->initialized = 1; return 0; } -static int wilc1000_firmware_download(struct net_device *dev) +static int wilc_firmware_download(struct wilc *wilc) { - struct wilc_vif *vif = netdev_priv(dev); - struct wilc *wilc = vif->wilc; - int ret = 0; + int ret; if (!wilc->firmware) { - netdev_err(dev, "Firmware buffer is NULL\n"); - return -ENOBUFS; + pr_err("Firmware buffer is NULL\n"); + ret = -ENOBUFS; } - + pr_info("Downloading Firmware ...\n"); ret = wilc_wlan_firmware_download(wilc, wilc->firmware->data, wilc->firmware->size); if (ret) - return ret; + goto fail; + pr_info("Download Succeeded\n"); + +fail: release_firmware(wilc->firmware); wilc->firmware = NULL; - netdev_dbg(dev, "Download Succeeded\n"); - - return 0; + return ret; } static int wilc_init_fw_config(struct net_device *dev, struct wilc_vif *vif) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:517 @ if (!wilc_wlan_cfg_set(vif, 0, WID_11G_OPERATING_MODE, &b, 1, 0, 0)) goto fail; - b = WILC_FW_PREAMBLE_SHORT; + b = WILC_FW_PREAMBLE_AUTO; if (!wilc_wlan_cfg_set(vif, 0, WID_PREAMBLE, &b, 1, 0, 0)) goto fail; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:647 @ if (!wilc_wlan_cfg_set(vif, 0, WID_11N_CURRENT_TX_MCS, &b, 1, 0, 0)) goto fail; + b = vif->wilc->attr_sysfs.fw_dbg_level; + if (!wilc_wlan_cfg_set(vif, 0, WID_FW_PRINT_LEVEL, &b, 1, 0, 0)) + goto fail; + b = 1; if (!wilc_wlan_cfg_set(vif, 0, WID_11N_IMMEDIATE_BA_ENABLED, &b, 1, - 1, 1)) + 1, 0)) goto fail; return 0; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:667 @ struct wilc_vif *vif = netdev_priv(dev); struct wilc *wl = vif->wilc; + PRINT_INFO(vif->ndev, INIT_DBG, "Deinitializing Threads\n"); + if (!recovery_on) { + PRINT_INFO(vif->ndev, INIT_DBG, "Deinit debug Thread\n"); + debug_running = false; + complete(&wl->debug_thread_started); + if (wl->debug_thread) { + kthread_stop(wl->debug_thread); + wl->debug_thread = NULL; + } + } + wl->close = 1; + PRINT_INFO(vif->ndev, INIT_DBG, "Deinitializing Threads\n"); complete(&wl->txq_event); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:691 @ static void wilc_wlan_deinitialize(struct net_device *dev) { + int ret; struct wilc_vif *vif = netdev_priv(dev); struct wilc *wl = vif->wilc; if (!wl) { - netdev_err(dev, "wl is NULL\n"); + PRINT_ER(dev, "wl is NULL\n"); return; } if (wl->initialized) { - netdev_info(dev, "Deinitializing wilc1000...\n"); + PRINT_INFO(vif->ndev, INIT_DBG, "Deinitializing wilc ...\n"); - if (!wl->dev_irq_num && - wl->hif_func->disable_interrupt) { - mutex_lock(&wl->hif_cs); - wl->hif_func->disable_interrupt(wl); - mutex_unlock(&wl->hif_cs); + PRINT_D(vif->ndev, INIT_DBG, "destroy aging timer\n"); + + PRINT_INFO(vif->ndev, INIT_DBG, "Disabling IRQ\n"); + if (wl->io_type == WILC_HIF_SPI || + wl->io_type == WILC_HIF_SDIO_GPIO_IRQ) { + wilc_disable_irq(wl, 1); + } else { + if (wl->hif_func->disable_interrupt) { + mutex_lock(&wl->hif_cs); + wl->hif_func->disable_interrupt(wl); + mutex_unlock(&wl->hif_cs); + } } complete(&wl->txq_event); + PRINT_INFO(vif->ndev, INIT_DBG, "Deinitializing Threads\n"); wlan_deinitialize_threads(dev); + PRINT_INFO(vif->ndev, INIT_DBG, "Deinitializing IRQ\n"); deinit_irq(dev); - wilc_wlan_stop(wl, vif); + ret = wilc_wlan_stop(wl, vif); + if (ret != 0) + PRINT_ER(dev, "failed in wlan_stop\n"); + + PRINT_INFO(vif->ndev, INIT_DBG, "Deinitializing WILC Wlan\n"); wilc_wlan_cleanup(dev); wl->initialized = false; - netdev_dbg(dev, "wilc1000 deinitialization Done\n"); + PRINT_INFO(dev, INIT_DBG, "wilc deinitialization Done\n"); } else { - netdev_dbg(dev, "wilc1000 is not initialized\n"); + PRINT_INFO(dev, INIT_DBG, "wilc is not initialized\n"); } } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:743 @ struct wilc_vif *vif = netdev_priv(dev); struct wilc *wilc = vif->wilc; + PRINT_INFO(vif->ndev, INIT_DBG, "Initializing Threads ...\n"); + PRINT_INFO(vif->ndev, INIT_DBG, "Creating kthread for transmission\n"); wilc->txq_thread = kthread_run(wilc_txq_task, (void *)wilc, "%s-tx", dev->name); if (IS_ERR(wilc->txq_thread)) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:754 @ } wait_for_completion(&wilc->txq_thread_started); + if (!debug_running) { + PRINT_INFO(vif->ndev, INIT_DBG, + "Creating kthread for Debugging\n"); + wilc->debug_thread = kthread_run(debug_thread, (void *)wilc, + "WILC_DEBUG"); + if (IS_ERR(wilc->debug_thread)) { + PRINT_ER(dev, "couldn't create debug thread\n"); + wilc->close = 1; + kthread_stop(wilc->txq_thread); + return PTR_ERR(wilc->debug_thread); + } + debug_running = true; + wait_for_completion(&wilc->debug_thread_started); + } + return 0; } +int wilc_s02_reset_firmware(struct wilc *wilc, u32 type) +{ + int ret; + + ret = wilc->hif_func->hif_write_reg(wilc, + wilc->vmm_ctl.host_vmm_tx_ctl, + type); + if (ret) + pr_err("fail to write reg host_vmm_tx_ctl"); + + return ret; +} + +int wilc_s02_check_firmware_download(struct wilc *wl) +{ + int ret; + + ret = wilc_wlan_get_firmware(wl); + if (ret) { + pr_err("FW file doesn't exist so proceed with pre installed image"); + return 0; + } + + ret = wilc_firmware_download(wl); + if (ret) + pr_err("Failed to download firmware"); + + return ret; +} + static int wilc_wlan_initialize(struct net_device *dev, struct wilc_vif *vif) { int ret = 0; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:819 @ if (ret) goto fail_wilc_wlan; - if (wl->dev_irq_num && init_irq(dev)) { - ret = -EIO; + ret = init_irq(dev); + if (ret) goto fail_threads; - } - if (!wl->dev_irq_num && - wl->hif_func->enable_interrupt && + if (wl->io_type == WILC_HIF_SDIO && wl->hif_func->enable_interrupt(wl)) { ret = -EIO; goto fail_irq_init; } - ret = wilc_wlan_get_firmware(dev); - if (ret) - goto fail_irq_enable; - - ret = wilc1000_firmware_download(dev); - if (ret) - goto fail_irq_enable; + if (wl->chip != WILC_S02) { + ret = wilc_wlan_get_firmware(wl); + if (ret) { + PRINT_ER(dev, "Can't get firmware\n"); + goto fail_irq_enable; + } + ret = wilc_firmware_download(wl); + if (ret) { + PRINT_ER(dev, "Failed to download firmware\n"); + goto fail_irq_enable; + } + } ret = wilc_start_firmware(dev); - if (ret) + if (ret) { + PRINT_ER(dev, "Failed to start firmware\n"); goto fail_irq_enable; - - if (wilc_wlan_cfg_get(vif, 1, WID_FIRMWARE_VERSION, 1, 0)) { - int size; - char firmware_ver[WILC_MAX_FW_VERSION_STR_SIZE]; - - size = wilc_wlan_cfg_get_val(wl, WID_FIRMWARE_VERSION, - firmware_ver, - sizeof(firmware_ver)); - firmware_ver[size] = '\0'; - netdev_dbg(dev, "Firmware Ver = %s\n", firmware_ver); } +#ifdef WILC_S02_TEST_BUS_INTERFACE + if (!is_test_mode) +#endif + { + if (wilc_wlan_cfg_get(vif, 1, WID_FIRMWARE_VERSION, 1, 0)) { + int size; + char firmware_ver[WILC_MAX_FW_VERSION_STR_SIZE]; + + size = wilc_wlan_cfg_get_val(wl, WID_FIRMWARE_VERSION, + firmware_ver, + sizeof(firmware_ver)); + firmware_ver[size] = '\0'; + PRINT_INFO(dev, INIT_DBG, "WILC Firmware Ver = %s\n", + firmware_ver); + } - ret = wilc_init_fw_config(dev, vif); - if (ret) { - netdev_err(dev, "Failed to configure firmware\n"); - goto fail_fw_start; + ret = wilc_init_fw_config(dev, vif); + if (ret < 0) { + netdev_err(dev, "Failed to configure firmware\n"); + ret = -EIO; + goto fail_fw_start; + } } wl->initialized = true; return 0; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:877 @ wilc_wlan_stop(wl, vif); fail_irq_enable: - if (!wl->dev_irq_num && - wl->hif_func->disable_interrupt) + if (wl->io_type == WILC_HIF_SDIO) wl->hif_func->disable_interrupt(wl); fail_irq_init: - if (wl->dev_irq_num) - deinit_irq(dev); + deinit_irq(dev); + fail_threads: wlan_deinitialize_threads(dev); fail_wilc_wlan: wilc_wlan_cleanup(dev); netdev_err(dev, "WLAN initialization FAILED\n"); } else { - netdev_dbg(dev, "wilc1000 already initialized\n"); + PRINT_WRN(vif->ndev, INIT_DBG, "wilc already initialized\n"); } return ret; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:914 @ return -ENODEV; } - netdev_dbg(ndev, "MAC OPEN[%p]\n", ndev); + PRINT_INFO(ndev, INIT_DBG, "MAC OPEN[%p] %s\n", ndev, ndev->name); - ret = wilc_init_host_int(ndev); - if (ret) - return ret; + if (wl->open_ifcs == 0) + wilc_bt_power_up(wl, DEV_WIFI); + if (!recovery_on) { + ret = wilc_init_host_int(ndev); + if (ret < 0) { + PRINT_ER(ndev, "Failed to initialize host interface\n"); + return ret; + } + } + + PRINT_INFO(vif->ndev, INIT_DBG, "*** re-init ***\n"); ret = wilc_wlan_initialize(ndev, vif); if (ret) { - wilc_deinit_host_int(ndev); + PRINT_ER(ndev, "Failed to initialize wilc\n"); + if (!recovery_on) + wilc_deinit_host_int(ndev); return ret; } - wilc_set_operation_mode(vif, wilc_get_vif_idx(vif), vif->iftype, +#ifdef WILC_S02_TEST_BUS_INTERFACE + if (!is_test_mode) +#endif + { + wait_for_recovery = 0; + wilc_set_operation_mode(vif, wilc_get_vif_idx(vif), vif->iftype, vif->idx); - if (is_valid_ether_addr(ndev->dev_addr)) { - ether_addr_copy(addr, ndev->dev_addr); - wilc_set_mac_address(vif, addr); - } else { - wilc_get_mac_address(vif, addr); - eth_hw_addr_set(ndev, addr); - } - netdev_dbg(ndev, "Mac address: %pM\n", ndev->dev_addr); + if (is_valid_ether_addr(ndev->dev_addr)) { + ether_addr_copy(addr, ndev->dev_addr); + wilc_set_mac_address(vif, addr); + } else { + wilc_get_mac_address(vif, addr); + eth_hw_addr_set(ndev, addr); + } + netdev_dbg(ndev, "Mac address: %pM\n", ndev->dev_addr); + if (!is_valid_ether_addr(ndev->dev_addr)) { + PRINT_ER(ndev, "Mac address is not configured"); + wilc_deinit_host_int(ndev); + wilc_wlan_deinitialize(ndev); + return -EINVAL; + } - if (!is_valid_ether_addr(ndev->dev_addr)) { - netdev_err(ndev, "Wrong MAC address\n"); - wilc_deinit_host_int(ndev); - wilc_wlan_deinitialize(ndev); - return -EINVAL; + mgmt_regs.interface_stypes = vif->mgmt_reg_stypes; + /* so we detect a change */ + vif->mgmt_reg_stypes = 0; + wilc_update_mgmt_frame_registrations(vif->ndev->ieee80211_ptr->wiphy, + vif->ndev->ieee80211_ptr, + &mgmt_regs); + netif_wake_queue(ndev); } - - mgmt_regs.interface_stypes = vif->mgmt_reg_stypes; - /* so we detect a change */ - vif->mgmt_reg_stypes = 0; - wilc_update_mgmt_frame_registrations(vif->ndev->ieee80211_ptr->wiphy, - vif->ndev->ieee80211_ptr, - &mgmt_regs); - netif_wake_queue(ndev); +#ifdef WILC_S02_TEST_BUS_INTERFACE + if (is_test_mode) { + //wilc_test_bus_interface(vif, WILC_S02_TEST_FRAME_SIZE, WILC_S02_TEST_FRAME_COUNT); + wilc_test_bus_interface(vif, frame_size, 1); + } +#endif wl->open_ifcs++; vif->mac_opened = 1; return 0; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:995 @ struct wilc_vif *tmp_vif; int srcu_idx; - if (!is_valid_ether_addr(addr->sa_data)) + if (!is_valid_ether_addr(addr->sa_data)) { + PRINT_INFO(vif->ndev, INIT_DBG, "Invalid MAC address\n"); return -EADDRNOTAVAIL; + } if (!vif->mac_opened) { eth_commit_mac_addr_change(dev, p); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1006 @ } /* Verify MAC Address is not already in use: */ - srcu_idx = srcu_read_lock(&wilc->srcu); list_for_each_entry_rcu(tmp_vif, &wilc->vif_list, list) { wilc_get_mac_address(tmp_vif, mac_addr); if (ether_addr_equal(addr->sa_data, mac_addr)) { if (vif != tmp_vif) { + PRINT_INFO(vif->ndev, INIT_DBG, + "MAC address is already in use\n"); srcu_read_unlock(&wilc->srcu, srcu_idx); return -EADDRNOTAVAIL; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1022 @ } srcu_read_unlock(&wilc->srcu, srcu_idx); + /* configure new MAC address */ result = wilc_set_mac_address(vif, (u8 *)addr->sa_data); if (result) return result; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1039 @ u8 *mc_list; u8 *cur_mc; - if (dev->flags & IFF_PROMISC) +#ifdef WILC_S02_TEST_BUS_INTERFACE + if (is_test_mode){ + return; + } +#endif + PRINT_INFO(vif->ndev, INIT_DBG, + "Setting mcast List with count = %d.\n", dev->mc.count); + if (dev->flags & IFF_PROMISC) { + PRINT_INFO(vif->ndev, INIT_DBG, + "Set promiscuous mode ON retrieve all pkts\n"); return; + } if (dev->flags & IFF_ALLMULTI || dev->mc.count > WILC_MULTICAST_TABLE_SIZE) { + PRINT_INFO(vif->ndev, INIT_DBG, + "Disable mcast filter retrieve multicast pkts\n"); wilc_setup_multicast_filter(vif, 0, 0, NULL); return; } if (dev->mc.count == 0) { + PRINT_INFO(vif->ndev, INIT_DBG, + "Enable mcast filter retrieve directed pkts only\n"); wilc_setup_multicast_filter(vif, 1, 0, NULL); return; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1099 @ struct tx_complete_data *tx_data = NULL; int queue_count; + PRINT_INFO(vif->ndev, TX_DBG, + "Sending packet just received from TCP/IP\n"); if (skb->dev != ndev) { netdev_err(ndev, "Packet not destined to this device\n"); dev_kfree_skb(skb); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1109 @ tx_data = kmalloc(sizeof(*tx_data), GFP_ATOMIC); if (!tx_data) { + PRINT_ER(ndev, "Failed to alloc memory for tx_data struct\n"); dev_kfree_skb(skb); netif_wake_queue(ndev); return NETDEV_TX_OK; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1119 @ tx_data->size = skb->len; tx_data->skb = skb; + PRINT_D(vif->ndev, TX_DBG, "Sending pkt Size= %d Add= %p SKB= %p\n", + tx_data->size, tx_data->buff, tx_data->skb); + PRINT_D(vif->ndev, TX_DBG, "Adding tx pkt to TX Queue\n"); vif->netstats.tx_packets++; vif->netstats.tx_bytes += tx_data->size; queue_count = wilc_wlan_txq_add_net_pkt(ndev, tx_data, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1148 @ struct wilc_vif *vif = netdev_priv(ndev); struct wilc *wl = vif->wilc; - netdev_dbg(ndev, "Mac close\n"); + PRINT_INFO(vif->ndev, GENERIC_DBG, "Mac close\n"); - if (wl->open_ifcs > 0) + if (wl->open_ifcs > 0) { wl->open_ifcs--; - else + } else { + PRINT_ER(ndev, "MAC close called with no opened interfaces\n"); return 0; + } + + if (wl->open_ifcs == 0) + wl->close = 1; if (vif->ndev) { netif_stop_queue(vif->ndev); wilc_handle_disconnect(vif); - wilc_deinit_host_int(vif->ndev); + + if (!recovery_on) + wilc_deinit_host_int(vif->ndev); } if (wl->open_ifcs == 0) { - netdev_dbg(ndev, "Deinitializing wilc1000\n"); - wl->close = 1; + netdev_dbg(ndev, "Deinitializing wilc\n"); wilc_wlan_deinitialize(ndev); } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1179 @ return 0; } -void wilc_frmw_to_host(struct wilc *wilc, u8 *buff, u32 size, - u32 pkt_offset) +void wilc_frmw_to_host(struct wilc_vif *vif, u8 *buff, u32 size, + u32 pkt_offset, u8 status) { unsigned int frame_len = 0; int stats; unsigned char *buff_to_send = NULL; struct sk_buff *skb; - struct net_device *wilc_netdev; - struct wilc_vif *vif; + struct wilc_priv *priv; + u8 null_bssid[ETH_ALEN] = {0}; - if (!wilc) - return; + buff += pkt_offset; + priv = &vif->priv; - wilc_netdev = get_if_handler(wilc, buff); - if (!wilc_netdev) + if (size == 0) { + pr_err("Discard sending packet with len = %d\n", size); return; + } - buff += pkt_offset; - vif = netdev_priv(wilc_netdev); + frame_len = size; + buff_to_send = buff; - if (size > 0) { - frame_len = size; - buff_to_send = buff; - - skb = dev_alloc_skb(frame_len); - if (!skb) - return; - - skb->dev = wilc_netdev; - - skb_put_data(skb, buff_to_send, frame_len); - - skb->protocol = eth_type_trans(skb, wilc_netdev); - vif->netstats.rx_packets++; - vif->netstats.rx_bytes += frame_len; - skb->ip_summed = CHECKSUM_UNNECESSARY; - stats = netif_rx(skb); - netdev_dbg(wilc_netdev, "netif_rx ret value is: %d\n", stats); + if (status == PKT_STATUS_NEW && buff_to_send[12] == 0x88 && + buff_to_send[13] == 0x8e && + (vif->iftype == WILC_STATION_MODE || + vif->iftype == WILC_CLIENT_MODE) && + ether_addr_equal_unaligned(priv->associated_bss, null_bssid)) { + if (!priv->buffered_eap) { + priv->buffered_eap = kmalloc(sizeof(struct + wilc_buffered_eap), + GFP_ATOMIC); + if (priv->buffered_eap) { + priv->buffered_eap->buff = NULL; + priv->buffered_eap->size = 0; + priv->buffered_eap->pkt_offset = 0; + } else { + pr_err("failed to alloc buffered_eap\n"); + return; + } + } else { + kfree(priv->buffered_eap->buff); + } + priv->buffered_eap->buff = kmalloc(size + pkt_offset, + GFP_ATOMIC); + priv->buffered_eap->size = size; + priv->buffered_eap->pkt_offset = pkt_offset; + memcpy(priv->buffered_eap->buff, buff - + pkt_offset, size + pkt_offset); + mod_timer(&priv->eap_buff_timer, (jiffies + + msecs_to_jiffies(10))); + return; + } + skb = dev_alloc_skb(frame_len); + if (!skb) { + return; } + + skb->dev = vif->ndev; + skb_put_data(skb, buff_to_send, frame_len); + + skb->protocol = eth_type_trans(skb, vif->ndev); + vif->netstats.rx_packets++; + vif->netstats.rx_bytes += frame_len; + skb->ip_summed = CHECKSUM_UNNECESSARY; + stats = netif_rx(skb); } void wilc_wfi_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size, bool is_auth) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1264 @ break; } - if (vif->priv.p2p_listen_state && - vif->mgmt_reg_stypes & type_bit) + if (vif->mgmt_reg_stypes & type_bit && + vif->p2p_listen_state) wilc_wfi_p2p_rx(vif, buff, size); + if (vif->monitor_flag) wilc_wfi_monitor_rx(wilc->monitor_dev, buff, size); } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1287 @ void wilc_netdev_cleanup(struct wilc *wilc) { - struct wilc_vif *vif, *vif_tmp; + struct wilc_vif *vif; + int srcu_idx, ifc_cnt = 0; if (!wilc) return; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1298 @ wilc->firmware = NULL; } - list_for_each_entry_safe(vif, vif_tmp, &wilc->vif_list, list) { - mutex_lock(&wilc->vif_mutex); - list_del_rcu(&vif->list); - wilc->vif_num--; - mutex_unlock(&wilc->vif_mutex); - synchronize_srcu(&wilc->srcu); + srcu_idx = srcu_read_lock(&wilc->srcu); + list_for_each_entry_rcu(vif, &wilc->vif_list, list) { if (vif->ndev) unregister_netdev(vif->ndev); } + srcu_read_unlock(&wilc->srcu, srcu_idx); wilc_wfi_deinit_mon_interface(wilc, false); destroy_workqueue(wilc->hif_workqueue); + wilc->hif_workqueue = NULL; + + while (ifc_cnt < WILC_NUM_CONCURRENT_IFC) { + mutex_lock(&wilc->vif_mutex); + if (wilc->vif_num <= 0) { + mutex_unlock(&wilc->vif_mutex); + break; + } + vif = wilc_get_wl_to_vif(wilc); + if (!IS_ERR(vif)) + list_del_rcu(&vif->list); + + wilc->vif_num--; + mutex_unlock(&wilc->vif_mutex); + synchronize_srcu(&wilc->srcu); + ifc_cnt++; + } wilc_wlan_cfg_deinit(wilc); +#ifdef WILC_DEBUGFS + wilc_debugfs_remove(); +#endif + wilc_sysfs_exit(); wlan_deinit_locks(wilc); wiphy_unregister(wilc->wiphy); + pr_info("Freeing wiphy\n"); wiphy_free(wilc->wiphy); + pr_info("Module_exit Done.\n"); } -EXPORT_SYMBOL_GPL(wilc_netdev_cleanup); static u8 wilc_get_available_idx(struct wilc *wl) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1377 @ SET_NETDEV_DEV(ndev, wiphy_dev(wl->wiphy)); + vif->ndev->ml_priv = vif; vif->priv.wdev.wiphy = wl->wiphy; vif->priv.wdev.netdev = ndev; vif->priv.wdev.iftype = type; vif->priv.dev = ndev; + vif->priv.dev = ndev; if (rtnl_locked) ret = cfg80211_register_netdevice(ndev); else @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1415 @ return ERR_PTR(ret); } +MODULE_DESCRIPTION("Atmel WILC1000 core wireless driver"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(WILC1000_FW(WILC1000_API_VER)); +MODULE_FIRMWARE(WILC3000_FW(WILC3000_API_VER)); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/wireless/microchip/wilc1000/netdev.h linux-microchip/drivers/net/wireless/microchip/wilc1000/netdev.h --- linux-6.6.51/drivers/net/wireless/microchip/wilc1000/netdev.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/net/wireless/microchip/wilc1000/netdev.h 2024-11-26 14:04:18.564303380 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:20 @ #include "hif.h" #include "wlan.h" #include "wlan_cfg.h" +#include "wilcs02_loopback.h" + +extern int wait_for_recovery; #define FLOW_CONTROL_LOWER_THRESHOLD 128 #define FLOW_CONTROL_UPPER_THRESHOLD 256 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:33 @ #define TCP_ACK_FILTER_LINK_SPEED_THRESH 54 #define DEFAULT_LINK_SPEED 72 +#define TX_BACKOFF_WEIGHT_MS 1 + +#define ANT_SWTCH_INVALID_GPIO_CTRL 0 +#define ANT_SWTCH_SNGL_GPIO_CTRL 1 +#define ANT_SWTCH_DUAL_GPIO_CTRL 2 + struct wilc_wfi_stats { unsigned long rx_packets; unsigned long tx_packets; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:68 @ u64 listen_cookie; }; +/* Struct to buffer eapol 1/4 frame */ +struct wilc_buffered_eap { + unsigned int size; + unsigned int pkt_offset; + u8 *buff; +}; + +struct wilc_p2p_var { + u8 local_random; + u8 recv_random; + bool is_wilc_ie; +}; + static const u32 wilc_cipher_suites[] = { WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_CCMP, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:159 @ /* mutexes */ struct mutex scan_req_lock; - bool p2p_listen_state; - int scanned_cnt; + struct wilc_buffered_eap *buffered_eap; + + struct timer_list eap_buff_timer; + int scanned_cnt; + struct wilc_p2p_var p2p; u64 inc_roc_cookie; }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:194 @ bool enabled; }; +#define WILC_P2P_ROLE_GO 1 +#define WILC_P2P_ROLE_CLIENT 0 + +struct sysfs_attr_group { + bool p2p_mode; + u8 ant_swtch_mode; + u8 antenna1; + u8 antenna2; + u8 fw_dbg_level; +}; + struct wilc_vif { u8 idx; u8 iftype; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:216 @ u8 bssid[ETH_ALEN]; struct host_if_drv *hif_drv; struct net_device *ndev; - struct timer_list during_ip_timer; + struct timer_list periodic_rssi; struct rf_info periodic_stat; struct tcp_ack_filter ack_filter; bool connecting; struct wilc_priv priv; struct list_head list; + u8 restart; + bool p2p_listen_state; struct cfg80211_bss *bss; struct cfg80211_external_auth_params auth; }; +struct wilc_power_gpios { + int reset; + int chip_en; +}; + +struct wilc_power { + struct wilc_power_gpios gpios; + u8 status[DEV_MAX]; +}; + struct wilc_tx_queue_status { u8 buffer[AC_BUFFER_SIZE]; u16 end_index; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:255 @ struct clk *rtc_clk; bool initialized; u32 chipid; - bool power_save_mode; int dev_irq_num; int close; u8 vif_num; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:281 @ struct completion sync_event; struct completion txq_event; struct completion txq_thread_started; - + struct completion debug_thread_started; struct task_struct *txq_thread; + struct task_struct *debug_thread; int quit; + bool is_mmc_spi; + /* lock to protect issue of wid command to firmware */ struct mutex cfg_cmd_lock; struct wilc_cfg_frame cfg_frame; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:309 @ const struct firmware *firmware; struct device *dev; - bool suspend_event; + struct device *dt_dev; + enum wilc_chip_type chip; + struct wilc_vmm_ctl vmm_ctl; + struct wilc_power power; + uint8_t keep_awake[DEV_MAX]; + struct mutex cs; struct workqueue_struct *hif_workqueue; struct wilc_cfg cfg; void *bus_data; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:325 @ struct mutex deinit_lock; u8 sta_ch; u8 op_ch; + struct sysfs_attr_group attr_sysfs; struct ieee80211_channel channels[ARRAY_SIZE(wilc_2ghz_channels)]; struct ieee80211_rate bitrates[ARRAY_SIZE(wilc_bitrates)]; struct ieee80211_supported_band band; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:336 @ struct net_device *real_ndev; }; -void wilc_frmw_to_host(struct wilc *wilc, u8 *buff, u32 size, u32 pkt_offset); +#ifdef WILC_S02_TEST_BUS_INTERFACE +extern bool is_test_mode; +#endif + +void wilc_frmw_to_host(struct wilc_vif *vif, u8 *buff, u32 size, + u32 pkt_offset, u8 status); void wilc_mac_indicate(struct wilc *wilc); void wilc_netdev_cleanup(struct wilc *wilc); void wilc_wfi_mgmt_rx(struct wilc *wilc, u8 *buff, u32 size, bool is_auth); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:350 @ struct wilc_vif *wilc_netdev_ifc_init(struct wilc *wl, const char *name, int vif_type, enum nl80211_iftype type, bool rtnl_locked); +int wilc_bt_power_up(struct wilc *wilc, int source); +int wilc_bt_power_down(struct wilc *wilc, int source); +int wilc_s02_reset_firmware(struct wilc *wilc, u32 type); +int wilc_s02_check_firmware_download(struct wilc *wl); #endif diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/wireless/microchip/wilc1000/power.c linux-microchip/drivers/net/wireless/microchip/wilc1000/power.c --- linux-6.6.51/drivers/net/wireless/microchip/wilc1000/power.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/net/wireless/microchip/wilc1000/power.c 2024-11-26 14:02:38.122504875 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2012 - 2023 Microchip Technology Inc., and its subsidiaries. + * All rights reserved. + */ + +#include <linux/delay.h> +#include <linux/of.h> +#include <linux/version.h> +#include <linux/of_gpio.h> +#include <linux/gpio.h> + +#include "netdev.h" + +/** + * wilc_of_parse_power_pins() - parse power sequence pins; to keep backward + * compatibility with old device trees that doesn't provide + * power sequence pins we check for default pins on proper boards + * + * @wilc: wilc data structure + * + * Returns: 0 on success, negative error number on failures. + */ +int wilc_of_parse_power_pins(struct wilc *wilc) +{ + static const struct wilc_power_gpios default_gpios[] = { + { .reset = GPIO_NUM_RESET, .chip_en = GPIO_NUM_CHIP_EN, }, + }; + struct device_node *of = wilc->dt_dev->of_node; + struct wilc_power *power = &wilc->power; + const struct wilc_power_gpios *gpios = &default_gpios[0]; + int ret; + + power->gpios.reset = of_get_named_gpio(of, "reset-gpios", 0); + if (!gpio_is_valid(power->gpios.reset)) + power->gpios.reset = gpios->reset; + + power->gpios.chip_en = of_get_named_gpio(of, "chip_en-gpios", 0); + if (!gpio_is_valid(power->gpios.chip_en)) + power->gpios.chip_en = gpios->chip_en; + + if (!gpio_is_valid(power->gpios.chip_en) || + !gpio_is_valid(power->gpios.reset)) + return -EINVAL; + + ret = devm_gpio_request(wilc->dev, power->gpios.chip_en, "CHIP_EN"); + if (ret) + return ret; + + ret = devm_gpio_request(wilc->dev, power->gpios.reset, "RESET"); + return ret; +} + +/** + * wilc_wlan_power() - handle power on/off commands + * + * @wilc: wilc data structure + * @on: requested power status + * + * Returns: none + */ +void wilc_wlan_power(struct wilc *wilc, bool on) +{ + if (!gpio_is_valid(wilc->power.gpios.chip_en) || + !gpio_is_valid(wilc->power.gpios.reset)) { + /* In case SDIO power sequence driver is used to power this + * device then the powering sequence is handled by the bus + * via pm_runtime_* functions. */ + return; + } + + if (on) { + gpio_direction_output(wilc->power.gpios.chip_en, 1); + mdelay(5); + gpio_direction_output(wilc->power.gpios.reset, 1); + } else { + gpio_direction_output(wilc->power.gpios.chip_en, 0); + gpio_direction_output(wilc->power.gpios.reset, 0); + } +} diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/wireless/microchip/wilc1000/sdio.c linux-microchip/drivers/net/wireless/microchip/wilc1000/sdio.c --- linux-6.6.51/drivers/net/wireless/microchip/wilc1000/sdio.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/net/wireless/microchip/wilc1000/sdio.c 2024-11-26 14:04:18.564303380 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:11 @ #include <linux/mmc/sdio_func.h> #include <linux/mmc/sdio_ids.h> #include <linux/mmc/host.h> +#include <linux/mmc/card.h> +#include <linux/pm_runtime.h> #include <linux/mmc/sdio.h> #include <linux/of_irq.h> #include "netdev.h" #include "cfg80211.h" -#define SDIO_MODALIAS "wilc1000_sdio" +enum sdio_host_lock { + WILC_SDIO_HOST_NO_TAKEN = 0, + WILC_SDIO_HOST_IRQ_TAKEN = 1, + WILC_SDIO_HOST_DIS_TAKEN = 2, +}; + +static enum sdio_host_lock sdio_intr_lock = WILC_SDIO_HOST_NO_TAKEN; +static wait_queue_head_t sdio_intr_waitqueue; +#define SDIO_MODALIAS "wilc_sdio" static const struct sdio_device_id wilc_sdio_ids[] = { { SDIO_DEVICE(SDIO_VENDOR_ID_MICROCHIP_WILC, SDIO_DEVICE_ID_MICROCHIP_WILC1000) }, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:35 @ }; MODULE_DEVICE_TABLE(sdio, wilc_sdio_ids); -#define WILC_SDIO_BLOCK_SIZE 512 +#define WILC_SDIO_BLOCK_SIZE 512 +#define WILC_MMC_SPI_BLOCK_SIZE 256 struct wilc_sdio { bool irq_gpio; u32 block_size; bool isinit; + struct wilc *wl; u8 *cmd53_buf; + bool is_mmc_spi; }; struct sdio_cmd52 { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:71 @ static void wilc_sdio_interrupt(struct sdio_func *func) { + if (sdio_intr_lock == WILC_SDIO_HOST_DIS_TAKEN) + return; + sdio_intr_lock = WILC_SDIO_HOST_IRQ_TAKEN; sdio_release_host(func); wilc_handle_isr(sdio_get_drvdata(func)); sdio_claim_host(func); + sdio_intr_lock = WILC_SDIO_HOST_NO_TAKEN; + wake_up_interruptible(&sdio_intr_waitqueue); } static int wilc_sdio_cmd52(struct wilc *wilc, struct sdio_cmd52 *cmd) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:127 @ size = cmd->count; if (cmd->use_global_buf) { - if (size > sizeof(u32)) - return -EINVAL; - + if (size > sizeof(u32)) { + ret = -EINVAL; + goto out; + } buf = sdio_priv->cmd53_buf; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:138 @ if (cmd->use_global_buf) memcpy(buf, cmd->buffer, size); - ret = sdio_memcpy_toio(func, cmd->address, buf, size); + if (cmd->increment) + ret = sdio_memcpy_toio(func, cmd->address, buf, size); + else + ret = sdio_writesb(func, cmd->address, buf, size); } else { /* read */ - ret = sdio_memcpy_fromio(func, buf, cmd->address, size); + if (cmd->increment) + ret = sdio_memcpy_fromio(func, buf, + cmd->address, size); + else + ret = sdio_readsb(func, buf, cmd->address, size); if (cmd->use_global_buf) memcpy(cmd->buffer, buf, size); } - +out: sdio_release_host(func); if (ret) - dev_err(&func->dev, "%s..failed, err(%d)\n", __func__, ret); + dev_err(&func->dev, "%s..%s failed, err(%d) %d %d\n", __func__, + cmd->read_write == 1 ? "write":"read", + ret, size, cmd->increment); return ret; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:167 @ const struct sdio_device_id *id) { struct wilc *wilc; - int ret; + int ret, io_type; + static bool init_power; struct wilc_sdio *sdio_priv; + struct device_node *np; + int irq_num; sdio_priv = kzalloc(sizeof(*sdio_priv), GFP_KERNEL); if (!sdio_priv) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:183 @ goto free; } - ret = wilc_cfg80211_init(&wilc, &func->dev, WILC_HIF_SDIO, - &wilc_hif_sdio); - if (ret) - goto free; - - if (IS_ENABLED(CONFIG_WILC1000_HW_OOB_INTR)) { - struct device_node *np = func->card->dev.of_node; - int irq_num = of_irq_get(np, 0); + sdio_priv->is_mmc_spi = func->card->host->caps & MMC_CAP_SPI; - if (irq_num > 0) { - wilc->dev_irq_num = irq_num; - sdio_priv->irq_gpio = true; - } + if (IS_ENABLED(CONFIG_WILC_HW_OOB_INTR) || func->card->host->caps & + MMC_CAP_SPI) { + io_type = WILC_HIF_SDIO_GPIO_IRQ; } + else + io_type = WILC_HIF_SDIO; + ret = wilc_cfg80211_init(&wilc, &func->dev, io_type, &wilc_hif_sdio); + if (ret) { + dev_err(&func->dev, "Couldn't initialize netdev\n"); + goto free; + } sdio_set_drvdata(func, wilc); wilc->bus_data = sdio_priv; wilc->dev = &func->dev; + wilc->dt_dev = &func->card->dev; + sdio_priv->wl = wilc; + + if (sdio_priv->is_mmc_spi) { + struct device *dev = func->card->host->parent; + struct gpio_desc *gpio_interrupt; + wilc->dt_dev = dev; + + /* mmc-spi holds the default irq for card detect, so use another GPIO for SPI interrupt */ + gpio_interrupt = devm_gpiod_get(dev, "intr", GPIOD_IN); + if (IS_ERR(gpio_interrupt)) { + goto free; + } else { + wilc->dev_irq_num = gpiod_to_irq(gpio_interrupt); + pr_info("got gpio_irq successfully %d\r\n", wilc->dev_irq_num); + if (wilc->dev_irq_num < 0) { + dev_warn(wilc->dev, "could not the IRQ %d\n", wilc->dev_irq_num); + gpiod_put(gpio_interrupt); + goto free; + } + } + } else { + irq_num = of_irq_get(func->card->dev.of_node, 0); + if (irq_num > 0) + wilc->dev_irq_num = irq_num; + } wilc->rtc_clk = devm_clk_get_optional(&func->card->dev, "rtc"); if (IS_ERR(wilc->rtc_clk)) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:234 @ } clk_prepare_enable(wilc->rtc_clk); + /* + * Some WILC SDIO setups needs a SD power sequence driver to be able + * to power the WILC devices before reaching this function. For those + * devices the power sequence driver already provides reset-gpios + * and chip_en-gpios. + */ + np = of_parse_phandle(func->card->host->parent->of_node, "mmc-pwrseq", + 0); + if ((np && of_device_is_available(np)) || sdio_priv->is_mmc_spi) { + init_power = 1; + of_node_put(np); + } else { + ret = wilc_of_parse_power_pins(wilc); + if (ret) + goto disable_rtc_clk; + } + + + if (!init_power) { + wilc_wlan_power(wilc, false); + init_power = 1; + wilc_wlan_power(wilc, true); + } + + wilc->is_mmc_spi = sdio_priv->is_mmc_spi; + if (!wilc->hif_func->hif_is_init(wilc)) { + if (wilc->hif_func->hif_init(wilc, false)) { + ret = -EIO; + goto disable_rtc_clk; + } + } + + if (wilc->chip == WILC_S02) { + ret = wilc_s02_reset_firmware(wilc, + WILC_S02_SOFT_RESET | (WILC_S02_WLAN_RESET << 8)); + if (ret < 0) { + pr_err("Failed to start firmware\n"); + ret = -EIO; + goto disable_rtc_clk; + } + ret = wilc_s02_check_firmware_download(wilc); + if (ret) + goto disable_rtc_clk; + + } + wilc_bt_init(wilc); + dev_info(&func->dev, "Driver Initializing success\n"); return 0; +disable_rtc_clk: + if (!IS_ERR(wilc->rtc_clk)) + clk_disable_unprepare(wilc->rtc_clk); dispose_irq: irq_dispose_mapping(wilc->dev_irq_num); wilc_netdev_cleanup(wilc); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:301 @ struct wilc *wilc = sdio_get_drvdata(func); struct wilc_sdio *sdio_priv = wilc->bus_data; + wilc->mac_status = WILC_MAC_STATUS_PRE_INIT; + complete(&wilc->sync_event); + clk_disable_unprepare(wilc->rtc_clk); wilc_netdev_cleanup(wilc); kfree(sdio_priv->cmd53_buf); kfree(sdio_priv); + wilc_bt_deinit(); } static int wilc_sdio_reset(struct wilc *wilc) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:317 @ int ret; struct sdio_func *func = dev_to_sdio_func(wilc->dev); + dev_info(&func->dev, "De Init SDIO\n"); + cmd.read_write = 1; cmd.function = 0; cmd.raw = 0; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:339 @ return sdio_priv->isinit; } +static int wilc_sdio_clear_init(struct wilc *wilc) +{ + struct wilc_sdio *sdio_priv = wilc->bus_data; + + sdio_priv->isinit = false; + + return 0; +} + static int wilc_sdio_suspend(struct device *dev) { struct sdio_func *func = dev_to_sdio_func(dev); struct wilc *wilc = sdio_get_drvdata(func); int ret; - dev_info(dev, "sdio suspend\n"); - chip_wakeup(wilc); + dev_info(&func->dev, "sdio suspend\n"); + mutex_lock(&wilc->hif_cs); - if (!IS_ERR(wilc->rtc_clk)) - clk_disable_unprepare(wilc->rtc_clk); + chip_wakeup(wilc, DEV_WIFI); - if (wilc->suspend_event) { - host_sleep_notify(wilc); - chip_allow_sleep(wilc); - } + if (mutex_is_locked(&wilc->hif_cs)) + mutex_unlock(&wilc->hif_cs); + + host_sleep_notify(wilc, DEV_WIFI); + chip_allow_sleep(wilc, DEV_WIFI); + + mutex_lock(&wilc->hif_cs); ret = wilc_sdio_reset(wilc); - if (ret) { - dev_err(&func->dev, "Fail reset sdio\n"); - return ret; - } - sdio_claim_host(func); return 0; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:377 @ struct sdio_func *func = container_of(dev->dev, struct sdio_func, dev); int ret = 0; + sdio_intr_lock = WILC_SDIO_HOST_NO_TAKEN; + + if(dev->chip == WILC_S02) + dev->hif_func->hif_clear_int_ext(dev, DATA_INT_CLR | ENABLE_RX_VMM); + sdio_claim_host(func); ret = sdio_claim_irq(func, wilc_sdio_interrupt); sdio_release_host(func); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:398 @ struct sdio_func *func = container_of(dev->dev, struct sdio_func, dev); int ret; + dev_info(&func->dev, "%s\n", __func__); + + if (sdio_intr_lock == WILC_SDIO_HOST_IRQ_TAKEN) + wait_event_interruptible(sdio_intr_waitqueue, + sdio_intr_lock == WILC_SDIO_HOST_NO_TAKEN); + sdio_intr_lock = WILC_SDIO_HOST_DIS_TAKEN; + sdio_claim_host(func); ret = sdio_release_irq(func); if (ret < 0) dev_err(&func->dev, "can't release sdio_irq, err(%d)\n", ret); sdio_release_host(func); + sdio_intr_lock = WILC_SDIO_HOST_NO_TAKEN; } /******************************************** @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:516 @ ret = wilc_sdio_cmd52(wilc, &cmd); if (ret) dev_err(&func->dev, - "Failed cmd 52, read reg (%08x) ...\n", addr); + "Failed cmd 52, write reg (%08x) ...\n", addr); } else { struct sdio_cmd53 cmd; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:528 @ return ret; cmd.read_write = 1; - cmd.function = 0; - cmd.address = WILC_SDIO_FBR_DATA_REG; + if (wilc->chip == WILC_S02) { + cmd.function = 1; + cmd.address = addr; + } else { + cmd.function = 0; + cmd.address = WILC_SDIO_FBR_DATA_REG; + } cmd.block_mode = 0; cmd.increment = 1; cmd.count = sizeof(u32); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:558 @ struct sdio_cmd53 cmd; int nblk, nleft, ret; + if (wilc->chip == WILC_S02) + cmd.increment = 0; + else + cmd.increment = 1; + cmd.read_write = 1; if (addr > 0) { /** @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:585 @ cmd.use_global_buf = false; if (nblk > 0) { cmd.block_mode = 1; - cmd.increment = 1; cmd.count = nblk; cmd.buffer = buf; cmd.block_size = block_size; - if (addr > 0) { + if (addr > 0) + { + ret = wilc_sdio_set_func0_csa_address(wilc, addr); if (ret) return ret; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:598 @ ret = wilc_sdio_cmd53(wilc, &cmd); if (ret) { dev_err(&func->dev, - "Failed cmd53 [%x], block send...\n", addr); + "Failed cmd53 [%x], block send...%d nblk %d\n", addr, block_size, nblk); return ret; } + if (wilc->chip == WILC_S02) + wilc->hif_func->hif_write_reg(wilc, + wilc->vmm_ctl.host_vmm_tx_ctl, + SDIO_MSG_VMM_INT_CLR); + if (addr > 0) addr += nblk * block_size; buf += nblk * block_size; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:613 @ if (nleft > 0) { cmd.block_mode = 0; - cmd.increment = 1; cmd.count = nleft; cmd.buffer = buf; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:629 @ "Failed cmd53 [%x], bytes send...\n", addr); return ret; } + if (wilc->chip == WILC_S02) + wilc->hif_func->hif_write_reg(wilc, + wilc->vmm_ctl.host_vmm_tx_ctl, + SDIO_MSG_VMM_INT_CLR); } return 0; } +static int wilc_read_chip_id(struct wilc *wilc, u32 addr, u32 *data) +{ + struct sdio_func *func = dev_to_sdio_func(wilc->dev); + struct wilc_sdio *sdio_priv = wilc->bus_data; + struct sdio_cmd53 cmd; + int ret; + + ret = wilc_sdio_set_func0_csa_address(wilc, addr); + if (ret) + return ret; + + cmd.read_write = 0; + cmd.function = 0; + cmd.address = WILC_SDIO_FBR_DATA_REG; + + cmd.block_mode = 0; + cmd.increment = 1; + cmd.count = sizeof(u32); + cmd.buffer = (u8 *)data; + cmd.use_global_buf = true; + + cmd.block_size = sdio_priv->block_size; + ret = wilc_sdio_cmd53(wilc, &cmd); + if (ret) { + dev_err(&func->dev, + "Failed cmd53, read reg (%08x)...\n", addr); + return ret; + } + le32_to_cpus(data); + return 0; + +} + +static int wilc_read_wilcs02_chip_id(struct wilc *wilc, u32 addr, u32 *data) +{ + struct sdio_func *func = dev_to_sdio_func(wilc->dev); + struct wilc_sdio *sdio_priv = wilc->bus_data; + struct sdio_cmd53 cmd; + int ret; + + ret = wilc_sdio_set_func0_csa_address(wilc, addr); + if (ret) + return ret; + + cmd.read_write = 0; + cmd.function = 0; + cmd.address = 0x10f; + cmd.block_mode = 0; + cmd.increment = 0; + + cmd.count = sizeof(u32); + cmd.buffer = (u8 *)data; + cmd.use_global_buf = true; + + cmd.block_size = sdio_priv->block_size; + ret = wilc_sdio_cmd53(wilc, &cmd); + if (ret) { + dev_err(&func->dev, + "Failed cmd53, read reg (%08x)...\n", addr); + return ret; + } + le32_to_cpus(data); + return 0; + +} + static int wilc_sdio_read_reg(struct wilc *wilc, u32 addr, u32 *data) { struct sdio_func *func = dev_to_sdio_func(wilc->dev); struct wilc_sdio *sdio_priv = wilc->bus_data; int ret; + /* read chip id first */ + if (addr == WILC_S02_CHIP_ID) { + ret = wilc_read_wilcs02_chip_id(wilc, addr, data); + return ret; + } else if (addr == WILC3000_CHIP_ID || addr == WILC_CHIPID) { + ret = wilc_read_chip_id(wilc, addr, data); + return ret; + } + if (addr >= 0xf0 && addr <= 0xff) { /* only vendor specific registers */ struct sdio_cmd52 cmd; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:736 @ } else { struct sdio_cmd53 cmd; - ret = wilc_sdio_set_func0_csa_address(wilc, addr); - if (ret) - return ret; + if (wilc->chip == WILC_S02) { + cmd.read_write = 0; + if (addr >= 0x01 && addr <= 0x0f) { + cmd.function = 0; + cmd.address = 0x10f; + cmd.increment = 0; + ret = wilc_sdio_set_func0_csa_address(wilc, addr); + if (ret) + return ret; + + } else { + cmd.function = 1; + cmd.address = addr; + cmd.increment = 1; + } + cmd.block_mode = 0; - cmd.read_write = 0; - cmd.function = 0; - cmd.address = WILC_SDIO_FBR_DATA_REG; - cmd.block_mode = 0; - cmd.increment = 1; - cmd.count = sizeof(u32); - cmd.buffer = (u8 *)data; - cmd.use_global_buf = true; + cmd.count = sizeof(u32); + cmd.buffer = (u8 *)data; + cmd.use_global_buf = true; - cmd.block_size = sdio_priv->block_size; - ret = wilc_sdio_cmd53(wilc, &cmd); - if (ret) { - dev_err(&func->dev, + cmd.block_size = sdio_priv->block_size; + ret = wilc_sdio_cmd53(wilc, &cmd); + if (ret) { + dev_err(&func->dev, + "Failed cmd53, read reg (%08x)...\n", addr); + return ret; + } + } else { + ret = wilc_sdio_set_func0_csa_address(wilc, addr); + if (ret) + return ret; + + cmd.read_write = 0; + cmd.function = 0; + cmd.address = WILC_SDIO_FBR_DATA_REG; + cmd.block_mode = 0; + cmd.increment = 1; + cmd.count = sizeof(u32); + cmd.buffer = (u8 *)data; + cmd.use_global_buf = true; + + cmd.block_size = sdio_priv->block_size; + ret = wilc_sdio_cmd53(wilc, &cmd); + if (ret) { + dev_err(&func->dev, "Failed cmd53, read reg (%08x)...\n", addr); - return ret; + return ret; + } } } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:820 @ nleft = size % block_size; cmd.use_global_buf = false; + + if (wilc->chip == WILC_S02) + cmd.increment = 0; + else + cmd.increment = 1; + if (nblk > 0) { cmd.block_mode = 1; - cmd.increment = 1; cmd.count = nblk; cmd.buffer = buf; cmd.block_size = block_size; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:842 @ "Failed cmd53 [%x], block read...\n", addr); return ret; } + if (wilc->chip == WILC_S02) + wilc->hif_func->hif_write_reg(wilc, + wilc->vmm_ctl.host_vmm_tx_ctl, + SDIO_MSG_VMM_INT_CLR); if (addr > 0) addr += nblk * block_size; buf += nblk * block_size; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:853 @ if (nleft > 0) { cmd.block_mode = 0; - cmd.increment = 1; cmd.count = nleft; cmd.buffer = buf; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:869 @ "Failed cmd53 [%x], bytes read...\n", addr); return ret; } + if (wilc->chip == WILC_S02) + wilc->hif_func->hif_write_reg(wilc, + wilc->vmm_ctl.host_vmm_tx_ctl, + SDIO_MSG_VMM_INT_CLR); + } return 0; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:887 @ static int wilc_sdio_deinit(struct wilc *wilc) { + struct sdio_func *func = dev_to_sdio_func(wilc->dev); struct wilc_sdio *sdio_priv = wilc->bus_data; sdio_priv->isinit = false; + + pm_runtime_put_sync_autosuspend(mmc_dev(func->card->host)); + wilc_wlan_power(wilc, false); + return 0; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:905 @ struct sdio_cmd52 cmd; int loop, ret; u32 chipid; + bool test_mode = false; + +#ifdef WILC_S02_TEST_BUS_INTERFACE + test_mode = is_test_mode; +#endif + dev_info(&func->dev, "SDIO single driver speed: %d %s\n", + func->card->host->ios.clock, test_mode? "loopback mode": ""); + + if (!sdio_priv->is_mmc_spi) { + /* Patch for sdio interrupt latency issue */ + ret = pm_runtime_get_sync(mmc_dev(func->card->host)); + if (ret < 0) { + pm_runtime_put_noidle(mmc_dev(func->card->host)); + return ret; + } + } + + init_waitqueue_head(&sdio_intr_waitqueue); + sdio_priv->irq_gpio = (wilc->io_type == WILC_HIF_SDIO_GPIO_IRQ); /** * function 0 csa enable @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:936 @ ret = wilc_sdio_cmd52(wilc, &cmd); if (ret) { dev_err(&func->dev, "Fail cmd 52, enable csa...\n"); - return ret; + goto pm_runtime_put; } + if (sdio_priv->is_mmc_spi) + sdio_priv->block_size = WILC_MMC_SPI_BLOCK_SIZE; + else + sdio_priv->block_size = WILC_SDIO_BLOCK_SIZE; + /** * function 0 block size **/ - ret = wilc_sdio_set_block_size(wilc, 0, WILC_SDIO_BLOCK_SIZE); + ret = wilc_sdio_set_block_size(wilc, 0, sdio_priv->block_size); if (ret) { dev_err(&func->dev, "Fail cmd 52, set func 0 block size...\n"); - return ret; + goto pm_runtime_put; } - sdio_priv->block_size = WILC_SDIO_BLOCK_SIZE; /** * enable func1 IO @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:965 @ if (ret) { dev_err(&func->dev, "Fail cmd 52, set IOE register...\n"); - return ret; + goto pm_runtime_put; } /** @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:982 @ if (ret) { dev_err(&func->dev, "Fail cmd 52, get IOR register...\n"); - return ret; + goto pm_runtime_put; } if (cmd.data == WILC_SDIO_CCCR_IO_EN_FUNC1) break; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:990 @ if (loop <= 0) { dev_err(&func->dev, "Fail func 1 is not ready...\n"); - return -EINVAL; + goto pm_runtime_put; } /** * func 1 is ready, set func 1 block size **/ - ret = wilc_sdio_set_block_size(wilc, 1, WILC_SDIO_BLOCK_SIZE); + ret = wilc_sdio_set_block_size(wilc, 1, sdio_priv->block_size); if (ret) { dev_err(&func->dev, "Fail set func 1 block size...\n"); - return ret; + goto pm_runtime_put; + } + + cmd.read_write = 1; + cmd.function = 0; + cmd.raw = 0; + cmd.address = 0x7; + cmd.data = 0xA2; + ret = wilc_sdio_cmd52(wilc, &cmd); + if (ret) { + dev_err(&func->dev, + "Fail cmd 52, set Bus width register ...[%x]\n", + cmd.address); + return 0; } /** @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1026 @ ret = wilc_sdio_cmd52(wilc, &cmd); if (ret) { dev_err(&func->dev, "Fail cmd 52, set IEN register...\n"); - return ret; + goto pm_runtime_put; + } + + cmd.read_write = 1; + cmd.function = 0; + cmd.raw = 0; + cmd.address = 0x18000; + cmd.data = 0x1; + ret = wilc_sdio_cmd52(wilc, &cmd); + if (ret) { + dev_err(&func->dev, + "Fail cmd 52, set wakeup register [%x]\n", cmd.address); + return 0; + } + + /** + * Review: BIG ENDIAN + **/ + cmd.read_write = 1; + cmd.function = 1; + cmd.raw = 0; + cmd.address = 0x9; + cmd.data = 0x1; + ret = wilc_sdio_cmd52(wilc, &cmd); + if (ret) { + dev_err(&func->dev, "Fail cmd 52, set Interrupt register...\n"); + return 0; } /** * make sure can read back chip id correctly **/ if (!resume) { - ret = wilc_sdio_read_reg(wilc, WILC_CHIPID, &chipid); - if (ret) { - dev_err(&func->dev, "Fail cmd read chip id...\n"); - return ret; + chipid = wilc_get_chipid(wilc, true); + if (is_wilcs02_chip(chipid)) { + wilc->chip = WILC_S02; + func->card->quirks |= MMC_QUIRK_BROKEN_BYTE_MODE_512; + wilcs02_init_vmm_registers(wilc); + } else if (is_wilc3000(chipid)) { + wilc->chip = WILC_3000; + } else if (is_wilc1000(chipid)) { + wilc->chip = WILC_1000; + } else { + ret = -EINVAL; + dev_err(&func->dev, "Unsupported chipid: %x\n", chipid); + goto pm_runtime_put; } - dev_err(&func->dev, "chipid (%08x)\n", chipid); + dev_info(&func->dev, "chipid %08x\n", chipid); } sdio_priv->isinit = true; + + return 0; + +pm_runtime_put: + if (!sdio_priv->is_mmc_spi) + pm_runtime_put_sync_autosuspend(mmc_dev(func->card->host)); + return ret; +} + +static int wilc_s02_sdio_read_size(struct wilc *wilc, u32 *size) +{ + u32 tmp = 0; + int ret; + + if (!wilc->is_mmc_spi) { + struct sdio_cmd52 cmd; + + /** + * Read DMA count in words + **/ + cmd.read_write = 0; + cmd.function = 1; + cmd.raw = 0; + cmd.address = 0x0c; + cmd.data = 0x0; + wilc_sdio_cmd52(wilc, &cmd); + tmp = cmd.data; + + cmd.address = 0x0d; + cmd.data = 0x0; + wilc_sdio_cmd52(wilc, &cmd); + tmp |= (cmd.data << 8); + } else { + ret = wilc->hif_func->hif_read_reg(wilc, 0x07, &tmp); + if (ret) + tmp = 0; + } + *size = tmp; return 0; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1123 @ u32 tmp; struct sdio_cmd52 cmd; + if (wilc->chip == WILC_S02) + { + wilc_s02_sdio_read_size(wilc, size); + return 0; + } + /** * Read DMA count in words **/ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1136 @ cmd.function = 0; cmd.raw = 0; cmd.address = WILC_SDIO_INTERRUPT_DATA_SZ_REG; - cmd.data = 0; wilc_sdio_cmd52(wilc, &cmd); tmp = cmd.data; cmd.address = WILC_SDIO_INTERRUPT_DATA_SZ_REG + 1; - cmd.data = 0; wilc_sdio_cmd52(wilc, &cmd); tmp |= (cmd.data << 8); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1152 @ struct sdio_func *func = dev_to_sdio_func(wilc->dev); struct wilc_sdio *sdio_priv = wilc->bus_data; u32 tmp; - u8 irq_flags; struct sdio_cmd52 cmd; + u32 irq_flags; - wilc_sdio_read_size(wilc, &tmp); + if (sdio_priv->irq_gpio) { + wilc_sdio_read_size(wilc, &tmp); + if (wilc->chip == WILC_S02) { + *int_status = tmp; + return 0; + } + cmd.read_write = 0; + cmd.function = 0; + cmd.raw = 0; + cmd.data = 0; + if (wilc->chip == WILC_1000) { + cmd.address = WILC1000_SDIO_IRQ_FLAG_REG; + wilc_sdio_cmd52(wilc, &cmd); + irq_flags = cmd.data & 0x1f; + } else { + cmd.address = WILC3000_SDIO_IRQ_FLAG_REG; + wilc_sdio_cmd52(wilc, &cmd); + irq_flags = cmd.data & 0x0f; + } + tmp |= FIELD_PREP(IRG_FLAGS_MASK, cmd.data); - /** - * Read IRQ flags - **/ - if (!sdio_priv->irq_gpio) { - cmd.function = 1; - cmd.address = WILC_SDIO_EXT_IRQ_FLAG_REG; + *int_status = tmp; } else { - cmd.function = 0; - cmd.address = WILC_SDIO_IRQ_FLAG_REG; + wilc_sdio_read_size(wilc, &tmp); + cmd.read_write = 0; + cmd.function = 1; + if (wilc->chip == WILC_S02) { + cmd.raw = 0; + cmd.address = 0x08; + cmd.data = 0x0; + wilc_sdio_cmd52(wilc, &cmd); + } else { + cmd.address = WILC_SDIO_EXT_IRQ_FLAG_REG; + cmd.data = 0; + wilc_sdio_cmd52(wilc, &cmd); + + irq_flags = cmd.data; + tmp |= FIELD_PREP(IRG_FLAGS_MASK, cmd.data); + + if (FIELD_GET(UNHANDLED_IRQ_MASK, irq_flags)) { + dev_err(&func->dev, "Unexpected interrupt (1) int=%lx\n", + FIELD_GET(UNHANDLED_IRQ_MASK, irq_flags)); + } + } + *int_status = tmp; } - cmd.raw = 0; - cmd.read_write = 0; - cmd.data = 0; - wilc_sdio_cmd52(wilc, &cmd); - irq_flags = cmd.data; - tmp |= FIELD_PREP(IRG_FLAGS_MASK, cmd.data); - - if (FIELD_GET(UNHANDLED_IRQ_MASK, irq_flags)) - dev_err(&func->dev, "Unexpected interrupt (1) int=%lx\n", - FIELD_GET(UNHANDLED_IRQ_MASK, irq_flags)); - - *int_status = tmp; return 0; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1209 @ { struct sdio_func *func = dev_to_sdio_func(wilc->dev); struct wilc_sdio *sdio_priv = wilc->bus_data; + struct sdio_cmd52 cmd; int ret; u32 reg = 0; - if (sdio_priv->irq_gpio) - reg = val & (BIT(MAX_NUM_INT) - 1); - - /* select VMM table 0 */ - if (val & SEL_VMM_TBL0) - reg |= BIT(5); - /* select VMM table 1 */ - if (val & SEL_VMM_TBL1) - reg |= BIT(6); - /* enable VMM */ - if (val & EN_VMM) - reg |= BIT(7); - if (reg) { - struct sdio_cmd52 cmd; - + if (wilc->chip == WILC_S02) { cmd.read_write = 1; - cmd.function = 0; - cmd.raw = 0; - cmd.address = WILC_SDIO_IRQ_CLEAR_FLAG_REG; - cmd.data = reg; - + cmd.function = 1; + cmd.raw = 1; + cmd.address = 0x08; + cmd.data = 0x1; ret = wilc_sdio_cmd52(wilc, &cmd); if (ret) { dev_err(&func->dev, - "Failed cmd52, set (%02x) data (%d) ...\n", - cmd.address, __LINE__); + "Failed cmd52, set 0x08 data (%d) ...\n", + __LINE__); return ret; } + + cmd.read_write = 1; + cmd.function = 1; + cmd.raw = 0; + cmd.address = 0x9; + cmd.data = 0x1; + ret = wilc_sdio_cmd52(wilc, &cmd); + if (ret) { + dev_err(&func->dev, "Fail cmd 52, set Interrupt register...\n"); + return 0; + } + } else if (wilc->chip == WILC_1000) { + if (sdio_priv->irq_gpio) + reg = val & (BIT(MAX_NUM_INT) - 1); + + /* select VMM table 0 */ + if (val & SEL_VMM_TBL0) + reg |= BIT(5); + /* select VMM table 1 */ + if (val & SEL_VMM_TBL1) + reg |= BIT(6); + /* enable VMM */ + if (val & EN_VMM) + reg |= BIT(7); + if (reg) { + struct sdio_cmd52 cmd; + + cmd.read_write = 1; + cmd.function = 0; + cmd.raw = 0; + cmd.address = WILC1000_SDIO_IRQ_CLEAR_FLAG_REG; + cmd.data = reg; + + ret = wilc_sdio_cmd52(wilc, &cmd); + if (ret) { + dev_err(&func->dev, + "Failed cmd52, set 0xf8 data (%d) ...\n", + __LINE__); + return ret; + } + } + } else { + if (sdio_priv->irq_gpio) { + reg = val & (BIT(MAX_NUM_INT) - 1); + if (reg) { + struct sdio_cmd52 cmd; + + cmd.read_write = 1; + cmd.function = 0; + cmd.raw = 0; + cmd.address = WILC3000_SDIO_IRQ_CLEAR_FLAG_REG; + cmd.data = reg; + + ret = wilc_sdio_cmd52(wilc, &cmd); + if (ret) { + dev_err(&func->dev, + "Failed cmd52, set 0xfe data (%d) ...\n", + __LINE__); + return ret; + } + } + } + reg = 0; + /* select VMM table 0 */ + if (val & SEL_VMM_TBL0) + reg |= BIT(0); + /* select VMM table 1 */ + if (val & SEL_VMM_TBL1) + reg |= BIT(1); + /* enable VMM */ + if (val & EN_VMM) + reg |= BIT(2); + + if (reg) { + struct sdio_cmd52 cmd; + + cmd.read_write = 1; + cmd.function = 0; + cmd.raw = 0; + cmd.address = WILC3000_SDIO_VMM_TBL_CTRL_REG; + cmd.data = reg; + + ret = wilc_sdio_cmd52(wilc, &cmd); + if (ret) { + dev_err(&func->dev, + "Failed cmd52, set 0xf6 data (%d) ...\n", + __LINE__); + return ret; + } + } } + return 0; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1326 @ struct sdio_func *func = dev_to_sdio_func(wilc->dev); struct wilc_sdio *sdio_priv = wilc->bus_data; u32 reg; + int ret, i; if (nint > MAX_NUM_INT) { - dev_err(&func->dev, "Too many interrupts (%d)...\n", nint); + dev_err(&func->dev, "Too many interrupts %d\n", nint); return -EINVAL; } - /** - * Disable power sequencer - **/ - if (wilc_sdio_read_reg(wilc, WILC_MISC, ®)) { - dev_err(&func->dev, "Failed read misc reg...\n"); - return -EINVAL; - } - - reg &= ~BIT(8); - if (wilc_sdio_write_reg(wilc, WILC_MISC, reg)) { - dev_err(&func->dev, "Failed write misc reg...\n"); - return -EINVAL; +/* WILC3000 only. Was removed in WILC1000 on revision 6200. + * Might be related to suspend/resume + */ + if (wilc->chip == WILC_3000) { + /** + * Disable power sequencer + **/ + if (wilc_sdio_read_reg(wilc, WILC_MISC, ®)) { + dev_err(&func->dev, "Failed read misc reg\n"); + return -EINVAL; + } + reg &= ~BIT(8); + if (wilc_sdio_write_reg(wilc, WILC_MISC, reg)) { + dev_err(&func->dev, "Failed write misc reg\n"); + return -EINVAL; + } } if (sdio_priv->irq_gpio) { - u32 reg; - int ret, i; - /** * interrupt pin mux select **/ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1429 @ .disable_interrupt = wilc_sdio_disable_interrupt, .hif_reset = wilc_sdio_reset, .hif_is_init = wilc_sdio_is_init, + .hif_clear_init = wilc_sdio_clear_init, }; static int wilc_sdio_resume(struct device *dev) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1437 @ struct sdio_func *func = dev_to_sdio_func(dev); struct wilc *wilc = sdio_get_drvdata(func); - dev_info(dev, "sdio resume\n"); - sdio_release_host(func); - chip_wakeup(wilc); + dev_info(&func->dev, "sdio resume\n"); + chip_wakeup(wilc, DEV_WIFI); wilc_sdio_init(wilc, true); - if (wilc->suspend_event) - host_wakeup_notify(wilc); + if (mutex_is_locked(&wilc->hif_cs)) + mutex_unlock(&wilc->hif_cs); + + host_wakeup_notify(wilc, DEV_WIFI); + + mutex_lock(&wilc->hif_cs); + + chip_allow_sleep(wilc, DEV_WIFI); - chip_allow_sleep(wilc); + if (mutex_is_locked(&wilc->hif_cs)) + mutex_unlock(&wilc->hif_cs); return 0; } static const struct of_device_id wilc_of_match[] = { { .compatible = "microchip,wilc1000", }, - { /* sentinel */ } + { .compatible = "microchip,wilc3000", }, + { /* sentinel */} }; MODULE_DEVICE_TABLE(of, wilc_of_match); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1481 @ module_driver(wilc_sdio_driver, sdio_register_driver, sdio_unregister_driver); +MODULE_DESCRIPTION("Atmel WILC1000 SDIO wireless driver"); MODULE_LICENSE("GPL"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/wireless/microchip/wilc1000/spi.c linux-microchip/drivers/net/wireless/microchip/wilc1000/spi.c --- linux-6.6.51/drivers/net/wireless/microchip/wilc1000/spi.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/net/wireless/microchip/wilc1000/spi.c 2024-11-26 14:02:38.122504875 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:42 @ * zero bytes when the SPI bus operates at 48MHz and none when it * operates at 1MHz. */ -#define WILC_SPI_RSP_HDR_EXTRA_DATA 8 +#define WILC_SPI_RSP_HDR_EXTRA_DATA 4 struct wilc_spi { bool isinit; /* true if SPI protocol has been configured */ bool probing_crc; /* true if we're probing chip's CRC config */ bool crc7_enabled; /* true if crc7 is currently enabled */ bool crc16_enabled; /* true if crc16 is currently enabled */ - struct wilc_gpios { - struct gpio_desc *enable; /* ENABLE GPIO or NULL */ - struct gpio_desc *reset; /* RESET GPIO or NULL */ - } gpios; }; static const struct wilc_hif_func wilc_hif_spi; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:73 @ #define CMD_SINGLE_READ 0xca #define CMD_RESET 0xcf +#define SPI_RESP_RETRY_COUNT (10) #define SPI_RETRY_MAX_LIMIT 10 #define SPI_ENABLE_VMM_RETRY_LIMIT 2 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:158 @ u8 status; } __packed; -static int wilc_parse_gpios(struct wilc *wilc) -{ - struct spi_device *spi = to_spi_device(wilc->dev); - struct wilc_spi *spi_priv = wilc->bus_data; - struct wilc_gpios *gpios = &spi_priv->gpios; - - /* get ENABLE pin and deassert it (if it is defined): */ - gpios->enable = devm_gpiod_get_optional(&spi->dev, - "enable", GPIOD_OUT_LOW); - /* get RESET pin and assert it (if it is defined): */ - if (gpios->enable) { - /* if enable pin exists, reset must exist as well */ - gpios->reset = devm_gpiod_get(&spi->dev, - "reset", GPIOD_OUT_HIGH); - if (IS_ERR(gpios->reset)) { - dev_err(&spi->dev, "missing reset gpio.\n"); - return PTR_ERR(gpios->reset); - } - } else { - gpios->reset = devm_gpiod_get_optional(&spi->dev, - "reset", GPIOD_OUT_HIGH); - } - return 0; -} - -static void wilc_wlan_power(struct wilc *wilc, bool on) -{ - struct wilc_spi *spi_priv = wilc->bus_data; - struct wilc_gpios *gpios = &spi_priv->gpios; - - if (on) { - /* assert ENABLE: */ - gpiod_set_value(gpios->enable, 1); - mdelay(5); - /* deassert RESET: */ - gpiod_set_value(gpios->reset, 0); - } else { - /* assert RESET: */ - gpiod_set_value(gpios->reset, 1); - /* deassert ENABLE: */ - gpiod_set_value(gpios->enable, 0); - } -} - static int wilc_bus_probe(struct spi_device *spi) { int ret; + static bool init_power; struct wilc *wilc; + struct device *dev = &spi->dev; struct wilc_spi *spi_priv; spi_priv = kzalloc(sizeof(*spi_priv), GFP_KERNEL); if (!spi_priv) return -ENOMEM; - ret = wilc_cfg80211_init(&wilc, &spi->dev, WILC_HIF_SPI, &wilc_hif_spi); + ret = wilc_cfg80211_init(&wilc, dev, WILC_HIF_SPI, &wilc_hif_spi); if (ret) goto free; spi_set_drvdata(spi, wilc); wilc->dev = &spi->dev; wilc->bus_data = spi_priv; + wilc->dt_dev = &spi->dev; wilc->dev_irq_num = spi->irq; - ret = wilc_parse_gpios(wilc); - if (ret < 0) - goto netdev_cleanup; - wilc->rtc_clk = devm_clk_get_optional(&spi->dev, "rtc"); if (IS_ERR(wilc->rtc_clk)) { ret = PTR_ERR(wilc->rtc_clk); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:187 @ } clk_prepare_enable(wilc->rtc_clk); + ret = wilc_of_parse_power_pins(wilc); + if (ret) + goto disable_rtc_clk; + + if (!init_power) { + wilc_wlan_power(wilc, false); + init_power = 1; + wilc_wlan_power(wilc, true); + } + + wilc_bt_init(wilc); + + dev_info(dev, "WILC SPI probe success\n"); return 0; +disable_rtc_clk: + if (!IS_ERR(wilc->rtc_clk)) + clk_disable_unprepare(wilc->rtc_clk); netdev_cleanup: wilc_netdev_cleanup(wilc); free: @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:220 @ clk_disable_unprepare(wilc->rtc_clk); wilc_netdev_cleanup(wilc); kfree(spi_priv); + + wilc_bt_deinit(); +} + +static int wilc_spi_suspend(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct wilc *wilc = spi_get_drvdata(spi); + + dev_info(&spi->dev, "\n\n << SUSPEND >>\n\n"); + mutex_lock(&wilc->hif_cs); + chip_wakeup(wilc, DEV_WIFI); + + if (mutex_is_locked(&wilc->hif_cs)) + mutex_unlock(&wilc->hif_cs); + + /* notify the chip that host will sleep */ + host_sleep_notify(wilc, DEV_WIFI); + chip_allow_sleep(wilc, DEV_WIFI); + mutex_lock(&wilc->hif_cs); + + return 0; +} + +static int wilc_spi_resume(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct wilc *wilc = spi_get_drvdata(spi); + + dev_info(&spi->dev, "\n\n <<RESUME>>\n\n"); + + /* wake the chip to compelete the re-initialization */ + chip_wakeup(wilc, DEV_WIFI); + + if (mutex_is_locked(&wilc->hif_cs)) + mutex_unlock(&wilc->hif_cs); + + host_wakeup_notify(wilc, DEV_WIFI); + + mutex_lock(&wilc->hif_cs); + + chip_allow_sleep(wilc, DEV_WIFI); + + if (mutex_is_locked(&wilc->hif_cs)) + mutex_unlock(&wilc->hif_cs); + + return 0; } static const struct of_device_id wilc_of_match[] = { { .compatible = "microchip,wilc1000", }, + { .compatible = "microchip,wilc3000", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, wilc_of_match); static const struct spi_device_id wilc_spi_id[] = { { "wilc1000", 0 }, + { "wilc3000", 0 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(spi, wilc_spi_id); +static const struct dev_pm_ops wilc_spi_pm_ops = { + .suspend = wilc_spi_suspend, + .resume = wilc_spi_resume, +}; + static struct spi_driver wilc_spi_driver = { .driver = { .name = SPI_MODALIAS, .of_match_table = wilc_of_match, + .pm = &wilc_spi_pm_ops, }, .id_table = wilc_spi_id, .probe = wilc_bus_probe, .remove = wilc_bus_remove, }; module_spi_driver(wilc_spi_driver); +MODULE_DESCRIPTION("Atmel WILC1000 SPI wireless driver"); MODULE_LICENSE("GPL"); static int wilc_spi_tx(struct wilc *wilc, u8 *b, u32 len) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:327 @ memset(&msg, 0, sizeof(msg)); spi_message_init(&msg); - msg.spi = spi; spi_message_add_tail(&tr, &msg); ret = spi_sync(spi, &msg); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:369 @ memset(&msg, 0, sizeof(msg)); spi_message_init(&msg); - msg.spi = spi; spi_message_add_tail(&tr, &msg); ret = spi_sync(spi, &msg); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:406 @ memset(&msg, 0, sizeof(msg)); spi_message_init(&msg); - msg.spi = spi; - spi_message_add_tail(&tr, &msg); ret = spi_sync(spi, &msg); if (ret < 0) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:499 @ ********************************************/ static u8 wilc_get_crc7(u8 *buffer, u32 len) { - return crc7_be(0xfe, buffer, len); + return crc7_be(0xfe, buffer, len) | 0x01; } static int wilc_spi_single_read(struct wilc *wilc, u8 cmd, u32 adr, void *b, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:555 @ } r = (struct wilc_spi_rsp_data *)&rb[cmd_len]; + /* + * Clockless registers operations might return unexptected responses, + * even if successful. + */ if (r->rsp_cmd_type != cmd && !clockless) { if (!spi_priv->probing_crc) dev_err(&spi->dev, - "Failed cmd, cmd (%02x), resp (%02x)\n", + "Failed cmd response, cmd (%02x), resp (%02x)\n", cmd, r->rsp_cmd_type); return -EINVAL; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:573 @ return -EINVAL; } - for (i = 0; i < WILC_SPI_RSP_HDR_EXTRA_DATA; ++i) + for (i = 0; i < SPI_RESP_RETRY_COUNT; ++i) if (WILC_GET_RESP_HDR_START(r->data[i]) == 0xf) break; - if (i >= WILC_SPI_RSP_HDR_EXTRA_DATA) { + if (i >= SPI_RESP_RETRY_COUNT) { dev_err(&spi->dev, "Error, data start missing\n"); return -EINVAL; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:758 @ /* * Data Response header */ - retry = 100; + retry = SPI_RESP_RETRY_COUNT; do { if (wilc_spi_rx(wilc, &rsp, 1)) { dev_err(&spi->dev, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1130 @ return 0; } +static int wilc_spi_clear_init(struct wilc *wilc) +{ + struct wilc_spi *spi_priv = wilc->bus_data; + + spi_priv->isinit = false; + + return 0; +} + static int wilc_spi_init(struct wilc *wilc, bool resume) { struct spi_device *spi = to_spi_device(wilc->dev); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1156 @ dev_err(&spi->dev, "Fail cmd read chip id...\n"); } - wilc_wlan_power(wilc, true); - /* * configure protocol */ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1219 @ return ret; } - spi_priv->isinit = true; + if (!resume) { + chipid = wilc_get_chipid(wilc, true); + if (is_wilc3000(chipid)) { + wilc->chip = WILC_3000; + } else if (is_wilc1000(chipid)) { + wilc->chip = WILC_1000; + } else { + dev_err(&spi->dev, "Unsupported chipid: %x\n", chipid); + return -EINVAL; + } + dev_dbg(&spi->dev, "chipid %08x\n", chipid); + } + spi_priv->isinit = true; return 0; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1362 @ .hif_sync_ext = wilc_spi_sync_ext, .hif_reset = wilc_spi_reset, .hif_is_init = wilc_spi_is_init, + .hif_clear_init = wilc_spi_clear_init, }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/wireless/microchip/wilc1000/sysfs.c linux-microchip/drivers/net/wireless/microchip/wilc1000/sysfs.c --- linux-6.6.51/drivers/net/wireless/microchip/wilc1000/sysfs.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/net/wireless/microchip/wilc1000/sysfs.c 2024-11-26 14:04:18.564303380 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2012 - 2018 Microchip Technology Inc., and its subsidiaries. + * All rights reserved. + */ + +#include <linux/kobject.h> +#include "cfg80211.h" + +static struct kobject *wilc_kobj; +static int device_created; +static struct wilc *wl; + +static ssize_t wilc_sysfs_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int attr_val = -1; + + if (strcmp(attr->attr.name, "p2p_mode") == 0) + attr_val = wl->attr_sysfs.p2p_mode; + if (strcmp(attr->attr.name, "ant_swtch_mode") == 0) + attr_val = wl->attr_sysfs.ant_swtch_mode; + else if (strcmp(attr->attr.name, "antenna1") == 0) + attr_val = wl->attr_sysfs.antenna1; + else if (strcmp(attr->attr.name, "antenna2") == 0) + attr_val = wl->attr_sysfs.antenna2; + else if (strcmp(attr->attr.name, "fw_dbg_level") == 0) + attr_val = wl->attr_sysfs.fw_dbg_level; + + return sprintf(buf, "%d\n", attr_val); +} + +static ssize_t wilc_sysfs_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, + size_t count) +{ + int attr_val; + + if (kstrtoint(buf, 10, &attr_val)) + pr_err("Failed to convert p2p_mode string"); + if (strcmp(attr->attr.name, "p2p_mode") == 0) { + wl->attr_sysfs.p2p_mode = (attr_val ? 1 : 0); + } else if (strcmp(attr->attr.name, "ant_swtch_mode") == 0) { + if (attr_val > ANT_SWTCH_DUAL_GPIO_CTRL) + pr_err("Valid antenna switch modes:\n1-Single Antenna, 2-Dual Antenna\n"); + else + wl->attr_sysfs.ant_swtch_mode = attr_val; + } else if (strcmp(attr->attr.name, "antenna1") == 0) { + wl->attr_sysfs.antenna1 = attr_val; + } else if (strcmp(attr->attr.name, "antenna2") == 0) { + wl->attr_sysfs.antenna2 = attr_val; + } else if (strcmp(attr->attr.name, "fw_dbg_level") == 0) { + if (attr_val < WILC_FW_PRINT_LVL_ERROR || attr_val > WILC_FW_PRINT_LVL_MAX) { + pr_err("valid fw debug levels:\n 1-WILC_FW_PRINT_LVL_ERROR, \n 2-WILC_FW_PRINT_LVL_DEBUG \n 3-WILC_FW_PRINT_LVL_INFO," + "\n 4-WILC_FW_PRINT_LVL_FUN_PT \n5-WILC_FW_PRINT_LVL_MAX\n"); + } + else { + wl->attr_sysfs.fw_dbg_level = attr_val; + wilc_set_fw_debug_level(wl, wl->attr_sysfs.fw_dbg_level); + } + } + + return count; +} + +static struct kobj_attribute p2p_mode_attr = + __ATTR(p2p_mode, 0664, wilc_sysfs_show, wilc_sysfs_store); + +static struct kobj_attribute ant_swtch_mode_attr = + __ATTR(ant_swtch_mode, 0664, wilc_sysfs_show, wilc_sysfs_store); + +static struct kobj_attribute ant_swtch_antenna1_attr = + __ATTR(antenna1, 0664, wilc_sysfs_show, wilc_sysfs_store); + +static struct kobj_attribute ant_swtch_antenna2_attr = + __ATTR(antenna2, 0664, wilc_sysfs_show, wilc_sysfs_store); + +static struct kobj_attribute fw_dbg_level_attr = + __ATTR(fw_dbg_level, 0664, wilc_sysfs_show, wilc_sysfs_store); + +static struct attribute *wilc_attrs[] = { + &p2p_mode_attr.attr, + &ant_swtch_mode_attr.attr, + &ant_swtch_antenna1_attr.attr, + &ant_swtch_antenna2_attr.attr, + &fw_dbg_level_attr.attr, + NULL +}; + +static struct attribute_group attr_group = { + .attrs = wilc_attrs, +}; + +void wilc_sysfs_init(struct wilc *wilc) +{ + int retval; + + if (device_created) + return; + + wilc_kobj = kobject_create_and_add("wilc", NULL); + if (!wilc_kobj) { + retval = -ENOMEM; + return; + } + + retval = sysfs_create_group(wilc_kobj, &attr_group); + device_created = 1; + wl = wilc; + /* By default p2p mode is Group Owner */ + wl->attr_sysfs.p2p_mode = WILC_P2P_ROLE_GO; + wl->attr_sysfs.ant_swtch_mode = ANT_SWTCH_INVALID_GPIO_CTRL; + wl->attr_sysfs.antenna1 = 0xFF; + wl->attr_sysfs.antenna2 = 0xFF; + wl->attr_sysfs.fw_dbg_level = WILC_FW_PRINT_LVL_ERROR; +} + +void wilc_sysfs_exit(void) +{ + device_created = 0; + sysfs_remove_group(wilc_kobj, &attr_group); + kobject_put(wilc_kobj); +} diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/wireless/microchip/wilc1000/wilcs02_loopback.c linux-microchip/drivers/net/wireless/microchip/wilc1000/wilcs02_loopback.c --- linux-6.6.51/drivers/net/wireless/microchip/wilc1000/wilcs02_loopback.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/net/wireless/microchip/wilc1000/wilcs02_loopback.c 2024-11-26 14:04:18.564303380 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +#include <linux/irq.h> +#include <linux/kthread.h> +#include <linux/firmware.h> +#include <linux/netdevice.h> +#include <linux/inetdevice.h> + +#include "cfg80211.h" +#include "wlan_cfg.h" +#include "wilcs02_loopback.h" +#ifdef WILC_S02_TEST_BUS_INTERFACE +u8 test_frame[WILC_S02_TEST_FRAME_COUNT][WILC_S02_TEST_FRAME_SIZE]; +u32 test_in_progress; +static u32 test_run_cnt; +static u8 fmt_str[4096] = {0}; + +extern int wilc_wlan_txq_add_cfg_pkt(struct wilc_vif *vif, u8 *buffer, + u32 buffer_size); + +int time_compare(struct timespec64 *time1, struct timespec64 *time2) +{ + if (time1->tv_sec < time2->tv_sec) + return -1; + if (time1->tv_sec > time2->tv_sec) + return 1; + if (time1->tv_nsec < time2->tv_nsec) + return -1; + if (time1->tv_nsec > time2->tv_nsec) + return 1; + + return 0; +} + +int time_diff(struct timespec64 *time1, struct timespec64 *time2, + struct timespec64 *diff) +{ + int past = 0; + int cmp = 0; + + cmp = time_compare(time1, time2); + if (cmp == 0) { + diff->tv_sec = 0; + diff->tv_nsec = 0; + past = 1; + } else if (cmp == 1) { + diff->tv_sec = time1->tv_sec - time2->tv_sec; + diff->tv_nsec = time1->tv_nsec; + + if (diff->tv_nsec < time2->tv_nsec) { + diff->tv_sec -= 1; + diff->tv_nsec += 1000000; + } + diff->tv_nsec = diff->tv_nsec - time2->tv_nsec; + } else { + diff->tv_sec = time2->tv_sec - time1->tv_sec; + diff->tv_nsec = time2->tv_nsec; + if (diff->tv_nsec < time1->tv_nsec) { + diff->tv_sec -= 1; + diff->tv_nsec += 1000000; + } + diff->tv_nsec = diff->tv_nsec - time1->tv_nsec; + past = 1; + } + return past; +} + +static void wilc_test_frame_tx_complete(void *priv, int status) +{ + pr_debug("%s tx frame copy completed\n", __func__); +} + +void wilc_test_bus_interface(struct wilc_vif *vif, u32 frame_size, + u32 frame_count) +{ + int i, ret; + struct wilc *wilc = 0; + u32 test_frame_size; + struct tx_complete_data *tx_data = NULL; + + if (!vif) { + pr_err("Test Failed: vif is null"); + return; + } + + test_frame_size = ALIGN((frame_size + ETH_ETHERNET_HDR_OFFSET), 4) & 0xFFFF; + + pr_info("TEST::STARTED ->wilc_test_bus_interface %d %d", test_frame_size, + frame_size); + + wilc = vif->wilc; + ret = wilc->hif_func->hif_write_reg(wilc, + wilc->vmm_ctl.host_vmm_tx_ctl, + 0x6 | (test_frame_size << 8)); + if (ret) { + pr_err("fail write reg host_vmm_ctl.."); + return; + } + for (i = 0; i < frame_size; i++) + test_frame[0][i] = i % 0xFF; + + test_in_progress = 0; + + tx_data = kmalloc(sizeof(*tx_data), GFP_ATOMIC); + if (!tx_data) + return; + + tx_data->buff = (u8 *)&test_frame[0]; + tx_data->size = frame_size; + + if (!wilc_wlan_txq_add_net_pkt(vif->ndev, tx_data, tx_data->buff, frame_size, + wilc_test_frame_tx_complete)) { + pr_err("Failed to send the test packet"); + return; + } + test_run_cnt++; + + // Required only for bulk loopback test + for (i = 1; i < frame_count; i++) { + memcpy(&test_frame[i], &test_frame[0][0], frame_size); + tx_data->buff = (u8 *)&test_frame[i][0]; + tx_data->size = frame_size; + + if (!wilc_wlan_txq_add_net_pkt(vif->ndev, tx_data, tx_data->buff, + frame_size, + wilc_test_frame_tx_complete)) { + pr_err("Failed to send the test packet"); + return; + } + test_run_cnt++; + } + test_in_progress = 1; +} + +int vspfunc(u8 *buffer, char *format, ...) +{ + va_list aptr; + int ret; + + va_start(aptr, format); + ret = vsprintf(buffer, format, aptr); + va_end(aptr); + return ret; +} + +void print_hex_string(char *buf, int len) +{ + int i; + int offset = 0; + + if (len == 0) { + vspfunc(fmt_str, "<empty string>"); return; + } + + for (i = 0; i < len; i++) { + offset += vspfunc(fmt_str + offset, "%02x ", + *((unsigned char *)buf + i)); + + if ((i & 0x1f) == 31) { + pr_debug("%s", fmt_str); + offset = 0; + } + } + + if (offset) + pr_err("%s", fmt_str); +} + +static void wilc_check_result(struct wilc *wilc, u8 *buffer, int size, + u32 frame_size) +{ + u32 offset = ETH_ETHERNET_HDR_OFFSET; + u32 i; + static u32 tested_frame_count; + + while ((offset + frame_size) <= size) { + for (i = 0; i < frame_size; i++) + if (buffer[offset + i] != test_frame[0][i]) + break; + if (i == frame_size) { + pr_info("Packet-%d Received correctly", + tested_frame_count + 1); + tested_frame_count++; + } else { + pr_err("Packet-%d Failed at offset = %d", + tested_frame_count + 1, i); + print_hex_string((buffer + offset + i), (frame_size - i)); + break; + } + + offset += ALIGN((frame_size + ETH_ETHERNET_HDR_OFFSET), 4); + } + + pr_info("TEST: Total RECEIVED frames: %d received size = %d %d", + tested_frame_count, size, test_run_cnt); + + if (tested_frame_count == WILC_S02_TEST_FRAME_COUNT - 1) + pr_info("TEST: COMPLETED::Success"); +} + +void wilc_test_verify_result(struct wilc *wilc, u8 *buffer, int size) +{ + int ret; + struct wilc_vif *vif = wilc_get_wl_to_vif(wilc); + + if (size > (WILC_S02_TEST_2BLOCK_FRAME_SIZE + ETH_ETHERNET_HDR_OFFSET) + 8) { + pr_info("BULK LOOPBACK TEST:\n Test Packet Sent to host: size=:%d %d %d", + WILC_S02_TEST_FRAME_SIZE, size, ETH_CONFIG_PKT_HDR_OFFSET); + wilc_check_result(wilc, buffer, size, WILC_S02_TEST_FRAME_SIZE); + + //Test2 - block transfer loopback + wilc_test_bus_interface(vif, WILC_S02_TEST_2BLOCK_FRAME_SIZE, 1); + } else if (size == ALIGN((WILC_S02_TEST_2BLOCK_FRAME_SIZE + ETH_ETHERNET_HDR_OFFSET), 4)) { + pr_info("2-BLOCK LOOPBACK TEST:\n Test Packet Sent to host: size= :%d", + WILC_S02_TEST_2BLOCK_FRAME_SIZE); + wilc_check_result(wilc, buffer, size, + WILC_S02_TEST_2BLOCK_FRAME_SIZE); + + //Test2 - block transfer loopback + wilc_test_bus_interface(vif, WILC_S02_TEST_1BLOCK_FRAME_SIZE, 1); + + } else if (size == ALIGN((WILC_S02_TEST_1BLOCK_FRAME_SIZE + ETH_ETHERNET_HDR_OFFSET), 4)) { + pr_info("1-BLOCK LOOPBACK TEST:\n Test Packet Sent to host: size= :%d", + WILC_S02_TEST_1BLOCK_FRAME_SIZE); + wilc_check_result(wilc, buffer, size, + WILC_S02_TEST_1BLOCK_FRAME_SIZE); + + //Test2 - block transfer loopback + wilc_test_bus_interface(vif, WILC_S02_TEST_BYTE_FRAME_SIZE, 1); + } else if (size == ALIGN((WILC_S02_TEST_BYTE_FRAME_SIZE + ETH_ETHERNET_HDR_OFFSET), 4)) { + pr_info("BYTE LOOPBACK TEST:\n Test Packet Sent to host: size= :%d", + WILC_S02_TEST_BYTE_FRAME_SIZE); + wilc_check_result(wilc, buffer, size, WILC_S02_TEST_BYTE_FRAME_SIZE); + } else { + /* stop the bus interface test */ + ret = wilc->hif_func->hif_write_reg(wilc, + wilc->vmm_ctl.host_vmm_tx_ctl, 0x6); + if (ret) + pr_err("fail write reg host_vmm_ctl.."); + } +} +#endif diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/wireless/microchip/wilc1000/wilcs02_loopback.h linux-microchip/drivers/net/wireless/microchip/wilc1000/wilcs02_loopback.h --- linux-6.6.51/drivers/net/wireless/microchip/wilc1000/wilcs02_loopback.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/net/wireless/microchip/wilc1000/wilcs02_loopback.h 2024-11-26 14:04:18.564303380 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +#ifndef WILC_S02_LOOP_BACK_H +#define WILC_S02_LOOP_BACK_H + +#define WILC_S02_TEST_BUS_INTERFACE + +//ETH_ETHERNET_HDR_OFFSET ~ 34 + +#define WILC_S02_TEST_FRAME_SIZE (1580 - ETH_ETHERNET_HDR_OFFSET) +#define WILC_S02_TEST_2BLOCK_FRAME_SIZE (1024 - ETH_ETHERNET_HDR_OFFSET) +#define WILC_S02_TEST_1BLOCK_FRAME_SIZE (512 - ETH_ETHERNET_HDR_OFFSET) +#define WILC_S02_TEST_BYTE_FRAME_SIZE (500 - ETH_ETHERNET_HDR_OFFSET) + +#define WILC_S02_TEST_FRAME_COUNT 5 + +extern u8 test_frame[WILC_S02_TEST_FRAME_COUNT][WILC_S02_TEST_FRAME_SIZE]; +extern u32 test_in_progress; + +void wilc_test_bus_interface(struct wilc_vif *vif, u32 frame_size, u32 + frame_count); +void wilc_test_verify_result(struct wilc *wilc, u8 *buffer, int size); +#endif diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/wireless/microchip/wilc1000/wlan.c linux-microchip/drivers/net/wireless/microchip/wilc1000/wlan.c --- linux-6.6.51/drivers/net/wireless/microchip/wilc1000/wlan.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/net/wireless/microchip/wilc1000/wlan.c 2024-11-26 14:04:18.564303380 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:14 @ #include "wlan_cfg.h" #define WAKE_UP_TRIAL_RETRY 10000 +#define CMD_RETRY_LIMIT 200 -static inline bool is_wilc1000(u32 id) -{ - return (id & (~WILC_CHIP_REV_FIELD)) == WILC_1000_BASE_ID; -} - -static inline void acquire_bus(struct wilc *wilc, enum bus_acquire acquire) +void acquire_bus(struct wilc *wilc, enum bus_acquire acquire, int source) { mutex_lock(&wilc->hif_cs); - if (acquire == WILC_BUS_ACQUIRE_AND_WAKEUP && wilc->power_save_mode) - chip_wakeup(wilc); + if (acquire == WILC_BUS_ACQUIRE_AND_WAKEUP) + chip_wakeup(wilc, source); } -static inline void release_bus(struct wilc *wilc, enum bus_release release) +void release_bus(struct wilc *wilc, enum bus_release release, int source) { - if (release == WILC_BUS_RELEASE_ALLOW_SLEEP && wilc->power_save_mode) - chip_allow_sleep(wilc); + if (release == WILC_BUS_RELEASE_ALLOW_SLEEP) + chip_allow_sleep(wilc, source); mutex_unlock(&wilc->hif_cs); } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:69 @ list_add_tail(&tqe->list, &wilc->txq[q_num].txq_head.list); wilc->txq_entries += 1; wilc->txq[q_num].count++; + PRINT_INFO(vif->ndev, TX_DBG, "Number of entries in TxQ = %d\n", + wilc->txq_entries); spin_unlock_irqrestore(&wilc->txq_spinlock, flags); + PRINT_INFO(vif->ndev, TX_DBG, "Wake the txq_handling\n"); complete(&wilc->txq_event); } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:91 @ list_add(&tqe->list, &wilc->txq[q_num].txq_head.list); wilc->txq_entries += 1; wilc->txq[q_num].count++; + PRINT_INFO(vif->ndev, TX_DBG, "Number of entries in TxQ = %d\n", + wilc->txq_entries); spin_unlock_irqrestore(&wilc->txq_spinlock, flags); mutex_unlock(&wilc->txq_add_to_head_cs); complete(&wilc->txq_event); + PRINT_INFO(vif->ndev, TX_DBG, "Wake up the txq_handler\n"); } #define NOT_TCP_ACK (-1) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:113 @ f->ack_session_info[f->tcp_session].src_port = src_prt; f->ack_session_info[f->tcp_session].dst_port = dst_prt; f->tcp_session++; + PRINT_INFO(vif->ndev, TCP_ENH, "TCP Session %d to Ack %d\n", + f->tcp_session, seq); } } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:223 @ if (f->pending_acks[i].ack_num < bigger_ack_num) { struct txq_entry_t *tqe; + PRINT_INFO(vif->ndev, TCP_ENH, "DROP ACK: %u\n", + f->pending_acks[i].ack_num); tqe = f->pending_acks[i].txqe; if (tqe) { wilc_wlan_txq_remove(wilc, tqe->q_num, tqe); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:248 @ spin_unlock_irqrestore(&wilc->txq_spinlock, flags); while (dropped > 0) { - wait_for_completion_timeout(&wilc->txq_event, - msecs_to_jiffies(1)); + if (!wait_for_completion_timeout(&wilc->txq_event, + msecs_to_jiffies(1))) + PRINT_ER(vif->ndev, "completion timedout\n"); dropped--; } } +static struct net_device *get_if_handler(struct wilc *wilc, u8 *mac_header) +{ + struct net_device *mon_netdev = NULL; + struct wilc_vif *vif; + struct ieee80211_hdr *h = (struct ieee80211_hdr *)mac_header; + + list_for_each_entry_rcu(vif, &wilc->vif_list, list) { + if (vif->iftype == WILC_STATION_MODE) + if (ether_addr_equal_unaligned(h->addr2, vif->bssid)) + return vif->ndev; + if (vif->iftype == WILC_AP_MODE) + if (ether_addr_equal_unaligned(h->addr1, vif->bssid)) + return vif->ndev; + if (vif->iftype == WILC_MONITOR_MODE) + mon_netdev = vif->ndev; + } + + if (!mon_netdev) + pr_warn("%s Invalid handle\n", __func__); + return mon_netdev; +} + void wilc_enable_tcp_ack_filter(struct wilc_vif *vif, bool value) { vif->ack_filter.enabled = value; } -static int wilc_wlan_txq_add_cfg_pkt(struct wilc_vif *vif, u8 *buffer, +int wilc_wlan_txq_add_cfg_pkt(struct wilc_vif *vif, u8 *buffer, u32 buffer_size) { struct txq_entry_t *tqe; struct wilc *wilc = vif->wilc; - netdev_dbg(vif->ndev, "Adding config packet ...\n"); +#ifdef WILC_S02_TEST_BUS_INTERFACE + if (is_test_mode && test_in_progress){ + return 0; + } +#endif + + PRINT_INFO(vif->ndev, TX_DBG, "Adding config packet ...\n"); if (wilc->quit) { netdev_dbg(vif->ndev, "Return due to clear function\n"); complete(&wilc->cfg_event); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:316 @ tqe->ack_idx = NOT_TCP_ACK; tqe->vif = vif; + PRINT_INFO(vif->ndev, TX_DBG, + "Adding the config packet at the Queue tail\n"); + wilc_wlan_txq_add_to_head(vif, AC_VO_Q, tqe); return 1; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:417 @ if (!ratio) return -EINVAL; - for (i = 0; i < NQUEUES; i++) + for (i = 0; i < NQUEUES; i++) { if (wl->txq[i].fw.count > max_count) max_count = wl->txq[i].fw.count; + } + + if (max_count > ECDA_MAX_LIMIT) + max_count = ECDA_MAX_LIMIT; - for (i = 0; i < NQUEUES; i++) - ratio[i] = max_count - wl->txq[i].fw.count; + for (i = 0; i < NQUEUES; i++) { + if (wl->txq[i].fw.count > ECDA_MAX_LIMIT) + ratio[i] = 0; + else + ratio[i] = max_count - wl->txq[i].fw.count; + } return 0; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:470 @ u8 q_num; wilc = vif->wilc; +#ifdef WILC_S02_TEST_BUS_INTERFACE + if (is_test_mode && test_in_progress) + return 0; +#endif if (wilc->quit) { + PRINT_INFO(vif->ndev, TX_DBG, + "drv is quitting, return from net_pkt\n"); tx_complete_fn(tx_data, 0); return 0; } if (!wilc->initialized) { + PRINT_INFO(vif->ndev, TX_DBG, + "not_init, return from net_pkt\n"); tx_complete_fn(tx_data, 0); return 0; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:492 @ tqe = kmalloc(sizeof(*tqe), GFP_ATOMIC); if (!tqe) { + PRINT_INFO(vif->ndev, TX_DBG, + "malloc failed, return from net_pkt\n"); tx_complete_fn(tx_data, 0); return 0; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:504 @ tqe->priv = tx_data; tqe->vif = vif; +#ifdef WILC_S02_TEST_BUS_INTERFACE + if(is_test_mode){ + q_num = 0; + tqe->ack_idx = NOT_TCP_ACK; + wilc_wlan_txq_add_to_tail(dev, q_num, tqe); + return 1; + } +#endif q_num = ac_classify(wilc, tx_data->skb); tqe->q_num = q_num; if (ac_change(wilc, &q_num)) { + PRINT_INFO(vif->ndev, GENERIC_DBG, + "No suitable non-ACM queue\n"); tx_complete_fn(tx_data, 0); kfree(tqe); return 0; } if (is_ac_q_limit(wilc, q_num)) { + PRINT_INFO(vif->ndev, TX_DBG, + "Adding mgmt packet at the Queue tail\n"); tqe->ack_idx = NOT_TCP_ACK; if (vif->ack_filter.enabled) tcp_process(dev, tqe); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:545 @ struct wilc_vif *vif = netdev_priv(dev); struct wilc *wilc; - wilc = vif->wilc; +#ifdef WILC_S02_TEST_BUS_INTERFACE + if (is_test_mode && test_in_progress) + return 0; +#endif + wilc = vif->wilc; if (wilc->quit) { + PRINT_INFO(vif->ndev, TX_DBG, "drv is quitting\n"); tx_complete_fn(priv, 0); return 0; } if (!wilc->initialized) { + PRINT_INFO(vif->ndev, TX_DBG, "wilc not_init\n"); tx_complete_fn(priv, 0); return 0; } tqe = kmalloc(sizeof(*tqe), GFP_ATOMIC); if (!tqe) { + PRINT_INFO(vif->ndev, TX_DBG, "Queue malloc failed\n"); tx_complete_fn(priv, 0); return 0; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:577 @ tqe->q_num = AC_BE_Q; tqe->ack_idx = NOT_TCP_ACK; tqe->vif = vif; + + PRINT_INFO(vif->ndev, TX_DBG, "Adding Mgmt packet to Queue tail\n"); wilc_wlan_txq_add_to_tail(dev, AC_VO_Q, tqe); return 1; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:640 @ return rqe; } -void chip_allow_sleep(struct wilc *wilc) +static int chip_allow_sleep_wilcs02(struct wilc *wilc, int source) +{ + return 0; +} + +static int chip_allow_sleep_wilc1000(struct wilc *wilc, int source) { u32 reg = 0; const struct wilc_hif_func *hif_func = wilc->hif_func; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:655 @ u32 trials = 100; int ret; - if (wilc->io_type == WILC_HIF_SDIO) { - wakeup_reg = WILC_SDIO_WAKEUP_REG; - wakeup_bit = WILC_SDIO_WAKEUP_BIT; + if (wilc->io_type == WILC_HIF_SDIO || + wilc->io_type == WILC_HIF_SDIO_GPIO_IRQ) { + wakeup_reg = WILC1000_SDIO_WAKEUP_REG; + wakeup_bit = WILC1000_SDIO_WAKEUP_BIT; from_host_to_fw_reg = WILC_SDIO_HOST_TO_FW_REG; from_host_to_fw_bit = WILC_SDIO_HOST_TO_FW_BIT; to_host_from_fw_reg = WILC_SDIO_FW_TO_HOST_REG; to_host_from_fw_bit = WILC_SDIO_FW_TO_HOST_BIT; } else { - wakeup_reg = WILC_SPI_WAKEUP_REG; - wakeup_bit = WILC_SPI_WAKEUP_BIT; + wakeup_reg = WILC1000_SPI_WAKEUP_REG; + wakeup_bit = WILC1000_SPI_WAKEUP_BIT; from_host_to_fw_reg = WILC_SPI_HOST_TO_FW_REG; from_host_to_fw_bit = WILC_SPI_HOST_TO_FW_BIT; to_host_from_fw_reg = WILC_SPI_FW_TO_HOST_REG; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:675 @ while (--trials) { ret = hif_func->hif_read_reg(wilc, to_host_from_fw_reg, ®); if (ret) - return; + return ret; if ((reg & to_host_from_fw_bit) == 0) break; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:685 @ /* Clear bit 1 */ ret = hif_func->hif_read_reg(wilc, wakeup_reg, ®); if (ret) - return; + return ret; if (reg & wakeup_bit) { reg &= ~wakeup_bit; ret = hif_func->hif_write_reg(wilc, wakeup_reg, reg); if (ret) - return; + return ret; } ret = hif_func->hif_read_reg(wilc, from_host_to_fw_reg, ®); if (ret) - return; + return ret; if (reg & from_host_to_fw_bit) { reg &= ~from_host_to_fw_bit; ret = hif_func->hif_write_reg(wilc, from_host_to_fw_reg, reg); if (ret) - return; + return ret; + } + return 0; +} + +static int chip_allow_sleep_wilc3000(struct wilc *wilc, int source) +{ + u32 reg = 0; + int ret; + const struct wilc_hif_func *hif_func = wilc->hif_func; + + if (wilc->io_type == WILC_HIF_SDIO || + wilc->io_type == WILC_HIF_SDIO_GPIO_IRQ) { + ret = hif_func->hif_read_reg(wilc, WILC3000_SDIO_WAKEUP_REG, + ®); + if (ret) + return ret; + ret = hif_func->hif_write_reg(wilc, WILC3000_SDIO_WAKEUP_REG, + reg & ~WILC3000_SDIO_WAKEUP_BIT); + if (ret) + return ret; + } else { + ret = hif_func->hif_read_reg(wilc, WILC3000_SPI_WAKEUP_REG, + ®); + if (ret) + return ret; + ret = hif_func->hif_write_reg(wilc, WILC3000_SPI_WAKEUP_REG, + reg & ~WILC3000_SPI_WAKEUP_BIT); + if (ret) + return ret; } + return 0; +} + +void chip_allow_sleep(struct wilc *wilc, int source) +{ + int ret = 0; + + if (((source == DEV_WIFI) && (wilc->keep_awake[DEV_BT] == true)) || + ((source == DEV_BT) && (wilc->keep_awake[DEV_WIFI] == true))) + pr_warn("Another device is preventing allow sleep operation. request source is %s\n", + (source == DEV_WIFI ? "Wifi" : "BT")); + else + if (wilc->chip == WILC_1000) + ret = chip_allow_sleep_wilc1000(wilc, source); + else if (wilc->chip == WILC_3000) + ret = chip_allow_sleep_wilc3000(wilc, source); + else + ret = chip_allow_sleep_wilcs02(wilc, source); + if (!ret) + wilc->keep_awake[source] = false; } -EXPORT_SYMBOL_GPL(chip_allow_sleep); -void chip_wakeup(struct wilc *wilc) +static void chip_wakeup_wilc1000(struct wilc *wilc, int source) { u32 ret = 0; u32 clk_status_val = 0, trials = 0; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:763 @ u32 from_host_to_fw_reg, from_host_to_fw_bit; const struct wilc_hif_func *hif_func = wilc->hif_func; - if (wilc->io_type == WILC_HIF_SDIO) { - wakeup_reg = WILC_SDIO_WAKEUP_REG; - wakeup_bit = WILC_SDIO_WAKEUP_BIT; - clk_status_reg = WILC_SDIO_CLK_STATUS_REG; - clk_status_bit = WILC_SDIO_CLK_STATUS_BIT; + if (wilc->io_type == WILC_HIF_SDIO || + wilc->io_type == WILC_HIF_SDIO_GPIO_IRQ) { + wakeup_reg = WILC1000_SDIO_WAKEUP_REG; + wakeup_bit = WILC1000_SDIO_WAKEUP_BIT; + clk_status_reg = WILC1000_SDIO_CLK_STATUS_REG; + clk_status_bit = WILC1000_SDIO_CLK_STATUS_BIT; from_host_to_fw_reg = WILC_SDIO_HOST_TO_FW_REG; from_host_to_fw_bit = WILC_SDIO_HOST_TO_FW_BIT; } else { - wakeup_reg = WILC_SPI_WAKEUP_REG; - wakeup_bit = WILC_SPI_WAKEUP_BIT; - clk_status_reg = WILC_SPI_CLK_STATUS_REG; - clk_status_bit = WILC_SPI_CLK_STATUS_BIT; + wakeup_reg = WILC1000_SPI_WAKEUP_REG; + wakeup_bit = WILC1000_SPI_WAKEUP_BIT; + clk_status_reg = WILC1000_SPI_CLK_STATUS_REG; + clk_status_bit = WILC1000_SPI_CLK_STATUS_BIT; from_host_to_fw_reg = WILC_SPI_HOST_TO_FW_REG; from_host_to_fw_bit = WILC_SPI_HOST_TO_FW_BIT; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:796 @ ret = hif_func->hif_read_reg(wilc, clk_status_reg, &clk_status_val); if (ret) { - pr_err("Bus error %d %x\n", ret, clk_status_val); + pr_err("Bus error %d %x", ret, clk_status_val); return; } if (clk_status_val & clk_status_bit) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:805 @ trials++; } if (trials >= WAKE_UP_TRIAL_RETRY) { - pr_err("Failed to wake-up the chip\n"); + pr_err("Failed to wake-up the chip"); return; } /* Sometimes spi fail to read clock regs after reading @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:814 @ if (wilc->io_type == WILC_HIF_SPI) wilc->hif_func->hif_reset(wilc); } -EXPORT_SYMBOL_GPL(chip_wakeup); -void host_wakeup_notify(struct wilc *wilc) +static void chip_wakeup_wilc3000(struct wilc *wilc, int source) +{ + u32 wakeup_reg_val, clk_status_reg_val, trials = 0; + u32 wakeup_reg, wakeup_bit; + u32 clk_status_reg, clk_status_bit; + int wake_seq_trials = 5; + const struct wilc_hif_func *hif_func = wilc->hif_func; + + if (wilc->io_type == WILC_HIF_SDIO || + wilc->io_type == WILC_HIF_SDIO_GPIO_IRQ) { + wakeup_reg = WILC3000_SDIO_WAKEUP_REG; + wakeup_bit = WILC3000_SDIO_WAKEUP_BIT; + clk_status_reg = WILC3000_SDIO_CLK_STATUS_REG; + clk_status_bit = WILC3000_SDIO_CLK_STATUS_BIT; + } else { + wakeup_reg = WILC3000_SPI_WAKEUP_REG; + wakeup_bit = WILC3000_SPI_WAKEUP_BIT; + clk_status_reg = WILC3000_SPI_CLK_STATUS_REG; + clk_status_bit = WILC3000_SPI_CLK_STATUS_BIT; + } + + hif_func->hif_read_reg(wilc, wakeup_reg, &wakeup_reg_val); + do { + hif_func->hif_write_reg(wilc, wakeup_reg, wakeup_reg_val | + wakeup_bit); + /* Check the clock status */ + hif_func->hif_read_reg(wilc, clk_status_reg, + &clk_status_reg_val); + + /* + * in case of clocks off, wait 1ms, and check it again. + * if still off, wait for another 1ms, for a total wait of 3ms. + * If still off, redo the wake up sequence + */ + while ((clk_status_reg_val & clk_status_bit) == 0 && + (++trials % 4) != 0) { + /* Wait for the chip to stabilize*/ + usleep_range(1000, 1100); + + /* + * Make sure chip is awake. This is an extra step that + * can be removed later to avoid the bus access + * overhead + */ + hif_func->hif_read_reg(wilc, clk_status_reg, + &clk_status_reg_val); + } + /* in case of failure, Reset the wakeup bit to introduce a new + * edge on the next loop + */ + if ((clk_status_reg_val & clk_status_bit) == 0) { + hif_func->hif_write_reg(wilc, wakeup_reg, + wakeup_reg_val & (~wakeup_bit)); + /* added wait before wakeup sequence retry */ + usleep_range(200, 300); + } + } while (((clk_status_reg_val & clk_status_bit) == 0) + && (wake_seq_trials-- > 0)); + if (!wake_seq_trials) + dev_err(wilc->dev, "clocks still OFF. Wake up failed\n"); + wilc->keep_awake[source] = true; +} + +static void chip_wakeup_wilcs02(struct wilc *wilc, int source) { - acquire_bus(wilc, WILC_BUS_ACQUIRE_ONLY); - wilc->hif_func->hif_write_reg(wilc, WILC_CORTUS_INTERRUPT_2, 1); - release_bus(wilc, WILC_BUS_RELEASE_ONLY); } -EXPORT_SYMBOL_GPL(host_wakeup_notify); -void host_sleep_notify(struct wilc *wilc) +void chip_wakeup(struct wilc *wilc, int source) { - acquire_bus(wilc, WILC_BUS_ACQUIRE_ONLY); - wilc->hif_func->hif_write_reg(wilc, WILC_CORTUS_INTERRUPT_1, 1); - release_bus(wilc, WILC_BUS_RELEASE_ONLY); + if (wilc->chip == WILC_1000) + chip_wakeup_wilc1000(wilc, source); + else if (wilc->chip == WILC_3000) + chip_wakeup_wilc3000(wilc, source); + else + chip_wakeup_wilcs02(wilc, source); +} + +void host_wakeup_notify(struct wilc *wilc, int source) +{ + acquire_bus(wilc, WILC_BUS_ACQUIRE_ONLY, source); + if (wilc->chip == WILC_1000) + wilc->hif_func->hif_write_reg(wilc, WILC1000_CORTUS_INTERRUPT_2, + 1); + else if (wilc->chip == WILC_3000) + wilc->hif_func->hif_write_reg(wilc, WILC3000_CORTUS_INTERRUPT_2, + 1); + release_bus(wilc, WILC_BUS_RELEASE_ONLY, source); +} + +void host_sleep_notify(struct wilc *wilc, int source) +{ + acquire_bus(wilc, WILC_BUS_ACQUIRE_ONLY, source); + if (wilc->chip == WILC_1000) + wilc->hif_func->hif_write_reg(wilc, WILC1000_CORTUS_INTERRUPT_1, + 1); + else if (wilc->chip == WILC_3000) + wilc->hif_func->hif_write_reg(wilc, WILC3000_CORTUS_INTERRUPT_1, + 1); + release_bus(wilc, WILC_BUS_RELEASE_ONLY, source); } -EXPORT_SYMBOL_GPL(host_sleep_notify); int wilc_wlan_handle_txq(struct wilc *wilc, u32 *txq_count) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:923 @ u32 sum; u32 reg; u8 ac_desired_ratio[NQUEUES] = {0, 0, 0, 0}; - u8 ac_preserve_ratio[NQUEUES] = {1, 1, 1, 1}; + u8 ac_preserve_ratio[NQUEUES] = {2, 2, 2, 2}; u8 *num_pkts_to_add; u8 vmm_entries_ac[WILC_VMM_TBL_SIZE]; u32 offset = 0; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:940 @ u8 *txb = wilc->tx_buffer; struct wilc_vif *vif; + if (!wilc->txq_entries) { + *txq_count = 0; + return 0; + } + if (wilc->quit) goto out_update_cnt; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:972 @ continue; ac_exist = 1; - for (k = 0; (k < num_pkts_to_add[ac]) && - (!max_size_over) && tqe_q[ac]; k++) { + for (k = 0; (!max_size_over && tqe_q[ac]) && + (tqe_q[ac]->type != WILC_NET_PKT || (k < num_pkts_to_add[ac])); k++) { if (i >= (WILC_VMM_TBL_SIZE - 1)) { max_size_over = 1; break; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1014 @ goto out_unlock; vmm_table[i] = 0x0; - acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP); + acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_WIFI); counter = 0; func = wilc->hif_func; do { - ret = func->hif_read_reg(wilc, WILC_HOST_TX_CTRL, ®); + reg = 0; + ret = func->hif_read_reg(wilc, wilc->vmm_ctl.host_wmm_stat, ®); if (ret) break; - if ((reg & 0x1) == 0) { + /* wmm stat received */ + if ((wilc->chip == WILC_S02 && (reg & 0x1) == 0x1) || + (wilc->chip != WILC_S02 && (reg & 0x1) == 0x0)) { ac_update_fw_ac_pkt_info(wilc, reg); break; } counter++; - if (counter > 200) { + if (counter > CMD_RETRY_LIMIT) { counter = 0; - ret = func->hif_write_reg(wilc, WILC_HOST_TX_CTRL, 0); + ret = func->hif_write_reg(wilc, + wilc->vmm_ctl.host_tx_ctl, 0); break; } } while (!wilc->quit); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1045 @ timeout = 200; do { ret = func->hif_block_tx(wilc, - WILC_VMM_TBL_RX_SHADOW_BASE, + wilc->vmm_ctl.vmm_tbl_rx_shadow_base, (u8 *)vmm_table, ((i + 1) * 4)); if (ret) break; - ret = func->hif_write_reg(wilc, WILC_HOST_VMM_CTL, 0x2); - if (ret) - break; + if (wilc->chip == WILC_S02) { + ret = wilc->hif_func->hif_write_reg(wilc, + wilc->vmm_ctl.host_vmm_tx_ctl, + WILC_S02_HOST_MALLOC); + if (ret) { + pr_err("fail write reg host_vmm_ctl"); + break; + } + + do { + ret = func->hif_read_reg(wilc, + wilc->vmm_ctl.host_vmm_rx_ctl, + ®); + if (ret) + break; + + if ((reg >> 2) & 0x1) { + entries = ((reg >> 3) & 0x3f); + break; + } + } while (--timeout); + } else if (wilc->chip == WILC_1000) { + ret = wilc->hif_func->hif_write_reg(wilc, + wilc->vmm_ctl.host_vmm_tx_ctl, + 0x2); + if (ret) + break; - do { - ret = func->hif_read_reg(wilc, WILC_HOST_VMM_CTL, ®); + do { + ret = func->hif_read_reg(wilc, + wilc->vmm_ctl.host_vmm_tx_ctl, + ®); + if (ret) + break; + if (FIELD_GET(WILC_VMM_ENTRY_AVAILABLE, reg)) { + entries = FIELD_GET(WILC_VMM_ENTRY_COUNT, + reg); + break; + } + } while (--timeout); + } else { + ret = func->hif_write_reg(wilc, + wilc->vmm_ctl.host_vmm_tx_ctl, + 0); if (ret) break; - if (FIELD_GET(WILC_VMM_ENTRY_AVAILABLE, reg)) { - entries = FIELD_GET(WILC_VMM_ENTRY_COUNT, reg); + + /* interrupt firmware */ + ret = func->hif_write_reg(wilc, + WILC_INTERRUPT_CORTUS_0, + 1); + if (ret) break; - } - } while (--timeout); + + do { + ret = func->hif_read_reg(wilc, + WILC_INTERRUPT_CORTUS_0, + ®); + if (ret) + break; + + if (reg == 0) { + /* Get the entries */ + + ret = func->hif_read_reg(wilc, + wilc->vmm_ctl.host_vmm_tx_ctl, + ®); + if (ret) + break; + + entries = FIELD_GET(WILC_VMM_ENTRY_COUNT, + reg); + break; + } + } while (--timeout); + } + if (timeout <= 0) { - ret = func->hif_write_reg(wilc, WILC_HOST_VMM_CTL, 0x0); + if (wilc->chip != WILC_S02) + ret = func->hif_write_reg(wilc, + wilc->vmm_ctl.host_vmm_tx_ctl, 0x0); break; } if (ret) break; - if (entries == 0) { - ret = func->hif_read_reg(wilc, WILC_HOST_TX_CTRL, ®); + if (entries == 0 && wilc->chip != WILC_S02) { + ret = func->hif_read_reg(wilc, wilc->vmm_ctl.host_tx_ctl, ®); if (ret) break; + reg &= ~BIT(0); - ret = func->hif_write_reg(wilc, WILC_HOST_TX_CTRL, reg); + ret = func->hif_write_reg(wilc, wilc->vmm_ctl.host_tx_ctl, reg); } } while (0); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1157 @ * the packet from tx queue. */ ret = WILC_VMM_ENTRY_FULL_RETRY; + if (!timeout && wilc->chip == WILC_S02) + wilc->hif_func->hif_write_reg(wilc, + wilc->vmm_ctl.host_vmm_tx_ctl, + DISBLE_RX_VMM); goto out_release_bus; } - release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP); - + release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI); + schedule(); offset = 0; i = 0; do { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1203 @ } else if (tqe->type == WILC_NET_PKT) { int prio = tqe->q_num; + wilc->txq[prio].fw.count++; + bssid = tqe->vif->bssid; buffer_offset = ETH_ETHERNET_HDR_OFFSET; memcpy(&txb[offset + 4], &prio, sizeof(prio)); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1225 @ vif->ack_filter.pending_acks[tqe->ack_idx].txqe = NULL; kfree(tqe); } while (--entries); - for (i = 0; i < NQUEUES; i++) - wilc->txq[i].fw.count += ac_pkt_num_to_chip[i]; - acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP); - ret = func->hif_clear_int_ext(wilc, ENABLE_TX_VMM); - if (ret) - goto out_release_bus; + acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_WIFI); + + if (wilc->chip != WILC_S02) { + ret = func->hif_clear_int_ext(wilc, ENABLE_TX_VMM); + + if (ret) + goto out_release_bus; + } ret = func->hif_block_tx_ext(wilc, 0, txb, offset); + if (!ret) + cfg_packet_timeout = 0; + out_release_bus: - release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP); + release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI); out_unlock: mutex_unlock(&wilc->txq_add_to_head_cs); + schedule(); out_update_cnt: *txq_count = wilc->txq_entries; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1261 @ int is_cfg_packet; u8 *buff_ptr; +#ifdef WILC_S02_TEST_BUS_INTERFACE + if (is_test_mode && test_in_progress) { + wilc_test_verify_result(wilc, buffer, size); + return; + } +#endif + do { buff_ptr = buffer + offset; header = get_unaligned_le32(buff_ptr); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1282 @ if (pkt_offset & IS_MANAGMEMENT) { buff_ptr += HOST_HDR_OFFSET; - wilc_wfi_mgmt_rx(wilc, buff_ptr, pkt_len, - pkt_offset & IS_MGMT_AUTH_PKT); + + if (pkt_offset & IS_MON_PKT) { + struct wilc_vif *vif; + + vif = wilc_get_vif_from_type(wilc, + WILC_MONITOR_MODE); + if (vif) + wilc_wfi_monitor_rx(vif->ndev, buff_ptr, + pkt_len); + } else { + wilc_wfi_mgmt_rx(wilc, buff_ptr, pkt_len, + pkt_offset & IS_MGMT_AUTH_PKT); + } } else { if (!is_cfg_packet) { - wilc_frmw_to_host(wilc, buff_ptr, pkt_len, - pkt_offset); + struct net_device *wilc_netdev; + struct wilc_vif *vif; + int srcu_idx; + + srcu_idx = srcu_read_lock(&wilc->srcu); + wilc_netdev = get_if_handler(wilc, buff_ptr); + + if (!wilc_netdev) { + pr_err("%s: wilc_netdev in wilc is NULL\n", + __func__); + srcu_read_unlock(&wilc->srcu, srcu_idx); + return; + } + vif = netdev_priv(wilc_netdev); + wilc_frmw_to_host(vif, buff_ptr, pkt_len, + pkt_offset, PKT_STATUS_NEW); + srcu_read_unlock(&wilc->srcu, srcu_idx); } else { struct wilc_cfg_rsp rsp; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1351 @ kfree(rqe); } - if (wilc->quit) + if (wilc->quit) { + pr_info("%s Quitting. Exit handle RX queue\n", + __func__); complete(&wilc->cfg_event); + } } static void wilc_unknown_isr_ext(struct wilc *wilc) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1372 @ int ret = 0; struct rxq_entry_t *rqe; - size = FIELD_GET(WILC_INTERRUPT_DATA_SIZE, int_status) << 2; + if (wilc->chip != WILC_S02) + size = FIELD_GET(WILC_INTERRUPT_DATA_SIZE, int_status) << 2; + else + size = int_status; while (!size && retries < 10) { wilc->hif_func->hif_read_size(wilc, &size); - size = FIELD_GET(WILC_INTERRUPT_DATA_SIZE, size) << 2; + + if (wilc->chip != WILC_S02) + size = FIELD_GET(WILC_INTERRUPT_DATA_SIZE, size) << 2; + retries++; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1396 @ wilc->hif_func->hif_clear_int_ext(wilc, DATA_INT_CLR | ENABLE_RX_VMM); ret = wilc->hif_func->hif_block_rx_ext(wilc, 0, buffer, size); - if (ret) + if (ret) { + pr_err("%s: fail block rx\n", __func__); return; + } offset += size; wilc->rx_buffer_offset = offset; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1417 @ { u32 int_status; - acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP); + if (wilc->close) + return; + + acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_WIFI); wilc->hif_func->hif_read_int(wilc, &int_status); - if (int_status & DATA_INT_EXT) + if (wilc->chip == WILC_S02 || (wilc->chip != WILC_S02 && int_status & + DATA_INT_EXT)) wilc_wlan_handle_isr_ext(wilc, int_status); - if (!(int_status & (ALL_INT_EXT))) + if (wilc->chip != WILC_S02 && !(int_status & (ALL_INT_EXT))) { + pr_warn("%s,>> UNKNOWN_INTERRUPT - 0x%08x\n", __func__, + int_status); wilc_unknown_isr_ext(wilc); + } - release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP); + release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI); } -EXPORT_SYMBOL_GPL(wilc_handle_isr); int wilc_wlan_firmware_download(struct wilc *wilc, const u8 *buffer, u32 buffer_size) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1444 @ u8 *dma_buffer; int ret = 0; u32 reg = 0; + int timeout = CMD_RETRY_LIMIT; blksz = BIT(12); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1455 @ offset = 0; pr_debug("%s: Downloading firmware size = %d\n", __func__, buffer_size); - acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP); - - wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, ®); - reg &= ~BIT(10); - ret = wilc->hif_func->hif_write_reg(wilc, WILC_GLB_RESET_0, reg); - wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, ®); - if (reg & BIT(10)) - pr_err("%s: Failed to reset\n", __func__); - release_bus(wilc, WILC_BUS_RELEASE_ONLY); + acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_WIFI); + if (wilc->chip != WILC_S02) { + wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, ®); + reg &= ~BIT(10); + ret = wilc->hif_func->hif_write_reg(wilc, WILC_GLB_RESET_0, reg); + wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, ®); + if (reg & BIT(10)) + pr_err("%s: Failed to reset\n", __func__); + } + release_bus(wilc, WILC_BUS_RELEASE_ONLY, DEV_WIFI); do { - addr = get_unaligned_le32(&buffer[offset]); - size = get_unaligned_le32(&buffer[offset + 4]); - acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP); - offset += 8; + if (wilc->chip == WILC_S02) { + addr = wilc->vmm_ctl.vmm_tbl_fw_update_base; + size = buffer_size; + offset = 0; + } else { + addr = get_unaligned_le32(&buffer[offset]); + size = get_unaligned_le32(&buffer[offset + 4]); + offset += 8; + } + acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_WIFI); + while (((int)size) && (offset < buffer_size)) { if (size <= blksz) size2 = size; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1490 @ if (ret) break; - addr += size2; + if (wilc->chip == WILC_S02) { + ret = wilc->hif_func->hif_write_reg(wilc, + wilc->vmm_ctl.host_vmm_tx_ctl, + WILC_S02_FW_UPGRADE | (offset << 8)); + if (ret) { + pr_err("fail write reg host_vmm_ctl..\n"); + ret = -EINVAL; + break; + } + + timeout = 50000; + do { + ret = wilc->hif_func->hif_read_reg(wilc, + wilc->vmm_ctl.host_vmm_rx_ctl, + ®); + if (ret) + break; + + if ((reg >> 1) & 0x3) + break; + + } while (--timeout); + + if ((reg >> 1) & 0x2) { + pr_err("The best firmware already installed on WILCS02"); + release_bus(wilc, + WILC_BUS_RELEASE_ALLOW_SLEEP, + DEV_WIFI); + goto fail; + } + + if (timeout <= 0 || ret) { + pr_err("timeout = %d ret = %d..\n", timeout, ret); + ret = -EINVAL; + break; + } + } else { + addr += size2; + } offset += size2; size -= size2; } - release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP); + release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI); if (ret) { pr_err("%s Bus error\n", __func__); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1541 @ pr_debug("%s Offset = %d\n", __func__, offset); } while (offset < buffer_size); + if (wilc->chip == WILC_S02) { + pr_err("FW reset after download"); + ret = wilc_s02_reset_firmware(wilc, WILC_S02_SOFT_RESET | (WILC_S02_FLASH_RESET << 8)); + } fail: - kfree(dma_buffer); + return (ret < 0) ? ret : 0; +} + +int wilc_wlan_start_wilcs02_fw(struct wilc *wilc) +{ + u32 reg = 0; + int ret; + + ret = wilc->hif_func->hif_write_reg(wilc, + WILC_HOST_GP_REG, + (WILC_MSG_MAC_OPEN | ((reg & GENMASK(15, 0)) < 16))); + if (ret) + pr_err("fail write reg host_vmm_ctl"); + pr_info("%s start firmware response [%d]", __func__, ret); + return ret; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1572 @ int ret; u32 chipid; - if (wilc->io_type == WILC_HIF_SDIO) { + if (wilc->io_type == WILC_HIF_SDIO || + wilc->io_type == WILC_HIF_SDIO_GPIO_IRQ) { reg = 0; reg |= BIT(3); } else if (wilc->io_type == WILC_HIF_SPI) { reg = 1; } - acquire_bus(wilc, WILC_BUS_ACQUIRE_ONLY); + acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_WIFI); ret = wilc->hif_func->hif_write_reg(wilc, WILC_VMM_CORE_CFG, reg); if (ret) goto release; reg = 0; - if (wilc->io_type == WILC_HIF_SDIO && wilc->dev_irq_num) + if (wilc->io_type == WILC_HIF_SDIO_GPIO_IRQ) reg |= WILC_HAVE_SDIO_IRQ_GPIO; + if (wilc->chip == WILC_3000) + reg |= WILC_HAVE_SLEEP_CLK_SRC_RTC; + ret = wilc->hif_func->hif_write_reg(wilc, WILC_GP_REG_1, reg); if (ret) goto release; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1613 @ wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, ®); release: - release_bus(wilc, WILC_BUS_RELEASE_ONLY); + release_bus(wilc, WILC_BUS_RELEASE_ONLY, DEV_WIFI); return ret; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1622 @ u32 reg = 0; int ret; - acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP); + if (wilc->chip == WILC_S02) { + acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_WIFI); + ret = wilc->hif_func->hif_write_reg(wilc, WILC_HOST_GP_REG, WILC_MSG_MAC_CLOSE); + if (ret) + pr_err("fail write reg host_vmm_ctl..\n"); - ret = wilc->hif_func->hif_read_reg(wilc, WILC_GP_REG_0, ®); - if (ret) { - netdev_err(vif->ndev, "Error while reading reg\n"); - goto release; + release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI); + return ret; } - ret = wilc->hif_func->hif_write_reg(wilc, WILC_GP_REG_0, - (reg | WILC_ABORT_REQ_BIT)); - if (ret) { - netdev_err(vif->ndev, "Error while writing reg\n"); + acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_WIFI); + + ret = wilc->hif_func->hif_read_reg(wilc, GLOBAL_MODE_CONTROL, ®); + if (ret) goto release; - } - ret = wilc->hif_func->hif_read_reg(wilc, WILC_FW_HOST_COMM, ®); + reg &= ~WILC_GLOBAL_MODE_ENABLE_WIFI; + ret = wilc->hif_func->hif_write_reg(wilc, GLOBAL_MODE_CONTROL, reg); + if (ret) + goto release; + + ret = wilc->hif_func->hif_read_reg(wilc, PWR_SEQ_MISC_CTRL, ®); + if (ret) + goto release; + + reg &= ~WILC_PWR_SEQ_ENABLE_WIFI_SLEEP; + ret = wilc->hif_func->hif_write_reg(wilc, PWR_SEQ_MISC_CTRL, reg); + if (ret) + goto release; + + ret = wilc->hif_func->hif_read_reg(wilc, WILC_GP_REG_0, ®); if (ret) { netdev_err(vif->ndev, "Error while reading reg\n"); goto release; } - reg = BIT(0); - ret = wilc->hif_func->hif_write_reg(wilc, WILC_FW_HOST_COMM, reg); + ret = wilc->hif_func->hif_write_reg(wilc, WILC_GP_REG_0, + (reg | WILC_ABORT_REQ_BIT)); if (ret) { netdev_err(vif->ndev, "Error while writing reg\n"); goto release; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1668 @ ret = 0; release: /* host comm is disabled - we can't issue sleep command anymore: */ - release_bus(wilc, WILC_BUS_RELEASE_ONLY); + release_bus(wilc, WILC_BUS_RELEASE_ONLY, DEV_WIFI); return ret; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1699 @ wilc->rx_buffer = NULL; kfree(wilc->tx_buffer); wilc->tx_buffer = NULL; - wilc->hif_func->hif_deinit(wilc); } static int wilc_wlan_cfg_commit(struct wilc_vif *vif, int type, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1747 @ return ret_size; } - netdev_dbg(vif->ndev, "%s: seqno[%d]\n", __func__, wilc->cfg_seq_no); + PRINT_INFO(vif->ndev, TX_DBG, + "[WILC]PACKET Commit with sequence number%d\n", + wilc->cfg_seq_no); if (wilc_wlan_cfg_commit(vif, WILC_CFG_SET, drv_handler)) ret_size = 0; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1804 @ return ret_size; } +unsigned int cfg_packet_timeout; int wilc_send_config_pkt(struct wilc_vif *vif, u8 mode, struct wid *wids, u32 count) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1812 @ int ret = 0; u32 drv = wilc_get_vif_idx(vif); +#ifdef WILC_S02_TEST_BUS_INTERFACE + if (is_test_mode && test_in_progress) + return 0; +#endif + + + if (wait_for_recovery) { + PRINT_INFO(vif->ndev, CORECONFIG_DBG, + "Host interface is suspended\n"); + while (wait_for_recovery) + msleep(300); + PRINT_INFO(vif->ndev, CORECONFIG_DBG, + "Host interface is resumed\n"); + } + if (mode == WILC_GET_CFG) { for (i = 0; i < count; i++) { - if (!wilc_wlan_cfg_get(vif, !i, - wids[i].id, - (i == count - 1), - drv)) { + PRINT_D(vif->ndev, CORECONFIG_DBG, + "Sending CFG packet [%d][%d]\n", !i, + (i == count - 1)); + if (!wilc_wlan_cfg_get(vif, !i, wids[i].id, + (i == count - 1), drv)) { ret = -ETIMEDOUT; + PRINT_ER(vif->ndev, "Get Timed out\n"); break; } } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1847 @ } } else if (mode == WILC_SET_CFG) { for (i = 0; i < count; i++) { - if (!wilc_wlan_cfg_set(vif, !i, - wids[i].id, - wids[i].val, + PRINT_INFO(vif->ndev, CORECONFIG_DBG, + "Sending config SET PACKET WID:%x\n", + wids[i].id); + if (!wilc_wlan_cfg_set(vif, !i, wids[i].id, wids[i].val, wids[i].size, - (i == count - 1), - drv)) { + (i == count - 1), drv)) { ret = -ETIMEDOUT; + PRINT_ER(vif->ndev, "Set Timed out\n"); break; } } } + cfg_packet_timeout = (ret < 0) ? cfg_packet_timeout + 1 : 0; + return ret; +} + +int wilcs02_init_vmm_registers(struct wilc *wilc) +{ + u32 reg; + int ret = 0; + + acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_WIFI); + + wilc->vmm_ctl.host_vmm_tx_ctl = WILC_HOST_GP_REG; + wilc->vmm_ctl.host_vmm_rx_ctl = WILC_ARM_GP_REG; + wilc->vmm_ctl.host_rx_ctl = WILC_ARM_GP_REG; + wilc->vmm_ctl.host_tx_ctl = WILC_HOST_GP_REG; + wilc->vmm_ctl.host_wmm_stat = WILC_S02_REG_ADDR_WMM_STAT; + ret = wilc->hif_func->hif_read_reg(wilc, WILC_S02_REG_ADDR_SHADOW_RX_TBL, + ®); + if (ret) { + pr_err("fail read reg WILC_S02_REG_ADDR_SHADOW_RX_TBL\n"); + goto end; + } + + wilc->vmm_ctl.vmm_tbl_rx_shadow_base = reg; + + ret = wilc->hif_func->hif_read_reg(wilc, WILC_S02_REG_ADDR_FW_UPDATE, + ®); + if (ret) { + pr_err("fail read reg WILC_S02_REG_ADDR_FW_UPDATE\n"); + goto end; + } + wilc->vmm_ctl.vmm_tbl_fw_update_base = reg; +end: + release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI); return ret; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1905 @ struct wilc_vif *vif = netdev_priv(dev); struct wilc *wilc = vif->wilc; - acquire_bus(wilc, WILC_BUS_ACQUIRE_ONLY); + acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP, DEV_WIFI); chipid = wilc_get_chipid(wilc, true); - if ((chipid & 0xfff) != 0xa0) { - ret = wilc->hif_func->hif_read_reg(wilc, - WILC_CORTUS_RESET_MUX_SEL, - ®); - if (ret) { - netdev_err(dev, "fail read reg 0x1118\n"); - goto release; - } - reg |= BIT(0); - ret = wilc->hif_func->hif_write_reg(wilc, - WILC_CORTUS_RESET_MUX_SEL, - reg); - if (ret) { - netdev_err(dev, "fail write reg 0x1118\n"); - goto release; - } - ret = wilc->hif_func->hif_write_reg(wilc, - WILC_CORTUS_BOOT_REGISTER, - WILC_CORTUS_BOOT_FROM_IRAM); + if (wilc->chip == WILC_S02) /* chip is initialized in probe */ + goto end; + + ret = wilc->hif_func->hif_read_reg(wilc, WILC_CORTUS_RESET_MUX_SEL, + ®); + if (ret) { + pr_err("fail read reg 0x1118\n"); + goto end; + } + + reg |= BIT(0); + ret = wilc->hif_func->hif_write_reg(wilc, WILC_CORTUS_RESET_MUX_SEL, + reg); + if (ret) { + pr_err("fail write reg 0x1118\n"); + goto end; + } + ret = wilc->hif_func->hif_write_reg(wilc, WILC_CORTUS_BOOT_REGISTER, + WILC_CORTUS_BOOT_FROM_IRAM); + if (ret) { + pr_err("fail write reg 0xc0000 ...\n"); + goto end; + } + + if (wilc->chip == WILC_3000) { + ret = wilc->hif_func->hif_read_reg(wilc, 0x207ac, ®); + PRINT_INFO(vif->ndev, INIT_DBG, "Bootrom sts = %x\n", reg); + ret = wilc->hif_func->hif_write_reg(wilc, 0x4f0000, + 0x71); if (ret) { - netdev_err(dev, "fail write reg 0xc0000\n"); - goto release; + pr_err("fail write reg 0x4f0000 ...\n"); + goto end; } } -release: - release_bus(wilc, WILC_BUS_RELEASE_ONLY); + wilc->vmm_ctl.host_vmm_tx_ctl = WILC_HOST_VMM_CTL; + wilc->vmm_ctl.host_vmm_rx_ctl = WILC_HOST_VMM_CTL; + wilc->vmm_ctl.host_tx_ctl = WILC_HOST_TX_CTRL; + wilc->vmm_ctl.host_rx_ctl = WILC_HOST_RX_CTRL; + wilc->vmm_ctl.vmm_tbl_rx_shadow_base = WILC_VMM_TBL_RX_SHADOW_BASE; + wilc->vmm_ctl.host_wmm_stat = WILC_HOST_TX_CTRL; + +end: + release_bus(wilc, WILC_BUS_RELEASE_ALLOW_SLEEP, DEV_WIFI); return ret; } u32 wilc_get_chipid(struct wilc *wilc, bool update) { + int ret; u32 chipid = 0; u32 rfrevid = 0; if (wilc->chipid == 0 || update) { - wilc->hif_func->hif_read_reg(wilc, WILC_CHIPID, &chipid); - wilc->hif_func->hif_read_reg(wilc, WILC_RF_REVISION_ID, - &rfrevid); - if (!is_wilc1000(chipid)) { - wilc->chipid = 0; + wilc->hif_func->hif_read_reg(wilc, WILC_S02_CHIP_ID, &chipid); + if (is_wilcs02_chip(chipid)) { + wilc->chipid = chipid; return wilc->chipid; } - if (chipid == WILC_1000_BASE_ID_2A) { /* 0x1002A0 */ - if (rfrevid != 0x1) - chipid = WILC_1000_BASE_ID_2A_REV1; - } else if (chipid == WILC_1000_BASE_ID_2B) { /* 0x1002B0 */ - if (rfrevid == 0x4) - chipid = WILC_1000_BASE_ID_2B_REV1; - else if (rfrevid != 0x3) - chipid = WILC_1000_BASE_ID_2B_REV2; - } + ret = wilc->hif_func->hif_read_reg(wilc, WILC3000_CHIP_ID, + &chipid); + if (!is_wilc3000(chipid)) { + wilc->hif_func->hif_read_reg(wilc, WILC_CHIPID, &chipid); + wilc->hif_func->hif_read_reg(wilc, WILC_RF_REVISION_ID, + &rfrevid); + + if (!is_wilc1000(chipid)) { + wilc->chipid = 0; + return wilc->chipid; + } + if (chipid == WILC_1000_BASE_ID_2A) { /* 0x1002A0 */ + if (rfrevid != 0x1) + chipid = WILC_1000_BASE_ID_2A_REV1; + } else if (chipid == WILC_1000_BASE_ID_2B) { /* 0x1002B0 */ + if (rfrevid == 0x4) + chipid = WILC_1000_BASE_ID_2B_REV1; + else if (rfrevid != 0x3) + chipid = WILC_1000_BASE_ID_2B_REV2; + } + } wilc->chipid = chipid; } return wilc->chipid; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2006 @ wilc->quit = 0; + PRINT_INFO(vif->ndev, INIT_DBG, "Initializing WILC_Wlan\n"); + if (!wilc->hif_func->hif_is_init(wilc)) { - acquire_bus(wilc, WILC_BUS_ACQUIRE_ONLY); + acquire_bus(wilc, WILC_BUS_ACQUIRE_ONLY, DEV_WIFI); ret = wilc->hif_func->hif_init(wilc, false); - release_bus(wilc, WILC_BUS_RELEASE_ONLY); + release_bus(wilc, WILC_BUS_RELEASE_ONLY, DEV_WIFI); if (ret) goto fail; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2029 @ if (!wilc->tx_buffer) { ret = -ENOBUFS; + PRINT_ER(vif->ndev, "Can't allocate Tx Buffer"); goto fail; } if (!wilc->rx_buffer) wilc->rx_buffer = kmalloc(WILC_RX_BUFF_SIZE, GFP_KERNEL); - + PRINT_D(vif->ndev, TX_DBG, "g_wlan.rx_buffer =%p\n", wilc->rx_buffer); if (!wilc->rx_buffer) { ret = -ENOBUFS; + PRINT_ER(vif->ndev, "Can't allocate Rx Buffer"); goto fail; } diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/wireless/microchip/wilc1000/wlan_cfg.c linux-microchip/drivers/net/wireless/microchip/wilc1000/wlan_cfg.c --- linux-6.6.51/drivers/net/wireless/microchip/wilc1000/wlan_cfg.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/net/wireless/microchip/wilc1000/wlan_cfg.c 2024-11-26 14:04:18.564303380 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:50 @ {WID_NIL, NULL} }; +static const struct wilc_cfg_bin g_cfg_bin[] = { + {WID_ANTENNA_SELECTION, NULL}, + {WID_NIL, NULL} +}; + #define WILC_RESP_MSG_TYPE_CONFIG_REPLY 'R' #define WILC_RESP_MSG_TYPE_STATUS_INFO 'I' #define WILC_RESP_MSG_TYPE_NETWORK_INFO 'N' @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:194 @ len = 2 + get_unaligned_le16(&info[2]); break; + case WID_BIN_DATA: + while (cfg->bin[i].id != WID_NIL && + cfg->bin[i].id != wid) + i++; + if (cfg->bin[i].id == wid) { + u16 length = (info[3] << 8) | info[2]; + u8 checksum = 0; + int j = 0; + + /* + * Compute the Checksum of received + * data field + */ + for (j = 0; j < length; j++) + checksum += info[4 + j]; + /* + * Verify the checksum of received BIN + * DATA + */ + if (checksum != info[4 + length]) { + pr_err("%s: Checksum Failed\n", + __func__); + return; + } + + memcpy(cfg->bin[i].bin, &info[2], length + 2); + /* + * value length + data length + + * checksum + */ + len = 2 + length + 1; + } + break; default: break; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:351 @ ret = size; } } + } else if (type == CFG_BIN_CMD) { /* binary command */ + while (cfg->bin[i].id != WID_NIL && cfg->bin[i].id != wid) + i++; + + if (cfg->bin[i].id == wid) { + u32 size = cfg->bin[i].bin[0] | + (cfg->bin[i].bin[1] << 8); + + if (buffer_size >= size) { + memcpy(buffer, &cfg->bin[i].bin[2], size); + ret = size; + } + } + } else { + pr_err("[CFG]: illegal type (%08x)\n", wid); } return ret; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:414 @ int wilc_wlan_cfg_init(struct wilc *wl) { struct wilc_cfg_str_vals *str_vals; + struct wilc_bin_vals *bin_vals; int i = 0; wl->cfg.b = kmemdup(g_cfg_byte, sizeof(g_cfg_byte), GFP_KERNEL); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:437 @ if (!str_vals) goto out_s; + wl->cfg.bin = kmemdup(g_cfg_bin, sizeof(g_cfg_bin), GFP_KERNEL); + if (!wl->cfg.bin) + goto out_str_val; + + bin_vals = kzalloc(sizeof(*bin_vals), GFP_KERNEL); + if (!bin_vals) + goto out_bin; + + /* store the string cfg parameters */ wl->cfg.str_vals = str_vals; /* store the string cfg parameters */ wl->cfg.s[i].id = WID_FIRMWARE_VERSION; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:459 @ i++; wl->cfg.s[i].id = WID_NIL; wl->cfg.s[i].str = NULL; + + /* store the bin parameters */ + i = 0; + wl->cfg.bin[i].id = WID_ANTENNA_SELECTION; + wl->cfg.bin[i].bin = bin_vals->antenna_param; + i++; + + wl->cfg.bin[i].id = WID_NIL; + wl->cfg.bin[i].bin = NULL; + return 0; +out_bin: + kfree(wl->cfg.bin); +out_str_val: + kfree(str_vals); out_s: kfree(wl->cfg.s); out_w: @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:493 @ kfree(wl->cfg.w); kfree(wl->cfg.s); kfree(wl->cfg.str_vals); + kfree(wl->cfg.bin); + kfree(wl->cfg.bin_vals); } diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/wireless/microchip/wilc1000/wlan_cfg.h linux-microchip/drivers/net/wireless/microchip/wilc1000/wlan_cfg.h --- linux-6.6.51/drivers/net/wireless/microchip/wilc1000/wlan_cfg.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/net/wireless/microchip/wilc1000/wlan_cfg.h 2024-11-26 14:04:18.564303380 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:30 @ u8 *str; }; +struct wilc_cfg_bin { + u16 id; + u8 *bin; +}; + struct wilc_cfg_str_vals { u8 mac_address[7]; u8 firmware_version[129]; u8 assoc_rsp[WILC_MAX_ASSOC_RESP_FRAME_SIZE]; }; +struct wilc_bin_vals { + u8 antenna_param[5]; +}; + struct wilc_cfg { struct wilc_cfg_byte *b; struct wilc_cfg_hword *hw; struct wilc_cfg_word *w; struct wilc_cfg_str *s; struct wilc_cfg_str_vals *str_vals; + struct wilc_cfg_bin *bin; + struct wilc_bin_vals *bin_vals; }; - +//#define WILCS02_TEST_BUS_INTERFACE struct wilc; int wilc_wlan_cfg_set_wid(u8 *frame, u32 offset, u16 id, u8 *buf, int size); int wilc_wlan_cfg_get_wid(u8 *frame, u32 offset, u16 id); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/wireless/microchip/wilc1000/wlan.h linux-microchip/drivers/net/wireless/microchip/wilc1000/wlan.h --- linux-6.6.51/drivers/net/wireless/microchip/wilc1000/wlan.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/net/wireless/microchip/wilc1000/wlan.h 2024-11-26 14:04:18.564303380 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:41 @ #define ETH_CONFIG_PKT_HDR_OFFSET (ETH_ETHERNET_HDR_OFFSET + \ ETH_CONFIG_PKT_HDR_LEN) +#define PKT_STATUS_NEW 0 +#define PKT_STATUS_BUFFERED 1 /******************************************** * @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:50 @ * ********************************************/ #define WILC_PERIPH_REG_BASE 0x1000 -#define WILC_CHANGING_VIR_IF 0x108c #define WILC_CHIPID WILC_PERIPH_REG_BASE #define WILC_GLB_RESET_0 (WILC_PERIPH_REG_BASE + 0x400) #define WILC_PIN_MUX_0 (WILC_PERIPH_REG_BASE + 0x408) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:57 @ #define WILC_HOST_RX_CTRL_0 (WILC_PERIPH_REG_BASE + 0x70) #define WILC_HOST_RX_CTRL_1 (WILC_PERIPH_REG_BASE + 0x74) #define WILC_HOST_VMM_CTL (WILC_PERIPH_REG_BASE + 0x78) +#define WILC_HOST_GP_REG 0x24 +#define WILC_ARM_GP_REG 0x28 +#define WILC_MSG_MAC_OPEN (0x1 | (0x1 << 8)) +#define WILC_MSG_MAC_CLOSE (0x1 | (0x2 << 8)) +#define WILC_MSG_MAC_RESET (0x1 | (0x3 << 8)) + #define WILC_HOST_RX_CTRL (WILC_PERIPH_REG_BASE + 0x80) #define WILC_HOST_RX_EXTRA_SIZE (WILC_PERIPH_REG_BASE + 0x84) #define WILC_HOST_TX_CTRL_1 (WILC_PERIPH_REG_BASE + 0x88) #define WILC_MISC (WILC_PERIPH_REG_BASE + 0x428) #define WILC_INTR_REG_BASE (WILC_PERIPH_REG_BASE + 0xa00) + +#define WILC_INTERRUPT_CORTUS_0 (WILC_PERIPH_REG_BASE + 0xa8) +#define WILC1000_CORTUS_INTERRUPT_1 (WILC_INTERRUPT_CORTUS_0 + 0x4) +#define WILC3000_CORTUS_INTERRUPT_1 (WILC_INTERRUPT_CORTUS_0 + 0x14) + +#define WILC1000_CORTUS_INTERRUPT_2 (WILC_INTERRUPT_CORTUS_0 + 0x8) +#define WILC3000_CORTUS_INTERRUPT_2 (WILC_INTERRUPT_CORTUS_0 + 0x18) + #define WILC_INTR_ENABLE WILC_INTR_REG_BASE #define WILC_INTR2_ENABLE (WILC_INTR_REG_BASE + 4) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:112 @ #define WILC_SPI_INT_STATUS (WILC_SPI_REG_BASE + 0x40) #define WILC_SPI_INT_CLEAR (WILC_SPI_REG_BASE + 0x44) -#define WILC_SPI_WAKEUP_REG 0x1 -#define WILC_SPI_WAKEUP_BIT BIT(1) +/* WILC1000 specific */ +#define WILC1000_SPI_WAKEUP_REG 0x1 +#define WILC1000_SPI_WAKEUP_BIT BIT(1) + +#define WILC1000_SPI_CLK_STATUS_REG 0x0f +#define WILC1000_SPI_CLK_STATUS_BIT BIT(2) + +/* WILC3000 specific */ +#define WILC3000_SPI_WAKEUP_REG 0x1 +#define WILC3000_SPI_WAKEUP_BIT BIT(1) + +#define WILC3000_SPI_CLK_STATUS_REG 0x13 +#define WILC3000_SPI_CLK_STATUS_BIT BIT(2) -#define WILC_SPI_CLK_STATUS_REG 0x0f -#define WILC_SPI_CLK_STATUS_BIT BIT(2) #define WILC_SPI_HOST_TO_FW_REG 0x0b #define WILC_SPI_HOST_TO_FW_BIT BIT(0) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:148 @ #define WILC_SDIO_CCCR_ABORT_RESET BIT(3) /* Vendor specific CCCR registers */ -#define WILC_SDIO_WAKEUP_REG 0xf0 -#define WILC_SDIO_WAKEUP_BIT BIT(0) +/* WILC1000 */ +#define WILC1000_SDIO_WAKEUP_REG 0xf0 +#define WILC1000_SDIO_WAKEUP_BIT BIT(0) + +#define WILC1000_SDIO_CLK_STATUS_REG 0xf1 +#define WILC1000_SDIO_CLK_STATUS_BIT BIT(0) + +#define WILC1000_SDIO_IRQ_FLAG_REG 0xf7 +#define WILC1000_SDIO_IRQ_CLEAR_FLAG_REG 0xf8 -#define WILC_SDIO_CLK_STATUS_REG 0xf1 -#define WILC_SDIO_CLK_STATUS_BIT BIT(0) +/* WILC3000 specific */ +#define WILC3000_SDIO_WAKEUP_REG 0xf0 +#define WILC3000_SDIO_WAKEUP_BIT BIT(0) +#define WILC3000_SDIO_CLK_STATUS_REG 0xf0 /* clk & wakeup are on same reg*/ +#define WILC3000_SDIO_CLK_STATUS_BIT BIT(4) + +#define WILC3000_SDIO_IRQ_FLAG_REG 0xfe +#define WILC3000_SDIO_IRQ_CLEAR_FLAG_REG 0xfe +#define WILC3000_SDIO_VMM_TBL_CTRL_REG 0xf1 + +/* Common vendor specific CCCR register */ #define WILC_SDIO_INTERRUPT_DATA_SZ_REG 0xf2 /* Read size (2 bytes) */ #define WILC_SDIO_VMM_TBL_CTRL_REG 0xf6 -#define WILC_SDIO_IRQ_FLAG_REG 0xf7 -#define WILC_SDIO_IRQ_CLEAR_FLAG_REG 0xf8 #define WILC_SDIO_HOST_TO_FW_REG 0xfa #define WILC_SDIO_HOST_TO_FW_BIT BIT(0) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:192 @ #define WILC_VMM_TBL_RX_SHADOW_BASE WILC_AHB_SHARE_MEM_BASE #define WILC_VMM_TBL_RX_SHADOW_SIZE 256 +#define VMM_TBL_RX_SHADOW_BASE 0xA0420 +#define VMM_TBL_RX_SHADOW_SIZE 256 -#define WILC_FW_HOST_COMM 0x13c0 #define WILC_GP_REG_0 0x149c #define WILC_GP_REG_1 0x14a0 +#define GLOBAL_MODE_CONTROL 0x1614 +#define PWR_SEQ_MISC_CTRL 0x3008 + +#define WILC_GLOBAL_MODE_ENABLE_WIFI BIT(0) +#define WILC_PWR_SEQ_ENABLE_WIFI_SLEEP BIT(28) + +#define COE_AUTO_PS_ON_NULL_PKT 0x160468 +#define COE_AUTO_PS_OFF_NULL_PKT 0x16046C +#define CCA_CTL_2 (0x160EF4) +#define CCA_CTL_7 (0x160F08) + #define WILC_HAVE_SDIO_IRQ_GPIO BIT(0) -#define WILC_HAVE_USE_PMU BIT(1) #define WILC_HAVE_SLEEP_CLK_SRC_RTC BIT(2) #define WILC_HAVE_SLEEP_CLK_SRC_XO BIT(3) -#define WILC_HAVE_EXT_PA_INV_TX_RX BIT(4) -#define WILC_HAVE_LEGACY_RF_SETTINGS BIT(5) -#define WILC_HAVE_XTAL_24 BIT(6) -#define WILC_HAVE_DISABLE_WILC_UART BIT(7) -#define WILC_HAVE_USE_IRQ_AS_HOST_WAKE BIT(8) - -#define WILC_CORTUS_INTERRUPT_BASE 0x10A8 -#define WILC_CORTUS_INTERRUPT_1 (WILC_CORTUS_INTERRUPT_BASE + 0x4) -#define WILC_CORTUS_INTERRUPT_2 (WILC_CORTUS_INTERRUPT_BASE + 0x8) /* tx control register 1 to 4 for RX */ #define WILC_REG_4_TO_1_RX 0x1e1c @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:221 @ #define WILC_CORTUS_RESET_MUX_SEL 0x1118 #define WILC_CORTUS_BOOT_REGISTER 0xc0000 +#define WILC3000_CHIP_ID 0x3b0000 +#define WILC_S02_CHIP_ID 0x1 +#define WILC_S02_FW_VERSION 0x2 + +/* WILC_S02- MSG Handler */ +#define WILC_S02_CANCEL_RX 0x0 +#define WILC_S02_START_WLAN 0x1 +#define WILC_S02_HOST_MALLOC 0x2 +#define WILC_S02_FW_UPGRADE 0x3 +#define WILC_S02_SOFT_RESET 0x4 +#define WILC_S02_SDIO_TEST 0x6 +#define WILC_S02_SDIO_VMM_CLEAR 0x7 +#define WILC_S02_REG_ADDR_WMM_STAT 0x9 + +/* type of soft resets */ +#define WILC_S02_RAM_RESET BIT(0) +#define WILC_S02_FLASH_RESET BIT(1) +#define WILC_S02_WLAN_RESET BIT(2) + +#define WILC_S02_REG_ADDR_FW_UPDATE 0x5 +#define WILC_S02_REG_ADDR_SHADOW_RX_TBL 0x6 + +#define WILC_S02_REG_ADDR_CFG 0x8 + #define WILC_CORTUS_BOOT_FROM_IRAM 0x71 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:257 @ #define WILC_1000_BASE_ID_2B_REV1 (WILC_1000_BASE_ID_2B + 1) #define WILC_1000_BASE_ID_2B_REV2 (WILC_1000_BASE_ID_2B + 2) +#define WILC_3000_BASE_ID 0x300000 +#define WILC_S02_BASE_ID 0x000053 + + #define WILC_CHIP_REV_FIELD GENMASK(11, 0) +static inline bool is_wilc1000(u32 id) +{ + return (id & (~WILC_CHIP_REV_FIELD)) == WILC_1000_BASE_ID; +} + +static inline bool is_wilc3000(u32 id) +{ + return (id & (~WILC_CHIP_REV_FIELD)) == WILC_3000_BASE_ID; +} + +static inline bool is_wilcs02_chip(u32 id) +{ + return (id & (WILC_CHIP_REV_FIELD)) == WILC_S02_BASE_ID; +} + + /******************************************** * * Wlan Defines @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:300 @ #define WILC_RX_BUFF_SIZE (96 * 1024) #define WILC_TX_BUFF_SIZE (64 * 1024) +#define GPIO_NUM_CHIP_EN 94 +#define GPIO_NUM_RESET 60 + #define NQUEUES 4 +#define ECDA_MAX_LIMIT 32 #define AC_BUFFER_SIZE 1000 #define VO_AC_COUNT_FIELD GENMASK(31, 25) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:353 @ #define INT_2 BIT(IRG_FLAGS_OFFSET + 2) #define INT_3 BIT(IRG_FLAGS_OFFSET + 3) #define INT_4 BIT(IRG_FLAGS_OFFSET + 4) -#define INT_5 BIT(IRG_FLAGS_OFFSET + 5) #define MAX_NUM_INT 5 #define IRG_FLAGS_MASK GENMASK(IRG_FLAGS_OFFSET + MAX_NUM_INT, \ IRG_FLAGS_OFFSET) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:387 @ #define DATA_INT_CLR CLR_INT0 +#define SDIO_MSG_VMM_INT_CLR 0x7 + #define ENABLE_RX_VMM (SEL_VMM_TBL1 | EN_VMM) +#define DISBLE_RX_VMM (SDIO_MSG_VMM_INT_CLR | 1 << 8) +#define DISBLE_TX_VMM (SDIO_MSG_VMM_INT_CLR | 2 << 8) #define ENABLE_TX_VMM (SEL_VMM_TBL0 | EN_VMM) /* time for expiring the completion of cfg packets */ #define WILC_CFG_PKTS_TIMEOUT msecs_to_jiffies(3000) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:400 @ #define IS_MANAGMEMENT_CALLBACK 0x080 #define IS_MGMT_STATUS_SUCCES 0x040 #define IS_MGMT_AUTH_PKT 0x010 +#define IS_MON_PKT 0x020 #define WILC_WID_TYPE GENMASK(15, 12) #define WILC_VMM_ENTRY_FULL_RETRY 1 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:446 @ int buffer_size; }; +enum wilc_chip_type { + WILC_1000, + WILC_3000, + WILC_S02, +}; + +struct wilc_vmm_ctl { + u32 vmm_tbl_rx_shadow_base; + u32 vmm_tbl_fw_update_base; + u32 host_vmm_rx_ctl; + u32 host_vmm_tx_ctl; + u32 host_tx_ctl; + u32 host_rx_ctl; + u32 host_wmm_stat; +}; + /******************************************** * * Host IF Structure @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:485 @ void (*disable_interrupt)(struct wilc *nic); int (*hif_reset)(struct wilc *wilc); bool (*hif_is_init)(struct wilc *wilc); + int (*hif_clear_init)(struct wilc *wilc); }; #define WILC_MAX_CFG_FRAME_SIZE 1468 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:518 @ int wilc_wlan_firmware_download(struct wilc *wilc, const u8 *buffer, u32 buffer_size); int wilc_wlan_start(struct wilc *wilc); +int wilc_wlan_start_wilcs02_fw(struct wilc *wilc); int wilc_wlan_stop(struct wilc *wilc, struct wilc_vif *vif); int wilc_wlan_txq_add_net_pkt(struct net_device *dev, struct tx_complete_data *tx_data, u8 *buffer, u32 buffer_size, void (*tx_complete_fn)(void *, int)); int wilc_wlan_handle_txq(struct wilc *wl, u32 *txq_count); +void wilc_wfi_handle_monitor_rx(struct wilc *wilc, u8 *buff, u32 size); void wilc_handle_isr(struct wilc *wilc); void wilc_wlan_cleanup(struct net_device *dev); int wilc_wlan_cfg_set(struct wilc_vif *vif, int start, u16 wid, u8 *buffer, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:535 @ int wilc_wlan_txq_add_mgmt_pkt(struct net_device *dev, void *priv, u8 *buffer, u32 buffer_size, void (*func)(void *, int)); void wilc_enable_tcp_ack_filter(struct wilc_vif *vif, bool value); -int wilc_wlan_get_num_conn_ifcs(struct wilc *wilc); netdev_tx_t wilc_mac_xmit(struct sk_buff *skb, struct net_device *dev); -void wilc_wfi_p2p_rx(struct wilc_vif *vif, u8 *buff, u32 size); +bool wilc_wfi_p2p_rx(struct wilc_vif *vif, u8 *buff, u32 size); bool wilc_wfi_mgmt_frame_rx(struct wilc_vif *vif, u8 *buff, u32 size); -void host_wakeup_notify(struct wilc *wilc); -void host_sleep_notify(struct wilc *wilc); -void chip_allow_sleep(struct wilc *wilc); -void chip_wakeup(struct wilc *wilc); +void host_wakeup_notify(struct wilc *wilc, int source); +void host_sleep_notify(struct wilc *wilc, int source); +void chip_allow_sleep(struct wilc *wilc, int source); +void chip_wakeup(struct wilc *wilc, int source); int wilc_send_config_pkt(struct wilc_vif *vif, u8 mode, struct wid *wids, u32 count); +void wilc_bt_init(struct wilc *wilc); +void wilc_bt_deinit(void); +void eap_buff_timeout(struct timer_list *t); +void acquire_bus(struct wilc *wilc, enum bus_acquire acquire, int source); +void release_bus(struct wilc *wilc, enum bus_release release, int source); int wilc_wlan_init(struct net_device *dev); u32 wilc_get_chipid(struct wilc *wilc, bool update); +int wilcs02_init_vmm_registers(struct wilc *wilc); #endif diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/net/wireless/microchip/wilc1000/wlan_if.h linux-microchip/drivers/net/wireless/microchip/wilc1000/wlan_if.h --- linux-6.6.51/drivers/net/wireless/microchip/wilc1000/wlan_if.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/net/wireless/microchip/wilc1000/wlan_if.h 2024-11-26 14:04:18.564303380 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:11 @ #define WILC_WLAN_IF_H #include <linux/netdevice.h> +#include "debugfs.h" #include "fw.h" #define WILC_MAX_ASSOC_RESP_FRAME_SIZE 512 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:21 @ * Wlan Configuration ID * ********************************************/ +#define FW_WILC3000_BLE "mchp/wilc3000_ble_firmware.bin" enum bss_types { WILC_FW_BSS_TYPE_INFRA = 0, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:42 @ WILC_FW_PREAMBLE_AUTO = 2, /* Auto Preamble Selection */ }; +#define DEV_WIFI 0 +#define DEV_BT 1 +#define DEV_MAX 2 + enum { WILC_FW_PASSIVE_SCAN = 0, WILC_FW_ACTIVE_SCAN = 1, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:200 @ WILC_FW_AUTH_REQ_IDX = 2 }; +enum { + WILC_FW_PRINT_LVL_ERROR = 1, + WILC_FW_PRINT_LVL_DEBUG = 2, + WILC_FW_PRINT_LVL_INFO = 3, + WILC_FW_PRINT_LVL_FUN_PT = 4, + WILC_FW_PRINT_LVL_MAX, +}; + enum wid_type { WID_CHAR = 0, WID_SHORT = 1, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:217 @ WID_BIN = 5, }; +enum { + ANTENNA1 = 0, + ANTENNA2 = 1, + DIVERSITY = 2, + NUM_ANT_MODE +}; + struct wid { u16 id; enum wid_type type; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:694 @ WID_TX_POWER = 0x00CE, WID_WOWLAN_TRIGGER = 0X00CF, WID_SET_MFP = 0x00D0, + WID_USE_PRIORITY_EAPOL = 0x00D1, WID_DEFAULT_MGMT_KEY_ID = 0x00D2, + WID_FW_PRINT_LEVEL = 0x0216, /* EMAC Short WID list */ /* RTS Threshold */ /* @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:831 @ WID_ADD_BEACON = 0x408a, WID_SETUP_MULTICAST_FILTER = 0x408b, + WID_ANTENNA_SELECTION = 0x408c, WID_EXTERNAL_AUTH_PARAM = 0x408d, + /* Miscellaneous WIDs */ WID_ALL = 0x7FFE, WID_MAX = 0xFFFF diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/of/configfs.c linux-microchip/drivers/of/configfs.c --- linux-6.6.51/drivers/of/configfs.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/of/configfs.c 2024-11-26 14:04:18.564303380 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Configfs entries for device-tree + * + * Copyright (C) 2013 - Pantelis Antoniou <panto@antoniou-consulting.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include <linux/ctype.h> +#include <linux/cpu.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_fdt.h> +#include <linux/spinlock.h> +#include <linux/sizes.h> +#include <linux/slab.h> +#include <linux/proc_fs.h> +#include <linux/configfs.h> +#include <linux/types.h> +#include <linux/stat.h> +#include <linux/limits.h> +#include <linux/file.h> +#include <linux/vmalloc.h> +#include <linux/firmware.h> + +#include "of_private.h" + +struct cfs_overlay_item { + struct config_item item; + + char path[PATH_MAX]; + + const struct firmware *fw; + struct device_node *overlay; + int ov_id; + + void *dtbo; + int dtbo_size; + + void *mem; +}; + +static DEFINE_MUTEX(overlay_lock); + +static int create_overlay(struct cfs_overlay_item *overlay, void *blob) +{ + int err; + + /* FIXME */ + err = of_overlay_fdt_apply(blob, overlay->dtbo_size, &overlay->ov_id, NULL); + if (err < 0) { + pr_err("%s: Failed to create overlay (err=%d)\n", + __func__, err); + return err; + } + + return err; +} + +static inline struct cfs_overlay_item + *to_cfs_overlay_item(struct config_item *item) +{ + return item ? container_of(item, struct cfs_overlay_item, item) : NULL; +} + +static ssize_t cfs_overlay_item_path_show(struct config_item *item, char *page) +{ + return sprintf(page, "%s\n", to_cfs_overlay_item(item)->path); +} + +static ssize_t cfs_overlay_item_path_store(struct config_item *item, + const char *page, size_t count) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + const char *p = page; + char *s; + int err; + + /* if it's set do not allow changes */ + if (overlay->path[0] != '\0' || overlay->dtbo_size > 0) + return -EPERM; + + /* copy to path buffer (and make sure it's always zero terminated */ + count = snprintf(overlay->path, sizeof(overlay->path) - 1, "%s", p); + overlay->path[sizeof(overlay->path) - 1] = '\0'; + + /* strip trailing newlines */ + s = overlay->path + strlen(overlay->path); + while (s > overlay->path && *--s == '\n') + *s = '\0'; + + pr_debug("%s: path is '%s'\n", __func__, overlay->path); + + err = request_firmware(&overlay->fw, overlay->path, NULL); + if (err != 0) + goto out_err; + + overlay->dtbo_size = overlay->fw->size; + err = create_overlay(overlay, (void *)overlay->fw->data); + if (err < 0) + goto out_err; + + return count; + +out_err: + + release_firmware(overlay->fw); + overlay->fw = NULL; + + overlay->path[0] = '\0'; + + return count; +} + +static ssize_t cfs_overlay_item_status_show(struct config_item *item, + char *page) +{ + return sprintf(page, "%s\n", to_cfs_overlay_item(item)->ov_id >= 0 ? + "applied" : "unapplied"); +} + +CONFIGFS_ATTR(cfs_overlay_item_, path); +CONFIGFS_ATTR_RO(cfs_overlay_item_, status); + +static struct configfs_attribute *cfs_overlay_attrs[] = { + &cfs_overlay_item_attr_path, + &cfs_overlay_item_attr_status, + NULL, +}; + +ssize_t cfs_overlay_item_dtbo_read(struct config_item *item, + void *buf, size_t max_count) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + + pr_debug("%s: buf=%p max_count=%zu\n", __func__, buf, max_count); + + if (!overlay->dtbo) + return 0; + + /* copy if buffer provided */ + if (buf) { + /* the buffer must be large enough */ + if (overlay->dtbo_size > max_count) + return -ENOSPC; + + memcpy(buf, overlay->dtbo, overlay->dtbo_size); + } + + return overlay->dtbo_size; +} + +ssize_t cfs_overlay_item_dtbo_write(struct config_item *item, + const void *buf, size_t count) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + int err; + + /* if it's set do not allow changes */ + if (overlay->path[0] != '\0' || overlay->dtbo_size > 0) + return -EPERM; + + /* copy the contents */ + overlay->dtbo = kmemdup(buf, count, GFP_KERNEL); + if (!overlay->dtbo) + return -ENOMEM; + + overlay->dtbo_size = count; + + err = create_overlay(overlay, overlay->dtbo); + if (err < 0) + goto out_err; + + return count; + +out_err: + kfree(overlay->dtbo); + overlay->dtbo = NULL; + overlay->dtbo_size = 0; + + return err; +} + +CONFIGFS_BIN_ATTR(cfs_overlay_item_, dtbo, NULL, SZ_1M); + +static struct configfs_bin_attribute *cfs_overlay_bin_attrs[] = { + &cfs_overlay_item_attr_dtbo, + NULL, +}; + +static void cfs_overlay_release(struct config_item *item) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + + if (overlay->ov_id >= 0) + of_overlay_remove(&overlay->ov_id); + if (overlay->fw) + release_firmware(overlay->fw); + /* kfree with NULL is safe */ + kfree(overlay->dtbo); + kfree(overlay->mem); + kfree(overlay); +} + +static struct configfs_item_operations cfs_overlay_item_ops = { + .release = cfs_overlay_release, +}; + +static struct config_item_type cfs_overlay_type = { + .ct_item_ops = &cfs_overlay_item_ops, + .ct_attrs = cfs_overlay_attrs, + .ct_bin_attrs = cfs_overlay_bin_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_item + *cfs_overlay_group_make_item(struct config_group *group, + const char *name) +{ + struct cfs_overlay_item *overlay; + + overlay = kzalloc(sizeof(*overlay), GFP_KERNEL); + if (!overlay) + return ERR_PTR(-ENOMEM); + overlay->ov_id = -1; + config_item_init_type_name(&overlay->item, name, &cfs_overlay_type); + + return &overlay->item; +} + +static void cfs_overlay_group_drop_item(struct config_group *group, + struct config_item *item) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + + config_item_put(&overlay->item); +} + +static struct configfs_group_operations overlays_ops = { + .make_item = cfs_overlay_group_make_item, + .drop_item = cfs_overlay_group_drop_item, +}; + +static struct config_item_type overlays_type = { + .ct_group_ops = &overlays_ops, + .ct_owner = THIS_MODULE, +}; + +static struct configfs_group_operations of_cfs_ops = { + /* empty - we don't allow anything to be created */ +}; + +static struct config_item_type of_cfs_type = { + .ct_group_ops = &of_cfs_ops, + .ct_owner = THIS_MODULE, +}; + +struct config_group of_cfs_overlay_group; + +static struct configfs_subsystem of_cfs_subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = "device-tree", + .ci_type = &of_cfs_type, + }, + }, + .su_mutex = __MUTEX_INITIALIZER(of_cfs_subsys.su_mutex), +}; + +static int __init of_cfs_init(void) +{ + int ret; + + config_group_init(&of_cfs_subsys.su_group); + config_group_init_type_name(&of_cfs_overlay_group, "overlays", + &overlays_type); + configfs_add_default_group(&of_cfs_overlay_group, + &of_cfs_subsys.su_group); + + ret = configfs_register_subsystem(&of_cfs_subsys); + if (ret != 0) { + pr_err("%s: failed to register subsys\n", __func__); + goto out; + } + pr_debug("%s: OK\n", __func__); +out: + return ret; +} +late_initcall(of_cfs_init); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/of/Kconfig linux-microchip/drivers/of/Kconfig --- linux-6.6.51/drivers/of/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/of/Kconfig 2024-11-26 14:04:18.564303380 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:105 @ config OF_NUMA bool + +config OF_CONFIGFS + bool "Device Tree Overlay ConfigFS interface" + select CONFIGFS_FS + depends on OF_OVERLAY + help + Select this option to enable simple user-space driven DT overlay + interface to support device tree manipulated at runtime. + Say Y here to include this support. + + If unsure, say N. + endif # OF diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/of/Makefile linux-microchip/drivers/of/Makefile --- linux-6.6.51/drivers/of/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/of/Makefile 2024-11-26 14:04:18.564303380 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ # SPDX-License-Identifier: GPL-2.0 obj-y = base.o cpu.o device.o module.o platform.o property.o obj-$(CONFIG_OF_KOBJ) += kobj.o +obj-$(CONFIG_OF_CONFIGFS) += configfs.o obj-$(CONFIG_OF_DYNAMIC) += dynamic.o obj-$(CONFIG_OF_FLATTREE) += fdt.o obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/of/of_reserved_mem.c linux-microchip/drivers/of/of_reserved_mem.c --- linux-6.6.51/drivers/of/of_reserved_mem.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/of/of_reserved_mem.c 2024-11-26 14:04:18.564303380 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:435 @ list_add(&rd->list, &of_rmem_assigned_device_list); mutex_unlock(&of_rmem_assigned_device_mutex); - dev_info(dev, "assigned reserved memory node %s\n", rmem->name); + // dev_info(dev, "assigned reserved memory node %s\n", rmem->name); } else { kfree(rd); } diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/pci/controller/pcie-microchip-host.c linux-microchip/drivers/pci/controller/pcie-microchip-host.c --- linux-6.6.51/drivers/pci/controller/pcie-microchip-host.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/pci/controller/pcie-microchip-host.c 2024-11-26 14:02:38.214506523 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:26 @ /* Number of MSI IRQs */ #define MC_MAX_NUM_MSI_IRQS 32 +#define MC_MAX_NUM_INBOUND_WINDOWS 8 +#define MC_ATT_MASK GENMASK_ULL(63, 31) + /* PCIe Bridge Phy and Controller Phy offsets */ #define MC_PCIE1_BRIDGE_ADDR 0x00008000u #define MC_PCIE1_CTRL_ADDR 0x0000a000u -#define MC_PCIE_BRIDGE_ADDR (MC_PCIE1_BRIDGE_ADDR) -#define MC_PCIE_CTRL_ADDR (MC_PCIE1_CTRL_ADDR) - /* PCIe Bridge Phy Regs */ #define PCIE_PCI_IRQ_DW0 0xa8 #define MSIX_CAP_MASK BIT(31) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:89 @ #define IMSI_ADDR 0x190 #define ISTATUS_MSI 0x194 +#define ATR_WINDOW_DESC_SIZE 32 +#define ATR_SIZE_SHIFT 1 +#define ATR_IMPL_ENABLE 1 + +#define ATR_PCIE_WIN0_SRCADDR 0x80000000 +#define ATR_PCIE_ATR_SIZE (512 * 1024 * 1024ul) +#define ATR_PCIE_NUM_WINDOWS 8 + /* PCIe Master table init defines */ #define ATR0_PCIE_WIN0_SRCADDR_PARAM 0x600u -#define ATR0_PCIE_ATR_SIZE 0x25 -#define ATR0_PCIE_ATR_SIZE_SHIFT 1 #define ATR0_PCIE_WIN0_SRC_ADDR 0x604u #define ATR0_PCIE_WIN0_TRSL_ADDR_LSB 0x608u #define ATR0_PCIE_WIN0_TRSL_ADDR_UDW 0x60cu #define ATR0_PCIE_WIN0_TRSL_PARAM 0x610u +enum { + TRSL_ID_PCIE_TXRX, + TRSL_ID_PCIE_CONFIG, + TRSL_ID_AXI4_LITE_MASTER, + TRSL_ID_AXI4_MASTER_0 = 4, + TRSL_ID_AXI4_MASTER_1, + TRSL_ID_AXI4_MASTER_2, + TRSL_ID_AXI4_MASTER_3, + TRSL_ID_AXI4_STREAM_0, + TRSL_ID_AXI4_STREAM_1, + TRSL_ID_AXI4_STREAM_2, + TRSL_ID_AXI4_STREAM_3, + TRSL_ID_INTERNAL_BRIDGE_REGISTERS +}; + +#define ATR0_PCIE_WIN0_TRSL_MASK_LSB 0x618u +#define ATR0_PCIE_WIN0_TRSL_MASK_UDW 0x61cu + /* PCIe AXI slave table init defines */ #define ATR0_AXI4_SLV0_SRCADDR_PARAM 0x800u -#define ATR_SIZE_SHIFT 1 -#define ATR_IMPL_ENABLE 1 #define ATR0_AXI4_SLV0_SRC_ADDR 0x804u #define ATR0_AXI4_SLV0_TRSL_ADDR_LSB 0x808u #define ATR0_AXI4_SLV0_TRSL_ADDR_UDW 0x80cu #define ATR0_AXI4_SLV0_TRSL_PARAM 0x810u -#define PCIE_TX_RX_INTERFACE 0x00000000u -#define PCIE_CONFIG_INTERFACE 0x00000001u - -#define ATR_ENTRY_SIZE 32 /* PCIe Controller Phy Regs */ #define SEC_ERROR_EVENT_CNT 0x20 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:228 @ [EVENT_LOCAL_ ## x] = { __stringify(x), s } #define PCIE_EVENT(x) \ - .base = MC_PCIE_CTRL_ADDR, \ .offset = PCIE_EVENT_INT, \ .mask_offset = PCIE_EVENT_INT, \ .mask_high = 1, \ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:235 @ .enb_mask = PCIE_EVENT_INT_ENB_MASK #define SEC_EVENT(x) \ - .base = MC_PCIE_CTRL_ADDR, \ .offset = SEC_ERROR_INT, \ .mask_offset = SEC_ERROR_INT_MASK, \ .mask = SEC_ERROR_INT_ ## x ## _INT, \ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:242 @ .enb_mask = 0 #define DED_EVENT(x) \ - .base = MC_PCIE_CTRL_ADDR, \ .offset = DED_ERROR_INT, \ .mask_offset = DED_ERROR_INT_MASK, \ .mask_high = 1, \ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:249 @ .enb_mask = 0 #define LOCAL_EVENT(x) \ - .base = MC_PCIE_BRIDGE_ADDR, \ .offset = ISTATUS_LOCAL, \ .mask_offset = IMASK_LOCAL, \ .mask_high = 0, \ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:281 @ DECLARE_BITMAP(used, MC_MAX_NUM_MSI_IRQS); }; +struct inbound_windows { + u64 axi_addr; + u64 pci_addr; + u64 size; +}; + struct mc_pcie { - void __iomem *axi_base_addr; + void __iomem *bridge_base_addr; + void __iomem *ctrl_base_addr; struct device *dev; struct irq_domain *intx_domain; struct irq_domain *event_domain; raw_spinlock_t lock; struct mc_msi msi; + u64 outbound_range_offset; + u32 num_inbound_windows; + struct inbound_windows inbound_windows[MC_MAX_NUM_INBOUND_WINDOWS]; }; struct cause { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:371 @ }; static struct { - u32 base; u32 offset; u32 mask; u32 shift; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:441 @ struct irq_chip *chip = irq_desc_get_chip(desc); struct device *dev = port->dev; struct mc_msi *msi = &port->msi; - void __iomem *bridge_base_addr = - port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; unsigned long status; u32 bit; int ret; chained_irq_enter(chip, desc); - status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); + status = readl_relaxed(port->bridge_base_addr + ISTATUS_LOCAL); if (status & PM_MSI_INT_MSI_MASK) { - writel_relaxed(status & PM_MSI_INT_MSI_MASK, bridge_base_addr + ISTATUS_LOCAL); - status = readl_relaxed(bridge_base_addr + ISTATUS_MSI); + writel_relaxed(status & PM_MSI_INT_MSI_MASK, + port->bridge_base_addr + ISTATUS_LOCAL); + status = readl_relaxed(port->bridge_base_addr + ISTATUS_MSI); for_each_set_bit(bit, &status, msi->num_vectors) { ret = generic_handle_domain_irq(msi->dev_domain, bit); if (ret) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:466 @ static void mc_msi_bottom_irq_ack(struct irq_data *data) { struct mc_pcie *port = irq_data_get_irq_chip_data(data); - void __iomem *bridge_base_addr = - port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; u32 bitpos = data->hwirq; - writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI); + writel_relaxed(BIT(bitpos), port->bridge_base_addr + ISTATUS_MSI); } static void mc_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:587 @ struct mc_pcie *port = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); struct device *dev = port->dev; - void __iomem *bridge_base_addr = - port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; unsigned long status; u32 bit; int ret; chained_irq_enter(chip, desc); - status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); + status = readl_relaxed(port->bridge_base_addr + ISTATUS_LOCAL); if (status & PM_MSI_INT_INTX_MASK) { status &= PM_MSI_INT_INTX_MASK; status >>= PM_MSI_INT_INTX_SHIFT; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:611 @ static void mc_ack_intx_irq(struct irq_data *data) { struct mc_pcie *port = irq_data_get_irq_chip_data(data); - void __iomem *bridge_base_addr = - port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); - writel_relaxed(mask, bridge_base_addr + ISTATUS_LOCAL); + writel_relaxed(mask, port->bridge_base_addr + ISTATUS_LOCAL); } static void mc_mask_intx_irq(struct irq_data *data) { struct mc_pcie *port = irq_data_get_irq_chip_data(data); - void __iomem *bridge_base_addr = - port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; unsigned long flags; u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); u32 val; raw_spin_lock_irqsave(&port->lock, flags); - val = readl_relaxed(bridge_base_addr + IMASK_LOCAL); + val = readl_relaxed(port->bridge_base_addr + IMASK_LOCAL); val &= ~mask; - writel_relaxed(val, bridge_base_addr + IMASK_LOCAL); + writel_relaxed(val, port->bridge_base_addr + IMASK_LOCAL); raw_spin_unlock_irqrestore(&port->lock, flags); } static void mc_unmask_intx_irq(struct irq_data *data) { struct mc_pcie *port = irq_data_get_irq_chip_data(data); - void __iomem *bridge_base_addr = - port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; unsigned long flags; u32 mask = BIT(data->hwirq + PM_MSI_INT_INTX_SHIFT); u32 val; raw_spin_lock_irqsave(&port->lock, flags); - val = readl_relaxed(bridge_base_addr + IMASK_LOCAL); + val = readl_relaxed(port->bridge_base_addr + IMASK_LOCAL); val |= mask; - writel_relaxed(val, bridge_base_addr + IMASK_LOCAL); + writel_relaxed(val, port->bridge_base_addr + IMASK_LOCAL); raw_spin_unlock_irqrestore(&port->lock, flags); } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:671 @ static u32 pcie_events(struct mc_pcie *port) { - void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; - u32 reg = readl_relaxed(ctrl_base_addr + PCIE_EVENT_INT); + u32 reg = readl_relaxed(port->ctrl_base_addr + PCIE_EVENT_INT); u32 val = 0; int i; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:683 @ static u32 sec_errors(struct mc_pcie *port) { - void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; - u32 reg = readl_relaxed(ctrl_base_addr + SEC_ERROR_INT); + u32 reg = readl_relaxed(port->ctrl_base_addr + SEC_ERROR_INT); u32 val = 0; int i; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:695 @ static u32 ded_errors(struct mc_pcie *port) { - void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; - u32 reg = readl_relaxed(ctrl_base_addr + DED_ERROR_INT); + u32 reg = readl_relaxed(port->ctrl_base_addr + DED_ERROR_INT); u32 val = 0; int i; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:707 @ static u32 local_events(struct mc_pcie *port) { - void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; - u32 reg = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL); + u32 reg = readl_relaxed(port->bridge_base_addr + ISTATUS_LOCAL); u32 val = 0; int i; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:769 @ void __iomem *addr; u32 mask; - addr = port->axi_base_addr + event_descs[event].base + - event_descs[event].offset; + if (event_descs[event].offset == ISTATUS_LOCAL) + addr = port->bridge_base_addr; + else + addr = port->ctrl_base_addr; + + addr += event_descs[event].offset; mask = event_descs[event].mask; mask |= event_descs[event].enb_mask; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:789 @ u32 mask; u32 val; - addr = port->axi_base_addr + event_descs[event].base + - event_descs[event].mask_offset; + if (event_descs[event].offset == ISTATUS_LOCAL) + addr = port->bridge_base_addr; + else + addr = port->ctrl_base_addr; + + addr += event_descs[event].mask_offset; mask = event_descs[event].mask; if (event_descs[event].enb_mask) { mask <<= PCIE_EVENT_INT_ENB_SHIFT; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:823 @ u32 mask; u32 val; - addr = port->axi_base_addr + event_descs[event].base + - event_descs[event].mask_offset; + if (event_descs[event].offset == ISTATUS_LOCAL) + addr = port->bridge_base_addr; + else + addr = port->ctrl_base_addr; + + addr += event_descs[event].mask_offset; mask = event_descs[event].mask; if (event_descs[event].enb_mask) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:954 @ return mc_allocate_msi_domains(port); } +static int mc_pcie_setup_inbound_ranges(struct platform_device *pdev, struct mc_pcie *port) +{ + phys_addr_t pcie_addr; + phys_addr_t axi_addr; + u32 atr_size; + u32 val; + int i; + + for (i = 0; i < port->num_inbound_windows; i++) { + atr_size = ilog2(port->inbound_windows[i].size) - 1; + atr_size &= GENMASK(5, 0); + + pcie_addr = port->inbound_windows[i].pci_addr; + + val = lower_32_bits(pcie_addr) & GENMASK(31, 12); + val |= (atr_size << ATR_SIZE_SHIFT); + val |= ATR_IMPL_ENABLE; + writel(val, port->bridge_base_addr + + ATR0_PCIE_WIN0_SRCADDR_PARAM + (i * ATR_WINDOW_DESC_SIZE)); + writel(upper_32_bits(pcie_addr), port->bridge_base_addr + + ATR0_PCIE_WIN0_SRC_ADDR + (i * ATR_WINDOW_DESC_SIZE)); + + axi_addr = port->inbound_windows[i].axi_addr; + + writel(lower_32_bits(axi_addr), port->bridge_base_addr + + ATR0_PCIE_WIN0_TRSL_ADDR_LSB + (i * ATR_WINDOW_DESC_SIZE)); + writel(upper_32_bits(axi_addr), port->bridge_base_addr + + ATR0_PCIE_WIN0_TRSL_ADDR_UDW + (i * ATR_WINDOW_DESC_SIZE)); + + writel(TRSL_ID_AXI4_MASTER_0, port->bridge_base_addr + + ATR0_PCIE_WIN0_TRSL_PARAM + (i * ATR_WINDOW_DESC_SIZE)); + } + + return 0; +} + static void mc_pcie_setup_window(void __iomem *bridge_base_addr, u32 index, phys_addr_t axi_addr, phys_addr_t pci_addr, size_t size) { - u32 atr_sz = ilog2(size) - 1; + u32 atr_size = ilog2(size) - 1; u32 val; if (index == 0) - val = PCIE_CONFIG_INTERFACE; + val = TRSL_ID_PCIE_CONFIG; else - val = PCIE_TX_RX_INTERFACE; + val = TRSL_ID_PCIE_TXRX; - writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + + writel(val, bridge_base_addr + (index * ATR_WINDOW_DESC_SIZE) + ATR0_AXI4_SLV0_TRSL_PARAM); - val = lower_32_bits(axi_addr) | (atr_sz << ATR_SIZE_SHIFT) | + val = lower_32_bits(axi_addr) | (atr_size << ATR_SIZE_SHIFT) | ATR_IMPL_ENABLE; - writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + + writel(val, bridge_base_addr + (index * ATR_WINDOW_DESC_SIZE) + ATR0_AXI4_SLV0_SRCADDR_PARAM); val = upper_32_bits(axi_addr); - writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + + writel(val, bridge_base_addr + (index * ATR_WINDOW_DESC_SIZE) + ATR0_AXI4_SLV0_SRC_ADDR); val = lower_32_bits(pci_addr); - writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + + writel(val, bridge_base_addr + (index * ATR_WINDOW_DESC_SIZE) + ATR0_AXI4_SLV0_TRSL_ADDR_LSB); val = upper_32_bits(pci_addr); - writel(val, bridge_base_addr + (index * ATR_ENTRY_SIZE) + + writel(val, bridge_base_addr + (index * ATR_WINDOW_DESC_SIZE) + ATR0_AXI4_SLV0_TRSL_ADDR_UDW); - - val = readl(bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM); - val |= (ATR0_PCIE_ATR_SIZE << ATR0_PCIE_ATR_SIZE_SHIFT); - writel(val, bridge_base_addr + ATR0_PCIE_WIN0_SRCADDR_PARAM); - writel(0, bridge_base_addr + ATR0_PCIE_WIN0_SRC_ADDR); } static int mc_pcie_setup_windows(struct platform_device *pdev, struct mc_pcie *port) { - void __iomem *bridge_base_addr = - port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; struct pci_host_bridge *bridge = platform_get_drvdata(pdev); struct resource_entry *entry; u64 pci_addr; - u32 index = 1; + u32 index = 1; /* Window 0 used for config space */ resource_list_for_each_entry(entry, &bridge->windows) { if (resource_type(entry->res) == IORESOURCE_MEM) { pci_addr = entry->res->start - entry->offset; - mc_pcie_setup_window(bridge_base_addr, index, - entry->res->start, pci_addr, - resource_size(entry->res)); + mc_pcie_setup_window(port->bridge_base_addr, index, + entry->res->start - port->outbound_range_offset, + pci_addr, resource_size(entry->res)); index++; } } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1046 @ static inline void mc_clear_secs(struct mc_pcie *port) { - void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; - - writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT, ctrl_base_addr + + writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT, port->ctrl_base_addr + SEC_ERROR_INT); - writel_relaxed(0, ctrl_base_addr + SEC_ERROR_EVENT_CNT); + writel_relaxed(0, port->ctrl_base_addr + SEC_ERROR_EVENT_CNT); } static inline void mc_clear_deds(struct mc_pcie *port) { - void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; - - writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT, ctrl_base_addr + + writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT, port->ctrl_base_addr + DED_ERROR_INT); - writel_relaxed(0, ctrl_base_addr + DED_ERROR_EVENT_CNT); + writel_relaxed(0, port->ctrl_base_addr + DED_ERROR_EVENT_CNT); } static void mc_disable_interrupts(struct mc_pcie *port) { - void __iomem *bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; - void __iomem *ctrl_base_addr = port->axi_base_addr + MC_PCIE_CTRL_ADDR; u32 val; /* Ensure ECC bypass is enabled */ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1067 @ ECC_CONTROL_RX_RAM_ECC_BYPASS | ECC_CONTROL_PCIE2AXI_RAM_ECC_BYPASS | ECC_CONTROL_AXI2PCIE_RAM_ECC_BYPASS; - writel_relaxed(val, ctrl_base_addr + ECC_CONTROL); + writel_relaxed(val, port->ctrl_base_addr + ECC_CONTROL); /* Disable SEC errors and clear any outstanding */ - writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT, ctrl_base_addr + + writel_relaxed(SEC_ERROR_INT_ALL_RAM_SEC_ERR_INT, port->ctrl_base_addr + SEC_ERROR_INT_MASK); mc_clear_secs(port); /* Disable DED errors and clear any outstanding */ - writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT, ctrl_base_addr + + writel_relaxed(DED_ERROR_INT_ALL_RAM_DED_ERR_INT, port->ctrl_base_addr + DED_ERROR_INT_MASK); mc_clear_deds(port); /* Disable local interrupts and clear any outstanding */ - writel_relaxed(0, bridge_base_addr + IMASK_LOCAL); - writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_LOCAL); - writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_MSI); + writel_relaxed(0, port->bridge_base_addr + IMASK_LOCAL); + writel_relaxed(GENMASK(31, 0), port->bridge_base_addr + ISTATUS_LOCAL); + writel_relaxed(GENMASK(31, 0), port->bridge_base_addr + ISTATUS_MSI); /* Disable PCIe events and clear any outstanding */ val = PCIE_EVENT_INT_L2_EXIT_INT | @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1091 @ PCIE_EVENT_INT_L2_EXIT_INT_MASK | PCIE_EVENT_INT_HOTRST_EXIT_INT_MASK | PCIE_EVENT_INT_DLUP_EXIT_INT_MASK; - writel_relaxed(val, ctrl_base_addr + PCIE_EVENT_INT); + writel_relaxed(val, port->ctrl_base_addr + PCIE_EVENT_INT); /* Disable host interrupts and clear any outstanding */ - writel_relaxed(0, bridge_base_addr + IMASK_HOST); - writel_relaxed(GENMASK(31, 0), bridge_base_addr + ISTATUS_HOST); + writel_relaxed(0, port->bridge_base_addr + IMASK_HOST); + writel_relaxed(GENMASK(31, 0), port->bridge_base_addr + ISTATUS_HOST); } static int mc_init_interrupts(struct platform_device *pdev, struct mc_pcie *port) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1154 @ return 0; } +static int mc_check_for_parent_range_handling(struct platform_device *pdev, struct mc_pcie *port) +{ + struct device *dev = &pdev->dev; + struct device_node *dn = dev->of_node; + struct of_range_parser parser; + struct of_range range; + u64 cpu_addr; + + /* Find any pcie range */ + if (of_range_parser_init(&parser, dn)) { + dev_err(dev, "missing ranges property\n"); + return -EINVAL; + } + + for_each_of_range(&parser, &range) { + cpu_addr = range.cpu_addr; + /* + * First range is enough - extend if anyone ever needs more + * than one fabric interface + */ + break; + } + + /* Check for one level up; that is enough */ + dn = of_get_parent(dn); + if (dn) { + of_range_parser_init(&parser, dn); + for_each_of_range(&parser, &range) { + /* Find the parent range that contains cpu_addr */ + if (range.cpu_addr > port->outbound_range_offset && + range.cpu_addr < cpu_addr) + port->outbound_range_offset = range.cpu_addr; + } + } + + return 0; +} + +static int mc_check_for_parent_dma_range_handling(struct platform_device *pdev, + struct mc_pcie *port) +{ + struct device *dev = &pdev->dev; + struct device_node *dn = dev->of_node; + struct of_range_parser parser; + struct of_range range; + int num_parent_ranges = 0; + int num_ranges = 0; + struct inbound_windows ranges[MC_MAX_NUM_INBOUND_WINDOWS] = { 0 }; + u64 start_axi = GENMASK_ULL(63, 0); + u64 end_axi = 0; + u64 start_pci = GENMASK_ULL(63, 0); + s64 size; + u64 window_size; + int i; + + /* Find all dma-ranges */ + if (of_pci_dma_range_parser_init(&parser, dn)) { + dev_err(dev, "missing dma-ranges property\n"); + return -EINVAL; + } + + for_each_of_range(&parser, &range) { + if (num_ranges > MC_MAX_NUM_INBOUND_WINDOWS) { + dev_err(dev, "too many inbound ranges; %d available tables\n", + MC_MAX_NUM_INBOUND_WINDOWS); + return -EINVAL; + } + ranges[num_ranges].axi_addr = range.cpu_addr; + ranges[num_ranges].pci_addr = range.pci_addr; + ranges[num_ranges].size = range.size; + + num_ranges++; + } + + /* + * Check for one level up; will need to adjust address translation + * tables for these + */ + dn = of_get_parent(dn); + if (dn) { + of_pci_dma_range_parser_init(&parser, dn); + + for_each_of_range(&parser, &range) { + if (num_parent_ranges > MC_MAX_NUM_INBOUND_WINDOWS) { + dev_err(dev, "too many parent inbound ranges; %d available tables\n", + MC_MAX_NUM_INBOUND_WINDOWS); + return -EINVAL; + } + ranges[num_parent_ranges].axi_addr = range.pci_addr; + num_parent_ranges++; + } + } + + if (num_parent_ranges) { + if (num_ranges != num_parent_ranges) { + dev_err(dev, "num parent inbound ranges must be 0 or match num inbound ranges\n"); + return -EINVAL; + } + } + + /* Merge ranges */ + for (i = 0; i < num_ranges; i++) { + struct inbound_windows *range = &ranges[i]; + + if (range->axi_addr < start_axi) { + start_axi = range->axi_addr; + start_pci = range->pci_addr; + } + + if (range->axi_addr + range->size > end_axi) + end_axi = range->axi_addr + range->size; + } + + /* Move starts back as far as possible */ + start_axi &= MC_ATT_MASK; + start_pci &= MC_ATT_MASK; + + /* Adjust size to take account of that change */ + size = end_axi - start_axi; + + /* May need to adjust size up to the next largest power of 2 */ + if (size < 1ull << ilog2(size)) + size = 1ull << (ilog2(size) + 1); + + window_size = 1ull << (ilog2(size) - 1); + + /* Divide merged range into windows */ + i = 0; + while (size > 0 && i < MC_MAX_NUM_INBOUND_WINDOWS) { + port->inbound_windows[i].axi_addr = start_axi; + port->inbound_windows[i].pci_addr = start_pci; + port->inbound_windows[i].size = window_size; + + size -= window_size; + start_axi += window_size; + start_pci += window_size; + i++; + port->num_inbound_windows = i; + } + + if (size < 0) { + dev_err(dev, "insufficient windows to map inbound ranges\n"); + return -EINVAL; + } + + return 0; +} + static int mc_platform_init(struct pci_config_window *cfg) { struct device *dev = cfg->parent; struct platform_device *pdev = to_platform_device(dev); - void __iomem *bridge_base_addr = - port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; int ret; + /* + * Need information about any parent bus that may be performing some + * of the outbound address translation to setup outbound address + * translation tables later + */ + ret = mc_check_for_parent_range_handling(pdev, port); + if (ret) + return ret; + + /* And similarly, check for inbound address translation */ + ret = mc_check_for_parent_dma_range_handling(pdev, port); + if (ret) + return ret; + /* Configure address translation table 0 for PCIe config space */ - mc_pcie_setup_window(bridge_base_addr, 0, cfg->res.start, - cfg->res.start, + mc_pcie_setup_window(port->bridge_base_addr, 0, + cfg->res.start - port->outbound_range_offset, + cfg->res.start - port->outbound_range_offset, resource_size(&cfg->res)); /* Need some fixups in config space */ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1336 @ if (ret) return ret; + /* Configure inbound translation tables */ + ret = mc_pcie_setup_inbound_ranges(pdev, port); + if (ret) + return ret; + /* Address translation is up; safe to enable interrupts */ ret = mc_init_interrupts(pdev, port); if (ret) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1352 @ static int mc_host_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - void __iomem *bridge_base_addr; + void __iomem *axi_base_addr; int ret; u32 val; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1362 @ port->dev = dev; - port->axi_base_addr = devm_platform_ioremap_resource(pdev, 1); - if (IS_ERR(port->axi_base_addr)) - return PTR_ERR(port->axi_base_addr); + port->bridge_base_addr = devm_platform_ioremap_resource_byname(pdev, "bridge"); + port->ctrl_base_addr = devm_platform_ioremap_resource_byname(pdev, "ctrl"); + if(!IS_ERR(port->bridge_base_addr) && !IS_ERR(port->ctrl_base_addr)) + goto addrs_set; - mc_disable_interrupts(port); + /* + * The original, incorrect, binding that lumped the control and + * bridge addresses together still needs to be handled by the driver. + */ + axi_base_addr = devm_platform_ioremap_resource_byname(pdev, "apb"); + if (IS_ERR(axi_base_addr)) + return dev_err_probe(dev, PTR_ERR(port->bridge_base_addr), + "both legacy apb register and ctrl/bridge regions missing"); - bridge_base_addr = port->axi_base_addr + MC_PCIE_BRIDGE_ADDR; + port->bridge_base_addr = axi_base_addr + MC_PCIE1_BRIDGE_ADDR; + port->ctrl_base_addr = axi_base_addr + MC_PCIE1_CTRL_ADDR; + +addrs_set: + mc_disable_interrupts(port); /* Allow enabling MSI by disabling MSI-X */ - val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0); + val = readl(port->bridge_base_addr + PCIE_PCI_IRQ_DW0); val &= ~MSIX_CAP_MASK; - writel(val, bridge_base_addr + PCIE_PCI_IRQ_DW0); + writel(val, port->bridge_base_addr + PCIE_PCI_IRQ_DW0); /* Pick num vectors from bitfile programmed onto FPGA fabric */ - val = readl(bridge_base_addr + PCIE_PCI_IRQ_DW0); + val = readl(port->bridge_base_addr + PCIE_PCI_IRQ_DW0); val &= NUM_MSI_MSGS_MASK; val >>= NUM_MSI_MSGS_SHIFT; port->msi.num_vectors = 1 << val; /* Pick vector address from design */ - port->msi.vector_phy = readl_relaxed(bridge_base_addr + IMSI_ADDR); + port->msi.vector_phy = readl_relaxed(port->bridge_base_addr+ IMSI_ADDR); ret = mc_pcie_init_clks(dev); if (ret) { diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/phy/microchip/Kconfig linux-microchip/drivers/phy/microchip/Kconfig --- linux-6.6.51/drivers/phy/microchip/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/phy/microchip/Kconfig 2024-11-26 14:02:38.234506882 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:6 @ # Phy drivers for Microchip devices # +config PHY_MICROCHIP_SAMA7_USB + tristate "Microchip SAMA7 USB 2.0 PHY" + depends on ARCH_AT91 || COMPILE_TEST + depends on OF + select GENERIC_PHY + help + Enable this to support SAMA7 USB 2.0 PHY + +config PHY_MICROCHIP_SAMA7_UTMI_CLK + bool + depends on PHY_MICROCHIP_SAMA7_USB + default n if OPTEE + default y + config PHY_SPARX5_SERDES tristate "Microchip Sparx5 SerDes PHY driver" select GENERIC_PHY diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/phy/microchip/Makefile linux-microchip/drivers/phy/microchip/Makefile --- linux-6.6.51/drivers/phy/microchip/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/phy/microchip/Makefile 2024-11-26 14:02:38.234506882 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:6 @ # Makefile for the Microchip phy drivers. # +obj-$(CONFIG_PHY_MICROCHIP_SAMA7_USB) += phy-sama7-usb.o +obj-$(CONFIG_PHY_MICROCHIP_SAMA7_UTMI_CLK) += phy-sama7-utmi-clk.o obj-$(CONFIG_PHY_SPARX5_SERDES) := sparx5_serdes.o obj-$(CONFIG_PHY_LAN966X_SERDES) := lan966x_serdes.o diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/phy/microchip/phy-sama7-usb.c linux-microchip/drivers/phy/microchip/phy-sama7-usb.c --- linux-6.6.51/drivers/phy/microchip/phy-sama7-usb.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/phy/microchip/phy-sama7-usb.c 2024-11-26 14:02:38.234506882 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for the Microchip SAMA7 USB 2.0 PHY + * + * Copyright (C) 2022 Microchip Technology, Inc. and its subsidiaries + * + * Author: Cristian Birsan <cristian.birsan@microchip.com> + * + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <soc/at91/sama7-sfr.h> + +struct sama7_usb_phy { + struct phy *phy; + struct clk *clk; + struct regmap *sfr; + enum phy_mode mode; + int port; +}; + +int sama7_usb_phy_set_mode(struct phy *phy, enum phy_mode mode, int submode) +{ + struct sama7_usb_phy *sama7_phy = phy_get_drvdata(phy); + int port = sama7_phy->port; + + sama7_phy->mode = PHY_MODE_INVALID; + + if (mode > 0) + sama7_phy->mode = mode; + + /* Notify the controller when VBUS is present for device mode */ + if (mode == PHY_MODE_USB_DEVICE) { + regmap_update_bits(sama7_phy->sfr, SAMA7_SFR_UTMI0R(port), + SAMA7_SFR_UTMI_RX_VBUS, + (submode ? SAMA7_SFR_UTMI_RX_VBUS : 0)); + } + + dev_dbg(&sama7_phy->phy->dev, "USB PHY Set Mode port=%d, mode=%d\n", + sama7_phy->port, sama7_phy->mode); + + return 0; +} + +int sama7_usb_phy_init(struct phy *phy) +{ + struct sama7_usb_phy *sama7_phy = phy_get_drvdata(phy); + int port = sama7_phy->port; + + /* Set Transmitter Pre-Emphasis AMP Tune to 1X */ + regmap_update_bits(sama7_phy->sfr, SAMA7_SFR_UTMI0R(port), + SAMA7_SFR_UTMI_RX_TX_PREEM_AMP_TUNE_1X, + SAMA7_SFR_UTMI_RX_TX_PREEM_AMP_TUNE_1X); + + dev_dbg(&sama7_phy->phy->dev, "USB PHY Init , port=%d\n", + sama7_phy->port); + + return 0; +} + +int sama7_phy_power_on(struct phy *phy) +{ + struct sama7_usb_phy *sama7_phy = phy_get_drvdata(phy); + + clk_prepare_enable(sama7_phy->clk); + + dev_dbg(&sama7_phy->phy->dev, "USB PHY Power On port=%d\n", + sama7_phy->port); + + return 0; +} + +int sama7_phy_power_off(struct phy *phy) +{ + struct sama7_usb_phy *sama7_phy = phy_get_drvdata(phy); + + clk_disable_unprepare(sama7_phy->clk); + + dev_dbg(&sama7_phy->phy->dev, "USB PHY Power Off port=%d\n", + sama7_phy->port); + + return 0; +} + +static const struct phy_ops sama7_usb_phy_ops = { + .init = sama7_usb_phy_init, + .power_on = sama7_phy_power_on, + .power_off = sama7_phy_power_off, + .set_mode = sama7_usb_phy_set_mode, + .owner = THIS_MODULE, +}; + +int sama7_usb_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct sama7_usb_phy *sama7_phy; + + sama7_phy = devm_kzalloc(dev, sizeof(*sama7_phy), GFP_KERNEL); + if (!sama7_phy) + return -ENOMEM; + + sama7_phy->clk = devm_clk_get(dev, "utmi_clk"); + if (IS_ERR(sama7_phy->clk)) { + dev_err(dev, "failed to get sama7 usb utmi phy clock\n"); + return PTR_ERR(sama7_phy->clk); + } + + sama7_phy->sfr = syscon_regmap_lookup_by_phandle(np, "sfr-phandle"); + if (IS_ERR(sama7_phy->sfr)) { + sama7_phy->sfr = NULL; + dev_err(dev, "failed to get sfr\n"); + return -ENODEV; + } + + if (of_property_read_u32(np, "reg", &sama7_phy->port)) { + dev_err(dev, "failed to get reg\n"); + return -ENODEV; + } + + sama7_phy->phy = devm_phy_create(dev, NULL, &sama7_usb_phy_ops); + if (IS_ERR(sama7_phy->phy)) + return PTR_ERR(sama7_phy->phy); + + phy_set_drvdata(sama7_phy->phy, sama7_phy); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + dev_info(dev, "probed\n"); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id sama7_usb_phy_of_match[] = { + { .compatible = "microchip,sama7g5-usb-phy", }, + { }, +}; +MODULE_DEVICE_TABLE(of, sama7_usb_phy_of_match); + +static struct platform_driver sama7_usb_phy_driver = { + .probe = sama7_usb_phy_probe, + .driver = { + .name = "sama7-usb-phy", + .of_match_table = + sama7_usb_phy_of_match, + } +}; +module_platform_driver(sama7_usb_phy_driver); + +MODULE_AUTHOR("Cristian Birsan <cristian.birsan@microchip.com>"); +MODULE_DESCRIPTION("Microchip SAMA7X USB PHY driver"); +MODULE_LICENSE("GPL"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/phy/microchip/phy-sama7-utmi-clk.c linux-microchip/drivers/phy/microchip/phy-sama7-utmi-clk.c --- linux-6.6.51/drivers/phy/microchip/phy-sama7-utmi-clk.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/phy/microchip/phy-sama7-utmi-clk.c 2024-11-26 14:02:38.234506882 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for the Microchip SAMA7 USB 2.0 PHY Clock + * + * Copyright (C) 2023 Microchip Technology, Inc. and its subsidiaries + * + * Author: Cristian Birsan <cristian.birsan@microchip.com> + * + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/clk/at91_pmc.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <soc/at91/sama7-sfr.h> + +struct sama7_utmi_clk { + struct clk_hw hw; + struct regmap *regmap_sfr; + struct reset_control *reset; + u8 id; +}; +#define to_sama7_utmi_clk(hw) container_of(hw, struct sama7_utmi_clk, hw) + +/* + * UTMI clock description + * @n: clock name + * @p: clock parent name + * @id: clock id + */ +static struct { + const char *n; + const char *p; + u8 id; +} sama7_utmick[] = { + { .n = "utmi1", .p = "utmick", .id = 0, }, + { .n = "utmi2", .p = "utmi1", .id = 1, }, + { .n = "utmi3", .p = "utmi1", .id = 2, }, +}; + +static int sama7_utmi_clk_enable(struct clk_hw *hw) +{ + int ret; + + struct sama7_utmi_clk *utmi = to_sama7_utmi_clk(hw); + u8 id = utmi->id; + + ret = reset_control_assert(utmi->reset); + if (ret) + return ret; + + ret = regmap_update_bits(utmi->regmap_sfr, SAMA7_SFR_UTMI0R(id), + SAMA7_SFR_UTMI_COMMONON, 0); + if (ret < 0) + return ret; + + ret = reset_control_deassert(utmi->reset); + if (ret) + return ret; + + /* Datasheet states a minimum of 45 us before any USB operation */ + udelay(50); + + return 0; +} + +static void sama7_utmi_clk_disable(struct clk_hw *hw) +{ + int ret; + struct sama7_utmi_clk *utmi = to_sama7_utmi_clk(hw); + u8 id = utmi->id; + + ret = reset_control_assert(utmi->reset); + if (ret) + return; + + regmap_update_bits(utmi->regmap_sfr, SAMA7_SFR_UTMI0R(id), + SAMA7_SFR_UTMI_COMMONON, SAMA7_SFR_UTMI_COMMONON); +} + +static int sama7_utmi_clk_is_enabled(struct clk_hw *hw) +{ + int ret; + struct sama7_utmi_clk *utmi = to_sama7_utmi_clk(hw); + + ret = reset_control_status(utmi->reset); + if (ret) + return false; + else + return true; +} + +static const struct clk_ops sama7_utmi_ops = { + .enable = sama7_utmi_clk_enable, + .disable = sama7_utmi_clk_disable, + .is_enabled = sama7_utmi_clk_is_enabled, +}; + +static struct clk_hw * +sama7_utmi_clk_register(struct device *dev, + struct regmap *regmap_sfr, struct reset_control *reset, + const char *name, const char *parent_name, + const struct clk_ops *ops, u8 id) +{ + struct clk_init_data init = {}; + struct clk_hw *hw; + struct sama7_utmi_clk *utmi_clk; + int ret; + + utmi_clk = devm_kzalloc(dev, sizeof(*utmi_clk), GFP_KERNEL); + if (!utmi_clk) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = ops; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + + utmi_clk->hw.init = &init; + utmi_clk->reset = reset; + utmi_clk->regmap_sfr = regmap_sfr; + utmi_clk->id = id; + + hw = &utmi_clk->hw; + ret = devm_clk_hw_register(dev, &utmi_clk->hw); + if (ret) { + devm_kfree(dev, utmi_clk); + hw = ERR_PTR(ret); + } + + return hw; +} + +int sama7_utmi_clk_probe(struct platform_device *pdev) +{ + struct clk *utmi_parent_clk; + struct clk_hw *hw; + struct clk_hw_onecell_data *hw_data; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct regmap *regmap_sfr; + struct reset_control *phy_reset; + int i; + + char name[16]; + + hw_data = devm_kzalloc(dev, struct_size(hw_data, hws, + ARRAY_SIZE(sama7_utmick)), GFP_KERNEL); + if (!hw_data) + return -ENOMEM; + + utmi_parent_clk = devm_clk_get(dev, "utmi_clk"); + if (IS_ERR(utmi_parent_clk)) + return PTR_ERR(utmi_parent_clk); + + sama7_utmick[0].p = __clk_get_name(utmi_parent_clk); + + regmap_sfr = syscon_regmap_lookup_by_phandle(np, "sfr-phandle"); + if (IS_ERR(regmap_sfr)) + return PTR_ERR(regmap_sfr); + + for (i = 0; i < ARRAY_SIZE(sama7_utmick); i++) { + + snprintf(name, sizeof(name), "usb%d_reset", i); + phy_reset = devm_reset_control_get(dev, name); + if (IS_ERR(phy_reset)) { + dev_err(dev, "failed to get reset %s\n", name); + return PTR_ERR(phy_reset); + } + + hw = sama7_utmi_clk_register(dev, regmap_sfr, phy_reset, + sama7_utmick[i].n, + sama7_utmick[i].p, + &sama7_utmi_ops, + sama7_utmick[i].id); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + hw_data->hws[i] = hw; + } + hw_data->num = ARRAY_SIZE(sama7_utmick); + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, hw_data); +}; + +static const struct of_device_id sama7_utmi_clk_dt_ids[] = { + { .compatible = "microchip,sama7g5-utmi-clk", }, + { /* santinel */}, +}; +MODULE_DEVICE_TABLE(of, sama7_utmi_clk_dt_ids); + +static struct platform_driver sama7_utmi_clk_driver = { + .probe = sama7_utmi_clk_probe, + .driver = { + .name = "sama7-utmi-clk", + .of_match_table = sama7_utmi_clk_dt_ids, + }, +}; +builtin_platform_driver(sama7_utmi_clk_driver); + +MODULE_AUTHOR("Cristian Birsan <cristian.birsan@microchip.com>"); +MODULE_DESCRIPTION("Microchip SAMA7X UTMI clock driver"); +MODULE_LICENSE("GPL v2"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/pinctrl/bcm/pinctrl-ns.c linux-microchip/drivers/pinctrl/bcm/pinctrl-ns.c --- linux-6.6.51/drivers/pinctrl/bcm/pinctrl-ns.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/pinctrl/bcm/pinctrl-ns.c 2024-11-26 14:02:38.246507097 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:10 @ #include <linux/io.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/pinctrl/pinconf-generic.h> #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinmux.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/slab.h> #include "../core.h" @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:211 @ static int ns_pinctrl_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - const struct of_device_id *of_id; struct ns_pinctrl *ns_pinctrl; struct pinctrl_desc *pctldesc; struct pinctrl_pin_desc *pin; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:227 @ ns_pinctrl->dev = dev; - of_id = of_match_device(ns_pinctrl_of_match_table, dev); - if (!of_id) - return -EINVAL; - ns_pinctrl->chipset_flag = (uintptr_t)of_id->data; + ns_pinctrl->chipset_flag = (uintptr_t)device_get_match_data(dev); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cru_gpio_control"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/pinctrl/berlin/berlin-bg2.c linux-microchip/drivers/pinctrl/berlin/berlin-bg2.c --- linux-6.6.51/drivers/pinctrl/berlin/berlin-bg2.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/pinctrl/berlin/berlin-bg2.c 2024-11-26 14:02:38.246507097 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:11 @ */ #include <linux/init.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/regmap.h> #include "berlin.h" @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:231 @ static int berlin2_pinctrl_probe(struct platform_device *pdev) { - const struct of_device_id *match = - of_match_device(berlin2_pinctrl_match, &pdev->dev); - - return berlin_pinctrl_probe(pdev, match->data); + return berlin_pinctrl_probe(pdev, device_get_match_data(&pdev->dev)); } static struct platform_driver berlin2_pinctrl_driver = { diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/pinctrl/berlin/berlin-bg2cd.c linux-microchip/drivers/pinctrl/berlin/berlin-bg2cd.c --- linux-6.6.51/drivers/pinctrl/berlin/berlin-bg2cd.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/pinctrl/berlin/berlin-bg2cd.c 2024-11-26 14:02:38.246507097 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:11 @ */ #include <linux/init.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/regmap.h> #include "berlin.h" @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:176 @ static int berlin2cd_pinctrl_probe(struct platform_device *pdev) { - const struct of_device_id *match = - of_match_device(berlin2cd_pinctrl_match, &pdev->dev); - - return berlin_pinctrl_probe(pdev, match->data); + return berlin_pinctrl_probe(pdev, device_get_match_data(&pdev->dev)); } static struct platform_driver berlin2cd_pinctrl_driver = { diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/pinctrl/berlin/berlin-bg2q.c linux-microchip/drivers/pinctrl/berlin/berlin-bg2q.c --- linux-6.6.51/drivers/pinctrl/berlin/berlin-bg2q.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/pinctrl/berlin/berlin-bg2q.c 2024-11-26 14:02:38.246507097 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:11 @ */ #include <linux/init.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/regmap.h> #include "berlin.h" @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:393 @ static int berlin2q_pinctrl_probe(struct platform_device *pdev) { - const struct of_device_id *match = - of_match_device(berlin2q_pinctrl_match, &pdev->dev); - - return berlin_pinctrl_probe(pdev, match->data); + return berlin_pinctrl_probe(pdev, device_get_match_data(&pdev->dev)); } static struct platform_driver berlin2q_pinctrl_driver = { diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/pinctrl/berlin/berlin-bg4ct.c linux-microchip/drivers/pinctrl/berlin/berlin-bg4ct.c --- linux-6.6.51/drivers/pinctrl/berlin/berlin-bg4ct.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/pinctrl/berlin/berlin-bg4ct.c 2024-11-26 14:02:38.246507097 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:11 @ */ #include <linux/init.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/regmap.h> #include "berlin.h" @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:453 @ static int berlin4ct_pinctrl_probe(struct platform_device *pdev) { - const struct of_device_id *match = - of_match_device(berlin4ct_pinctrl_match, &pdev->dev); + const struct berlin_pinctrl_desc *desc = + device_get_match_data(&pdev->dev); struct regmap_config *rmconfig; struct regmap *regmap; struct resource *res; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:477 @ if (IS_ERR(regmap)) return PTR_ERR(regmap); - return berlin_pinctrl_probe_regmap(pdev, match->data, regmap); + return berlin_pinctrl_probe_regmap(pdev, desc, regmap); } static struct platform_driver berlin4ct_pinctrl_driver = { diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/pinctrl/berlin/pinctrl-as370.c linux-microchip/drivers/pinctrl/berlin/pinctrl-as370.c --- linux-6.6.51/drivers/pinctrl/berlin/pinctrl-as370.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/pinctrl/berlin/pinctrl-as370.c 2024-11-26 14:02:38.246507097 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:11 @ */ #include <linux/init.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/regmap.h> #include "berlin.h" @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:334 @ static int as370_pinctrl_probe(struct platform_device *pdev) { - const struct of_device_id *match = - of_match_device(as370_pinctrl_match, &pdev->dev); + const struct berlin_pinctrl_desc *desc = + device_get_match_data(&pdev->dev); struct regmap_config *rmconfig; struct regmap *regmap; struct resource *res; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:358 @ if (IS_ERR(regmap)) return PTR_ERR(regmap); - return berlin_pinctrl_probe_regmap(pdev, match->data, regmap); + return berlin_pinctrl_probe_regmap(pdev, desc, regmap); } static struct platform_driver as370_pinctrl_driver = { diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/pinctrl/mvebu/pinctrl-armada-38x.c linux-microchip/drivers/pinctrl/mvebu/pinctrl-armada-38x.c --- linux-6.6.51/drivers/pinctrl/mvebu/pinctrl-armada-38x.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/pinctrl/mvebu/pinctrl-armada-38x.c 2024-11-26 14:02:38.254507240 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:15 @ #include <linux/io.h> #include <linux/platform_device.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/pinctrl/pinctrl.h> +#include <linux/property.h> #include "pinctrl-mvebu.h" @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:407 @ static int armada_38x_pinctrl_probe(struct platform_device *pdev) { struct mvebu_pinctrl_soc_info *soc = &armada_38x_pinctrl_info; - const struct of_device_id *match = - of_match_device(armada_38x_pinctrl_of_match, &pdev->dev); - if (!match) - return -ENODEV; - - soc->variant = (unsigned) match->data & 0xff; + soc->variant = (unsigned)device_get_match_data(&pdev->dev) & 0xff; soc->controls = armada_38x_mpp_controls; soc->ncontrols = ARRAY_SIZE(armada_38x_mpp_controls); soc->gpioranges = armada_38x_mpp_gpio_ranges; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/pinctrl/mvebu/pinctrl-armada-39x.c linux-microchip/drivers/pinctrl/mvebu/pinctrl-armada-39x.c --- linux-6.6.51/drivers/pinctrl/mvebu/pinctrl-armada-39x.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/pinctrl/mvebu/pinctrl-armada-39x.c 2024-11-26 14:02:38.254507240 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:15 @ #include <linux/io.h> #include <linux/platform_device.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/pinctrl/pinctrl.h> +#include <linux/property.h> #include "pinctrl-mvebu.h" @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:389 @ static int armada_39x_pinctrl_probe(struct platform_device *pdev) { struct mvebu_pinctrl_soc_info *soc = &armada_39x_pinctrl_info; - const struct of_device_id *match = - of_match_device(armada_39x_pinctrl_of_match, &pdev->dev); - if (!match) - return -ENODEV; - - soc->variant = (unsigned) match->data & 0xff; + soc->variant = (unsigned)device_get_match_data(&pdev->dev) & 0xff; soc->controls = armada_39x_mpp_controls; soc->ncontrols = ARRAY_SIZE(armada_39x_mpp_controls); soc->gpioranges = armada_39x_mpp_gpio_ranges; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/pinctrl/mvebu/pinctrl-armada-ap806.c linux-microchip/drivers/pinctrl/mvebu/pinctrl-armada-ap806.c --- linux-6.6.51/drivers/pinctrl/mvebu/pinctrl-armada-ap806.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/pinctrl/mvebu/pinctrl-armada-ap806.c 2024-11-26 14:02:38.254507240 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:16 @ #include <linux/io.h> #include <linux/platform_device.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/pinctrl/pinctrl.h> #include "pinctrl-mvebu.h" @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:108 @ static int armada_ap806_pinctrl_probe(struct platform_device *pdev) { struct mvebu_pinctrl_soc_info *soc = &armada_ap806_pinctrl_info; - const struct of_device_id *match = - of_match_device(armada_ap806_pinctrl_of_match, &pdev->dev); - if (!match || !pdev->dev.parent) + if (!pdev->dev.parent) return -ENODEV; soc->variant = 0; /* no variants for Armada AP806 */ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/pinctrl/mvebu/pinctrl-armada-cp110.c linux-microchip/drivers/pinctrl/mvebu/pinctrl-armada-cp110.c --- linux-6.6.51/drivers/pinctrl/mvebu/pinctrl-armada-cp110.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/pinctrl/mvebu/pinctrl-armada-cp110.c 2024-11-26 14:02:38.254507240 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:15 @ #include <linux/io.h> #include <linux/mfd/syscon.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/pinctrl/pinctrl.h> #include <linux/platform_device.h> +#include <linux/property.h> #include "pinctrl-mvebu.h" @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:641 @ static int armada_cp110_pinctrl_probe(struct platform_device *pdev) { struct mvebu_pinctrl_soc_info *soc; - const struct of_device_id *match = - of_match_device(armada_cp110_pinctrl_of_match, &pdev->dev); int i; if (!pdev->dev.parent) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:651 @ if (!soc) return -ENOMEM; - soc->variant = (unsigned long) match->data & 0xff; + soc->variant = (unsigned long)device_get_match_data(&pdev->dev) & 0xff; soc->controls = armada_cp110_mpp_controls; soc->ncontrols = ARRAY_SIZE(armada_cp110_mpp_controls); soc->modes = armada_cp110_mpp_modes; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/pinctrl/mvebu/pinctrl-armada-xp.c linux-microchip/drivers/pinctrl/mvebu/pinctrl-armada-xp.c --- linux-6.6.51/drivers/pinctrl/mvebu/pinctrl-armada-xp.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/pinctrl/mvebu/pinctrl-armada-xp.c 2024-11-26 14:02:38.254507240 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:22 @ #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/pinctrl/pinctrl.h> +#include <linux/property.h> #include <linux/bitops.h> #include "pinctrl-mvebu.h" @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:571 @ static int armada_xp_pinctrl_probe(struct platform_device *pdev) { struct mvebu_pinctrl_soc_info *soc = &armada_xp_pinctrl_info; - const struct of_device_id *match = - of_match_device(armada_xp_pinctrl_of_match, &pdev->dev); int nregs; - if (!match) - return -ENODEV; - - soc->variant = (unsigned) match->data & 0xff; + soc->variant = (unsigned)device_get_match_data(&pdev->dev) & 0xff; switch (soc->variant) { case V_MV78230: diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/pinctrl/mvebu/pinctrl-dove.c linux-microchip/drivers/pinctrl/mvebu/pinctrl-dove.c --- linux-6.6.51/drivers/pinctrl/mvebu/pinctrl-dove.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/pinctrl/mvebu/pinctrl-dove.c 2024-11-26 14:02:38.254507240 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:15 @ #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/mfd/syscon.h> #include <linux/pinctrl/pinctrl.h> +#include <linux/property.h> #include <linux/regmap.h> #include "pinctrl-mvebu.h" @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:768 @ { struct resource *res, *mpp_res; struct resource fb_res; - const struct of_device_id *match = - of_match_device(dove_pinctrl_of_match, &pdev->dev); struct mvebu_mpp_ctrl_data *mpp_data; void __iomem *base; int i; - pdev->dev.platform_data = (void *)match->data; + pdev->dev.platform_data = (void *)device_get_match_data(&pdev->dev); /* * General MPP Configuration Register is part of pdma registers. diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/pinctrl/mvebu/pinctrl-kirkwood.c linux-microchip/drivers/pinctrl/mvebu/pinctrl-kirkwood.c --- linux-6.6.51/drivers/pinctrl/mvebu/pinctrl-kirkwood.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/pinctrl/mvebu/pinctrl-kirkwood.c 2024-11-26 14:02:38.254507240 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:14 @ #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/pinctrl/pinctrl.h> +#include <linux/property.h> #include "pinctrl-mvebu.h" @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:473 @ static int kirkwood_pinctrl_probe(struct platform_device *pdev) { - const struct of_device_id *match = - of_match_device(kirkwood_pinctrl_of_match, &pdev->dev); - - pdev->dev.platform_data = (void *)match->data; + pdev->dev.platform_data = (void *)device_get_match_data(&pdev->dev); return mvebu_pinctrl_simple_mmio_probe(pdev); } diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/pinctrl/mvebu/pinctrl-orion.c linux-microchip/drivers/pinctrl/mvebu/pinctrl-orion.c --- linux-6.6.51/drivers/pinctrl/mvebu/pinctrl-orion.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/pinctrl/mvebu/pinctrl-orion.c 2024-11-26 14:02:38.254507240 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:22 @ #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/pinctrl/pinctrl.h> +#include <linux/property.h> #include "pinctrl-mvebu.h" @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:221 @ static int orion_pinctrl_probe(struct platform_device *pdev) { - const struct of_device_id *match = - of_match_device(orion_pinctrl_of_match, &pdev->dev); - - pdev->dev.platform_data = (void*)match->data; + pdev->dev.platform_data = (void*)device_get_match_data(&pdev->dev); mpp_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(mpp_base)) diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/pinctrl/nomadik/pinctrl-abx500.c linux-microchip/drivers/pinctrl/nomadik/pinctrl-abx500.c --- linux-6.6.51/drivers/pinctrl/nomadik/pinctrl-abx500.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/pinctrl/nomadik/pinctrl-abx500.c 2024-11-26 14:02:38.254507240 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:20 @ #include <linux/of.h> #include <linux/of_device.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/seq_file.h> #include <linux/slab.h> #include <linux/types.h> @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:989 @ static int abx500_gpio_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - const struct of_device_id *match; struct abx500_pinctrl *pct; unsigned int id = -1; int ret; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1009 @ pct->chip.parent = &pdev->dev; pct->chip.base = -1; /* Dynamic allocation */ - match = of_match_device(abx500_gpio_match, &pdev->dev); - if (!match) { - dev_err(&pdev->dev, "gpio dt not matching\n"); - return -ENODEV; - } - id = (unsigned long)match->data; + id = (unsigned long)device_get_match_data(&pdev->dev); /* Poke in other ASIC variants here */ switch (id) { diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/pinctrl/nomadik/pinctrl-nomadik.c linux-microchip/drivers/pinctrl/nomadik/pinctrl-nomadik.c --- linux-6.6.51/drivers/pinctrl/nomadik/pinctrl-nomadik.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/pinctrl/nomadik/pinctrl-nomadik.c 2024-11-26 14:02:38.254507240 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:19 @ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/kernel.h> +#include <linux/of.h> #include <linux/of_address.h> -#include <linux/of_device.h> +#include <linux/of_platform.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/seq_file.h> #include <linux/slab.h> #include <linux/spinlock.h> @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1845 @ static int nmk_pinctrl_probe(struct platform_device *pdev) { - const struct of_device_id *match; struct device_node *np = pdev->dev.of_node; struct device_node *prcm_np; struct nmk_pinctrl *npct; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1855 @ if (!npct) return -ENOMEM; - match = of_match_device(nmk_pinctrl_match, &pdev->dev); - if (!match) - return -ENODEV; - version = (unsigned int) match->data; + version = (unsigned int)device_get_match_data(&pdev->dev); /* Poke in other ASIC variants here */ if (version == PINCTRL_NMK_STN8815) diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/pinctrl/pinctrl-at91.c linux-microchip/drivers/pinctrl/pinctrl-at91.c --- linux-6.6.51/drivers/pinctrl/pinctrl-at91.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/pinctrl/pinctrl-at91.c 2024-11-26 14:02:38.258507312 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:15 @ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/of.h> -#include <linux/of_address.h> -#include <linux/of_device.h> -#include <linux/of_irq.h> +#include <linux/platform_device.h> #include <linux/pm.h> +#include <linux/property.h> #include <linux/seq_file.h> #include <linux/slab.h> #include <linux/string_helpers.h> @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1304 @ if (!np) return -ENODEV; - info->dev = dev; - info->ops = of_device_get_match_data(dev); + info->dev = &pdev->dev; + info->ops = device_get_match_data(&pdev->dev); at91_pinctrl_child_count(info, np); /* @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1847 @ if (IS_ERR(at91_chip->regbase)) return PTR_ERR(at91_chip->regbase); - at91_chip->ops = of_device_get_match_data(dev); + at91_chip->ops = device_get_match_data(dev); at91_chip->pioc_virq = irq; at91_chip->clock = devm_clk_get_enabled(dev, NULL); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/pinctrl/pinctrl-at91-pio4.c linux-microchip/drivers/pinctrl/pinctrl-at91-pio4.c --- linux-6.6.51/drivers/pinctrl/pinctrl-at91-pio4.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/pinctrl/pinctrl-at91-pio4.c 2024-11-26 14:02:38.258507312 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:437 @ } static struct gpio_chip atmel_gpio_chip = { + .request = gpiochip_generic_request, + .free = gpiochip_generic_free, .direction_input = atmel_gpio_direction_input, .get = atmel_gpio_get, .get_multiple = atmel_gpio_get_multiple, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:725 @ return 0; } +static int atmel_pmx_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset) +{ + u32 conf; + + conf = atmel_pin_config_read(pctldev, offset); + conf &= (~ATMEL_PIO_CFGR_FUNC_MASK); + atmel_pin_config_write(pctldev, offset, conf); + + dev_dbg(pctldev->dev, "enable pin %u as GPIO\n", offset); + + return 0; +} + static const struct pinmux_ops atmel_pmxops = { .get_functions_count = atmel_pmx_get_functions_count, .get_function_name = atmel_pmx_get_function_name, .get_function_groups = atmel_pmx_get_function_groups, .set_mux = atmel_pmx_set_mux, + .gpio_request_enable = atmel_pmx_gpio_request_enable, }; static int atmel_conf_pin_config_group_get(struct pinctrl_dev *pctldev, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1059 @ } static const struct dev_pm_ops atmel_pctrl_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(atmel_pctrl_suspend, atmel_pctrl_resume) + SET_LATE_SYSTEM_SLEEP_PM_OPS(atmel_pctrl_suspend, atmel_pctrl_resume) }; /* @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1077 @ .slew_rate_support = 1, }; +static const struct atmel_pioctrl_data microchip_sama7d65_pioctrl_data = { + .nbanks = 5, + .last_bank_count = 14, /* sama7d65 has only PE0 to PE13 */ + .slew_rate_support = 1, +}; + static const struct of_device_id atmel_pctrl_of_match[] = { { .compatible = "atmel,sama5d2-pinctrl", @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1091 @ .compatible = "microchip,sama7g5-pinctrl", .data = µchip_sama7g5_pioctrl_data, }, { + .compatible = "microchip,sama7d65-pinctrl", + .data = µchip_sama7d65_pioctrl_data, + }, { /* sentinel */ } }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/pinctrl/pinctrl-xway.c linux-microchip/drivers/pinctrl/pinctrl-xway.c --- linux-6.6.51/drivers/pinctrl/pinctrl-xway.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/pinctrl/pinctrl-xway.c 2024-11-26 14:02:38.262507382 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:14 @ #include <linux/gpio/driver.h> #include <linux/slab.h> #include <linux/module.h> -#include <linux/of_platform.h> -#include <linux/of_address.h> +#include <linux/of.h> #include <linux/ioport.h> #include <linux/io.h> #include <linux/device.h> #include <linux/platform_device.h> +#include <linux/property.h> #include "pinctrl-lantiq.h" @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1454 @ static int pinmux_xway_probe(struct platform_device *pdev) { - const struct of_device_id *match; const struct pinctrl_xway_soc *xway_soc; int ret, i; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1462 @ if (IS_ERR(xway_info.membase[0])) return PTR_ERR(xway_info.membase[0]); - match = of_match_device(xway_match, &pdev->dev); - if (match) - xway_soc = (const struct pinctrl_xway_soc *) match->data; - else + xway_soc = device_get_match_data(&pdev->dev); + if (!xway_soc) xway_soc = &danube_pinctrl; /* find out how many pads we have */ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/pinctrl/ti/pinctrl-ti-iodelay.c linux-microchip/drivers/pinctrl/ti/pinctrl-ti-iodelay.c --- linux-6.6.51/drivers/pinctrl/ti/pinctrl-ti-iodelay.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/pinctrl/ti/pinctrl-ti-iodelay.c 2024-11-26 14:02:38.278507669 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:17 @ #include <linux/io.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/property.h> #include <linux/regmap.h> #include <linux/seq_file.h> #include <linux/slab.h> @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:826 @ { struct device *dev = &pdev->dev; struct device_node *np = of_node_get(dev->of_node); - const struct of_device_id *match; struct resource *res; struct ti_iodelay_device *iod; int ret = 0; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:836 @ goto exit_out; } - match = of_match_device(ti_iodelay_of_match, dev); - if (!match) { - ret = -EINVAL; - dev_err(dev, "No DATA match\n"); - goto exit_out; - } - iod = devm_kzalloc(dev, sizeof(*iod), GFP_KERNEL); if (!iod) { ret = -ENOMEM; goto exit_out; } iod->dev = dev; - iod->reg_data = match->data; + iod->reg_data = device_get_match_data(dev); + if (!iod->reg_data) { + ret = -EINVAL; + dev_err(dev, "No DATA match\n"); + goto exit_out; + } /* So far We can assume there is only 1 bank of registers */ iod->reg_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/power/reset/at91-sama5d2_shdwc.c linux-microchip/drivers/power/reset/at91-sama5d2_shdwc.c --- linux-6.6.51/drivers/power/reset/at91-sama5d2_shdwc.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/power/reset/at91-sama5d2_shdwc.c 2024-11-26 14:02:38.310508242 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:329 @ { .compatible = "atmel,sama5d2-pmc" }, { .compatible = "microchip,sam9x60-pmc" }, { .compatible = "microchip,sama7g5-pmc" }, + { .compatible = "microchip,sama7d65-pmc" }, + { .compatible = "microchip,sam9x7-pmc" }, { /* Sentinel. */ } }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/power/reset/Kconfig linux-microchip/drivers/power/reset/Kconfig --- linux-6.6.51/drivers/power/reset/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/power/reset/Kconfig 2024-11-26 14:02:38.310508242 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:29 @ config POWER_RESET_AT91_RESET tristate "Atmel AT91 reset driver" depends on ARCH_AT91 - default SOC_AT91SAM9 || SOC_SAM9X60 || SOC_SAMA5 + default SOC_AT91SAM9 || SOC_SAM9X60 || SOC_SAM9X7 || SOC_SAMA5 help This driver supports restart for Atmel AT91SAM9 and SAMA5 SoCs @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:37 @ config POWER_RESET_AT91_SAMA5D2_SHDWC tristate "Atmel AT91 SAMA5D2-Compatible shutdown controller driver" depends on ARCH_AT91 - default SOC_SAM9X60 || SOC_SAMA5 + default SOC_SAM9X60 || SOC_SAM9X7 || SOC_SAMA5 help This driver supports the alternate shutdown controller for some Atmel SAMA5 SoCs. It is present for example on SAMA5D2 SoC. diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/pwm/pwm-atmel-hlcdc.c linux-microchip/drivers/pwm/pwm-atmel-hlcdc.c --- linux-6.6.51/drivers/pwm/pwm-atmel-hlcdc.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/pwm/pwm-atmel-hlcdc.c 2024-11-26 14:02:38.322508458 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:23 @ #define ATMEL_HLCDC_PWMPOL BIT(4) #define ATMEL_HLCDC_PWMPS_MASK GENMASK(2, 0) #define ATMEL_HLCDC_PWMPS_MAX 0x6 +#define ATMEL_XLCDC_PWMPS_MASK GENMASK(3, 0) +#define ATMEL_XLCDC_PWMPS_MAX 0xF #define ATMEL_HLCDC_PWMPS(x) ((x) & ATMEL_HLCDC_PWMPS_MASK) +#define ATMEL_XLCDC_PWMPS(x) ((x) & ATMEL_XLCDC_PWMPS_MASK) struct atmel_hlcdc_pwm_errata { bool slow_clk_erratum; bool div1_clk_erratum; + bool is_xlcdc; }; struct atmel_hlcdc_pwm { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:53 @ struct atmel_hlcdc *hlcdc = atmel->hlcdc; unsigned int status; int ret; + bool is_xlcdc = false; + + if (atmel->errata) + is_xlcdc = atmel->errata->is_xlcdc; if (state->enabled) { struct clk *new_clk = hlcdc->slow_clk; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:78 @ /* Errata: cannot use slow clk on some IP revisions */ if ((atmel->errata && atmel->errata->slow_clk_erratum) || clk_period_ns > state->period) { - new_clk = hlcdc->sys_clk; + new_clk = hlcdc->periph_clk; clk_freq = clk_get_rate(new_clk); if (!clk_freq) return -EINVAL; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:87 @ do_div(clk_period_ns, clk_freq); } - for (pres = 0; pres <= ATMEL_HLCDC_PWMPS_MAX; pres++) { + for (pres = 0; pres <= (is_xlcdc ? ATMEL_XLCDC_PWMPS_MAX : ATMEL_HLCDC_PWMPS_MAX); pres++) { /* Errata: cannot divide by 1 on some IP revisions */ if (!pres && atmel->errata && atmel->errata->div1_clk_erratum) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:97 @ break; } - if (pres > ATMEL_HLCDC_PWMPS_MAX) + if (pres > (is_xlcdc ? ATMEL_XLCDC_PWMPS_MAX : ATMEL_HLCDC_PWMPS_MAX)) return -EINVAL; - pwmcfg = ATMEL_HLCDC_PWMPS(pres); + if (is_xlcdc) + pwmcfg = ATMEL_XLCDC_PWMPS(pres); + else + pwmcfg = ATMEL_HLCDC_PWMPS(pres); if (new_clk != atmel->cur_clk) { u32 gencfg = 0; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:116 @ clk_disable_unprepare(atmel->cur_clk); atmel->cur_clk = new_clk; - if (new_clk == hlcdc->sys_clk) + if (new_clk == hlcdc->periph_clk) gencfg = ATMEL_HLCDC_CLKPWMSEL; ret = regmap_update_bits(hlcdc->regmap, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:145 @ ret = regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(6), ATMEL_HLCDC_PWMCVAL_MASK | - ATMEL_HLCDC_PWMPS_MASK | + (is_xlcdc ? ATMEL_XLCDC_PWMPS_MASK : ATMEL_HLCDC_PWMPS_MASK) | ATMEL_HLCDC_PWMPOL, pwmcfg); if (ret) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:195 @ .div1_clk_erratum = true, }; +static const struct atmel_hlcdc_pwm_errata atmel_hlcdc_pwm_sama7d65_errata = { + .is_xlcdc = true, +}; + #ifdef CONFIG_PM_SLEEP static int atmel_hlcdc_pwm_suspend(struct device *dev) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:256 @ .data = &atmel_hlcdc_pwm_sama5d3_errata, }, { .compatible = "microchip,sam9x60-hlcdc", }, + + { .compatible = "microchip,sama7d65-xlcdc", + .data = &atmel_hlcdc_pwm_sama7d65_errata, + }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, atmel_hlcdc_dt_ids); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/remoteproc/Kconfig linux-microchip/drivers/remoteproc/Kconfig --- linux-6.6.51/drivers/remoteproc/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/remoteproc/Kconfig 2024-11-26 14:02:38.338508744 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:57 @ This can be either built-in or a loadable module. If unsure say N. +config MIV_REMOTEPROC + tristate "MIV remoteproc support" + depends on SOC_MICROCHIP_POLARFIRE + depends on MAILBOX + depends on MIV_IHC + depends on RISCV_SBI + help + Say y here to support booting and loading firmware to harts (cores) + associated with an Asymmetric Multiprocessing (AMP) context in + Microchip's SoCs using Mi-V ecosystem. + + This can be either built-in or a loadable module. + If compiled as module, the module will be called miv_remoteproc. + If unsure say N. + +config MCHP_IPC_REMOTEPROC + tristate "Microchip IPC remoteproc support" + depends on MCHP_SBI_IPC_MBOX || COMPILE_TEST + depends on RISCV_SBI + help + Say y here to support booting and loading firmware to remote + processors on various Microchip family of RISC-V SoCs via the + remote processor framework. + This can be either built-in or a loadable module. + If compiled as module, the module will be called mchp_ipc_remoteproc. + If unsure say N. + config MTK_SCP tristate "Mediatek SCP support" depends on ARCH_MEDIATEK || COMPILE_TEST diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/remoteproc/Makefile linux-microchip/drivers/remoteproc/Makefile --- linux-6.6.51/drivers/remoteproc/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/remoteproc/Makefile 2024-11-26 14:02:38.338508744 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:17 @ obj-$(CONFIG_IMX_REMOTEPROC) += imx_rproc.o obj-$(CONFIG_IMX_DSP_REMOTEPROC) += imx_dsp_rproc.o obj-$(CONFIG_INGENIC_VPU_RPROC) += ingenic_rproc.o +obj-$(CONFIG_MCHP_IPC_REMOTEPROC) += mchp_ipc_remoteproc.o +obj-$(CONFIG_MIV_REMOTEPROC) += miv_remoteproc.o obj-$(CONFIG_MTK_SCP) += mtk_scp.o mtk_scp_ipi.o obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o obj-$(CONFIG_WKUP_M3_RPROC) += wkup_m3_rproc.o diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/remoteproc/mchp_ipc_remoteproc.c linux-microchip/drivers/remoteproc/mchp_ipc_remoteproc.c --- linux-6.6.51/drivers/remoteproc/mchp_ipc_remoteproc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/remoteproc/mchp_ipc_remoteproc.c 2024-11-26 14:02:38.338508744 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Microchip IPC Remoteproc driver + * + * Copyright (c) 2021 - 2024 Microchip Technology Inc. All rights reserved. + * + * Author: Valentina Fernandez <valentina.fernandezalanis@microchip.com> + * + * Derived from the imx_rproc implementation: + * Copyright (c) 2017 Pengutronix, Oleksij Rempel <kernel@pengutronix.de> + */ + +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/mailbox_client.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_reserved_mem.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/remoteproc.h> +#include <linux/workqueue.h> + +#include <asm/sbi.h> +#include <asm/vendorid_list.h> +#include <linux/mailbox/mchp-ipc.h> + +#include "remoteproc_internal.h" + +#define SBI_EXT_MICROCHIP_TECHNOLOGY (SBI_EXT_VENDOR_START | \ + MICROCHIP_VENDOR_ID) + +#define MIV_RPROC_MEM_MAX 2 + +enum { + SBI_EXT_RPROC_STATE = 0x3, + SBI_EXT_RPROC_START, + SBI_EXT_RPROC_STOP, +}; + +/** + * enum mchp_ipc_rproc_mbox_messages - predefined mailbox messages + * + * @MCHP_IPC_RPROC_MBOX_READY: a ready message response from a remote context indicating + * that the remote context is up and running. + * + * @MIV_RP_MBOX_PENDING_MSG: Not currently in use, but reserved for future use + * to inform the receiver that there is a message awaiting in its receive-side + * vring. At the moment, one can explicitly send the index of the triggered + * virtqueue as a payload. + * + * @MIV_RP_MBOX_STOP: a stop request for the remote context + * + * @MIV_RP_MBOX_END_MSG: Indicates end of known/defined messages. + * This should be the last definition. + * + */ +enum mchp_ipc_rproc_mbox_messages { + MCHP_IPC_RPROC_MBOX_READY = 0xFFFFFF00, + MCHP_IPC_RPROC_MBOX_PENDING_MSG = 0xFFFFFF01, + MCHP_IPC_RPROC_MBOX_STOP = 0xFFFFFF02, + MCHP_IPC_RPROC_MBOX_END_MSG = 0xFFFFFF03, +}; + +struct mchp_ipc_rproc { + struct device *dev; + struct rproc *rproc; + struct mbox_chan *mbox_channel; + struct workqueue_struct *workqueue; + struct mbox_client mbox_client; + struct completion start_done; + struct work_struct rproc_work; + struct mchp_ipc_msg message; + void __iomem *rsc_table; + bool initialized; + u32 chan_id; +}; + +static int mchp_ipc_rproc_get_status(u32 chan) +{ + struct sbiret ret; + + ret = sbi_ecall(SBI_EXT_MICROCHIP_TECHNOLOGY, SBI_EXT_RPROC_STATE, + chan, 0, 0, 0, 0, 0); + + if (ret.error) + return sbi_err_map_linux_errno(ret.error); + + return ret.value; +} + +static int mchp_ipc_rproc_start(struct rproc *rproc) +{ + struct mchp_ipc_rproc *priv = rproc->priv; + struct sbiret ret; + int result; + + ret = sbi_ecall(SBI_EXT_MICROCHIP_TECHNOLOGY, SBI_EXT_RPROC_START, + priv->chan_id, rproc->bootaddr, 0, 0, 0, 0); + + if (ret.error) + return sbi_err_map_linux_errno(ret.error); + + result = wait_for_completion_timeout(&priv->start_done, + msecs_to_jiffies(5000)); + if (!result) { + dev_err(priv->dev, "timeout waiting for ready notification\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int mchp_ipc_rproc_stop(struct rproc *rproc) +{ + struct mchp_ipc_rproc *priv = rproc->priv; + struct sbiret ret; + + ret = sbi_ecall(SBI_EXT_MICROCHIP_TECHNOLOGY, SBI_EXT_RPROC_STOP, + priv->chan_id, MCHP_IPC_RPROC_MBOX_STOP, 0, 0, 0, 0); + + if (ret.error) + return sbi_err_map_linux_errno(ret.error); + + return ret.value; +} + +static int mchp_ipc_rproc_mem_alloc(struct rproc *rproc, struct rproc_mem_entry *mem) +{ + struct device *dev = rproc->dev.parent; + void *va; + + dev_dbg(dev, "map memory: %pad+%zx\n", &mem->dma, mem->len); + va = ioremap_wc(mem->dma, mem->len); + if (IS_ERR_OR_NULL(va)) { + dev_err(dev, "Unable to map memory region: %p+%zx\n", + &mem->dma, mem->len); + return -ENOMEM; + } + + mem->va = va; + + return 0; +} + +static int mchp_ipc_rproc_mem_release(struct rproc *rproc, + struct rproc_mem_entry *mem) +{ + dev_dbg(rproc->dev.parent, "unmap memory: %pad\n", &mem->dma); + iounmap(mem->va); + + return 0; +} + +static int mchp_ipc_rproc_prepare(struct rproc *rproc) +{ + struct mchp_ipc_rproc *priv = rproc->priv; + struct device_node *np = priv->dev->of_node; + struct rproc_mem_entry *mem; + struct reserved_mem *rmem; + struct of_phandle_iterator it; + u64 device_address; + + reinit_completion(&priv->start_done); + + of_phandle_iterator_init(&it, np, "memory-region", NULL, 0); + while (of_phandle_iterator_next(&it) == 0) { + /* + * Ignore the first memory region which will be used vdev + * buffer. No need to do extra handlings, rproc_add_virtio_dev + * will handle it. + */ + if (!strcmp(it.node->name, "vdev0buffer")) + continue; + + if (!strcmp(it.node->name, "rsc-table")) + continue; + + rmem = of_reserved_mem_lookup(it.node); + if (!rmem) { + dev_err(priv->dev, "unable to acquire memory-region\n"); + return -EINVAL; + } + + device_address = rmem->base; + + mem = rproc_mem_entry_init(priv->dev, NULL, (dma_addr_t)rmem->base, + rmem->size, device_address, mchp_ipc_rproc_mem_alloc, + mchp_ipc_rproc_mem_release, it.node->name); + + if (!mem) + return -ENOMEM; + + rproc_coredump_add_segment(rproc, device_address, rmem->size); + rproc_add_carveout(rproc, mem); + } + + return 0; +} + +static int mchp_ipc_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) +{ + int ret; + + ret = rproc_elf_load_rsc_table(rproc, fw); + if (ret) + dev_info(&rproc->dev, "No resource table in elf\n"); + + return 0; +} + +static void mchp_ipc_rproc_kick(struct rproc *rproc, int vqid) +{ + struct mchp_ipc_rproc *priv = (struct mchp_ipc_rproc *)rproc->priv; + struct mchp_ipc_msg msg; + int ret; + + msg.buf = (void *)&vqid; + msg.size = sizeof(vqid); + + ret = mbox_send_message(priv->mbox_channel, (void *)&msg); + if (ret < 0) + dev_err(priv->dev, + "failed to send mbox message, status = %d\n", ret); +} + +static int mchp_ipc_rproc_attach(struct rproc *rproc) +{ + return 0; +} + +static struct resource_table +*mchp_ipc_rproc_get_loaded_rsc_table(struct rproc *rproc, size_t *table_sz) +{ + struct mchp_ipc_rproc *priv = rproc->priv; + + if (!priv->rsc_table) + return NULL; + + *table_sz = SZ_1K; + return (struct resource_table *)priv->rsc_table; +} + +static const struct rproc_ops mchp_ipc_rproc_ops = { + .prepare = mchp_ipc_rproc_prepare, + .start = mchp_ipc_rproc_start, + .get_loaded_rsc_table = mchp_ipc_rproc_get_loaded_rsc_table, + .attach = mchp_ipc_rproc_attach, + .stop = mchp_ipc_rproc_stop, + .kick = mchp_ipc_rproc_kick, + .load = rproc_elf_load_segments, + .parse_fw = mchp_ipc_rproc_parse_fw, + .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table, + .sanity_check = rproc_elf_sanity_check, + .get_boot_addr = rproc_elf_get_boot_addr, +}; + +static int mchp_ipc_rproc_addr_init(struct mchp_ipc_rproc *priv, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + int i, err, rmem_np; + + rmem_np = of_count_phandle_with_args(np, "memory-region", NULL); + if (rmem_np <= 0) + return 0; + + for (i = 0; i < rmem_np; i++) { + struct device_node *node; + struct resource res; + + node = of_parse_phandle(np, "memory-region", i); + + if (!strncmp(node->name, "vdev", strlen("vdev"))) { + of_node_put(node); + continue; + } + + if (!strncmp(node->name, "rsc-table", strlen("rsc-table"))) { + of_node_put(node); + err = of_address_to_resource(node, 0, &res); + if (err) + return dev_err_probe(dev, err, + "unable to resolve memory region\n"); + priv->rsc_table = devm_ioremap(&pdev->dev, + res.start, resource_size(&res)); + } + } + + return 0; +} + +static void mchp_ipc_rproc_vq_work(struct work_struct *work) +{ + struct mchp_ipc_rproc *priv = container_of(work, struct mchp_ipc_rproc, rproc_work); + struct device *dev = priv->rproc->dev.parent; + + u32 msg = priv->message.buf[0]; + + /* + * Currently, we are expected to receive the following messages + * from the remote cluster: a ready message or receive the index + * of the triggered virtqueue as a payload. + * We can silently ignore any other type of mailbox messages since + * they are not meant for us and are meant to be received by the + * remote cluster only. + */ + switch (msg) { + case MCHP_IPC_RPROC_MBOX_READY: + complete(&priv->start_done); + break; + default: + if (msg >= MCHP_IPC_RPROC_MBOX_READY && msg < MCHP_IPC_RPROC_MBOX_END_MSG) + return; + if (msg > priv->rproc->max_notifyid) { + dev_info(dev, "dropping unknown message 0x%x", msg); + return; + } + /* msg contains the index of the triggered vring */ + if (rproc_vq_interrupt(priv->rproc, msg) == IRQ_NONE) + dev_dbg(dev, "no message was found in vqid %d\n", msg); + } +} + +static void mchp_ipc_rproc_rx_callback(struct mbox_client *mbox_client, void *msg) +{ + struct rproc *rproc = dev_get_drvdata(mbox_client->dev); + struct mchp_ipc_rproc *priv = rproc->priv; + + priv->message = *(struct mchp_ipc_msg *)msg; + queue_work(priv->workqueue, &priv->rproc_work); +} + +static int mchp_ipc_rproc_mbox_init(struct rproc *rproc) +{ + struct mchp_ipc_rproc *priv = rproc->priv; + struct device *dev = priv->dev; + struct mbox_client *mbox_client; + + mbox_client = &priv->mbox_client; + mbox_client->dev = dev; + mbox_client->tx_block = true; + mbox_client->tx_tout = 100; + mbox_client->knows_txdone = false; + mbox_client->rx_callback = mchp_ipc_rproc_rx_callback; + + priv->mbox_channel = mbox_request_channel(mbox_client, 0); + if (IS_ERR(priv->mbox_channel)) + return dev_err_probe(mbox_client->dev, + PTR_ERR(priv->mbox_channel), + "failed to request mailbox channel\n"); + + priv->chan_id = mchp_ipc_get_chan_id(priv->mbox_channel); + + return 0; +} + +static int mchp_ipc_rproc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct mchp_ipc_rproc *priv; + struct rproc *rproc; + int ret; + + rproc = rproc_alloc(dev, np->name, &mchp_ipc_rproc_ops, + NULL, sizeof(*priv)); + if (!rproc) + return -ENOMEM; + + priv = rproc->priv; + priv->rproc = rproc; + priv->dev = dev; + + dev_set_drvdata(dev, rproc); + + priv->workqueue = create_workqueue(dev_name(dev)); + if (!priv->workqueue) { + ret = -ENOMEM; + dev_err_probe(dev, ret, "cannot create workqueue\n"); + goto err_put_rproc; + } + + INIT_WORK(&priv->rproc_work, mchp_ipc_rproc_vq_work); + init_completion(&priv->start_done); + + ret = mchp_ipc_rproc_mbox_init(rproc); + if (ret) + goto err_put_wkq; + + ret = mchp_ipc_rproc_addr_init(priv, pdev); + if (ret) + goto err_put_mbox; + + rproc->state = mchp_ipc_rproc_get_status(priv->chan_id); + if (ret < 0) { + dev_err_probe(dev, ret, "Couldn't get channel status\n"); + goto err_put_mbox; + } + + if (rproc->state != RPROC_DETACHED) + rproc->auto_boot = of_property_present(np, "microchip,auto-boot"); + + ret = rproc_add(rproc); + if (ret) { + dev_err_probe(dev, ret, "rproc_add failed\n"); + goto err_put_mbox; + } + + return 0; + +err_put_mbox: + mbox_free_channel(priv->mbox_channel); +err_put_wkq: + destroy_workqueue(priv->workqueue); +err_put_rproc: + rproc_free(rproc); + + return ret; +} + +static int mchp_ipc_rproc_remove(struct platform_device *pdev) +{ + struct rproc *rproc = platform_get_drvdata(pdev); + struct mchp_ipc_rproc *priv = rproc->priv; + + rproc_del(rproc); + mbox_free_channel(priv->mbox_channel); + destroy_workqueue(priv->workqueue); + rproc_free(rproc); + + return 0; +} + +static const struct of_device_id mchp_ipc_rproc_of_match[] __maybe_unused = { + { .compatible = "microchip,ipc-remoteproc", }, + {} +}; +MODULE_DEVICE_TABLE(of, mchp_ipc_rproc_of_match); + +static struct platform_driver mchp_ipc_rproc_driver = { + .probe = mchp_ipc_rproc_probe, + .remove = mchp_ipc_rproc_remove, + .driver = { + .name = "microchip-ipc-rproc", + .of_match_table = of_match_ptr(mchp_ipc_rproc_of_match), + }, +}; + +module_platform_driver(mchp_ipc_rproc_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Valentina Fernandez <valentina.fernandezalanis@microchip.com>"); +MODULE_DESCRIPTION("Microchip IPC Remote Processor control driver"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/remoteproc/miv_remoteproc.c linux-microchip/drivers/remoteproc/miv_remoteproc.c --- linux-6.6.51/drivers/remoteproc/miv_remoteproc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/remoteproc/miv_remoteproc.c 2024-11-26 14:02:38.338508744 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Microchip Mi-V Remote Proc driver + * + * Copyright (c) 2021 - 2022 Microchip Technology Inc. All rights reserved. + * + * Author: Valentina Fernandez <valentina.fernandezalanis@microchip.com> + * + * Derived from the imx_rproc implementation: + * Copyright (c) 2017 Pengutronix, Oleksij Rempel <kernel@pengutronix.de> + */ + +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/mailbox_client.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_reserved_mem.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/remoteproc.h> +#include <linux/workqueue.h> + +#include <asm/sbi.h> +#include <linux/mailbox/miv_ihc.h> +#include <soc/microchip/mpfs.h> + +#include "remoteproc_internal.h" + +#define SBI_EXT_VENDOR_START 0x09000000 +#define MICROCHIP_TECHNOLOGY_MVENDOR_ID 0x029 + +#define SBI_EXT_MICROCHIP_TECHNOLOGY (SBI_EXT_VENDOR_START | \ + MICROCHIP_TECHNOLOGY_MVENDOR_ID) + +#define MIV_RPROC_MEM_MAX 2 + +/** + * enum miv_remote_state - remote context state + * + * @CONTEXT_OFFLINE: The remote context is offline, so loading of firmware + * and booting should be done by remoteproc. + * + * @CONTEXT_RUNNNING: The remote context has been booted by the bootloader + */ +enum miv_remote_state { + CONTEXT_OFFLINE, + CONTEXT_RUNNNING, +}; + +/** + * enum miv_rp_mbox_messages - predefined mailbox messages + * + * @MIV_RP_MBOX_READY: a ready message response from a remote context indicating + * that the remote context is up and running. + * + * @MIV_RP_MBOX_PENDING_MSG: Not currently in use, but reserved for future use + * to inform the receiver that there is a message awaiting in its receive-side + * vring. At the moment, one can explicitly send the index of the triggered + * virtqueue as a payload. + * + * @MIV_RP_MBOX_STOP: a stop request for the remote context + * + * @MIV_RP_MBOX_END_MSG: Indicates end of known/defined messages. + * This should be the last definition. + * + */ +enum miv_rp_mbox_messages { + MIV_RP_MBOX_READY = 0xFFFFFF00, + MIV_RP_MBOX_PENDING_MSG = 0xFFFFFF01, + MIV_RP_MBOX_STOP = 0xFFFFFF02, + MIV_RP_MBOX_END_MSG = 0xFFFFFF03, +}; + +struct miv_rproc_mem { + void __iomem *cpu_addr; + phys_addr_t sys_addr; + size_t size; +}; + +struct miv_rproc { + struct device *dev; + struct rproc *rproc; + struct mbox_chan *mbox_channel; + struct workqueue_struct *workqueue; + struct miv_rproc_mem mem[MIV_RPROC_MEM_MAX]; + struct mbox_client mbox_client; + struct completion start_done; + struct work_struct rproc_work; + struct miv_ihc_msg miv_ihc_message; + void __iomem *rsc_table; + bool initialized; + u32 remote_context_id; +}; + +static int miv_rproc_get_status(u32 remote_context_id) +{ + struct sbiret ret; + + ret = sbi_ecall(SBI_EXT_MICROCHIP_TECHNOLOGY, SBI_EXT_RPROC_STATE, + remote_context_id, 0, 0, 0, 0, 0); + + if (ret.error) + return sbi_err_map_linux_errno(ret.error); + + return ret.value; +} + +static int miv_rproc_start(struct rproc *rproc) +{ + struct miv_rproc *priv = rproc->priv; + struct sbiret ret; + int result; + + ret = sbi_ecall(SBI_EXT_MICROCHIP_TECHNOLOGY, SBI_EXT_RPROC_START, + priv->remote_context_id, rproc->bootaddr, 0, 0, 0, 0); + + if (ret.error) + return sbi_err_map_linux_errno(ret.error); + + if (rproc->nb_vdev > 0) { + result = wait_for_completion_timeout(&priv->start_done, + msecs_to_jiffies(5000)); + if (!result) + dev_err(priv->dev, "timeout waiting for ready notification\n"); + } + + return ret.value; +} + +static int miv_rproc_stop(struct rproc *rproc) +{ + struct miv_rproc *priv = rproc->priv; + struct sbiret ret; + + ret = sbi_ecall(SBI_EXT_MICROCHIP_TECHNOLOGY, SBI_EXT_RPROC_STOP, + priv->remote_context_id, MIV_RP_MBOX_STOP, 0, 0, 0, 0); + + priv->initialized = false; + + if (ret.error) + return sbi_err_map_linux_errno(ret.error); + + return ret.value; +} + +static int miv_rproc_mem_alloc(struct rproc *rproc, + struct rproc_mem_entry *mem) +{ + struct device *dev = rproc->dev.parent; + void *va; + + dev_dbg(dev, "map memory: %p+%zx\n", &mem->dma, mem->len); + va = ioremap_wc(mem->dma, mem->len); + if (IS_ERR_OR_NULL(va)) { + dev_err(dev, "Unable to map memory region: %p+%zx\n", + &mem->dma, mem->len); + return -ENOMEM; + } + + mem->va = va; + + return 0; +} + +static int miv_rproc_mem_release(struct rproc *rproc, + struct rproc_mem_entry *mem) +{ + dev_dbg(rproc->dev.parent, "unmap memory: %pa\n", &mem->dma); + iounmap(mem->va); + + return 0; +} + +static int miv_rproc_prepare(struct rproc *rproc) +{ + struct miv_rproc *priv = rproc->priv; + struct device_node *np = priv->dev->of_node; + struct rproc_mem_entry *mem; + struct reserved_mem *rmem; + struct of_phandle_iterator it; + u64 da; + + reinit_completion(&priv->start_done); + + of_phandle_iterator_init(&it, np, "memory-region", NULL, 0); + while (of_phandle_iterator_next(&it) == 0) { + /* + * Ignore the first memory region which will be used vdev + * buffer. No need to do extra handlings, rproc_add_virtio_dev + * will handle it. + */ + if (!strcmp(it.node->name, "vdev0buffer")) + continue; + + rmem = of_reserved_mem_lookup(it.node); + if (!rmem) { + dev_err(priv->dev, "unable to acquire memory-region\n"); + return -EINVAL; + } + + da = rmem->base; + + mem = rproc_mem_entry_init(priv->dev, NULL, (dma_addr_t)rmem->base, + rmem->size, da, miv_rproc_mem_alloc, + miv_rproc_mem_release, it.node->name); + + if (!mem) + return -ENOMEM; + + rproc_coredump_add_segment(rproc, da, rmem->size); + rproc_add_carveout(rproc, mem); + } + + return 0; +} + +static int miv_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) +{ + int ret; + + ret = rproc_elf_load_rsc_table(rproc, fw); + if (ret) + dev_info(&rproc->dev, "No resource table in elf\n"); + + return 0; +} + +static void miv_rproc_kick(struct rproc *rproc, int vqid) +{ + struct miv_rproc *priv = (struct miv_rproc *)rproc->priv; + int ret; + + /* + * If already initialized, do not notify each time rx buffers + * are consumed, since we are already sending an ACK using the + * Inter-Hart Communication (IHC) driver. The RPMsg-lite on the + * remote side has the RL_ALLOW_CONSUMED_BUFFERS_NOTIFICATION + * macro set to 0. + */ + if (vqid == 0) { + if (!priv->initialized) + priv->initialized = true; + else + return; + } + + ret = mbox_send_message(priv->mbox_channel, (void *)&vqid); + if (ret < 0) + dev_err(priv->dev, + "failed to send mbox message, status = %d\n", ret); +} + +static int miv_rproc_attach(struct rproc *rproc) +{ + return 0; +} + +static struct resource_table +*miv_rproc_get_loaded_rsc_table(struct rproc *rproc, size_t *table_sz) +{ + struct miv_rproc *priv = rproc->priv; + + if (!priv->rsc_table) + return NULL; + + *table_sz = SZ_1K; + return (struct resource_table *)priv->rsc_table; +} + +static const struct rproc_ops miv_rproc_ops = { + .prepare = miv_rproc_prepare, + .start = miv_rproc_start, + .get_loaded_rsc_table = miv_rproc_get_loaded_rsc_table, + .attach = miv_rproc_attach, + .stop = miv_rproc_stop, + .kick = miv_rproc_kick, + .load = rproc_elf_load_segments, + .parse_fw = miv_rproc_parse_fw, + .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table, + .sanity_check = rproc_elf_sanity_check, + .get_boot_addr = rproc_elf_get_boot_addr, +}; + +static int miv_rproc_addr_init(struct miv_rproc *priv, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + int a, b = 0, err, rmem_np; + + /* memory-region is an optional property */ + rmem_np = of_count_phandle_with_args(np, "memory-region", NULL); + if (rmem_np <= 0) + return 0; + + /* remap optional addresses */ + for (a = 0; a < rmem_np; a++) { + struct device_node *node; + struct resource res; + + node = of_parse_phandle(np, "memory-region", a); + + if (!strncmp(node->name, "vdev", strlen("vdev"))) { + of_node_put(node); + continue; + } + + err = of_address_to_resource(node, 0, &res); + of_node_put(node); + if (err) + return dev_err_probe(dev, err, + "unable to resolve memory region\n"); + + if (b >= MIV_RPROC_MEM_MAX) + break; + + priv->mem[b].cpu_addr = devm_ioremap(&pdev->dev, + res.start, resource_size(&res)); + + if (!priv->mem[b].cpu_addr) + return dev_err_probe(dev, -ENOMEM, + "failed to remap %pr\n", &res); + + priv->mem[b].sys_addr = res.start; + priv->mem[b].size = resource_size(&res); + if (!strcmp(node->name, "rsc-table")) + priv->rsc_table = priv->mem[b].cpu_addr; + b++; + } + + return 0; +} + +static void miv_rproc_vq_work(struct work_struct *work) +{ + struct miv_rproc *priv = container_of(work, struct miv_rproc, rproc_work); + struct device *dev = priv->rproc->dev.parent; + u32 msg = priv->miv_ihc_message.msg[0]; + + /* + * Currently, we are expected to receive the following messages + * from the remote context; a ready message or simply receive the + * index of the triggered virtqueue as a payload. + * We can silently ignore any other type of mailbox messages since + * they are not meant for us and are meant to be received by the + * remote context only. + */ + switch (msg) { + case MIV_RP_MBOX_READY: + complete(&priv->start_done); + break; + default: + if (msg >= MIV_RP_MBOX_READY && msg < MIV_RP_MBOX_END_MSG) + return; + if (msg > priv->rproc->max_notifyid) { + dev_info(dev, "dropping unknown message 0x%x", msg); + return; + } + /* msg contains the index of the triggered vring */ + if (rproc_vq_interrupt(priv->rproc, msg) == IRQ_NONE) + dev_dbg(dev, "no message was found in vqid %d\n", msg); + } +} + +static void miv_rproc_rx_callback(struct mbox_client *mbox_client, void *msg) +{ + struct rproc *rproc = dev_get_drvdata(mbox_client->dev); + struct miv_rproc *priv = rproc->priv; + + priv->miv_ihc_message = *(struct miv_ihc_msg *)msg; + queue_work(priv->workqueue, &priv->rproc_work); +} + +static int miv_rproc_mbox_init(struct rproc *rproc) +{ + struct miv_rproc *priv = rproc->priv; + struct device *dev = priv->dev; + struct mbox_client *mbox_client; + struct miv_ihc *ihc; + + mbox_client = &priv->mbox_client; + mbox_client->dev = dev; + mbox_client->tx_block = true; + mbox_client->tx_tout = 100; + mbox_client->knows_txdone = false; + mbox_client->rx_callback = miv_rproc_rx_callback; + + priv->mbox_channel = mbox_request_channel(mbox_client, 0); + if (IS_ERR(priv->mbox_channel)) + return dev_err_probe(mbox_client->dev, + PTR_ERR(priv->mbox_channel), + "failed to request mailbox channel\n"); + + ihc = (struct miv_ihc *)priv->mbox_channel->con_priv; + priv->remote_context_id = ihc->remote_context_id; + + return 0; +} + +static void miv_rproc_free_mbox(struct rproc *rproc) +{ + struct miv_rproc *priv = rproc->priv; + + mbox_free_channel(priv->mbox_channel); +} + +static int miv_rproc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct miv_rproc *priv; + struct device_node *np = dev->of_node; + struct rproc *rproc; + int ret; + + dev_info(dev, "Started Mi-V Remote Processor driver\n"); + + rproc = rproc_alloc(dev, "miv-rproc", &miv_rproc_ops, + NULL, sizeof(*priv)); + if (!rproc) + return -ENOMEM; + + priv = rproc->priv; + priv->rproc = rproc; + priv->dev = dev; + + dev_set_drvdata(dev, rproc); + + priv->workqueue = create_workqueue(dev_name(dev)); + if (!priv->workqueue) { + ret = -ENOMEM; + dev_err_probe(dev, ret, "cannot create workqueue\n"); + goto err_put_rproc; + } + + INIT_WORK(&priv->rproc_work, miv_rproc_vq_work); + init_completion(&priv->start_done); + + ret = miv_rproc_mbox_init(rproc); + if (ret) + goto err_put_wkq; + + ret = miv_rproc_addr_init(priv, pdev); + if (ret) + goto err_put_mbox; + + ret = miv_rproc_get_status(priv->remote_context_id); + if (ret < 0) { + dev_err_probe(dev, ret, "Couldn't get remote context status\n"); + goto err_put_mbox; + } + + if (ret == CONTEXT_RUNNNING) + rproc->state = RPROC_DETACHED; + + if (rproc->state != RPROC_DETACHED) + rproc->auto_boot = of_property_read_bool(np, "microchip,auto-boot"); + + ret = rproc_add(rproc); + if (ret) { + return dev_err_probe(dev, ret, "rproc_add failed\n"); + goto err_put_mbox; + } + + return 0; + +err_put_mbox: + miv_rproc_free_mbox(rproc); +err_put_wkq: + destroy_workqueue(priv->workqueue); +err_put_rproc: + rproc_free(rproc); + + return ret; +} + +static int miv_rproc_remove(struct platform_device *pdev) +{ + struct rproc *rproc = platform_get_drvdata(pdev); + struct miv_rproc *priv = rproc->priv; + + rproc_del(rproc); + miv_rproc_free_mbox(rproc); + destroy_workqueue(priv->workqueue); + rproc_free(rproc); + + return 0; +} + +static const struct of_device_id miv_rproc_of_match[] __maybe_unused = { + { .compatible = "microchip,miv-remoteproc", }, + {} +}; +MODULE_DEVICE_TABLE(of, miv_rproc_of_match); + +static struct platform_driver miv_rproc_driver = { + .probe = miv_rproc_probe, + .remove = miv_rproc_remove, + .driver = { + .name = "miv-rproc", + .of_match_table = of_match_ptr(miv_rproc_of_match), + }, +}; + +module_platform_driver(miv_rproc_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Valentina Fernandez <valentina.fernandezalanis@microchip.com>"); +MODULE_DESCRIPTION("Microchip Mi-V Remote Processor control driver"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/reset/reset-mpfs.c linux-microchip/drivers/reset/reset-mpfs.c --- linux-6.6.51/drivers/reset/reset-mpfs.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/reset/reset-mpfs.c 2024-11-26 14:02:38.342508816 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:11 @ */ #include <linux/auxiliary_bus.h> #include <linux/delay.h> +#include <linux/io.h> +#include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/regmap.h> #include <linux/reset-controller.h> #include <dt-bindings/clock/microchip,mpfs-clock.h> #include <soc/microchip/mpfs.h> @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:32 @ #define MPFS_SLEEP_MIN_US 100 #define MPFS_SLEEP_MAX_US 200 +#define REG_SUBBLK_RESET_CR 0x88u + /* block concurrent access to the soft reset register */ static DEFINE_SPINLOCK(mpfs_reset_lock); +struct mpfs_reset { + void __iomem *base; + struct regmap *regmap; + struct reset_controller_dev rcdev; +}; + +static inline u32 mpfs_reset_read(struct mpfs_reset *rst) +{ + u32 ret; + + if (rst->regmap) + regmap_read(rst->regmap, REG_SUBBLK_RESET_CR, &ret); + else + ret = readl(rst->base); + + return ret; +} + +static inline void mpfs_reset_write(struct mpfs_reset *rst, u32 val) +{ + if (rst->regmap) + regmap_write(rst->regmap, REG_SUBBLK_RESET_CR, val); + else + writel(val, rst->base); +} + +static inline struct mpfs_reset *to_mpfs_reset(struct reset_controller_dev *rcdev) +{ + return container_of(rcdev, struct mpfs_reset, rcdev); +} + /* * Peripheral clock resets */ - static int mpfs_assert(struct reset_controller_dev *rcdev, unsigned long id) { + struct mpfs_reset *rst = to_mpfs_reset(rcdev); unsigned long flags; u32 reg; spin_lock_irqsave(&mpfs_reset_lock, flags); - reg = mpfs_reset_read(rcdev->dev); + reg = mpfs_reset_read(rst); reg |= BIT(id); - mpfs_reset_write(rcdev->dev, reg); + mpfs_reset_write(rst, reg); spin_unlock_irqrestore(&mpfs_reset_lock, flags); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:90 @ static int mpfs_deassert(struct reset_controller_dev *rcdev, unsigned long id) { + struct mpfs_reset *rst = to_mpfs_reset(rcdev); unsigned long flags; u32 reg; spin_lock_irqsave(&mpfs_reset_lock, flags); - reg = mpfs_reset_read(rcdev->dev); + reg = mpfs_reset_read(rst); reg &= ~BIT(id); - mpfs_reset_write(rcdev->dev, reg); + mpfs_reset_write(rst, reg); spin_unlock_irqrestore(&mpfs_reset_lock, flags); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:107 @ static int mpfs_status(struct reset_controller_dev *rcdev, unsigned long id) { - u32 reg = mpfs_reset_read(rcdev->dev); + struct mpfs_reset *rst = to_mpfs_reset(rcdev); + u32 reg = mpfs_reset_read(rst); /* * It is safe to return here as MPFS_NUM_RESETS makes sure the sign bit @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:158 @ return index - MPFS_PERIPH_OFFSET; } -static int mpfs_reset_probe(struct auxiliary_device *adev, - const struct auxiliary_device_id *id) +static int mpfs_reset_mfd_probe(struct platform_device *pdev) { - struct device *dev = &adev->dev; struct reset_controller_dev *rcdev; + struct device *dev = &pdev->dev; + struct mpfs_reset *rst; - rcdev = devm_kzalloc(dev, sizeof(*rcdev), GFP_KERNEL); - if (!rcdev) + rst = devm_kzalloc(dev, sizeof(*rst), GFP_KERNEL); + if (!rst) return -ENOMEM; + rcdev = &rst->rcdev; rcdev->dev = dev; - rcdev->dev->parent = dev->parent; rcdev->ops = &mpfs_reset_ops; + + rcdev->of_node = pdev->dev.parent->of_node; + rcdev->of_reset_n_cells = 1; + rcdev->of_xlate = mpfs_reset_xlate; + rcdev->nr_resets = MPFS_NUM_RESETS; + + rst->regmap = device_node_to_regmap(pdev->dev.parent->of_node); + if (IS_ERR(rst->regmap)) + dev_err_probe(dev, PTR_ERR(rst->regmap), "Failed to find syscon regmap\n"); + + return devm_reset_controller_register(dev, rcdev); +} + +static struct platform_driver mpfs_reset_mfd_driver = { + .probe = mpfs_reset_mfd_probe, + .driver = { + .name = "mpfs-reset", + }, +}; +module_platform_driver(mpfs_reset_mfd_driver); + +static int mpfs_reset_adev_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct reset_controller_dev *rcdev; + struct device *dev = &adev->dev; + struct mpfs_reset *rst; + + rst = devm_kzalloc(dev, sizeof(*rst), GFP_KERNEL); + if (!rst) + return -ENOMEM; + + rst->base = (void __iomem *)adev->dev.platform_data; + + rcdev = &rst->rcdev; + rcdev->dev = dev; + rcdev->ops = &mpfs_reset_ops; + rcdev->of_node = dev->parent->of_node; rcdev->of_reset_n_cells = 1; rcdev->of_xlate = mpfs_reset_xlate; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:217 @ return devm_reset_controller_register(dev, rcdev); } +static void mpfs_reset_unregister_adev(void *_adev) +{ + struct auxiliary_device *adev = _adev; + + auxiliary_device_delete(adev); + auxiliary_device_uninit(adev); +} + +static void mpfs_reset_adev_release(struct device *dev) +{ + struct auxiliary_device *adev = to_auxiliary_dev(dev); + + kfree(adev); +} + +static struct auxiliary_device *mpfs_reset_adev_alloc(struct device *clk_dev) +{ + struct auxiliary_device *adev; + int ret; + + adev = kzalloc(sizeof(*adev), GFP_KERNEL); + if (!adev) + return ERR_PTR(-ENOMEM); + + adev->name = "reset-mpfs"; + adev->dev.parent = clk_dev; + adev->dev.release = mpfs_reset_adev_release; + adev->id = 666u; + + ret = auxiliary_device_init(adev); + if (ret) { + kfree(adev); + return ERR_PTR(ret); + } + + return adev; +} + +int mpfs_reset_controller_register(struct device *clk_dev, void __iomem *base) +{ + struct auxiliary_device *adev; + int ret; + + adev = mpfs_reset_adev_alloc(clk_dev); + if (IS_ERR(adev)) + return PTR_ERR(adev); + + ret = auxiliary_device_add(adev); + if (ret) { + auxiliary_device_uninit(adev); + return ret; + } + + adev->dev.platform_data = (__force void *)base; + + return devm_add_action_or_reset(clk_dev, mpfs_reset_unregister_adev, adev); +} +EXPORT_SYMBOL_NS_GPL(mpfs_reset_controller_register, MCHP_CLK_MPFS); + static const struct auxiliary_device_id mpfs_reset_ids[] = { { - .name = "clk_mpfs.reset-mpfs", + .name = "reset_mpfs.reset-mpfs", }, { } }; MODULE_DEVICE_TABLE(auxiliary, mpfs_reset_ids); -static struct auxiliary_driver mpfs_reset_driver = { - .probe = mpfs_reset_probe, +static struct auxiliary_driver mpfs_reset_aux_driver = { + .probe = mpfs_reset_adev_probe, .id_table = mpfs_reset_ids, }; -module_auxiliary_driver(mpfs_reset_driver); +module_auxiliary_driver(mpfs_reset_aux_driver); MODULE_DESCRIPTION("Microchip PolarFire SoC Reset Driver"); MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/soc/atmel/soc.c linux-microchip/drivers/soc/atmel/soc.c --- linux-6.6.51/drivers/soc/atmel/soc.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/soc/atmel/soc.c 2024-11-26 14:02:38.462510965 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:104 @ AT91_CIDR_VERSION_MASK, SAM9X60_D6K_EXID_MATCH, "sam9x60 8MiB SDRAM SiP", "sam9x60"), #endif +#ifdef CONFIG_SOC_SAM9X7 + AT91_SOC(SAM9X7_CIDR_MATCH, AT91_CIDR_MATCH_MASK, + AT91_CIDR_VERSION_MASK, SAM9X75_EXID_MATCH, + "sam9x75", "sam9x7"), + AT91_SOC(SAM9X7_CIDR_MATCH, AT91_CIDR_MATCH_MASK, + AT91_CIDR_VERSION_MASK, SAM9X72_EXID_MATCH, + "sam9x72", "sam9x7"), + AT91_SOC(SAM9X7_CIDR_MATCH, AT91_CIDR_MATCH_MASK, + AT91_CIDR_VERSION_MASK, SAM9X70_EXID_MATCH, + "sam9x70", "sam9x7"), + AT91_SOC(SAM9X7_CIDR_MATCH, SAM9X75_D1G_EXID_MATCH, + AT91_CIDR_VERSION_MASK, SAM9X75_EXID_MATCH, + "sam9x75 1Gb DDR3L SiP ", "sam9x7"), + AT91_SOC(SAM9X7_CIDR_MATCH, SAM9X75_D5M_EXID_MATCH, + AT91_CIDR_VERSION_MASK, SAM9X75_EXID_MATCH, + "sam9x75 512Mb DDR2 SiP", "sam9x7"), + AT91_SOC(SAM9X7_CIDR_MATCH, SAM9X75_D1M_EXID_MATCH, + AT91_CIDR_VERSION_MASK, SAM9X75_EXID_MATCH, + "sam9x75 128Mb DDR2 SiP", "sam9x7"), + AT91_SOC(SAM9X7_CIDR_MATCH, SAM9X75_D2G_EXID_MATCH, + AT91_CIDR_VERSION_MASK, SAM9X75_EXID_MATCH, + "sam9x75 2Gb DDR3L SiP", "sam9x7"), +#endif #ifdef CONFIG_SOC_SAMA5 AT91_SOC(SAMA5D2_CIDR_MATCH, AT91_CIDR_MATCH_MASK, AT91_CIDR_VERSION_MASK, SAMA5D21CU_EXID_MATCH, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:249 @ "samv70q19", "samv7"), #endif #ifdef CONFIG_SOC_SAMA7 + AT91_SOC(SAMA7D65_CIDR_MATCH, AT91_CIDR_MASK_SAMA7G5, + AT91_CIDR_VERSION_MASK_SAMA7G5, SAMA7D65_EXID_MATCH, + "sama7d65", "sama7d6"), AT91_SOC(SAMA7G5_CIDR_MATCH, AT91_CIDR_MATCH_MASK, AT91_CIDR_VERSION_MASK_SAMA7G5, SAMA7G51_EXID_MATCH, "sama7g51", "sama7g5"), @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:311 @ void __iomem *regs; static const struct of_device_id chipids[] = { { .compatible = "atmel,sama5d2-chipid" }, + { .compatible = "microchip,sama7d65-chipid"}, { .compatible = "microchip,sama7g5-chipid" }, { }, }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:400 @ { .compatible = "atmel,at91sam9", }, { .compatible = "atmel,sama5", }, { .compatible = "atmel,samv7", }, + { .compatible = "microchip,sama7d65",}, { .compatible = "microchip,sama7g5", }, { } }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/soc/atmel/soc.h linux-microchip/drivers/soc/atmel/soc.h --- linux-6.6.51/drivers/soc/atmel/soc.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/soc/atmel/soc.h 2024-11-26 14:02:38.462510965 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:47 @ #define AT91SAM9X5_CIDR_MATCH 0x019a05a0 #define AT91SAM9N12_CIDR_MATCH 0x019a07a0 #define SAM9X60_CIDR_MATCH 0x019b35a0 +#define SAMA7D65_CIDR_MATCH 0x00262100 #define SAMA7G5_CIDR_MATCH 0x00162100 +#define SAM9X7_CIDR_MATCH 0x09750020 #define AT91SAM9M11_EXID_MATCH 0x00000001 #define AT91SAM9M10_EXID_MATCH 0x00000002 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:71 @ #define SAM9X60_D1G_EXID_MATCH 0x00000010 #define SAM9X60_D6K_EXID_MATCH 0x00000011 +#define SAMA7D65_EXID_MATCH 0x00000080 + #define SAMA7G51_EXID_MATCH 0x3 #define SAMA7G52_EXID_MATCH 0x2 #define SAMA7G53_EXID_MATCH 0x1 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:81 @ #define SAMA7G54_D2G_EXID_MATCH 0x00000020 #define SAMA7G54_D4G_EXID_MATCH 0x00000028 +#define SAM9X75_EXID_MATCH 0x00000000 +#define SAM9X72_EXID_MATCH 0x00000004 +#define SAM9X70_EXID_MATCH 0x00000005 +#define SAM9X75_D1G_EXID_MATCH 0x00000001 +#define SAM9X75_D5M_EXID_MATCH 0x00000002 +#define SAM9X75_D1M_EXID_MATCH 0x00000003 +#define SAM9X75_D2G_EXID_MATCH 0x00000006 + #define AT91SAM9XE128_CIDR_MATCH 0x329973a0 #define AT91SAM9XE256_CIDR_MATCH 0x329a93a0 #define AT91SAM9XE512_CIDR_MATCH 0x329aa3a0 diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/soc/Kconfig linux-microchip/drivers/soc/Kconfig --- linux-6.6.51/drivers/soc/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/soc/Kconfig 2024-11-26 14:02:38.462510965 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:26 @ source "drivers/soc/renesas/Kconfig" source "drivers/soc/rockchip/Kconfig" source "drivers/soc/samsung/Kconfig" -source "drivers/soc/sifive/Kconfig" source "drivers/soc/starfive/Kconfig" source "drivers/soc/sunxi/Kconfig" source "drivers/soc/tegra/Kconfig" diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/soc/Makefile linux-microchip/drivers/soc/Makefile --- linux-6.6.51/drivers/soc/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/soc/Makefile 2024-11-26 14:02:38.462510965 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:31 @ obj-y += renesas/ obj-y += rockchip/ obj-$(CONFIG_SOC_SAMSUNG) += samsung/ -obj-y += sifive/ obj-y += sunxi/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-y += ti/ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/soc/microchip/Kconfig linux-microchip/drivers/soc/microchip/Kconfig --- linux-6.6.51/drivers/soc/microchip/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/soc/microchip/Kconfig 2024-11-26 14:02:38.466511038 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ config POLARFIRE_SOC_SYS_CTRL tristate "Microchip PolarFire SoC (MPFS) system controller support" depends on POLARFIRE_SOC_MAILBOX + depends on MTD help This driver adds support for the PolarFire SoC (MPFS) system controller. @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:12 @ module will be called mpfs_system_controller. If unsure, say N. + +config POLARFIRE_SOC_GENERIC_SERVICE + tristate "PFSOC FPGA generic system services" + depends on POLARFIRE_SOC_SYS_CTRL + help + The PolarFire SoC system controller is communicated with via a mailbox. + This driver implemtents the services provided by the system controller + which do not belong in a specific subsystem, such as reading the fpga + device certificate, all of which follow the same format: + - a command + optional payload sent to the sys controller + - a status + a payload returned to Linux + + To compile this driver as a module, choose M here. the + module will be called mpfs_generic_service. + + If unsure, say N. + +config POLARFIRE_SOC_SYSCONS + bool "PolarFire SoC (MPFS) syscon drivers" + default y + depends on SOC_MICROCHIP_POLARFIRE + select MFD_CORE + help + Filler text + + If unsure, say N. + diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/soc/microchip/Makefile linux-microchip/drivers/soc/microchip/Makefile --- linux-6.6.51/drivers/soc/microchip/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/soc/microchip/Makefile 2024-11-26 14:02:38.466511038 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1 @ obj-$(CONFIG_POLARFIRE_SOC_SYS_CTRL) += mpfs-sys-controller.o +obj-$(CONFIG_POLARFIRE_SOC_GENERIC_SERVICE) += mpfs-generic-service.o +obj-$(CONFIG_POLARFIRE_SOC_SYSCONS) += mpfs-control-scb.o mpfs-mss-top-sysreg.o diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/soc/microchip/mpfs-control-scb.c linux-microchip/drivers/soc/microchip/mpfs-control-scb.c --- linux-6.6.51/drivers/soc/microchip/mpfs-control-scb.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/soc/microchip/mpfs-control-scb.c 2024-11-26 14:02:38.466511038 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/mfd/core.h> +#include <linux/mfd/syscon.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> + +static const struct mfd_cell mpfs_control_scb_devs[] = { + { .name = "mpfs-tvs", }, +}; + +static int mpfs_control_scb_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int ret; + + ret = mfd_add_devices(dev, PLATFORM_DEVID_NONE, mpfs_control_scb_devs, + 1, NULL, 0, NULL); + if (ret) + return ret; + + return 0; +} + +static const struct of_device_id mpfs_control_scb_of_match[] = { + {.compatible = "microchip,mpfs-control-scb", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mpfs_control_scb_of_match); + +static struct platform_driver mpfs_control_scb_driver = { + .driver = { + .name = "mpfs-control-scb", + .of_match_table = mpfs_control_scb_of_match, + }, + .probe = mpfs_control_scb_probe, +}; +module_platform_driver(mpfs_control_scb_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>"); +MODULE_DESCRIPTION("PolarFire SoC control scb driver"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/soc/microchip/mpfs-generic-service.c linux-microchip/drivers/soc/microchip/mpfs-generic-service.c --- linux-6.6.51/drivers/soc/microchip/mpfs-generic-service.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/soc/microchip/mpfs-generic-service.c 2024-11-26 14:02:38.466511038 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microchip PolarFire SoC (MPFS) generic service driver + * + * Copyright (c) 2020-2022 Microchip Corporation. All rights reserved. + * + * Author: Conor Dooley <conor.dooley@microchip.com> + * + */ + +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/uaccess.h> +#include <linux/miscdevice.h> +#include <linux/of_platform.h> +#include <soc/microchip/mpfs.h> + +static DEFINE_MUTEX(mpfs_generic_service_mutex); + +#define MPFS_MAX_PAYLOAD 4096U +#define MPFS_MIN_PAYLOAD 9u + +struct mpfs_generic_service_priv { + struct mpfs_sys_controller *sys_controller; + struct mpfs_mss_response *response; + struct device *dev; + u8 *response_msg; +}; + +struct mpfs_generic_service_priv *generic_service_priv; + +static inline void mpfs_generic_service_free_message_structs(void) +{ + devm_kfree(generic_service_priv->dev, generic_service_priv->response->resp_msg); + devm_kfree(generic_service_priv->dev, generic_service_priv->response); + generic_service_priv->response = NULL; +} + +static ssize_t mpfs_generic_service_write(struct file *filep, const char __user *userbuf, + size_t len, loff_t *offset) +{ + struct mpfs_mss_response *response; + u8 *cmd_data; + struct mpfs_mss_msg msg; + int ret; + + if (len < MPFS_MIN_PAYLOAD || len > MPFS_MAX_PAYLOAD) + return -EINVAL; + + cmd_data = devm_kmalloc(generic_service_priv->dev, len, GFP_KERNEL); + if (!cmd_data) + return -ENOMEM; + + ret = copy_from_user(cmd_data, userbuf, len); + if (ret) { + devm_kfree(generic_service_priv->dev, cmd_data); + return -EFAULT; + } + + if (generic_service_priv->response) + mpfs_generic_service_free_message_structs(); + + response = devm_kmalloc(generic_service_priv->dev, sizeof(*response), GFP_KERNEL); + if (!response) { + devm_kfree(generic_service_priv->dev, cmd_data); + return -ENOMEM; + } + + response->resp_status = 0U; + response->resp_size = ALIGN(*(u16 *)(cmd_data + 3), (4U)); + response->resp_msg = devm_kzalloc(generic_service_priv->dev, + response->resp_size * sizeof(u8), GFP_KERNEL); + if (!response->resp_msg) { + devm_kfree(generic_service_priv->dev, cmd_data); + devm_kfree(generic_service_priv->dev, response); + return -ENOMEM; + } + + generic_service_priv->response = response; + + msg = (struct mpfs_mss_msg) { + .cmd_opcode = *(cmd_data), + .cmd_data_size = *(u16 *)(cmd_data + 1), + .response = generic_service_priv->response, + .cmd_data = cmd_data + MPFS_MIN_PAYLOAD, + .mbox_offset = *(u16 *)(cmd_data + 5), + .resp_offset = *(u16 *)(cmd_data + 7) + }; + + if (msg.cmd_data_size != len - MPFS_MIN_PAYLOAD) { + devm_kfree(generic_service_priv->dev, cmd_data); + mpfs_generic_service_free_message_structs(); + return -EINVAL; + } + + ret = mpfs_blocking_transaction(generic_service_priv->sys_controller, &msg); + devm_kfree(generic_service_priv->dev, cmd_data); + if (ret) { + mpfs_generic_service_free_message_structs(); + return -EIO; + } + + return len; +} + +static ssize_t mpfs_generic_service_read(struct file *filp, char __user *userbuf, + size_t len, loff_t *f_pos) +{ + u8 *buffer, *response_msg; + int ret; + + if (!generic_service_priv->response) + return -ENOMSG; + + response_msg = (u8 *)generic_service_priv->response->resp_msg; + buffer = devm_kmalloc(generic_service_priv->dev, + generic_service_priv->response->resp_size + 1, GFP_KERNEL); + + if (!buffer) { + mpfs_generic_service_free_message_structs(); + return -ENOMEM; + } + + *buffer = generic_service_priv->response->resp_status; + + memcpy(buffer + 1, response_msg, generic_service_priv->response->resp_size); + + ret = simple_read_from_buffer(userbuf, len, f_pos, buffer, + generic_service_priv->response->resp_size + 1); + + mpfs_generic_service_free_message_structs(); + devm_kfree(generic_service_priv->dev, buffer); + + return ret; +} + +static int mpfs_generic_service_open(struct inode *inode, struct file *filp) +{ + if (!mutex_trylock(&mpfs_generic_service_mutex)) { + pr_debug("Device Busy\n"); + return -EBUSY; + } + return 0; +} + +static int mpfs_generic_service_release(struct inode *inode, struct file *filp) +{ + mutex_unlock(&mpfs_generic_service_mutex); + return 0; +} + +static const struct file_operations mpfs_generic_service_fops = { + .owner = THIS_MODULE, + .read = mpfs_generic_service_read, + .open = mpfs_generic_service_open, + .write = mpfs_generic_service_write, + .release = mpfs_generic_service_release +}; + +static struct miscdevice mpfs_generic_service_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "mpfs_generic_service", + .fops = &mpfs_generic_service_fops +}; + +static int mpfs_generic_service_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + generic_service_priv = + devm_kzalloc(dev, sizeof(*generic_service_priv), GFP_KERNEL); + if (!generic_service_priv) + return -ENOMEM; + + generic_service_priv->sys_controller = mpfs_sys_controller_get(&pdev->dev); + if (IS_ERR(generic_service_priv->sys_controller)) + return dev_err_probe(dev, PTR_ERR(generic_service_priv->sys_controller), + "Could not register as a sub device of the system controller\n"); + + generic_service_priv->dev = dev; + + platform_set_drvdata(pdev, generic_service_priv); + misc_register(&mpfs_generic_service_dev); + /* + * This driver exposes all the system controller functions to userspace + * without performing any validation. Additionally, while access to the + * system controller itself is serialised, services requested using this + * driver from userspace may interfere with kernel's own use of the + * same services. + */ + dev_warn(&pdev->dev, + "Registered MPFS generic service - FOR DEVELOPMENT ONLY, DO NOT USE IN PRODUCTION\n"); + + return 0; +} + +static const struct of_device_id mpfs_generic_service_of_match[] = { + { + .compatible = "microchip,mpfs-generic-service", + }, + {} +}; +MODULE_DEVICE_TABLE(of, mpfs_generic_service_of_match); + +static struct platform_driver mpfs_generic_service_driver = { + .driver = { + .name = "mpfs-generic-service", + .of_match_table = mpfs_generic_service_of_match, + }, + .probe = mpfs_generic_service_probe, +}; +module_platform_driver(mpfs_generic_service_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>"); +MODULE_DESCRIPTION("PolarFire SoC (MPFS) generic service driver"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/soc/microchip/mpfs-mss-top-sysreg.c linux-microchip/drivers/soc/microchip/mpfs-mss-top-sysreg.c --- linux-6.6.51/drivers/soc/microchip/mpfs-mss-top-sysreg.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/soc/microchip/mpfs-mss-top-sysreg.c 2024-11-26 14:02:38.466511038 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/mfd/core.h> +#include <linux/mfd/syscon.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> + +static const struct mfd_cell mpfs_mss_top_sysreg_devs[] = { + { .name = "mpfs-reset", }, +}; + +static int mpfs_mss_top_sysreg_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int ret; + + ret = mfd_add_devices(dev, PLATFORM_DEVID_NONE, mpfs_mss_top_sysreg_devs, + 1, NULL, 0, NULL); + if (ret) + return ret; + + if (devm_of_platform_populate(dev)) + dev_err(dev, "Error populating children\n"); + + return 0; +} + +static const struct of_device_id mpfs_mss_top_sysreg_of_match[] = { + {.compatible = "microchip,mpfs-mss-top-sysreg", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mpfs_mss_top_sysreg_of_match); + +static struct platform_driver mpfs_mss_top_sysreg_driver = { + .driver = { + .name = "mpfs-mss-top-sysreg", + .of_match_table = mpfs_mss_top_sysreg_of_match, + }, + .probe = mpfs_mss_top_sysreg_probe, +}; +module_platform_driver(mpfs_mss_top_sysreg_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>"); +MODULE_DESCRIPTION("PolarFire SoC mss top sysreg driver"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/soc/microchip/mpfs-sys-controller.c linux-microchip/drivers/soc/microchip/mpfs-sys-controller.c --- linux-6.6.51/drivers/soc/microchip/mpfs-sys-controller.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/soc/microchip/mpfs-sys-controller.c 2024-11-26 14:02:38.466511038 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:15 @ #include <linux/kref.h> #include <linux/module.h> #include <linux/jiffies.h> +#include <linux/mtd/mtd.h> +#include <linux/spi/spi.h> #include <linux/interrupt.h> #include <linux/of.h> #include <linux/mailbox_client.h> @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:35 @ struct mbox_client client; struct mbox_chan *chan; struct completion c; + struct mtd_info *flash; struct kref consumers; }; +struct mpfs_syscon_config { + unsigned int nb_subdevs; + struct platform_device *subdevs; +}; + int mpfs_blocking_transaction(struct mpfs_sys_controller *sys_controller, struct mpfs_mss_msg *msg) { unsigned long timeout = msecs_to_jiffies(MPFS_SYS_CTRL_TIMEOUT_MS); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:74 @ */ if (!wait_for_completion_timeout(&sys_controller->c, timeout)) { ret = -EBADMSG; - dev_warn(sys_controller->client.dev, "MPFS sys controller service failed\n"); + dev_warn(sys_controller->client.dev, + "MPFS sys controller service failed with status: %d\n", + msg->response->resp_status); } else { ret = 0; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:112 @ kref_put(&sys_controller->consumers, mpfs_sys_controller_delete); } -static struct platform_device subdevs[] = { - { - .name = "mpfs-rng", - .id = -1, - }, - { - .name = "mpfs-generic-service", - .id = -1, - } -}; +struct mtd_info *mpfs_sys_controller_get_flash(struct mpfs_sys_controller *mpfs_client) +{ + return mpfs_client->flash; +} +EXPORT_SYMBOL(mpfs_sys_controller_get_flash); static int mpfs_sys_controller_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct mpfs_sys_controller *sys_controller; + struct mpfs_syscon_config *of_data; + struct device_node *np; int i, ret; sys_controller = kzalloc(sizeof(*sys_controller), GFP_KERNEL); if (!sys_controller) return -ENOMEM; + np = of_parse_phandle(dev->of_node, "microchip,bitstream-flash", 0); + if (!np) + goto no_flash; + + sys_controller->flash = of_get_mtd_device_by_node(np); + of_node_put(np); + if (IS_ERR(sys_controller->flash)) + return dev_err_probe(dev, PTR_ERR(sys_controller->flash), "Failed to get flash\n"); + +no_flash: sys_controller->client.dev = dev; sys_controller->client.rx_callback = mpfs_sys_controller_rx_callback; sys_controller->client.tx_block = 1U; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:158 @ platform_set_drvdata(pdev, sys_controller); - dev_info(&pdev->dev, "Registered MPFS system controller\n"); + of_data = (struct mpfs_syscon_config *) device_get_match_data(dev); + if (!of_data) { + dev_err(dev, "Error getting match data\n"); + return -EINVAL; + } - for (i = 0; i < ARRAY_SIZE(subdevs); i++) { - subdevs[i].dev.parent = dev; - if (platform_device_register(&subdevs[i])) - dev_warn(dev, "Error registering sub device %s\n", subdevs[i].name); + for (i = 0; i < of_data->nb_subdevs; i++) { + of_data->subdevs[i].dev.parent = dev; + if (platform_device_register(&of_data->subdevs[i])) + dev_warn(dev, "Error registering sub device %s\n", + of_data->subdevs[i].name); } + dev_info(&pdev->dev, "Registered MPFS system controller\n"); + return 0; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:185 @ return 0; } +static struct platform_device mpfs_subdevs[] = { + { + .name = "mpfs-rng", + .id = -1, + }, + { + .name = "mpfs-generic-service", + .id = -1, + }, + { + .name = "mpfs-auto-update", + .id = -1, + }, +}; + +static struct platform_device pic64gx_subdevs[] = { + { + .name = "mpfs-rng", + .id = -1, + }, + { + .name = "mpfs-generic-service", + .id = -1, + }, +}; + +static const struct mpfs_syscon_config mpfs_config = { + .nb_subdevs = ARRAY_SIZE(mpfs_subdevs), + .subdevs = mpfs_subdevs, +}; + +static const struct mpfs_syscon_config pic64gx_config = { + .nb_subdevs = ARRAY_SIZE(pic64gx_subdevs), + .subdevs = pic64gx_subdevs, +}; + static const struct of_device_id mpfs_sys_controller_of_match[] = { - {.compatible = "microchip,mpfs-sys-controller", }, + {.compatible = "microchip,mpfs-sys-controller", .data = &mpfs_config}, + {.compatible = "microchip,pic64gx-sys-controller", .data = &pic64gx_config}, {}, }; MODULE_DEVICE_TABLE(of, mpfs_sys_controller_of_match); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:238 @ goto err_no_device; match = of_match_node(mpfs_sys_controller_of_match, dev->parent->of_node); - of_node_put(dev->parent->of_node); if (!match) goto err_no_device; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/soc/sifive/Kconfig linux-microchip/drivers/soc/sifive/Kconfig --- linux-6.6.51/drivers/soc/sifive/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/soc/sifive/Kconfig 1970-01-01 01:00:00.000000000 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1 @ -# SPDX-License-Identifier: GPL-2.0 - -if SOC_SIFIVE || SOC_STARFIVE - -config SIFIVE_CCACHE - bool "Sifive Composable Cache controller" - help - Support for the composable cache controller on SiFive platforms. - -endif diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/soc/sifive/Makefile linux-microchip/drivers/soc/sifive/Makefile --- linux-6.6.51/drivers/soc/sifive/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/soc/sifive/Makefile 1970-01-01 01:00:00.000000000 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1 @ -# SPDX-License-Identifier: GPL-2.0 - -obj-$(CONFIG_SIFIVE_CCACHE) += sifive_ccache.o diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/soc/sifive/sifive_ccache.c linux-microchip/drivers/soc/sifive/sifive_ccache.c --- linux-6.6.51/drivers/soc/sifive/sifive_ccache.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/soc/sifive/sifive_ccache.c 1970-01-01 01:00:00.000000000 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1 @ -// SPDX-License-Identifier: GPL-2.0 -/* - * SiFive composable cache controller Driver - * - * Copyright (C) 2018-2022 SiFive, Inc. - * - */ - -#define pr_fmt(fmt) "CCACHE: " fmt - -#include <linux/debugfs.h> -#include <linux/interrupt.h> -#include <linux/of_irq.h> -#include <linux/of_address.h> -#include <linux/device.h> -#include <linux/bitfield.h> -#include <asm/cacheinfo.h> -#include <soc/sifive/sifive_ccache.h> - -#define SIFIVE_CCACHE_DIRECCFIX_LOW 0x100 -#define SIFIVE_CCACHE_DIRECCFIX_HIGH 0x104 -#define SIFIVE_CCACHE_DIRECCFIX_COUNT 0x108 - -#define SIFIVE_CCACHE_DIRECCFAIL_LOW 0x120 -#define SIFIVE_CCACHE_DIRECCFAIL_HIGH 0x124 -#define SIFIVE_CCACHE_DIRECCFAIL_COUNT 0x128 - -#define SIFIVE_CCACHE_DATECCFIX_LOW 0x140 -#define SIFIVE_CCACHE_DATECCFIX_HIGH 0x144 -#define SIFIVE_CCACHE_DATECCFIX_COUNT 0x148 - -#define SIFIVE_CCACHE_DATECCFAIL_LOW 0x160 -#define SIFIVE_CCACHE_DATECCFAIL_HIGH 0x164 -#define SIFIVE_CCACHE_DATECCFAIL_COUNT 0x168 - -#define SIFIVE_CCACHE_CONFIG 0x00 -#define SIFIVE_CCACHE_CONFIG_BANK_MASK GENMASK_ULL(7, 0) -#define SIFIVE_CCACHE_CONFIG_WAYS_MASK GENMASK_ULL(15, 8) -#define SIFIVE_CCACHE_CONFIG_SETS_MASK GENMASK_ULL(23, 16) -#define SIFIVE_CCACHE_CONFIG_BLKS_MASK GENMASK_ULL(31, 24) - -#define SIFIVE_CCACHE_WAYENABLE 0x08 -#define SIFIVE_CCACHE_ECCINJECTERR 0x40 - -#define SIFIVE_CCACHE_MAX_ECCINTR 4 - -static void __iomem *ccache_base; -static int g_irq[SIFIVE_CCACHE_MAX_ECCINTR]; -static struct riscv_cacheinfo_ops ccache_cache_ops; -static int level; - -enum { - DIR_CORR = 0, - DATA_CORR, - DATA_UNCORR, - DIR_UNCORR, -}; - -#ifdef CONFIG_DEBUG_FS -static struct dentry *sifive_test; - -static ssize_t ccache_write(struct file *file, const char __user *data, - size_t count, loff_t *ppos) -{ - unsigned int val; - - if (kstrtouint_from_user(data, count, 0, &val)) - return -EINVAL; - if ((val < 0xFF) || (val >= 0x10000 && val < 0x100FF)) - writel(val, ccache_base + SIFIVE_CCACHE_ECCINJECTERR); - else - return -EINVAL; - return count; -} - -static const struct file_operations ccache_fops = { - .owner = THIS_MODULE, - .open = simple_open, - .write = ccache_write -}; - -static void setup_sifive_debug(void) -{ - sifive_test = debugfs_create_dir("sifive_ccache_cache", NULL); - - debugfs_create_file("sifive_debug_inject_error", 0200, - sifive_test, NULL, &ccache_fops); -} -#endif - -static void ccache_config_read(void) -{ - u32 cfg; - - cfg = readl(ccache_base + SIFIVE_CCACHE_CONFIG); - pr_info("%llu banks, %llu ways, sets/bank=%llu, bytes/block=%llu\n", - FIELD_GET(SIFIVE_CCACHE_CONFIG_BANK_MASK, cfg), - FIELD_GET(SIFIVE_CCACHE_CONFIG_WAYS_MASK, cfg), - BIT_ULL(FIELD_GET(SIFIVE_CCACHE_CONFIG_SETS_MASK, cfg)), - BIT_ULL(FIELD_GET(SIFIVE_CCACHE_CONFIG_BLKS_MASK, cfg))); - - cfg = readl(ccache_base + SIFIVE_CCACHE_WAYENABLE); - pr_info("Index of the largest way enabled: %u\n", cfg); -} - -static const struct of_device_id sifive_ccache_ids[] = { - { .compatible = "sifive,fu540-c000-ccache" }, - { .compatible = "sifive,fu740-c000-ccache" }, - { .compatible = "sifive,ccache0" }, - { /* end of table */ } -}; - -static ATOMIC_NOTIFIER_HEAD(ccache_err_chain); - -int register_sifive_ccache_error_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_register(&ccache_err_chain, nb); -} -EXPORT_SYMBOL_GPL(register_sifive_ccache_error_notifier); - -int unregister_sifive_ccache_error_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_unregister(&ccache_err_chain, nb); -} -EXPORT_SYMBOL_GPL(unregister_sifive_ccache_error_notifier); - -static int ccache_largest_wayenabled(void) -{ - return readl(ccache_base + SIFIVE_CCACHE_WAYENABLE) & 0xFF; -} - -static ssize_t number_of_ways_enabled_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - return sprintf(buf, "%u\n", ccache_largest_wayenabled()); -} - -static DEVICE_ATTR_RO(number_of_ways_enabled); - -static struct attribute *priv_attrs[] = { - &dev_attr_number_of_ways_enabled.attr, - NULL, -}; - -static const struct attribute_group priv_attr_group = { - .attrs = priv_attrs, -}; - -static const struct attribute_group *ccache_get_priv_group(struct cacheinfo - *this_leaf) -{ - /* We want to use private group for composable cache only */ - if (this_leaf->level == level) - return &priv_attr_group; - else - return NULL; -} - -static irqreturn_t ccache_int_handler(int irq, void *device) -{ - unsigned int add_h, add_l; - - if (irq == g_irq[DIR_CORR]) { - add_h = readl(ccache_base + SIFIVE_CCACHE_DIRECCFIX_HIGH); - add_l = readl(ccache_base + SIFIVE_CCACHE_DIRECCFIX_LOW); - pr_err("DirError @ 0x%08X.%08X\n", add_h, add_l); - /* Reading this register clears the DirError interrupt sig */ - readl(ccache_base + SIFIVE_CCACHE_DIRECCFIX_COUNT); - atomic_notifier_call_chain(&ccache_err_chain, - SIFIVE_CCACHE_ERR_TYPE_CE, - "DirECCFix"); - } - if (irq == g_irq[DIR_UNCORR]) { - add_h = readl(ccache_base + SIFIVE_CCACHE_DIRECCFAIL_HIGH); - add_l = readl(ccache_base + SIFIVE_CCACHE_DIRECCFAIL_LOW); - /* Reading this register clears the DirFail interrupt sig */ - readl(ccache_base + SIFIVE_CCACHE_DIRECCFAIL_COUNT); - atomic_notifier_call_chain(&ccache_err_chain, - SIFIVE_CCACHE_ERR_TYPE_UE, - "DirECCFail"); - panic("CCACHE: DirFail @ 0x%08X.%08X\n", add_h, add_l); - } - if (irq == g_irq[DATA_CORR]) { - add_h = readl(ccache_base + SIFIVE_CCACHE_DATECCFIX_HIGH); - add_l = readl(ccache_base + SIFIVE_CCACHE_DATECCFIX_LOW); - pr_err("DataError @ 0x%08X.%08X\n", add_h, add_l); - /* Reading this register clears the DataError interrupt sig */ - readl(ccache_base + SIFIVE_CCACHE_DATECCFIX_COUNT); - atomic_notifier_call_chain(&ccache_err_chain, - SIFIVE_CCACHE_ERR_TYPE_CE, - "DatECCFix"); - } - if (irq == g_irq[DATA_UNCORR]) { - add_h = readl(ccache_base + SIFIVE_CCACHE_DATECCFAIL_HIGH); - add_l = readl(ccache_base + SIFIVE_CCACHE_DATECCFAIL_LOW); - pr_err("DataFail @ 0x%08X.%08X\n", add_h, add_l); - /* Reading this register clears the DataFail interrupt sig */ - readl(ccache_base + SIFIVE_CCACHE_DATECCFAIL_COUNT); - atomic_notifier_call_chain(&ccache_err_chain, - SIFIVE_CCACHE_ERR_TYPE_UE, - "DatECCFail"); - } - - return IRQ_HANDLED; -} - -static int __init sifive_ccache_init(void) -{ - struct device_node *np; - struct resource res; - int i, rc, intr_num; - - np = of_find_matching_node(NULL, sifive_ccache_ids); - if (!np) - return -ENODEV; - - if (of_address_to_resource(np, 0, &res)) { - rc = -ENODEV; - goto err_node_put; - } - - ccache_base = ioremap(res.start, resource_size(&res)); - if (!ccache_base) { - rc = -ENOMEM; - goto err_node_put; - } - - if (of_property_read_u32(np, "cache-level", &level)) { - rc = -ENOENT; - goto err_unmap; - } - - intr_num = of_property_count_u32_elems(np, "interrupts"); - if (!intr_num) { - pr_err("No interrupts property\n"); - rc = -ENODEV; - goto err_unmap; - } - - for (i = 0; i < intr_num; i++) { - g_irq[i] = irq_of_parse_and_map(np, i); - rc = request_irq(g_irq[i], ccache_int_handler, 0, "ccache_ecc", - NULL); - if (rc) { - pr_err("Could not request IRQ %d\n", g_irq[i]); - goto err_free_irq; - } - } - of_node_put(np); - - ccache_config_read(); - - ccache_cache_ops.get_priv_group = ccache_get_priv_group; - riscv_set_cacheinfo_ops(&ccache_cache_ops); - -#ifdef CONFIG_DEBUG_FS - setup_sifive_debug(); -#endif - return 0; - -err_free_irq: - while (--i >= 0) - free_irq(g_irq[i], NULL); -err_unmap: - iounmap(ccache_base); -err_node_put: - of_node_put(np); - return rc; -} - -device_initcall(sifive_ccache_init); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/spi/atmel-quadspi.c linux-microchip/drivers/spi/atmel-quadspi.c --- linux-6.6.51/drivers/spi/atmel-quadspi.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/spi/atmel-quadspi.c 2024-11-26 14:02:38.474511181 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:14 @ * This driver is based on drivers/mtd/spi-nor/fsl-quadspi.c from Freescale. */ +#include <linux/bitfield.h> #include <linux/clk.h> +#include <linux/clk-provider.h> #include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> #include <linux/err.h> #include <linux/interrupt.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:42 @ #define QSPI_IDR 0x0018 /* Interrupt Disable Register */ #define QSPI_IMR 0x001c /* Interrupt Mask Register */ #define QSPI_SCR 0x0020 /* Serial Clock Register */ +#define QSPI_SR2 0x0024 /* SAMA7G5 Status Register */ #define QSPI_IAR 0x0030 /* Instruction Address Register */ #define QSPI_ICR 0x0034 /* Instruction Code Register */ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:53 @ #define QSPI_SMR 0x0040 /* Scrambling Mode Register */ #define QSPI_SKR 0x0044 /* Scrambling Key Register */ +#define QSPI_REFRESH 0x0050 /* Refresh Register */ +#define QSPI_WRACNT 0x0054 /* Write Access Counter Register */ +#define QSPI_DLLCFG 0x0058 /* DLL Configuration Register */ +#define QSPI_PCALCFG 0x005C /* Pad Calibration Configuration Register */ +#define QSPI_PCALBP 0x0060 /* Pad Calibration Bypass Register */ +#define QSPI_TOUT 0x0064 /* Timeout Register */ + #define QSPI_WPMR 0x00E4 /* Write Protection Mode Register */ #define QSPI_WPSR 0x00E8 /* Write Protection Status Register */ #define QSPI_VERSION 0x00FC /* Version Register */ +#define SAMA7G5_QSPI0_MAX_SPEED_HZ 200000000 +#define SAMA7G5_QSPI1_SDR_MAX_SPEED_HZ 133000000 + +#define SAM9X7_QSPI_MAX_SPEED_HZ 100000000 /* Bitfields in QSPI_CR (Control Register) */ #define QSPI_CR_QSPIEN BIT(0) #define QSPI_CR_QSPIDIS BIT(1) +#define QSPI_CR_DLLON BIT(2) +#define QSPI_CR_DLLOFF BIT(3) +#define QSPI_CR_STPCAL BIT(4) +#define QSPI_CR_SRFRSH BIT(5) #define QSPI_CR_SWRST BIT(7) +#define QSPI_CR_UPDCFG BIT(8) +#define QSPI_CR_STTFR BIT(9) +#define QSPI_CR_RTOUT BIT(10) #define QSPI_CR_LASTXFER BIT(24) /* Bitfields in QSPI_MR (Mode Register) */ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:88 @ #define QSPI_MR_LLB BIT(1) #define QSPI_MR_WDRBT BIT(2) #define QSPI_MR_SMRM BIT(3) +#define QSPI_MR_DQSDLYEN BIT(3) #define QSPI_MR_CSMODE_MASK GENMASK(5, 4) #define QSPI_MR_CSMODE_NOT_RELOADED (0 << 4) #define QSPI_MR_CSMODE_LASTXFER (1 << 4) #define QSPI_MR_CSMODE_SYSTEMATICALLY (2 << 4) #define QSPI_MR_NBBITS_MASK GENMASK(11, 8) #define QSPI_MR_NBBITS(n) ((((n) - 8) << 8) & QSPI_MR_NBBITS_MASK) +#define QSPI_MR_OENSD BIT(15) #define QSPI_MR_DLYBCT_MASK GENMASK(23, 16) #define QSPI_MR_DLYBCT(n) (((n) << 16) & QSPI_MR_DLYBCT_MASK) #define QSPI_MR_DLYCS_MASK GENMASK(31, 24) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:109 @ #define QSPI_SR_CSR BIT(8) #define QSPI_SR_CSS BIT(9) #define QSPI_SR_INSTRE BIT(10) +#define QSPI_SR_LWRA BIT(11) +#define QSPI_SR_QITF BIT(12) +#define QSPI_SR_QITR BIT(13) +#define QSPI_SR_CSFA BIT(14) +#define QSPI_SR_CSRA BIT(15) +#define QSPI_SR_RFRSHD BIT(16) +#define QSPI_SR_TOUT BIT(17) #define QSPI_SR_QSPIENS BIT(24) #define QSPI_SR_CMD_COMPLETED (QSPI_SR_INSTRE | QSPI_SR_CSR) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:128 @ #define QSPI_SCR_DLYBS_MASK GENMASK(23, 16) #define QSPI_SCR_DLYBS(n) (((n) << 16) & QSPI_SCR_DLYBS_MASK) +/* Bitfields in QSPI_SR2 (SAMA7G5 Status Register) */ +#define QSPI_SR2_SYNCBSY BIT(0) +#define QSPI_SR2_QSPIENS BIT(1) +#define QSPI_SR2_CSS BIT(2) +#define QSPI_SR2_RBUSY BIT(3) +#define QSPI_SR2_HIDLE BIT(4) +#define QSPI_SR2_DLOCK BIT(5) +#define QSPI_SR2_CALBSY BIT(6) + +/* Bitfields in QSPI_IAR (Instruction Address Register) */ +#define QSPI_IAR_ADDR GENMASK(31, 0) + /* Bitfields in QSPI_ICR (Read/Write Instruction Code Register) */ #define QSPI_ICR_INST_MASK GENMASK(7, 0) #define QSPI_ICR_INST(inst) (((inst) << 0) & QSPI_ICR_INST_MASK) +#define QSPI_ICR_INST_MASK_SAMA7G5 GENMASK(15, 0) #define QSPI_ICR_OPT_MASK GENMASK(23, 16) #define QSPI_ICR_OPT(opt) (((opt) << 16) & QSPI_ICR_OPT_MASK) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:156 @ #define QSPI_IFR_WIDTH_QUAD_IO (4 << 0) #define QSPI_IFR_WIDTH_DUAL_CMD (5 << 0) #define QSPI_IFR_WIDTH_QUAD_CMD (6 << 0) +#define QSPI_IFR_WIDTH_OCT_OUTPUT (7 << 0) +#define QSPI_IFR_WIDTH_OCT_IO (8 << 0) +#define QSPI_IFR_WIDTH_OCT_CMD (9 << 0) #define QSPI_IFR_INSTEN BIT(4) #define QSPI_IFR_ADDREN BIT(5) #define QSPI_IFR_OPTEN BIT(6) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:169 @ #define QSPI_IFR_OPTL_4BIT (2 << 8) #define QSPI_IFR_OPTL_8BIT (3 << 8) #define QSPI_IFR_ADDRL BIT(10) +#define QSPI_IFR_ADDRL_SAMA7G5 GENMASK(11, 10) #define QSPI_IFR_TFRTYP_MEM BIT(12) #define QSPI_IFR_SAMA5D2_WRITE_TRSFR BIT(13) #define QSPI_IFR_CRM BIT(14) +#define QSPI_IFR_DDREN BIT(15) #define QSPI_IFR_NBDUM_MASK GENMASK(20, 16) #define QSPI_IFR_NBDUM(n) (((n) << 16) & QSPI_IFR_NBDUM_MASK) +#define QSPI_IFR_END BIT(22) +#define QSPI_IFR_SMRM BIT(23) #define QSPI_IFR_APBTFRTYP_READ BIT(24) /* Defined in SAM9X60 */ +#define QSPI_IFR_DQSEN BIT(25) +#define QSPI_IFR_DDRCMDEN BIT(26) +#define QSPI_IFR_HFWBEN BIT(27) +#define QSPI_IFR_PROTTYP GENMASK(29, 28) +#define QSPI_IFR_PROTTYP_STD_SPI 0 +#define QSPI_IFR_PROTTYP_TWIN_QUAD 1 +#define QSPI_IFR_PROTTYP_OCTAFLASH 2 +#define QSPI_IFR_PROTTYP_HYPERFLASH 3 /* Bitfields in QSPI_SMR (Scrambling Mode Register) */ #define QSPI_SMR_SCREN BIT(0) #define QSPI_SMR_RVDIS BIT(1) +#define QSPI_SMR_SCRKL BIT(2) + +/* Bitfields in QSPI_REFRESH (Refresh Register) */ +#define QSPI_REFRESH_DELAY_COUNTER GENMASK(31, 0) + +/* Bitfields in QSPI_WRACNT (Write Access Counter Register) */ +#define QSPI_WRACNT_NBWRA GENMASK(31, 0) + +/* Bitfields in QSPI_DLLCFG (DLL Configuration Register) */ +#define QSPI_DLLCFG_RANGE BIT(0) + +/* Bitfields in QSPI_PCALCFG (DLL Pad Calibration Configuration Register) */ +#define QSPI_PCALCFG_AAON BIT(0) +#define QSPI_PCALCFG_DAPCAL BIT(1) +#define QSPI_PCALCFG_DIFFPM BIT(2) +#define QSPI_PCALCFG_CLKDIV GENMASK(6, 4) +#define QSPI_PCALCFG_CALCNT GENMASK(16, 8) +#define QSPI_PCALCFG_CALP GENMASK(27, 24) +#define QSPI_PCALCFG_CALN GENMASK(31, 28) + +/* Bitfields in QSPI_PCALBP (DLL Pad Calibration Bypass Register) */ +#define QSPI_PCALBP_BPEN BIT(0) +#define QSPI_PCALBP_CALPBP GENMASK(11, 8) +#define QSPI_PCALBP_CALNBP GENMASK(19, 16) + +/* Bitfields in QSPI_TOUT (Timeout Register) */ +#define QSPI_TOUT_TCNTM GENMASK(15, 0) /* Bitfields in QSPI_WPMR (Write Protection Mode Register) */ #define QSPI_WPMR_WPEN BIT(0) +#define QSPI_WPMR_WPITEN BIT(1) +#define QSPI_WPMR_WPCREN BIT(2) #define QSPI_WPMR_WPKEY_MASK GENMASK(31, 8) #define QSPI_WPMR_WPKEY(wpkey) (((wpkey) << 8) & QSPI_WPMR_WPKEY_MASK) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:231 @ #define QSPI_WPSR_WPVSRC_MASK GENMASK(15, 8) #define QSPI_WPSR_WPVSRC(src) (((src) << 8) & QSPI_WPSR_WPVSRC) +#define ATMEL_QSPI_TIMEOUT 1000 /* ms */ +#define ATMEL_QSPI_SYNC_TIMEOUT 300 /* ms */ +#define QSPI_DLLCFG_THRESHOLD_FREQ 90000000U +#define QSPI_CALIB_TIME 2000 /* 2 us */ + +/* Use PIO for small transfers. */ +#define ATMEL_QSPI_DMA_MIN_BYTES 0 +/** + * struct atmel_qspi_pcal - Pad Calibration Clock Division + * @pclk_rate: peripheral clock rate. + * @pclkdiv: calibration clock division. The clock applied to the calibration + * cell is divided by pclkdiv + 1. + */ +struct atmel_qspi_pcal { + u32 pclk_rate; + u8 pclk_div; +}; + +#define ATMEL_QSPI_PCAL_ARRAY_SIZE 8 +static const struct atmel_qspi_pcal pcal[ATMEL_QSPI_PCAL_ARRAY_SIZE] = { + {25000000, 0}, + {50000000, 1}, + {75000000, 2}, + {100000000, 3}, + {125000000, 4}, + {150000000, 5}, + {175000000, 6}, + {200000000, 7}, +}; + struct atmel_qspi_caps { + u32 max_speed_hz; bool has_qspick; + bool has_gclk; bool has_ricr; + bool octal; + bool has_dma; + bool has_2xgclk; + bool has_padcalib; + bool has_dllon; }; +struct atmel_qspi_ops; + struct atmel_qspi { void __iomem *regs; void __iomem *mem; struct clk *pclk; struct clk *qspick; + struct clk *gclk; struct platform_device *pdev; const struct atmel_qspi_caps *caps; + const struct atmel_qspi_ops *ops; resource_size_t mmap_size; u32 pending; + u32 irq_mask; u32 mr; u32 scr; + u32 slave_max_speed_hz; struct completion cmd_completion; + struct completion dma_completion; + dma_addr_t mmap_phys_base; + struct dma_chan *rx_chan; + struct dma_chan *tx_chan; +}; + +struct atmel_qspi_ops { + int (*set_cfg)(struct atmel_qspi *aq, const struct spi_mem_op *op, + u32 *offset); + int (*transfer)(struct spi_mem *mem, const struct spi_mem_op *op, + u32 offset); }; struct atmel_qspi_mode { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:321 @ { 4, 4, 4, QSPI_IFR_WIDTH_QUAD_CMD }, }; +static const struct atmel_qspi_mode atmel_qspi_sama7g5_modes[] = { + { 1, 1, 1, QSPI_IFR_WIDTH_SINGLE_BIT_SPI }, + { 1, 1, 2, QSPI_IFR_WIDTH_DUAL_OUTPUT }, + { 1, 1, 4, QSPI_IFR_WIDTH_QUAD_OUTPUT }, + { 1, 2, 2, QSPI_IFR_WIDTH_DUAL_IO }, + { 1, 4, 4, QSPI_IFR_WIDTH_QUAD_IO }, + { 2, 2, 2, QSPI_IFR_WIDTH_DUAL_CMD }, + { 4, 4, 4, QSPI_IFR_WIDTH_QUAD_CMD }, + { 1, 1, 8, QSPI_IFR_WIDTH_OCT_OUTPUT }, + { 1, 8, 8, QSPI_IFR_WIDTH_OCT_IO }, + { 8, 8, 8, QSPI_IFR_WIDTH_OCT_CMD }, +}; + #ifdef VERBOSE_DEBUG static const char *atmel_qspi_reg_name(u32 offset, char *tmp, size_t sz) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:356 @ return "IMR"; case QSPI_SCR: return "SCR"; + case QSPI_SR2: + return "SR2"; case QSPI_IAR: return "IAR"; case QSPI_ICR: @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:370 @ return "SMR"; case QSPI_SKR: return "SKR"; + case QSPI_REFRESH: + return "REFRESH"; + case QSPI_WRACNT: + return "WRACNT"; + case QSPI_DLLCFG: + return "DLLCFG"; + case QSPI_PCALCFG: + return "PCALCFG"; + case QSPI_PCALBP: + return "PCALBP"; + case QSPI_TOUT: + return "TOUT"; case QSPI_WPMR: return "WPMR"; case QSPI_WPSR: @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:449 @ return -ENOTSUPP; } +static int atmel_qspi_sama7g5_find_mode(const struct spi_mem_op *op) +{ + u32 i; + + for (i = 0; i < ARRAY_SIZE(atmel_qspi_sama7g5_modes); i++) + if (atmel_qspi_is_compatible(op, &atmel_qspi_sama7g5_modes[i])) + return i; + + return -EOPNOTSUPP; +} + static bool atmel_qspi_supports_op(struct spi_mem *mem, const struct spi_mem_op *op) { + struct atmel_qspi *aq = spi_controller_get_devdata(mem->spi->master); if (!spi_mem_default_supports_op(mem, op)) return false; + if (aq->caps->octal) { + if (atmel_qspi_sama7g5_find_mode(op) < 0) + return false; + else + return true; + } + if (atmel_qspi_find_mode(op) < 0) return false; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:597 @ return 0; } +static int atmel_qspi_wait_for_completion(struct atmel_qspi *aq, u32 irq_mask) +{ + int err = 0; + u32 sr; + + /* Poll INSTRuction End status */ + sr = atmel_qspi_read(aq, QSPI_SR); + if ((sr & irq_mask) == irq_mask) + return 0; + + /* Wait for INSTRuction End interrupt */ + reinit_completion(&aq->cmd_completion); + aq->pending = sr & irq_mask; + aq->irq_mask = irq_mask; + atmel_qspi_write(irq_mask, aq, QSPI_IER); + if (!wait_for_completion_timeout(&aq->cmd_completion, + msecs_to_jiffies(ATMEL_QSPI_TIMEOUT))) + err = -ETIMEDOUT; + atmel_qspi_write(irq_mask, aq, QSPI_IDR); + + return err; +} + +static int atmel_qspi_transfer(struct spi_mem *mem, + const struct spi_mem_op *op, u32 offset) +{ + struct atmel_qspi *aq = + spi_controller_get_devdata(mem->spi->controller); + + if (!op->data.nbytes) + return atmel_qspi_wait_for_completion(aq, + QSPI_SR_CMD_COMPLETED); + + /* Dummy read of QSPI_IFR to synchronize APB and AHB accesses */ + (void)atmel_qspi_read(aq, QSPI_IFR); + + /* Send/Receive data */ + if (op->data.dir == SPI_MEM_DATA_IN) + memcpy_fromio(op->data.buf.in, aq->mem + offset, + op->data.nbytes); + else + memcpy_toio(aq->mem + offset, op->data.buf.out, + op->data.nbytes); + + /* Release the chip-select */ + atmel_qspi_write(QSPI_CR_LASTXFER, aq, QSPI_CR); + + return atmel_qspi_wait_for_completion(aq, QSPI_SR_CMD_COMPLETED); +} + +static int atmel_qspi_reg_sync(struct atmel_qspi *aq) +{ + u32 val; + int ret; + + ret = readl_poll_timeout(aq->regs + QSPI_SR2, val, + !(val & QSPI_SR2_SYNCBSY), 40, + ATMEL_QSPI_SYNC_TIMEOUT); + return ret; +} + +static int atmel_qspi_update_config(struct atmel_qspi *aq) +{ + int ret; + + ret = atmel_qspi_reg_sync(aq); + if (ret) + return ret; + atmel_qspi_write(QSPI_CR_UPDCFG, aq, QSPI_CR); + return atmel_qspi_reg_sync(aq); +} + +static int atmel_qspi_sama7g5_set_cfg(struct atmel_qspi *aq, + const struct spi_mem_op *op, u32 *offset) +{ + u32 iar, icr, ifr; + int mode, ret; + + iar = 0; + icr = FIELD_PREP(QSPI_ICR_INST_MASK_SAMA7G5, op->cmd.opcode); + ifr = QSPI_IFR_INSTEN; + + mode = atmel_qspi_sama7g5_find_mode(op); + if (mode < 0) + return mode; + ifr |= atmel_qspi_sama7g5_modes[mode].config; + + if (op->dummy.buswidth && op->dummy.nbytes) { + if (op->addr.dtr && op->dummy.dtr && op->data.dtr) + ifr |= QSPI_IFR_NBDUM(op->dummy.nbytes * 8 / + (2 * op->dummy.buswidth)); + else + ifr |= QSPI_IFR_NBDUM(op->dummy.nbytes * 8 / + op->dummy.buswidth); + } + + if (op->addr.buswidth && op->addr.nbytes) { + ifr |= FIELD_PREP(QSPI_IFR_ADDRL_SAMA7G5, op->addr.nbytes - 1) | + QSPI_IFR_ADDREN; + iar = FIELD_PREP(QSPI_IAR_ADDR, op->addr.val); + } + + if (op->addr.dtr && op->dummy.dtr && op->data.dtr) { + ifr |= QSPI_IFR_DDREN; + if (op->cmd.dtr) + ifr |= QSPI_IFR_DDRCMDEN; + if (op->data.dtr_bswap16) + ifr |= QSPI_IFR_END; + + ifr |= QSPI_IFR_DQSEN; + } + + if (op->cmd.buswidth == 8 || op->addr.buswidth == 8 || + op->data.buswidth == 8) + ifr |= FIELD_PREP(QSPI_IFR_PROTTYP, QSPI_IFR_PROTTYP_OCTAFLASH); + + /* offset of the data access in the QSPI memory space */ + *offset = iar; + + /* Set data enable */ + if (op->data.nbytes) { + ifr |= QSPI_IFR_DATAEN; + + if (op->addr.nbytes) + ifr |= QSPI_IFR_TFRTYP_MEM; + } + + /* + * If the QSPI controller is set in regular SPI mode, set it in + * Serial Memory Mode (SMM). + */ + if (aq->mr != QSPI_MR_SMM) { + atmel_qspi_write(QSPI_MR_SMM | QSPI_MR_DQSDLYEN, aq, QSPI_MR); + aq->mr = QSPI_MR_SMM; + + ret = atmel_qspi_update_config(aq); + if (ret) + return ret; + } + + /* Clear pending interrupts */ + (void)atmel_qspi_read(aq, QSPI_SR); + + /* Set QSPI Instruction Frame registers */ + if (op->addr.nbytes && !op->data.nbytes) + atmel_qspi_write(iar, aq, QSPI_IAR); + + if (op->data.dir == SPI_MEM_DATA_IN) { + atmel_qspi_write(icr, aq, QSPI_RICR); + } else { + atmel_qspi_write(icr, aq, QSPI_WICR); + if (op->data.nbytes) + atmel_qspi_write(FIELD_PREP(QSPI_WRACNT_NBWRA, + op->data.nbytes), + aq, QSPI_WRACNT); + } + + atmel_qspi_write(ifr, aq, QSPI_IFR); + + return atmel_qspi_update_config(aq); +} + +static void atmel_qspi_dma_callback(void *param) +{ + struct atmel_qspi *aq = param; + + complete(&aq->dma_completion); +} + +static int atmel_qspi_dma_xfer(struct atmel_qspi *aq, struct dma_chan *chan, + dma_addr_t dma_dst, dma_addr_t dma_src, + unsigned int len) +{ + struct dma_async_tx_descriptor *tx; + dma_cookie_t cookie; + int ret; + + tx = dmaengine_prep_dma_memcpy(chan, dma_dst, dma_src, len, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!tx) { + dev_err(&aq->pdev->dev, "device_prep_dma_memcpy error\n"); + return -EIO; + } + + reinit_completion(&aq->dma_completion); + tx->callback = atmel_qspi_dma_callback; + tx->callback_param = aq; + cookie = tx->tx_submit(tx); + ret = dma_submit_error(cookie); + if (ret) { + dev_err(&aq->pdev->dev, "dma_submit_error %d\n", cookie); + return ret; + } + + dma_async_issue_pending(chan); + ret = wait_for_completion_timeout(&aq->dma_completion, + msecs_to_jiffies(20 * ATMEL_QSPI_TIMEOUT)); + if (ret == 0) { + dmaengine_terminate_sync(chan); + dev_err(&aq->pdev->dev, "DMA wait_for_completion_timeout\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int atmel_qspi_dma_rx_xfer(struct spi_mem *mem, + const struct spi_mem_op *op, + struct sg_table *sgt, loff_t loff) +{ + struct atmel_qspi *aq = + spi_controller_get_devdata(mem->spi->controller); + struct scatterlist *sg; + dma_addr_t dma_src; + unsigned int i, len; + int ret; + + dma_src = aq->mmap_phys_base + loff; + + for_each_sg(sgt->sgl, sg, sgt->nents, i) { + len = sg_dma_len(sg); + ret = atmel_qspi_dma_xfer(aq, aq->rx_chan, sg_dma_address(sg), + dma_src, len); + if (ret) + return ret; + dma_src += len; + } + + return 0; +} + +static int atmel_qspi_dma_tx_xfer(struct spi_mem *mem, + const struct spi_mem_op *op, + struct sg_table *sgt, loff_t loff) +{ + struct atmel_qspi *aq = + spi_controller_get_devdata(mem->spi->controller); + struct scatterlist *sg; + dma_addr_t dma_dst; + unsigned int i, len; + int ret; + + dma_dst = aq->mmap_phys_base + loff; + + for_each_sg(sgt->sgl, sg, sgt->nents, i) { + len = sg_dma_len(sg); + ret = atmel_qspi_dma_xfer(aq, aq->tx_chan, dma_dst, + sg_dma_address(sg), len); + if (ret) + return ret; + dma_dst += len; + } + + return 0; +} + +static int atmel_qspi_dma_transfer(struct spi_mem *mem, + const struct spi_mem_op *op, loff_t loff) +{ + struct sg_table sgt; + int ret; + + ret = spi_controller_dma_map_mem_op_data(mem->spi->controller, op, + &sgt); + if (ret) + return ret; + + if (op->data.dir == SPI_MEM_DATA_IN) + ret = atmel_qspi_dma_rx_xfer(mem, op, &sgt, loff); + else + ret = atmel_qspi_dma_tx_xfer(mem, op, &sgt, loff); + + spi_controller_dma_unmap_mem_op_data(mem->spi->controller, op, &sgt); + + return ret; +} + +static int atmel_qspi_sama7g5_transfer(struct spi_mem *mem, + const struct spi_mem_op *op, u32 offset) +{ + struct atmel_qspi *aq = + spi_controller_get_devdata(mem->spi->controller); + u32 val; + int ret; + + if (!op->data.nbytes) { + /* Start the transfer. */ + ret = atmel_qspi_reg_sync(aq); + if (ret) + return ret; + atmel_qspi_write(QSPI_CR_STTFR, aq, QSPI_CR); + + return atmel_qspi_wait_for_completion(aq, QSPI_SR_CSRA); + } + + /* Send/Receive data. */ + if (op->data.dir == SPI_MEM_DATA_IN) { + if (aq->rx_chan && + op->data.nbytes > ATMEL_QSPI_DMA_MIN_BYTES) { + ret = atmel_qspi_dma_transfer(mem, op, offset); + if (ret) + return ret; + } else { + memcpy_fromio(op->data.buf.in, aq->mem + offset, + op->data.nbytes); + } + + if (op->addr.nbytes) { + ret = readl_poll_timeout(aq->regs + QSPI_SR2, val, + !(val & QSPI_SR2_RBUSY), 40, + ATMEL_QSPI_SYNC_TIMEOUT); + if (ret) + return ret; + } + } else { + if (aq->tx_chan && + op->data.nbytes > ATMEL_QSPI_DMA_MIN_BYTES) { + ret = atmel_qspi_dma_transfer(mem, op, offset); + if (ret) + return ret; + } else { + memcpy_toio(aq->mem + offset, op->data.buf.out, + op->data.nbytes); + } + + ret = atmel_qspi_wait_for_completion(aq, QSPI_SR_LWRA); + if (ret) + return ret; + } + + /* Release the chip-select. */ + ret = atmel_qspi_reg_sync(aq); + if (ret) + return ret; + atmel_qspi_write(QSPI_CR_LASTXFER, aq, QSPI_CR); + + return atmel_qspi_wait_for_completion(aq, QSPI_SR_CSRA); +} + static int atmel_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) { struct atmel_qspi *aq = spi_controller_get_devdata(mem->spi->controller); - u32 sr, offset; + u32 offset; int err; /* @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:948 @ * when the flash memories overrun the controller's memory space. */ if (op->addr.val + op->data.nbytes > aq->mmap_size) - return -ENOTSUPP; + return -EOPNOTSUPP; + + if (op->addr.nbytes > 4) + return -EOPNOTSUPP; err = pm_runtime_resume_and_get(&aq->pdev->dev); if (err < 0) return err; - err = atmel_qspi_set_cfg(aq, op, &offset); + err = aq->ops->set_cfg(aq, op, &offset); if (err) goto pm_runtime_put; - /* Skip to the final steps if there is no data */ - if (op->data.nbytes) { - /* Dummy read of QSPI_IFR to synchronize APB and AHB accesses */ - (void)atmel_qspi_read(aq, QSPI_IFR); - - /* Send/Receive data */ - if (op->data.dir == SPI_MEM_DATA_IN) - memcpy_fromio(op->data.buf.in, aq->mem + offset, - op->data.nbytes); - else - memcpy_toio(aq->mem + offset, op->data.buf.out, - op->data.nbytes); - - /* Release the chip-select */ - atmel_qspi_write(QSPI_CR_LASTXFER, aq, QSPI_CR); - } - - /* Poll INSTRuction End status */ - sr = atmel_qspi_read(aq, QSPI_SR); - if ((sr & QSPI_SR_CMD_COMPLETED) == QSPI_SR_CMD_COMPLETED) - goto pm_runtime_put; - - /* Wait for INSTRuction End interrupt */ - reinit_completion(&aq->cmd_completion); - aq->pending = sr & QSPI_SR_CMD_COMPLETED; - atmel_qspi_write(QSPI_SR_CMD_COMPLETED, aq, QSPI_IER); - if (!wait_for_completion_timeout(&aq->cmd_completion, - msecs_to_jiffies(1000))) - err = -ETIMEDOUT; - atmel_qspi_write(QSPI_SR_CMD_COMPLETED, aq, QSPI_IDR); + err = aq->ops->transfer(mem, op, offset); pm_runtime_put: pm_runtime_mark_last_busy(&aq->pdev->dev); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:980 @ .get_name = atmel_qspi_get_name }; +static int atmel_qspi_set_pad_calibration(struct atmel_qspi *aq) +{ + unsigned long pclk_rate; + u32 status, val; + int i, ret; + u8 pclk_div = 0; + + pclk_rate = clk_get_rate(aq->pclk); + if (!pclk_rate) + return -EINVAL; + + for (i = 0; i < ATMEL_QSPI_PCAL_ARRAY_SIZE; i++) { + if (pclk_rate <= pcal[i].pclk_rate) { + pclk_div = pcal[i].pclk_div; + break; + } + } + + /* + * Use the biggest divider in case the peripheral clock exceeds + * 200MHZ. + */ + if (pclk_rate > pcal[ATMEL_QSPI_PCAL_ARRAY_SIZE - 1].pclk_rate) + pclk_div = pcal[ATMEL_QSPI_PCAL_ARRAY_SIZE - 1].pclk_div; + + /* Disable QSPI while configuring the pad calibration. */ + status = atmel_qspi_read(aq, QSPI_SR2); + if (status & QSPI_SR2_QSPIENS) { + ret = atmel_qspi_reg_sync(aq); + if (ret) + return ret; + atmel_qspi_write(QSPI_CR_QSPIDIS, aq, QSPI_CR); + } + + /* + * The analog circuitry is not shut down at the end of the calibration + * and the start-up time is only required for the first calibration + * sequence, thus increasing performance. Set the delay between the Pad + * calibration analog circuitry and the calibration request to 2us. + */ + atmel_qspi_write(QSPI_PCALCFG_AAON | + FIELD_PREP(QSPI_PCALCFG_CLKDIV, pclk_div) | + FIELD_PREP(QSPI_PCALCFG_CALCNT, + 2 * (pclk_rate / 1000000)), + aq, QSPI_PCALCFG); + + /* DLL On + start calibration. */ + if (aq->caps->has_dllon) + atmel_qspi_write(QSPI_CR_DLLON | QSPI_CR_STPCAL, aq, QSPI_CR); + /* If there is no DLL support only start calibration. */ + else + atmel_qspi_write(QSPI_CR_STPCAL, aq, QSPI_CR); + + /* + * Check DLL clock lock and synchronization status before updating + * configuration. + */ + if (aq->caps->has_dllon) + ret = readl_poll_timeout(aq->regs + QSPI_SR2, val, + (val & QSPI_SR2_DLOCK) && + !(val & QSPI_SR2_CALBSY), 40, + ATMEL_QSPI_TIMEOUT); + else + ret = readl_poll_timeout(aq->regs + QSPI_SR2, val, + !(val & QSPI_SR2_CALBSY), 40, + ATMEL_QSPI_TIMEOUT); + + /* Refresh analogic blocks every 1 ms.*/ + atmel_qspi_write(FIELD_PREP(QSPI_REFRESH_DELAY_COUNTER, + aq->slave_max_speed_hz / 1000), + aq, QSPI_REFRESH); + + return ret; +} + +static int atmel_qspi_set_gclk(struct atmel_qspi *aq) +{ + u32 status, val; + int ret; + + if (aq->caps->has_dllon) { + /* Disable DLL before setting GCLK */ + status = atmel_qspi_read(aq, QSPI_SR2); + if (status & QSPI_SR2_DLOCK) { + atmel_qspi_write(QSPI_CR_DLLOFF, aq, QSPI_CR); + + ret = readl_poll_timeout(aq->regs + QSPI_SR2, val, + !(val & QSPI_SR2_DLOCK), 40, + ATMEL_QSPI_TIMEOUT); + if (ret) + return ret; + } + + if (aq->slave_max_speed_hz > QSPI_DLLCFG_THRESHOLD_FREQ) + atmel_qspi_write(QSPI_DLLCFG_RANGE, aq, QSPI_DLLCFG); + else + atmel_qspi_write(0, aq, QSPI_DLLCFG); + } + if (aq->caps->has_2xgclk) + ret = clk_set_rate(aq->gclk, 2 * aq->slave_max_speed_hz); + else + ret = clk_set_rate(aq->gclk, aq->slave_max_speed_hz); + + if (ret) { + dev_err(&aq->pdev->dev, "Failed to set generic clock rate.\n"); + return ret; + } + + /* Enable the QSPI generic clock */ + ret = clk_prepare_enable(aq->gclk); + if (ret) + dev_err(&aq->pdev->dev, "Failed to enable generic clock.\n"); + + return ret; +} + +static int atmel_qspi_sama7g5_init(struct atmel_qspi *aq) +{ + u32 val; + int ret; + + ret = pm_runtime_resume_and_get(&aq->pdev->dev); + if (ret < 0) + return ret; + + ret = atmel_qspi_set_gclk(aq); + if (ret) + goto pm_runtime_put; + + /* + * Check if the SoC supports pad calibration in Octal SPI mode. + * Proceed only if both the capabilities are true. + */ + if (aq->caps->octal && aq->caps->has_padcalib) { + ret = atmel_qspi_set_pad_calibration(aq); + if (ret) + goto pm_runtime_put; + /* Start DLL on only if the SoC supports the same */ + } else if(aq->caps->has_dllon) { + atmel_qspi_write(QSPI_CR_DLLON, aq, QSPI_CR); + ret = readl_poll_timeout(aq->regs + QSPI_SR2, val, + (val & QSPI_SR2_DLOCK), 40, + ATMEL_QSPI_TIMEOUT); + } + + /* Set the QSPI controller by default in Serial Memory Mode */ + atmel_qspi_write(QSPI_MR_SMM | QSPI_MR_DQSDLYEN, aq, QSPI_MR); + aq->mr = QSPI_MR_SMM; + ret = atmel_qspi_update_config(aq); + if (ret) + goto pm_runtime_put; + + /* Enable the QSPI controller. */ + atmel_qspi_write(QSPI_CR_QSPIEN, aq, QSPI_CR); + ret = readl_poll_timeout(aq->regs + QSPI_SR2, val, + val & QSPI_SR2_QSPIENS, 40, + ATMEL_QSPI_SYNC_TIMEOUT); + if (ret) + goto pm_runtime_put; + + if (aq->caps->octal) { + ret = readl_poll_timeout(aq->regs + QSPI_SR, val, + val & QSPI_SR_RFRSHD, 40, + ATMEL_QSPI_TIMEOUT); + } + + atmel_qspi_write(QSPI_TOUT_TCNTM, aq, QSPI_TOUT); + +pm_runtime_put: + pm_runtime_mark_last_busy(&aq->pdev->dev); + pm_runtime_put_autosuspend(&aq->pdev->dev); + return ret; +} + +static int atmel_qspi_sama7g5_setup(struct spi_device *spi) +{ + struct atmel_qspi *aq = spi_controller_get_devdata(spi->controller); + + /* The controller can communicate with a single slave. */ + aq->slave_max_speed_hz = spi->max_speed_hz; + + return atmel_qspi_sama7g5_init(aq); +} + static int atmel_qspi_setup(struct spi_device *spi) { struct spi_controller *ctrl = spi->controller; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1178 @ if (!spi->max_speed_hz) return -EINVAL; + if (aq->caps->has_gclk) + return atmel_qspi_sama7g5_setup(spi); + src_rate = clk_get_rate(aq->pclk); if (!src_rate) return -EINVAL; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1236 @ return 0; } -static void atmel_qspi_init(struct atmel_qspi *aq) +static int atmel_qspi_init(struct atmel_qspi *aq) { + int ret; + + if (aq->caps->has_gclk) { + ret = atmel_qspi_reg_sync(aq); + if (ret) + return ret; + atmel_qspi_write(QSPI_CR_SWRST, aq, QSPI_CR); + return 0; + } + /* Reset the QSPI controller */ atmel_qspi_write(QSPI_CR_SWRST, aq, QSPI_CR); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1257 @ /* Enable the QSPI controller */ atmel_qspi_write(QSPI_CR_QSPIEN, aq, QSPI_CR); + return 0; } static irqreturn_t atmel_qspi_interrupt(int irq, void *dev_id) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1273 @ return IRQ_NONE; aq->pending |= pending; - if ((aq->pending & QSPI_SR_CMD_COMPLETED) == QSPI_SR_CMD_COMPLETED) + if ((aq->pending & aq->irq_mask) == aq->irq_mask) complete(&aq->cmd_completion); return IRQ_HANDLED; } +static int atmel_qspi_dma_init(struct spi_controller *ctrl) +{ + struct atmel_qspi *aq = spi_controller_get_devdata(ctrl); + int ret; + + aq->rx_chan = dma_request_chan(&aq->pdev->dev, "rx"); + if (IS_ERR(aq->rx_chan)) { + ret = PTR_ERR(aq->rx_chan); + aq->rx_chan = NULL; + return dev_err_probe(&aq->pdev->dev, ret, + "RX DMA channel is not available\n"); + } + + aq->tx_chan = dma_request_chan(&aq->pdev->dev, "tx"); + if (IS_ERR(aq->tx_chan)) { + ret = dev_err_probe(&aq->pdev->dev, PTR_ERR(aq->tx_chan), + "TX DMA channel is not available\n"); + goto release_rx_chan; + } + + ctrl->dma_rx = aq->rx_chan; + ctrl->dma_tx = aq->tx_chan; + init_completion(&aq->dma_completion); + + dev_info(&aq->pdev->dev, "Using %s (tx) and %s (rx) for DMA transfers\n", + dma_chan_name(aq->tx_chan), dma_chan_name(aq->rx_chan)); + + return 0; + +release_rx_chan: + dma_release_channel(aq->rx_chan); + aq->rx_chan = NULL; + aq->tx_chan = NULL; + return ret; +} + +static void atmel_qspi_dma_release(struct atmel_qspi *aq) +{ + if (aq->rx_chan) + dma_release_channel(aq->rx_chan); + if (aq->tx_chan) + dma_release_channel(aq->tx_chan); +} + +static const struct atmel_qspi_ops atmel_qspi_ops = { + .set_cfg = atmel_qspi_set_cfg, + .transfer = atmel_qspi_transfer, +}; + +static const struct atmel_qspi_ops atmel_qspi_sama7g5_ops = { + .set_cfg = atmel_qspi_sama7g5_set_cfg, + .transfer = atmel_qspi_sama7g5_transfer, +}; + static int atmel_qspi_probe(struct platform_device *pdev) { struct spi_controller *ctrl; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1344 @ if (!ctrl) return -ENOMEM; + aq = spi_controller_get_devdata(ctrl); + + aq->caps = of_device_get_match_data(&pdev->dev); + if (!aq->caps) { + dev_err(&pdev->dev, "Could not retrieve QSPI caps\n"); + return -EINVAL; + } + + init_completion(&aq->cmd_completion); + aq->pdev = pdev; + ctrl->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_DUAL | SPI_TX_QUAD; + if (aq->caps->octal) + ctrl->mode_bits |= SPI_RX_OCTAL | SPI_TX_OCTAL; + + if (aq->caps->has_gclk) + aq->ops = &atmel_qspi_sama7g5_ops; + else + aq->ops = &atmel_qspi_ops; + + ctrl->max_speed_hz = aq->caps->max_speed_hz; ctrl->setup = atmel_qspi_setup; ctrl->set_cs_timing = atmel_qspi_set_cs_timing; ctrl->bus_num = -1; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1373 @ ctrl->dev.of_node = pdev->dev.of_node; platform_set_drvdata(pdev, ctrl); - aq = spi_controller_get_devdata(ctrl); - - init_completion(&aq->cmd_completion); - aq->pdev = pdev; - /* Map the registers */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi_base"); aq->regs = devm_ioremap_resource(&pdev->dev, res); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1390 @ } aq->mmap_size = resource_size(res); + aq->mmap_phys_base = (dma_addr_t)res->start; /* Get the peripheral clock */ aq->pclk = devm_clk_get(&pdev->dev, "pclk"); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1409 @ return err; } - aq->caps = of_device_get_match_data(&pdev->dev); - if (!aq->caps) { - dev_err(&pdev->dev, "Could not retrieve QSPI caps\n"); - err = -EINVAL; - goto disable_pclk; - } - if (aq->caps->has_qspick) { /* Get the QSPI system clock */ aq->qspick = devm_clk_get(&pdev->dev, "qspick"); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1425 @ "failed to enable the QSPI system clock\n"); goto disable_pclk; } + } else if (aq->caps->has_gclk) { + /* Get the QSPI generic clock */ + aq->gclk = devm_clk_get(&pdev->dev, "gclk"); + if (IS_ERR(aq->gclk)) { + dev_err(&pdev->dev, "missing Generic clock\n"); + err = PTR_ERR(aq->gclk); + goto disable_pclk; + } + } + + if (aq->caps->has_dma) { + err = atmel_qspi_dma_init(ctrl); + if (err == -EPROBE_DEFER) + goto disable_qspick; } /* Request the IRQ */ irq = platform_get_irq(pdev, 0); if (irq < 0) { err = irq; - goto disable_qspick; + goto dma_release; } err = devm_request_irq(&pdev->dev, irq, atmel_qspi_interrupt, 0, dev_name(&pdev->dev), aq); if (err) - goto disable_qspick; + goto dma_release; pm_runtime_set_autosuspend_delay(&pdev->dev, 500); pm_runtime_use_autosuspend(&pdev->dev); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1458 @ pm_runtime_enable(&pdev->dev); pm_runtime_get_noresume(&pdev->dev); - atmel_qspi_init(aq); + err = atmel_qspi_init(aq); + if (err) + goto dma_release; err = spi_register_controller(ctrl); if (err) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1468 @ pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev); - goto disable_qspick; + goto dma_release; } pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); return 0; +dma_release: + if (aq->caps->has_dma) + atmel_qspi_dma_release(aq); disable_qspick: clk_disable_unprepare(aq->qspick); disable_pclk: @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1486 @ return err; } +static int atmel_qspi_sama7g5_suspend(struct atmel_qspi *aq) +{ + int ret; + u32 val; + + ret = readl_poll_timeout(aq->regs + QSPI_SR2, val, + !(val & QSPI_SR2_RBUSY) && + (val & QSPI_SR2_HIDLE), 40, + ATMEL_QSPI_SYNC_TIMEOUT); + if (ret) + return ret; + + atmel_qspi_write(QSPI_CR_QSPIDIS, aq, QSPI_CR); + ret = readl_poll_timeout(aq->regs + QSPI_SR2, val, + !(val & QSPI_SR2_QSPIENS), 40, + ATMEL_QSPI_SYNC_TIMEOUT); + if (ret) + return ret; + + clk_disable_unprepare(aq->gclk); + + if (aq->caps->has_dllon) { + atmel_qspi_write(QSPI_CR_DLLOFF, aq, QSPI_CR); + ret = readl_poll_timeout(aq->regs + QSPI_SR2, val, + !(val & QSPI_SR2_DLOCK), 40, + ATMEL_QSPI_TIMEOUT); + if (ret) + return ret; + } + + if (aq->caps->has_padcalib) + return readl_poll_timeout(aq->regs + QSPI_SR2, val, + !(val & QSPI_SR2_CALBSY), 40, + ATMEL_QSPI_TIMEOUT); + return 0; +} + static void atmel_qspi_remove(struct platform_device *pdev) { struct spi_controller *ctrl = platform_get_drvdata(pdev); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1531 @ spi_unregister_controller(ctrl); + if (aq->caps->has_gclk) + atmel_qspi_sama7g5_suspend(aq); + + if (aq->caps->has_dma) + atmel_qspi_dma_release(aq); + ret = pm_runtime_get_sync(&pdev->dev); if (ret >= 0) { atmel_qspi_write(QSPI_CR_QSPIDIS, aq, QSPI_CR); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1568 @ if (ret < 0) return ret; - atmel_qspi_write(QSPI_CR_QSPIDIS, aq, QSPI_CR); + if (aq->caps->has_gclk) + ret = atmel_qspi_sama7g5_suspend(aq); + else + atmel_qspi_write(QSPI_CR_QSPIDIS, aq, QSPI_CR); pm_runtime_mark_last_busy(dev); pm_runtime_force_suspend(dev); - clk_unprepare(aq->qspick); + if (aq->caps->has_qspick) + clk_unprepare(aq->qspick); clk_unprepare(aq->pclk); - return 0; + return ret; } static int __maybe_unused atmel_qspi_resume(struct device *dev) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1592 @ ret = clk_prepare(aq->pclk); if (ret) return ret; - - ret = clk_prepare(aq->qspick); - if (ret) { - clk_unprepare(aq->pclk); - return ret; + + if (aq->caps->has_qspick) { + ret = clk_prepare(aq->qspick); + if (ret) { + clk_unprepare(aq->pclk); + return ret; + } } ret = pm_runtime_force_resume(dev); if (ret < 0) return ret; - atmel_qspi_init(aq); - - atmel_qspi_write(aq->scr, aq, QSPI_SCR); + if (aq->caps->has_gclk) { + ret = atmel_qspi_sama7g5_init(aq); + } else { + atmel_qspi_init(aq); + atmel_qspi_write(aq->scr, aq, QSPI_SCR); + } pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); - return 0; + return ret; } static int __maybe_unused atmel_qspi_runtime_suspend(struct device *dev) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1623 @ struct spi_controller *ctrl = dev_get_drvdata(dev); struct atmel_qspi *aq = spi_controller_get_devdata(ctrl); - clk_disable(aq->qspick); + if (aq->caps->has_qspick) + clk_disable(aq->qspick); clk_disable(aq->pclk); return 0; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1639 @ ret = clk_enable(aq->pclk); if (ret) return ret; + if (aq->caps->has_qspick) + ret = clk_enable(aq->qspick); - ret = clk_enable(aq->qspick); if (ret) clk_disable(aq->pclk); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1661 @ .has_ricr = true, }; +static const struct atmel_qspi_caps atmel_sama7g5_ospi_caps = { + .max_speed_hz = SAMA7G5_QSPI0_MAX_SPEED_HZ, + .has_gclk = true, + .octal = true, + .has_dma = true, + .has_padcalib = true, + .has_dllon = true, +}; + +static const struct atmel_qspi_caps atmel_sama7g5_qspi_caps = { + .max_speed_hz = SAMA7G5_QSPI1_SDR_MAX_SPEED_HZ, + .has_gclk = true, + .has_dma = true, + .has_dllon = true, +}; + +static const struct atmel_qspi_caps atmel_sam9x7_ospi_caps = { + .max_speed_hz = SAM9X7_QSPI_MAX_SPEED_HZ, + .has_gclk = true, + .octal = true, + .has_dma = true, + .has_2xgclk = true, + .has_padcalib = false, + .has_dllon = false, +}; + static const struct of_device_id atmel_qspi_dt_ids[] = { { .compatible = "atmel,sama5d2-qspi", @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1696 @ .compatible = "microchip,sam9x60-qspi", .data = &atmel_sam9x60_qspi_caps, }, + { + .compatible = "microchip,sama7g5-ospi", + .data = &atmel_sama7g5_ospi_caps, + }, + { + .compatible = "microchip,sama7g5-qspi", + .data = &atmel_sama7g5_qspi_caps, + }, + { + .compatible = "microchip,sam9x7-ospi", + .data = &atmel_sam9x7_ospi_caps, + }, + { /* sentinel */ } }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/spi/spi.c linux-microchip/drivers/spi/spi.c --- linux-6.6.51/drivers/spi/spi.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/spi/spi.c 2024-11-26 14:02:38.486511396 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2011 @ int ret; mesg = ctlr->cur_msg; + if (!mesg) + return; if (!ctlr->ptp_sts_supported && !ctlr->transfer_one) { list_for_each_entry(xfer, &mesg->transfers, transfer_list) { diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/spi/spi-microchip-core.c linux-microchip/drivers/spi/spi-microchip-core.c --- linux-6.6.51/drivers/spi/spi-microchip-core.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/spi/spi-microchip-core.c 2024-11-26 14:02:38.478511252 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:114 @ int irq; int tx_len; int rx_len; - int pending; + int n_bytes; }; static inline u32 mchp_corespi_read(struct mchp_corespi *spi, unsigned int reg) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:138 @ static inline void mchp_corespi_read_fifo(struct mchp_corespi *spi) { - u8 data; - int fifo_max, i = 0; + while (spi->rx_len >= spi->n_bytes && !(mchp_corespi_read(spi, REG_STATUS) & STATUS_RXFIFO_EMPTY)) { + u32 data = mchp_corespi_read(spi, REG_RX_DATA); - fifo_max = min(spi->rx_len, FIFO_DEPTH); + spi->rx_len -= spi->n_bytes; - while ((i < fifo_max) && !(mchp_corespi_read(spi, REG_STATUS) & STATUS_RXFIFO_EMPTY)) { - data = mchp_corespi_read(spi, REG_RX_DATA); + if (!spi->rx_buf) + continue; - if (spi->rx_buf) - *spi->rx_buf++ = data; - i++; + if (spi->n_bytes == 4) + *((u32 *)spi->rx_buf) = data; + else if (spi->n_bytes == 2) + *((u16 *)spi->rx_buf) = data; + else + *spi->rx_buf = data; + + spi->rx_buf += spi->n_bytes; } - spi->rx_len -= i; - spi->pending -= i; } static void mchp_corespi_enable_ints(struct mchp_corespi *spi) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:216 @ static inline void mchp_corespi_write_fifo(struct mchp_corespi *spi) { - u8 byte; int fifo_max, i = 0; - fifo_max = min(spi->tx_len, FIFO_DEPTH); + fifo_max = DIV_ROUND_UP(min(spi->tx_len, FIFO_DEPTH), spi->n_bytes); mchp_corespi_set_xfer_size(spi, fifo_max); while ((i < fifo_max) && !(mchp_corespi_read(spi, REG_STATUS) & STATUS_TXFIFO_FULL)) { - byte = spi->tx_buf ? *spi->tx_buf++ : 0xaa; - mchp_corespi_write(spi, REG_TX_DATA, byte); + u32 word; + + if (spi->n_bytes == 4) + word = spi->tx_buf ? *((u32 *)spi->tx_buf) : 0xaa; + else if (spi->n_bytes == 2) + word = spi->tx_buf ? *((u16 *)spi->tx_buf) : 0xaa; + else + word = spi->tx_buf ? *spi->tx_buf : 0xaa; + + mchp_corespi_write(spi, REG_TX_DATA, word); + if (spi->tx_buf) + spi->tx_buf += spi->n_bytes; i++; } - spi->tx_len -= i; - spi->pending += i; + spi->tx_len -= i * spi->n_bytes; } static inline void mchp_corespi_set_framesize(struct mchp_corespi *spi, int bt) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:289 @ struct mchp_corespi *corespi = spi_controller_get_devdata(spi->controller); u32 reg; + if (spi_get_csgpiod(spi, 0)) + return 0; + /* * Active high targets need to be specifically set to their inactive * states during probe by adding them to the "control group" & thus @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:507 @ spi->rx_buf = xfer->rx_buf; spi->tx_len = xfer->len; spi->rx_len = xfer->len; - spi->pending = 0; + spi->n_bytes = roundup_pow_of_two(DIV_ROUND_UP(xfer->bits_per_word, BITS_PER_BYTE)); - mchp_corespi_set_xfer_size(spi, (spi->tx_len > FIFO_DEPTH) - ? FIFO_DEPTH : spi->tx_len); + mchp_corespi_set_framesize(spi, xfer->bits_per_word); mchp_corespi_write(spi, REG_COMMAND, COMMAND_RXFIFORST | COMMAND_TXFIFORST); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:527 @ struct spi_device *spi_dev = msg->spi; struct mchp_corespi *spi = spi_controller_get_devdata(host); - mchp_corespi_set_framesize(spi, DEFAULT_FRAMESIZE); mchp_corespi_set_mode(spi, spi_dev->mode); return 0; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:553 @ host->num_chipselect = num_cs; host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; host->setup = mchp_corespi_setup; - host->bits_per_word_mask = SPI_BPW_MASK(8); + host->use_gpio_descriptors = true; + host->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); host->transfer_one = mchp_corespi_transfer_one; host->prepare_message = mchp_corespi_prepare_message; host->set_cs = mchp_corespi_set_cs; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/spi/spi-microchip-core-qspi.c linux-microchip/drivers/spi/spi-microchip-core-qspi.c --- linux-6.6.51/drivers/spi/spi-microchip-core-qspi.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/spi/spi-microchip-core-qspi.c 2024-11-26 14:02:38.478511252 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:197 @ } } -static inline void mchp_coreqspi_write_op(struct mchp_coreqspi *qspi, bool word) +static inline void mchp_coreqspi_write_op(struct mchp_coreqspi *qspi) { u32 control, data; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:225 @ } } +static inline void mchp_coreqspi_write_read_op(struct mchp_coreqspi *qspi) +{ + u32 control, data; + + qspi->rx_len = qspi->tx_len; + + control = readl_relaxed(qspi->regs + REG_CONTROL); + control |= CONTROL_FLAGSX4; + writel_relaxed(control, qspi->regs + REG_CONTROL); + + while (qspi->tx_len >= 4) { + while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_TXFIFOFULL) + ; + + data = *(u32 *)qspi->txbuf; + qspi->txbuf += 4; + qspi->tx_len -= 4; + writel_relaxed(data, qspi->regs + REG_X4_TX_DATA); + + /* + * The rx FIFO is twice the size of the tx FIFO, so there is + * no requirement to block transmission if receive data is not + * ready, and it is fine to let the tx FIFO completely fill + * without reading anything from the rx FIFO. Once the tx FIFO + * has been filled and becomes non-full due to a transmission + * occurring there will always be something to receive. + * IOW, this is safe as TX_FIFO_SIZE + 4 < 2 * TX_FIFO_SIZE + */ + if (qspi->rx_len >= 4) { + if (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_RXAVAILABLE) { + data = readl_relaxed(qspi->regs + REG_X4_RX_DATA); + *(u32 *)qspi->rxbuf = data; + qspi->rxbuf += 4; + qspi->rx_len -= 4; + } + } + } + + /* + * Since transmission is not being blocked by clearing the rx FIFO, + * loop here until all received data "leaked" by the loop above has + * been dealt with. + */ + while (qspi->rx_len >= 4) { + while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_RXFIFOEMPTY) + ; + data = readl_relaxed(qspi->regs + REG_X4_RX_DATA); + *(u32 *)qspi->rxbuf = data; + qspi->rxbuf += 4; + qspi->rx_len -= 4; + } + + /* + * Since rx_len and tx_len must be < 4 bytes at this point, there's no + * concern about overflowing the rx or tx FIFOs any longer. It's + * therefore safe to loop over the remainder of the transmit data before + * handling the remaining receive data. + */ + if (!qspi->tx_len) + return; + + control &= ~CONTROL_FLAGSX4; + writel_relaxed(control, qspi->regs + REG_CONTROL); + + while (qspi->tx_len--) { + while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_TXFIFOFULL) + ; + data = *qspi->txbuf++; + writel_relaxed(data, qspi->regs + REG_TX_DATA); + } + + while (qspi->rx_len--) { + while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_RXFIFOEMPTY) + ; + data = readl_relaxed(qspi->regs + REG_RX_DATA); + *qspi->rxbuf++ = (data & 0xFF); + } +} + + static void mchp_coreqspi_enable_ints(struct mchp_coreqspi *qspi) { u32 mask = IEN_TXDONE | @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:449 @ writel_relaxed(frames, qspi->regs + REG_FRAMES); } -static int mchp_qspi_wait_for_ready(struct spi_mem *mem) +static int mchp_coreqspi_wait_for_ready(struct mchp_coreqspi *qspi) { - struct mchp_coreqspi *qspi = spi_controller_get_devdata - (mem->spi->master); u32 status; - int ret; - ret = readl_poll_timeout(qspi->regs + REG_STATUS, status, + return readl_poll_timeout(qspi->regs + REG_STATUS, status, (status & STATUS_READY), 0, TIMEOUT_MS); - if (ret) { - dev_err(&mem->spi->dev, - "Timeout waiting on QSPI ready.\n"); - return -ETIMEDOUT; - } - - return ret; } static int mchp_coreqspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:468 @ int err, i; mutex_lock(&qspi->op_lock); - err = mchp_qspi_wait_for_ready(mem); - if (err) + err = mchp_coreqspi_wait_for_ready(qspi); + if (err) { + dev_err(&mem->spi->dev, "Timeout waiting on QSPI ready.\n"); goto error; + } err = mchp_coreqspi_setup_clock(qspi, mem->spi); if (err) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:489 @ qspi->rxbuf = NULL; qspi->tx_len = op->cmd.nbytes; qspi->rx_len = 0; - mchp_coreqspi_write_op(qspi, false); + mchp_coreqspi_write_op(qspi); } qspi->txbuf = &opaddr[0]; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:500 @ qspi->rxbuf = NULL; qspi->tx_len = op->addr.nbytes; qspi->rx_len = 0; - mchp_coreqspi_write_op(qspi, false); + mchp_coreqspi_write_op(qspi); } if (op->data.nbytes) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:509 @ qspi->rxbuf = NULL; qspi->rx_len = 0; qspi->tx_len = op->data.nbytes; - mchp_coreqspi_write_op(qspi, true); + mchp_coreqspi_write_op(qspi); } else { qspi->txbuf = NULL; qspi->rxbuf = (u8 *)op->data.buf.in; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:573 @ .exec_op = mchp_coreqspi_exec_op, }; +static int mchp_coreqspi_unprepare_message(struct spi_controller *ctlr, + struct spi_message *m) +{ + struct mchp_coreqspi *qspi = spi_controller_get_devdata(ctlr); + + udelay(750); + mutex_unlock(&qspi->op_lock); + + return 0; +} + +static int mchp_coreqspi_prepare_message(struct spi_controller *ctlr, + struct spi_message *m) +{ + struct mchp_coreqspi *qspi = spi_controller_get_devdata(ctlr); + struct spi_transfer *t = NULL; + u32 control, frames; + u32 total_bytes = 0, cmd_bytes = 0, idle_cycles = 0; + int ret; + bool quad = false, dual = false; + + mutex_lock(&qspi->op_lock); + ret = mchp_coreqspi_wait_for_ready(qspi); + if (ret) { + mutex_unlock(&qspi->op_lock); + dev_err(&ctlr->dev, "Timeout waiting on QSPI ready.\n"); + return ret; + } + + ret = mchp_coreqspi_setup_clock(qspi, m->spi); + if (ret) { + mutex_unlock(&qspi->op_lock); + return ret; + } + + control = readl_relaxed(qspi->regs + REG_CONTROL); + control &= ~(CONTROL_MODE12_MASK | CONTROL_MODE0); + writel_relaxed(control, qspi->regs + REG_CONTROL); + + reinit_completion(&qspi->data_completion); + + list_for_each_entry(t, &m->transfers, transfer_list) { + total_bytes += t->len; + if ((!cmd_bytes) && !(t->tx_buf && t->rx_buf)) + cmd_bytes = t->len; + if (!t->rx_buf) + cmd_bytes = total_bytes; + if (t->tx_nbits == SPI_NBITS_QUAD || t->rx_nbits == SPI_NBITS_QUAD) + quad = true; + else if (t->tx_nbits == SPI_NBITS_DUAL || t->rx_nbits == SPI_NBITS_DUAL) + dual = true; + } + + control = readl_relaxed(qspi->regs + REG_CONTROL); + if (quad) { + control |= (CONTROL_MODE0 | CONTROL_MODE12_EX_RW); + } else if (dual) { + control &= ~CONTROL_MODE0; + control |= CONTROL_MODE12_FULL; + } else { + control &= ~(CONTROL_MODE12_MASK | CONTROL_MODE0); + } + writel_relaxed(control, qspi->regs + REG_CONTROL); + + frames = total_bytes & BYTESUPPER_MASK; + writel_relaxed(frames, qspi->regs + REG_FRAMESUP); + frames = total_bytes & BYTESLOWER_MASK; + frames |= cmd_bytes << FRAMES_CMDBYTES_SHIFT; + frames |= idle_cycles << FRAMES_IDLE_SHIFT; + control = readl_relaxed(qspi->regs + REG_CONTROL); + if (control & CONTROL_MODE12_MASK) + frames |= (1 << FRAMES_SHIFT); + + frames |= FRAMES_FLAGWORD; + writel_relaxed(frames, qspi->regs + REG_FRAMES); + + return 0; +}; + +static int mchp_coreqspi_transfer_one(struct spi_controller *ctlr, struct spi_device *spi, + struct spi_transfer *t) +{ + struct mchp_coreqspi *qspi = spi_controller_get_devdata(ctlr); + + if ((t->tx_buf) && (t->rx_buf)){ + qspi->txbuf = (u8 *)t->tx_buf; + qspi->rxbuf = (u8 *)t->rx_buf; + qspi->tx_len = t->len; + mchp_coreqspi_write_read_op(qspi); + } else if (t->tx_buf) { + qspi->txbuf = (u8 *)t->tx_buf; + qspi->tx_len = t->len; + mchp_coreqspi_write_op(qspi); + } else { + qspi->rxbuf = (u8 *)t->rx_buf; + qspi->rx_len = t->len; + mchp_coreqspi_read_op(qspi); + } + + return 0; +} + static int mchp_coreqspi_probe(struct platform_device *pdev) { struct spi_controller *ctlr; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:728 @ ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_DUAL | SPI_TX_QUAD; ctlr->dev.of_node = np; + ctlr->min_speed_hz = clk_get_rate(qspi->clk) / 30; + ctlr->prepare_message = mchp_coreqspi_prepare_message; + ctlr->unprepare_message = mchp_coreqspi_unprepare_message; + ctlr->transfer_one = mchp_coreqspi_transfer_one; + ctlr->num_chipselect = 2; + ctlr->use_gpio_descriptors = true; ret = devm_spi_register_controller(&pdev->dev, ctlr); if (ret) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:763 @ static const struct of_device_id mchp_coreqspi_of_match[] = { { .compatible = "microchip,coreqspi-rtl-v2" }, + { .compatible = "microchip,mpfs-qspi" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mchp_coreqspi_of_match); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/tty/serial/atmel_serial.c linux-microchip/drivers/tty/serial/atmel_serial.c --- linux-6.6.51/drivers/tty/serial/atmel_serial.c 2024-11-26 13:56:48.000000000 +0100 +++ linux-microchip/drivers/tty/serial/atmel_serial.c 2024-11-26 14:02:38.574512972 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2127 @ { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); unsigned long flags; - unsigned int old_mode, mode, imr, quot, div, cd, fp = 0; + unsigned int old_mode, mode, mdrop, imr, quot, div, cd, fp = 0; unsigned int baud, actual_baud, gclk_rate; int ret; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2165 @ /* parity */ if (termios->c_cflag & PARENB) { - /* Mark or Space parity */ + /* Mark, Space or Multidrop parity */ if (termios->c_cflag & CMSPAR) { - if (termios->c_cflag & PARODD) + if (termios->c_cflag & PARMD) + mode |= ATMEL_US_PAR_MULTI_DROP; + else if (termios->c_cflag & PARODD) mode |= ATMEL_US_PAR_MARK; else mode |= ATMEL_US_PAR_SPACE; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2179 @ mode |= ATMEL_US_PAR_EVEN; } else mode |= ATMEL_US_PAR_NONE; + mdrop = termios->c_cflag & SENDA ? ATMEL_US_SENDA : 0; + termios->c_cflag &= ~SENDA; /* SENDA bit must be cleared once used */ spin_lock_irqsave(&port->lock, flags); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2372 @ } atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX); - atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN); + atmel_uart_writel(port, ATMEL_US_CR, + mdrop | ATMEL_US_TXEN | ATMEL_US_RXEN); atmel_port->tx_stopped = false; /* restore interrupts */ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/uio/Kconfig linux-microchip/drivers/uio/Kconfig --- linux-6.6.51/drivers/uio/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/uio/Kconfig 2024-11-26 14:04:18.564303380 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:185 @ opae-sdk/tools/libopaeuio/ If you compile this as a module, it will be called uio_dfl. + +config UIO_MICROCHIP_DMA + tristate "Generic driver for Microchip Fabric DMA controller" + depends on UIO + help + Userspace I/O interface for the Microchip Fabric DMA controller. + If compiled as a module, it will be called uio-microchip-dma. endif diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/uio/Makefile linux-microchip/drivers/uio/Makefile --- linux-6.6.51/drivers/uio/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/uio/Makefile 2024-11-26 14:04:18.564303380 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:15 @ obj-$(CONFIG_UIO_FSL_ELBC_GPCM) += uio_fsl_elbc_gpcm.o obj-$(CONFIG_UIO_HV_GENERIC) += uio_hv_generic.o obj-$(CONFIG_UIO_DFL) += uio_dfl.o +obj-$(CONFIG_UIO_MICROCHIP_DMA) += uio-microchip-dma.o diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/uio/uio-microchip-dma.c linux-microchip/drivers/uio/uio-microchip-dma.c --- linux-6.6.51/drivers/uio/uio-microchip-dma.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/drivers/uio/uio-microchip-dma.c 2024-11-26 14:04:18.564303380 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +/* + * Microchip userspace FPGA fabric DMA driver + * + * Copyright (C) 2021 Microchip Incorporated - http://www.microchip.com/ + * + * Author: Vattipalli Praveen <praveen.kumar@microchip.com> + * + */ +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/genalloc.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/sizes.h> +#include <linux/slab.h> +#include <linux/uio_driver.h> + +#define DRV_NAME "mss-dma-uio" +#define DRV_VERSION "0.1" + +#define MAX_MSS_DMA_EVT (0x01) +#define DMA_INT_OCCURED (0x01) +#define DMA_INT_STA (0x10) +#define DMA_INT_CLR (0x18) + +struct uio_mss_dma_dev { + struct uio_info *info; + struct clk *mss_dma_clk; + void __iomem *base; + int irq; +}; + +static irqreturn_t mss_dma_handler(int irq, struct uio_info *info) +{ + struct uio_mss_dma_dev *dev_info = info->priv; + void __iomem *base = dev_info->base; + + if ((ioread32(base + DMA_INT_STA) & DMA_INT_OCCURED)) { + iowrite32(DMA_INT_OCCURED, base + DMA_INT_CLR); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static void mss_dma_cleanup(struct device *dev, struct uio_mss_dma_dev *uio_dma) +{ + struct uio_info *uio_dma_info = uio_dma->info; + int cnt; + + for (cnt = 0; cnt < MAX_MSS_DMA_EVT; cnt++, uio_dma_info++) { + uio_unregister_device(uio_dma_info); + kfree(uio_dma_info->name); + } + + iounmap(uio_dma->base); + kfree(uio_dma->info); + kfree(uio_dma); +} + +static int mss_dma_probe(struct platform_device *pdev) +{ + struct uio_info *uio_dma_info; + struct uio_mss_dma_dev *uio_dma; + struct resource *regs; + struct device *dev = &pdev->dev; + int ret = -ENODEV, len; + + uio_dma = kzalloc(sizeof(*uio_dma), GFP_KERNEL); + if (!uio_dma) + return -ENOMEM; + + uio_dma->info = kzalloc(sizeof(*uio_dma_info) * MAX_MSS_DMA_EVT, GFP_KERNEL); + if (!uio_dma->info) { + kfree(uio_dma); + return -ENOMEM; + } + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + dev_err(dev, "No MSS DMA I/O resource specified\n"); + goto out_free; + } + + len = resource_size(regs); + uio_dma->base = ioremap(regs->start, len); + if (!uio_dma->base) { + dev_err(dev, "Can't remap MSS DMA I/O address range\n"); + goto out_free; + } + + uio_dma->irq = platform_get_irq(pdev, 0); + + uio_dma_info = uio_dma->info; + + uio_dma_info->mem[0].addr = regs->start; + uio_dma_info->mem[0].size = resource_size(regs); + uio_dma_info->mem[0].memtype = UIO_MEM_PHYS; + + uio_dma_info->mem[1].size = 0; + + uio_dma_info->name = dev->of_node->full_name; + uio_dma_info->version = DRV_VERSION; + + uio_dma_info->irq = uio_dma->irq; + uio_dma_info->irq_flags = IRQF_SHARED; + uio_dma_info->handler = mss_dma_handler; + uio_dma_info->priv = uio_dma; + + ret = uio_register_device(dev, uio_dma_info); + if (ret < 0) { + dev_err(dev, "failed to register device\n"); + goto out_free; + } + + platform_set_drvdata(pdev, uio_dma); + + dev_info(dev, "registered device as %s\n", uio_dma_info->name); + + return 0; + +out_free: + mss_dma_cleanup(dev, uio_dma); + + return ret; +} + +static int mss_dma_remove(struct platform_device *dev) +{ + struct uio_mss_dma_dev *dev_info = platform_get_drvdata(dev); + + mss_dma_cleanup(&dev->dev, dev_info); + + return 0; +} + +#define MICROCHIP_DMA_PM_OPS (NULL) + +#if defined(CONFIG_OF) +static const struct of_device_id mss_dma_dt_ids[] = { + { .compatible = "microchip,mpfs-fpga-dma" }, + { /*sentinel */ } +}; +#endif + +static struct platform_driver mss_dma_driver = { + .probe = mss_dma_probe, + .remove = mss_dma_remove, + .driver = { + .name = DRV_NAME, + .pm = MICROCHIP_DMA_PM_OPS, + .of_match_table = of_match_ptr(mss_dma_dt_ids), + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(mss_dma_driver); + +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); +MODULE_AUTHOR("Vattipalli Praveen <praveen.kumar@microchip.com>"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/usb/gadget/udc/atmel_usba_udc.c linux-microchip/drivers/usb/gadget/udc/atmel_usba_udc.c --- linux-6.6.51/drivers/usb/gadget/udc/atmel_usba_udc.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/usb/gadget/udc/atmel_usba_udc.c 2024-11-26 14:02:38.606513545 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1867 @ pm_stay_awake(&udc->pdev->dev); + phy_power_on(udc->phy); + ret = clk_prepare_enable(udc->pclk); if (ret) return ret; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1890 @ clk_disable_unprepare(udc->hclk); clk_disable_unprepare(udc->pclk); + phy_power_off(udc->phy); + udc->clocked = false; pm_relax(&udc->pdev->dev); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1957 @ vbus = vbus_is_present(udc); if (vbus != udc->vbus_prev) { if (vbus) { + phy_set_mode_ext(udc->phy, PHY_MODE_USB_DEVICE, 1); usba_start(udc); } else { udc->suspended = false; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1965 @ udc->driver->disconnect(&udc->gadget); usba_stop(udc); + phy_set_mode_ext(udc->phy, PHY_MODE_USB_DEVICE, 0); } udc->vbus_prev = vbus; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2012 @ /* If Vbus is present, enable the controller and wait for reset */ udc->vbus_prev = vbus_is_present(udc); if (udc->vbus_prev) { + phy_set_mode_ext(udc->phy, PHY_MODE_USB_DEVICE, 1); ret = usba_start(udc); if (ret) goto err; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2099 @ { .nr_banks = 2, .can_isoc = 1 }, /* ep 15 */ }; +static const struct usba_ep_config ep_config_sama7[] __initconst = { + { .nr_banks = 1 }, /* ep 0 */ + { .nr_banks = 3, .can_dma = 1, .can_isoc = 1 }, /* ep 1 */ + { .nr_banks = 3, .can_dma = 1, .can_isoc = 1 }, /* ep 2 */ + { .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 3 */ + { .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 4 */ + { .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 5 */ + { .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 6 */ + { .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 7 */ + { .nr_banks = 1 }, /* ep 8 */ + { .nr_banks = 1 }, /* ep 9 */ + { .nr_banks = 1 }, /* ep 10 */ + { .nr_banks = 1 }, /* ep 11 */ + { .nr_banks = 1 }, /* ep 12 */ + { .nr_banks = 1 }, /* ep 13 */ + { .nr_banks = 1 }, /* ep 14 */ + { .nr_banks = 1 }, /* ep 15 */ +}; + static const struct usba_udc_config udc_at91sam9rl_cfg = { .errata = &at91sam9rl_errata, .config = ep_config_sam9, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2144 @ .ep_prealloc = false, }; +static const struct usba_udc_config udc_sama7g5_cfg = { + .num_ep = ARRAY_SIZE(ep_config_sama7), + .config = ep_config_sama7, + .ep_prealloc = false, +}; + static const struct of_device_id atmel_udc_dt_ids[] = { { .compatible = "atmel,at91sam9rl-udc", .data = &udc_at91sam9rl_cfg }, { .compatible = "atmel,at91sam9g45-udc", .data = &udc_at91sam9g45_cfg }, { .compatible = "atmel,sama5d3-udc", .data = &udc_sama5d3_cfg }, { .compatible = "microchip,sam9x60-udc", .data = &udc_sam9x60_cfg }, + { .compatible = "microchip,sama7g5-udc", .data = &udc_sama7g5_cfg }, { /* sentinel */ } }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2197 @ return ERR_CAST(udc->pmc); } + udc->phy = devm_phy_optional_get(&pdev->dev, "usb"); + udc->num_ep = 0; udc->vbus_pin = devm_gpiod_get_optional(&pdev->dev, "atmel,vbus", @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2365 @ udc->usba_ep = atmel_udc_of_init(pdev, udc); + phy_init(udc->phy); + phy_set_mode(udc->phy, PHY_MODE_USB_DEVICE); + toggle_bias(udc, 0); if (IS_ERR(udc->usba_ep)) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2475 @ /* If Vbus is present, enable the controller and wait for reset */ mutex_lock(&udc->vbus_mutex); udc->vbus_prev = vbus_is_present(udc); - if (udc->vbus_prev) + if (udc->vbus_prev) { + phy_set_mode_ext(udc->phy, PHY_MODE_USB_DEVICE, 1); usba_start(udc); + } mutex_unlock(&udc->vbus_mutex); return 0; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/usb/gadget/udc/atmel_usba_udc.h linux-microchip/drivers/usb/gadget/udc/atmel_usba_udc.h --- linux-6.6.51/drivers/usb/gadget/udc/atmel_usba_udc.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/usb/gadget/udc/atmel_usba_udc.h 2024-11-26 14:02:38.606513545 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:11 @ #define __LINUX_USB_GADGET_USBA_UDC_H__ #include <linux/gpio/consumer.h> +#include <linux/phy/phy.h> /* USB register offsets */ #define USBA_CTRL 0x0000 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:361 @ struct dentry *debugfs_root; #endif + struct phy *phy; struct regmap *pmc; }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/usb/host/ohci-at91.c linux-microchip/drivers/usb/host/ohci-at91.c --- linux-6.6.51/drivers/usb/host/ohci-at91.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/usb/host/ohci-at91.c 2024-11-26 14:02:38.618513761 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:27 @ #include <linux/module.h> #include <linux/mfd/syscon.h> #include <linux/of.h> +#include <linux/phy/phy.h> #include <linux/regmap.h> #include <linux/usb.h> #include <linux/usb/hcd.h> #include <soc/at91/atmel-sfr.h> +#include <soc/at91/sama7-sfr.h> #include "ohci.h" @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:44 @ #define hcd_to_ohci_at91_priv(h) \ ((struct ohci_at91_priv *)hcd_to_ohci(h)->priv) +struct at91_ohci_cfg { + unsigned int ohciicr; +}; + +static const struct of_device_id at91_ohci_dt_ids[]; + #define AT91_MAX_USBH_PORTS 3 struct at91_usbh_data { struct gpio_desc *vbus_pin[AT91_MAX_USBH_PORTS]; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:58 @ u8 overcurrent_supported; u8 overcurrent_status[AT91_MAX_USBH_PORTS]; u8 overcurrent_changed[AT91_MAX_USBH_PORTS]; + struct ohci_at91_priv *ohci_at91; }; struct ohci_at91_priv { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:69 @ bool wakeup; /* Saved wake-up state for resume */ struct regmap *sfr_regmap; u32 suspend_smc_id; + const struct at91_ohci_cfg *cfg; + struct phy *phy[AT91_MAX_USBH_PORTS]; }; /* interface and function clocks; sometimes also an AHB clock */ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:86 @ static void at91_start_clock(struct ohci_at91_priv *ohci_at91) { + int i; + if (ohci_at91->clocked) return; + at91_for_each_port(i) { + phy_power_on(ohci_at91->phy[i]); + } + clk_set_rate(ohci_at91->fclk, 48000000); clk_prepare_enable(ohci_at91->hclk); clk_prepare_enable(ohci_at91->iclk); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:104 @ static void at91_stop_clock(struct ohci_at91_priv *ohci_at91) { + int i; + if (!ohci_at91->clocked) return; clk_disable_unprepare(ohci_at91->fclk); clk_disable_unprepare(ohci_at91->iclk); clk_disable_unprepare(ohci_at91->hclk); + + at91_for_each_port(i) { + phy_power_off(ohci_at91->phy[i]); + } + ohci_at91->clocked = false; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:180 @ struct regmap *regmap; regmap = syscon_regmap_lookup_by_compatible("atmel,sama5d2-sfr"); - if (IS_ERR(regmap)) { + if (IS_ERR(regmap)) regmap = syscon_regmap_lookup_by_compatible("microchip,sam9x60-sfr"); - if (IS_ERR(regmap)) - regmap = NULL; - } + if (IS_ERR(regmap)) + regmap = syscon_regmap_lookup_by_compatible("microchip,sama7g5-sfr"); + if (IS_ERR(regmap)) + regmap = NULL; return regmap; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:214 @ struct usb_hcd *hcd; struct ohci_at91_priv *ohci_at91; struct device *dev = &pdev->dev; + const struct of_device_id *match; + struct device_node *np = dev->of_node; struct resource *res; - int irq; + int i, irq; irq = platform_get_irq(pdev, 0); if (irq < 0) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:236 @ hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); + match = of_match_node(at91_ohci_dt_ids, np); + if (match && match->data) { + ohci_at91->cfg = match->data; + } else { + dev_err(dev, "failed to match compatible node\n"); + retval = PTR_ERR(match); + goto err; + } + ohci_at91->iclk = devm_clk_get(dev, "ohci_clk"); if (IS_ERR(ohci_at91->iclk)) { dev_err(dev, "failed to get ohci_clk\n"); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:272 @ dev_dbg(dev, "failed to find sfr node\n"); } + at91_for_each_port(i) { + ohci_at91->phy[i] = devm_of_phy_get_by_index(dev, np, i); + + /* The phy is optional */ + if (IS_ERR(ohci_at91->phy[i]) && + (PTR_ERR(ohci_at91->phy[i]) == -ENODEV)) { + dev_dbg(dev, "Optional PHY %d missing\n", i); + ohci_at91->phy[i] = NULL; + } + + phy_init(ohci_at91->phy[i]); + phy_set_mode(ohci_at91->phy[i], PHY_MODE_USB_HOST); + } + board = hcd->self.controller->platform_data; + board->ohci_at91 = ohci_at91; ohci = hcd_to_ohci(hcd); ohci->num_ports = board->ports; at91_start_hc(pdev); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:384 @ if (res.a0) return -EINVAL; } else if (regmap) { - ret = regmap_read(regmap, AT91_SFR_OHCIICR, ®val); + ret = regmap_read(regmap, ohci_at91->cfg->ohciicr, ®val); if (ret) return ret; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:393 @ else regval &= ~AT91_OHCIICR_USB_SUSPEND; - regmap_write(regmap, AT91_SFR_OHCIICR, regval); + regmap_write(regmap, ohci_at91->cfg->ohciicr, regval); } return 0; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:573 @ return IRQ_HANDLED; } +static const struct at91_ohci_cfg at91rm9200_cfg = { + .ohciicr = AT91_SFR_OHCIICR, +}; + +static const struct at91_ohci_cfg sama7g5_cfg = { + .ohciicr = SAMA7_SFR_OHCIICR, +}; + static const struct of_device_id at91_ohci_dt_ids[] = { - { .compatible = "atmel,at91rm9200-ohci" }, + { .compatible = "atmel,at91rm9200-ohci", .data = &at91rm9200_cfg }, + { .compatible = "microchip,sama7g5-ohci", .data = &sama7g5_cfg }, { /* sentinel */ } }; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:724 @ { struct usb_hcd *hcd = dev_get_drvdata(dev); struct ohci_at91_priv *ohci_at91 = hcd_to_ohci_at91_priv(hcd); + int i; + + at91_for_each_port(i) { + if (!ohci_at91->phy[i]) + continue; + + phy_init(ohci_at91->phy[i]); + phy_set_mode(ohci_at91->phy[i], PHY_MODE_USB_HOST); + } ohci_at91_port_suspend(ohci_at91, 0); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/usb/musb/mpfs.c linux-microchip/drivers/usb/musb/mpfs.c --- linux-6.6.51/drivers/usb/musb/mpfs.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/usb/musb/mpfs.c 2024-11-26 14:02:38.626513903 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:178 @ musb_pdev->dev.parent = dev; musb_pdev->dev.coherent_dma_mask = DMA_BIT_MASK(39); musb_pdev->dev.dma_mask = &musb_pdev->dev.coherent_dma_mask; + musb_pdev->dev.dma_range_map = pdev->dev.dma_range_map; device_set_of_node_from_dev(&musb_pdev->dev, dev); glue->dev = dev; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:194 @ pdata->config = &mpfs_musb_hdrc_config; pdata->platform_ops = &mpfs_ops; + pdata->extvbus = device_property_read_bool(dev, "microchip,ext-vbus-drv"); + pdata->mode = usb_get_dr_mode(dev); if (pdata->mode == USB_DR_MODE_UNKNOWN) { dev_info(dev, "No dr_mode property found, defaulting to otg\n"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/drivers/watchdog/sama5d4_wdt.c linux-microchip/drivers/watchdog/sama5d4_wdt.c --- linux-6.6.51/drivers/watchdog/sama5d4_wdt.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/drivers/watchdog/sama5d4_wdt.c 2024-11-26 14:02:38.690515049 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:257 @ struct sama5d4_wdt *wdt; void __iomem *regs; u32 irq = 0; - u32 reg; int ret; wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:307 @ watchdog_init_timeout(wdd, wdt_timeout, dev); - reg = wdt_read(wdt, AT91_WDT_MR); - if (!(reg & AT91_WDT_WDDIS)) { - wdt->mr &= ~AT91_WDT_WDDIS; - set_bit(WDOG_HW_RUNNING, &wdd->status); - } - ret = sama5d4_wdt_init(wdt); if (ret) return ret; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/drm/drm_atomic_helper.h linux-microchip/include/drm/drm_atomic_helper.h --- linux-6.6.51/include/drm/drm_atomic_helper.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/include/drm/drm_atomic_helper.h 2024-11-26 14:02:38.850517916 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:79 @ int drm_atomic_helper_wait_for_fences(struct drm_device *dev, struct drm_atomic_state *state, bool pre_swap); +bool drm_atomic_helper_framebuffer_changed(struct drm_device *dev, + struct drm_atomic_state *old_state, + struct drm_crtc *crtc); void drm_atomic_helper_wait_for_vblanks(struct drm_device *dev, struct drm_atomic_state *old_state); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/dt-bindings/clock/at91.h linux-microchip/include/dt-bindings/clock/at91.h --- linux-6.6.51/include/dt-bindings/clock/at91.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/include/dt-bindings/clock/at91.h 2024-11-26 14:02:38.854517987 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:40 @ #define PMC_ETHPLL (PMC_MAIN + 8) #define PMC_CPU (PMC_MAIN + 9) #define PMC_MCK1 (PMC_MAIN + 10) +#define UTMI1 0 +#define UTMI2 1 +#define UTMI3 2 + +/* SAM9X7 */ +#define PMC_PLLADIV2 (PMC_MAIN + 11) +#define PMC_LVDSPLL (PMC_MAIN + 12) #ifndef AT91_PMC_MOSCS #define AT91_PMC_MOSCS 0 /* MOSCS Flag */ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:61 @ #define AT91_PMC_GCKRDY 24 /* Generated Clocks */ #endif +#define AT91_SCMI_CLK_CORE_MCK 0 +#define AT91_SCMI_CLK_CORE_UTMI 1 +#define AT91_SCMI_CLK_CORE_MAIN 2 +#define AT91_SCMI_CLK_CORE_MCK2 3 +#define AT91_SCMI_CLK_CORE_I2S0_MUX 4 +#define AT91_SCMI_CLK_CORE_I2S1_MUX 5 +#define AT91_SCMI_CLK_CORE_PLLACK 6 +#define AT91_SCMI_CLK_CORE_PLLBCK 7 +#define AT91_SCMI_CLK_CORE_AUDIOPLLCK 8 +#define AT91_SCMI_CLK_CORE_MCK_PRES 9 + +#define AT91_SCMI_CLK_SYSTEM_DDRCK 10 +#define AT91_SCMI_CLK_SYSTEM_LCDCK 11 +#define AT91_SCMI_CLK_SYSTEM_UHPCK 12 +#define AT91_SCMI_CLK_SYSTEM_UDPCK 13 +#define AT91_SCMI_CLK_SYSTEM_PCK0 14 +#define AT91_SCMI_CLK_SYSTEM_PCK1 15 +#define AT91_SCMI_CLK_SYSTEM_PCK2 16 +#define AT91_SCMI_CLK_SYSTEM_ISCCK 17 + +#define AT91_SCMI_CLK_PERIPH_MACB0_CLK 18 +#define AT91_SCMI_CLK_PERIPH_TDES_CLK 19 +#define AT91_SCMI_CLK_PERIPH_MATRIX1_CLK 20 +#define AT91_SCMI_CLK_PERIPH_HSMC_CLK 21 +#define AT91_SCMI_CLK_PERIPH_PIOA_CLK 22 +#define AT91_SCMI_CLK_PERIPH_FLX0_CLK 23 +#define AT91_SCMI_CLK_PERIPH_FLX1_CLK 24 +#define AT91_SCMI_CLK_PERIPH_FLX2_CLK 25 +#define AT91_SCMI_CLK_PERIPH_FLX3_CLK 26 +#define AT91_SCMI_CLK_PERIPH_FLX4_CLK 27 +#define AT91_SCMI_CLK_PERIPH_UART0_CLK 28 +#define AT91_SCMI_CLK_PERIPH_UART1_CLK 29 +#define AT91_SCMI_CLK_PERIPH_UART2_CLK 30 +#define AT91_SCMI_CLK_PERIPH_UART3_CLK 31 +#define AT91_SCMI_CLK_PERIPH_UART4_CLK 32 +#define AT91_SCMI_CLK_PERIPH_TWI0_CLK 33 +#define AT91_SCMI_CLK_PERIPH_TWI1_CLK 34 +#define AT91_SCMI_CLK_PERIPH_SPI0_CLK 35 +#define AT91_SCMI_CLK_PERIPH_SPI1_CLK 36 +#define AT91_SCMI_CLK_PERIPH_TCB0_CLK 37 +#define AT91_SCMI_CLK_PERIPH_TCB1_CLK 38 +#define AT91_SCMI_CLK_PERIPH_PWM_CLK 39 +#define AT91_SCMI_CLK_PERIPH_ADC_CLK 40 +#define AT91_SCMI_CLK_PERIPH_UHPHS_CLK 41 +#define AT91_SCMI_CLK_PERIPH_UDPHS_CLK 42 +#define AT91_SCMI_CLK_PERIPH_SSC0_CLK 43 +#define AT91_SCMI_CLK_PERIPH_SSC1_CLK 44 +#define AT91_SCMI_CLK_PERIPH_TRNG_CLK 45 +#define AT91_SCMI_CLK_PERIPH_PDMIC_CLK 46 +#define AT91_SCMI_CLK_PERIPH_SECURAM_CLK 47 +#define AT91_SCMI_CLK_PERIPH_I2S0_CLK 48 +#define AT91_SCMI_CLK_PERIPH_I2S1_CLK 49 +#define AT91_SCMI_CLK_PERIPH_CAN0_CLK 50 +#define AT91_SCMI_CLK_PERIPH_CAN1_CLK 51 +#define AT91_SCMI_CLK_PERIPH_PTC_CLK 52 +#define AT91_SCMI_CLK_PERIPH_CLASSD_CLK 53 +#define AT91_SCMI_CLK_PERIPH_DMA0_CLK 54 +#define AT91_SCMI_CLK_PERIPH_DMA1_CLK 55 +#define AT91_SCMI_CLK_PERIPH_AES_CLK 56 +#define AT91_SCMI_CLK_PERIPH_AESB_CLK 57 +#define AT91_SCMI_CLK_PERIPH_SHA_CLK 58 +#define AT91_SCMI_CLK_PERIPH_MPDDR_CLK 59 +#define AT91_SCMI_CLK_PERIPH_MATRIX0_CLK 60 +#define AT91_SCMI_CLK_PERIPH_SDMMC0_HCLK 61 +#define AT91_SCMI_CLK_PERIPH_SDMMC1_HCLK 62 +#define AT91_SCMI_CLK_PERIPH_LCDC_CLK 63 +#define AT91_SCMI_CLK_PERIPH_ISC_CLK 64 +#define AT91_SCMI_CLK_PERIPH_QSPI0_CLK 65 +#define AT91_SCMI_CLK_PERIPH_QSPI1_CLK 66 + +#define AT91_SCMI_CLK_GCK_SDMMC0_GCLK 67 +#define AT91_SCMI_CLK_GCK_SDMMC1_GCLK 68 +#define AT91_SCMI_CLK_GCK_TCB0_GCLK 69 +#define AT91_SCMI_CLK_GCK_TCB1_GCLK 70 +#define AT91_SCMI_CLK_GCK_PWM_GCLK 71 +#define AT91_SCMI_CLK_GCK_ISC_GCLK 72 +#define AT91_SCMI_CLK_GCK_PDMIC_GCLK 73 +#define AT91_SCMI_CLK_GCK_I2S0_GCLK 74 +#define AT91_SCMI_CLK_GCK_I2S1_GCLK 75 +#define AT91_SCMI_CLK_GCK_CAN0_GCLK 76 +#define AT91_SCMI_CLK_GCK_CAN1_GCLK 77 +#define AT91_SCMI_CLK_GCK_CLASSD_GCLK 78 + +#define AT91_SCMI_CLK_PROG_PROG0 79 +#define AT91_SCMI_CLK_PROG_PROG1 80 +#define AT91_SCMI_CLK_PROG_PROG2 81 + +#define AT91_SCMI_CLK_SCKC_SLOWCK_32K 82 + +/* SAMA7G5 */ +#define AT91_SCMI_CLK_CORE_CPUPLLCK 4 +#define AT91_SCMI_CLK_CORE_SYSPLLCK 5 +#define AT91_SCMI_CLK_CORE_DDRPLLCK 6 +#define AT91_SCMI_CLK_CORE_IMGPLLCK 7 +#define AT91_SCMI_CLK_CORE_ETHPLLCK 10 +#define AT91_SCMI_CLK_SYSTEM_PCK3 11 +#define AT91_SCMI_CLK_SYSTEM_PCK4 12 +#define AT91_SCMI_CLK_SYSTEM_PCK5 13 +#define AT91_SCMI_CLK_SYSTEM_PCK6 17 +#define AT91_SCMI_CLK_SYSTEM_PCK7 20 +#define AT91_SCMI_CLK_PERIPH_DMA2_CLK 100 +#define AT91_SCMI_CLK_PERIPH_FLX5_CLK 28 +#define AT91_SCMI_CLK_PERIPH_FLX6_CLK 29 +#define AT91_SCMI_CLK_PERIPH_FLX7_CLK 30 +#define AT91_SCMI_CLK_PERIPH_FLX8_CLK 31 +#define AT91_SCMI_CLK_PERIPH_FLX9_CLK 32 +#define AT91_SCMI_CLK_PERIPH_FLX10_CLK 33 +#define AT91_SCMI_CLK_PERIPH_FLX11_CLK 34 +#define AT91_SCMI_CLK_PERIPH_UDPHSB_CLK 35 +#define AT91_SCMI_CLK_PERIPH_PDMC1_CLK 36 +#define AT91_SCMI_CLK_PERIPH_UDPHSA_CLK 42 +#define AT91_SCMI_CLK_GCK_ADC_GCLK 40 +#define AT91_SCMI_CLK_PERIPH_PDMC0_CLK 46 +#define AT91_SCMI_CLK_PERIPH_CAN2_CLK 52 +#define AT91_SCMI_CLK_PERIPH_CAN3_CLK 53 +#define AT91_SCMI_CLK_PERIPH_CAN4_CLK 59 +#define AT91_SCMI_CLK_PERIPH_CAN5_CLK 60 +#define AT91_SCMI_CLK_PERIPH_SDMMC2_HCLK 63 +#define AT91_SCMI_CLK_GCK_SDMMC2_GCLK 71 +#define AT91_SCMI_CLK_GCK_MACB0_GCLK 72 +#define AT91_SCMI_CLK_GCK_MACB0_TSU 73 +#define AT91_SCMI_CLK_GCK_CAN2_GCLK 78 +#define AT91_SCMI_CLK_GCK_CAN3_GCLK 79 +#define AT91_SCMI_CLK_GCK_CAN4_GCLK 80 +#define AT91_SCMI_CLK_GCK_CAN5_GCLK 81 +#define AT91_SCMI_CLK_PERIPH_SPDIFRX_CLK 83 +#define AT91_SCMI_CLK_PERIPH_SPDIFTX_CLK 84 +#define AT91_SCMI_CLK_GCK_QSPI0_GCLK 85 +#define AT91_SCMI_CLK_GCK_QSPI1_GCLK 86 +#define AT91_SCMI_CLK_GCK_SPDIFRX_GCLK 87 +#define AT91_SCMI_CLK_GCK_SPDIFTX_GCLK 88 +#define AT91_SCMI_CLK_GCK_MACB1_GCLK 89 +#define AT91_SCMI_CLK_PERIPH_MACB1_CLK 90 +#define AT91_SCMI_CLK_GCK_MACB1_TSU 91 +#define AT91_SCMI_CLK_PERIPH_CSI_CLK 92 +#define AT91_SCMI_CLK_GCK_CSI_GCLK 93 +#define AT91_SCMI_CLK_PERIPH_CSI2DC_CLK 94 +#define AT91_SCMI_CLK_PERIPH_ASRC_CLK 95 +#define AT91_SCMI_CLK_GCK_ASRC_GCLK 96 +#define AT91_SCMI_CLK_UTMI1 97 +#define AT91_SCMI_CLK_UTMI2 98 +#define AT91_SCMI_CLK_UTMI3 99 +#define AT91_SCMI_CLK_PERIPH_DMA2_CLK 100 +#define AT91_SCMI_CLK_CPU_OPP 101 + #endif diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/dt-bindings/clock/microchip,mpfs-clock.h linux-microchip/include/dt-bindings/clock/microchip,mpfs-clock.h --- linux-6.6.51/include/dt-bindings/clock/microchip,mpfs-clock.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/include/dt-bindings/clock/microchip,mpfs-clock.h 2024-11-26 14:02:38.858518060 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:47 @ #define CLK_RTCREF 33 #define CLK_MSSPLL 34 +#define CLK_MSSPLL0 34 +#define CLK_MSSPLL1 35 +#define CLK_MSSPLL2 36 +#define CLK_MSSPLL3 37 +/* 38 is reserved for MSS PLL internals */ /* Clock Conditioning Circuitry Clock IDs */ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/dt-bindings/mailbox/miv-ihc.h linux-microchip/include/dt-bindings/mailbox/miv-ihc.h --- linux-6.6.51/include/dt-bindings/mailbox/miv-ihc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/include/dt-bindings/mailbox/miv-ihc.h 2024-11-26 14:02:38.870518274 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ +/* + * Copyright (c) 2021 Microchip Technology Inc. All rights reserved. + */ + +#ifndef __DT_BINDINGS_MIV_IHC_H +#define __DT_BINDINGS_MIV_IHC_H + +#define IHC_CONTEXT_A 5 +#define IHC_CONTEXT_B 6 + +#define IHC_HART1_INT 180 +#define IHC_HART2_INT 179 +#define IHC_HART3_INT 178 +#define IHC_HART4_INT 177 + +#endif diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/dt-bindings/media/microchip-common.h linux-microchip/include/dt-bindings/media/microchip-common.h --- linux-6.6.51/include/dt-bindings/media/microchip-common.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/include/dt-bindings/media/microchip-common.h 2024-11-26 14:02:38.870518274 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + */ + +#ifndef __DT_BINDINGS_MICROCHIP_COMMON_H__ +#define __DT_BINDINGS_MICROCHIP_COMMON_H__ + +/* + * Video format codes as defined in "AXI4-Stream Video IP and System Design + * Guide". + */ +#define MVCF_MONO_SENSOR 0 +#define MVCF_YUV_444 1 +#define MVCF_YUV_422 2 +#define MVCF_YUV_420 3 +#define MVCF_RBG 4 +#define MVCF_H264 5 +#define MVCF_MJPEG 6 +#define MVCF_RGB 7 + +#define MVC_MIN_WIDTH 32 +#define MVC_MAX_WIDTH 7680 +#define MVC_MIN_HEIGHT 32 +#define MVC_MAX_HEIGHT 7680 + +#endif /* __DT_BINDINGS_MICROCHIP_COMMON_H__ */ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/dt-bindings/sound/microchip,asrc-card.h linux-microchip/include/dt-bindings/sound/microchip,asrc-card.h --- linux-6.6.51/include/dt-bindings/sound/microchip,asrc-card.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/include/dt-bindings/sound/microchip,asrc-card.h 2024-11-26 14:02:38.878518418 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __DT_BINDINGS_MICROCHIP_ASRC_CARD_H__ +#define __DT_BINDINGS_MICROCHIP_ASRC_CARD_H__ + +/* formats to be used for Back-ends of Microhip's ASRC */ +/* must be kept in-sync with include/uapi/sound/asound.h */ +#define MCHP_ASRC_PCM_FORMAT_S8 0 +#define MCHP_ASRC_PCM_FORMAT_S16_LE 2 +#define MCHP_ASRC_PCM_FORMAT_S24_LE 6 +#define MCHP_ASRC_PCM_FORMAT_S32_LE 10 + +#endif /* __DT_BINDINGS_MICROCHIP_ASRC_CARD_H__ */ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/linux/clk/clk-regmap.h linux-microchip/include/linux/clk/clk-regmap.h --- linux-6.6.51/include/linux/clk/clk-regmap.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/include/linux/clk/clk-regmap.h 2024-11-26 14:02:38.894518704 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018 BayLibre, SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> + */ + +#ifndef __CLK_REGMAP_H +#define __CLK_REGMAP_H + +#include <linux/clk-provider.h> +#include <linux/regmap.h> + +/** + * struct clk_regmap - regmap backed clock + * + * @hw: handle between common and hardware-specific interfaces + * @map: pointer to the regmap structure controlling the clock + * @data: data specific to the clock type + * + * Clock which is controlled by regmap backed registers. The actual type of + * of the clock is controlled by the clock_ops and data. + */ +struct clk_regmap { + struct clk_hw hw; + struct regmap *map; + void *data; +}; + +static inline struct clk_regmap *to_clk_regmap(struct clk_hw *hw) +{ + return container_of(hw, struct clk_regmap, hw); +} + +/** + * struct clk_regmap_gate_data - regmap backed gate specific data + * + * @offset: offset of the register controlling gate + * @bit_idx: single bit controlling gate + * @flags: hardware-specific flags + * + * Flags: + * Same as clk_gate except CLK_GATE_HIWORD_MASK which is ignored + */ +struct clk_regmap_gate_data { + unsigned int offset; + u8 bit_idx; + u8 flags; +}; + +static inline struct clk_regmap_gate_data * +clk_get_regmap_gate_data(struct clk_regmap *clk) +{ + return (struct clk_regmap_gate_data *)clk->data; +} + +extern const struct clk_ops clk_regmap_gate_ops; +extern const struct clk_ops clk_regmap_gate_ro_ops; + +/** + * struct clk_regmap_div_data - regmap backed adjustable divider specific data + * + * @offset: offset of the register controlling the divider + * @shift: shift to the divider bit field + * @width: width of the divider bit field + * @table: array of value/divider pairs, last entry should have div = 0 + * + * Flags: + * Same as clk_divider except CLK_DIVIDER_HIWORD_MASK which is ignored + */ +struct clk_regmap_div_data { + unsigned int offset; + u8 shift; + u8 width; + u8 flags; + const struct clk_div_table *table; +}; + +static inline struct clk_regmap_div_data * +clk_get_regmap_div_data(struct clk_regmap *clk) +{ + return (struct clk_regmap_div_data *)clk->data; +} + +extern const struct clk_ops clk_regmap_divider_ops; +extern const struct clk_ops clk_regmap_divider_ro_ops; + +/** + * struct clk_regmap_mux_data - regmap backed multiplexer clock specific data + * + * @hw: handle between common and hardware-specific interfaces + * @offset: offset of theregister controlling multiplexer + * @table: array of parent indexed register values + * @shift: shift to multiplexer bit field + * @mask: mask of mutliplexer bit field + * @flags: hardware-specific flags + * + * Flags: + * Same as clk_divider except CLK_MUX_HIWORD_MASK which is ignored + */ +struct clk_regmap_mux_data { + unsigned int offset; + u32 *table; + u32 mask; + u8 shift; + u8 flags; +}; + +static inline struct clk_regmap_mux_data * +clk_get_regmap_mux_data(struct clk_regmap *clk) +{ + return (struct clk_regmap_mux_data *)clk->data; +} + +extern const struct clk_ops clk_regmap_mux_ops; +extern const struct clk_ops clk_regmap_mux_ro_ops; + +#define __MESON_PCLK(_name, _reg, _bit, _ops, _pname) \ +struct clk_regmap _name = { \ + .data = &(struct clk_regmap_gate_data){ \ + .offset = (_reg), \ + .bit_idx = (_bit), \ + }, \ + .hw.init = &(struct clk_init_data) { \ + .name = #_name, \ + .ops = _ops, \ + .parent_hws = (const struct clk_hw *[]) { _pname }, \ + .num_parents = 1, \ + .flags = (CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED), \ + }, \ +} + +#define MESON_PCLK(_name, _reg, _bit, _pname) \ + __MESON_PCLK(_name, _reg, _bit, &clk_regmap_gate_ops, _pname) + +#define MESON_PCLK_RO(_name, _reg, _bit, _pname) \ + __MESON_PCLK(_name, _reg, _bit, &clk_regmap_gate_ro_ops, _pname) +#endif /* __CLK_REGMAP_H */ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/linux/firmware.h linux-microchip/include/linux/firmware.h --- linux-6.6.51/include/linux/firmware.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/include/linux/firmware.h 2024-11-26 14:02:38.906518920 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:30 @ * @FW_UPLOAD_ERR_INVALID_SIZE: invalid firmware image size * @FW_UPLOAD_ERR_RW_ERROR: read or write to HW failed, see kernel log * @FW_UPLOAD_ERR_WEAROUT: FLASH device is approaching wear-out, wait & retry + * @FW_UPLOAD_ERR_FW_INVALID: invalid firmware file * @FW_UPLOAD_ERR_MAX: Maximum error code marker */ enum fw_upload_err { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:42 @ FW_UPLOAD_ERR_INVALID_SIZE, FW_UPLOAD_ERR_RW_ERROR, FW_UPLOAD_ERR_WEAROUT, + FW_UPLOAD_ERR_FW_INVALID, FW_UPLOAD_ERR_MAX }; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/linux/mailbox/mchp-ipc.h linux-microchip/include/linux/mailbox/mchp-ipc.h --- linux-6.6.51/include/linux/mailbox/mchp-ipc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/include/linux/mailbox/mchp-ipc.h 2024-11-26 14:02:38.922519205 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + *Copyright (c) 2024 Microchip Technology Inc. All rights reserved. + */ + +#ifndef _LINUX_MCHP_IPC_H_ +#define _LINUX_MCHP_IPC_H_ + +#include <linux/mailbox_controller.h> +#include <linux/types.h> + +struct mchp_ipc_msg { + u32 *buf; + u16 size; +}; + +#if IS_ENABLED(CONFIG_MCHP_SBI_IPC_MBOX) +u32 mchp_ipc_get_chan_id(struct mbox_chan *chan); +#else +u32 mchp_ipc_get_chan_id(struct mbox_chan *chan) { return 0; } +#endif + +#endif /* _LINUX_MCHP_IPC_H_ */ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/linux/mailbox/miv_ihc.h linux-microchip/include/linux/mailbox/miv_ihc.h --- linux-6.6.51/include/linux/mailbox/miv_ihc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/include/linux/mailbox/miv_ihc.h 2024-11-26 14:02:38.922519205 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + *Copyright (c) 2021 Microchip Technology Inc. All rights reserved. + */ + +#ifndef _LINUX_MIV_IHC_H_ +#define _LINUX_MIV_IHC_H_ + +#include <linux/mailbox_controller.h> +#include <linux/types.h> + +/* Data structure for data-transfer protocol */ +#define IHC_MAX_MESSAGE_SIZE 4U + +enum { + SBI_EXT_IHC_INIT, + SBI_EXT_IHC_TX, + SBI_EXT_IHC_RX, + SBI_EXT_RPROC_STATE, + SBI_EXT_RPROC_START, + SBI_EXT_RPROC_STOP, +}; + +struct miv_ihc_msg { + u32 msg[IHC_MAX_MESSAGE_SIZE]; +}; + +struct miv_ihc { + struct device *dev; + void *buf_base; + struct dma_pool *pool; + struct mbox_controller controller; + struct mbox_chan channel; + int irq; + dma_addr_t dma_addr; + u32 remote_context_id; +}; + +#endif /* _LINUX_MIV_IHC_H_ */ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/linux/mfd/atmel-hlcdc.h linux-microchip/include/linux/mfd/atmel-hlcdc.h --- linux-6.6.51/include/linux/mfd/atmel-hlcdc.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/include/linux/mfd/atmel-hlcdc.h 2024-11-26 14:02:38.926519278 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:25 @ #define ATMEL_HLCDC_DITHER BIT(6) #define ATMEL_HLCDC_DISPDLY BIT(7) #define ATMEL_HLCDC_MODE_MASK GENMASK(9, 8) +#define ATMEL_XLCDC_MODE_MASK GENMASK(10, 8) +#define ATMEL_XLCDC_DPI BIT(11) #define ATMEL_HLCDC_PP BIT(10) #define ATMEL_HLCDC_VSPSU BIT(12) #define ATMEL_HLCDC_VSPHO BIT(13) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:39 @ #define ATMEL_HLCDC_IDR 0x30 #define ATMEL_HLCDC_IMR 0x34 #define ATMEL_HLCDC_ISR 0x38 +#define ATMEL_XLCDC_ATTRE 0x3c + +#define ATMEL_XLCDC_BASE_UPDATE BIT(0) +#define ATMEL_XLCDC_OVR1_UPDATE BIT(1) +#define ATMEL_XLCDC_OVR3_UPDATE BIT(2) +#define ATMEL_XLCDC_HEO_UPDATE BIT(3) #define ATMEL_HLCDC_CLKPOL BIT(0) +#define ATMEL_XLCDC_CLKBYP BIT(1) #define ATMEL_HLCDC_CLKSEL BIT(2) #define ATMEL_HLCDC_CLKPWMSEL BIT(3) #define ATMEL_HLCDC_CGDIS(i) BIT(8 + (i)) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:60 @ #define ATMEL_HLCDC_DISP BIT(2) #define ATMEL_HLCDC_PWM BIT(3) #define ATMEL_HLCDC_SIP BIT(4) +#define ATMEL_XLCDC_SD BIT(5) +#define ATMEL_XLCDC_CM BIT(6) #define ATMEL_HLCDC_SOF BIT(0) #define ATMEL_HLCDC_SYNCDIS BIT(1) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:79 @ */ struct atmel_hlcdc { struct regmap *regmap; + struct clk *lvds_pll_clk; struct clk *periph_clk; struct clk *sys_clk; struct clk *slow_clk; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/linux/mmc/card.h linux-microchip/include/linux/mmc/card.h --- linux-6.6.51/include/linux/mmc/card.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/include/linux/mmc/card.h 2024-11-26 14:02:38.938519493 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:35 @ unsigned int r2w_factor; unsigned int max_dtr; unsigned int erase_size; /* In sectors */ + unsigned int wp_grp_size; unsigned int read_blkbits; unsigned int write_blkbits; unsigned int capacity; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:56 @ u8 part_config; u8 cache_ctrl; u8 rst_n_function; - u8 max_packed_writes; - u8 max_packed_reads; - u8 packed_event_en; unsigned int part_time; /* Units: ms */ unsigned int sa_timeout; /* Units: 100ns */ unsigned int generic_cmd6_time; /* Units: 10ms */ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:297 @ #define MMC_QUIRK_BROKEN_SD_DISCARD (1<<14) /* Disable broken SD discard support */ #define MMC_QUIRK_BROKEN_SD_CACHE (1<<15) /* Disable broken SD cache support */ #define MMC_QUIRK_BROKEN_CACHE_FLUSH (1<<16) /* Don't flush cache until the write has occurred */ +#define MMC_QUIRK_HS_CLK_REVERSED (1<<17) /* Reversed high-speed/clock init order */ bool written_flag; /* Indicates eMMC has been written since power on */ bool reenable_cmdq; /* Re-enable Command Queue */ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:308 @ unsigned int eg_boundary; /* don't cross erase-group boundaries */ unsigned int erase_arg; /* erase / trim / discard */ u8 erased_byte; /* value of erased bytes */ + unsigned int wp_grp_size; /* write group size in sectors */ u32 raw_cid[4]; /* raw card CID */ u32 raw_csd[4]; /* raw card CSD */ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/linux/mmc/core.h linux-microchip/include/linux/mmc/core.h --- linux-6.6.51/include/linux/mmc/core.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/include/linux/mmc/core.h 2024-11-26 14:02:38.938519493 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:30 @ u32 opcode; u32 arg; #define MMC_CMD23_ARG_REL_WR (1 << 31) -#define MMC_CMD23_ARG_PACKED ((0 << 31) | (1 << 30)) #define MMC_CMD23_ARG_TAG_REQ (1 << 29) u32 resp[4]; unsigned int flags; /* expected response type */ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/linux/mmc/host.h linux-microchip/include/linux/mmc/host.h --- linux-6.6.51/include/linux/mmc/host.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/include/linux/mmc/host.h 2024-11-26 14:02:38.938519493 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:529 @ /* Host Software Queue support */ bool hsq_enabled; + int hsq_depth; u32 err_stats[MMC_ERR_MAX]; unsigned long private[] ____cacheline_aligned; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/linux/mmc/mmc.h linux-microchip/include/linux/mmc/mmc.h --- linux-6.6.51/include/linux/mmc/mmc.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/include/linux/mmc/mmc.h 2024-11-26 14:02:38.938519493 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:260 @ #define EXT_CSD_FLUSH_CACHE 32 /* W */ #define EXT_CSD_CACHE_CTRL 33 /* R/W */ #define EXT_CSD_POWER_OFF_NOTIFICATION 34 /* R/W */ -#define EXT_CSD_PACKED_FAILURE_INDEX 35 /* RO */ -#define EXT_CSD_PACKED_CMD_STATUS 36 /* RO */ #define EXT_CSD_EXP_EVENTS_STATUS 54 /* RO, 2 bytes */ #define EXT_CSD_EXP_EVENTS_CTRL 56 /* R/W, 2 bytes */ #define EXT_CSD_DATA_SECTOR_SIZE 61 /* R */ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:322 @ #define EXT_CSD_SUPPORTED_MODE 493 /* RO */ #define EXT_CSD_TAG_UNIT_SIZE 498 /* RO */ #define EXT_CSD_DATA_TAG_SUPPORT 499 /* RO */ -#define EXT_CSD_MAX_PACKED_WRITES 500 /* RO */ -#define EXT_CSD_MAX_PACKED_READS 501 /* RO */ #define EXT_CSD_BKOPS_SUPPORT 502 /* RO */ #define EXT_CSD_HPI_FEATURES 503 /* RO */ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:401 @ #define EXT_CSD_PWR_CL_8BIT_SHIFT 4 #define EXT_CSD_PWR_CL_4BIT_SHIFT 0 -#define EXT_CSD_PACKED_EVENT_EN BIT(3) - /* * EXCEPTION_EVENT_STATUS field */ #define EXT_CSD_URGENT_BKOPS BIT(0) #define EXT_CSD_DYNCAP_NEEDED BIT(1) #define EXT_CSD_SYSPOOL_EXHAUSTED BIT(2) -#define EXT_CSD_PACKED_FAILURE BIT(3) - -#define EXT_CSD_PACKED_GENERIC_ERROR BIT(0) -#define EXT_CSD_PACKED_INDEXED_ERROR BIT(1) /* * BKOPS status level diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/linux/mtd/spi-nor.h linux-microchip/include/linux/mtd/spi-nor.h --- linux-6.6.51/include/linux/mtd/spi-nor.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/include/linux/mtd/spi-nor.h 2024-11-26 14:02:38.938519493 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:150 @ SNOR_PROTO_DATA_MASK) #define SNOR_PROTO_IS_DTR BIT(24) /* Double Transfer Rate */ +/* + * Byte order of 16-bit words is swapped when read or written in DTR mode + * compared to STR mode. + */ +#define SNOR_PROTO_IS_DTR_BSWAP16 BIT(25) #define SNOR_PROTO_STR(_inst_nbits, _addr_nbits, _data_nbits) \ (SNOR_PROTO_INST(_inst_nbits) | \ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:188 @ return !!(proto & SNOR_PROTO_IS_DTR); } +static inline bool spi_nor_protocol_is_octal_dtr(enum spi_nor_protocol proto) +{ + return ((proto & SNOR_PROTO_8_8_8_DTR) == SNOR_PROTO_8_8_8_DTR); +} + +static inline bool spi_nor_protocol_is_dtr_bswap16(enum spi_nor_protocol proto) +{ + u32 mask = SNOR_PROTO_IS_DTR | SNOR_PROTO_IS_DTR_BSWAP16; + + return ((proto & mask) == mask); +} + static inline u8 spi_nor_get_protocol_inst_nbits(enum spi_nor_protocol proto) { return ((unsigned long)(proto & SNOR_PROTO_INST_MASK)) >> diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/linux/phylink.h linux-microchip/include/linux/phylink.h --- linux-6.6.51/include/linux/phylink.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/include/linux/phylink.h 2024-11-26 14:02:38.950519707 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:692 @ int phylink_mii_ioctl(struct phylink *, struct ifreq *, int); int phylink_speed_down(struct phylink *pl, bool sync); int phylink_speed_up(struct phylink *pl); +int phylink_init_phydev(struct phylink *pl); #define phylink_zero(bm) \ bitmap_zero(bm, __ETHTOOL_LINK_MODE_MASK_NBITS) diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/linux/platform_data/atmel.h linux-microchip/include/linux/platform_data/atmel.h --- linux-6.6.51/include/linux/platform_data/atmel.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/include/linux/platform_data/atmel.h 2024-11-26 14:02:38.950519707 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:10 @ #define __ATMEL_H__ /* FIXME: this needs a better location, but gets stuff building again */ -#ifdef CONFIG_ATMEL_PM +#if defined(CONFIG_ATMEL_PM) || defined(CONFIG_ATMEL_SECURE_PM) extern int at91_suspend_entering_slow_clock(void); #else static inline int at91_suspend_entering_slow_clock(void) diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/linux/regmap.h linux-microchip/include/linux/regmap.h --- linux-6.6.51/include/linux/regmap.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/include/linux/regmap.h 2024-11-26 14:02:38.958519850 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:649 @ const struct regmap_config *config, struct lock_class_key *lock_key, const char *lock_name); + struct regmap *__regmap_init_fsi(struct fsi_device *fsi_dev, const struct regmap_config *config, struct lock_class_key *lock_key, const char *lock_name); +struct regmap *__regmap_init_smccc(struct device *dev, u32 regmap_smc_id, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name); + struct regmap *__devm_regmap_init(struct device *dev, const struct regmap_bus *bus, void *bus_context, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:724 @ const struct regmap_config *config, struct lock_class_key *lock_key, const char *lock_name); + struct regmap *__devm_regmap_init_fsi(struct fsi_device *fsi_dev, const struct regmap_config *config, struct lock_class_key *lock_key, const char *lock_name); +struct regmap *__devm_regmap_init_smccc(struct device *dev, u32 regmap_smc_id, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name); /* * Wrapper for regmap_init macros to include a unique lockdep key and name * for each call. No-op if CONFIG_LOCKDEP is not set. @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:973 @ __regmap_lockdep_wrapper(__regmap_init_fsi, #config, fsi_dev, \ config) +/* + * regmap_init_smccc() - Initialize register map for ARM SMCCC + * + * @dev: Device that will be interacted with + * @smc_id: SMC id to used for calls + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. + */ +#define regmap_init_smccc(dev, smc_id, config) \ + __regmap_lockdep_wrapper(__regmap_init_smccc, #config, \ + dev, smc_id, config) + /** * devm_regmap_init() - Initialise managed register map * @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1230 @ __regmap_lockdep_wrapper(__devm_regmap_init_fsi, #config, \ fsi_dev, config) +/* + * devm_regmap_init_smccc() - Initialize register map for ARM SMCCC + * + * @dev: Device that will be interacted with + * @smc_id: SMC id to used for calls + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. The map will be automatically freed by the + * device management code. + */ +#define devm_regmap_init_smccc(dev, smc_id, config) \ + __regmap_lockdep_wrapper(__devm_regmap_init_smccc, #config, \ + dev, smc_id, config) + int regmap_mmio_attach_clk(struct regmap *map, struct clk *clk); void regmap_mmio_detach_clk(struct regmap *map); void regmap_exit(struct regmap *map); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/linux/spi/spi-mem.h linux-microchip/include/linux/spi/spi-mem.h --- linux-6.6.51/include/linux/spi/spi-mem.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/include/linux/spi/spi-mem.h 2024-11-26 14:02:38.970520065 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:93 @ * @data.buswidth: number of IO lanes used to send/receive the data * @data.dtr: whether the data should be sent in DTR mode or not * @data.ecc: whether error correction is required or not + * @data.dtr_bswap16: whether the byte order of 16-bit words is swapped when + * read or written in DTR mode compared to STR mode. * @data.dir: direction of the transfer * @data.nbytes: number of data bytes to send/receive. Can be zero if the * operation does not involve transferring data @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:130 @ u8 dtr : 1; u8 ecc : 1; u8 __pad : 6; + u8 dtr_bswap16 : 1; enum spi_mem_data_dir dir; unsigned int nbytes; union { diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/linux/tty.h linux-microchip/include/linux/tty.h --- linux-6.6.51/include/linux/tty.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/include/linux/tty.h 2024-11-26 14:02:38.974520138 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:100 @ #define C_CIBAUD(tty) _C_FLAG((tty), CIBAUD) #define C_CRTSCTS(tty) _C_FLAG((tty), CRTSCTS) #define C_CMSPAR(tty) _C_FLAG((tty), CMSPAR) +#define C_PARMD(tty) _C_FLAG((tty), PARMD) +#define C_SENDA(tty) _C_FLAG((tty), SENDA) #define L_ISIG(tty) _L_FLAG((tty), ISIG) #define L_ICANON(tty) _L_FLAG((tty), ICANON) diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/media/dwc/dw-csi-data.h linux-microchip/include/media/dwc/dw-csi-data.h --- linux-6.6.51/include/media/dwc/dw-csi-data.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/include/media/dwc/dw-csi-data.h 2024-11-26 14:02:38.982520281 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates. + * + * Synopsys DesignWare MIPI CSI-2 platform data + * + * Author: Luis Oliveira <Luis.Oliveira@synopsys.com> + */ + +#include <linux/kernel.h> +#include <media/dwc/dw-mipi-csi-pltfrm.h> + +struct dw_csih_pdata { + u8 eotp_enabled; + u32 hs_freq; + u32 lanes; + u32 pclk; + u32 fps; + u32 bpp; + u8 id; +}; + +static const struct pdata_names csis[] = { + { .name = "dw-csi.0", }, + { .name = "dw-csi.1", }, +}; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/media/dwc/dw-dphy-data.h linux-microchip/include/media/dwc/dw-dphy-data.h --- linux-6.6.51/include/media/dwc/dw-dphy-data.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/include/media/dwc/dw-dphy-data.h 2024-11-26 14:02:38.982520281 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates. + * + * Synopsys DesignWare MIPI D-PHY platform data + * + * Author: Luis Oliveira <Luis.Oliveira@synopsys.com> + */ + +#include <linux/phy/phy.h> +#include <linux/kernel.h> +#include <media/dwc/dw-mipi-csi-pltfrm.h> + +struct dw_phy_pdata { + u32 dphy_frequency; + u8 dphy_te_len; + u32 config_8l; + u8 dphy_gen; + u8 phy_type; + u8 id; +}; + +static const struct pdata_names phys[] = { + { .name = "phy-dw-dphy.0.0", }, + { .name = "phy-dw-dphy.1.1", }, +}; + +struct dw_dphy_rx; + +struct plat_dw_dphy { + int (*get_resources)(struct device *dev, struct dw_dphy_rx *dphy); +}; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/media/dwc/dw-mipi-csi-pltfrm.h linux-microchip/include/media/dwc/dw-mipi-csi-pltfrm.h --- linux-6.6.51/include/media/dwc/dw-mipi-csi-pltfrm.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/include/media/dwc/dw-mipi-csi-pltfrm.h 2024-11-26 14:02:38.982520281 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018-2019 Synopsys, Inc. and/or its affiliates. + * + * Synopsys DesignWare MIPI CSI-2 Host media entities + * + * Author: Luis Oliveira <Luis.Oliveira@synopsys.com> + */ + +#ifndef __DW_MIPI_CSI_PLTFRM_INCLUDES_H_ +#define __DW_MIPI_CSI_PLTFRM_INCLUDES_H_ + +#include <media/media-entity.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-mediabus.h> +#include <media/v4l2-subdev.h> + +#define MAX_WIDTH 3280 +#define MAX_HEIGHT 1852 + +/* The subdevices' group IDs. */ +#define GRP_ID_SENSOR (10) +#define GRP_ID_CSI (20) +#define GRP_ID_VIF (30) +#define GRP_ID_VIDEODEV (40) + +#define CSI_MAX_ENTITIES (2) +#define VIF_MAX_ENTITIES (2) +#define PLAT_MAX_SENSORS (2) + +struct pdata_names { + char *name; +}; + +enum video_dev_pads { + VIDEO_DEV_SD_PAD_SINK_VIF1, + VIDEO_DEV_SD_PAD_SINK_VIF2, + VIDEO_DEV_SD_PAD_SOURCE_DMA, + VIDEO_DEV_SD_PADS_NUM, +}; + +enum vif_pads { + VIF_PAD_SINK_CSI, + VIF_PAD_SOURCE_DMA, + VIF_PADS_NUM, +}; + +enum mipi_csi_pads { + CSI_PAD_SINK, + CSI_PAD_SOURCE, + CSI_PADS_NUM, +}; + +struct plat_csi_source_info { + u16 flags; + u16 mux_id; +}; + +struct plat_csi_fmt { + char *name; + u32 mbus_code; + u32 fourcc; + u8 depth; +}; + +struct plat_csi_media_pipeline; + +/* + * Media pipeline operations to be called from within a video node, i.e. the + * last entity within the pipeline. Implemented by related media device driver. + */ +struct plat_csi_media_pipeline_ops { + int (*prepare)(struct plat_csi_media_pipeline *p, + struct media_entity *me); + int (*unprepare)(struct plat_csi_media_pipeline *p); + int (*open)(struct plat_csi_media_pipeline *p, struct media_entity *me, + bool resume); + int (*close)(struct plat_csi_media_pipeline *p); + int (*set_stream)(struct plat_csi_media_pipeline *p, bool state); + int (*set_format)(struct plat_csi_media_pipeline *p, + struct v4l2_subdev_format *fmt); +}; + +struct plat_csi_video_entity { + struct video_device vdev; + struct plat_csi_media_pipeline *pipe; +}; + +struct plat_csi_media_pipeline { + struct media_pipeline mp; + const struct plat_csi_media_pipeline_ops *ops; +}; + +static inline struct plat_csi_video_entity +*vdev_to_plat_csi_video_entity(struct video_device *vdev) +{ + return container_of(vdev, struct plat_csi_video_entity, vdev); +} + +#define plat_csi_pipeline_call(ent, op, args...) \ + (!(ent) ? -ENOENT : (((ent)->pipe->ops && (ent)->pipe->ops->op) ? \ + (ent)->pipe->ops->op(((ent)->pipe), ##args) : -ENOIOCTLCMD)) \ + +#endif /* __DW_MIPI_CSI_PLTFRM_INCLUDES_H_ */ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/soc/at91/sama7-sfrbu.h linux-microchip/include/soc/at91/sama7-sfrbu.h --- linux-6.6.51/include/soc/at91/sama7-sfrbu.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/include/soc/at91/sama7-sfrbu.h 2024-11-26 14:02:39.006520710 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:21 @ #define AT91_SFRBU_PSWBU_SOFTSWITCH (1 << 1) /* Power switch BU source selection */ #define AT91_SFRBU_PSWBU_CTRL (1 << 0) /* Power switch BU control */ -#define AT91_SFRBU_25LDOCR (0x0C) /* SFRBU 2.5V LDO Control Register */ -#define AT91_SFRBU_25LDOCR_LDOANAKEY (0x3B6E18 << 8) /* Specific value mandatory to allow writing of other register bits. */ -#define AT91_SFRBU_25LDOCR_STATE (1 << 3) /* LDOANA Switch On/Off Control */ -#define AT91_SFRBU_25LDOCR_LP (1 << 2) /* LDOANA Low-Power Mode Control */ -#define AT91_SFRBU_PD_VALUE_MSK (0x3) -#define AT91_SFRBU_25LDOCR_PD_VALUE(v) ((v) & AT91_SFRBU_PD_VALUE_MSK) /* LDOANA Pull-down value */ - #define AT91_FRBU_DDRPWR (0x10) /* SFRBU DDR Power Control Register */ #define AT91_FRBU_DDRPWR_STATE (1 << 0) /* DDR Power Mode State */ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/soc/at91/sama7-sfr.h linux-microchip/include/soc/at91/sama7-sfr.h --- linux-6.6.51/include/soc/at91/sama7-sfr.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/include/soc/at91/sama7-sfr.h 2024-11-26 14:02:39.006520710 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Microchip SFR (Special Function Registers) registers for SAMA7 family. + * + * Copyright (C) 2023 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 */ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/soc/microchip/mpfs.h linux-microchip/include/soc/microchip/mpfs.h --- linux-6.6.51/include/soc/microchip/mpfs.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/include/soc/microchip/mpfs.h 2024-11-26 14:02:39.006520710 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:41 @ struct mpfs_sys_controller *mpfs_sys_controller_get(struct device *dev); +struct mtd_info *mpfs_sys_controller_get_flash(struct mpfs_sys_controller *mpfs_client); + #endif /* if IS_ENABLED(CONFIG_POLARFIRE_SOC_SYS_CTRL) */ #if IS_ENABLED(CONFIG_MCHP_CLK_MPFS) - -u32 mpfs_reset_read(struct device *dev); - -void mpfs_reset_write(struct device *dev, u32 val); - +#if IS_ENABLED(CONFIG_RESET_CONTROLLER) +int mpfs_reset_controller_register(struct device *clk_dev, void __iomem *base); +#else +static inline int mpfs_reset_controller_register(struct device *clk_dev, void __iomem *base) { return 0; } +#endif /* if IS_ENABLED(CONFIG_RESET_CONTROLLER) */ #endif /* if IS_ENABLED(CONFIG_MCHP_CLK_MPFS) */ #endif /* __SOC_MPFS_H__ */ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/sound/pcm.h linux-microchip/include/sound/pcm.h --- linux-6.6.51/include/sound/pcm.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/include/sound/pcm.h 2024-11-26 14:02:39.010520783 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:582 @ } #endif int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg); +struct snd_pcm_runtime *snd_pcm_runtime_alloc(void); +void snd_pcm_runtime_free(struct snd_pcm_runtime *runtime); +void snd_pcm_runtime_set(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params); int snd_pcm_open_substream(struct snd_pcm *pcm, int stream, struct file *file, struct snd_pcm_substream **rsubstream); void snd_pcm_release_substream(struct snd_pcm_substream *substream); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1094 @ snd_pcm_hw_rule_func_t func, void *private, int dep, ...); +int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream); +int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream); + /** * snd_pcm_hw_constraint_single() - Constrain parameter to a single value * @runtime: PCM runtime instance diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/sound/soc.h linux-microchip/include/sound/soc.h --- linux-6.6.51/include/sound/soc.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/include/sound/soc.h 2024-11-26 14:02:39.014520854 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:754 @ /* This DAI link can route to other DAI links at runtime (Frontend)*/ unsigned int dynamic:1; + unsigned int dpcm_loopback:1; /* DPCM capture and Playback support */ unsigned int dpcm_capture:1; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/uapi/asm-generic/termbits.h linux-microchip/include/uapi/asm-generic/termbits.h --- linux-6.6.51/include/uapi/asm-generic/termbits.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/include/uapi/asm-generic/termbits.h 2024-11-26 14:02:39.022520997 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:108 @ #define HUPCL 0x00000400 #define CLOCAL 0x00000800 #define CBAUDEX 0x00001000 +#define PARMD 040000000 +#define SENDA 0100000000 #define BOTHER 0x00001000 #define B57600 0x00001001 #define B115200 0x00001002 diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/uapi/linux/media-bus-format.h linux-microchip/include/uapi/linux/media-bus-format.h --- linux-6.6.51/include/uapi/linux/media-bus-format.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/include/uapi/linux/media-bus-format.h 2024-11-26 14:02:39.034521212 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:160 @ /* JPEG compressed formats - next is 0x4002 */ #define MEDIA_BUS_FMT_JPEG_1X8 0x4001 -/* Vendor specific formats - next is 0x5002 */ +/* Vendor specific formats - next is 0x5003 */ /* S5C73M3 sensor specific interleaved UYVY and JPEG */ #define MEDIA_BUS_FMT_S5C_UYVY_JPEG_1X8 0x5001 +#define MEDIA_BUS_FMT_H264_1X8 0x5002 + /* HSV - next is 0x6002 */ #define MEDIA_BUS_FMT_AHSV8888_1X32 0x6001 diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/uapi/linux/v4l2-mediabus.h linux-microchip/include/uapi/linux/v4l2-mediabus.h --- linux-6.6.51/include/uapi/linux/v4l2-mediabus.h 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/include/uapi/linux/v4l2-mediabus.h 2024-11-26 14:02:39.050521499 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:144 @ V4L2_MBUS_FROM_MEDIA_BUS_FMT(S5C_UYVY_JPEG_1X8), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(H264_1X8), + V4L2_MBUS_FROM_MEDIA_BUS_FMT(AHSV8888_1X32), }; #endif /* __KERNEL__ */ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/include/uapi/misc/mpfs-dma-proxy.h linux-microchip/include/uapi/misc/mpfs-dma-proxy.h --- linux-6.6.51/include/uapi/misc/mpfs-dma-proxy.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/include/uapi/misc/mpfs-dma-proxy.h 2024-11-26 14:02:39.050521499 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * Microchip PolarFire SoC DMA proxy userspace header + * + * Copyright (C) 2022 Microchip Technology Inc. and its subsidiaries + * + * Author: Shravan Chippa <shravan.chippa@microchip.com> + */ + +#ifndef _MPFS_DMA_PROXY_H +#define _MPFS_DMA_PROXY_H + +#include <linux/types.h> + +#define MPFS_DMA_PROXY_IOC_MAGIC 'u' + +#define MPFS_DMA_PROXY_FINISH_XFER _IOW(MPFS_DMA_PROXY_IOC_MAGIC, \ + '1', enum mpfs_dma_proxy_status*) +#define MPFS_DMA_PROXY_START_XFER _IOW(MPFS_DMA_PROXY_IOC_MAGIC, \ + '2', struct mpfs_dma_proxy_channel_config*) +#define MPFS_DMA_PROXY_IOC_MAXNR 2 + +/** + * struct mpfs_dma_proxy_channel_config - dma config info + * @src: source dma phy address + * @dst: destination dma phy address + * @length: size of xfer + */ +struct mpfs_dma_proxy_channel_config { + __u64 src; + __u64 dst; + __kernel_size_t length; +}; + +enum mpfs_dma_proxy_status { + PROXY_SUCCESS = 0, + PROXY_BUSY, + PROXY_TIMEOUT, + PROXY_ERROR, +}; + +#endif /* _MPFS_DMA_PROXY_H */ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/lib/test_firmware.c linux-microchip/lib/test_firmware.c --- linux-6.6.51/lib/test_firmware.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/lib/test_firmware.c 2024-11-26 14:02:39.126522860 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1135 @ [FW_UPLOAD_ERR_INVALID_SIZE] = "invalid-file-size", [FW_UPLOAD_ERR_RW_ERROR] = "read-write-error", [FW_UPLOAD_ERR_WEAROUT] = "flash-wearout", + [FW_UPLOAD_ERR_FW_INVALID] = "firmware-invalid", }; static void upload_err_inject_error(struct test_firmware_upload *tst, diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/MAINTAINERS linux-microchip/MAINTAINERS --- linux-6.6.51/MAINTAINERS 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/MAINTAINERS 2024-11-26 14:04:18.560303308 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:3288 @ F: Documentation/devicetree/bindings/input/atmel,maxtouch.yaml F: drivers/input/touchscreen/atmel_mxt_ts.c +ATMEL PTC SUBSYSTEM DRIVER +M: Ludovic Desroches <ludovic.desroches@microchip.com> +L: linux-input@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/input/atmel,ptc.txt +F: drivers/input/misc/atmel_ptc.c + ATMEL WIRELESS DRIVER L: linux-wireless@vger.kernel.org S: Orphan @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:14037 @ F: Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt F: drivers/regulator/mcp16502.c +MICROCHIP MCP3564 ADC DRIVER +M: Marius Cristea <marius.cristea@microchip.com> +L: linux-iio@vger.kernel.org +S: Supported +F: Documentation/ABI/testing/sysfs-bus-iio-adc-mcp3564 +F: Documentation/devicetree/bindings/iio/adc/microchip,mcp3564.yaml +F: drivers/iio/adc/mcp3564.c + MICROCHIP MCP3911 ADC DRIVER M: Marcus Folkesson <marcus.folkesson@gmail.com> M: Kent Gustavsson <kent@minoris.se> @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:14073 @ F: drivers/nvmem/microchip-otpc.c F: include/dt-bindings/nvmem/microchip,sama7g5-otpc.h +MICROCHIP PAC1934 POWER/ENERGY MONITOR DRIVER +M: Marius Cristea <marius.cristea@microchip.com> +L: linux-iio@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/iio/adc/microchip,pac1934.yaml +F: drivers/iio/adc/pac1934.c + MICROCHIP PCI1XXXX GP DRIVER M: Vaibhaav Ram T.L <vaibhaavram.tl@microchip.com> M: Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com> @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:14141 @ T: git https://git.kernel.org/pub/scm/linux/kernel/git/conor/linux.git/ F: drivers/soc/microchip/ +MICROCHIP SAM9x7-COMPATIBLE LVDS CONTROLLER +M: Manikandan Muralidharan <manikandan.m@microchip.com> +M: Dharma Balasubiramani <dharma.b@microchip.com> +L: dri-devel@lists.freedesktop.org +S: Supported +F: Documentation/devicetree/bindings/display/bridge/microchip,sam9x7-lvds.yaml +F: drivers/gpu/drm/bridge/microchip-lvds.c + +DRM DRIVER FOR MICROCHIP SAM9X7-COMPATIBLE MIPI DSI HOST CONTROLLER +M: Manikandan Muralidharan <manikandan.m@microchip.com> +S: Supported +T: git git://anongit.freedesktop.org/drm/drm-misc +F: Documentation/devicetree/bindings/display/bridge/microchip,sam9x75-mipi-dsi.yaml +F: drivers/gpu/drm/bridge/dw-mipi-dsi-mchp.c + MICROCHIP SPI DRIVER M: Ryan Wanner <ryan.wanner@microchip.com> S: Supported @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:19385 @ F: Documentation/devicetree/bindings/iio/chemical/senseair,sunrise.yaml F: drivers/iio/chemical/sunrise_co2.c +SENSEHAT DRIVER +M: Charles Mirabile <cmirabil@redhat.com> +M: Joel Savitz <jsavitz@redhat.com> +S: Maintained +F: Documentation/devicetree/bindings/auxdisplay/raspberrypi,sensehat-display.yaml +F: Documentation/devicetree/bindings/input/raspberrypi,sensehat-joystick.yaml +F: Documentation/devicetree/bindings/mfd/raspberrypi,sensehat.yaml +F: drivers/auxdisplay/sensehat-display.c +F: drivers/input/joystick/sensehat-joystick.c + SENSIRION SCD30 CARBON DIOXIDE SENSOR DRIVER M: Tomasz Duszynski <tomasz.duszynski@octakon.com> S: Maintained @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:19635 @ N: sifive K: [^@]sifive +SIFIVE CACHE DRIVER +M: Conor Dooley <conor@kernel.org> +L: linux-riscv@lists.infradead.org +S: Maintained +F: Documentation/devicetree/bindings/cache/sifive,ccache0.yaml +F: drivers/cache/sifive_ccache.c + SIFIVE FU540 SYSTEM-ON-CHIP M: Paul Walmsley <paul.walmsley@sifive.com> M: Palmer Dabbelt <palmer@dabbelt.com> @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:19657 @ F: Documentation/devicetree/bindings/dma/sifive,fu540-c000-pdma.yaml F: drivers/dma/sf-pdma/ -SIFIVE SOC DRIVERS -M: Conor Dooley <conor@kernel.org> -L: linux-riscv@lists.infradead.org -S: Maintained -T: git https://git.kernel.org/pub/scm/linux/kernel/git/conor/linux.git/ -F: Documentation/devicetree/bindings/cache/sifive,ccache0.yaml -F: drivers/soc/sifive/ SILEAD TOUCHSCREEN DRIVER M: Hans de Goede <hdegoede@redhat.com> @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:20888 @ F: Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml F: drivers/dma/dw-axi-dmac/ +SYNOPSYS DESIGNWARE MIPI DPHY CSI-2 HOST DRIVER +M: Luis Oliveira <luis.oliveira@synopsys.com> +L: linux-media@vger.kernel.org +S: Maintained +T: git git://linuxtv.org/media_tree.git +F: drivers/media/platform/dwc +F: Documentation/devicetree/bindings/media/snps,dw-csi.yaml +F: Documentation/devicetree/bindings/phy/snps,dw-dphy-rx.txt +F: include/media/dwc/dw-csi-data.h +F: include/media/dwc/dw-dphy-data.h + SYNOPSYS DESIGNWARE DMAC DRIVER M: Viresh Kumar <vireshk@kernel.org> R: Andy Shevchenko <andriy.shevchenko@linux.intel.com> diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/Makefile linux-microchip/Makefile --- linux-6.6.51/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/Makefile 2024-11-26 14:04:18.560303308 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:5 @ VERSION = 6 PATCHLEVEL = 6 SUBLEVEL = 51 -EXTRAVERSION = +EXTRAVERSION = -linux4microchip+fpga-2024.09 NAME = Hurr durr I'ma ninja sloth # *DOCUMENTATION* diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/sound/core/pcm.c linux-microchip/sound/core/pcm.c --- linux-6.6.51/sound/core/pcm.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/sound/core/pcm.c 2024-11-26 14:02:39.318526300 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:887 @ return snd_pcm_free(pcm); } +struct snd_pcm_runtime *snd_pcm_runtime_alloc(void) +{ + struct snd_pcm_runtime *runtime; + size_t size; + + runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); + if (!runtime) + return NULL; + + size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)); + runtime->status = alloc_pages_exact(size, GFP_KERNEL); + if (!runtime->status) { + kfree(runtime); + return NULL; + } + memset(runtime->status, 0, size); + + size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)); + runtime->control = alloc_pages_exact(size, GFP_KERNEL); + if (!runtime->control) { + free_pages_exact(runtime->status, + PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); + kfree(runtime); + return NULL; + } + memset(runtime->control, 0, size); + + init_waitqueue_head(&runtime->sleep); + init_waitqueue_head(&runtime->tsleep); + + __snd_pcm_set_state(runtime, SNDRV_PCM_STATE_OPEN); + mutex_init(&runtime->buffer_mutex); + atomic_set(&runtime->buffer_accessing, 0); + + return runtime; +} +EXPORT_SYMBOL_GPL(snd_pcm_runtime_alloc); + +void snd_pcm_runtime_free(struct snd_pcm_runtime *runtime) +{ + free_pages_exact(runtime->status, + PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); + free_pages_exact(runtime->control, + PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))); + kfree(runtime->hw_constraints.rules); + mutex_destroy(&runtime->buffer_mutex); + snd_fasync_free(runtime->fasync); + kfree(runtime); +} +EXPORT_SYMBOL_GPL(snd_pcm_runtime_free); + int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, struct file *file, struct snd_pcm_substream **rsubstream) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:947 @ struct snd_pcm_runtime *runtime; struct snd_card *card; int prefer_subdevice; - size_t size; if (snd_BUG_ON(!pcm || !rsubstream)) return -ENXIO; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1000 @ if (substream == NULL) return -EAGAIN; - runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); - if (runtime == NULL) + runtime = snd_pcm_runtime_alloc(); + if (!runtime) return -ENOMEM; - size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)); - runtime->status = alloc_pages_exact(size, GFP_KERNEL); - if (runtime->status == NULL) { - kfree(runtime); - return -ENOMEM; - } - memset(runtime->status, 0, size); - - size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)); - runtime->control = alloc_pages_exact(size, GFP_KERNEL); - if (runtime->control == NULL) { - free_pages_exact(runtime->status, - PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); - kfree(runtime); - return -ENOMEM; - } - memset(runtime->control, 0, size); - - init_waitqueue_head(&runtime->sleep); - init_waitqueue_head(&runtime->tsleep); - - __snd_pcm_set_state(runtime, SNDRV_PCM_STATE_OPEN); - mutex_init(&runtime->buffer_mutex); - atomic_set(&runtime->buffer_accessing, 0); - substream->runtime = runtime; substream->private_data = pcm->private_data; substream->ref_count = 1; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1023 @ runtime = substream->runtime; if (runtime->private_free != NULL) runtime->private_free(runtime); - free_pages_exact(runtime->status, - PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); - free_pages_exact(runtime->control, - PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))); - kfree(runtime->hw_constraints.rules); /* Avoid concurrent access to runtime via PCM timer interface */ if (substream->timer) { spin_lock_irq(&substream->timer->lock); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1031 @ } else { substream->runtime = NULL; } - mutex_destroy(&runtime->buffer_mutex); - snd_fasync_free(runtime->fasync); - kfree(runtime); + snd_pcm_runtime_free(runtime); put_pid(substream->pid); substream->pid = NULL; substream->pstr->substream_opened--; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/sound/core/pcm_dmaengine.c linux-microchip/sound/core/pcm_dmaengine.c --- linux-6.6.51/sound/core/pcm_dmaengine.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/sound/core/pcm_dmaengine.c 2024-11-26 14:02:39.318526300 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:140 @ struct snd_pcm_substream *substream = arg; struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + /* + * workaround to clear mchp-pdmc's channel index when used as BE with + * mchp-asrc + */ + if (substream->pcm->internal && + !strncmp(prtd->dma_chan->slave->driver->name, "mchp-pdmc", sizeof("mchp-pdmc"))) { + unsigned int sample_size = samples_to_bytes(substream->runtime, 1); + u8 *dma_ptr = substream->runtime->dma_area + prtd->pos; + u8 *dma_ptr_end = dma_ptr + snd_pcm_lib_period_bytes(substream); + + for (; dma_ptr < dma_ptr_end; dma_ptr += sample_size) + *dma_ptr = 0; + } + new_pos = prtd->pos + snd_pcm_lib_period_bytes(substream); if (new_pos >= snd_pcm_lib_buffer_bytes(substream)) new_pos = 0; diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/sound/core/pcm_native.c linux-microchip/sound/core/pcm_native.c --- linux-6.6.51/sound/core/pcm_native.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/sound/core/pcm_native.c 2024-11-26 14:02:39.318526300 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:264 @ if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_MMAP)) return false; - if (substream->ops->mmap || substream->ops->page) + if (substream->ops && (substream->ops->mmap || substream->ops->page)) return true; dmabuf = snd_pcm_get_dma_buf(substream); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:712 @ #define is_oss_stream(substream) false #endif +void snd_pcm_runtime_set(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int bits; + snd_pcm_uframes_t frames; + + runtime->access = params_access(params); + runtime->format = params_format(params); + runtime->subformat = params_subformat(params); + runtime->channels = params_channels(params); + runtime->rate = params_rate(params); + runtime->period_size = params_period_size(params); + runtime->periods = params_periods(params); + runtime->buffer_size = params_buffer_size(params); + runtime->info = params->info; + runtime->rate_num = params->rate_num; + runtime->rate_den = params->rate_den; + runtime->no_period_wakeup = + (params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) && + (params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP); + + bits = snd_pcm_format_physical_width(runtime->format); + runtime->sample_bits = bits; + bits *= runtime->channels; + runtime->frame_bits = bits; + frames = 1; + while (bits % 8 != 0) { + bits *= 2; + frames *= 2; + } + runtime->byte_align = bits / 8; + runtime->min_align = frames; + + /* Default sw params */ + runtime->tstamp_mode = SNDRV_PCM_TSTAMP_NONE; + runtime->period_step = 1; + runtime->control->avail_min = runtime->period_size; + runtime->start_threshold = 1; + runtime->stop_threshold = runtime->buffer_size; + runtime->silence_threshold = 0; + runtime->silence_size = 0; + runtime->boundary = runtime->buffer_size; + while (runtime->boundary * 2 <= LONG_MAX - runtime->buffer_size) + runtime->boundary *= 2; + + /* clear the buffer for avoiding possible kernel info leaks */ + if (runtime->dma_area && + !(substream->ops && substream->ops->copy)) { + size_t size = runtime->dma_bytes; + + if (runtime->info & SNDRV_PCM_INFO_MMAP) + size = PAGE_ALIGN(size); + memset(runtime->dma_area, 0, size); + } +} +EXPORT_SYMBOL(snd_pcm_runtime_set); + static int snd_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_pcm_runtime *runtime; int err, usecs; - unsigned int bits; - snd_pcm_uframes_t frames; if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:828 @ goto _error; } - runtime->access = params_access(params); - runtime->format = params_format(params); - runtime->subformat = params_subformat(params); - runtime->channels = params_channels(params); - runtime->rate = params_rate(params); - runtime->period_size = params_period_size(params); - runtime->periods = params_periods(params); - runtime->buffer_size = params_buffer_size(params); - runtime->info = params->info; - runtime->rate_num = params->rate_num; - runtime->rate_den = params->rate_den; - runtime->no_period_wakeup = - (params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) && - (params->flags & SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP); - - bits = snd_pcm_format_physical_width(runtime->format); - runtime->sample_bits = bits; - bits *= runtime->channels; - runtime->frame_bits = bits; - frames = 1; - while (bits % 8 != 0) { - bits *= 2; - frames *= 2; - } - runtime->byte_align = bits / 8; - runtime->min_align = frames; - - /* Default sw params */ - runtime->tstamp_mode = SNDRV_PCM_TSTAMP_NONE; - runtime->period_step = 1; - runtime->control->avail_min = runtime->period_size; - runtime->start_threshold = 1; - runtime->stop_threshold = runtime->buffer_size; - runtime->silence_threshold = 0; - runtime->silence_size = 0; - runtime->boundary = runtime->buffer_size; - while (runtime->boundary * 2 <= LONG_MAX - runtime->buffer_size) - runtime->boundary *= 2; - - /* clear the buffer for avoiding possible kernel info leaks */ - if (runtime->dma_area && !substream->ops->copy) { - size_t size = runtime->dma_bytes; - - if (runtime->info & SNDRV_PCM_INFO_MMAP) - size = PAGE_ALIGN(size); - memset(runtime->dma_area, 0, size); - } + snd_pcm_runtime_set(substream, params); snd_pcm_timer_resolution_change(substream); snd_pcm_set_state(substream, SNDRV_PCM_STATE_SETUP); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2498 @ return snd_interval_refine(hw_param_interval(params, rule->var), &t); } -static int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream) +int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2621 @ return err; return 0; } +EXPORT_SYMBOL(snd_pcm_hw_constraints_init); -static int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream) +int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_hardware *hw = &runtime->hw; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2706 @ return 0; } +EXPORT_SYMBOL(snd_pcm_hw_constraints_complete); static void pcm_release_private(struct snd_pcm_substream *substream) { diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/sound/soc/atmel/atmel_ssc_dai.c linux-microchip/sound/soc/atmel/atmel_ssc_dai.c --- linux-6.6.51/sound/soc/atmel/atmel_ssc_dai.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/sound/soc/atmel/atmel_ssc_dai.c 2024-11-26 14:02:39.366527160 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:825 @ } #define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\ - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + SNDRV_PCM_FMTBIT_S32_LE) static const struct snd_soc_dai_ops atmel_ssc_dai_ops = { .startup = atmel_ssc_startup, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:839 @ static struct snd_soc_dai_driver atmel_ssc_dai = { .playback = { + .stream_name = "Playback", .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_CONTINUOUS, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:847 @ .rate_max = 384000, .formats = ATMEL_SSC_FORMATS,}, .capture = { + .stream_name = "Capture", .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_CONTINUOUS, diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/sound/soc/atmel/Kconfig linux-microchip/sound/soc/atmel/Kconfig --- linux-6.6.51/sound/soc/atmel/Kconfig 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/sound/soc/atmel/Kconfig 2024-11-26 14:02:39.366527160 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:179 @ 2 data lines. The signal path includes an audio grade programmable decimation filter and outputs 24-bit audio words. +config SND_MCHP_SOC_ASRC + tristate "Microchip ASoC driver for boards using ASRC" + depends on OF && (ARCH_AT91 || COMPILE_TEST) + select SND_SOC_GENERIC_DMAENGINE_PCM + select REGMAP_MMIO + help + Say Y or M if you want to add support for Microchip ASRC ASoC + driver on the following Microchip platforms: + - sama7g5 + + The Asynchronous Sample Rate Converter (ASRC) converts the sample + rate of an incoming audio frame without affecting quality. It + supports input and output sampling rates up to 192 kHz. + +config SND_MCHP_SOC_ASRC_CARD + tristate "Mirochop ASoC Sound Card driver for boards using ASRC" + depends on ARCH_AT91 || COMPILE_TEST + depends on SND_ATMEL_SOC && SND_MCHP_SOC_ASRC + help + Say Y or M if you want to enable the Sound Card driver with support for + Microchip's ASRC. endif diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/sound/soc/atmel/Makefile linux-microchip/sound/soc/atmel/Makefile --- linux-6.6.51/sound/soc/atmel/Makefile 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/sound/soc/atmel/Makefile 2024-11-26 14:02:39.366527160 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:11 @ snd-soc-mchp-spdiftx-objs := mchp-spdiftx.o snd-soc-mchp-spdifrx-objs := mchp-spdifrx.o snd-soc-mchp-pdmc-objs := mchp-pdmc.o +snd-soc-mchp-asrc-objs := mchp-asrc.o mchp-asrc-dma.o # pdc and dma need to both be built-in if any user of # ssc is built-in. @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:27 @ obj-$(CONFIG_SND_MCHP_SOC_SPDIFTX) += snd-soc-mchp-spdiftx.o obj-$(CONFIG_SND_MCHP_SOC_SPDIFRX) += snd-soc-mchp-spdifrx.o obj-$(CONFIG_SND_MCHP_SOC_PDMC) += snd-soc-mchp-pdmc.o +obj-$(CONFIG_SND_MCHP_SOC_ASRC) += snd-soc-mchp-asrc.o # AT91 Machine Support snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:37 @ snd-atmel-soc-pdmic-objs := atmel-pdmic.o snd-atmel-soc-tse850-pcm5142-objs := tse850-pcm5142.o snd-soc-mikroe-proto-objs := mikroe-proto.o +snd-soc-mchp-asrc-card-objs := mchp-asrc-card.o obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:46 @ obj-$(CONFIG_SND_ATMEL_SOC_PDMIC) += snd-atmel-soc-pdmic.o obj-$(CONFIG_SND_ATMEL_SOC_TSE850_PCM5142) += snd-atmel-soc-tse850-pcm5142.o obj-$(CONFIG_SND_SOC_MIKROE_PROTO) += snd-soc-mikroe-proto.o +obj-$(CONFIG_SND_MCHP_SOC_ASRC_CARD) += snd-soc-mchp-asrc-card.o diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/sound/soc/atmel/mchp-asrc.c linux-microchip/sound/soc/atmel/mchp-asrc.c --- linux-6.6.51/sound/soc/atmel/mchp-asrc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/sound/soc/atmel/mchp-asrc.c 2024-11-26 14:02:39.366527160 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +// ASoC driver for Microchip ASRC +// +// Copyright (C) 2018-2022 Microchip Technology Inc. and its subsidiaries +// +// Author: Codrin Ciubotariu <codrin.ciubotariu@microchip.com> + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/lcm.h> +#include <linux/list.h> +#include <linux/minmax.h> +#include <linux/module.h> +#include <linux/slab.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/soc.h> +#include <sound/dmaengine_pcm.h> + +#include "mchp-asrc.h" + +/* + * ---- ASRC Controller Register map ---- + */ +#define MCHP_ASRC_CR 0x0000 /* Control Register */ +#define MCHP_ASRC_MR 0x0004 /* Mode Register */ +#define MCHP_ASRC_RATIO(block) (0x0008 + (block) * 4) /* Ratio Register x */ +#define MCHP_ASRC_VBPS_IN 0x0018 /* Valid Bit Per Sample In Register */ +#define MCHP_ASRC_VBPS_OUT 0x001C /* Valid Bit Per Sample Out Register */ +#define MCHP_ASRC_CH_CONF 0x0020 /* Channel Configuration Register */ +#define MCHP_ASRC_TRIG 0x0024 /* Trigger Selection Register */ + +#define MCHP_ASRC_RHR(block) (0x0028 + 4 * (block)) /* Receive Holding Registers */ +#define MCHP_ASRC_THR(block) (0x0048 + 4 * (block)) /* Transmit Holding Registers */ + +#define MCHP_ASRC_IER(block) (0x0068 + 4 * (block)) +#define MCHP_ASRC_IDR(block) (0x0078 + 4 * (block)) +#define MCHP_ASRC_IMR(block) (0x0088 + 4 * (block)) +#define MCHP_ASRC_ISR(block) (0x0098 + 4 * (block)) + +#define MCHP_ASRC_ESR 0x00A8 /* Error Status Register */ + +#define MCHP_ASRC_WPMR 0x00E4 /* Write Protection Mode Register */ +#define MCHP_ASRC_WPSR 0x00E8 /* Write Protection Status Register */ + +#define MCHP_ASRC_VERSION 0x00FC /* Version Register */ + +/* + * ---- Control Register (Write-only) ---- + */ +#define MCHP_ASRC_CR_SWRST BIT(0) + +/* + * ---- Mode Register (Read/Write) ---- + */ +/* ASRC Stereo Channel x Enable */ +#define MCHP_ASRC_MR_ASRCEN_MASK GENMASK(3, 0) +#define MCHP_ASRC_MR_ASRCEN(block) BIT(block) + +/* Sampling frequency greater than 96 KHz */ +#define MCHP_ASRC_MR_GT96K BIT(12) + +/* + * ---- Ratio Register of Stereo Channel x (Read/Write) ---- + */ +/* Input Internal Sampling Rate Ratio */ +#define MCHP_ASRC_RATIO_INRATIO_MASK GENMASK(15, 0) +#define MCHP_ASRC_RATIO_INRATIO(ratio) ((ratio) & MCHP_ASRC_RATIO_INRATIO_MASK) + +/* Output Internal Sampling Rate Ratio */ +#define MCHP_ASRC_RATIO_OUTRATIO_MASK GENMASK(31, 16) +#define MCHP_ASRC_RATIO_OUTRATIO(ratio) \ + (((ratio) << 16) & MCHP_ASRC_RATIO_OUTRATIO_MASK) + +/* + * ---- Valid bit Per Sample In/Out (Read/Write) ---- + */ +#define MCHP_ASRC_VBPS_MASK(block) \ + (GENMASK(3, 0) << ((block) * 8)) +#define MCHP_ASRC_VBPS(block, val) \ + (((val) & GENMASK(3, 0)) << ((block) * 8)) + +#define MCHP_ASRC_VBPS_8_BIT(block) MCHP_ASRC_VBPS(block, 0) +#define MCHP_ASRC_VBPS_16_BIT(block) MCHP_ASRC_VBPS(block, 1) +#define MCHP_ASRC_VBPS_20_BIT(block) MCHP_ASRC_VBPS(block, 2) +#define MCHP_ASRC_VBPS_24_BIT(block) MCHP_ASRC_VBPS(block, 3) +#define MCHP_ASRC_VBPS_32_BIT(block) MCHP_ASRC_VBPS(block, 4) +#define MCHP_ASRC_VBPS_10_BIT(block) MCHP_ASRC_VBPS(block, 5) +#define MCHP_ASRC_VBPS_12_BIT(block) MCHP_ASRC_VBPS(block, 6) +#define MCHP_ASRC_VBPS_14_BIT(block) MCHP_ASRC_VBPS(block, 7) +#define MCHP_ASRC_VBPS_18_BIT(block) MCHP_ASRC_VBPS(block, 8) + +/* + * ---- Channel configuration Registers (Read/Write) ---- + */ +/* Operating Mode for Transmit Holding Registers */ +#define MCHP_ASRC_CH_CONF_THROPMODE_MASK GENMASK(2, 0) +#define MCHP_ASRC_CH_CONF_THROPMODE(val) \ + ((val) & MCHP_ASRC_CH_CONF_THROPMODE_MASK) + +/* Operating Mode for Receive Holding Registers */ +#define MCHP_ASRC_CH_CONF_RHROPMODE_MASK GENMASK(6, 4) +#define MCHP_ASRC_CH_CONF_RHROPMODE(val) \ + (((val) << 4) & MCHP_ASRC_CH_CONF_RHROPMODE_MASK) + +/* Audio Block Operating Mode; Mono or Stereo */ +#define MCHP_ASRC_CH_CONF_MONO_MASK GENMASK(11, 8) +#define MCHP_ASRC_CH_CONF_MONO(ch) BIT((ch) + 8) + +/* DMA Audio Block x CHUNK Size */ +#define MCHP_ASRC_CH_CONF_CHUNK_MASK(block) \ + (GENMASK(2, 0) << ((block) * 4 + 16)) +#define MCHP_ASRC_CH_CONF_CHUNK(block, val) \ + (((val) & GENMASK(2, 0)) << ((block) * 4 + 16)) +#define MCHP_ASRC_CH_CONF_CHUNK_1_DATA(block) MCHP_ASRC_CH_CONF_CHUNK(block, 0) +#define MCHP_ASRC_CH_CONF_CHUNK_2_DATA(block) MCHP_ASRC_CH_CONF_CHUNK(block, 1) +#define MCHP_ASRC_CH_CONF_CHUNK_4_DATA(block) MCHP_ASRC_CH_CONF_CHUNK(block, 2) +#define MCHP_ASRC_CH_CONF_CHUNK_8_DATA(block) MCHP_ASRC_CH_CONF_CHUNK(block, 3) +#define MCHP_ASRC_CH_CONF_CHUNK_16_DATA(block) MCHP_ASRC_CH_CONF_CHUNK(block, 4) + +/* + * ---- Trigger Selection Registers (Read/Write) ---- + */ +#define MCHP_ASRC_TRIG_TRIGSELIN_MASK(ch) \ + (GENMASK(3, 0) << ((ch) * 4)) +#define MCHP_ASRC_TRIG_TRIGSELIN(src, ch) \ + (((src) & GENMASK(3, 0)) << ((ch) * 4)) + +#define MCHP_ASRC_TRIG_TRIGSELOUT_MASK(ch) \ + (GENMASK(3, 0) << ((ch) * 4 + 16)) +#define MCHP_ASRC_TRIG_TRIGSELOUT(src, ch) \ + (((src) & GENMASK(3, 0)) << ((ch) * 4 + 16)) + +/* + * ---- Interrupt Enable/Disable/Mask/Status Registers ---- + */ +#define MCHP_ASRC_IR_RXRDY BIT(0) +#define MCHP_ASRC_IR_RXEMPTY BIT(1) +#define MCHP_ASRC_IR_RXFULL BIT(2) +#define MCHP_ASRC_IR_RXCHUNK BIT(3) +#define MCHP_ASRC_IR_RXUDR BIT(4) +#define MCHP_ASRC_IR_RXOVR BIT(5) + +#define MCHP_ASRC_IR_TXRDY BIT(8) +#define MCHP_ASRC_IR_TXEMPTY BIT(9) +#define MCHP_ASRC_IR_TXFULL BIT(10) +#define MCHP_ASRC_IR_TXCHUNK BIT(11) +#define MCHP_ASRC_IR_TXUDR BIT(12) +#define MCHP_ASRC_IR_TXOVR BIT(13) + +#define MCHP_ASRC_IR_SECE BIT(16) + +#define MCHP_ASRC_IR_LOCK BIT(30) + +/* + * ---- Error Status Register (Read-only) ---- + */ +#define MCHP_ASRC_ESR_INCFGERR_MASK GENMASK(4, 0) +#define MCHP_ASRC_ESR_OUTCFGERR_MASK GENMASK(12, 8) + +/* + * ---- Version Register (Read-only) ---- + */ +#define MCHP_ASRC_VERSION_MASK GENMASK(11, 0) + +#define MCHP_ASRC_LT96K_RATIO_MIN 2048 +#define MCHP_ASRC_GT96K_RATIO_MIN 1024 +#define MCHP_ASRC_TRIG_IDX_MAX_VAL 16 +#define MCHP_ASRC_RATIO_MAX MCHP_ASRC_RATIO_INRATIO_MASK +#define MCHP_ASRC_OPMODE_MAX 5 + +static const struct regmap_config mchp_asrc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = MCHP_ASRC_VERSION, +}; + +static const struct snd_soc_dapm_route mchp_asrc_route[] = { + {"ASRC-Playback 1", NULL, "ASRC-Capture 1"}, + {"ASRC-Playback 2", NULL, "ASRC-Capture 2"}, + {"ASRC-Playback 3", NULL, "ASRC-Capture 3"}, + {"ASRC-Playback 4", NULL, "ASRC-Capture 4"}, +}; + +static const struct snd_soc_component_driver mchp_asrc_component = { + .name = "mchp-asrc-dai", + .dapm_routes = mchp_asrc_route, + .num_dapm_routes = ARRAY_SIZE(mchp_asrc_route), +}; + +/* one slot in the ASRC, representing one or more DSPs reserverd for PCM */ +struct mchp_asrc_slot { + int first_dsp; + int dsp_count; + int slot_id; +}; + +struct opmode_to_slot { + struct mchp_asrc_slot slot[MCHP_ASRC_NB_STEREO_CH]; +}; + +/* represents the slots with the used DSPs for each opmode */ +static const struct opmode_to_slot asrc_map[] = { + {{{0, 1, 0}, {1, 1, 1}, {2, 1, 2}, {3, 1, 3}}}, + {{{0, 2, 0}, {2, 1, 1}, {3, 1, 2}}}, + {{{0, 2, 0}, {2, 2, 1}}}, + {{{0, 3, 0}, {3, 1, 1}}}, + {{{0, 4, 0}}}, +}; + +struct xmit_ch { + int thr_ch[MCHP_ASRC_NB_STEREO_CH]; + int rhr_ch[MCHP_ASRC_NB_STEREO_CH]; + int thr_ch_count; + int rhr_ch_count; + int dsps_used; + bool in_use; +}; + +struct opmode { + struct xmit_ch tc[MCHP_ASRC_NB_STEREO_CH]; + int tc_count; +}; + +/* represents the transmit and receive channels for all opmode combinations */ +static struct opmode ch_conf[MCHP_ASRC_OPMODE_MAX][MCHP_ASRC_OPMODE_MAX] = { + [0][0] = {{ + {{0}, {0}, 1, 1, 1}, + {{1}, {1}, 1, 1, 1}, + {{2}, {2}, 1, 1, 1}, + {{3}, {3}, 1, 1, 1}, + }, 4, /* THOPMODE 0, RHOPMODE 0 */ + }, + [0][1] = {{ + {{0, 1}, {0}, 2, 1, 2}, + {{2}, {1}, 1, 1, 1}, + {{3}, {2}, 1, 1, 1}, + }, 3, /* THOPMODE 0, RHOPMODE 1 */ + }, + [0][2] = {{ + {{0, 1}, {0}, 2, 1, 2}, + {{2, 3}, {1}, 2, 1, 2}, + }, 2, /* THOPMODE 0, RHOPMODE 2 */ + }, + [0][3] = {{ + {{0, 1, 2}, {0}, 3, 1, 3}, + {{3}, {1}, 1, 1, 1}, + }, 2, /* THOPMODE 0, RHOPMODE 3 */ + }, + [0][4] = {{ + {{0, 1, 2, 3}, {0}, 4, 1, 4}, + }, 1, /* THOPMODE 0, RHOPMODE 4 */ + }, + [1][0] = {{ + {{0}, {0, 1}, 1, 2, 2}, + {{1}, {2}, 1, 1, 1}, + {{2}, {3}, 1, 1, 1}, + }, 3, /* THOPMODE 1, RHOPMODE 0 */ + }, + [1][1] = {{ + {{0}, {0}, 1, 1, 2}, + {{1}, {1}, 1, 1, 1}, + {{2}, {2}, 1, 1, 1}, + }, 3, /* THOPMODE 1, RHOPMODE 1 */ + }, + [1][2] = {{ + {{0}, {0}, 1, 1, 2}, + {{1, 2}, {1}, 2, 1, 2}, + }, 2, /* THOPMODE 1, RHOPMODE 2 */ + }, + [1][3] = {{ + {{0, 1}, {0}, 2, 1, 3}, + {{2}, {1}, 1, 1, 1}, + }, 2, /* THOPMODE 1, RHOPMODE 3 */ + }, + [1][4] = {{ + {{0, 1, 2}, {0}, 3, 1, 4}, + }, 1, /* THOPMODE 1, RHOPMODE 4 */ + }, + [2][0] = {{ + {{0}, {0, 1}, 1, 2, 2}, + {{1}, {2, 3}, 1, 2, 2}, + }, 2, /* THOPMODE 2, RHOPMODE 0 */ + }, + [2][1] = {{ + {{0}, {0}, 1, 1, 2}, + {{1}, {1, 2}, 1, 2, 2}, + }, 2, /* THOPMODE 2, RHOPMODE 1 */ + }, + [2][2] = {{ + {{0}, {0}, 1, 1, 2}, + {{1}, {1}, 1, 1, 2}, + }, 2, /* THOPMODE 2, RHOPMODE 2 */ + }, + [2][3] = {{ + /* not implemented for now */ + {{0}, {0}, 1, 1, 2}, + {{1}, {0}, 1, 1, 1}, + {{1}, {1}, 1, 1, 1}, + }, 3, /* THOPMODE 2, RHOPMODE 3 */ + }, + [2][4] = {{ + {{0, 1}, {0}, 2, 1, 4}, + }, 1, /* THOPMODE 2, RHOPMODE 4 */ + }, + [3][0] = {{ + {{0}, {0, 1, 2}, 1, 3, 3}, + {{1}, {3}, 1, 1, 1}, + }, 2, /* THOPMODE 3, RHOPMODE 0 */ + }, + [3][1] = {{ + {{0}, {0, 1}, 1, 2, 3}, + {{1}, {2}, 1, 1, 1}, + }, 2, /* THOPMODE 3, RHOPMODE 1 */ + }, + [3][2] = {{ + /* not implemented for now */ + {{0}, {0}, 1, 1, 2}, + {{0}, {1}, 1, 1, 1}, + {{1}, {1}, 1, 1, 1}, + }, 3, /* THOPMODE 3, RHOPMODE 2 */ + }, + [3][3] = {{ + {{0}, {0}, 1, 1, 3}, + {{1}, {1}, 1, 1, 1}, + }, 2, /* THOPMODE 3, RHOPMODE 3 */ + }, + [3][4] = {{ + {{0, 1}, {0}, 2, 1, 4}, + }, 1, /* THOPMODE 3, RHOPMODE 4 */ + }, + [4][0] = {{ + {{0}, {0, 1, 2, 3}, 1, 4, 4}, + }, 1, /* THOPMODE 4, RHOPMODE 0 */ + }, + [4][1] = {{ + {{0}, {0, 1, 2}, 1, 3, 4}, + }, 1, /* THOPMODE 4, RHOPMODE 1 */ + }, + [4][2] = {{ + {{0}, {0, 1}, 1, 2, 4}, + }, 1, /* THOPMODE 4, RHOPMODE 2 */ + }, + [4][3] = {{ + {{0}, {0, 1}, 1, 2, 4}, + }, 1, /* THOPMODE 4, RHOPMODE 3 */ + }, + [4][4] = {{ + {{0}, {0}, 1, 1, 4}, + }, 1, /* THOPMODE 4, RHOPMODE 4 */ + }, +}; + +struct mchp_asrc_be_trigger; + +/* runtime structure to dynamically associate a trigger wth a BE DAI */ +struct mchp_asrc_be_rtm { + struct mchp_asrc_be_trigger *trig; /* backpointer */ + struct snd_pcm_hw_params *hw_params; // used by the links that are not BE + struct mchp_asrc_slot const *slot; + struct list_head list; +}; + +/* associated with one HW BE audio IP */ +struct mchp_asrc_be_trigger { + struct mchp_asrc_be_rtm *rtm[SNDRV_PCM_STREAM_LAST + 1]; + struct mchp_asrc_dev *priv; /* backpointer */ + struct of_phandle_args *phandle; + u32 idx; +}; + +struct mchp_asrc_dsp_priv { + struct mchp_asrc_dmaengine_dai_dma *dma; +}; + +struct mchp_asrc_pcm_priv { + struct mchp_asrc_dmaengine_dai_dma dma; + struct list_head list_head_in; + struct list_head list_head_out; + struct xmit_ch *tc; + struct mchp_asrc_be_rtm rtm_fe; /* used for FE --- BEs links */ + int refcount; + int maxburst; + bool is_hostless; +}; + +struct mchp_asrc_dev { + struct mchp_asrc_dsp_priv dsp[MCHP_ASRC_NB_STEREO_CH]; + struct mchp_asrc_pcm_priv *pcm[MCHP_ASRC_NB_STEREO_CH]; + struct device *dev; + struct regmap *regmap; + struct clk *pclk; + struct clk *gclk; + struct resource *mem; + struct mchp_asrc_be_trigger *trig; + int trig_count; + int thr_opmode; + int rhr_opmode; +}; + +static inline int mchp_asrc_period_to_burst(int period_size, int sample_size) +{ + if (!(period_size % (sample_size * 16))) + return 16; + + if (!(period_size % (sample_size * 8))) + return 8; + + if (!(period_size % (sample_size * 4))) + return 4; + + if (!(period_size % (sample_size * 2))) + return 2; + + return 1; +} + +static inline int mchp_asrc_burst_to_chunk(int dsp, int burst) +{ + switch (burst) { + case 16: + return MCHP_ASRC_CH_CONF_CHUNK_16_DATA(dsp); + case 8: + return MCHP_ASRC_CH_CONF_CHUNK_8_DATA(dsp); + case 4: + return MCHP_ASRC_CH_CONF_CHUNK_4_DATA(dsp); + case 2: + return MCHP_ASRC_CH_CONF_CHUNK_2_DATA(dsp); + default: + return MCHP_ASRC_CH_CONF_CHUNK_1_DATA(dsp); + } +} + +/* rule to limit the number of channels based on available DSPs and available BEs */ +static int mchp_asrc_ch_rule(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) +{ + struct mchp_asrc_dev *priv = rule->private; + struct snd_interval t; + int dsps_running; + int max_dsps_avail; + u32 mr; + + regmap_read(priv->regmap, MCHP_ASRC_MR, &mr); + dsps_running = hweight32(mr & MCHP_ASRC_MR_ASRCEN_MASK); + + /* there are no limitatins if there are no running DSPs */ + if (!dsps_running) + return 0; + + /* with sr over 96000, only 2 DSPs can be used */ + if (mr & MCHP_ASRC_MR_GT96K) + max_dsps_avail = 2; + else + max_dsps_avail = MCHP_ASRC_NB_STEREO_CH; + + if (max_dsps_avail <= dsps_running) + return -EBUSY; + + snd_interval_any(&t); + t.max = (max_dsps_avail - dsps_running) * 2; + + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +/* rule to never have a sampling rate greater than 96kHz if more than 2 DSPs are used */ +static int mchp_asrc_ch_rate_rule(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) +{ + struct snd_interval t; + struct mchp_asrc_dev *priv = rule->private; + unsigned int gclk_max_rate; + unsigned int fs = params_rate(params); + int dsps_running; + int dsps_req = (params_channels(params) + 1) / 2; + int gt96k; + u32 mr; + + snd_interval_any(&t); + + /* GCLK can't be 4 times higher than PCLK */ + gclk_max_rate = clk_get_rate(priv->pclk) * 4 - 1; + + regmap_read(priv->regmap, MCHP_ASRC_MR, &mr); + dsps_running = hweight32(mr & MCHP_ASRC_MR_ASRCEN_MASK); + + if (!dsps_running) { + if (fs > 96000) + t.max = gclk_max_rate / MCHP_ASRC_GT96K_RATIO_MIN; + else + t.max = min(gclk_max_rate / MCHP_ASRC_LT96K_RATIO_MIN, 96000U); + return snd_interval_refine(hw_param_interval(params, rule->var), &t); + } + + /* some DSPs are running so GT96K can't be changed */ + gt96k = !!(mr & MCHP_ASRC_MR_GT96K); + if (dsps_running + dsps_req <= 2) { + if (gt96k) + t.max = gclk_max_rate / MCHP_ASRC_GT96K_RATIO_MIN; + else + t.max = min(gclk_max_rate / MCHP_ASRC_LT96K_RATIO_MIN, 96000U); + return snd_interval_refine(hw_param_interval(params, rule->var), &t); + } + + /* if GT96K bit is set, only 2 DSPs can run */ + if (dsps_running + dsps_req > 2 && gt96k) + return -EBUSY; + + t.max = min(gclk_max_rate / MCHP_ASRC_LT96K_RATIO_MIN, 96000U); + return snd_interval_refine(hw_param_interval(params, rule->var), &t); +} + +static int mchp_asrc_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mchp_asrc_dev *priv = snd_soc_dai_get_drvdata(dai); + struct snd_soc_dpcm *dpcm; + int dsps_running; + int ret; + u32 mr; + int i; + + regmap_read(priv->regmap, MCHP_ASRC_MR, &mr); + dsps_running = hweight32(mr & MCHP_ASRC_MR_ASRCEN_MASK); + + /* + * if there are already 2 DSPs running at a sampling rate higher than + * 96kHz, we can not use the rest of the DSPs + */ + if (dsps_running >= 2 && (mr & MCHP_ASRC_MR_GT96K)) + return -EBUSY; + + ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + mchp_asrc_ch_rule, priv, SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (ret < 0) { + dev_err(rtd->dev, "failed to add rule for no of channels: %d", ret); + return ret; + } + + ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + mchp_asrc_ch_rate_rule, priv, SNDRV_PCM_HW_PARAM_CHANNELS, + SNDRV_PCM_HW_PARAM_RATE, -1); + if (ret < 0) { + dev_err(rtd->dev, "failed to add rule for sampling rate: %d", ret); + return ret; + } + + for_each_dpcm_be(rtd, substream->stream, dpcm) { + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_soc_dai *dai_be = asoc_rtd_to_cpu(be, 0); + struct snd_pcm_substream *substream_be; + + if (dpcm->fe != rtd) + continue; + + substream_be = snd_soc_dpcm_get_substream(be, substream->stream); + if (!substream_be) + continue; + + dev_dbg(rtd->dev, "%s() Found BE DAI %s\n", __func__, dai_be->name); + + for (i = 0; i < priv->trig_count; i++) { + struct mchp_asrc_be_trigger *trig_be = &priv->trig[i]; + struct mchp_asrc_be_rtm *rtm_be; + + if (!trig_be->phandle || trig_be->phandle->np != dai_be->dev->of_node) + continue; + + dev_dbg(priv->dev, "BE %s found in triger list with id %d\n", + dai_be->name, trig_be->idx); + + if (trig_be->rtm[substream->stream]) { + dev_err(priv->dev, "interface %s already running\n", + trig_be->phandle->np->full_name); + ret = -EBUSY; + goto __cleanup_rtm; + } + + rtm_be = kzalloc(sizeof(*rtm_be), GFP_KERNEL); + if (!rtm_be) { + ret = -ENOMEM; + goto __cleanup_rtm; + } + + rtm_be->trig = trig_be; + trig_be->rtm[substream->stream] = rtm_be; + break; + } + if (i == priv->trig_count) { + dev_warn(rtd->dev, "Trigger not found for BE DAI %s\n", dai_be->name); + continue; + } + } + + return 0; + +__cleanup_rtm: + for_each_dpcm_be_rollback(rtd, substream->stream, dpcm) { + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_soc_dai *dai_be = asoc_rtd_to_cpu(be, 0); + struct snd_pcm_substream *substream_be; + int i; + + if (dpcm->fe != rtd) + continue; + + substream_be = snd_soc_dpcm_get_substream(be, substream->stream); + if (!substream_be) + continue; + + for (i = 0; i < priv->trig_count; i++) { + struct mchp_asrc_be_trigger *trig_be = &priv->trig[i]; + + if (!trig_be->phandle || trig_be->phandle->np != dai_be->dev->of_node) + continue; + + if (!trig_be->rtm[substream->stream]) + break; + + kfree(trig_be->rtm[substream->stream]); + trig_be->rtm[substream->stream] = NULL; + } + } + + return ret; +} + +static void mchp_asrc_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct mchp_asrc_dev *priv = snd_soc_dai_get_drvdata(dai); + struct snd_soc_dpcm *dpcm; + int i; + + for_each_dpcm_be(rtd, substream->stream, dpcm) { + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_soc_dai *dai_be = asoc_rtd_to_cpu(be, 0); + struct snd_pcm_substream *substream_be; + + if (dpcm->fe != rtd) + continue; + + substream_be = snd_soc_dpcm_get_substream(be, substream->stream); + if (!substream_be) + continue; + + for (i = 0; i < priv->trig_count; i++) { + struct mchp_asrc_be_trigger *trig_be = &priv->trig[i]; + + if (!trig_be->phandle || trig_be->phandle->np != dai_be->dev->of_node) + continue; + + kfree(trig_be->rtm[substream->stream]); + trig_be->rtm[substream->stream] = NULL; + break; + } + } +} + +static u32 mchp_asrc_vbps_to_reg(int vbps, int block) +{ + switch (vbps) { + case 8: + return MCHP_ASRC_VBPS_8_BIT(block); + case 10: + return MCHP_ASRC_VBPS_10_BIT(block); + case 12: + return MCHP_ASRC_VBPS_12_BIT(block); + case 14: + return MCHP_ASRC_VBPS_14_BIT(block); + case 16: + return MCHP_ASRC_VBPS_16_BIT(block); + case 18: + return MCHP_ASRC_VBPS_18_BIT(block); + case 20: + return MCHP_ASRC_VBPS_20_BIT(block); + case 24: + return MCHP_ASRC_VBPS_24_BIT(block); + case 32: + return MCHP_ASRC_VBPS_32_BIT(block); + default: + return 0; + } +} + +static int mchp_asrc_bes_get(struct mchp_asrc_dev *priv, struct snd_pcm_substream *substream, + struct list_head *head, bool set_trig) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dpcm *dpcm; + int i; + + for_each_dpcm_be(rtd, substream->stream, dpcm) { + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_soc_dai *dai = asoc_rtd_to_cpu(be, 0); + struct snd_pcm_substream *substream_be; + + if (dpcm->fe != rtd) + continue; + + substream_be = snd_soc_dpcm_get_substream(be, substream->stream); + if (!substream_be) + continue; + + dev_dbg(rtd->dev, + "%s() BE DAI %s: (dpcm_params) rate=%u format=%#x width=%u channels=%u\n", + __func__, dai->name, + params_rate(&be->dpcm[substream->stream].hw_params), + params_format(&be->dpcm[substream->stream].hw_params), + params_width(&be->dpcm[substream->stream].hw_params), + params_channels(&be->dpcm[substream->stream].hw_params)); + + for (i = 0; i < priv->trig_count; i++) { + struct mchp_asrc_be_trigger *trig_be = &priv->trig[i]; + struct mchp_asrc_be_rtm *rtm_be; + + if (!trig_be->phandle || trig_be->phandle->np != dai->dev->of_node) + continue; + + dev_dbg(priv->dev, "BE found in triger list with id %d\n", + trig_be->idx); + + rtm_be = trig_be->rtm[substream->stream]; + if (set_trig) + rtm_be->trig = trig_be; + + rtm_be->hw_params = &be->dpcm[substream->stream].hw_params; + + /* keep the order */ + list_add_tail(&rtm_be->list, head); + break; + } + + if (i == priv->trig_count) { + dev_err(rtd->dev, "no BE trigger found for DAI %s\n", dai->name); + return -EINVAL; + } + } + + return 0; +} + +static int mchp_asrc_validate_bes(struct mchp_asrc_dev *priv, struct list_head *head, int channels) +{ + struct mchp_asrc_be_rtm *rtm_be; + int dsps_req = (channels + 1) / 2; + int ch_be_sum = 0; + int dsps_avail; + int dsps_prev_be = MCHP_ASRC_NB_STEREO_CH; + u32 reg; + + /* get the number of free DSPs */ + regmap_read(priv->regmap, MCHP_ASRC_MR, ®); + dsps_avail = MCHP_ASRC_NB_STEREO_CH - hweight32(reg & MCHP_ASRC_MR_ASRCEN_MASK); + + if (dsps_req && dsps_avail < dsps_req) { + dev_err(priv->dev, "Not enough DSPs available (%d) to play %d audio channel(s)\n", + dsps_avail, channels); + return -EBUSY; + } + + /* the number of DSPs needed for each BE needs to be in a descending order */ + list_for_each_entry(rtm_be, head, list) { + int ch_be = params_channels(rtm_be->hw_params); + + if ((ch_be + 1) / 2 > dsps_prev_be) { + dev_err(priv->dev, + "the nr of DSPs needed for each BE is not in a descending order\n"); + return -EINVAL; + } + ch_be_sum += ch_be; + dsps_prev_be = (ch_be + 1) / 2; + } + + /* + * the number of channels on FE needs to be the same with + * the one used on all BEs + */ + if (channels && channels != ch_be_sum) { + dev_err(priv->dev, "FE uses %d channels, BEs uses %d channels\n", + channels, ch_be_sum); + return -EINVAL; + } + + return ch_be_sum; +} + +static int mchp_asrc_is_tc_valid(struct mchp_asrc_pcm_priv *pcm, struct xmit_ch *tc, + int single_aif_is_in, int thr_opmode, int rhr_opmode, + int *aif, int aif_nr) +{ + struct opmode_to_slot const *be_opm; + int *be_chan; + int fe_chan_count, be_chan_count; + int i; + + /* for now we support only one FE */ + /* one-to-many scenario */ + if (single_aif_is_in) { + be_chan = tc->rhr_ch; + fe_chan_count = tc->thr_ch_count; + be_chan_count = tc->rhr_ch_count; + be_opm = &asrc_map[rhr_opmode]; + } else { + /* many-to-one scenario */ + be_chan = tc->thr_ch; + fe_chan_count = tc->rhr_ch_count; + be_chan_count = tc->thr_ch_count; + be_opm = &asrc_map[thr_opmode]; + } + + if (fe_chan_count != 1 || aif_nr != be_chan_count) + return -EINVAL; + + for (i = 0; i < aif_nr; i++) { + int dsps_avail = be_opm->slot[be_chan[i]].dsp_count; + + if (dsps_avail != aif[i]) + return -EINVAL; + } + + tc->in_use = true; + + return 0; +} + +static struct xmit_ch * +mchp_asrc_find_tc(struct mchp_asrc_pcm_priv *pcm, int dsps_req, int single_aif_is_in, + int thr_opmode, int rhr_opmode, int *aif, int aif_nr) +{ + struct opmode *current_conf = &ch_conf[thr_opmode][rhr_opmode]; + int i; + int ret; + + for (i = 0; i < current_conf->tc_count; i++) { + struct xmit_ch *tc = ¤t_conf->tc[i]; + + if (tc->in_use || dsps_req != tc->dsps_used) + continue; + + /* the first slot is used both as FE and BE */ + ret = mchp_asrc_is_tc_valid(pcm, tc, single_aif_is_in, thr_opmode, rhr_opmode, + aif, aif_nr); + if (!ret) + return tc; + } + + return NULL; +} + +static int mchp_asrc_tc_get(struct mchp_asrc_dev *priv, struct snd_soc_dai *dai) +{ + int aif_in[MCHP_ASRC_NB_STEREO_CH] = {0}; + int aif_out[MCHP_ASRC_NB_STEREO_CH] = {0}; + int aif_in_nr = 0; + int aif_out_nr = 0; + struct mchp_asrc_be_rtm *rtm_be; + int dsps_in = 0, dsps_out = 0; + struct xmit_ch *tc; + int thr, rhr; + int *aif, aif_nr; + bool single_aif_is_in; + struct mchp_asrc_pcm_priv *pcm = priv->pcm[dai->id]; + int i; + + /* both OPMODES must be set, or none */ + if (priv->thr_opmode * priv->rhr_opmode < 0) + return -EINVAL; + + /* get no of DSPs needed for each IN and OUT AIFs */ + list_for_each_entry(rtm_be, &pcm->list_head_in, list) { + int dsps = (params_channels(rtm_be->hw_params) + 1) / 2; + + aif_in[aif_in_nr++] = dsps; + + dev_dbg(priv->dev, "AIF %s needs %d DSPs, index %d\n", + rtm_be->trig ? rtm_be->trig->phandle->np->full_name : "front-end", + dsps, aif_in_nr - 1); + } + list_for_each_entry(rtm_be, &pcm->list_head_out, list) { + int dsps = (params_channels(rtm_be->hw_params) + 1) / 2; + + aif_out[aif_out_nr++] = dsps; + + dev_dbg(priv->dev, "AIF %s needs %d DSPs, index %d\n", + rtm_be->trig ? rtm_be->trig->phandle->np->full_name : "front-end", + dsps, aif_out_nr - 1); + } + + if (aif_in_nr == 1) { + single_aif_is_in = true; + aif = aif_out; + aif_nr = aif_out_nr; + } else if (aif_out_nr == 1) { + single_aif_is_in = false; + aif = aif_in; + aif_nr = aif_in_nr; + } else { + dev_err(priv->dev, + "PCM %s (de)mux %d -> %d AIFs; do not use many-to-many\n", + dai->name, aif_in_nr, aif_out_nr); + return -EINVAL; + } + + for (i = 0; i < aif_in_nr; i++) + dsps_in += aif_in[i]; + for (i = 0; i < aif_out_nr; i++) + dsps_out += aif_out[i]; + if (dsps_in != dsps_out) { + dev_err(priv->dev, "%d -> %d DSPs; number needs to match\n", + dsps_in, dsps_out); + return -EINVAL; + } + + /* the OPMODEs are already set, check if there are free transmit channels */ + if (priv->thr_opmode != -1) { + thr = priv->thr_opmode; + rhr = priv->rhr_opmode; + tc = mchp_asrc_find_tc(pcm, dsps_in, single_aif_is_in, thr, rhr, + aif, aif_nr); + if (tc) + goto __found_tc; + + /* already selected opmode is not ok for FE */ + dev_err(priv->dev, "no compatible transmit channels left in THR/RHR opmode %d/%d\n", + priv->thr_opmode, priv->rhr_opmode); + return -EBUSY; + } + for (thr = 0; thr < MCHP_ASRC_OPMODE_MAX; thr++) { + for (rhr = 0; rhr < MCHP_ASRC_OPMODE_MAX; rhr++) { + tc = mchp_asrc_find_tc(pcm, dsps_in, single_aif_is_in, thr, + rhr, aif, aif_nr); + if (tc) + goto __found_tc_init; + } + } + + return -EINVAL; + +__found_tc_init: + dev_dbg(priv->dev, "%s() opmodes THR %d RHR %d\n", __func__, + thr, rhr); + priv->thr_opmode = thr; + priv->rhr_opmode = rhr; + regmap_update_bits(priv->regmap, MCHP_ASRC_CH_CONF, + MCHP_ASRC_CH_CONF_THROPMODE_MASK | + MCHP_ASRC_CH_CONF_RHROPMODE_MASK, + MCHP_ASRC_CH_CONF_THROPMODE(thr) | + MCHP_ASRC_CH_CONF_RHROPMODE(rhr)); +__found_tc: +#ifdef DEBUG + dev_dbg(priv->dev, "%s() transmit channels:\n", __func__); + for (i = 0; i < tc->thr_ch_count; i++) + dev_dbg(priv->dev, "%s() %d, ", __func__, tc->thr_ch[i]); + dev_dbg(priv->dev, "%s() receive channels:\n", __func__); + for (i = 0; i < tc->rhr_ch_count; i++) + dev_dbg(priv->dev, "%s() %d, ", __func__, tc->rhr_ch[i]); +#endif + /* select one FE slot, the first one */ + pcm->tc = tc; + return 0; +} + +static void mchp_asrc_tc_free(struct mchp_asrc_dev *priv, struct mchp_asrc_pcm_priv *pcm) +{ + struct opmode *op; + int i; + + pcm->tc->in_use = false; + + op = &ch_conf[priv->thr_opmode][priv->rhr_opmode]; + for (i = 0; i < op->tc_count; i++) { + /* do not clear OPMODES if there are running PCMs */ + if (op->tc[i].in_use) + return; + } + + priv->thr_opmode = -1; + priv->rhr_opmode = -1; + regmap_update_bits(priv->regmap, MCHP_ASRC_CH_CONF, + MCHP_ASRC_CH_CONF_THROPMODE_MASK | MCHP_ASRC_CH_CONF_RHROPMODE_MASK, 0); +} + +/* apply HW DMA Chunk Size limitations */ +static void mchp_asrc_maxburst_limit(int thr_opmode, int rhr_opmode, bool is_mono, + int *maxburst) +{ + if ((thr_opmode == 0 || rhr_opmode == 0) && + *maxburst > 4) { + if (is_mono) + *maxburst = 4; + else + *maxburst = 8; + return; + } + if ((thr_opmode != 4 || rhr_opmode != 4) && is_mono && *maxburst > 8) + *maxburst = 8; +} + +static int mchp_asrc_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct mchp_asrc_dev *priv = snd_soc_dai_get_drvdata(dai); + struct mchp_asrc_dmaengine_dai_dma *dma; + struct mchp_asrc_dmaengine_be_dma *dma_be, *tmp; + struct mchp_asrc_pcm_priv *pcm; + int width = params ? params_width(params) : 0; + int src_width, dst_width; + unsigned long rate; + unsigned int fs = params ? params_rate(params) : 0; + unsigned int max_sr = fs; + bool is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + u32 ratio; + unsigned int channels = params ? params_channels(params) : 0; + u32 ch_conf = 0; + u32 ch_conf_mask = 0; + int ret; + u32 mr; + u32 trig = 0, trig_mask = 0; + u32 vbps_in = 0, vbps_out = 0, vbps_mask = 0; + struct mchp_asrc_be_rtm *rtm_be; + int dsp; + int dsps_running; + int ratio_min; + struct list_head *list_head_bes; + int i; + struct opmode_to_slot const *be_opm; + struct xmit_ch *tc; + bool is_mono = false; + + dev_dbg(priv->dev, "%s() DAI %s id %d rate=%u format=%#x width=%u channels=%u\n", + __func__, dai->name, dai->id, fs, params ? params_format(params) : 0, + width, channels); + + /* on a hostless link, pcm might be allocated previously */ + if (priv->pcm[dai->id]) { + if (params) { + dev_err(priv->dev, "PCM running on id %d\n", dai->id); + return -EBUSY; + } + pcm = priv->pcm[dai->id]; + pcm->refcount++; + } else { + pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + if (!params) + pcm->is_hostless = true; + + INIT_LIST_HEAD(&pcm->list_head_in); + INIT_LIST_HEAD(&pcm->list_head_out); + INIT_LIST_HEAD(&pcm->dma.dma_in_list); + INIT_LIST_HEAD(&pcm->dma.dma_out_list); + pcm->refcount++; + priv->pcm[dai->id] = pcm; + } + dma = &pcm->dma; + + if (is_playback) { + /* for playback, this callback is for the OUT BEs */ + list_head_bes = &pcm->list_head_out; + } else { + /* for capture, this callback is for the IN BEs */ + list_head_bes = &pcm->list_head_in; + } + + INIT_LIST_HEAD(list_head_bes); + + ret = mchp_asrc_bes_get(priv, substream, list_head_bes, true); + if (ret < 0) + goto __cleanup_free_pcm; + + dev_dbg(priv->dev, "%s() BE triggers:\n", __func__); +#ifdef DEBUG + list_for_each_entry(rtm_be, list_head_bes, list) { + struct mchp_asrc_be_trigger *trig_be = rtm_be->trig; + + dev_dbg(priv->dev, "%s, index %d\n", trig_be->phandle->np->full_name, trig_be->idx); + } +#endif + + ret = mchp_asrc_validate_bes(priv, list_head_bes, channels); + if (ret < 0) + goto __cleanup_bes; + + if (!pcm->is_hostless) { + pcm->rtm_fe.hw_params = params; + pcm->rtm_fe.trig = NULL; + + /* keep the order */ + if (is_playback) + list_add_tail(&pcm->rtm_fe.list, &pcm->list_head_in); + else + list_add_tail(&pcm->rtm_fe.list, &pcm->list_head_out); + } + + if (list_empty(&pcm->list_head_in) || list_empty(&pcm->list_head_out)) { + /* incomplete BE-to-BE link; return and wait for the BEs on the other stream */ + dev_dbg(priv->dev, "%s() first half for %s finished\n", __func__, + is_playback ? "playback" : "capture"); + goto __skip_conf; + } + + ret = mchp_asrc_tc_get(priv, dai); + if (ret < 0) + goto __cleanup_bes; + + tc = pcm->tc; + + /* check if DSPs needed by this TC are free */ + for (i = 0; i < tc->thr_ch_count; i++) { + struct mchp_asrc_slot const *slot = &asrc_map[priv->thr_opmode].slot[tc->thr_ch[i]]; + int j; + + for (j = slot->first_dsp; j < slot->first_dsp + slot->dsp_count; j++) { + if (priv->dsp[j].dma) { + dev_err(priv->dev, "DSP %d already in use\n", j); + ret = -EBUSY; + goto __cleanup_free_tc; + } + } + } + + /* reserve DSPs */ + for (i = 0; i < tc->thr_ch_count; i++) { + struct mchp_asrc_slot const *slot = &asrc_map[priv->thr_opmode].slot[tc->thr_ch[i]]; + int j; + + for (j = slot->first_dsp; j < slot->first_dsp + slot->dsp_count; j++) + priv->dsp[j].dma = dma; + } + + /* assign in and out slots for AIFs */ + be_opm = &asrc_map[priv->rhr_opmode]; + i = 0; + list_for_each_entry(rtm_be, &pcm->list_head_out, list) { + dev_dbg(priv->dev, "%s(): AIF %s slot id %d\n", __func__, + rtm_be->trig ? rtm_be->trig->phandle->np->full_name : dai->name, + tc->rhr_ch[i]); + rtm_be->slot = &be_opm->slot[tc->rhr_ch[i++]]; + } + + be_opm = &asrc_map[priv->thr_opmode]; + i = 0; + list_for_each_entry(rtm_be, &pcm->list_head_in, list) { + dev_dbg(priv->dev, "%s(): AIF %s slot id %d\n", __func__, + rtm_be->trig ? rtm_be->trig->phandle->np->full_name : dai->name, + tc->thr_ch[i]); + rtm_be->slot = &be_opm->slot[tc->thr_ch[i++]]; + } + + /* set mono DSPs, trigger index and valid bits */ + list_for_each_entry(rtm_be, &pcm->list_head_in, list) { + struct mchp_asrc_be_trigger *trig_be = rtm_be->trig; + struct mchp_asrc_slot const *slot_be = rtm_be->slot; + int chan_be = params_channels(rtm_be->hw_params); + u32 idx = trig_be ? trig_be->idx : 0; + int physical_width = params_physical_width(rtm_be->hw_params) / 8; + int period_bytes = params_period_size(rtm_be->hw_params) * + params_channels(rtm_be->hw_params) * physical_width; + int maxburst = mchp_asrc_period_to_burst(period_bytes, physical_width); + + /* treat odd streams, set only last DSP as mono */ + if (chan_be % 2) { + dsp = slot_be->first_dsp + slot_be->dsp_count - 1; + ch_conf |= MCHP_ASRC_CH_CONF_MONO(dsp); + ch_conf_mask |= MCHP_ASRC_CH_CONF_MONO(dsp); + is_mono = true; + } + + for (dsp = slot_be->first_dsp; + dsp < slot_be->first_dsp + slot_be->dsp_count; dsp++) { + trig_mask |= MCHP_ASRC_TRIG_TRIGSELIN_MASK(dsp); + trig |= MCHP_ASRC_TRIG_TRIGSELIN(idx, dsp); + } + + src_width = params_width(rtm_be->hw_params); + for (dsp = slot_be->first_dsp; dsp < slot_be->first_dsp + slot_be->dsp_count; + dsp++) { + vbps_mask |= MCHP_ASRC_VBPS_MASK(dsp); + vbps_in |= mchp_asrc_vbps_to_reg(src_width, dsp); + } + + if (max_sr < params_rate(rtm_be->hw_params)) + max_sr = params_rate(rtm_be->hw_params); + + if (pcm->maxburst) + pcm->maxburst = min(pcm->maxburst, maxburst); + else + pcm->maxburst = maxburst; + + dev_dbg(priv->dev, "%s: period bytes %d, width %d -> maxburst %d\n", + trig_be ? trig_be->phandle->np->full_name : dai->name, + period_bytes, physical_width, maxburst); + } + + list_for_each_entry(rtm_be, &pcm->list_head_out, list) { + struct mchp_asrc_be_trigger *trig_be = rtm_be->trig; + struct mchp_asrc_slot const *slot_be = rtm_be->slot; + int chan_be = params_channels(rtm_be->hw_params); + int physical_width = params_physical_width(rtm_be->hw_params) / 8; + int period_bytes = params_period_size(rtm_be->hw_params) * + params_channels(rtm_be->hw_params) * physical_width; + int maxburst = mchp_asrc_period_to_burst(period_bytes, physical_width); + u32 idx = trig_be ? trig_be->idx : 0; + + /* treat odd streams, set only last DSP as mono */ + if (chan_be % 2) { + dsp = slot_be->first_dsp + slot_be->dsp_count - 1; + ch_conf |= MCHP_ASRC_CH_CONF_MONO(dsp); + ch_conf_mask |= MCHP_ASRC_CH_CONF_MONO(dsp); + is_mono = true; + } + + for (dsp = slot_be->first_dsp; + dsp < slot_be->first_dsp + slot_be->dsp_count; dsp++) { + trig_mask |= MCHP_ASRC_TRIG_TRIGSELOUT_MASK(dsp); + trig |= MCHP_ASRC_TRIG_TRIGSELOUT(idx, dsp); + } + + dst_width = params_width(rtm_be->hw_params); + for (dsp = slot_be->first_dsp; dsp < slot_be->first_dsp + slot_be->dsp_count; + dsp++) { + vbps_mask |= MCHP_ASRC_VBPS_MASK(dsp); + vbps_out |= mchp_asrc_vbps_to_reg(dst_width, dsp); + } + + if (max_sr < params_rate(rtm_be->hw_params)) + max_sr = params_rate(rtm_be->hw_params); + + if (pcm->maxburst) + pcm->maxburst = min(pcm->maxburst, maxburst); + else + pcm->maxburst = maxburst; + + dev_dbg(priv->dev, "%s: period bytes %d, width %d -> maxburst %d\n", + trig_be ? trig_be->phandle->np->full_name : dai->name, + period_bytes, physical_width, maxburst); + } + + mchp_asrc_maxburst_limit(priv->thr_opmode, priv->rhr_opmode, is_mono, &pcm->maxburst); + dev_dbg(priv->dev, "maxburst value to be used by all DSPs: %d\n", pcm->maxburst); + + /* set DMAengine data for each IN/OUT AIF */ + INIT_LIST_HEAD(&dma->dma_in_list); + list_for_each_entry(rtm_be, &pcm->list_head_in, list) { + struct mchp_asrc_be_trigger *trig_be = rtm_be->trig; + struct mchp_asrc_slot const *slot_be = rtm_be->slot; + struct snd_dmaengine_dai_dma_data *dma_data_be; + + dma_be = kzalloc(sizeof(*dma_be), GFP_KERNEL); + if (!dma_be) { + ret = -ENOMEM; + goto __cleanup_dma_bes; + } + + dma_be->phandle = trig_be ? trig_be->phandle : NULL; + dma_data_be = &dma_be->dma_data; + dma_data_be->addr = (dma_addr_t)priv->mem->start + MCHP_ASRC_THR(slot_be->slot_id); + dma_data_be->chan_name = kasprintf(GFP_KERNEL, "tx%d", slot_be->slot_id); + if (!dma_data_be->chan_name) { + ret = -ENOMEM; + goto __cleanup_dma_bes; + } + dma_data_be->maxburst = pcm->maxburst; + + /* keep the order of the BEs */ + list_add_tail(&dma_be->list, &dma->dma_in_list); + + dev_dbg(priv->dev, + "%s() IN AIF %s: DMA addr %pad DMA chan %s DMA maxburst %d\n", + __func__, trig_be ? trig_be->phandle->np->full_name : dai->name, + &dma_data_be->addr, dma_data_be->chan_name, dma_data_be->maxburst); + } + + INIT_LIST_HEAD(&dma->dma_out_list); + list_for_each_entry(rtm_be, &pcm->list_head_out, list) { + struct mchp_asrc_be_trigger *trig_be = rtm_be->trig; + struct mchp_asrc_slot const *slot_be = rtm_be->slot; + struct snd_dmaengine_dai_dma_data *dma_data_be; + int dsp; + + dma_be = kzalloc(sizeof(*dma_be), GFP_KERNEL); + if (!dma_be) { + ret = -ENOMEM; + goto __cleanup_dma_bes; + } + + dma_be->phandle = trig_be ? trig_be->phandle : NULL; + dma_data_be = &dma_be->dma_data; + dma_data_be->addr = (dma_addr_t)priv->mem->start + MCHP_ASRC_RHR(slot_be->slot_id); + dma_data_be->chan_name = kasprintf(GFP_KERNEL, "rx%d", slot_be->slot_id); + if (!dma_data_be->chan_name) { + ret = -ENOMEM; + goto __cleanup_dma_bes; + } + dma_data_be->maxburst = pcm->maxburst; + + /* needed only once, since the same DPSs are used for IN and OUT AIFs */ + for (dsp = slot_be->first_dsp; dsp < slot_be->first_dsp + slot_be->dsp_count; + dsp++) { + ch_conf |= mchp_asrc_burst_to_chunk(dsp, + pcm->maxburst); + ch_conf_mask |= MCHP_ASRC_CH_CONF_CHUNK_MASK(dsp); + } + + /* keep the order of the BEs */ + list_add_tail(&dma_be->list, &dma->dma_out_list); + + dev_dbg(priv->dev, + "%s() OUT AIF %s: DMA addr %pad DMA chan %s DMA maxburst %d\n", + __func__, trig_be ? trig_be->phandle->np->full_name : dai->name, + &dma_data_be->addr, dma_data_be->chan_name, dma_data_be->maxburst); + } + + /* get the number of free DSPs */ + regmap_read(priv->regmap, MCHP_ASRC_MR, &mr); + dsps_running = hweight32(mr & MCHP_ASRC_MR_ASRCEN_MASK); + + /* GCLK could be set to a good value using assigned clocks or by a previous DPCM */ + rate = clk_get_rate(priv->gclk); + if (!rate) { + dev_err(priv->dev, "Unable to get rate for gclk\n"); + ret = -ENODEV; + goto __cleanup_dma_bes; + } + dev_dbg(priv->dev, "Current gclk rate: %lu\n", rate); + + /* we assume GCLK is enabled if there are running DSPs */ + if (dsps_running) { + if (mr & MCHP_ASRC_MR_GT96K) + ratio_min = MCHP_ASRC_GT96K_RATIO_MIN; + else + ratio_min = MCHP_ASRC_LT96K_RATIO_MIN; + } else { + if (max_sr > 96000) { + ratio_min = MCHP_ASRC_GT96K_RATIO_MIN; + regmap_update_bits(priv->regmap, MCHP_ASRC_MR, MCHP_ASRC_MR_GT96K, + MCHP_ASRC_MR_GT96K); + } else { + ratio_min = MCHP_ASRC_LT96K_RATIO_MIN; + } + } + if (!dsps_running && ((rate % max_sr) || ratio_min > (rate / max_sr))) { + /* try to change GCLK if current value is not ok */ + unsigned long round_rate = (unsigned long)ratio_min * max_sr; + unsigned long best_rate; + unsigned long diff; + + for (rate = round_rate; + rate <= (unsigned long)ratio_min * MCHP_ASRC_RATIO_MAX; + rate += max_sr) { + round_rate = clk_round_rate(priv->gclk, rate); + if (diff > abs(round_rate - rate)) { + diff = abs(round_rate - rate); + best_rate = rate; + } + if (!diff) + break; + } + + dev_dbg(priv->dev, "%s() trying to set GCLK to %lu\n", __func__, best_rate); + ret = clk_set_rate(priv->gclk, best_rate); + if (ret) { + dev_err(priv->dev, "unable to set GCLK rate: SR %u * ratio %lu\n", + max_sr, (best_rate / max_sr)); + goto __cleanup_mr; + } + } + + /* get real GCLK rate, to set the correct ratio */ + rate = clk_get_rate(priv->gclk); + if (!rate) { + dev_err(priv->dev, "Unable to get rate for GCLK\n"); + ret = -ENODEV; + goto __cleanup_mr; + } + dev_dbg(priv->dev, "Real GCLK rate: %lu\n", rate); + + ret = clk_prepare_enable(priv->gclk); + if (ret < 0) { + dev_err(priv->dev, "unable to enable GCLK: %d\n", ret); + goto __cleanup_mr; + } + + /* IN/OUT RATIO are not needed on a hostless PCM */ + if (pcm->is_hostless) + goto __skip_inout; + + /* set IN/OUT RATIO for all DSPs in the FE slot */ + if (is_playback) + list_head_bes = &pcm->list_head_in; + else + list_head_bes = &pcm->list_head_out; + + list_for_each_entry(rtm_be, list_head_bes, list) { + struct mchp_asrc_slot const *slot_fe = rtm_be->slot; + + ratio = rate / params_rate(rtm_be->hw_params); + for (dsp = slot_fe->first_dsp; dsp < slot_fe->first_dsp + slot_fe->dsp_count; + dsp++) { + if (is_playback) { + regmap_update_bits(priv->regmap, MCHP_ASRC_RATIO(dsp), + MCHP_ASRC_RATIO_INRATIO_MASK, + MCHP_ASRC_RATIO_INRATIO(ratio)); + } else { + regmap_update_bits(priv->regmap, MCHP_ASRC_RATIO(dsp), + MCHP_ASRC_RATIO_OUTRATIO_MASK, + MCHP_ASRC_RATIO_OUTRATIO(ratio)); + } + } + } + +__skip_inout: + dev_dbg(priv->dev, "writing TRIG %#x, VBPS_IN %#x, VBPS_OUT %#x, CH_CONF %#x\n", + trig & trig_mask, vbps_in & vbps_mask, vbps_out & vbps_mask, + ch_conf & ch_conf_mask); + + regmap_update_bits(priv->regmap, MCHP_ASRC_TRIG, trig_mask, trig); + regmap_update_bits(priv->regmap, MCHP_ASRC_VBPS_IN, vbps_mask, vbps_in); + regmap_update_bits(priv->regmap, MCHP_ASRC_VBPS_OUT, vbps_mask, vbps_out); + regmap_update_bits(priv->regmap, MCHP_ASRC_CH_CONF, ch_conf_mask, ch_conf); + + snd_soc_dai_init_dma_data(dai, dma, dma); +__skip_conf: + + list_for_each_entry(rtm_be, &pcm->list_head_in, list) { + dev_dbg(priv->dev, "%s(): looking at IN %s\n", __func__, + rtm_be->trig ? rtm_be->trig->phandle->np->full_name : "front-end"); + } + list_for_each_entry(rtm_be, &pcm->list_head_out, list) { + dev_dbg(priv->dev, "%s(): looking at OUT %s\n", __func__, + rtm_be->trig ? rtm_be->trig->phandle->np->full_name : "front-end"); + } + return 0; + +__cleanup_mr: + if (!dsps_running) + regmap_update_bits(priv->regmap, MCHP_ASRC_MR, MCHP_ASRC_MR_GT96K, 0); +__cleanup_dma_bes: + list_for_each_entry_safe(dma_be, tmp, &dma->dma_in_list, list) { + list_del(&dma_be->list); + kfree(dma_be->dma_data.chan_name); + dma_be->dma_data.chan_name = NULL; + kfree(dma_be); + } + list_for_each_entry_safe(dma_be, tmp, &dma->dma_out_list, list) { + list_del(&dma_be->list); + kfree(dma_be->dma_data.chan_name); + dma_be->dma_data.chan_name = NULL; + kfree(dma_be); + } + + list_for_each_entry(rtm_be, &pcm->list_head_in, list) + rtm_be->slot = NULL; + list_for_each_entry(rtm_be, &pcm->list_head_out, list) + rtm_be->slot = NULL; + for (i = 0; i < pcm->tc->thr_ch_count; i++) { + struct mchp_asrc_slot const *slot = &asrc_map[priv->thr_opmode].slot[tc->thr_ch[i]]; + int j; + + for (j = slot->first_dsp; j < slot->first_dsp + slot->dsp_count; j++) + priv->dsp[j].dma = NULL; + } +__cleanup_free_tc: + mchp_asrc_tc_free(priv, pcm); +__cleanup_bes: + if (is_playback) + INIT_LIST_HEAD(&pcm->list_head_out); + else + INIT_LIST_HEAD(&pcm->list_head_in); +__cleanup_free_pcm: + pcm->refcount--; + if (!pcm->refcount) { + kfree(pcm); + priv->pcm[dai->id] = NULL; + } + + return ret; +} + +static int mchp_asrc_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct mchp_asrc_dev *priv = snd_soc_dai_get_drvdata(dai); + struct mchp_asrc_dmaengine_be_dma *dma_be, *tmp; + struct mchp_asrc_dmaengine_dai_dma *dma; + struct mchp_asrc_be_rtm *rtm_be; + struct xmit_ch *tc; + struct list_head *list_head_fes; + bool is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + u32 val = 0; + u32 mask = 0; + u32 ch_conf_mask = 0; + int dsp; + struct mchp_asrc_pcm_priv *pcm = priv->pcm[dai->id]; + bool are_dsps_running; + int i; + + /* nothing to do if no pcm is available */ + if (!pcm) + return 0; + + regmap_read(priv->regmap, MCHP_ASRC_MR, &val); + are_dsps_running = !!(val & MCHP_ASRC_MR_ASRCEN_MASK); + + if (!pcm->is_hostless) { + if (is_playback) + list_head_fes = &pcm->list_head_in; + else + list_head_fes = &pcm->list_head_out; + + list_for_each_entry(rtm_be, list_head_fes, list) { + struct mchp_asrc_slot const *slot_fe = rtm_be->slot; + + for (dsp = slot_fe->first_dsp; + dsp < slot_fe->first_dsp + slot_fe->dsp_count; dsp++) { + if (is_playback) { + regmap_update_bits(priv->regmap, MCHP_ASRC_RATIO(dsp), + MCHP_ASRC_RATIO_INRATIO_MASK, 0); + } else { + regmap_update_bits(priv->regmap, MCHP_ASRC_RATIO(dsp), + MCHP_ASRC_RATIO_OUTRATIO_MASK, 0); + } + } + } + } + + if (pcm->refcount == 2 || !pcm->is_hostless) { + /* unset DMAengine data */ + snd_soc_dai_init_dma_data(dai, NULL, NULL); + + clk_disable_unprepare(priv->gclk); + + /* cleanup BE channel names */ + dma = &pcm->dma; + list_for_each_entry_safe(dma_be, tmp, &dma->dma_in_list, list) { + struct snd_dmaengine_dai_dma_data *dma_data_be; + + list_del(&dma_be->list); + dma_data_be = &dma_be->dma_data; + kfree(dma_data_be->chan_name); + dma_data_be->chan_name = NULL; + kfree(dma_be); + } + + /* free DSPs */ + tc = pcm->tc; + for (i = 0; i < tc->thr_ch_count; i++) { + struct mchp_asrc_slot const *slot; + int j; + + slot = &asrc_map[priv->thr_opmode].slot[tc->thr_ch[i]]; + for (j = slot->first_dsp; j < slot->first_dsp + slot->dsp_count; j++) + priv->dsp[j].dma = NULL; + } + list_for_each_entry_safe(dma_be, tmp, &dma->dma_out_list, list) { + struct snd_dmaengine_dai_dma_data *dma_data_be; + + list_del(&dma_be->list); + dma_data_be = &dma_be->dma_data; + kfree(dma_data_be->chan_name); + dma_data_be->chan_name = NULL; + kfree(dma_be); + } + + /* free mono DSPs, trigger index, and chunk size */ + list_for_each_entry(rtm_be, &pcm->list_head_in, list) { + struct mchp_asrc_slot const *slot_be = rtm_be->slot; + + for (dsp = slot_be->first_dsp; + dsp < slot_be->first_dsp + slot_be->dsp_count; dsp++) { + ch_conf_mask |= MCHP_ASRC_CH_CONF_MONO(dsp); + mask |= MCHP_ASRC_TRIG_TRIGSELIN_MASK(dsp); + } + ch_conf_mask |= MCHP_ASRC_CH_CONF_CHUNK_MASK(slot_be->slot_id); + rtm_be->slot = NULL; + } + list_for_each_entry(rtm_be, &pcm->list_head_out, list) { + struct mchp_asrc_slot const *slot_be = rtm_be->slot; + + for (dsp = slot_be->first_dsp; + dsp < slot_be->first_dsp + slot_be->dsp_count; dsp++) { + ch_conf_mask |= MCHP_ASRC_CH_CONF_MONO(dsp); + mask |= MCHP_ASRC_TRIG_TRIGSELOUT_MASK(dsp); + } + ch_conf_mask |= MCHP_ASRC_CH_CONF_CHUNK_MASK(slot_be->slot_id); + rtm_be->slot = NULL; + } + + mchp_asrc_tc_free(priv, pcm); + } + + pcm->refcount--; + if (!pcm->refcount) { + kfree(pcm); + priv->pcm[dai->id] = NULL; + } + + if (!are_dsps_running) + regmap_update_bits(priv->regmap, MCHP_ASRC_MR, MCHP_ASRC_MR_GT96K, 0); + + /* cleanup the triggers for the used DSPs */ + regmap_update_bits(priv->regmap, MCHP_ASRC_TRIG, mask, 0); + + /* cleanup CHUNK and MONO DSPs */ + regmap_update_bits(priv->regmap, MCHP_ASRC_CH_CONF, ch_conf_mask, 0); + + return 0; +} + +#ifdef DEBUG +static void mchp_asrc_reg_dump(struct mchp_asrc_dev *priv) +{ + int i; + u32 val; + + regmap_read(priv->regmap, MCHP_ASRC_MR, &val); + dev_dbg(priv->dev, "%s() Mode register %#x\n", __func__, val); + for (i = 0; i < MCHP_ASRC_NB_STEREO_CH; i++) { + regmap_read(priv->regmap, MCHP_ASRC_RATIO(i), &val); + dev_dbg(priv->dev, "%s() Ratio%d register %#x\n", __func__, i, val); + } + regmap_read(priv->regmap, MCHP_ASRC_VBPS_IN, &val); + dev_dbg(priv->dev, "%s() VBPS in register %#x\n", __func__, val); + regmap_read(priv->regmap, MCHP_ASRC_VBPS_OUT, &val); + dev_dbg(priv->dev, "%s() VBPS out register %#x\n", __func__, val); + regmap_read(priv->regmap, MCHP_ASRC_CH_CONF, &val); + dev_dbg(priv->dev, "%s() Channel conf register %#x\n", __func__, val); + regmap_read(priv->regmap, MCHP_ASRC_TRIG, &val); + dev_dbg(priv->dev, "%s() Trigger sel register %#x\n", __func__, val); + for (i = 0; i < MCHP_ASRC_NB_STEREO_CH; i++) { + regmap_read(priv->regmap, MCHP_ASRC_IMR(i), &val); + dev_dbg(priv->dev, "%s() Interrupt mask register%d %#x\n", __func__, i, val); + } +} +#else +static void mchp_asrc_reg_dump(struct mchp_asrc_dev *priv) {} +#endif + +static void mchp_asrc_dsps_trigger(struct mchp_asrc_dev *priv, struct mchp_asrc_be_rtm *rtm_be, + bool enable, u32 *mr) +{ + struct mchp_asrc_slot const *slot_be = rtm_be->slot; + u32 ir_reg = 0; + u32 ir = 0; + int dsp; + + if (!slot_be) { + dev_dbg(priv->dev, "%s has no slot\n", + rtm_be->trig ? rtm_be->trig->phandle->np->full_name : "front-end"); + return; + } +#ifdef DEBUG + ir |= MCHP_ASRC_IR_RXUDR | MCHP_ASRC_IR_RXOVR | MCHP_ASRC_IR_TXUDR | MCHP_ASRC_IR_TXOVR; +#endif + + if (enable) + ir |= MCHP_ASRC_IR_LOCK; + + dev_dbg(priv->dev, "%s() DSPs %s for BE %s:\n", __func__, enable ? "enabled" : "disabled", + rtm_be->trig ? rtm_be->trig->phandle->np->full_name : "front-end"); + + for (dsp = slot_be->first_dsp; dsp < slot_be->first_dsp + slot_be->dsp_count; dsp++) { + /* Enable interrupt and wait for DPLL LOCK */ + if (enable) + ir_reg = MCHP_ASRC_IER(dsp); + else + ir_reg = MCHP_ASRC_IDR(dsp); + regmap_update_bits(priv->regmap, ir_reg, ir, ir); + *mr |= MCHP_ASRC_MR_ASRCEN(dsp); + if (enable) + priv->dsp[dsp].dma->unlocked_dsps++; + dev_dbg(priv->dev, "%s() %d\n", __func__, dsp); + } +} + +static int mchp_asrc_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) +{ + struct mchp_asrc_dev *priv = snd_soc_dai_get_drvdata(dai); + struct mchp_asrc_pcm_priv *pcm = priv->pcm[dai->id]; + struct mchp_asrc_dmaengine_dai_dma *dma; + struct mchp_asrc_be_rtm *rtm_be; + u32 mr = 0; + + if (!pcm) { + dev_err(priv->dev, "PCM not allocated for id %d\n", dai->id); + return -EINVAL; + } + + dma = &pcm->dma; + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + list_for_each_entry(rtm_be, &pcm->list_head_in, list) + mchp_asrc_dsps_trigger(priv, rtm_be, true, &mr); + list_for_each_entry(rtm_be, &pcm->list_head_out, list) + mchp_asrc_dsps_trigger(priv, rtm_be, true, &mr); + dma->unlocked_dsps /= 2; + regmap_update_bits(priv->regmap, MCHP_ASRC_MR, mr, mr); + mchp_asrc_reg_dump(priv); + return 0; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + list_for_each_entry(rtm_be, &pcm->list_head_out, list) + mchp_asrc_dsps_trigger(priv, rtm_be, false, &mr); + list_for_each_entry(rtm_be, &pcm->list_head_in, list) + mchp_asrc_dsps_trigger(priv, rtm_be, false, &mr); + + regmap_update_bits(priv->regmap, MCHP_ASRC_MR, mr, 0); + mchp_asrc_reg_dump(priv); + return 0; + default: + return -EINVAL; + } +} + +static const struct snd_soc_dai_ops mchp_asrc_dai_ops = { + .startup = mchp_asrc_startup, + .trigger = mchp_asrc_trigger, + .hw_params = mchp_asrc_hw_params, + .hw_free = mchp_asrc_hw_free, + .shutdown = mchp_asrc_shutdown, +}; + +#define MCHP_ASRC_RATES SNDRV_PCM_RATE_8000_192000 +#define MCHP_ASRC_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static struct snd_soc_dai_driver mchp_asrc_dai[] = { + { + .name = "asrc-pcm-1", + .playback = { + .stream_name = "ASRC-Playback 1", + .channels_min = 1, + .channels_max = 8, + .rates = MCHP_ASRC_RATES, + .formats = MCHP_ASRC_FORMATS, + }, + .capture = { + .stream_name = "ASRC-Capture 1", + .channels_min = 1, + .channels_max = 8, + .rates = MCHP_ASRC_RATES, + .formats = MCHP_ASRC_FORMATS, + }, + .ops = &mchp_asrc_dai_ops, + }, + { + .name = "asrc-pcm-2", + .playback = { + .stream_name = "ASRC-Playback 2", + .channels_min = 1, + .channels_max = 8, + .rates = MCHP_ASRC_RATES, + .formats = MCHP_ASRC_FORMATS, + }, + .capture = { + .stream_name = "ASRC-Capture 2", + .channels_min = 1, + .channels_max = 8, + .rates = MCHP_ASRC_RATES, + .formats = MCHP_ASRC_FORMATS, + }, + .ops = &mchp_asrc_dai_ops, + }, + { + .name = "asrc-pcm-3", + .playback = { + .stream_name = "ASRC-Playback 3", + .channels_min = 1, + .channels_max = 8, + .rates = MCHP_ASRC_RATES, + .formats = MCHP_ASRC_FORMATS, + }, + .capture = { + .stream_name = "ASRC-Capture 3", + .channels_min = 1, + .channels_max = 8, + .rates = MCHP_ASRC_RATES, + .formats = MCHP_ASRC_FORMATS, + }, + .ops = &mchp_asrc_dai_ops, + }, + { + .name = "asrc-pcm-4", + .playback = { + .stream_name = "ASRC-Playback 4", + .channels_min = 1, + .channels_max = 8, + .rates = MCHP_ASRC_RATES, + .formats = MCHP_ASRC_FORMATS, + }, + .capture = { + .stream_name = "ASRC-Capture 4", + .channels_min = 1, + .channels_max = 8, + .rates = MCHP_ASRC_RATES, + .formats = MCHP_ASRC_FORMATS, + }, + .ops = &mchp_asrc_dai_ops, + }, +}; + +static void mchp_asrc_block_pending_intr(struct mchp_asrc_dev *priv, + u32 pending, int block) +{ + struct device *dev = priv->dev; + +#ifdef DEBUG + if (pending & MCHP_ASRC_IR_RXRDY) + dev_info_ratelimited(dev, "block %d: RX Ready detected\n", block); + + if (pending & MCHP_ASRC_IR_RXEMPTY) + dev_warn_ratelimited(dev, "block %d: RX FIFO Empty detected\n", block); + + if (pending & MCHP_ASRC_IR_RXFULL) + dev_warn_ratelimited(dev, "block %d: RX FIFO Full detected\n", block); + + if (pending & MCHP_ASRC_IR_RXCHUNK) + dev_info_ratelimited(dev, "block %d: RX FIFO Chunk detected\n", block); + + if (pending & MCHP_ASRC_IR_TXRDY) + dev_info_ratelimited(dev, "block %d: TX Ready detected\n", block); + + if (pending & MCHP_ASRC_IR_TXEMPTY) + dev_warn_ratelimited(dev, "block %d: TX FIFO Empty detected\n", block); + + if (pending & MCHP_ASRC_IR_TXFULL) + dev_warn_ratelimited(dev, "block %d: TX FIFO Full detected\n", block); + + if (pending & MCHP_ASRC_IR_TXCHUNK) + dev_info_ratelimited(dev, "block %d: TX FIFO Chunk detected\n", block); + +#endif + if (pending & MCHP_ASRC_IR_RXUDR) + dev_err_ratelimited(dev, "block %d: RX Underflow detected\n", block); + + if (pending & MCHP_ASRC_IR_RXOVR) + dev_err_ratelimited(dev, "block %d: RX Overflow detected\n", block); + + if (pending & MCHP_ASRC_IR_TXUDR) + dev_err_ratelimited(dev, "block %d: TX Underflow detected\n", block); + + if (pending & MCHP_ASRC_IR_TXOVR) + dev_err_ratelimited(dev, "block %d: TX Overflow detected\n", block); + + if (pending & MCHP_ASRC_IR_LOCK) { + dev_dbg_ratelimited(dev, "block %d: DPLL locked detected\n", block); + /* Disable DPLL LOCK interrupt */ + regmap_update_bits(priv->regmap, MCHP_ASRC_IDR(block), + MCHP_ASRC_IR_LOCK, MCHP_ASRC_IR_LOCK); + + if (!priv->dsp[block].dma) { + dev_err_ratelimited(dev, "block %d: DSP locked but no PCM assigned\n", + block); + return; + } + + if (!priv->dsp[block].dma->unlocked_dsps) + return; + priv->dsp[block].dma->unlocked_dsps--; + + /* DPLLs locked, start DMA transfers */ + if (!priv->dsp[block].dma->unlocked_dsps) { + dev_dbg_ratelimited(dev, "block %d: DSPs ready, calling DMA handle\n", + block); + tasklet_schedule(&priv->dsp[block].dma->start_handle); + } else { + dev_dbg_ratelimited(dev, "block %d: waiting for %d more DSPs to lock\n", + block, priv->dsp[block].dma->unlocked_dsps); + } + } +} + +static void mchp_asrc_block_interrupts(struct mchp_asrc_dev *priv, int block, + irqreturn_t *ret) +{ + u32 isr, imr, pending; + + regmap_read(priv->regmap, MCHP_ASRC_ISR(block), &isr); + regmap_read(priv->regmap, MCHP_ASRC_IMR(block), &imr); + dev_dbg_ratelimited(priv->dev, "IRQ handler: IMR %#x ISR %#x\n", imr, isr); + + pending = isr & imr; + if (!pending) + return; + + if (pending & MCHP_ASRC_IR_SECE) { + dev_err_ratelimited(priv->dev, "block %d: Security/Safety Event detected\n", block); + *ret = IRQ_HANDLED; + } + + mchp_asrc_block_pending_intr(priv, pending, block); + *ret = IRQ_HANDLED; +} + +static irqreturn_t mchp_asrc_interrupt(int irq, void *dev_id) +{ + struct mchp_asrc_dev *priv = dev_id; + irqreturn_t ret = IRQ_NONE; + int block; + + for (block = 0; block < MCHP_ASRC_NB_STEREO_CH; block++) + mchp_asrc_block_interrupts(priv, block, &ret); + + return ret; +} + +static int mchp_asrc_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct mchp_asrc_dev *priv; + struct regmap *regmap; + void __iomem *base; + struct property *prop; + const __be32 *cur; + u32 pv; + u32 version; + int irq; + int err; + int count; + int i; + + /* Get memory for driver data */ + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* Map I/O registers */ + base = devm_platform_get_and_ioremap_resource(pdev, 0, &priv->mem); + if (IS_ERR(base)) + return PTR_ERR(base); + + regmap = devm_regmap_init_mmio(&pdev->dev, base, &mchp_asrc_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&pdev->dev, "failed to init regmap: %ld\n", PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + priv->dev = &pdev->dev; + priv->regmap = regmap; + + /* Request IRQ */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get IRQ no: %d\n", irq); + return irq; + } + + err = devm_request_irq(&pdev->dev, irq, mchp_asrc_interrupt, 0, dev_name(&pdev->dev), priv); + if (err) { + dev_err(&pdev->dev, "failed to claim IRQ %d: %d\n", irq, err); + return err; + } + + priv->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(priv->pclk)) { + dev_err(&pdev->dev, "failed to get pclk clock: %ld\n", PTR_ERR(priv->pclk)); + return PTR_ERR(priv->pclk); + } + + priv->gclk = devm_clk_get(&pdev->dev, "gclk"); + if (IS_ERR(priv->gclk)) { + dev_err(&pdev->dev, "failed to get gclk clock: %ld\n", PTR_ERR(priv->gclk)); + return PTR_ERR(priv->gclk); + } + + /* get the triggers list */ + priv->trig_count = of_count_phandle_with_args(np, "microchip,triggers", NULL); + if (priv->trig_count < 0) { + dev_err(&pdev->dev, "failed to get microchip,triggers: %d\n", + priv->trig_count); + return priv->trig_count; + } + if (priv->trig_count == 0) { + dev_err(&pdev->dev, "no triggers found\n"); + return -EINVAL; + } + dev_dbg(&pdev->dev, "found %d triggers:\n", priv->trig_count); + priv->trig = devm_kcalloc(&pdev->dev, priv->trig_count, sizeof(*priv->trig), GFP_KERNEL); + if (!priv->trig) + return -ENOMEM; + + for (i = 0; i < priv->trig_count; i++) { + struct mchp_asrc_be_trigger *trig_be = &priv->trig[i]; + + trig_be->phandle = devm_kzalloc(&pdev->dev, sizeof(*trig_be->phandle), GFP_KERNEL); + if (!trig_be->phandle) { + err = -ENOMEM; + goto __cleanup_of_phandle_triggers; + } + err = of_parse_phandle_with_fixed_args(np, "microchip,triggers", + 0, i, trig_be->phandle); + if (err < 0) { + dev_err(&pdev->dev, "failed to get phandle for trigger %d: %d\n", i, err); + devm_kfree(&pdev->dev, trig_be->phandle); + trig_be->phandle = NULL; + continue; + } + } + count = 0; + of_property_for_each_u32(np, "microchip,trigger-indexes", prop, cur, pv) { + struct mchp_asrc_be_trigger *trig_be = &priv->trig[count]; + + if (pv >= MCHP_ASRC_TRIG_IDX_MAX_VAL) { + dev_err(&pdev->dev, + "error: microchip,trigger-indexes too big: %d\n", + pv); + err = -EINVAL; + goto __cleanup_of_phandle_triggers; + } + + /* skip not found phandle */ + if (!trig_be->phandle) + continue; + + trig_be->idx = pv; + trig_be->priv = priv; + count++; + if (count > priv->trig_count) { + dev_err(&pdev->dev, + "Too many microchip,trigger-indexes: %d\n", count); + err = -EINVAL; + goto __cleanup_of_phandle_triggers; + } + } + +#ifdef DEBUG + dev_dbg(&pdev->dev, "Triggers %d:\n", count); + for (i = 0; i < count; i++) { + dev_dbg(&pdev->dev, "%s, index %d\n", priv->trig[i].phandle->np->full_name, + priv->trig[i].idx); + } +#endif + priv->thr_opmode = -1; + priv->rhr_opmode = -1; + + platform_set_drvdata(pdev, priv); + + err = devm_snd_soc_register_component(&pdev->dev, &mchp_asrc_component, mchp_asrc_dai, + ARRAY_SIZE(mchp_asrc_dai)); + if (err) { + dev_err(&pdev->dev, "failed to register ASoC DAI: %d\n", err); + clk_disable_unprepare(priv->pclk); + goto __cleanup_of_phandle_triggers; + } + + err = mchp_asrc_pcm_register(&pdev->dev); + if (err) { + dev_err(&pdev->dev, "failed to register ASoC platform\n"); + goto __cleanup_of_phandle_triggers; + } + + /* Enable the peripheral clock */ + err = clk_prepare_enable(priv->pclk); + if (err) { + dev_err(&pdev->dev, "failed to enable the peripheral clock: %d\n", err); + goto __cleanup_of_phandle_triggers; + } + + /* Software reset for all registers */ + regmap_write(priv->regmap, MCHP_ASRC_CR, MCHP_ASRC_CR_SWRST); + + /* Get IP version. */ + regmap_read(priv->regmap, MCHP_ASRC_VERSION, &version); + + dev_info(priv->dev, "hw version: %#lx\n", version & MCHP_ASRC_VERSION_MASK); + + return 0; + +__cleanup_of_phandle_triggers: + for (i = 0; i < priv->trig_count; i++) { + struct mchp_asrc_be_trigger *trig_be = &priv->trig[i]; + + if (trig_be->phandle) + of_node_put(trig_be->phandle->np); + } + return err; +} + +static int mchp_asrc_remove(struct platform_device *pdev) +{ + struct mchp_asrc_dev *priv = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < priv->trig_count; i++) { + struct mchp_asrc_be_trigger *trig_be = &priv->trig[i]; + + if (trig_be->phandle) + of_node_put(trig_be->phandle->np); + } + clk_disable_unprepare(priv->pclk); + + return 0; +} + +static const struct of_device_id mchp_asrc_dt_ids[] = { + { + .compatible = "microchip,sama7g5-asrc", + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mchp_asrc_dt_ids); + +static struct platform_driver mchp_asrc_driver = { + .driver = { + .name = "mchp_asrc", + .of_match_table = of_match_ptr(mchp_asrc_dt_ids), + }, + .probe = mchp_asrc_probe, + .remove = mchp_asrc_remove, +}; +module_platform_driver(mchp_asrc_driver); + +MODULE_DESCRIPTION("Microchip Asynchronous Sample Rate Converter (ASRC) driver"); +MODULE_AUTHOR("Codrin Ciubotariu <codrin.ciubotariu@microchip.com>"); +MODULE_LICENSE("GPL v2"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/sound/soc/atmel/mchp-asrc-card.c linux-microchip/sound/soc/atmel/mchp-asrc-card.c --- linux-6.6.51/sound/soc/atmel/mchp-asrc-card.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/sound/soc/atmel/mchp-asrc-card.c 2024-11-26 14:02:39.366527160 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +// ASoC Sound Card driver for Microchip's ASRC +// +// Copyright (C) 2018-2022 Microchip Technology Inc. and its subsidiaries +// +// Author: Codrin Ciubotariu <codrin.ciubotariu@microchip.com> + +#include <dt-bindings/sound/microchip,asrc-card.h> +#include <linux/device.h> +#include <linux/export.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +#include <sound/control.h> +#include <sound/pcm_params.h> +#include <sound/simple_card_utils.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> +#include <sound/soc-dapm.h> + +#include "../codecs/ad193x.h" +#include "../codecs/wm8731.h" + +#define MCHP_ASRC_DSPS 4 +#define XTAL_RATE 12288000 /* used for Mikroe Proto board*/ + +#define MCHP_PERIOD_SIZE_MIN 16384u /* Workaround, calculated by trial and error */ + +static const char * const mchp_asrc_ctrl_playback_texts[] = {"Off", + "ASRC Playback PCM 1 BE 1", "ASRC Playback PCM 1 BE 2", + "ASRC Playback PCM 1 BE 3", "ASRC Playback PCM 1 BE 4", + "ASRC Playback PCM 2 BE 1", "ASRC Playback PCM 2 BE 2", + "ASRC Playback PCM 2 BE 3", "ASRC Playback PCM 2 BE 4", + "ASRC Playback PCM 3 BE 1", "ASRC Playback PCM 3 BE 2", + "ASRC Playback PCM 3 BE 3", "ASRC Playback PCM 3 BE 4", + "ASRC Playback PCM 4 BE 1", "ASRC Playback PCM 4 BE 2", + "ASRC Playback PCM 4 BE 3", "ASRC Playback PCM 4 BE 4", +}; + +static const char * const mchp_asrc_ctrl_capture_texts[] = {"Off", + "ASRC Capture PCM 1 BE 1", "ASRC Capture PCM 1 BE 2", + "ASRC Capture PCM 1 BE 3", "ASRC Capture PCM 1 BE 4", + "ASRC Capture PCM 2 BE 1", "ASRC Capture PCM 2 BE 2", + "ASRC Capture PCM 2 BE 3", "ASRC Capture PCM 2 BE 4", + "ASRC Capture PCM 3 BE 1", "ASRC Capture PCM 3 BE 2", + "ASRC Capture PCM 3 BE 3", "ASRC Capture PCM 3 BE 4", + "ASRC Capture PCM 4 BE 1", "ASRC Capture PCM 4 BE 2", + "ASRC Capture PCM 4 BE 3", "ASRC Capture PCM 4 BE 4", +}; + +static const struct soc_enum mchp_asrc_playback_enum = + SOC_ENUM_SINGLE_VIRT(ARRAY_SIZE(mchp_asrc_ctrl_playback_texts), + mchp_asrc_ctrl_playback_texts); + +static const struct soc_enum mchp_asrc_capture_enum = + SOC_ENUM_SINGLE_VIRT(ARRAY_SIZE(mchp_asrc_ctrl_capture_texts), + mchp_asrc_ctrl_capture_texts); + +static const struct snd_soc_dapm_route audio_playback_map[MCHP_ASRC_DSPS][MCHP_ASRC_DSPS] = { + { + {NULL, "ASRC Playback PCM 1 BE 1", "ASRC-Playback 1"}, + {NULL, "ASRC Playback PCM 2 BE 1", "ASRC-Playback 2"}, + {NULL, "ASRC Playback PCM 3 BE 1", "ASRC-Playback 3"}, + {NULL, "ASRC Playback PCM 4 BE 1", "ASRC-Playback 4"}, + }, { + {NULL, "ASRC Playback PCM 1 BE 2", "ASRC-Playback 1"}, + {NULL, "ASRC Playback PCM 2 BE 2", "ASRC-Playback 2"}, + {NULL, "ASRC Playback PCM 3 BE 2", "ASRC-Playback 3"}, + {NULL, "ASRC Playback PCM 4 BE 2", "ASRC-Playback 4"}, + }, { + {NULL, "ASRC Playback PCM 1 BE 3", "ASRC-Playback 1"}, + {NULL, "ASRC Playback PCM 2 BE 3", "ASRC-Playback 2"}, + {NULL, "ASRC Playback PCM 3 BE 3", "ASRC-Playback 3"}, + {NULL, "ASRC Playback PCM 4 BE 3", "ASRC-Playback 4"}, + }, { + {NULL, "ASRC Playback PCM 1 BE 4", "ASRC-Playback 1"}, + {NULL, "ASRC Playback PCM 2 BE 4", "ASRC-Playback 2"}, + {NULL, "ASRC Playback PCM 3 BE 4", "ASRC-Playback 3"}, + {NULL, "ASRC Playback PCM 4 BE 4", "ASRC-Playback 4"}, + } +}; + +static const struct snd_soc_dapm_route audio_capture_map[MCHP_ASRC_DSPS][MCHP_ASRC_DSPS] = { + { + {"ASRC-Capture 1", "ASRC Capture PCM 1 BE 1", NULL}, + {"ASRC-Capture 2", "ASRC Capture PCM 2 BE 1", NULL}, + {"ASRC-Capture 3", "ASRC Capture PCM 3 BE 1", NULL}, + {"ASRC-Capture 4", "ASRC Capture PCM 4 BE 1", NULL}, + }, { + {"ASRC-Capture 1", "ASRC Capture PCM 1 BE 2", NULL}, + {"ASRC-Capture 2", "ASRC Capture PCM 2 BE 2", NULL}, + {"ASRC-Capture 3", "ASRC Capture PCM 3 BE 2", NULL}, + {"ASRC-Capture 4", "ASRC Capture PCM 4 BE 2", NULL}, + }, { + {"ASRC-Capture 1", "ASRC Capture PCM 1 BE 3", NULL}, + {"ASRC-Capture 2", "ASRC Capture PCM 2 BE 3", NULL}, + {"ASRC-Capture 3", "ASRC Capture PCM 3 BE 3", NULL}, + {"ASRC-Capture 4", "ASRC Capture PCM 4 BE 3", NULL}, + }, { + {"ASRC-Capture 1", "ASRC Capture PCM 1 BE 4", NULL}, + {"ASRC-Capture 2", "ASRC Capture PCM 2 BE 4", NULL}, + {"ASRC-Capture 3", "ASRC Capture PCM 3 BE 4", NULL}, + {"ASRC-Capture 4", "ASRC Capture PCM 4 BE 4", NULL}, + } +}; + +#define PREFIX "microchip," + +struct mchp_dai_priv { + int slots; + int slot_width; +}; + +struct mchp_dai_link_priv { + int id; + struct mchp_dai_priv cpu; + struct mchp_dai_priv codec; + int convert_rate; + int convert_channels; + int convert_format; +}; + +struct mchp_card_priv { + struct mchp_dai_link_priv *dai_link; + int ctrl_playback_no; + int ctrl_capture_no; +}; + +static int mchp_asrc_card_add_rtm_route(struct snd_soc_card *card, + struct snd_soc_pcm_runtime *rtd, + struct snd_soc_dapm_route *route, + int *aifs_added) +{ + struct snd_soc_dapm_context *dapm = &card->dapm; + struct mchp_card_priv *card_priv = snd_soc_card_get_drvdata(card); + struct snd_soc_dai *cpu_dai; + struct snd_kcontrol_new *control; + struct snd_soc_dapm_widget *widget; + int aifs = card_priv->ctrl_playback_no + card_priv->ctrl_capture_no; + char *aux; + int i, j, k; + int err; + + for_each_rtd_cpu_dais(rtd, i, cpu_dai) { + int route_pos; + bool codec_playback_avail = false; + bool codec_capture_avail = false; + + for (j = 0; j < rtd->dai_link->num_codecs; j++) { + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, j); + + if (codec_dai->driver->playback.stream_name) + codec_playback_avail = true; + if (codec_dai->driver->capture.stream_name) + codec_capture_avail = true; + } + if (cpu_dai->stream[SNDRV_PCM_STREAM_PLAYBACK].widget && codec_playback_avail) { + dev_dbg(card->dev, "CPU DAI %s: playback widget %s\n", + cpu_dai->name, cpu_dai->stream[SNDRV_PCM_STREAM_PLAYBACK].widget->name); + + aux = devm_kmalloc(card->dev, 255, GFP_KERNEL); + if (!aux) + return -ENOMEM; + if (cpu_dai->component->name_prefix) + snprintf(aux, 255, "%s TX", cpu_dai->component->name_prefix); + else + snprintf(aux, 255, "%s TX", cpu_dai->name); + + control = devm_kzalloc(card->dev, sizeof(*control), GFP_KERNEL); + if (!control) + return -ENOMEM; + control->iface = SNDRV_CTL_ELEM_IFACE_MIXER; + control->name = "Playback Route"; + control->info = snd_soc_info_enum_double; + control->get = snd_soc_dapm_get_enum_double; + control->put = snd_soc_dapm_put_enum_double; + control->private_value = (unsigned long)&mchp_asrc_playback_enum; + + widget = devm_kzalloc(card->dev, sizeof(*widget), GFP_KERNEL); + if (!widget) + return -ENOMEM; + widget->id = snd_soc_dapm_mux; + widget->name = aux; + widget->kcontrol_news = control; + widget->num_kcontrols = 1; + err = snd_soc_dapm_new_controls(dapm, widget, 1); + if (err) { + dev_err(card->dev, "failed to add widget for %s: %d\n", aux, err); + return err; + } + + for (j = 0; j < MCHP_ASRC_DSPS; j++) { /* position in ASRC instance */ + for (k = 0; k < MCHP_ASRC_DSPS; k++) { /* ASRC instance number */ + route_pos = *aifs_added * MCHP_ASRC_DSPS + + j * aifs * MCHP_ASRC_DSPS + k; + + route[route_pos] = audio_playback_map[j][k]; + route[route_pos].sink = aux; + dev_dbg(card->dev, "adding route[%d]: %s -> %s -> %s\n", + route_pos, route[route_pos].source, + route[route_pos].control, + route[route_pos].sink); + } + } + route_pos = aifs * MCHP_ASRC_DSPS * MCHP_ASRC_DSPS + *aifs_added; + route[route_pos].source = aux; + route[route_pos].sink = cpu_dai->stream[SNDRV_PCM_STREAM_PLAYBACK].widget->name; + dev_dbg(card->dev, "adding route[%d]: %s -> %s -> %s\n", + route_pos, route[route_pos].source, route[route_pos].control, + route[route_pos].sink); + (*aifs_added)++; + } + if (cpu_dai->stream[SNDRV_PCM_STREAM_CAPTURE].widget && codec_capture_avail) { + dev_dbg(card->dev, "CPU DAI %s: capture widget %s\n", + cpu_dai->name, cpu_dai->stream[SNDRV_PCM_STREAM_CAPTURE].widget->name); + + aux = devm_kmalloc(card->dev, 255, GFP_KERNEL); + if (!aux) + return -ENOMEM; + if (cpu_dai->component->name_prefix) + snprintf(aux, 255, "%s RX", cpu_dai->component->name_prefix); + else + snprintf(aux, 255, "%s RX", cpu_dai->name); + + control = devm_kzalloc(card->dev, sizeof(*control), GFP_KERNEL); + if (!control) + return -ENOMEM; + control->iface = SNDRV_CTL_ELEM_IFACE_MIXER; + control->name = "Capture Route"; + control->info = snd_soc_info_enum_double; + control->get = snd_soc_dapm_get_enum_double; + control->put = snd_soc_dapm_put_enum_double; + control->private_value = (unsigned long)&mchp_asrc_capture_enum; + widget = devm_kzalloc(card->dev, sizeof(*widget), GFP_KERNEL); + if (!widget) + return -ENOMEM; + widget->id = snd_soc_dapm_demux; + widget->name = aux; + widget->kcontrol_news = control; + widget->num_kcontrols = 1; + err = snd_soc_dapm_new_controls(dapm, widget, 1); + if (err) { + dev_err(card->dev, "failed to add widget for %s: %d\n", aux, err); + return err; + } + for (j = 0; j < MCHP_ASRC_DSPS; j++) { + for (k = 0; k < MCHP_ASRC_DSPS; k++) { + route_pos = *aifs_added * MCHP_ASRC_DSPS + + j * aifs * MCHP_ASRC_DSPS + k; + + route[route_pos] = audio_capture_map[j][k]; + route[route_pos].source = aux; + dev_dbg(card->dev, "adding route[%d]: %s -> %s -> %s\n", + route_pos, route[route_pos].source, + route[route_pos].control, route[route_pos].sink); + } + } + route_pos = aifs * MCHP_ASRC_DSPS * MCHP_ASRC_DSPS + *aifs_added; + route[route_pos].source = cpu_dai->stream[SNDRV_PCM_STREAM_CAPTURE].widget->name; + route[route_pos].sink = aux; + dev_dbg(card->dev, "adding route[%d]: %s -> %s -> %s\n", route_pos, + route[route_pos].source, route[route_pos].control, + route[route_pos].sink); + (*aifs_added)++; + } + } + return 0; +} + +static int mchp_asrc_card_late_probe(struct snd_soc_card *card) +{ + struct mchp_card_priv *card_priv = snd_soc_card_get_drvdata(card); + struct snd_soc_dapm_context *dapm = &card->dapm; + struct snd_soc_dapm_route *route; +#ifdef DEBUG + struct snd_soc_dapm_widget *widget; +#endif + int aifs = card_priv->ctrl_playback_no + card_priv->ctrl_capture_no; + int aifs_added = 0; + int ret; + struct snd_soc_pcm_runtime *rtd; + + route = devm_kzalloc(card->dev, sizeof(*route) * + (MCHP_ASRC_DSPS * MCHP_ASRC_DSPS + 1) * + aifs, GFP_KERNEL); + if (!route) + return -ENOMEM; + + for_each_card_rtds(card, rtd) { + if (!rtd->dai_link->no_pcm) + continue; + ret = mchp_asrc_card_add_rtm_route(card, rtd, route, &aifs_added); + if (ret < 0) + return ret; + } + + dev_dbg(card->dev, "Adding %d routes from total of %d\n", aifs, aifs_added); + ret = snd_soc_dapm_add_routes(dapm, route, aifs_added * + (MCHP_ASRC_DSPS * MCHP_ASRC_DSPS + 1)); + if (ret < 0) { + dev_err(card->dev, "failed to add routes: %d\n", ret); + return ret; + } + dev_dbg(card->dev, "Added routes\n"); + + dev_dbg(card->dev, "widgets available:\n"); +#ifdef DEBUG + for_each_card_widgets(dapm->card, widget) + dev_dbg(card->dev, "%s\n", widget->name); +#endif + + return 0; +} + +static int mchp_asrc_card_be_dai_link_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_soc_card *card = rtd->card; + struct mchp_card_priv *card_priv = snd_soc_card_get_drvdata(card); + int i; + int codec_playback_avail = false; + int codec_capture_avail = false; + + if (cpu_dai->component) + dev_dbg(card->dev, "%s: prefix %s\n", __func__, cpu_dai->component->name_prefix); + else + dev_dbg(card->dev, "%s: no prefix for DAI %s\n", __func__, cpu_dai->name); + + for (i = 0; i < rtd->dai_link->num_codecs; i++) { + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, i); + + if (codec_dai->driver->playback.stream_name) + codec_playback_avail = true; + if (codec_dai->driver->capture.stream_name) + codec_capture_avail = true; + } + if (cpu_dai->driver->playback.stream_name && codec_playback_avail) + card_priv->ctrl_playback_no++; + + if (cpu_dai->driver->capture.stream_name && codec_capture_avail) + card_priv->ctrl_capture_no++; + + return 0; +} + +static int mchp_asoc_card_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) +{ + struct snd_soc_card *card = rtd->card; + struct mchp_card_priv *card_priv = snd_soc_card_get_drvdata(card); + struct mchp_dai_link_priv *priv = card_priv->dai_link; + struct snd_interval *rate, *period_size; + struct snd_mask *mask; + struct snd_interval *ct = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + int i; + + for (i = 0; i < card->num_links; i++, priv++) { + if (rtd->dai_link->id == priv->id) + break; + } + if (i == card->num_links) { + dev_err(card->dev, "priv not found for %s\n", rtd->dai_link->name); + return -EINVAL; + } + + ct->min = priv->convert_channels; + ct->max = priv->convert_channels; + + rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + rate->max = priv->convert_rate; + rate->min = priv->convert_rate; + + mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + snd_mask_none(mask); + snd_mask_set(mask, priv->convert_format); + + /* set workaround to avoid ASRC noise issue */ + period_size = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_BYTES); + period_size->min = max(period_size->min, MCHP_PERIOD_SIZE_MIN); + + return 0; +} + +static int mchp_asrc_card_dai_link_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai_link *dai_link = rtd->dai_link; + struct snd_soc_card *card = rtd->card; + unsigned int i; + int err = 0; + struct mchp_card_priv *card_priv = snd_soc_card_get_drvdata(card); + struct mchp_dai_link_priv *priv = card_priv->dai_link; + + for (i = 0; i < card->num_links; i++, priv++) { + if (dai_link->id == priv->id) + break; + } + if (i == card->num_links) { + dev_err(card->dev, "private not found for %s\n", dai_link->name); + return -EINVAL; + } + + dev_dbg(card->dev, "Found priv id %d, cpu slot %d, codec slot %d\n", + priv->id, priv->cpu.slots, priv->codec.slots); + + /* get TDM values */ + if (priv->cpu.slots) { + dev_dbg(card->dev, "Setting TDM slot %d, width %d in %s node\n", + priv->cpu.slots, priv->cpu.slot_width, cpu_dai->name); + err = snd_soc_dai_set_tdm_slot(cpu_dai, + GENMASK(priv->cpu.slots - 1, 0), + GENMASK(priv->cpu.slots - 1, 0), + priv->cpu.slots, + priv->cpu.slot_width); + if (err) { + dev_err(card->dev, "Failed to set TDM params for %s node: %d\n", + cpu_dai->name, err); + } + } + + for (i = 0; i < rtd->dai_link->num_codecs; i++) { + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, i); + + /* Mikroe Proto Board uses a 12.288MhZ XTAL */ + if (strstr(codec_dai->driver->name, "wm8731")) { + dev_dbg(card->dev, "setting sysclk for %s\n", + codec_dai->driver->name); + err = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL, + XTAL_RATE, SND_SOC_CLOCK_IN); + if (err < 0) { + dev_err(card->dev, "Failed to set WM8731 SYSCLK: %d\n", + err); + } + } + if (priv->codec.slots) { + dev_info(card->dev, "Setting TDM slot %d, width %d in %s node\n", + priv->codec.slots, priv->codec.slot_width, + codec_dai->name); + err = snd_soc_dai_set_tdm_slot(codec_dai, + GENMASK(priv->codec.slots - 1, 0), + GENMASK(priv->codec.slots - 1, 0), + priv->codec.slots, + priv->codec.slot_width); + if (err) + dev_err(card->dev, "Failed to set TDM params for %s node: %d\n", + codec_dai->name, err); + } + } + + return 0; +} + +static int mchp_asoc_card_parse_convert(struct device *dev, struct device_node *np, + char *prefix, + struct mchp_dai_link_priv *priv) +{ + char prop[128]; + int ret; + + if (!prefix) + prefix = ""; + + /* sampling rate convert */ + snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-rate"); + ret = of_property_read_u32(np, prop, &priv->convert_rate); + if (ret < 0) { + dev_err(dev, "unable to get convert-rate for %s", np->full_name); + return ret; + } + if (priv->convert_rate < 8000 || priv->convert_rate > 192000) { + dev_err(dev, "invalid %sconvert-rate value for %s: %d", + prefix, np->full_name, priv->convert_rate); + return -EINVAL; + } + dev_dbg(dev, "convert-rate for %s: %d\n", np->full_name, priv->convert_rate); + + /* channels transfer */ + snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-channels"); + ret = of_property_read_u32(np, prop, &priv->convert_channels); + if (ret < 0) { + dev_err(dev, "unable to get %sconvert-channels for %s", prefix, + np->full_name); + return ret; + } + if (priv->convert_channels < 1 || priv->convert_channels > 8) { + dev_err(dev, "invalid %sconvert-channels value for %s: %d", + prefix, np->full_name, priv->convert_channels); + return -EINVAL; + } + dev_dbg(dev, "convert-channels for %s: %d\n", np->full_name, + priv->convert_channels); + + /* sample format */ + snprintf(prop, sizeof(prop), "%s%s", prefix, "convert-sample-format"); + ret = of_property_read_u32(np, prop, &priv->convert_format); + if (ret < 0) { + dev_err(dev, "unable to get convert-sample-format for %s", np->full_name); + return ret; + } + if (priv->convert_format != MCHP_ASRC_PCM_FORMAT_S8 && + priv->convert_format != MCHP_ASRC_PCM_FORMAT_S16_LE && + priv->convert_format != MCHP_ASRC_PCM_FORMAT_S24_LE && + priv->convert_format != MCHP_ASRC_PCM_FORMAT_S32_LE) { + dev_err(dev, "invalid %sconvert-sample-format value for %s: %d", + prefix, np->full_name, priv->convert_format); + return -EINVAL; + } + dev_dbg(dev, "convert-sample-format for %s: %d\n", np->full_name, + priv->convert_format); + + return 0; +} + +static void mchp_parse_tdm_params(struct device *dev, struct device_node *np, + struct mchp_dai_priv *priv) +{ + int err; + + if (!np || !priv) + return; + + err = snd_soc_of_parse_tdm_slot(np, NULL, NULL, &priv->slots, + &priv->slot_width); + if (err) { + dev_dbg(dev, + "TDM slot num/width not available in %s node: %d\n", + np->name, err); + } else { + dev_dbg(dev, "TDM slot %d, width %d in %s node\n", + priv->slots, priv->slot_width, np->name); + } +} + +static void mchp_asrc_card_of_put(struct snd_soc_card *card) +{ + int i; + + for (i = 0; i < card->num_links; i++) { + struct snd_soc_dai_link *dai_link = &card->dai_link[i]; + + if (dai_link->no_pcm) + continue; + + of_node_put(dai_link->cpus->of_node); + snd_soc_of_put_dai_link_codecs(dai_link); + } +} + +static int mchp_asrc_card_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *node; + struct snd_soc_card *card; + struct mchp_card_priv *priv; + struct snd_soc_dai_link *dai_link; + int err, num_links, num_dai_links; + int be_index = 0; + int i; + int asrc_fe_no = 0; + struct of_phandle_args *asrc_args; + + if (!np) { + dev_err(&pdev->dev, "No device node supplied\n"); + return -EINVAL; + } + + card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; + + node = of_get_child_by_name(np, PREFIX "dai-link"); + if (node) { + of_node_put(node); + node = NULL; + num_links = of_get_available_child_count(np); + } else { + dev_err(&pdev->dev, "no DT dai-links found\n"); + return -EINVAL; + } + + /* allocate private structure for BEs */ + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + priv->dai_link = devm_kzalloc(&pdev->dev, + sizeof(*priv->dai_link) * num_links, + GFP_KERNEL); + if (!priv->dai_link) + return -ENOMEM; + + snd_soc_card_set_drvdata(card, priv); + + asrc_fe_no = of_count_phandle_with_args(np, PREFIX "audio-asrc", "#sound-dai-cells"); + if (asrc_fe_no < 0) { + dev_info(&pdev->dev, "no ASRC instances found\n"); + asrc_fe_no = 0; + } else { + dev_info(&pdev->dev, "ASRC %d FEs\n", asrc_fe_no); + } + + if (asrc_fe_no) { + asrc_args = devm_kmalloc(&pdev->dev, sizeof(*asrc_args) * asrc_fe_no, + GFP_KERNEL); + if (!asrc_args) + return -ENOMEM; + + /* all DAI links will also get a BE */ + num_dai_links = num_links * 2; + } else { + num_dai_links = num_links; + } + + num_dai_links += asrc_fe_no; + + card->dai_link = devm_kzalloc(&pdev->dev, + sizeof(*card->dai_link) * num_dai_links, + GFP_KERNEL); + if (!card->dai_link) + return -ENOMEM; + + dai_link = card->dai_link; + + for (i = 0; i < asrc_fe_no; i++) { + struct snd_soc_dai_link_component *comp; + struct device_node *asrc; + + err = of_parse_phandle_with_args(np, PREFIX "audio-asrc", + "#sound-dai-cells", i, &asrc_args[i]); + if (err < 0) { + if (err == -EPROBE_DEFER) + return -EPROBE_DEFER; + + dev_warn(&pdev->dev, "ASRC DT phandle %d not found: %d\n", + i, err); + continue; + } + + dev_dbg(&pdev->dev, "ASRC DT phandle %d found: %s\n", + i, asrc_args[i].np->name); + + asrc = asrc_args[i].np; + comp = devm_kzalloc(&pdev->dev, 3 * sizeof(*comp), GFP_KERNEL); + if (!comp) { + of_node_put(asrc); + return -ENOMEM; + } + + dai_link->cpus = &comp[0]; + dai_link->codecs = &comp[1]; + dai_link->platforms = &comp[2]; + + dai_link->cpus->of_node = asrc; + dai_link->platforms->of_node = asrc; + dai_link->codecs->name = "snd-soc-dummy"; + dai_link->codecs->dai_name = "snd-soc-dummy-dai"; + dai_link->num_cpus = 1; + dai_link->num_codecs = 1; + dai_link->num_platforms = 1; + + dai_link->dpcm_playback = 1; + dai_link->dpcm_capture = 1; + dai_link->dynamic = 1; + dai_link->dpcm_loopback = 1; + dai_link->id = card->num_links; + dai_link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = + dai_link->trigger[SNDRV_PCM_STREAM_CAPTURE] = SND_SOC_DPCM_TRIGGER_PRE; + err = snd_soc_get_dai_name(&asrc_args[i], &dai_link->cpus->dai_name); + if (err) { + if (err == -EPROBE_DEFER) { + of_node_put(asrc); + return -EPROBE_DEFER; + } + dev_err(&pdev->dev, "error getting asrc dai name\n"); + } else { + dev_dbg(&pdev->dev, "asrc cpu_dai_name %s\n", + dai_link->cpus->dai_name); + dai_link->name = dai_link->cpus->dai_name; + dai_link->stream_name = dai_link->cpus->dai_name; + } + dev_info(&pdev->dev, "Found DPCM FE: %s\n", dai_link->cpus->dai_name); + + of_node_put(asrc); + dai_link++; + card->num_links++; + } + + for_each_available_child_of_node(np, node) { + struct device_node *cpu; + struct device_node *codec; + int index; + char *name; + struct snd_soc_dai_link_component *comp; + + /* add normal PCM first */ + comp = devm_kzalloc(&pdev->dev, 3 * sizeof(*comp), GFP_KERNEL); + if (!comp) + return -ENOMEM; + + dai_link->cpus = &comp[0]; + dai_link->codecs = &comp[1]; + dai_link->platforms = &comp[2]; + dai_link->num_cpus = 1; + dai_link->num_codecs = 1; + dai_link->num_platforms = 1; + + cpu = of_get_child_by_name(node, "cpu"); + if (!cpu) { + dev_err(&pdev->dev, "no cpu DT node found\n"); + continue; + } + mchp_parse_tdm_params(&pdev->dev, cpu, &priv->dai_link[be_index].cpu); + codec = of_get_child_by_name(node, "codec"); + if (!codec) { + dev_err(&pdev->dev, "no codec DT node found\n"); + of_node_put(cpu); + continue; + } + mchp_parse_tdm_params(&pdev->dev, codec, &priv->dai_link[be_index].codec); + err = asoc_simple_parse_daifmt(&pdev->dev, node, codec, PREFIX, + &dai_link->dai_fmt); + if (err < 0) { + of_node_put(codec); + of_node_put(cpu); + if (err == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_err(&pdev->dev, "error getting daifmt from DT\n"); + continue; + } + + dai_link->cpus->of_node = of_parse_phandle(cpu, "sound-dai", 0); + if (!dai_link->cpus->of_node) { + dev_err(&pdev->dev, "error getting cpu phandle for %s\n", + cpu->name); + of_node_put(codec); + of_node_put(cpu); + continue; + } + err = snd_soc_of_get_dai_link_codecs(&pdev->dev, codec, dai_link); + if (err) { + of_node_put(dai_link->cpus->of_node); + of_node_put(codec); + of_node_put(cpu); + if (err == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_err(&pdev->dev, "error getting DAI codecs: %d\n", err); + continue; + } + + err = snd_soc_of_get_dai_name(cpu, &dai_link->cpus->dai_name, 0); + if (err) { + snd_soc_of_put_dai_link_codecs(dai_link); + of_node_put(dai_link->cpus->of_node); + of_node_put(codec); + of_node_put(cpu); + if (err == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_err(&pdev->dev, "error getting cpu dai name: %d\n", err); + continue; + } + dev_dbg(&pdev->dev, "current cpu_dai_name %s\n", + dai_link->cpus->dai_name); + + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s -", + dai_link->cpus->dai_name); + if (!name) { + snd_soc_of_put_dai_link_codecs(dai_link); + of_node_put(dai_link->cpus->of_node); + of_node_put(codec); + of_node_put(cpu); + return -ENOMEM; + } + + for (index = 0; + index < dai_link->num_codecs; + index++) { + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s %s", + name, + dai_link->codecs[index].dai_name); + if (!name) { + snd_soc_of_put_dai_link_codecs(dai_link); + of_node_put(dai_link->cpus->of_node); + of_node_put(codec); + of_node_put(cpu); + return -ENOMEM; + } + } + + dai_link->name = name; + dai_link->stream_name = name; + dai_link->init = &mchp_asrc_card_dai_link_init; + dai_link->id = card->num_links; + dai_link->platforms->of_node = dai_link->cpus->of_node; + priv->dai_link[be_index].id = card->num_links; + + dai_link++; + card->num_links++; + + /* + * if the convert properties are not available, do not create a + * BE DAI Link for this DAI + */ + err = mchp_asoc_card_parse_convert(&pdev->dev, node, PREFIX, + &priv->dai_link[be_index]); + if (err) { + dev_info(&pdev->dev, "not registering DAI %s as BE\n", + (dai_link - 1)->name); + } + if (!asrc_fe_no || err) { + be_index++; + of_node_put(codec); + of_node_put(cpu); + continue; + } + + /* add the DPCM BE */ + + memcpy(dai_link, &card->dai_link[card->num_links - 1], sizeof(*dai_link)); + + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "BE %s", + card->dai_link[card->num_links - 1].name); + dai_link->name = name; + dai_link->stream_name = name; + dai_link->no_pcm = 1; + dai_link->ignore_suspend = 1; + dai_link->ignore_pmdown_time = 1; + dai_link->init = &mchp_asrc_card_be_dai_link_init; + dai_link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = + dai_link->trigger[SNDRV_PCM_STREAM_CAPTURE] = SND_SOC_DPCM_TRIGGER_POST; + + if (strstr(name, "spdiftx")) { + dai_link->dpcm_playback = 1; + } else if (strstr(name, "spdifrx")) { + dai_link->dpcm_capture = 1; + } else if (strstr(name, "pdmc")) { + dai_link->dpcm_capture = 1; + } else { + dai_link->dpcm_playback = 1; + dai_link->dpcm_capture = 1; + } + dai_link->be_hw_params_fixup = mchp_asoc_card_fixup; + dev_info(&pdev->dev, "Found DPCM BE: %s, id %d\n", + dai_link->name, dai_link->id); + + of_node_put(codec); + of_node_put(cpu); + + dai_link++; + card->num_links++; + be_index++; + } + + if (!card->num_links) { + dev_err(&pdev->dev, "no legit dai-links found\n"); + return -EINVAL; + } + dev_dbg(&pdev->dev, "Found %d links\n", card->num_links); + + card->dev = &pdev->dev; + card->owner = THIS_MODULE; + + err = snd_soc_of_parse_card_name(card, PREFIX "model"); + if (err) { + dev_err(&pdev->dev, PREFIX "model not found in DT\n"); + goto _dai_link_put; + } + + if (of_property_read_bool(np, PREFIX "audio-routing")) + snd_soc_of_parse_audio_routing(card, PREFIX "audio-routing"); + + priv->ctrl_playback_no = 0; + priv->ctrl_capture_no = 0; + + card->late_probe = mchp_asrc_card_late_probe; + + err = snd_soc_register_card(card); + if (err) { + dev_err(&pdev->dev, + "ASoc: Platform device allocation failed: %d\n", err); + goto _dai_link_put; + } + +_dai_link_put: + mchp_asrc_card_of_put(card); + + return err; +} + +static int mchp_asrc_card_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + + return 0; +} + +static const struct of_device_id mchp_asrc_card_of_match[] = { + { .compatible = "microchip,asrc-card",}, + {}, +}; +MODULE_DEVICE_TABLE(of, mchp_asrc_card_of_match); + +static struct platform_driver mchp_asrc_card_driver = { + .driver = { + .name = "mchp-asrc-card", + .of_match_table = of_match_ptr(mchp_asrc_card_of_match), + }, + .probe = mchp_asrc_card_probe, + .remove = mchp_asrc_card_remove, +}; +module_platform_driver(mchp_asrc_card_driver); + +/* Module information */ +MODULE_AUTHOR("Codrin Ciubotariu <codrin.ciubotariu@microchip.com>"); +MODULE_DESCRIPTION("ALSA SoC machine driver for Microchip's ASRC"); +MODULE_LICENSE("GPL v2"); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/sound/soc/atmel/mchp-asrc-dma.c linux-microchip/sound/soc/atmel/mchp-asrc-dma.c --- linux-6.6.51/sound/soc/atmel/mchp-asrc-dma.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/sound/soc/atmel/mchp-asrc-dma.c 2024-11-26 14:02:39.366527160 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +// SPDX-License-Identifier: GPL-2.0 +// ASoC Platform (DMA) driver for Microchip ASRC +// +// Copyright (C) 2018-2022 Microchip Technology Inc. and its subsidiaries +// +// Author: Codrin Ciubotariu <codrin.ciubotariu@microchip.com> + +#include <linux/dma-mapping.h> +#include <linux/module.h> +#include <sound/dmaengine_pcm.h> +#include <sound/pcm_params.h> + +#include "mchp-asrc.h" + +#define MCHP_ASRC_DMAENGINE_PCM_DRV_NAME "mchp_snd_asrc_dmaengine_pcm" +#define MCHP_ASRC_DMABUF_SIZE (512 * 1024) + +struct mchp_asrc_dma_be_priv { + struct device *dev; + struct of_phandle_args *phandle; + struct snd_soc_dpcm *dpcm; + struct dma_chan *chan; + struct dma_async_tx_descriptor *desc; + struct list_head list; +}; + +struct mchp_asrc_dma_priv { + struct device *dev; + struct list_head dma_in_head; + struct list_head dma_out_head; + unsigned int refcount; + /* used for FE */ + dma_cookie_t cookie; + unsigned int pos; + unsigned int no_residue: 1; +}; + +static struct snd_pcm_hardware mchp_asrc_hw = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID, + .buffer_bytes_max = MCHP_ASRC_DMABUF_SIZE, + .period_bytes_min = 256, + .periods_min = 2, + .periods_max = UINT_MAX, + .fifo_size = 0, +}; + +static inline struct mchp_asrc_dma_priv * +mchp_asrc_substream_to_prtd(const struct snd_pcm_substream *substream) +{ + return substream->runtime->private_data; +} + +static void +snd_asrc_pcm_refine_runtime_hwparams(struct snd_pcm_substream *substream, + struct snd_pcm_hardware *hw, struct dma_slave_caps *dma_caps) +{ + u32 addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); + snd_pcm_format_t i; + + if (dma_caps) { + if (dma_caps->cmd_pause && dma_caps->cmd_resume) + hw->info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME; + if (dma_caps->residue_granularity <= DMA_RESIDUE_GRANULARITY_SEGMENT) + hw->info |= SNDRV_PCM_INFO_BATCH; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + addr_widths = dma_caps->dst_addr_widths; + else + addr_widths = dma_caps->src_addr_widths; + } + + pcm_for_each_format(i) { + int bits = snd_pcm_format_physical_width(i); + + /* + * Enable only samples with DMA supported physical + * widths + */ + switch (bits) { + case 8: + case 16: + case 24: + case 32: + case 64: + if (addr_widths & (1 << (bits / 8))) + hw->formats |= pcm_format_to_bits(i); + break; + default: + /* Unsupported types */ + break; + } + } +} + +static int mchp_asrc_dma_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct dma_slave_caps dma_caps; + struct snd_pcm_runtime *runtime = substream->runtime; + struct device *dev = component->dev; + struct dma_chan *tmp_chan; + struct mchp_asrc_dma_priv *dma_priv; + bool is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + char tmp_str[4]; + int ret; + int i; + + if (runtime->private_data) { + dma_priv = runtime->private_data; + dma_priv->refcount++; + dev_dbg(dev, "%s(): DMA private data already allocated\n", __func__); + return 0; + } + + dma_priv = kzalloc(sizeof(*dma_priv), GFP_KERNEL); + if (!dma_priv) + return -ENOMEM; + dma_priv->dev = dev; + INIT_LIST_HEAD(&dma_priv->dma_in_head); + INIT_LIST_HEAD(&dma_priv->dma_out_head); + dma_priv->refcount++; + runtime->private_data = dma_priv; + + ret = snd_pcm_hw_constraint_integer(substream->runtime, SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) { + dev_err(dev, "snd_pcm_hw_constraint_integer() failed for HW_PARAM_PERIODS: %d\n", + ret); + goto __out_data_free; + } + + /* since we are not preallocating the DMA channels, we use a + * dummy DMA channel, just to get the capabilities + */ + for (i = 0; i < MCHP_ASRC_NB_STEREO_CH; i++) { + sprintf(tmp_str, "%cx%d", is_playback ? 't' : 'r', i); + + tmp_chan = dma_request_chan(dev, tmp_str); + if (!IS_ERR(tmp_chan)) + break; + } + if (i == MCHP_ASRC_NB_STEREO_CH) { + dev_err(dev, "no free DMA %s channels\n", is_playback ? "tx" : "rx"); + ret = -EINVAL; + goto __out_data_free; + } + + mchp_asrc_hw.period_bytes_max = dma_get_max_seg_size(tmp_chan->device->dev); + ret = dma_get_slave_caps(tmp_chan, &dma_caps); + if (ret == 0) { + dma_priv->no_residue = dma_caps.residue_granularity == + DMA_RESIDUE_GRANULARITY_DESCRIPTOR; + + /* Refine mchp_asrc_hw according to caps of DMA */ + snd_asrc_pcm_refine_runtime_hwparams(substream, &mchp_asrc_hw, &dma_caps); + } else { + snd_asrc_pcm_refine_runtime_hwparams(substream, &mchp_asrc_hw, NULL); + } + + ret = snd_soc_set_runtime_hwparams(substream, &mchp_asrc_hw); + if (ret < 0) { + dev_err(dev, "runtime_hwparams() failed: %d\n", ret); + goto __out_release_chan; + } + + dma_release_channel(tmp_chan); + + return 0; + +__out_release_chan: + dma_release_channel(tmp_chan); +__out_data_free: + dma_priv->refcount--; + if (!dma_priv->refcount) { + kfree(dma_priv); + runtime->private_data = NULL; + } + + return ret; +} + +static int mchp_asrc_dma_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct mchp_asrc_dma_priv *dma_priv = runtime->private_data; + + dma_priv->refcount--; + if (!dma_priv->refcount) { + kfree(dma_priv); + runtime->private_data = NULL; + } + + return 0; +} + +static void mchp_asrc_start_handle(unsigned long data) +{ + struct mchp_asrc_dma_priv *dma_priv = (struct mchp_asrc_dma_priv *)data; + struct mchp_asrc_dma_be_priv *dma_be_priv; + + list_for_each_entry(dma_be_priv, &dma_priv->dma_in_head, list) { + if (unlikely(!dma_be_priv->desc)) { + dev_err(dma_priv->dev, "in BE %s DMA descriptor not initialized\n", + dma_be_priv->phandle ? dma_be_priv->phandle->np->name : + "front-end"); + return; + } + dev_dbg(dma_priv->dev, "%s() DMA: in BE channel %s for %s\n", __func__, + dma_be_priv->desc->chan->name, + dma_be_priv->phandle ? dma_be_priv->phandle->np->name : "front-end"); + } + list_for_each_entry(dma_be_priv, &dma_priv->dma_out_head, list) { + if (unlikely(!dma_be_priv->desc)) { + dev_err(dma_priv->dev, "out BE %s DMA descriptor not initialized\n", + dma_be_priv->phandle ? dma_be_priv->phandle->np->name : + "front-end"); + return; + } + dev_dbg(dma_priv->dev, "%s() DMA: out BE channel %s for %s\n", __func__, + dma_be_priv->desc->chan->name, + dma_be_priv->phandle ? dma_be_priv->phandle->np->name : "front-end"); + } + + /* start transmit channel DMAs */ + dev_info(dma_priv->dev, "ASRC IN AIF(s):\n"); + list_for_each_entry(dma_be_priv, &dma_priv->dma_in_head, list) { + if (!dma_be_priv->phandle) { + dma_priv->cookie = dmaengine_submit(dma_be_priv->desc); + dev_info(dma_priv->dev, "\tfront-end\n"); + } else { + dmaengine_submit(dma_be_priv->desc); + dev_info(dma_priv->dev, "\t%s, %d channels\n", + dma_be_priv->phandle->np->full_name, + params_channels(&dma_be_priv->dpcm->be->dpcm[SNDRV_PCM_STREAM_CAPTURE].hw_params)); + } + } + list_for_each_entry(dma_be_priv, &dma_priv->dma_in_head, list) + dma_async_issue_pending(dma_be_priv->desc->chan); + + /* start receive channel DMAs */ + dev_info(dma_priv->dev, "ASRC OUT AIF(s):\n"); + list_for_each_entry(dma_be_priv, &dma_priv->dma_out_head, list) { + if (!dma_be_priv->phandle) { + dma_priv->cookie = dmaengine_submit(dma_be_priv->desc); + dev_info(dma_priv->dev, "\tfront-end\n"); + } else { + dmaengine_submit(dma_be_priv->desc); + dev_info(dma_priv->dev, "\t%s, %d channels\n", + dma_be_priv->phandle->np->full_name, + params_channels(&dma_be_priv->dpcm->be->dpcm[SNDRV_PCM_STREAM_PLAYBACK].hw_params)); + } + } + list_for_each_entry(dma_be_priv, &dma_priv->dma_out_head, list) + dma_async_issue_pending(dma_be_priv->desc->chan); +} + +static int mchp_asrc_dmaengine_pcm_prepare_slave_config(struct device *dev, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_dmaengine_dai_dma_data *dma_data, + struct dma_slave_config *slave_config) +{ + int ret; + + ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config); + if (ret) + return ret; + + snd_dmaengine_pcm_set_config_from_dai_data(substream, dma_data, slave_config); + + dev_dbg(dev, + "%s() FE AIF dir %s, src addr %pap, dst addr: %pap, src_addr_width %d, dst_addr_width %d, src maxburst %u, dst maxburst %u\n", + __func__, + slave_config->direction == DMA_MEM_TO_DEV ? "DMA_MEM_TO_DEV" : + "DMA_DEV_TO_MEM", + &slave_config->src_addr, &slave_config->dst_addr, + slave_config->src_addr_width, slave_config->dst_addr_width, + slave_config->src_maxburst, slave_config->dst_maxburst); + + return 0; +} + +static int +mchp_asrc_be_dmaengine_slave_config(struct mchp_asrc_dmaengine_dai_dma *dma, + struct mchp_asrc_dma_be_priv *dma_be_priv, + int is_playback) +{ + struct dma_slave_config config_be = {0}; + struct snd_soc_dpcm *dpcm = dma_be_priv->dpcm; + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_soc_dai *dai = asoc_rtd_to_cpu(be, 0); + struct device *dev = dma_be_priv->dev; + struct mchp_asrc_dmaengine_be_dma *dma_be; + struct snd_dmaengine_dai_dma_data *dma_data_be; + struct list_head *dmaengine_list; + enum dma_slave_buswidth buswidth; + int ret; + int bits_be; + + dmaengine_list = is_playback ? &dma->dma_out_list : &dma->dma_in_list; + list_for_each_entry(dma_be, dmaengine_list, list) { + if (dma_be->phandle && dma_be->phandle->np == dai->dev->of_node) { + dev_dbg(dev, "found DMA data %s\n", dma_be->phandle->np->name); + break; + } + } + if (list_entry_is_head(dma_be, dmaengine_list, list)) { + dev_err(dev, "DMA data not found for BE %s\n", dai->name); + return -EINVAL; + } + + dma_be_priv->phandle = dma_be->phandle; + + dma_data_be = &dma_be->dma_data; + dma_be_priv->chan = dma_request_slave_channel(dev, dma_data_be->chan_name); + if (!dma_be_priv->chan) { + dev_err(dai->dev, "failed to request DMA channel %s for BE\n", + dma_data_be->chan_name); + ret = -EBUSY; + goto __cleanup_phandle; + } + + bits_be = params_physical_width(&be->dpcm[!is_playback].hw_params); + + if (bits_be < 8 || bits_be > 64) { + dev_err(dai->dev, "invalid physical width %d for BE %s\n", bits_be, dai->name); + ret = -EINVAL; + goto __cleanup_chan; + } + if (bits_be == 8) + buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE; + else if (bits_be == 16) + buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; + else if (bits_be == 24) + buswidth = DMA_SLAVE_BUSWIDTH_3_BYTES; + else if (bits_be <= 32) + buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; + else + buswidth = DMA_SLAVE_BUSWIDTH_8_BYTES; + + if (is_playback) { + config_be.direction = DMA_DEV_TO_MEM; + config_be.src_addr_width = buswidth; + config_be.src_maxburst = dma_data_be->maxburst; + config_be.src_addr = dma_data_be->addr; + } else { + config_be.direction = DMA_MEM_TO_DEV; + config_be.dst_addr_width = buswidth; + config_be.dst_maxburst = dma_data_be->maxburst; + config_be.dst_addr = dma_data_be->addr; + } + config_be.device_fc = false; + + dev_dbg(dev, + "%s() AIF %s dir %s, src addr %pap, dst addr: %pap, src_addr_width %d, dst_addr_width %d, src maxburst %u, dst maxburst %u\n", + __func__, dai->name, + config_be.direction == DMA_MEM_TO_DEV ? "DMA_MEM_TO_DEV" : + "DMA_DEV_TO_MEM", + &config_be.src_addr, &config_be.dst_addr, + config_be.src_addr_width, config_be.dst_addr_width, + config_be.src_maxburst, config_be.dst_maxburst); + + ret = dmaengine_slave_config(dma_be_priv->chan, &config_be); + if (ret) { + dev_err(dev, "failed to config DMA channel for BE %s: %d\n", dai->name, ret); + goto __cleanup_chan; + } + + return 0; + +__cleanup_chan: + dma_release_channel(dma_be_priv->chan); +__cleanup_phandle: + dma_be_priv->phandle = NULL; + return ret; +} + +static int mchp_asrc_dma_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct dma_slave_config config_fe = {0}; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct mchp_asrc_dma_priv *priv = mchp_asrc_substream_to_prtd(substream); + struct snd_soc_dpcm *dpcm; + struct device *dev = component->dev; + struct mchp_asrc_dmaengine_dai_dma *dma; + struct snd_dmaengine_dai_dma_data *dma_data_fe = NULL; + struct snd_pcm_substream *substream_be; + struct mchp_asrc_dma_be_priv *dma_be_priv, *tmp, *dma_fe_priv; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct mchp_asrc_dmaengine_be_dma *dma_be; + struct list_head *dma_be_list; + struct list_head *dma_be_head; + int ret; + bool is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + + /* save BEs for this substream */ + for_each_dpcm_be(rtd, substream->stream, dpcm) { + struct snd_soc_pcm_runtime *be = dpcm->be; + + if (dpcm->fe != rtd) + continue; + + substream_be = snd_soc_dpcm_get_substream(be, substream->stream); + if (!substream_be) + continue; + + dma_be_priv = kzalloc(sizeof(*dma_be_priv), GFP_KERNEL); + if (!dma_be_priv) { + ret = -ENOMEM; + goto __cleanup_dma_free; + } + dma_be_priv->dev = component->dev; + dma_be_priv->dpcm = dpcm; + + if (is_playback) + list_add_tail(&dma_be_priv->list, &priv->dma_out_head); + else + list_add_tail(&dma_be_priv->list, &priv->dma_in_head); + dev_dbg(dev, "%s: add %s %s\n", __func__, is_playback ? "OUT" : "IN", + asoc_rtd_to_cpu(be, 0)->name); + } + + dma = snd_soc_dai_get_dma_data(cpu_dai, substream); + if (!dma) { + if (params) { + dev_err(rtd->dev, "platform data not found for FE %s\n", cpu_dai->name); + ret = -EINVAL; + goto __cleanup_dma_free; + } + dev_dbg(rtd->dev, "platform data not set; probably half stream called: stream %s\n", + is_playback ? "playback" : "capture"); + return 0; + } + + /* start DMA config; start with current stream DMAs */ + dma_be_head = is_playback ? &priv->dma_out_head : &priv->dma_in_head; + list_for_each_entry(dma_be_priv, dma_be_head, list) { + ret = mchp_asrc_be_dmaengine_slave_config(dma, dma_be_priv, is_playback); + if (ret < 0) + goto __cleanup_dma_be; + } + + /* configure DMAs for the 'other' substream */ + if (!params) { + dma_be_head = is_playback ? &priv->dma_in_head : &priv->dma_out_head; + list_for_each_entry(dma_be_priv, dma_be_head, list) { + ret = mchp_asrc_be_dmaengine_slave_config(dma, dma_be_priv, !is_playback); + if (ret < 0) + goto __cleanup_dma_rev; + } + + return 0; + } + + /* set FE */ + dma_be_list = is_playback ? &dma->dma_in_list : &dma->dma_out_list; + + list_for_each_entry(dma_be, dma_be_list, list) { + if (!dma_be->phandle) { + dma_data_fe = &dma_be->dma_data; + break; + } + } + if (!dma_data_fe) { + dev_err(dev, "FE DMA data not found\n"); + ret = -EINVAL; + goto __cleanup_dma_rev; + } + + dma_fe_priv = kzalloc(sizeof(*dma_fe_priv), GFP_KERNEL); + if (!dma_fe_priv) { + ret = -ENOMEM; + goto __cleanup_dma_rev; + } + + dma_fe_priv->chan = dma_request_slave_channel(dev, dma_data_fe->chan_name); + if (!dma_fe_priv->chan) { + dev_err(dev, "failed to request DMA channel %s for FE %s\n", + dma_data_fe->chan_name, cpu_dai->name); + ret = -EINVAL; + goto __cleanup_free_fe; + } + + ret = mchp_asrc_dmaengine_pcm_prepare_slave_config(dev, substream, params, + dma_data_fe, &config_fe); + if (ret) { + dev_err(dev, "failed to prepare DMA config for FE %s\n", cpu_dai->name); + goto __cleanup_dma_fe; + } + + dev_dbg(dev, + "%s() config %s: dir %s, src addr %pap, dst addr %pap, src_addr_width %d, dst_addr_width %d, src maxburst %u, dst maxburst %u\n", + __func__, cpu_dai->name, + config_fe.direction == DMA_MEM_TO_DEV ? "MEM_TO_DEV" : + "DEV_TO_MEM", + &config_fe.src_addr, &config_fe.dst_addr, + config_fe.src_addr_width, config_fe.dst_addr_width, + config_fe.src_maxburst, config_fe.dst_maxburst); + + ret = dmaengine_slave_config(dma_fe_priv->chan, &config_fe); + if (ret) { + dev_err(dev, "failed to config DMA channel for FE %s\n", cpu_dai->name); + goto __cleanup_dma_fe; + } + + /* on playback, FE is in AIF and on capture FE is out AIF */ + if (is_playback) + list_add_tail(&dma_fe_priv->list, &priv->dma_in_head); + else + list_add_tail(&dma_fe_priv->list, &priv->dma_out_head); + + dev_dbg(component->dev, "%s: added %s %s\n", __func__, is_playback ? "IN" : "OUT", + cpu_dai->name); + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_IRAM, dev, params_buffer_bytes(params), + &substream->dma_buffer); + if (ret < 0) + goto __cleanup_dma_fe_list; + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + + return 0; + +__cleanup_dma_fe_list: + list_del(&dma_fe_priv->list); +__cleanup_dma_fe: + dma_release_channel(dma_fe_priv->chan); + dma_fe_priv->chan = NULL; +__cleanup_free_fe: + kfree(dma_fe_priv); +__cleanup_dma_rev: + if (!params) { + dma_be_head = is_playback ? &priv->dma_in_head : &priv->dma_out_head; + list_for_each_entry(dma_be_priv, dma_be_head, list) { + if (dma_be_priv->chan) { + dma_release_channel(dma_be_priv->chan); + dma_be_priv->chan = NULL; + } + } + } +__cleanup_dma_be: + dma_be_head = is_playback ? &priv->dma_out_head : &priv->dma_in_head; + list_for_each_entry(dma_be_priv, dma_be_head, list) { + if (dma_be_priv->chan) { + dma_release_channel(dma_be_priv->chan); + dma_be_priv->chan = NULL; + } + } +__cleanup_dma_free: + dma_be_head = is_playback ? &priv->dma_out_head : &priv->dma_in_head; + list_for_each_entry_safe(dma_be_priv, tmp, dma_be_head, list) { + list_del(&dma_be_priv->list); + kfree(dma_be_priv); + } + + return ret; +} + +static int mchp_asrc_dma_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct mchp_asrc_dma_priv *priv = mchp_asrc_substream_to_prtd(substream); + struct mchp_asrc_dma_be_priv *dma_be_priv, *tmp; + + if (substream->dma_buffer.area) { + snd_dma_free_pages(&substream->dma_buffer); + substream->dma_buffer.area = NULL; + snd_pcm_set_runtime_buffer(substream, NULL); + } + + list_for_each_entry(dma_be_priv, &priv->dma_in_head, list) { + if (dma_be_priv->chan) + dmaengine_synchronize(dma_be_priv->chan); + } + + list_for_each_entry(dma_be_priv, &priv->dma_out_head, list) { + if (dma_be_priv->chan) + dmaengine_synchronize(dma_be_priv->chan); + } + + list_for_each_entry_safe(dma_be_priv, tmp, &priv->dma_in_head, list) { + if (dma_be_priv->chan) + dma_release_channel(dma_be_priv->chan); + dma_be_priv->chan = NULL; + list_del(&dma_be_priv->list); + kfree(dma_be_priv); + } + + list_for_each_entry_safe(dma_be_priv, tmp, &priv->dma_out_head, list) { + if (dma_be_priv->chan) + dma_release_channel(dma_be_priv->chan); + dma_be_priv->chan = NULL; + list_del(&dma_be_priv->list); + kfree(dma_be_priv); + } + + return 0; +} + +static void mchp_asrc_pcm_dma_complete(void *arg) +{ + struct snd_pcm_substream *substream = arg; + struct mchp_asrc_dma_priv *priv = mchp_asrc_substream_to_prtd(substream); + + priv->pos += snd_pcm_lib_period_bytes(substream); + if (priv->pos >= snd_pcm_lib_buffer_bytes(substream)) + priv->pos = 0; + snd_pcm_period_elapsed(substream); +} + +static int mchp_asrc_dma_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *subs, struct mchp_asrc_dma_priv *priv) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(subs); + struct device *dev = component->dev; + unsigned long flags = DMA_CTRL_ACK; + enum dma_transfer_direction dir; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct mchp_asrc_dmaengine_dai_dma *dma; + struct mchp_asrc_dma_be_priv *dma_fe_priv = NULL, *dma_be_priv; + struct dma_async_tx_descriptor *desc; + bool is_playback = subs->stream == SNDRV_PCM_STREAM_PLAYBACK; + struct list_head *dma_be_head; + int stream; + + dma = snd_soc_dai_get_dma_data(cpu_dai, subs); + if (!dma) { + dev_dbg(rtd->dev, "platform data not found for FE %s\n", cpu_dai->name); + return 0; + } + + if (is_playback) { + dma_be_head = &priv->dma_out_head; + stream = SNDRV_PCM_STREAM_PLAYBACK; + } else { + dma_be_head = &priv->dma_in_head; + stream = SNDRV_PCM_STREAM_CAPTURE; + } + list_for_each_entry(dma_be_priv, dma_be_head, list) { + struct snd_soc_dpcm *dpcm; + struct snd_soc_pcm_runtime *be; + struct snd_soc_dai *dai; + struct snd_pcm_substream *subs_be; + struct snd_pcm_runtime *rtm_be; + + if (!dma_be_priv->phandle) { + if (dma_fe_priv) { + dev_err(dev, "found multiple AIFs without DMA data\n"); + return -EINVAL; + } + /* private DMA data for FE */ + dma_fe_priv = dma_be_priv; + continue; + } + + dpcm = dma_be_priv->dpcm; + be = dpcm->be; + dai = asoc_rtd_to_cpu(be, 0); + + dev_dbg(dev, "%s() OUT BE DAI %s: rate=%u format=%#x width=%u channels=%u\n", + __func__, dai->name, params_rate(&be->dpcm[stream].hw_params), + params_format(&be->dpcm[stream].hw_params), + params_width(&be->dpcm[stream].hw_params), + params_channels(&be->dpcm[stream].hw_params)); + + if (!dma_be_priv->chan) { + dev_err(dev, "DMA channel not found for BE %s\n", dai->name); + return -EINVAL; + } + + subs_be = snd_soc_dpcm_get_substream(be, stream); + rtm_be = subs_be->runtime; + + dev_dbg(dev, + "%s() BE buffer_size(periods) %ld, buffer_size(bytes) %zu period_size(frames) %ld period_size(bytes) %zu\n", + __func__, + rtm_be->buffer_size, snd_pcm_lib_buffer_bytes(subs_be), + rtm_be->period_size, snd_pcm_lib_period_bytes(subs_be)); + + desc = dmaengine_prep_dma_cyclic(dma_be_priv->chan, rtm_be->dma_addr, + snd_pcm_lib_buffer_bytes(subs_be), + snd_pcm_lib_period_bytes(subs_be), + is_playback ? DMA_DEV_TO_MEM : + DMA_MEM_TO_DEV, DMA_CTRL_ACK); + if (!desc) { + dev_err(dev, "failed to prepare client DMA for BE %s\n", dai->name); + return -ENOMEM; + } + dma_be_priv->desc = desc; + } + + if (is_playback) { + dma_be_head = &priv->dma_in_head; + stream = SNDRV_PCM_STREAM_CAPTURE; + } else { + dma_be_head = &priv->dma_out_head; + stream = SNDRV_PCM_STREAM_PLAYBACK; + } + + /* get FE DMA data, if available */ + list_for_each_entry(dma_be_priv, dma_be_head, list) { + struct snd_soc_dpcm *dpcm; + struct snd_soc_pcm_runtime *be; + struct snd_soc_dai *dai; + struct snd_pcm_substream *subs_be; + struct snd_pcm_runtime *rtm_be; + + if (!dma_be_priv->phandle) { + if (dma_fe_priv) { + dev_err(dev, "found multiple AIFs without DMA data\n"); + return -EINVAL; + } + /* private DMA data for FE */ + dma_fe_priv = dma_be_priv; + continue; + } + + dpcm = dma_be_priv->dpcm; + be = dpcm->be; + dai = asoc_rtd_to_cpu(be, 0); + + dev_dbg(dev, "%s() IN BE DAI %s: rate=%u format=%#x width=%u channels=%u\n", + __func__, dai->name, + params_rate(&be->dpcm[stream].hw_params), + params_format(&be->dpcm[stream].hw_params), + params_width(&be->dpcm[stream].hw_params), + params_channels(&be->dpcm[stream].hw_params)); + + if (!dma_be_priv->chan) { + dev_err(dev, "DMA channel not found for BE %s\n", dai->name); + return -EINVAL; + } + + subs_be = snd_soc_dpcm_get_substream(be, stream); + rtm_be = subs_be->runtime; + + dev_dbg(dev, + "%s() BE buffer_size(periods) %ld, buffer_size(bytes) %zu period_size(frames) %ld period_size(bytes) %zu\n", + __func__, + rtm_be->buffer_size, snd_pcm_lib_buffer_bytes(subs_be), + rtm_be->period_size, snd_pcm_lib_period_bytes(subs_be)); + + desc = dmaengine_prep_dma_cyclic(dma_be_priv->chan, rtm_be->dma_addr, + snd_pcm_lib_buffer_bytes(subs_be), + snd_pcm_lib_period_bytes(subs_be), + !is_playback ? DMA_DEV_TO_MEM : + DMA_MEM_TO_DEV, DMA_CTRL_ACK); + if (!desc) { + dev_err(dev, "failed to prepare client DMA for BE %s\n", dai->name); + return -ENOMEM; + } + dma_be_priv->desc = desc; + } + + /* FE present, initialize the DMA transfer to user space */ + if (dma_fe_priv) { + if (!subs->runtime->no_period_wakeup) + flags |= DMA_PREP_INTERRUPT; + + priv->pos = 0; + dir = snd_pcm_substream_to_dma_direction(subs); + desc = dmaengine_prep_dma_cyclic(dma_fe_priv->chan, subs->runtime->dma_addr, + snd_pcm_lib_buffer_bytes(subs), + snd_pcm_lib_period_bytes(subs), dir, flags); + if (!desc) { + dev_err(dev, "failed to prepare client DMA for FE %s\n", cpu_dai->name); + return -ENOMEM; + } + + dma_fe_priv->desc = desc; + dma_fe_priv->desc->callback = mchp_asrc_pcm_dma_complete; + dma_fe_priv->desc->callback_param = subs; + + dev_dbg(dev, + "%s() FE buffer_size(periods) %ld, buffer_size(bytes) %zu period_size(frames) %ld period_size(bytes) %zu\n", + __func__, + subs->runtime->buffer_size, snd_pcm_lib_buffer_bytes(subs), + subs->runtime->period_size, snd_pcm_lib_period_bytes(subs)); + } else { + dev_dbg(dev, "%s() FE DMA not found\n", __func__); + } + + dev_dbg(dev, "%s: initializing start_handle tasklet\n", __func__); + tasklet_init(&dma->start_handle, &mchp_asrc_start_handle, (unsigned long)priv); + + return 0; +} + +static void mchp_asrc_dmaengine_resume(struct mchp_asrc_dma_priv *priv) +{ + struct mchp_asrc_dma_be_priv *dma_be_priv; + + list_for_each_entry(dma_be_priv, &priv->dma_in_head, list) { + if (dma_be_priv->chan) + dmaengine_resume(dma_be_priv->chan); + } + list_for_each_entry(dma_be_priv, &priv->dma_out_head, list) { + if (dma_be_priv->chan) + dmaengine_resume(dma_be_priv->chan); + } +} + +static void mchp_asrc_dmaengine_pause(struct mchp_asrc_dma_priv *priv) +{ + struct mchp_asrc_dma_be_priv *dma_be_priv; + + list_for_each_entry(dma_be_priv, &priv->dma_in_head, list) { + if (dma_be_priv->chan) + dmaengine_pause(dma_be_priv->chan); + } + list_for_each_entry(dma_be_priv, &priv->dma_out_head, list) { + if (dma_be_priv->chan) + dmaengine_pause(dma_be_priv->chan); + } +} + +static void mchp_asrc_dmaengine_term(struct mchp_asrc_dma_priv *priv) +{ + struct mchp_asrc_dma_be_priv *dma_be_priv; + + list_for_each_entry(dma_be_priv, &priv->dma_in_head, list) { + if (dma_be_priv->chan) + dmaengine_terminate_async(dma_be_priv->chan); + else + dev_dbg(priv->dev, "no DMA channel in IN queue\n"); + } + list_for_each_entry(dma_be_priv, &priv->dma_out_head, list) { + if (dma_be_priv->chan) + dmaengine_terminate_async(dma_be_priv->chan); + else + dev_dbg(priv->dev, "no DMA channel in OUT queue\n"); + } +} + +static int mchp_asrc_dma_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct mchp_asrc_dma_priv *priv = mchp_asrc_substream_to_prtd(substream); + int ret; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + ret = mchp_asrc_dma_prepare(component, substream, priv); + if (ret) + return ret; + break; + case SNDRV_PCM_TRIGGER_RESUME: + fallthrough; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + mchp_asrc_dmaengine_resume(priv); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + if (runtime->info & SNDRV_PCM_INFO_PAUSE) + mchp_asrc_dmaengine_pause(priv); + else + mchp_asrc_dmaengine_term(priv); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + mchp_asrc_dmaengine_pause(priv); + break; + case SNDRV_PCM_TRIGGER_STOP: + mchp_asrc_dmaengine_term(priv); + break; + default: + return -EINVAL; + } + + return 0; +} + +static snd_pcm_uframes_t +mchp_asrc_dma_pcm_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct mchp_asrc_dma_priv *priv = mchp_asrc_substream_to_prtd(substream); + struct mchp_asrc_dma_be_priv *dma_fe_priv; + struct dma_tx_state state; + enum dma_status status; + unsigned int buf_size; + unsigned int pos = 0; + bool is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + struct list_head *dma_fe_head = is_playback ? &priv->dma_in_head : &priv->dma_out_head; + + priv = mchp_asrc_substream_to_prtd(substream); + if (priv->no_residue) + return bytes_to_frames(runtime, priv->pos); + + list_for_each_entry(dma_fe_priv, dma_fe_head, list) { + if (!dma_fe_priv->phandle) + break; + } + if (list_entry_is_head(dma_fe_priv, dma_fe_head, list)) { + dev_err(priv->dev, "FE DMA phandle not found\n"); + return 0; + } + status = dmaengine_tx_status(dma_fe_priv->chan, priv->cookie, &state); + if (status == DMA_IN_PROGRESS || status == DMA_PAUSED) { + buf_size = snd_pcm_lib_buffer_bytes(substream); + if (state.residue > 0 && state.residue <= buf_size) + pos = buf_size - state.residue; + + runtime->delay = bytes_to_frames(runtime, state.in_flight_bytes); + } + + return bytes_to_frames(substream->runtime, pos); +} + +static int mchp_asrc_dma_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm_substream *substream; + struct snd_pcm *pcm = rtd->pcm; + int i; + + for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_LAST; i++) { + substream = pcm->streams[i].substream; + if (!substream) + continue; + + substream->dma_buffer.bytes = 0; + substream->dma_buffer.area = NULL; + substream->dma_buffer.addr = 0; + substream->dma_buffer.private_data = NULL; + } + + return 0; +} + +static void mchp_asrc_dma_pcm_free(struct snd_soc_component *component, + struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + int i; + + for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_LAST; i++) { + substream = pcm->streams[i].substream; + if (!substream) + continue; + + substream->dma_buffer.area = NULL; + substream->dma_buffer.addr = 0; + } +} + +const struct snd_soc_component_driver mchp_asrc_platform = { + .name = MCHP_ASRC_DMAENGINE_PCM_DRV_NAME, + .pcm_construct = mchp_asrc_dma_pcm_new, + .pcm_destruct = mchp_asrc_dma_pcm_free, + .hw_params = mchp_asrc_dma_hw_params, + .hw_free = mchp_asrc_dma_hw_free, + .trigger = mchp_asrc_dma_trigger, + .open = mchp_asrc_dma_open, + .close = mchp_asrc_dma_close, + .pointer = mchp_asrc_dma_pcm_pointer, +}; +EXPORT_SYMBOL_GPL(mchp_asrc_platform); + +int mchp_asrc_pcm_register(struct device *dev) +{ + int err; + + err = devm_snd_soc_register_component(dev, &mchp_asrc_platform, NULL, 0); + if (err) { + dev_err(dev, "failed to register ASoC platform for ASRC\n"); + return err; + } + + return 0; +} diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/sound/soc/atmel/mchp-asrc.h linux-microchip/sound/soc/atmel/mchp-asrc.h --- linux-6.6.51/sound/soc/atmel/mchp-asrc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-microchip/sound/soc/atmel/mchp-asrc.h 2024-11-26 14:02:39.366527160 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4 @ +/* SPDX-License-Identifier: GPL-2.0 */ +// ASoC Platform header file for Microchip ASRC +// +// Copyright (C) 2018 Microchip Technology Inc. and its subsidiaries +// +// Author: Codrin Ciubotariu <codrin.ciubotariu@microchip.com> + +#ifndef __MCHP_ASRC_H +#define __MCHP_ASRC_H + +#include <sound/soc.h> +#include <sound/dmaengine_pcm.h> + +#define MCHP_ASRC_NB_CHANNELS 8 +#define MCHP_ASRC_NB_STEREO_CH ((MCHP_ASRC_NB_CHANNELS) / 2) + +struct mchp_asrc_dmaengine_be_dma { + struct of_phandle_args *phandle; + struct snd_dmaengine_dai_dma_data dma_data; + struct list_head list; +}; + +struct mchp_asrc_dmaengine_dai_dma { + struct list_head dma_in_list; + struct list_head dma_out_list; + struct tasklet_struct start_handle; + int unlocked_dsps; +}; + +int mchp_asrc_pcm_register(struct device *dev); + +#endif /* __MCHP_ASRC_H */ diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/sound/soc/atmel/mchp-i2s-mcc.c linux-microchip/sound/soc/atmel/mchp-i2s-mcc.c --- linux-6.6.51/sound/soc/atmel/mchp-i2s-mcc.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/sound/soc/atmel/mchp-i2s-mcc.c 2024-11-26 14:02:39.366527160 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:507 @ return !!(sr & (MCHP_I2SMCC_SR_TXEN | MCHP_I2SMCC_SR_RXEN)); } +static inline int mchp_i2s_mcc_period_to_maxburst(int period_size, int sample_size) +{ + if (!(period_size % (sample_size * 8))) + return 8; + if (!(period_size % (sample_size * 4))) + return 4; + if (!(period_size % (sample_size * 2))) + return 2; + return 1; +} + static int mchp_i2s_mcc_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { unsigned long rate = 0; struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai); + int sample_bytes = params_physical_width(params) / 8; + int period_bytes = params_period_size(params) * + params_channels(params) * sample_bytes; + int maxburst; u32 mra = 0; u32 mrb = 0; unsigned int channels = params_channels(params); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:537 @ int ret; bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); - dev_dbg(dev->dev, "%s() rate=%u format=%#x width=%u channels=%u\n", + dev_dbg(dev->dev, "%s() rate=%u format=%#x width=%u channels=%u period_bytes=%d\n", __func__, params_rate(params), params_format(params), - params_width(params), params_channels(params)); + params_width(params), params_channels(params), period_bytes); switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:648 @ * We must have the same burst size configured * in the DMA transfer and in out IP */ - mrb |= MCHP_I2SMCC_MRB_DMACHUNK(channels); + maxburst = mchp_i2s_mcc_period_to_maxburst(period_bytes, sample_bytes); + mrb |= MCHP_I2SMCC_MRB_DMACHUNK(maxburst); if (is_playback) - dev->playback.maxburst = 1 << (fls(channels) - 1); + dev->playback.maxburst = maxburst; else - dev->capture.maxburst = 1 << (fls(channels) - 1); + dev->capture.maxburst = maxburst; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S8: @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:927 @ static struct snd_soc_dai_driver mchp_i2s_mcc_dai = { .playback = { - .stream_name = "I2SMCC-Playback", + .stream_name = "Playback", .channels_min = 1, .channels_max = 8, .rates = MCHP_I2SMCC_RATES, .formats = MCHP_I2SMCC_FORMATS, }, .capture = { - .stream_name = "I2SMCC-Capture", + .stream_name = "Capture", .channels_min = 1, .channels_max = 8, .rates = MCHP_I2SMCC_RATES, diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/sound/soc/atmel/mchp-pdmc.c linux-microchip/sound/soc/atmel/mchp-pdmc.c --- linux-6.6.51/sound/soc/atmel/mchp-pdmc.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/sound/soc/atmel/mchp-pdmc.c 2024-11-26 14:02:39.366527160 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:17 @ #include <linux/of.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> +#include <linux/spinlock.h> #include <sound/core.h> #include <sound/dmaengine_pcm.h> @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:108 @ struct mchp_pdmc { struct mic_map channel_mic_map[MCHP_PDMC_MAX_CHANNELS]; + spinlock_t busy_lock; /* lock protecting busy */ struct device *dev; struct snd_dmaengine_dai_dma_data addr; struct regmap *regmap; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:120 @ int mic_no; int sinc_order; bool audio_filter_en; + u8 gclk_enabled:1; + u8 busy:1; }; static const char *const mchp_pdmc_sinc_filter_order_text[] = { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:165 @ return -EINVAL; val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; - if (val == dd->sinc_order) + + spin_lock(&dd->busy_lock); + if (dd->busy) { + spin_unlock((&dd->busy_lock)); + return -EBUSY; + } + if (val == dd->sinc_order) { + spin_unlock((&dd->busy_lock)); return 0; + } dd->sinc_order = val; + spin_unlock((&dd->busy_lock)); return 1; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:200 @ struct mchp_pdmc *dd = snd_soc_component_get_drvdata(component); bool af = uvalue->value.integer.value[0] ? true : false; - if (dd->audio_filter_en == af) + spin_lock(&dd->busy_lock); + if (dd->busy) { + spin_unlock((&dd->busy_lock)); + return -EBUSY; + } + if (dd->audio_filter_en == af) { + spin_unlock((&dd->busy_lock)); return 0; + } dd->audio_filter_en = af; + spin_unlock((&dd->busy_lock)); return 1; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:309 @ if (!substream) return -ENODEV; + if (!substream->runtime) + return 0; /* just for avoiding error from alsactl restore */ + map = mchp_pdmc_chmap_get(substream, info); if (!map) return -EINVAL; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:397 @ }, }; -static int mchp_pdmc_close(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - return snd_soc_add_component_controls(component, mchp_pdmc_snd_controls, - ARRAY_SIZE(mchp_pdmc_snd_controls)); -} - -static int mchp_pdmc_open(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - int i; - - /* remove controls that can't be changed at runtime */ - for (i = 0; i < ARRAY_SIZE(mchp_pdmc_snd_controls); i++) { - const struct snd_kcontrol_new *control = &mchp_pdmc_snd_controls[i]; - struct snd_ctl_elem_id id; - int err; - - if (component->name_prefix) - snprintf(id.name, sizeof(id.name), "%s %s", component->name_prefix, - control->name); - else - strscpy(id.name, control->name, sizeof(id.name)); - - id.numid = 0; - id.iface = control->iface; - id.device = control->device; - id.subdevice = control->subdevice; - id.index = control->index; - err = snd_ctl_remove_id(component->card->snd_card, &id); - if (err < 0) - dev_err(component->dev, "%d: Failed to remove %s\n", err, - control->name); - } - - return 0; -} - static const struct snd_soc_component_driver mchp_pdmc_dai_component = { .name = "mchp-pdmc", .controls = mchp_pdmc_snd_controls, .num_controls = ARRAY_SIZE(mchp_pdmc_snd_controls), - .open = &mchp_pdmc_open, - .close = &mchp_pdmc_close, - .legacy_dai_naming = 1, - .trigger_start = SND_SOC_TRIGGER_ORDER_LDC, }; static const unsigned int mchp_pdmc_1mic[] = {1}; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:496 @ return 0; } -static inline int mchp_pdmc_period_to_maxburst(int period_size) +static inline int mchp_pdmc_period_to_maxburst(int period_size, int sample_size) { - if (!(period_size % 8)) + if (!(period_size % (sample_size * 8))) return 8; - if (!(period_size % 4)) + if (!(period_size % (sample_size * 4))) return 4; - if (!(period_size % 2)) + if (!(period_size % (sample_size * 2))) return 2; return 1; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:532 @ unsigned int channels = params_channels(params); unsigned int osr = 0, osr_start; unsigned int fs = params_rate(params); + int sample_bytes = params_physical_width(params) / 8; + int period_bytes = params_period_size(params) * + params_channels(params) * sample_bytes; + int maxburst; u32 mr_val = 0; u32 cfgr_val = 0; int i; int ret; - dev_dbg(comp->dev, "%s() rate=%u format=%#x width=%u channels=%u\n", + dev_dbg(comp->dev, "%s() rate=%u format=%#x width=%u channels=%u period_bytes=%d\n", __func__, params_rate(params), params_format(params), - params_width(params), params_channels(params)); + params_width(params), params_channels(params), period_bytes); if (channels > dd->mic_no) { dev_err(comp->dev, "more channels %u than microphones %d\n", @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:560 @ cfgr_val |= MCHP_PDMC_CFGR_BSSEL(i); } + if (dd->gclk_enabled) { + clk_disable_unprepare(dd->gclk); + dd->gclk_enabled = 0; + } + + /* + * from these point forward, we consider the controller busy, so the + * audio filter and SINC order can't be changed + */ + spin_lock(&dd->busy_lock); + dd->busy = 1; + spin_unlock((&dd->busy_lock)); for (osr_start = dd->audio_filter_en ? 64 : 8; osr_start <= 256 && best_diff_rate; osr_start *= 2) { long round_rate; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:609 @ mr_val |= FIELD_PREP(MCHP_PDMC_MR_SINCORDER_MASK, dd->sinc_order); - dd->addr.maxburst = mchp_pdmc_period_to_maxburst(snd_pcm_lib_period_bytes(substream)); + maxburst = mchp_pdmc_period_to_maxburst(period_bytes, sample_bytes); + dd->addr.maxburst = maxburst; mr_val |= FIELD_PREP(MCHP_PDMC_MR_CHUNK_MASK, dd->addr.maxburst); dev_dbg(comp->dev, "maxburst set to %d\n", dd->addr.maxburst); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:625 @ return 0; } +static int mchp_pdmc_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai); + + spin_lock(&dd->busy_lock); + dd->busy = 0; + spin_unlock((&dd->busy_lock)); + + return 0; +} + static void mchp_pdmc_noise_filter_workaround(struct mchp_pdmc *dd) { u32 tmp, steps = 16; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:769 @ .set_fmt = mchp_pdmc_set_fmt, .startup = mchp_pdmc_startup, .hw_params = mchp_pdmc_hw_params, + .hw_free = mchp_pdmc_hw_free, .trigger = mchp_pdmc_trigger, .pcm_new = &mchp_pdmc_pcm_new, }; static struct snd_soc_dai_driver mchp_pdmc_dai = { + .name = "mchp-pdmc", .capture = { .stream_name = "Capture", .channels_min = 1, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1097 @ */ dd->audio_filter_en = true; dd->sinc_order = 3; + spin_lock_init(&dd->busy_lock); dd->addr.addr = (dma_addr_t)res->start + MCHP_PDMC_RHR; platform_set_drvdata(pdev, dd); diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/sound/soc/atmel/mchp-spdifrx.c linux-microchip/sound/soc/atmel/mchp-spdifrx.c --- linux-6.6.51/sound/soc/atmel/mchp-spdifrx.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/sound/soc/atmel/mchp-spdifrx.c 2024-11-26 14:02:39.366527160 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1017 @ static struct snd_soc_dai_driver mchp_spdifrx_dai = { .name = "mchp-spdifrx", .capture = { - .stream_name = "S/PDIF Capture", + .stream_name = "Capture", .channels_min = SPDIFRX_CHANNELS, .channels_max = SPDIFRX_CHANNELS, .rates = MCHP_SPDIF_RATES, diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/sound/soc/atmel/mchp-spdiftx.c linux-microchip/sound/soc/atmel/mchp-spdiftx.c --- linux-6.6.51/sound/soc/atmel/mchp-spdiftx.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/sound/soc/atmel/mchp-spdiftx.c 2024-11-26 14:02:39.366527160 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:710 @ static struct snd_soc_dai_driver mchp_spdiftx_dai = { .name = "mchp-spdiftx", .playback = { - .stream_name = "S/PDIF Playback", + .stream_name = "Playback", .channels_min = 1, .channels_max = 2, .rates = MCHP_SPDIFTX_RATES, diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/sound/soc/soc-dapm.c linux-microchip/sound/soc/soc-dapm.c --- linux-6.6.51/sound/soc/soc-dapm.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/sound/soc/soc-dapm.c 2024-11-26 14:02:39.442528521 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4035 @ return ret; } +static int +snd_soc_dai_link_loopback_event_pre_pmu(struct snd_soc_dapm_widget *w, + struct snd_pcm_substream *substream) +{ + struct snd_soc_dapm_path *path; + struct snd_soc_dai *source, *sink; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + int ret = 0; + + rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].state = SND_SOC_DPCM_STATE_START; + rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].state = SND_SOC_DPCM_STATE_START; + + substream->stream = SNDRV_PCM_STREAM_CAPTURE; + snd_soc_dapm_widget_for_each_source_path(w, path) { + source = path->source->priv; + snd_soc_dai_activate(source, substream->stream); + } + + substream->stream = SNDRV_PCM_STREAM_PLAYBACK; + snd_soc_dapm_widget_for_each_sink_path(w, path) { + sink = path->sink->priv; + snd_soc_dai_activate(sink, substream->stream); + } + + return ret; +} + +static int snd_soc_dai_link_loopback_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_dapm_path *path; + struct snd_soc_dai *source, *sink; + struct snd_pcm_substream *substream = w->priv; + int ret = 0, saved_stream = substream->stream; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + + if (WARN_ON(list_empty(&w->edges[SND_SOC_DAPM_DIR_OUT]) || + list_empty(&w->edges[SND_SOC_DAPM_DIR_IN]))) + return -EINVAL; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = snd_soc_dai_link_loopback_event_pre_pmu(w, substream); + if (ret < 0) + goto out; + + break; + + case SND_SOC_DAPM_POST_PMU: + snd_soc_dapm_widget_for_each_sink_path(w, path) { + sink = path->sink->priv; + + ret = snd_soc_dai_digital_mute(sink, 0, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret != 0 && ret != -ENOTSUPP) + dev_warn(sink->dev, + "ASoC: Failed to unmute: %d\n", ret); + ret = 0; + } + break; + + case SND_SOC_DAPM_PRE_PMD: + snd_soc_dapm_widget_for_each_sink_path(w, path) { + sink = path->sink->priv; + + ret = snd_soc_dai_digital_mute(sink, 1, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret != 0 && ret != -ENOTSUPP) + dev_warn(sink->dev, + "ASoC: Failed to mute: %d\n", ret); + ret = 0; + } + + substream->stream = SNDRV_PCM_STREAM_CAPTURE; + snd_soc_dapm_widget_for_each_source_path(w, path) { + source = path->source->priv; + snd_soc_dai_deactivate(source, substream->stream); + } + + substream->stream = SNDRV_PCM_STREAM_PLAYBACK; + snd_soc_dapm_widget_for_each_sink_path(w, path) { + sink = path->sink->priv; + snd_soc_dai_deactivate(sink, substream->stream); + } + break; + + case SND_SOC_DAPM_POST_PMD: + rtd->dpcm[SNDRV_PCM_STREAM_PLAYBACK].state = SND_SOC_DPCM_STATE_CLOSE; + rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].state = SND_SOC_DPCM_STATE_CLOSE; + break; + + default: + WARN(1, "Unknown event %d\n", event); + ret = -EINVAL; + } + +out: + /* Restore the substream direction */ + substream->stream = saved_stream; + return ret; +} + static int snd_soc_dapm_dai_link_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4256 @ } static struct snd_soc_dapm_widget * +snd_soc_dapm_new_loopback_dai(struct snd_soc_card *card, struct snd_pcm_substream *substream, + char *id) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dapm_widget template; + struct snd_soc_dapm_widget *w; + char *link_name; + int ret; + + link_name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-%s", + rtd->dai_link->name, id); + if (!link_name) + return ERR_PTR(-ENOMEM); + + memset(&template, 0, sizeof(template)); + template.reg = SND_SOC_NOPM; + template.id = snd_soc_dapm_dai_link; + template.name = link_name; + template.event = snd_soc_dai_link_loopback_event; + template.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD; + template.kcontrol_news = NULL; + + w = snd_soc_dapm_new_control_unlocked(&card->dapm, &template); + if (IS_ERR(w)) { + ret = PTR_ERR(w); + dev_err(rtd->dev, "ASoC: Failed to create %s widget: %d\n", + link_name, ret); + goto outfree_link_name; + } + + w->priv = substream; + + return w; + +outfree_link_name: + devm_kfree(card->dev, link_name); + return ERR_PTR(ret); +} + +static struct snd_soc_dapm_widget * snd_soc_dapm_new_dai(struct snd_soc_card *card, struct snd_pcm_substream *substream, char *id) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4511 @ struct snd_soc_dapm_widget **src[] = { &cpu, &codec }; struct snd_soc_dapm_widget **sink[] = { &codec, &cpu }; char *widget_name[] = { "playback", "capture" }; + struct snd_pcm_str *streams = rtd->pcm->streams; int stream; + if (dai_link->c2c_params || (rtd->dai_link->dynamic && rtd->dai_link->dpcm_loopback)) { + struct snd_soc_dapm_widget *dai = NULL; + + /*Playback CPU to Capture Widget*/ + cpu = snd_soc_dai_get_widget(cpu_dai, SNDRV_PCM_STREAM_CAPTURE); + /*Capture CPU to Playback Widget*/ + codec = snd_soc_dai_get_widget(cpu_dai, SNDRV_PCM_STREAM_PLAYBACK); + + if (!rtd->c2c_widget[SNDRV_PCM_STREAM_PLAYBACK] && + !rtd->c2c_widget[SNDRV_PCM_STREAM_CAPTURE]) { + struct snd_pcm_substream *substream; + + streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd; + streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd; + substream = streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + dai = snd_soc_dapm_new_loopback_dai(card, substream, "hostless"); + if (IS_ERR(dai)) + return; + rtd->c2c_widget[SNDRV_PCM_STREAM_PLAYBACK] = dai; + rtd->c2c_widget[SNDRV_PCM_STREAM_CAPTURE] = dai; + } + dapm_connect_dai_routes(&card->dapm, cpu_dai, cpu, + dai, cpu_dai, codec); + return; + } + for_each_pcm_streams(stream) { int stream_cpu, stream_codec; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:4621 @ * dynamic FE links have no fixed DAI mapping. * CODEC<->CODEC links have no direct connection. */ - if (rtd->dai_link->dynamic) + if (rtd->dai_link->dynamic && !rtd->dai_link->dpcm_loopback) continue; if (rtd->dai_link->num_cpus == 1) { - for_each_rtd_codec_dais(rtd, i, codec_dai) - dapm_connect_dai_pair(card, rtd, codec_dai, + /* connect FE to FE */ + if (rtd->dai_link->dynamic && rtd->dai_link->dpcm_loopback) { + dapm_connect_dai_pair(card, rtd, asoc_rtd_to_cpu(rtd, 0), asoc_rtd_to_cpu(rtd, 0)); + } else { + for_each_rtd_codec_dais(rtd, i, codec_dai) + dapm_connect_dai_pair(card, rtd, codec_dai, + asoc_rtd_to_cpu(rtd, 0)); + } } else if (rtd->dai_link->num_codecs == rtd->dai_link->num_cpus) { for_each_rtd_codec_dais(rtd, i, codec_dai) dapm_connect_dai_pair(card, rtd, codec_dai, diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/sound/soc/soc-generic-dmaengine-pcm.c linux-microchip/sound/soc/soc-generic-dmaengine-pcm.c --- linux-6.6.51/sound/soc/soc-generic-dmaengine-pcm.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/sound/soc/soc-generic-dmaengine-pcm.c 2024-11-26 14:02:39.442528521 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:94 @ if (ret) return ret; - return dmaengine_slave_config(chan, &slave_config); + ret = dmaengine_slave_config(chan, &slave_config); + if (ret) + return ret; + + if (!substream->managed_buffer_alloc) { + substream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_IRAM; + substream->dma_buffer.dev.dev = chan->device->dev; + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(params)); + } + + return 0; +} + +static int dmaengine_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + if (!substream->managed_buffer_alloc) + return snd_pcm_lib_free_pages(substream); + + return 0; } static int @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:353 @ .open = dmaengine_pcm_open, .close = dmaengine_pcm_close, .hw_params = dmaengine_pcm_hw_params, + .hw_free = dmaengine_pcm_hw_free, .trigger = dmaengine_pcm_trigger, .pointer = dmaengine_pcm_pointer, .pcm_construct = dmaengine_pcm_new, @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:366 @ .open = dmaengine_pcm_open, .close = dmaengine_pcm_close, .hw_params = dmaengine_pcm_hw_params, + .hw_free = dmaengine_pcm_hw_free, .trigger = dmaengine_pcm_trigger, .pointer = dmaengine_pcm_pointer, .copy = dmaengine_copy, diff '--color=auto' -Nur '--exclude=.git*' --no-dereference linux-6.6.51/sound/soc/soc-pcm.c linux-microchip/sound/soc/soc-pcm.c --- linux-6.6.51/sound/soc/soc-pcm.c 2024-09-12 11:11:45.000000000 +0200 +++ linux-microchip/sound/soc/soc-pcm.c 2024-11-26 14:02:39.442528521 +0100 @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1004 @ snd_soc_dpcm_mutex_assert_held(rtd); - ret = soc_pcm_params_symmetry(substream, params); - if (ret) - goto out; + if (params) { + ret = soc_pcm_params_symmetry(substream, params); + if (ret) + goto out; + } ret = snd_soc_link_hw_params(substream, params); if (ret < 0) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1034 @ if (!snd_soc_dai_stream_valid(codec_dai, substream->stream)) continue; + if (!params) { + ret = snd_soc_dai_hw_params(codec_dai, substream, NULL); + + if (ret < 0) + goto out; + continue; + } /* copy params for each codec */ tmp_params = *params; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1068 @ if (!snd_soc_dai_stream_valid(cpu_dai, substream->stream)) continue; + /* spawn a branch to configure the hostless connction */ + if (!params) { + ret = snd_soc_dai_hw_params(cpu_dai, substream, NULL); + + if (ret < 0) + goto out; + + /* set the pcm dai params to NULL for hostless conn */ + soc_pcm_set_dai_params(cpu_dai, NULL); + /* make an update for soc dapm dai according with + * NULL params for hostless connection + */ + snd_soc_dapm_update_dai(substream, &tmp_params, cpu_dai); + continue; + } + /* copy params for each cpu */ tmp_params = *params; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1297 @ return 1; } -/* reparent a BE onto another FE */ -static void dpcm_be_reparent(struct snd_soc_pcm_runtime *fe, - struct snd_soc_pcm_runtime *be, int stream) -{ - struct snd_soc_dpcm *dpcm; - struct snd_pcm_substream *fe_substream, *be_substream; - - /* reparent if BE is connected to other FEs */ - if (!be->dpcm[stream].users) - return; - - be_substream = snd_soc_dpcm_get_substream(be, stream); - if (!be_substream) - return; - - for_each_dpcm_fe(be, stream, dpcm) { - if (dpcm->fe == fe) - continue; - - dev_dbg(fe->dev, "reparent %s path %s %s %s\n", - stream ? "capture" : "playback", - dpcm->fe->dai_link->name, - stream ? "<-" : "->", dpcm->be->dai_link->name); - - fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, stream); - be_substream->runtime = fe_substream->runtime; - break; - } -} - /* disconnect a BE and FE */ void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream) { @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1318 @ stream ? "capture" : "playback", fe->dai_link->name, stream ? "<-" : "->", dpcm->be->dai_link->name); - /* BEs still alive need new FE */ - dpcm_be_reparent(fe, dpcm->be, stream); - list_del(&dpcm->list_be); list_move(&dpcm->list_fe, &deleted_dpcms); } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1461 @ /* Destroy any old FE <--> BE connections */ for_each_dpcm_be(fe, stream, dpcm) { - if (dpcm_be_is_active(dpcm, stream, *list_)) + if (!fe->dai_link->dpcm_loopback && dpcm_be_is_active(dpcm, stream, *list_)) { continue; + } dev_dbg(fe->dev, "ASoC: pruning %s BE %s for %s\n", stream ? "capture" : "playback", @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1488 @ int i, new = 0, err; /* don't connect if FE is not running */ - if (!fe_substream->runtime && !fe->fe_compr) + if (!fe->dai_link->dpcm_loopback && !fe_substream->runtime && !fe->fe_compr) return new; /* Create any new FE <--> BE connections */ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1520 @ * This helps to avoid unnecessary re-configuration of an * already active BE on such systems. */ - if (fe->card->component_chaining && + if (!fe->dai_link->dpcm_loopback && + fe->card->component_chaining && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_NEW) && (be->dpcm[stream].state != SND_SOC_DPCM_STATE_CLOSE)) continue; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1604 @ } __soc_pcm_close(be, be_substream); - be_substream->runtime = NULL; + if (be_substream->runtime) { + snd_pcm_runtime_free(be_substream->runtime); + be_substream->runtime = NULL; + } + be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; } } int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream) { - struct snd_pcm_substream *fe_substream = snd_soc_dpcm_get_substream(fe, stream); struct snd_soc_pcm_runtime *be; struct snd_soc_dpcm *dpcm; int err, count = 0; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1622 @ /* only startup BE DAIs that are either sinks or sources to this FE DAI */ for_each_dpcm_be(fe, stream, dpcm) { struct snd_pcm_substream *be_substream; + struct snd_pcm_runtime *runtime; + struct snd_soc_pcm_stream *pcm_stream; be = dpcm->be; be_substream = snd_soc_dpcm_get_substream(be, stream); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1656 @ dev_dbg(be->dev, "ASoC: open %s BE %s\n", stream ? "capture" : "playback", be->dai_link->name); - be_substream->runtime = fe_substream->runtime; + runtime = snd_pcm_runtime_alloc(); + if (!runtime) { + err = -ENOMEM; + goto unwind; + } + + be_substream->runtime = runtime; + err = __soc_pcm_open(be, be_substream); if (err < 0) { be->dpcm[stream].users--; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:1675 @ be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE; goto unwind; } + be->dpcm[stream].be_start = 0; + + /* initialize the BE HW parameters */ + soc_pcm_init_runtime_hw(be_substream); + + /* initialize the BE constraints */ + err = snd_pcm_hw_constraints_init(be_substream); + if (err < 0) { + dev_err(be->dev, "dpcm_be_hw_constraints_init failed\n"); + goto unwind; + } + + pcm_stream = snd_soc_dai_get_pcm_stream(asoc_rtd_to_cpu(be, 0), + stream); + + soc_pcm_hw_update_rate(&runtime->hw, pcm_stream); + soc_pcm_hw_update_chan(&runtime->hw, pcm_stream); + soc_pcm_hw_update_format(&runtime->hw, pcm_stream); + + err = snd_pcm_hw_constraints_complete(be_substream); + if (err < 0) { + dev_err(fe->dev, "snd_pcm_hw_constraints_complete failed\n"); + goto unwind; + } + be->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN; count++; } @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2044 @ return 0; } +static int dpcm_be_dai_hw_params_init(struct snd_soc_pcm_runtime *fe, int stream) +{ + struct snd_pcm_hw_params *params = &fe->dpcm[stream].hw_params; + int k; + + for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; + k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) + snd_mask_any(hw_param_mask(params, k)); + + for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; + k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) + snd_interval_any(hw_param_interval(params, k)); + + if (fe->dai_link->dpcm_merged_format) { + memcpy(hw_param_interval(&fe->dpcm[stream].hw_params, + SNDRV_PCM_HW_PARAM_FORMAT), + hw_param_interval(params, SNDRV_PCM_HW_PARAM_FORMAT), + sizeof(struct snd_interval)); + } + if (fe->dai_link->dpcm_merged_chan) { + memcpy(hw_param_interval(&fe->dpcm[stream].hw_params, + SNDRV_PCM_HW_PARAM_CHANNELS), + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS), + sizeof(struct snd_interval)); + } + if (fe->dai_link->dpcm_merged_rate) { + memcpy(hw_param_interval(&fe->dpcm[stream].hw_params, + SNDRV_PCM_HW_PARAM_RATE), + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE), + sizeof(struct snd_interval)); + } + + return 0; +} + int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_soc_pcm_runtime *be; struct snd_pcm_substream *be_substream; struct snd_soc_dpcm *dpcm; int ret; + int current_stream = stream; - for_each_dpcm_be(fe, stream, dpcm) { + /* initialize the BE HW params */ + dpcm_be_dai_hw_params_init(fe, stream); + + for_each_dpcm_be(fe, current_stream, dpcm) { struct snd_pcm_hw_params hw_params; be = dpcm->be; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2100 @ if (!snd_soc_dpcm_be_can_update(fe, be, stream)) continue; + if (!be_substream) { + dev_err(be->dev, "ASoC: no backend %s stream\n", + stream ? "capture" : "playback"); + continue; + } /* copy params for each dpcm */ memcpy(&hw_params, &fe->dpcm[stream].hw_params, sizeof(struct snd_pcm_hw_params)); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2114 @ if (ret < 0) goto unwind; + /* apply constrains */ + hw_params.rmask = ~0U; + ret = snd_pcm_hw_refine(be_substream, &hw_params); + if (ret < 0) { + dev_err(fe->dev, "failed to refine hw parameters: %d\n", ret); + goto unwind; + } + /* copy the fixed-up hw params for BE dai */ memcpy(&be->dpcm[stream].hw_params, &hw_params, sizeof(struct snd_pcm_hw_params)); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2143 @ goto unwind; be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS; + snd_pcm_runtime_set(be_substream, &be->dpcm[stream].hw_params); } return 0; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2184 @ snd_soc_dpcm_mutex_lock(fe); dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE); - memcpy(&fe->dpcm[stream].hw_params, params, - sizeof(struct snd_pcm_hw_params)); ret = dpcm_be_dai_hw_params(fe, stream); if (ret < 0) goto out; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2593 @ { struct snd_pcm_substream *substream = snd_soc_dpcm_get_substream(fe, stream); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream]; int err; dev_dbg(fe->dev, "ASoC: runtime %s close on FE %s\n", stream ? "capture" : "playback", fe->dai_link->name); - if (trigger == SND_SOC_DPCM_TRIGGER_BESPOKE) { + switch (trigger) { + case SND_SOC_DPCM_TRIGGER_BESPOKE: /* call bespoke trigger - FE takes care of all BE triggers */ dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd stop\n", fe->dai_link->name); err = snd_soc_pcm_dai_bespoke_trigger(substream, SNDRV_PCM_TRIGGER_STOP); - } else { - dev_dbg(fe->dev, "ASoC: trigger FE %s cmd stop\n", + if (err < 0) + dev_err(fe->dev, "ASoC: trigger FE failed %d\n", err); + break; + case SND_SOC_DPCM_TRIGGER_PRE: + dev_dbg(fe->dev, "ASoC: PRE trigger FE %s cmd stop\n", fe->dai_link->name); + if (fe->dai_link->dpcm_loopback) { + err = soc_pcm_trigger(substream, SNDRV_PCM_TRIGGER_STOP); + if (err < 0) + dev_err(fe->dev, "ASoC: trigger for FE failed %d\n", err); + } err = dpcm_be_dai_trigger(fe, stream, SNDRV_PCM_TRIGGER_STOP); + if (err < 0) + dev_err(fe->dev, "ASoC: trigger FE failed %d\n", err); + break; + case SND_SOC_DPCM_TRIGGER_POST: + dev_dbg(fe->dev, "ASoC: POST trigger FE %s cmd stop\n", + fe->dai_link->name); + + err = dpcm_be_dai_trigger(fe, stream, SNDRV_PCM_TRIGGER_STOP); + if (err < 0) + dev_err(fe->dev, "ASoC: trigger FE failed %d\n", err); + + if (fe->dai_link->dpcm_loopback) { + err = soc_pcm_trigger(substream, SNDRV_PCM_TRIGGER_STOP); + if (err < 0) + dev_err(fe->dev, "ASoC: trigger for FE failed %d\n", err); + } + break; + } + + if (fe->dai_link->dpcm_loopback) { + err = __soc_pcm_hw_free(rtd, substream); + if (err < 0) + dev_err(fe->dev, "ASoC: hw_free for FE failed %d\n", err); } dpcm_be_dai_hw_free(fe, stream); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2652 @ /* run the stream event for each BE */ dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP); - return soc_pcm_ret(fe, err); + if (err < 0) + dev_err(fe->dev, "ASoC: %s() failed (%d)\n", __func__, err); + + if (fe->dai_link->dpcm_loopback) { + err = __soc_pcm_close(rtd, substream); + if (err < 0) + dev_err(fe->dev, "ASoC: shutdown FE failed %d\n", err); + } + + return err; } static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream) { struct snd_pcm_substream *substream = snd_soc_dpcm_get_substream(fe, stream); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dpcm *dpcm; enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream]; int ret = 0; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2690 @ if (ret < 0) goto disconnect; + /* start the DAI frontend */ + if (fe->dai_link->dpcm_loopback) { + ret = __soc_pcm_open(rtd, substream); + if (ret < 0) { + dev_err(fe->dev, "ASoC: failed to start FE %d\n", ret); + goto close; + } + } + /* keep going if FE state is > open */ if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_OPEN) return 0; ret = dpcm_be_dai_hw_params(fe, stream); if (ret < 0) - goto close; + goto fe_shutdown; + + /* call hw_params on the frontend if it's a loopback FE */ + if (fe->dai_link->dpcm_loopback) { + ret = __soc_pcm_hw_params(rtd, substream, NULL); + if (ret < 0) { + dev_err(fe->dev, "ASoC: hw_params for FE failed %d\n", ret); + goto hw_free; + } + } /* keep going if FE state is > hw_params */ if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_PARAMS) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2722 @ ret = dpcm_be_dai_prepare(fe, stream); if (ret < 0) - goto hw_free; + goto fe_hw_free; /* run the stream event for each BE */ dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP); + /* call prepare on the frontend if it's a loopback FE */ + if (fe->dai_link->dpcm_loopback) { + ret = __soc_pcm_prepare(rtd, substream); + if (ret < 0) { + dev_err(fe->dev, "ASoC: prepare for FE %s failed\n", + fe->dai_link->name); + goto fe_hw_free; + } + } + /* keep going if FE state is > prepare */ if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_PREPARE || fe->dpcm[stream].state == SND_SOC_DPCM_STATE_STOP) return 0; - if (trigger == SND_SOC_DPCM_TRIGGER_BESPOKE) { + switch (trigger) { + case SND_SOC_DPCM_TRIGGER_BESPOKE: /* call trigger on the frontend - FE takes care of all BE triggers */ dev_dbg(fe->dev, "ASoC: bespoke trigger FE %s cmd start\n", fe->dai_link->name); ret = snd_soc_pcm_dai_bespoke_trigger(substream, SNDRV_PCM_TRIGGER_START); - if (ret < 0) - goto hw_free; - } else { - dev_dbg(fe->dev, "ASoC: trigger FE %s cmd start\n", + if (ret < 0) { + dev_err(fe->dev, "ASoC: bespoke trigger FE failed %d\n", ret); + goto fe_hw_free; + } + break; + case SND_SOC_DPCM_TRIGGER_PRE: + dev_dbg(fe->dev, "ASoC: PRE trigger FE %s cmd start\n", fe->dai_link->name); - ret = dpcm_be_dai_trigger(fe, stream, - SNDRV_PCM_TRIGGER_START); - if (ret < 0) - goto hw_free; + /* call trigger on the frontend if it's a loopback FE */ + if (fe->dai_link->dpcm_loopback) { + ret = soc_pcm_trigger(substream, SNDRV_PCM_TRIGGER_START); + if (ret < 0) { + dev_err(fe->dev, "ASoC: trigger for FE failed %d\n", ret); + goto fe_hw_free; + } + } + + ret = dpcm_be_dai_trigger(fe, stream, SNDRV_PCM_TRIGGER_START); + if (ret < 0) { + dev_err(fe->dev, "ASoC: trigger FE failed %d\n", ret); + if (fe->dai_link->dpcm_loopback) { + ret = soc_pcm_trigger(substream, SNDRV_PCM_TRIGGER_STOP); + if (ret < 0) { + dev_err(fe->dev, "ASoC: stop trigger for FE failed %d\n", + ret); + } + } + goto fe_hw_free; + } + break; + case SND_SOC_DPCM_TRIGGER_POST: + dev_dbg(fe->dev, "ASoC: POST trigger FE %s cmd start\n", + fe->dai_link->name); + + ret = dpcm_be_dai_trigger(fe, stream, SNDRV_PCM_TRIGGER_START); + if (ret < 0) { + dev_err(fe->dev, "ASoC: trigger FE failed %d\n", ret); + goto fe_hw_free; + } + + /* call trigger on the frontend if it's a loopback FE */ + if (fe->dai_link->dpcm_loopback) { + ret = soc_pcm_trigger(substream, SNDRV_PCM_TRIGGER_START); + if (ret < 0) { + dev_err(fe->dev, "ASoC: trigger for FE failed %d\n", ret); + dpcm_be_dai_trigger(fe, stream, SNDRV_PCM_TRIGGER_STOP); + goto fe_hw_free; + } + } + break; } return 0; +fe_hw_free: + if (fe->dai_link->dpcm_loopback) + __soc_pcm_hw_free(rtd, substream); hw_free: dpcm_be_dai_hw_free(fe, stream); +fe_shutdown: + if (fe->dai_link->dpcm_loopback) + __soc_pcm_close(rtd, substream); close: dpcm_be_dai_shutdown(fe, stream); disconnect: @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2836 @ struct snd_soc_dapm_widget_list *list; int stream; int count, paths; + int ret = 0; if (!fe->dai_link->dynamic) return 0; @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2855 @ dev_dbg(fe->dev, "ASoC: DPCM %s runtime update for FE %s\n", new ? "new" : "old", fe->dai_link->name); + if (fe->dai_link->dpcm_loopback && new) { + if (!fe->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->runtime || + !fe->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->runtime) { + struct snd_pcm_runtime *runtime; + + runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); + if (!runtime) + return -ENOMEM; + + fe->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->runtime = runtime; + fe->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->runtime = runtime; + } + } + for_each_pcm_streams(stream) { /* skip if FE doesn't have playback/capture capability */ @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2877 @ continue; /* skip if FE isn't currently playing/capturing */ - if (!snd_soc_dai_stream_active(asoc_rtd_to_cpu(fe, 0), stream) || - !snd_soc_dai_stream_active(asoc_rtd_to_codec(fe, 0), stream)) + if (!fe->dai_link->dpcm_loopback && + (!snd_soc_dai_stream_active(asoc_rtd_to_cpu(fe, 0), stream) || + !snd_soc_dai_stream_active(asoc_rtd_to_codec(fe, 0), stream))) { continue; + } paths = dpcm_path_get(fe, stream, &list); if (paths < 0) @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2891 @ count = dpcm_process_paths(fe, stream, &list, new); if (count) { dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE); - if (new) - dpcm_run_update_startup(fe, stream); - else - dpcm_run_update_shutdown(fe, stream); + if (new) { + ret = dpcm_run_update_startup(fe, stream); + if (ret < 0) + dev_err(fe->dev, "ASoC: failed to startup some BEs\n"); + } else { + ret = dpcm_run_update_shutdown(fe, stream); + if (ret < 0) + dev_err(fe->dev, "ASoC: failed to shutdown some BEs\n"); + ret = 0; + } dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); dpcm_clear_pending_state(fe, stream); @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2910 @ dpcm_path_put(&list); } - return 0; + if (fe->dai_link->dpcm_loopback && !new) { + struct snd_pcm_runtime *runtime = NULL; + + if (fe->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->runtime) + runtime = fe->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->runtime; + else if (fe->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->runtime) + runtime = fe->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->runtime; + + kfree(runtime); + fe->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->runtime = NULL; + fe->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->runtime = NULL; + } + return ret; } /* Called by DAPM mixer/mux changes to update audio routing between PCMs and @ linux-microchip/arch/arm/boot/dts/microchip/at91-sam9x60_curiosity.dts:2944 @ /* bring new paths up */ for_each_card_rtds(card, fe) { ret = soc_dpcm_fe_runtime_update(fe, 1); - if (ret) + if (ret) { + soc_dpcm_fe_runtime_update(fe, 0); goto out; + } } out: